/* (C) 2008-2018 by Harald Welte * (C) 2021 by sysmocom s.f.m.c. GmbH * * All Rights Reserved * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . * */ #include #include #include #include #include const struct value_string bts_attribute_names[] = { OSMO_VALUE_STRING(BTS_TYPE_VARIANT), OSMO_VALUE_STRING(BTS_SUB_MODEL), OSMO_VALUE_STRING(TRX_PHY_VERSION), { 0, NULL } }; enum bts_attribute str2btsattr(const char *s) { return get_string_value(bts_attribute_names, s); } const char *btsatttr2str(enum bts_attribute v) { return get_value_string(bts_attribute_names, v); } const struct value_string osmo_bts_variant_names[_NUM_BTS_VARIANT + 1] = { { BTS_UNKNOWN, "unknown" }, { BTS_OSMO_LITECELL15, "osmo-bts-lc15" }, { BTS_OSMO_OCTPHY, "osmo-bts-octphy" }, { BTS_OSMO_SYSMO, "osmo-bts-sysmo" }, { BTS_OSMO_TRX, "omso-bts-trx" }, { 0, NULL } }; enum gsm_bts_type_variant str2btsvariant(const char *arg) { return get_string_value(osmo_bts_variant_names, arg); } const char *btsvariant2str(enum gsm_bts_type_variant v) { return get_value_string(osmo_bts_variant_names, v); } const struct value_string bts_type_names[_NUM_GSM_BTS_TYPE + 1] = { { GSM_BTS_TYPE_UNKNOWN, "unknown" }, { GSM_BTS_TYPE_BS11, "bs11" }, { GSM_BTS_TYPE_NANOBTS, "nanobts" }, { GSM_BTS_TYPE_RBS2000, "rbs2000" }, { GSM_BTS_TYPE_NOKIA_SITE, "nokia_site" }, { GSM_BTS_TYPE_OSMOBTS, "osmo-bts" }, { 0, NULL } }; const struct value_string bts_type_descs[_NUM_GSM_BTS_TYPE+1] = { { GSM_BTS_TYPE_UNKNOWN, "Unknown BTS Type" }, { GSM_BTS_TYPE_BS11, "Siemens BTS (BS-11 or compatible)" }, { 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, "Osmocom Base Transceiver Station" }, { 0, NULL } }; enum gsm_bts_type str2btstype(const char *arg) { return get_string_value(bts_type_names, arg); } 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) { struct gsm_bts_model *model; llist_for_each_entry(model, &bts_models, list) { if (model->type == type) return model; } return NULL; } int gsm_bts_model_register(struct gsm_bts_model *model) { if (bts_model_find(model->type)) return -EEXIST; tlv_def_patch(&model->nm_att_tlvdef, &abis_nm_att_tlvdef); llist_add_tail(&model->list, &bts_models); return 0; } 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 = { .parameter = { [RLC_T3142] = 20, [RLC_T3169] = 5, [RLC_T3191] = 5, [RLC_T3193] = 160, /* 10ms */ [RLC_T3195] = 5, [RLC_N3101] = 10, [RLC_N3103] = 4, [RLC_N3105] = 8, [CV_COUNTDOWN] = 15, [T_DL_TBF_EXT] = 250 * 10, /* ms */ [T_UL_TBF_EXT] = 250 * 10, /* ms */ }, .paging = { .repeat_time = 5 * 50, /* ms */ .repeat_count = 3, }, .cs_mask = 0x1fff, .initial_cs = 2, .initial_mcs = 6, }; static int gsm_bts_talloc_destructor(struct gsm_bts *bts) { 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; } 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, struct gsm_bts_sm *bts_sm, uint8_t bts_num) { struct gsm_bts *bts = talloc_zero(bts_sm, struct gsm_bts); struct gsm48_multi_rate_conf mr_cfg; int i; 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); bts->network = net; bts->ms_max_power = 15; /* dBm */ 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)); memcpy(&bts->gprs.cell.rlc_cfg, &rlc_cfg_default, sizeof(bts->gprs.cell.rlc_cfg)); 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); if (!bts->bts_ctrs) { talloc_free(bts); return NULL; } 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) { rate_ctr_group_free(bts->bts_ctrs); osmo_stat_item_group_free(bts->bts_statg); talloc_free(bts); return NULL; } 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->rach_b_thresh = -1; bts->rach_ldavg_slots = -1; bts->paging.free_chans_need = -1; INIT_LLIST_HEAD(&bts->paging.pending_requests); bts->features.data = &bts->_features_data[0]; bts->features.data_len = sizeof(bts->_features_data); /* si handling */ bts->bcch_change_mark = 1; bts->chan_load_avg = 0; /* timer overrides */ bts->T3122 = 0; /* not overridden by default */ bts->T3113_dynamic = true; /* dynamic by default */ bts->dtxu = GSM48_DTX_SHALL_NOT_BE_USED; bts->dtxd = false; bts->gprs.ctrl_ack_type_use_block = true; /* use RLC/MAC control block */ 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_allow_tch_for_signalling = true; 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; bts->si_common.si2quater_neigh_list.meas_bw = bts->si_common.data.meas_bw_list; bts->si_common.si2quater_neigh_list.length = MAX_EARFCN_LIST; bts->si_common.si2quater_neigh_list.thresh_hi = 0; osmo_earfcn_init(&bts->si_common.si2quater_neigh_list); bts->si_common.neigh_list.data = bts->si_common.data.neigh_list; bts->si_common.neigh_list.data_len = sizeof(bts->si_common.data.neigh_list); bts->si_common.si5_neigh_list.data = bts->si_common.data.si5_neigh_list; bts->si_common.si5_neigh_list.data_len = sizeof(bts->si_common.data.si5_neigh_list); bts->si_common.cell_alloc.data = bts->si_common.data.cell_alloc; bts->si_common.cell_alloc.data_len = sizeof(bts->si_common.data.cell_alloc); bts->si_common.rach_control.re = 1; /* no re-establishment */ 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->neighbors); INIT_LLIST_HEAD(&bts->oml_fail_rep); INIT_LLIST_HEAD(&bts->chan_rqd_queue); /* 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){ .hr = 1, .efr = 1, .amr = 1, }; /* 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) { .m4_75 = 1, .m5_15 = 0, .m5_90 = 1, .m6_70 = 0, .m7_40 = 1, .m7_95 = 0, .m10_2 = 0, .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) { .m4_75 = 1, .m5_15 = 0, .m5_90 = 1, .m6_70 = 0, .m7_40 = 1, .m7_95 = 0, .m10_2 = 0, .m12_2 = 0 }; 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); 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; } static char ts2str[255]; char *gsm_bts_name(const struct gsm_bts *bts) { if (!bts) snprintf(ts2str, sizeof(ts2str), "(bts=NULL)"); else snprintf(ts2str, sizeof(ts2str), "(bts=%d)", bts->nr); return ts2str; } bool gsm_bts_matches_lai(const struct gsm_bts *bts, const struct osmo_location_area_id *lai) { return osmo_plmn_cmp(&lai->plmn, &bts->network->plmn) == 0 && lai->lac == bts->location_area_code; } bool gsm_bts_matches_cell_id(const struct gsm_bts *bts, const struct gsm0808_cell_id *cell_id) { const union gsm0808_cell_id_u *id = &cell_id->id; if (!bts || !cell_id) return false; switch (cell_id->id_discr) { 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; case CELL_IDENT_CI: return id->ci == bts->cell_identity; case CELL_IDENT_NO_CELL: return false; case CELL_IDENT_LAI_AND_LAC: return gsm_bts_matches_lai(bts, &id->lai_and_lac); case CELL_IDENT_LAC: return id->lac == bts->location_area_code; case CELL_IDENT_BSS: return true; case CELL_IDENT_UTRAN_PLMN_LAC_RNC: case CELL_IDENT_UTRAN_RNC: case CELL_IDENT_UTRAN_LAC_RNC: return false; default: OSMO_ASSERT(false); } } /* 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) { *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, }, }; } /* 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 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; 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 lchan; } 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; if (model->start && !model->started) { int ret = model->start(bts->network); if (ret < 0) return ret; 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_OSMOBTS: case GSM_BTS_TYPE_NANOBTS: /* Set the default OML Stream ID to 0xff */ bts->oml_tei = 0xff; bts->c0->nominal_power = 23; break; case GSM_BTS_TYPE_RBS2000: INIT_LLIST_HEAD(&bts->rbs2000.is.conn_groups); INIT_LLIST_HEAD(&bts->rbs2000.con.conn_groups); break; case GSM_BTS_TYPE_BS11: case GSM_BTS_TYPE_UNKNOWN: case GSM_BTS_TYPE_NOKIA_SITE: /* Set default BTS reset timer */ bts->nokia.bts_reset_timer_cnf = 15; case _NUM_GSM_BTS_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)) { return 0; } if (mode == BTS_GPRS_EGPRS && !osmo_bts_has_feature(&bts->model->features, BTS_FEAT_EGPRS)) { return 0; } return 1; } struct gsm_bts_trx *gsm_bts_trx_num(const struct gsm_bts *bts, int num) { struct gsm_bts_trx *trx; if (num >= bts->num_trx) return NULL; llist_for_each_entry(trx, &bts->trx_list, list) { if (trx->nr == num) return trx; } 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)); } unsigned long long bts_uptime(const struct gsm_bts *bts) { struct timespec tp; if (!bts->uptime || !bts->oml_link) return 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; } /* monotonic clock helps to ensure that the conversion is valid */ return difftime(tp.tv_sec, bts->uptime); } char *get_model_oml_status(const struct gsm_bts *bts) { if (bts->model->oml_status) return bts->model->oml_status(bts); return "unknown"; } /* reset the state of all MO in the BTS */ void gsm_bts_mo_reset(struct gsm_bts *bts) { struct gsm_bts_trx *trx; unsigned int i; gsm_abis_mo_reset(&bts->mo); gsm_abis_mo_reset(&bts->gprs.cell.mo); llist_for_each_entry(trx, &bts->trx_list, list) { gsm_abis_mo_reset(&trx->mo); gsm_abis_mo_reset(&trx->bb_transc.mo); for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { struct gsm_bts_trx_ts *ts = &trx->ts[i]; gsm_abis_mo_reset(&ts->mo); } } } /* Assume there are only 256 possible bts */ osmo_static_assert(sizeof(((struct gsm_bts *) 0)->nr) == 1, _bts_nr_is_256); static void depends_calc_index_bit(int bts_nr, int *idx, int *bit) { *idx = bts_nr / (8 * 4); *bit = bts_nr % (8 * 4); } 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; } 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); } int bts_depend_is_depedency(struct gsm_bts *base, struct gsm_bts *other) { int idx, bit; depends_calc_index_bit(other->nr, &idx, &bit); /* Check if there is a depends bit */ return (base->depends_on[idx] & (1 << bit)) > 0; } static int bts_is_online(struct gsm_bts *bts) { /* TODO: support E1 BTS too */ if (!is_ipaccess_bts(bts)) return 1; if (!bts->oml_link) return 0; return bts->mo.nm_state.operational == NM_OPSTATE_ENABLED; } int bts_depend_check(struct gsm_bts *bts) { struct gsm_bts *other_bts; llist_for_each_entry(other_bts, &bts->network->bts_list, list) { if (!bts_depend_is_depedency(bts, other_bts)) continue; if (bts_is_online(other_bts)) continue; return 0; } return 1; } /* get the radio link timeout (based on SACCH decode errors, according * to algorithm specified in TS 05.08 section 5.2. A value of -1 * indicates we should use an infinitely long timeout, which only works * with OsmoBTS as the BTS implementation */ int gsm_bts_get_radio_link_timeout(const struct gsm_bts *bts) { const struct gsm48_cell_options *cell_options = &bts->si_common.cell_options; if (bts->infinite_radio_link_timeout) return -1; else { /* Encoding as per Table 10.5.21 of TS 04.08 */ return (cell_options->radio_link_timeout + 1) << 2; } } /* set the radio link timeout (based on SACCH decode errors, according * to algorithm specified in TS 05.08 Section 5.2. A value of -1 * indicates we should use an infinitely long timeout, which only works * with OsmoBTS as the BTS implementation */ void gsm_bts_set_radio_link_timeout(struct gsm_bts *bts, int value) { struct gsm48_cell_options *cell_options = &bts->si_common.cell_options; if (value < 0) bts->infinite_radio_link_timeout = true; else { bts->infinite_radio_link_timeout = false; /* Encoding as per Table 10.5.21 of TS 04.08 */ if (value < 4) value = 4; if (value > 64) value = 64; cell_options->radio_link_timeout = (value >> 2) - 1; } } void gsm_bts_all_ts_dispatch(struct gsm_bts *bts, uint32_t ts_ev, void *data) { struct gsm_bts_trx *trx; llist_for_each_entry(trx, &bts->trx_list, list) gsm_trx_all_ts_dispatch(trx, ts_ev, data); } /* set all system information types for a BTS */ int gsm_bts_set_system_infos(struct gsm_bts *bts) { struct gsm_bts_trx *trx; /* Generate a new ID */ bts->bcch_change_mark += 1; bts->bcch_change_mark %= 0x7; llist_for_each_entry(trx, &bts->trx_list, list) { int rc; rc = gsm_bts_trx_set_system_infos(trx); if (rc != 0) return rc; } 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); } 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 OML 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_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_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_tch_f_pdch:used", "Number of TCH/F_PDCH channels used", "", 60, 0 }, [BTS_STAT_CHAN_TCH_F_PDCH_TOTAL] = \ { "chan_tch_f_pdch:total", "Number of TCH/F_PDCH 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_osmo_dyn:used", "Number of TCH/F_TCH/H_SDCCH8_PDCH channels used", "", 60, 0 }, [BTS_STAT_CHAN_OSMO_DYN_TOTAL] = \ { "chan_osmo_dyn:total", "Number of TCH/F_TCH/H_SDCCH8_PDCH 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", "" }, }; 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, };