aboutsummaryrefslogtreecommitdiffstats
path: root/src/gsm/gsm48.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/gsm/gsm48.c')
-rw-r--r--src/gsm/gsm48.c265
1 files changed, 228 insertions, 37 deletions
diff --git a/src/gsm/gsm48.c b/src/gsm/gsm48.c
index eb1d055b..64b17658 100644
--- a/src/gsm/gsm48.c
+++ b/src/gsm/gsm48.c
@@ -19,10 +19,6 @@
* 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 <stdint.h>
@@ -46,6 +42,7 @@
#include <osmocom/gsm/protocol/gsm_08_58.h>
#include <osmocom/gsm/protocol/gsm_04_08_gprs.h>
#include <osmocom/gsm/protocol/gsm_23_003.h>
+#include <osmocom/gsm/protocol/gsm_44_068.h>
/*! \addtogroup gsm0408
* @{
@@ -124,6 +121,7 @@ const struct tlv_definition gsm48_rr_att_tlvdef = {
[GSM48_IE_CHDES_2_AFTER] = { TLV_TYPE_FIXED, 3 },
[GSM48_IE_MODE_SEC_CH] = { TLV_TYPE_TV },
[GSM48_IE_F_CH_SEQ_AFTER] = { TLV_TYPE_FIXED, 9 },
+ [GSM48_IE_EXTENDED_TSC_SET] = { TLV_TYPE_TV },
[GSM48_IE_MA_AFTER] = { TLV_TYPE_TLV },
[GSM48_IE_BA_RANGE] = { TLV_TYPE_TLV },
[GSM48_IE_GROUP_CHDES] = { TLV_TYPE_TLV },
@@ -132,10 +130,10 @@ const struct tlv_definition gsm48_rr_att_tlvdef = {
[GSM48_IE_REALTIME_DIFF] = { TLV_TYPE_TLV },
[GSM48_IE_START_TIME] = { TLV_TYPE_FIXED, 2 },
[GSM48_IE_TIMING_ADVANCE] = { TLV_TYPE_TV },
- [GSM48_IE_GROUP_CIP_SEQ] = { TLV_TYPE_SINGLE_TV },
- [GSM48_IE_CIP_MODE_SET] = { TLV_TYPE_SINGLE_TV },
- [GSM48_IE_GPRS_RESUMPT] = { TLV_TYPE_SINGLE_TV },
- [GSM48_IE_SYNC_IND] = { TLV_TYPE_SINGLE_TV },
+ [GSM48_IE_GROUP_CIP_SEQ_HO] = { TLV_TYPE_SINGLE_TV },
+ [GSM48_IE_CIP_MODE_SET_HO] = { TLV_TYPE_SINGLE_TV },
+ [GSM48_IE_GPRS_RESUMPT_HO] = { TLV_TYPE_SINGLE_TV },
+ [GSM48_IE_SYNC_IND_HO] = { TLV_TYPE_SINGLE_TV },
},
};
@@ -151,7 +149,7 @@ const struct tlv_definition gsm48_mm_att_tlvdef = {
[GSM48_IE_NET_DST] = { TLV_TYPE_TLV },
[GSM48_IE_LOCATION_AREA] = { TLV_TYPE_FIXED, 5 },
- [GSM48_IE_PRIORITY_LEV] = { TLV_TYPE_SINGLE_TV },
+ [GSM48_IE_PRIORITY_LEV_HO] = { TLV_TYPE_SINGLE_TV },
[GSM48_IE_FOLLOW_ON_PROC] = { TLV_TYPE_T },
[GSM48_IE_CTS_PERMISSION] = { TLV_TYPE_T },
},
@@ -164,13 +162,16 @@ static const struct value_string rr_cause_names[] = {
{ GSM48_RR_CAUSE_ABNORMAL_TIMER, "Abnormal release, timer expired" },
{ GSM48_RR_CAUSE_ABNORMAL_NOACT, "Abnormal release, no activity on radio path" },
{ GSM48_RR_CAUSE_PREMPTIVE_REL, "Preemptive release" },
+ { GSM48_RR_CAUSE_UTRAN_CFG_UNK, "UTRAN configuration unknown" },
{ GSM48_RR_CAUSE_HNDOVER_IMP, "Handover impossible, timing advance out of range" },
{ GSM48_RR_CAUSE_CHAN_MODE_UNACCT, "Channel mode unacceptable" },
{ GSM48_RR_CAUSE_FREQ_NOT_IMPL, "Frequency not implemented" },
+ { GSM48_RR_CAUSE_LEAVE_GROUP_CA, "Originator or talker leaving group call area" },
+ { GSM48_RR_CAUSE_LOW_LEVEL_FAIL, "Lower layer failure" },
{ GSM48_RR_CAUSE_CALL_CLEARED, "Call already cleared" },
{ GSM48_RR_CAUSE_SEMANT_INCORR, "Semantically incorrect message" },
{ GSM48_RR_CAUSE_INVALID_MAND_INF, "Invalid mandatory information" },
- { GSM48_RR_CAUSE_MSG_TYPE_N, "Message type non-existant or not implemented" },
+ { GSM48_RR_CAUSE_MSG_TYPE_N, "Message type non-existent or not implemented" },
{ GSM48_RR_CAUSE_MSG_TYPE_N_COMPAT, "Message type not compatible with protocol state" },
{ GSM48_RR_CAUSE_COND_IE_ERROR, "Conditional IE error" },
{ GSM48_RR_CAUSE_NO_CELL_ALLOC_A, "No cell allocation available" },
@@ -419,19 +420,102 @@ const char *gsm48_rr_msg_name(uint8_t msgtype)
return get_value_string(rr_msg_names, msgtype);
}
+/* 3GPP TS 44.018 Table 10.4.2 */
+static const struct value_string rr_msg_type_short_names[] = {
+ { GSM48_MT_RR_SH_SI10, "System Information Type 10" },
+ { GSM48_MT_RR_SH_FACCH, "Notification/FACCH" },
+ { GSM48_MT_RR_SH_UL_FREE, "Uplink Free" },
+ { GSM48_MT_RR_SH_MEAS_REP, "Enhanced Measurement Report (uplink)" },
+ { GSM48_MT_RR_SH_MEAS_INFO, "Measurement Information (downlink)" },
+ { GSM48_MT_RR_SH_VGCS_RECON, "VBS/VGCS Reconfigure" },
+ { GSM48_MT_RR_SH_VGCS_RECON2, "VBS/VGCS Reconfigure2" },
+ { GSM48_MT_RR_SH_VGCS_INFO, "VGCS Additional Information" },
+ { GSM48_MT_RR_SH_VGCS_SMS, "VGCS SMS Information" },
+ { GSM48_MT_RR_SH_SI10bis, "System Information Type 10bis" },
+ { GSM48_MT_RR_SH_SI10ter, "System Information Type 10ter" },
+ { GSM48_MT_RR_SH_VGCS_NEIGH, "VGCS Neighbour Cell Information" },
+ { GSM48_MT_RR_SH_APP_DATA, "Notify Application Data" },
+ { 0, NULL }
+};
+
+/*! return string representation of RR Message Type using the RR short protocol discriminator */
+const char *gsm48_rr_short_pd_msg_name(uint8_t msgtype)
+{
+ return get_value_string(rr_msg_type_short_names, msgtype);
+}
const struct value_string gsm48_chan_mode_names[] = {
{ GSM48_CMODE_SIGN, "SIGNALLING" },
{ GSM48_CMODE_SPEECH_V1, "SPEECH_V1" },
{ GSM48_CMODE_SPEECH_EFR, "SPEECH_EFR" },
{ GSM48_CMODE_SPEECH_AMR, "SPEECH_AMR" },
+ { GSM48_CMODE_SPEECH_V4, "SPEECH_V4" },
+ { GSM48_CMODE_SPEECH_V5, "SPEECH_V5" },
+ { GSM48_CMODE_SPEECH_V6, "SPEECH_V6" },
+
+ { GSM48_CMODE_DATA_43k5_14k5, "DATA_43k5_14k5" },
+ { GSM48_CMODE_DATA_29k0_14k5, "DATA_29k0_14k5" },
+ { GSM48_CMODE_DATA_43k5_29k0, "DATA_43k5_29k0" },
+ { GSM48_CMODE_DATA_14k5_43k5, "DATA_14k5_43k5" },
+ { GSM48_CMODE_DATA_14k5_29k0, "DATA_14k5_29k0" },
+ { GSM48_CMODE_DATA_29k0_43k5, "DATA_29k0_43k5" },
+
+ { GSM48_CMODE_DATA_43k5, "DATA_43k5" },
+ { GSM48_CMODE_DATA_32k0, "DATA_32k0" },
+ { GSM48_CMODE_DATA_29k0, "DATA_29k0" },
{ GSM48_CMODE_DATA_14k5, "DATA_14k5" },
{ GSM48_CMODE_DATA_12k0, "DATA_12k0" },
{ GSM48_CMODE_DATA_6k0, "DATA_6k0" },
{ GSM48_CMODE_DATA_3k6, "DATA_3k6" },
+
+ { GSM48_CMODE_SPEECH_V1_VAMOS, "SPEECH_V1_VAMOS" },
+ { GSM48_CMODE_SPEECH_V2_VAMOS, "SPEECH_V2_VAMOS" },
+ { GSM48_CMODE_SPEECH_V3_VAMOS, "SPEECH_V3_VAMOS" },
+ { GSM48_CMODE_SPEECH_V5_VAMOS, "SPEECH_V5_VAMOS" },
{ 0, NULL },
};
+/*! Translate GSM48_CMODE_SPEECH_* to its corresponding GSM48_CMODE_SPEECH_*_VAMOS mode.
+ * If the mode has no equivalent VAMOS mode, return a negative value.
+ */
+enum gsm48_chan_mode gsm48_chan_mode_to_vamos(enum gsm48_chan_mode mode)
+{
+ switch (mode) {
+ case GSM48_CMODE_SPEECH_V1:
+ case GSM48_CMODE_SPEECH_V1_VAMOS:
+ return GSM48_CMODE_SPEECH_V1_VAMOS;
+ case GSM48_CMODE_SPEECH_EFR:
+ case GSM48_CMODE_SPEECH_V2_VAMOS:
+ return GSM48_CMODE_SPEECH_V2_VAMOS;
+ case GSM48_CMODE_SPEECH_AMR:
+ case GSM48_CMODE_SPEECH_V3_VAMOS:
+ return GSM48_CMODE_SPEECH_V3_VAMOS;
+ case GSM48_CMODE_SPEECH_V5_VAMOS:
+ return GSM48_CMODE_SPEECH_V5_VAMOS;
+ default:
+ return -1;
+ }
+}
+
+/*! Translate GSM48_CMODE_SPEECH_*_VAMOS to its corresponding GSM48_CMODE_SPEECH_* non-vamos mode.
+ * If the mode is not a VAMOS mode, return the unchanged mode.
+ */
+enum gsm48_chan_mode gsm48_chan_mode_to_non_vamos(enum gsm48_chan_mode mode)
+{
+ switch (mode) {
+ case GSM48_CMODE_SPEECH_V1_VAMOS:
+ return GSM48_CMODE_SPEECH_V1;
+ case GSM48_CMODE_SPEECH_V2_VAMOS:
+ return GSM48_CMODE_SPEECH_EFR;
+ case GSM48_CMODE_SPEECH_V3_VAMOS:
+ return GSM48_CMODE_SPEECH_AMR;
+ case GSM48_CMODE_SPEECH_V5_VAMOS:
+ return GSM48_CMODE_SPEECH_V5;
+ default:
+ return mode;
+ }
+}
+
const struct value_string gsm_chan_t_names[] = {
{ GSM_LCHAN_NONE, "NONE" },
{ GSM_LCHAN_SDCCH, "SDCCH" },
@@ -554,11 +638,13 @@ int osmo_mobile_identity_decode(struct osmo_mobile_identity *mi, const uint8_t *
{
int rc;
int nibbles_len;
- char *str;
- size_t str_size;
+ char *str = NULL; /* initialize to avoid uninitialized false warnings on some gcc versions (11.1.0) */
+ size_t str_size = 0; /* initialize to avoid uninitialized false warnings on some gcc versions (11.1.0) */
- if (!mi_data || mi_len < 1)
- return -EBADMSG;
+ if (!mi_data || mi_len < 1) {
+ rc = -EBADMSG;
+ goto return_error;
+ }
nibbles_len = (mi_len - 1) * 2 + ((mi_data[0] & GSM_MI_ODD) ? 1 : 0);
@@ -630,8 +716,12 @@ int osmo_mobile_identity_decode(struct osmo_mobile_identity *mi, const uint8_t *
goto return_error;
}
rc = osmo_bcd2str(str, str_size, mi_data, 1, 1 + nibbles_len, allow_hex);
- /* rc checked below */
- break;
+ /* check mi->str printing rc */
+ if (rc < 1 || rc >= str_size) {
+ rc = -EBADMSG;
+ goto return_error;
+ }
+ return 0;
default:
/* Already handled above, but as future bug paranoia: */
@@ -639,13 +729,6 @@ int osmo_mobile_identity_decode(struct osmo_mobile_identity *mi, const uint8_t *
goto return_error;
}
- /* check mi->str printing rc */
- if (rc < 1 || rc >= str_size) {
- rc = -EBADMSG;
- goto return_error;
- }
- return 0;
-
return_error:
*mi = (struct osmo_mobile_identity){
.type = GSM_MI_TYPE_NONE,
@@ -783,11 +866,13 @@ int osmo_mobile_identity_encode_msgb(struct msgb *msg, const struct osmo_mobile_
* osmo_mobile_identity.
*
* \param[out] mi Return buffer for decoded Mobile Identity.
- * \param[in] msg The Complete Layer 3 message to extract from (LU, CM Service Req or Paging Resp).
+ * \param[in] l3_data The Complete Layer 3 message to extract from (LU, CM Service Req or Paging Resp).
+ * \param[in] l3_len Length of l3_data in bytes.
* \returns 0 on success, negative on error: return codes as defined in osmo_mobile_identity_decode(), or
* -ENOTSUP = not a Complete Layer 3 message,
*/
-int osmo_mobile_identity_decode_from_l3(struct osmo_mobile_identity *mi, struct msgb *msg, bool allow_hex)
+int osmo_mobile_identity_decode_from_l3_buf(struct osmo_mobile_identity *mi, const uint8_t *l3_data, size_t l3_len,
+ bool allow_hex)
{
const struct gsm48_hdr *gh;
int8_t pdisc = 0;
@@ -806,10 +891,10 @@ int osmo_mobile_identity_decode_from_l3(struct osmo_mobile_identity *mi, struct
.tmsi = GSM_RESERVED_TMSI,
};
- if (msgb_l3len(msg) < sizeof(*gh))
+ if (l3_len < sizeof(*gh))
return -EBADMSG;
- gh = msgb_l3(msg);
+ gh = (void *)l3_data;
pdisc = gsm48_hdr_pdisc(gh);
mtype = gsm48_hdr_msg_type(gh);
@@ -819,12 +904,12 @@ int osmo_mobile_identity_decode_from_l3(struct osmo_mobile_identity *mi, struct
switch (mtype) {
case GSM48_MT_MM_LOC_UPD_REQUEST:
/* First make sure that lu-> can be dereferenced */
- if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*lu))
+ if (l3_len < sizeof(*gh) + sizeof(*lu))
return -EBADMSG;
/* Now we know there is enough msgb data to read a lu->mi_len, so also check that */
lu = (struct gsm48_loc_upd_req*)gh->data;
- if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*lu) + lu->mi_len)
+ if (l3_len < sizeof(*gh) + sizeof(*lu) + lu->mi_len)
return -EBADMSG;
mi_data = lu->mi;
mi_len = lu->mi_len;
@@ -834,7 +919,7 @@ int osmo_mobile_identity_decode_from_l3(struct osmo_mobile_identity *mi, struct
case GSM48_MT_MM_CM_REEST_REQ:
/* Unfortunately in Phase1 the Classmark2 length is variable, so we cannot
* just use gsm48_service_request struct, and need to parse it manually. */
- if (msgb_l3len(msg) < sizeof(*gh) + 2)
+ if (l3_len < sizeof(*gh) + 2)
return -EBADMSG;
cm2_len = gh->data[1];
@@ -842,7 +927,7 @@ int osmo_mobile_identity_decode_from_l3(struct osmo_mobile_identity *mi, struct
goto got_cm2;
case GSM48_MT_MM_IMSI_DETACH_IND:
- if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*idi))
+ if (l3_len < sizeof(*gh) + sizeof(*idi))
return -EBADMSG;
idi = (struct gsm48_imsi_detach_ind*) gh->data;
mi_data = idi->mi;
@@ -850,7 +935,7 @@ int osmo_mobile_identity_decode_from_l3(struct osmo_mobile_identity *mi, struct
goto got_mi;
case GSM48_MT_MM_ID_RESP:
- if (msgb_l3len(msg) < sizeof(*gh) + 2)
+ if (l3_len < sizeof(*gh) + 2)
return -EBADMSG;
mi_data = gh->data+1;
mi_len = gh->data[0];
@@ -865,13 +950,24 @@ int osmo_mobile_identity_decode_from_l3(struct osmo_mobile_identity *mi, struct
switch (mtype) {
case GSM48_MT_RR_PAG_RESP:
- if (msgb_l3len(msg) < sizeof(*gh) + sizeof(*paging_response))
+ if (l3_len < sizeof(*gh) + sizeof(*paging_response))
return -EBADMSG;
paging_response = (struct gsm48_pag_resp*)gh->data;
cm2_len = paging_response->cm2_len;
cm2_buf = (uint8_t*)&paging_response->cm2;
goto got_cm2;
+ case GSM48_MT_RR_TALKER_IND:
+ /* Check minimum size: Header + CM2 LV + minimum MI LV */
+ if (l3_len < sizeof(*gh) + 4 + 2)
+ return -EBADMSG;
+ /* CM2 shall be always 3 bytes in length */
+ if (gh->data[0] != 3)
+ return -EBADMSG;
+ cm2_len = gh->data[0];
+ cm2_buf = gh->data + 1;
+ goto got_cm2;
+
default:
break;
}
@@ -884,7 +980,7 @@ got_cm2:
/* MI (Mobile Identity) LV follows the Classmark2 */
/* There must be at least a mi_len byte after the CM2 */
- if (cm2_buf + cm2_len + 1 > msg->tail)
+ if (cm2_buf + cm2_len + 1 > l3_data + l3_len)
return -EBADMSG;
mi_start = cm2_buf + cm2_len;
@@ -893,12 +989,27 @@ got_cm2:
got_mi:
/* mi_data points at the start of the Mobile Identity coding of mi_len bytes */
- if (mi_data + mi_len > msg->tail)
+ if (mi_data + mi_len > l3_data + l3_len)
return -EBADMSG;
return osmo_mobile_identity_decode(mi, mi_data, mi_len, allow_hex);
}
+/*! Extract Mobile Identity from a Complete Layer 3 message.
+ *
+ * Determine the Mobile Identity data and call osmo_mobile_identity_decode() to return a decoded struct
+ * osmo_mobile_identity.
+ *
+ * \param[out] mi Return buffer for decoded Mobile Identity.
+ * \param[in] msg The Complete Layer 3 message to extract from (LU, CM Service Req or Paging Resp).
+ * \returns 0 on success, negative on error: return codes as defined in osmo_mobile_identity_decode(), or
+ * -ENOTSUP = not a Complete Layer 3 message,
+ */
+int osmo_mobile_identity_decode_from_l3(struct osmo_mobile_identity *mi, struct msgb *msg, bool allow_hex)
+{
+ return osmo_mobile_identity_decode_from_l3_buf(mi, msgb_l3(msg), msgb_l3len(msg), allow_hex);
+}
+
/*! Return a human readable representation of a struct osmo_mobile_identity.
* Write a string like "IMSI-1234567", "TMSI-0x1234ABCD" or "NONE", "NULL".
* \param[out] buf String buffer to write to.
@@ -1237,7 +1348,64 @@ int gsm48_mi_to_string(char *string, int str_len, const uint8_t *mi, int mi_len)
return 1;
}
-/*! Parse TS 04.08 Routing Area Identifier
+/*! Decode to struct osmo_routing_area_id from a 3GPP TS 24.008 § 10.5.5.15 Routing area identification.
+ * \param[out] dst Store the decoded result here.
+ * \param[in] ra_data The start of a Routing Area ID in encoded form, to be decoded.
+ * \param[in] ra_data_len Buffer size available to read from at *ra_data.
+ * \return the number of decoded bytes on success, or negative on error (if the input buffer is too small).
+ */
+int osmo_routing_area_id_decode(struct osmo_routing_area_id *dst, const uint8_t *ra_data, size_t ra_data_len)
+{
+ const struct gsm48_ra_id *ra_id;
+ if (ra_data_len < sizeof(*ra_id))
+ return -ENOSPC;
+
+ gsm48_decode_lai2((void *)ra_data, &dst->lac);
+
+ ra_id = (void *)ra_data;
+ dst->rac = ra_id->rac;
+
+ return sizeof(*ra_id);
+}
+
+/*! Encode struct osmo_routing_area_id to a 3GPP TS 24.008 § 10.5.5.15 Routing area identification: write to a buffer.
+ * \param[out] buf Return buffer for encoded Mobile Identity.
+ * \param[in] buflen sizeof(buf).
+ * \param[in] src RA to encode.
+ * \return Amount of bytes written to buf, or negative on error.
+ */
+int osmo_routing_area_id_encode_buf(uint8_t *buf, size_t buflen, const struct osmo_routing_area_id *src)
+{
+ struct gsm48_ra_id *ra_id;
+ if (buflen < sizeof(*ra_id))
+ return -ENOSPC;
+
+ gsm48_generate_lai2((void *)buf, &src->lac);
+
+ ra_id = (void *)buf;
+ ra_id->rac = src->rac;
+
+ return sizeof(*ra_id);
+}
+
+/*! Encode struct osmo_routing_area_id to a 3GPP TS 24.008 § 10.5.5.15 Routing area identification: append to msgb.
+ * To succeed, the msgb must have tailroom >= sizeof(struct gsm48_ra_id).
+ * \param[out] msg Append to this msgb.
+ * \param[in] src Encode this Routing Area ID.
+ * \return Number of bytes appended to msgb, or negative on error.
+ */
+int osmo_routing_area_id_encode_msgb(struct msgb *msg, const struct osmo_routing_area_id *src)
+{
+ int rc = osmo_routing_area_id_encode_buf(msg->tail, msgb_tailroom(msg), src);
+ if (rc <= 0)
+ return rc;
+ msgb_put(msg, rc);
+ return rc;
+}
+
+/*! Parse TS 04.08 Routing Area Identifier.
+ * Preferably use osmo_routing_area_id_decode() instead: struct osmo_routing_area_id is better integrated with other API
+ * like osmo_plmn_cmp().
* \param[out] Caller-provided memory for decoded RA ID
* \param[in] buf Input buffer pointing to RAI IE value */
void gsm48_parse_ra(struct gprs_ra_id *raid, const uint8_t *buf)
@@ -1295,6 +1463,25 @@ int gsm48_construct_ra(uint8_t *buf, const struct gprs_ra_id *raid)
return 6;
}
+/*! Compare a TS 04.08 Routing Area Identifier
+ * \param[in] raid1 first Routing Area ID to compare.
+ * \param[in] raid2 second Routing Area ID to compare.
+ * \returns true if raid1 and raid2 match, false otherwise. */
+bool gsm48_ra_equal(const struct gprs_ra_id *raid1, const struct gprs_ra_id *raid2)
+{
+ if (raid1->mcc != raid2->mcc)
+ return false;
+ if (raid1->mnc != raid2->mnc)
+ return false;
+ if (raid1->mnc_3_digits != raid2->mnc_3_digits)
+ return false;
+ if (raid1->lac != raid2->lac)
+ return false;
+ if (raid1->rac != raid2->rac)
+ return false;
+ return true;
+}
+
/*! Determine number of paging sub-channels
* \param[in] chan_desc Control Channel Description
* \returns number of paging sub-channels
@@ -1302,7 +1489,7 @@ int gsm48_construct_ra(uint8_t *buf, const struct gprs_ra_id *raid)
* Uses From Table 10.5.33 of GSM 04.08 to determine the number of
* paging sub-channels in the given control channel configuration
*/
-int gsm48_number_of_paging_subchannels(struct gsm48_control_channel_descr *chan_desc)
+int gsm48_number_of_paging_subchannels(const struct gsm48_control_channel_descr *chan_desc)
{
unsigned int n_pag_blocks = gsm0502_get_n_pag_blocks(chan_desc);
@@ -1582,6 +1769,10 @@ char *gsm48_pdisc_msgtype_name_buf(char *buf, size_t buf_len, uint8_t pdisc, uin
case GSM48_PDISC_CC:
msgt_names = gsm48_cc_msgtype_names;
break;
+ case GSM48_PDISC_GROUP_CC:
+ case GSM48_PDISC_BCAST_CC:
+ msgt_names = osmo_gsm44068_msg_type_names;
+ break;
case GSM48_PDISC_NC_SS:
msgt_names = gsm48_nc_ss_msgtype_names;
break;