aboutsummaryrefslogtreecommitdiffstats
path: root/src/osmo-bsc/bts.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/osmo-bsc/bts.c')
-rw-r--r--src/osmo-bsc/bts.c1361
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;
+}