fixeria has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-bts/+/43863?usp=email )
Change subject: rsl: support Early IA with frequency hopping ......................................................................
rsl: support Early IA with frequency hopping
When osmo-bsc sends the RR Immediate Assignment before the target lchan is active (early IA), osmo-bts holds it back until the lchan is activated. The target TRX is currently found by the ARFCN in the Channel Description, so this only works if hopping is disabled (H=0). With hopping enabled (H=1), the Channel Description has MAIO/HSN and no ARFCN, so the IMM ASS is sent to the MS right away.
For H=1, find the target TRX whose timeslot hops with the given MAIO/HSN instead. If more than one TRX matches (different hopping groups sharing the same MAIO/HSN on the same timeslot), don't hold back the IMM ASS, the same as when no TRX matches.
This makes BTS_Tests.TC_early_immediate_assignment pass when run in the frequency hopping configuration.
Change-Id: I632e46cccbf1151a88d2dfaf8cb42ce2647206a5 AI-Assisted: yes (Claude) Related: SYS#5559 --- M src/common/rsl.c 1 file changed, 45 insertions(+), 19 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/osmo-bts refs/changes/63/43863/1
diff --git a/src/common/rsl.c b/src/common/rsl.c index 88a5f17..428a3a9 100644 --- a/src/common/rsl.c +++ b/src/common/rsl.c @@ -394,6 +394,28 @@ return NULL; }
+/* Find the TRX whose timeslot tn hops with the given MAIO/HSN. + * Returns NULL if no TRX matches, or if more than one does. */ +static struct gsm_bts_trx *trx_lookup_by_maio_hsn(struct gsm_bts *bts, uint8_t tn, + uint8_t maio, uint8_t hsn) +{ + struct gsm_bts_trx *trx, *found = NULL; + llist_for_each_entry(trx, &bts->trx_list, list) { + const struct gsm_bts_trx_ts *ts = &trx->ts[tn]; + if (!ts->hopping.enabled) + continue; + if (ts->hopping.maio != maio || ts->hopping.hsn != hsn) + continue; + if (found) { + LOGP(DRSL, LOGL_NOTICE, "Ambiguous MAIO=%u/HSN=%u on TS%u " + "(TRX%u and TRX%u)\n", maio, hsn, tn, found->nr, trx->nr); + return NULL; + } + found = trx; + } + return found; +} + static struct gsm_lchan *lchan_lookup(struct gsm_bts_trx *trx, uint8_t chan_nr, const char *log_name) { @@ -1409,29 +1431,33 @@ * 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. */ + * ARFCN, so instead we look up the TRX whose timeslot hops with the given MAIO/HSN. If this is ambiguous + * (different hopping groups sharing the same MAIO/HSN on the same timeslot), don't do the RR IA caching. */ 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; + const struct gsm48_imm_ass *rr_ia = (void *)msg->data; + const struct gsm48_chan_desc *cd = &rr_ia->chan_desc; + struct gsm_bts_trx *ia_target_trx;
+ if (cd->h0.h == 0) { /* hopping is disabled */ + uint16_t arfcn = (cd->h0.arfcn_high << 8) + cd->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; + } else { /* hopping is enabled */ + uint8_t maio = (cd->h1.maio_high << 2) + cd->h1.maio_low; + ia_target_trx = trx_lookup_by_maio_hsn(trx->bts, cd->chan_nr & 0x07, maio, cd->h1.hsn); + }
- /* return 1 means: don't msgb_free() the msg */ - return 1; - } + if (ia_target_trx) { + /* found the target trx */ + struct gsm_lchan *ia_target_lchan; + ia_target_lchan = lchan_lookup(ia_target_trx, cd->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; } } }