diff options
Diffstat (limited to 'src/osmo-bsc/bts.c')
-rw-r--r-- | src/osmo-bsc/bts.c | 1361 |
1 files changed, 1190 insertions, 171 deletions
diff --git a/src/osmo-bsc/bts.c b/src/osmo-bsc/bts.c index ce1b20068..108c35539 100644 --- a/src/osmo-bsc/bts.c +++ b/src/osmo-bsc/bts.c @@ -1,5 +1,5 @@ /* (C) 2008-2018 by Harald Welte <laforge@gnumonks.org> - * (C) 2020 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> + * (C) 2021 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> * * All Rights Reserved * @@ -23,6 +23,9 @@ #include <osmocom/bsc/gsm_data.h> #include <osmocom/bsc/bts.h> #include <osmocom/bsc/debug.h> +#include <osmocom/bsc/nm_common_fsm.h> +#include <osmocom/bsc/paging.h> +#include <osmocom/bsc/smscb.h> const struct value_string bts_attribute_names[] = { OSMO_VALUE_STRING(BTS_TYPE_VARIANT), @@ -66,7 +69,7 @@ const struct value_string bts_type_names[_NUM_GSM_BTS_TYPE + 1] = { { GSM_BTS_TYPE_NANOBTS, "nanobts" }, { GSM_BTS_TYPE_RBS2000, "rbs2000" }, { GSM_BTS_TYPE_NOKIA_SITE, "nokia_site" }, - { GSM_BTS_TYPE_OSMOBTS, "sysmobts" }, + { GSM_BTS_TYPE_OSMOBTS, "osmo-bts" }, { 0, NULL } }; @@ -76,7 +79,7 @@ const struct value_string bts_type_descs[_NUM_GSM_BTS_TYPE+1] = { { GSM_BTS_TYPE_NANOBTS, "ip.access nanoBTS or compatible" }, { GSM_BTS_TYPE_RBS2000, "Ericsson RBS2000 Series" }, { GSM_BTS_TYPE_NOKIA_SITE, "Nokia {Metro,Ultra,In}Site" }, - { GSM_BTS_TYPE_OSMOBTS, "sysmocom sysmoBTS" }, + { GSM_BTS_TYPE_OSMOBTS, "Osmocom Base Transceiver Station" }, { 0, NULL } }; @@ -90,12 +93,6 @@ const char *btstype2str(enum gsm_bts_type type) return get_value_string(bts_type_names, type); } -static void bts_init_cbch_state(struct bts_smscb_chan_state *cstate, struct gsm_bts *bts) -{ - cstate->bts = bts; - INIT_LLIST_HEAD(&cstate->messages); -} - static LLIST_HEAD(bts_models); struct gsm_bts_model *bts_model_find(enum gsm_bts_type type) @@ -120,7 +117,6 @@ int gsm_bts_model_register(struct gsm_bts_model *model) return 0; } -static const uint8_t bts_nse_timer_default[] = { 3, 3, 3, 3, 30, 3, 10 }; static const uint8_t bts_cell_timer_default[] = { 3, 3, 3, 3, 3, 10, 3, 10, 3, 10, 3 }; static const struct gprs_rlc_cfg rlc_cfg_default = { @@ -146,19 +142,43 @@ static const struct gprs_rlc_cfg rlc_cfg_default = { .initial_mcs = 6, }; +static int gsm_bts_talloc_destructor(struct gsm_bts *bts) +{ + paging_destructor(bts); + bts_setup_ramp_remove(bts); + + osmo_timer_del(&bts->cbch_timer); + + bts->site_mgr->bts[0] = NULL; + + if (bts->gprs.cell.mo.fi) { + osmo_fsm_inst_free(bts->gprs.cell.mo.fi); + bts->gprs.cell.mo.fi = NULL; + } + + if (bts->mo.fi) { + osmo_fsm_inst_free(bts->mo.fi); + bts->mo.fi = NULL; + } + + osmo_stat_item_group_free(bts->bts_statg); + rate_ctr_group_free(bts->bts_ctrs); + return 0; +} + /* Initialize those parts that don't require osmo-bsc specific dependencies. * This part is shared among the thin programs in osmo-bsc/src/utils/. * osmo-bsc requires further initialization that pulls in more dependencies (see * bsc_bts_alloc_register()). */ -struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, uint8_t bts_num) +struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, struct gsm_bts_sm *bts_sm, uint8_t bts_num) { - struct gsm_bts *bts = talloc_zero(net, struct gsm_bts); - struct gsm48_multi_rate_conf mr_cfg; - int i; + struct gsm_bts *bts = talloc_zero(bts_sm, struct gsm_bts); if (!bts) return NULL; + talloc_set_destructor(bts, gsm_bts_talloc_destructor); + bts->nr = bts_num; bts->num_trx = 0; INIT_LLIST_HEAD(&bts->trx_list); @@ -166,30 +186,24 @@ struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, uint8_t bts_num) bts->ms_max_power = 15; /* dBm */ - gsm_mo_init(&bts->mo, bts, NM_OC_BTS, - bts->nr, 0xff, 0xff); - gsm_mo_init(&bts->site_mgr.mo, bts, NM_OC_SITE_MANAGER, - 0xff, 0xff, 0xff); - - for (i = 0; i < ARRAY_SIZE(bts->gprs.nsvc); i++) { - bts->gprs.nsvc[i].bts = bts; - bts->gprs.nsvc[i].id = i; - gsm_mo_init(&bts->gprs.nsvc[i].mo, bts, NM_OC_GPRS_NSVC, - bts->nr, i, 0xff); - } - memcpy(&bts->gprs.nse.timer, bts_nse_timer_default, - sizeof(bts->gprs.nse.timer)); - gsm_mo_init(&bts->gprs.nse.mo, bts, NM_OC_GPRS_NSE, - bts->nr, 0xff, 0xff); + bts->site_mgr = bts_sm; + + bts->mo.fi = osmo_fsm_inst_alloc(&nm_bts_fsm, bts, bts, + LOGL_INFO, NULL); + osmo_fsm_inst_update_id_f(bts->mo.fi, "bts%d", bts->nr); + gsm_mo_init(&bts->mo, bts, NM_OC_BTS, bts->nr, 0xff, 0xff); + + /* 3GPP TS 08.18, chapter 5.4.1: 0 is reserved for signalling */ + bts->gprs.cell.bvci = 2; memcpy(&bts->gprs.cell.timer, bts_cell_timer_default, sizeof(bts->gprs.cell.timer)); - gsm_mo_init(&bts->gprs.cell.mo, bts, NM_OC_GPRS_CELL, - bts->nr, 0xff, 0xff); memcpy(&bts->gprs.cell.rlc_cfg, &rlc_cfg_default, sizeof(bts->gprs.cell.rlc_cfg)); - - /* 3GPP TS 08.18, chapter 5.4.1: 0 is reserved for signalling */ - bts->gprs.cell.bvci = 2; + bts->gprs.cell.mo.fi = osmo_fsm_inst_alloc(&nm_gprs_cell_fsm, bts, + &bts->gprs.cell, LOGL_INFO, NULL); + osmo_fsm_inst_update_id_f(bts->gprs.cell.mo.fi, "gprs-cell%d", bts->nr); + gsm_mo_init(&bts->gprs.cell.mo, bts, NM_OC_GPRS_CELL, + bts->nr, 0xff, 0xff); /* init statistics */ bts->bts_ctrs = rate_ctr_group_alloc(bts, &bts_ctrg_desc, bts->nr); @@ -199,6 +213,51 @@ struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, uint8_t bts_num) } bts->bts_statg = osmo_stat_item_group_alloc(bts, &bts_statg_desc, bts->nr); + bts->all_allocated.sdcch = (struct osmo_time_cc){ + .cfg = { + .gran_usec = 1*1000000, + .forget_sum_usec = 60*1000000, + .rate_ctr = rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_ALL_ALLOCATED_SDCCH), + .T_gran = -16, + .T_round_threshold = -17, + .T_forget_sum = -18, + .T_defs = net->T_defs, + }, + }; + bts->all_allocated.static_sdcch = (struct osmo_time_cc){ + .cfg = { + .gran_usec = 1*1000000, + .forget_sum_usec = 60*1000000, + .rate_ctr = rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_ALL_ALLOCATED_STATIC_SDCCH), + .T_gran = -16, + .T_round_threshold = -17, + .T_forget_sum = -18, + .T_defs = net->T_defs, + }, + }; + bts->all_allocated.tch = (struct osmo_time_cc){ + .cfg = { + .gran_usec = 1*1000000, + .forget_sum_usec = 60*1000000, + .rate_ctr = rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_ALL_ALLOCATED_TCH), + .T_gran = -16, + .T_round_threshold = -17, + .T_forget_sum = -18, + .T_defs = net->T_defs, + }, + }; + bts->all_allocated.static_tch = (struct osmo_time_cc){ + .cfg = { + .gran_usec = 1*1000000, + .forget_sum_usec = 60*1000000, + .rate_ctr = rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_ALL_ALLOCATED_STATIC_TCH), + .T_gran = -16, + .T_round_threshold = -17, + .T_forget_sum = -18, + .T_defs = net->T_defs, + }, + }; + /* create our primary TRX */ bts->c0 = gsm_bts_trx_alloc(bts); if (!bts->c0) { @@ -210,11 +269,11 @@ struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, uint8_t bts_num) bts->c0->ts[0].pchan_from_config = GSM_PCHAN_CCCH_SDCCH4; /* TODO: really?? */ bts->ccch_load_ind_thresh = 10; /* 10% of Load: Start sending CCCH LOAD IND */ + bts->ccch_load_ind_period = 1; /* Send CCCH LOAD IND every 1 second */ bts->rach_b_thresh = -1; bts->rach_ldavg_slots = -1; - bts->paging.free_chans_need = -1; - INIT_LLIST_HEAD(&bts->paging.pending_requests); + paging_init(bts); bts->features.data = &bts->_features_data[0]; bts->features.data_len = sizeof(bts->_features_data); @@ -233,6 +292,10 @@ struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, uint8_t bts_num) bts->neigh_list_manual_mode = NL_MODE_AUTOMATIC; bts->early_classmark_allowed_3g = true; /* 3g Early Classmark Sending controlled by bts->early_classmark_allowed param */ bts->si_unused_send_empty = true; + bts->chan_alloc_tch_signalling_policy = BTS_TCH_SIGNALLING_ALWAYS; + bts->chan_alloc_dyn_params.ul_rxlev_thresh = 50; /* >= -60 dBm */ + bts->chan_alloc_dyn_params.ul_rxlev_avg_num = 2; /* at least 2 samples */ + bts->chan_alloc_dyn_params.c0_chan_load_thresh = 60; /* >= 60% */ bts->si_common.cell_sel_par.cell_resel_hyst = 2; /* 4 dB */ bts->si_common.cell_sel_par.rxlev_acc_min = 0; bts->si_common.si2quater_neigh_list.arfcn = bts->si_common.data.earfcn_list; @@ -253,18 +316,25 @@ struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, uint8_t bts_num) bts->si_common.rach_control.tx_integer = 9; /* 12 slots spread - 217/115 slots delay */ bts->si_common.rach_control.max_trans = 3; /* 7 retransmissions */ bts->si_common.rach_control.t2 = 4; /* no emergency calls */ + bts->si_common.chan_desc.mscr = 1; /* Indicate R99 MSC in SI3 */ bts->si_common.chan_desc.att = 1; /* attachment required */ bts->si_common.chan_desc.bs_pa_mfrms = RSL_BS_PA_MFRMS_5; /* paging frames */ bts->si_common.chan_desc.bs_ag_blks_res = 1; /* reserved AGCH blocks */ bts->si_common.chan_desc.t3212 = osmo_tdef_get(net->T_defs, 3212, OSMO_TDEF_CUSTOM, -1); + bts->si_common.cell_options.pwrc = 0; /* PWRC not set */ + bts->si_common.cell_sel_par.acs = 0; + bts->si_common.ncc_permitted = 0xff; gsm_bts_set_radio_link_timeout(bts, 32); /* Use RADIO LINK TIMEOUT of 32 */ INIT_LLIST_HEAD(&bts->abis_queue); INIT_LLIST_HEAD(&bts->loc_list); - INIT_LLIST_HEAD(&bts->local_neighbors); + INIT_LLIST_HEAD(&bts->neighbors); INIT_LLIST_HEAD(&bts->oml_fail_rep); INIT_LLIST_HEAD(&bts->chan_rqd_queue); + /* Don't pin the BTS to any MGW by default: */ + bts->mgw_pool_target = -1; + /* Enable all codecs by default. These get reset to a more fine grained selection IF a * 'codec-support' config appears in the config file (see bsc_vty.c). */ bts->codec = (struct bts_codec_conf){ @@ -274,70 +344,200 @@ struct gsm_bts *gsm_bts_alloc(struct gsm_network *net, uint8_t bts_num) }; /* Set reasonable defaults for AMR-FR and AMR-HR rate configuration. - * (see also 3GPP TS 28.062, Table 7.11.3.1.3-2) */ - mr_cfg = (struct gsm48_multi_rate_conf) { + * The values are taken from 3GPP TS 51.010-1 (version 13.11.0). + * See 14.2.19.4.1 and 14.2.20.4.1 for AMR-FR and AMR-HR, respectively. */ + static const struct gsm48_multi_rate_conf amr_fr_mr_cfg = { .m4_75 = 1, - .m5_15 = 0, .m5_90 = 1, - .m6_70 = 0, - .m7_40 = 1, - .m7_95 = 0, - .m10_2 = 0, + .m7_95 = 1, .m12_2 = 1 }; - memcpy(bts->mr_full.gsm48_ie, &mr_cfg, sizeof(bts->mr_full.gsm48_ie)); - bts->mr_full.ms_mode[0].mode = 0; - bts->mr_full.ms_mode[1].mode = 2; - bts->mr_full.ms_mode[2].mode = 4; - bts->mr_full.ms_mode[3].mode = 7; - bts->mr_full.bts_mode[0].mode = 0; - bts->mr_full.bts_mode[1].mode = 2; - bts->mr_full.bts_mode[2].mode = 4; - bts->mr_full.bts_mode[3].mode = 7; - for (i = 0; i < 3; i++) { - bts->mr_full.ms_mode[i].hysteresis = 8; - bts->mr_full.ms_mode[i].threshold = 32; - bts->mr_full.bts_mode[i].hysteresis = 8; - bts->mr_full.bts_mode[i].threshold = 32; - } - bts->mr_full.num_modes = 4; - - mr_cfg = (struct gsm48_multi_rate_conf) { + static const struct gsm48_multi_rate_conf amr_hr_mr_cfg = { .m4_75 = 1, - .m5_15 = 0, .m5_90 = 1, - .m6_70 = 0, - .m7_40 = 1, - .m7_95 = 0, - .m10_2 = 0, - .m12_2 = 0 + .m6_70 = 1, + .m7_95 = 1, }; - memcpy(bts->mr_half.gsm48_ie, &mr_cfg, sizeof(bts->mr_half.gsm48_ie)); - bts->mr_half.ms_mode[0].mode = 0; - bts->mr_half.ms_mode[1].mode = 2; - bts->mr_half.ms_mode[2].mode = 4; - bts->mr_half.ms_mode[3].mode = 7; - bts->mr_half.bts_mode[0].mode = 0; - bts->mr_half.bts_mode[1].mode = 2; - bts->mr_half.bts_mode[2].mode = 4; - bts->mr_half.bts_mode[3].mode = 7; - for (i = 0; i < 3; i++) { - bts->mr_half.ms_mode[i].hysteresis = 8; - bts->mr_half.ms_mode[i].threshold = 32; - bts->mr_half.bts_mode[i].hysteresis = 8; - bts->mr_half.bts_mode[i].threshold = 32; - } - bts->mr_half.num_modes = 3; - bts_init_cbch_state(&bts->cbch_basic, bts); - bts_init_cbch_state(&bts->cbch_extended, bts); + memcpy(bts->mr_full.gsm48_ie, &amr_fr_mr_cfg, sizeof(bts->mr_full.gsm48_ie)); + memcpy(bts->mr_half.gsm48_ie, &amr_hr_mr_cfg, sizeof(bts->mr_half.gsm48_ie)); + + /* ^ C/I (dB) | FR / HR | + * | | + * | | + * MODE4 | | + * = | ----+---- THR_MX_Up(3) | 20.5 / 18.0 | + * | | | + * | = ----+---- THR_MX_Dn(4) | 18.5 / 16.0 | + * MODE3 | | + * | = ----+---- THR_MX_Up(2) | 14.5 / 14.0 | + * | | | + * = | ----+---- THR_MX_Dn(3) | 12.5 / 12.0 | + * MODE2 | | + * = | ----+---- THR_MX_Up(1) | 8.5 / 10.0 | + * | | | + * | = ----+---- THR_MX_Dn(2) | 6.5 / 8.0 | + * MODE1 | | + * | | + * | | + */ + static const struct amr_mode amr_fr_ms_bts_mode[] = { + { + .mode = 0, /* 4.75k */ + .threshold = 13, /* THR_MX_Dn(2): 6.5 dB */ + .hysteresis = 4, /* THR_MX_Up(1): 8.5 dB */ + }, + { + .mode = 2, /* 5.90k */ + .threshold = 25, /* THR_MX_Dn(3): 12.5 dB */ + .hysteresis = 4, /* THR_MX_Up(2): 14.5 dB */ + }, + { + .mode = 5, /* 7.95k */ + .threshold = 37, /* THR_MX_Dn(4): 18.5 dB */ + .hysteresis = 4, /* THR_MX_Up(3): 20.5 dB */ + }, + { + .mode = 7, /* 12.2k */ + /* this is the last mode, so no threshold */ + }, + }; + static const struct amr_mode amr_hr_ms_bts_mode[] = { + { + .mode = 0, /* 4.75k */ + .threshold = 16, /* THR_MX_Dn(2): 8.0 dB */ + .hysteresis = 4, /* THR_MX_Up(1): 10.0 dB */ + }, + { + .mode = 2, /* 5.90k */ + .threshold = 24, /* THR_MX_Dn(3): 12.0 dB */ + .hysteresis = 4, /* THR_MX_Up(2): 14.0 dB */ + }, + { + .mode = 3, /* 6.70k */ + .threshold = 32, /* THR_MX_Dn(4): 16.0 dB */ + .hysteresis = 4, /* THR_MX_Up(3): 18.0 dB */ + }, + { + .mode = 5, /* 7.95k */ + /* this is the last mode, so no threshold */ + }, + }; + + memcpy(&bts->mr_full.ms_mode[0], &amr_fr_ms_bts_mode[0], sizeof(amr_fr_ms_bts_mode)); + memcpy(&bts->mr_full.bts_mode[0], &amr_fr_ms_bts_mode[0], sizeof(amr_fr_ms_bts_mode)); + bts->mr_full.num_modes = ARRAY_SIZE(amr_fr_ms_bts_mode); + + memcpy(&bts->mr_half.ms_mode[0], &amr_hr_ms_bts_mode[0], sizeof(amr_hr_ms_bts_mode)); + memcpy(&bts->mr_half.bts_mode[0], &amr_hr_ms_bts_mode[0], sizeof(amr_hr_ms_bts_mode)); + bts->mr_half.num_modes = ARRAY_SIZE(amr_hr_ms_bts_mode); + + bts->use_osmux = OSMUX_USAGE_OFF; + bts_cbch_init(bts); + bts_etws_init(bts); + + bts_setup_ramp_init_bts(bts); acc_mgr_init(&bts->acc_mgr, bts); acc_ramp_init(&bts->acc_ramp, bts); + /* Default RxQual threshold for ACCH repetition/overpower */ + bts->rep_acch_cap.rxqual = 4; + bts->top_acch_cap.rxqual = 4; + + /* Permit ACCH overpower only for speech channels using AMR */ + bts->top_acch_chan_mode = TOP_ACCH_CHAN_MODE_SPEECH_V3; + + /* MS Power Control parameters (defaults) */ + power_ctrl_params_def_reset(&bts->ms_power_ctrl, GSM_PWR_CTRL_DIR_UL); + + /* BS Power Control parameters (defaults) */ + power_ctrl_params_def_reset(&bts->bs_power_ctrl, GSM_PWR_CTRL_DIR_DL); + + /* Interference Measurement Parameters (defaults) */ + bts->interf_meas_params_cfg = interf_meas_params_def; + + bts->rach_max_delay = 63; + + /* SRVCC is enabled by default */ + bts->srvcc_fast_return_allowed = true; + return bts; } +/* Validate BTS configuration (ARFCN settings and physical channel configuration) */ +__attribute__((weak)) int gsm_bts_check_cfg(struct gsm_bts *bts) +{ + struct gsm_bts_trx *trx; + + if (!bts->model) + return -EFAULT; + + switch (bts->band) { + case GSM_BAND_1800: + if (bts->c0->arfcn < 512 || bts->c0->arfcn > 885) { + LOGP(DNM, LOGL_ERROR, "(bts=%u) GSM1800 channel (%u) must be between 512-885.\n", + bts->nr, bts->c0->arfcn); + return -EINVAL; + } + break; + case GSM_BAND_1900: + if (bts->c0->arfcn < 512 || bts->c0->arfcn > 810) { + LOGP(DNM, LOGL_ERROR, "(bts=%u) GSM1900 channel (%u) must be between 512-810.\n", + bts->nr, bts->c0->arfcn); + return -EINVAL; + } + break; + case GSM_BAND_900: + if ((bts->c0->arfcn > 124 && bts->c0->arfcn < 955) || + bts->c0->arfcn > 1023) { + LOGP(DNM, LOGL_ERROR, "(bts=%u) GSM900 channel (%u) must be between 0-124, 955-1023.\n", + bts->nr, bts->c0->arfcn); + return -EINVAL; + } + break; + case GSM_BAND_850: + if (bts->c0->arfcn < 128 || bts->c0->arfcn > 251) { + LOGP(DNM, LOGL_ERROR, "(bts=%u) GSM850 channel (%u) must be between 128-251.\n", + bts->nr, bts->c0->arfcn); + return -EINVAL; + } + break; + default: + LOGP(DNM, LOGL_ERROR, "(bts=%u) Unsupported frequency band.\n", bts->nr); + return -EINVAL; + } + + if (bts->features_known) { + if (!bts_gprs_mode_is_compat(bts, bts->gprs.mode)) { + LOGP(DNM, LOGL_ERROR, "(bts=%u) GPRS mode set to '%s', but BTS does not support it\n", bts->nr, + bts_gprs_mode_name(bts->gprs.mode)); + return -EINVAL; + } + if (bts->use_osmux == OSMUX_USAGE_ONLY && + !osmo_bts_has_feature(&bts->features, BTS_FEAT_OSMUX)) { + LOGP(DNM, LOGL_ERROR, + "(bts=%u) osmux use set to 'only', but BTS does not support Osmux\n", + bts->nr); + return -EINVAL; + } + } + + /* Verify the physical channel mapping */ + llist_for_each_entry(trx, &bts->trx_list, list) { + if (!trx_has_valid_pchan_config(trx)) { + LOGP(DNM, LOGL_ERROR, "TRX %u has invalid timeslot " + "configuration\n", trx->nr); + return -EINVAL; + } + } + + if (!gsm_bts_check_ny1(bts)) + return -EINVAL; + + return 0; +} + static char ts2str[255]; char *gsm_bts_name(const struct gsm_bts *bts) @@ -366,6 +566,10 @@ bool gsm_bts_matches_cell_id(const struct gsm_bts *bts, const struct gsm0808_cel case CELL_IDENT_WHOLE_GLOBAL: return gsm_bts_matches_lai(bts, &id->global.lai) && id->global.cell_identity == bts->cell_identity; + case CELL_IDENT_WHOLE_GLOBAL_PS: + return gsm_bts_matches_lai(bts, &id->global_ps.rai.lac) + && id->global_ps.rai.rac == bts->gprs.rac + && id->global_ps.cell_identity == bts->cell_identity; case CELL_IDENT_LAC_AND_CI: return id->lac_and_ci.lac == bts->location_area_code && id->lac_and_ci.ci == bts->cell_identity; @@ -382,96 +586,93 @@ bool gsm_bts_matches_cell_id(const struct gsm_bts *bts, const struct gsm0808_cel case CELL_IDENT_UTRAN_PLMN_LAC_RNC: case CELL_IDENT_UTRAN_RNC: case CELL_IDENT_UTRAN_LAC_RNC: + case CELL_IDENT_SAI: return false; default: OSMO_ASSERT(false); } } -static struct gsm_bts_ref *gsm_bts_ref_find(const struct llist_head *list, const struct gsm_bts *bts) -{ - struct gsm_bts_ref *ref; - if (!bts) - return NULL; - llist_for_each_entry(ref, list, entry) { - if (ref->bts == bts) - return ref; - } - return NULL; -} - -/* Add a BTS reference to the local_neighbors list. - * Return 1 if added, 0 if such an entry already existed, and negative on errors. */ -int gsm_bts_local_neighbor_add(struct gsm_bts *bts, struct gsm_bts *neighbor) +/* Return a LAC+CI cell identity for the given BTS. + * (For matching a BTS within the local BSS, the PLMN code is not important.) */ +void gsm_bts_cell_id(struct gsm0808_cell_id *cell_id, const struct gsm_bts *bts) { - struct gsm_bts_ref *ref; - if (!bts || !neighbor) - return -ENOMEM; - - if (bts == neighbor) - return -EINVAL; - - /* Already got this entry? */ - ref = gsm_bts_ref_find(&bts->local_neighbors, neighbor); - if (ref) - return 0; - - ref = talloc_zero(bts, struct gsm_bts_ref); - if (!ref) - return -ENOMEM; - ref->bts = neighbor; - llist_add_tail(&ref->entry, &bts->local_neighbors); - return 1; + *cell_id = (struct gsm0808_cell_id){ + .id_discr = CELL_IDENT_LAC_AND_CI, + .id.lac_and_ci = { + .lac = bts->location_area_code, + .ci = bts->cell_identity, + }, + }; } -/* Remove a BTS reference from the local_neighbors list. - * Return 1 if removed, 0 if no such entry existed, and negative on errors. */ -int gsm_bts_local_neighbor_del(struct gsm_bts *bts, const struct gsm_bts *neighbor) +/* Same as gsm_bts_cell_id(), but return in a single-entry gsm0808_cell_id_list2. Useful for e.g. + * gsm0808_cell_id_list_add() and gsm0808_cell_id_lists_same(). */ +void gsm_bts_cell_id_list(struct gsm0808_cell_id_list2 *cell_id_list, const struct gsm_bts *bts) { - struct gsm_bts_ref *ref; - if (!bts || !neighbor) - return -ENOMEM; - - ref = gsm_bts_ref_find(&bts->local_neighbors, neighbor); - if (!ref) - return 0; - - llist_del(&ref->entry); - talloc_free(ref); - return 1; + struct gsm0808_cell_id cell_id; + struct gsm0808_cell_id_list2 add; + int rc; + gsm_bts_cell_id(&cell_id, bts); + gsm0808_cell_id_to_list(&add, &cell_id); + /* Since the target list is empty, this should always succeed. */ + (*cell_id_list) = (struct gsm0808_cell_id_list2){}; + rc = gsm0808_cell_id_list_add(cell_id_list, &add); + OSMO_ASSERT(rc > 0); } /* return the gsm_lchan for the CBCH (if it exists at all) */ struct gsm_lchan *gsm_bts_get_cbch(struct gsm_bts *bts) { - struct gsm_lchan *lchan = NULL; struct gsm_bts_trx *trx = bts->c0; + /* According to 3GPP TS 45.002, table 3, CBCH can be allocated + * either on C0/TS0 (CCCH+SDCCH4) or on C0..n/TS0..3 (SDCCH/8). */ if (trx->ts[0].pchan_from_config == GSM_PCHAN_CCCH_SDCCH4_CBCH) - lchan = &trx->ts[0].lchan[2]; - else { - int i; - for (i = 0; i < 8; i++) { - if (trx->ts[i].pchan_from_config == GSM_PCHAN_SDCCH8_SACCH8C_CBCH) { - lchan = &trx->ts[i].lchan[2]; - break; - } + return &trx->ts[0].lchan[2]; /* C0/TS0 */ + + llist_for_each_entry(trx, &bts->trx_list, list) { /* C0..n */ + unsigned int tn; + for (tn = 0; tn < 4; tn++) { /* TS0..3 */ + struct gsm_bts_trx_ts *ts = &trx->ts[tn]; + if (ts->pchan_from_config == GSM_PCHAN_SDCCH8_SACCH8C_CBCH) + return &ts->lchan[2]; } } - return lchan; + return NULL; +} + +int gsm_set_bts_model(struct gsm_bts *bts, struct gsm_bts_model *model) +{ + bts->model = model; + + /* Copy hardcoded feature list from BTS model, unless the BTS supports + * reporting features at runtime (as of writing nanobts, OsmoBTS). */ + if (!model || model->features_get_reported) { + memset(bts->_features_data, 0, sizeof(bts->_features_data)); + bts->features_known = false; + } else { + memcpy(bts->_features_data, bts->model->_features_data, sizeof(bts->_features_data)); + bts->features_known = true; + } + + return 0; } int gsm_set_bts_type(struct gsm_bts *bts, enum gsm_bts_type type) { struct gsm_bts_model *model; + if (bts->type != GSM_BTS_TYPE_UNKNOWN && type != bts->type) + return -EBUSY; + model = bts_model_find(type); if (!model) return -EINVAL; bts->type = type; - bts->model = model; + gsm_set_bts_model(bts, model); if (model->start && !model->started) { int ret = model->start(bts->network); @@ -481,9 +682,22 @@ int gsm_set_bts_type(struct gsm_bts *bts, enum gsm_bts_type type) model->started = true; } + if (model->bts_init) { + int rc = model->bts_init(bts); + if (rc < 0) + return rc; + } + + /* handle those TRX which are already allocated at the time we set the type */ + if (model->trx_init) { + struct gsm_bts_trx *trx; + llist_for_each_entry(trx, &bts->trx_list, list) + model->trx_init(trx); + } + switch (bts->type) { - case GSM_BTS_TYPE_NANOBTS: case GSM_BTS_TYPE_OSMOBTS: + case GSM_BTS_TYPE_NANOBTS: /* Set the default OML Stream ID to 0xff */ bts->oml_tei = 0xff; bts->c0->nominal_power = 23; @@ -501,17 +715,21 @@ int gsm_set_bts_type(struct gsm_bts *bts, enum gsm_bts_type type) break; } + /* Enable dynamic Uplink power control by default (if supported) */ + if (model->power_ctrl_enc_rsl_params != NULL) + bts->ms_power_ctrl.mode = GSM_PWR_CTRL_MODE_DYN_BTS; + return 0; } int bts_gprs_mode_is_compat(struct gsm_bts *bts, enum bts_gprs_mode mode) { if (mode != BTS_GPRS_NONE && - !osmo_bts_has_feature(&bts->model->features, BTS_FEAT_GPRS)) { + !osmo_bts_has_feature(&bts->features, BTS_FEAT_GPRS)) { return 0; } if (mode == BTS_GPRS_EGPRS && - !osmo_bts_has_feature(&bts->model->features, BTS_FEAT_EGPRS)) { + !osmo_bts_has_feature(&bts->features, BTS_FEAT_EGPRS)) { return 0; } @@ -533,16 +751,74 @@ struct gsm_bts_trx *gsm_bts_trx_num(const struct gsm_bts *bts, int num) return NULL; } +void bts_store_uptime(struct gsm_bts *bts) +{ + osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_UPTIME_SECONDS), bts_uptime(bts)); +} + +void bts_store_lchan_durations(struct gsm_bts *bts) +{ + struct gsm_bts_trx *trx; + int i, j; + struct timespec now, elapsed; + uint64_t elapsed_ms; + uint64_t elapsed_tch_ms = 0; + uint64_t elapsed_sdcch_ms = 0; + + /* Ignore BTS that are not in operation. */ + if (!trx_is_usable(bts->c0)) + return; + + /* Grab storage time to be used for all lchans. */ + osmo_clock_gettime(CLOCK_MONOTONIC, &now); + + /* Iterate over all lchans. */ + llist_for_each_entry(trx, &bts->trx_list, list) { + for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { + struct gsm_bts_trx_ts *ts = &trx->ts[i]; + for (j = 0; j < ARRAY_SIZE(ts->lchan); j++) { + struct gsm_lchan *lchan = &ts->lchan[j]; + + /* Ignore lchans whose activation timestamps are not yet set. */ + if (lchan->active_stored.tv_sec == 0 && lchan->active_stored.tv_nsec == 0) + continue; + + /* Calculate elapsed time since last storage. */ + timespecsub(&now, &lchan->active_stored, &elapsed); + elapsed_ms = elapsed.tv_sec * 1000 + elapsed.tv_nsec / 1000000; + + /* Assign elapsed time to appropriate bucket. */ + switch (lchan->type) { + case GSM_LCHAN_TCH_H: + case GSM_LCHAN_TCH_F: + elapsed_tch_ms += elapsed_ms; + break; + case GSM_LCHAN_SDCCH: + elapsed_sdcch_ms += elapsed_ms; + break; + default: + continue; + } + + /* Update storage time. */ + lchan->active_stored = now; + } + } + } + + /* Export to rate counters. */ + rate_ctr_add(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CHAN_TCH_ACTIVE_MILLISECONDS_TOTAL), elapsed_tch_ms); + rate_ctr_add(rate_ctr_group_get_ctr(bts->bts_ctrs, BTS_CTR_CHAN_SDCCH_ACTIVE_MILLISECONDS_TOTAL), elapsed_sdcch_ms); +} + unsigned long long bts_uptime(const struct gsm_bts *bts) { struct timespec tp; - if (!bts->uptime || !bts->oml_link) { - LOGP(DNM, LOGL_ERROR, "BTS %u OML link uptime unavailable\n", bts->nr); + if (!bts->uptime || !bts->oml_link) return 0; - } - if (clock_gettime(CLOCK_MONOTONIC, &tp) != 0) { + if (osmo_clock_gettime(CLOCK_MONOTONIC, &tp) != 0) { LOGP(DNM, LOGL_ERROR, "BTS %u uptime computation failure: %s\n", bts->nr, strerror(errno)); return 0; } @@ -566,10 +842,6 @@ void gsm_bts_mo_reset(struct gsm_bts *bts) unsigned int i; gsm_abis_mo_reset(&bts->mo); - gsm_abis_mo_reset(&bts->site_mgr.mo); - for (i = 0; i < ARRAY_SIZE(bts->gprs.nsvc); i++) - gsm_abis_mo_reset(&bts->gprs.nsvc[i].mo); - gsm_abis_mo_reset(&bts->gprs.nse.mo); gsm_abis_mo_reset(&bts->gprs.cell.mo); llist_for_each_entry(trx, &bts->trx_list, list) { @@ -596,7 +868,7 @@ void bts_depend_mark(struct gsm_bts *bts, int dep) int idx, bit; depends_calc_index_bit(dep, &idx, &bit); - bts->depends_on[idx] |= 1 << bit; + bts->depends_on[idx] |= 1U << bit; } void bts_depend_clear(struct gsm_bts *bts, int dep) @@ -604,7 +876,7 @@ void bts_depend_clear(struct gsm_bts *bts, int dep) int idx, bit; depends_calc_index_bit(dep, &idx, &bit); - bts->depends_on[idx] &= ~(1 << bit); + bts->depends_on[idx] &= ~(1U << bit); } int bts_depend_is_depedency(struct gsm_bts *base, struct gsm_bts *other) @@ -613,7 +885,7 @@ int bts_depend_is_depedency(struct gsm_bts *base, struct gsm_bts *other) depends_calc_index_bit(other->nr, &idx, &bit); /* Check if there is a depends bit */ - return (base->depends_on[idx] & (1 << bit)) > 0; + return (base->depends_on[idx] & (1U << bit)) > 0; } static int bts_is_online(struct gsm_bts *bts) @@ -686,19 +958,6 @@ void gsm_bts_all_ts_dispatch(struct gsm_bts *bts, uint32_t ts_ev, void *data) gsm_trx_all_ts_dispatch(trx, ts_ev, data); } - -/* Count number of free TS of given pchan type */ -int bts_count_free_ts(struct gsm_bts *bts, enum gsm_phys_chan_config pchan) -{ - struct gsm_bts_trx *trx; - int count = 0; - - llist_for_each_entry(trx, &bts->trx_list, list) - count += trx_count_free_ts(trx, pchan); - - return count; -} - /* set all system information types for a BTS */ int gsm_bts_set_system_infos(struct gsm_bts *bts) { @@ -718,3 +977,763 @@ int gsm_bts_set_system_infos(struct gsm_bts *bts) return 0; } + +int gsm_bts_set_c0_power_red(struct gsm_bts *bts, const uint8_t red) +{ + struct gsm_bts_trx *c0 = bts->c0; + unsigned int tn; + int rc; + + if (!osmo_bts_has_feature(&bts->features, BTS_FEAT_BCCH_POWER_RED)) + return -ENOTSUP; + if (bts->model->power_ctrl_set_c0_power_red == NULL) + return -ENOTSUP; + + rc = bts->model->power_ctrl_set_c0_power_red(bts, red); + if (rc != 0) + return rc; + + /* Timeslot 0 is always transmitting BCCH/CCCH */ + c0->ts[0].c0_max_power_red_db = 0; + + for (tn = 1; tn < ARRAY_SIZE(c0->ts); tn++) { + struct gsm_bts_trx_ts *ts = &c0->ts[tn]; + struct gsm_bts_trx_ts *prev = ts - 1; + + switch (ts->pchan_is) { + /* Not allowed on CCCH/BCCH */ + case GSM_PCHAN_CCCH: + /* Preceeding timeslot shall not exceed 2 dB */ + if (prev->c0_max_power_red_db > 0) + prev->c0_max_power_red_db = 2; + /* fall-through */ + /* Not recommended on SDCCH/8 */ + case GSM_PCHAN_SDCCH8_SACCH8C: + case GSM_PCHAN_SDCCH8_SACCH8C_CBCH: + ts->c0_max_power_red_db = 0; + break; + default: + ts->c0_max_power_red_db = red; + break; + } + } + + /* Timeslot 7 is always preceding BCCH/CCCH */ + if (c0->ts[7].c0_max_power_red_db > 0) + c0->ts[7].c0_max_power_red_db = 2; + + bts->c0_max_power_red_db = red; + + return 0; +} + +void gsm_bts_stats_reset(struct gsm_bts *bts) +{ + osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_CCCH_SDCCH4_USED), 0); + osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_CCCH_SDCCH4_TOTAL), 0); + osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_TCH_F_USED), 0); + osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_TCH_F_TOTAL), 0); + osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_TCH_H_USED), 0); + osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_TCH_H_TOTAL), 0); + osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_SDCCH8_USED), 0); + osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_SDCCH8_TOTAL), 0); + osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_TCH_F_PDCH_USED), 0); + osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_TCH_F_PDCH_TOTAL), 0); + osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_CCCH_SDCCH4_CBCH_USED), 0); + osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_CCCH_SDCCH4_CBCH_TOTAL), 0); + osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_SDCCH8_CBCH_USED), 0); + osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_SDCCH8_CBCH_TOTAL), 0); + osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_OSMO_DYN_USED), 0); + osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_CHAN_OSMO_DYN_TOTAL), 0); + osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_PAGING_T3113), 0); + osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_PAGING_REQ_QUEUE_LENGTH), paging_pending_requests_nr(bts)); + osmo_stat_item_set(osmo_stat_item_group_get_item(bts->bts_statg, BTS_STAT_PAGING_AVAILABLE_SLOTS), bts->paging.available_slots); +} + +const struct rate_ctr_desc bts_ctr_description[] = { + [BTS_CTR_CHREQ_TOTAL] = \ + { "chreq:total", + "Received channel requests" }, + [BTS_CTR_CHREQ_ATTEMPTED_EMERG] = \ + { "chreq:attempted_emerg", + "Received channel requests EMERG" }, + [BTS_CTR_CHREQ_ATTEMPTED_CALL] = \ + { "chreq:attempted_call", + "Received channel requests CALL" }, + [BTS_CTR_CHREQ_ATTEMPTED_LOCATION_UPD] = \ + { "chreq:attempted_location_upd", + "Received channel requests LOCATION_UPD" }, + [BTS_CTR_CHREQ_ATTEMPTED_PAG] = \ + { "chreq:attempted_pag", + "Received channel requests PAG" }, + [BTS_CTR_CHREQ_ATTEMPTED_PDCH] = \ + { "chreq:attempted_pdch", + "Received channel requests PDCH" }, + [BTS_CTR_CHREQ_ATTEMPTED_OTHER] = \ + { "chreq:attempted_other", + "Received channel requests OTHER" }, + [BTS_CTR_CHREQ_ATTEMPTED_UNKNOWN] = \ + { "chreq:attempted_unknown", + "Received channel requests UNKNOWN" }, + [BTS_CTR_CHREQ_SUCCESSFUL] = \ + { "chreq:successful", + "Successful channel requests (immediate assign sent)" }, + [BTS_CTR_CHREQ_SUCCESSFUL_EMERG] = \ + { "chreq:successful_emerg", + "Sent Immediate Assignment for EMERG" }, + [BTS_CTR_CHREQ_SUCCESSFUL_CALL] = \ + { "chreq:successful_call", + "Sent Immediate Assignment for CALL" }, + [BTS_CTR_CHREQ_SUCCESSFUL_LOCATION_UPD] = \ + { "chreq:successful_location_upd", + "Sent Immediate Assignment for LOCATION_UPD" }, + [BTS_CTR_CHREQ_SUCCESSFUL_PAG] = \ + { "chreq:successful_pag", + "Sent Immediate Assignment for PAG" }, + [BTS_CTR_CHREQ_SUCCESSFUL_PDCH] = \ + { "chreq:successful_pdch", + "Sent Immediate Assignment for PDCH" }, + [BTS_CTR_CHREQ_SUCCESSFUL_OTHER] = \ + { "chreq:successful_other", + "Sent Immediate Assignment for OTHER" }, + [BTS_CTR_CHREQ_SUCCESSFUL_UNKNOWN] = \ + { "chreq:successful_unknown", + "Sent Immediate Assignment for UNKNOWN" }, + [BTS_CTR_CHREQ_NO_CHANNEL] = \ + { "chreq:no_channel", + "Sent to MS no channel available" }, + [BTS_CTR_CHREQ_MAX_DELAY_EXCEEDED] = \ + { "chreq:max_delay_exceeded", + "Received channel requests with greater than permitted access delay" }, + [BTS_CTR_CHAN_RF_FAIL] = \ + { "chan:rf_fail", + "Received a RF failure indication from BTS" }, + [BTS_CTR_CHAN_RF_FAIL_TCH] = \ + { "chan:rf_fail_tch", + "Received a RF failure indication from BTS on a TCH channel" }, + [BTS_CTR_CHAN_RF_FAIL_SDCCH] = \ + { "chan:rf_fail_sdcch", + "Received a RF failure indication from BTS on an SDCCH channel" }, + [BTS_CTR_CHAN_RLL_ERR] = \ + { "chan:rll_err", + "Received a RLL failure with T200 cause from BTS" }, + [BTS_CTR_BTS_OML_FAIL] = \ + { "oml_fail", + "Received a TEI down on a OML link" }, + [BTS_CTR_BTS_RSL_FAIL] = \ + { "rsl_fail", + "Received a TEI down on a RSL link" }, + [BTS_CTR_CODEC_AMR_F] = \ + { "codec:amr_f", + "Count the usage of AMR/F codec by channel mode requested" }, + [BTS_CTR_CODEC_AMR_H] = \ + { "codec:amr_h", + "Count the usage of AMR/H codec by channel mode requested" }, + [BTS_CTR_CODEC_EFR] = \ + { "codec:efr", + "Count the usage of EFR codec by channel mode requested" }, + [BTS_CTR_CODEC_V1_FR] = \ + { "codec:fr", + "Count the usage of FR codec by channel mode requested" }, + [BTS_CTR_CODEC_V1_HR] = \ + { "codec:hr", + "Count the usage of HR codec by channel mode requested" }, + [BTS_CTR_PAGING_ATTEMPTED] = \ + { "paging:attempted", + "Paging attempts for a subscriber" }, + [BTS_CTR_PAGING_ALREADY] = \ + { "paging:already", + "Paging attempts ignored as subscriber was already being paged" }, + [BTS_CTR_PAGING_RESPONDED] = \ + { "paging:responded", + "Paging attempts with successful paging response" }, + [BTS_CTR_PAGING_EXPIRED] = \ + { "paging:expired", + "Paging Request expired because of timeout T3113" }, + [BTS_CTR_PAGING_NO_ACTIVE_PAGING] = \ + { "paging:no_active_paging", + "Paging response without an active paging request (arrived after paging expiration?)" }, + [BTS_CTR_PAGING_MSC_FLUSH] = \ + { "paging:msc_flush", + "Paging flushed due to MSC Reset BSSMAP message" }, + [BTS_CTR_PAGING_OVERLOAD] = \ + { "paging:overload", + "Paging dropped due to BSC Paging queue overload" }, + [BTS_CTR_CHAN_ACT_TOTAL] = \ + { "chan_act:total", + "Total number of Channel Activations" }, + [BTS_CTR_CHAN_ACT_SDCCH] = \ + { "chan_act:sdcch", + "Number of SDCCH Channel Activations" }, + [BTS_CTR_CHAN_ACT_TCH] = \ + { "chan_act:tch", + "Number of TCH Channel Activations" }, + [BTS_CTR_CHAN_ACT_NACK] = \ + { "chan_act:nack", + "Number of Channel Activations that the BTS NACKed" }, + [BTS_CTR_CHAN_TCH_ACTIVE_MILLISECONDS_TOTAL] = \ + { "chan_tch:active_milliseconds:total", + "Cumulative number of milliseconds of TCH channel activity" }, + [BTS_CTR_CHAN_SDCCH_ACTIVE_MILLISECONDS_TOTAL] = \ + { "chan_sdcch:active_milliseconds:total", + "Cumulative number of milliseconds of SDCCH channel activity" }, + [BTS_CTR_CHAN_TCH_FULLY_ESTABLISHED] = \ + { "chan_tch:fully_established", + "Number of TCH channels which have reached the fully established state" }, + [BTS_CTR_CHAN_SDCCH_FULLY_ESTABLISHED] = \ + { "chan_sdcch:fully_established", + "Number of SDCCH channels which have reached the fully established state" }, + [BTS_CTR_RSL_UNKNOWN] = \ + { "rsl:unknown", + "Number of unknown/unsupported RSL messages received from BTS" }, + [BTS_CTR_RSL_IPA_NACK] = \ + { "rsl:ipa_nack", + "Number of IPA (RTP/dyn-PDCH) related NACKs received from BTS" }, + [BTS_CTR_RSL_DELETE_IND] = \ + { "rsl:delete_ind", + "Number of RSL DELETE INDICATION (DL CCCH overload)" }, + [BTS_CTR_MODE_MODIFY_NACK] = \ + { "chan:mode_modify_nack", + "Number of Channel Mode Modify NACKs received from BTS" }, + + /* lchan/TS BORKEN state counters */ + [BTS_CTR_LCHAN_BORKEN_FROM_UNUSED] = \ + { "lchan_borken:from_state:unused", + "Transitions from lchan UNUSED state to BORKEN state" }, + [BTS_CTR_LCHAN_BORKEN_FROM_WAIT_ACTIV_ACK] = \ + { "lchan_borken:from_state:wait_activ_ack", + "Transitions from lchan WAIT_ACTIV_ACK state to BORKEN state" }, + [BTS_CTR_LCHAN_BORKEN_FROM_WAIT_RF_RELEASE_ACK] = \ + { "lchan_borken:from_state:wait_rf_release_ack", + "Transitions from lchan WAIT_RF_RELEASE_ACK state to BORKEN state" }, + [BTS_CTR_LCHAN_BORKEN_FROM_BORKEN] = \ + { "lchan_borken:from_state:borken", + "Transitions from lchan BORKEN state to BORKEN state" }, + [BTS_CTR_LCHAN_BORKEN_FROM_WAIT_RR_CHAN_MODE_MODIFY_ACK] = \ + { "lchan_borken:from_state:wait_rr_chan_mode_modify_ack", + "Transitions from lchan WAIT_RR_CHAN_MODE_MODIFY_ACK state to BORKEN state" }, + [BTS_CTR_LCHAN_BORKEN_FROM_WAIT_RSL_CHAN_MODE_MODIFY_ACK] = \ + { "lchan_borken:from_state:wait_rsl_chan_mode_modify_ack", + "Transitions from lchan RSL_CHAN_MODE_MODIFY_ACK state to BORKEN state" }, + [BTS_CTR_LCHAN_BORKEN_FROM_UNKNOWN] = \ + { "lchan_borken:from_state:unknown", + "Transitions from an unknown lchan state to BORKEN state" }, + [BTS_CTR_LCHAN_BORKEN_EV_CHAN_ACTIV_ACK] = \ + { "lchan_borken:event:chan_activ_ack", + "CHAN_ACTIV_ACK received in the lchan BORKEN state" }, + [BTS_CTR_LCHAN_BORKEN_EV_CHAN_ACTIV_NACK] = \ + { "lchan_borken:event:chan_activ_nack", + "CHAN_ACTIV_NACK received in the lchan BORKEN state" }, + [BTS_CTR_LCHAN_BORKEN_EV_RF_CHAN_REL_ACK] = \ + { "lchan_borken:event:rf_chan_rel_ack", + "RF_CHAN_REL_ACK received in the lchan BORKEN state" }, + [BTS_CTR_LCHAN_BORKEN_EV_VTY] = \ + { "lchan_borken:event:vty", + "VTY commands received in the lchan BORKEN state" }, + [BTS_CTR_LCHAN_BORKEN_EV_TEARDOWN] = \ + { "lchan_borken:event:teardown", + "lchan in a BORKEN state is shutting down (BTS disconnected?)" }, + [BTS_CTR_LCHAN_BORKEN_EV_TS_ERROR] = \ + { "lchan_borken:event:ts_error", + "LCHAN_EV_TS_ERROR received in a BORKEN state" }, + [BTS_CTR_TS_BORKEN_FROM_NOT_INITIALIZED] = \ + { "ts_borken:from_state:not_initialized", + "Transitions from TS NOT_INITIALIZED state to BORKEN state" }, + [BTS_CTR_TS_BORKEN_FROM_UNUSED] = \ + { "ts_borken:from_state:unused", + "Transitions from TS UNUSED state to BORKEN state" }, + [BTS_CTR_TS_BORKEN_FROM_WAIT_PDCH_ACT] = \ + { "ts_borken:from_state:wait_pdch_act", + "Transitions from TS WAIT_PDCH_ACT state to BORKEN state" }, + [BTS_CTR_TS_BORKEN_FROM_PDCH] = \ + { "ts_borken:from_state:pdch", + "Transitions from TS PDCH state to BORKEN state" }, + [BTS_CTR_TS_BORKEN_FROM_WAIT_PDCH_DEACT] = \ + { "ts_borken:from_state:wait_pdch_deact", + "Transitions from TS WAIT_PDCH_DEACT state to BORKEN state" }, + [BTS_CTR_TS_BORKEN_FROM_IN_USE] = \ + { "ts_borken:from_state:in_use", + "Transitions from TS IN_USE state to BORKEN state" }, + [BTS_CTR_TS_BORKEN_FROM_BORKEN] = \ + { "ts_borken:from_state:borken", + "Transitions from TS BORKEN state to BORKEN state" }, + [BTS_CTR_TS_BORKEN_FROM_UNKNOWN] = \ + { "ts_borken:from_state:unknown", + "Transitions from an unknown TS state to BORKEN state" }, + [BTS_CTR_TS_BORKEN_EV_PDCH_ACT_ACK_NACK] = \ + { "ts_borken:event:pdch_act_ack_nack", + "PDCH_ACT_ACK/NACK received in the TS BORKEN state" }, + [BTS_CTR_TS_BORKEN_EV_PDCH_DEACT_ACK_NACK] = \ + { "ts_borken:event:pdch_deact_ack_nack", + "PDCH_DEACT_ACK/NACK received in the TS BORKEN state" }, + [BTS_CTR_TS_BORKEN_EV_TEARDOWN] = \ + { "ts_borken:event:teardown", + "TS in a BORKEN state is shutting down (BTS disconnected?)" }, + [BTS_CTR_ASSIGNMENT_ATTEMPTED] = \ + { "assignment:attempted", + "Assignment attempts" }, + [BTS_CTR_ASSIGNMENT_ATTEMPTED_SIGN] = \ + { "assignment:attempted_sign", + "Assignment of signaling lchan attempts" }, + [BTS_CTR_ASSIGNMENT_ATTEMPTED_SPEECH] = \ + { "assignment:attempted_speech", + "Assignment of speech lchan attempts" }, + [BTS_CTR_ASSIGNMENT_COMPLETED] = \ + { "assignment:completed", + "Assignment completed" }, + [BTS_CTR_ASSIGNMENT_COMPLETED_SIGN] = \ + { "assignment:completed_sign", + "Assignment of signaling lchan completed" }, + [BTS_CTR_ASSIGNMENT_COMPLETED_SPEECH] = \ + { "assignment:completed_speech", + "Assignment if speech lchan completed" }, + [BTS_CTR_ASSIGNMENT_STOPPED] = \ + { "assignment:stopped", + "Connection ended during Assignment" }, + [BTS_CTR_ASSIGNMENT_STOPPED_SIGN] = \ + { "assignment:stopped_sign", + "Connection ended during signaling lchan Assignment" }, + [BTS_CTR_ASSIGNMENT_STOPPED_SPEECH] = \ + { "assignment:stopped_speech", + "Connection ended during speech lchan Assignment" }, + [BTS_CTR_ASSIGNMENT_NO_CHANNEL] = \ + { "assignment:no_channel", + "Failure to allocate lchan for Assignment" }, + [BTS_CTR_ASSIGNMENT_NO_CHANNEL_SIGN] = \ + { "assignment:no_channel_sign", + "Failure to allocate signaling lchan for Assignment" }, + [BTS_CTR_ASSIGNMENT_NO_CHANNEL_SPEECH] = \ + { "assignment:no_channel_speech", + "Failure to allocate speech lchan for Assignment" }, + [BTS_CTR_ASSIGNMENT_TIMEOUT] = \ + { "assignment:timeout", + "Assignment timed out" }, + [BTS_CTR_ASSIGNMENT_TIMEOUT_SIGN] = \ + { "assignment:timeout_sign", + "Assignment of signaling lchan timed out" }, + [BTS_CTR_ASSIGNMENT_TIMEOUT_SPEECH] = \ + { "assignment:timeout_speech", + "Assignment of speech lchan timed out" }, + [BTS_CTR_ASSIGNMENT_FAILED] = \ + { "assignment:failed", + "Received Assignment Failure message" }, + [BTS_CTR_ASSIGNMENT_FAILED_SIGN] = \ + { "assignment:failed_sign", + "Received Assignment Failure message on signaling lchan" }, + [BTS_CTR_ASSIGNMENT_FAILED_SPEECH] = \ + { "assignment:failed_speech", + "Received Assignment Failure message on speech lchan" }, + [BTS_CTR_ASSIGNMENT_ERROR] = \ + { "assignment:error", + "Assignment failed for other reason" }, + [BTS_CTR_ASSIGNMENT_ERROR_SIGN] = \ + { "assignment:error_sign", + "Assignment of signaling lchan failed for other reason" }, + [BTS_CTR_ASSIGNMENT_ERROR_SPEECH] = \ + { "assignment:error_speech", + "Assignment of speech lchan failed for other reason" }, + [BTS_CTR_LOCATION_UPDATE_ACCEPT] = \ + { "location_update:accept", + "Location Update Accept" }, + [BTS_CTR_LOCATION_UPDATE_REJECT] = \ + { "location_update:reject", + "Location Update Reject" }, + [BTS_CTR_LOCATION_UPDATE_DETACH] = \ + { "location_update:detach", + "Location Update Detach" }, + [BTS_CTR_LOCATION_UPDATE_UNKNOWN] = \ + { "location_update:unknown", + "Location Update UNKNOWN" }, + [BTS_CTR_HANDOVER_ATTEMPTED] = \ + { "handover:attempted", + "Intra-BSC handover attempts" }, + [BTS_CTR_HANDOVER_COMPLETED] = \ + { "handover:completed", + "Intra-BSC handover completed" }, + [BTS_CTR_HANDOVER_STOPPED] = \ + { "handover:stopped", + "Connection ended during HO" }, + [BTS_CTR_HANDOVER_NO_CHANNEL] = \ + { "handover:no_channel", + "Failure to allocate lchan for HO" }, + [BTS_CTR_HANDOVER_TIMEOUT] = \ + { "handover:timeout", + "Handover timed out" }, + [BTS_CTR_HANDOVER_FAILED] = \ + { "handover:failed", + "Received Handover Fail messages" }, + [BTS_CTR_HANDOVER_ERROR] = \ + { "handover:error", + "Re-assignment failed for other reason" }, + + [BTS_CTR_INTRA_CELL_HO_ATTEMPTED] = \ + { "intra_cell_ho:attempted", + "Intra-Cell handover attempts" }, + [BTS_CTR_INTRA_CELL_HO_COMPLETED] = \ + { "intra_cell_ho:completed", + "Intra-Cell handover completed" }, + [BTS_CTR_INTRA_CELL_HO_STOPPED] = \ + { "intra_cell_ho:stopped", + "Connection ended during HO" }, + [BTS_CTR_INTRA_CELL_HO_NO_CHANNEL] = \ + { "intra_cell_ho:no_channel", + "Failure to allocate lchan for HO" }, + [BTS_CTR_INTRA_CELL_HO_TIMEOUT] = \ + { "intra_cell_ho:timeout", + "Handover timed out" }, + [BTS_CTR_INTRA_CELL_HO_FAILED] = \ + { "intra_cell_ho:failed", + "Received Handover Fail messages" }, + [BTS_CTR_INTRA_CELL_HO_ERROR] = \ + { "intra_cell_ho:error", + "Re-assignment failed for other reason" }, + + [BTS_CTR_INTRA_BSC_HO_ATTEMPTED] = \ + { "intra_bsc_ho:attempted", + "Intra-BSC inter-cell handover attempts" }, + [BTS_CTR_INTRA_BSC_HO_COMPLETED] = \ + { "intra_bsc_ho:completed", + "Intra-BSC inter-cell handover completed" }, + [BTS_CTR_INTRA_BSC_HO_STOPPED] = \ + { "intra_bsc_ho:stopped", + "Connection ended during HO" }, + [BTS_CTR_INTRA_BSC_HO_NO_CHANNEL] = \ + { "intra_bsc_ho:no_channel", + "Failure to allocate lchan for HO" }, + [BTS_CTR_INTRA_BSC_HO_TIMEOUT] = \ + { "intra_bsc_ho:timeout", + "Handover timed out" }, + [BTS_CTR_INTRA_BSC_HO_FAILED] = \ + { "intra_bsc_ho:failed", + "Received Handover Fail messages" }, + [BTS_CTR_INTRA_BSC_HO_ERROR] = \ + { "intra_bsc_ho:error", + "Intra-BSC inter-cell HO failed for other reason" }, + + [BTS_CTR_INCOMING_INTRA_BSC_HO_ATTEMPTED] = \ + { "incoming_intra_bsc_ho:attempted", + "Incoming intra-BSC inter-cell handover attempts" }, + [BTS_CTR_INCOMING_INTRA_BSC_HO_COMPLETED] = \ + { "incoming_intra_bsc_ho:completed", + "Incoming intra-BSC inter-cell handover completed" }, + [BTS_CTR_INCOMING_INTRA_BSC_HO_STOPPED] = \ + { "incoming_intra_bsc_ho:stopped", + "Connection ended during HO" }, + [BTS_CTR_INCOMING_INTRA_BSC_HO_NO_CHANNEL] = \ + { "incoming_intra_bsc_ho:no_channel", + "Failure to allocate lchan for HO" }, + [BTS_CTR_INCOMING_INTRA_BSC_HO_TIMEOUT] = \ + { "incoming_intra_bsc_ho:timeout", + "Handover timed out" }, + [BTS_CTR_INCOMING_INTRA_BSC_HO_FAILED] = \ + { "incoming_intra_bsc_ho:failed", + "Received Handover Fail messages" }, + [BTS_CTR_INCOMING_INTRA_BSC_HO_ERROR] = \ + { "incoming_intra_bsc_ho:error", + "Incoming intra-BSC inter-cell HO failed for other reason" }, + + [BTS_CTR_INTER_BSC_HO_OUT_ATTEMPTED] = \ + { "interbsc_ho_out:attempted", + "Attempts to handover to remote BSS" }, + [BTS_CTR_INTER_BSC_HO_OUT_COMPLETED] = \ + { "interbsc_ho_out:completed", + "Handover to remote BSS completed" }, + [BTS_CTR_INTER_BSC_HO_OUT_STOPPED] = \ + { "interbsc_ho_out:stopped", + "Connection ended during HO" }, + [BTS_CTR_INTER_BSC_HO_OUT_TIMEOUT] = \ + { "interbsc_ho_out:timeout", + "Handover timed out" }, + [BTS_CTR_INTER_BSC_HO_OUT_FAILED] = \ + { "interbsc_ho_out:failed", + "Received Handover Fail message" }, + [BTS_CTR_INTER_BSC_HO_OUT_ERROR] = \ + { "interbsc_ho_out:error", + "Handover to remote BSS failed for other reason" }, + [BTS_CTR_INTER_BSC_HO_IN_ATTEMPTED] = \ + { "interbsc_ho_in:attempted", + "Attempts to handover from remote BSS" }, + [BTS_CTR_INTER_BSC_HO_IN_COMPLETED] = \ + { "interbsc_ho_in:completed", + "Handover from remote BSS completed" }, + [BTS_CTR_INTER_BSC_HO_IN_STOPPED] = \ + { "interbsc_ho_in:stopped", + "Connection ended during HO" }, + [BTS_CTR_INTER_BSC_HO_IN_NO_CHANNEL] = \ + { "interbsc_ho_in:no_channel", + "Failure to allocate lchan for HO" }, + [BTS_CTR_INTER_BSC_HO_IN_TIMEOUT] = \ + { "interbsc_ho_in:timeout", + "Handover from remote BSS timed out" }, + [BTS_CTR_INTER_BSC_HO_IN_FAILED] = \ + { "interbsc_ho_in:failed", + "Received Handover Fail message" }, + [BTS_CTR_INTER_BSC_HO_IN_ERROR] = \ + { "interbsc_ho_in:error", + "Handover from remote BSS failed for other reason" }, + + [BTS_CTR_SRVCC_ATTEMPTED] = \ + { "srvcc:attempted", + "Intra-BSC handover attempts" }, + [BTS_CTR_SRVCC_COMPLETED] = \ + { "srvcc:completed", + "Intra-BSC handover completed" }, + [BTS_CTR_SRVCC_STOPPED] = \ + { "srvcc:stopped", + "Connection ended during HO" }, + [BTS_CTR_SRVCC_NO_CHANNEL] = \ + { "srvcc:no_channel", + "Failure to allocate lchan for HO" }, + [BTS_CTR_SRVCC_TIMEOUT] = \ + { "srvcc:timeout", + "Handover timed out" }, + [BTS_CTR_SRVCC_FAILED] = \ + { "srvcc:failed", + "Received Handover Fail messages" }, + [BTS_CTR_SRVCC_ERROR] = \ + { "srvcc:error", + "Re-assignment failed for other reason" }, + [BTS_CTR_ALL_ALLOCATED_SDCCH] = \ + { "all_allocated:sdcch", + "Cumulative counter of seconds where all SDCCH channels were allocated" }, + [BTS_CTR_ALL_ALLOCATED_STATIC_SDCCH] = \ + { "all_allocated:static_sdcch", + "Cumulative counter of seconds where all non-dynamic SDCCH channels were allocated" }, + [BTS_CTR_ALL_ALLOCATED_TCH] = \ + { "all_allocated:tch", + "Cumulative counter of seconds where all TCH channels were allocated" }, + [BTS_CTR_ALL_ALLOCATED_STATIC_TCH] = \ + { "all_allocated:static_tch", + "Cumulative counter of seconds where all non-dynamic TCH channels were allocated" }, + + [BTS_CTR_CM_SERV_REJ] = \ + { "cm_serv_rej", "MSC sent CM Service Reject" }, + [BTS_CTR_CM_SERV_REJ_IMSI_UNKNOWN_IN_HLR] = \ + { "cm_serv_rej:imsi_unknown_in_hlr", + "MSC sent CM Service Reject with cause IMSI_UNKNOWN_IN_HLR" }, + [BTS_CTR_CM_SERV_REJ_ILLEGAL_MS] = \ + { "cm_serv_rej:illegal_ms", + "MSC sent CM Service Reject with cause ILLEGAL_MS" }, + [BTS_CTR_CM_SERV_REJ_IMSI_UNKNOWN_IN_VLR] = \ + { "cm_serv_rej:imsi_unknown_in_vlr", + "MSC sent CM Service Reject with cause IMSI_UNKNOWN_IN_VLR" }, + [BTS_CTR_CM_SERV_REJ_IMEI_NOT_ACCEPTED] = \ + { "cm_serv_rej:imei_not_accepted", + "MSC sent CM Service Reject with cause IMEI_NOT_ACCEPTED" }, + [BTS_CTR_CM_SERV_REJ_ILLEGAL_ME] = \ + { "cm_serv_rej:illegal_me", + "MSC sent CM Service Reject with cause ILLEGAL_ME" }, + [BTS_CTR_CM_SERV_REJ_PLMN_NOT_ALLOWED] = \ + { "cm_serv_rej:plmn_not_allowed", + "MSC sent CM Service Reject with cause PLMN_NOT_ALLOWED" }, + [BTS_CTR_CM_SERV_REJ_LOC_NOT_ALLOWED] = \ + { "cm_serv_rej:loc_not_allowed", + "MSC sent CM Service Reject with cause LOC_NOT_ALLOWED" }, + [BTS_CTR_CM_SERV_REJ_ROAMING_NOT_ALLOWED] = \ + { "cm_serv_rej:roaming_not_allowed", + "MSC sent CM Service Reject with cause ROAMING_NOT_ALLOWED" }, + [BTS_CTR_CM_SERV_REJ_NETWORK_FAILURE] = \ + { "cm_serv_rej:network_failure", + "MSC sent CM Service Reject with cause NETWORK_FAILURE" }, + [BTS_CTR_CM_SERV_REJ_SYNCH_FAILURE] = \ + { "cm_serv_rej:synch_failure", + "MSC sent CM Service Reject with cause SYNCH_FAILURE" }, + [BTS_CTR_CM_SERV_REJ_CONGESTION] = \ + { "cm_serv_rej:congestion", + "MSC sent CM Service Reject with cause CONGESTION" }, + [BTS_CTR_CM_SERV_REJ_SRV_OPT_NOT_SUPPORTED] = \ + { "cm_serv_rej:srv_opt_not_supported", + "MSC sent CM Service Reject with cause SRV_OPT_NOT_SUPPORTED" }, + [BTS_CTR_CM_SERV_REJ_RQD_SRV_OPT_NOT_SUPPORTED] = \ + { "cm_serv_rej:rqd_srv_opt_not_supported", + "MSC sent CM Service Reject with cause RQD_SRV_OPT_NOT_SUPPORTED" }, + [BTS_CTR_CM_SERV_REJ_SRV_OPT_TMP_OUT_OF_ORDER] = \ + { "cm_serv_rej:srv_opt_tmp_out_of_order", + "MSC sent CM Service Reject with cause SRV_OPT_TMP_OUT_OF_ORDER" }, + [BTS_CTR_CM_SERV_REJ_CALL_CAN_NOT_BE_IDENTIFIED] = \ + { "cm_serv_rej:call_can_not_be_identified", + "MSC sent CM Service Reject with cause CALL_CAN_NOT_BE_IDENTIFIED" }, + [BTS_CTR_CM_SERV_REJ_INCORRECT_MESSAGE] = \ + { "cm_serv_rej:incorrect_message", + "MSC sent CM Service Reject with cause INCORRECT_MESSAGE" }, + [BTS_CTR_CM_SERV_REJ_INVALID_MANDANTORY_INF] = \ + { "cm_serv_rej:invalid_mandantory_inf", + "MSC sent CM Service Reject with cause INVALID_MANDANTORY_INF" }, + [BTS_CTR_CM_SERV_REJ_MSG_TYPE_NOT_IMPLEMENTED] = \ + { "cm_serv_rej:msg_type_not_implemented", + "MSC sent CM Service Reject with cause MSG_TYPE_NOT_IMPLEMENTED" }, + [BTS_CTR_CM_SERV_REJ_MSG_TYPE_NOT_COMPATIBLE] = \ + { "cm_serv_rej:msg_type_not_compatible", + "MSC sent CM Service Reject with cause MSG_TYPE_NOT_COMPATIBLE" }, + [BTS_CTR_CM_SERV_REJ_INF_ELEME_NOT_IMPLEMENTED] = \ + { "cm_serv_rej:inf_eleme_not_implemented", + "MSC sent CM Service Reject with cause INF_ELEME_NOT_IMPLEMENTED" }, + [BTS_CTR_CM_SERV_REJ_CONDTIONAL_IE_ERROR] = \ + { "cm_serv_rej:condtional_ie_error", + "MSC sent CM Service Reject with cause CONDTIONAL_IE_ERROR" }, + [BTS_CTR_CM_SERV_REJ_MSG_NOT_COMPATIBLE] = \ + { "cm_serv_rej:msg_not_compatible", + "MSC sent CM Service Reject with cause MSG_NOT_COMPATIBLE" }, + [BTS_CTR_CM_SERV_REJ_PROTOCOL_ERROR] = \ + { "cm_serv_rej:protocol_error", + "MSC sent CM Service Reject with cause PROTOCOL_ERROR" }, + [BTS_CTR_CM_SERV_REJ_RETRY_IN_NEW_CELL] = \ + { "cm_serv_rej:retry_in_new_cell", + "MSC sent CM Service Reject with cause 00110000..00111111, Retry upon entry in a new cell" }, +}; + +const struct rate_ctr_group_desc bts_ctrg_desc = { + "bts", + "base transceiver station", + OSMO_STATS_CLASS_GLOBAL, + ARRAY_SIZE(bts_ctr_description), + bts_ctr_description, +}; + +const struct osmo_stat_item_desc bts_stat_desc[] = { + [BTS_STAT_UPTIME_SECONDS] = \ + { "uptime:seconds", + "Seconds of uptime", + "s", 60, 0 }, + [BTS_STAT_CHAN_LOAD_AVERAGE] = \ + { "chanloadavg", + "Channel load average", + "%", 60, 0 }, + [BTS_STAT_CHAN_CCCH_SDCCH4_USED] = \ + { "chan_ccch_sdcch4:used", + "Number of CCCH+SDCCH4 channels used", + "", 60, 0 }, + [BTS_STAT_CHAN_CCCH_SDCCH4_TOTAL] = \ + { "chan_ccch_sdcch4:total", + "Number of CCCH+SDCCH4 channels total", + "", 60, 0 }, + [BTS_STAT_CHAN_TCH_F_USED] = \ + { "chan_tch_f:used", + "Number of TCH/F channels used", + "", 60, 0 }, + [BTS_STAT_CHAN_TCH_F_TOTAL] = \ + { "chan_tch_f:total", + "Number of TCH/F channels total", + "", 60, 0 }, + [BTS_STAT_CHAN_TCH_H_USED] = \ + { "chan_tch_h:used", + "Number of TCH/H channels used", + "", 60, 0 }, + [BTS_STAT_CHAN_TCH_H_TOTAL] = \ + { "chan_tch_h:total", + "Number of TCH/H channels total", + "", 60, 0 }, + [BTS_STAT_CHAN_SDCCH8_USED] = \ + { "chan_sdcch8:used", + "Number of SDCCH8 channels used", + "", 60, 0 }, + [BTS_STAT_CHAN_SDCCH8_TOTAL] = \ + { "chan_sdcch8:total", + "Number of SDCCH8 channels total", + "", 60, 0 }, + [BTS_STAT_CHAN_TCH_F_PDCH_USED] = \ + { "chan_dynamic_ipaccess:used", + "Number of DYNAMIC/IPACCESS channels used", + "", 60, 0 }, + [BTS_STAT_CHAN_TCH_F_PDCH_TOTAL] = \ + { "chan_dynamic_ipaccess:total", + "Number of DYNAMIC/IPACCESS channels total", + "", 60, 0 }, + [BTS_STAT_CHAN_CCCH_SDCCH4_CBCH_USED] = \ + { "chan_ccch_sdcch4_cbch:used", + "Number of CCCH+SDCCH4+CBCH channels used", + "", 60, 0 }, + [BTS_STAT_CHAN_CCCH_SDCCH4_CBCH_TOTAL] = \ + { "chan_ccch_sdcch4_cbch:total", + "Number of CCCH+SDCCH4+CBCH channels total", + "", 60, 0 }, + [BTS_STAT_CHAN_SDCCH8_CBCH_USED] = \ + { "chan_sdcch8_cbch:used", + "Number of SDCCH8+CBCH channels used", + "", 60, 0 }, + [BTS_STAT_CHAN_SDCCH8_CBCH_TOTAL] = \ + { "chan_sdcch8_cbch:total", + "Number of SDCCH8+CBCH channels total", + "", 60, 0 }, + [BTS_STAT_CHAN_OSMO_DYN_USED] = \ + { "chan_dynamic_osmocom:used", + "Number of DYNAMIC/OSMOCOM channels used", + "", 60, 0 }, + [BTS_STAT_CHAN_OSMO_DYN_TOTAL] = \ + { "chan_dynamic_osmocom:total", + "Number of DYNAMIC/OSMOCOM channels total", + "", 60, 0 }, + [BTS_STAT_T3122] = \ + { "T3122", + "T3122 IMMEDIATE ASSIGNMENT REJECT wait indicator", + "s", 60, GSM_T3122_DEFAULT }, + [BTS_STAT_RACH_BUSY] = \ + { "rach_busy", + "RACH slots with signal above threshold", + "%", 60, 0 }, + [BTS_STAT_RACH_ACCESS] = \ + { "rach_access", + "RACH slots with access bursts in them", + "%", 60, 0 }, + [BTS_STAT_OML_CONNECTED] = \ + { "oml_connected", + "Number of OML links connected", + "", 16, 0 }, + [BTS_STAT_RSL_CONNECTED] = \ + { "rsl_connected", + "Number of RSL links connected (same as num_trx:rsl_connected)", + "", 16, 0 }, + [BTS_STAT_LCHAN_BORKEN] = \ + { "lchan_borken", + "Number of lchans in the BORKEN state", + "", 16, 0 }, + [BTS_STAT_TS_BORKEN] = \ + { "ts_borken", + "Number of timeslots in the BORKEN state", + "", 16, 0 }, + [BTS_STAT_NUM_TRX_RSL_CONNECTED] = \ + { "num_trx:rsl_connected", + "Number of TRX in this BTS where RSL is up", + "" }, + [BTS_STAT_NUM_TRX_TOTAL] = \ + { "num_trx:total", + "Number of configured TRX in this BTS", + "" }, + [BTS_STAT_PAGING_REQ_QUEUE_LENGTH] = \ + { "paging:request_queue_length", + "Paging Request queue length", + "", 60, 0 }, + [BTS_STAT_PAGING_AVAILABLE_SLOTS] = \ + { "paging:available_slots", + "Available paging slots in this BTS", + "", 60, 0 }, + [BTS_STAT_PAGING_T3113] = \ + { "paging:t3113", + "T3113 paging timer", + "s", 60, 0 }, +}; + +const struct osmo_stat_item_group_desc bts_statg_desc = { + .group_name_prefix = "bts", + .group_description = "base transceiver station", + .class_id = OSMO_STATS_CLASS_GLOBAL, + .num_items = ARRAY_SIZE(bts_stat_desc), + .item_desc = bts_stat_desc, +}; + +/* Return 'true' if and only if Ny1 satisfies network requirements */ +bool gsm_bts_check_ny1(const struct gsm_bts *bts) +{ + unsigned long T3105, ny1, ny1_recommended; + T3105 = osmo_tdef_get(bts->network->T_defs, 3105, OSMO_TDEF_MS, -1); + ny1 = osmo_tdef_get(bts->network->T_defs, -3105, OSMO_TDEF_CUSTOM, -1); + if (!(T3105 * ny1 > GSM_T3124_MAX + GSM_NY1_REQ_DELTA)) { + /* See comment for GSM_NY1_DEFAULT */ + ny1_recommended = (GSM_T3124_MAX + GSM_NY1_REQ_DELTA)/T3105 + 1; + LOGP(DNM, LOGL_ERROR, "Value of Ny1 should be higher. " + "Is: %lu, lowest recommendation: %lu\n", + ny1, ny1_recommended); + return false; + } + return true; +} |