aboutsummaryrefslogtreecommitdiffstats
path: root/src/gsm/gsm0808.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gsm/gsm0808.c')
-rw-r--r--src/gsm/gsm0808.c1138
1 files changed, 1065 insertions, 73 deletions
diff --git a/src/gsm/gsm0808.c b/src/gsm/gsm0808.c
index e1db175f..529dbdfe 100644
--- a/src/gsm/gsm0808.c
+++ b/src/gsm/gsm0808.c
@@ -15,20 +15,20 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
+#include "config.h"
+
#include <string.h>
#include <osmocom/core/byteswap.h>
#include <osmocom/core/endian.h>
#include <osmocom/gsm/gsm0808.h>
+#include <osmocom/gsm/gsm0808_lcs.h>
#include <osmocom/gsm/gsm0808_utils.h>
#include <osmocom/gsm/protocol/gsm_08_08.h>
#include <osmocom/gsm/gsm48.h>
+#include <osmocom/gsm/gad.h>
/*! \addtogroup gsm0808
* @{
@@ -102,13 +102,19 @@ struct msgb *gsm0808_create_layer3_2(const struct msgb *msg_l3, const struct osm
msgb_l3len(msg_l3), msg_l3->l3h);
/* AoIP: add Codec List (BSS Supported) 3.2.2.103 */
- if (scl)
- gsm0808_enc_speech_codec_list(msg, scl);
+ if (scl) {
+ if (gsm0808_enc_speech_codec_list2(msg, scl) < 0)
+ goto exit_free;
+ }
/* push the bssmap header */
msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
return msg;
+
+exit_free:
+ msgb_free(msg);
+ return NULL;
}
/*! Create "Complete L3 Info" for A, legacy implementation.
@@ -226,22 +232,32 @@ struct msgb *gsm0808_create_clear_command2(uint8_t cause, bool csfb_ind)
return msg;
}
-/*! Create BSSMAP Cipher Mode Command message
+/*! Superseded by gsm0808_create_cipher2() to include Kc128.
+ * Create BSSMAP Cipher Mode Command message (without Kc128).
* \param[in] ei Mandatory Encryption Information
+ * \param[in] kc128 optional kc128 key for A5/4
* \param[in] cipher_response_mode optional 1-byte Cipher Response Mode
* \returns callee-allocated msgb with BSSMAP Cipher Mode Command message */
struct msgb *gsm0808_create_cipher(const struct gsm0808_encrypt_info *ei,
const uint8_t *cipher_response_mode)
{
+ struct gsm0808_cipher_mode_command cmc = {
+ .ei = *ei,
+ .cipher_response_mode_present = (cipher_response_mode != NULL),
+ .cipher_response_mode = (cipher_response_mode ? *cipher_response_mode : 0),
+ };
+ return gsm0808_create_cipher2(&cmc);
+}
+
+/*! Create BSSMAP Cipher Mode Command message.
+ * \param[in] cmc Information to encode.
+ * \returns callee-allocated msgb with BSSMAP Cipher Mode Command message */
+struct msgb *gsm0808_create_cipher2(const struct gsm0808_cipher_mode_command *cmc)
+{
/* See also: 3GPP TS 48.008 3.2.1.30 CIPHER MODE COMMAND */
struct msgb *msg;
- /* Mandatory emelent! */
- OSMO_ASSERT(ei);
-
- msg =
- msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
- "cipher-mode-command");
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "cipher-mode-command");
if (!msg)
return NULL;
@@ -249,12 +265,16 @@ struct msgb *gsm0808_create_cipher(const struct gsm0808_encrypt_info *ei,
msgb_v_put(msg, BSS_MAP_MSG_CIPHER_MODE_CMD);
/* Encryption Information 3.2.2.10 */
- gsm0808_enc_encrypt_info(msg, ei);
+ gsm0808_enc_encrypt_info(msg, &cmc->ei);
/* Cipher Response Mode 3.2.2.34 */
- if (cipher_response_mode)
+ if (cmc->cipher_response_mode_present)
msgb_tv_put(msg, GSM0808_IE_CIPHER_RESPONSE_MODE,
- *cipher_response_mode);
+ cmc->cipher_response_mode);
+
+ /* Kc128 3.2.2.109 */
+ if (cmc->kc128_present)
+ gsm0808_enc_kc128(msg, cmc->kc128);
/* pre-pend the header */
msg->l3h =
@@ -283,8 +303,9 @@ struct msgb *gsm0808_create_cipher_complete(struct msgb *layer3, uint8_t alg_id)
msgb_l3len(layer3), layer3->l3h);
}
- /* and the optional BSS message */
- msgb_tv_put(msg, GSM0808_IE_CHOSEN_ENCR_ALG, alg_id);
+ /* Optional Chosen Encryption Algorithm IE */
+ if (alg_id > 0)
+ msgb_tv_put(msg, GSM0808_IE_CHOSEN_ENCR_ALG, alg_id);
/* pre-pend the header */
msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
@@ -360,7 +381,7 @@ struct msgb *gsm0808_create_lcls_conn_ctrl(enum gsm0808_lcls_config config,
if (config != GSM0808_LCLS_CFG_NA)
msgb_tv_put(msg, GSM0808_IE_LCLS_CONFIG, config);
if (control != GSM0808_LCLS_CSC_NA)
- msgb_tv_put(msg, GSM0808_IE_LCLS_CONFIG, control);
+ msgb_tv_put(msg, GSM0808_IE_LCLS_CONN_STATUS_CTRL, control);
msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
return msg;
@@ -405,7 +426,7 @@ struct msgb *gsm0808_create_lcls_notification(enum gsm0808_lcls_status status, b
/*! Create BSSMAP Classmark Request message
* \returns callee-allocated msgb with BSSMAP Classmark Request message */
-struct msgb *gsm0808_create_classmark_request()
+struct msgb *gsm0808_create_classmark_request(void)
{
struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
"classmark-request");
@@ -444,8 +465,9 @@ struct msgb *gsm0808_create_classmark_update(const uint8_t *cm2, uint8_t cm2_len
/*! Create BSSMAP SAPI N Reject message
* \param[in] link_id Link Identifier
+ * \param[in] cause BSSAP Cause value (see 3GPP TS 48.008, section 3.2.2.5)
* \returns callee-allocated msgb with BSSMAP SAPI N Reject message */
-struct msgb *gsm0808_create_sapi_reject(uint8_t link_id)
+struct msgb *gsm0808_create_sapi_reject_cause(uint8_t link_id, uint16_t cause)
{
struct msgb *msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM,
"bssmap: sapi 'n' reject");
@@ -454,13 +476,23 @@ struct msgb *gsm0808_create_sapi_reject(uint8_t link_id)
msgb_v_put(msg, BSS_MAP_MSG_SAPI_N_REJECT);
msgb_tv_put(msg, GSM0808_IE_DLCI, link_id);
- gsm0808_enc_cause(msg, GSM0808_CAUSE_BSS_NOT_EQUIPPED);
+ gsm0808_enc_cause(msg, cause);
msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
return msg;
}
+/*! Create BSSMAP SAPI N Reject message (with hard-coded cause "BSS not equipped").
+ * DEPRECATED: use gsm0808_create_sapi_reject_cause() instead.
+ * \param[in] link_id Link Identifier
+ * \param[in] cause BSSAP Cause value (see 3GPP TS 48.008, section 3.2.2.5)
+ * \returns callee-allocated msgb with BSSMAP SAPI N Reject message */
+struct msgb *gsm0808_create_sapi_reject(uint8_t link_id)
+{
+ return gsm0808_create_sapi_reject_cause(link_id, GSM0808_CAUSE_BSS_NOT_EQUIPPED);
+}
+
/*! Create BSSMAP Assignment Request message, 3GPP TS 48.008 §3.2.1.1.
* This is identical to gsm0808_create_ass(), but adds KC and LCLS IEs.
* \param[in] ct Channel Type
@@ -480,8 +512,6 @@ struct msgb *gsm0808_create_ass2(const struct gsm0808_channel_type *ct,
{
/* See also: 3GPP TS 48.008 3.2.1.1 ASSIGNMENT REQUEST */
struct msgb *msg;
- uint16_t cic_sw;
- uint32_t ci_sw;
/* Mandatory emelent! */
OSMO_ASSERT(ct);
@@ -499,11 +529,8 @@ struct msgb *gsm0808_create_ass2(const struct gsm0808_channel_type *ct,
gsm0808_enc_channel_type(msg, ct);
/* Circuit Identity Code 3.2.2.2 */
- if (cic) {
- cic_sw = osmo_htons(*cic);
- msgb_tv_fixed_put(msg, GSM0808_IE_CIRCUIT_IDENTITY_CODE,
- sizeof(cic_sw), (uint8_t *) & cic_sw);
- }
+ if (cic)
+ msgb_tv16_put(msg, GSM0808_IE_CIRCUIT_IDENTITY_CODE, *cic);
/* AoIP: AoIP Transport Layer Address (MGW) 3.2.2.102 */
if (ss) {
@@ -511,23 +538,17 @@ struct msgb *gsm0808_create_ass2(const struct gsm0808_channel_type *ct,
}
/* AoIP: Codec List (MSC Preferred) 3.2.2.103 */
- if (scl)
- gsm0808_enc_speech_codec_list(msg, scl);
+ if (scl) {
+ if (gsm0808_enc_speech_codec_list2(msg, scl) < 0)
+ goto exit_free;
+ }
/* AoIP: Call Identifier 3.2.2.105 */
if (ci) {
/* NOTE: 3GPP TS 48.008, section 3.2.2.105 specifies that
- the least significant byte should be transmitted first.
- On x86, this would mean that the endieness is already
- correct, however a platform independed implementation
- is required: */
-#ifndef OSMO_IS_LITTLE_ENDIAN
- ci_sw = osmo_swab32(*ci);
-#else
- ci_sw = *ci;
-#endif
- msgb_tv_fixed_put(msg, GSM0808_IE_CALL_ID, sizeof(ci_sw),
- (uint8_t *) & ci_sw);
+ * the least significant byte shall be transmitted first. */
+ msgb_v_put(msg, GSM0808_IE_CALL_ID);
+ osmo_store32le(*ci, msgb_put(msg, sizeof(*ci)));
}
if (kc)
@@ -541,6 +562,10 @@ struct msgb *gsm0808_create_ass2(const struct gsm0808_channel_type *ct,
msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
return msg;
+
+exit_free:
+ msgb_free(msg);
+ return NULL;
}
/*! Create BSSMAP Assignment Request message, 3GPP TS 48.008 §3.2.1.1.
@@ -592,7 +617,8 @@ struct msgb *gsm0808_create_ass_compl2(uint8_t rr_cause, uint8_t chosen_channel,
msgb_tv_put(msg, GSM0808_IE_CHOSEN_CHANNEL, chosen_channel);
/* write chosen encryption algorithm 3.2.2.44 */
- msgb_tv_put(msg, GSM0808_IE_CHOSEN_ENCR_ALG, encr_alg_id);
+ if (encr_alg_id > 0)
+ msgb_tv_put(msg, GSM0808_IE_CHOSEN_ENCR_ALG, encr_alg_id);
/* write circuit pool 3.2.2.45 */
/* write speech version chosen: 3.2.2.51 when BTS picked it */
@@ -604,12 +630,16 @@ struct msgb *gsm0808_create_ass_compl2(uint8_t rr_cause, uint8_t chosen_channel,
gsm0808_enc_aoip_trasp_addr(msg, ss);
/* AoIP: Speech Codec (Chosen) 3.2.2.104 */
- if (sc)
- gsm0808_enc_speech_codec(msg, sc);
+ if (sc) {
+ if (gsm0808_enc_speech_codec2(msg, sc) < 0)
+ goto exit_free;
+ }
/* AoIP: add Codec List (BSS Supported) 3.2.2.103 */
- if (scl)
- gsm0808_enc_speech_codec_list(msg, scl);
+ if (scl) {
+ if (gsm0808_enc_speech_codec_list2(msg, scl) < 0)
+ goto exit_free;
+ }
/* FIXME: write LSA identifier 3.2.2.15 - see 3GPP TS 43.073 */
@@ -620,6 +650,10 @@ struct msgb *gsm0808_create_ass_compl2(uint8_t rr_cause, uint8_t chosen_channel,
msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
return msg;
+
+exit_free:
+ msgb_free(msg);
+ return NULL;
}
/*! Create BSSMAP Assignment Completed message
@@ -681,13 +715,19 @@ struct msgb *gsm0808_create_ass_fail(uint8_t cause, const uint8_t *rr_cause,
/* Circuit pool list 3.2.2.46 */
/* AoIP: add Codec List (BSS Supported) 3.2.2.103 */
- if (scl)
- gsm0808_enc_speech_codec_list(msg, scl);
+ if (scl) {
+ if (gsm0808_enc_speech_codec_list2(msg, scl) < 0)
+ goto exit_free;
+ }
/* update the size */
msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
return msg;
+
+exit_free:
+ msgb_free(msg);
+ return NULL;
}
/*! Create BSSMAP Assignment Failure message
@@ -789,7 +829,7 @@ struct msgb *gsm0808_create_paging(const char *imsi, const uint32_t *tmsi,
{
struct gsm0808_cell_id_list2 cil2 = {};
- /* Mandatory emelents! */
+ /* Mandatory elements! */
OSMO_ASSERT(cil);
if (cil->id_list_len > GSM0808_CELL_ID_LIST2_MAXLEN)
@@ -831,6 +871,11 @@ static uint8_t put_old_bss_to_new_bss_information(struct msgb *msg,
msgb_tlv_put(msg, GSM0808_FE_IE_CURRENT_CHANNEL_TYPE_2, 2, val);
}
+ if (i->last_eutran_plmn_id_present) {
+ msgb_put_u8(msg, GSM0808_FE_IE_LAST_USED_EUTRAN_PLMN_ID);
+ osmo_plmn_to_bcd(msgb_put(msg, 3), &i->last_eutran_plmn_id);
+ }
+
*tlv_len = (uint8_t) (msg->tail - old_tail);
return *tlv_len + 2;
}
@@ -953,7 +998,7 @@ struct msgb *gsm0808_create_handover_request(const struct gsm0808_handover_reque
}
/* Chosen Encryption Algorithm (Serving) 3.2.2.44 */
- if (params->chosen_encryption_algorithm_serving)
+ if (params->chosen_encryption_algorithm_serving > 0)
msgb_tv_put(msg, GSM0808_IE_CHOSEN_ENCR_ALG, params->chosen_encryption_algorithm_serving);
/* Old BSS to New BSS Information 3.2.2.58 */
@@ -975,8 +1020,10 @@ struct msgb *gsm0808_create_handover_request(const struct gsm0808_handover_reque
if (params->aoip_transport_layer)
gsm0808_enc_aoip_trasp_addr(msg, params->aoip_transport_layer);
- if (params->codec_list_msc_preferred)
- gsm0808_enc_speech_codec_list(msg, params->codec_list_msc_preferred);
+ if (params->codec_list_msc_preferred) {
+ if (gsm0808_enc_speech_codec_list2(msg, params->codec_list_msc_preferred) < 0)
+ goto exit_free;
+ }
if (params->call_id_present) {
uint8_t val[4];
@@ -984,6 +1031,9 @@ struct msgb *gsm0808_create_handover_request(const struct gsm0808_handover_reque
msgb_tv_fixed_put(msg, GSM0808_IE_CALL_ID, 4, val);
}
+ if (params->more_items && params->kc128_present)
+ gsm0808_enc_kc128(msg, params->kc128);
+
if (params->global_call_reference && params->global_call_reference_len) {
msgb_tlv_put(msg, GSM0808_IE_GLOBAL_CALL_REF,
params->global_call_reference_len, params->global_call_reference);
@@ -993,6 +1043,10 @@ struct msgb *gsm0808_create_handover_request(const struct gsm0808_handover_reque
msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
return msg;
+
+exit_free:
+ msgb_free(msg);
+ return NULL;
}
/*! Create BSSMAP HANDOVER REQUEST ACKNOWLEDGE message, 3GPP TS 48.008 3.2.1.10.
@@ -1016,7 +1070,7 @@ struct msgb *gsm0808_create_handover_request_ack2(const struct gsm0808_handover_
if (params->chosen_channel_present)
msgb_tv_put(msg, GSM0808_IE_CHOSEN_CHANNEL, params->chosen_channel);
- if (params->chosen_encr_alg)
+ if (params->chosen_encr_alg > 0)
msgb_tv_put(msg, GSM0808_IE_CHOSEN_ENCR_ALG, params->chosen_encr_alg);
if (params->chosen_speech_version != 0)
@@ -1025,14 +1079,28 @@ struct msgb *gsm0808_create_handover_request_ack2(const struct gsm0808_handover_
if (params->aoip_transport_layer)
gsm0808_enc_aoip_trasp_addr(msg, params->aoip_transport_layer);
+ /* AoIP: add Codec List (BSS Supported) 3.2.2.103.
+ * (codec_list_bss_supported was added to struct gsm0808_handover_request_ack later than speech_codec_chosen
+ * below, but it needs to come before it in the message coding). */
+ if (params->more_items && params->codec_list_bss_supported.len) {
+ if (gsm0808_enc_speech_codec_list2(msg, &params->codec_list_bss_supported) < 0)
+ goto exit_free;
+ }
+
/* AoIP: Speech Codec (Chosen) 3.2.2.104 */
- if (params->speech_codec_chosen_present)
- gsm0808_enc_speech_codec(msg, &params->speech_codec_chosen);
+ if (params->speech_codec_chosen_present) {
+ if (gsm0808_enc_speech_codec2(msg, &params->speech_codec_chosen) < 0)
+ goto exit_free;
+ }
/* prepend header with final length */
msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
return msg;
+
+exit_free:
+ msgb_free(msg);
+ return NULL;
}
/*! Same as gsm0808_create_handover_request_ack2() but with less parameters.
@@ -1084,7 +1152,7 @@ struct msgb *gsm0808_create_handover_command(const struct gsm0808_handover_comma
/*! Create BSSMAP HANDOVER DETECT message, 3GPP TS 48.008 3.2.1.40.
* Sent from the MT BSC back to the MSC when the MS has sent a handover RACH request and the MT BSC has
* received the Handover Detect message. */
-struct msgb *gsm0808_create_handover_detect()
+struct msgb *gsm0808_create_handover_detect(void)
{
struct msgb *msg;
@@ -1103,7 +1171,7 @@ struct msgb *gsm0808_create_handover_detect()
/*! Create BSSMAP HANDOVER SUCCEEDED message, 3GPP TS 48.008 3.2.1.13.
* Sent from the MSC back to the old BSS to notify that the MS has successfully accessed the new BSS. */
-struct msgb *gsm0808_create_handover_succeeded()
+struct msgb *gsm0808_create_handover_succeeded(void)
{
struct msgb *msg;
@@ -1138,15 +1206,19 @@ struct msgb *gsm0808_create_handover_complete(const struct gsm0808_handover_comp
msgb_tlv_put(msg, GSM0808_IE_RR_CAUSE, 1, &params->rr_cause);
/* AoIP: Speech Codec (Chosen) 3.2.2.104 */
- if (params->speech_codec_chosen_present)
- gsm0808_enc_speech_codec(msg, &params->speech_codec_chosen);
+ if (params->speech_codec_chosen_present) {
+ if (gsm0808_enc_speech_codec2(msg, &params->speech_codec_chosen) < 0)
+ goto exit_free;
+ }
/* AoIP: add Codec List (BSS Supported) 3.2.2.103 */
- if (params->codec_list_bss_supported.len)
- gsm0808_enc_speech_codec_list(msg, &params->codec_list_bss_supported);
+ if (params->codec_list_bss_supported.len) {
+ if (gsm0808_enc_speech_codec_list2(msg, &params->codec_list_bss_supported) < 0)
+ goto exit_free;
+ }
/* Chosen Encryption Algorithm 3.2.2.44 */
- if (params->chosen_encr_alg_present)
+ if (params->chosen_encr_alg_present && params->chosen_encr_alg > 0)
msgb_tv_put(msg, GSM0808_IE_CHOSEN_ENCR_ALG, params->chosen_encr_alg);
/* LCLS-BSS-Status 3.2.2.119 */
@@ -1157,6 +1229,10 @@ struct msgb *gsm0808_create_handover_complete(const struct gsm0808_handover_comp
msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
return msg;
+
+exit_free:
+ msgb_free(msg);
+ return NULL;
}
/*! Create BSSMAP HANDOVER FAILURE message, 3GPP TS 48.008 3.2.1.16.
@@ -1180,13 +1256,19 @@ struct msgb *gsm0808_create_handover_failure(const struct gsm0808_handover_failu
msgb_tlv_put(msg, GSM0808_IE_RR_CAUSE, 1, &params->rr_cause);
/* AoIP: add Codec List (BSS Supported) 3.2.2.103 */
- if (params->codec_list_bss_supported.len)
- gsm0808_enc_speech_codec_list(msg, &params->codec_list_bss_supported);
+ if (params->codec_list_bss_supported.len) {
+ if (gsm0808_enc_speech_codec_list2(msg, &params->codec_list_bss_supported) < 0)
+ goto exit_free;
+ }
/* prepend header with final length */
msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
return msg;
+
+exit_free:
+ msgb_free(msg);
+ return NULL;
}
/*! Create BSSMAP HANDOVER PERFORMED message, 3GPP TS 48.008 3.2.1.25.
@@ -1214,7 +1296,7 @@ struct msgb *gsm0808_create_handover_performed(const struct gsm0808_handover_per
msgb_tv_put(msg, GSM0808_IE_CHOSEN_CHANNEL, params->chosen_channel);
/* Chosen Encryption Algorithm 3.2.2.44 */
- if (params->chosen_encr_alg_present)
+ if (params->chosen_encr_alg_present && params->chosen_encr_alg > 0)
msgb_tv_put(msg, GSM0808_IE_CHOSEN_ENCR_ALG, params->chosen_encr_alg);
/* Speech Version (chosen) 3.2.2.51 */
@@ -1222,8 +1304,10 @@ struct msgb *gsm0808_create_handover_performed(const struct gsm0808_handover_per
msgb_tv_put(msg, GSM0808_IE_SPEECH_VERSION, params->speech_version_chosen);
/* AoIP: Speech Codec (chosen) 3.2.2.104 */
- if (params->speech_codec_chosen_present)
- gsm0808_enc_speech_codec(msg, &params->speech_codec_chosen);
+ if (params->speech_codec_chosen_present) {
+ if (gsm0808_enc_speech_codec2(msg, &params->speech_codec_chosen) < 0)
+ goto exit_free;
+ }
/* LCLS-BSS-Status 3.2.2.119 */
if (params->lcls_bss_status_present)
@@ -1233,16 +1317,25 @@ struct msgb *gsm0808_create_handover_performed(const struct gsm0808_handover_per
msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
return msg;
+
+exit_free:
+ msgb_free(msg);
+ return NULL;
}
+/*! Create BSSMAP COMMON ID message, 3GPP TS 48.008 3.2.1.68.
+ * \param[in] imsi IMSI digits (decimal string).
+ * \param[in] selected_plmn_id Selected PLMN ID to encode, or NULL to not encode this IE.
+ * \param[in] last_used_eutran_plnm_id Last used E-UTRAN PLMN ID to encode, or NULL to not encode this IE.
+ * \returns callee-allocated msgb with BSSMAP COMMON ID message, or NULL if encoding failed. */
struct msgb *gsm0808_create_common_id(const char *imsi,
const struct osmo_plmn_id *selected_plmn_id,
const struct osmo_plmn_id *last_used_eutran_plnm_id)
{
struct msgb *msg;
- uint8_t mid_buf[GSM48_MI_SIZE + 2];
uint8_t *out;
- int mid_len;
+ struct osmo_mobile_identity mi;
+ int rc;
msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "COMMON-ID");
if (!msg)
@@ -1252,8 +1345,16 @@ struct msgb *gsm0808_create_common_id(const char *imsi,
msgb_v_put(msg, BSS_MAP_MSG_COMMON_ID);
/* mandatory IMSI 3.2.2.6 */
- mid_len = gsm48_generate_mid_from_imsi(mid_buf, imsi);
- msgb_tlv_put(msg, GSM0808_IE_IMSI, mid_len - 2, mid_buf + 2);
+ mi = (struct osmo_mobile_identity){ .type = GSM_MI_TYPE_IMSI };
+ OSMO_STRLCPY_ARRAY(mi.imsi, imsi);
+ out = msgb_tl_put(msg, GSM0808_IE_IMSI);
+ rc = osmo_mobile_identity_encode_msgb(msg, &mi, false);
+ if (rc < 0) {
+ msgb_free(msg);
+ return NULL;
+ }
+ /* write the MI value length */
+ *out = rc;
/* not implemented: SNA Access Information */
@@ -1315,6 +1416,763 @@ struct msgb *gsm0808_create_dtap(struct msgb *msg_l3, uint8_t link_id)
return msg;
}
+struct msgb *gsm0808_create_perform_location_request(const struct gsm0808_perform_location_request *params)
+{
+ struct msgb *msg;
+ uint8_t *out;
+ int rc;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-PERFORM-LOCATION-REQUEST");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_PERFORM_LOCATION_RQST);
+
+ /* Location Type 3.2.2.63 */
+ osmo_bssmap_le_ie_enc_location_type(msg, &params->location_type);
+
+ if (params->imsi.type == GSM_MI_TYPE_IMSI) {
+ /* IMSI 3.2.2.6 */
+ out = msgb_tl_put(msg, GSM0808_IE_IMSI);
+ rc = osmo_mobile_identity_encode_msgb(msg, &params->imsi, false);
+ if (rc < 0) {
+ msgb_free(msg);
+ return NULL;
+ }
+ /* write the MI value length */
+ *out = rc;
+ }
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+struct msgb *gsm0808_create_perform_location_response(const struct gsm0808_perform_location_response *params)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-PERFORM-LOCATION-RESPONSE");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_PERFORM_LOCATION_RESPONSE);
+
+ if (params->location_estimate_present) {
+ uint8_t *l = msgb_tl_put(msg, GSM0808_IE_LOCATION_ESTIMATE);
+ int rc = osmo_gad_raw_write(msg, &params->location_estimate);
+ if (rc < 0) {
+ msgb_free(msg);
+ return NULL;
+ }
+ *l = rc;
+ }
+
+ if (params->lcs_cause.present) {
+ uint8_t *l = msgb_tl_put(msg, GSM0808_IE_LCS_CAUSE);
+ int rc = osmo_lcs_cause_enc(msg, &params->lcs_cause);
+ if (rc < 0) {
+ msgb_free(msg);
+ return NULL;
+ }
+ *l = rc;
+ }
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+int gsm0808_enc_lcs_cause(struct msgb *msg, const struct lcs_cause_ie *lcs_cause)
+{
+ uint8_t *l = msgb_tl_put(msg, GSM0808_IE_LCS_CAUSE);
+ int rc = osmo_lcs_cause_enc(msg, lcs_cause);
+ if (rc <= 0)
+ return rc;
+ *l = rc;
+ return rc + 2;
+}
+
+struct msgb *gsm0808_create_perform_location_abort(const struct lcs_cause_ie *lcs_cause)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-PERFORM-LOCATION-ABORT");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_PERFORM_LOCATION_ABORT);
+
+ gsm0808_enc_lcs_cause(msg, lcs_cause);
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/*! Create BSSMAP VGCS/VBS SETUP message, 3GPP TS 48.008 3.2.1.50.
+ * Sent from the MSC to the BSC to request VGCS/VBS call. */
+struct msgb *gsm0808_create_vgcs_vbs_setup(const struct gsm0808_vgcs_vbs_setup *params)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-VGCS/VBS-SETUP");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_VGCS_VBS_SETUP);
+
+ /* Group Call Reference, 3.2.2.55 */
+ gsm0808_enc_group_callref(msg, &params->callref);
+
+ /* Priority, 3.2.2.18 */
+ if (params->priority_present)
+ gsm0808_enc_priority(msg, &params->priority);
+
+ /* VGCS Feature Flags, 3.2.2.88 */
+ if (params->vgcs_feature_flags_present)
+ gsm0808_enc_vgcs_feature_flags(msg, &params->flags);
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/*! Create BSSMAP VGCS/VBS SETUP ACK message, 3GPP TS 48.008 3.2.1.51.
+ * Sent from the BSC to the MSC to confirm VGCS/VBS call. */
+struct msgb *gsm0808_create_vgcs_vbs_setup_ack(const struct gsm0808_vgcs_vbs_setup_ack *params)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-VGCS/VBS-SETUP-ACK");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_VGCS_VBS_SETUP_ACK);
+
+ /* VGCS Feature Flags, 3.2.2.88 */
+ if (params->vgcs_feature_flags_present)
+ gsm0808_enc_vgcs_feature_flags(msg, &params->flags);
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/*! Create BSSMAP VGCS/VBS SETUP REFUSE message, 3GPP TS 48.008 3.2.1.52.
+ * Sent from the BSC to the MSC to reject VGCS/VBS call. */
+struct msgb *gsm0808_create_vgcs_vbs_setup_refuse(enum gsm0808_cause cause)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-VGCS/VBS-SETUP-REFUSE");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_VGCS_VBS_SETUP_REFUSE);
+
+ /* Cause, 3.2.2.5 */
+ gsm0808_enc_cause(msg, cause);
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/*! Create BSSMAP VGCS/VBS ASSIGNMENT REQUEST message, 3GPP TS 48.008 3.2.1.53.
+ * Sent from the MSC to the BSC to assign radio resources for a VGCS/VBS. */
+struct msgb *gsm0808_create_vgcs_vbs_assign_req(const struct gsm0808_vgcs_vbs_assign_req *params)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-VGCS/VBS-ASSIGNMENT-REQUEST");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_RQST);
+
+ /* Channel Type, 3.2.2.11 */
+ gsm0808_enc_channel_type(msg, &params->channel_type);
+
+ /* Assignment Requrirement, 3.2.2.52 */
+ gsm0808_enc_assign_req(msg, params->ass_req);
+
+ /* Cell Identifier, 3.2.2.17 */
+ gsm0808_enc_cell_id(msg, &params->cell_identifier);
+
+ /* Group Call Reference, 3.2.2.55 */
+ gsm0808_enc_group_callref(msg, &params->callref);
+
+ /* Priority, 3.2.2.18 */
+ if (params->priority_present)
+ gsm0808_enc_priority(msg, &params->priority);
+
+ /* Circuit Identity Code, 3.2.2.2 */
+ if (params->cic_present)
+ msgb_tv16_put(msg, GSM0808_IE_CIRCUIT_IDENTITY_CODE, params->cic);
+
+ /* Downlink DTX Flag, 3.2.2.26 */
+ if (params->downlink_dtx_flag_present)
+ msgb_tv_put(msg, GSM0808_IE_DOWNLINK_DTX_FLAG, params->downlink_dtx_flag);
+
+ /* Encryption Information, 3.2.2.10 */
+ if (params->encryption_information_present)
+ gsm0808_enc_encrypt_info(msg, &params->encryption_information);
+
+ /* VSTK_RAND Imformation, 3.2.2.83 */
+ if (params->vstk_rand_present)
+ msgb_tlv_put(msg, GSM0808_IE_VSTK_RAND_INFO, sizeof(params->vstk_rand), params->vstk_rand);
+
+ /* VSTK Information, 3.2.2.84 */
+ if (params->vstk_present)
+ msgb_tlv_put(msg, GSM0808_IE_VSTK_INFO, sizeof(params->vstk), params->vstk);
+
+ /* Cell Identifier List Segment, 3.2.2.27a */
+ if (params->cils_present)
+ gsm0808_enc_cell_id_list_segment(msg, GSM0808_IE_CELL_ID_LIST_SEGMENT, &params->cils);
+
+ /* AoIP Transport Layer Address (MGW), 3.2.2.102 */
+ if (params->aoip_transport_layer_present)
+ gsm0808_enc_aoip_trasp_addr(msg, &params->aoip_transport_layer);
+
+ /* Call Identifier, 3.2.2.105 */
+ if (params->call_id_present) {
+ /* NOTE: 3GPP TS 48.008, section 3.2.2.105 specifies that
+ * the least significant byte shall be transmitted first. */
+ msgb_v_put(msg, GSM0808_IE_CALL_ID);
+ osmo_store32le(params->call_id, msgb_put(msg, sizeof(uint32_t)));
+ }
+
+ /* Codec List (MSC Preferred) 3.2.2.103 */
+ if (params->codec_list_present) {
+ if (gsm0808_enc_speech_codec_list2(msg, &params->codec_list_msc_preferred) < 0)
+ goto exit_free;
+ }
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+
+exit_free:
+ msgb_free(msg);
+ return NULL;
+}
+
+/*! Create BSSMAP VGCS/VBS ASSIGNMENT RESULT message, 3GPP TS 48.008 3.2.1.54.
+ * Sent from the BSC to the MSC to indicate assignment/deassingment of radio resources for a VGCS/VBS. */
+struct msgb *gsm0808_create_vgcs_vbs_assign_res(const struct gsm0808_vgcs_vbs_assign_res *params)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-VGCS/VBS-ASSIGNMENT-RESULT");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_RESULT);
+
+ /* Channel Type, 3.2.2.11 */
+ gsm0808_enc_channel_type(msg, &params->channel_type);
+
+ /* Cell Identifier, 3.2.2.17 */
+ gsm0808_enc_cell_id(msg, &params->cell_identifier);
+
+ /* Chosen Channel, 3.2.2.33 */
+ if (params->chosen_channel_present)
+ msgb_tv_put(msg, GSM0808_IE_CHOSEN_CHANNEL, params->chosen_channel);
+
+ /* Circuit Identity Code, 3.2.2.2 */
+ if (params->cic_present)
+ msgb_tv16_put(msg, GSM0808_IE_CIRCUIT_IDENTITY_CODE, params->cic);
+
+ /* Circuit Pool, 3.2.2.45 */
+ if (params->circuit_pool_present)
+ msgb_tv_put(msg, GSM0808_IE_CIRCUIT_POOL, params->circuit_pool);
+
+ /* AoIP Transport Layer Address (BSS), 3.2.2.102 */
+ if (params->aoip_transport_layer_present)
+ gsm0808_enc_aoip_trasp_addr(msg, &params->aoip_transport_layer);
+
+ /* Codec (MSC Chosen) 3.2.2.103 */
+ if (params->codec_present) {
+ if (gsm0808_enc_speech_codec2(msg, &params->codec_msc_chosen) < 0)
+ goto exit_free;
+ }
+
+ /* Call Identifier, 3.2.2.105 */
+ if (params->call_id_present) {
+ /* NOTE: 3GPP TS 48.008, section 3.2.2.105 specifies that
+ * the least significant byte shall be transmitted first. */
+ msgb_v_put(msg, GSM0808_IE_CALL_ID);
+ osmo_store32le(params->call_id, msgb_put(msg, sizeof(uint32_t)));
+ }
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+
+exit_free:
+ msgb_free(msg);
+ return NULL;
+}
+
+/*! Create BSSMAP VGCS/VBS ASSIGNMENT FAILURE message, 3GPP TS 48.008 3.2.1.55.
+ * Sent from the BSC to the MSC to indicate assignment failure for a VGCS/VBS. */
+struct msgb *gsm0808_create_vgcs_vbs_assign_fail(const struct gsm0808_vgcs_vbs_assign_fail *params)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-VGCS/VBS-ASSIGNMENT-RESULT");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_FAILURE);
+
+ /* Cause, 3.2.2.5 */
+ gsm0808_enc_cause(msg, params->cause);
+
+ /* Circuit Pool, 3.2.2.45 */
+ if (params->circuit_pool_present)
+ msgb_tv_put(msg, GSM0808_IE_CIRCUIT_POOL, params->circuit_pool);
+
+ /* Circuit Pool List, 3.2.2.46 */
+ if (params->circuit_pool_present)
+ msgb_tlv_put(msg, GSM0808_IE_CIRCUIT_POOL_LIST, params->cpl.list_len, params->cpl.pool);
+
+ /* Codec List (BSS Supported) 3.2.2.103 */
+ if (params->codec_list_present) {
+ if (gsm0808_enc_speech_codec_list2(msg, &params->codec_list_bss_supported) < 0)
+ goto exit_free;
+ }
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+
+exit_free:
+ msgb_free(msg);
+ return NULL;
+}
+
+/*! Create BSSMAP VGCS/VBS QUEUING INDICATION message, 3GPP TS 48.008 3.2.1.56.
+ * Sent from the BSC to the MSC to indicate delay in assignment for a VGCS/VBS. */
+struct msgb *gsm0808_create_vgcs_queuing_ind(void)
+{
+ struct msgb *msg;
+ uint8_t val = BSS_MAP_MSG_VGCS_VBS_QUEUING_INDICATION;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-VGCS/VBS-QUEUING-INDICATION");
+ if (!msg)
+ return NULL;
+
+ msg->l3h = msg->data;
+ msgb_tlv_put(msg, BSSAP_MSG_BSS_MANAGEMENT, 1, &val);
+
+ return msg;
+}
+
+/*! Create BSSMAP (VGCS) UPLINK REQUEST message, 3GPP TS 48.008 3.2.1.57.
+ * Sent from the BSC to the MSC to indicate that a mobile requested access to uplink. */
+struct msgb *gsm0808_create_uplink_request(const struct gsm0808_uplink_request *params)
+{
+ struct msgb *msg;
+ int rc;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-UPLINK-REQUEST");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_UPLINK_RQST);
+
+ /* Talker Priority, 3.2.2.89 */
+ if (params->talker_priority_present)
+ msgb_tv_put(msg, GSM0808_IE_TALKER_PRIORITY, params->talker_priority);
+
+ /* Cell Identifier, 3.2.2.17 */
+ if (params->cell_identifier_present)
+ gsm0808_enc_cell_id(msg, &params->cell_identifier);
+
+ /* Layer 3 Information, 3.2.2.24 */
+ if (params->l3_present)
+ msgb_tlv_put(msg, GSM0808_IE_LAYER_3_INFORMATION, params->l3.l3_len, params->l3.l3);
+
+ /* Mobile Identity, 3.2.2.41 */
+ if (params->mi_present) {
+ rc = osmo_mobile_identity_encode_msgb(msg, &params->mi, false);
+ if (rc < 0) {
+ msgb_free(msg);
+ return NULL;
+ }
+ }
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/*! Create BSSMAP (VGCS) UPLINK REQUEST ACKNOWLEDGE message, 3GPP TS 48.008 3.2.1.58.
+ * Sent from the MSC to the BSC to indicate that access to uplink was granted. */
+struct msgb *gsm0808_create_uplink_request_ack(const struct gsm0808_uplink_request_ack *params)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-UPLINK-REQUEST-ACKNOWLEDGE");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_UPLINK_RQST_ACKNOWLEDGE);
+
+ /* Talker Priority, 3.2.2.89 */
+ if (params->talker_priority_present)
+ msgb_tv_put(msg, GSM0808_IE_TALKER_PRIORITY, params->talker_priority);
+
+ /* Emergency set indication, 3.2.2.90 */
+ if (params->emerg_set_ind_present)
+ msgb_v_put(msg, GSM0808_IE_EMERGENCY_SET_INDICATION);
+
+ /* Talker Identity, 3.2.2.91 */
+ if (params->talker_identity_present)
+ gsm0808_enc_talker_identity(msg, &params->talker_identity);
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/*! Create BSSMAP (VGCS) UPLINK CONFIRM message, 3GPP TS 48.008 3.2.1.59.
+ * Sent from the BSC to the MSC to indicate that access to uplink was has been successfully established. */
+struct msgb *gsm0808_create_uplink_request_cnf(const struct gsm0808_uplink_request_cnf *params)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-UPLINK-REQUEST-CONFIRM");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_UPLINK_RQST_CONFIRMATION);
+
+ /* Cell Identifier, 3.2.2.17 */
+ gsm0808_enc_cell_id(msg, &params->cell_identifier);
+
+ /* Talker Identity, 3.2.2.91 */
+ if (params->talker_identity_present)
+ gsm0808_enc_talker_identity(msg, &params->talker_identity);
+
+ /* Layer 3 Information, 3.2.2.24 */
+ msgb_tlv_put(msg, GSM0808_IE_LAYER_3_INFORMATION, params->l3.l3_len, params->l3.l3);
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/*! Create BSSMAP (VGCS) UPLINK APPLICATION DATA message, 3GPP TS 48.008 3.2.1.59a.
+ * Sent from the BSC to the MSC to pass L3 info from the talker. */
+struct msgb *gsm0808_create_uplink_app_data(const struct gsm0808_uplink_app_data *params)
+{
+ struct msgb *msg;
+ uint8_t val;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-UPLINK-APPLICATION-DATA");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_UPLINK_APP_DATA);
+
+ /* Cell Identifier, 3.2.2.17 */
+ gsm0808_enc_cell_id(msg, &params->cell_identifier);
+
+ /* Layer 3 Information, 3.2.2.24 */
+ msgb_tlv_put(msg, GSM0808_IE_LAYER_3_INFORMATION, params->l3.l3_len, params->l3.l3);
+
+ /* Application Data Information, 3.2.2.100 */
+ val = params->bt_ind;
+ msgb_tlv_put(msg, GSM0808_IE_APP_DATA_INFO, 1, &val);
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/*! Create BSSMAP (VGCS) UPLINK RELEASE INDICATION message, 3GPP TS 48.008 3.2.1.60.
+ * Sent from the BSC to the MSC to indicate that the uplink has been released. */
+struct msgb *gsm0808_create_uplink_release_ind(const struct gsm0808_uplink_release_ind *params)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-UPLINK-RELEASE-INDICATION");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_UPLINK_RELEASE_INDICATION);
+
+ /* Cause, 3.2.2.5 */
+ gsm0808_enc_cause(msg, params->cause);
+
+ /* Talker Priority, 3.2.2.89 */
+ if (params->talker_priority_present)
+ msgb_tv_put(msg, GSM0808_IE_TALKER_PRIORITY, params->talker_priority);
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/*! Create BSSMAP (VGCS) UPLINK REJECT COMMAND message, 3GPP TS 48.008 3.2.1.61.
+ * Sent from the MSC to the BSC to indicate that the uplink is not available for allocation. */
+struct msgb *gsm0808_create_uplink_reject_cmd(const struct gsm0808_uplink_reject_cmd *params)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-UPLINK-REJECT-COMMAND");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_UPLINK_REJECT_CMD);
+
+ /* Cause, 3.2.2.5 */
+ gsm0808_enc_cause(msg, params->cause);
+
+ /* Talker Priority, 3.2.2.89 */
+ if (params->current_talker_priority_present)
+ msgb_tv_put(msg, GSM0808_IE_TALKER_PRIORITY, params->current_talker_priority);
+
+ /* Talker Priority, 3.2.2.89 */
+ if (params->rejected_talker_priority_present)
+ msgb_tv_put(msg, GSM0808_IE_TALKER_PRIORITY, params->rejected_talker_priority);
+
+ /* Talker Identity, 3.2.2.91 */
+ if (params->talker_identity_present)
+ gsm0808_enc_talker_identity(msg, &params->talker_identity);
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/*! Create BSSMAP (VGCS) UPLINK RELEASE COMMAND message, 3GPP TS 48.008 3.2.1.62.
+ * Sent from the MSC to the BSC to indicate that the uplink is available for allocation. */
+struct msgb *gsm0808_create_uplink_release_cmd(const enum gsm0808_cause cause)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-UPLINK-RELEASE-COMMAND");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_UPLINK_RELEASE_CMD);
+
+ /* Cause, 3.2.2.5 */
+ gsm0808_enc_cause(msg, cause);
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/*! Create BSSMAP (VGCS) UPLINK SEIZED COMMAND message, 3GPP TS 48.008 3.2.1.62.
+ * Sent from the MSC to the BSC to indicate that the uplink is no longer available for allocation. */
+struct msgb *gsm0808_create_uplink_seized_cmd(const struct gsm0808_uplink_seized_cmd *params)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-UPLINK-SEIZED-COMMAND");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_UPLINK_SEIZED_CMD);
+
+ /* Cause, 3.2.2.5 */
+ gsm0808_enc_cause(msg, params->cause);
+
+ /* Talker Priority, 3.2.2.89 */
+ if (params->talker_priority_present)
+ msgb_tv_put(msg, GSM0808_IE_TALKER_PRIORITY, params->talker_priority);
+
+ /* Emergency set indication, 3.2.2.90 */
+ if (params->emerg_set_ind_present)
+ msgb_v_put(msg, GSM0808_IE_EMERGENCY_SET_INDICATION);
+
+ /* Talker Identity, 3.2.2.91 */
+ if (params->talker_identity_present)
+ gsm0808_enc_talker_identity(msg, &params->talker_identity);
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/*! Create BSSMAP VGCS ADDITIONAL INFORMATION message, 3GPP TS 48.008 3.2.1.78.
+ * Sent from the MSC to the BSC to transfer talker identity. */
+struct msgb *gsm0808_create_vgcs_additional_info(const struct gsm0808_talker_identity *ti)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-VGCS-ADDITIONAL-INFO");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_VGCS_ADDL_INFO);
+
+ /* Talker Identity, 3.2.2.91 */
+ gsm0808_enc_talker_identity(msg, ti);
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/*! Create BSSMAP VGCS/VBS AREA CELL INFO message, 3GPP TS 48.008 3.2.1.79.
+ * Sent from the BSC to the MSC to transfer additional infos about cells. */
+struct msgb *gsm0808_create_vgcs_vbs_area_cell_info(const struct gsm0808_vgcs_vbs_area_cell_info *params)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-VGCS/VBS-AREA-CELL-INFO");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_RQST);
+
+ /* Cell Identifier List Segment, 3.2.2.27a */
+ gsm0808_enc_cell_id_list_segment(msg, GSM0808_IE_CELL_ID_LIST_SEGMENT, &params->cils);
+
+ /* Assignment Requrirement, 3.2.2.52 */
+ if (params->ass_req_present)
+ gsm0808_enc_assign_req(msg, params->ass_req);
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/*! Create BSSMAP VGCS/VBS ASSIGNMENT STATUS message, 3GPP TS 48.008 3.2.1.80.
+ * Sent from the BSC to the MSC to indicate assignment status for each cell. */
+struct msgb *gsm0808_create_vgcs_vbs_assign_stat(const struct gsm0808_vgcs_vbs_assign_stat *params)
+{
+ struct msgb *msg;
+ uint8_t val;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-VGCS/VBS-ASSIGNMENT-STATUS");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_STATUS);
+
+ /* Cell Identifier List Segment, 3.2.2.27b */
+ if (params->cils_est_present)
+ gsm0808_enc_cell_id_list_segment(msg, GSM0808_IE_CELL_ID_LIST_SEG_EST_CELLS, &params->cils_est);
+ /* Cell Identifier List Segment, 3.2.2.27c */
+ if (params->cils_tbe_present)
+ gsm0808_enc_cell_id_list_segment(msg, GSM0808_IE_CELL_ID_LIST_SEG_CELLS_TBE, &params->cils_tbe);
+ /* Cell Identifier List Segment, 3.2.2.27e */
+ if (params->cils_rel_present)
+ gsm0808_enc_cell_id_list_segment(msg, GSM0808_IE_CELL_ID_LIST_SEG_REL_CELLS, &params->cils_rel);
+ /* Cell Identifier List Segment, 3.2.2.27f */
+ if (params->cils_ne_present)
+ gsm0808_enc_cell_id_list_segment(msg, GSM0808_IE_CELL_ID_LIST_SEG_NE_CELLS, &params->cils_ne);
+
+ /* VGCS/VBS Cell Status, 3.2.2.94 */
+ if (params->cell_status_present) {
+ val = params->cell_status;
+ msgb_tlv_put(msg, GSM0808_IE_VGCS_VBS_CELL_STATUS, 1, &val);
+ }
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/*! Create BSSMAP VGCS SMS message, 3GPP TS 48.008 3.2.1.81.
+ * Sent from the MSC to the BSC to send an SMS to VGCS. */
+struct msgb *gsm0808_create_vgcs_sms(const struct gsm0808_sms_to_vgcs *sms)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-VGCS-SMS");
+ if (!msg)
+ return NULL;
+
+ /* SMS to VGCS, 3.2.2.92 */
+ msgb_tlv_put(msg, GSM0808_IE_VGCS_VBS_CELL_STATUS, sms->sms_len, sms->sms);
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/*! Create BSSMAP (VGCS/VBS) NOTIFICATION DATA message, 3GPP TS 48.008 3.2.1.82.
+ * Sent from the MSC to the BSC to send application specific data. */
+struct msgb *gsm0808_create_notification_data(const struct gsm0808_notification_data *params)
+{
+ struct msgb *msg;
+
+ msg = msgb_alloc_headroom(BSSMAP_MSG_SIZE, BSSMAP_MSG_HEADROOM, "BSSMAP-VGCS-SMS");
+ if (!msg)
+ return NULL;
+
+ /* Message Type, 3.2.2.1 */
+ msgb_v_put(msg, BSS_MAP_MSG_NOTIFICATION_DATA);
+
+ /* Application Data, 3.2.2.98 */
+ msgb_tlv_put(msg, GSM0808_IE_APP_DATA, params->app_data.data_len, params->app_data.data);
+
+ /* Data Identity, 3.2.2.99 */
+ gsm0808_enc_data_identity(msg, &params->data_ident);
+
+ /* MSISDN, 3.2.2.101 */
+ if (params->msisdn_present)
+ gsm0808_enc_msisdn(msg, params->msisdn);
+
+ /* prepend header with final length */
+ msg->l3h = msgb_tv_push(msg, BSSAP_MSG_BSS_MANAGEMENT, msgb_length(msg));
+
+ return msg;
+}
+
+/* Note that EMERGENCY RESET INDICATION and EMERGENCY RESET COMMAND cannot be implemented, due to lack of
+ * message type information in the specifications. */
+
/* As per 3GPP TS 48.008 version 11.7.0 Release 11 */
static const struct tlv_definition bss_att_tlvdef = {
.def = {
@@ -1386,6 +2244,7 @@ static const struct tlv_definition bss_att_tlvdef = {
[GSM0808_IE_LOCATION_ESTIMATE] = { TLV_TYPE_TLV },
[GSM0808_IE_POSITIONING_DATA] = { TLV_TYPE_TLV },
[GSM0808_IE_LCS_CAUSE] = { TLV_TYPE_TLV },
+ [GSM0808_IE_LCS_CLIENT_TYPE] = { TLV_TYPE_TLV },
[GSM0808_IE_APDU] = { TLV_TYPE_TLV },
[GSM0808_IE_NETWORK_ELEMENT_IDENTITY] = { TLV_TYPE_TLV },
[GSM0808_IE_GPS_ASSISTANCE_DATA] = { TLV_TYPE_TLV },
@@ -1402,6 +2261,7 @@ static const struct tlv_definition bss_att_tlvdef = {
[GSM0800_IE_INTER_SYSTEM_INFO] = { TLV_TYPE_TLV },
[GSM0808_IE_SNA_ACCESS_INFO] = { TLV_TYPE_TLV },
[GSM0808_IE_VSTK_RAND_INFO] = { TLV_TYPE_TLV },
+ [GSM0808_IE_VSTK_INFO] = { TLV_TYPE_TLV },
[GSM0808_IE_PAGING_INFO] = { TLV_TYPE_TV },
[GSM0808_IE_IMEI] = { TLV_TYPE_TLV },
[GSM0808_IE_VELOCITY_ESTIMATE] = { TLV_TYPE_TLV },
@@ -1444,6 +2304,11 @@ static const struct tlv_definition bss_att_tlvdef = {
[GSM0808_IE_CN_TO_MS_TRANSP_INFO] = { TLV_TYPE_TLV },
[GSM0808_IE_SELECTED_PLMN_ID] = { TLV_TYPE_FIXED, 3 },
[GSM0808_IE_LAST_USED_EUTRAN_PLMN_ID] = { TLV_TYPE_FIXED, 3 },
+ [GSM0808_IE_OLD_LAI] = { TLV_TYPE_FIXED, 5 },
+ [GSM0808_IE_ATTACH_INDICATOR] = { TLV_TYPE_T },
+ [GSM0808_IE_SELECTED_OPERATOR] = { TLV_TYPE_FIXED, 3 },
+ [GSM0808_IE_PS_REGISTERED_OPERATOR] = { TLV_TYPE_FIXED, 3 },
+ [GSM0808_IE_CS_REGISTERED_OPERATOR] = { TLV_TYPE_FIXED, 3 },
/* Osmocom extensions */
[GSM0808_IE_OSMO_OSMUX_SUPPORT] = { TLV_TYPE_T },
@@ -1456,6 +2321,32 @@ const struct tlv_definition *gsm0808_att_tlvdef(void)
return &bss_att_tlvdef;
}
+/* As per 3GPP TS 48.008 version 16.0.0 Release 16 § 3.2.2.58 Old BSS to New BSS Information */
+const struct tlv_definition gsm0808_old_bss_to_new_bss_info_att_tlvdef = {
+ .def = {
+ [GSM0808_FE_IE_EXTRA_INFORMATION] = { TLV_TYPE_TLV },
+ [GSM0808_FE_IE_CURRENT_CHANNEL_TYPE_2] = { TLV_TYPE_TLV },
+ [GSM0808_FE_IE_TARGET_CELL_RADIO_INFORMATION] = { TLV_TYPE_TLV },
+ [GSM0808_FE_IE_GPRS_SUSPEND_INFORMATION] = { TLV_TYPE_TLV },
+ [GSM0808_FE_IE_MULTIRATE_CONFIGURATION_INFORMATION] = { TLV_TYPE_TLV },
+ [GSM0808_FE_IE_DUAL_TRANSFER_MODE_INFORMATION] = { TLV_TYPE_TLV },
+ [GSM0808_FE_IE_INTER_RAT_HANDOVER_INFO] = { TLV_TYPE_TLV },
+ [GSM0808_FE_IE_CDMA2000_CAPABILITY_INFORMATION] = { TLV_TYPE_TLV },
+ [GSM0808_FE_IE_DOWNLINK_CELL_LOAD_INFORMATION] = { TLV_TYPE_TLV },
+ [GSM0808_FE_IE_UPLINK_CELL_LOAD_INFORMATION] = { TLV_TYPE_TLV },
+ [GSM0808_FE_IE_CELL_LOAD_INFORMATION_GROUP] = { TLV_TYPE_TLV },
+ [GSM0808_FE_IE_CELL_LOAD_INFORMATION] = { TLV_TYPE_TLV },
+ [GSM0808_FE_IE_PS_INDICATION] = { TLV_TYPE_TLV },
+ [GSM0808_FE_IE_DTM_HANDOVER_COMMAND_INDICATION] = { TLV_TYPE_TLV },
+ [GSM0808_FE_IE_D_RNTI] = { TLV_TYPE_TLV },
+ [GSM0808_FE_IE_IRAT_MEASUREMENT_CONFIGURATION] = { TLV_TYPE_TLV },
+ [GSM0808_FE_IE_SOURCE_CELL_ID] = { TLV_TYPE_TLV },
+ [GSM0808_FE_IE_IRAT_MEASUREMENT_CONFIGURATION_EXTENDED_E_ARFCNS] = { TLV_TYPE_TLV },
+ [GSM0808_FE_IE_VGCS_TALKER_MODE] = { TLV_TYPE_TLV },
+ [GSM0808_FE_IE_LAST_USED_EUTRAN_PLMN_ID] = { TLV_TYPE_FIXED, 3 },
+ },
+};
+
const struct value_string gsm0406_dlci_sapi_names[] = {
{ DLCI_SAPI_RR_MM_CC, "RR/MM/CC" },
{ DLCI_SAPI_SMS, "SMS" },
@@ -1543,7 +2434,9 @@ static const struct value_string gsm0808_msgt_names[] = {
{ BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_RQST, "VGCS/VBS ASSIGN REQ" },
{ BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_RESULT, "VGCS/VBS ASSIGN RES" },
{ BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_FAILURE, "VGCS/VBS ASSIGN FAIL" },
+ { BSS_MAP_MSG_VGCS_VBS_ASSIGNMENT_STATUS, "VGCS/VBS ASSIGN STATUS" },
{ BSS_MAP_MSG_VGCS_VBS_QUEUING_INDICATION, "VGCS/VBS QUEUING IND" },
+ { BSS_MAP_MSG_VGCS_VBS_AREA_CELL_INFO, "VGCS/VBS AREA CELL INFO" },
{ BSS_MAP_MSG_UPLINK_RQST, "UPLINK REQ" },
{ BSS_MAP_MSG_UPLINK_RQST_ACKNOWLEDGE, "UPLINK REQ ACK" },
{ BSS_MAP_MSG_UPLINK_RQST_CONFIRMATION, "UPLINK REQ CONF" },
@@ -1552,11 +2445,12 @@ static const struct value_string gsm0808_msgt_names[] = {
{ BSS_MAP_MSG_UPLINK_RELEASE_CMD, "UPLINK REL CMD" },
{ BSS_MAP_MSG_UPLINK_SEIZED_CMD, "UPLINK SEIZED CMD" },
{ BSS_MAP_MSG_VGCS_ADDL_INFO, "VGCS ADDL INFO" },
+ { BSS_MAP_MSG_VGCS_SMS, "VGCS SMS" },
{ BSS_MAP_MSG_NOTIFICATION_DATA, "NOTIF DATA" },
{ BSS_MAP_MSG_UPLINK_APP_DATA, "UPLINK APP DATA" },
{ BSS_MAP_MSG_LCLS_CONNECT_CTRL, "LCLS-CONNECT-CONTROL" },
- { BSS_MAP_MSG_LCLS_CONNECT_CTRL_ACK, "CLS-CONNECT-CONTROL-ACK" },
+ { BSS_MAP_MSG_LCLS_CONNECT_CTRL_ACK, "LCLS-CONNECT-CONTROL-ACK" },
{ BSS_MAP_MSG_LCLS_NOTIFICATION, "LCLS-NOTIFICATION" },
{ 0, NULL }
@@ -1590,6 +2484,7 @@ const struct value_string gsm0808_speech_codec_type_names[] = {
{ GSM0808_SCT_HR3, "HR3" },
{ GSM0808_SCT_HR4, "HR4" },
{ GSM0808_SCT_HR6, "HR6" },
+ { GSM0808_SCT_EXT, "Codec Extension" },
{ GSM0808_SCT_CSD, "CSD" },
{ 0, NULL }
};
@@ -1796,4 +2691,101 @@ const struct value_string gsm0808_lcls_status_names[] = {
{ 0, NULL }
};
+/* Convert one S0-S15 bit to its set of AMR modes, for HR AMR and FR AMR.
+ * This is 3GPP TS 28.062 Table 7.11.3.1.3-2: "Preferred Configurations", with some configurations removed as specified
+ * in 3GPP TS 48.008 3.2.2.103:
+ *
+ * FR_AMR is coded ‘0011’.
+ * S11, S13 and S15 are reserved and coded with zeroes.
+ *
+ * HR_AMR is coded ‘0100’.
+ * S6 - S7 and S11 – S15 are reserved and coded with zeroes.
+ *
+ * Meaning: for FR, exclude all Optimisation Mode configurations.
+ * For HR, exclude all that are not supported by HR AMR -- drop all that include at least one of
+ * 10.2 or 12.2.
+ *
+ * Also, for HR, drop 12.2k from S1.
+ *
+ * The first array dimension is 0 for half rate and 1 for full rate.
+ * The second array dimension is the configuration number (0..15) aka Sn.
+ * The values are bitmask combinations of (1 << GSM0808_AMR_MODE_nnnn).
+ *
+ * For example, accumulate all modes that are possible in a given my_s15_s0:
+ *
+ * uint8_t modes = 0;
+ * for (s_bit = 0; s_bit < 15; s_bit++)
+ * if (my_s15_s0 & (1 << s_bit))
+ * modes |= gsm0808_amr_modes_from_cfg[full_rate ? 1 : 0][s_bit];
+ * for (i = 0; i < 8; i++)
+ * if (modes & (1 << i))
+ * printf(" %s", gsm0808_amr_mode_name(i));
+ */
+const uint8_t gsm0808_amr_modes_from_cfg[2][16] = {
+#define B(X) (1 << (GSM0808_AMR_MODE_##X))
+ /* HR */
+ {
+ /* Sn = modes */
+ [0] = B(4_75),
+ [1] = B(4_75) | B(5_90) | B(7_40),
+ [2] = B(5_90),
+ [3] = B(6_70),
+ [4] = B(7_40),
+ [5] = B(7_95),
+ [6] = 0,
+ [7] = 0,
+
+ [8] = B(4_75) | B(5_90),
+ [9] = B(4_75) | B(5_90) | B(6_70),
+ [10] = B(4_75) | B(5_90) | B(6_70) | B(7_40),
+ [11] = 0,
+ [12] = 0,
+ [13] = 0,
+ [14] = 0,
+ [15] = 0,
+ },
+ /* FR */
+ {
+ /* Sn = modes */
+ [0] = B(4_75),
+ [1] = B(4_75) | B(5_90) | B(7_40) | B(12_2),
+ [2] = B(5_90),
+ [3] = B(6_70),
+ [4] = B(7_40),
+ [5] = B(7_95),
+ [6] = B(10_2),
+ [7] = B(12_2),
+
+ [8] = B(4_75) | B(5_90),
+ [9] = B(4_75) | B(5_90) | B(6_70),
+ [10] = B(4_75) | B(5_90) | B(6_70) | B(7_40),
+ [11] = 0,
+ [12] = B(4_75) | B(5_90) | B(6_70) | B(10_2),
+ [13] = 0,
+ [14] = B(4_75) | B(5_90) | B(7_95) | B(12_2),
+ [15] = 0,
+ }
+};
+
+/* AMR mode names from GSM0808_AMR_MODE_*, for use with gsm0808_amr_modes_from_cfg.
+ *
+ * For example:
+ * printf("S9: ");
+ * uint8_t s9_modes = gsm0808_amr_modes_from_cfg[full_rate ? 1 : 0][9];
+ * for (bit = 0; bit < 8; bit++)
+ * if (s9_modes & (1 << bit))
+ * printf("%s,", gsm0808_amr_mode_name(bit));
+ */
+const struct value_string gsm0808_amr_mode_names[] = {
+ { GSM0808_AMR_MODE_4_75, "4.75" },
+ { GSM0808_AMR_MODE_5_15, "5.15" },
+ { GSM0808_AMR_MODE_5_90, "5.90" },
+ { GSM0808_AMR_MODE_6_70, "6.70" },
+ { GSM0808_AMR_MODE_7_40, "7.40" },
+ { GSM0808_AMR_MODE_7_95, "7.95" },
+ { GSM0808_AMR_MODE_10_2, "10.2" },
+ { GSM0808_AMR_MODE_12_2, "12.2" },
+ {}
+};
+
/*! @} */