/* OsmoBSC interface to quagga VTY, TRX (and TS) node */ /* (C) 2009-2017 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../bscconfig.h" #define X(x) (1 << x) static struct cmd_node trx_node = { TRX_NODE, "%s(config-net-bts-trx)# ", 1, }; static struct cmd_node ts_node = { TS_NODE, "%s(config-net-bts-trx-ts)# ", 1, }; /* utility functions */ void parse_e1_link(struct gsm_e1_subslot *e1_link, const char *line, const char *ts, const char *ss) { e1_link->e1_nr = atoi(line); e1_link->e1_ts = atoi(ts); if (!strcmp(ss, "full")) e1_link->e1_ts_ss = E1_SUBSLOT_FULL; else e1_link->e1_ts_ss = atoi(ss); } #define TRX_TEXT "Radio Transceiver\n" /* per TRX configuration */ DEFUN_ATTR(cfg_trx, cfg_trx_cmd, "trx <0-255>", TRX_TEXT "Select a TRX to configure\n", CMD_ATTR_IMMEDIATE) { int trx_nr = atoi(argv[0]); struct gsm_bts *bts = vty->index; struct gsm_bts_trx *trx; if (trx_nr > bts->num_trx) { vty_out(vty, "%% The next unused TRX number in this BTS is %u%s", bts->num_trx, VTY_NEWLINE); return CMD_WARNING; } else if (trx_nr == bts->num_trx) { /* we need to allocate a new one */ trx = gsm_bts_trx_alloc(bts); } else trx = gsm_bts_trx_num(bts, trx_nr); if (!trx) return CMD_WARNING; vty->index = trx; vty->node = TRX_NODE; return CMD_SUCCESS; } DEFUN_USRATTR(cfg_trx_arfcn, cfg_trx_arfcn_cmd, X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK), "arfcn <0-1023>", "Set the ARFCN for this TRX\n" "Absolute Radio Frequency Channel Number\n") { enum gsm_band unused; struct gsm_bts_trx *trx = vty->index; int arfcn = atoi(argv[0]); if (gsm_arfcn2band_rc(arfcn, &unused) < 0) { vty_out(vty, "%% Invalid arfcn %" PRIu16 " detected%s", arfcn, VTY_NEWLINE); return CMD_WARNING; } /* FIXME: check if this ARFCN is supported by this TRX */ trx->arfcn = arfcn; /* Update Cell Allocation (list of all the frequencies allocated to a cell) */ if (generate_cell_chan_alloc(trx->bts) != 0) { vty_out(vty, "%% Failed to re-generate Cell Allocation%s", VTY_NEWLINE); return CMD_WARNING; } /* FIXME: patch ARFCN into SYSTEM INFORMATION */ /* FIXME: use OML layer to update the ARFCN */ /* FIXME: use RSL layer to update SYSTEM INFORMATION */ return CMD_SUCCESS; } DEFUN_USRATTR(cfg_trx_nominal_power, cfg_trx_nominal_power_cmd, X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK), "nominal power <-20-100>", "Nominal TRX RF Power in dBm\n" "Nominal TRX RF Power in dBm\n" "Nominal TRX RF Power in dBm\n") { struct gsm_bts_trx *trx = vty->index; trx->nominal_power = atoi(argv[0]); return CMD_SUCCESS; } DEFUN_USRATTR(cfg_trx_max_power_red, cfg_trx_max_power_red_cmd, X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK), "max_power_red <0-100>", "Reduction of maximum BS RF Power (relative to nominal power)\n" "Reduction of maximum BS RF Power in dB\n") { int maxpwr_r = atoi(argv[0]); struct gsm_bts_trx *trx = vty->index; int upper_limit = 24; /* default 12.21 max power red. */ /* FIXME: check if our BTS type supports more than 12 */ if (maxpwr_r < 0 || maxpwr_r > upper_limit) { vty_out(vty, "%% Power %d dB is not in the valid range%s", maxpwr_r, VTY_NEWLINE); return CMD_WARNING; } if (maxpwr_r & 1) { vty_out(vty, "%% Power %d dB is not an even value%s", maxpwr_r, VTY_NEWLINE); return CMD_WARNING; } trx->max_power_red = maxpwr_r; /* FIXME: make sure we update this using OML */ return CMD_SUCCESS; } /* NOTE: This requires a full restart as bsc_network_configure() is executed * only once on startup from osmo_bsc_main.c */ DEFUN(cfg_trx_rsl_e1, cfg_trx_rsl_e1_cmd, "rsl e1 line E1_LINE timeslot <1-31> sub-slot (0|1|2|3|full)", "RSL Parameters\n" "E1/T1 interface to be used for RSL\n" "E1/T1 interface to be used for RSL\n" "E1/T1 Line Number to be used for RSL\n" "E1/T1 Timeslot to be used for RSL\n" "E1/T1 Timeslot to be used for RSL\n" "E1/T1 Sub-slot to be used for RSL\n" "E1/T1 Sub-slot 0 is to be used for RSL\n" "E1/T1 Sub-slot 1 is to be used for RSL\n" "E1/T1 Sub-slot 2 is to be used for RSL\n" "E1/T1 Sub-slot 3 is to be used for RSL\n" "E1/T1 full timeslot is to be used for RSL\n") { struct gsm_bts_trx *trx = vty->index; parse_e1_link(&trx->rsl_e1_link, argv[0], argv[1], argv[2]); return CMD_SUCCESS; } DEFUN_USRATTR(cfg_trx_rsl_e1_tei, cfg_trx_rsl_e1_tei_cmd, X(BSC_VTY_ATTR_RESTART_ABIS_RSL_LINK), "rsl e1 tei <0-63>", "RSL Parameters\n" "Set the TEI to be used for RSL\n" "Set the TEI to be used for RSL\n" "TEI to be used for RSL\n") { struct gsm_bts_trx *trx = vty->index; trx->rsl_tei_primary = atoi(argv[0]); return CMD_SUCCESS; } DEFUN_ATTR(cfg_trx_rf_locked, cfg_trx_rf_locked_cmd, "rf_locked (0|1)", "Set or unset the RF Locking (Turn off RF of the TRX)\n" "TRX is NOT RF locked (active)\n" "TRX is RF locked (turned off)\n", CMD_ATTR_IMMEDIATE) { int locked = atoi(argv[0]); struct gsm_bts_trx *trx = vty->index; gsm_trx_lock_rf(trx, locked, "vty"); return CMD_SUCCESS; } /* per TS configuration */ DEFUN_ATTR(cfg_ts, cfg_ts_cmd, "timeslot <0-7>", "Select a Timeslot to configure\n" "Timeslot number\n", CMD_ATTR_IMMEDIATE) { int ts_nr = atoi(argv[0]); struct gsm_bts_trx *trx = vty->index; struct gsm_bts_trx_ts *ts; if (ts_nr >= TRX_NR_TS) { vty_out(vty, "%% A GSM TRX only has %u Timeslots per TRX%s", TRX_NR_TS, VTY_NEWLINE); return CMD_WARNING; } ts = &trx->ts[ts_nr]; vty->index = ts; vty->node = TS_NODE; return CMD_SUCCESS; } DEFUN_USRATTR(cfg_ts_pchan, cfg_ts_pchan_cmd, X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK), "phys_chan_config PCHAN", /* dynamically generated! */ "Physical Channel configuration (TCH/SDCCH/...)\n" "Physical Channel\n") { struct gsm_bts_trx_ts *ts = vty->index; int pchanc; pchanc = gsm_pchan_parse(argv[0]); if (pchanc < 0) return CMD_WARNING; ts->pchan_from_config = pchanc; return CMD_SUCCESS; } /* used for backwards compatibility with old config files that still * have uppercase pchan type names. Also match older names for existing types. */ DEFUN_HIDDEN(cfg_ts_pchan_compat, cfg_ts_pchan_compat_cmd, "phys_chan_config PCHAN", "Physical Channel configuration (TCH/SDCCH/...)\n" "Physical Channel\n") { struct gsm_bts_trx_ts *ts = vty->index; int pchanc; pchanc = gsm_pchan_parse(argv[0]); if (pchanc < 0) { if (strcasecmp(argv[0], "tch/f_tch/h_pdch") == 0) { pchanc = GSM_PCHAN_OSMO_DYN; } else { vty_out(vty, "Unknown physical channel name '%s'%s", argv[0], VTY_NEWLINE); return CMD_ERR_NO_MATCH; } } ts->pchan_from_config = pchanc; return CMD_SUCCESS; } DEFUN_USRATTR(cfg_ts_tsc, cfg_ts_tsc_cmd, X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK), "training_sequence_code <0-7>", "Training Sequence Code of the Timeslot\n" "TSC\n") { struct gsm_bts_trx_ts *ts = vty->index; const struct gsm_bts *bts = ts->trx->bts; if (bts->features_known && !osmo_bts_has_feature(&bts->features, BTS_FEAT_MULTI_TSC)) { vty_out(vty, "%% This BTS does not support a TSC != BCC, " "falling back to BCC%s", VTY_NEWLINE); ts->tsc = -1; return CMD_WARNING; } ts->tsc = atoi(argv[0]); return CMD_SUCCESS; } #define HOPPING_STR "Configure frequency hopping\n" DEFUN_USRATTR(cfg_ts_hopping, cfg_ts_hopping_cmd, X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK), "hopping enabled (0|1)", HOPPING_STR "Enable or disable frequency hopping\n" "Disable frequency hopping\n" "Enable frequency hopping\n") { struct gsm_bts_trx_ts *ts = vty->index; const struct gsm_bts *bts = ts->trx->bts; int enabled = atoi(argv[0]); if (enabled && bts->features_known && !osmo_bts_has_feature(&bts->features, BTS_FEAT_HOPPING)) { vty_out(vty, "%% BTS does not support freq. hopping%s", VTY_NEWLINE); return CMD_WARNING; } if (enabled && bts->imm_ass_time != IMM_ASS_TIME_POST_CHAN_ACK) { vty_out(vty, "%% ERROR: 'hopping enabled 1' works only with 'immediate-assignment post-chan-ack'%s", VTY_NEWLINE); return CMD_WARNING; } ts->hopping.enabled = enabled; return CMD_SUCCESS; } DEFUN_USRATTR(cfg_ts_hsn, cfg_ts_hsn_cmd, X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK), "hopping sequence-number <0-63>", HOPPING_STR "Which hopping sequence to use for this channel\n" "Hopping Sequence Number (HSN)\n") { struct gsm_bts_trx_ts *ts = vty->index; ts->hopping.hsn = atoi(argv[0]); return CMD_SUCCESS; } DEFUN_USRATTR(cfg_ts_maio, cfg_ts_maio_cmd, X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK), "hopping maio <0-63>", HOPPING_STR "Which hopping MAIO to use for this channel\n" "Mobile Allocation Index Offset (MAIO)\n") { struct gsm_bts_trx_ts *ts = vty->index; ts->hopping.maio = atoi(argv[0]); return CMD_SUCCESS; } DEFUN_USRATTR(cfg_ts_arfcn_add, cfg_ts_arfcn_add_cmd, X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK), "hopping arfcn add <0-1023>", HOPPING_STR "Configure hopping ARFCN list\n" "Add an entry to the hopping ARFCN list\n" "ARFCN\n") { enum gsm_band unused; struct gsm_bts_trx_ts *ts = vty->index; int arfcn = atoi(argv[0]); if (gsm_arfcn2band_rc(arfcn, &unused) < 0) { vty_out(vty, "%% Invalid arfcn %" PRIu16 " detected%s", arfcn, VTY_NEWLINE); return CMD_WARNING; } if (bitvec_get_bit_pos(&ts->hopping.arfcns, arfcn) == ONE) { vty_out(vty, "%% ARFCN %" PRIu16 " is already set%s", arfcn, VTY_NEWLINE); return CMD_SUCCESS; } bitvec_set_bit_pos(&ts->hopping.arfcns, arfcn, 1); /* Update Cell Allocation (list of all the frequencies allocated to a cell) */ if (generate_cell_chan_alloc(ts->trx->bts) != 0) { vty_out(vty, "%% Failed to re-generate Cell Allocation%s", VTY_NEWLINE); bitvec_set_bit_pos(&ts->hopping.arfcns, arfcn, ZERO); /* roll-back */ return CMD_WARNING; } return CMD_SUCCESS; } DEFUN_USRATTR(cfg_ts_arfcn_del, cfg_ts_arfcn_del_cmd, X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK), "hopping arfcn del <0-1023>", HOPPING_STR "Configure hopping ARFCN list\n" "Delete an entry to the hopping ARFCN list\n" "ARFCN\n") { enum gsm_band unused; struct gsm_bts_trx_ts *ts = vty->index; int arfcn = atoi(argv[0]); if (gsm_arfcn2band_rc(arfcn, &unused) < 0) { vty_out(vty, "%% Invalid arfcn %" PRIu16 " detected%s", arfcn, VTY_NEWLINE); return CMD_WARNING; } if (bitvec_get_bit_pos(&ts->hopping.arfcns, arfcn) != ONE) { vty_out(vty, "%% ARFCN %" PRIu16 " is not set%s", arfcn, VTY_NEWLINE); return CMD_WARNING; } bitvec_set_bit_pos(&ts->hopping.arfcns, arfcn, 0); /* Update Cell Allocation (list of all the frequencies allocated to a cell) */ if (generate_cell_chan_alloc(ts->trx->bts) != 0) { vty_out(vty, "%% Failed to re-generate Cell Allocation%s", VTY_NEWLINE); /* It's unlikely to happen on removal, so we don't roll-back */ return CMD_WARNING; } return CMD_SUCCESS; } DEFUN_USRATTR(cfg_ts_arfcn_del_all, cfg_ts_arfcn_del_all_cmd, X(BSC_VTY_ATTR_RESTART_ABIS_OML_LINK), "hopping arfcn del-all", HOPPING_STR "Configure hopping ARFCN list\n" "Delete all previously configured entries\n") { struct gsm_bts_trx_ts *ts = vty->index; bitvec_zero(&ts->hopping.arfcns); /* Update Cell Allocation (list of all the frequencies allocated to a cell) */ if (generate_cell_chan_alloc(ts->trx->bts) != 0) { vty_out(vty, "%% Failed to re-generate Cell Allocation%s", VTY_NEWLINE); /* It's unlikely to happen on removal, so we don't roll-back */ return CMD_WARNING; } return CMD_SUCCESS; } /* NOTE: This will have an effect on newly created voice lchans since the E1 * voice channels are handled by osmo-mgw and the information put in e1_link * here is only used to generate the MGCP messages for the mgw. */ DEFUN_ATTR(cfg_ts_e1_subslot, cfg_ts_e1_subslot_cmd, "e1 line E1_LINE timeslot <1-31> sub-slot (0|1|2|3|full)", "E1/T1 channel connected to this on-air timeslot\n" "E1/T1 channel connected to this on-air timeslot\n" "E1/T1 line connected to this on-air timeslot\n" "E1/T1 timeslot connected to this on-air timeslot\n" "E1/T1 timeslot connected to this on-air timeslot\n" "E1/T1 sub-slot connected to this on-air timeslot\n" "E1/T1 sub-slot 0 connected to this on-air timeslot\n" "E1/T1 sub-slot 1 connected to this on-air timeslot\n" "E1/T1 sub-slot 2 connected to this on-air timeslot\n" "E1/T1 sub-slot 3 connected to this on-air timeslot\n" "Full E1/T1 timeslot connected to this on-air timeslot\n", CMD_ATTR_IMMEDIATE) { struct gsm_bts_trx_ts *ts = vty->index; parse_e1_link(&ts->e1_link, argv[0], argv[1], argv[2]); return CMD_SUCCESS; } /* call vty_out() to print a string like " as TCH/H" for dynamic timeslots. * Don't do anything if the ts is not dynamic. */ static void vty_out_dyn_ts_status(struct vty *vty, struct gsm_bts_trx_ts *ts) { enum gsm_phys_chan_config target; if (ts_is_pchan_switching(ts, &target)) { vty_out(vty, " switching %s -> %s", gsm_pchan_name(ts->pchan_is), gsm_pchan_name(target)); } else if (ts->pchan_is != ts->pchan_on_init) { vty_out(vty, " as %s", gsm_pchan_name(ts->pchan_is)); } } static void vty_out_dyn_ts_details(struct vty *vty, struct gsm_bts_trx_ts *ts) { /* show dyn TS details, if applicable */ switch (ts->pchan_on_init) { case GSM_PCHAN_OSMO_DYN: vty_out(vty, " Osmocom Dyn TS:"); vty_out_dyn_ts_status(vty, ts); vty_out(vty, VTY_NEWLINE); break; case GSM_PCHAN_TCH_F_PDCH: vty_out(vty, " IPACC Dyn PDCH TS:"); vty_out_dyn_ts_status(vty, ts); vty_out(vty, VTY_NEWLINE); break; default: /* no dyn ts */ break; } } static void meas_rep_dump_uni_vty(struct vty *vty, struct gsm_meas_rep_unidir *mru, const char *prefix, const char *dir) { vty_out(vty, "%s RXL-FULL-%s: %4d dBm, RXL-SUB-%s: %4d dBm ", prefix, dir, rxlev2dbm(mru->full.rx_lev), dir, rxlev2dbm(mru->sub.rx_lev)); vty_out(vty, "RXQ-FULL-%s: %d, RXQ-SUB-%s: %d%s", dir, mru->full.rx_qual, dir, mru->sub.rx_qual, VTY_NEWLINE); } static void meas_rep_dump_vty(struct vty *vty, struct gsm_meas_rep *mr, const char *prefix) { vty_out(vty, "%sMeasurement Report:%s", prefix, VTY_NEWLINE); vty_out(vty, "%s Flags: %s%s%s%s%s", prefix, mr->flags & MEAS_REP_F_UL_DTX ? "DTXu " : "", mr->flags & MEAS_REP_F_DL_DTX ? "DTXd " : "", mr->flags & MEAS_REP_F_FPC ? "FPC " : "", mr->flags & MEAS_REP_F_DL_VALID ? " " : "DLinval ", VTY_NEWLINE); if (mr->flags & MEAS_REP_F_MS_TO) vty_out(vty, "%s MS Timing Offset: %d%s", prefix, mr->ms_timing_offset, VTY_NEWLINE); if (mr->flags & MEAS_REP_F_MS_L1) vty_out(vty, "%s L1 MS Power: %u dBm, Timing Advance: %u%s", prefix, mr->ms_l1.pwr, mr->ms_l1.ta, VTY_NEWLINE); if (mr->flags & MEAS_REP_F_DL_VALID) meas_rep_dump_uni_vty(vty, &mr->dl, prefix, "dl"); meas_rep_dump_uni_vty(vty, &mr->ul, prefix, "ul"); } void lchan_dump_full_vty(struct vty *vty, struct gsm_lchan *lchan) { int idx; vty_out(vty, "BTS %u, TRX %u, Timeslot %u, Lchan %u: Type %s%s", lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr, lchan->nr, gsm_chan_t_name(lchan->type), VTY_NEWLINE); if (lchan->activate.concluded) { vty_out(vty, " Active for: %s seconds%s", osmo_int_to_float_str_c(OTC_SELECT, gsm_lchan_active_duration_ms(lchan), 3), VTY_NEWLINE); } vty_out_dyn_ts_details(vty, lchan->ts); vty_out(vty, " Connection: %u, State: %s%s%s%s", lchan->conn ? 1: 0, lchan_state_name(lchan), lchan->fi && lchan->fi->state == LCHAN_ST_BORKEN ? " Error reason: " : "", lchan->fi && lchan->fi->state == LCHAN_ST_BORKEN ? lchan->last_error : "", VTY_NEWLINE); vty_out(vty, " BS Power: %u dBm, MS Power: %u dBm%s", lchan->ts->trx->nominal_power - lchan->ts->trx->max_power_red - lchan->bs_power_db, ms_pwr_dbm(lchan->ts->trx->bts->band, lchan->ms_power), VTY_NEWLINE); vty_out(vty, " Interference Level: "); if (lchan->interf_dbm == INTERF_DBM_UNKNOWN) vty_out(vty, "unknown"); else vty_out(vty, "%d dBm (%u)", lchan->interf_dbm, lchan->interf_band); vty_out(vty, "%s", VTY_NEWLINE); vty_out(vty, " Channel Mode / Codec: %s%s", gsm48_chan_mode_name(lchan->current_ch_mode_rate.chan_mode), VTY_NEWLINE); if (!lchan_state_is(lchan, LCHAN_ST_UNUSED)) vty_out(vty, " Training Sequence: Set %d Code %u%s", (lchan->tsc_set > 0 ? lchan->tsc_set : 1), lchan->tsc, VTY_NEWLINE); if (lchan->vamos.enabled) vty_out(vty, " VAMOS: enabled%s", VTY_NEWLINE); if (lchan->conn && lchan->conn->bsub) { vty_out(vty, " Subscriber:%s", VTY_NEWLINE); bsc_subscr_dump_vty(vty, lchan->conn->bsub); } else { vty_out(vty, " No Subscriber%s", VTY_NEWLINE); } if (is_ipa_abisip_bts(lchan->ts->trx->bts)) { struct in_addr ia; if (lchan->abis_ip.bound_ip) { ia.s_addr = htonl(lchan->abis_ip.bound_ip); vty_out(vty, " Bound IP: %s Port %u CONN_ID=%u", inet_ntoa(ia), lchan->abis_ip.bound_port, lchan->abis_ip.conn_id); if (lchan->abis_ip.osmux.use) vty_out(vty, " Osmux_CID=%u%s", lchan->abis_ip.osmux.local_cid, VTY_NEWLINE); else vty_out(vty, " RTP_TYPE2=%u%s", lchan->abis_ip.rtp_payload2, VTY_NEWLINE); } if (lchan->abis_ip.connect_ip) { ia.s_addr = htonl(lchan->abis_ip.connect_ip); vty_out(vty, " Conn. IP: %s Port %u SPEECH_MODE=0x%02x", inet_ntoa(ia), lchan->abis_ip.connect_port, lchan->abis_ip.speech_mode); if (lchan->abis_ip.osmux.use) { if (lchan->abis_ip.osmux.remote_cid_present) vty_out(vty, " Osmux_CID=%u%s", lchan->abis_ip.osmux.remote_cid, VTY_NEWLINE); else vty_out(vty, " Osmux_CID=?%s", VTY_NEWLINE); } else { vty_out(vty, " RTP_TYPE=%u%s", lchan->abis_ip.rtp_payload, VTY_NEWLINE); } } } /* we want to report the last measurement report */ idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep), lchan->meas_rep_idx, 1); meas_rep_dump_vty(vty, &lchan->meas_rep[idx], " "); } void lchan_dump_short_vty(struct vty *vty, struct gsm_lchan *lchan) { struct gsm_meas_rep *mr; int idx; /* we want to report the last measurement report */ idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep), lchan->meas_rep_idx, 1); mr = &lchan->meas_rep[idx]; vty_out(vty, "BTS %u, TRX %u, Timeslot %u %s", lchan->ts->trx->bts->nr, lchan->ts->trx->nr, lchan->ts->nr, gsm_pchan_name(lchan->ts->pchan_on_init)); vty_out_dyn_ts_status(vty, lchan->ts); vty_out(vty, ", Lchan %u", lchan->nr); if (lchan_state_is(lchan, LCHAN_ST_UNUSED)) { vty_out(vty, ", Type %s, State %s - Interference Level: ", gsm_pchan_name(lchan->ts->pchan_is), lchan_state_name(lchan)); if (lchan->interf_dbm == INTERF_DBM_UNKNOWN) vty_out(vty, "unknown"); else vty_out(vty, "%d dBm (%u)", lchan->interf_dbm, lchan->interf_band); vty_out(vty, "%s", VTY_NEWLINE); return; } vty_out(vty, ", Type %s%s TSC-s%dc%u, State %s - L1 MS Power: %u dBm RXL-FULL-dl: %4d dBm RXL-FULL-ul: %4d dBm%s", gsm_chan_t_name(lchan->type), lchan->vamos.enabled ? " (VAMOS)" : "", lchan->tsc_set > 0 ? lchan->tsc_set : 1, lchan->tsc, lchan_state_name(lchan), mr->ms_l1.pwr, rxlev2dbm(mr->dl.full.rx_lev), rxlev2dbm(mr->ul.full.rx_lev), VTY_NEWLINE); } void ts_dump_vty(struct vty *vty, struct gsm_bts_trx_ts *ts) { vty_out(vty, "BTS %u, TRX %u, Timeslot %u, phys cfg %s (active %s)", ts->trx->bts->nr, ts->trx->nr, ts->nr, gsm_pchan_name(ts->pchan_on_init), gsm_pchan_name(ts->pchan_is)); if (ts->pchan_is != ts->pchan_on_init) vty_out(vty, " (%s mode)", gsm_pchan_name(ts->pchan_is)); vty_out(vty, ", TSC %u%s NM State: ", gsm_ts_tsc(ts), VTY_NEWLINE); vty_out_dyn_ts_details(vty, ts); net_dump_nmstate(vty, &ts->mo.nm_state); if (!is_ipa_abisip_bts(ts->trx->bts)) vty_out(vty, " E1 Line %u, Timeslot %u, Subslot %u%s", ts->e1_link.e1_nr, ts->e1_link.e1_ts, ts->e1_link.e1_ts_ss, VTY_NEWLINE); } void e1isl_dump_vty(struct vty *vty, struct e1inp_sign_link *e1l) { struct e1inp_line *line; if (!e1l) { vty_out(vty, " None%s", VTY_NEWLINE); return; } line = e1l->ts->line; vty_out(vty, " E1 Line %u, Type %s: Timeslot %u, Mode %s%s", line->num, line->driver->name, e1l->ts->num, e1inp_signtype_name(e1l->type), VTY_NEWLINE); vty_out(vty, " E1 TEI %u, SAPI %u%s", e1l->tei, e1l->sapi, VTY_NEWLINE); } /*! Dump the IP addresses and ports of the input signal link's timeslot. * This only makes sense for links connected with ipaccess. * Example output: "(r=10.1.42.1:55416<->l=10.1.42.123:3003)" */ void e1isl_dump_vty_tcp(struct vty *vty, const struct e1inp_sign_link *e1l) { if (e1l) { char *name = osmo_sock_get_name(NULL, e1l->ts->driver.ipaccess.fd.fd); vty_out(vty, "%s", name); talloc_free(name); } vty_out(vty, "%s", VTY_NEWLINE); } void trx_dump_vty(struct vty *vty, struct gsm_bts_trx *trx, bool print_rsl, bool show_connected) { if (show_connected && !trx->rsl_link_primary) return; if (!show_connected && trx->rsl_link_primary) return; vty_out(vty, "TRX %u of BTS %u is on ARFCN %u%s", trx->nr, trx->bts->nr, trx->arfcn, VTY_NEWLINE); vty_out(vty, " RF Nominal Power: %d dBm, reduced by %u dB, " "resulting BS power: %d dBm%s", trx->nominal_power, trx->max_power_red, trx->nominal_power - trx->max_power_red, VTY_NEWLINE); vty_out(vty, " Radio Carrier NM State: "); net_dump_nmstate(vty, &trx->mo.nm_state); if (print_rsl) vty_out(vty, " RSL State: %s%s", trx->rsl_link_primary? "connected" : "disconnected", VTY_NEWLINE); vty_out(vty, " %sBaseband Transceiver NM State: ", is_ericsson_bts(trx->bts) ? "[Virtual] " : ""); net_dump_nmstate(vty, &trx->bb_transc.mo.nm_state); if (is_ipa_abisip_bts(trx->bts)) { vty_out(vty, " IPA Abis/IP stream ID: 0x%02x ", trx->rsl_tei_primary); e1isl_dump_vty_tcp(vty, trx->rsl_link_primary); } else { vty_out(vty, " E1 Signalling Link:%s", VTY_NEWLINE); e1isl_dump_vty(vty, trx->rsl_link_primary); } const struct load_counter *ll = &trx->lchan_load; vty_out(vty, " Channel load: %u%%%s", ll->total ? ll->used * 100 / ll->total : 0, VTY_NEWLINE); } void config_write_e1_link(struct vty *vty, struct gsm_e1_subslot *e1_link, const char *prefix) { if (!e1_link->e1_ts) return; if (e1_link->e1_ts_ss == E1_SUBSLOT_FULL) vty_out(vty, "%se1 line %u timeslot %u sub-slot full%s", prefix, e1_link->e1_nr, e1_link->e1_ts, VTY_NEWLINE); else vty_out(vty, "%se1 line %u timeslot %u sub-slot %u%s", prefix, e1_link->e1_nr, e1_link->e1_ts, e1_link->e1_ts_ss, VTY_NEWLINE); } static void config_write_ts_single(struct vty *vty, struct gsm_bts_trx_ts *ts) { vty_out(vty, " timeslot %u%s", ts->nr, VTY_NEWLINE); if (ts->tsc != -1) vty_out(vty, " training_sequence_code %u%s", ts->tsc, VTY_NEWLINE); if (ts->pchan_from_config != GSM_PCHAN_NONE) vty_out(vty, " phys_chan_config %s%s", gsm_pchan_name(ts->pchan_from_config), VTY_NEWLINE); vty_out(vty, " hopping enabled %u%s", ts->hopping.enabled, VTY_NEWLINE); if (ts->hopping.enabled) { unsigned int i; vty_out(vty, " hopping sequence-number %u%s", ts->hopping.hsn, VTY_NEWLINE); vty_out(vty, " hopping maio %u%s", ts->hopping.maio, VTY_NEWLINE); for (i = 0; i < ts->hopping.arfcns.data_len*8; i++) { if (!bitvec_get_bit_pos(&ts->hopping.arfcns, i)) continue; vty_out(vty, " hopping arfcn add %u%s", i, VTY_NEWLINE); } } config_write_e1_link(vty, &ts->e1_link, " "); if (ts->trx->bts->model->config_write_ts) ts->trx->bts->model->config_write_ts(vty, ts); } void config_write_trx_single(struct vty *vty, struct gsm_bts_trx *trx) { int i; vty_out(vty, " trx %u%s", trx->nr, VTY_NEWLINE); vty_out(vty, " rf_locked %u%s", trx->mo.force_rf_lock ? 1 : 0, VTY_NEWLINE); vty_out(vty, " arfcn %u%s", trx->arfcn, VTY_NEWLINE); vty_out(vty, " nominal power %u%s", trx->nominal_power, VTY_NEWLINE); vty_out(vty, " max_power_red %u%s", trx->max_power_red, VTY_NEWLINE); config_write_e1_link(vty, &trx->rsl_e1_link, " rsl "); vty_out(vty, " rsl e1 tei %u%s", trx->rsl_tei_primary, VTY_NEWLINE); if (trx->bts->model->config_write_trx) trx->bts->model->config_write_trx(vty, trx); for (i = 0; i < TRX_NR_TS; i++) config_write_ts_single(vty, &trx->ts[i]); } int bts_trx_vty_init(void) { cfg_ts_pchan_cmd.string = vty_cmd_string_from_valstr(tall_bsc_ctx, gsm_pchant_names, "phys_chan_config (", "|", ")", VTY_DO_LOWER); cfg_ts_pchan_cmd.doc = vty_cmd_string_from_valstr(tall_bsc_ctx, gsm_pchant_descs, "Physical Channel Combination\n", "\n", "", 0); install_element(BTS_NODE, &cfg_trx_cmd); install_node(&trx_node, dummy_config_write); install_element(TRX_NODE, &cfg_trx_arfcn_cmd); install_element(TRX_NODE, &cfg_description_cmd); install_element(TRX_NODE, &cfg_no_description_cmd); install_element(TRX_NODE, &cfg_trx_nominal_power_cmd); install_element(TRX_NODE, &cfg_trx_max_power_red_cmd); install_element(TRX_NODE, &cfg_trx_rsl_e1_cmd); install_element(TRX_NODE, &cfg_trx_rsl_e1_tei_cmd); install_element(TRX_NODE, &cfg_trx_rf_locked_cmd); install_element(TRX_NODE, &cfg_ts_cmd); install_node(&ts_node, dummy_config_write); install_element(TS_NODE, &cfg_ts_pchan_cmd); install_element(TS_NODE, &cfg_ts_pchan_compat_cmd); install_element(TS_NODE, &cfg_ts_tsc_cmd); install_element(TS_NODE, &cfg_ts_hopping_cmd); install_element(TS_NODE, &cfg_ts_hsn_cmd); install_element(TS_NODE, &cfg_ts_maio_cmd); install_element(TS_NODE, &cfg_ts_arfcn_add_cmd); install_element(TS_NODE, &cfg_ts_arfcn_del_cmd); install_element(TS_NODE, &cfg_ts_arfcn_del_all_cmd); install_element(TS_NODE, &cfg_ts_e1_subslot_cmd); return 0; }