diff options
Diffstat (limited to 'src/osmo-bsc/bts_trx.c')
-rw-r--r-- | src/osmo-bsc/bts_trx.c | 271 |
1 files changed, 180 insertions, 91 deletions
diff --git a/src/osmo-bsc/bts_trx.c b/src/osmo-bsc/bts_trx.c index 25a3fc73e..49702d084 100644 --- a/src/osmo-bsc/bts_trx.c +++ b/src/osmo-bsc/bts_trx.c @@ -30,6 +30,31 @@ #include <osmocom/bsc/system_information.h> #include <osmocom/bsc/pcu_if.h> #include <osmocom/bsc/debug.h> +#include <osmocom/bsc/nm_common_fsm.h> +#include <osmocom/bsc/lchan.h> + +static int gsm_bts_trx_talloc_destructor(struct gsm_bts_trx *trx) +{ + unsigned int i; + + if (trx->bb_transc.mo.fi) { + osmo_fsm_inst_free(trx->bb_transc.mo.fi); + trx->bb_transc.mo.fi = NULL; + } + if (trx->mo.fi) { + osmo_fsm_inst_free(trx->mo.fi); + trx->mo.fi = NULL; + } + for (i = 0; i < TRX_NR_TS; i++) { + struct gsm_bts_trx_ts *ts = &trx->ts[i]; + if (ts->mo.fi) { + osmo_fsm_inst_free(ts->mo.fi); + ts->mo.fi = NULL; + } + ts_fsm_free(ts); + } + return 0; +} struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts) { @@ -39,12 +64,22 @@ struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts) if (!trx) return NULL; + talloc_set_destructor(trx, gsm_bts_trx_talloc_destructor); + trx->bts = bts; trx->nr = bts->num_trx++; - trx->mo.nm_state.administrative = NM_STATE_UNLOCKED; + trx->rsl_tei_primary = trx->nr; + + trx->mo.fi = osmo_fsm_inst_alloc(&nm_rcarrier_fsm, trx, trx, + LOGL_INFO, NULL); + osmo_fsm_inst_update_id_f(trx->mo.fi, "bts%d-trx%d", bts->nr, trx->nr); gsm_mo_init(&trx->mo, bts, NM_OC_RADIO_CARRIER, bts->nr, trx->nr, 0xff); + + trx->bb_transc.mo.fi = osmo_fsm_inst_alloc(&nm_bb_transc_fsm, trx, &trx->bb_transc, + LOGL_INFO, NULL); + osmo_fsm_inst_update_id_f(trx->bb_transc.mo.fi, "bts%d-trx%d", bts->nr, trx->nr); gsm_mo_init(&trx->bb_transc.mo, bts, NM_OC_BASEB_TRANSC, bts->nr, trx->nr, 0xff); @@ -60,6 +95,10 @@ struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts) ts_fsm_alloc(ts); + ts->mo.fi = osmo_fsm_inst_alloc(&nm_chan_fsm, trx, ts, + LOGL_INFO, NULL); + osmo_fsm_inst_update_id_f(ts->mo.fi, "bts%d-trx%d-ts%d", + bts->nr, trx->nr, ts->nr); gsm_mo_init(&ts->mo, bts, NM_OC_CHANNEL, bts->nr, trx->nr, ts->nr); @@ -69,22 +108,21 @@ struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts) ts->hopping.ma.data = ts->hopping.ma_data; for (l = 0; l < TS_MAX_LCHAN; l++) { - struct gsm_lchan *lchan; - char *name; - lchan = &ts->lchan[l]; - - lchan->ts = ts; - lchan->nr = l; - lchan->type = GSM_LCHAN_NONE; - - name = gsm_lchan_name_compute(lchan); - lchan->name = talloc_strdup(trx, name); + struct gsm_lchan *lchan = &ts->lchan[l]; + lchan_init(lchan, ts, l); } } if (trx->nr != 0) trx->nominal_power = bts->c0->nominal_power; + if (bts->model && bts->model->trx_init) { + if (bts->model->trx_init(trx) < 0) { + talloc_free(trx); + return NULL; + } + } + llist_add_tail(&trx->list, &bts->trx_list); return trx; @@ -111,37 +149,98 @@ struct gsm_lchan *rsl_lchan_lookup(struct gsm_bts_trx *trx, uint8_t chan_nr, uint8_t cbits = chan_nr >> 3; uint8_t lch_idx; struct gsm_bts_trx_ts *ts = &trx->ts[ts_nr]; + bool vamos = false; bool ok; if (rc) *rc = -EINVAL; - if (cbits == 0x01) { + /* Why call ts_is_capable_of_pchan() here? Dynamic timeslots may receive RSL Channel Activation ACK on a + * timeslot that is in transition between pchan modes. That ACK actually confirms the pchan switch, so instead + * of checking the current pchan mode, we must allow any pchans that a dyn TS is capable of. */ + + /* Interpret Osmocom specific cbits only for OsmoBTS type */ + if (trx->bts->model->type == GSM_BTS_TYPE_OSMOBTS) { + /* For VAMOS cbits, set vamos = true and handle cbits as their equivalent non-VAMOS cbits below. */ + switch (cbits) { + case ABIS_RSL_CHAN_NR_CBITS_OSMO_VAMOS_Bm_ACCHs: + case ABIS_RSL_CHAN_NR_CBITS_OSMO_VAMOS_Lm_ACCHs(0): + case ABIS_RSL_CHAN_NR_CBITS_OSMO_VAMOS_Lm_ACCHs(1): + cbits = (chan_nr & ~RSL_CHAN_OSMO_VAMOS_MASK) >> 3; + vamos = true; + break; + default: + break; + } + } + + switch (cbits) { + case ABIS_RSL_CHAN_NR_CBITS_Bm_ACCHs: lch_idx = 0; /* TCH/F */ ok = ts_is_capable_of_pchan(ts, GSM_PCHAN_TCH_F) || ts->pchan_on_init == GSM_PCHAN_PDCH; /* PDCH? really? */ - } else if ((cbits & 0x1e) == 0x02) { + if (!ok) + LOG_TRX(trx, DRSL, LOGL_ERROR, "chan_nr %x cbits %x: ts %s is not capable of GSM_PCHAN_TCH_F\n", + chan_nr, cbits, gsm_ts_and_pchan_name(ts)); + break; + case ABIS_RSL_CHAN_NR_CBITS_Lm_ACCHs(0): + case ABIS_RSL_CHAN_NR_CBITS_Lm_ACCHs(1): lch_idx = cbits & 0x1; /* TCH/H */ ok = ts_is_capable_of_pchan(ts, GSM_PCHAN_TCH_H); - } else if ((cbits & 0x1c) == 0x04) { + if (!ok) + LOG_TRX(trx, DRSL, LOGL_ERROR, "chan_nr 0x%x cbits 0x%x: %s is not capable of GSM_PCHAN_TCH_H\n", + chan_nr, cbits, gsm_ts_and_pchan_name(ts)); + break; + case ABIS_RSL_CHAN_NR_CBITS_SDCCH4_ACCH(0): + case ABIS_RSL_CHAN_NR_CBITS_SDCCH4_ACCH(1): + case ABIS_RSL_CHAN_NR_CBITS_SDCCH4_ACCH(2): + case ABIS_RSL_CHAN_NR_CBITS_SDCCH4_ACCH(3): lch_idx = cbits & 0x3; /* SDCCH/4 */ ok = ts_is_capable_of_pchan(ts, GSM_PCHAN_CCCH_SDCCH4); - } else if ((cbits & 0x18) == 0x08) { + if (!ok) + LOG_TRX(trx, DRSL, LOGL_ERROR, "chan_nr 0x%x cbits 0x%x: %s is not capable of GSM_PCHAN_CCCH_SDCCH4\n", + chan_nr, cbits, gsm_ts_and_pchan_name(ts)); + break; + 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): lch_idx = cbits & 0x7; /* SDCCH/8 */ ok = ts_is_capable_of_pchan(ts, GSM_PCHAN_SDCCH8_SACCH8C); - } else if (cbits == 0x10 || cbits == 0x11 || cbits == 0x12) { + if (!ok) + LOG_TRX(trx, DRSL, LOGL_ERROR, "chan_nr 0x%x cbits 0x%x: %s is not capable of GSM_PCHAN_SDCCH8_SACCH8C\n", + chan_nr, cbits, gsm_ts_and_pchan_name(ts)); + break; + case ABIS_RSL_CHAN_NR_CBITS_BCCH: + case ABIS_RSL_CHAN_NR_CBITS_RACH: + case ABIS_RSL_CHAN_NR_CBITS_PCH_AGCH: lch_idx = 0; /* CCCH? */ ok = ts_is_capable_of_pchan(ts, GSM_PCHAN_CCCH); + if (!ok) + LOG_TRX(trx, DRSL, LOGL_ERROR, "chan_nr 0x%x cbits 0x%x: %s is not capable of GSM_PCHAN_CCCH\n", + chan_nr, cbits, gsm_ts_and_pchan_name(ts)); /* FIXME: we should not return first sdcch4 !!! */ - } else if ((chan_nr & RSL_CHAN_NR_MASK) == RSL_CHAN_OSMO_PDCH) { + break; + case ABIS_RSL_CHAN_NR_CBITS_OSMO_PDCH: lch_idx = 0; - ok = (ts->pchan_on_init == GSM_PCHAN_TCH_F_TCH_H_PDCH); - } else + ok = ts_is_capable_of_pchan(ts, GSM_PCHAN_PDCH); + if (!ok) + LOG_TRX(trx, DRSL, LOGL_ERROR, "chan_nr 0x%x cbits 0x%x: %s is not capable of GSM_PCHAN_PDCH\n", + chan_nr, cbits, gsm_ts_and_pchan_name(ts)); + break; + default: return NULL; + } if (rc && ok) *rc = 0; + if (vamos) + lch_idx += ts->max_primary_lchans; return &ts->lchan[lch_idx]; } @@ -149,10 +248,9 @@ void gsm_trx_lock_rf(struct gsm_bts_trx *trx, bool locked, const char *reason) { uint8_t new_state = locked ? NM_STATE_LOCKED : NM_STATE_UNLOCKED; - + /* State will be sent when BTS connects. */ if (!trx->bts || !trx->bts->oml_link) { - /* Set initial state which will be sent when BTS connects. */ - trx->mo.nm_state.administrative = new_state; + trx->mo.force_rf_lock = locked; return; } @@ -160,9 +258,7 @@ void gsm_trx_lock_rf(struct gsm_bts_trx *trx, bool locked, const char *reason) get_value_string(abis_nm_adm_state_names, trx->mo.nm_state.administrative), get_value_string(abis_nm_adm_state_names, new_state), reason); - abis_nm_chg_adm_state(trx->bts, NM_OC_RADIO_CARRIER, - trx->bts->bts_nr, trx->nr, 0xff, - new_state); + osmo_fsm_inst_dispatch(trx->mo.fi, NM_EV_FORCE_LOCK, (void*)(intptr_t)locked); } bool trx_is_usable(const struct gsm_bts_trx *trx) @@ -191,54 +287,6 @@ void gsm_trx_all_ts_dispatch(struct gsm_bts_trx *trx, uint32_t ts_ev, void *data } } -int trx_count_free_ts(struct gsm_bts_trx *trx, enum gsm_phys_chan_config pchan) -{ - struct gsm_bts_trx_ts *ts; - struct gsm_lchan *lchan; - int j; - int count = 0; - - if (!trx_is_usable(trx)) - return 0; - - for (j = 0; j < ARRAY_SIZE(trx->ts); j++) { - ts = &trx->ts[j]; - if (!ts_is_usable(ts)) - continue; - - if (ts->pchan_is == GSM_PCHAN_PDCH) { - /* Dynamic timeslots in PDCH mode will become TCH if needed. */ - switch (ts->pchan_on_init) { - case GSM_PCHAN_TCH_F_PDCH: - if (pchan == GSM_PCHAN_TCH_F) - count++; - continue; - - case GSM_PCHAN_TCH_F_TCH_H_PDCH: - if (pchan == GSM_PCHAN_TCH_F) - count++; - else if (pchan == GSM_PCHAN_TCH_H) - count += 2; - continue; - - default: - /* Not dynamic, not applicable. */ - continue; - } - } - - if (ts->pchan_is != pchan) - continue; - - ts_for_each_lchan(lchan, ts) { - if (lchan_state_is(lchan, LCHAN_ST_UNUSED)) - count++; - } - } - - return count; -} - bool trx_has_valid_pchan_config(const struct gsm_bts_trx *trx) { bool combined = false; @@ -278,6 +326,23 @@ bool trx_has_valid_pchan_config(const struct gsm_bts_trx *trx) } break; + case GSM_PCHAN_PDCH: + if (is_ericsson_bts(trx->bts)) { + /* NOTE: A static PDCH is usually handled by the BTS/PCU internally, the BSC + * will not actively manage this channel. It will just keep the timeslot + * unused so that it is free for the BTS/PCU to use it as PDCH. Not all BTSs + * work well in this scheme. Ericsson RBS BTSs support dynamic channels natively + * and require a channel activation on RSL level before the PDCH can be used. + * One could work around this by activating the PDCH once on startup and + * leave it on indefinetly but we decided not to do so. Users of Ericsson RBS + * BTSs must configure a dynamic PDCH channel. */ + LOGP(DNM, LOGL_ERROR, "%s is not allowed, because Ericsson RBS does" + "not support static PDCH (use TCH/F_TCH/H_SDCCH8_PDCH)\n", + gsm_pchan_name(ts->pchan_from_config)); + result = false; + } + break; + default: /* CCCH on TS0 is mandatory for C0 */ if (trx->bts->c0 == trx && i == 0) { @@ -285,6 +350,20 @@ bool trx_has_valid_pchan_config(const struct gsm_bts_trx *trx) result = false; } } + + if (trx->bts->features_known) { + const struct bitvec *ft = &trx->bts->features; + + if (ts->hopping.enabled && !osmo_bts_has_feature(ft, BTS_FEAT_HOPPING)) { + LOGP(DNM, LOGL_ERROR, "TS%d has freq. hopping enabled, but BTS does not support it\n", i); + result = false; + } + + if (ts->tsc != -1 && !osmo_bts_has_feature(ft, BTS_FEAT_MULTI_TSC)) { + LOGP(DNM, LOGL_ERROR, "TS%d has TSC != BCC, but BTS does not support it\n", i); + result = false; + } + } } return result; @@ -329,7 +408,7 @@ static int rsl_si(struct gsm_bts_trx *trx, enum osmo_sysinfo_type i, int si_len) /* set all system information types for a TRX */ int gsm_bts_trx_set_system_infos(struct gsm_bts_trx *trx) { - int i, rc; + int rc; struct gsm_bts *bts = trx->bts; uint8_t gen_si[_MAX_SYSINFO_TYPE], n_si = 0, n; int si_len[_MAX_SYSINFO_TYPE]; @@ -367,42 +446,52 @@ int gsm_bts_trx_set_system_infos(struct gsm_bts_trx *trx) /* Second, we generate the selected SI via RSL */ for (n = 0; n < n_si; n++) { - i = gen_si[n]; + const enum osmo_sysinfo_type si_type = gen_si[n]; + /* Only generate SI if this SI is not in "static" (user-defined) mode */ - if (!(bts->si_mode_static & (1 << i))) { + if (!(bts->si_mode_static & (1 << si_type))) { /* Set SI as being valid. gsm_generate_si() might unset * it, if SI is not required. */ - bts->si_valid |= (1 << i); - rc = gsm_generate_si(bts, i); + bts->si_valid |= (1 << si_type); + rc = gsm_generate_si(bts, si_type); if (rc < 0) goto err_out; - si_len[i] = rc; + si_len[si_type] = rc; } else { - if (i == SYSINFO_TYPE_5 || i == SYSINFO_TYPE_5bis - || i == SYSINFO_TYPE_5ter) - si_len[i] = 18; - else if (i == SYSINFO_TYPE_6) - si_len[i] = 11; - else - si_len[i] = 23; + switch (si_type) { + case SYSINFO_TYPE_5: + case SYSINFO_TYPE_5bis: + case SYSINFO_TYPE_5ter: + si_len[si_type] = 18; + break; + case SYSINFO_TYPE_6: + si_len[si_type] = 11; + break; + case SYSINFO_TYPE_10: + si_len[si_type] = 21; + break; + default: + si_len[si_type] = GSM_MACBLOCK_LEN; + } } } /* Third, we send the selected SI via RSL */ for (n = 0; n < n_si; n++) { - i = gen_si[n]; + const enum osmo_sysinfo_type si_type = gen_si[n]; + /* 3GPP TS 08.58 ยง8.5.1 BCCH INFORMATION. If we don't currently * have this SI, we send a zero-length RSL BCCH FILLING / * SACCH FILLING in order to deactivate the SI, in case it * might have previously been active */ - if (!GSM_BTS_HAS_SI(bts, i)) { + if (!GSM_BTS_HAS_SI(bts, si_type)) { if (bts->si_unused_send_empty) - rc = rsl_si(trx, i, 0); + rc = rsl_si(trx, si_type, 0); else rc = 0; /* some nanoBTS fw don't like receiving empty unsupported SI */ } else - rc = rsl_si(trx, i, si_len[i]); + rc = rsl_si(trx, si_type, si_len[si_type]); if (rc < 0) return rc; } @@ -415,6 +504,6 @@ int gsm_bts_trx_set_system_infos(struct gsm_bts_trx *trx) err_out: LOGP(DRR, LOGL_ERROR, "Cannot generate SI%s for BTS %u: error <%s>, " "most likely a problem with neighbor cell list generation\n", - get_value_string(osmo_sitype_strs, i), bts->nr, strerror(-rc)); + get_value_string(osmo_sitype_strs, gen_si[n]), bts->nr, strerror(-rc)); return rc; } |