aboutsummaryrefslogtreecommitdiffstats
path: root/src/osmo-bsc/gsm_04_08_rr.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/osmo-bsc/gsm_04_08_rr.c')
-rw-r--r--src/osmo-bsc/gsm_04_08_rr.c339
1 files changed, 232 insertions, 107 deletions
diff --git a/src/osmo-bsc/gsm_04_08_rr.c b/src/osmo-bsc/gsm_04_08_rr.c
index a44812618..ee772df6c 100644
--- a/src/osmo-bsc/gsm_04_08_rr.c
+++ b/src/osmo-bsc/gsm_04_08_rr.c
@@ -46,12 +46,12 @@
#include <osmocom/bsc/bsc_msc_data.h>
#include <osmocom/bsc/system_information.h>
#include <osmocom/bsc/bts.h>
-
+#include <osmocom/bsc/lchan.h>
int gsm48_sendmsg(struct msgb *msg)
{
if (msg->lchan)
- msg->dst = msg->lchan->ts->trx->rsl_link;
+ msg->dst = rsl_chan_link(msg->lchan);
msg->l3h = msg->data;
return rsl_data_request(msg, 0);
@@ -232,11 +232,11 @@ int get_reason_by_chreq(uint8_t ra, int neci)
return GSM_CHREQ_REASON_OTHER;
}
-static void mr_config_for_ms(struct gsm_lchan *lchan, struct msgb *msg)
+static int put_mr_config_for_ms(struct msgb *msg, const struct gsm48_multi_rate_conf *mr_conf_filtered,
+ const struct amr_multirate_conf *mr_modes)
{
- if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR)
- msgb_tlv_put(msg, GSM48_IE_MUL_RATE_CFG, lchan->mr_ms_lv[0],
- lchan->mr_ms_lv + 1);
+ msgb_put_u8(msg, GSM48_IE_MUL_RATE_CFG);
+ return gsm48_multirate_config(msg, mr_conf_filtered, mr_modes->ms_mode, mr_modes->num_modes);
}
#define CELL_SEL_IND_AFTER_REL_EARCFN_ENTRY (1+16+4+1+1)
@@ -244,7 +244,7 @@ static void mr_config_for_ms(struct gsm_lchan *lchan, struct msgb *msg)
#define CELL_SEL_IND_AFTER_REL_MAX_BYTES OSMO_BYTES_FOR_BITS(CELL_SEL_IND_AFTER_REL_MAX_BITS)
/* Generate a CSN.1 encoded "Cell Selection Indicator after release of all TCH and SDCCH"
- * as per TF 44.018 version 15.3.0 Table 10.5.2.1e.1. This only generates the "value"
+ * as per TS 44.018 version 15.3.0 Table 10.5.2.1e.1. This only generates the "value"
* part of the IE, not the tag+length wrapper */
static int generate_cell_sel_ind_after_rel(uint8_t *out, unsigned int out_len, const struct gsm_bts *bts)
{
@@ -313,10 +313,10 @@ int gsm48_send_rr_release(struct gsm_lchan *lchan)
cause = msgb_put(msg, 1);
cause[0] = lchan->release.rr_cause;
- if (lchan->release.is_csfb) {
+ if (lchan->release.last_eutran_plmn_valid) {
uint8_t buf[CELL_SEL_IND_AFTER_REL_MAX_BYTES];
int len;
-
+ /* FIXME: so far we assume all configured neigbhors match last_eutran_plmn */
len = generate_cell_sel_ind_after_rel(buf, sizeof(buf), lchan->ts->trx->bts);
if (len == 0) {
LOGPLCHAN(lchan, DRR, LOGL_NOTICE, "MSC indicated CSFB Fast Return, but "
@@ -325,8 +325,9 @@ int gsm48_send_rr_release(struct gsm_lchan *lchan)
msgb_tlv_put(msg, GSM48_IE_CELL_SEL_IND_AFTER_REL, len, buf);
}
- DEBUGP(DRR, "Sending Channel Release: Chan: Number: %d Type: %d RR-Cause: 0x%x '%s'\n",
- lchan->nr, lchan->type, lchan->release.rr_cause, rr_cause_name(lchan->release.rr_cause));
+ DEBUGP(DRR, "%s Tx Channel Release (cause=0x%02x '%s')\n",
+ gsm_lchan_name(lchan), lchan->release.rr_cause,
+ rr_cause_name(lchan->release.rr_cause));
/* Send actual release request to MS */
return gsm48_sendmsg(msg);
@@ -358,7 +359,8 @@ int gsm48_send_rr_classmark_enquiry(struct gsm_lchan *lchan)
gh->proto_discr = GSM48_PDISC_RR;
gh->msg_type = GSM48_MT_RR_CLSM_ENQ;
- DEBUGP(DRR, "%s TX CLASSMARK ENQUIRY %u\n", gsm_lchan_name(lchan), msgb_length(msg));
+ DEBUGP(DRR, "%s Tx CLASSMARK ENQUIRY (len=%u)\n",
+ gsm_lchan_name(lchan), msgb_length(msg));
return gsm48_sendmsg(msg);
}
@@ -368,16 +370,14 @@ int gsm48_send_rr_ciph_mode(struct gsm_lchan *lchan, int want_imeisv)
{
struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CIPH");
struct gsm48_hdr *gh;
- uint8_t ciph_mod_set;
+ uint8_t ciph_mod_set = 0x00;
msg->lchan = lchan;
- DEBUGP(DRR, "TX CIPHERING MODE CMD\n");
+ DEBUGP(DRR, "%s Tx CIPHERING MODE CMD\n", gsm_lchan_name(lchan));
- if (lchan->encr.alg_id <= RSL_ENC_ALG_A5(0))
- ciph_mod_set = 0;
- else
- ciph_mod_set = (lchan->encr.alg_id-2)<<1 | 1;
+ if (lchan->encr.alg_a5_n > 0)
+ ciph_mod_set = (lchan->encr.alg_a5_n - 1) << 1 | 0x01;
gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1);
gh->proto_discr = GSM48_PDISC_RR;
@@ -397,12 +397,12 @@ static void gsm48_cell_desc(struct gsm48_cell_desc *cd,
}
/*! \brief Encode a TS 04.08 multirate config LV according to 10.5.2.21aa.
- * \param[out] lv caller-allocated buffer of 7 bytes. First octet is is length.
+ * \param[out] msg msgb to append to.
* \param[in] mr_conf multi-rate configuration to encode (selected modes).
* \param[in] modes array describing the AMR modes.
* \param[in] num_modes length of the modes array.
* \returns 0 on success, -EINVAL on failure. */
-int gsm48_multirate_config(uint8_t *lv,
+int gsm48_multirate_config(struct msgb *msg,
const struct gsm48_multi_rate_conf *mr_conf,
const struct amr_mode *modes, unsigned int num_modes)
{
@@ -413,15 +413,17 @@ int gsm48_multirate_config(uint8_t *lv,
bool mode_valid;
uint8_t *gsm48_ie = (uint8_t *) mr_conf;
const struct amr_mode *modes_selected[4];
+ uint8_t *len;
+ uint8_t *data;
/* Check if modes for consistency (order and duplicates) */
- for (i = 0; i < num_modes; i++) {
- if (i > 0 && modes[i - 1].mode > modes[i].mode) {
+ for (i = 1; i < num_modes; i++) {
+ if (modes[i - 1].mode > modes[i].mode) {
LOGP(DRR, LOGL_ERROR,
"BUG: Multirate codec with inconsistent config (mode order).\n");
return -EINVAL;
}
- if (i > 0 && modes[i - 1].mode == modes[i].mode) {
+ if (modes[i - 1].mode == modes[i].mode) {
LOGP(DRR, LOGL_ERROR,
"BUG: Multirate codec with inconsistent config (duplicate modes).\n");
return -EINVAL;
@@ -482,52 +484,91 @@ int gsm48_multirate_config(uint8_t *lv,
/* When the caller is not interested in any result, skip the actual
* composition of the IE (dry run) */
- if (!lv)
+ if (!msg)
return 0;
/* Compose output buffer */
- lv[0] = (num == 1) ? 2 : (num + 2);
- memcpy(lv + 1, gsm48_ie, 2);
+ /* length */
+ len = msgb_put(msg, 1);
+
+ /* Write octet 3 (Multirate speech version, NSCB, ICMI, spare, Start mode)
+ * and octet 4 (Set of AMR codec modes) */
+ data = msgb_put(msg, 2);
+ memcpy(data, gsm48_ie, 2);
if (num == 1)
- return 0;
+ goto return_msg;
- lv[3] = modes_selected[0]->threshold & 0x3f;
- lv[4] = modes_selected[0]->hysteresis << 4;
+ /* more than 1 mode: write octet 5 and 6: threshold 1 and hysteresis 1 */
+ data = msgb_put(msg, 2);
+ data[0] = modes_selected[0]->threshold & 0x3f;
+ data[1] = modes_selected[0]->hysteresis << 4;
if (num == 2)
- return 0;
- lv[4] |= (modes_selected[1]->threshold & 0x3f) >> 2;
- lv[5] = modes_selected[1]->threshold << 6;
- lv[5] |= (modes_selected[1]->hysteresis & 0x0f) << 2;
+ goto return_msg;
+
+ /* more than 2 modes: complete octet 6 and add octet 7: threshold 2 and hysteresis 2.
+ * Threshold 2 starts in octet 6. */
+ data[1] |= (modes_selected[1]->threshold & 0x3f) >> 2;
+ /* octet 7 */
+ data = msgb_put(msg, 1);
+ data[0] = modes_selected[1]->threshold << 6;
+ data[0] |= (modes_selected[1]->hysteresis & 0x0f) << 2;
if (num == 3)
- return 0;
- lv[5] |= (modes_selected[2]->threshold & 0x3f) >> 4;
- lv[6] = modes_selected[2]->threshold << 4;
- lv[6] |= modes_selected[2]->hysteresis & 0x0f;
-
+ goto return_msg;
+
+ /* four modes: complete octet 7 and add octet 8: threshold 3 and hysteresis 3.
+ * Threshold 3 starts in octet 7. */
+ data[0] |= (modes_selected[2]->threshold & 0x3f) >> 4;
+ /* octet 8 */
+ data = msgb_put(msg, 1);
+ data[0] = modes_selected[2]->threshold << 4;
+ data[0] |= modes_selected[2]->hysteresis & 0x0f;
+
+return_msg:
+ /* Place written len in the IE length field. msg->tail points one byte after the last data octet, len points at
+ * the L octet of the TLV. */
+ *len = (msg->tail - 1) - len;
return 0;
}
#define GSM48_HOCMD_CCHDESC_LEN 16
/* Chapter 9.1.15: Handover Command */
-struct msgb *gsm48_make_ho_cmd(struct gsm_lchan *new_lchan, uint8_t power_command, uint8_t ho_ref)
+struct msgb *gsm48_make_ho_cmd(const struct gsm_lchan *new_lchan,
+ enum handover_scope ho_scope, bool async,
+ uint8_t power_command, uint8_t ho_ref)
{
struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 HO CMD");
struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
struct gsm48_ho_cmd *ho =
(struct gsm48_ho_cmd *) msgb_put(msg, sizeof(*ho));
+ const struct gsm_bts *bts = new_lchan->ts->trx->bts;
gh->proto_discr = GSM48_PDISC_RR;
gh->msg_type = GSM48_MT_RR_HANDO_CMD;
/* mandatory bits */
- gsm48_cell_desc(&ho->cell_desc, new_lchan->ts->trx->bts);
- gsm48_lchan2chan_desc(&ho->chan_desc, new_lchan);
+ gsm48_cell_desc(&ho->cell_desc, bts);
+ if (gsm48_lchan2chan_desc(&ho->chan_desc, new_lchan, gsm_ts_tsc(new_lchan->ts), false)) {
+ msgb_free(msg);
+ return NULL;
+ }
ho->ho_ref = ho_ref;
ho->power_command = power_command;
+ /* Synchronization Indication, TV (see 3GPP TS 44.018, 9.1.15.1).
+ * In the case of inter-RAT handover, always include this IE for the sake of
+ * explicitness. In the case of intra-RAT handover, include this IE only for
+ * the synchronized handover. If omitted, non-synchronized handover is assumed. */
+ if (!async || (ho_scope & HO_INTER_BSC_IN)) {
+ /* Only the SI field (Non-synchronized/Synchronized) is present.
+ * TODO: ROT (Report Observed Time Difference), currently 0.
+ * TODO: NCI (Normal cell indication), currently 0. */
+ const uint8_t sync_ind = async ? 0x00 : 0x01;
+ /* T (4 bit) + V (4 bit), see 3GPP TS 44.018, 10.5.2.39 */
+ msgb_v_put(msg, GSM48_IE_SYNC_IND | (sync_ind & 0x0f));
+ }
+
if (new_lchan->ts->hopping.enabled) {
- struct gsm_bts *bts = new_lchan->ts->trx->bts;
struct gsm48_system_information_type_1 *si1;
si1 = GSM_BTS_SI(bts, SYSINFO_TYPE_1);
@@ -536,9 +577,8 @@ struct msgb *gsm48_make_ho_cmd(struct gsm_lchan *new_lchan, uint8_t power_comman
GSM48_HOCMD_CCHDESC_LEN,
si1->cell_channel_description);
}
- /* FIXME: optional bits for type of synchronization? */
- msgb_tv_put(msg, GSM48_IE_CHANMODE_1, new_lchan->tch_mode);
+ msgb_tv_put(msg, GSM48_IE_CHANMODE_1, new_lchan->current_ch_mode_rate.chan_mode);
/* Mobile Allocation (after time), TLV (see 3GPP TS 44.018, 10.5.2.21) */
if (new_lchan->ts->hopping.enabled) {
@@ -547,35 +587,45 @@ struct msgb *gsm48_make_ho_cmd(struct gsm_lchan *new_lchan, uint8_t power_comman
new_lchan->ts->hopping.ma_data);
}
+ /* (O) Cipher Mode Setting, TV (see 3GPP TS 44.018, 9.1.15.10).
+ * Omitted in the case of intra-RAT (GERAN-to-GERAN) handover.
+ * Shall be included in the case of inter-RAT handover. */
+ if (ho_scope & HO_INTER_BSC_IN) {
+ uint8_t cms = 0x00;
+ if (new_lchan->encr.alg_a5_n > 0)
+ cms = (new_lchan->encr.alg_a5_n - 1) << 1 | 1;
+ /* T (4 bit) + V (4 bit), see 3GPP TS 44.018, 10.5.2.9 */
+ msgb_v_put(msg, GSM48_IE_CIP_MODE_SET | (cms & 0x0f));
+ }
+
/* in case of multi rate we need to attach a config */
- if (new_lchan->tch_mode == GSM48_CMODE_SPEECH_AMR)
- msgb_tlv_put(msg, GSM48_IE_MUL_RATE_CFG, new_lchan->mr_ms_lv[0],
- new_lchan->mr_ms_lv + 1);
+ if (gsm48_chan_mode_to_non_vamos(new_lchan->current_ch_mode_rate.chan_mode) == GSM48_CMODE_SPEECH_AMR) {
+ if (put_mr_config_for_ms(msg, &new_lchan->current_mr_conf,
+ (new_lchan->type == GSM_LCHAN_TCH_F) ? &bts->mr_full : &bts->mr_half)) {
+ LOG_LCHAN(new_lchan, LOGL_ERROR, "Cannot encode MultiRate Configuration IE\n");
+ msgb_free(msg);
+ return NULL;
+ }
+ }
return msg;
}
-int gsm48_send_ho_cmd(struct gsm_lchan *old_lchan, struct gsm_lchan *new_lchan,
- uint8_t power_command, uint8_t ho_ref)
-{
- struct msgb *msg = gsm48_make_ho_cmd(new_lchan, power_command, ho_ref);
- if (!msg)
- return -EINVAL;
- msg->lchan = old_lchan;
- return gsm48_sendmsg(msg);
-}
-
/* Chapter 9.1.2: Assignment Command */
-int gsm48_send_rr_ass_cmd(struct gsm_lchan *dest_lchan, struct gsm_lchan *lchan, uint8_t power_command)
+int gsm48_send_rr_ass_cmd(struct gsm_lchan *current_lchan, struct gsm_lchan *new_lchan, uint8_t power_command)
{
+ int rc;
struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 ASS CMD");
struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
struct gsm48_ass_cmd *ass =
(struct gsm48_ass_cmd *) msgb_put(msg, sizeof(*ass));
+ struct gsm_bts *bts = new_lchan->ts->trx->bts;
- DEBUGP(DRR, "-> ASSIGNMENT COMMAND tch_mode=0x%02x\n", lchan->tch_mode);
+ DEBUGP(DRR, "%s Tx ASSIGNMENT COMMAND (tch_mode=0x%02x)\n",
+ gsm_lchan_name(current_lchan),
+ new_lchan->current_ch_mode_rate.chan_mode);
- msg->lchan = dest_lchan;
+ msg->lchan = current_lchan;
gh->proto_discr = GSM48_PDISC_RR;
gh->msg_type = GSM48_MT_RR_ASS_CMD;
@@ -587,26 +637,45 @@ int gsm48_send_rr_ass_cmd(struct gsm_lchan *dest_lchan, struct gsm_lchan *lchan,
* the chan_desc. But as long as multi-slot configurations
* are not used we seem to be fine.
*/
- gsm48_lchan2chan_desc(&ass->chan_desc, lchan);
+ rc = gsm48_lchan2chan_desc(&ass->chan_desc, new_lchan, new_lchan->tsc, false);
+ if (rc) {
+ msgb_free(msg);
+ return rc;
+ }
ass->power_command = power_command;
/* Cell Channel Description (freq. hopping), TV (see 3GPP TS 44.018, 10.5.2.1b) */
- if (lchan->ts->hopping.enabled) {
- uint8_t *chan_desc = msgb_put(msg, 1 + 16); /* tag + fixed length */
- generate_cell_chan_list(chan_desc + 1, dest_lchan->ts->trx->bts);
- chan_desc[0] = GSM48_IE_CELL_CH_DESC;
+ if (new_lchan->ts->hopping.enabled) {
+ const struct gsm48_system_information_type_1 *si1 = GSM_BTS_SI(bts, 1);
+ msgb_tv_fixed_put(msg, GSM48_IE_CELL_CH_DESC,
+ sizeof(si1->cell_channel_description),
+ si1->cell_channel_description);
}
- msgb_tv_put(msg, GSM48_IE_CHANMODE_1, lchan->tch_mode);
+ msgb_tv_put(msg, GSM48_IE_CHANMODE_1, new_lchan->current_ch_mode_rate.chan_mode);
/* Mobile Allocation (freq. hopping), TLV (see 3GPP TS 44.018, 10.5.2.21) */
- if (lchan->ts->hopping.enabled) {
- msgb_tlv_put(msg, GSM48_IE_MA_AFTER, lchan->ts->hopping.ma_len,
- lchan->ts->hopping.ma_data);
+ if (new_lchan->ts->hopping.enabled) {
+ msgb_tlv_put(msg, GSM48_IE_MA_AFTER, new_lchan->ts->hopping.ma_len,
+ new_lchan->ts->hopping.ma_data);
}
/* in case of multi rate we need to attach a config */
- mr_config_for_ms(lchan, msg);
+ if (gsm48_chan_mode_to_non_vamos(new_lchan->current_ch_mode_rate.chan_mode) == GSM48_CMODE_SPEECH_AMR) {
+ int rc = put_mr_config_for_ms(msg, &new_lchan->current_mr_conf,
+ (new_lchan->type == GSM_LCHAN_TCH_F) ? &bts->mr_full : &bts->mr_half);
+ if (rc) {
+ LOG_LCHAN(current_lchan, LOGL_ERROR, "Cannot encode MultiRate Configuration IE\n");
+ msgb_free(msg);
+ return rc;
+ }
+ }
+
+ /* For VAMOS, include the TSC Set number in the Extended TSC Set IE.
+ * We don't put any PS domain related values, only the lowest two CS domain bits.
+ * Convert from spec conforming "human readable" TSC Set 1-4 to 0-3 on the wire. */
+ if (new_lchan->vamos.enabled && new_lchan->tsc_set > 0)
+ msgb_tv_put(msg, GSM48_IE_EXTENDED_TSC_SET, new_lchan->tsc_set - 1);
return gsm48_sendmsg(msg);
}
@@ -636,25 +705,43 @@ int gsm48_send_rr_app_info(struct gsm_lchan *lchan, uint8_t apdu_id, uint8_t apd
/* 9.1.5 Channel mode modify: Modify the mode on the MS side */
int gsm48_lchan_modify(struct gsm_lchan *lchan, uint8_t mode)
{
+ int rc;
struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CHN MOD");
struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh));
struct gsm48_chan_mode_modify *cmm =
(struct gsm48_chan_mode_modify *) msgb_put(msg, sizeof(*cmm));
+ struct gsm_bts *bts = lchan->ts->trx->bts;
- DEBUGP(DRR, "-> CHANNEL MODE MODIFY mode=0x%02x\n", mode);
+ DEBUGP(DRR, "%s Tx CHANNEL MODE MODIFY (mode=0x%02x)\n",
+ gsm_lchan_name(lchan), mode);
- lchan->tch_mode = mode;
msg->lchan = lchan;
gh->proto_discr = GSM48_PDISC_RR;
gh->msg_type = GSM48_MT_RR_CHAN_MODE_MODIF;
- /* fill the channel information element, this code
- * should probably be shared with rsl_rx_chan_rqd() */
- gsm48_lchan2chan_desc(&cmm->chan_desc, lchan);
+ rc = gsm48_lchan2chan_desc(&cmm->chan_desc, lchan, lchan->modify.tsc, false);
+ if (rc) {
+ msgb_free(msg);
+ return rc;
+ }
cmm->mode = mode;
/* in case of multi rate we need to attach a config */
- mr_config_for_ms(lchan, msg);
+ if (gsm48_chan_mode_to_non_vamos(lchan->modify.ch_mode_rate.chan_mode) == GSM48_CMODE_SPEECH_AMR) {
+ int rc = put_mr_config_for_ms(msg, &lchan->modify.mr_conf_filtered,
+ (lchan->type == GSM_LCHAN_TCH_F) ? &bts->mr_full : &bts->mr_half);
+ if (rc) {
+ LOG_LCHAN(lchan, LOGL_ERROR, "Cannot encode MultiRate Configuration IE\n");
+ msgb_free(msg);
+ return rc;
+ }
+ }
+
+ if (lchan->modify.info.vamos && lchan->modify.tsc_set > 0) {
+ /* Add the Extended TSC Set IE. So far we only need a TSC Set sent for VAMOS.
+ * Convert from spec conforming "human readable" TSC Set 1-4 to 0-3 on the wire */
+ msgb_tv_put(msg, GSM48_IE_EXTENDED_TSC_SET, (lchan->modify.tsc_set - 1) & 0x3);
+ }
return gsm48_sendmsg(msg);
}
@@ -668,32 +755,64 @@ int gsm48_rx_rr_modif_ack(struct msgb *msg)
LOG_LCHAN(msg->lchan, LOGL_DEBUG, "CHANNEL MODE MODIFY ACK for %s\n",
gsm48_chan_mode_name(mod->mode));
- if (mod->mode != msg->lchan->tch_mode) {
+ if (mod->mode != msg->lchan->modify.ch_mode_rate.chan_mode) {
LOG_LCHAN(msg->lchan, LOGL_ERROR,
"CHANNEL MODE MODIFY ACK has wrong mode: Wanted: %s Got: %s\n",
- gsm48_chan_mode_name(msg->lchan->tch_mode),
+ gsm48_chan_mode_name(msg->lchan->modify.ch_mode_rate.chan_mode),
gsm48_chan_mode_name(mod->mode));
return -1;
}
- /* update the channel type */
- switch (mod->mode) {
- case GSM48_CMODE_SIGN:
- msg->lchan->rsl_cmode = RSL_CMOD_SPD_SIGN;
- break;
- case GSM48_CMODE_SPEECH_V1:
- case GSM48_CMODE_SPEECH_EFR:
- case GSM48_CMODE_SPEECH_AMR:
- msg->lchan->rsl_cmode = RSL_CMOD_SPD_SPEECH;
- break;
- case GSM48_CMODE_DATA_14k5:
- case GSM48_CMODE_DATA_12k0:
- case GSM48_CMODE_DATA_6k0:
- case GSM48_CMODE_DATA_3k6:
- msg->lchan->rsl_cmode = RSL_CMOD_SPD_DATA;
- break;
+ return 0;
+}
+
+/* Get the ARFCN from the BCCH channel list by the index "BCCH-FREQ-NCELL i"
+ * (idx) as described in 3GPP TS 144.018 ยง 10.5.2.20. The BCCH channel list is
+ * split into two sub lists, each ascendingly ordered by ARFCN > 0:
+ * 1) SI* and SI*bis entries
+ * 2) SI*ter entries (if available)
+ * ARFCN 0 is at the end of each sub list.
+ * Both sub lists are stored in one bitvec (nbv), iterate twice through it. */
+static int neigh_list_get_arfcn(struct gsm_bts *bts, const struct bitvec *nbv, unsigned int idx)
+{
+ unsigned int arfcn, i = 0;
+
+ /* First sub list, ARFCN > 0 */
+ for (arfcn = 1; arfcn < nbv->data_len * 8; arfcn++) {
+ if (bitvec_get_bit_pos(nbv, arfcn) == ZERO)
+ continue;
+ /* Skip SI*ter */
+ if (!band_compatible(bts, arfcn))
+ continue;
+ if (i == idx)
+ return arfcn;
+ i++;
+ }
+
+ /* First sub list, ARFCN == 0 (last position) */
+ if (bitvec_get_bit_pos(nbv, 0) == ONE && band_compatible(bts, 0)) {
+ if (i == idx)
+ return 0;
+ i++;
}
+ /* Second sub list, ARFCN > 0 */
+ for (arfcn = 1; arfcn < nbv->data_len * 8; arfcn++) {
+ if (bitvec_get_bit_pos(nbv, arfcn) == ZERO)
+ continue;
+ /* Require SI*ter */
+ if (band_compatible(bts, arfcn))
+ continue;
+ if (i == idx)
+ return arfcn;
+ i++;
+ }
+
+ /* Second sub list, ARFCN == 0 (last position) */
+ if (bitvec_get_bit_pos(nbv, 0) == ONE && !band_compatible(bts, 0) && i == idx)
+ return 0;
+
+ LOGP(DRR, LOGL_ERROR, "Invalid BCCH channel list index %d in measurement report\n", idx);
return 0;
}
@@ -731,7 +850,7 @@ int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg)
mrc = &rep->cell[0];
mrc->rxlev = data[3] & 0x3f;
mrc->neigh_idx = data[4] >> 3;
- mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1);
+ mrc->arfcn = neigh_list_get_arfcn(bts, nbv, mrc->neigh_idx);
mrc->bsic = ((data[4] & 0x07) << 3) | (data[5] >> 5);
if (rep->num_cell < 2)
return 0;
@@ -739,7 +858,7 @@ int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg)
mrc = &rep->cell[1];
mrc->rxlev = ((data[5] & 0x1f) << 1) | (data[6] >> 7);
mrc->neigh_idx = (data[6] >> 2) & 0x1f;
- mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1);
+ mrc->arfcn = neigh_list_get_arfcn(bts, nbv, mrc->neigh_idx);
mrc->bsic = ((data[6] & 0x03) << 4) | (data[7] >> 4);
if (rep->num_cell < 3)
return 0;
@@ -747,7 +866,7 @@ int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg)
mrc = &rep->cell[2];
mrc->rxlev = ((data[7] & 0x0f) << 2) | (data[8] >> 6);
mrc->neigh_idx = (data[8] >> 1) & 0x1f;
- mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1);
+ mrc->arfcn = neigh_list_get_arfcn(bts, nbv, mrc->neigh_idx);
mrc->bsic = ((data[8] & 0x01) << 5) | (data[9] >> 3);
if (rep->num_cell < 4)
return 0;
@@ -755,7 +874,7 @@ int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg)
mrc = &rep->cell[3];
mrc->rxlev = ((data[9] & 0x07) << 3) | (data[10] >> 5);
mrc->neigh_idx = data[10] & 0x1f;
- mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1);
+ mrc->arfcn = neigh_list_get_arfcn(bts, nbv, mrc->neigh_idx);
mrc->bsic = data[11] >> 2;
if (rep->num_cell < 5)
return 0;
@@ -763,7 +882,7 @@ int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg)
mrc = &rep->cell[4];
mrc->rxlev = ((data[11] & 0x03) << 4) | (data[12] >> 4);
mrc->neigh_idx = ((data[12] & 0xf) << 1) | (data[13] >> 7);
- mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1);
+ mrc->arfcn = neigh_list_get_arfcn(bts, nbv, mrc->neigh_idx);
mrc->bsic = (data[13] >> 1) & 0x3f;
if (rep->num_cell < 6)
return 0;
@@ -771,7 +890,7 @@ int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg)
mrc = &rep->cell[5];
mrc->rxlev = ((data[13] & 0x01) << 5) | (data[14] >> 3);
mrc->neigh_idx = ((data[14] & 0x07) << 2) | (data[15] >> 6);
- mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1);
+ mrc->arfcn = neigh_list_get_arfcn(bts, nbv, mrc->neigh_idx);
mrc->bsic = data[15] & 0x3f;
return 0;
@@ -946,7 +1065,7 @@ static void dispatch_dtap(struct gsm_subscriber_connection *conn,
osmo_fsm_inst_dispatch(conn->ho.fi, HO_EV_RR_HO_FAIL, msg);
break;
case GSM48_MT_RR_CIPH_M_COMPL:
- bsc_cipher_mode_compl(conn, msg, conn->lchan->encr.alg_id);
+ bsc_cipher_mode_compl(conn, msg, conn->lchan->encr.alg_a5_n);
break;
case GSM48_MT_RR_ASS_COMPL:
if (conn->assignment.fi)
@@ -974,6 +1093,9 @@ static void dispatch_dtap(struct gsm_subscriber_connection *conn,
case GSM48_MT_RR_CLSM_CHG:
handle_classmark_chg(conn, msg);
break;
+ case GSM48_MT_RR_UTRAN_CLSM_CHG:
+ /* TODO: forward to the MSC? */
+ break;
case GSM48_MT_RR_APP_INFO:
/* Passing RR APP INFO to MSC, not quite
* according to spec */
@@ -992,15 +1114,18 @@ static void dispatch_dtap(struct gsm_subscriber_connection *conn,
* VTY configuration does not permit. */
if (msg_type == GSM48_MT_CC_EMERG_SETUP) {
if (msg->lchan->ts->trx->bts->si_common.rach_control.t2 & 0x4) {
- LOG_LCHAN(msg->lchan, LOGL_NOTICE, "MS attempts EMERGENCY SETUP although EMERGENCY CALLS"
- " are not allowed in sysinfo (spec violation by MS!)\n");
- lchan_release(msg->lchan, true, true, GSM48_RR_CAUSE_PREMPTIVE_REL);
+ LOG_LCHAN(msg->lchan, LOGL_ERROR, "MS attempts EMERGENCY SETUP although EMERGENCY CALLS"
+ " are not allowed in sysinfo (cfg: network / bts / rach emergency call allowed 0)\n");
+ lchan_release(msg->lchan, true, true, GSM48_RR_CAUSE_PROT_ERROR_UNSPC,
+ gscon_last_eutran_plmn(msg->lchan->conn));
break;
}
if (!conn->sccp.msc->allow_emerg) {
- LOG_LCHAN(msg->lchan, LOGL_NOTICE, "MS attempts EMERGENCY SETUP, but EMERGENCY CALLS are"
- " denied on this BSC (check BTS config!)\n");
- lchan_release(msg->lchan, true, true, GSM48_RR_CAUSE_PREMPTIVE_REL);
+ LOG_LCHAN(msg->lchan, LOGL_ERROR, "MS attempts EMERGENCY SETUP, but EMERGENCY CALLS are"
+ " denied on MSC %d (cfg: msc %d / allow-emergency deny)\n",
+ conn->sccp.msc->nr, conn->sccp.msc->nr);
+ lchan_release(msg->lchan, true, true, GSM48_RR_CAUSE_PROT_ERROR_UNSPC,
+ gscon_last_eutran_plmn(msg->lchan->conn));
break;
}
}