diff options
Diffstat (limited to 'src/common/l1sap.c')
-rw-r--r-- | src/common/l1sap.c | 1706 |
1 files changed, 1357 insertions, 349 deletions
diff --git a/src/common/l1sap.c b/src/common/l1sap.c index f07e79ca..5f275cd8 100644 --- a/src/common/l1sap.c +++ b/src/common/l1sap.c @@ -13,7 +13,7 @@ * 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/>. @@ -33,10 +33,13 @@ #include <osmocom/gsm/l1sap.h> #include <osmocom/gsm/gsm_utils.h> #include <osmocom/gsm/rsl.h> +#include <osmocom/gsm/rlp.h> #include <osmocom/core/gsmtap.h> #include <osmocom/core/gsmtap_util.h> #include <osmocom/core/utils.h> +#include <osmocom/codec/codec.h> + #include <osmocom/trau/osmo_ortp.h> #include <osmo-bts/logging.h> @@ -51,41 +54,17 @@ #include <osmo-bts/abis.h> #include <osmo-bts/bts_model.h> #include <osmo-bts/handover.h> -#include <osmo-bts/power_control.h> #include <osmo-bts/msg_utils.h> +#include <osmo-bts/rtp_input_preen.h> #include <osmo-bts/pcuif_proto.h> #include <osmo-bts/cbch.h> - - -#define CB_FCCH -1 -#define CB_SCH -2 -#define CB_BCCH -3 -#define CB_IDLE -4 - -/* according to TS 05.02 Clause 7 Table 3 of 9 an Figure 8a */ -static const int ccch_block_table[51] = { - CB_FCCH, CB_SCH,/* 0..1 */ - CB_BCCH, CB_BCCH, CB_BCCH, CB_BCCH, /* 2..5: BCCH */ - 0, 0, 0, 0, /* 6..9: B0 */ - CB_FCCH, CB_SCH,/* 10..11 */ - 1, 1, 1, 1, /* 12..15: B1 */ - 2, 2, 2, 2, /* 16..19: B2 */ - CB_FCCH, CB_SCH,/* 20..21 */ - 3, 3, 3, 3, /* 22..25: B3 */ - 4, 4, 4, 4, /* 26..29: B4 */ - CB_FCCH, CB_SCH,/* 30..31 */ - 5, 5, 5, 5, /* 32..35: B5 */ - 6, 6, 6, 6, /* 36..39: B6 */ - CB_FCCH, CB_SCH,/* 40..41 */ - 7, 7, 7, 7, /* 42..45: B7 */ - 8, 8, 8, 8, /* 46..49: B8 */ - -4 /* 50: Idle */ -}; +#include <osmo-bts/asci.h> +#include <osmo-bts/csd_v110.h> /* determine the CCCH block number based on the frame number */ unsigned int l1sap_fn2ccch_block(uint32_t fn) { - int rc = ccch_block_table[fn%51]; + int rc = gsm0502_fn2ccch_block(fn); /* if FN is negative, we were called for something that's not CCCH! */ OSMO_ASSERT(rc >= 0); return rc; @@ -94,18 +73,25 @@ unsigned int l1sap_fn2ccch_block(uint32_t fn) struct gsm_lchan *get_lchan_by_chan_nr(struct gsm_bts_trx *trx, unsigned int chan_nr) { + struct gsm_bts_trx_ts *ts; unsigned int tn, ss; tn = L1SAP_CHAN2TS(chan_nr); - OSMO_ASSERT(tn < ARRAY_SIZE(trx->ts)); + ts = &trx->ts[tn]; + + if (L1SAP_IS_CHAN_VAMOS(chan_nr)) { + if (ts->vamos.peer == NULL) + return NULL; + ts = ts->vamos.peer; + } if (L1SAP_IS_CHAN_CBCH(chan_nr)) ss = 2; /* CBCH is always on sub-slot 2 */ else ss = l1sap_chan2ss(chan_nr); - OSMO_ASSERT(ss < ARRAY_SIZE(trx->ts[tn].lchan)); + OSMO_ASSERT(ss < ARRAY_SIZE(ts->lchan)); - return &trx->ts[tn].lchan[ss]; + return &ts->lchan[ss]; } static struct gsm_lchan * @@ -131,7 +117,8 @@ static uint32_t fn_ms_adj(uint32_t fn, const struct gsm_lchan *lchan) /* 12/13 frames usable for audio in TCH, 160 samples per RTP packet, 1 RTP packet per 4 frames */ - samples_passed = (fn - lchan->tch.last_fn) * 12 * 160 / (13 * 4); + const uint32_t num_fn = GSM_TDMA_FN_SUB(fn, lchan->tch.last_fn); + samples_passed = num_fn * 12 * 160 / (13 * 4); /* round number of samples to the nearest multiple of GSM_RTP_DURATION */ r = samples_passed + GSM_RTP_DURATION / 2; @@ -145,27 +132,13 @@ static uint32_t fn_ms_adj(uint32_t fn, const struct gsm_lchan *lchan) return GSM_RTP_DURATION; } -/*! limit number of queue entries to %u; drops any surplus messages */ -static void queue_limit_to(const char *prefix, struct llist_head *queue, unsigned int limit) -{ - unsigned int count = llist_count(queue); - - if (count > limit) - LOGP(DL1P, LOGL_NOTICE, "%s: freeing %d queued frames\n", prefix, count-limit); - while (count > limit) { - struct msgb *tmp = msgb_dequeue(queue); - msgb_free(tmp); - count--; - } -} - /* allocate a msgb containing a osmo_phsap_prim + optional l2 data - * in order to wrap femtobts header arround l2 data, there must be enough space + * in order to wrap femtobts header around l2 data, there must be enough space * in front and behind data pointer */ struct msgb *l1sap_msgb_alloc(unsigned int l2_len) { - int headroom = 128; - int size = headroom + sizeof(struct osmo_phsap_prim) + l2_len; + const int headroom = L1SAP_MSGB_HEADROOM; + const int size = headroom + sizeof(struct osmo_phsap_prim) + l2_len; struct msgb *msg = msgb_alloc_headroom(size, headroom, "l1sap_prim"); if (!msg) @@ -176,9 +149,15 @@ struct msgb *l1sap_msgb_alloc(unsigned int l2_len) return msg; } +/* Enclose rmsg into an osmo_phsap primitive and hand it over to the higher + * layers. The phsap primitive also contains measurement information. The + * parameters rssi, ta_offs and is_sub are only needed when the measurement + * information is passed along with the TCH data. When separate measurement + * indications are used, those last three parameters may be set to zero. */ int add_l1sap_header(struct gsm_bts_trx *trx, struct msgb *rmsg, struct gsm_lchan *lchan, uint8_t chan_nr, uint32_t fn, - uint16_t ber10k, int16_t lqual_cb) + uint16_t ber10k, int16_t lqual_cb, int8_t rssi, + int16_t ta_offs, uint8_t is_sub) { struct osmo_phsap_prim *l1sap; @@ -194,6 +173,10 @@ int add_l1sap_header(struct gsm_bts_trx *trx, struct msgb *rmsg, l1sap->u.tch.ber10k = ber10k; l1sap->u.tch.lqual_cb = lqual_cb; + l1sap->u.tch.rssi = rssi; + l1sap->u.tch.ta_offs_256bits = ta_offs; + l1sap->u.tch.is_sub = is_sub; + return l1sap_up(trx, l1sap); } @@ -257,37 +240,96 @@ int bts_check_for_ciph_cmd(struct msgb *msg, struct gsm_lchan *lchan, return check_for_ciph_cmd(msg, lchan, chan_nr); } -struct gsmtap_inst *gsmtap = NULL; -uint32_t gsmtap_sapi_mask = 0; -uint8_t gsmtap_sapi_acch = 0; - -const struct value_string gsmtap_sapi_names[] = { - { GSMTAP_CHANNEL_BCCH, "BCCH" }, - { GSMTAP_CHANNEL_CCCH, "CCCH" }, - { GSMTAP_CHANNEL_RACH, "RACH" }, - { GSMTAP_CHANNEL_AGCH, "AGCH" }, - { GSMTAP_CHANNEL_PCH, "PCH" }, - { GSMTAP_CHANNEL_SDCCH, "SDCCH" }, - { GSMTAP_CHANNEL_TCH_F, "TCH/F" }, - { GSMTAP_CHANNEL_TCH_H, "TCH/H" }, - { GSMTAP_CHANNEL_PACCH, "PACCH" }, - { GSMTAP_CHANNEL_PDCH, "PDTCH" }, - { GSMTAP_CHANNEL_PTCCH, "PTCCH" }, - { GSMTAP_CHANNEL_CBCH51,"CBCH" }, - { GSMTAP_CHANNEL_ACCH, "SACCH" }, +uint16_t l1sap_log_ctx_sapi; + +const struct value_string l1sap_common_sapi_names[] = { + { L1SAP_COMMON_SAPI_UNKNOWN, "UNKNOWN" }, + /* alphabetic order */ + { L1SAP_COMMON_SAPI_AGCH, "AGCH" }, + { L1SAP_COMMON_SAPI_BCCH, "BCCH" }, + { L1SAP_COMMON_SAPI_CBCH, "CBCH" }, + { L1SAP_COMMON_SAPI_FACCH_F, "FACCH/F" }, + { L1SAP_COMMON_SAPI_FACCH_H, "FACCH/H" }, + { L1SAP_COMMON_SAPI_FCCH, "FCCH" }, + { L1SAP_COMMON_SAPI_IDLE, "IDLE" }, + { L1SAP_COMMON_SAPI_NCH, "NCH" }, + { L1SAP_COMMON_SAPI_PACCH, "PACCH" }, + { L1SAP_COMMON_SAPI_PAGCH, "PAGCH" }, + { L1SAP_COMMON_SAPI_PBCCH, "PBCCH" }, + { L1SAP_COMMON_SAPI_PCH, "PCH" }, + { L1SAP_COMMON_SAPI_PDTCH, "PDTCH" }, + { L1SAP_COMMON_SAPI_PNCH, "PNCH" }, + { L1SAP_COMMON_SAPI_PPCH, "PPCH" }, + { L1SAP_COMMON_SAPI_PRACH, "PRACH" }, + { L1SAP_COMMON_SAPI_PTCCH, "PTCCH" }, + { L1SAP_COMMON_SAPI_RACH, "RACH" }, + { L1SAP_COMMON_SAPI_SACCH, "SACCH" }, + { L1SAP_COMMON_SAPI_SCH, "SCH" }, + { L1SAP_COMMON_SAPI_SDCCH, "SDCCH" }, + { L1SAP_COMMON_SAPI_TCH_F, "TCH/F" }, + { L1SAP_COMMON_SAPI_TCH_H, "TCH/H" }, { 0, NULL } }; +static enum l1sap_common_sapi get_common_sapi_ph_data(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap) +{ + uint8_t link_id = l1sap->u.data.link_id; + uint8_t chan_nr = l1sap->u.data.chan_nr; + uint32_t u32Fn = l1sap->u.data.fn; + + if (L1SAP_IS_CHAN_TCHF(chan_nr)) + return L1SAP_COMMON_SAPI_TCH_F; + + if (L1SAP_IS_CHAN_TCHH(chan_nr)) + return L1SAP_COMMON_SAPI_TCH_H; + + if (L1SAP_IS_CHAN_SDCCH4(chan_nr) || L1SAP_IS_CHAN_SDCCH8(chan_nr)) + return L1SAP_COMMON_SAPI_SDCCH; + + if (L1SAP_IS_CHAN_BCCH(chan_nr)) + return L1SAP_COMMON_SAPI_BCCH; + + if (L1SAP_IS_CHAN_AGCH_PCH(chan_nr)) + /* The sapi depends on DSP configuration, not on the actual SYSTEM INFORMATION 3. */ + return ((l1sap_fn2ccch_block(u32Fn) >= num_agch(trx, "PH-DATA-REQ")) + ? L1SAP_COMMON_SAPI_PCH + : L1SAP_COMMON_SAPI_AGCH); + + if (L1SAP_IS_CHAN_CBCH(chan_nr)) + return L1SAP_COMMON_SAPI_CBCH; + + if (L1SAP_IS_LINK_SACCH(link_id)) + return L1SAP_COMMON_SAPI_SACCH; + + return L1SAP_COMMON_SAPI_UNKNOWN; +} + +static enum l1sap_common_sapi get_common_sapi_by_trx_prim(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap) +{ + /* Only downlink prims are relevant */ + switch (OSMO_PRIM_HDR(&l1sap->oph)) { + case OSMO_PRIM(PRIM_PH_DATA, PRIM_OP_REQUEST): + if (ts_is_pdch(&trx->ts[L1SAP_CHAN2TS(l1sap->u.data.chan_nr)])) + return ((L1SAP_IS_PTCCH(l1sap->u.data.fn)) + ? L1SAP_COMMON_SAPI_PTCCH + : L1SAP_COMMON_SAPI_PDTCH); + return get_common_sapi_ph_data(trx, l1sap); + default: + return L1SAP_COMMON_SAPI_UNKNOWN; + } +} + /* send primitive as gsmtap */ -static int gsmtap_ph_data(struct osmo_phsap_prim *l1sap, uint8_t *chan_type, - uint8_t *ss, uint32_t fn, uint8_t **data, unsigned int *len, +static int gsmtap_ph_data(const struct osmo_phsap_prim *l1sap, + uint8_t *chan_type, uint8_t *ss, uint32_t fn, + uint8_t **data, unsigned int *len, uint8_t num_agch) { struct msgb *msg = l1sap->oph.msg; uint8_t chan_nr, link_id; *data = msgb_l2(msg); - *len = msgb_l2len(msg); + *len = msgb_l2(msg) ? msgb_l2len(msg) : 0; chan_nr = l1sap->u.data.chan_nr; link_id = l1sap->u.data.link_id; @@ -322,47 +364,63 @@ static int gsmtap_ph_data(struct osmo_phsap_prim *l1sap, uint8_t *chan_type, return 0; } -static int gsmtap_pdch(struct osmo_phsap_prim *l1sap, uint8_t *chan_type, - uint8_t *ss, uint32_t fn, uint8_t **data, unsigned int *len) +static int gsmtap_pdch(const struct osmo_phsap_prim *l1sap, + uint8_t *chan_type, uint8_t *ss, uint32_t fn, + uint8_t **data, unsigned int *len) { struct msgb *msg = l1sap->oph.msg; *data = msgb_l2(msg); - *len = msgb_l2len(msg); + *len = msgb_l2(msg) ? msgb_l2len(msg) : 0; if (L1SAP_IS_PTCCH(fn)) { *chan_type = GSMTAP_CHANNEL_PTCCH; *ss = L1SAP_FN2PTCCHBLOCK(fn); - if (l1sap->oph.primitive == PRIM_OP_INDICATION) { - OSMO_ASSERT(len > 0); - if ((*data[0]) == 7) - return -EINVAL; - (*data)++; - (*len)--; - } - } else - *chan_type = GSMTAP_CHANNEL_PACCH; + } else { + /* TODO: distinguish PACCH */ + *chan_type = GSMTAP_CHANNEL_PDTCH; + } return 0; } -static int gsmtap_ph_rach(struct osmo_phsap_prim *l1sap, uint8_t *chan_type, - uint8_t *tn, uint8_t *ss, uint32_t *fn, uint8_t **data, unsigned int *len) +static int gsmtap_ph_rach(const struct osmo_phsap_prim *l1sap, uint8_t *chan_type, + uint8_t *tn, uint8_t *ss, uint32_t *fn, + uint8_t **data, unsigned int *len) { - uint8_t chan_nr; + uint8_t chan_nr = l1sap->u.rach_ind.chan_nr; + static uint8_t ra_buf[2]; *chan_type = GSMTAP_CHANNEL_RACH; *fn = l1sap->u.rach_ind.fn; - *tn = L1SAP_CHAN2TS(l1sap->u.rach_ind.chan_nr); - chan_nr = l1sap->u.rach_ind.chan_nr; + *tn = L1SAP_CHAN2TS(chan_nr); + if (L1SAP_IS_CHAN_TCHH(chan_nr)) *ss = L1SAP_CHAN2SS_TCHH(chan_nr); else if (L1SAP_IS_CHAN_SDCCH4(chan_nr)) *ss = L1SAP_CHAN2SS_SDCCH4(chan_nr); else if (L1SAP_IS_CHAN_SDCCH8(chan_nr)) *ss = L1SAP_CHAN2SS_SDCCH8(chan_nr); - *data = (uint8_t *)&l1sap->u.rach_ind.ra; - *len = 1; + else if (L1SAP_IS_CHAN_PDCH(chan_nr)) { + if (L1SAP_IS_PTCCH(*fn)) { + /* TODO: calculate sub-slot from frame-number */ + *chan_type = GSMTAP_CHANNEL_PTCCH; + } else { + *chan_type = GSMTAP_CHANNEL_PDTCH; + } + } + + if (l1sap->u.rach_ind.is_11bit) { + /* Pack as described in 3GPP TS 44.004, figure 7.4a.b */ + ra_buf[0] = (uint8_t) (l1sap->u.rach_ind.ra >> 3); + ra_buf[1] = (uint8_t) (l1sap->u.rach_ind.ra & 0x07); + *len = sizeof(ra_buf); + *data = ra_buf; + } else { + ra_buf[0] = (uint8_t) (l1sap->u.rach_ind.ra & 0xff); + *len = sizeof(ra_buf[0]); + *data = ra_buf; + } return 0; } @@ -375,8 +433,14 @@ static const uint8_t paging_fill[GSM_MACBLOCK_LEN] = { static bool is_fill_frame(uint8_t chan_type, const uint8_t *data, unsigned int len) { + if (len != GSM_MACBLOCK_LEN) + return false; + switch (chan_type) { case GSMTAP_CHANNEL_AGCH: + case GSMTAP_CHANNEL_SDCCH: + case GSMTAP_CHANNEL_TCH_F: + case GSMTAP_CHANNEL_TCH_H: if (!memcmp(data, fill_frame, GSM_MACBLOCK_LEN)) return true; break; @@ -384,6 +448,7 @@ static bool is_fill_frame(uint8_t chan_type, const uint8_t *data, unsigned int l if (!memcmp(data, paging_fill, GSM_MACBLOCK_LEN)) return true; break; + /* FIXME: implement the same for GSMTAP_CHANNEL_PDTCH from/to PCU */ /* don't use 'default' case here as the above only conditionally return true */ } return false; @@ -396,9 +461,11 @@ static int to_gsmtap(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap) uint8_t chan_type = 0, tn = 0, ss = 0; uint32_t fn; uint16_t uplink = GSMTAP_ARFCN_F_UPLINK; + int8_t signal_dbm; int rc; - if (!gsmtap) + struct gsmtap_inst *inst = trx->bts->gsmtap.inst; + if (!inst) return 0; switch (OSMO_PRIM_HDR(&l1sap->oph)) { @@ -414,10 +481,12 @@ static int to_gsmtap(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap) else rc = gsmtap_ph_data(l1sap, &chan_type, &ss, fn, &data, &len, num_agch(trx, "GSMTAP")); + signal_dbm = l1sap->u.data.rssi; break; case OSMO_PRIM(PRIM_PH_RACH, PRIM_OP_INDICATION): rc = gsmtap_ph_rach(l1sap, &chan_type, &tn, &ss, &fn, &data, &len); + signal_dbm = l1sap->u.rach_ind.rssi; break; default: rc = -ENOTSUP; @@ -429,10 +498,10 @@ static int to_gsmtap(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap) if (len == 0) return 0; if ((chan_type & GSMTAP_CHANNEL_ACCH)) { - if (!gsmtap_sapi_acch) + if (!trx->bts->gsmtap.sapi_acch) return 0; } else { - if (!((1 << (chan_type & 31)) & gsmtap_sapi_mask)) + if (!((1 << (chan_type & 31)) & trx->bts->gsmtap.sapi_mask)) return 0; } @@ -441,8 +510,8 @@ static int to_gsmtap(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap) if (is_fill_frame(chan_type, data, len)) return 0; - gsmtap_send(gsmtap, trx->arfcn | uplink, tn, chan_type, ss, fn, 0, 0, - data, len); + gsmtap_send(inst, trx->arfcn | uplink, tn, chan_type, ss, fn, + signal_dbm, 0 /* TODO: SNR */, data, len); return 0; } @@ -487,22 +556,73 @@ static unsigned int calc_exprd_rach_frames(struct gsm_bts *bts, uint32_t fn) return rach_frames_expired; } +static void l1sap_interf_meas_calc_avg(struct gsm_bts_trx *trx) +{ + unsigned int tn, ln; + + for (tn = 0; tn < ARRAY_SIZE(trx->ts); tn++) { + 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++) { + struct gsm_lchan *lchan = &ts->lchan[ln]; + + lchan->meas.interf_meas_avg_dbm = 0; + lchan->meas.interf_band = 0; + + /* There must be at least one sample */ + if (lchan->meas.interf_meas_num == 0) + continue; + + /* Average all collected samples */ + gsm_lchan_interf_meas_calc_avg(lchan); + } + } +} + +static void l1sap_interf_meas_report(struct gsm_bts *bts) +{ + const uint32_t period = bts->interference.intave * 104; + struct gsm_bts_trx *trx; + + if (bts->interference.intave == 0) + return; + if (bts->gsm_time.fn % period != 0) + return; + + llist_for_each_entry(trx, &bts->trx_list, list) { + if (trx->mo.nm_state.operational != NM_OPSTATE_ENABLED || + trx->bb_transc.mo.nm_state.operational != NM_OPSTATE_ENABLED) + continue; + /* Calculate the average of all received samples */ + l1sap_interf_meas_calc_avg(trx); + /* Report to the BSC over the A-bis/RSL */ + rsl_tx_rf_res(trx); + /* Report to the PCU over the PCUIF */ + pcu_tx_interf_ind(trx, bts->gsm_time.fn); + } +} + /* time information received from bts model */ static int l1sap_info_time_ind(struct gsm_bts *bts, struct osmo_phsap_prim *l1sap, struct info_time_ind_param *info_time_ind) { - int frames_expired; - int i; + unsigned int frames_expired; + unsigned int i; DEBUGPFN(DL1P, info_time_ind->fn, "Rx MPH_INFO time ind\n"); /* Calculate and check frame difference */ - frames_expired = info_time_ind->fn - bts->gsm_time.fn; + frames_expired = GSM_TDMA_FN_SUB(info_time_ind->fn, bts->gsm_time.fn); if (frames_expired > 1) { if (bts->gsm_time.fn) LOGPFN(DL1P, LOGL_ERROR, info_time_ind->fn, - "Invalid condition detected: Frame difference is %"PRIu32"-%"PRIu32"=%d > 1!\n", + "Invalid condition detected: Frame difference is %"PRIu32"-%"PRIu32"=%u > 1!\n", info_time_ind->fn, bts->gsm_time.fn, frames_expired); } @@ -515,10 +635,14 @@ static int l1sap_info_time_ind(struct gsm_bts *bts, /* increment number of RACH slots that have passed by since the * last time indication */ for (i = 0; i < frames_expired; i++) { - uint32_t fn = (info_time_ind->fn + GSM_HYPERFRAME - i) % GSM_HYPERFRAME; + uint32_t fn = GSM_TDMA_FN_SUB(info_time_ind->fn, i); bts->load.rach.total += calc_exprd_rach_frames(bts, fn); } + /* Report interference levels to the BSC */ + if (bts_internal_flag_get(bts, BTS_INTERNAL_FLAG_INTERF_MEAS)) + l1sap_interf_meas_report(bts); + return 0; } @@ -542,49 +666,93 @@ static inline void set_ms_to_data(struct gsm_lchan *lchan, int16_t data, bool se } } +bool trx_sched_is_sacch_fn(const struct gsm_bts_trx_ts *ts, uint32_t fn, bool uplink); + /* measurement information received from bts model */ -static int l1sap_info_meas_ind(struct gsm_bts_trx *trx, - struct osmo_phsap_prim *l1sap, - struct info_meas_ind_param *info_meas_ind) +static void process_l1sap_meas_data(struct gsm_lchan *lchan, + const struct osmo_phsap_prim *l1sap, + enum osmo_ph_prim ind_type) { struct bts_ul_meas ulm; - struct gsm_lchan *lchan; - - lchan = get_active_lchan_by_chan_nr(trx, info_meas_ind->chan_nr); - if (!lchan) { - LOGPFN(DL1P, LOGL_ERROR, info_meas_ind->fn, - "No lchan for MPH INFO MEAS IND (chan_nr=%s)\n", rsl_chan_nr_str(info_meas_ind->chan_nr)); - return 0; - } + const struct info_meas_ind_param *info_meas_ind; + const struct ph_data_param *ph_data_ind; + const struct ph_tch_param *ph_tch_ind; + uint32_t fn; + const char *ind_name; - DEBUGPFN(DL1P, info_meas_ind->fn, - "%s MPH_INFO meas ind, ta_offs_256bits=%d, ber10k=%d, inv_rssi=%u\n", - gsm_lchan_name(lchan), info_meas_ind->ta_offs_256bits, - info_meas_ind->ber10k, info_meas_ind->inv_rssi); + /* Do not process measurement reports from non-active VGCS calls. */ + if (rsl_chan_rt_is_asci(lchan->rsl_chan_rt) && lchan->asci.talker_active != VGCS_TALKER_ACTIVE) + return; - /* in the GPRS case we are not interested in measurement - * processing. The PCU will take care of it */ - if (lchan->type == GSM_LCHAN_PDTCH) - return 0; + switch (ind_type) { + case PRIM_MPH_INFO: + /* (legacy way, see also OS#2977) */ + info_meas_ind = &l1sap->u.info.u.meas_ind; + fn = info_meas_ind->fn; + ind_name = "MPH INFO"; + ulm = (struct bts_ul_meas) { + .ta_offs_256bits = info_meas_ind->ta_offs_256bits, + .inv_rssi = info_meas_ind->inv_rssi, + .ber10k = info_meas_ind->ber10k, + .ci_cb = info_meas_ind->c_i_cb, + }; + /* additionally treat SACCH frames (match by TDMA FN) as SUB frames */ + if (info_meas_ind->is_sub || trx_sched_is_sacch_fn(lchan->ts, fn, true)) + ulm.is_sub = 1; + break; + case PRIM_TCH: + ph_tch_ind = &l1sap->u.tch; + if (ph_tch_ind->rssi == 0) + return; + fn = ph_tch_ind->fn; + ind_name = "TCH"; + ulm = (struct bts_ul_meas) { + .ta_offs_256bits = ph_tch_ind->ta_offs_256bits, + .inv_rssi = abs(ph_tch_ind->rssi), + .ber10k = ph_tch_ind->ber10k, + .ci_cb = ph_tch_ind->lqual_cb, + .is_sub = ph_tch_ind->is_sub, + }; + /* PRIM_TCH always carries DCCH, not SACCH */ + break; + case PRIM_PH_DATA: + ph_data_ind = &l1sap->u.data; + if (ph_data_ind->rssi == 0) + return; + fn = ph_data_ind->fn; + ind_name = "DATA"; + ulm = (struct bts_ul_meas) { + .ta_offs_256bits = ph_data_ind->ta_offs_256bits, + .inv_rssi = abs(ph_data_ind->rssi), + .ber10k = ph_data_ind->ber10k, + .ci_cb = ph_data_ind->lqual_cb, + }; + /* additionally treat SACCH frames (match by RSL link ID) as SUB frames */ + if (ph_data_ind->is_sub || L1SAP_IS_LINK_SACCH(ph_data_ind->link_id)) + ulm.is_sub = 1; + break; + default: + OSMO_ASSERT(false); + } - memset(&ulm, 0, sizeof(ulm)); - ulm.ta_offs_256bits = info_meas_ind->ta_offs_256bits; - ulm.ber10k = info_meas_ind->ber10k; - ulm.inv_rssi = info_meas_ind->inv_rssi; - ulm.is_sub = info_meas_ind->is_sub; + LOGPLCFN(lchan, fn, DL1P, LOGL_DEBUG, + "%s meas ind, ta_offs_256bits=%d, ber10k=%d, inv_rssi=%u, C/I=%d cB\n", ind_name, + ulm.ta_offs_256bits, ulm.ber10k, ulm.inv_rssi, ulm.ci_cb); /* we assume that symbol period is 1 bit: */ - set_ms_to_data(lchan, info_meas_ind->ta_offs_256bits / 256, true); + set_ms_to_data(lchan, ulm.ta_offs_256bits / 256, true); - lchan_meas_process_measurement(lchan, &ulm, info_meas_ind->fn); + lchan_meas_process_measurement(lchan, &ulm, fn); - return 0; + return; } -/* any L1 MPH_INFO indication prim recevied from bts model */ +/* any L1 MPH_INFO indication prim received from bts model */ static int l1sap_mph_info_ind(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap, struct mph_info_param *info) { + const struct info_meas_ind_param *meas_ind; + struct gsm_lchan *lchan; int rc = 0; switch (info->type) { @@ -599,7 +767,22 @@ static int l1sap_mph_info_ind(struct gsm_bts_trx *trx, &info->u.time_ind); break; case PRIM_INFO_MEAS: - rc = l1sap_info_meas_ind(trx, l1sap, &info->u.meas_ind); + /* We should never get an INFO_IND with PRIM_INFO_MEAS + * when BTS_INTERNAL_FLAG_MEAS_PAYLOAD_COMB is set */ + if (bts_internal_flag_get(trx->bts, BTS_INTERNAL_FLAG_MEAS_PAYLOAD_COMB)) + OSMO_ASSERT(false); + + meas_ind = &l1sap->u.info.u.meas_ind; + + lchan = get_active_lchan_by_chan_nr(trx, meas_ind->chan_nr); + if (!lchan) { + LOGPFN(DL1P, LOGL_ERROR, meas_ind->fn, + "No lchan for chan_nr=%s\n", + rsl_chan_nr_str(meas_ind->chan_nr)); + return 0; + } + + process_l1sap_meas_data(lchan, l1sap, PRIM_MPH_INFO); break; default: LOGP(DL1P, LOGL_NOTICE, "unknown MPH_INFO ind type %d\n", @@ -616,6 +799,12 @@ static int l1sap_info_act_cnf(struct gsm_bts_trx *trx, struct info_act_cnf_param *info_act_cnf) { struct gsm_lchan *lchan = get_lchan_by_chan_nr(trx, info_act_cnf->chan_nr); + if (lchan == NULL) { + LOGPTRX(trx, DL1C, LOGL_ERROR, "get_lchan_by_chan_nr(chan_nr=%s) " + "yields NULL for PRIM_INFO_ACTIVATE.conf\n", + rsl_chan_nr_str(info_act_cnf->chan_nr)); + return -ENODEV; + } LOGPLCHAN(lchan, DL1C, LOGL_INFO, "activate confirm chan_nr=%s trx=%d\n", rsl_chan_nr_str(info_act_cnf->chan_nr), trx->nr); @@ -639,6 +828,12 @@ static int l1sap_info_rel_cnf(struct gsm_bts_trx *trx, struct info_act_cnf_param *info_act_cnf) { struct gsm_lchan *lchan = get_lchan_by_chan_nr(trx, info_act_cnf->chan_nr); + if (lchan == NULL) { + LOGPTRX(trx, DL1C, LOGL_ERROR, "get_lchan_by_chan_nr(chan_nr=%s) " + "yields NULL for PRIM_INFO_ACTIVATE.conf\n", + rsl_chan_nr_str(info_act_cnf->chan_nr)); + return -ENODEV; + } LOGPLCHAN(lchan, DL1C, LOGL_INFO, "deactivate confirm chan_nr=%s trx=%d\n", rsl_chan_nr_str(info_act_cnf->chan_nr), trx->nr); @@ -655,7 +850,7 @@ static int l1sap_info_rel_cnf(struct gsm_bts_trx *trx, return 0; } -/* any L1 MPH_INFO confirm prim recevied from bts model */ +/* any L1 MPH_INFO confirm prim received from bts model */ static int l1sap_mph_info_cnf(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap, struct mph_info_param *info) { @@ -692,16 +887,15 @@ static int lchan_pdtch_ph_rts_ind_loop(struct gsm_lchan *lchan, uint8_t *p; /* de-queue response message (loopback) */ - loop_msg = msgb_dequeue(&lchan->dl_tch_queue); + loop_msg = msgb_dequeue_count(&lchan->dl_tch_queue, &lchan->dl_tch_queue_len); if (!loop_msg) { - LOGPGT(DL1P, LOGL_NOTICE, tm, "%s: no looped PDTCH message, sending empty\n", - gsm_lchan_name(lchan)); + LOGPLCGT(lchan, tm, DL1P, LOGL_NOTICE, "no looped PDTCH message, sending empty\n"); /* empty downlink message */ p = msgb_put(msg, GSM_MACBLOCK_LEN); memset(p, 0, GSM_MACBLOCK_LEN); } else { - LOGPGT(DL1P, LOGL_NOTICE, tm, "%s: looped PDTCH message of %u bytes\n", - gsm_lchan_name(lchan), msgb_l2len(loop_msg)); + LOGPLCGT(lchan, tm, DL1P, LOGL_NOTICE, "looped PDTCH message of %u bytes\n", + msgb_l2len(loop_msg)); /* copy over data from queued response message */ p = msgb_put(msg, msgb_l2len(loop_msg)); memcpy(p, msgb_l2(loop_msg), msgb_l2len(loop_msg)); @@ -710,17 +904,34 @@ static int lchan_pdtch_ph_rts_ind_loop(struct gsm_lchan *lchan, return 0; } -/* Check if given CCCH frame number is for a PCH or for an AGCH (this function is +/* Check if given CCCH frame number is for a NCH, PCH or for an AGCH (this function is * only used internally, it is public to call it from unit-tests) */ -int is_ccch_for_agch(struct gsm_bts_trx *trx, uint32_t fn) { +enum ccch_msgt get_ccch_msgt(struct gsm_bts_trx *trx, uint32_t fn) +{ + uint8_t block, first_block, num_blocks; + int rc; + + block = l1sap_fn2ccch_block(fn); + + /* If there is an NCH, check if the block number matches. It has priority over PCH/AGCH. */ + if (trx->bts->asci.pos_nch >= 0) { + rc = osmo_gsm48_si1ro_nch_pos_decode(trx->bts->asci.pos_nch, &num_blocks, &first_block); + if (rc >= 0 && block >= first_block && block < first_block + num_blocks) + return CCCH_MSGT_NCH; + } + /* Note: The number of available access grant channels is set by the * parameter BS_AG_BLKS_RES via system information type 3. This SI is - * transfered to osmo-bts via RSL */ - return l1sap_fn2ccch_block(fn) < num_agch(trx, "PH-RTS-IND"); + * transferred to osmo-bts via RSL */ + if (l1sap_fn2ccch_block(fn) < num_agch(trx, "PH-RTS-IND")) + return CCCH_MSGT_AGCH; + + return CCCH_MSGT_PCH; } + /* return the measured average of frame numbers that the RTS clock is running in advance */ -int32_t bts_get_avg_fn_advance(struct gsm_bts *bts) +int32_t bts_get_avg_fn_advance(const struct gsm_bts *bts) { if (bts->fn_stats.avg_count == 0) return 0; @@ -729,7 +940,7 @@ int32_t bts_get_avg_fn_advance(struct gsm_bts *bts) static void l1sap_update_fnstats(struct gsm_bts *bts, uint32_t rts_fn) { - int32_t delta = (rts_fn + GSM_HYPERFRAME - bts->gsm_time.fn) % GSM_HYPERFRAME; + int32_t delta = GSM_TDMA_FN_SUB(rts_fn, bts->gsm_time.fn); if (delta < bts->fn_stats.min) bts->fn_stats.min = delta; @@ -746,6 +957,100 @@ static void l1sap_update_fnstats(struct gsm_bts *bts, uint32_t rts_fn) } } +/* Common dequeueing function */ +static inline struct msgb *lapdm_phsap_dequeue_msg(struct lapdm_entity *le, uint32_t fn) +{ + struct osmo_phsap_prim pp; + if (lapdm_phsap_dequeue_prim_fn(le, &pp, fn) < 0) + return NULL; + return pp.oph.msg; +} + +/* Special dequeueing function with FACCH repetition (3GPP TS 44.006, section 10) */ +static inline struct msgb *lapdm_phsap_dequeue_msg_facch(struct gsm_lchan *lchan, struct lapdm_entity *le, uint32_t fn) +{ + struct osmo_phsap_prim pp; + struct msgb *msg; + + /* Note: The repeated version of the FACCH block must be scheduled 8 or 9 bursts after the original + * transmission. see 3GPP TS 44.006, section 10.2 for a more detailed explaination. */ + if (lchan->rep_acch.dl_facch[0].msg && GSM_TDMA_FN_SUB(fn, lchan->rep_acch.dl_facch[0].fn) >= 8) { + /* Re-use stored FACCH message buffer from SLOT #0 for repetition. */ + msg = lchan->rep_acch.dl_facch[0].msg; + lchan->rep_acch.dl_facch[0].msg = NULL; + } else if (lchan->rep_acch.dl_facch[1].msg && GSM_TDMA_FN_SUB(fn, lchan->rep_acch.dl_facch[1].fn) >= 8) { + /* Re-use stored FACCH message buffer from SLOT #1 for repetition. */ + msg = lchan->rep_acch.dl_facch[1].msg; + lchan->rep_acch.dl_facch[1].msg = NULL; + } else { + /* Fetch new FACCH from queue ... */ + if (lapdm_phsap_dequeue_prim_fn(le, &pp, fn) < 0) + return NULL; + msg = pp.oph.msg; + + /* Check if the LAPDm frame is a command frame, + * see also: 3GPP TS 04.06 section 3.2 and 3.3.2. + * If the MS explicitly indicated that repeated ACCH is + * supported, than all FACCH frames may be repeated + * see also: 3GPP TS 44.006, section 10.3). */ + if (!(lchan->rep_acch_cap.dl_facch_all || msg->data[0] & 0x02)) + return msg; + + /* ... and store the message buffer for repetition. */ + if (lchan->rep_acch.dl_facch[0].msg == NULL) { + lchan->rep_acch.dl_facch[0].msg = msgb_copy(msg, "rep_facch_0"); + lchan->rep_acch.dl_facch[0].fn = fn; + } else if (lchan->rep_acch.dl_facch[1].msg == NULL) { + lchan->rep_acch.dl_facch[1].msg = msgb_copy(msg, "rep_facch_1"); + lchan->rep_acch.dl_facch[1].fn = fn; + } else { + /* By definition 3GPP TS 05.02 does not allow more than two (for TCH/H only one) FACCH blocks + * to be transmitted simultaniously. */ + OSMO_ASSERT(false); + } + } + + return msg; +} + +/* Special dequeueing function with SACCH repetition (3GPP TS 44.006, section 11) */ +static inline struct msgb *lapdm_phsap_dequeue_msg_sacch(struct gsm_lchan *lchan, struct lapdm_entity *le, uint32_t fn) +{ + struct osmo_phsap_prim pp; + struct msgb *msg; + uint8_t sapi; + + /* Note: When the MS disables SACCH repetition, we still must collect + * possible candidates in order to have one ready in case the MS enables + * SACCH repetition. */ + + if (lchan->rep_acch.dl_sacch_msg) { + if (lchan->meas.l1_info.srr_sro == 0) { + /* Toss previous repetition candidate */ + msgb_free(lchan->rep_acch.dl_sacch_msg); + lchan->rep_acch.dl_sacch_msg = NULL; + } else { + /* Use previous repetition candidate */ + msg = lchan->rep_acch.dl_sacch_msg; + lchan->rep_acch.dl_sacch_msg = NULL; + return msg; + } + } + + /* Fetch new repetition candidate from queue */ + if (lapdm_phsap_dequeue_prim_fn(le, &pp, fn) < 0) + return NULL; + msg = pp.oph.msg; + sapi = (msg->data[0] >> 2) & 0x07; + + /* Only LAPDm frames for SAPI 0 may become a repetition + * candidate. */ + if (sapi == 0) + lchan->rep_acch.dl_sacch_msg = msgb_copy(msg, "rep_sacch"); + + return msg; +} + /* PH-RTS-IND prim received from bts model */ static int l1sap_ph_rts_ind(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap, struct ph_data_param *rts_ind) @@ -756,12 +1061,12 @@ static int l1sap_ph_rts_ind(struct gsm_bts_trx *trx, uint8_t chan_nr, link_id; uint8_t tn; uint32_t fn; - uint8_t *p, *si; + uint8_t *p = NULL; + uint8_t *si; struct lapdm_entity *le; - struct osmo_phsap_prim pp; + struct msgb *pp_msg; bool dtxd_facch = false; int rc; - int is_ag_res; chan_nr = rts_ind->chan_nr; link_id = rts_ind->link_id; @@ -793,10 +1098,10 @@ static int l1sap_ph_rts_ind(struct gsm_bts_trx *trx, } else { /* forward RTS.ind to PCU */ if (L1SAP_IS_PTCCH(rts_ind->fn)) { - pcu_tx_rts_req(&trx->ts[tn], 1, fn, 1 /* ARFCN */, + pcu_tx_rts_req(&trx->ts[tn], 1, fn, trx->arfcn, L1SAP_FN2PTCCHBLOCK(fn)); } else { - pcu_tx_rts_req(&trx->ts[tn], 0, fn, 0 /* ARFCN */, + pcu_tx_rts_req(&trx->ts[tn], 0, fn, trx->arfcn, L1SAP_FN2MACBLOCK(fn)); } /* return early, PCU takes care of rest */ @@ -820,19 +1125,45 @@ static int l1sap_ph_rts_ind(struct gsm_bts_trx *trx, rsl_chan_nr_str(chan_nr)); return 0; } + if (lchan->pending_rel_ind_msg) { + LOGPLCGT(lchan, &g_time, DRSL, LOGL_INFO, "Forward RLL RELease INDication to the BSC\n"); + abis_bts_rsl_sendmsg(lchan->pending_rel_ind_msg); + lchan->pending_rel_ind_msg = NULL; + } if (L1SAP_IS_LINK_SACCH(link_id)) { p = msgb_put(msg, GSM_MACBLOCK_LEN); /* L1-header, if not set/modified by layer 1 */ p[0] = lchan->ms_power_ctrl.current; - p[1] = lchan->rqd_ta; + if (lchan->rep_acch.ul_sacch_active) + p[0] |= 0x40; /* See also: 3GPP TS 44.004, section 7.1 */ + p[1] = lchan->ta_ctrl.current; le = &lchan->lapdm_ch.lapdm_acch; + if (lchan->rep_acch_cap.dl_sacch) { + /* Check if MS requests SACCH repetition and update state accordingly */ + if (lchan->meas.l1_info.srr_sro) { + if (lchan->rep_acch.dl_sacch_active == false) + LOGPLCHAN(lchan, DL1P, LOGL_DEBUG, "DL-SACCH repetition: inactive => active\n"); + lchan->rep_acch.dl_sacch_active = true; + } else { + if (lchan->rep_acch.dl_sacch_active == true) + LOGPLCHAN(lchan, DL1P, LOGL_DEBUG, "DL-SACCH repetition: active => inactive\n"); + lchan->rep_acch.dl_sacch_active = false; + } + pp_msg = lapdm_phsap_dequeue_msg_sacch(lchan, le, fn); + } else { + pp_msg = lapdm_phsap_dequeue_msg(le, fn); + } } else { if (lchan->ts->trx->bts->dtxd) dtxd_facch = true; le = &lchan->lapdm_ch.lapdm_dcch; + if (lchan->rep_acch.dl_facch_active && lchan->rsl_cmode != RSL_CMOD_SPD_SIGN) + pp_msg = lapdm_phsap_dequeue_msg_facch(lchan, le, fn); + else + pp_msg = lapdm_phsap_dequeue_msg(le, fn); + lchan->tch.dtx_fr_hr_efr.dl_facch_stealing = (pp_msg != NULL); } - rc = lapdm_phsap_dequeue_prim(le, &pp); - if (rc < 0) { + if (!pp_msg) { if (L1SAP_IS_LINK_SACCH(link_id)) { /* No SACCH data from LAPDM pending, send SACCH filling */ uint8_t *si = lchan_sacch_get(lchan); @@ -841,6 +1172,10 @@ static int l1sap_ph_rts_ind(struct gsm_bts_trx *trx, memcpy(p + 2, si, GSM_MACBLOCK_LEN - 2); } else memcpy(p + 2, fill_frame, GSM_MACBLOCK_LEN - 2); + } else if (vgcs_is_uplink_free(lchan)) { + /* If UPLINK FREE message is stored, send it with every DCCH frame. */ + p = msgb_put(msg, GSM_MACBLOCK_LEN); + vgcs_uplink_free_get(lchan, p); } else if (L1SAP_IS_CHAN_SDCCH4(chan_nr) || L1SAP_IS_CHAN_SDCCH8(chan_nr) || (lchan->rsl_cmode == RSL_CMOD_SPD_SIGN && !lchan->ts->trx->bts->dtxd)) { /* @@ -857,21 +1192,28 @@ static int l1sap_ph_rts_ind(struct gsm_bts_trx *trx, } else { /* The +2 is empty space where the DSP inserts the L1 hdr */ if (L1SAP_IS_LINK_SACCH(link_id)) - memcpy(p + 2, pp.oph.msg->data + 2, GSM_MACBLOCK_LEN - 2); + memcpy(p + 2, pp_msg->data + 2, GSM_MACBLOCK_LEN - 2); else { p = msgb_put(msg, GSM_MACBLOCK_LEN); - memcpy(p, pp.oph.msg->data, GSM_MACBLOCK_LEN); + memcpy(p, pp_msg->data, GSM_MACBLOCK_LEN); /* check if it is a RR CIPH MODE CMD. if yes, enable RX ciphering */ - check_for_ciph_cmd(pp.oph.msg, lchan, chan_nr); + check_for_ciph_cmd(pp_msg, lchan, chan_nr); if (dtxd_facch) dtx_dispatch(lchan, E_FACCH); + if (rsl_chan_rt_is_vgcs(lchan->rsl_chan_rt)) { + /* Check for UPLINK FREE message and store. */ + if (pp_msg->data[0] == GSM48_MT_RR_SH_UL_FREE << 2) + vgcs_uplink_free_set(lchan, pp_msg->data); + /* Keep UPLINK FREE message when sending short header messages. */ + else if ((pp_msg->data[0] & 0x03) != 0x00) + vgcs_uplink_free_reset(lchan); + } } - msgb_free(pp.oph.msg); + msgb_free(pp_msg); } } else if (L1SAP_IS_CHAN_AGCH_PCH(chan_nr)) { p = msgb_put(msg, GSM_MACBLOCK_LEN); - is_ag_res = is_ccch_for_agch(trx, fn); - rc = bts_ccch_copy_msg(trx->bts, p, &g_time, is_ag_res); + rc = bts_ccch_copy_msg(trx->bts, p, &g_time, get_ccch_msgt(trx, fn)); if (rc <= 0) memcpy(p, fill_frame, GSM_MACBLOCK_LEN); } @@ -884,48 +1226,270 @@ static int l1sap_ph_rts_ind(struct gsm_bts_trx *trx, return 1; } -static bool rtppayload_is_octet_aligned(const uint8_t *rtp_pl, uint8_t payload_len) -{ - /* - * Logic: If 1st bit padding is not zero, packet is either: - * - bandwidth-efficient AMR payload. - * - malformed packet. - * However, Bandwidth-efficient AMR 4,75 frame last in payload(F=0, FT=0) - * with 4th,5ht,6th AMR payload to 0 matches padding==0. - * Furthermore, both AMR 4,75 bw-efficient and octet alignment are 14 bytes long (AMR 4,75 encodes 95b): - * bw-efficient: 95b, + 4b hdr + 6b ToC = 105b, + padding = 112b = 14B. - * octet-aligned: 1B hdr + 1B ToC + 95b = 111b, + padding = 112b = 14B. - * We cannot use other fields to match since they are inside the AMR - * payload bits which are unknown. - * As a result, this function may return false positive (true) for some AMR - * 4,75 AMR frames, but given the length, CMR and FT read is the same as a - * consequence, the damage in here is harmless other than being unable to - * decode the audio at the other side. - */ - #define AMR_PADDING1(rtp_pl) (rtp_pl[0] & 0x0f) - #define AMR_PADDING2(rtp_pl) (rtp_pl[1] & 0x03) +/* The following static functions are helpers for l1sap_tch_rts_ind(), + * used only for FR/HR/EFR speech modes. For these speech TCH modes, + * if our incoming RTP stream includes SID frames, we need to apply + * the following transformations to the downlink frame stream we actually + * transmit: + * + * - We need to cache the last received SID and retransmit it in + * SACCH-aligned frame positions, or if the SACCH-aligned frame + * position was stolen by FACCH, then right after that FACCH. + * + * - That cached SID needs to be aged and expired, in accord with + * TS 28.062 section C.3.2.1.1 paragraph 5 - the paragraph concerning + * expiration of cached downlink SID. + * + * - In all other frame positions, extraneous SID frames need to be + * dropped - we send an empty payload to the BTS model, causing it + * to transmit an induced BFI condition on the air (fake DTXd), + * or perhaps real DTXd (actually turning off Tx) in the future. + */ - if(payload_len < 2 || AMR_PADDING1(rtp_pl) || AMR_PADDING2(rtp_pl)) +/*! \brief Check if the given FN of TCH-RTS-IND corresponds to a mandatory + * SID position for non-AMR codecs that follow SACCH alignment. + * \param[in] lchan Logical channel on which we check scheduling + * \param[in] fn Frame Number for which we check scheduling + * \returns true if this FN is a mandatory SID position, false otherwise + */ +static inline bool fr_hr_efr_sid_position(struct gsm_lchan *lchan, uint32_t fn) +{ + /* See GSM 05.08 section 8.3 for frame numbers - but we are + * specifically looking for FNs corresponding to the beginning + * of the complete SID frame to be transmitted, rather than all FNs + * where we have to put out a non-suppressed burst. */ + switch (lchan->type) { + case GSM_LCHAN_TCH_F: + return fn % 104 == 52; + case GSM_LCHAN_TCH_H: + switch (lchan->nr) { + case 0: + return fn % 104 == 0 || fn % 104 == 52; + case 1: + return fn % 104 == 14 || fn % 104 == 66; + default: + return false; + } + default: return false; + } +} +/*! \brief This helper function implements DTXd input processing for FR/HR/EFR: + * we got an RTP input frame, now we need to check if it is SID or not, + * and update our DL SID reshaper state accordingly. + * \param[in] lchan Logical channel structure of the TCH we work with + * \param[in] resp_msg The input frame from RTP + * \param[out] sid_result Output flag indicating if the received frame is SID + * \returns true if the frame in resp_msg is good, false otherwise + */ +static bool fr_hr_efr_dtxd_input(struct gsm_lchan *lchan, struct msgb *resp_msg, + bool *sid_result) +{ + enum osmo_gsm631_sid_class sidc; + bool is_sid; + + switch (lchan->tch_mode) { + case GSM48_CMODE_SPEECH_V1: + if (lchan->type == GSM_LCHAN_TCH_F) { + sidc = osmo_fr_sid_classify(msgb_l2(resp_msg)); + switch (sidc) { + case OSMO_GSM631_SID_CLASS_SPEECH: + is_sid = false; + break; + case OSMO_GSM631_SID_CLASS_INVALID: + /* TS 28.062 section C.3.2.1.1: invalid SIDs + * from call leg A UL are treated like BFIs. + * Drop this frame and act as if we got nothing + * at all from RTP for this FN. */ + return false; + case OSMO_GSM631_SID_CLASS_VALID: + is_sid = true; + /* The SID code word may have a one bit error - + * rejuvenate it. */ + osmo_fr_sid_reset(msgb_l2(resp_msg)); + break; + default: + /* SID classification per GSM 06.31 section + * 6.1.1 has only 3 possible outcomes. */ + OSMO_ASSERT(0); + } + } else { + /* The same kind of classification as we do in + * osmo_{fr,efr}_sid_classify() is impossible for HR: + * the equivalent ternary SID classification per + * GSM 06.41 can only be done in the UL-handling BTS, + * directly coupled to the GSM 05.03 channel decoder, + * and cannot be reconstructed downstream from frame + * payload bits. The only kind of SID we can detect + * here is the perfect, error-free kind. However, + * if we received RFC 5993 payload and the sender + * told us it is valid SID, honor that indication + * and rejuvenate the SID codeword. */ + if (rtpmsg_is_rfc5993_sid(resp_msg)) { + is_sid = true; + osmo_hr_sid_reset(msgb_l2(resp_msg)); + } else { + is_sid = osmo_hr_check_sid(msgb_l2(resp_msg), + msgb_l2len(resp_msg)); + } + } + break; + case GSM48_CMODE_SPEECH_EFR: + /* same logic as for FRv1 */ + sidc = osmo_efr_sid_classify(msgb_l2(resp_msg)); + switch (sidc) { + case OSMO_GSM631_SID_CLASS_SPEECH: + is_sid = false; + break; + case OSMO_GSM631_SID_CLASS_INVALID: + return false; + case OSMO_GSM631_SID_CLASS_VALID: + is_sid = true; + osmo_efr_sid_reset(msgb_l2(resp_msg)); + break; + default: + OSMO_ASSERT(0); + } + break; + default: + /* This static function should never be called except for + * V1 and EFR speech modes. */ + OSMO_ASSERT(0); + } + *sid_result = is_sid; + lchan->tch.dtx_fr_hr_efr.last_rtp_input_was_sid = is_sid; + if (is_sid) { + uint8_t copy_len; + + copy_len = OSMO_MIN(msgb_l2len(resp_msg), + ARRAY_SIZE(lchan->tch.dtx_fr_hr_efr.last_sid)); + memcpy(lchan->tch.dtx_fr_hr_efr.last_sid, + msgb_l2(resp_msg), copy_len); + lchan->tch.dtx_fr_hr_efr.last_sid_len = copy_len; + lchan->tch.dtx_fr_hr_efr.last_sid_age = 0; + } else { + /* We got a speech frame, not SID - therefore, the state flag + * of "we already transmitted a SID" needs to be cleared, + * so that the very first SID that follows this talkspurt + * will get transmitted right away, without waiting for + * the next mandatory SID position. */ + lchan->tch.dtx_fr_hr_efr.dl_sid_transmitted = false; + } return true; } -static bool rtppayload_is_valid(struct gsm_lchan *lchan, struct msgb *resp_msg) +/*! \brief This helper function implements DTXd output processing for FR/HR/EFR: + * here we update our state to deal with cached SID aging, mandatory-Tx + * frame positions and FACCH stealing, and we make the desired output + * transformations of either regurgitating a cached SID or vice-versa, + * dropping a SID we received from RTP. + * \param[in] lchan Logical channel structure of the TCH we work with + * \param[in] fn Frame Number for which we are preparing DL + * \param[in] current_input_is_sid Self-explanatory + * \param[in] resp_msg_p Pointer to l1sap_tch_rts_ind() internal variable + * \param[in] resp_l1sap_p ditto + * \param[in] empty_l1sap_p ditto + * + * This function gets called with pointers to l1sap_tch_rts_ind() internal + * variables and cannot be properly understood on its own, without + * understanding the parent function first. This situation is unfortunate, + * but it was the only way to factor the present logic out of + * l1sap_tch_rts_ind() main body. + */ +static void fr_hr_efr_dtxd_output(struct gsm_lchan *lchan, uint32_t fn, + bool current_input_is_sid, + struct msgb **resp_msg_p, + struct osmo_phsap_prim **resp_l1sap_p, + struct osmo_phsap_prim *empty_l1sap_p) { - /* Avoid sending bw-efficient AMR to lower layers, most bts models - * don't support it. */ - if(lchan->tch_mode == GSM48_CMODE_SPEECH_AMR && - !rtppayload_is_octet_aligned(resp_msg->data, resp_msg->len)) { - LOGPLCHAN(lchan, DL1P, LOGL_NOTICE, - "RTP->L1: Dropping unexpected AMR encoding (bw-efficient?) %s\n", - osmo_hexdump(resp_msg->data, resp_msg->len)); - return false; + struct msgb *resp_msg = *resp_msg_p; + bool sid_in_hand = current_input_is_sid; + + /* Are we at a mandatory-Tx frame position? If so, clear the state flag + * of "we already transmitted a SID in this window" - as of right now, + * a SID has _not_ been transmitted in the present window yet, and if + * we are in a DTX pause, we do need to transmit a SID update as soon + * as we are able to, FACCH permitting. */ + if (fr_hr_efr_sid_position(lchan, fn)) + lchan->tch.dtx_fr_hr_efr.dl_sid_transmitted = false; + + /* The next stanza implements logic that was originally meant to reside + * in the TFO block in TRAUs: if the source feeding us RTP is in a DTXu + * pause (resp_msg == NULL, meaning we got nothing from RTP) *and* the + * most recent RTP input we did get was a SID, and that SID hasn't + * expired, then we need to replicate that SID. */ + if (!resp_msg && lchan->tch.dtx_fr_hr_efr.last_rtp_input_was_sid && + lchan->tch.dtx_fr_hr_efr.last_sid_age < 47) { + /* Whatever we do further below, any time another 20 ms window + * has passed since the last SID was received in RTP, we have + * to age that cached SID. */ + lchan->tch.dtx_fr_hr_efr.last_sid_age++; + + /* The following conditional checking dl_sid_transmitted flag + * is peculiar to our sans-E1 architecture. In the original + * T1/E1 Abis architecture the TFO-enabled TRAU would repeat + * the cached SID from call leg A into *every* 20 ms frame in + * its Abis output, and then the BTS would select which ones + * it should transmit per the rules of GSM 06.31/06.81 section + * 5.1.2. But in our architecture it would be silly and + * wasteful to allocate and fill an msgb for this cached SID + * only to toss it later in the same function - hence the + * logic of section 5.1.2 is absorbed into the decision right + * here to proceed with cached SID regurgitation or not, + * in the form of the following conditional. */ + if (!lchan->tch.dtx_fr_hr_efr.dl_sid_transmitted) + resp_msg = l1sap_msgb_alloc(lchan->tch.dtx_fr_hr_efr.last_sid_len); + if (resp_msg) { + resp_msg->l2h = msgb_put(resp_msg, + lchan->tch.dtx_fr_hr_efr.last_sid_len); + memcpy(resp_msg->l2h, lchan->tch.dtx_fr_hr_efr.last_sid, + lchan->tch.dtx_fr_hr_efr.last_sid_len); + *resp_msg_p = resp_msg; + *resp_l1sap_p = msgb_l1sap_prim(resp_msg); + sid_in_hand = true; + } + } else if (resp_msg && sid_in_hand) { + /* This "else if" leg implements the logic of section 5.1.2 + * for cases when a SID is already present in the RTP input, + * as indicated by (resp_msg != NULL) and sid_in_hand being + * true. Because we are in the "else" clause of the big "if" + * above, this path executes only when the SID has come from + * RTP in _this_ frame, rather than regurgitated from cache. + * But be it fresh or cached, the rules of section 5.1.2 still + * apply, and if we've already transmitted a SID in the current + * window, then we need to suppress further SIDs and send + * an empty payload to the BTS model, causing the latter + * to transmit an induced BFI condition on the air. This + * strange-seeming behavior is needed so that the spec-compliant + * Rx DTX handler in the MS will produce the expected output, + * same as if the GSM network were the old-fashioned kind with + * Abis TRAUs and TFO. */ + if (lchan->tch.dtx_fr_hr_efr.dl_sid_transmitted) { + msgb_free(resp_msg); + resp_msg = NULL; + *resp_msg_p = NULL; + *resp_l1sap_p = empty_l1sap_p; + } } - return true; + + /* The following conditional answers this question: are we actually + * transmitting a SID frame on the air right now, at this frame number? + * If we are certain the BTS model is going to transmit this SID, + * we set the state flag so we won't be transmitting any more SIDs + * until we either hit the next mandatory-Tx position or get another + * little talkspurt followed by new SID. The check for FACCH stealing + * is included because if the BTS model is going to transmit FACCH in + * the current FN, then we are not actually transmitting SID right now, + * and we still need to transmit a SID ASAP, as soon as the TCH becomes + * becomes free from FACCH activity. GSM 06.31/06.81 section 5.1.2 + * does mention that if the mandatory-Tx frame position is taken up + * by FACCH, then we need to send SID in the following frame. */ + if (resp_msg && sid_in_hand && !lchan->tch.dtx_fr_hr_efr.dl_facch_stealing) + lchan->tch.dtx_fr_hr_efr.dl_sid_transmitted = true; } -/* TCH-RTS-IND prim recevied from bts model */ +/* TCH-RTS-IND prim received from bts model */ static int l1sap_tch_rts_ind(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap, struct ph_tch_param *rts_ind) { @@ -935,19 +1499,19 @@ static int l1sap_tch_rts_ind(struct gsm_bts_trx *trx, struct gsm_lchan *lchan; uint8_t chan_nr, marker = 0; uint32_t fn; - int rc; + bool is_fr_hr_efr_sid = false; chan_nr = rts_ind->chan_nr; fn = rts_ind->fn; gsm_fn2gsmtime(&g_time, fn); - DEBUGPGT(DL1P, &g_time, "Rx TCH-RTS.ind chan_nr=%s\n", rsl_chan_nr_str(chan_nr)); - lchan = get_active_lchan_by_chan_nr(trx, chan_nr); if (!lchan) { LOGPGT(DL1P, LOGL_ERROR, &g_time, "No lchan for PH-RTS.ind (chan_nr=%s)\n", rsl_chan_nr_str(chan_nr)); return 0; + } else { + LOGPLCGT(lchan, &g_time, DL1P, LOGL_DEBUG, "Rx TCH-RTS.ind\n"); } if (!lchan->loopback && lchan->abis_ip.rtp_socket) { @@ -961,13 +1525,9 @@ static int l1sap_tch_rts_ind(struct gsm_bts_trx *trx, lchan->abis_ip.rtp_socket->rx_user_ts += GSM_RTP_DURATION; } /* get a msgb from the dl_tx_queue */ - resp_msg = msgb_dequeue(&lchan->dl_tch_queue); + resp_msg = msgb_dequeue_count(&lchan->dl_tch_queue, &lchan->dl_tch_queue_len); if (!resp_msg) { - DEBUGPGT(DL1P, &g_time, "%s DL TCH Tx queue underrun\n", gsm_lchan_name(lchan)); - resp_l1sap = &empty_l1sap; - } else if(!rtppayload_is_valid(lchan, resp_msg)) { - msgb_free(resp_msg); - resp_msg = NULL; + LOGPLCGT(lchan, &g_time, DL1P, LOGL_DEBUG, "DL TCH Tx queue underrun\n"); resp_l1sap = &empty_l1sap; } else { /* Obtain RTP header Marker bit from control buffer */ @@ -977,16 +1537,27 @@ static int l1sap_tch_rts_ind(struct gsm_bts_trx *trx, msgb_push(resp_msg, sizeof(*resp_l1sap)); resp_msg->l1h = resp_msg->data; resp_l1sap = msgb_l1sap_prim(resp_msg); + + /* FR/HR/EFR SID or non-SID input handling */ + if (lchan->tch_mode == GSM48_CMODE_SPEECH_V1 || + lchan->tch_mode == GSM48_CMODE_SPEECH_EFR) { + bool drop; + + drop = !fr_hr_efr_dtxd_input(lchan, resp_msg, + &is_fr_hr_efr_sid); + if (OSMO_UNLIKELY(drop)) { + msgb_free(resp_msg); + resp_msg = NULL; + resp_l1sap = &empty_l1sap; + } + } } - /* check for pending REL_IND */ - if (lchan->pending_rel_ind_msg) { - LOGPGT(DRSL, LOGL_INFO, &g_time, "%s Forward REL_IND to L3\n", gsm_lchan_name(lchan)); - /* Forward it to L3 */ - rc = abis_bts_rsl_sendmsg(lchan->pending_rel_ind_msg); - lchan->pending_rel_ind_msg = NULL; - if (rc < 0) - return rc; + /* FR/HR/EFR DTXd output stage */ + if (lchan->tch_mode == GSM48_CMODE_SPEECH_V1 || + lchan->tch_mode == GSM48_CMODE_SPEECH_EFR) { + fr_hr_efr_dtxd_output(lchan, fn, is_fr_hr_efr_sid, &resp_msg, + &resp_l1sap, &empty_l1sap); } memset(resp_l1sap, 0, sizeof(*resp_l1sap)); @@ -996,48 +1567,67 @@ static int l1sap_tch_rts_ind(struct gsm_bts_trx *trx, resp_l1sap->u.tch.fn = fn; resp_l1sap->u.tch.marker = marker; - DEBUGPGT(DL1P, &g_time, "Tx TCH.req chan_nr=%s\n", rsl_chan_nr_str(chan_nr)); + LOGPLCGT(lchan, &g_time, DL1P, LOGL_DEBUG, "Tx TCH.req\n"); l1sap_down(trx, resp_l1sap); return 0; } +/* Reset link timeout to current value. */ +void radio_link_timeout_reset(struct gsm_lchan *lchan) +{ + struct gsm_bts *bts = lchan->ts->trx->bts; + + lchan->s = bts->radio_link_timeout.current; +} + /* process radio link timeout counter S. Follows TS 05.08 Section 5.2 * "MS Procedure" as the "BSS Procedure [...] shall be determined by the * network operator." */ -static void radio_link_timeout(struct gsm_lchan *lchan, int bad_frame) +static void radio_link_timeout(struct gsm_lchan *lchan, bool bad_frame) { struct gsm_bts *bts = lchan->ts->trx->bts; + /* Bypass radio link timeout on VGCS/VBS channels: There is no + * uplink SACCH on these when talker is not active. */ + if (rsl_chan_rt_is_asci(lchan->rsl_chan_rt) && lchan->asci.talker_active != VGCS_TALKER_ACTIVE) + return; + /* Bypass radio link timeout if set to -1 */ - if (bts->radio_link_timeout < 0) + if (bts->radio_link_timeout.current < 0) return; /* if link loss criterion already reached */ if (lchan->s == 0) { - DEBUGP(DMEAS, "%s radio link counter S already 0.\n", - gsm_lchan_name(lchan)); + LOGPLCHAN(lchan, DMEAS, LOGL_DEBUG, + "radio link timeout counter S is already 0\n"); return; } if (bad_frame) { - /* count down radio link counter S */ - lchan->s--; - DEBUGP(DMEAS, "%s counting down radio link counter S=%d\n", - gsm_lchan_name(lchan), lchan->s); - if (lchan->s == 0) + LOGPLCHAN(lchan, DMEAS, LOGL_DEBUG, + "decreasing radio link timeout counter S=%d -> %d\n", + lchan->s, lchan->s - 1); + lchan->s--; /* count down radio link counter S */ + if (lchan->s == 0) { + LOGPLCHAN(lchan, DMEAS, LOGL_NOTICE, + "radio link timeout counter S reached zero, " + "dropping connection\n"); rsl_tx_conn_fail(lchan, RSL_ERR_RADIO_LINK_FAIL); + } return; } - if (lchan->s < bts->radio_link_timeout) { + if (lchan->s < bts->radio_link_timeout.current) { /* count up radio link counter S */ - lchan->s += 2; - if (lchan->s > bts->radio_link_timeout) - lchan->s = bts->radio_link_timeout; - DEBUGP(DMEAS, "%s counting up radio link counter S=%d\n", - gsm_lchan_name(lchan), lchan->s); + int s = lchan->s + 2; + if (s > bts->radio_link_timeout.current) + s = bts->radio_link_timeout.current; + LOGPLCHAN(lchan, DMEAS, LOGL_DEBUG, + "increasing radio link timeout counter S=%d -> %d\n", + lchan->s, s); + lchan->s = s; } } @@ -1074,6 +1664,59 @@ int bts_check_for_first_ciphrd(struct gsm_lchan *lchan, return check_for_first_ciphrd(lchan, data, len); } +/* Decide if repeated UL-SACCH should be applied or not. If the BER level, of + * the received SACCH blocks rises above a certain threshold UL-SACCH + * repetition is enabled */ +static void repeated_ul_sacch_active_decision(struct gsm_lchan *lchan, + uint16_t ber10k) +{ + uint16_t upper = 0; + uint16_t lower = 0; + bool prev_repeated_ul_sacch_active = lchan->rep_acch.ul_sacch_active; + + /* This is an optimization so that we exit as quickly as possible if + * there are no uplink SACCH repetition capabilities present. + * However If the repeated UL-SACCH capabilities vanish for whatever + * reason, we must be sure that UL-SACCH repetition is disabled. */ + if (!lchan->rep_acch_cap.ul_sacch) { + lchan->rep_acch.ul_sacch_active = false; + goto out; + } + + /* Threshold disabled (repetition is always on) */ + if (lchan->rep_acch_cap.rxqual == 0) { + lchan->rep_acch.ul_sacch_active = true; + goto out; + } + + /* convert from RXQUAL value to ber10k value. + * see also GSM 05.08, section 8.2.4 (first table, without frame */ + static const uint16_t ber10k_by_rxqual_upper[] = + { 0, 20, 40, 80, 160, 320, 640, 1280 }; + static const uint16_t ber10k_by_rxqual_lower[] = + { 0, 0, 0, 20, 40, 80, 160, 320 }; + /* Note: The values in the upper vector are taken from the left side + * of the table in GSM 05.08, section 8.2.4. The lower vector is just + * the upper vector shifted by 2. */ + + upper = ber10k_by_rxqual_upper[lchan->rep_acch_cap.rxqual]; + lower = ber10k_by_rxqual_lower[lchan->rep_acch_cap.rxqual]; + + /* If upper/rxqual == 0, then repeated UL-SACCH is always on */ + if (ber10k >= upper) + lchan->rep_acch.ul_sacch_active = true; + else if (ber10k <= lower) + lchan->rep_acch.ul_sacch_active = false; + +out: + if (lchan->rep_acch.ul_sacch_active == prev_repeated_ul_sacch_active) + return; + if (lchan->rep_acch.ul_sacch_active) + LOGPLCHAN(lchan, DL1P, LOGL_DEBUG, "UL-SACCH repetition: inactive => active\n"); + else + LOGPLCHAN(lchan, DL1P, LOGL_DEBUG, "UL-SACCH repetition: active => inactive\n"); +} + /* DATA received from bts model */ static int l1sap_ph_data_ind(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap, struct ph_data_param *data_ind) @@ -1087,10 +1730,8 @@ static int l1sap_ph_data_ind(struct gsm_bts_trx *trx, uint8_t chan_nr, link_id; uint8_t tn; uint32_t fn; - int8_t rssi; enum osmo_ph_pres_info_type pr_info = data_ind->pdch_presence_info; - rssi = data_ind->rssi; chan_nr = data_ind->chan_nr; link_id = data_ind->link_id; fn = data_ind->fn; @@ -1105,36 +1746,33 @@ static int l1sap_ph_data_ind(struct gsm_bts_trx *trx, lchan = get_lchan_by_chan_nr(trx, chan_nr); if (!lchan) LOGPGT(DL1P, LOGL_ERROR, &g_time, "No lchan for chan_nr=%s\n", rsl_chan_nr_str(chan_nr)); - if (lchan && lchan->loopback && !L1SAP_IS_PTCCH(fn)) { + if (lchan && lchan->loopback) { /* we are in loopback mode (for BER testing) * mode and need to enqeue the frame to be * returned in downlink */ - queue_limit_to(gsm_lchan_name(lchan), &lchan->dl_tch_queue, 1); - msgb_enqueue(&lchan->dl_tch_queue, msg); + lchan_dl_tch_queue_enqueue(lchan, msg, 1); /* Return 1 to signal that we're still using msg * and it should not be freed */ return 1; } - /* don't send bad frames to PCU */ - if (len == 0) - return -EINVAL; + /* There can be no DATA.ind on PTCCH/U (rather RACH.ind instead), but some + * BTS models with buggy implementation may still be sending them to us. */ if (L1SAP_IS_PTCCH(fn)) { - pcu_tx_data_ind(&trx->ts[tn], PCU_IF_SAPI_PTCCH, fn, - 0 /* ARFCN */, L1SAP_FN2PTCCHBLOCK(fn), - data, len, rssi, data_ind->ber10k, - data_ind->ta_offs_256bits/64, - data_ind->lqual_cb); - } else { - /* drop incomplete UL block */ - if (pr_info != PRES_INFO_BOTH) - return 0; - /* PDTCH / PACCH frame handling */ - pcu_tx_data_ind(&trx->ts[tn], PCU_IF_SAPI_PDTCH, fn, 0 /* ARFCN */, - L1SAP_FN2MACBLOCK(fn), data, len, rssi, data_ind->ber10k, - data_ind->ta_offs_256bits/64, data_ind->lqual_cb); + LOGPGT(DL1P, LOGL_NOTICE, &g_time, "There can be no DATA.ind on PTCCH/U. " + "This is probably a bug of the BTS model you're using, please fix!\n"); + return -EINVAL; } + + /* Drop all data from incomplete UL block */ + if (pr_info != PRES_INFO_BOTH) + len = 0; + + /* PDTCH / PACCH frame handling */ + pcu_tx_data_ind(&trx->ts[tn], PCU_IF_SAPI_PDTCH, fn, trx->arfcn, + L1SAP_FN2MACBLOCK(fn), data, len, data_ind->rssi, data_ind->ber10k, + data_ind->ta_offs_256bits/64, data_ind->lqual_cb); return 0; } @@ -1144,10 +1782,37 @@ static int l1sap_ph_data_ind(struct gsm_bts_trx *trx, return 0; } + /* The ph_data_param contained in the l1sap primitive may contain + * measurement data. If this data is present, forward it for + * processing */ + if (bts_internal_flag_get(trx->bts, BTS_INTERNAL_FLAG_MEAS_PAYLOAD_COMB)) + process_l1sap_meas_data(lchan, l1sap, PRIM_PH_DATA); + + if (L1SAP_IS_LINK_SACCH(link_id)) { + repeated_ul_sacch_active_decision(lchan, data_ind->ber10k); + + /* Radio Link Timeout counter */ + if (len == 0) { + LOGPLCGT(lchan, &g_time, DL1P, LOGL_INFO, "Lost SACCH block\n"); + radio_link_timeout(lchan, true); + } else { + radio_link_timeout(lchan, false); + } + + /* Trigger the measurement reporting/processing logic */ + lchan_meas_handle_sacch(lchan, msg); + } + + if (L1SAP_IS_LINK_SACCH(link_id)) + le = &lchan->lapdm_ch.lapdm_acch; + else + le = &lchan->lapdm_ch.lapdm_dcch; + /* bad frame */ if (len == 0) { - if (L1SAP_IS_LINK_SACCH(link_id)) - radio_link_timeout(lchan, 1); + /* Notify current receive FN to lapdm. */ + lapdm_t200_fn(le, fn); + return -EINVAL; } @@ -1155,39 +1820,221 @@ static int l1sap_ph_data_ind(struct gsm_bts_trx *trx, if (lchan->ho.active == HANDOVER_WAIT_FRAME) handover_frame(lchan); - if (L1SAP_IS_LINK_SACCH(link_id)) { - radio_link_timeout(lchan, 0); - le = &lchan->lapdm_ch.lapdm_acch; - /* save the SACCH L1 header in the lchan struct for RSL MEAS RES */ - if (len < 2) { - LOGPGT(DL1P, LOGL_NOTICE, &g_time, "SACCH with size %u<2 !?!\n", len); - return -EINVAL; - } - /* Some brilliant engineer decided that the ordering of - * fields on the Um interface is different from the - * order of fields in RLS. See TS 04.04 (Chapter 7.2) - * vs. TS 08.58 (Chapter 9.3.10). */ - lchan->meas.l1_info[0] = data[0] << 3; - lchan->meas.l1_info[0] |= ((data[0] >> 5) & 1) << 2; - lchan->meas.l1_info[1] = data[1]; - lchan->meas.flags |= LC_UL_M_F_L1_VALID; - - lchan_ms_pwr_ctrl(lchan, data[0] & 0x1f, data_ind->rssi); - } else - le = &lchan->lapdm_ch.lapdm_dcch; + if (rsl_chan_rt_is_asci(lchan->rsl_chan_rt)) { + /* report first valid received frame to VGCS talker process */ + if (lchan->asci.talker_active == VGCS_TALKER_WAIT_FRAME) + vgcs_talker_frame(lchan); + /* Do not forward any message that is received on the uplink to LAPD while + * the uplink is not active. If the MS did not recognize (fast enough) that + * the uplink is free, it may continue to transmit LAPD messages. A + * response by LAPD to these messages is not desired and not required. If + * LAPD would respond, it would cause stopping transmission of UPLINK FREE + * messages. No MS could access the uplink anymore. + */ + if (lchan->asci.talker_active != VGCS_TALKER_ACTIVE) + return 0; + } if (check_for_first_ciphrd(lchan, data, len)) l1sap_tx_ciph_req(lchan->ts->trx, chan_nr, 1, 0); /* SDCCH, SACCH and FACCH all go to LAPDm */ - msgb_pull(msg, (msg->l2h - msg->data)); - msg->l1h = NULL; + msgb_pull_to_l2(msg); lapdm_phsap_up(&l1sap->oph, le); + /* Notify current receive FN to lapdm. */ + lapdm_t200_fn(le, fn); + /* don't free, because we forwarded data */ return 1; } +/* process one MAC block of unpacked bits of a non-transparent CSD channel */ +static void gsmtap_csd_rlp_process(struct gsm_lchan *lchan, bool is_uplink, + const struct ph_tch_param *tch_ind, + const uint8_t *data, unsigned int data_len) +{ + struct gsm_bts_trx *trx = lchan->ts->trx; + struct gsmtap_inst *inst = trx->bts->gsmtap.inst; + struct osmo_rlp_frame_decoded rlpf; + pbit_t *rlp_buf; + uint16_t arfcn; + int byte_len; + + if (!inst || !trx->bts->gsmtap.rlp) + return; + + if (lchan->csd_mode != LCHAN_CSD_M_NT) + return; + + if (is_uplink) + rlp_buf = lchan->tch.csd.rlp_buf_ul; + else + rlp_buf = lchan->tch.csd.rlp_buf_dl; + + /* TCH/F 9.6: 4x60bit block => 240bit RLP frame + * TCH/F 4.8: 2x 2x60bit blocks starting at B0/B2/B4 => 240bit RLP frame + * TCH/H 4.8: 4x60bit block => 240bit RLP frame + * TCH/F 2.4: 2x36bit blocks => transparent only + * TCH/H 2.4: 4x36bit blocks => transparent only + * TCH/F 14.4: 2x 290 bit block (starting with M1=0) => 576-bit RLP frame + */ + + if (lchan->type == GSM_LCHAN_TCH_F && lchan->tch_mode == GSM48_CMODE_DATA_6k0) { + /* in this mode we have 120bit MAC blocks; two of them need to be concatenated + * to render a 240-bit RLP frame. The fist block is present in B0/B2/B4. + * The E7 bit is used to indicate the Frame MF0a */ + OSMO_ASSERT(data_len == 120); + ubit_t e7 = data[4*7+3]; + if (e7 == 0) { + osmo_ubit2pbit_ext(rlp_buf, 0, data, 0, data_len, 1); + return; + } else { + osmo_ubit2pbit_ext(rlp_buf, 120, data, 0, data_len, 1); + byte_len = 240/8; + } + } else if (lchan->type == GSM_LCHAN_TCH_F && lchan->tch_mode == GSM48_CMODE_DATA_14k5) { + /* in this mode we have 290bit MAC blocks containing M1, M2 and 288 data bits; + * two of them need to be concatenated to render a + * 576-bit RLP frame. The start of a RLP frame is + * denoted by a block with M1-bit set to 0. */ + OSMO_ASSERT(data_len == 290); + ubit_t m1 = data[0]; + if (m1 == 0) { + osmo_ubit2pbit_ext(rlp_buf, 0, data, 2, data_len, 1); + return; + } else { + osmo_ubit2pbit_ext(rlp_buf, 288, data, 2, data_len, 1); + byte_len = 576/8; + } + } else { + byte_len = osmo_ubit2pbit_ext(rlp_buf, 0, data, 0, data_len, 1); + } + + if (trx->bts->gsmtap.rlp_skip_null) { + int rc = osmo_rlp_decode(&rlpf, 0, rlp_buf, byte_len); + if (rc == 0 && rlpf.ftype == OSMO_RLP_FT_U && rlpf.u_ftype == OSMO_RLP_U_FT_NULL) + return; + } + + arfcn = trx->arfcn; + if (is_uplink) + arfcn |= GSMTAP_ARFCN_F_UPLINK; + + gsmtap_send_ex(inst, GSMTAP_TYPE_GSM_RLP, arfcn, lchan->ts->nr, + lchan->type == GSM_LCHAN_TCH_H ? GSMTAP_CHANNEL_VOICE_H : GSMTAP_CHANNEL_VOICE_F, + lchan->nr, tch_ind->fn, tch_ind->rssi, 0, rlp_buf, byte_len); + +} + +static void send_ul_rtp_packet_data(struct gsm_lchan *lchan, const struct ph_tch_param *tch_ind, + const uint8_t *data, uint16_t data_len) +{ + struct gsm_bts *bts = lchan->ts->trx->bts; + uint8_t rtp_pl[RFC4040_RTP_PLEN]; + int rc; + + gsmtap_csd_rlp_process(lchan, true, tch_ind, data, data_len); + + rc = csd_v110_rtp_encode(lchan, &rtp_pl[0], data, data_len); + if (rc < 0) + return; + + rate_ctr_inc2(bts->ctrs, BTS_CTR_RTP_TX_TOTAL); + if (lchan->rtp_tx_marker) + rate_ctr_inc2(bts->ctrs, BTS_CTR_RTP_TX_MARKER); + + osmo_rtp_send_frame_ext(lchan->abis_ip.rtp_socket, + &rtp_pl[0], sizeof(rtp_pl), + fn_ms_adj(tch_ind->fn, lchan), + lchan->rtp_tx_marker); + /* Only clear the marker bit once we have sent a RTP packet with it */ + lchan->rtp_tx_marker = false; +} + +/* a helper function for the logic in l1sap_tch_ind() */ +static void send_ul_rtp_packet_speech(struct gsm_lchan *lchan, uint32_t fn, + const uint8_t *rtp_pl, uint16_t rtp_pl_len) +{ + struct gsm_bts *bts = lchan->ts->trx->bts; + + if (lchan->abis_ip.osmux.use) { + lchan_osmux_send_frame(lchan, rtp_pl, rtp_pl_len, + fn_ms_adj(fn, lchan), lchan->rtp_tx_marker); + } else if (lchan->abis_ip.rtp_socket) { + osmo_rtp_send_frame_ext(lchan->abis_ip.rtp_socket, + rtp_pl, rtp_pl_len, fn_ms_adj(fn, lchan), lchan->rtp_tx_marker); + rate_ctr_inc2(bts->ctrs, BTS_CTR_RTP_TX_TOTAL); + if (lchan->rtp_tx_marker) + rate_ctr_inc2(bts->ctrs, BTS_CTR_RTP_TX_MARKER); + } + /* Only clear the marker bit once we have sent a RTP packet with it */ + lchan->rtp_tx_marker = false; +} + +/* a helper function for emitting HR1 UL in RFC 5993 format */ +static void send_rtp_rfc5993(struct gsm_lchan *lchan, uint32_t fn, + struct msgb *msg) +{ + uint8_t toc; + + OSMO_ASSERT(msg->len == GSM_HR_BYTES); + /* FIXME: implement proper SID classification per GSM 06.41 section + * 6.1.1; see OS#6036. Until then, detect error-free SID frames + * using our existing osmo_hr_check_sid() function. */ + if (osmo_hr_check_sid(msg->data, msg->len)) + toc = 0x20; + else + toc = 0x00; + msgb_push_u8(msg, toc); + send_ul_rtp_packet_speech(lchan, fn, msg->data, msg->len); +} + +/* A helper function for l1sap_tch_ind(): handling BFI + * + * Please note that we pass the msgb to this function, even though it is + * currently not used. This msgb passing is a provision for adding + * support for TRAU-UL-like RTP payload formats like TW-TS-001 that allow + * indicating BFI along with deemed-bad frame data bits, just like + * GSM 08.60 and 08.61 TRAU-UL frames. + */ +static void tch_ul_bfi_handler(struct gsm_lchan *lchan, + const struct gsm_time *g_time, struct msgb *msg) +{ + uint32_t fn = g_time->fn; + uint8_t ecu_out[GSM_FR_BYTES]; + int rc; + + /* Are we applying an ECU to this uplink, and are we in a state + * (not DTX pause) where we emit ECU output? */ + if (lchan->ecu_state && !osmo_ecu_is_dtx_pause(lchan->ecu_state)) { + rc = osmo_ecu_frame_out(lchan->ecu_state, ecu_out); + /* did it actually give us some output? */ + if (rc > 0) { + /* yes, send it out in RTP */ + send_ul_rtp_packet_speech(lchan, fn, ecu_out, rc); + return; + } + } + + /* Are we in rtp continuous-streaming special mode? If so, send out + * a BFI packet as zero-length RTP payload. */ + if (lchan->ts->trx->bts->rtp_nogaps_mode) { + send_ul_rtp_packet_speech(lchan, fn, NULL, 0); + return; + } + + /* Most classic form of BFI handling: generate an intentional gap + * in the outgoing RTP stream. */ + LOGPLCGT(lchan, g_time, DRTP, LOGL_DEBUG, + "Skipping RTP frame with lost payload\n"); + if (lchan->abis_ip.osmux.use) + lchan_osmux_skipped_frame(lchan, fn_ms_adj(fn, lchan)); + else if (lchan->abis_ip.rtp_socket) + osmo_rtp_skipped_frame(lchan->abis_ip.rtp_socket, fn_ms_adj(fn, lchan)); + lchan->rtp_tx_marker = true; +} + /* TCH received from bts model */ static int l1sap_tch_ind(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap, struct ph_tch_param *tch_ind) @@ -1204,42 +2051,62 @@ static int l1sap_tch_ind(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap, gsm_fn2gsmtime(&g_time, fn); - LOGPGT(DL1P, LOGL_INFO, &g_time, "Rx TCH.ind chan_nr=%s\n", rsl_chan_nr_str(chan_nr)); - lchan = get_active_lchan_by_chan_nr(trx, chan_nr); if (!lchan) { LOGPGT(DL1P, LOGL_ERROR, &g_time, "No lchan for TCH.ind (chan_nr=%s)\n", rsl_chan_nr_str(chan_nr)); return 0; + } else { + LOGPLCGT(lchan, &g_time, DL1P, LOGL_DEBUG, "Rx TCH.ind\n"); } - msgb_pull(msg, sizeof(*l1sap)); + /* Notify current receive FN to lapdm. + * TCH frames may be indicated before FACCH frames are indicated. To prevent T200 timeout before FACCH is + * received, subtract one frame number, so that timeout is processed next time after FACCH is received. + */ + lapdm_t200_fn(&lchan->lapdm_ch.lapdm_dcch, GSM_TDMA_FN_SUB(fn, 1)); + + /* The ph_tch_param contained in the l1sap primitive may contain + * measurement data. If this data is present, forward it for + * processing */ + if (bts_internal_flag_get(trx->bts, BTS_INTERNAL_FLAG_MEAS_PAYLOAD_COMB)) + process_l1sap_meas_data(lchan, l1sap, PRIM_TCH); + + msgb_pull_to_l2(msg); /* Low level layers always call us when TCH content is expected, even if * the content is not available due to decoding issues. Content not * available is expected as empty payload. We also check if quality is * good enough. */ if (msg->len && tch_ind->lqual_cb >= bts->min_qual_norm) { + /* feed the good frame to the ECU, if we are applying one */ + if (lchan->ecu_state) + osmo_ecu_frame_in(lchan->ecu_state, false, msg->data, msg->len); /* hand msg to RTP code for transmission */ - if (lchan->abis_ip.rtp_socket) - osmo_rtp_send_frame_ext(lchan->abis_ip.rtp_socket, - msg->data, msg->len, fn_ms_adj(fn, lchan), lchan->rtp_tx_marker); + switch (lchan->rsl_cmode) { + case RSL_CMOD_SPD_SPEECH: + if (bts->emit_hr_rfc5993 && lchan->type == GSM_LCHAN_TCH_H && + lchan->tch_mode == GSM48_CMODE_SPEECH_V1) + send_rtp_rfc5993(lchan, fn, msg); + else + send_ul_rtp_packet_speech(lchan, fn, msg->data, msg->len); + break; + case RSL_CMOD_SPD_DATA: + send_ul_rtp_packet_data(lchan, tch_ind, msg->data, msg->len); + break; + case RSL_CMOD_SPD_SIGN: + return 0; /* drop stale TCH.ind */ + default: /* shall not happen */ + OSMO_ASSERT(0); + } /* if loopback is enabled, also queue received RTP data */ if (lchan->loopback) { - /* make sure the queue doesn't get too long */ - queue_limit_to(gsm_lchan_name(lchan), &lchan->dl_tch_queue, 1); - /* add new frame to queue */ - msgb_enqueue(&lchan->dl_tch_queue, msg); + /* add new frame to queue, make sure the queue doesn't get too long */ + lchan_dl_tch_queue_enqueue(lchan, msg, 1); /* Return 1 to signal that we're still using msg and it should not be freed */ return 1; } - /* Only clear the marker bit once we have sent a RTP packet with it */ - lchan->rtp_tx_marker = false; } else { - DEBUGPGT(DRTP, &g_time, "Skipping RTP frame with lost payload (chan_nr=0x%02x)\n", - chan_nr); - if (lchan->abis_ip.rtp_socket) - osmo_rtp_skipped_frame(lchan->abis_ip.rtp_socket, fn_ms_adj(fn, lchan)); - lchan->rtp_tx_marker = true; + tch_ul_bfi_handler(lchan, &g_time, msg); } lchan->tch.last_fn = fn; @@ -1248,14 +2115,15 @@ static int l1sap_tch_ind(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap, #define RACH_MIN_TOA256 -2 * 256 -static bool rach_pass_filter(struct ph_rach_ind_param *rach_ind, struct gsm_bts *bts) +static bool rach_pass_filter(struct ph_rach_ind_param *rach_ind, struct gsm_bts *bts, + const char *chan_name) { int16_t toa256 = rach_ind->acc_delay_256bits; /* Check for RACH exceeding BER threshold (ghost RACH) */ if (rach_ind->ber10k > bts->max_ber10k_rach) { - LOGPFN(DL1C, LOGL_INFO, rach_ind->fn, "Ignoring RACH request: " - "BER10k(%u) > BER10k_MAX(%u)\n", + LOGPFN(DL1C, LOGL_DEBUG, rach_ind->fn, "Ignoring an Access Burst on %s: " + "BER10k(%u) > BER10k_MAX(%u)\n", chan_name, rach_ind->ber10k, bts->max_ber10k_rach); return false; } @@ -1266,16 +2134,16 @@ static bool rach_pass_filter(struct ph_rach_ind_param *rach_ind, struct gsm_bts * according to maximal allowed Timing Advance value. */ if (toa256 < RACH_MIN_TOA256 || toa256 > bts->max_ta * 256) { - LOGPFN(DL1C, LOGL_INFO, rach_ind->fn, "Ignoring RACH request: " - "ToA(%d) exceeds the allowed range (%d..%d)\n", + LOGPFN(DL1C, LOGL_DEBUG, rach_ind->fn, "Ignoring an Access Burst on %s: " + "ToA(%d) exceeds the allowed range (%d..%d)\n", chan_name, toa256, RACH_MIN_TOA256, bts->max_ta * 256); return false; } /* Link quality defined by C/I (Carrier-to-Interference ratio) */ if (rach_ind->lqual_cb < bts->min_qual_rach) { - LOGPFN(DL1C, LOGL_INFO, rach_ind->fn, "Ignoring RACH request: " - "link quality (%d) below the minimum (%d)\n", + LOGPFN(DL1C, LOGL_DEBUG, rach_ind->fn, "Ignoring an Access Burst on %s: " + "link quality (%d) below the minimum (%d)\n", chan_name, rach_ind->lqual_cb, bts->min_qual_rach); return false; } @@ -1283,23 +2151,58 @@ static bool rach_pass_filter(struct ph_rach_ind_param *rach_ind, struct gsm_bts return true; } -/* Special case where handover RACH is detected */ -static int l1sap_handover_rach(struct gsm_bts_trx *trx, - struct osmo_phsap_prim *l1sap, struct ph_rach_ind_param *rach_ind) +/* Special case where RACH on DCCH uplink is detected */ +static int l1sap_dcch_rach(struct gsm_bts_trx *trx, struct ph_rach_ind_param *rach_ind) { + struct gsm_lchan *lchan; + /* Filter out noise / interference / ghosts */ - if (!rach_pass_filter(rach_ind, trx->bts)) { + if (!rach_pass_filter(rach_ind, trx->bts, "DCCH")) { rate_ctr_inc2(trx->bts->ctrs, BTS_CTR_RACH_DROP); return 0; } - handover_rach(get_lchan_by_chan_nr(trx, rach_ind->chan_nr), - rach_ind->ra, rach_ind->acc_delay); + lchan = get_lchan_by_chan_nr(trx, rach_ind->chan_nr); + /* Differentiate + dispatch hand-over and VGCS RACH */ + if (rsl_chan_rt_is_asci(lchan->rsl_chan_rt)) { + rate_ctr_inc2(trx->bts->ctrs, BTS_CTR_RACH_VGCS); + vgcs_rach(lchan, rach_ind->ra, rach_ind->acc_delay, rach_ind->fn); + } else { + rate_ctr_inc2(trx->bts->ctrs, BTS_CTR_RACH_HO); + handover_rach(lchan, rach_ind->ra, rach_ind->acc_delay); + } /* must return 0, so in case of msg at l1sap, it will be freed */ return 0; } +/* Special case for Access Bursts on PDTCH/U or PTCCH/U */ +static int l1sap_pdch_rach(struct gsm_bts_trx *trx, struct ph_rach_ind_param *rach_ind) +{ + /* Filter out noise / interference / ghosts */ + if (!rach_pass_filter(rach_ind, trx->bts, "PDCH")) + return -EAGAIN; + + /* PTCCH/U (Packet Timing Advance Control Channel) */ + if (L1SAP_IS_PTCCH(rach_ind->fn)) { + LOGPFN(DL1P, LOGL_DEBUG, rach_ind->fn, + /* TODO: calculate and print Timing Advance Index */ + "Access Burst for continuous Timing Advance control (toa256=%d)\n", + rach_ind->acc_delay_256bits); + + /* QTA: Timing Advance in units of 1/4 of a symbol */ + pcu_tx_rach_ind(trx->bts->nr, trx->nr, rach_ind->chan_nr & 0x07, + rach_ind->acc_delay_256bits >> 6, + rach_ind->ra, rach_ind->fn, rach_ind->is_11bit, + rach_ind->burst_type, PCU_IF_SAPI_PTCCH); + return 0; + } else { /* The MS may acknowledge DL data by 4 consequent Access Bursts */ + LOGPFN(DL1P, LOGL_NOTICE, rach_ind->fn, + "Access Bursts on PDTCH/U are not (yet) supported\n"); + return -ENOTSUP; + } +} + /* RACH received from bts model */ static int l1sap_ph_rach_ind(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap, struct ph_rach_ind_param *rach_ind) @@ -1309,10 +2212,16 @@ static int l1sap_ph_rach_ind(struct gsm_bts_trx *trx, DEBUGPFN(DL1P, rach_ind->fn, "Rx PH-RA.ind\n"); - /* check for handover access burst on dedicated channels */ - if (!L1SAP_IS_CHAN_RACH(rach_ind->chan_nr)) { - rate_ctr_inc2(trx->bts->ctrs, BTS_CTR_RACH_HO); - return l1sap_handover_rach(trx, l1sap, rach_ind); + /* Check the origin of an Access Burst */ + switch (rach_ind->chan_nr & 0xf8) { + case RSL_CHAN_RACH: + /* CS or PS RACH, to be handled in this function */ + break; + case RSL_CHAN_OSMO_PDCH: + /* TODO: do we need to count Access Bursts on PDCH? */ + return l1sap_pdch_rach(trx, rach_ind); + default: + return l1sap_dcch_rach(trx, rach_ind); } rate_ctr_inc2(trx->bts->ctrs, BTS_CTR_RACH_RCVD); @@ -1322,7 +2231,7 @@ static int l1sap_ph_rach_ind(struct gsm_bts_trx *trx, bts->load.rach.busy++; /* Filter out noise / interference / ghosts */ - if (!rach_pass_filter(rach_ind, bts)) { + if (!rach_pass_filter(rach_ind, bts, "CCCH")) { rate_ctr_inc2(trx->bts->ctrs, BTS_CTR_RACH_DROP); return 0; } @@ -1344,9 +2253,11 @@ static int l1sap_ph_rach_ind(struct gsm_bts_trx *trx, LOGPFN(DL1P, LOGL_INFO, rach_ind->fn, "RACH for packet access (toa=%d, ra=%d)\n", rach_ind->acc_delay, rach_ind->ra); - pcu_tx_rach_ind(bts, rach_ind->acc_delay << 2, - rach_ind->ra, rach_ind->fn, - rach_ind->is_11bit, rach_ind->burst_type); + /* QTA: Timing Advance in units of 1/4 of a symbol */ + pcu_tx_rach_ind(bts->nr, trx->nr, rach_ind->chan_nr & 0x07, + rach_ind->acc_delay_256bits >> 6, + rach_ind->ra, rach_ind->fn, rach_ind->is_11bit, + rach_ind->burst_type, PCU_IF_SAPI_RACH); return 0; } @@ -1412,6 +2323,9 @@ int l1sap_up(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap) /* any L1 prim sent to bts model */ static int l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap) { + l1sap_log_ctx_sapi = get_common_sapi_by_trx_prim(trx, l1sap); + log_set_context(LOG_CTX_L1_SAPI, &l1sap_log_ctx_sapi); + if (OSMO_PRIM_HDR(&l1sap->oph) == OSMO_PRIM(PRIM_PH_DATA, PRIM_OP_REQUEST)) to_gsmtap(trx, l1sap); @@ -1440,8 +2354,12 @@ int l1sap_pdch_req(struct gsm_bts_trx_ts *ts, int is_ptcch, uint32_t fn, l1sap->u.data.chan_nr = RSL_CHAN_OSMO_PDCH | ts->nr; l1sap->u.data.link_id = 0x00; l1sap->u.data.fn = fn; - msg->l2h = msgb_put(msg, len); - memcpy(msg->l2h, data, len); + if (len) { + msg->l2h = msgb_put(msg, len); + memcpy(msg->l2h, data, len); + } else { + msg->l2h = NULL; /* Idle block */ + } return l1sap_down(ts->trx, l1sap); } @@ -1452,19 +2370,59 @@ void l1sap_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl, uint32_t timestamp, bool marker) { struct gsm_lchan *lchan = rs->priv; + struct gsm_bts *bts = lchan->ts->trx->bts; struct msgb *msg; - struct osmo_phsap_prim *l1sap; + bool rfc5993_sid = false; + + rate_ctr_inc2(bts->ctrs, BTS_CTR_RTP_RX_TOTAL); + if (marker) + rate_ctr_inc2(bts->ctrs, BTS_CTR_RTP_RX_MARKER); /* if we're in loopback mode, we don't accept frames from the * RTP socket anymore */ - if (lchan->loopback) + if (lchan->loopback) { + rate_ctr_inc2(bts->ctrs, BTS_CTR_RTP_RX_DROP_LOOPBACK); return; + } - msg = l1sap_msgb_alloc(rtp_pl_len); + /* initial preen */ + switch (rtp_payload_input_preen(lchan, rtp_pl, rtp_pl_len, &rfc5993_sid)) { + case PL_DECISION_DROP: + rate_ctr_inc2(bts->ctrs, BTS_CTR_RTP_RX_DROP_PREEN); + return; + case PL_DECISION_ACCEPT: + break; + case PL_DECISION_STRIP_HDR_OCTET: + rtp_pl++; + rtp_pl_len--; + break; + default: + OSMO_ASSERT(0); + } + + msg = l1sap_msgb_alloc(512); if (!msg) return; - memcpy(msgb_put(msg, rtp_pl_len), rtp_pl, rtp_pl_len); - msgb_pull(msg, sizeof(*l1sap)); + + if (lchan->rsl_cmode == RSL_CMOD_SPD_DATA) { + int rc = csd_v110_rtp_decode(lchan, msg->tail, + rtp_pl, rtp_pl_len); + if (rc > 0) { + /* 'fake' tch_ind containing all-zero so gsmtap code can be shared + * between UL and DL */ + static const struct ph_tch_param fake_tch_ind = {}; + gsmtap_csd_rlp_process(lchan, false, &fake_tch_ind, msg->tail, rc); + msgb_put(msg, rc); + } else { + rate_ctr_inc2(bts->ctrs, BTS_CTR_RTP_RX_DROP_V110_DEC); + msgb_free(msg); + return; + } + } else { + memcpy(msgb_put(msg, rtp_pl_len), rtp_pl, rtp_pl_len); + } + + msgb_pull(msg, sizeof(struct osmo_phsap_prim)); /* Store RTP header Marker bit in control buffer */ rtpmsg_marker_bit(msg) = marker; @@ -1472,11 +2430,11 @@ void l1sap_rtp_rx_cb(struct osmo_rtp_socket *rs, const uint8_t *rtp_pl, rtpmsg_seq(msg) = seq_number; /* Store RTP header Timestamp in control buffer */ rtpmsg_ts(msg) = timestamp; + /* Store RFC 5993 SID flag likewise */ + rtpmsg_is_rfc5993_sid(msg) = rfc5993_sid; /* make sure the queue doesn't get too long */ - queue_limit_to(gsm_lchan_name(lchan), &lchan->dl_tch_queue, 1); - - msgb_enqueue(&lchan->dl_tch_queue, msg); + lchan_dl_tch_queue_enqueue(lchan, msg, 1); } static int l1sap_chan_act_dact_modify(struct gsm_bts_trx *trx, uint8_t chan_nr, @@ -1494,53 +2452,50 @@ static int l1sap_chan_act_dact_modify(struct gsm_bts_trx *trx, uint8_t chan_nr, return l1sap_down(trx, &l1sap); } -int l1sap_chan_act(struct gsm_bts_trx *trx, uint8_t chan_nr, struct tlv_parsed *tp) +int l1sap_chan_act(struct gsm_bts_trx *trx, uint8_t chan_nr) { struct gsm_lchan *lchan = get_lchan_by_chan_nr(trx, chan_nr); - struct gsm48_chan_desc *cd; int rc; - LOGPLCHAN(lchan, DL1C, LOGL_INFO, "activating channel chan_nr=%s trx=%d\n", - rsl_chan_nr_str(chan_nr), trx->nr); - - /* osmo-pcu calls this without a valid 'tp' parameter, so we - * need to make sure ew don't crash here */ - if (tp && TLVP_PRESENT(tp, GSM48_IE_CHANDESC_2) && - TLVP_LEN(tp, GSM48_IE_CHANDESC_2) >= sizeof(*cd)) { - cd = (struct gsm48_chan_desc *) - TLVP_VAL(tp, GSM48_IE_CHANDESC_2); - - /* our L1 only supports one global TSC for all channels - * one one TRX, so we need to make sure not to activate - * channels with a different TSC!! */ - if (cd->h0.tsc != (lchan->ts->trx->bts->bsic & 7)) { - LOGPLCHAN(lchan, DL1C, LOGL_ERROR, "lchan TSC %u != BSIC-TSC %u\n", - cd->h0.tsc, lchan->ts->trx->bts->bsic & 7); - return -RSL_ERR_SERV_OPT_UNIMPL; - } + if (lchan->state == LCHAN_S_ACTIVE) { + LOGPLCHAN(lchan, DL1C, LOGL_ERROR, "Trying to activate already active channel %s\n", + rsl_chan_nr_str(chan_nr)); + return -1; } - lchan->sacch_deact = 0; - lchan->s = lchan->ts->trx->bts->radio_link_timeout; + LOGPLCHAN(lchan, DL1C, LOGL_INFO, "Activating channel %s\n", rsl_chan_nr_str(chan_nr)); + + radio_link_timeout_reset(lchan); rc = l1sap_chan_act_dact_modify(trx, chan_nr, PRIM_INFO_ACTIVATE, 0); if (rc) return -RSL_ERR_EQUIPMENT_FAIL; + /* Is it TCH? If it is, attempt to allocate an Error Concealment Unit + * instance, if available, unless it is disabled by vty config. */ + if (lchan_is_tch(lchan) && trx->bts->use_ul_ecu) + lchan->ecu_state = osmo_ecu_init(trx, lchan2ecu_codec(lchan)); + else + lchan->ecu_state = NULL; + /* Init DTX DL FSM if necessary */ - if (trx->bts->dtxd && lchan->type != GSM_LCHAN_SDCCH) { - char name[32]; - snprintf(name, sizeof(name), "bts%u-trx%u-ts%u-ss%u", lchan->ts->trx->bts->nr, - lchan->ts->trx->nr, lchan->ts->nr, lchan->nr); + if (trx->bts->dtxd && lchan_is_tch(lchan)) { lchan->tch.dtx.dl_amr_fsm = osmo_fsm_inst_alloc(&dtx_dl_amr_fsm, tall_bts_ctx, lchan, LOGL_DEBUG, - name); + NULL); if (!lchan->tch.dtx.dl_amr_fsm) { l1sap_chan_act_dact_modify(trx, chan_nr, PRIM_INFO_DEACTIVATE, 0); return -RSL_ERR_EQUIPMENT_FAIL; } + + rc = osmo_fsm_inst_update_id_f(lchan->tch.dtx.dl_amr_fsm, + "bts%u-trx%u-ts%u-ss%u%s", + trx->bts->nr, trx->nr, + lchan->ts->nr, lchan->nr, + lchan->ts->vamos.is_shadow ? "-shadow" : ""); + OSMO_ASSERT(rc == 0); } return 0; } @@ -1548,14 +2503,31 @@ int l1sap_chan_act(struct gsm_bts_trx *trx, uint8_t chan_nr, struct tlv_parsed * int l1sap_chan_rel(struct gsm_bts_trx *trx, uint8_t chan_nr) { struct gsm_lchan *lchan = get_lchan_by_chan_nr(trx, chan_nr); - LOGPLCHAN(lchan, DL1C, LOGL_INFO, "deactivating channel chan_nr=%s trx=%d\n", - rsl_chan_nr_str(chan_nr), trx->nr); + + if (lchan->state == LCHAN_S_NONE) { + LOGPLCHAN(lchan, DL1C, LOGL_ERROR, "Trying to deactivate already deactivated channel %s\n", + rsl_chan_nr_str(chan_nr)); + return -1; + } + + LOGPLCHAN(lchan, DL1C, LOGL_INFO, "Deactivating channel %s\n", + rsl_chan_nr_str(chan_nr)); if (lchan->tch.dtx.dl_amr_fsm) { osmo_fsm_inst_free(lchan->tch.dtx.dl_amr_fsm); lchan->tch.dtx.dl_amr_fsm = NULL; } + /* clear ECU state (if any) */ + if (lchan->ecu_state) { + osmo_ecu_destroy(lchan->ecu_state); + lchan->ecu_state = NULL; + } + + /* reset CSD RLP buffers to avoid any user plane data leaking from + * one previous lchan into a later one */ + memset(&lchan->tch.csd, 0, sizeof(lchan->tch.csd)); + return l1sap_chan_act_dact_modify(trx, chan_nr, PRIM_INFO_DEACTIVATE, 0); } @@ -1564,10 +2536,8 @@ int l1sap_chan_deact_sacch(struct gsm_bts_trx *trx, uint8_t chan_nr) { struct gsm_lchan *lchan = get_lchan_by_chan_nr(trx, chan_nr); - LOGPLCHAN(lchan, DL1C, LOGL_INFO, "deactivating sacch chan_nr=%s trx=%d\n", - rsl_chan_nr_str(chan_nr), trx->nr); - - lchan->sacch_deact = 1; + LOGPLCHAN(lchan, DL1C, LOGL_INFO, "Deactivating SACCH on channel %s\n", + rsl_chan_nr_str(chan_nr)); return l1sap_chan_act_dact_modify(trx, chan_nr, PRIM_INFO_DEACTIVATE, 1); @@ -1575,8 +2545,46 @@ int l1sap_chan_deact_sacch(struct gsm_bts_trx *trx, uint8_t chan_nr) int l1sap_chan_modify(struct gsm_bts_trx *trx, uint8_t chan_nr) { - LOGP(DL1C, LOGL_INFO, "modifying channel chan_nr=%s trx=%d\n", - rsl_chan_nr_str(chan_nr), trx->nr); + struct gsm_lchan *lchan = get_lchan_by_chan_nr(trx, chan_nr); + + LOGPLCHAN(lchan, DL1C, LOGL_INFO, "Modifying channel %s\n", + rsl_chan_nr_str(chan_nr)); + + /* Is it TCH? If it is and we are applying internal uplink ECUs, + * the new channel mode calls for a different ECU. Any changes + * in vty config (enabling or disabling this ECU application) + * will also take effect upon channel modification. */ + if (lchan_is_tch(lchan)) { + if (lchan->ecu_state) + osmo_ecu_destroy(lchan->ecu_state); + if (trx->bts->use_ul_ecu) + lchan->ecu_state = osmo_ecu_init(trx, lchan2ecu_codec(lchan)); + else + lchan->ecu_state = NULL; + } return l1sap_chan_act_dact_modify(trx, chan_nr, PRIM_INFO_MODIFY, 0); } + +int l1sap_uplink_access(struct gsm_lchan *lchan, bool active) +{ + uint8_t chan_nr = gsm_lchan2chan_nr(lchan); + struct osmo_phsap_prim l1sap; + + if (lchan->state != LCHAN_S_ACTIVE && !rsl_chan_rt_is_asci(lchan->rsl_chan_rt)) { + LOGPLCHAN(lchan, DL1C, LOGL_ERROR, "Channel %s is not an active ASCI type channel.\n", + rsl_chan_nr_str(chan_nr)); + return -EINVAL; + } + + LOGPLCHAN(lchan, DL1C, LOGL_INFO, "%s uplink access detection on channel %s\n", + (active) ? "Activating" : "Deactivating", rsl_chan_nr_str(chan_nr)); + + + memset(&l1sap, 0, sizeof(l1sap)); + osmo_prim_init(&l1sap.oph, SAP_GSM_PH, PRIM_MPH_INFO, PRIM_OP_REQUEST, NULL); + l1sap.u.info.type = (active) ? PRIM_INFO_ACT_UL_ACC : PRIM_INFO_DEACT_UL_ACC; + l1sap.u.info.u.ulacc_req.chan_nr = chan_nr; + + return l1sap_down(lchan->ts->trx, &l1sap); +} |