diff options
Diffstat (limited to 'src/common/rsl.c')
-rw-r--r-- | src/common/rsl.c | 2032 |
1 files changed, 1496 insertions, 536 deletions
diff --git a/src/common/rsl.c b/src/common/rsl.c index c0d43d0e..40690f05 100644 --- a/src/common/rsl.c +++ b/src/common/rsl.c @@ -2,6 +2,7 @@ /* (C) 2011 by Andreas Eversberg <jolly@eversberg.eu> * (C) 2011-2019 by Harald Welte <laforge@gnumonks.org> + * (C) 2020 by sysmocom - s.f.m.c. GmbH <info@sysmocom.de> * * All Rights Reserved * @@ -13,19 +14,18 @@ * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. + * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ -#include "btsconfig.h" /* for PACKAGE_VERSION */ - #include <stdio.h> #include <errno.h> #include <netdb.h> #include <stdbool.h> +#include <inttypes.h> #include <sys/types.h> #include <arpa/inet.h> @@ -56,9 +56,39 @@ #include <osmo-bts/l1sap.h> #include <osmo-bts/bts_model.h> #include <osmo-bts/pcuif_proto.h> +#include <osmo-bts/notification.h> +#include <osmo-bts/asci.h> //#define FAKE_CIPH_MODE_COMPL +/* Parse power attenuation (in dB) from BS Power IE (see 9.3.4) */ +#define BS_POWER2DB(bs_power) \ + ((bs_power & 0x0f) * 2) + +bool rsl_chan_rt_is_asci(enum rsl_cmod_crt chan_rt) +{ + switch (chan_rt) { + case RSL_CMOD_CRT_TCH_GROUP_Bm: + case RSL_CMOD_CRT_TCH_GROUP_Lm: + case RSL_CMOD_CRT_TCH_BCAST_Bm: + case RSL_CMOD_CRT_TCH_BCAST_Lm: + return true; + default: + return false; + } +} + +bool rsl_chan_rt_is_vgcs(enum rsl_cmod_crt chan_rt) +{ + switch (chan_rt) { + case RSL_CMOD_CRT_TCH_GROUP_Bm: + case RSL_CMOD_CRT_TCH_GROUP_Lm: + return true; + default: + return false; + } +} + static int rsl_tx_error_report(struct gsm_bts_trx *trx, uint8_t cause, const uint8_t *chan_nr, const uint8_t *link_id, const struct msgb *orig_msg); @@ -69,6 +99,7 @@ static const unsigned int rsl_sacch_sitypes[] = { RSL_SYSTEM_INFO_6, RSL_SYSTEM_INFO_5bis, RSL_SYSTEM_INFO_5ter, + RSL_SYSTEM_INFO_10, RSL_EXT_MEAS_ORDER, RSL_MEAS_INFO, }; @@ -85,19 +116,6 @@ int osmo_in_array(unsigned int search, const unsigned int *arr, unsigned int siz } #define OSMO_IN_ARRAY(search, arr) osmo_in_array(search, arr, ARRAY_SIZE(arr)) -int msgb_queue_flush(struct llist_head *list) -{ - struct msgb *msg, *msg2; - int count = 0; - - llist_for_each_entry_safe(msg, msg2, list, list) { - msgb_free(msg); - count++; - } - - return count; -} - /* FIXME: move this to libosmocore */ void gsm48_gen_starting_time(uint8_t *out, struct gsm_time *gtime) { @@ -106,33 +124,236 @@ void gsm48_gen_starting_time(uint8_t *out, struct gsm_time *gtime) out[1] = (gtime->t3 << 5) | gtime->t2; } -/* compute lchan->rsl_cmode and lchan->tch_mode from RSL CHAN MODE IE */ -static void lchan_tchmode_from_cmode(struct gsm_lchan *lchan, - struct rsl_ie_chan_mode *cm) +/* Handle RSL Channel Mode IE (see section 9.3.6) */ +static int rsl_handle_chan_mod_ie(struct gsm_lchan *lchan, + const struct tlv_parsed *tp, + uint8_t *cause) { + const struct rsl_ie_chan_mode *cm; + + if (!TLVP_PRES_LEN(tp, RSL_IE_CHAN_MODE, sizeof(*cm))) { + LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "Channel Mode IE is not present\n"); + *cause = RSL_ERR_MAND_IE_ERROR; + return -ENODEV; + } + + cm = (const struct rsl_ie_chan_mode *) TLVP_VAL(tp, RSL_IE_CHAN_MODE); lchan->rsl_cmode = cm->spd_ind; + lchan->rsl_chan_rt = cm->chan_rt; lchan->ts->trx->bts->dtxd = (cm->dtx_dtu & RSL_CMOD_DTXd) ? true : false; - switch (cm->chan_rate) { - case RSL_CMOD_SP_GSM1: + /* Octet 5: Channel rate and type */ + switch (cm->chan_rt) { + case RSL_CMOD_CRT_SDCCH: + case RSL_CMOD_CRT_TCH_Bm: + case RSL_CMOD_CRT_TCH_Lm: + case RSL_CMOD_CRT_TCH_GROUP_Bm: + case RSL_CMOD_CRT_TCH_GROUP_Lm: + case RSL_CMOD_CRT_TCH_BCAST_Bm: + case RSL_CMOD_CRT_TCH_BCAST_Lm: + break; + case RSL_CMOD_CRT_OSMO_TCH_VAMOS_Bm: + case RSL_CMOD_CRT_OSMO_TCH_VAMOS_Lm: + /* Make sure that Osmocom specific TSC IE is present */ + if (!TLVP_PRES_LEN(tp, RSL_IE_OSMO_TRAINING_SEQUENCE, 2)) { + LOGPLCHAN(lchan, DRSL, LOGL_ERROR, + "Training Sequence IE is not present\n"); + *cause = RSL_ERR_MAND_IE_ERROR; + return -ENODEV; + } + break; + default: + LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "Channel Mode IE contains " + "unknown 'Channel rate and type' value 0x%02x\n", + cm->chan_rt); + *cause = RSL_ERR_IE_CONTENT; + return -ENOTSUP; + } + +#define RSL_CMODE(spd_ind, chan_rate) \ + ((spd_ind << 8) | chan_rate) + + /* Octet 6: Speech coding algorithm/data rate + transparency indicator. + * NOTE: coding of this octet depends on 'Speech or data indicator' */ + switch (RSL_CMODE(cm->spd_ind, cm->chan_rate)) { + /* If octet 4 indicates signalling */ + case RSL_CMODE(RSL_CMOD_SPD_SIGN, 0x00): + /* No resources required, all other values are reserved */ + lchan->tch_mode = GSM48_CMODE_SIGN; + break; + + /* If octet 4 indicates speech */ + case RSL_CMODE(RSL_CMOD_SPD_SPEECH, RSL_CMOD_SP_GSM1): lchan->tch_mode = GSM48_CMODE_SPEECH_V1; break; - case RSL_CMOD_SP_GSM2: + case RSL_CMODE(RSL_CMOD_SPD_SPEECH, RSL_CMOD_SP_GSM2): lchan->tch_mode = GSM48_CMODE_SPEECH_EFR; break; - case RSL_CMOD_SP_GSM3: + case RSL_CMODE(RSL_CMOD_SPD_SPEECH, RSL_CMOD_SP_GSM3): lchan->tch_mode = GSM48_CMODE_SPEECH_AMR; break; - case RSL_CMOD_SP_NT_14k5: + case RSL_CMODE(RSL_CMOD_SPD_SPEECH, RSL_CMOD_SP_GSM4): + lchan->tch_mode = GSM48_CMODE_SPEECH_V4; + break; + case RSL_CMODE(RSL_CMOD_SPD_SPEECH, RSL_CMOD_SP_GSM5): + lchan->tch_mode = GSM48_CMODE_SPEECH_V5; + break; + case RSL_CMODE(RSL_CMOD_SPD_SPEECH, RSL_CMOD_SP_GSM6): + lchan->tch_mode = GSM48_CMODE_SPEECH_V6; + break; + + /* If octet 4 indicates non-transparent data */ + case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NT_14k5): lchan->tch_mode = GSM48_CMODE_DATA_14k5; break; - case RSL_CMOD_SP_NT_12k0: + case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NT_12k0): lchan->tch_mode = GSM48_CMODE_DATA_12k0; break; - case RSL_CMOD_SP_NT_6k0: + case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NT_6k0): lchan->tch_mode = GSM48_CMODE_DATA_6k0; break; + case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NT_43k5): + lchan->tch_mode = GSM48_CMODE_DATA_43k5; + break; + case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NT_28k8): + /* 28.8 kbit/s services, 29.0 kbit/s radio interface rate */ + lchan->tch_mode = GSM48_CMODE_DATA_29k0; + break; + case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NTA_43k5_14k5): + lchan->tch_mode = GSM48_CMODE_DATA_43k5_14k5; + break; + case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NTA_29k0_14k5): + lchan->tch_mode = GSM48_CMODE_DATA_29k0_14k5; + break; + case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NTA_43k5_29k0): + lchan->tch_mode = GSM48_CMODE_DATA_43k5_29k0; + break; + case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NTA_14k5_43k5): + lchan->tch_mode = GSM48_CMODE_DATA_14k5_43k5; + break; + case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NTA_14k5_29k0): + lchan->tch_mode = GSM48_CMODE_DATA_14k5_29k0; + break; + case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_NTA_29k0_43k5): + lchan->tch_mode = GSM48_CMODE_DATA_29k0_43k5; + break; + + /* If octet 4 indicates transparent data */ + case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_T_32k0): + /* 32.0 kbit/s services, 32.0 kbit/s radio interface rate */ + lchan->tch_mode = GSM48_CMODE_DATA_32k0; + lchan->csd_mode = LCHAN_CSD_M_T_32000; + break; + case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_T_29k0): + /* 29.0 kbit/s services, 29.0 kbit/s radio interface rate */ + lchan->tch_mode = GSM48_CMODE_DATA_29k0; + lchan->csd_mode = LCHAN_CSD_M_T_29000; + break; + case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_T_14k4): + /* 14.4 kbit/s services, 14.5 kbit/s radio interface rate */ + lchan->tch_mode = GSM48_CMODE_DATA_14k5; + lchan->csd_mode = LCHAN_CSD_M_T_14400; + break; + case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_T_9k6): + /* 9.6 kbit/s services, 12.0 kbit/s radio interface rate */ + lchan->tch_mode = GSM48_CMODE_DATA_12k0; + lchan->csd_mode = LCHAN_CSD_M_T_9600; + break; + case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_T_4k8): + /* 4.8 kbit/s services, 6.0 kbit/s radio interface rate */ + lchan->tch_mode = GSM48_CMODE_DATA_6k0; + lchan->csd_mode = LCHAN_CSD_M_T_4800; + break; + case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_T_2k4): + /* 2.4 kbit/s *and less* services, 3.6 kbit/s radio interface rate */ + lchan->tch_mode = GSM48_CMODE_DATA_3k6; + lchan->csd_mode = LCHAN_CSD_M_T_2400; + break; + case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_T_1k2): + /* 2.4 kbit/s *and less* services, 3.6 kbit/s radio interface rate */ + lchan->tch_mode = GSM48_CMODE_DATA_3k6; + lchan->csd_mode = LCHAN_CSD_M_T_1200; + break; + case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_T_600): + /* 2.4 kbit/s *and less* services, 3.6 kbit/s radio interface rate */ + lchan->tch_mode = GSM48_CMODE_DATA_3k6; + lchan->csd_mode = LCHAN_CSD_M_T_600; + break; + case RSL_CMODE(RSL_CMOD_SPD_DATA, RSL_CMOD_CSD_T_1200_75): + /* 2.4 kbit/s *and less* services, 3.6 kbit/s radio interface rate */ + lchan->tch_mode = GSM48_CMODE_DATA_3k6; + lchan->csd_mode = LCHAN_CSD_M_T_1200_75; + break; + + default: + LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "Channel Mode IE contains " + "an unknown/unhandled combination of " + "'Speech or data indicator' 0x%02x and " + "'Speech coding algorithm/data rate' 0x%02x\n", + cm->spd_ind, cm->chan_rate); + *cause = RSL_ERR_IE_CONTENT; + return -ENOPROTOOPT; + } + +#undef RSL_CMODE + + if (!bts_supports_cm(lchan->ts->trx->bts, cm)) { + LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "Channel type=0x%02x/mode=%s " + "is not supported by the PHY\n", cm->chan_rt, + gsm48_chan_mode_name(lchan->tch_mode)); + *cause = RSL_ERR_SERV_OPT_UNAVAIL; + return -ENOTSUP; + } + + return 0; +} + +/* Handle RSL Channel Identification IE (see section 9.3.5) */ +static int rsl_handle_chan_ident_ie(struct gsm_lchan *lchan, + const struct tlv_parsed *tp, + uint8_t *cause) +{ + const struct gsm_bts_trx_ts *ts = lchan->ts; + const struct gsm_bts *bts = ts->trx->bts; + const struct gsm48_chan_desc *cd; + + if (TLVP_PRES_LEN(tp, RSL_IE_CHAN_IDENT, sizeof(*cd) + 1)) { + /* Channel Description IE comes together with its IEI (see 9.3.5) */ + cd = (const struct gsm48_chan_desc *) (TLVP_VAL(tp, RSL_IE_CHAN_IDENT) + 1); + + /* The PHY may not support using different TSCs */ + if (!osmo_bts_has_feature(bts->features, BTS_FEAT_MULTI_TSC) + && cd->h0.tsc != BTS_TSC(bts)) { + LOGPLCHAN(lchan, DL1C, LOGL_ERROR, "This PHY does not support " + "lchan TSC %u != BSIC-TSC %u, sending NACK\n", + cd->h0.tsc, BTS_TSC(bts)); + *cause = RSL_ERR_SERV_OPT_UNIMPL; + return -ENOTSUP; + } + } + + return 0; +} + +/* Handle Osmocom specific TSC IE */ +static int rsl_handle_osmo_tsc_ie(struct gsm_lchan *lchan, + const struct tlv_parsed *tp, + uint8_t *cause) +{ + /* Osmocom specific IE indicating Training Sequence Code and Set */ + if (TLVP_PRES_LEN(tp, RSL_IE_OSMO_TRAINING_SEQUENCE, 2)) { + const uint8_t *ie = TLVP_VAL(tp, RSL_IE_OSMO_TRAINING_SEQUENCE); + lchan->ts->tsc_set = ie[0] & 0x03; /* Range: 0..3 */ + lchan->ts->tsc_rsl = ie[1] & 0x07; /* Range: 0..7 */ + lchan->ts->tsc_rsl_configured = true; + } else { + lchan->ts->tsc_rsl_configured = false; + lchan->ts->tsc_rsl = 0xff; + lchan->ts->tsc_set = 0; } + gsm_ts_apply_configured_tsc(lchan->ts); + + return 0; } @@ -150,6 +371,16 @@ static bool chan_nr_is_dchan(uint8_t chan_nr) return true; } +static struct gsm_bts_trx *trx_lookup_by_arfcn(struct llist_head *trx_list, uint16_t arfcn) +{ + struct gsm_bts_trx *trx; + llist_for_each_entry(trx, trx_list, list) { + if (trx->arfcn == arfcn) + return trx; + } + return NULL; +} + static struct gsm_lchan *lchan_lookup(struct gsm_bts_trx *trx, uint8_t chan_nr, const char *log_name) { @@ -269,6 +500,7 @@ static int rsl_tx_error_report(struct gsm_bts_trx *trx, uint8_t cause, const uin /* 8.6.1 sending RF RESOURCE INDICATION */ int rsl_tx_rf_res(struct gsm_bts_trx *trx) { + unsigned int tn, ln; struct msgb *nmsg; LOGP(DRSL, LOGL_INFO, "Tx RSL RF RESource INDication\n"); @@ -276,8 +508,48 @@ int rsl_tx_rf_res(struct gsm_bts_trx *trx) nmsg = rsl_msgb_alloc(sizeof(struct abis_rsl_common_hdr)); if (!nmsg) return -ENOMEM; - // FIXME: add interference levels of TRX - msgb_tlv_put(nmsg, RSL_IE_RESOURCE_INFO, 0, NULL); + + /* Add interference levels for each logical channel */ + uint8_t *len = msgb_tl_put(nmsg, RSL_IE_RESOURCE_INFO); + + for (tn = 0; tn < ARRAY_SIZE(trx->ts); tn++) { + const struct gsm_bts_trx_ts *ts = &trx->ts[tn]; + + if (ts->mo.nm_state.operational != NM_OPSTATE_ENABLED) + continue; + if (ts->mo.nm_state.availability != NM_AVSTATE_OK) + continue; + + for (ln = 0; ln < ARRAY_SIZE(ts->lchan); ln++) { + const struct gsm_lchan *lchan = &ts->lchan[ln]; + + /* No average interference value => no band */ + if (lchan->meas.interf_meas_avg_dbm == 0) + continue; + + /* Only for GSM_LCHAN_{SDCCH,TCH_F,TCH_H,PDTCH} */ + switch (lchan->type) { + case GSM_LCHAN_SDCCH: + case GSM_LCHAN_TCH_F: + case GSM_LCHAN_TCH_H: + /* We're not interested in active CS lchans */ + if (lchan->state == LCHAN_S_ACTIVE) + continue; + break; + case GSM_LCHAN_PDTCH: + break; + default: + continue; + } + + msgb_v_put(nmsg, gsm_lchan2chan_nr_rsl(lchan)); + msgb_v_put(nmsg, (lchan->meas.interf_band & 0x07) << 5); + } + } + + /* Calculate length of the V part */ + *len = msgb_l3len(nmsg) - 2; + rsl_trx_push_hdr(nmsg, RSL_MT_RF_RES_IND); nmsg->trx = trx; @@ -285,7 +557,7 @@ int rsl_tx_rf_res(struct gsm_bts_trx *trx) } /* - * common channel releated messages + * common channel related messages */ /* 8.5.1 BCCH INFOrmation is received */ @@ -298,7 +570,13 @@ static int rsl_rx_bcch_info(struct gsm_bts_trx *trx, struct msgb *msg) enum osmo_sysinfo_type osmo_si; struct gsm48_system_information_type_2quater *si2q; struct bitvec bv; - rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)); + const uint8_t *si_buf; + uint8_t prev_bs_ag_blks_res = 0xff; /* 0xff = unknown */ + + if (rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)) < 0) { + LOGPTRX(trx, DRSL, LOGL_ERROR, "%s(): rsl_tlv_parse() failed\n", __func__); + return rsl_tx_error_report(trx, RSL_ERR_PROTO, &cch->chan_nr, NULL, msg); + } /* 9.3.30 System Info Type */ if (!TLVP_PRESENT(&tp, RSL_IE_SYSINFO_TYPE)) @@ -325,7 +603,8 @@ static int rsl_rx_bcch_info(struct gsm_bts_trx *trx, struct msgb *msg) LOGP(DRSL, LOGL_INFO, " Rx RSL BCCH INFO (SI%s, %u bytes)\n", get_value_string(osmo_sitype_strs, osmo_si), len); - if (SYSINFO_TYPE_2quater == osmo_si) { + switch (osmo_si) { + case SYSINFO_TYPE_2quater: si2q = (struct gsm48_system_information_type_2quater *) TLVP_VAL(&tp, RSL_IE_FULL_BCCH_INFO); bv.data = si2q->rest_octets; bv.data_len = GSM_MACBLOCK_LEN; @@ -353,32 +632,68 @@ static int rsl_rx_bcch_info(struct gsm_bts_trx *trx, struct msgb *msg) memset(GSM_BTS_SI2Q(bts, bts->si2q_index), GSM_MACBLOCK_PADDING, sizeof(sysinfo_buf_t)); memcpy(GSM_BTS_SI2Q(bts, bts->si2q_index), TLVP_VAL(&tp, RSL_IE_FULL_BCCH_INFO), len); - } else { + break; + case SYSINFO_TYPE_3: + /* Keep previous BS_AG_BLKS_RES, used below */ + if (GSM_BTS_HAS_SI(bts, SYSINFO_TYPE_3)) { + const struct gsm48_system_information_type_3 *si3 = GSM_BTS_SI(bts, SYSINFO_TYPE_3); + prev_bs_ag_blks_res = si3->control_channel_desc.bs_ag_blks_res; + } + /* fall-through */ + default: memset(bts->si_buf[osmo_si], GSM_MACBLOCK_PADDING, sizeof(sysinfo_buf_t)); memcpy(bts->si_buf[osmo_si], TLVP_VAL(&tp, RSL_IE_FULL_BCCH_INFO), len); } bts->si_valid |= (1 << osmo_si); - if (SYSINFO_TYPE_3 == osmo_si) { - if (trx->nr == 0 && num_agch(trx, "RSL") != 1) { - lchan_deactivate(&trx->bts->c0->ts[0].lchan[CCCH_LCHAN]); - /* will be reactivated by sapi_deactivate_cb() */ + switch (osmo_si) { + case SYSINFO_TYPE_3: + /* If CCCH config on TS0 changed, reactivate the chan with the new config: */ + if (trx->nr == 0 && trx->bts->c0->ts[0].lchan[CCCH_LCHAN].state != LCHAN_S_NONE && + num_agch(trx, "RSL") != prev_bs_ag_blks_res) { trx->bts->c0->ts[0].lchan[CCCH_LCHAN].rel_act_kind = LCHAN_REL_ACT_REACT; + lchan_deactivate(&trx->bts->c0->ts[0].lchan[CCCH_LCHAN]); + /* will be reactivated by (see OS#1575): + * - bts-trx: lchan_deactivate() + * - sysmo,lc15,oc2g: lchan_deactivate()....[async]...sapi_deactivate_cb() */ } /* decode original SI3 Rest Octets as sent by BSC */ - const uint8_t *si3_ro_buf = (uint8_t *) GSM_BTS_SI(bts, osmo_si); - si3_ro_buf += offsetof(struct gsm48_system_information_type_3, rest_octets); - osmo_gsm48_rest_octets_si3_decode(&bts->si3_ro_decoded, si3_ro_buf); + si_buf = (const uint8_t *) GSM_BTS_SI(bts, osmo_si); + si_buf += offsetof(struct gsm48_system_information_type_3, rest_octets); + osmo_gsm48_rest_octets_si3_decode(&bts->si3_ro_decoded, si_buf); /* patch out GPRS indicator from binary if PCU is not connected; will be enabled * after PCU connects */ regenerate_si3_restoctets(bts); + pcu_tx_si(trx->bts, SYSINFO_TYPE_3, true); + break; + case SYSINFO_TYPE_4: + /* decode original SI4 Rest Octets as sent by BSC */ + si_buf = (const uint8_t *) GSM_BTS_SI(bts, osmo_si); + int si4_ro_offset = get_si4_ro_offset(si_buf); + if (si4_ro_offset > 0) { + osmo_gsm48_rest_octets_si4_decode(&bts->si4_ro_decoded, + si_buf + si4_ro_offset, + GSM_MACBLOCK_LEN - si4_ro_offset); + /* patch out GPRS indicator from binary if PCU is not connected; will be + * enabled after PCU connects */ + regenerate_si4_restoctets(bts); + } + break; + case SYSINFO_TYPE_1: + /* Get the position of the NCH, if enabled. */ + trx->bts->asci.pos_nch = pos_nch(trx, "BCCH INFO"); + pcu_tx_si(trx->bts, SYSINFO_TYPE_1, true); + break; + case SYSINFO_TYPE_2: + case SYSINFO_TYPE_13: + pcu_tx_si(trx->bts, osmo_si, true); + break; + default: + break; } - if (SYSINFO_TYPE_13 == osmo_si) - pcu_tx_si13(trx->bts, true); - } else if (TLVP_PRESENT(&tp, RSL_IE_L3_INFO)) { uint16_t len = TLVP_LEN(&tp, RSL_IE_L3_INFO); if (len > sizeof(sysinfo_buf_t)) @@ -393,10 +708,20 @@ static int rsl_rx_bcch_info(struct gsm_bts_trx *trx, struct msgb *msg) bts->si_valid &= ~(1 << osmo_si); LOGP(DRSL, LOGL_INFO, " RX RSL Disabling BCCH INFO (SI%s)\n", get_value_string(osmo_sitype_strs, osmo_si)); - if (SYSINFO_TYPE_13 == osmo_si) - pcu_tx_si13(trx->bts, false); - if (SYSINFO_TYPE_3 == osmo_si) + switch (osmo_si) { + case SYSINFO_TYPE_13: + pcu_tx_si(trx->bts, SYSINFO_TYPE_13, false); + break; + case SYSINFO_TYPE_3: memset(&bts->si3_ro_decoded, 0, sizeof(bts->si3_ro_decoded)); + pcu_tx_si(trx->bts, SYSINFO_TYPE_3, false); + break; + case SYSINFO_TYPE_1: + pcu_tx_si(trx->bts, SYSINFO_TYPE_1, false); + break; + default: + break; + } } osmo_signal_dispatch(SS_GLOBAL, S_NEW_SYSINFO, bts); @@ -465,7 +790,10 @@ static int rsl_rx_paging_cmd(struct gsm_bts_trx *trx, struct msgb *msg) const uint8_t *identity_lv; int rc; - rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)); + if (rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)) < 0) { + LOGPTRX(trx, DRSL, LOGL_ERROR, "%s(): rsl_tlv_parse() failed\n", __func__); + return rsl_tx_error_report(trx, RSL_ERR_PROTO, &cch->chan_nr, NULL, msg); + } if (!TLVP_PRESENT(&tp, RSL_IE_PAGING_GROUP) || !TLVP_PRESENT(&tp, RSL_IE_MS_IDENTITY)) @@ -500,7 +828,10 @@ static int rsl_rx_sms_bcast_cmd(struct gsm_bts_trx *trx, struct msgb *msg) bool extended_cbch = false; int rc; - rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)); + if (rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)) < 0) { + LOGPTRX(trx, DRSL, LOGL_ERROR, "%s(): rsl_tlv_parse() failed\n", __func__); + return rsl_tx_error_report(trx, RSL_ERR_PROTO, &cch->chan_nr, NULL, msg); + } if (!TLVP_PRESENT(&tp, RSL_IE_CB_CMD_TYPE) || !TLVP_PRESENT(&tp, RSL_IE_SMSCB_MSG)) @@ -522,6 +853,100 @@ static int rsl_rx_sms_bcast_cmd(struct gsm_bts_trx *trx, struct msgb *msg) return 0; } +/* Broadcast notification about new VGCS/VBS call on every dedicated channel. + * This is required for MSs that are currently in dedicated mode that there is an ongoing call and on which channel + * the call is active. Most MSs in dedicated mode may not be able to receive the NCH otherwise. + * MSs that do not support ASCI will ignore it, as it is an unsupported message for them. + */ +static int asci_broadcast_facch(struct gsm_bts *bts, const uint8_t *group_call_ref, const uint8_t *chan_desc, + uint8_t chan_desc_len, unsigned int count) +{ + uint8_t notif[23]; + struct msgb *msg, *cmsg; + struct gsm_bts_trx *trx; + struct gsm_lchan *lchan; + unsigned int tn, ln, n; + int rc; + + rc = bts_asci_notify_facch_gen_msg(bts, notif, group_call_ref, chan_desc, chan_desc_len); + if (rc < 0) + return rc; + + llist_for_each_entry(trx, &bts->trx_list, list) { + for (tn = 0; tn < ARRAY_SIZE(trx->ts); tn++) { + for (ln = 0; ln < ARRAY_SIZE(trx->ts[tn].lchan); ln++) { + lchan = &trx->ts[tn].lchan[ln]; + if (!lchan_is_dcch(lchan)) + continue; + if (lchan->state != LCHAN_S_ACTIVE) + continue; + msg = rsl_rll_simple(RSL_MT_UNIT_DATA_REQ, gsm_lchan2chan_nr(lchan), 0x00, 0); + msg->l3h = msg->tail; /* emulate rsl_rx_rll() behaviour */ + msgb_tl16v_put(msg, RSL_IE_L3_INFO, sizeof(notif), (uint8_t *) ¬if); + for (n = 1; n < count; n++) { + cmsg = msgb_copy(msg, "FACCH copy"); + lapdm_rslms_recvmsg(cmsg, &lchan->lapdm_ch); + } + lapdm_rslms_recvmsg(msg, &lchan->lapdm_ch); + } + } + } + + return 0; +} + +/* Number of times to broadcast ASCI call on every dedicated channel. */ +#define ASCI_BROADCAST_NUM 3 + +/* 8.5.10 NOTIFICATION COMMAND */ +static int rsl_rx_notification_cmd(struct gsm_bts_trx *trx, struct msgb *msg) +{ + struct abis_rsl_cchan_hdr *cch = msgb_l2(msg); + struct tlv_parsed tp; + uint8_t command_indicator; + int rc; + + if (rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)) < 0) { + LOGPTRX(trx, DRSL, LOGL_ERROR, "%s(): rsl_tlv_parse() failed\n", __func__); + return rsl_tx_error_report(trx, RSL_ERR_PROTO, &cch->chan_nr, NULL, msg); + } + + if (cch->chan_nr != RSL_CHAN_PCH_AGCH) { + LOGPTRX(trx, DRSL, LOGL_ERROR, "%s(): chan nr is not Downlink CCCH\n", __func__); + return rsl_tx_error_report(trx, RSL_ERR_IE_CONTENT, &cch->chan_nr, NULL, msg); + } + + if (!TLVP_PRES_LEN(&tp, RSL_IE_CMD_INDICATOR, 1)) + return rsl_tx_error_report(trx, RSL_ERR_MAND_IE_ERROR, &cch->chan_nr, NULL, msg); + command_indicator = *TLVP_VAL(&tp, RSL_IE_CMD_INDICATOR); + + switch (command_indicator) { + case RSL_CMD_INDICATOR_START: + /* we need at least a Group Call Reference to start notification */ + if (!TLVP_PRES_LEN(&tp, RSL_IE_GROUP_CALL_REF, 5)) + return rsl_tx_error_report(trx, RSL_ERR_OPT_IE_ERROR, &cch->chan_nr, NULL, msg); + rc = bts_asci_notification_add(trx->bts, TLVP_VAL(&tp, RSL_IE_GROUP_CALL_REF), + TLVP_VAL(&tp, RSL_IE_CHAN_DESC), TLVP_LEN(&tp, RSL_IE_CHAN_DESC), + (struct rsl_ie_nch_drx_info *) TLVP_VAL(&tp, RSL_IE_NCH_DRX_INFO)); + /* Broadcast to FACCH */ + asci_broadcast_facch(trx->bts, TLVP_VAL(&tp, RSL_IE_GROUP_CALL_REF), TLVP_VAL(&tp, RSL_IE_CHAN_DESC), + TLVP_LEN(&tp, RSL_IE_CHAN_DESC), ASCI_BROADCAST_NUM); + break; + case RSL_CMD_INDICATOR_STOP: + if (!TLVP_PRES_LEN(&tp, RSL_IE_GROUP_CALL_REF, 5)) { + /* interpret this as stopping of all notification */ + rc = bts_asci_notification_reset(trx->bts); + } else { + rc = bts_asci_notification_del(trx->bts, TLVP_VAL(&tp, RSL_IE_GROUP_CALL_REF)); + } + break; + default: + return rsl_tx_error_report(trx, RSL_ERR_IE_CONTENT, &cch->chan_nr, NULL, msg); + } + + return rc; +} + /* OSMO_ETWS_CMD - proprietary extension as TS 48.058 has no standardized way to do this :( */ static int rsl_rx_osmo_etws_cmd(struct gsm_bts_trx *trx, struct msgb *msg) { @@ -529,7 +954,10 @@ static int rsl_rx_osmo_etws_cmd(struct gsm_bts_trx *trx, struct msgb *msg) struct gsm_bts *bts = trx->bts; struct tlv_parsed tp; - rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)); + if (rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)) < 0) { + LOGPTRX(trx, DRSL, LOGL_ERROR, "%s(): rsl_tlv_parse() failed\n", __func__); + return rsl_tx_error_report(trx, RSL_ERR_PROTO, &cch->chan_nr, NULL, msg); + } if (!TLVP_PRESENT(&tp, RSL_IE_SMSCB_MSG)) return rsl_tx_error_report(trx, RSL_ERR_MAND_IE_ERROR, &cch->chan_nr, NULL, msg); @@ -571,10 +999,19 @@ static int rsl_rx_osmo_etws_cmd(struct gsm_bts_trx *trx, struct msgb *msg) * \param[out] buf Output buffer, must be caller-allocated and hold at least len + 2 or sizeof(sysinfo_buf_t) bytes * \param[out] valid pointer to bit-mask of 'valid' System information types * \param[in] current input data (L3 without L2/L1 header) - * \param[in] osmo_si Sytstem Infrormation Type (SYSINFO_TYPE_*) + * \param[in] osmo_si Sytstem Information Type (SYSINFO_TYPE_*) * \param[in] len length of \a current in octets */ static inline void lapdm_ui_prefix(uint8_t *buf, uint32_t *valid, const uint8_t *current, uint8_t osmo_si, uint16_t len) { + /* Special case for short header SI. Do not pre-fix the two-byte UI header. */ + switch (osmo_si) { + case SYSINFO_TYPE_10: + (*valid) |= (1 << osmo_si); + memset(buf, GSM_MACBLOCK_PADDING, sizeof(sysinfo_buf_t)); + memcpy(buf, current, len); + return; + } + /* We have to pre-fix with the two-byte LAPDM UI header */ if (len > sizeof(sysinfo_buf_t) - 2) { LOGP(DRSL, LOGL_ERROR, "Truncating received SI%s (%u -> %zu) to prepend LAPDM UI header (2 bytes)\n", @@ -593,7 +1030,7 @@ static inline void lapdm_ui_prefix(uint8_t *buf, uint32_t *valid, const uint8_t /*! Prefix a given SACCH frame with a L2/LAPDm UI header and store it in given BTS SACCH buffer * \param[out] bts BTS in whose System Information State we shall store * \param[in] current input data (L3 without L2/L1 header) - * \param[in] osmo_si Sytstem Infrormation Type (SYSINFO_TYPE_*) + * \param[in] osmo_si Sytstem Information Type (SYSINFO_TYPE_*) * \param[in] len length of \a current in octets */ static inline void lapdm_ui_prefix_bts(struct gsm_bts *bts, const uint8_t *current, uint8_t osmo_si, uint16_t len) { @@ -603,7 +1040,7 @@ static inline void lapdm_ui_prefix_bts(struct gsm_bts *bts, const uint8_t *curre /*! Prefix a given SACCH frame with a L2/LAPDm UI header and store it in given lchan SACCH buffer * \param[out] lchan Logical Channel in whose System Information State we shall store * \param[in] current input data (L3 without L2/L1 header) - * \param[in] osmo_si Sytstem Infrormation Type (SYSINFO_TYPE_*) + * \param[in] osmo_si Sytstem Information Type (SYSINFO_TYPE_*) * \param[in] len length of \a current in octets */ static inline void lapdm_ui_prefix_lchan(struct gsm_lchan *lchan, const uint8_t *current, uint8_t osmo_si, uint16_t len) { @@ -618,7 +1055,10 @@ static int rsl_rx_sacch_fill(struct gsm_bts_trx *trx, struct msgb *msg) uint8_t rsl_si; enum osmo_sysinfo_type osmo_si; - rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)); + if (rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)) < 0) { + LOGPTRX(trx, DRSL, LOGL_ERROR, "%s(): rsl_tlv_parse() failed\n", __func__); + return rsl_tx_error_report(trx, RSL_ERR_PROTO, NULL, NULL, msg); + } /* 9.3.30 System Info Type */ if (!TLVP_PRESENT(&tp, RSL_IE_SYSINFO_TYPE)) @@ -682,13 +1122,256 @@ static int rsl_rx_sacch_fill(struct gsm_bts_trx *trx, struct msgb *msg) } +/* Parser for ip.access specific MS/BS Power parameters */ +static int parse_power_ctrl_params(struct gsm_power_ctrl_params *params, + const uint8_t *data, size_t data_len) +{ + const struct tlv_p_entry *ie; + struct tlv_parsed tp[3]; + unsigned int i; + int rc; + + /* There can be multiple RSL_IPAC_EIE_MEAS_AVG_CFG, so we use tlv_parse2() */ + rc = tlv_parse2(&tp[0], ARRAY_SIZE(tp), &rsl_ipac_eie_tlvdef, + data, data_len, 0, 0); + if (rc < 0) + return rc; + + /* Either of RSL_IPAC_EIE_{BS,MS}_PWR_CTL must be present */ + if (TLVP_PRESENT(&tp[0], RSL_IPAC_EIE_BS_PWR_CTL) && + TLVP_PRESENT(&tp[0], RSL_IPAC_EIE_MS_PWR_CTL)) + return -EINVAL; + + /* (TV) Thresholds: {L,U}_RXLEV_XX_P and {L,U}_RXQUAL_XX_P */ + if ((ie = TLVP_GET(&tp[0], RSL_IPAC_EIE_BS_PWR_CTL)) != NULL || + (ie = TLVP_GET(&tp[0], RSL_IPAC_EIE_MS_PWR_CTL)) != NULL) { + const struct ipac_preproc_pc_thresh *thresh; + + thresh = (const struct ipac_preproc_pc_thresh *) ie->val; + + params->rxlev_meas.lower_thresh = thresh->l_rxlev; + params->rxlev_meas.upper_thresh = thresh->u_rxlev; + + params->rxqual_meas.lower_thresh = thresh->l_rxqual; + params->rxqual_meas.upper_thresh = thresh->u_rxqual; + } + + /* Osmocom extension, C/I related thresholds: */ + if (TLVP_PRES_LEN(&tp[0], RSL_IPAC_EIE_OSMO_MS_PWR_CTL, sizeof(struct osmo_preproc_pc_thresh))) { + const struct osmo_preproc_pc_thresh *osmo_thresh; + ie = TLVP_GET(&tp[0], RSL_IPAC_EIE_OSMO_MS_PWR_CTL); + osmo_thresh = (const struct osmo_preproc_pc_thresh *) ie->val; + params->ci_fr_meas.lower_thresh = osmo_thresh->l_ci_fr; + params->ci_fr_meas.upper_thresh = osmo_thresh->u_ci_fr; + + params->ci_hr_meas.lower_thresh = osmo_thresh->l_ci_hr; + params->ci_hr_meas.upper_thresh = osmo_thresh->u_ci_hr; + + params->ci_amr_fr_meas.lower_thresh = osmo_thresh->l_ci_amr_fr; + params->ci_amr_fr_meas.upper_thresh = osmo_thresh->u_ci_amr_fr; + + params->ci_amr_hr_meas.lower_thresh = osmo_thresh->l_ci_amr_hr; + params->ci_amr_hr_meas.upper_thresh = osmo_thresh->u_ci_amr_hr; + + params->ci_sdcch_meas.lower_thresh = osmo_thresh->l_ci_sdcch; + params->ci_sdcch_meas.upper_thresh = osmo_thresh->u_ci_sdcch; + + params->ci_gprs_meas.lower_thresh = osmo_thresh->l_ci_gprs; + params->ci_gprs_meas.upper_thresh = osmo_thresh->u_ci_gprs; + } + + /* (TV) PC Threshold Comparators */ + if ((ie = TLVP_GET(&tp[0], RSL_IPAC_EIE_PC_THRESH_COMP)) != NULL) { + const struct ipac_preproc_pc_comp *thresh_comp; + + thresh_comp = (const struct ipac_preproc_pc_comp *) ie->val; + + /* RxLev: P1, N1, P2, N2 (see 3GPP TS 45.008, A.3.2.1, a & b) */ + params->rxlev_meas.lower_cmp_p = thresh_comp->p1; + params->rxlev_meas.lower_cmp_n = thresh_comp->n1; + params->rxlev_meas.upper_cmp_p = thresh_comp->p2; + params->rxlev_meas.upper_cmp_n = thresh_comp->n2; + + /* RxQual: P3, N3, P4, N4 (see 3GPP TS 45.008, A.3.2.1, c & d) */ + params->rxqual_meas.lower_cmp_p = thresh_comp->p3; + params->rxqual_meas.lower_cmp_n = thresh_comp->n3; + params->rxqual_meas.upper_cmp_p = thresh_comp->p4; + params->rxqual_meas.upper_cmp_n = thresh_comp->n4; + + /* Minimum interval between power level changes (P_Con_INTERVAL) */ + params->ctrl_interval = thresh_comp->pc_interval; + + /* Power increase / reduce step size: POWER_{INC,RED}_STEP_SIZE */ + params->inc_step_size_db = thresh_comp->inc_step_size; + params->red_step_size_db = thresh_comp->red_step_size; + } + + /* Osmocom extension, C/I related thresholds: */ + if (TLVP_PRES_LEN(&tp[0], RSL_IPAC_EIE_OSMO_PC_THRESH_COMP, sizeof(struct osmo_preproc_pc_thresh))) { + const struct osmo_preproc_pc_comp *osmo_thresh_comp; + ie = TLVP_GET(&tp[0], RSL_IPAC_EIE_OSMO_PC_THRESH_COMP); + osmo_thresh_comp = (const struct osmo_preproc_pc_comp *) ie->val; + #define SET_PREPROC_PC(PARAMS, FROM, TYPE) \ + (PARAMS)->TYPE##_meas.lower_cmp_p = (FROM)->TYPE.lower_p; \ + (PARAMS)->TYPE##_meas.lower_cmp_n = (FROM)->TYPE.lower_n; \ + (PARAMS)->TYPE##_meas.upper_cmp_p = (FROM)->TYPE.upper_p; \ + (PARAMS)->TYPE##_meas.upper_cmp_n = (FROM)->TYPE.upper_n + SET_PREPROC_PC(params, osmo_thresh_comp, ci_fr); + SET_PREPROC_PC(params, osmo_thresh_comp, ci_hr); + SET_PREPROC_PC(params, osmo_thresh_comp, ci_amr_fr); + SET_PREPROC_PC(params, osmo_thresh_comp, ci_amr_hr); + SET_PREPROC_PC(params, osmo_thresh_comp, ci_sdcch); + SET_PREPROC_PC(params, osmo_thresh_comp, ci_gprs); + #undef SET_PREPROC_PC + } + + /* (TLV) Measurement Averaging parameters for RxLev/RxQual */ + for (i = 0; i < ARRAY_SIZE(tp); i++) { + const struct ipac_preproc_ave_cfg *ave_cfg; + struct gsm_power_ctrl_meas_params *mp; + + ie = TLVP_GET(&tp[i], RSL_IPAC_EIE_MEAS_AVG_CFG); + if (ie == NULL) + break; + + if (ie->len < sizeof(*ave_cfg)) + return -EINVAL; + + ave_cfg = (const struct ipac_preproc_ave_cfg *) ie->val; + + switch (ave_cfg->param_id) { + case IPAC_RXQUAL_AVE: + mp = ¶ms->rxqual_meas; + break; + case IPAC_RXLEV_AVE: + mp = ¶ms->rxlev_meas; + break; + default: + /* Skip unknown parameters */ + continue; + } + + mp->h_reqave = ave_cfg->h_reqave; + mp->h_reqt = ave_cfg->h_reqt; + + mp->algo = ave_cfg->ave_method + 1; + switch (ave_cfg->ave_method) { + case IPAC_OSMO_EWMA_AVE: + if (ie->len > sizeof(*ave_cfg)) + mp->ewma.alpha = ave_cfg->params[0]; + break; + + /* FIXME: not implemented */ + case IPAC_UNWEIGHTED_AVE: + case IPAC_WEIGHTED_AVE: + case IPAC_MEDIAN_AVE: + break; + } + } + + /* (TLV) Measurement Averaging parameters for C/I (Osmocom extension)*/ + if (TLVP_PRES_LEN(&tp[0], RSL_IPAC_EIE_OSMO_MEAS_AVG_CFG, sizeof(struct osmo_preproc_ave_cfg))) { + ie = TLVP_GET(&tp[0], RSL_IPAC_EIE_OSMO_MEAS_AVG_CFG); + const struct osmo_preproc_ave_cfg *cfg = (const struct osmo_preproc_ave_cfg *) ie->val; + unsigned params_offset = 0; + #define SET_AVE_CFG(PARAMS, FROM, TYPE, PARAM_OFFSET) do {\ + if ((FROM)->TYPE.ave_enabled) { \ + (PARAMS)->TYPE##_meas.h_reqave = (FROM)->TYPE.h_reqave; \ + (PARAMS)->TYPE##_meas.h_reqt = (FROM)->TYPE.h_reqt; \ + (PARAMS)->TYPE##_meas.algo = (FROM)->TYPE.ave_method + 1; \ + switch ((FROM)->TYPE.ave_method) { \ + case IPAC_OSMO_EWMA_AVE: \ + if (ie->len > sizeof(*cfg) + (PARAM_OFFSET)) { \ + (PARAMS)->TYPE##_meas.ewma.alpha = (FROM)->params[PARAM_OFFSET]; \ + (PARAM_OFFSET)++; \ + } \ + break; \ + /* FIXME: not implemented */ \ + case IPAC_UNWEIGHTED_AVE: \ + case IPAC_WEIGHTED_AVE: \ + case IPAC_MEDIAN_AVE: \ + break; \ + } \ + } else { \ + (PARAMS)->TYPE##_meas.algo = GSM_PWR_CTRL_MEAS_AVG_ALGO_NONE; \ + } \ + } while(0) + SET_AVE_CFG(params, cfg, ci_fr, params_offset); + SET_AVE_CFG(params, cfg, ci_hr, params_offset); + SET_AVE_CFG(params, cfg, ci_amr_fr, params_offset); + SET_AVE_CFG(params, cfg, ci_amr_hr, params_offset); + SET_AVE_CFG(params, cfg, ci_sdcch, params_offset); + SET_AVE_CFG(params, cfg, ci_gprs, params_offset); + #undef SET_AVE_CFG + } + + return 0; +} + +/* ip.access specific Measurement Pre-processing Defaults for MS/BS Power control */ +static int rsl_rx_meas_preproc_dft(struct gsm_bts_trx *trx, struct msgb *msg) +{ + const struct gsm_bts *bts = trx->bts; + struct gsm_power_ctrl_params *params; + const struct tlv_p_entry *ie; + struct tlv_parsed tp; + + LOGPTRX(trx, DRSL, LOGL_INFO, "Rx Measurement Pre-processing Defaults\n"); + + if (rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)) < 0) { + LOGPTRX(trx, DRSL, LOGL_ERROR, "%s(): rsl_tlv_parse() failed\n", __func__); + return rsl_tx_error_report(trx, RSL_ERR_PROTO, NULL, NULL, msg); + } + + /* TLV (O) BS Power Parameters IE */ + if ((ie = TLVP_GET(&tp, RSL_IE_BS_POWER_PARAM)) != NULL) { + /* Allocate a new chunk and initialize with default values */ + params = talloc(trx, struct gsm_power_ctrl_params); + power_ctrl_params_def_reset(params, true); + + if (ie->len && parse_power_ctrl_params(params, ie->val, ie->len) == 0) { + /* Initially it points to the global defaults */ + if (trx->bs_dpc_params != &bts->bs_dpc_params) + talloc_free(trx->bs_dpc_params); + trx->bs_dpc_params = params; + } else { + LOGPTRX(trx, DRSL, LOGL_ERROR, "Failed to parse BS Power Parameters IE\n"); + rsl_tx_error_report(trx, RSL_ERR_IE_CONTENT, NULL, NULL, msg); + talloc_free(params); + } + } + + /* TLV (O) MS Power Parameters IE */ + if ((ie = TLVP_GET(&tp, RSL_IE_MS_POWER_PARAM)) != NULL) { + /* Allocate a new chunk and initialize with default values */ + params = talloc(trx, struct gsm_power_ctrl_params); + power_ctrl_params_def_reset(params, false); + + if (ie->len && parse_power_ctrl_params(params, ie->val, ie->len) == 0) { + /* Initially it points to the global defaults */ + if (trx->ms_dpc_params != &bts->ms_dpc_params) + talloc_free(trx->ms_dpc_params); + trx->ms_dpc_params = params; + } else { + LOGPTRX(trx, DRSL, LOGL_ERROR, "Failed to parse MS Power Parameters IE\n"); + rsl_tx_error_report(trx, RSL_ERR_IE_CONTENT, NULL, NULL, msg); + talloc_free(params); + } + } + + return 0; +} + /* 8.5.6 IMMEDIATE ASSIGN COMMAND is received */ static int rsl_rx_imm_ass(struct gsm_bts_trx *trx, struct msgb *msg) { struct abis_rsl_cchan_hdr *cch = msgb_l2(msg); struct tlv_parsed tp; - rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)); + if (rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)) < 0) { + LOGPTRX(trx, DRSL, LOGL_ERROR, "%s(): rsl_tlv_parse() failed\n", __func__); + return rsl_tx_error_report(trx, RSL_ERR_PROTO, &cch->chan_nr, NULL, msg); + } if (!TLVP_PRESENT(&tp, RSL_IE_FULL_IMM_ASS_INFO)) return rsl_tx_error_report(trx, RSL_ERR_MAND_IE_ERROR, &cch->chan_nr, NULL, msg); @@ -701,6 +1384,45 @@ static int rsl_rx_imm_ass(struct gsm_bts_trx *trx, struct msgb *msg) msg->l2h = NULL; msg->len = TLVP_LEN(&tp, RSL_IE_FULL_IMM_ASS_INFO); + /* Early Immediate Assignment: when there is a lot of latency on Abis, the Abis roundtrip of Chan Activ -> Chan + * Activ ACK -> Immediate Assignment may take so long that each MS sends a second RACH for Chan Rqd, reserving + * two SDCCH for each request but using only one. To help with that, the Early IA feature in osmo-bsc sends the + * Immediate Assignment without waiting for the Channel Activation ACK. This may then be too early, and the MS + * may not be able to establish a channel. So to help with Early IA, look up whether the target lchan is already + * active. If not, then hold back the RR Immediate Assignment message, and send it once L1 has confirmed that + * the channel is active. Hence we still wait for the activation, but don't need the Abis roundtrip of Activ ACK + * -> Immediate Assignment via the BSC. + * If anything is wrong with the sizes or the lchan lookup, behave normally, i.e. do not do the RR IA caching, + * but just send the RR message to the MS as-is. + * 'trx' here is the TRX of the BCCH channel. To find the correct TRX for the IMM ASS target, we need to look up + * the ARFCN that is contained in the IMM ASS message. When frequency hopping is enabled, there will not be an + * ARFCN, so we cannot support early-IA with frequency hopping enabled. */ + if (msg->len >= sizeof(struct gsm48_imm_ass)) { + struct gsm48_imm_ass *rr_ia = (void*)msg->data; + if (rr_ia->chan_desc.h0.h == 0) { + /* hopping is disabled. */ + struct gsm_bts_trx *ia_target_trx; + uint16_t arfcn; + arfcn = (rr_ia->chan_desc.h0.arfcn_high << 8) + rr_ia->chan_desc.h0.arfcn_low; + + ia_target_trx = trx_lookup_by_arfcn(&trx->bts->trx_list, arfcn); + if (ia_target_trx) { + /* found the ARFCN's trx */ + struct gsm_lchan *ia_target_lchan; + ia_target_lchan = lchan_lookup(ia_target_trx, rr_ia->chan_desc.chan_nr, "Early IA check: "); + if (ia_target_lchan && ia_target_lchan->state != LCHAN_S_ACTIVE) { + /* Target lchan is not yet active. Cache the IA. + * If a previous IA is still lingering, free it. */ + msgb_free(ia_target_lchan->early_rr_ia); + ia_target_lchan->early_rr_ia = msg; + + /* return 1 means: don't msgb_free() the msg */ + return 1; + } + } + } + } + /* put into the AGCH queue of the BTS */ if (bts_agch_enqueue(trx->bts, msg) < 0) { /* if there is no space in the queue: send DELETE IND */ @@ -738,7 +1460,7 @@ static int tx_rf_rel_ack(struct gsm_lchan *lchan, uint8_t chan_nr) /* 8.4.19 sending RF CHANnel RELease ACKnowledge */ int rsl_tx_rf_rel_ack(struct gsm_lchan *lchan) { - uint8_t chan_nr = gsm_lchan2chan_nr(lchan); + uint8_t chan_nr = gsm_lchan2chan_nr_rsl(lchan); bool send_rel_ack; switch (lchan->rel_act_kind) { @@ -748,26 +1470,40 @@ int rsl_tx_rf_rel_ack(struct gsm_lchan *lchan) case LCHAN_REL_ACT_PCU: switch (lchan->ts->pchan) { - case GSM_PCHAN_TCH_F_TCH_H_PDCH: + case GSM_PCHAN_OSMO_DYN: if (lchan->ts->dyn.pchan_is != GSM_PCHAN_PDCH) { LOGP(DRSL, LOGL_ERROR, "%s (ss=%d) PDCH release: not in PDCH mode\n", gsm_ts_and_pchan_name(lchan->ts), lchan->nr); /* well, what to do about it ... carry on and hope it's fine. */ } - /* remember the fact that the TS is now released */ - lchan->ts->dyn.pchan_is = GSM_PCHAN_NONE; - /* Continue to ack the release below. (This is a non-standard rel ack invented - * specifically for GSM_PCHAN_TCH_F_TCH_H_PDCH). */ - send_rel_ack = true; + if (lchan->ts->dyn.pchan_want != GSM_PCHAN_PDCH) { + /* Continue to ack the release below. (This is a non-standard rel ack invented + * specifically for GSM_PCHAN_OSMO_DYN). */ + /* remember the fact that the TS is now released */ + lchan->ts->dyn.pchan_is = GSM_PCHAN_NONE; + send_rel_ack = true; + } else { + /* Administrteively locked TRX, no need to + inform BSC. Keep pchan_is for when we are + unlocked again, since lower layers are stil + lconfigured for PDCH but we simply annonced + non-availability to PCU */ + send_rel_ack = false; + } break; case GSM_PCHAN_TCH_F_PDCH: /* GSM_PCHAN_TCH_F_PDCH, does not require a rel ack. The caller * l1sap_info_rel_cnf() will continue with bts_model_ts_disconnect(). */ send_rel_ack = false; break; + case GSM_PCHAN_PDCH: + /* Release was instructed by the BTS, for instance because the TRX is + * administrateively Locked */ + send_rel_ack = false; + break; default: LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "PCU rel ack for unexpected lchan kind %s\n", - gsm_pchan_name(lchan->rel_act_kind)); + gsm_pchan_name(lchan->ts->pchan)); /* Release certainly was not requested by the BSC via RSL, so don't ack. */ send_rel_ack = false; break; @@ -782,20 +1518,14 @@ int rsl_tx_rf_rel_ack(struct gsm_lchan *lchan) } if (!send_rel_ack) { - LOGPLCHAN(lchan, DRSL, LOGL_NOTICE, "%s not sending REL ACK\n", gsm_lchan_name(lchan)); + LOGPLCHAN(lchan, DRSL, LOGL_INFO, "not sending REL ACK\n"); return 0; } - LOGP(DRSL, LOGL_NOTICE, "%s (ss=%d) %s Tx CHAN REL ACK\n", + LOGP(DRSL, LOGL_INFO, "%s (ss=%d) %s Tx CHAN REL ACK\n", gsm_ts_and_pchan_name(lchan->ts), lchan->nr, gsm_lchant_name(lchan->type)); - /* - * Free the LAPDm resources now that the BTS - * has released all the resources. - */ - lapdm_channel_exit(&lchan->lapdm_ch); - return tx_rf_rel_ack(lchan, chan_nr); } @@ -804,10 +1534,10 @@ static int rsl_tx_chan_act_ack(struct gsm_lchan *lchan) { struct gsm_time *gtime = get_time(lchan->ts->trx->bts); struct msgb *msg; - uint8_t chan_nr = gsm_lchan2chan_nr(lchan); + uint8_t chan_nr = gsm_lchan2chan_nr_rsl(lchan); uint8_t ie[2]; - LOGP(DRSL, LOGL_NOTICE, "%s (ss=%d) %s Tx CHAN ACT ACK\n", + LOGP(DRSL, LOGL_INFO, "%s (ss=%d) %s Tx CHAN ACT ACK\n", gsm_ts_and_pchan_name(lchan->ts), lchan->nr, gsm_lchant_name(lchan->type)); @@ -826,28 +1556,50 @@ static int rsl_tx_chan_act_ack(struct gsm_lchan *lchan) return abis_bts_rsl_sendmsg(msg); } -/* 8.4.7 sending HANDOver DETection */ -int rsl_tx_hando_det(struct gsm_lchan *lchan, uint8_t *ho_delay) +/* common helper function for *_DETECT */ +static int _rsl_tx_detect(struct gsm_lchan *lchan, uint8_t msg_type, uint8_t *acc_delay) { struct msgb *msg; - uint8_t chan_nr = gsm_lchan2chan_nr(lchan); - - LOGPLCHAN(lchan, DRSL, LOGL_INFO, "Sending HANDOver DETect\n"); + uint8_t chan_nr = gsm_lchan2chan_nr_rsl(lchan); msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr)); if (!msg) return -ENOMEM; /* 9.3.17 Access Delay */ - if (ho_delay) - msgb_tv_put(msg, RSL_IE_ACCESS_DELAY, *ho_delay); + if (acc_delay) + msgb_tv_put(msg, RSL_IE_ACCESS_DELAY, *acc_delay); - rsl_dch_push_hdr(msg, RSL_MT_HANDO_DET, chan_nr); + rsl_dch_push_hdr(msg, msg_type, chan_nr); msg->trx = lchan->ts->trx; return abis_bts_rsl_sendmsg(msg); } +/* 8.4.7 sending HANDOver DETection */ +int rsl_tx_hando_det(struct gsm_lchan *lchan, uint8_t *ho_delay) +{ + LOGPLCHAN(lchan, DRSL, LOGL_INFO, "Sending HANDOver DETect\n"); + + return _rsl_tx_detect(lchan, RSL_MT_HANDO_DET, ho_delay); +} + +/* 8.4.22 sending LISTENER DETection */ +int rsl_tx_listener_det(struct gsm_lchan *lchan, uint8_t *acc_delay) +{ + LOGPLCHAN(lchan, DRSL, LOGL_INFO, "Sending LISTENER DETect\n"); + + return _rsl_tx_detect(lchan, RSL_MT_LISTENER_DET, acc_delay); +} + +/* 8.4.21 sending TALKER DETection */ +int rsl_tx_talker_det(struct gsm_lchan *lchan, uint8_t *acc_delay) +{ + LOGPLCHAN(lchan, DRSL, LOGL_INFO, "Sending TALKER DETect\n"); + + return _rsl_tx_detect(lchan, RSL_MT_TALKER_DET, acc_delay); +} + /* 8.4.3 sending CHANnel ACTIVation Negative ACK */ static int _rsl_tx_chan_act_nack(struct gsm_bts_trx *trx, uint8_t chan_nr, uint8_t cause, struct gsm_lchan *lchan) @@ -855,7 +1607,7 @@ static int _rsl_tx_chan_act_nack(struct gsm_bts_trx *trx, uint8_t chan_nr, uint8 struct msgb *msg; if (lchan) - LOGP(DRSL, LOGL_NOTICE, "%s: ", gsm_lchan_name(lchan)); + LOGPLCHAN(lchan, DRSL, LOGL_NOTICE, ""); else LOGP(DRSL, LOGL_NOTICE, "0x%02x: ", chan_nr); LOGPC(DRSL, LOGL_NOTICE, "Sending Channel Activated NACK: cause = 0x%02x\n", cause); @@ -872,14 +1624,14 @@ static int _rsl_tx_chan_act_nack(struct gsm_bts_trx *trx, uint8_t chan_nr, uint8 return abis_bts_rsl_sendmsg(msg); } static int rsl_tx_chan_act_nack(struct gsm_lchan *lchan, uint8_t cause) { - return _rsl_tx_chan_act_nack(lchan->ts->trx, gsm_lchan2chan_nr(lchan), cause, lchan); + return _rsl_tx_chan_act_nack(lchan->ts->trx, gsm_lchan2chan_nr_rsl(lchan), cause, lchan); } /* Send an RSL Channel Activation Ack if cause is zero, a Nack otherwise. */ int rsl_tx_chan_act_acknack(struct gsm_lchan *lchan, uint8_t cause) { if (lchan->rel_act_kind != LCHAN_REL_ACT_RSL) { - LOGPLCHAN(lchan, DRSL, LOGL_NOTICE, "not sending CHAN ACT %s\n", + LOGPLCHAN(lchan, DRSL, LOGL_INFO, "not sending CHAN ACT %s\n", cause ? "NACK" : "ACK"); return 0; } @@ -890,10 +1642,10 @@ int rsl_tx_chan_act_acknack(struct gsm_lchan *lchan, uint8_t cause) } /* 8.4.4 sending CONNection FAILure */ -int rsl_tx_conn_fail(struct gsm_lchan *lchan, uint8_t cause) +int rsl_tx_conn_fail(const struct gsm_lchan *lchan, uint8_t cause) { struct msgb *msg; - uint8_t chan_nr = gsm_lchan2chan_nr(lchan); + uint8_t chan_nr = gsm_lchan2chan_nr_rsl(lchan); LOGPLCHAN(lchan, DRSL, LOGL_NOTICE, "Sending Connection Failure: cause = 0x%02x\n", cause); @@ -996,17 +1748,13 @@ static void clear_lchan_for_pdch_activ(struct gsm_lchan *lchan) { /* These values don't apply to PDCH, just clear them. Particularly the encryption must be * cleared, or we would enable encryption on PDCH with parameters remaining from the TCH. */ - lchan->ms_power = ms_pwr_ctl_lvl(lchan->ts->trx->bts->band, 0); - lchan->ms_power_ctrl.current = lchan->ms_power; - lchan->ms_power_ctrl.fixed = 0; lchan->rsl_cmode = 0; lchan->tch_mode = 0; memset(&lchan->encr, 0, sizeof(lchan->encr)); memset(&lchan->ho, 0, sizeof(lchan->ho)); - lchan->bs_power = 0; - lchan->ms_power = 0; memset(&lchan->ms_power_ctrl, 0, sizeof(lchan->ms_power_ctrl)); - lchan->rqd_ta = 0; + memset(&lchan->bs_power_ctrl, 0, sizeof(lchan->bs_power_ctrl)); + lchan->ta_ctrl.current = 0; copy_sacch_si_to_lchan(lchan); memset(&lchan->tch, 0, sizeof(lchan->tch)); } @@ -1015,13 +1763,14 @@ static void clear_lchan_for_pdch_activ(struct gsm_lchan *lchan) * Store the CHAN_ACTIV msg, connect the L1 timeslot in the proper type and * then invoke rsl_rx_chan_activ() with msg. */ -static int dyn_ts_l1_reconnect(struct gsm_bts_trx_ts *ts, struct msgb *msg) +static int dyn_ts_l1_reconnect(struct gsm_bts_trx_ts *ts) { DEBUGP(DRSL, "%s dyn_ts_l1_reconnect\n", gsm_ts_and_pchan_name(ts)); switch (ts->dyn.pchan_want) { case GSM_PCHAN_TCH_F: case GSM_PCHAN_TCH_H: + case GSM_PCHAN_SDCCH8_SACCH8C: break; case GSM_PCHAN_PDCH: /* Only the first lchan matters for PDCH */ @@ -1035,9 +1784,6 @@ static int dyn_ts_l1_reconnect(struct gsm_bts_trx_ts *ts, struct msgb *msg) return -EINVAL; } - /* We will feed this back to rsl_rx_chan_activ() later */ - ts->dyn.pending_chan_activ = msg; - /* Disconnect, continue connecting from cb_ts_disconnected(). */ DEBUGP(DRSL, "%s Disconnect\n", gsm_ts_and_pchan_name(ts)); return bts_model_ts_disconnect(ts); @@ -1045,14 +1791,23 @@ static int dyn_ts_l1_reconnect(struct gsm_bts_trx_ts *ts, struct msgb *msg) static enum gsm_phys_chan_config dyn_pchan_from_chan_nr(uint8_t chan_nr) { - uint8_t cbits = chan_nr & RSL_CHAN_NR_MASK; + uint8_t cbits = chan_nr >> 3; switch (cbits) { - case RSL_CHAN_Bm_ACCHs: + case ABIS_RSL_CHAN_NR_CBITS_Bm_ACCHs: return GSM_PCHAN_TCH_F; - case RSL_CHAN_Lm_ACCHs: - case (RSL_CHAN_Lm_ACCHs + RSL_CHAN_NR_1): + case ABIS_RSL_CHAN_NR_CBITS_Lm_ACCHs(0): + case ABIS_RSL_CHAN_NR_CBITS_Lm_ACCHs(1): return GSM_PCHAN_TCH_H; - case RSL_CHAN_OSMO_PDCH: + case ABIS_RSL_CHAN_NR_CBITS_SDCCH8_ACCH(0): + case ABIS_RSL_CHAN_NR_CBITS_SDCCH8_ACCH(1): + case ABIS_RSL_CHAN_NR_CBITS_SDCCH8_ACCH(2): + case ABIS_RSL_CHAN_NR_CBITS_SDCCH8_ACCH(3): + case ABIS_RSL_CHAN_NR_CBITS_SDCCH8_ACCH(4): + case ABIS_RSL_CHAN_NR_CBITS_SDCCH8_ACCH(5): + case ABIS_RSL_CHAN_NR_CBITS_SDCCH8_ACCH(6): + case ABIS_RSL_CHAN_NR_CBITS_SDCCH8_ACCH(7): + return GSM_PCHAN_SDCCH8_SACCH8C; + case ABIS_RSL_CHAN_NR_CBITS_OSMO_PDCH: return GSM_PCHAN_PDCH; default: LOGP(DRSL, LOGL_ERROR, @@ -1062,24 +1817,145 @@ static enum gsm_phys_chan_config dyn_pchan_from_chan_nr(uint8_t chan_nr) } } +/* Parse RSL_IE_OSMO_REP_ACCH_CAP */ +static int parse_repeated_acch_capability(struct gsm_lchan *lchan, struct tlv_parsed *tp) +{ + /* 3GPP TS 24.008, section 10.5.1.7 defines a Repeated ACCH Capability + * bit that indicates if REPEATED FACCH/SACCH is supported or not. + * Unfortunately there is not 3gpp spec that describes how this bit + * should be communicated in the RSL CHANNEL ACTIVATION. For osmo-bts + * we will use a propritary IE. */ + + memset(&lchan->rep_acch_cap, 0, sizeof(lchan->rep_acch_cap)); + + if (!TLVP_PRES_LEN(tp, RSL_IE_OSMO_REP_ACCH_CAP, sizeof(lchan->rep_acch_cap))) + return 0; + + if (!osmo_bts_has_feature(lchan->ts->trx->bts->features, BTS_FEAT_ACCH_REP)) + return -RSL_ERR_OPT_IE_ERROR; + + memcpy(&lchan->rep_acch_cap, TLVP_VAL(tp, RSL_IE_OSMO_REP_ACCH_CAP), + sizeof(lchan->rep_acch_cap)); + + return 0; +} + +/* Parse RSL_IE_OSMO_TOP_ACCH_CAP */ +static int parse_temporary_overpower_acch_capability(struct gsm_lchan *lchan, + const struct tlv_parsed *tp) +{ + memset(&lchan->top_acch_cap, 0, sizeof(lchan->top_acch_cap)); + + if (!TLVP_PRES_LEN(tp, RSL_IE_OSMO_TEMP_OVP_ACCH_CAP, sizeof(lchan->top_acch_cap))) + return 0; + + if (!osmo_bts_has_feature(lchan->ts->trx->bts->features, BTS_FEAT_ACCH_TEMP_OVP)) + return -RSL_ERR_OPT_IE_ERROR; + + memcpy(&lchan->top_acch_cap, + TLVP_VAL(tp, RSL_IE_OSMO_TEMP_OVP_ACCH_CAP), + sizeof(lchan->top_acch_cap)); + + /* Simplify checking whether the overpower is enabled at all: allow + * testing just one parameter (overpower_db > 0) instead of all three. */ + if (!lchan->top_acch_cap.sacch_enable && !lchan->top_acch_cap.facch_enable) + lchan->top_acch_cap.overpower_db = 0; + + return 0; +} + +/* Parse (O) MultiRate configuration IE (see 9.3.52) */ +static int parse_multirate_config(struct gsm_lchan *lchan, + const struct tlv_parsed *tp) +{ + int rc; + + if (!TLVP_PRESENT(tp, RSL_IE_MR_CONFIG)) { + /* Included if the Channel Mode indicates that a multi-rate codec is used */ + if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) { + LOGPLCHAN(lchan, DRSL, LOGL_NOTICE, "Missing MultiRate conf IE " + "(TCH mode is %s)\n", gsm48_chan_mode_name(lchan->tch_mode)); + /* Init lchan->tch.amr_mr with hard-coded default values */ + amr_init_mr_conf_def(lchan); + goto parsed; + } + return 0; + } + + /* Included if the Channel Mode indicates that a multi-rate codec is used */ + if (lchan->tch_mode != GSM48_CMODE_SPEECH_AMR) { + LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "Unexpected MultiRate conf IE " + "(TCH mode is %s)\n", gsm48_chan_mode_name(lchan->tch_mode)); + return -RSL_ERR_OPT_IE_ERROR; + } + + rc = amr_parse_mr_conf(&lchan->tch.amr_mr, + TLVP_VAL(tp, RSL_IE_MR_CONFIG), + TLVP_LEN(tp, RSL_IE_MR_CONFIG)); + if (rc < 0) { + LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "Error parsing MultiRate conf IE\n"); + return -RSL_ERR_IE_CONTENT; + } + +parsed: + amr_log_mr_conf(DRTP, LOGL_DEBUG, gsm_lchan_name(lchan), &lchan->tch.amr_mr); + lchan->tch.last_cmr = AMR_CMR_NONE; + return 0; +} + /* 8.4.1 CHANnel ACTIVation is received */ static int rsl_rx_chan_activ(struct msgb *msg) { struct abis_rsl_dchan_hdr *dch = msgb_l2(msg); struct gsm_lchan *lchan = msg->lchan; struct gsm_bts_trx_ts *ts = lchan->ts; - struct rsl_ie_chan_mode *cm; + struct gsm_bts_trx_ts *primary_ts; struct tlv_parsed tp; - uint8_t type; + const struct tlv_p_entry *ie; + uint8_t type, cause; + bool reactivation = false; int rc; - if (lchan->state != LCHAN_S_NONE) { + if (rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)) < 0) { + LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "%s(): rsl_tlv_parse() failed\n", __func__); + return rsl_tx_chan_act_nack(lchan, RSL_ERR_PROTO); + } + + /* 9.3.3 Activation Type */ + if (!TLVP_PRESENT(&tp, RSL_IE_ACT_TYPE)) { + LOGPLCHAN(lchan, DRSL, LOGL_NOTICE, "missing Activation Type\n"); + return rsl_tx_chan_act_nack(lchan, RSL_ERR_MAND_IE_ERROR); + } + type = *TLVP_VAL(&tp, RSL_IE_ACT_TYPE); + if ((type & RSL_ACT_TYPE_REACT)) { + type -= RSL_ACT_TYPE_REACT; + reactivation = true; + } + + /* If Activation Type is IMMEDIATE ASSIGNMENT, we expect L3 info with establishment. */ + lchan->l3_info_estab = (type == RSL_ACT_INTRA_IMM_ASS); + + if (!reactivation && lchan->state != LCHAN_S_NONE) { LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "error: lchan is not available, but in state: %s.\n", gsm_lchans_name(lchan->state)); return rsl_tx_chan_act_nack(lchan, RSL_ERR_EQUIPMENT_FAIL); } - if (ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH) { + if (reactivation && lchan->state == LCHAN_S_NONE) { + LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "error: reactivation on inactive lchan.\n"); + return rsl_tx_chan_act_nack(lchan, RSL_ERR_EQUIPMENT_FAIL); + } + + /* We need to pick the real TS here to check NM state: */ + primary_ts = ts->vamos.is_shadow ? ts->vamos.peer : ts; + if (primary_ts->mo.nm_state.operational != NM_OPSTATE_ENABLED || + primary_ts->mo.nm_state.availability != NM_AVSTATE_OK) { + LOGP(DRSL, LOGL_ERROR, "%s rx chan activ but TS not in nm_state oper=ENABLED avail=OK, nack!\n", + gsm_ts_and_pchan_name(ts)); + return rsl_tx_chan_act_nack(lchan, RSL_ERR_RR_UNAVAIL); + } + + if (ts->pchan == GSM_PCHAN_OSMO_DYN) { ts->dyn.pchan_want = dyn_pchan_from_chan_nr(dch->chan_nr); DEBUGP(DRSL, "%s rx chan activ\n", gsm_ts_and_pchan_name(ts)); @@ -1089,39 +1965,37 @@ static int rsl_rx_chan_activ(struct msgb *msg) * mode than this activation needs it to be. * Re-connect, then come back to rsl_rx_chan_activ(). */ - rc = dyn_ts_l1_reconnect(ts, msg); + rc = dyn_ts_l1_reconnect(ts); if (rc) return rsl_tx_chan_act_nack(lchan, RSL_ERR_NORMAL_UNSPEC); + /* will be fed back to rsl_rx_chan_activ() later */ + OSMO_ASSERT(lchan->pending_chan_activ == NULL); + lchan->pending_chan_activ = msg; /* indicate that the msgb should not be freed. */ return 1; } } - LOGPLCHAN(lchan, DRSL, LOGL_DEBUG, "rx Channel Activation in state: %s.\n", - gsm_lchans_name(lchan->state)); - - /* Initialize channel defaults */ - lchan->ms_power = ms_pwr_ctl_lvl(lchan->ts->trx->bts->band, 0); - lchan->ms_power_ctrl.current = lchan->ms_power; - lchan->ms_power_ctrl.fixed = 0; - - rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)); + /* Initialize MS Power Control defaults */ + lchan->ms_power_ctrl = (struct lchan_power_ctrl_state) { + .max = ms_pwr_ctl_lvl(lchan->ts->trx->bts->band, 0), + .current = lchan->ms_power_ctrl.max, + }; - /* 9.3.3 Activation Type */ - if (!TLVP_PRESENT(&tp, RSL_IE_ACT_TYPE)) { - LOGPLCHAN(lchan, DRSL, LOGL_NOTICE, "missing Activation Type\n"); - return rsl_tx_chan_act_nack(lchan, RSL_ERR_MAND_IE_ERROR); - } - type = *TLVP_VAL(&tp, RSL_IE_ACT_TYPE); + /* Initialize BS Power Control defaults */ + lchan->bs_power_ctrl = (struct lchan_power_ctrl_state) { + .max = 2 * 15, /* maximum defined in 9.3.4 */ + .current = 0, + }; /* 9.3.6 Channel Mode */ if (type != RSL_ACT_OSMO_PDCH) { - if (!TLVP_PRESENT(&tp, RSL_IE_CHAN_MODE)) { - LOGPLCHAN(lchan, DRSL, LOGL_NOTICE, "missing Channel Mode\n"); - return rsl_tx_chan_act_nack(lchan, RSL_ERR_MAND_IE_ERROR); - } - cm = (struct rsl_ie_chan_mode *) TLVP_VAL(&tp, RSL_IE_CHAN_MODE); - lchan_tchmode_from_cmode(lchan, cm); + if (rsl_handle_chan_mod_ie(lchan, &tp, &cause) != 0) + return rsl_tx_chan_act_nack(lchan, cause); + if (rsl_handle_chan_ident_ie(lchan, &tp, &cause) != 0) + return rsl_tx_chan_act_nack(lchan, cause); + if (rsl_handle_osmo_tsc_ie(lchan, &tp, &cause) != 0) + return rsl_tx_chan_act_nack(lchan, cause); } /* 9.3.7 Encryption Information */ @@ -1149,28 +2023,73 @@ static int rsl_rx_chan_activ(struct msgb *msg) } /* 9.3.4 BS Power */ - if (TLVP_PRES_LEN(&tp, RSL_IE_BS_POWER, 1)) - lchan->bs_power = *TLVP_VAL(&tp, RSL_IE_BS_POWER); + if (TLVP_PRES_LEN(&tp, RSL_IE_BS_POWER, 1)) { + if (*TLVP_VAL(&tp, RSL_IE_BS_POWER) & (1 << 4)) { + LOGPLCHAN(lchan, DRSL, LOGL_NOTICE, + "Fast Power Control is not supported\n"); + return rsl_tx_chan_act_nack(lchan, RSL_ERR_SERV_OPT_UNIMPL); + } + + uint8_t red = BS_POWER2DB(*TLVP_VAL(&tp, RSL_IE_BS_POWER)); + + /* BS power reduction is generally not allowed on BCCH/CCCH carrier. + * However, we allow it in the BCCH carrier power reduction operation. + * Constrain BS power value by the maximum reduction for this timeslot. */ + if (ts->trx->bts->c0 == ts->trx) + red = OSMO_MIN(red, ts->c0_power_red_db); + + lchan->bs_power_ctrl.max = red; + lchan->bs_power_ctrl.current = red; + + LOGPLCHAN(lchan, DRSL, LOGL_DEBUG, "BS Power attenuation %u dB\n", + lchan->bs_power_ctrl.current); + } + /* 9.3.13 MS Power */ if (TLVP_PRES_LEN(&tp, RSL_IE_MS_POWER, 1)) { - lchan->ms_power = *TLVP_VAL(&tp, RSL_IE_MS_POWER); - lchan->ms_power_ctrl.current = lchan->ms_power; - lchan->ms_power_ctrl.fixed = 0; + lchan->ms_power_ctrl.max = *TLVP_VAL(&tp, RSL_IE_MS_POWER) & 0x1F; + lchan->ms_power_ctrl.current = lchan->ms_power_ctrl.max; } /* 9.3.24 Timing Advance */ if (TLVP_PRES_LEN(&tp, RSL_IE_TIMING_ADVANCE, 1)) - lchan->rqd_ta = *TLVP_VAL(&tp, RSL_IE_TIMING_ADVANCE); + lchan->ta_ctrl.current = *TLVP_VAL(&tp, RSL_IE_TIMING_ADVANCE); + + /* 9.3.31 (TLV) MS Power Parameters IE (vendor specific) */ + if ((ie = TLVP_GET(&tp, RSL_IE_MS_POWER_PARAM)) != NULL) { + struct gsm_power_ctrl_params *params = &lchan->ms_dpc_params; + + /* Parsed parameters will override per-TRX defaults */ + memcpy(params, ts->trx->ms_dpc_params, sizeof(*params)); + + if (ie->len && parse_power_ctrl_params(params, ie->val, ie->len) != 0) { + LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "Failed to parse MS Power Parameters IE\n"); + return rsl_tx_chan_act_nack(lchan, RSL_ERR_IE_CONTENT); + } - /* 9.3.32 BS Power Parameters */ - /* 9.3.31 MS Power Parameters */ - if (TLVP_PRESENT(&tp, RSL_IE_MS_POWER_PARAM)) - lchan->ms_power_ctrl.fixed = 0; - else { /* Spec explicitly states BTS should only perform * autonomous MS power control loop in BTS if 'MS Power * Parameters' IE is present! */ - lchan->ms_power_ctrl.fixed = 1; + lchan->ms_power_ctrl.dpc_params = params; + } + + /* 9.3.32 (TLV) BS Power Parameters IE (vendor specific) */ + if ((ie = TLVP_GET(&tp, RSL_IE_BS_POWER_PARAM)) != NULL) { + struct gsm_power_ctrl_params *params = &lchan->bs_dpc_params; + + /* Parsed parameters will override per-TRX defaults */ + memcpy(params, ts->trx->bs_dpc_params, sizeof(*params)); + + /* Parsed parameters will override per-TRX defaults */ + if (ie->len && parse_power_ctrl_params(params, ie->val, ie->len) != 0) { + LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "Failed to parse BS Power Parameters IE\n"); + return rsl_tx_chan_act_nack(lchan, RSL_ERR_IE_CONTENT); + } + + /* NOTE: it's safer to start from 0 */ + lchan->bs_power_ctrl.current = 0; + lchan->bs_power_ctrl.dpc_params = params; } + /* 9.3.16 Physical Context */ /* 9.3.29 SACCH Information */ @@ -1213,29 +2132,22 @@ static int rsl_rx_chan_activ(struct msgb *msg) /* use standard SACCH filling of the BTS */ copy_sacch_si_to_lchan(lchan); } + /* 9.3.52 MultiRate Configuration */ - if (TLVP_PRESENT(&tp, RSL_IE_MR_CONFIG)) { - if (TLVP_LEN(&tp, RSL_IE_MR_CONFIG) > sizeof(lchan->mr_bts_lv) - 1) { - LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "Error parsing MultiRate conf IE\n"); - rsl_tx_error_report(msg->trx, RSL_ERR_IE_CONTENT, &dch->chan_nr, NULL, msg); - return rsl_tx_chan_act_acknack(lchan, RSL_ERR_IE_CONTENT); - } - memcpy(lchan->mr_bts_lv, TLVP_VAL(&tp, RSL_IE_MR_CONFIG) - 1, - TLVP_LEN(&tp, RSL_IE_MR_CONFIG) + 1); - amr_parse_mr_conf(&lchan->tch.amr_mr, TLVP_VAL(&tp, RSL_IE_MR_CONFIG), - TLVP_LEN(&tp, RSL_IE_MR_CONFIG)); - amr_log_mr_conf(DRTP, LOGL_DEBUG, gsm_lchan_name(lchan), - &lchan->tch.amr_mr); - lchan->tch.last_cmr = AMR_CMR_NONE; - } + rc = parse_multirate_config(lchan, &tp); + if (rc < 0) + return rsl_tx_chan_act_acknack(lchan, -rc); + /* 9.3.53 MultiRate Control */ /* 9.3.54 Supported Codec Types */ - LOGPLCHAN(lchan, DRSL, LOGL_INFO, "chan_nr=%s type=0x%02x mode=%s\n", - rsl_chan_nr_str(dch->chan_nr), type, gsm48_chan_mode_name(lchan->tch_mode)); + LOGPLCHAN(lchan, DRSL, LOGL_INFO, "chan_nr=%s type=0x%02x=%s mode=%s\n", + rsl_chan_nr_str(dch->chan_nr), + type, get_value_string(rsl_act_type_names, type), + gsm48_chan_mode_name(lchan->tch_mode)); /* Connecting PDCH on dyn TS goes via PCU instead. */ - if (ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH + if (ts->pchan == GSM_PCHAN_OSMO_DYN && ts->dyn.pchan_want == GSM_PCHAN_PDCH) { /* * We ack the activation to the BSC right away, regardless of @@ -1281,48 +2193,75 @@ static int rsl_rx_chan_activ(struct msgb *msg) return 0; } + /* Indicate which SAPIs should be enabled before the first RACH is received, for handover. See 3GPP TS 48.058 + * 4.1.3 and 4.1.4. + * + * | | Timing || transmit | activate | This implementation + * | MS Power | Advance || on main channel | dl SACCH | activates DL SACCH + * ----------------------------------------------------------------------------------------- + * async ho no * --> yes no no + * async ho yes * --> yes may be started no + * async ho yes yes --> yes may be started yes + * sync ho no no --> yes no no + * sync ho yes no --> yes may be started no + * sync ho yes yes --> yes shall be started yes + */ + switch (type) { + case RSL_ACT_INTER_ASYNC: + case RSL_ACT_INTER_SYNC: + lchan->want_dl_sacch_active = (TLVP_PRES_LEN(&tp, RSL_IE_MS_POWER, 1) + && TLVP_PRES_LEN(&tp, RSL_IE_TIMING_ADVANCE, 1)); + break; + default: + lchan->want_dl_sacch_active = true; + break; + } + /* Remember to send an RSL ACK once the lchan is active */ lchan->rel_act_kind = LCHAN_REL_ACT_RSL; - /* actually activate the channel in the BTS */ - rc = l1sap_chan_act(lchan->ts->trx, dch->chan_nr, &tp); + rc = parse_repeated_acch_capability(lchan, &tp); + if (rc < 0) + return rsl_tx_chan_act_acknack(lchan, -rc); + rc = parse_temporary_overpower_acch_capability(lchan, &tp); if (rc < 0) return rsl_tx_chan_act_acknack(lchan, -rc); - return 0; -} - -static int dyn_ts_pdch_release(struct gsm_lchan *lchan) -{ - struct gsm_bts_trx_ts *ts = lchan->ts; + /* Take the first ACCH overpower decision (if allowed): it can be + * enabled immediately if the RxQual threshold is disabled (0). */ + if (lchan->top_acch_cap.overpower_db > 0) + lchan->top_acch_active = !lchan->top_acch_cap.rxqual; + else + lchan->top_acch_active = false; - if (ts->dyn.pchan_is != ts->dyn.pchan_want) { - LOGP(DRSL, LOGL_ERROR, "%s: PDCH release requested but already" - " in switchover\n", gsm_ts_and_pchan_name(ts)); - return -EINVAL; + /* set ASCI channel into right state */ + if (rsl_chan_rt_is_asci(lchan->rsl_chan_rt)) { + if (reactivation) + vgcs_lchan_react(lchan); + else + vgcs_lchan_activate(lchan); } - /* - * Indicate PDCH Disconnect in dyn_pdch.want, let pcu_tx_info_ind() - * pick it up and wait for PCU to disable the channel. - */ - ts->dyn.pchan_want = GSM_PCHAN_NONE; - - if (!pcu_connected()) { - /* PCU not connected yet. Just record the new type and done, - * the PCU will pick it up once connected. */ - ts->dyn.pchan_is = GSM_PCHAN_NONE; - return 1; + /* on reactivation, the channel is already activated */ + if (reactivation) { + rc = rsl_tx_chan_act_ack(lchan); + if (rc < 0) + LOGP(DRSL, LOGL_ERROR, "%s Cannot send act ack: %d\n", + gsm_ts_and_pchan_name(ts), rc); + return 0; } - return pcu_tx_info_ind(); + /* actually activate the channel in the BTS */ + rc = l1sap_chan_act(lchan->ts->trx, dch->chan_nr); + if (rc < 0) + return rsl_tx_chan_act_acknack(lchan, -rc); + + return 0; } /* 8.4.14 RF CHANnel RELease is received */ static int rsl_rx_rf_chan_rel(struct gsm_lchan *lchan, uint8_t chan_nr) { - int rc; - if (lchan->state == LCHAN_S_NONE) { LOGP(DRSL, LOGL_ERROR, "%s ss=%d state=%s Rx RSL RF Channel Release, but is already inactive;" @@ -1333,38 +2272,7 @@ static int rsl_rx_rf_chan_rel(struct gsm_lchan *lchan, uint8_t chan_nr) * not necessarily reflecting the current lchan state. */ return tx_rf_rel_ack(lchan, chan_nr); } - - if (lchan->abis_ip.rtp_socket) { - rsl_tx_ipac_dlcx_ind(lchan, RSL_ERR_NORMAL_UNSPEC); - osmo_rtp_socket_log_stats(lchan->abis_ip.rtp_socket, DRTP, LOGL_INFO, - "Closing RTP socket on Channel Release "); - osmo_rtp_socket_free(lchan->abis_ip.rtp_socket); - lchan->abis_ip.rtp_socket = NULL; - msgb_queue_flush(&lchan->dl_tch_queue); - } - - /* release handover state */ - handover_reset(lchan); - - lchan->rel_act_kind = LCHAN_REL_ACT_RSL; - - /* Dynamic channel in PDCH mode is released via PCU */ - if (lchan->ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH - && lchan->ts->dyn.pchan_is == GSM_PCHAN_PDCH) { - rc = dyn_ts_pdch_release(lchan); - if (rc == 1) { - /* If the PCU is not connected, continue to rel ack right away. */ - lchan->rel_act_kind = LCHAN_REL_ACT_PCU; - return rsl_tx_rf_rel_ack(lchan); - } - /* Waiting for PDCH release */ - return rc; - } - - l1sap_chan_rel(lchan->ts->trx, chan_nr); - - lapdm_channel_exit(&lchan->lapdm_ch); - + gsm_lchan_release(lchan, LCHAN_REL_ACT_RSL); return 0; } @@ -1398,7 +2306,7 @@ static int tx_ciph_mod_compl_hack(struct gsm_lchan *lchan, uint8_t link_id, } } - rsl_rll_push_l3(fake_msg, RSL_MT_DATA_IND, gsm_lchan2chan_nr(lchan), + rsl_rll_push_l3(fake_msg, RSL_MT_DATA_IND, gsm_lchan2chan_nr_rsl(lchan), link_id, 1); fake_msg->lchan = lchan; @@ -1443,7 +2351,8 @@ static int rsl_rx_encr_cmd(struct msgb *msg) uint8_t link_id; if (rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)) < 0) { - return rsl_tx_error_report(msg->trx, RSL_ERR_IE_CONTENT, &dch->chan_nr, NULL, msg); + LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "%s(): rsl_tlv_parse() failed\n", __func__); + return rsl_tx_error_report(msg->trx, RSL_ERR_PROTO, &dch->chan_nr, NULL, msg); } if (!TLVP_PRESENT(&tp, RSL_IE_ENCR_INFO) || @@ -1512,7 +2421,7 @@ static int _rsl_tx_mode_modif_nack(struct gsm_bts_trx *trx, uint8_t chan_nr, uin struct msgb *msg; if (lchan) - LOGP(DRSL, LOGL_NOTICE, "%s: ", gsm_lchan_name(lchan)); + LOGPLCHAN(lchan, DRSL, LOGL_NOTICE, ""); else LOGP(DRSL, LOGL_NOTICE, "0x%02x: ", chan_nr); LOGPC(DRSL, LOGL_NOTICE, "Tx MODE MODIFY NACK (cause = 0x%02x)\n", cause); @@ -1533,7 +2442,7 @@ static int _rsl_tx_mode_modif_nack(struct gsm_bts_trx *trx, uint8_t chan_nr, uin } static int rsl_tx_mode_modif_nack(struct gsm_lchan *lchan, uint8_t cause) { - return _rsl_tx_mode_modif_nack(lchan->ts->trx, gsm_lchan2chan_nr(lchan), cause, lchan); + return _rsl_tx_mode_modif_nack(lchan->ts->trx, gsm_lchan2chan_nr_rsl(lchan), cause, lchan); } @@ -1541,7 +2450,7 @@ static int rsl_tx_mode_modif_nack(struct gsm_lchan *lchan, uint8_t cause) static int rsl_tx_mode_modif_ack(struct gsm_lchan *lchan) { struct msgb *msg; - uint8_t chan_nr = gsm_lchan2chan_nr(lchan); + uint8_t chan_nr = gsm_lchan2chan_nr_rsl(lchan); LOGPLCHAN(lchan, DRSL, LOGL_INFO, "Tx MODE MODIF ACK\n"); @@ -1560,24 +2469,24 @@ static int rsl_rx_mode_modif(struct msgb *msg) { struct abis_rsl_dchan_hdr *dch = msgb_l2(msg); struct gsm_lchan *lchan = msg->lchan; - struct rsl_ie_chan_mode *cm; struct tlv_parsed tp; + uint8_t cause; + int rc; - rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)); - - /* 9.3.6 Channel Mode */ - if (!TLVP_PRESENT(&tp, RSL_IE_CHAN_MODE)) { - LOGPLCHAN(lchan, DRSL, LOGL_NOTICE, "missing Channel Mode\n"); - return rsl_tx_mode_modif_nack(lchan, RSL_ERR_MAND_IE_ERROR); + if (rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)) < 0) { + LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "%s(): rsl_tlv_parse() failed\n", __func__); + return rsl_tx_mode_modif_nack(lchan, RSL_ERR_PROTO); } - cm = (struct rsl_ie_chan_mode *) TLVP_VAL(&tp, RSL_IE_CHAN_MODE); - lchan_tchmode_from_cmode(lchan, cm); - if (bts_supports_cm(lchan->ts->trx->bts, ts_pchan(lchan->ts), lchan->tch_mode) != 1) { - LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "%s: invalid mode: %s (wrong BSC configuration?)\n", - gsm_ts_and_pchan_name(lchan->ts), gsm48_chan_mode_name(lchan->tch_mode)); - return rsl_tx_mode_modif_nack(lchan, RSL_ERR_SERV_OPT_UNAVAIL); - } + /* 9.3.6 Channel Mode */ + if (rsl_handle_chan_mod_ie(lchan, &tp, &cause) != 0) + return rsl_tx_mode_modif_nack(lchan, cause); + /* 9.3.5 Channel Identification */ + if (rsl_handle_chan_ident_ie(lchan, &tp, &cause) != 0) + return rsl_tx_mode_modif_nack(lchan, cause); + /* Osmocom specific TSC IE for VAMOS */ + if (rsl_handle_osmo_tsc_ie(lchan, &tp, &cause) != 0) + return rsl_tx_mode_modif_nack(lchan, cause); /* 9.3.7 Encryption Information */ if (TLVP_PRESENT(&tp, RSL_IE_ENCR_INFO)) { @@ -1593,23 +2502,27 @@ static int rsl_rx_mode_modif(struct msgb *msg) /* 9.3.45 Main channel reference */ /* 9.3.52 MultiRate Configuration */ - if (TLVP_PRESENT(&tp, RSL_IE_MR_CONFIG)) { - if (TLVP_LEN(&tp, RSL_IE_MR_CONFIG) > sizeof(lchan->mr_bts_lv) - 1) { - LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "Error parsing MultiRate conf IE\n"); - rsl_tx_error_report(msg->trx, RSL_ERR_IE_CONTENT, &dch->chan_nr, NULL, msg); - return rsl_tx_mode_modif_nack(lchan, RSL_ERR_IE_CONTENT);; - } - memcpy(lchan->mr_bts_lv, TLVP_VAL(&tp, RSL_IE_MR_CONFIG) - 1, - TLVP_LEN(&tp, RSL_IE_MR_CONFIG) + 1); - amr_parse_mr_conf(&lchan->tch.amr_mr, TLVP_VAL(&tp, RSL_IE_MR_CONFIG), - TLVP_LEN(&tp, RSL_IE_MR_CONFIG)); - amr_log_mr_conf(DRTP, LOGL_DEBUG, gsm_lchan_name(lchan), - &lchan->tch.amr_mr); - lchan->tch.last_cmr = AMR_CMR_NONE; - } + rc = parse_multirate_config(lchan, &tp); + if (rc < 0) + return rsl_tx_mode_modif_nack(lchan, -rc); + /* 9.3.53 MultiRate Control */ /* 9.3.54 Supported Codec Types */ + rc = parse_repeated_acch_capability(lchan, &tp); + if (rc < 0) + return rsl_tx_mode_modif_nack(lchan, -rc); + rc = parse_temporary_overpower_acch_capability(lchan, &tp); + if (rc < 0) + return rsl_tx_mode_modif_nack(lchan, -rc); + + /* Immediately disable ACCH overpower if the value is 0 dB, + * or enable if the RxQual threshold becomes disabled (0). */ + if (lchan->top_acch_cap.overpower_db == 0) + lchan->top_acch_active = false; + else if (lchan->top_acch_cap.rxqual == 0) + lchan->top_acch_active = true; + l1sap_chan_modify(lchan->ts->trx, dch->chan_nr); /* FIXME: delay this until L1 says OK? */ @@ -1623,28 +2536,61 @@ static int rsl_rx_ms_pwr_ctrl(struct msgb *msg) { struct abis_rsl_dchan_hdr *dch = msgb_l2(msg); struct gsm_lchan *lchan = msg->lchan; + struct gsm_bts *bts = lchan->ts->trx->bts; + const struct tlv_p_entry *ie; struct tlv_parsed tp; uint8_t pwr; + int max_pwr, curr_pwr; - rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)); + if (rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)) < 0) { + LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "%s(): rsl_tlv_parse() failed\n", __func__); + return rsl_tx_error_report(msg->trx, RSL_ERR_PROTO, &dch->chan_nr, NULL, msg); + } /* 9.3.13 MS Power (M) */ if (!TLVP_PRES_LEN(&tp, RSL_IE_MS_POWER, 1)) return rsl_tx_error_report(msg->trx, RSL_ERR_MAND_IE_ERROR, &dch->chan_nr, NULL, msg); pwr = *TLVP_VAL(&tp, RSL_IE_MS_POWER) & 0x1F; - lchan->ms_power_ctrl.current = pwr; + lchan->ms_power_ctrl.max = pwr; - LOGPLCHAN(lchan, DRSL, LOGL_NOTICE, "Rx MS POWER CONTROL %d\n", lchan->ms_power_ctrl.current); + LOGPLCHAN(lchan, DRSL, LOGL_INFO, "Rx MS POWER CONTROL %" PRIu8 "\n", pwr); - /* 9.3.31 MS Power Parameters (O) */ - if (TLVP_PRESENT(&tp, RSL_IE_MS_POWER_PARAM)) - lchan->ms_power_ctrl.fixed = 0; - else { - /* Spec explicitly states BTS should only perform - * autonomous MS power control loop in BTS if 'MS Power - * Parameters' IE is present! */ - lchan->ms_power_ctrl.fixed = 1; + /* Spec explicitly states BTS should only perform autonomous MS Power + * control loop in BTS if 'MS Power Parameters' IE is present! */ + lchan->ms_power_ctrl.dpc_params = NULL; + + /* 9.3.31 (TLV) MS Power Parameters IE (vendor specific) */ + if ((ie = TLVP_GET(&tp, RSL_IE_MS_POWER_PARAM)) != NULL) { + struct gsm_power_ctrl_params *params = &lchan->ms_dpc_params; + + /* Parsed parameters will override per-TRX defaults */ + memcpy(params, msg->trx->ms_dpc_params, sizeof(*params)); + + /* Parsed parameters will override per-TRX defaults */ + if (ie->len && parse_power_ctrl_params(params, ie->val, ie->len) != 0) { + LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "Failed to parse MS Power Parameters IE\n"); + return rsl_tx_chan_act_nack(lchan, RSL_ERR_IE_CONTENT); + } + + lchan->ms_power_ctrl.dpc_params = params; + } + + /* Only set current to max if actual value of current + in dBm > value in dBm from max, or if fixed. */ + if (lchan->ms_power_ctrl.dpc_params == NULL) { + lchan->ms_power_ctrl.current = lchan->ms_power_ctrl.max; + } else { + max_pwr = ms_pwr_dbm(bts->band, lchan->ms_power_ctrl.max); + curr_pwr = ms_pwr_dbm(bts->band, lchan->ms_power_ctrl.current); + if (max_pwr < 0 || curr_pwr < 0) { + LOGPLCHAN(lchan, DRSL, LOGL_ERROR, + "Unable to calculate power levels to dBm: %" PRIu8 " -> %d, %" PRIu8 " -> %d\n", + lchan->ms_power_ctrl.max, max_pwr, + lchan->ms_power_ctrl.current, curr_pwr); + } else if (curr_pwr > max_pwr) { + lchan->ms_power_ctrl.current = lchan->ms_power_ctrl.max; + } } bts_model_adjst_ms_pwr(lchan); @@ -1652,41 +2598,77 @@ static int rsl_rx_ms_pwr_ctrl(struct msgb *msg) return 0; } -/* See TS 48.058 Section 9.3.4 */ -static int bs_power_attenuation_dB(uint8_t bs_power) -{ - /* the lower nibble contains the number of 2dB steps that the BS power is reduced compared - * to its nominal transmit power */ - return - ((bs_power & 0xF) *2); -} - /* 8.4.16 BS POWER CONTROL */ static int rsl_rx_bs_pwr_ctrl(struct msgb *msg) { struct abis_rsl_dchan_hdr *dch = msgb_l2(msg); struct gsm_lchan *lchan = msg->lchan; + struct gsm_bts_trx *trx = msg->trx; + const struct tlv_p_entry *ie; struct tlv_parsed tp; - uint8_t new_bs_power; + uint8_t old, new; - rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)); + if (rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)) < 0) { + LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "%s(): rsl_tlv_parse() failed\n", __func__); + return rsl_tx_error_report(trx, RSL_ERR_PROTO, &dch->chan_nr, NULL, msg); + } /* 9.3.4 BS Power (M) */ if (!TLVP_PRES_LEN(&tp, RSL_IE_BS_POWER, 1)) - return rsl_tx_error_report(msg->trx, RSL_ERR_MAND_IE_ERROR, &dch->chan_nr, NULL, msg); + return rsl_tx_error_report(trx, RSL_ERR_MAND_IE_ERROR, &dch->chan_nr, NULL, msg); + + if (*TLVP_VAL(&tp, RSL_IE_BS_POWER) & (1 << 4)) { + LOGPLCHAN(lchan, DRSL, LOGL_NOTICE, "Fast Power Control is not supported\n"); + return rsl_tx_error_report(trx, RSL_ERR_SERV_OPT_UNIMPL, &dch->chan_nr, NULL, msg); + } + + new = BS_POWER2DB(*TLVP_VAL(&tp, RSL_IE_BS_POWER)); + old = lchan->bs_power_ctrl.current; + + /* Osmocom specific extension for BCCH carrier power reduction */ + if (dch->chan_nr == RSL_CHAN_BCCH) { + int rc = bts_set_c0_pwr_red(trx->bts, new); + if (rc != 0) { + const uint8_t cause = (rc == -ENOTSUP) ? + RSL_ERR_SERV_OPT_UNIMPL : RSL_ERR_IE_CONTENT; + return rsl_tx_error_report(trx, cause, &dch->chan_nr, NULL, msg); + } - new_bs_power = *TLVP_VAL(&tp, RSL_IE_BS_POWER); + return 0; + } + + /* BS power reduction is generally not allowed on BCCH/CCCH carrier. + * However, we allow it in the BCCH carrier power reduction operation. + * Constrain BS power value by the maximum reduction for this timeslot. */ + if (trx->bts->c0 == trx) + new = OSMO_MIN(new, lchan->ts->c0_power_red_db); - LOGPLCHAN(lchan, DRSL, LOGL_INFO, "BS POWER CONTROL Attenuation %d -> %d dB\n", - bs_power_attenuation_dB(lchan->bs_power), bs_power_attenuation_dB(new_bs_power)); + /* 9.3.32 (TLV) BS Power Parameters IE (vendor specific) */ + if ((ie = TLVP_GET(&tp, RSL_IE_BS_POWER_PARAM)) != NULL) { + struct gsm_power_ctrl_params *params = &lchan->bs_dpc_params; - lchan->bs_power = new_bs_power; + /* Parsed parameters will override per-TRX defaults */ + memcpy(params, trx->bs_dpc_params, sizeof(*params)); + + /* Parsed parameters will override per-TRX defaults */ + if (ie->len && parse_power_ctrl_params(params, ie->val, ie->len) != 0) { + LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "Failed to parse BS Power Parameters IE\n"); + return rsl_tx_chan_act_nack(lchan, RSL_ERR_IE_CONTENT); + } - /* 9.3.31 MS Power Parameters (O) */ - if (TLVP_PRESENT(&tp, RSL_IE_BS_POWER_PARAM)) { - /* Spec explicitly states BTS should perform autonomous - * BS power control loop in BTS if 'BS Power Parameters' - * IE is present! WE don't support that. */ - return rsl_tx_error_report(msg->trx, RSL_ERR_OPT_IE_ERROR, &dch->chan_nr, NULL, msg); + /* NOTE: it's safer to start from 0 */ + lchan->bs_power_ctrl.current = 0; + lchan->bs_power_ctrl.max = new; + lchan->bs_power_ctrl.dpc_params = params; + } else { + lchan->bs_power_ctrl.dpc_params = NULL; + lchan->bs_power_ctrl.current = new; + } + + if (lchan->bs_power_ctrl.current != old) { + LOGPLCHAN(lchan, DRSL, LOGL_INFO, "BS POWER CONTROL: " + "attenuation change %u -> %u dB\n", + old, lchan->bs_power_ctrl.current); } return 0; @@ -1702,7 +2684,10 @@ static int rsl_rx_sacch_inf_mod(struct msgb *msg) struct tlv_parsed tp; uint8_t rsl_si, osmo_si; - rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)); + if (rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)) < 0) { + LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "%s(): rsl_tlv_parse() failed\n", __func__); + return rsl_tx_error_report(msg->trx, RSL_ERR_PROTO, &dch->chan_nr, NULL, msg); + } if (TLVP_PRESENT(&tp, RSL_IE_STARTNG_TIME)) { LOGPLCHAN(lchan, DRSL, LOGL_NOTICE, "Starting time not supported\n"); @@ -1758,7 +2743,7 @@ int rsl_tx_cbch_load_indication(struct gsm_bts *bts, bool ext_cbch, bool overflo return -ENOMEM; /* 9.3.1 Channel Number */ - rsl_cch_push_hdr(msg, RSL_MT_CBCH_LOAD_IND, gsm_lchan2chan_nr(lchan)); + rsl_cch_push_hdr(msg, RSL_MT_CBCH_LOAD_IND, gsm_lchan2chan_nr_rsl(lchan)); /* 9.3.43 CBCH Load Information */ load_info = ((overflow & 1) << 7) | (amount & 0x0F); @@ -1810,7 +2795,9 @@ int rsl_tx_ipac_dlcx_ind(struct gsm_lchan *lchan, uint8_t cause) { struct msgb *nmsg; - LOGPLCHAN(lchan, DRSL, LOGL_NOTICE, "Sending RTP delete indication: cause = %s\n", + LOGPLCHAN(lchan, DRSL, + (cause == RSL_ERR_NORMAL_UNSPEC) ? LOGL_INFO : LOGL_NOTICE, + "Sending RTP delete indication: cause = %s\n", rsl_err_name(cause)); nmsg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr)); @@ -1820,7 +2807,7 @@ int rsl_tx_ipac_dlcx_ind(struct gsm_lchan *lchan, uint8_t cause) msgb_tv16_put(nmsg, RSL_IE_IPAC_CONN_ID, htons(lchan->abis_ip.conn_id)); rsl_add_rtp_stats(lchan, nmsg); msgb_tlv_put(nmsg, RSL_IE_CAUSE, 1, &cause); - rsl_ipa_push_hdr(nmsg, RSL_MT_IPAC_DLCX_IND, gsm_lchan2chan_nr(lchan)); + rsl_ipa_push_hdr(nmsg, RSL_MT_IPAC_DLCX_IND, gsm_lchan2chan_nr_rsl(lchan)); nmsg->trx = lchan->ts->trx; @@ -1832,7 +2819,7 @@ static int rsl_tx_ipac_XXcx_ack(struct gsm_lchan *lchan, int inc_pt2, uint8_t orig_msgt) { struct msgb *msg; - uint8_t chan_nr = gsm_lchan2chan_nr(lchan); + uint8_t chan_nr = gsm_lchan2chan_nr_rsl(lchan); const char *name; struct in_addr ia; @@ -1870,6 +2857,11 @@ static int rsl_tx_ipac_XXcx_ack(struct gsm_lchan *lchan, int inc_pt2, lchan->abis_ip.rtp_payload2); } + /* Osmocom Extension: Osmux CID */ + if (lchan->abis_ip.osmux.use) + msgb_tlv_put(msg, RSL_IE_OSMO_OSMUX_CID, 1, + &lchan->abis_ip.osmux.local_cid); + /* push the header in front */ rsl_ipa_push_hdr(msg, orig_msgt + 1, chan_nr); msg->trx = lchan->ts->trx; @@ -1880,7 +2872,7 @@ static int rsl_tx_ipac_XXcx_ack(struct gsm_lchan *lchan, int inc_pt2, static int rsl_tx_ipac_dlcx_ack(struct gsm_lchan *lchan, int inc_conn_id) { struct msgb *msg; - uint8_t chan_nr = gsm_lchan2chan_nr(lchan); + uint8_t chan_nr = gsm_lchan2chan_nr_rsl(lchan); LOGPLCHAN(lchan, DRSL, LOGL_INFO, "RSL Tx IPAC_DLCX_ACK\n"); @@ -1903,7 +2895,7 @@ static int rsl_tx_ipac_dlcx_nack(struct gsm_lchan *lchan, int inc_conn_id, uint8_t cause) { struct msgb *msg; - uint8_t chan_nr = gsm_lchan2chan_nr(lchan); + uint8_t chan_nr = gsm_lchan2chan_nr_rsl(lchan); LOGPLCHAN(lchan, DRSL, LOGL_INFO, "RSL Tx IPAC_DLCX_NACK\n"); @@ -1924,15 +2916,15 @@ static int rsl_tx_ipac_dlcx_nack(struct gsm_lchan *lchan, int inc_conn_id, } -/* transmit an CRCX NACK for the lchan */ +/* Send an xxCX NACK for the given xxCX message type and lchan */ static int tx_ipac_XXcx_nack(struct gsm_lchan *lchan, uint8_t cause, int inc_ipport, uint8_t orig_msgtype) { struct msgb *msg; - uint8_t chan_nr = gsm_lchan2chan_nr(lchan); + uint8_t chan_nr = gsm_lchan2chan_nr_rsl(lchan); + uint8_t msg_type = orig_msgtype + 2; - /* FIXME: allocate new msgb and copy old over */ - LOGPLCHAN(lchan, DRSL, LOGL_NOTICE, "RSL Tx IPAC_BIND_NACK\n"); + LOGPLCHAN(lchan, DRSL, LOGL_NOTICE, "RSL Tx %s\n", rsl_ipac_msg_name(msg_type)); msg = rsl_msgb_alloc(sizeof(struct abis_rsl_dchan_hdr)); if (!msg) @@ -1952,7 +2944,7 @@ static int tx_ipac_XXcx_nack(struct gsm_lchan *lchan, uint8_t cause, msgb_tlv_put(msg, RSL_IE_CAUSE, 1, &cause); /* push the header in front */ - rsl_ipa_push_hdr(msg, orig_msgtype + 2, chan_nr); + rsl_ipa_push_hdr(msg, msg_type, chan_nr); msg->trx = lchan->ts->trx; return abis_bts_rsl_sendmsg(msg); @@ -1960,7 +2952,7 @@ static int tx_ipac_XXcx_nack(struct gsm_lchan *lchan, uint8_t cause, static char *get_rsl_local_ip(struct gsm_bts_trx *trx) { - struct e1inp_ts *ts = trx->rsl_link->ts; + struct e1inp_ts *ts = trx->bb_transc.rsl.link->ts; struct sockaddr_storage ss; socklen_t sa_len = sizeof(ss); static char hostbuf[256]; @@ -1980,42 +2972,21 @@ static char *get_rsl_local_ip(struct gsm_bts_trx *trx) return hostbuf; } -static int bind_rtp(struct gsm_bts *bts, struct osmo_rtp_socket *rs, const char *ip) -{ - int rc; - unsigned int i; - unsigned int tries; - - tries = (bts->rtp_port_range_end - bts->rtp_port_range_start) / 2; - for (i = 0; i < tries; i++) { - - if (bts->rtp_port_range_next >= bts->rtp_port_range_end) - bts->rtp_port_range_next = bts->rtp_port_range_start; - - rc = osmo_rtp_socket_bind(rs, ip, bts->rtp_port_range_next); - - bts->rtp_port_range_next += 2; - - if (rc == 0) - return 0; - } - - return -1; -} - static int rsl_rx_ipac_XXcx(struct msgb *msg) { struct abis_rsl_dchan_hdr *dch = msgb_l2(msg); struct tlv_parsed tp; struct gsm_lchan *lchan = msg->lchan; struct gsm_bts *bts = lchan->ts->trx->bts; - const uint8_t *payload_type, *speech_mode, *payload_type2; + const uint8_t *payload_type, *speech_mode, *payload_type2, *csd_fmt; + const uint8_t *osmux_cid = NULL; uint32_t connect_ip = 0; uint16_t connect_port = 0; - int rc, inc_ip_port = 0, port; + int rc, inc_ip_port = 0; char *name; struct in_addr ia; - struct in_addr addr; + enum rsl_ipac_rtp_csd_format_d csd_fmt_d; + enum rsl_ipac_rtp_csd_format_ir csd_fmt_ir; if (dch->c.msg_type == RSL_MT_IPAC_CRCX) name = "CRCX"; @@ -2027,22 +2998,22 @@ static int rsl_rx_ipac_XXcx(struct msgb *msg) return tx_ipac_XXcx_nack(lchan, 0x52, 0, dch->c.msg_type); - rc = rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)); - if (rc < 0) - return tx_ipac_XXcx_nack(lchan, RSL_ERR_MAND_IE_ERROR, - 0, dch->c.msg_type); + if (rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)) < 0) { + LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "%s(): rsl_tlv_parse() failed\n", __func__); + return tx_ipac_XXcx_nack(lchan, RSL_ERR_PROTO, 0, dch->c.msg_type); + } LOGPLCHAN(lchan, DRSL, LOGL_DEBUG, "IPAC_%s: ", name); if (TLVP_PRES_LEN(&tp, RSL_IE_IPAC_REMOTE_IP, 4)) { + struct in_addr addr; connect_ip = tlvp_val32_unal(&tp, RSL_IE_IPAC_REMOTE_IP); addr.s_addr = connect_ip; LOGPC(DRSL, LOGL_DEBUG, "connect_ip=%s ", inet_ntoa(addr)); } if (TLVP_PRES_LEN(&tp, RSL_IE_IPAC_REMOTE_PORT, 2)) { - connect_port = tlvp_val16_unal(&tp, RSL_IE_IPAC_REMOTE_PORT); - LOGPC(DRSL, LOGL_DEBUG, "connect_port=%u ", - ntohs(connect_port)); + connect_port = tlvp_val16be(&tp, RSL_IE_IPAC_REMOTE_PORT); + LOGPC(DRSL, LOGL_DEBUG, "connect_port=%u ", connect_port); } speech_mode = TLVP_VAL(&tp, RSL_IE_IPAC_SPEECH_MODE); @@ -2056,6 +3027,14 @@ static int rsl_rx_ipac_XXcx(struct msgb *msg) LOGPC(DRSL, LOGL_DEBUG, "\n"); payload_type2 = TLVP_VAL(&tp, RSL_IE_IPAC_RTP_PAYLOAD2); + if (payload_type2) + LOGPC(DRSL, LOGL_DEBUG, "payload_type2=%u ", *payload_type2); + + /* this IE has TLV format when TV would have been good enough */ + if (TLVP_PRES_LEN(&tp, RSL_IE_OSMO_OSMUX_CID, 1)) + osmux_cid = TLVP_VAL(&tp, RSL_IE_OSMO_OSMUX_CID); + if (osmux_cid) + LOGPC(DRSL, LOGL_DEBUG, "osmux_cid=%u ", *osmux_cid); if (dch->c.msg_type == RSL_MT_IPAC_CRCX && connect_ip && connect_port) inc_ip_port = 1; @@ -2067,116 +3046,107 @@ static int rsl_rx_ipac_XXcx(struct msgb *msg) inc_ip_port, dch->c.msg_type); } - if (dch->c.msg_type == RSL_MT_IPAC_CRCX) { - char cname[32]; - char *ipstr = NULL; - if (lchan->abis_ip.rtp_socket) { - LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "Rx RSL IPAC CRCX, " - "but we already have socket!\n"); - return tx_ipac_XXcx_nack(lchan, RSL_ERR_RES_UNAVAIL, - inc_ip_port, dch->c.msg_type); + if ((csd_fmt = TLVP_VAL(&tp, RSL_IE_IPAC_RTP_CSD_FMT))) { + csd_fmt_d = *csd_fmt & 0xf; + csd_fmt_ir = *csd_fmt >> 4; + LOGPC(DRSL, LOGL_DEBUG, "csd_fmt_d=%d csd_fmt_ir=%d ", csd_fmt_d, csd_fmt_ir); + if (csd_fmt_d != RSL_IPAC_RTP_CSD_TRAU_BTS) { + LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "Rx RSL IPAC %s, csd_fmt_d=%d is not supported\n", + name, csd_fmt_d); + return tx_ipac_XXcx_nack(lchan, RSL_ERR_SERV_OPT_UNIMPL, inc_ip_port, dch->c.msg_type); } - /* FIXME: select default value depending on speech_mode */ - //if (!payload_type) - lchan->tch.last_fn = LCHAN_FN_DUMMY; - lchan->abis_ip.rtp_socket = osmo_rtp_socket_create(lchan->ts->trx, - OSMO_RTP_F_POLL); - if (!lchan->abis_ip.rtp_socket) { - LOGPLCHAN(lchan, DRTP, LOGL_ERROR, "IPAC Failed to create RTP/RTCP sockets\n"); - oml_tx_failure_event_rep(&lchan->ts->trx->mo, - NM_SEVER_MINOR, OSMO_EVT_CRIT_RTP_TOUT, - "%s IPAC Failed to create RTP/RTCP sockets", - gsm_lchan_name(lchan)); + } + + if (!osmux_cid) { /* Regular RTP */ + if (bts->osmux.use == OSMUX_USAGE_ONLY) { + LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "Rx RSL IPAC XXcx without Osmux CID" + "goes against configured Osmux policy 'only'\n"); return tx_ipac_XXcx_nack(lchan, RSL_ERR_RES_UNAVAIL, inc_ip_port, dch->c.msg_type); } - rc = osmo_rtp_socket_set_param(lchan->abis_ip.rtp_socket, - bts->rtp_jitter_adaptive ? - OSMO_RTP_P_JIT_ADAP : - OSMO_RTP_P_JITBUF, - bts->rtp_jitter_buf_ms); - if (rc < 0) - LOGPLCHAN(lchan, DRTP, LOGL_ERROR, - "IPAC Failed to set RTP socket parameters: %s\n", strerror(-rc)); - else - LOGPLCHAN(lchan, DRTP, LOGL_INFO, "IPAC set RTP socket parameters: %d\n", rc); - lchan->abis_ip.rtp_socket->priv = lchan; - lchan->abis_ip.rtp_socket->rx_cb = &l1sap_rtp_rx_cb; - - if (connect_ip && connect_port) { - /* if CRCX specifies a remote IP, we can bind() - * here to 0.0.0.0 and wait for the connect() - * below, after which the kernel will have - * selected the local IP address. */ - ipstr = "0.0.0.0"; - } else { - /* if CRCX does not specify a remote IP, we will - * not do any connect() below, and thus the - * local socket will remain bound to 0.0.0.0 - - * which however we cannot legitimately report - * back to the BSC in the CRCX_ACK */ - ipstr = get_rsl_local_ip(lchan->ts->trx); + + if (dch->c.msg_type == RSL_MT_IPAC_CRCX) { /* CRCX */ + char *ipstr = NULL; + if (connect_ip && connect_port) { + /* if CRCX specifies a remote IP, we can bind() + * here to 0.0.0.0 and wait for the connect() + * below, after which the kernel will have + * selected the local IP address. */ + ipstr = "0.0.0.0"; + } else { + /* if CRCX does not specify a remote IP, we will + * not do any connect() below, and thus the + * local socket will remain bound to 0.0.0.0 - + * which however we cannot legitimately report + * back to the BSC in the CRCX_ACK */ + ipstr = get_rsl_local_ip(lchan->ts->trx); + } + rc = lchan_rtp_socket_create(lchan, ipstr); + if (rc < 0) + return tx_ipac_XXcx_nack(lchan, RSL_ERR_RES_UNAVAIL, + inc_ip_port, dch->c.msg_type); + } else { /* MDCX */ + if (!lchan->abis_ip.rtp_socket) { + LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "Rx RSL IPAC MDCX, " + "but we have no RTP socket!\n"); + return tx_ipac_XXcx_nack(lchan, RSL_ERR_RES_UNAVAIL, + inc_ip_port, dch->c.msg_type); + } } - rc = bind_rtp(bts, lchan->abis_ip.rtp_socket, ipstr); + + /* Special rule: If connect_ip == 0.0.0.0, use RSL IP + * address */ + if (connect_ip == 0) { + struct e1inp_sign_link *sign_link = + lchan->ts->trx->bb_transc.rsl.link; + + ia.s_addr = htonl(get_signlink_remote_ip(sign_link)); + } else + ia.s_addr = connect_ip; + rc = lchan_rtp_socket_connect(lchan, &ia, connect_port); if (rc < 0) { - LOGPLCHAN(lchan, DRTP, LOGL_ERROR, "IPAC Failed to bind RTP/RTCP sockets\n"); - oml_tx_failure_event_rep(&lchan->ts->trx->mo, - NM_SEVER_MINOR, OSMO_EVT_CRIT_RTP_TOUT, - "%s IPAC Failed to bind RTP/RTCP sockets", - gsm_lchan_name(lchan)); - osmo_rtp_socket_free(lchan->abis_ip.rtp_socket); - lchan->abis_ip.rtp_socket = NULL; - msgb_queue_flush(&lchan->dl_tch_queue); + lchan_rtp_socket_free(lchan); return tx_ipac_XXcx_nack(lchan, RSL_ERR_RES_UNAVAIL, inc_ip_port, dch->c.msg_type); } - /* Ensure RTCP SDES contains some useful information */ - snprintf(cname, sizeof(cname), "bts@%s", ipstr); - osmo_rtp_set_source_desc(lchan->abis_ip.rtp_socket, cname, - gsm_lchan_name(lchan), NULL, NULL, - gsm_trx_unit_id(lchan->ts->trx), - "OsmoBTS-" PACKAGE_VERSION, NULL); - /* FIXME: multiplex connection, BSC proxy */ - } else { - /* MDCX */ - if (!lchan->abis_ip.rtp_socket) { - LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "Rx RSL IPAC MDCX, " - "but we have no RTP socket!\n"); + + } else { /* Osmux */ + if (bts->osmux.use == OSMUX_USAGE_OFF) { + LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "Rx RSL IPAC XXcx with Osmux CID" + "goes against configured Osmux policy 'off'\n"); return tx_ipac_XXcx_nack(lchan, RSL_ERR_RES_UNAVAIL, inc_ip_port, dch->c.msg_type); } - } - - /* Special rule: If connect_ip == 0.0.0.0, use RSL IP - * address */ - if (connect_ip == 0) { - struct e1inp_sign_link *sign_link = - lchan->ts->trx->rsl_link; + if (dch->c.msg_type == RSL_MT_IPAC_CRCX) { /* CRCX */ + rc = lchan_osmux_init(lchan, payload_type ? *payload_type : 0); + if (rc < 0) + return tx_ipac_XXcx_nack(lchan, RSL_ERR_RES_UNAVAIL, + inc_ip_port, dch->c.msg_type); + } else { /* MDCX */ + if (!lchan->abis_ip.osmux.use) { + LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "Rx RSL IPAC MDCX with Osmux CID, " + "CRCX was configured as RTP!\n"); + return tx_ipac_XXcx_nack(lchan, RSL_ERR_RES_UNAVAIL, + inc_ip_port, dch->c.msg_type); + } + } - ia.s_addr = htonl(get_signlink_remote_ip(sign_link)); - } else - ia.s_addr = connect_ip; - rc = osmo_rtp_socket_connect(lchan->abis_ip.rtp_socket, - inet_ntoa(ia), ntohs(connect_port)); - if (rc < 0) { - LOGPLCHAN(lchan, DRTP, LOGL_ERROR, "Failed to connect RTP/RTCP sockets\n"); - osmo_rtp_socket_free(lchan->abis_ip.rtp_socket); - lchan->abis_ip.rtp_socket = NULL; - msgb_queue_flush(&lchan->dl_tch_queue); - return tx_ipac_XXcx_nack(lchan, RSL_ERR_RES_UNAVAIL, - inc_ip_port, dch->c.msg_type); + if (connect_ip != 0) + lchan->abis_ip.connect_ip = connect_ip; + if (connect_port != 0) + lchan->abis_ip.connect_port = connect_port; + lchan->abis_ip.osmux.remote_cid = *osmux_cid; + if (lchan->abis_ip.connect_ip && lchan->abis_ip.connect_port && + !lchan_osmux_connected(lchan)) { + rc = lchan_osmux_connect(lchan); + if (rc < 0) { + lchan_osmux_release(lchan); + return tx_ipac_XXcx_nack(lchan, RSL_ERR_RES_UNAVAIL, + inc_ip_port, dch->c.msg_type); + } + } } - /* save IP address and port number */ - lchan->abis_ip.connect_ip = ntohl(ia.s_addr); - lchan->abis_ip.connect_port = ntohs(connect_port); - - rc = osmo_rtp_get_bound_ip_port(lchan->abis_ip.rtp_socket, - &lchan->abis_ip.bound_ip, - &port); - if (rc < 0) - LOGPLCHAN(lchan, DRTP, LOGL_ERROR, "IPAC cannot obtain locally bound IP/port: %d\n", rc); - lchan->abis_ip.bound_port = port; /* Everything has succeeded, we can store new values in lchan */ if (payload_type) { @@ -2206,9 +3176,10 @@ static int rsl_rx_ipac_dlcx(struct msgb *msg) struct gsm_lchan *lchan = msg->lchan; int rc, inc_conn_id = 0; - rc = rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)); - if (rc < 0) - return rsl_tx_ipac_dlcx_nack(lchan, 0, RSL_ERR_MAND_IE_ERROR); + if (rsl_tlv_parse(&tp, msgb_l3(msg), msgb_l3len(msg)) < 0) { + LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "%s(): rsl_tlv_parse() failed\n", __func__); + return rsl_tx_ipac_dlcx_nack(lchan, 0, RSL_ERR_PROTO); + } if (TLVP_PRESENT(&tp, RSL_IE_IPAC_CONN_ID)) inc_conn_id = 1; @@ -2217,9 +3188,7 @@ static int rsl_rx_ipac_dlcx(struct msgb *msg) if (lchan->abis_ip.rtp_socket) { osmo_rtp_socket_log_stats(lchan->abis_ip.rtp_socket, DRTP, LOGL_INFO, "Closing RTP socket on DLCX "); - osmo_rtp_socket_free(lchan->abis_ip.rtp_socket); - lchan->abis_ip.rtp_socket = NULL; - msgb_queue_flush(&lchan->dl_tch_queue); + lchan_rtp_socket_free(lchan); } return rc; } @@ -2233,7 +3202,7 @@ static int rsl_rx_ipac_dlcx(struct msgb *msg) static int rsl_tx_dyn_pdch_ack(struct gsm_lchan *lchan, bool pdch_act) { struct gsm_time *gtime = get_time(lchan->ts->trx->bts); - uint8_t chan_nr = gsm_lchan2chan_nr(lchan); + uint8_t chan_nr = gsm_lchan2chan_nr_rsl(lchan); struct msgb *msg; uint8_t ie[2]; @@ -2262,7 +3231,7 @@ static int rsl_tx_dyn_pdch_nack(struct gsm_lchan *lchan, bool pdch_act, uint8_t cause) { struct msgb *msg; - uint8_t chan_nr = gsm_lchan2chan_nr(lchan); + uint8_t chan_nr = gsm_lchan2chan_nr_rsl(lchan); LOGPLCHAN(lchan, DRSL, LOGL_NOTICE, "Tx PDCH %s NACK (cause = 0x%02x)\n", pdch_act ? "ACT" : "DEACT", cause); @@ -2371,7 +3340,6 @@ static void rsl_rx_dyn_pdch(struct msgb *msg, bool pdch_act) if (pdch_act) { /* Clear TCH state. Only first lchan matters for PDCH */ clear_lchan_for_pdch_activ(ts->lchan); - /* First, disconnect the TCH channel, to connect PDTCH later */ rc = bts_model_ts_disconnect(ts); } else { @@ -2400,14 +3368,12 @@ static void ipacc_dyn_pdch_ts_disconnected(struct gsm_bts_trx_ts *ts) enum gsm_phys_chan_config as_pchan; if (ts->flags & TS_F_PDCH_DEACT_PENDING) { - LOGP(DRSL, LOGL_DEBUG, - "%s PDCH DEACT operation: channel disconnected, will reconnect as TCH\n", - gsm_lchan_name(ts->lchan)); + LOGPLCHAN(ts->lchan, DRSL, LOGL_DEBUG, + "PDCH DEACT operation: channel disconnected, will reconnect as TCH\n"); as_pchan = GSM_PCHAN_TCH_F; } else if (ts->flags & TS_F_PDCH_ACT_PENDING) { - LOGP(DRSL, LOGL_DEBUG, - "%s PDCH ACT operation: channel disconnected, will reconnect as PDTCH\n", - gsm_lchan_name(ts->lchan)); + LOGPLCHAN(ts->lchan, DRSL, LOGL_DEBUG, + "PDCH ACT operation: channel disconnected, will reconnect as PDTCH\n"); as_pchan = GSM_PCHAN_PDCH; } else /* No reconnect pending. */ @@ -2434,6 +3400,7 @@ static void osmo_dyn_ts_disconnected(struct gsm_bts_trx_ts *ts) switch (ts->dyn.pchan_want) { case GSM_PCHAN_TCH_F: case GSM_PCHAN_TCH_H: + case GSM_PCHAN_SDCCH8_SACCH8C: case GSM_PCHAN_PDCH: break; default: @@ -2457,7 +3424,7 @@ void cb_ts_disconnected(struct gsm_bts_trx_ts *ts) switch (ts->pchan) { case GSM_PCHAN_TCH_F_PDCH: return ipacc_dyn_pdch_ts_disconnected(ts); - case GSM_PCHAN_TCH_F_TCH_H_PDCH: + case GSM_PCHAN_OSMO_DYN: return osmo_dyn_ts_disconnected(ts); default: return; @@ -2467,39 +3434,31 @@ void cb_ts_disconnected(struct gsm_bts_trx_ts *ts) static void ipacc_dyn_pdch_ts_connected(struct gsm_bts_trx_ts *ts, int rc) { if (rc) { - LOGP(DRSL, LOGL_NOTICE, "%s PDCH ACT IPA operation failed (%d) in bts model\n", - gsm_lchan_name(ts->lchan), rc); + LOGPLCHAN(ts->lchan, DRSL, LOGL_NOTICE, "PDCH ACT IPA operation failed (%d) in bts model\n", rc); ipacc_dyn_pdch_complete(ts, rc); return; } if (ts->flags & TS_F_PDCH_DEACT_PENDING) { - if (ts->lchan[0].type != GSM_LCHAN_TCH_F) - LOGP(DRSL, LOGL_ERROR, "%s PDCH DEACT error:" - " timeslot connected, so expecting" - " lchan type TCH/F, but is %s\n", - gsm_lchan_name(ts->lchan), - gsm_lchant_name(ts->lchan[0].type)); + if (ts->lchan[0].type != GSM_LCHAN_TCH_F) { + LOGPLCHAN(ts->lchan, DRSL, LOGL_ERROR, "PDCH DEACT error: timeslot connected, so " + "expecting lchan type TCH/F, but is %s\n", gsm_lchant_name(ts->lchan[0].type)); + } - LOGP(DRSL, LOGL_DEBUG, "%s PDCH DEACT operation:" - " timeslot connected as TCH/F\n", - gsm_lchan_name(ts->lchan)); + LOGPLCHAN(ts->lchan, DRSL, LOGL_DEBUG, "PDCH DEACT operation: timeslot connected as TCH/F\n"); /* During PDCH DEACT, we're done right after the TCH/F came * back up. */ ipacc_dyn_pdch_complete(ts, 0); } else if (ts->flags & TS_F_PDCH_ACT_PENDING) { - if (ts->lchan[0].type != GSM_LCHAN_PDTCH) - LOGP(DRSL, LOGL_ERROR, "%s PDCH ACT error:" - " timeslot connected, so expecting" - " lchan type PDTCH, but is %s\n", - gsm_lchan_name(ts->lchan), + if (ts->lchan[0].type != GSM_LCHAN_PDTCH) { + LOGPLCHAN(ts->lchan, DRSL, LOGL_ERROR, "PDCH ACT error: timeslot connected, " + "so expecting lchan type PDTCH, but is %s\n", gsm_lchant_name(ts->lchan[0].type)); + } - LOGP(DRSL, LOGL_DEBUG, "%s PDCH ACT operation:" - " timeslot connected as PDTCH\n", - gsm_lchan_name(ts->lchan)); + LOGPLCHAN(ts->lchan, DRSL, LOGL_DEBUG, "PDCH ACT operation: timeslot connected as PDTCH\n"); /* The PDTCH is connected, now tell the PCU about it. Except * when the PCU is not connected (yet), then there's nothing @@ -2522,30 +3481,31 @@ static void ipacc_dyn_pdch_ts_connected(struct gsm_bts_trx_ts *ts, int rc) static void osmo_dyn_ts_connected(struct gsm_bts_trx_ts *ts, int rc) { - struct msgb *msg = ts->dyn.pending_chan_activ; - ts->dyn.pending_chan_activ = NULL; + unsigned int ln; if (rc) { - LOGP(DRSL, LOGL_NOTICE, "%s PDCH ACT OSMO operation failed (%d) in bts model\n", - gsm_lchan_name(ts->lchan), rc); + LOGPLCHAN(ts->lchan, DRSL, LOGL_NOTICE, "PDCH ACT OSMO operation failed (%d) in bts model\n", rc); ipacc_dyn_pdch_complete(ts, rc); return; } - if (!msg) { - LOGP(DRSL, LOGL_ERROR, - "%s TS re-connected, but no chan activ msg pending\n", - gsm_ts_and_pchan_name(ts)); - return; - } - ts->dyn.pchan_is = ts->dyn.pchan_want; DEBUGP(DRSL, "%s Connected\n", gsm_ts_and_pchan_name(ts)); - /* continue where we left off before re-connecting the TS. */ - rc = rsl_rx_chan_activ(msg); - if (rc != 1) - msgb_free(msg); + /* Handle postponed RSL CHANnel ACTIVation messages (if any) */ + for (ln = 0; ln < ARRAY_SIZE(ts->lchan); ln++) { + struct gsm_lchan *lchan = &ts->lchan[ln]; + + if (lchan->pending_chan_activ == NULL) + continue; + + struct msgb *msg = lchan->pending_chan_activ; + lchan->pending_chan_activ = NULL; + + /* Continue where we left off before re-connecting the TS */ + if (rsl_rx_chan_activ(msg) != 1) + msgb_free(msg); + } } void cb_ts_connected(struct gsm_bts_trx_ts *ts, int rc) @@ -2555,7 +3515,7 @@ void cb_ts_connected(struct gsm_bts_trx_ts *ts, int rc) switch (ts->pchan) { case GSM_PCHAN_TCH_F_PDCH: return ipacc_dyn_pdch_ts_connected(ts, rc); - case GSM_PCHAN_TCH_F_TCH_H_PDCH: + case GSM_PCHAN_OSMO_DYN: return osmo_dyn_ts_connected(ts, rc); default: return; @@ -2569,10 +3529,10 @@ void ipacc_dyn_pdch_complete(struct gsm_bts_trx_ts *ts, int rc) pdch_act = ts->flags & TS_F_PDCH_ACT_PENDING; - if ((ts->flags & TS_F_PDCH_PENDING_MASK) == TS_F_PDCH_PENDING_MASK) - LOGP(DRSL, LOGL_ERROR, - "%s Internal Error: both PDCH ACT and PDCH DEACT pending\n", - gsm_lchan_name(ts->lchan)); + if ((ts->flags & TS_F_PDCH_PENDING_MASK) == TS_F_PDCH_PENDING_MASK) { + LOGPLCHAN(ts->lchan, DRSL, LOGL_ERROR, + "Internal Error: both PDCH ACT and PDCH DEACT pending\n"); + } ts->flags &= ~TS_F_PDCH_PENDING_MASK; @@ -2588,9 +3548,8 @@ void ipacc_dyn_pdch_complete(struct gsm_bts_trx_ts *ts, int rc) ts->flags |= TS_F_PDCH_ACTIVE; else ts->flags &= ~TS_F_PDCH_ACTIVE; - DEBUGP(DRSL, "%s %s switched to %s mode (ts->flags == %x)\n", - gsm_lchan_name(ts->lchan), gsm_pchan_name(ts->pchan), - pdch_act? "PDCH" : "TCH/F", ts->flags); + LOGPLCHAN(ts->lchan, DRSL, LOGL_DEBUG, "%s switched to %s mode (ts->flags == %x)\n", + gsm_pchan_name(ts->pchan), pdch_act ? "PDCH" : "TCH/F", ts->flags); rc = rsl_tx_dyn_pdch_ack(ts->lchan, pdch_act); if (rc) @@ -2692,8 +3651,11 @@ static int rsl_rx_rll(struct gsm_bts_trx *trx, struct msgb *msg) return -1; } - DEBUGP(DRLL, "%s Rx RLL %s Abis -> LAPDm\n", gsm_lchan_name(lchan), - rsl_msg_name(rh->c.msg_type)); + /* VGCS Uplink is released by MSC using REL-REQ. */ + if (rh->c.msg_type == RSL_MT_REL_REQ) + vgcs_talker_reset(lchan, true); + + LOGPLCHAN(lchan, DRLL, LOGL_DEBUG, "Rx RLL %s Abis -> LAPDm\n", rsl_msg_name(rh->c.msg_type)); /* make copy of RLL header, as the message will be free'd in case of erroneous return */ rh2 = *rh; @@ -2788,15 +3750,15 @@ static int handle_gprs_susp_req(struct msgb *msg) int rc; if (!gh || msgb_l3len(msg) < sizeof(*gh)+sizeof(*gsr)) { - LOGP(DRSL, LOGL_NOTICE, "%s Short GPRS SUSPEND REQ received, ignoring\n", gsm_lchan_name(msg->lchan)); + LOGPLCHAN(msg->lchan, DRSL, LOGL_NOTICE, "Short GPRS SUSPEND REQ received, ignoring\n"); + msgb_free(msg); return -EINVAL; } gsr = (struct gsm48_gprs_susp_req *) gh->data; tlli = osmo_ntohl(gsr->tlli); - LOGP(DRSL, LOGL_INFO, "%s Fwd GPRS SUSPEND REQ for TLLI=0x%08x to PCU\n", - gsm_lchan_name(msg->lchan), tlli); + LOGPLCHAN(msg->lchan, DRSL, LOGL_INFO, "Fwd GPRS SUSPEND REQ for TLLI=0x%08x to PCU\n", tlli); rc = pcu_tx_susp_req(msg->lchan, tlli, gsr->ra_id, gsr->cause); msgb_free(msg); @@ -2804,16 +3766,6 @@ static int handle_gprs_susp_req(struct msgb *msg) return rc; } -static inline uint8_t ms_to2rsl(const struct gsm_lchan *lchan, const struct lapdm_entity *le) -{ - return (lchan->ms_t_offs >= 0) ? lchan->ms_t_offs : (lchan->p_offs - le->ta); -} - -static inline bool ms_to_valid(const struct gsm_lchan *lchan) -{ - return (lchan->ms_t_offs >= 0) || (lchan->p_offs >= 0); -} - struct osmo_bts_supp_meas_info { int16_t toa256_mean; int16_t toa256_min; @@ -2821,12 +3773,12 @@ struct osmo_bts_supp_meas_info { uint16_t toa256_std_dev; } __attribute__((packed)); -/* 8.4.8 MEASUREMENT RESult */ -static int rsl_tx_meas_res(struct gsm_lchan *lchan, uint8_t *l3, int l3_len, const struct lapdm_entity *le) +/* Compose and send 8.4.8 MEASUREMENT RESult via RSL. (timing_offset=-1 -> not present) */ +int rsl_tx_meas_res(struct gsm_lchan *lchan, const uint8_t *l3, unsigned int l3_len, int timing_offset) { struct msgb *msg; uint8_t meas_res[16]; - uint8_t chan_nr = gsm_lchan2chan_nr(lchan); + uint8_t chan_nr = gsm_lchan2chan_nr_rsl(lchan); int res_valid = lchan->meas.flags & LC_UL_M_F_RES_VALID; struct gsm_bts *bts = lchan->ts->trx->bts; @@ -2841,20 +3793,19 @@ static int rsl_tx_meas_res(struct gsm_lchan *lchan, uint8_t *l3, int l3_len, con return -ENOMEM; LOGPLCHAN(lchan, DRSL, LOGL_DEBUG, - "Send Meas RES: NUM:%u, RXLEV_FULL:%u, RXLEV_SUB:%u, RXQUAL_FULL:%u, RXQUAL_SUB:%u, MS_PWR:%u, UL_TA:%u, L3_LEN:%d, TimingOff:%u\n", + "Send Meas RES: NUM:%u, RXLEV_FULL:%u, RXLEV_SUB:%u, RXQUAL_FULL:%u, RXQUAL_SUB:%u, MS_PWR:%u, UL_TA:%u, L3_LEN:%u, TimingOff:%u\n", lchan->meas.res_nr, lchan->meas.ul_res.full.rx_lev, lchan->meas.ul_res.sub.rx_lev, lchan->meas.ul_res.full.rx_qual, lchan->meas.ul_res.sub.rx_qual, - lchan->meas.l1_info[0], - lchan->meas.l1_info[1], l3_len, ms_to2rsl(lchan, le) - MEAS_MAX_TIMING_ADVANCE); + lchan->meas.l1_info.ms_pwr, + lchan->meas.l1_info.ta, l3_len, timing_offset - MEAS_MAX_TIMING_ADVANCE); - msgb_tv_put(msg, RSL_IE_MEAS_RES_NR, lchan->meas.res_nr++); + msgb_tv_put(msg, RSL_IE_MEAS_RES_NR, lchan->meas.res_nr); size_t ie_len = gsm0858_rsl_ul_meas_enc(&lchan->meas.ul_res, lchan->tch.dtx.dl_active, meas_res); - lchan->tch.dtx.dl_active = false; if (ie_len >= 3) { if (bts->supp_meas_toa256 && lchan->meas.flags & LC_UL_M_F_OSMO_EXT_VALID) { struct osmo_bts_supp_meas_info *smi; @@ -2867,26 +3818,23 @@ static int rsl_tx_meas_res(struct gsm_lchan *lchan, uint8_t *l3, int l3_len, con * to know the total propagation time between MS and BTS, we need to add * the actual TA value applied by the MS plus the respective toa256 value in * 1/256 symbol periods. */ - int16_t ta256 = lchan_get_ta(lchan) * 256; + int16_t ta256 = lchan->meas.l1_info.ta * 256; smi->toa256_mean = htons(ta256 + lchan->meas.ms_toa256); smi->toa256_min = htons(ta256 + lchan->meas.ext.toa256_min); smi->toa256_max = htons(ta256 + lchan->meas.ext.toa256_max); smi->toa256_std_dev = htons(lchan->meas.ext.toa256_std_dev); - lchan->meas.flags &= ~LC_UL_M_F_OSMO_EXT_VALID; } msgb_tlv_put(msg, RSL_IE_UPLINK_MEAS, ie_len, meas_res); - lchan->meas.flags &= ~LC_UL_M_F_RES_VALID; } - msgb_tv_put(msg, RSL_IE_BS_POWER, lchan->meas.bts_tx_pwr); + msgb_tv_put(msg, RSL_IE_BS_POWER, lchan->bs_power_ctrl.current / 2); if (lchan->meas.flags & LC_UL_M_F_L1_VALID) { - msgb_tv_fixed_put(msg, RSL_IE_L1_INFO, 2, lchan->meas.l1_info); - lchan->meas.flags &= ~LC_UL_M_F_L1_VALID; + msgb_tv_fixed_put(msg, RSL_IE_L1_INFO, sizeof(lchan->meas.l1_info), (uint8_t*)&lchan->meas.l1_info); } - msgb_tl16v_put(msg, RSL_IE_L3_INFO, l3_len, l3); - if (ms_to_valid(lchan)) { - msgb_tv_put(msg, RSL_IE_MS_TIMING_OFFSET, ms_to2rsl(lchan, le)); - lchan->ms_t_offs = -1; - lchan->p_offs = -1; + + if (l3 && l3_len > 0) { + msgb_tl16v_put(msg, RSL_IE_L3_INFO, l3_len, l3); + if (timing_offset != -1) + msgb_tv_put(msg, RSL_IE_MS_TIMING_OFFSET, timing_offset); } rsl_dch_push_hdr(msg, RSL_MT_MEAS_RES, chan_nr); @@ -2914,22 +3862,42 @@ int lapdm_rll_tx_cb(struct msgb *msg, struct lapdm_entity *le, void *ctx) msg->trx = lchan->ts->trx; msg->lchan = lchan; - /* check if this is a measurement report from SACCH which needs special - * processing before forwarding */ - if (rslms_is_meas_rep(msg)) { - int rc; + /* If DL estabishment on main signaling link and SAPI 0 with L3 info is expected. */ + if (lchan->l3_info_estab && rh->msg_type == RSL_MT_EST_IND) { + struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); + if ((rllh->link_id & 0xc7) == 0) { + /* Reject initial establishment without L3 info. */ + if (msgb_l2len(msg) == sizeof(struct abis_rsl_rll_hdr)) { + LOGPLCHAN(lchan, DRSL, LOGL_ERROR, "RLL EST IND without contention resolution.\n"); + /* Release normally, re-use the msgb. */ + rh->msg_type = RSL_MT_REL_REQ; + msgb_tv_put(msg, RSL_IE_RELEASE_MODE, RSL_REL_NORMAL); + return rsl_rx_rll(lchan->ts->trx, msg); + } + /* Re-estabishment without contention resoltuion is allowed. */ + lchan->l3_info_estab = false; + } + } - LOGPLCHAN(lchan, DRSL, LOGL_INFO, "Handing RLL msg %s from LAPDm to MEAS REP\n", + /* If this is a Measurement Report, then we simply ignore it, + * because it has already been processed in l1sap_ph_data_ind(). */ + if (rslms_is_meas_rep(msg)) { + msgb_free(msg); + return 0; + } else if (rslms_is_gprs_susp_req(msg)) { + return handle_gprs_susp_req(msg); + } else { + LOGPLCHAN(lchan, DRSL, LOGL_INFO, "Fwd RLL msg %s from LAPDm to A-bis\n", rsl_msg_name(rh->msg_type)); /* REL_IND handling */ - if (rh->msg_type == RSL_MT_REL_IND && - (lchan->type == GSM_LCHAN_TCH_F || lchan->type == GSM_LCHAN_TCH_H)) { + if (rh->msg_type == RSL_MT_REL_IND && lchan_is_tch(lchan)) { + vgcs_talker_reset(lchan, true); LOGPLCHAN(lchan, DRSL, LOGL_INFO, "Scheduling %s to L3 in next associated TCH-RTS.ind\n", rsl_msg_name(rh->msg_type)); - if(lchan->pending_rel_ind_msg) { + if (lchan->pending_rel_ind_msg) { LOGPLCHAN(lchan, DRSL, LOGL_INFO, "Dropping pending release indication message\n"); msgb_free(lchan->pending_rel_ind_msg); @@ -2939,15 +3907,6 @@ int lapdm_rll_tx_cb(struct msgb *msg, struct lapdm_entity *le, void *ctx) return 0; } - rc = rsl_tx_meas_res(lchan, msgb_l3(msg), msgb_l3len(msg), le); - msgb_free(msg); - return rc; - } else if (rslms_is_gprs_susp_req(msg)) { - return handle_gprs_susp_req(msg); - } else { - LOGPLCHAN(lchan, DRSL, LOGL_INFO, "Fwd RLL msg %s from LAPDm to A-bis\n", - rsl_msg_name(rh->msg_type)); - return abis_bts_rsl_sendmsg(msg); } } @@ -2994,8 +3953,10 @@ static int rsl_rx_cchan(struct gsm_bts_trx *trx, struct msgb *msg) case RSL_MT_SMS_BC_CMD: ret = rsl_rx_sms_bcast_cmd(trx, msg); break; - case RSL_MT_SMS_BC_REQ: case RSL_MT_NOT_CMD: + ret = rsl_rx_notification_cmd(trx, msg); + break; + case RSL_MT_SMS_BC_REQ: LOGPLCHAN(msg->lchan, DRSL, LOGL_NOTICE, "unimplemented RSL cchan msg_type %s\n", rsl_msg_name(cch->c.msg_type)); rsl_tx_error_report(trx, RSL_ERR_MSG_TYPE, &cch->chan_nr, NULL, msg); @@ -3003,6 +3964,10 @@ static int rsl_rx_cchan(struct gsm_bts_trx *trx, struct msgb *msg) case RSL_MT_OSMO_ETWS_CMD: ret = rsl_rx_osmo_etws_cmd(trx, msg); break; + /* Osmocom specific extension for BCCH carrier power reduction */ + case RSL_MT_BS_POWER_CONTROL: + ret = rsl_rx_bs_pwr_ctrl(msg); + break; default: LOGPLCHAN(msg->lchan, DRSL, LOGL_NOTICE, "undefined RSL cchan msg_type 0x%02x\n", cch->c.msg_type); @@ -3113,6 +4078,9 @@ static int rsl_rx_trx(struct gsm_bts_trx *trx, struct msgb *msg) case RSL_MT_SACCH_FILL: ret = rsl_rx_sacch_fill(trx, msg); break; + case RSL_MT_IPAC_MEAS_PREPROC_DFT: + ret = rsl_rx_meas_preproc_dft(trx, msg); + break; default: LOGP(DRSL, LOGL_NOTICE, "undefined RSL TRX msg_type 0x%02x\n", th->msg_type); @@ -3171,14 +4139,6 @@ static int rsl_rx_ipaccess(struct gsm_bts_trx *trx, struct msgb *msg) return ret; } -int lchan_deactivate(struct gsm_lchan *lchan) -{ - OSMO_ASSERT(lchan); - - lchan->ciph_state = 0; - return bts_model_lchan_deactivate(lchan); -} - int down_rsl(struct gsm_bts_trx *trx, struct msgb *msg) { struct abis_rsl_common_hdr *rslh; |