diff options
Diffstat (limited to 'openbsc/src/libbsc')
39 files changed, 0 insertions, 26620 deletions
diff --git a/openbsc/src/libbsc/Makefile.am b/openbsc/src/libbsc/Makefile.am deleted file mode 100644 index e78bde624..000000000 --- a/openbsc/src/libbsc/Makefile.am +++ /dev/null @@ -1,57 +0,0 @@ -AM_CPPFLAGS = \ - $(all_includes) \ - -I$(top_srcdir)/include \ - -I$(top_builddir) \ - $(NULL) - -AM_CFLAGS = \ - -Wall \ - $(LIBOSMOCORE_CFLAGS) \ - $(LIBOSMOGSM_CFLAGS) \ - $(LIBOSMOVTY_CFLAGS) \ - $(LIBOSMOABIS_CFLAGS) \ - $(COVERAGE_CFLAGS) \ - $(NULL) - -noinst_LIBRARIES = \ - libbsc.a \ - $(NULL) - -libbsc_a_SOURCES = \ - abis_nm.c \ - abis_nm_vty.c \ - abis_om2000.c \ - abis_om2000_vty.c \ - abis_rsl.c \ - bsc_rll.c \ - bsc_subscriber.c \ - paging.c \ - bts_ericsson_rbs2000.c \ - bts_ipaccess_nanobts.c \ - bts_siemens_bs11.c \ - bts_nokia_site.c \ - bts_unknown.c \ - bts_sysmobts.c \ - chan_alloc.c \ - handover_decision.c \ - handover_logic.c \ - meas_rep.c \ - pcu_sock.c \ - rest_octets.c \ - system_information.c \ - e1_config.c \ - bsc_api.c \ - bsc_msc.c bsc_vty.c \ - gsm_04_08_utils.c \ - gsm_04_80_utils.c \ - bsc_init.c \ - bts_init.c \ - bsc_rf_ctrl.c \ - arfcn_range_encode.c \ - bsc_ctrl_commands.c \ - bsc_ctrl_lookup.c \ - net_init.c \ - bsc_dyn_ts.c \ - bts_ipaccess_nanobts_omlattr.c \ - $(NULL) - diff --git a/openbsc/src/libbsc/abis_nm.c b/openbsc/src/libbsc/abis_nm.c deleted file mode 100644 index 019d03952..000000000 --- a/openbsc/src/libbsc/abis_nm.c +++ /dev/null @@ -1,2917 +0,0 @@ -/* GSM Network Management (OML) messages on the A-bis interface - * 3GPP TS 12.21 version 8.0.0 Release 1999 / ETSI TS 100 623 V8.0.0 */ - -/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org> - * - * 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 <http://www.gnu.org/licenses/>. - * - */ - - -#include <errno.h> -#include <unistd.h> -#include <stdio.h> -#include <fcntl.h> -#include <stdlib.h> -#include <libgen.h> -#include <time.h> -#include <limits.h> - -#include <sys/stat.h> -#include <netinet/in.h> -#include <arpa/inet.h> - -#include <openbsc/gsm_data.h> -#include <openbsc/debug.h> -#include <osmocom/core/msgb.h> -#include <osmocom/gsm/protocol/gsm_12_21.h> -#include <osmocom/gsm/tlv.h> -#include <osmocom/gsm/abis_nm.h> -#include <osmocom/core/talloc.h> -#include <osmocom/core/utils.h> -#include <openbsc/abis_nm.h> -#include <openbsc/misdn.h> -#include <openbsc/signal.h> -#include <osmocom/abis/e1_input.h> - -#define OM_ALLOC_SIZE 1024 -#define OM_HEADROOM_SIZE 128 -#define IPACC_SEGMENT_SIZE 245 - -int abis_nm_tlv_parse(struct tlv_parsed *tp, struct gsm_bts *bts, const uint8_t *buf, int len) -{ - if (!bts->model) - return -EIO; - return tlv_parse(tp, &bts->model->nm_att_tlvdef, buf, len, 0, 0); -} - -static int is_in_arr(enum abis_nm_msgtype mt, const enum abis_nm_msgtype *arr, int size) -{ - int i; - - for (i = 0; i < size; i++) { - if (arr[i] == mt) - return 1; - } - - return 0; -} - -#if 0 -/* is this msgtype the usual ACK/NACK type ? */ -static int is_ack_nack(enum abis_nm_msgtype mt) -{ - return !is_in_arr(mt, no_ack_nack, ARRAY_SIZE(no_ack_nack)); -} -#endif - -/* is this msgtype a report ? */ -static int is_report(enum abis_nm_msgtype mt) -{ - return is_in_arr(mt, abis_nm_reports, ARRAY_SIZE(abis_nm_reports)); -} - -#define MT_ACK(x) (x+1) -#define MT_NACK(x) (x+2) - -static void fill_om_hdr(struct abis_om_hdr *oh, uint8_t len) -{ - oh->mdisc = ABIS_OM_MDISC_FOM; - oh->placement = ABIS_OM_PLACEMENT_ONLY; - oh->sequence = 0; - oh->length = len; -} - -static struct abis_om_fom_hdr *fill_om_fom_hdr(struct abis_om_hdr *oh, uint8_t len, - uint8_t msg_type, uint8_t obj_class, - uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr) -{ - struct abis_om_fom_hdr *foh = - (struct abis_om_fom_hdr *) oh->data; - - fill_om_hdr(oh, len+sizeof(*foh)); - foh->msg_type = msg_type; - foh->obj_class = obj_class; - foh->obj_inst.bts_nr = bts_nr; - foh->obj_inst.trx_nr = trx_nr; - foh->obj_inst.ts_nr = ts_nr; - return foh; -} - -static struct msgb *nm_msgb_alloc(void) -{ - return msgb_alloc_headroom(OM_ALLOC_SIZE, OM_HEADROOM_SIZE, - "OML"); -} - -int _abis_nm_sendmsg(struct msgb *msg) -{ - msg->l2h = msg->data; - - if (!msg->dst) { - LOGP(DNM, LOGL_ERROR, "%s: msg->dst == NULL\n", __func__); - return -EINVAL; - } - - return abis_sendmsg(msg); -} - -/* Send a OML NM Message from BSC to BTS */ -static int abis_nm_queue_msg(struct gsm_bts *bts, struct msgb *msg) -{ - msg->dst = bts->oml_link; - - /* queue OML messages */ - if (llist_empty(&bts->abis_queue) && !bts->abis_nm_pend) { - bts->abis_nm_pend = OBSC_NM_W_ACK_CB(msg); - return _abis_nm_sendmsg(msg); - } else { - msgb_enqueue(&bts->abis_queue, msg); - return 0; - } - -} - -int abis_nm_sendmsg(struct gsm_bts *bts, struct msgb *msg) -{ - OBSC_NM_W_ACK_CB(msg) = 1; - return abis_nm_queue_msg(bts, msg); -} - -static int abis_nm_sendmsg_direct(struct gsm_bts *bts, struct msgb *msg) -{ - OBSC_NM_W_ACK_CB(msg) = 0; - return abis_nm_queue_msg(bts, msg); -} - -static int abis_nm_rcvmsg_sw(struct msgb *mb); - -int nm_is_running(struct gsm_nm_state *s) { - return (s->operational == NM_OPSTATE_ENABLED) && ( - (s->availability == NM_AVSTATE_OK) || - (s->availability == 0xff) - ); -} - -/* Update the administrative state of a given object in our in-memory data - * structures and send an event to the higher layer */ -static int update_admstate(struct gsm_bts *bts, uint8_t obj_class, - struct abis_om_obj_inst *obj_inst, uint8_t adm_state) -{ - struct gsm_nm_state *nm_state, new_state; - struct nm_statechg_signal_data nsd; - - memset(&nsd, 0, sizeof(nsd)); - - nsd.obj = gsm_objclass2obj(bts, obj_class, obj_inst); - if (!nsd.obj) - return -EINVAL; - nm_state = gsm_objclass2nmstate(bts, obj_class, obj_inst); - if (!nm_state) - return -1; - - new_state = *nm_state; - new_state.administrative = adm_state; - - nsd.bts = bts; - nsd.obj_class = obj_class; - nsd.old_state = nm_state; - nsd.new_state = &new_state; - nsd.obj_inst = obj_inst; - osmo_signal_dispatch(SS_NM, S_NM_STATECHG_ADM, &nsd); - - nm_state->administrative = adm_state; - - return 0; -} - -static int abis_nm_rx_statechg_rep(struct msgb *mb) -{ - struct abis_om_hdr *oh = msgb_l2(mb); - struct abis_om_fom_hdr *foh = msgb_l3(mb); - struct e1inp_sign_link *sign_link = mb->dst; - struct gsm_bts *bts = sign_link->trx->bts; - struct tlv_parsed tp; - struct gsm_nm_state *nm_state, new_state; - - DEBUGPC(DNM, "STATE CHG: "); - - memset(&new_state, 0, sizeof(new_state)); - - nm_state = gsm_objclass2nmstate(bts, foh->obj_class, &foh->obj_inst); - if (!nm_state) { - DEBUGPC(DNM, "unknown object class\n"); - return -EINVAL; - } - - new_state = *nm_state; - - abis_nm_tlv_parse(&tp, bts, foh->data, oh->length-sizeof(*foh)); - if (TLVP_PRESENT(&tp, NM_ATT_OPER_STATE)) { - new_state.operational = *TLVP_VAL(&tp, NM_ATT_OPER_STATE); - DEBUGPC(DNM, "OP_STATE=%s ", - abis_nm_opstate_name(new_state.operational)); - } - if (TLVP_PRESENT(&tp, NM_ATT_AVAIL_STATUS)) { - if (TLVP_LEN(&tp, NM_ATT_AVAIL_STATUS) == 0) - new_state.availability = 0xff; - else - new_state.availability = *TLVP_VAL(&tp, NM_ATT_AVAIL_STATUS); - DEBUGPC(DNM, "AVAIL=%s(%02x) ", - abis_nm_avail_name(new_state.availability), - new_state.availability); - } else - new_state.availability = 0xff; - if (TLVP_PRESENT(&tp, NM_ATT_ADM_STATE)) { - new_state.administrative = *TLVP_VAL(&tp, NM_ATT_ADM_STATE); - DEBUGPC(DNM, "ADM=%2s ", - get_value_string(abis_nm_adm_state_names, - new_state.administrative)); - } - DEBUGPC(DNM, "\n"); - - if ((new_state.administrative != 0 && nm_state->administrative == 0) || - new_state.operational != nm_state->operational || - new_state.availability != nm_state->availability) { - /* Update the operational state of a given object in our in-memory data - * structures and send an event to the higher layer */ - struct nm_statechg_signal_data nsd; - nsd.obj = gsm_objclass2obj(bts, foh->obj_class, &foh->obj_inst); - nsd.obj_class = foh->obj_class; - nsd.old_state = nm_state; - nsd.new_state = &new_state; - nsd.obj_inst = &foh->obj_inst; - nsd.bts = bts; - osmo_signal_dispatch(SS_NM, S_NM_STATECHG_OPER, &nsd); - nm_state->operational = new_state.operational; - nm_state->availability = new_state.availability; - if (nm_state->administrative == 0) - nm_state->administrative = new_state.administrative; - } -#if 0 - if (op_state == 1) { - /* try to enable objects that are disabled */ - abis_nm_opstart(bts, foh->obj_class, - foh->obj_inst.bts_nr, - foh->obj_inst.trx_nr, - foh->obj_inst.ts_nr); - } -#endif - return 0; -} - -static inline void log_oml_fail_rep(const struct gsm_bts *bts, const char *type, - const char *severity, const uint8_t *p_val, - const char *text) -{ - enum abis_nm_pcause_type pcause = p_val[0]; - enum abis_mm_event_causes cause = osmo_load16be(p_val + 1); - - LOGPC(DNM, LOGL_ERROR, "BTS %u: Failure Event Report: ", bts->nr); - if (type) - LOGPC(DNM, LOGL_ERROR, "Type=%s, ", type); - if (severity) - LOGPC(DNM, LOGL_ERROR, "Severity=%s, ", severity); - - LOGPC(DNM, LOGL_ERROR, "Probable cause=%s: ", - get_value_string(abis_nm_pcause_type_names, pcause)); - - if (pcause == NM_PCAUSE_T_MANUF) - LOGPC(DNM, LOGL_ERROR, "%s, ", - get_value_string(abis_mm_event_cause_names, cause)); - else - LOGPC(DNM, LOGL_ERROR, "%02X %02X ", p_val[1], p_val[2]); - - if (text) { - LOGPC(DNM, LOGL_ERROR, "Additional Text=%s. ", text); - } - - LOGPC(DNM, LOGL_ERROR, "\n"); -} - -static inline void handle_manufact_report(struct gsm_bts *bts, const uint8_t *p_val, const char *type, - const char *severity, const char *text) -{ - enum abis_mm_event_causes cause = osmo_load16be(p_val + 1); - - switch (cause) { - case OSMO_EVT_PCU_VERS: - if (text) { - LOGPC(DNM, LOGL_NOTICE, "BTS %u reported connected PCU version %s\n", bts->nr, text); - osmo_strlcpy(bts->pcu_version, text, sizeof(bts->pcu_version)); - } else { - LOGPC(DNM, LOGL_ERROR, "BTS %u reported PCU disconnection.\n", bts->nr); - bts->pcu_version[0] = '\0'; - } - break; - default: - log_oml_fail_rep(bts, type, severity, p_val, text); - }; -} - -static int rx_fail_evt_rep(struct msgb *mb, struct gsm_bts *bts) -{ - struct abis_om_hdr *oh = msgb_l2(mb); - struct abis_om_fom_hdr *foh = msgb_l3(mb); - struct e1inp_sign_link *sign_link = mb->dst; - struct tlv_parsed tp; - int rc = 0; - const uint8_t *p_val = NULL; - char *p_text = NULL; - const char *e_type = NULL, *severity = NULL; - - abis_nm_tlv_parse(&tp, sign_link->trx->bts, foh->data, - oh->length-sizeof(*foh)); - - if (TLVP_PRESENT(&tp, NM_ATT_ADD_TEXT)) { - p_val = TLVP_VAL(&tp, NM_ATT_ADD_TEXT); - p_text = talloc_strndup(tall_bsc_ctx, (const char *) p_val, - TLVP_LEN(&tp, NM_ATT_ADD_TEXT)); - } - - if (TLVP_PRESENT(&tp, NM_ATT_EVENT_TYPE)) - e_type = abis_nm_event_type_name(*TLVP_VAL(&tp, - NM_ATT_EVENT_TYPE)); - - if (TLVP_PRESENT(&tp, NM_ATT_SEVERITY)) - severity = abis_nm_severity_name(*TLVP_VAL(&tp, - NM_ATT_SEVERITY)); - - if (TLVP_PRESENT(&tp, NM_ATT_PROB_CAUSE)) { - p_val = TLVP_VAL(&tp, NM_ATT_PROB_CAUSE); - - switch (p_val[0]) { - case NM_PCAUSE_T_MANUF: - handle_manufact_report(bts, p_val, e_type, severity, - p_text); - break; - default: - log_oml_fail_rep(bts, e_type, severity, p_val, p_text); - }; - } else { - LOGPC(DNM, LOGL_ERROR, "BTS%u: Failure Event Report without " - "Probable Cause?!\n", bts->nr); - rc = -EINVAL; - } - - if (p_text) - talloc_free(p_text); - - return rc; -} - -static int abis_nm_rcvmsg_report(struct msgb *mb, struct gsm_bts *bts) -{ - struct abis_om_fom_hdr *foh = msgb_l3(mb); - uint8_t mt = foh->msg_type; - - abis_nm_debugp_foh(DNM, foh); - - //nmh->cfg->report_cb(mb, foh); - - switch (mt) { - case NM_MT_STATECHG_EVENT_REP: - return abis_nm_rx_statechg_rep(mb); - break; - case NM_MT_SW_ACTIVATED_REP: - DEBUGPC(DNM, "Software Activated Report\n"); - osmo_signal_dispatch(SS_NM, S_NM_SW_ACTIV_REP, mb); - break; - case NM_MT_FAILURE_EVENT_REP: - rx_fail_evt_rep(mb, bts); - osmo_signal_dispatch(SS_NM, S_NM_FAIL_REP, mb); - break; - case NM_MT_TEST_REP: - DEBUGPC(DNM, "Test Report\n"); - osmo_signal_dispatch(SS_NM, S_NM_TEST_REP, mb); - break; - default: - DEBUGPC(DNM, "reporting NM MT 0x%02x\n", mt); - break; - - }; - - return 0; -} - -/* Activate the specified software into the BTS */ -static int ipacc_sw_activate(struct gsm_bts *bts, uint8_t obj_class, uint8_t i0, uint8_t i1, - uint8_t i2, const struct abis_nm_sw_desc *sw_desc) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - uint16_t len = abis_nm_sw_desc_len(sw_desc, true); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, len, NM_MT_ACTIVATE_SW, obj_class, i0, i1, i2); - abis_nm_put_sw_desc(msg, sw_desc, true); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_select_newest_sw(const struct abis_nm_sw_desc *sw_descr, - const size_t size) -{ - int res = 0; - int i; - - for (i = 1; i < size; ++i) { - if (memcmp(sw_descr[res].file_version, sw_descr[i].file_version, - OSMO_MIN(sw_descr[i].file_version_len, - sw_descr[res].file_version_len)) < 0) { - res = i; - } - } - - return res; -} - -static inline bool handle_attr(const struct gsm_bts *bts, enum bts_attribute id, uint8_t *val, uint8_t len) -{ - switch (id) { - case BTS_TYPE_VARIANT: - LOGP(DNM, LOGL_NOTICE, "BTS%u reported variant: %s\n", bts->nr, val); - break; - case BTS_SUB_MODEL: - LOGP(DNM, LOGL_NOTICE, "BTS%u reported submodel: %s\n", bts->nr, val); - break; - default: - return false; - } - return true; -} - -/* Parse Attribute Response Info - return pointer to the actual content */ -static inline uint8_t *parse_attr_resp_info_unreported(uint8_t bts_nr, uint8_t *ari, uint16_t ari_len, uint16_t *out_len) -{ - uint8_t num_unreported = ari[0], i; - - DEBUGP(DNM, "BTS%u Get Attributes Response Info: %u bytes total with %u unreported attributes\n", - bts_nr, ari_len, num_unreported); - - /* +1 because we have to account for number of unreported attributes, prefixing the list: */ - for (i = 0; i < num_unreported; i++) - LOGP(DNM, LOGL_ERROR, "BTS%u Attribute %s is unreported\n", - bts_nr, get_value_string(abis_nm_att_names, ari[i + 1])); - - /* the data starts right after the list of unreported attributes + space for length of that list */ - *out_len = ari_len - (num_unreported + 2); - - return ari + num_unreported + 1; /* we have to account for 1st byte with number of unreported attributes */ -} - -/* Parse Attribute Response Info content for 3GPP TS 52.021 §9.4.30 Manufacturer Id */ -static inline uint8_t *parse_attr_resp_info_manuf_id(struct gsm_bts *bts, uint8_t *data, uint16_t *data_len) -{ - struct tlv_parsed tp; - uint16_t m_id_len = 0; - uint8_t adjust = 0, i; - - abis_nm_tlv_parse(&tp, bts, data, *data_len); - if (TLVP_PRES_LEN(&tp, NM_ATT_MANUF_ID, 2)) { - m_id_len = TLVP_LEN(&tp, NM_ATT_MANUF_ID); - - /* log potential BTS feature vector overflow */ - if (m_id_len > sizeof(bts->_features_data)) - LOGP(DNM, LOGL_NOTICE, "BTS%u Get Attributes Response: feature vector is truncated to %u bytes\n", - bts->nr, MAX_BTS_FEATURES/8); - - /* check that max. expected BTS attribute is above given feature vector length */ - if (m_id_len > OSMO_BYTES_FOR_BITS(_NUM_BTS_FEAT)) - LOGP(DNM, LOGL_NOTICE, "BTS%u Get Attributes Response: reported unexpectedly long (%u bytes) " - "feature vector - most likely it was compiled against newer BSC headers. " - "Consider upgrading your BSC to later version.\n", - bts->nr, m_id_len); - - memcpy(bts->_features_data, TLVP_VAL(&tp, NM_ATT_MANUF_ID), sizeof(bts->_features_data)); - adjust = m_id_len + 3; /* adjust for parsed TL16V struct */ - - for (i = 0; i < _NUM_BTS_FEAT; i++) - if (gsm_bts_has_feature(bts, i) != gsm_btsmodel_has_feature(bts->model, i)) - LOGP(DNM, LOGL_NOTICE, "BTS%u feature '%s' reported via OML does not match statically " - "set feature: %u != %u. Please fix.\n", bts->nr, - get_value_string(gsm_bts_features_descs, i), - gsm_bts_has_feature(bts, i), gsm_btsmodel_has_feature(bts->model, i)); - } - - *data_len -= adjust; - - return data + adjust; -} - -/* Parse Attribute Response Info content for 3GPP TS 52.021 §9.4.28 Manufacturer Dependent State */ -static inline uint8_t *parse_attr_resp_info_manuf_state(const struct gsm_bts_trx *trx, uint8_t *data, uint16_t *data_len) -{ - struct tlv_parsed tp; - const uint8_t *power; - uint8_t adjust = 0; - - if (!trx) /* this attribute does not make sense on BTS level, only on TRX level */ - return data; - - abis_nm_tlv_parse(&tp, trx->bts, data, *data_len); - if (TLVP_PRES_LEN(&tp, NM_ATT_MANUF_STATE, 1)) { - power = TLVP_VAL(&tp, NM_ATT_MANUF_STATE); - LOGP(DNM, LOGL_NOTICE, "%s Get Attributes Response: nominal power is %u\n", gsm_trx_name(trx), *power); - adjust = 2; /* adjust for parsed TV struct */ - } - - *data_len -= adjust; - - return data + adjust; -} - -/* Handle 3GPP TS 52.021 §9.4.64 Get Attribute Response Info */ -static int abis_nm_rx_get_attr_resp(struct msgb *mb, const struct gsm_bts_trx *trx) -{ - struct abis_om_hdr *oh = msgb_l2(mb); - struct abis_om_fom_hdr *foh = msgb_l3(mb); - struct e1inp_sign_link *sign_link = mb->dst; - struct gsm_bts *bts = trx ? trx->bts : sign_link->trx->bts; - struct tlv_parsed tp; - uint8_t *data, i; - uint16_t data_len; - int rc; - struct abis_nm_sw_desc sw_descr[MAX_BTS_ATTR]; - - abis_nm_debugp_foh(DNM, foh); - - DEBUGPC(DNM, "Get Attributes Response for BTS%u\n", bts->nr); - - abis_nm_tlv_parse(&tp, bts, foh->data, oh->length-sizeof(*foh)); - if (!TLVP_PRES_LEN(&tp, NM_ATT_GET_ARI, 1)) { - LOGP(DNM, LOGL_ERROR, "BTS%u: Get Attributes Response without Response Info?!\n", bts->nr); - return -EINVAL; - } - - data = parse_attr_resp_info_unreported(bts->nr, TLVP_VAL(&tp, NM_ATT_GET_ARI), TLVP_LEN(&tp, NM_ATT_GET_ARI), - &data_len); - - data = parse_attr_resp_info_manuf_state(trx, data, &data_len); - data = parse_attr_resp_info_manuf_id(bts, data, &data_len); - - /* after parsing manufacturer-specific attributes there's list of replies in form of sw-conf structure: */ - rc = abis_nm_get_sw_conf(data, data_len, &sw_descr[0], ARRAY_SIZE(sw_descr)); - if (rc > 0) { - for (i = 0; i < rc; i++) { - if (!handle_attr(bts, str2btsattr((const char *)sw_descr[i].file_id), - sw_descr[i].file_version, sw_descr[i].file_version_len)) - LOGP(DNM, LOGL_NOTICE, "BTS%u: ARI reported sw[%d/%d]: %s is %s\n", - bts->nr, i, rc, sw_descr[i].file_id, sw_descr[i].file_version); - } - } else - LOGP(DNM, LOGL_ERROR, "BTS%u: failed to parse SW-Config part of Get Attribute Response Info: %s\n", - bts->nr, strerror(-rc)); - - return 0; -} - -/* 3GPP TS 52.021 §6.2.5 */ -static int abis_nm_rx_sw_act_req(struct msgb *mb) -{ - struct abis_om_hdr *oh = msgb_l2(mb); - struct abis_om_fom_hdr *foh = msgb_l3(mb); - struct e1inp_sign_link *sign_link = mb->dst; - struct tlv_parsed tp; - const uint8_t *sw_config; - int ret, sw_config_len, len; - struct abis_nm_sw_desc sw_descr[MAX_BTS_ATTR]; - - abis_nm_debugp_foh(DNM, foh); - - DEBUGPC(DNM, "SW Activate Request: "); - - DEBUGP(DNM, "Software Activate Request, ACKing and Activating\n"); - - ret = abis_nm_sw_act_req_ack(sign_link->trx->bts, foh->obj_class, - foh->obj_inst.bts_nr, - foh->obj_inst.trx_nr, - foh->obj_inst.ts_nr, 0, - foh->data, oh->length-sizeof(*foh)); - if (ret != 0) { - LOGP(DNM, LOGL_ERROR, - "Sending SW ActReq ACK failed: %d\n", ret); - return ret; - } - - abis_nm_tlv_parse(&tp, sign_link->trx->bts, foh->data, oh->length-sizeof(*foh)); - sw_config = TLVP_VAL(&tp, NM_ATT_SW_CONFIG); - sw_config_len = TLVP_LEN(&tp, NM_ATT_SW_CONFIG); - if (!TLVP_PRESENT(&tp, NM_ATT_SW_CONFIG)) { - LOGP(DNM, LOGL_ERROR, - "SW config not found! Can't continue.\n"); - return -EINVAL; - } else { - DEBUGP(DNM, "Found SW config: %s\n", osmo_hexdump(sw_config, sw_config_len)); - } - - /* Parse up to two sw descriptions from the data */ - len = abis_nm_get_sw_conf(sw_config, sw_config_len, &sw_descr[0], - ARRAY_SIZE(sw_descr)); - if (len <= 0) { - LOGP(DNM, LOGL_ERROR, "Failed to parse SW Config.\n"); - return -EINVAL; - } - - ret = abis_nm_select_newest_sw(&sw_descr[0], len); - DEBUGP(DNM, "Selected sw description %d of %d\n", ret, len); - - return ipacc_sw_activate(sign_link->trx->bts, foh->obj_class, - foh->obj_inst.bts_nr, - foh->obj_inst.trx_nr, - foh->obj_inst.ts_nr, - &sw_descr[ret]); -} - -/* Receive a CHANGE_ADM_STATE_ACK, parse the TLV and update local state */ -static int abis_nm_rx_chg_adm_state_ack(struct msgb *mb) -{ - struct abis_om_hdr *oh = msgb_l2(mb); - struct abis_om_fom_hdr *foh = msgb_l3(mb); - struct e1inp_sign_link *sign_link = mb->dst; - struct tlv_parsed tp; - uint8_t adm_state; - - abis_nm_tlv_parse(&tp, sign_link->trx->bts, foh->data, oh->length-sizeof(*foh)); - if (!TLVP_PRESENT(&tp, NM_ATT_ADM_STATE)) - return -EINVAL; - - adm_state = *TLVP_VAL(&tp, NM_ATT_ADM_STATE); - - return update_admstate(sign_link->trx->bts, foh->obj_class, &foh->obj_inst, adm_state); -} - -static int abis_nm_rx_lmt_event(struct msgb *mb) -{ - struct abis_om_hdr *oh = msgb_l2(mb); - struct abis_om_fom_hdr *foh = msgb_l3(mb); - struct e1inp_sign_link *sign_link = mb->dst; - struct tlv_parsed tp; - - DEBUGP(DNM, "LMT Event "); - abis_nm_tlv_parse(&tp, sign_link->trx->bts, foh->data, oh->length-sizeof(*foh)); - if (TLVP_PRESENT(&tp, NM_ATT_BS11_LMT_LOGON_SESSION) && - TLVP_LEN(&tp, NM_ATT_BS11_LMT_LOGON_SESSION) >= 1) { - uint8_t onoff = *TLVP_VAL(&tp, NM_ATT_BS11_LMT_LOGON_SESSION); - DEBUGPC(DNM, "LOG%s ", onoff ? "ON" : "OFF"); - } - if (TLVP_PRESENT(&tp, NM_ATT_BS11_LMT_USER_ACC_LEV) && - TLVP_LEN(&tp, NM_ATT_BS11_LMT_USER_ACC_LEV) >= 1) { - uint8_t level = *TLVP_VAL(&tp, NM_ATT_BS11_LMT_USER_ACC_LEV); - DEBUGPC(DNM, "Level=%u ", level); - } - if (TLVP_PRESENT(&tp, NM_ATT_BS11_LMT_USER_NAME) && - TLVP_LEN(&tp, NM_ATT_BS11_LMT_USER_NAME) >= 1) { - char *name = (char *) TLVP_VAL(&tp, NM_ATT_BS11_LMT_USER_NAME); - DEBUGPC(DNM, "Username=%s ", name); - } - DEBUGPC(DNM, "\n"); - /* FIXME: parse LMT LOGON TIME */ - return 0; -} - -void abis_nm_queue_send_next(struct gsm_bts *bts) -{ - int wait = 0; - struct msgb *msg; - /* the queue is empty */ - while (!llist_empty(&bts->abis_queue)) { - msg = msgb_dequeue(&bts->abis_queue); - wait = OBSC_NM_W_ACK_CB(msg); - _abis_nm_sendmsg(msg); - - if (wait) - break; - } - - bts->abis_nm_pend = wait; -} - -/* Receive a OML NM Message from BTS */ -static int abis_nm_rcvmsg_fom(struct msgb *mb) -{ - struct abis_om_hdr *oh = msgb_l2(mb); - struct abis_om_fom_hdr *foh = msgb_l3(mb); - struct e1inp_sign_link *sign_link = mb->dst; - uint8_t mt = foh->msg_type; - /* sign_link might get deleted via osmo_signal_dispatch -> save bts */ - struct gsm_bts *bts = sign_link->trx->bts; - int ret = 0; - - /* check for unsolicited message */ - if (is_report(mt)) - return abis_nm_rcvmsg_report(mb, bts); - - if (is_in_arr(mt, abis_nm_sw_load_msgs, ARRAY_SIZE(abis_nm_sw_load_msgs))) - return abis_nm_rcvmsg_sw(mb); - - if (is_in_arr(mt, abis_nm_nacks, ARRAY_SIZE(abis_nm_nacks))) { - struct nm_nack_signal_data nack_data; - struct tlv_parsed tp; - - abis_nm_debugp_foh(DNM, foh); - - DEBUGPC(DNM, "%s NACK ", abis_nm_nack_name(mt)); - - abis_nm_tlv_parse(&tp, bts, foh->data, oh->length-sizeof(*foh)); - if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES)) - DEBUGPC(DNM, "CAUSE=%s\n", - abis_nm_nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES))); - else - DEBUGPC(DNM, "\n"); - - nack_data.msg = mb; - nack_data.mt = mt; - nack_data.bts = bts; - osmo_signal_dispatch(SS_NM, S_NM_NACK, &nack_data); - abis_nm_queue_send_next(bts); - return 0; - } -#if 0 - /* check if last message is to be acked */ - if (is_ack_nack(nmh->last_msgtype)) { - if (mt == MT_ACK(nmh->last_msgtype)) { - DEBUGP(DNM, "received ACK (0x%x)\n", foh->msg_type); - /* we got our ACK, continue sending the next msg */ - } else if (mt == MT_NACK(nmh->last_msgtype)) { - /* we got a NACK, signal this to the caller */ - DEBUGP(DNM, "received NACK (0x%x)\n", foh->msg_type); - /* FIXME: somehow signal this to the caller */ - } else { - /* really strange things happen */ - return -EINVAL; - } - } -#endif - - switch (mt) { - case NM_MT_CHG_ADM_STATE_ACK: - ret = abis_nm_rx_chg_adm_state_ack(mb); - break; - case NM_MT_SW_ACT_REQ: - ret = abis_nm_rx_sw_act_req(mb); - break; - case NM_MT_BS11_LMT_SESSION: - ret = abis_nm_rx_lmt_event(mb); - break; - case NM_MT_OPSTART_ACK: - abis_nm_debugp_foh(DNM, foh); - DEBUGPC(DNM, "Opstart ACK\n"); - break; - case NM_MT_SET_CHAN_ATTR_ACK: - abis_nm_debugp_foh(DNM, foh); - DEBUGPC(DNM, "Set Channel Attributes ACK\n"); - break; - case NM_MT_SET_RADIO_ATTR_ACK: - abis_nm_debugp_foh(DNM, foh); - DEBUGPC(DNM, "Set Radio Carrier Attributes ACK\n"); - break; - case NM_MT_CONN_MDROP_LINK_ACK: - abis_nm_debugp_foh(DNM, foh); - DEBUGPC(DNM, "CONN MDROP LINK ACK\n"); - break; - case NM_MT_IPACC_RESTART_ACK: - osmo_signal_dispatch(SS_NM, S_NM_IPACC_RESTART_ACK, NULL); - break; - case NM_MT_IPACC_RESTART_NACK: - osmo_signal_dispatch(SS_NM, S_NM_IPACC_RESTART_NACK, NULL); - break; - case NM_MT_SET_BTS_ATTR_ACK: - break; - case NM_MT_GET_ATTR_RESP: - ret = abis_nm_rx_get_attr_resp(mb, gsm_bts_trx_num(bts, (foh)->obj_inst.trx_nr)); - break; - default: - abis_nm_debugp_foh(DNM, foh); - LOGPC(DNM, LOGL_ERROR, "Unhandled message %s\n", - get_value_string(abis_nm_msgtype_names, mt)); - } - - abis_nm_queue_send_next(bts); - return ret; -} - -static int abis_nm_rx_ipacc(struct msgb *mb); - -static int abis_nm_rcvmsg_manuf(struct msgb *mb) -{ - int rc; - struct e1inp_sign_link *sign_link = mb->dst; - int bts_type = sign_link->trx->bts->type; - - switch (bts_type) { - case GSM_BTS_TYPE_NANOBTS: - case GSM_BTS_TYPE_OSMOBTS: - rc = abis_nm_rx_ipacc(mb); - abis_nm_queue_send_next(sign_link->trx->bts); - break; - default: - LOGP(DNM, LOGL_ERROR, "don't know how to parse OML for this " - "BTS type (%u)\n", bts_type); - rc = 0; - break; - } - - return rc; -} - -/* High-Level API */ -/* Entry-point where L2 OML from BTS enters the NM code */ -int abis_nm_rcvmsg(struct msgb *msg) -{ - struct abis_om_hdr *oh = msgb_l2(msg); - int rc = 0; - - /* Various consistency checks */ - if (oh->placement != ABIS_OM_PLACEMENT_ONLY) { - LOGP(DNM, LOGL_ERROR, "ABIS OML placement 0x%x not supported\n", - oh->placement); - if (oh->placement != ABIS_OM_PLACEMENT_FIRST) { - rc = -EINVAL; - goto err; - } - } - if (oh->sequence != 0) { - LOGP(DNM, LOGL_ERROR, "ABIS OML sequence 0x%x != 0x00\n", - oh->sequence); - rc = -EINVAL; - goto err; - } -#if 0 - unsigned int l2_len = msg->tail - (uint8_t *)msgb_l2(msg); - unsigned int hlen = sizeof(*oh) + sizeof(struct abis_om_fom_hdr); - if (oh->length + hlen > l2_len) { - LOGP(DNM, LOGL_ERROR, "ABIS OML truncated message (%u > %u)\n", - oh->length + sizeof(*oh), l2_len); - return -EINVAL; - } - if (oh->length + hlen < l2_len) - LOGP(DNM, LOGL_ERROR, "ABIS OML message with extra trailer?!? (oh->len=%d, sizeof_oh=%d l2_len=%d\n", oh->length, sizeof(*oh), l2_len); -#endif - msg->l3h = (unsigned char *)oh + sizeof(*oh); - - switch (oh->mdisc) { - case ABIS_OM_MDISC_FOM: - rc = abis_nm_rcvmsg_fom(msg); - break; - case ABIS_OM_MDISC_MANUF: - rc = abis_nm_rcvmsg_manuf(msg); - break; - case ABIS_OM_MDISC_MMI: - case ABIS_OM_MDISC_TRAU: - LOGP(DNM, LOGL_ERROR, "unimplemented ABIS OML message discriminator 0x%x\n", - oh->mdisc); - break; - default: - LOGP(DNM, LOGL_ERROR, "unknown ABIS OML message discriminator 0x%x\n", - oh->mdisc); - rc = -EINVAL; - break; - } -err: - msgb_free(msg); - return rc; -} - -#if 0 -/* initialized all resources */ -struct abis_nm_h *abis_nm_init(struct abis_nm_cfg *cfg) -{ - struct abis_nm_h *nmh; - - nmh = malloc(sizeof(*nmh)); - if (!nmh) - return NULL; - - nmh->cfg = cfg; - - return nmh; -} - -/* free all resources */ -void abis_nm_fini(struct abis_nm_h *nmh) -{ - free(nmh); -} -#endif - -/* Here we are trying to define a high-level API that can be used by - * the actual BSC implementation. However, the architecture is currently - * still under design. Ideally the calls to this API would be synchronous, - * while the underlying stack behind the APi runs in a traditional select - * based state machine. - */ - -/* 6.2 Software Load: */ -enum sw_state { - SW_STATE_NONE, - SW_STATE_WAIT_INITACK, - SW_STATE_WAIT_SEGACK, - SW_STATE_WAIT_ENDACK, - SW_STATE_WAIT_ACTACK, - SW_STATE_ERROR, -}; - -struct abis_nm_sw { - struct gsm_bts *bts; - int trx_nr; - gsm_cbfn *cbfn; - void *cb_data; - int forced; - - /* this will become part of the SW LOAD INITIATE */ - uint8_t obj_class; - uint8_t obj_instance[3]; - - uint8_t file_id[255]; - uint8_t file_id_len; - - uint8_t file_version[255]; - uint8_t file_version_len; - - uint8_t window_size; - uint8_t seg_in_window; - - int fd; - FILE *stream; - enum sw_state state; - int last_seg; -}; - -static struct abis_nm_sw g_sw; - -static void sw_add_file_id_and_ver(struct abis_nm_sw *sw, struct msgb *msg) -{ - if (sw->bts->type == GSM_BTS_TYPE_NANOBTS) { - msgb_v_put(msg, NM_ATT_SW_DESCR); - msgb_tl16v_put(msg, NM_ATT_FILE_ID, sw->file_id_len, sw->file_id); - msgb_tl16v_put(msg, NM_ATT_FILE_VERSION, sw->file_version_len, - sw->file_version); - } else if (sw->bts->type == GSM_BTS_TYPE_BS11) { - msgb_tlv_put(msg, NM_ATT_FILE_ID, sw->file_id_len, sw->file_id); - msgb_tlv_put(msg, NM_ATT_FILE_VERSION, sw->file_version_len, - sw->file_version); - } else { - LOGP(DNM, LOGL_ERROR, "Please implement this for the BTS.\n"); - } -} - -/* 6.2.1 / 8.3.1: Load Data Initiate */ -static int sw_load_init(struct abis_nm_sw *sw) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - uint8_t len = 3*2 + sw->file_id_len + sw->file_version_len; - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, len, NM_MT_LOAD_INIT, sw->obj_class, - sw->obj_instance[0], sw->obj_instance[1], - sw->obj_instance[2]); - - sw_add_file_id_and_ver(sw, msg); - msgb_tv_put(msg, NM_ATT_WINDOW_SIZE, sw->window_size); - - return abis_nm_sendmsg(sw->bts, msg); -} - -static int is_last_line(FILE *stream) -{ - char next_seg_buf[256]; - long pos; - - /* check if we're sending the last line */ - pos = ftell(stream); - - /* Did ftell fail? Then we are at the end for sure */ - if (pos < 0) - return 1; - - if (!fgets(next_seg_buf, sizeof(next_seg_buf)-2, stream)) { - int rc = fseek(stream, pos, SEEK_SET); - if (rc < 0) - return rc; - return 1; - } - - fseek(stream, pos, SEEK_SET); - return 0; -} - -/* 6.2.2 / 8.3.2 Load Data Segment */ -static int sw_load_segment(struct abis_nm_sw *sw) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - char seg_buf[256]; - char *line_buf = seg_buf+2; - unsigned char *tlv; - int len; - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - - switch (sw->bts->type) { - case GSM_BTS_TYPE_BS11: - if (fgets(line_buf, sizeof(seg_buf)-2, sw->stream) == NULL) { - perror("fgets reading segment"); - return -EINVAL; - } - seg_buf[0] = 0x00; - - /* check if we're sending the last line */ - sw->last_seg = is_last_line(sw->stream); - if (sw->last_seg) - seg_buf[1] = 0; - else - seg_buf[1] = 1 + sw->seg_in_window++; - - len = strlen(line_buf) + 2; - tlv = msgb_put(msg, TLV_GROSS_LEN(len)); - tlv_put(tlv, NM_ATT_BS11_FILE_DATA, len, (uint8_t *)seg_buf); - /* BS11 wants CR + LF in excess of the TLV length !?! */ - tlv[1] -= 2; - - /* we only now know the exact length for the OM hdr */ - len = strlen(line_buf)+2; - break; - case GSM_BTS_TYPE_NANOBTS: { - osmo_static_assert(sizeof(seg_buf) >= IPACC_SEGMENT_SIZE, buffer_big_enough); - len = read(sw->fd, &seg_buf, IPACC_SEGMENT_SIZE); - if (len < 0) { - perror("read failed"); - return -EINVAL; - } - - if (len != IPACC_SEGMENT_SIZE) - sw->last_seg = 1; - - ++sw->seg_in_window; - msgb_tl16v_put(msg, NM_ATT_IPACC_FILE_DATA, len, (const uint8_t *) seg_buf); - len += 3; - break; - } - default: - LOGP(DNM, LOGL_ERROR, "sw_load_segment needs implementation for the BTS.\n"); - /* FIXME: Other BTS types */ - return -1; - } - - fill_om_fom_hdr(oh, len, NM_MT_LOAD_SEG, sw->obj_class, - sw->obj_instance[0], sw->obj_instance[1], - sw->obj_instance[2]); - - return abis_nm_sendmsg_direct(sw->bts, msg); -} - -/* 6.2.4 / 8.3.4 Load Data End */ -static int sw_load_end(struct abis_nm_sw *sw) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - uint8_t len = 2*2 + sw->file_id_len + sw->file_version_len; - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, len, NM_MT_LOAD_END, sw->obj_class, - sw->obj_instance[0], sw->obj_instance[1], - sw->obj_instance[2]); - - sw_add_file_id_and_ver(sw, msg); - return abis_nm_sendmsg(sw->bts, msg); -} - -/* Activate the specified software into the BTS */ -static int sw_activate(struct abis_nm_sw *sw) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - uint8_t len = 2*2 + sw->file_id_len + sw->file_version_len; - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, len, NM_MT_ACTIVATE_SW, sw->obj_class, - sw->obj_instance[0], sw->obj_instance[1], - sw->obj_instance[2]); - - /* FIXME: this is BS11 specific format */ - msgb_tlv_put(msg, NM_ATT_FILE_ID, sw->file_id_len, sw->file_id); - msgb_tlv_put(msg, NM_ATT_FILE_VERSION, sw->file_version_len, - sw->file_version); - - return abis_nm_sendmsg(sw->bts, msg); -} - -struct sdp_firmware { - char magic[4]; - char more_magic[4]; - unsigned int header_length; - unsigned int file_length; -} __attribute__ ((packed)); - -static int parse_sdp_header(struct abis_nm_sw *sw) -{ - struct sdp_firmware firmware_header; - int rc; - struct stat stat; - - rc = read(sw->fd, &firmware_header, sizeof(firmware_header)); - if (rc != sizeof(firmware_header)) { - LOGP(DNM, LOGL_ERROR, "Could not read SDP file header.\n"); - return -1; - } - - if (strncmp(firmware_header.magic, " SDP", 4) != 0) { - LOGP(DNM, LOGL_ERROR, "The magic number1 is wrong.\n"); - return -1; - } - - if (firmware_header.more_magic[0] != 0x10 || - firmware_header.more_magic[1] != 0x02 || - firmware_header.more_magic[2] != 0x00 || - firmware_header.more_magic[3] != 0x00) { - LOGP(DNM, LOGL_ERROR, "The more magic number is wrong.\n"); - return -1; - } - - - if (fstat(sw->fd, &stat) == -1) { - LOGP(DNM, LOGL_ERROR, "Could not stat the file.\n"); - return -1; - } - - if (ntohl(firmware_header.file_length) != stat.st_size) { - LOGP(DNM, LOGL_ERROR, "The filesizes do not match.\n"); - return -1; - } - - /* go back to the start as we checked the whole filesize.. */ - lseek(sw->fd, 0l, SEEK_SET); - LOGP(DNM, LOGL_NOTICE, "The ipaccess SDP header is not fully understood.\n" - "There might be checksums in the file that are not\n" - "verified and incomplete firmware might be flashed.\n" - "There is absolutely no WARRANTY that flashing will\n" - "work.\n"); - return 0; -} - -static int sw_open_file(struct abis_nm_sw *sw, const char *fname) -{ - char file_id[12+1]; - char file_version[80+1]; - int rc; - - sw->fd = open(fname, O_RDONLY); - if (sw->fd < 0) - return sw->fd; - - switch (sw->bts->type) { - case GSM_BTS_TYPE_BS11: - sw->stream = fdopen(sw->fd, "r"); - if (!sw->stream) { - perror("fdopen"); - return -1; - } - /* read first line and parse file ID and VERSION */ - rc = fscanf(sw->stream, "@(#)%12s:%80s\r\n", - file_id, file_version); - if (rc != 2) { - perror("parsing header line of software file"); - return -1; - } - strcpy((char *)sw->file_id, file_id); - sw->file_id_len = strlen(file_id); - strcpy((char *)sw->file_version, file_version); - sw->file_version_len = strlen(file_version); - /* rewind to start of file */ - rewind(sw->stream); - break; - case GSM_BTS_TYPE_NANOBTS: - /* TODO: extract that from the filename or content */ - rc = parse_sdp_header(sw); - if (rc < 0) { - fprintf(stderr, "Could not parse the ipaccess SDP header\n"); - return -1; - } - - strcpy((char *)sw->file_id, "id"); - sw->file_id_len = 3; - strcpy((char *)sw->file_version, "version"); - sw->file_version_len = 8; - break; - default: - /* We don't know how to treat them yet */ - close(sw->fd); - return -EINVAL; - } - - return 0; -} - -static void sw_close_file(struct abis_nm_sw *sw) -{ - switch (sw->bts->type) { - case GSM_BTS_TYPE_BS11: - fclose(sw->stream); - break; - default: - close(sw->fd); - break; - } -} - -/* Fill the window */ -static int sw_fill_window(struct abis_nm_sw *sw) -{ - int rc; - - while (sw->seg_in_window < sw->window_size) { - rc = sw_load_segment(sw); - if (rc < 0) - return rc; - if (sw->last_seg) - break; - } - return 0; -} - -/* callback function from abis_nm_rcvmsg() handler */ -static int abis_nm_rcvmsg_sw(struct msgb *mb) -{ - struct abis_om_fom_hdr *foh = msgb_l3(mb); - struct e1inp_sign_link *sign_link = mb->dst; - int rc = -1; - struct abis_nm_sw *sw = &g_sw; - enum sw_state old_state = sw->state; - - //DEBUGP(DNM, "state %u, NM MT 0x%02x\n", sw->state, foh->msg_type); - - switch (sw->state) { - case SW_STATE_WAIT_INITACK: - switch (foh->msg_type) { - case NM_MT_LOAD_INIT_ACK: - /* fill window with segments */ - if (sw->cbfn) - sw->cbfn(GSM_HOOK_NM_SWLOAD, - NM_MT_LOAD_INIT_ACK, mb, - sw->cb_data, NULL); - rc = sw_fill_window(sw); - sw->state = SW_STATE_WAIT_SEGACK; - abis_nm_queue_send_next(sign_link->trx->bts); - break; - case NM_MT_LOAD_INIT_NACK: - if (sw->forced) { - DEBUGP(DNM, "FORCED: Ignoring Software Load " - "Init NACK\n"); - if (sw->cbfn) - sw->cbfn(GSM_HOOK_NM_SWLOAD, - NM_MT_LOAD_INIT_ACK, mb, - sw->cb_data, NULL); - rc = sw_fill_window(sw); - sw->state = SW_STATE_WAIT_SEGACK; - } else { - DEBUGP(DNM, "Software Load Init NACK\n"); - /* FIXME: cause */ - if (sw->cbfn) - sw->cbfn(GSM_HOOK_NM_SWLOAD, - NM_MT_LOAD_INIT_NACK, mb, - sw->cb_data, NULL); - sw->state = SW_STATE_ERROR; - } - abis_nm_queue_send_next(sign_link->trx->bts); - break; - } - break; - case SW_STATE_WAIT_SEGACK: - switch (foh->msg_type) { - case NM_MT_LOAD_SEG_ACK: - if (sw->cbfn) - sw->cbfn(GSM_HOOK_NM_SWLOAD, - NM_MT_LOAD_SEG_ACK, mb, - sw->cb_data, NULL); - sw->seg_in_window = 0; - if (!sw->last_seg) { - /* fill window with more segments */ - rc = sw_fill_window(sw); - sw->state = SW_STATE_WAIT_SEGACK; - } else { - /* end the transfer */ - sw->state = SW_STATE_WAIT_ENDACK; - rc = sw_load_end(sw); - } - abis_nm_queue_send_next(sign_link->trx->bts); - break; - case NM_MT_LOAD_ABORT: - if (sw->cbfn) - sw->cbfn(GSM_HOOK_NM_SWLOAD, - NM_MT_LOAD_ABORT, mb, - sw->cb_data, NULL); - break; - } - break; - case SW_STATE_WAIT_ENDACK: - switch (foh->msg_type) { - case NM_MT_LOAD_END_ACK: - sw_close_file(sw); - DEBUGP(DNM, "Software Load End (BTS %u)\n", - sw->bts->nr); - sw->state = SW_STATE_NONE; - if (sw->cbfn) - sw->cbfn(GSM_HOOK_NM_SWLOAD, - NM_MT_LOAD_END_ACK, mb, - sw->cb_data, NULL); - rc = 0; - abis_nm_queue_send_next(sign_link->trx->bts); - break; - case NM_MT_LOAD_END_NACK: - if (sw->forced) { - DEBUGP(DNM, "FORCED: Ignoring Software Load" - "End NACK\n"); - sw->state = SW_STATE_NONE; - if (sw->cbfn) - sw->cbfn(GSM_HOOK_NM_SWLOAD, - NM_MT_LOAD_END_ACK, mb, - sw->cb_data, NULL); - } else { - DEBUGP(DNM, "Software Load End NACK\n"); - /* FIXME: cause */ - sw->state = SW_STATE_ERROR; - if (sw->cbfn) - sw->cbfn(GSM_HOOK_NM_SWLOAD, - NM_MT_LOAD_END_NACK, mb, - sw->cb_data, NULL); - } - abis_nm_queue_send_next(sign_link->trx->bts); - break; - } - case SW_STATE_WAIT_ACTACK: - switch (foh->msg_type) { - case NM_MT_ACTIVATE_SW_ACK: - /* we're done */ - DEBUGP(DNM, "Activate Software DONE!\n"); - sw->state = SW_STATE_NONE; - rc = 0; - if (sw->cbfn) - sw->cbfn(GSM_HOOK_NM_SWLOAD, - NM_MT_ACTIVATE_SW_ACK, mb, - sw->cb_data, NULL); - abis_nm_queue_send_next(sign_link->trx->bts); - break; - case NM_MT_ACTIVATE_SW_NACK: - DEBUGP(DNM, "Activate Software NACK\n"); - /* FIXME: cause */ - sw->state = SW_STATE_ERROR; - if (sw->cbfn) - sw->cbfn(GSM_HOOK_NM_SWLOAD, - NM_MT_ACTIVATE_SW_NACK, mb, - sw->cb_data, NULL); - abis_nm_queue_send_next(sign_link->trx->bts); - break; - } - case SW_STATE_NONE: - switch (foh->msg_type) { - case NM_MT_ACTIVATE_SW_ACK: - rc = 0; - break; - } - break; - case SW_STATE_ERROR: - break; - } - - if (rc) - DEBUGP(DNM, "unexpected NM MT 0x%02x in state %u -> %u\n", - foh->msg_type, old_state, sw->state); - - return rc; -} - -/* Load the specified software into the BTS */ -int abis_nm_software_load(struct gsm_bts *bts, int trx_nr, const char *fname, - uint8_t win_size, int forced, - gsm_cbfn *cbfn, void *cb_data) -{ - struct abis_nm_sw *sw = &g_sw; - int rc; - - DEBUGP(DNM, "Software Load (BTS %u, File \"%s\")\n", - bts->nr, fname); - - if (sw->state != SW_STATE_NONE) - return -EBUSY; - - sw->bts = bts; - sw->trx_nr = trx_nr; - - switch (bts->type) { - case GSM_BTS_TYPE_BS11: - sw->obj_class = NM_OC_SITE_MANAGER; - sw->obj_instance[0] = 0xff; - sw->obj_instance[1] = 0xff; - sw->obj_instance[2] = 0xff; - break; - case GSM_BTS_TYPE_NANOBTS: - sw->obj_class = NM_OC_BASEB_TRANSC; - sw->obj_instance[0] = sw->bts->nr; - sw->obj_instance[1] = sw->trx_nr; - sw->obj_instance[2] = 0xff; - break; - case GSM_BTS_TYPE_UNKNOWN: - default: - LOGPC(DNM, LOGL_ERROR, "Software Load not properly implemented.\n"); - return -1; - break; - } - sw->window_size = win_size; - sw->state = SW_STATE_WAIT_INITACK; - sw->cbfn = cbfn; - sw->cb_data = cb_data; - sw->forced = forced; - - rc = sw_open_file(sw, fname); - if (rc < 0) { - sw->state = SW_STATE_NONE; - return rc; - } - - return sw_load_init(sw); -} - -int abis_nm_software_load_status(struct gsm_bts *bts) -{ - struct abis_nm_sw *sw = &g_sw; - struct stat st; - int rc, percent; - - rc = fstat(sw->fd, &st); - if (rc < 0) { - perror("ERROR during stat"); - return rc; - } - - if (sw->stream) - percent = (ftell(sw->stream) * 100) / st.st_size; - else - percent = (lseek(sw->fd, 0, SEEK_CUR) * 100) / st.st_size; - return percent; -} - -/* Activate the specified software into the BTS */ -int abis_nm_software_activate(struct gsm_bts *bts, const char *fname, - gsm_cbfn *cbfn, void *cb_data) -{ - struct abis_nm_sw *sw = &g_sw; - int rc; - - DEBUGP(DNM, "Activating Software (BTS %u, File \"%s\")\n", - bts->nr, fname); - - if (sw->state != SW_STATE_NONE) - return -EBUSY; - - sw->bts = bts; - sw->obj_class = NM_OC_SITE_MANAGER; - sw->obj_instance[0] = 0xff; - sw->obj_instance[1] = 0xff; - sw->obj_instance[2] = 0xff; - sw->state = SW_STATE_WAIT_ACTACK; - sw->cbfn = cbfn; - sw->cb_data = cb_data; - - /* Open the file in order to fill some sw struct members */ - rc = sw_open_file(sw, fname); - if (rc < 0) { - sw->state = SW_STATE_NONE; - return rc; - } - sw_close_file(sw); - - return sw_activate(sw); -} - -static void fill_nm_channel(struct abis_nm_channel *ch, uint8_t bts_port, - uint8_t ts_nr, uint8_t subslot_nr) -{ - ch->attrib = NM_ATT_ABIS_CHANNEL; - ch->bts_port = bts_port; - ch->timeslot = ts_nr; - ch->subslot = subslot_nr; -} - -int abis_nm_establish_tei(struct gsm_bts *bts, uint8_t trx_nr, - uint8_t e1_port, uint8_t e1_timeslot, uint8_t e1_subslot, - uint8_t tei) -{ - struct abis_om_hdr *oh; - struct abis_nm_channel *ch; - uint8_t len = sizeof(*ch) + 2; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, len, NM_MT_ESTABLISH_TEI, NM_OC_RADIO_CARRIER, - bts->bts_nr, trx_nr, 0xff); - - msgb_tv_put(msg, NM_ATT_TEI, tei); - - ch = (struct abis_nm_channel *) msgb_put(msg, sizeof(*ch)); - fill_nm_channel(ch, e1_port, e1_timeslot, e1_subslot); - - return abis_nm_sendmsg(bts, msg); -} - -/* connect signalling of one (BTS,TRX) to a particular timeslot on the E1 */ -int abis_nm_conn_terr_sign(struct gsm_bts_trx *trx, - uint8_t e1_port, uint8_t e1_timeslot, uint8_t e1_subslot) -{ - struct gsm_bts *bts = trx->bts; - struct abis_om_hdr *oh; - struct abis_nm_channel *ch; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, sizeof(*ch), NM_MT_CONN_TERR_SIGN, - NM_OC_RADIO_CARRIER, bts->bts_nr, trx->nr, 0xff); - - ch = (struct abis_nm_channel *) msgb_put(msg, sizeof(*ch)); - fill_nm_channel(ch, e1_port, e1_timeslot, e1_subslot); - - return abis_nm_sendmsg(bts, msg); -} - -#if 0 -int abis_nm_disc_terr_sign(struct abis_nm_h *h, struct abis_om_obj_inst *inst, - struct abis_nm_abis_channel *chan) -{ -} -#endif - -int abis_nm_conn_terr_traf(struct gsm_bts_trx_ts *ts, - uint8_t e1_port, uint8_t e1_timeslot, - uint8_t e1_subslot) -{ - struct gsm_bts *bts = ts->trx->bts; - struct abis_om_hdr *oh; - struct abis_nm_channel *ch; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, sizeof(*ch), NM_MT_CONN_TERR_TRAF, - NM_OC_CHANNEL, bts->bts_nr, ts->trx->nr, ts->nr); - - ch = (struct abis_nm_channel *) msgb_put(msg, sizeof(*ch)); - fill_nm_channel(ch, e1_port, e1_timeslot, e1_subslot); - - DEBUGP(DNM, "CONNECT TERR TRAF Um=%s E1=(%u,%u,%u)\n", - gsm_ts_name(ts), - e1_port, e1_timeslot, e1_subslot); - - return abis_nm_sendmsg(bts, msg); -} - -#if 0 -int abis_nm_disc_terr_traf(struct abis_nm_h *h, struct abis_om_obj_inst *inst, - struct abis_nm_abis_channel *chan, - uint8_t subchan) -{ -} -#endif - -/* 3GPP TS 52.021 § 8.11.1 */ -int abis_nm_get_attr(struct gsm_bts *bts, uint8_t obj_class, uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr, - const uint8_t *attr, uint8_t attr_len) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - - DEBUGP(DNM, "Get Attr (bts=%d)\n", bts->nr); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, attr_len, NM_MT_GET_ATTR, obj_class, - bts_nr, trx_nr, ts_nr); - msgb_tl16v_put(msg, NM_ATT_LIST_REQ_ATTR, attr_len, attr); - - return abis_nm_sendmsg(bts, msg); -} - -/* Chapter 8.6.1 */ -int abis_nm_set_bts_attr(struct gsm_bts *bts, uint8_t *attr, int attr_len) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - uint8_t *cur; - - DEBUGP(DNM, "Set BTS Attr (bts=%d)\n", bts->nr); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, attr_len, NM_MT_SET_BTS_ATTR, NM_OC_BTS, bts->bts_nr, 0xff, 0xff); - cur = msgb_put(msg, attr_len); - memcpy(cur, attr, attr_len); - - return abis_nm_sendmsg(bts, msg); -} - -/* Chapter 8.6.2 */ -int abis_nm_set_radio_attr(struct gsm_bts_trx *trx, uint8_t *attr, int attr_len) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - uint8_t *cur; - - DEBUGP(DNM, "Set TRX Attr (bts=%d,trx=%d)\n", trx->bts->nr, trx->nr); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, attr_len, NM_MT_SET_RADIO_ATTR, NM_OC_RADIO_CARRIER, - trx->bts->bts_nr, trx->nr, 0xff); - cur = msgb_put(msg, attr_len); - memcpy(cur, attr, attr_len); - - return abis_nm_sendmsg(trx->bts, msg); -} - -int abis_nm_update_max_power_red(struct gsm_bts_trx *trx) -{ - uint8_t attr[] = { NM_ATT_RF_MAXPOWR_R, trx->max_power_red / 2 }; - return abis_nm_set_radio_attr(trx, attr, ARRAY_SIZE(attr)); -} - -static int verify_chan_comb(struct gsm_bts_trx_ts *ts, uint8_t chan_comb, - const char **reason) -{ - int i; - - *reason = "Reason unknown"; - - /* As it turns out, the BS-11 has some very peculiar restrictions - * on the channel combinations it allows */ - switch (ts->trx->bts->type) { - case GSM_BTS_TYPE_BS11: - switch (chan_comb) { - case NM_CHANC_TCHHalf: - case NM_CHANC_TCHHalf2: - case NM_CHANC_OSMO_TCHFull_TCHHalf_PDCH: - /* not supported */ - *reason = "TCH/H is not supported."; - return -EINVAL; - case NM_CHANC_SDCCH: - /* only one SDCCH/8 per TRX */ - for (i = 0; i < TRX_NR_TS; i++) { - if (i == ts->nr) - continue; - if (ts->trx->ts[i].nm_chan_comb == - NM_CHANC_SDCCH) { - *reason = "Only one SDCCH/8 per TRX allowed."; - return -EINVAL; - } - } - /* not allowed for TS0 of BCCH-TRX */ - if (ts->trx == ts->trx->bts->c0 && - ts->nr == 0) { - *reason = "SDCCH/8 must be on TS0."; - return -EINVAL; - } - - /* not on the same TRX that has a BCCH+SDCCH4 - * combination */ - if (ts->trx != ts->trx->bts->c0 && - (ts->trx->ts[0].nm_chan_comb == 5 || - ts->trx->ts[0].nm_chan_comb == 8)) { - *reason = "SDCCH/8 and BCCH must be on the same TRX."; - return -EINVAL; - } - break; - case NM_CHANC_mainBCCH: - case NM_CHANC_BCCHComb: - /* allowed only for TS0 of C0 */ - if (ts->trx != ts->trx->bts->c0 || ts->nr != 0) { - *reason = "Main BCCH must be on TS0."; - return -EINVAL; - } - break; - case NM_CHANC_BCCH: - /* allowed only for TS 2/4/6 of C0 */ - if (ts->trx != ts->trx->bts->c0) { - *reason = "BCCH must be on C0."; - return -EINVAL; - } - if (ts->nr != 2 && ts->nr != 4 && ts->nr != 6) { - *reason = "BCCH must be on TS 2/4/6."; - return -EINVAL; - } - break; - case 8: /* this is not like 08.58, but in fact - * FCCH+SCH+BCCH+CCCH+SDCCH/4+SACCH/C4+CBCH */ - /* FIXME: only one CBCH allowed per cell */ - break; - } - break; - case GSM_BTS_TYPE_NANOBTS: - switch (ts->nr) { - case 0: - if (ts->trx->nr == 0) { - /* only on TRX0 */ - switch (chan_comb) { - case NM_CHANC_BCCH: - case NM_CHANC_mainBCCH: - case NM_CHANC_BCCHComb: - return 0; - break; - default: - *reason = "TS0 of TRX0 must carry a BCCH."; - return -EINVAL; - } - } else { - switch (chan_comb) { - case NM_CHANC_TCHFull: - case NM_CHANC_TCHHalf: - case NM_CHANC_IPAC_TCHFull_TCHHalf: - return 0; - default: - *reason = "TS0 must carry a TCH/F or TCH/H."; - return -EINVAL; - } - } - break; - case 1: - if (ts->trx->nr == 0) { - switch (chan_comb) { - case NM_CHANC_SDCCH_CBCH: - if (ts->trx->ts[0].nm_chan_comb == - NM_CHANC_mainBCCH) - return 0; - *reason = "TS0 must be the main BCCH for CBCH."; - return -EINVAL; - case NM_CHANC_SDCCH: - case NM_CHANC_TCHFull: - case NM_CHANC_TCHHalf: - case NM_CHANC_IPAC_TCHFull_TCHHalf: - case NM_CHANC_IPAC_TCHFull_PDCH: - case NM_CHANC_OSMO_TCHFull_TCHHalf_PDCH: - return 0; - default: - *reason = "TS1 must carry a CBCH, SDCCH or TCH."; - return -EINVAL; - } - } else { - switch (chan_comb) { - case NM_CHANC_SDCCH: - case NM_CHANC_TCHFull: - case NM_CHANC_TCHHalf: - case NM_CHANC_IPAC_TCHFull_TCHHalf: - return 0; - default: - *reason = "TS1 must carry a SDCCH or TCH."; - return -EINVAL; - } - } - break; - case 2: - case 3: - case 4: - case 5: - case 6: - case 7: - switch (chan_comb) { - case NM_CHANC_TCHFull: - case NM_CHANC_TCHHalf: - case NM_CHANC_IPAC_TCHFull_TCHHalf: - return 0; - case NM_CHANC_IPAC_PDCH: - case NM_CHANC_IPAC_TCHFull_PDCH: - case NM_CHANC_OSMO_TCHFull_TCHHalf_PDCH: - if (ts->trx->nr == 0) - return 0; - else { - *reason = "PDCH must be on TRX0."; - return -EINVAL; - } - } - break; - } - *reason = "Unknown combination"; - return -EINVAL; - case GSM_BTS_TYPE_OSMOBTS: - /* no known restrictions */ - return 0; - default: - /* unknown BTS type */ - return 0; - } - return 0; -} - -/* Chapter 8.6.3 */ -int abis_nm_set_channel_attr(struct gsm_bts_trx_ts *ts, uint8_t chan_comb) -{ - struct gsm_bts *bts = ts->trx->bts; - struct abis_om_hdr *oh; - uint8_t zero = 0x00; - struct msgb *msg = nm_msgb_alloc(); - uint8_t len = 2 + 2; - const char *reason = NULL; - - if (bts->type == GSM_BTS_TYPE_BS11) - len += 4 + 2 + 2 + 3; - - DEBUGP(DNM, "Set Chan Attr %s\n", gsm_ts_name(ts)); - if (verify_chan_comb(ts, chan_comb, &reason) < 0) { - msgb_free(msg); - LOGP(DNM, LOGL_ERROR, - "Invalid Channel Combination %d on %s. Reason: %s\n", - chan_comb, gsm_ts_name(ts), reason); - return -EINVAL; - } - ts->nm_chan_comb = chan_comb; - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, len, NM_MT_SET_CHAN_ATTR, - NM_OC_CHANNEL, bts->bts_nr, - ts->trx->nr, ts->nr); - msgb_tv_put(msg, NM_ATT_CHAN_COMB, chan_comb); - if (ts->hopping.enabled) { - unsigned int i; - uint8_t *len; - - msgb_tv_put(msg, NM_ATT_HSN, ts->hopping.hsn); - msgb_tv_put(msg, NM_ATT_MAIO, ts->hopping.maio); - - /* build the ARFCN list */ - msgb_put_u8(msg, NM_ATT_ARFCN_LIST); - len = msgb_put(msg, 1); - *len = 0; - for (i = 0; i < ts->hopping.arfcns.data_len*8; i++) { - if (bitvec_get_bit_pos(&ts->hopping.arfcns, i)) { - msgb_put_u16(msg, i); - /* At least BS-11 wants a TLV16 here */ - if (bts->type == GSM_BTS_TYPE_BS11) - *len += 1; - else - *len += sizeof(uint16_t); - } - } - } - msgb_tv_put(msg, NM_ATT_TSC, gsm_ts_tsc(ts)); /* training sequence */ - if (bts->type == GSM_BTS_TYPE_BS11) - msgb_tlv_put(msg, 0x59, 1, &zero); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_sw_act_req_ack(struct gsm_bts *bts, uint8_t obj_class, uint8_t i1, - uint8_t i2, uint8_t i3, int nack, uint8_t *attr, int att_len) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - uint8_t msgtype = NM_MT_SW_ACT_REQ_ACK; - uint8_t len = att_len; - - if (nack) { - len += 2; - msgtype = NM_MT_SW_ACT_REQ_NACK; - } - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, att_len, msgtype, obj_class, i1, i2, i3); - - if (attr) { - uint8_t *ptr = msgb_put(msg, att_len); - memcpy(ptr, attr, att_len); - } - if (nack) - msgb_tv_put(msg, NM_ATT_NACK_CAUSES, NM_NACK_OBJCLASS_NOTSUPP); - - return abis_nm_sendmsg_direct(bts, msg); -} - -int abis_nm_raw_msg(struct gsm_bts *bts, int len, uint8_t *rawmsg) -{ - struct msgb *msg = nm_msgb_alloc(); - struct abis_om_hdr *oh; - uint8_t *data; - - oh = (struct abis_om_hdr *) msgb_put(msg, sizeof(*oh)); - fill_om_hdr(oh, len); - data = msgb_put(msg, len); - memcpy(data, rawmsg, len); - - return abis_nm_sendmsg(bts, msg); -} - -/* Siemens specific commands */ -static int __simple_cmd(struct gsm_bts *bts, uint8_t msg_type) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 0, msg_type, NM_OC_SITE_MANAGER, - 0xff, 0xff, 0xff); - - return abis_nm_sendmsg(bts, msg); -} - -/* Chapter 8.9.2 */ -int abis_nm_opstart(struct gsm_bts *bts, uint8_t obj_class, uint8_t i0, uint8_t i1, uint8_t i2) -{ - struct abis_om_hdr *oh; - struct abis_om_fom_hdr *foh; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - foh = fill_om_fom_hdr(oh, 0, NM_MT_OPSTART, obj_class, i0, i1, i2); - - abis_nm_debugp_foh(DNM, foh); - DEBUGPC(DNM, "Sending OPSTART\n"); - - return abis_nm_sendmsg(bts, msg); -} - -/* Chapter 8.8.5 */ -int abis_nm_chg_adm_state(struct gsm_bts *bts, uint8_t obj_class, uint8_t i0, - uint8_t i1, uint8_t i2, enum abis_nm_adm_state adm_state) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 2, NM_MT_CHG_ADM_STATE, obj_class, i0, i1, i2); - msgb_tv_put(msg, NM_ATT_ADM_STATE, adm_state); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_conn_mdrop_link(struct gsm_bts *bts, uint8_t e1_port0, uint8_t ts0, - uint8_t e1_port1, uint8_t ts1) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - uint8_t *attr; - - DEBUGP(DNM, "CONNECT MDROP LINK E1=(%u,%u) -> E1=(%u, %u)\n", - e1_port0, ts0, e1_port1, ts1); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 6, NM_MT_CONN_MDROP_LINK, - NM_OC_SITE_MANAGER, 0x00, 0x00, 0x00); - - attr = msgb_put(msg, 3); - attr[0] = NM_ATT_MDROP_LINK; - attr[1] = e1_port0; - attr[2] = ts0; - - attr = msgb_put(msg, 3); - attr[0] = NM_ATT_MDROP_NEXT; - attr[1] = e1_port1; - attr[2] = ts1; - - return abis_nm_sendmsg(bts, msg); -} - -/* Chapter 8.7.1 */ -int abis_nm_perform_test(struct gsm_bts *bts, uint8_t obj_class, - uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr, - uint8_t test_nr, uint8_t auton_report, struct msgb *msg) -{ - struct abis_om_hdr *oh; - - DEBUGP(DNM, "PEFORM TEST %s\n", abis_nm_test_name(test_nr)); - - if (!msg) - msg = nm_msgb_alloc(); - - msgb_tv_push(msg, NM_ATT_AUTON_REPORT, auton_report); - msgb_tv_push(msg, NM_ATT_TEST_NO, test_nr); - oh = (struct abis_om_hdr *) msgb_push(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, msgb_l3len(msg), NM_MT_PERF_TEST, - obj_class, bts_nr, trx_nr, ts_nr); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_event_reports(struct gsm_bts *bts, int on) -{ - if (on == 0) - return __simple_cmd(bts, NM_MT_STOP_EVENT_REP); - else - return __simple_cmd(bts, NM_MT_REST_EVENT_REP); -} - -/* Siemens (or BS-11) specific commands */ - -int abis_nm_bs11_bsc_disconnect(struct gsm_bts *bts, int reconnect) -{ - if (reconnect == 0) - return __simple_cmd(bts, NM_MT_BS11_DISCONNECT); - else - return __simple_cmd(bts, NM_MT_BS11_RECONNECT); -} - -int abis_nm_bs11_restart(struct gsm_bts *bts) -{ - return __simple_cmd(bts, NM_MT_BS11_RESTART); -} - - -struct bs11_date_time { - uint16_t year; - uint8_t month; - uint8_t day; - uint8_t hour; - uint8_t min; - uint8_t sec; -} __attribute__((packed)); - - -void get_bs11_date_time(struct bs11_date_time *aet) -{ - time_t t; - struct tm *tm; - - t = time(NULL); - tm = localtime(&t); - aet->sec = tm->tm_sec; - aet->min = tm->tm_min; - aet->hour = tm->tm_hour; - aet->day = tm->tm_mday; - aet->month = tm->tm_mon; - aet->year = htons(1900 + tm->tm_year); -} - -int abis_nm_bs11_reset_resource(struct gsm_bts *bts) -{ - return __simple_cmd(bts, NM_MT_BS11_RESET_RESOURCE); -} - -int abis_nm_bs11_db_transmission(struct gsm_bts *bts, int begin) -{ - if (begin) - return __simple_cmd(bts, NM_MT_BS11_BEGIN_DB_TX); - else - return __simple_cmd(bts, NM_MT_BS11_END_DB_TX); -} - -int abis_nm_bs11_create_object(struct gsm_bts *bts, - enum abis_bs11_objtype type, uint8_t idx, - uint8_t attr_len, const uint8_t *attr) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - uint8_t *cur; - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, attr_len, NM_MT_BS11_CREATE_OBJ, - NM_OC_BS11, type, 0, idx); - cur = msgb_put(msg, attr_len); - memcpy(cur, attr, attr_len); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_bs11_delete_object(struct gsm_bts *bts, - enum abis_bs11_objtype type, uint8_t idx) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 0, NM_MT_BS11_DELETE_OBJ, - NM_OC_BS11, type, 0, idx); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_bs11_create_envaBTSE(struct gsm_bts *bts, uint8_t idx) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - uint8_t zero = 0x00; - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 3, NM_MT_BS11_CREATE_OBJ, - NM_OC_BS11_ENVABTSE, 0, idx, 0xff); - msgb_tlv_put(msg, 0x99, 1, &zero); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_bs11_create_bport(struct gsm_bts *bts, uint8_t idx) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 0, NM_MT_BS11_CREATE_OBJ, NM_OC_BS11_BPORT, - idx, 0xff, 0xff); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_bs11_delete_bport(struct gsm_bts *bts, uint8_t idx) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 0, NM_MT_BS11_DELETE_OBJ, NM_OC_BS11_BPORT, - idx, 0xff, 0xff); - - return abis_nm_sendmsg(bts, msg); -} - -static const uint8_t sm_attr[] = { NM_ATT_TEI, NM_ATT_ABIS_CHANNEL }; -int abis_nm_bs11_get_oml_tei_ts(struct gsm_bts *bts) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 2+sizeof(sm_attr), NM_MT_GET_ATTR, NM_OC_SITE_MANAGER, - 0xff, 0xff, 0xff); - msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(sm_attr), sm_attr); - - return abis_nm_sendmsg(bts, msg); -} - -/* like abis_nm_conn_terr_traf + set_tei */ -int abis_nm_bs11_conn_oml_tei(struct gsm_bts *bts, uint8_t e1_port, - uint8_t e1_timeslot, uint8_t e1_subslot, - uint8_t tei) -{ - struct abis_om_hdr *oh; - struct abis_nm_channel *ch; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, sizeof(*ch)+2, NM_MT_BS11_SET_ATTR, - NM_OC_SITE_MANAGER, 0xff, 0xff, 0xff); - - ch = (struct abis_nm_channel *) msgb_put(msg, sizeof(*ch)); - fill_nm_channel(ch, e1_port, e1_timeslot, e1_subslot); - msgb_tv_put(msg, NM_ATT_TEI, tei); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_bs11_set_trx_power(struct gsm_bts_trx *trx, uint8_t level) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 3, NM_MT_BS11_SET_ATTR, - NM_OC_BS11, BS11_OBJ_PA, 0x00, trx->nr); - msgb_tlv_put(msg, NM_ATT_BS11_TXPWR, 1, &level); - - return abis_nm_sendmsg(trx->bts, msg); -} - -int abis_nm_bs11_get_trx_power(struct gsm_bts_trx *trx) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - uint8_t attr = NM_ATT_BS11_TXPWR; - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 2+sizeof(attr), NM_MT_GET_ATTR, - NM_OC_BS11, BS11_OBJ_PA, 0x00, trx->nr); - msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(attr), &attr); - - return abis_nm_sendmsg(trx->bts, msg); -} - -int abis_nm_bs11_get_pll_mode(struct gsm_bts *bts) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - uint8_t attr[] = { NM_ATT_BS11_PLL_MODE }; - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 2+sizeof(attr), NM_MT_GET_ATTR, - NM_OC_BS11, BS11_OBJ_LI, 0x00, 0x00); - msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(attr), attr); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_bs11_get_cclk(struct gsm_bts *bts) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - uint8_t attr[] = { NM_ATT_BS11_CCLK_ACCURACY, - NM_ATT_BS11_CCLK_TYPE }; - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 2+sizeof(attr), NM_MT_GET_ATTR, - NM_OC_BS11, BS11_OBJ_CCLK, 0x00, 0x00); - msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(attr), attr); - - return abis_nm_sendmsg(bts, msg); - -} - -//static const uint8_t bs11_logon_c7[] = { 0x07, 0xd9, 0x01, 0x11, 0x0d, 0x10, 0x20 }; - -int abis_nm_bs11_factory_logon(struct gsm_bts *bts, int on) -{ - return abis_nm_bs11_logon(bts, 0x02, "FACTORY", on); -} - -int abis_nm_bs11_infield_logon(struct gsm_bts *bts, int on) -{ - return abis_nm_bs11_logon(bts, 0x03, "FIELD ", on); -} - -int abis_nm_bs11_logon(struct gsm_bts *bts, uint8_t level, const char *name, int on) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - struct bs11_date_time bdt; - - get_bs11_date_time(&bdt); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - if (on) { - uint8_t len = 3*2 + sizeof(bdt) - + 1 + strlen(name); - fill_om_fom_hdr(oh, len, NM_MT_BS11_LMT_LOGON, - NM_OC_BS11_BTSE, 0xff, 0xff, 0xff); - msgb_tlv_put(msg, NM_ATT_BS11_LMT_LOGIN_TIME, - sizeof(bdt), (uint8_t *) &bdt); - msgb_tlv_put(msg, NM_ATT_BS11_LMT_USER_ACC_LEV, - 1, &level); - msgb_tlv_put(msg, NM_ATT_BS11_LMT_USER_NAME, - strlen(name), (uint8_t *)name); - } else { - fill_om_fom_hdr(oh, 0, NM_MT_BS11_LMT_LOGOFF, - NM_OC_BS11_BTSE, 0xff, 0xff, 0xff); - } - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_bs11_set_trx1_pw(struct gsm_bts *bts, const char *password) -{ - struct abis_om_hdr *oh; - struct msgb *msg; - - if (strlen(password) != 10) - return -EINVAL; - - msg = nm_msgb_alloc(); - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 2+strlen(password), NM_MT_BS11_SET_ATTR, - NM_OC_BS11, BS11_OBJ_TRX1, 0x00, 0x00); - msgb_tlv_put(msg, NM_ATT_BS11_PASSWORD, 10, (const uint8_t *)password); - - return abis_nm_sendmsg(bts, msg); -} - -/* change the BS-11 PLL Mode to either locked (E1 derived) or standalone */ -int abis_nm_bs11_set_pll_locked(struct gsm_bts *bts, int locked) -{ - struct abis_om_hdr *oh; - struct msgb *msg; - uint8_t tlv_value; - - msg = nm_msgb_alloc(); - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 3, NM_MT_BS11_SET_ATTR, NM_OC_BS11, - BS11_OBJ_LI, 0x00, 0x00); - - if (locked) - tlv_value = BS11_LI_PLL_LOCKED; - else - tlv_value = BS11_LI_PLL_STANDALONE; - - msgb_tlv_put(msg, NM_ATT_BS11_PLL_MODE, 1, &tlv_value); - - return abis_nm_sendmsg(bts, msg); -} - -/* Set the calibration value of the PLL (work value/set value) - * It depends on the login which one is changed */ -int abis_nm_bs11_set_pll(struct gsm_bts *bts, int value) -{ - struct abis_om_hdr *oh; - struct msgb *msg; - uint8_t tlv_value[2]; - - msg = nm_msgb_alloc(); - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 3, NM_MT_BS11_SET_ATTR, NM_OC_BS11, - BS11_OBJ_TRX1, 0x00, 0x00); - - tlv_value[0] = value>>8; - tlv_value[1] = value&0xff; - - msgb_tlv_put(msg, NM_ATT_BS11_PLL, 2, tlv_value); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_bs11_get_state(struct gsm_bts *bts) -{ - return __simple_cmd(bts, NM_MT_BS11_GET_STATE); -} - -/* BS11 SWL */ - -void *tall_fle_ctx; - -struct abis_nm_bs11_sw { - struct gsm_bts *bts; - char swl_fname[PATH_MAX]; - uint8_t win_size; - int forced; - struct llist_head file_list; - gsm_cbfn *user_cb; /* specified by the user */ -}; -static struct abis_nm_bs11_sw _g_bs11_sw, *g_bs11_sw = &_g_bs11_sw; - -struct file_list_entry { - struct llist_head list; - char fname[PATH_MAX]; -}; - -struct file_list_entry *fl_dequeue(struct llist_head *queue) -{ - struct llist_head *lh; - - if (llist_empty(queue)) - return NULL; - - lh = queue->next; - llist_del(lh); - - return llist_entry(lh, struct file_list_entry, list); -} - -static int bs11_read_swl_file(struct abis_nm_bs11_sw *bs11_sw) -{ - char linebuf[255]; - struct llist_head *lh, *lh2; - FILE *swl; - int rc = 0; - - swl = fopen(bs11_sw->swl_fname, "r"); - if (!swl) - return -ENODEV; - - /* zero the stale file list, if any */ - llist_for_each_safe(lh, lh2, &bs11_sw->file_list) { - llist_del(lh); - talloc_free(lh); - } - - while (fgets(linebuf, sizeof(linebuf), swl)) { - char file_id[12+1]; - char file_version[80+1]; - struct file_list_entry *fle; - static char dir[PATH_MAX]; - - if (strlen(linebuf) < 4) - continue; - - rc = sscanf(linebuf+4, "%12s:%80s\r\n", file_id, file_version); - if (rc < 0) { - perror("ERR parsing SWL file"); - rc = -EINVAL; - goto out; - } - if (rc < 2) - continue; - - fle = talloc_zero(tall_fle_ctx, struct file_list_entry); - if (!fle) { - rc = -ENOMEM; - goto out; - } - - /* construct new filename */ - osmo_strlcpy(dir, bs11_sw->swl_fname, sizeof(dir)); - strncat(fle->fname, dirname(dir), sizeof(fle->fname) - 1); - strcat(fle->fname, "/"); - strncat(fle->fname, file_id, sizeof(fle->fname) - 1 -strlen(fle->fname)); - - llist_add_tail(&fle->list, &bs11_sw->file_list); - } - -out: - fclose(swl); - return rc; -} - -/* bs11 swload specific callback, passed to abis_nm core swload */ -static int bs11_swload_cbfn(unsigned int hook, unsigned int event, - struct msgb *msg, void *data, void *param) -{ - struct abis_nm_bs11_sw *bs11_sw = data; - struct file_list_entry *fle; - int rc = 0; - - switch (event) { - case NM_MT_LOAD_END_ACK: - fle = fl_dequeue(&bs11_sw->file_list); - if (fle) { - /* start download the next file of our file list */ - rc = abis_nm_software_load(bs11_sw->bts, 0xff, fle->fname, - bs11_sw->win_size, - bs11_sw->forced, - &bs11_swload_cbfn, bs11_sw); - talloc_free(fle); - } else { - /* activate the SWL */ - rc = abis_nm_software_activate(bs11_sw->bts, - bs11_sw->swl_fname, - bs11_swload_cbfn, - bs11_sw); - } - break; - case NM_MT_LOAD_SEG_ACK: - case NM_MT_LOAD_END_NACK: - case NM_MT_LOAD_INIT_ACK: - case NM_MT_LOAD_INIT_NACK: - case NM_MT_ACTIVATE_SW_NACK: - case NM_MT_ACTIVATE_SW_ACK: - default: - /* fallthrough to the user callback */ - if (bs11_sw->user_cb) - rc = bs11_sw->user_cb(hook, event, msg, NULL, NULL); - break; - } - - return rc; -} - -/* Siemens provides a SWL file that is a mere listing of all the other - * files that are part of a software release. We need to upload first - * the list file, and then each file that is listed in the list file */ -int abis_nm_bs11_load_swl(struct gsm_bts *bts, const char *fname, - uint8_t win_size, int forced, gsm_cbfn *cbfn) -{ - struct abis_nm_bs11_sw *bs11_sw = g_bs11_sw; - struct file_list_entry *fle; - int rc = 0; - - INIT_LLIST_HEAD(&bs11_sw->file_list); - bs11_sw->bts = bts; - bs11_sw->win_size = win_size; - bs11_sw->user_cb = cbfn; - bs11_sw->forced = forced; - - osmo_strlcpy(bs11_sw->swl_fname, fname, sizeof(bs11_sw->swl_fname)); - rc = bs11_read_swl_file(bs11_sw); - if (rc < 0) - return rc; - - /* dequeue next item in file list */ - fle = fl_dequeue(&bs11_sw->file_list); - if (!fle) - return -EINVAL; - - /* start download the next file of our file list */ - rc = abis_nm_software_load(bts, 0xff, fle->fname, win_size, forced, - bs11_swload_cbfn, bs11_sw); - talloc_free(fle); - return rc; -} - -#if 0 -static uint8_t req_attr_btse[] = { - NM_ATT_ADM_STATE, NM_ATT_BS11_LMT_LOGON_SESSION, - NM_ATT_BS11_LMT_LOGIN_TIME, NM_ATT_BS11_LMT_USER_ACC_LEV, - NM_ATT_BS11_LMT_USER_NAME, - - 0xaf, NM_ATT_BS11_RX_OFFSET, NM_ATT_BS11_VENDOR_NAME, - - NM_ATT_BS11_SW_LOAD_INTENDED, NM_ATT_BS11_SW_LOAD_SAFETY, - - NM_ATT_BS11_SW_LOAD_STORED }; - -static uint8_t req_attr_btsm[] = { - NM_ATT_ABIS_CHANNEL, NM_ATT_TEI, NM_ATT_BS11_ABIS_EXT_TIME, - NM_ATT_ADM_STATE, NM_ATT_AVAIL_STATUS, 0xce, NM_ATT_FILE_ID, - NM_ATT_FILE_VERSION, NM_ATT_OPER_STATE, 0xe8, NM_ATT_BS11_ALL_TEST_CATG, - NM_ATT_SW_DESCR, NM_ATT_GET_ARI }; -#endif - -static uint8_t req_attr[] = { - NM_ATT_ADM_STATE, NM_ATT_AVAIL_STATUS, 0xa8, NM_ATT_OPER_STATE, - 0xd5, 0xa1, NM_ATT_BS11_ESN_FW_CODE_NO, NM_ATT_BS11_ESN_HW_CODE_NO, - 0x42, NM_ATT_BS11_ESN_PCB_SERIAL, NM_ATT_BS11_PLL }; - -int abis_nm_bs11_get_serno(struct gsm_bts *bts) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - /* SiemensHW CCTRL object */ - fill_om_fom_hdr(oh, 2+sizeof(req_attr), NM_MT_GET_ATTR, NM_OC_BS11, - 0x03, 0x00, 0x00); - msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(req_attr), req_attr); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_bs11_set_ext_time(struct gsm_bts *bts) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - struct bs11_date_time aet; - - get_bs11_date_time(&aet); - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - /* SiemensHW CCTRL object */ - fill_om_fom_hdr(oh, 2+sizeof(aet), NM_MT_BS11_SET_ATTR, NM_OC_SITE_MANAGER, - 0xff, 0xff, 0xff); - msgb_tlv_put(msg, NM_ATT_BS11_ABIS_EXT_TIME, sizeof(aet), (uint8_t *) &aet); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_bs11_get_bport_line_cfg(struct gsm_bts *bts, uint8_t bport) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - uint8_t attr = NM_ATT_BS11_LINE_CFG; - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 2+sizeof(attr), NM_MT_GET_ATTR, - NM_OC_BS11_BPORT, bport, 0xff, 0x02); - msgb_tlv_put(msg, NM_ATT_LIST_REQ_ATTR, sizeof(attr), &attr); - - return abis_nm_sendmsg(bts, msg); -} - -int abis_nm_bs11_set_bport_line_cfg(struct gsm_bts *bts, uint8_t bport, enum abis_bs11_line_cfg line_cfg) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - struct bs11_date_time aet; - - get_bs11_date_time(&aet); - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 2, NM_MT_BS11_SET_ATTR, NM_OC_BS11_BPORT, - bport, 0xff, 0x02); - msgb_tv_put(msg, NM_ATT_BS11_LINE_CFG, line_cfg); - - return abis_nm_sendmsg(bts, msg); -} - -/* ip.access nanoBTS specific commands */ -static const char ipaccess_magic[] = "com.ipaccess"; - - -static int abis_nm_rx_ipacc(struct msgb *msg) -{ - struct in_addr addr; - struct abis_om_hdr *oh = msgb_l2(msg); - struct abis_om_fom_hdr *foh; - uint8_t idstrlen = oh->data[0]; - struct tlv_parsed tp; - struct ipacc_ack_signal_data signal; - struct e1inp_sign_link *sign_link = msg->dst; - - if (strncmp((char *)&oh->data[1], ipaccess_magic, idstrlen)) { - LOGP(DNM, LOGL_ERROR, "id string is not com.ipaccess !?!\n"); - return -EINVAL; - } - - foh = (struct abis_om_fom_hdr *) (oh->data + 1 + idstrlen); - abis_nm_tlv_parse(&tp, sign_link->trx->bts, foh->data, oh->length-sizeof(*foh)); - - abis_nm_debugp_foh(DNM, foh); - - DEBUGPC(DNM, "IPACCESS(0x%02x): ", foh->msg_type); - - switch (foh->msg_type) { - case NM_MT_IPACC_RSL_CONNECT_ACK: - DEBUGPC(DNM, "RSL CONNECT ACK "); - if (TLVP_PRESENT(&tp, NM_ATT_IPACC_DST_IP)) { - memcpy(&addr, - TLVP_VAL(&tp, NM_ATT_IPACC_DST_IP), sizeof(addr)); - - DEBUGPC(DNM, "IP=%s ", inet_ntoa(addr)); - } - if (TLVP_PRESENT(&tp, NM_ATT_IPACC_DST_IP_PORT)) - DEBUGPC(DNM, "PORT=%u ", - ntohs(*((uint16_t *) - TLVP_VAL(&tp, NM_ATT_IPACC_DST_IP_PORT)))); - if (TLVP_PRESENT(&tp, NM_ATT_IPACC_STREAM_ID)) - DEBUGPC(DNM, "STREAM=0x%02x ", - *TLVP_VAL(&tp, NM_ATT_IPACC_STREAM_ID)); - DEBUGPC(DNM, "\n"); - break; - case NM_MT_IPACC_RSL_CONNECT_NACK: - LOGP(DNM, LOGL_ERROR, "RSL CONNECT NACK "); - if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES)) - LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n", - abis_nm_nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES))); - else - LOGPC(DNM, LOGL_ERROR, "\n"); - break; - case NM_MT_IPACC_SET_NVATTR_ACK: - DEBUGPC(DNM, "SET NVATTR ACK\n"); - /* FIXME: decode and show the actual attributes */ - break; - case NM_MT_IPACC_SET_NVATTR_NACK: - LOGP(DNM, LOGL_ERROR, "SET NVATTR NACK "); - if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES)) - LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n", - abis_nm_nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES))); - else - LOGPC(DNM, LOGL_ERROR, "\n"); - break; - case NM_MT_IPACC_GET_NVATTR_ACK: - DEBUGPC(DNM, "GET NVATTR ACK\n"); - /* FIXME: decode and show the actual attributes */ - break; - case NM_MT_IPACC_GET_NVATTR_NACK: - LOGPC(DNM, LOGL_ERROR, "GET NVATTR NACK "); - if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES)) - LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n", - abis_nm_nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES))); - else - LOGPC(DNM, LOGL_ERROR, "\n"); - break; - case NM_MT_IPACC_SET_ATTR_ACK: - DEBUGPC(DNM, "SET ATTR ACK\n"); - break; - case NM_MT_IPACC_SET_ATTR_NACK: - LOGPC(DNM, LOGL_ERROR, "SET ATTR NACK "); - if (TLVP_PRESENT(&tp, NM_ATT_NACK_CAUSES)) - LOGPC(DNM, LOGL_ERROR, " CAUSE=%s\n", - abis_nm_nack_cause_name(*TLVP_VAL(&tp, NM_ATT_NACK_CAUSES))); - else - LOGPC(DNM, LOGL_ERROR, "\n"); - break; - default: - DEBUGPC(DNM, "unknown\n"); - break; - } - - /* signal handling */ - switch (foh->msg_type) { - case NM_MT_IPACC_RSL_CONNECT_NACK: - case NM_MT_IPACC_SET_NVATTR_NACK: - case NM_MT_IPACC_GET_NVATTR_NACK: - signal.trx = gsm_bts_trx_by_nr(sign_link->trx->bts, foh->obj_inst.trx_nr); - signal.msg_type = foh->msg_type; - osmo_signal_dispatch(SS_NM, S_NM_IPACC_NACK, &signal); - break; - case NM_MT_IPACC_SET_NVATTR_ACK: - signal.trx = gsm_bts_trx_by_nr(sign_link->trx->bts, foh->obj_inst.trx_nr); - signal.msg_type = foh->msg_type; - osmo_signal_dispatch(SS_NM, S_NM_IPACC_ACK, &signal); - break; - default: - break; - } - - return 0; -} - -/* send an ip-access manufacturer specific message */ -int abis_nm_ipaccess_msg(struct gsm_bts *bts, uint8_t msg_type, - uint8_t obj_class, uint8_t bts_nr, - uint8_t trx_nr, uint8_t ts_nr, - uint8_t *attr, int attr_len) -{ - struct msgb *msg = nm_msgb_alloc(); - struct abis_om_hdr *oh; - struct abis_om_fom_hdr *foh; - uint8_t *data; - - /* construct the 12.21 OM header, observe the erroneous length */ - oh = (struct abis_om_hdr *) msgb_put(msg, sizeof(*oh)); - fill_om_hdr(oh, sizeof(*foh) + attr_len); - oh->mdisc = ABIS_OM_MDISC_MANUF; - - /* add the ip.access magic */ - data = msgb_put(msg, sizeof(ipaccess_magic)+1); - *data++ = sizeof(ipaccess_magic); - memcpy(data, ipaccess_magic, sizeof(ipaccess_magic)); - - /* fill the 12.21 FOM header */ - foh = (struct abis_om_fom_hdr *) msgb_put(msg, sizeof(*foh)); - foh->msg_type = msg_type; - foh->obj_class = obj_class; - foh->obj_inst.bts_nr = bts_nr; - foh->obj_inst.trx_nr = trx_nr; - foh->obj_inst.ts_nr = ts_nr; - - if (attr && attr_len) { - data = msgb_put(msg, attr_len); - memcpy(data, attr, attr_len); - } - - return abis_nm_sendmsg(bts, msg); -} - -/* set some attributes in NVRAM */ -int abis_nm_ipaccess_set_nvattr(struct gsm_bts_trx *trx, uint8_t *attr, - int attr_len) -{ - return abis_nm_ipaccess_msg(trx->bts, NM_MT_IPACC_SET_NVATTR, - NM_OC_BASEB_TRANSC, 0, trx->nr, 0xff, attr, - attr_len); -} - -int abis_nm_ipaccess_rsl_connect(struct gsm_bts_trx *trx, - uint32_t ip, uint16_t port, uint8_t stream) -{ - struct in_addr ia; - uint8_t attr[] = { NM_ATT_IPACC_STREAM_ID, 0, - NM_ATT_IPACC_DST_IP_PORT, 0, 0, - NM_ATT_IPACC_DST_IP, 0, 0, 0, 0 }; - - int attr_len = sizeof(attr); - - ia.s_addr = htonl(ip); - attr[1] = stream; - attr[3] = port >> 8; - attr[4] = port & 0xff; - *(uint32_t *)(attr+6) = ia.s_addr; - - /* if ip == 0, we use the default IP */ - if (ip == 0) - attr_len -= 5; - - DEBUGP(DNM, "ip.access RSL CONNECT IP=%s PORT=%u STREAM=0x%02x\n", - inet_ntoa(ia), port, stream); - - return abis_nm_ipaccess_msg(trx->bts, NM_MT_IPACC_RSL_CONNECT, - NM_OC_BASEB_TRANSC, trx->bts->bts_nr, - trx->nr, 0xff, attr, attr_len); -} - -/* restart / reboot an ip.access nanoBTS */ -int abis_nm_ipaccess_restart(struct gsm_bts_trx *trx) -{ - struct abis_om_hdr *oh; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *) msgb_put(msg, ABIS_OM_FOM_HDR_SIZE); - fill_om_fom_hdr(oh, 0, NM_MT_IPACC_RESTART, NM_OC_BASEB_TRANSC, - trx->bts->nr, trx->nr, 0xff); - - return abis_nm_sendmsg_direct(trx->bts, msg); -} - -int abis_nm_ipaccess_set_attr(struct gsm_bts *bts, uint8_t obj_class, - uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr, - uint8_t *attr, uint8_t attr_len) -{ - return abis_nm_ipaccess_msg(bts, NM_MT_IPACC_SET_ATTR, - obj_class, bts_nr, trx_nr, ts_nr, - attr, attr_len); -} - -void abis_nm_ipaccess_cgi(uint8_t *buf, struct gsm_bts *bts) -{ - /* we simply reuse the GSM48 function and overwrite the RAC - * with the Cell ID */ - gsm48_ra_id_by_bts(buf, bts); - *((uint16_t *)(buf + 5)) = htons(bts->cell_identity); -} - -void gsm_trx_lock_rf(struct gsm_bts_trx *trx, int locked) -{ - int new_state = locked ? NM_STATE_LOCKED : NM_STATE_UNLOCKED; - - trx->mo.nm_state.administrative = new_state; - if (!trx->bts || !trx->bts->oml_link) - return; - - abis_nm_chg_adm_state(trx->bts, NM_OC_RADIO_CARRIER, - trx->bts->bts_nr, trx->nr, 0xff, - new_state); -} - -static const struct value_string ipacc_testres_names[] = { - { NM_IPACC_TESTRES_SUCCESS, "SUCCESS" }, - { NM_IPACC_TESTRES_TIMEOUT, "TIMEOUT" }, - { NM_IPACC_TESTRES_NO_CHANS, "NO CHANNELS" }, - { NM_IPACC_TESTRES_PARTIAL, "PARTIAL" }, - { NM_IPACC_TESTRES_STOPPED, "STOPPED" }, - { 0, NULL } -}; - -const char *ipacc_testres_name(uint8_t res) -{ - return get_value_string(ipacc_testres_names, res); -} - -void ipac_parse_cgi(struct cell_global_id *cid, const uint8_t *buf) -{ - cid->mcc = (buf[0] & 0xf) * 100; - cid->mcc += (buf[0] >> 4) * 10; - cid->mcc += (buf[1] & 0xf) * 1; - - if (buf[1] >> 4 == 0xf) { - cid->mnc = (buf[2] & 0xf) * 10; - cid->mnc += (buf[2] >> 4) * 1; - } else { - cid->mnc = (buf[2] & 0xf) * 100; - cid->mnc += (buf[2] >> 4) * 10; - cid->mnc += (buf[1] >> 4) * 1; - } - - cid->lac = ntohs(*((uint16_t *)&buf[3])); - cid->ci = ntohs(*((uint16_t *)&buf[5])); -} - -/* parse BCCH information IEI from wire format to struct ipac_bcch_info */ -int ipac_parse_bcch_info(struct ipac_bcch_info *binf, uint8_t *buf) -{ - uint8_t *cur = buf; - uint16_t len __attribute__((unused)); - - memset(binf, 0, sizeof(*binf)); - - if (cur[0] != NM_IPAC_EIE_BCCH_INFO) - return -EINVAL; - cur++; - - len = ntohs(*(uint16_t *)cur); - cur += 2; - - binf->info_type = ntohs(*(uint16_t *)cur); - cur += 2; - - if (binf->info_type & IPAC_BINF_FREQ_ERR_QUAL) - binf->freq_qual = *cur >> 2; - - binf->arfcn = (*cur++ & 3) << 8; - binf->arfcn |= *cur++; - - if (binf->info_type & IPAC_BINF_RXLEV) - binf->rx_lev = *cur & 0x3f; - cur++; - - if (binf->info_type & IPAC_BINF_RXQUAL) - binf->rx_qual = *cur & 0x7; - cur++; - - if (binf->info_type & IPAC_BINF_FREQ_ERR_QUAL) - binf->freq_err = ntohs(*(uint16_t *)cur); - cur += 2; - - if (binf->info_type & IPAC_BINF_FRAME_OFFSET) - binf->frame_offset = ntohs(*(uint16_t *)cur); - cur += 2; - - if (binf->info_type & IPAC_BINF_FRAME_NR_OFFSET) - binf->frame_nr_offset = ntohl(*(uint32_t *)cur); - cur += 4; - -#if 0 - /* Somehow this is not set correctly */ - if (binf->info_type & IPAC_BINF_BSIC) -#endif - binf->bsic = *cur & 0x3f; - cur++; - - ipac_parse_cgi(&binf->cgi, cur); - cur += 7; - - if (binf->info_type & IPAC_BINF_NEIGH_BA_SI2) { - memcpy(binf->ba_list_si2, cur, sizeof(binf->ba_list_si2)); - cur += sizeof(binf->ba_list_si2); - } - - if (binf->info_type & IPAC_BINF_NEIGH_BA_SI2bis) { - memcpy(binf->ba_list_si2bis, cur, - sizeof(binf->ba_list_si2bis)); - cur += sizeof(binf->ba_list_si2bis); - } - - if (binf->info_type & IPAC_BINF_NEIGH_BA_SI2ter) { - memcpy(binf->ba_list_si2ter, cur, - sizeof(binf->ba_list_si2ter)); - cur += sizeof(binf->ba_list_si2ter); - } - - return 0; -} - -void abis_nm_clear_queue(struct gsm_bts *bts) -{ - struct msgb *msg; - - while (!llist_empty(&bts->abis_queue)) { - msg = msgb_dequeue(&bts->abis_queue); - msgb_free(msg); - } - - bts->abis_nm_pend = 0; -} diff --git a/openbsc/src/libbsc/abis_nm_ipaccess.c b/openbsc/src/libbsc/abis_nm_ipaccess.c deleted file mode 100644 index b8225383a..000000000 --- a/openbsc/src/libbsc/abis_nm_ipaccess.c +++ /dev/null @@ -1,87 +0,0 @@ -/* GSM Network Management (OML) messages on the A-bis interface - * Extensions for the ip.access A-bis over IP protocol*/ - -/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org> - * - * 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 <http://www.gnu.org/licenses/>. - * - */ - -/* A list of all the 'embedded' attributes of ip.access */ -enum ipa_embedded_att { - IPA_ATT_ARFCN_WHITELIST = 0x01, - IPA_ATT_ARFCN_BLACKLIST = 0x02, - IPA_ATT_FREQ_ERR_LIST = 0x03, - IPA_ATT_CHAN_USAGE_LIST = 0x04, - IPA_ATT_BCCH_INF_TYPE = 0x05, - IPA_ATT_BCCH_INF = 0x06, - IPA_ATT_CONFIG = 0x07, - IPA_ATT_RESULT_DETAILS = 0x08, - IPA_ATT_RXLEV_THRESH = 0x09, - IPA_ATT_FREQ_SYNC_OPT = 0x0a, - IPA_ATT_MAC_ADDR = 0x0b, - IPA_ATT_HW_SW_COMPAT_NR = 0x0c, - IPA_ATT_MANUF_SER_NR = 0x0d, - IPA_ATT_OEM_ID = 0x0e, - IPA_ATT_DATETIME_MANUF = 0x0f, - IPA_ATT_DATETIME_CALIB = 0x10, - IPA_ATT_BEACON_INF = 0x11, - IPA_ATT_FREQ_ERR = 0x12, - IPA_ATT_SNMP_COMM_STRING = 0x13, - IPA_ATT_SNMP_TRAP_ADDR = 0x14, - IPA_ATT_SNMP_TRAP_PORT = 0x15, - IPA_ATT_SNMP_MAN_ADDR = 0x16, - IPA_ATT_SNMP_SYS_CONTACT = 0x17, - IPA_ATT_FACTORY_ID = 0x18, - IPA_ATT_FACTORY_SERIAL = 0x19, - IPA_ATT_LOGGED_EVT_IND = 0x1a, - IPA_ATT_LOCAL_ADD_TEXT = 0x1b, - IPA_ATT_FREQ_BANDS = 0x1c, - IPA_ATT_MAX_TA = 0x1d, - IPA_ATT_CIPH_ALG = 0x1e, - IPA_ATT_CHAN_TYPES = 0x1f, - IPA_ATT_CHAN_MODES = 0x20, - IPA_ATT_GPRS_CODING_SCHEMES = 0x21, - IPA_ATT_RTP_FEATURES = 0x22, - IPA_ATT_RSL_FEATURES = 0x23, - IPA_ATT_BTS_HW_CLASS = 0x24, - IPA_ATT_BTS_ID = 0x25, - IPA_ATT_BCAST_L2_MSG = 0x26, -}; - -/* append an ip.access channel list to the given msgb */ -static int ipa_chan_list_append(struct msgb *msg, uint8_t ie, - uint16_t *arfcns, int arfcn_count) -{ - int i; - uint8_t *u8; - uint16_t *u16; - - /* tag */ - u8 = msgb_push(msg, 1); - *u8 = ie; - - /* length in octets */ - u16 = msgb_push(msg, 2); - *u16 = htons(arfcn_count * 2); - - for (i = 0; i < arfcn_count; i++) { - u16 = msgb_push(msg, 2); - *u16 = htons(arfcns[i]); - } - - return 0; -} diff --git a/openbsc/src/libbsc/abis_nm_vty.c b/openbsc/src/libbsc/abis_nm_vty.c deleted file mode 100644 index 6ec0a4a21..000000000 --- a/openbsc/src/libbsc/abis_nm_vty.c +++ /dev/null @@ -1,191 +0,0 @@ -/* VTY interface for A-bis OML (Netowrk Management) */ - -/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org> - * - * 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 <http://www.gnu.org/licenses/>. - * - */ - -#include <stdlib.h> -#include <unistd.h> -#include <errno.h> -#include <stdint.h> - -#include <arpa/inet.h> - -#include <osmocom/gsm/abis_nm.h> - -#include <openbsc/gsm_data.h> -#include <osmocom/core/msgb.h> -#include <osmocom/gsm/tlv.h> -#include <osmocom/core/talloc.h> -#include <openbsc/debug.h> -#include <openbsc/signal.h> -#include <openbsc/abis_nm.h> -#include <openbsc/vty.h> - -#include <osmocom/vty/vty.h> -#include <osmocom/vty/command.h> -#include <osmocom/vty/logging.h> -#include <osmocom/vty/telnet_interface.h> - -extern struct gsm_network *bsc_gsmnet; - -static struct cmd_node oml_node = { - OML_NODE, - "%s(oml)# ", - 1, -}; - -struct oml_node_state { - struct gsm_bts *bts; - uint8_t obj_class; - uint8_t obj_inst[3]; -}; - -static int dummy_config_write(struct vty *v) -{ - return CMD_SUCCESS; -} - -/* FIXME: auto-generate those strings from the value_string lists */ -#define NM_OBJCLASS_VTY "(site-manager|bts|radio-carrier|baseband-transceiver|channel|adjc|handover|power-contorl|btse|rack|test|envabtse|bport|gprs-nse|gprs-cell|gprs-nsvc|siemenshw)" -#define NM_OBJCLASS_VTY_HELP "Site Manager Object\n" \ - "BTS Object\n" \ - "Radio Carrier Object\n" \ - "Baseband Transceiver Object\n" \ - "Channel (Timeslot) Object\n" \ - "Adjacent Object (Siemens)\n" \ - "Handover Object (Siemens)\n" \ - "Power Control Object (Siemens)\n" \ - "BTSE Object (Siemens)\n" \ - "Rack Object (Siemens)\n" \ - "Test Object (Siemens)\n" \ - "ENVABTSE Object (Siemens)\n" \ - "BPORT Object (Siemens)\n" \ - "GPRS NSE Object (ip.access/osmo-bts)\n" \ - "GPRS Cell Object (ip.acecss/osmo-bts)\n" \ - "GPRS NSVC Object (ip.acecss/osmo-bts)\n" \ - "SIEMENSHW Object (Siemens)\n" - - -DEFUN(oml_class_inst, oml_class_inst_cmd, - "bts <0-255> oml class " NM_OBJCLASS_VTY - " instance <0-255> <0-255> <0-255>", - "BTS related commands\n" "BTS Number\n" - "Manipulate the OML managed objects\n" - "Object Class\n" NM_OBJCLASS_VTY_HELP - "Object Instance\n" "BTS Number\n" "TRX Number\n" "TS Number\n") -{ - struct gsm_bts *bts; - struct oml_node_state *oms; - int bts_nr = atoi(argv[0]); - - bts = gsm_bts_num(gsmnet_from_vty(vty), bts_nr); - if (!bts) { - vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE); - return CMD_WARNING; - } - - oms = talloc_zero(tall_bsc_ctx, struct oml_node_state); - if (!oms) - return CMD_WARNING; - - oms->bts = bts; - oms->obj_class = get_string_value(abis_nm_obj_class_names, argv[1]); - oms->obj_inst[0] = atoi(argv[2]); - oms->obj_inst[1] = atoi(argv[3]); - oms->obj_inst[2] = atoi(argv[4]); - - vty->index = oms; - vty->node = OML_NODE; - - return CMD_SUCCESS; - -} - -DEFUN(oml_classnum_inst, oml_classnum_inst_cmd, - "bts <0-255> oml class <0-255> instance <0-255> <0-255> <0-255>", - "BTS related commands\n" "BTS Number\n" - "Manipulate the OML managed objects\n" - "Object Class\n" "Object Class\n" - "Object Instance\n" "BTS Number\n" "TRX Number\n" "TS Number\n") -{ - struct gsm_bts *bts; - struct oml_node_state *oms; - int bts_nr = atoi(argv[0]); - - bts = gsm_bts_num(gsmnet_from_vty(vty), bts_nr); - if (!bts) { - vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE); - return CMD_WARNING; - } - - oms = talloc_zero(tall_bsc_ctx, struct oml_node_state); - if (!oms) - return CMD_WARNING; - - oms->bts = bts; - oms->obj_class = atoi(argv[1]); - oms->obj_inst[0] = atoi(argv[2]); - oms->obj_inst[1] = atoi(argv[3]); - oms->obj_inst[2] = atoi(argv[4]); - - vty->index = oms; - vty->node = OML_NODE; - - return CMD_SUCCESS; -} - -DEFUN(oml_chg_adm_state, oml_chg_adm_state_cmd, - "change-adm-state (locked|unlocked|shutdown|null)", - "Change the Administrative State\n" - "Locked\n" "Unlocked\n" "Shutdown\n" "NULL\n") -{ - struct oml_node_state *oms = vty->index; - enum abis_nm_adm_state state; - - state = get_string_value(abis_nm_adm_state_names, argv[0]); - - abis_nm_chg_adm_state(oms->bts, oms->obj_class, oms->obj_inst[0], - oms->obj_inst[1], oms->obj_inst[2], state); - - return CMD_SUCCESS; -} - -DEFUN(oml_opstart, oml_opstart_cmd, - "opstart", "Send an OPSTART message to the object") -{ - struct oml_node_state *oms = vty->index; - - abis_nm_opstart(oms->bts, oms->obj_class, oms->obj_inst[0], - oms->obj_inst[1], oms->obj_inst[2]); - - return CMD_SUCCESS; -} - -int abis_nm_vty_init(void) -{ - install_element(ENABLE_NODE, &oml_class_inst_cmd); - install_element(ENABLE_NODE, &oml_classnum_inst_cmd); - install_node(&oml_node, dummy_config_write); - - vty_install_default(OML_NODE); - install_element(OML_NODE, &oml_chg_adm_state_cmd); - install_element(OML_NODE, &oml_opstart_cmd); - - return 0; -} diff --git a/openbsc/src/libbsc/abis_om2000.c b/openbsc/src/libbsc/abis_om2000.c deleted file mode 100644 index 82a14b269..000000000 --- a/openbsc/src/libbsc/abis_om2000.c +++ /dev/null @@ -1,2776 +0,0 @@ -/* Ericsson RBS 2xxx GSM O&M (OM2000) messages on the A-bis interface - * implemented based on protocol trace analysis, no formal documentation */ - -/* (C) 2010-2011,2016 by Harald Welte <laforge@gnumonks.org> - * - * 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 <http://www.gnu.org/licenses/>. - * - */ - - -#include <errno.h> -#include <unistd.h> -#include <stdio.h> -#include <stdlib.h> -#include <time.h> -#include <stdint.h> - -#include <arpa/inet.h> - -#include <osmocom/core/msgb.h> -#include <osmocom/gsm/tlv.h> -#include <osmocom/core/talloc.h> -#include <osmocom/core/utils.h> -#include <osmocom/core/fsm.h> - -#include <openbsc/gsm_data.h> -#include <openbsc/debug.h> -#include <openbsc/abis_nm.h> -#include <openbsc/abis_rsl.h> -#include <openbsc/abis_om2000.h> -#include <openbsc/signal.h> -#include <osmocom/abis/e1_input.h> - -/* FIXME: move to libosmocore */ -struct osmo_fsm_inst *osmo_fsm_inst_alloc_child_id(struct osmo_fsm *fsm, - struct osmo_fsm_inst *parent, - uint32_t parent_term_event, - const char *id) -{ - struct osmo_fsm_inst *fi; - - fi = osmo_fsm_inst_alloc(fsm, parent, NULL, parent->log_level, - id ? id : parent->id); - if (!fi) { - /* indicate immediate termination to caller */ - osmo_fsm_inst_dispatch(parent, parent_term_event, NULL); - return NULL; - } - - LOGPFSM(fi, "is child of %s\n", osmo_fsm_inst_name(parent)); - - fi->proc.parent = parent; - fi->proc.parent_term_event = parent_term_event; - llist_add(&fi->proc.child, &parent->proc.children); - - return fi; -} - - -#define OM_ALLOC_SIZE 1024 -#define OM_HEADROOM_SIZE 128 - -#define OM2K_TIMEOUT 10 -#define TRX_FSM_TIMEOUT 60 -#define BTS_FSM_TIMEOUT 60 - -/* use following functions from abis_nm.c: - * om2k_msgb_alloc() - * abis_om2k_sendmsg() - */ - -struct abis_om2k_hdr { - struct abis_om_hdr om; - uint16_t msg_type; - struct abis_om2k_mo mo; - uint8_t data[0]; -} __attribute__ ((packed)); - -enum abis_om2k_msgtype { - OM2K_MSGT_ABORT_SP_CMD = 0x0000, - OM2K_MSGT_ABORT_SP_COMPL = 0x0002, - OM2K_MSGT_ALARM_REP_ACK = 0x0004, - OM2K_MSGT_ALARM_REP_NACK = 0x0005, - OM2K_MSGT_ALARM_REP = 0x0006, - OM2K_MSGT_ALARM_STATUS_REQ = 0x0008, - OM2K_MSGT_ALARM_STATUS_REQ_ACK = 0x000a, - OM2K_MSGT_ALARM_STATUS_REQ_REJ = 0x000b, - OM2K_MSGT_ALARM_STATUS_RES_ACK = 0x000c, - OM2K_MSGT_ALARM_STATUS_RES_NACK = 0x000d, - OM2K_MSGT_ALARM_STATUS_RES = 0x000e, - OM2K_MSGT_CAL_TIME_RESP = 0x0010, - OM2K_MSGT_CAL_TIME_REJ = 0x0011, - OM2K_MSGT_CAL_TIME_REQ = 0x0012, - - OM2K_MSGT_CON_CONF_REQ = 0x0014, - OM2K_MSGT_CON_CONF_REQ_ACK = 0x0016, - OM2K_MSGT_CON_CONF_REQ_REJ = 0x0017, - OM2K_MSGT_CON_CONF_RES_ACK = 0x0018, - OM2K_MSGT_CON_CONF_RES_NACK = 0x0019, - OM2K_MSGT_CON_CONF_RES = 0x001a, - - OM2K_MSGT_CONNECT_CMD = 0x001c, - OM2K_MSGT_CONNECT_COMPL = 0x001e, - OM2K_MSGT_CONNECT_REJ = 0x001f, - - OM2K_MSGT_DISABLE_REQ = 0x0028, - OM2K_MSGT_DISABLE_REQ_ACK = 0x002a, - OM2K_MSGT_DISABLE_REQ_REJ = 0x002b, - OM2K_MSGT_DISABLE_RES_ACK = 0x002c, - OM2K_MSGT_DISABLE_RES_NACK = 0x002d, - OM2K_MSGT_DISABLE_RES = 0x002e, - OM2K_MSGT_DISCONNECT_CMD = 0x0030, - OM2K_MSGT_DISCONNECT_COMPL = 0x0032, - OM2K_MSGT_DISCONNECT_REJ = 0x0033, - OM2K_MSGT_ENABLE_REQ = 0x0034, - OM2K_MSGT_ENABLE_REQ_ACK = 0x0036, - OM2K_MSGT_ENABLE_REQ_REJ = 0x0037, - OM2K_MSGT_ENABLE_RES_ACK = 0x0038, - OM2K_MSGT_ENABLE_RES_NACK = 0x0039, - OM2K_MSGT_ENABLE_RES = 0x003a, - - OM2K_MSGT_FAULT_REP_ACK = 0x0040, - OM2K_MSGT_FAULT_REP_NACK = 0x0041, - OM2K_MSGT_FAULT_REP = 0x0042, - - OM2K_MSGT_IS_CONF_REQ = 0x0060, - OM2K_MSGT_IS_CONF_REQ_ACK = 0x0062, - OM2K_MSGT_IS_CONF_REQ_REJ = 0x0063, - OM2K_MSGT_IS_CONF_RES_ACK = 0x0064, - OM2K_MSGT_IS_CONF_RES_NACK = 0x0065, - OM2K_MSGT_IS_CONF_RES = 0x0066, - - OM2K_MSGT_OP_INFO = 0x0074, - OM2K_MSGT_OP_INFO_ACK = 0x0076, - OM2K_MSGT_OP_INFO_REJ = 0x0077, - OM2K_MSGT_RESET_CMD = 0x0078, - OM2K_MSGT_RESET_COMPL = 0x007a, - OM2K_MSGT_RESET_REJ = 0x007b, - OM2K_MSGT_RX_CONF_REQ = 0x007c, - OM2K_MSGT_RX_CONF_REQ_ACK = 0x007e, - OM2K_MSGT_RX_CONF_REQ_REJ = 0x007f, - OM2K_MSGT_RX_CONF_RES_ACK = 0x0080, - OM2K_MSGT_RX_CONF_RES_NACK = 0x0081, - OM2K_MSGT_RX_CONF_RES = 0x0082, - OM2K_MSGT_START_REQ = 0x0084, - OM2K_MSGT_START_REQ_ACK = 0x0086, - OM2K_MSGT_START_REQ_REJ = 0x0087, - OM2K_MSGT_START_RES_ACK = 0x0088, - OM2K_MSGT_START_RES_NACK = 0x0089, - OM2K_MSGT_START_RES = 0x008a, - OM2K_MSGT_STATUS_REQ = 0x008c, - OM2K_MSGT_STATUS_RESP = 0x008e, - OM2K_MSGT_STATUS_REJ = 0x008f, - - OM2K_MSGT_TEST_REQ = 0x0094, - OM2K_MSGT_TEST_REQ_ACK = 0x0096, - OM2K_MSGT_TEST_REQ_REJ = 0x0097, - OM2K_MSGT_TEST_RES_ACK = 0x0098, - OM2K_MSGT_TEST_RES_NACK = 0x0099, - OM2K_MSGT_TEST_RES = 0x009a, - - OM2K_MSGT_TF_CONF_REQ = 0x00a0, - OM2K_MSGT_TF_CONF_REQ_ACK = 0x00a2, - OM2K_MSGT_TF_CONF_REQ_REJ = 0x00a3, - OM2K_MSGT_TF_CONF_RES_ACK = 0x00a4, - OM2K_MSGT_TF_CONF_RES_NACK = 0x00a5, - OM2K_MSGT_TF_CONF_RES = 0x00a6, - OM2K_MSGT_TS_CONF_REQ = 0x00a8, - OM2K_MSGT_TS_CONF_REQ_ACK = 0x00aa, - OM2K_MSGT_TS_CONF_REQ_REJ = 0x00ab, - OM2K_MSGT_TS_CONF_RES_ACK = 0x00ac, - OM2K_MSGT_TS_CONF_RES_NACK = 0x00ad, - OM2K_MSGT_TS_CONF_RES = 0x00ae, - OM2K_MSGT_TX_CONF_REQ = 0x00b0, - OM2K_MSGT_TX_CONF_REQ_ACK = 0x00b2, - OM2K_MSGT_TX_CONF_REQ_REJ = 0x00b3, - OM2K_MSGT_TX_CONF_RES_ACK = 0x00b4, - OM2K_MSGT_TX_CONF_RES_NACK = 0x00b5, - OM2K_MSGT_TX_CONF_RES = 0x00b6, - - OM2K_MSGT_CAPA_REQ = 0x00e8, - OM2K_MSGT_CAPA_REQ_ACK = 0x00ea, - OM2K_MSGT_CAPA_REQ_REJ = 0x00eb, - OM2K_MSGT_CAPA_RES = 0x00ee, - OM2K_MSGT_CAPA_RES_ACK = 0x00ec, - OM2K_MSGT_CAPA_RES_NACK = 0x00ed, - - OM2K_MSGT_NEGOT_REQ_ACK = 0x0104, - OM2K_MSGT_NEGOT_REQ_NACK = 0x0105, - OM2K_MSGT_NEGOT_REQ = 0x0106, -}; - -enum abis_om2k_dei { - OM2K_DEI_ACCORDANCE_IND = 0x00, - OM2K_DEI_BCC = 0x06, - OM2K_DEI_BS_AG_BKS_RES = 0x07, - OM2K_DEI_BSIC = 0x09, - OM2K_DEI_BA_PA_MFRMS = 0x0a, - OM2K_DEI_CBCH_INDICATOR = 0x0b, - OM2K_DEI_CCCH_OPTIONS = 0x0c, - OM2K_DEI_CAL_TIME = 0x0d, - OM2K_DEI_COMBINATION = 0x0f, - OM2K_DEI_CON_CONN_LIST = 0x10, - OM2K_DEI_DRX_DEV_MAX = 0x12, - OM2K_DEI_END_LIST_NR = 0x13, - OM2K_DEI_EXT_COND_MAP_1 = 0x14, - OM2K_DEI_EXT_COND_MAP_2 = 0x15, - OM2K_DEI_FILLING_MARKER = 0x1c, - OM2K_DEI_FN_OFFSET = 0x1d, - OM2K_DEI_FREQ_LIST = 0x1e, - OM2K_DEI_FREQ_SPEC_RX = 0x1f, - OM2K_DEI_FREQ_SPEC_TX = 0x20, - OM2K_DEI_HSN = 0x21, - OM2K_DEI_ICM_INDICATOR = 0x22, - OM2K_DEI_INT_FAULT_MAP_1A = 0x23, - OM2K_DEI_INT_FAULT_MAP_1B = 0x24, - OM2K_DEI_INT_FAULT_MAP_2A = 0x25, - OM2K_DEI_INT_FAULT_MAP_2A_EXT = 0x26, - OM2K_DEI_IS_CONN_LIST = 0x27, - OM2K_DEI_LIST_NR = 0x28, - OM2K_DEI_LOCAL_ACCESS = 0x2a, - OM2K_DEI_MAIO = 0x2b, - OM2K_DEI_MO_STATE = 0x2c, - OM2K_DEI_NY1 = 0x2d, - OM2K_DEI_OP_INFO = 0x2e, - OM2K_DEI_POWER = 0x2f, - OM2K_DEI_REASON_CODE = 0x32, - OM2K_DEI_RX_DIVERSITY = 0x33, - OM2K_DEI_REPL_UNIT_MAP = 0x34, - OM2K_DEI_RESULT_CODE = 0x35, - OM2K_DEI_T3105 = 0x38, - OM2K_DEI_TF_MODE = 0x3a, - OM2K_DEI_TS_NR = 0x3c, - OM2K_DEI_TSC = 0x3d, - OM2K_DEI_BTS_VERSION = 0x40, - OM2K_DEI_OML_IWD_VERSION = 0x41, - OM2K_DEI_RSL_IWD_VERSION = 0x42, - OM2K_DEI_OML_FUNC_MAP_1 = 0x43, - OM2K_DEI_OML_FUNC_MAP_2 = 0x44, - OM2K_DEI_RSL_FUNC_MAP_1 = 0x45, - OM2K_DEI_RSL_FUNC_MAP_2 = 0x46, - OM2K_DEI_EXT_RANGE = 0x47, - OM2K_DEI_REQ_IND = 0x48, - OM2K_DEI_REPL_UNIT_MAP_EXT = 0x50, - OM2K_DEI_ICM_BOUND_PARAMS = 0x74, - OM2K_DEI_LSC = 0x79, - OM2K_DEI_LSC_FILT_TIME = 0x7a, - OM2K_DEI_CALL_SUPV_TIME = 0x7b, - OM2K_DEI_ICM_CHAN_RATE = 0x7e, - OM2K_DEI_HW_INFO_SIG = 0x84, - OM2K_DEI_TF_SYNC_SRC = 0x86, - OM2K_DEI_TTA = 0x87, - OM2K_DEI_CAPA_SIG = 0x8a, - OM2K_DEI_NEGOT_REC1 = 0x90, - OM2K_DEI_NEGOT_REC2 = 0x91, - OM2K_DEI_ENCR_ALG = 0x92, - OM2K_DEI_INTERF_REJ_COMB = 0x94, - OM2K_DEI_FS_OFFSET = 0x98, - OM2K_DEI_EXT_COND_MAP_2_EXT = 0x9c, - OM2K_DEI_TSS_MO_STATE = 0x9d, -}; - -const struct tlv_definition om2k_att_tlvdef = { - .def = { - [OM2K_DEI_ACCORDANCE_IND] = { TLV_TYPE_TV }, - [OM2K_DEI_BCC] = { TLV_TYPE_TV }, - [OM2K_DEI_BS_AG_BKS_RES] = { TLV_TYPE_TV }, - [OM2K_DEI_BSIC] = { TLV_TYPE_TV }, - [OM2K_DEI_BA_PA_MFRMS] = { TLV_TYPE_TV }, - [OM2K_DEI_CBCH_INDICATOR] = { TLV_TYPE_TV }, - [OM2K_DEI_INT_FAULT_MAP_1A] = { TLV_TYPE_FIXED, 6 }, - [OM2K_DEI_INT_FAULT_MAP_1B] = { TLV_TYPE_FIXED, 6 }, - [OM2K_DEI_INT_FAULT_MAP_2A] = { TLV_TYPE_FIXED, 6 }, - [OM2K_DEI_INT_FAULT_MAP_2A_EXT]={ TLV_TYPE_FIXED, 6 }, - [OM2K_DEI_CCCH_OPTIONS] = { TLV_TYPE_TV }, - [OM2K_DEI_CAL_TIME] = { TLV_TYPE_FIXED, 6 }, - [OM2K_DEI_COMBINATION] = { TLV_TYPE_TV }, - [OM2K_DEI_CON_CONN_LIST] = { TLV_TYPE_TLV }, - [OM2K_DEI_DRX_DEV_MAX] = { TLV_TYPE_TV }, - [OM2K_DEI_END_LIST_NR] = { TLV_TYPE_TV }, - [OM2K_DEI_EXT_COND_MAP_1] = { TLV_TYPE_FIXED, 2 }, - [OM2K_DEI_EXT_COND_MAP_2] = { TLV_TYPE_FIXED, 2 }, - [OM2K_DEI_FILLING_MARKER] = { TLV_TYPE_TV }, - [OM2K_DEI_FN_OFFSET] = { TLV_TYPE_FIXED, 2 }, - [OM2K_DEI_FREQ_LIST] = { TLV_TYPE_TLV }, - [OM2K_DEI_FREQ_SPEC_RX] = { TLV_TYPE_FIXED, 2 }, - [OM2K_DEI_FREQ_SPEC_TX] = { TLV_TYPE_FIXED, 2 }, - [OM2K_DEI_HSN] = { TLV_TYPE_TV }, - [OM2K_DEI_ICM_INDICATOR] = { TLV_TYPE_TV }, - [OM2K_DEI_IS_CONN_LIST] = { TLV_TYPE_TLV }, - [OM2K_DEI_LIST_NR] = { TLV_TYPE_TV }, - [OM2K_DEI_LOCAL_ACCESS] = { TLV_TYPE_TV }, - [OM2K_DEI_MAIO] = { TLV_TYPE_TV }, - [OM2K_DEI_MO_STATE] = { TLV_TYPE_TV }, - [OM2K_DEI_NY1] = { TLV_TYPE_TV }, - [OM2K_DEI_OP_INFO] = { TLV_TYPE_TV }, - [OM2K_DEI_POWER] = { TLV_TYPE_TV }, - [OM2K_DEI_REASON_CODE] = { TLV_TYPE_TV }, - [OM2K_DEI_RX_DIVERSITY] = { TLV_TYPE_TV }, - [OM2K_DEI_RESULT_CODE] = { TLV_TYPE_TV }, - [OM2K_DEI_T3105] = { TLV_TYPE_TV }, - [OM2K_DEI_TF_MODE] = { TLV_TYPE_TV }, - [OM2K_DEI_TS_NR] = { TLV_TYPE_TV }, - [OM2K_DEI_TSC] = { TLV_TYPE_TV }, - [OM2K_DEI_BTS_VERSION] = { TLV_TYPE_FIXED, 12 }, - [OM2K_DEI_OML_IWD_VERSION] = { TLV_TYPE_FIXED, 6 }, - [OM2K_DEI_RSL_IWD_VERSION] = { TLV_TYPE_FIXED, 6 }, - [OM2K_DEI_OML_FUNC_MAP_1] = { TLV_TYPE_TLV }, - [OM2K_DEI_OML_FUNC_MAP_2] = { TLV_TYPE_TLV }, - [OM2K_DEI_RSL_FUNC_MAP_1] = { TLV_TYPE_TLV }, - [OM2K_DEI_RSL_FUNC_MAP_2] = { TLV_TYPE_TLV }, - [OM2K_DEI_EXT_RANGE] = { TLV_TYPE_TV }, - [OM2K_DEI_REQ_IND] = { TLV_TYPE_TV }, - [OM2K_DEI_REPL_UNIT_MAP] = { TLV_TYPE_FIXED, 6 }, - [OM2K_DEI_REPL_UNIT_MAP_EXT] = {TLV_TYPE_FIXED, 6}, - [OM2K_DEI_ICM_BOUND_PARAMS] = { TLV_TYPE_FIXED, 5 }, - [OM2K_DEI_LSC] = { TLV_TYPE_TV }, - [OM2K_DEI_LSC_FILT_TIME] = { TLV_TYPE_TV }, - [OM2K_DEI_CALL_SUPV_TIME] = { TLV_TYPE_TV }, - [OM2K_DEI_ICM_CHAN_RATE] = { TLV_TYPE_TV }, - [OM2K_DEI_HW_INFO_SIG] = { TLV_TYPE_FIXED, 2 }, - [OM2K_DEI_TF_SYNC_SRC] = { TLV_TYPE_TV }, - [OM2K_DEI_TTA] = { TLV_TYPE_TV }, - [OM2K_DEI_CAPA_SIG] = { TLV_TYPE_FIXED, 2 }, - [OM2K_DEI_NEGOT_REC1] = { TLV_TYPE_TLV }, - [OM2K_DEI_NEGOT_REC2] = { TLV_TYPE_TLV }, - [OM2K_DEI_ENCR_ALG] = { TLV_TYPE_TV }, - [OM2K_DEI_INTERF_REJ_COMB] = { TLV_TYPE_TV }, - [OM2K_DEI_FS_OFFSET] = { TLV_TYPE_FIXED, 5 }, - [OM2K_DEI_EXT_COND_MAP_2_EXT] = { TLV_TYPE_FIXED, 4 }, - [OM2K_DEI_TSS_MO_STATE] = { TLV_TYPE_FIXED, 4 }, - }, -}; - -static const struct value_string om2k_msgcode_vals[] = { - { 0x0000, "Abort SP Command" }, - { 0x0002, "Abort SP Complete" }, - { 0x0004, "Alarm Report ACK" }, - { 0x0005, "Alarm Report NACK" }, - { 0x0006, "Alarm Report" }, - { 0x0008, "Alarm Status Request" }, - { 0x000a, "Alarm Status Request Accept" }, - { 0x000b, "Alarm Status Request Reject" }, - { 0x000c, "Alarm Status Result ACK" }, - { 0x000d, "Alarm Status Result NACK" }, - { 0x000e, "Alarm Status Result" }, - { 0x0010, "Calendar Time Response" }, - { 0x0011, "Calendar Time Reject" }, - { 0x0012, "Calendar Time Request" }, - { 0x0014, "CON Configuration Request" }, - { 0x0016, "CON Configuration Request Accept" }, - { 0x0017, "CON Configuration Request Reject" }, - { 0x0018, "CON Configuration Result ACK" }, - { 0x0019, "CON Configuration Result NACK" }, - { 0x001a, "CON Configuration Result" }, - { 0x001c, "Connect Command" }, - { 0x001e, "Connect Complete" }, - { 0x001f, "Connect Reject" }, - { 0x0028, "Disable Request" }, - { 0x002a, "Disable Request Accept" }, - { 0x002b, "Disable Request Reject" }, - { 0x002c, "Disable Result ACK" }, - { 0x002d, "Disable Result NACK" }, - { 0x002e, "Disable Result" }, - { 0x0030, "Disconnect Command" }, - { 0x0032, "Disconnect Complete" }, - { 0x0033, "Disconnect Reject" }, - { 0x0034, "Enable Request" }, - { 0x0036, "Enable Request Accept" }, - { 0x0037, "Enable Request Reject" }, - { 0x0038, "Enable Result ACK" }, - { 0x0039, "Enable Result NACK" }, - { 0x003a, "Enable Result" }, - { 0x003c, "Escape Downlink Normal" }, - { 0x003d, "Escape Downlink NACK" }, - { 0x003e, "Escape Uplink Normal" }, - { 0x003f, "Escape Uplink NACK" }, - { 0x0040, "Fault Report ACK" }, - { 0x0041, "Fault Report NACK" }, - { 0x0042, "Fault Report" }, - { 0x0044, "File Package End Command" }, - { 0x0046, "File Package End Result" }, - { 0x0047, "File Package End Reject" }, - { 0x0048, "File Relation Request" }, - { 0x004a, "File Relation Response" }, - { 0x004b, "File Relation Request Reject" }, - { 0x004c, "File Segment Transfer" }, - { 0x004e, "File Segment Transfer Complete" }, - { 0x004f, "File Segment Transfer Reject" }, - { 0x0050, "HW Information Request" }, - { 0x0052, "HW Information Request Accept" }, - { 0x0053, "HW Information Request Reject" }, - { 0x0054, "HW Information Result ACK" }, - { 0x0055, "HW Information Result NACK" }, - { 0x0056, "HW Information Result" }, - { 0x0060, "IS Configuration Request" }, - { 0x0062, "IS Configuration Request Accept" }, - { 0x0063, "IS Configuration Request Reject" }, - { 0x0064, "IS Configuration Result ACK" }, - { 0x0065, "IS Configuration Result NACK" }, - { 0x0066, "IS Configuration Result" }, - { 0x0068, "Load Data End" }, - { 0x006a, "Load Data End Result" }, - { 0x006b, "Load Data End Reject" }, - { 0x006c, "Load Data Init" }, - { 0x006e, "Load Data Init Accept" }, - { 0x006f, "Load Data Init Reject" }, - { 0x0070, "Loop Control Command" }, - { 0x0072, "Loop Control Complete" }, - { 0x0073, "Loop Control Reject" }, - { 0x0074, "Operational Information" }, - { 0x0076, "Operational Information Accept" }, - { 0x0077, "Operational Information Reject" }, - { 0x0078, "Reset Command" }, - { 0x007a, "Reset Complete" }, - { 0x007b, "Reset Reject" }, - { 0x007c, "RX Configuration Request" }, - { 0x007e, "RX Configuration Request Accept" }, - { 0x007f, "RX Configuration Request Reject" }, - { 0x0080, "RX Configuration Result ACK" }, - { 0x0081, "RX Configuration Result NACK" }, - { 0x0082, "RX Configuration Result" }, - { 0x0084, "Start Request" }, - { 0x0086, "Start Request Accept" }, - { 0x0087, "Start Request Reject" }, - { 0x0088, "Start Result ACK" }, - { 0x0089, "Start Result NACK" }, - { 0x008a, "Start Result" }, - { 0x008c, "Status Request" }, - { 0x008e, "Status Response" }, - { 0x008f, "Status Reject" }, - { 0x0094, "Test Request" }, - { 0x0096, "Test Request Accept" }, - { 0x0097, "Test Request Reject" }, - { 0x0098, "Test Result ACK" }, - { 0x0099, "Test Result NACK" }, - { 0x009a, "Test Result" }, - { 0x00a0, "TF Configuration Request" }, - { 0x00a2, "TF Configuration Request Accept" }, - { 0x00a3, "TF Configuration Request Reject" }, - { 0x00a4, "TF Configuration Result ACK" }, - { 0x00a5, "TF Configuration Result NACK" }, - { 0x00a6, "TF Configuration Result" }, - { 0x00a8, "TS Configuration Request" }, - { 0x00aa, "TS Configuration Request Accept" }, - { 0x00ab, "TS Configuration Request Reject" }, - { 0x00ac, "TS Configuration Result ACK" }, - { 0x00ad, "TS Configuration Result NACK" }, - { 0x00ae, "TS Configuration Result" }, - { 0x00b0, "TX Configuration Request" }, - { 0x00b2, "TX Configuration Request Accept" }, - { 0x00b3, "TX Configuration Request Reject" }, - { 0x00b4, "TX Configuration Result ACK" }, - { 0x00b5, "TX Configuration Result NACK" }, - { 0x00b6, "TX Configuration Result" }, - { 0x00bc, "DIP Alarm Report ACK" }, - { 0x00bd, "DIP Alarm Report NACK" }, - { 0x00be, "DIP Alarm Report" }, - { 0x00c0, "DIP Alarm Status Request" }, - { 0x00c2, "DIP Alarm Status Response" }, - { 0x00c3, "DIP Alarm Status Reject" }, - { 0x00c4, "DIP Quality Report I ACK" }, - { 0x00c5, "DIP Quality Report I NACK" }, - { 0x00c6, "DIP Quality Report I" }, - { 0x00c8, "DIP Quality Report II ACK" }, - { 0x00c9, "DIP Quality Report II NACK" }, - { 0x00ca, "DIP Quality Report II" }, - { 0x00dc, "DP Configuration Request" }, - { 0x00de, "DP Configuration Request Accept" }, - { 0x00df, "DP Configuration Request Reject" }, - { 0x00e0, "DP Configuration Result ACK" }, - { 0x00e1, "DP Configuration Result NACK" }, - { 0x00e2, "DP Configuration Result" }, - { 0x00e4, "Capabilities HW Info Report ACK" }, - { 0x00e5, "Capabilities HW Info Report NACK" }, - { 0x00e6, "Capabilities HW Info Report" }, - { 0x00e8, "Capabilities Request" }, - { 0x00ea, "Capabilities Request Accept" }, - { 0x00eb, "Capabilities Request Reject" }, - { 0x00ec, "Capabilities Result ACK" }, - { 0x00ed, "Capabilities Result NACK" }, - { 0x00ee, "Capabilities Result" }, - { 0x00f0, "FM Configuration Request" }, - { 0x00f2, "FM Configuration Request Accept" }, - { 0x00f3, "FM Configuration Request Reject" }, - { 0x00f4, "FM Configuration Result ACK" }, - { 0x00f5, "FM Configuration Result NACK" }, - { 0x00f6, "FM Configuration Result" }, - { 0x00f8, "FM Report Request" }, - { 0x00fa, "FM Report Response" }, - { 0x00fb, "FM Report Reject" }, - { 0x00fc, "FM Start Command" }, - { 0x00fe, "FM Start Complete" }, - { 0x00ff, "FM Start Reject" }, - { 0x0100, "FM Stop Command" }, - { 0x0102, "FM Stop Complete" }, - { 0x0103, "FM Stop Reject" }, - { 0x0104, "Negotiation Request ACK" }, - { 0x0105, "Negotiation Request NACK" }, - { 0x0106, "Negotiation Request" }, - { 0x0108, "BTS Initiated Request ACK" }, - { 0x0109, "BTS Initiated Request NACK" }, - { 0x010a, "BTS Initiated Request" }, - { 0x010c, "Radio Channels Release Command" }, - { 0x010e, "Radio Channels Release Complete" }, - { 0x010f, "Radio Channels Release Reject" }, - { 0x0118, "Feature Control Command" }, - { 0x011a, "Feature Control Complete" }, - { 0x011b, "Feature Control Reject" }, - - { 0, NULL } -}; - -/* TS 12.21 Section 9.4: Attributes */ -static const struct value_string om2k_attr_vals[] = { - { 0x00, "Accordance indication" }, - { 0x01, "Alarm Id" }, - { 0x02, "Alarm Data" }, - { 0x03, "Alarm Severity" }, - { 0x04, "Alarm Status" }, - { 0x05, "Alarm Status Type" }, - { 0x06, "BCC" }, - { 0x07, "BS_AG_BKS_RES" }, - { 0x09, "BSIC" }, - { 0x0a, "BA_PA_MFRMS" }, - { 0x0b, "CBCH Indicator" }, - { 0x0c, "CCCH Options" }, - { 0x0d, "Calendar Time" }, - { 0x0f, "Channel Combination" }, - { 0x10, "CON Connection List" }, - { 0x11, "Data End Indication" }, - { 0x12, "DRX_DEV_MAX" }, - { 0x13, "End List Number" }, - { 0x14, "External Condition Map Class 1" }, - { 0x15, "External Condition Map Class 2" }, - { 0x16, "File Relation Indication" }, - { 0x17, "File Revision" }, - { 0x18, "File Segment Data" }, - { 0x19, "File Segment Length" }, - { 0x1a, "File Segment Sequence Number" }, - { 0x1b, "File Size" }, - { 0x1c, "Filling Marker" }, - { 0x1d, "FN Offset" }, - { 0x1e, "Frequency List" }, - { 0x1f, "Frequency Specifier RX" }, - { 0x20, "Frequency Specifier TX" }, - { 0x21, "HSN" }, - { 0x22, "ICM Indicator" }, - { 0x23, "Internal Fault Map Class 1A" }, - { 0x24, "Internal Fault Map Class 1B" }, - { 0x25, "Internal Fault Map Class 2A" }, - { 0x26, "Internal Fault Map Class 2A Extension" }, - { 0x27, "IS Connection List" }, - { 0x28, "List Number" }, - { 0x29, "File Package State Indication" }, - { 0x2a, "Local Access State" }, - { 0x2b, "MAIO" }, - { 0x2c, "MO State" }, - { 0x2d, "Ny1" }, - { 0x2e, "Operational Information" }, - { 0x2f, "Power" }, - { 0x30, "RU Position Data" }, - { 0x31, "Protocol Error" }, - { 0x32, "Reason Code" }, - { 0x33, "Receiver Diversity" }, - { 0x34, "Replacement Unit Map" }, - { 0x35, "Result Code" }, - { 0x36, "RU Revision Data" }, - { 0x38, "T3105" }, - { 0x39, "Test Loop Setting" }, - { 0x3a, "TF Mode" }, - { 0x3b, "TF Compensation Value" }, - { 0x3c, "Time Slot Number" }, - { 0x3d, "TSC" }, - { 0x3e, "RU Logical Id" }, - { 0x3f, "RU Serial Number Data" }, - { 0x40, "BTS Version" }, - { 0x41, "OML IWD Version" }, - { 0x42, "RWL IWD Version" }, - { 0x43, "OML Function Map 1" }, - { 0x44, "OML Function Map 2" }, - { 0x45, "RSL Function Map 1" }, - { 0x46, "RSL Function Map 2" }, - { 0x47, "Extended Range Indicator" }, - { 0x48, "Request Indicators" }, - { 0x49, "DIP Alarm Condition Map" }, - { 0x4a, "ES Incoming" }, - { 0x4b, "ES Outgoing" }, - { 0x4e, "SES Incoming" }, - { 0x4f, "SES Outgoing" }, - { 0x50, "Replacement Unit Map Extension" }, - { 0x52, "UAS Incoming" }, - { 0x53, "UAS Outgoing" }, - { 0x58, "DF Incoming" }, - { 0x5a, "DF Outgoing" }, - { 0x5c, "SF" }, - { 0x60, "S Bits Setting" }, - { 0x61, "CRC-4 Use Option" }, - { 0x62, "T Parameter" }, - { 0x63, "N Parameter" }, - { 0x64, "N1 Parameter" }, - { 0x65, "N3 Parameter" }, - { 0x66, "N4 Parameter" }, - { 0x67, "P Parameter" }, - { 0x68, "Q Parameter" }, - { 0x69, "BI_Q1" }, - { 0x6a, "BI_Q2" }, - { 0x74, "ICM Boundary Parameters" }, - { 0x77, "AFT" }, - { 0x78, "AFT RAI" }, - { 0x79, "Link Supervision Control" }, - { 0x7a, "Link Supervision Filtering Time" }, - { 0x7b, "Call Supervision Time" }, - { 0x7c, "Interval Length UAS Incoming" }, - { 0x7d, "Interval Length UAS Outgoing" }, - { 0x7e, "ICM Channel Rate" }, - { 0x7f, "Attribute Identifier" }, - { 0x80, "FM Frequency List" }, - { 0x81, "FM Frequency Report" }, - { 0x82, "FM Percentile" }, - { 0x83, "FM Clear Indication" }, - { 0x84, "HW Info Signature" }, - { 0x85, "MO Record" }, - { 0x86, "TF Synchronisation Source" }, - { 0x87, "TTA" }, - { 0x88, "End Segment Number" }, - { 0x89, "Segment Number" }, - { 0x8a, "Capabilities Signature" }, - { 0x8c, "File Relation List" }, - { 0x90, "Negotiation Record I" }, - { 0x91, "Negotiation Record II" }, - { 0x92, "Encryption Algorithm" }, - { 0x94, "Interference Rejection Combining" }, - { 0x95, "Dedication Information" }, - { 0x97, "Feature Code" }, - { 0x98, "FS Offset" }, - { 0x99, "ESB Timeslot" }, - { 0x9a, "Master TG Instance" }, - { 0x9b, "Master TX Chain Delay" }, - { 0x9c, "External Condition Class 2 Extension" }, - { 0x9d, "TSs MO State" }, - { 0, NULL } -}; - -const struct value_string om2k_mo_class_short_vals[] = { - { 0x01, "TRXC" }, - { 0x03, "TS" }, - { 0x04, "TF" }, - { 0x05, "IS" }, - { 0x06, "CON" }, - { 0x07, "DP" }, - { 0x0a, "CF" }, - { 0x0b, "TX" }, - { 0x0c, "RX" }, - { 0, NULL } -}; - -const struct value_string om2k_result_strings[] = { - { 0x02, "Wrong state or out of sequence" }, - { 0x03, "File error" }, - { 0x04, "Fault, unspecified" }, - { 0x05, "Tuning fault" }, - { 0x06, "Protocol error" }, - { 0x07, "MO not connected" }, - { 0x08, "Parameter error" }, - { 0x09, "Optional function not supported" }, - { 0x0a, "Local access state LOCALLY DISCONNECTED" }, - { 0, NULL } -}; - -const struct value_string om2k_accordance_strings[] = { - { 0x00, "Data according to request" }, - { 0x01, "Data not according to request" }, - { 0x02, "Inconsistent MO data" }, - { 0x03, "Capability constraint violation" }, - { 0, NULL } -}; - -const struct value_string om2k_mostate_vals[] = { - { 0x00, "RESET" }, - { 0x01, "STARTED" }, - { 0x02, "ENABLED" }, - { 0x03, "DISABLED" }, - { 0, NULL } -}; - -/* entire decoded OM2K message (header + parsed TLV) */ -struct om2k_decoded_msg { - struct abis_om2k_hdr o2h; - uint16_t msg_type; - struct tlv_parsed tp; -}; - -/* resolve the OM2000 Managed Object by BTS + MO Address */ -static struct om2k_mo * -get_om2k_mo(struct gsm_bts *bts, const struct abis_om2k_mo *abis_mo) -{ - struct om2k_mo *mo = NULL; - struct gsm_bts_trx *trx; - - switch (abis_mo->class) { - case OM2K_MO_CLS_CF: - mo = &bts->rbs2000.cf.om2k_mo; - break; - case OM2K_MO_CLS_CON: - mo = &bts->rbs2000.con.om2k_mo; - break; - case OM2K_MO_CLS_IS: - mo = &bts->rbs2000.is.om2k_mo; - break; - case OM2K_MO_CLS_TF: - mo = &bts->rbs2000.tf.om2k_mo; - break; - - case OM2K_MO_CLS_TRXC: - trx = gsm_bts_trx_num(bts, abis_mo->inst); - if (!trx) - return NULL; - mo = &trx->rbs2000.trxc.om2k_mo; - break; - case OM2K_MO_CLS_TX: - trx = gsm_bts_trx_num(bts, abis_mo->inst); - if (!trx) - return NULL; - mo = &trx->rbs2000.tx.om2k_mo; - break; - case OM2K_MO_CLS_RX: - trx = gsm_bts_trx_num(bts, abis_mo->inst); - if (!trx) - return NULL; - mo = &trx->rbs2000.rx.om2k_mo; - break; - case OM2K_MO_CLS_TS: - trx = gsm_bts_trx_num(bts, abis_mo->assoc_so); - if (!trx) - return NULL; - if (abis_mo->inst >= ARRAY_SIZE(trx->ts)) - return NULL; - mo = &trx->ts[abis_mo->inst].rbs2000.om2k_mo; - break; - default: - return NULL; - }; - - return mo; -} - -static struct msgb *om2k_msgb_alloc(void) -{ - return msgb_alloc_headroom(OM_ALLOC_SIZE, OM_HEADROOM_SIZE, - "OM2000"); -} - -static int abis_om2k_tlv_parse(struct tlv_parsed *tp, const uint8_t *buf, int len) -{ - return tlv_parse(tp, &om2k_att_tlvdef, buf, len, 0, 0); -} - -static int abis_om2k_msg_tlv_parse(struct tlv_parsed *tp, struct abis_om2k_hdr *oh) -{ - return abis_om2k_tlv_parse(tp, oh->data, oh->om.length - 6); -} - -/* decode/parse the message */ -static int om2k_decode_msg(struct om2k_decoded_msg *odm, struct msgb *msg) -{ - struct abis_om2k_hdr *o2h = msgb_l2(msg); - odm->msg_type = ntohs(o2h->msg_type); - odm->o2h = *o2h; - return abis_om2k_msg_tlv_parse(&odm->tp, o2h); -} - -static char *om2k_mo_name(const struct abis_om2k_mo *mo) -{ - static char mo_buf[64]; - - memset(mo_buf, 0, sizeof(mo_buf)); - snprintf(mo_buf, sizeof(mo_buf), "%s/%02x/%02x/%02x", - get_value_string(om2k_mo_class_short_vals, mo->class), - mo->bts, mo->assoc_so, mo->inst); - return mo_buf; -} - -/* resolve the gsm_nm_state data structure for a given MO */ -static struct gsm_nm_state * -mo2nm_state(struct gsm_bts *bts, const struct abis_om2k_mo *mo) -{ - struct gsm_bts_trx *trx; - struct gsm_nm_state *nm_state = NULL; - - switch (mo->class) { - case OM2K_MO_CLS_TRXC: - trx = gsm_bts_trx_num(bts, mo->inst); - if (!trx) - return NULL; - nm_state = &trx->mo.nm_state; - break; - case OM2K_MO_CLS_TS: - trx = gsm_bts_trx_num(bts, mo->assoc_so); - if (!trx) - return NULL; - if (mo->inst >= ARRAY_SIZE(trx->ts)) - return NULL; - nm_state = &trx->ts[mo->inst].mo.nm_state; - break; - case OM2K_MO_CLS_TF: - nm_state = &bts->rbs2000.tf.mo.nm_state; - break; - case OM2K_MO_CLS_IS: - nm_state = &bts->rbs2000.is.mo.nm_state; - break; - case OM2K_MO_CLS_CON: - nm_state = &bts->rbs2000.con.mo.nm_state; - break; - case OM2K_MO_CLS_DP: - nm_state = &bts->rbs2000.con.mo.nm_state; - break; - case OM2K_MO_CLS_CF: - nm_state = &bts->mo.nm_state; - break; - case OM2K_MO_CLS_TX: - trx = gsm_bts_trx_num(bts, mo->inst); - if (!trx) - return NULL; - /* FIXME */ - break; - case OM2K_MO_CLS_RX: - trx = gsm_bts_trx_num(bts, mo->inst); - if (!trx) - return NULL; - /* FIXME */ - break; - } - - return nm_state; -} - -static void *mo2obj(struct gsm_bts *bts, struct abis_om2k_mo *mo) -{ - struct gsm_bts_trx *trx; - - switch (mo->class) { - case OM2K_MO_CLS_TX: - case OM2K_MO_CLS_RX: - case OM2K_MO_CLS_TRXC: - return gsm_bts_trx_num(bts, mo->inst); - case OM2K_MO_CLS_TS: - trx = gsm_bts_trx_num(bts, mo->assoc_so); - if (!trx) - return NULL; - if (mo->inst >= ARRAY_SIZE(trx->ts)) - return NULL; - return &trx->ts[mo->inst]; - case OM2K_MO_CLS_TF: - case OM2K_MO_CLS_IS: - case OM2K_MO_CLS_CON: - case OM2K_MO_CLS_DP: - case OM2K_MO_CLS_CF: - return bts; - } - - return NULL; -} - -static void update_mo_state(struct gsm_bts *bts, struct abis_om2k_mo *mo, - uint8_t mo_state) -{ - struct gsm_nm_state *nm_state = mo2nm_state(bts, mo); - struct gsm_nm_state new_state; - struct nm_statechg_signal_data nsd; - - if (!nm_state) - return; - - new_state = *nm_state; - /* NOTICE: 12.21 Availability state values != OM2000 */ - new_state.availability = mo_state; - - memset(&nsd, 0, sizeof(nsd)); - - nsd.bts = bts; - nsd.obj = mo2obj(bts, mo); - nsd.old_state = nm_state; - nsd.new_state = &new_state; - nsd.om2k_mo = mo; - - osmo_signal_dispatch(SS_NM, S_NM_STATECHG_ADM, &nsd); - - nm_state->availability = new_state.availability; -} - -static void update_op_state(struct gsm_bts *bts, const struct abis_om2k_mo *mo, - uint8_t op_state) -{ - struct gsm_nm_state *nm_state = mo2nm_state(bts, mo); - struct gsm_nm_state new_state; - - if (!nm_state) - return; - - new_state = *nm_state; - switch (op_state) { - case 1: - new_state.operational = NM_OPSTATE_ENABLED; - break; - case 0: - new_state.operational = NM_OPSTATE_DISABLED; - break; - default: - new_state.operational = NM_OPSTATE_NULL; - break; - } - - nm_state->operational = new_state.operational; -} - -static int abis_om2k_sendmsg(struct gsm_bts *bts, struct msgb *msg) -{ - struct abis_om2k_hdr *o2h; - struct gsm_bts_trx *trx; - - msg->l2h = msg->data; - o2h = (struct abis_om2k_hdr *) msg->l2h; - - /* Compute the length in the OML header */ - o2h->om.length = 6 + msgb_l2len(msg)-sizeof(*o2h); - - switch (o2h->mo.class) { - case OM2K_MO_CLS_TRXC: - case OM2K_MO_CLS_TX: - case OM2K_MO_CLS_RX: - /* Route through per-TRX OML Link to the appropriate TRX */ - trx = gsm_bts_trx_by_nr(bts, o2h->mo.inst); - if (!trx) { - LOGP(DNM, LOGL_ERROR, "MO=%s Tx Dropping msg to " - "non-existing TRX\n", om2k_mo_name(&o2h->mo)); - return -ENODEV; - } - msg->dst = trx->oml_link; - break; - case OM2K_MO_CLS_TS: - /* Route through per-TRX OML Link to the appropriate TRX */ - trx = gsm_bts_trx_by_nr(bts, o2h->mo.assoc_so); - if (!trx) { - LOGP(DNM, LOGL_ERROR, "MO=%s Tx Dropping msg to " - "non-existing TRX\n", om2k_mo_name(&o2h->mo)); - return -ENODEV; - } - msg->dst = trx->oml_link; - break; - default: - /* Route through the IXU/DXU OML Link */ - msg->dst = bts->oml_link; - break; - } - - return _abis_nm_sendmsg(msg); -} - -static void fill_om2k_hdr(struct abis_om2k_hdr *o2h, const struct abis_om2k_mo *mo, - uint16_t msg_type) -{ - o2h->om.mdisc = ABIS_OM_MDISC_FOM; - o2h->om.placement = ABIS_OM_PLACEMENT_ONLY; - o2h->om.sequence = 0; - /* We fill o2h->om.length later during om2k_sendmsg() */ - o2h->msg_type = htons(msg_type); - memcpy(&o2h->mo, mo, sizeof(o2h->mo)); -} - -static int abis_om2k_cal_time_resp(struct gsm_bts *bts) -{ - struct msgb *msg = om2k_msgb_alloc(); - struct abis_om2k_hdr *o2k; - time_t tm_t; - struct tm *tm; - - o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); - fill_om2k_hdr(o2k, &bts->rbs2000.cf.om2k_mo.addr, - OM2K_MSGT_CAL_TIME_RESP); - - tm_t = time(NULL); - tm = localtime(&tm_t); - - msgb_put_u8(msg, OM2K_DEI_CAL_TIME); - msgb_put_u8(msg, tm->tm_year % 100); - msgb_put_u8(msg, tm->tm_mon + 1); - msgb_put_u8(msg, tm->tm_mday); - msgb_put_u8(msg, tm->tm_hour); - msgb_put_u8(msg, tm->tm_min); - msgb_put_u8(msg, tm->tm_sec); - - return abis_om2k_sendmsg(bts, msg); -} - -static int abis_om2k_tx_simple(struct gsm_bts *bts, const struct abis_om2k_mo *mo, - uint8_t msg_type) -{ - struct msgb *msg = om2k_msgb_alloc(); - struct abis_om2k_hdr *o2k; - - o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); - fill_om2k_hdr(o2k, mo, msg_type); - - DEBUGP(DNM, "Tx MO=%s %s\n", om2k_mo_name(mo), - get_value_string(om2k_msgcode_vals, msg_type)); - - return abis_om2k_sendmsg(bts, msg); -} - -int abis_om2k_tx_reset_cmd(struct gsm_bts *bts, const struct abis_om2k_mo *mo) -{ - return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_RESET_CMD); -} - -int abis_om2k_tx_start_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo) -{ - return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_START_REQ); -} - -int abis_om2k_tx_status_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo) -{ - return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_STATUS_REQ); -} - -int abis_om2k_tx_connect_cmd(struct gsm_bts *bts, const struct abis_om2k_mo *mo) -{ - return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_CONNECT_CMD); -} - -int abis_om2k_tx_disconnect_cmd(struct gsm_bts *bts, const struct abis_om2k_mo *mo) -{ - return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_DISCONNECT_CMD); -} - -int abis_om2k_tx_test_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo) -{ - return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_TEST_REQ); -} - -int abis_om2k_tx_enable_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo) -{ - return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_ENABLE_REQ); -} - -int abis_om2k_tx_disable_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo) -{ - return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_DISABLE_REQ); -} - -int abis_om2k_tx_op_info(struct gsm_bts *bts, const struct abis_om2k_mo *mo, - uint8_t operational) -{ - struct msgb *msg = om2k_msgb_alloc(); - struct abis_om2k_hdr *o2k; - - o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); - fill_om2k_hdr(o2k, mo, OM2K_MSGT_OP_INFO); - - msgb_tv_put(msg, OM2K_DEI_OP_INFO, operational); - - DEBUGP(DNM, "Tx MO=%s %s\n", om2k_mo_name(mo), - get_value_string(om2k_msgcode_vals, OM2K_MSGT_OP_INFO)); - - /* we update the state here... and send the signal at ACK */ - update_op_state(bts, mo, operational); - - return abis_om2k_sendmsg(bts, msg); -} - -int abis_om2k_tx_cap_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo) -{ - return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_CAPA_REQ); -} - -static void om2k_fill_is_conn_grp(struct om2k_is_conn_grp *grp, uint16_t icp1, - uint16_t icp2, uint8_t cont_idx) -{ - grp->icp1 = htons(icp1); - grp->icp2 = htons(icp2); - grp->cont_idx = cont_idx; -} - -int abis_om2k_tx_is_conf_req(struct gsm_bts *bts) -{ - struct msgb *msg = om2k_msgb_alloc(); - struct abis_om2k_hdr *o2k; - struct is_conn_group *grp; - unsigned int num_grps = 0, i = 0; - struct om2k_is_conn_grp *cg; - - /* count number of groups in linked list */ - llist_for_each_entry(grp, &bts->rbs2000.is.conn_groups, list) - num_grps++; - - if (!num_grps) - return -EINVAL; - - /* allocate buffer for oml group array */ - cg = talloc_zero_array(bts, struct om2k_is_conn_grp, num_grps); - - /* fill array with data from linked list */ - llist_for_each_entry(grp, &bts->rbs2000.is.conn_groups, list) - om2k_fill_is_conn_grp(&cg[i++], grp->icp1, grp->icp2, grp->ci); - - o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); - fill_om2k_hdr(o2k, &bts->rbs2000.is.om2k_mo.addr, - OM2K_MSGT_IS_CONF_REQ); - - msgb_tv_put(msg, OM2K_DEI_LIST_NR, 1); - msgb_tv_put(msg, OM2K_DEI_END_LIST_NR, 1); - - msgb_tlv_put(msg, OM2K_DEI_IS_CONN_LIST, - num_grps * sizeof(*cg), (uint8_t *)cg); - - talloc_free(cg); - - DEBUGP(DNM, "Tx MO=%s %s\n", - om2k_mo_name(&bts->rbs2000.is.om2k_mo.addr), - get_value_string(om2k_msgcode_vals, OM2K_MSGT_IS_CONF_REQ)); - - return abis_om2k_sendmsg(bts, msg); -} - -int abis_om2k_tx_con_conf_req(struct gsm_bts *bts) -{ - struct msgb *msg = om2k_msgb_alloc(); - struct abis_om2k_hdr *o2k; - struct con_group *grp; - unsigned int num_grps = 0; - - /* count number of groups in linked list */ - llist_for_each_entry(grp, &bts->rbs2000.con.conn_groups, list) - num_grps++; - - if (!num_grps) - return -EINVAL; - - /* first build the value part of the OM2K_DEI_CON_CONN_LIST DEI */ - msgb_put_u8(msg, num_grps); - llist_for_each_entry(grp, &bts->rbs2000.con.conn_groups, list) { - struct con_path *cp; - unsigned int num_paths = 0; - llist_for_each_entry(cp, &grp->paths, list) - num_paths++; - msgb_put_u8(msg, num_paths); - llist_for_each_entry(cp, &grp->paths, list) { - struct om2k_con_path *om2k_cp; - om2k_cp = (struct om2k_con_path *) msgb_put(msg, sizeof(*om2k_cp)); - om2k_cp->ccp = htons(cp->ccp); - om2k_cp->ci = cp->ci; - om2k_cp->tag = cp->tag; - om2k_cp->tei = cp->tei; - } - } - msgb_push_u8(msg, msgb_length(msg)); - msgb_push_u8(msg, OM2K_DEI_CON_CONN_LIST); - - /* pre-pend the list number DEIs */ - msgb_tv_push(msg, OM2K_DEI_END_LIST_NR, 1); - msgb_tv_push(msg, OM2K_DEI_LIST_NR, 1); - - /* pre-pend the OM2K header */ - o2k = (struct abis_om2k_hdr *) msgb_push(msg, sizeof(*o2k)); - fill_om2k_hdr(o2k, &bts->rbs2000.con.om2k_mo.addr, - OM2K_MSGT_CON_CONF_REQ); - - DEBUGP(DNM, "Tx MO=%s %s\n", - om2k_mo_name(&bts->rbs2000.con.om2k_mo.addr), - get_value_string(om2k_msgcode_vals, OM2K_MSGT_CON_CONF_REQ)); - - return abis_om2k_sendmsg(bts, msg); -} - -static void om2k_trx_to_mo(struct abis_om2k_mo *mo, - const struct gsm_bts_trx *trx, - enum abis_om2k_mo_cls cls) -{ - mo->class = cls; - mo->bts = 0; - mo->inst = trx->nr; - mo->assoc_so = 255; -} - -static void om2k_ts_to_mo(struct abis_om2k_mo *mo, - const struct gsm_bts_trx_ts *ts) -{ - mo->class = OM2K_MO_CLS_TS; - mo->bts = 0; - mo->inst = ts->nr; - mo->assoc_so = ts->trx->nr; -} - -/* Configure a Receiver MO */ -int abis_om2k_tx_rx_conf_req(struct gsm_bts_trx *trx) -{ - struct msgb *msg = om2k_msgb_alloc(); - struct abis_om2k_hdr *o2k; - struct abis_om2k_mo mo; - - om2k_trx_to_mo(&mo, trx, OM2K_MO_CLS_RX); - - o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); - fill_om2k_hdr(o2k, &mo, OM2K_MSGT_RX_CONF_REQ); - - msgb_tv16_put(msg, OM2K_DEI_FREQ_SPEC_RX, trx->arfcn); - msgb_tv_put(msg, OM2K_DEI_RX_DIVERSITY, 0x02); /* A */ - - return abis_om2k_sendmsg(trx->bts, msg); -} - -/* Configure a Transmitter MO */ -int abis_om2k_tx_tx_conf_req(struct gsm_bts_trx *trx) -{ - struct msgb *msg = om2k_msgb_alloc(); - struct abis_om2k_hdr *o2k; - struct abis_om2k_mo mo; - - om2k_trx_to_mo(&mo, trx, OM2K_MO_CLS_TX); - - o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); - fill_om2k_hdr(o2k, &mo, OM2K_MSGT_TX_CONF_REQ); - - msgb_tv16_put(msg, OM2K_DEI_FREQ_SPEC_TX, trx->arfcn); - msgb_tv_put(msg, OM2K_DEI_POWER, trx->nominal_power-trx->max_power_red); - msgb_tv_put(msg, OM2K_DEI_FILLING_MARKER, 0); /* Filling enabled */ - msgb_tv_put(msg, OM2K_DEI_BCC, trx->bts->bsic & 0x7); - /* Dedication Information is optional */ - - return abis_om2k_sendmsg(trx->bts, msg); -} - -enum abis_om2k_tf_mode { - OM2K_TF_MODE_MASTER = 0x00, - OM2K_TF_MODE_STANDALONE = 0x01, - OM2K_TF_MODE_SLAVE = 0x02, - OM2K_TF_MODE_UNDEFINED = 0xff, -}; - -static const uint8_t fs_offset_undef[5] = { 0xff, 0xff, 0xff, 0xff, 0xff }; - -int abis_om2k_tx_tf_conf_req(struct gsm_bts *bts) -{ - struct msgb *msg = om2k_msgb_alloc(); - struct abis_om2k_hdr *o2k; - - o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); - fill_om2k_hdr(o2k, &bts->rbs2000.tf.om2k_mo.addr, - OM2K_MSGT_TF_CONF_REQ); - - msgb_tv_put(msg, OM2K_DEI_TF_MODE, OM2K_TF_MODE_STANDALONE); - msgb_tv_put(msg, OM2K_DEI_TF_SYNC_SRC, 0x00); - msgb_tv_fixed_put(msg, OM2K_DEI_FS_OFFSET, - sizeof(fs_offset_undef), fs_offset_undef); - - DEBUGP(DNM, "Tx MO=%s %s\n", - om2k_mo_name(&bts->rbs2000.tf.om2k_mo.addr), - get_value_string(om2k_msgcode_vals, OM2K_MSGT_TF_CONF_REQ)); - - return abis_om2k_sendmsg(bts, msg); -} - -static uint8_t pchan2comb(enum gsm_phys_chan_config pchan) -{ - switch (pchan) { - case GSM_PCHAN_CCCH: - return 4; - case GSM_PCHAN_CCCH_SDCCH4: - return 5; - case GSM_PCHAN_SDCCH8_SACCH8C: - return 3; - case GSM_PCHAN_TCH_F: - case GSM_PCHAN_TCH_H: - case GSM_PCHAN_PDCH: - case GSM_PCHAN_TCH_F_PDCH: - case GSM_PCHAN_TCH_F_TCH_H_PDCH: - return 8; - default: - return 0; - } -} - -static uint8_t ts2comb(struct gsm_bts_trx_ts *ts) -{ - switch (ts->pchan) { - case GSM_PCHAN_TCH_F_PDCH: - LOGP(DNM, LOGL_ERROR, "%s pchan %s not intended for use" - " with OM2000, use %s instead\n", - gsm_ts_and_pchan_name(ts), - gsm_pchan_name(GSM_PCHAN_TCH_F_PDCH), - gsm_pchan_name(GSM_PCHAN_TCH_F_TCH_H_PDCH)); - /* If we allowed initialization of TCH/F_PDCH, it would fail - * when we try to send the ip.access specific RSL PDCH Act - * message for it. Rather fail completely right now: */ - return 0; - case GSM_PCHAN_TCH_F_TCH_H_PDCH: - return pchan2comb(GSM_PCHAN_TCH_F); - default: - return pchan2comb(ts->pchan); - } -} - -static int put_freq_list(uint8_t *buf, uint16_t arfcn) -{ - buf[0] = 0x00; /* TX/RX address */ - buf[1] = (arfcn >> 8); - buf[2] = (arfcn & 0xff); - - return 3; -} - -/* Compute a frequency list in OM2000 fomrmat */ -static int om2k_gen_freq_list(uint8_t *list, struct gsm_bts_trx_ts *ts) -{ - uint8_t *cur = list; - int len; - - if (ts->hopping.enabled) { - unsigned int i; - for (i = 0; i < ts->hopping.arfcns.data_len*8; i++) { - if (bitvec_get_bit_pos(&ts->hopping.arfcns, i)) - cur += put_freq_list(cur, i); - } - } else - cur += put_freq_list(cur, ts->trx->arfcn); - - len = cur - list; - - return len; -} - -const uint8_t icm_bound_params[] = { 0x02, 0x06, 0x0c, 0x16, 0x06 }; - -int abis_om2k_tx_ts_conf_req(struct gsm_bts_trx_ts *ts) -{ - struct msgb *msg = om2k_msgb_alloc(); - struct abis_om2k_hdr *o2k; - struct abis_om2k_mo mo; - uint8_t freq_list[64*3]; /* BA max size: 64 ARFCN */ - int freq_list_len; - - om2k_ts_to_mo(&mo, ts); - - memset(freq_list, 0, sizeof(freq_list)); - freq_list_len = om2k_gen_freq_list(freq_list, ts); - if (freq_list_len < 0) - return freq_list_len; - - o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); - fill_om2k_hdr(o2k, &mo, OM2K_MSGT_TS_CONF_REQ); - - msgb_tv_put(msg, OM2K_DEI_COMBINATION, ts2comb(ts)); - msgb_tv_put(msg, OM2K_DEI_TS_NR, ts->nr); - msgb_tlv_put(msg, OM2K_DEI_FREQ_LIST, freq_list_len, freq_list); - msgb_tv_put(msg, OM2K_DEI_HSN, ts->hopping.hsn); - msgb_tv_put(msg, OM2K_DEI_MAIO, ts->hopping.maio); - msgb_tv_put(msg, OM2K_DEI_BSIC, ts->trx->bts->bsic); - msgb_tv_put(msg, OM2K_DEI_RX_DIVERSITY, 0x02); /* A */ - msgb_tv16_put(msg, OM2K_DEI_FN_OFFSET, 0); - msgb_tv_put(msg, OM2K_DEI_EXT_RANGE, 0); /* Off */ - /* Optional: Interference Rejection Combining */ - msgb_tv_put(msg, OM2K_DEI_INTERF_REJ_COMB, 0x00); - switch (ts->pchan) { - case GSM_PCHAN_CCCH: - msgb_tv_put(msg, OM2K_DEI_BA_PA_MFRMS, 0x06); - msgb_tv_put(msg, OM2K_DEI_BS_AG_BKS_RES, 0x01); - msgb_tv_put(msg, OM2K_DEI_DRX_DEV_MAX, 0x05); - /* Repeat Paging/IMM.ASS: True, Allow Paging Type 3: Yes, Page for 5 seconds (default) */ - msgb_tv_put(msg, OM2K_DEI_CCCH_OPTIONS, 0x01); - break; - case GSM_PCHAN_CCCH_SDCCH4: - msgb_tv_put(msg, OM2K_DEI_T3105, ts->trx->bts->network->T3105 / 10); - msgb_tv_put(msg, OM2K_DEI_NY1, 35); - msgb_tv_put(msg, OM2K_DEI_BA_PA_MFRMS, 0x06); - msgb_tv_put(msg, OM2K_DEI_CBCH_INDICATOR, 0); - msgb_tv_put(msg, OM2K_DEI_TSC, gsm_ts_tsc(ts)); - msgb_tv_put(msg, OM2K_DEI_BS_AG_BKS_RES, 0x01); - msgb_tv_put(msg, OM2K_DEI_ICM_INDICATOR, 0); - msgb_tv_put(msg, OM2K_DEI_DRX_DEV_MAX, 0x05); - /* Repeat Paging/IMM.ASS: True, Allow Paging Type 3: Yes, Page for 5 seconds (default) */ - msgb_tv_put(msg, OM2K_DEI_CCCH_OPTIONS, 0x01); - msgb_tv_fixed_put(msg, OM2K_DEI_ICM_BOUND_PARAMS, - sizeof(icm_bound_params), icm_bound_params); - break; - case GSM_PCHAN_SDCCH8_SACCH8C: - msgb_tv_put(msg, OM2K_DEI_T3105, ts->trx->bts->network->T3105 / 10); - msgb_tv_put(msg, OM2K_DEI_NY1, 35); - msgb_tv_put(msg, OM2K_DEI_CBCH_INDICATOR, 0); - msgb_tv_put(msg, OM2K_DEI_TSC, gsm_ts_tsc(ts)); - /* Disable RF RESOURCE INDICATION on idle channels */ - msgb_tv_put(msg, OM2K_DEI_ICM_INDICATOR, 0); - msgb_tv_fixed_put(msg, OM2K_DEI_ICM_BOUND_PARAMS, - sizeof(icm_bound_params), icm_bound_params); - break; - default: - msgb_tv_put(msg, OM2K_DEI_T3105, ts->trx->bts->network->T3105 / 10); - msgb_tv_put(msg, OM2K_DEI_NY1, 35); - msgb_tv_put(msg, OM2K_DEI_TSC, gsm_ts_tsc(ts)); - /* Disable RF RESOURCE INDICATION on idle channels */ - msgb_tv_put(msg, OM2K_DEI_ICM_INDICATOR, 0); - msgb_tv_fixed_put(msg, OM2K_DEI_ICM_BOUND_PARAMS, - sizeof(icm_bound_params), icm_bound_params); - msgb_tv_put(msg, OM2K_DEI_TTA, 10); /* Timer for Time Alignment */ - if (ts->pchan == GSM_PCHAN_TCH_H) - msgb_tv_put(msg, OM2K_DEI_ICM_CHAN_RATE, 1); /* TCH/H */ - else - msgb_tv_put(msg, OM2K_DEI_ICM_CHAN_RATE, 0); /* TCH/F */ - msgb_tv_put(msg, OM2K_DEI_LSC, 1); /* enabled */ - msgb_tv_put(msg, OM2K_DEI_LSC_FILT_TIME, 10); /* units of 100ms */ - msgb_tv_put(msg, OM2K_DEI_CALL_SUPV_TIME, 8); - msgb_tv_put(msg, OM2K_DEI_ENCR_ALG, 0x00); - /* Not sure what those below mean */ - msgb_tv_put(msg, 0x9e, 0x00); - msgb_tv_put(msg, 0x9f, 0x37); - msgb_tv_put(msg, 0xa0, 0x01); - break; - } - - DEBUGP(DNM, "Tx MO=%s %s\n", - om2k_mo_name(&mo), - get_value_string(om2k_msgcode_vals, OM2K_MSGT_TS_CONF_REQ)); - - return abis_om2k_sendmsg(ts->trx->bts, msg); -} - - -/*********************************************************************** - * OM2000 Managed Object (MO) FSM - ***********************************************************************/ - -#define S(x) (1 << (x)) - -enum om2k_event_name { - OM2K_MO_EVT_START, - OM2K_MO_EVT_RX_CONN_COMPL, - OM2K_MO_EVT_RX_RESET_COMPL, - OM2K_MO_EVT_RX_START_REQ_ACCEPT, - OM2K_MO_EVT_RX_START_RES, - OM2K_MO_EVT_RX_CFG_REQ_ACCEPT, - OM2K_MO_EVT_RX_CFG_RES, - OM2K_MO_EVT_RX_ENA_REQ_ACCEPT, - OM2K_MO_EVT_RX_ENA_RES, - OM2K_MO_EVT_RX_OPINFO_ACC, -}; - -static const struct value_string om2k_event_names[] = { - { OM2K_MO_EVT_START, "START" }, - { OM2K_MO_EVT_RX_CONN_COMPL, "RX-CONN-COMPL" }, - { OM2K_MO_EVT_RX_RESET_COMPL, "RX-RESET-COMPL" }, - { OM2K_MO_EVT_RX_START_REQ_ACCEPT, "RX-RESET-REQ-ACCEPT" }, - { OM2K_MO_EVT_RX_START_RES, "RX-START-RESULT" }, - { OM2K_MO_EVT_RX_CFG_REQ_ACCEPT, "RX-CFG-REQ-ACCEPT" }, - { OM2K_MO_EVT_RX_CFG_RES, "RX-CFG-RESULT" }, - { OM2K_MO_EVT_RX_ENA_REQ_ACCEPT, "RX-ENABLE-REQ-ACCEPT" }, - { OM2K_MO_EVT_RX_ENA_RES, "RX-ENABLE-RESULT" }, - { OM2K_MO_EVT_RX_OPINFO_ACC, "RX-OPINFO-ACCEPT" }, - { 0, NULL } -}; - -enum om2k_mo_fsm_state { - OM2K_ST_INIT, - OM2K_ST_WAIT_CONN_COMPL, - OM2K_ST_WAIT_RES_COMPL, - OM2K_ST_WAIT_START_ACCEPT, - OM2K_ST_WAIT_START_RES, - OM2K_ST_WAIT_CFG_ACCEPT, - OM2K_ST_WAIT_CFG_RES, - OM2K_ST_WAIT_ENABLE_ACCEPT, - OM2K_ST_WAIT_ENABLE_RES, - OM2K_ST_WAIT_OPINFO_ACCEPT, - OM2K_ST_DONE, - OM2K_ST_ERROR, -}; - -struct om2k_mo_fsm_priv { - struct gsm_bts_trx *trx; - struct om2k_mo *mo; - uint8_t ts_nr; -}; - -static void om2k_mo_st_init(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct om2k_mo_fsm_priv *omfp = fi->priv; - - OSMO_ASSERT(event == OM2K_MO_EVT_START); - - switch (omfp->mo->addr.class) { - case OM2K_MO_CLS_CF: - /* no Connect required, is always connected */ - osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_START_ACCEPT, - OM2K_TIMEOUT, 0); - abis_om2k_tx_start_req(omfp->trx->bts, &omfp->mo->addr); - break; - case OM2K_MO_CLS_TRXC: - /* no Connect required, start with Reset */ - osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_RES_COMPL, - OM2K_TIMEOUT, 0); - abis_om2k_tx_reset_cmd(omfp->trx->bts, &omfp->mo->addr); - break; - default: - /* start with Connect */ - osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_CONN_COMPL, - OM2K_TIMEOUT, 0); - abis_om2k_tx_connect_cmd(omfp->trx->bts, &omfp->mo->addr); - break; - } -} - -static void om2k_mo_st_wait_conn_compl(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct om2k_mo_fsm_priv *omfp = fi->priv; - - switch (omfp->mo->addr.class) { -#if 0 - case OM2K_MO_CLS_TF: - /* skip the reset, hope that helps */ - osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_START_ACCEPT, - OM2K_TIMEOUT, 0); - abis_om2k_tx_start_req(omfp->trx->bts, &omfp->mo->addr); - break; -#endif - default: - osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_RES_COMPL, - OM2K_TIMEOUT, 0); - abis_om2k_tx_reset_cmd(omfp->trx->bts, &omfp->mo->addr); - break; - } -} - -static void om2k_mo_st_wait_res_compl(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct om2k_mo_fsm_priv *omfp = fi->priv; - - osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_START_ACCEPT, - OM2K_TIMEOUT, 0); - abis_om2k_tx_start_req(omfp->trx->bts, &omfp->mo->addr); -} - -static void om2k_mo_st_wait_start_accept(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct om2k_decoded_msg *omd = data; - - switch (omd->msg_type) { - case OM2K_MSGT_START_REQ_ACK: - osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_START_RES, - OM2K_TIMEOUT, 0); - break; - case OM2K_MSGT_START_REQ_REJ: - osmo_fsm_inst_state_chg(fi, OM2K_ST_ERROR, 0, 0); - break; - } -} - -static void om2k_mo_st_wait_start_res(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct om2k_mo_fsm_priv *omfp = fi->priv; - struct gsm_bts_trx_ts *ts; - - switch (omfp->mo->addr.class) { - case OM2K_MO_CLS_CF: - case OM2K_MO_CLS_TRXC: - /* Transition directly to Operational Info */ - osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_OPINFO_ACCEPT, - OM2K_TIMEOUT, 0); - abis_om2k_tx_op_info(omfp->trx->bts, &omfp->mo->addr, 1); - return; - case OM2K_MO_CLS_DP: - /* Transition directoy to WAIT_ENABLE_ACCEPT */ - osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_ENABLE_ACCEPT, - OM2K_TIMEOUT, 0); - abis_om2k_tx_enable_req(omfp->trx->bts, &omfp->mo->addr); - return; -#if 0 - case OM2K_MO_CLS_TF: - /* skip the config, hope that helps speeding things up */ - osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_ENABLE_ACCEPT, - OM2K_TIMEOUT, 0); - abis_om2k_tx_enable_req(omfp->trx->bts, &omfp->mo->addr); - return; -#endif - } - - osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_CFG_ACCEPT, - OM2K_TIMEOUT, 0); - switch (omfp->mo->addr.class) { - case OM2K_MO_CLS_TF: - abis_om2k_tx_tf_conf_req(omfp->trx->bts); - break; - case OM2K_MO_CLS_IS: - abis_om2k_tx_is_conf_req(omfp->trx->bts); - break; - case OM2K_MO_CLS_CON: - abis_om2k_tx_con_conf_req(omfp->trx->bts); - break; - case OM2K_MO_CLS_TX: - abis_om2k_tx_tx_conf_req(omfp->trx); - break; - case OM2K_MO_CLS_RX: - abis_om2k_tx_rx_conf_req(omfp->trx); - break; - case OM2K_MO_CLS_TS: - ts = mo2obj(omfp->trx->bts, &omfp->mo->addr); - abis_om2k_tx_ts_conf_req(ts); - break; - } -} - -static void om2k_mo_st_wait_cfg_accept(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct om2k_mo_fsm_priv *omfp = fi->priv; - uint32_t timeout = OM2K_TIMEOUT; - - if (omfp->mo->addr.class == OM2K_MO_CLS_TF) - timeout = 600; - - osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_CFG_RES, timeout, 0); -} - -static void om2k_mo_st_wait_cfg_res(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct om2k_mo_fsm_priv *omfp = fi->priv; - struct om2k_decoded_msg *omd = data; - uint8_t accordance; - - if (!TLVP_PRESENT(&omd->tp, OM2K_DEI_ACCORDANCE_IND)) { - osmo_fsm_inst_state_chg(fi, OM2K_ST_ERROR, 0, 0); - return; - } - accordance = *TLVP_VAL(&omd->tp, OM2K_DEI_ACCORDANCE_IND); - - if (accordance != 0) { - /* accordance not OK */ - osmo_fsm_inst_state_chg(fi, OM2K_ST_ERROR, 0, 0); - return; - } - - osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_ENABLE_ACCEPT, - OM2K_TIMEOUT, 0); - abis_om2k_tx_enable_req(omfp->trx->bts, &omfp->mo->addr); -} - -static void om2k_mo_st_wait_enable_accept(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct om2k_mo_fsm_priv *omfp = fi->priv; - struct om2k_decoded_msg *omd = data; - - switch (omd->msg_type) { - case OM2K_MSGT_ENABLE_REQ_REJ: - osmo_fsm_inst_state_chg(fi, OM2K_ST_ERROR, 0, 0); - break; - case OM2K_MSGT_ENABLE_REQ_ACK: - if (omfp->mo->addr.class == OM2K_MO_CLS_IS && - omfp->trx->bts->rbs2000.use_superchannel) - e1inp_ericsson_set_altc(omfp->trx->bts->oml_link->ts->line, 1); - osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_ENABLE_RES, - OM2K_TIMEOUT, 0); - } -} - -static void om2k_mo_st_wait_enable_res(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct om2k_mo_fsm_priv *omfp = fi->priv; - //struct om2k_decoded_msg *omd = data; - /* TODO: check if state is actually enabled now? */ - - osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_OPINFO_ACCEPT, - OM2K_TIMEOUT, 0); - abis_om2k_tx_op_info(omfp->trx->bts, &omfp->mo->addr, 1); -} - -static void om2k_mo_st_wait_opinfo_accept(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct om2k_mo_fsm_priv *omfp = fi->priv; - - /* if we have just received opinfo accept for the timeslot, - * start dynamic TCH switching procedures */ - if (omfp->mo->addr.class == OM2K_MO_CLS_TS) { - struct gsm_bts_trx_ts *ts; - ts = mo2obj(omfp->trx->bts, &omfp->mo->addr); - dyn_ts_init(ts); - } - osmo_fsm_inst_state_chg(fi, OM2K_ST_DONE, 0, 0); -} - -static void om2k_mo_s_done_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) -{ - struct om2k_mo_fsm_priv *omfp = fi->priv; - omfp->mo->fsm = NULL; - osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL); -} - -static void om2k_mo_s_error_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) -{ - struct om2k_mo_fsm_priv *omfp = fi->priv; - - omfp->mo->fsm = NULL; - osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL); -} - -static const struct osmo_fsm_state om2k_is_states[] = { - [OM2K_ST_INIT] = { - .name = "INIT", - .in_event_mask = S(OM2K_MO_EVT_START), - .out_state_mask = S(OM2K_ST_DONE) | - S(OM2K_ST_ERROR) | - S(OM2K_ST_WAIT_CONN_COMPL) | - S(OM2K_ST_WAIT_START_ACCEPT) | - S(OM2K_ST_WAIT_RES_COMPL), - .action = om2k_mo_st_init, - }, - [OM2K_ST_WAIT_CONN_COMPL] = { - .name = "WAIT-CONN-COMPL", - .in_event_mask = S(OM2K_MO_EVT_RX_CONN_COMPL), - .out_state_mask = S(OM2K_ST_DONE) | - S(OM2K_ST_ERROR) | - S(OM2K_ST_WAIT_START_ACCEPT) | - S(OM2K_ST_WAIT_RES_COMPL), - .action = om2k_mo_st_wait_conn_compl, - }, - [OM2K_ST_WAIT_RES_COMPL] = { - .name = "WAIT-RES-COMPL", - .in_event_mask = S(OM2K_MO_EVT_RX_RESET_COMPL), - .out_state_mask = S(OM2K_ST_DONE) | - S(OM2K_ST_ERROR) | - S(OM2K_ST_WAIT_START_ACCEPT), - .action = om2k_mo_st_wait_res_compl, - }, - [OM2K_ST_WAIT_START_ACCEPT] = { - .name = "WAIT-START-ACCEPT", - .in_event_mask = S(OM2K_MO_EVT_RX_START_REQ_ACCEPT), - .out_state_mask = S(OM2K_ST_DONE) | - S(OM2K_ST_ERROR) | - S(OM2K_ST_WAIT_START_RES), - .action =om2k_mo_st_wait_start_accept, - }, - [OM2K_ST_WAIT_START_RES] = { - .name = "WAIT-START-RES", - .in_event_mask = S(OM2K_MO_EVT_RX_START_RES), - .out_state_mask = S(OM2K_ST_DONE) | - S(OM2K_ST_ERROR) | - S(OM2K_ST_WAIT_CFG_ACCEPT) | - S(OM2K_ST_WAIT_OPINFO_ACCEPT), - .action = om2k_mo_st_wait_start_res, - }, - [OM2K_ST_WAIT_CFG_ACCEPT] = { - .name = "WAIT-CFG-ACCEPT", - .in_event_mask = S(OM2K_MO_EVT_RX_CFG_REQ_ACCEPT), - .out_state_mask = S(OM2K_ST_DONE) | - S(OM2K_ST_ERROR) | - S(OM2K_ST_WAIT_CFG_RES), - .action = om2k_mo_st_wait_cfg_accept, - }, - [OM2K_ST_WAIT_CFG_RES] = { - .name = "WAIT-CFG-RES", - .in_event_mask = S(OM2K_MO_EVT_RX_CFG_RES), - .out_state_mask = S(OM2K_ST_DONE) | - S(OM2K_ST_ERROR) | - S(OM2K_ST_WAIT_ENABLE_ACCEPT), - .action = om2k_mo_st_wait_cfg_res, - }, - [OM2K_ST_WAIT_ENABLE_ACCEPT] = { - .name = "WAIT-ENABLE-ACCEPT", - .in_event_mask = S(OM2K_MO_EVT_RX_ENA_REQ_ACCEPT), - .out_state_mask = S(OM2K_ST_DONE) | - S(OM2K_ST_ERROR) | - S(OM2K_ST_WAIT_ENABLE_RES), - .action = om2k_mo_st_wait_enable_accept, - }, - [OM2K_ST_WAIT_ENABLE_RES] = { - .name = "WAIT-ENABLE-RES", - .in_event_mask = S(OM2K_MO_EVT_RX_ENA_RES), - .out_state_mask = S(OM2K_ST_DONE) | - S(OM2K_ST_ERROR) | - S(OM2K_ST_WAIT_OPINFO_ACCEPT), - .action = om2k_mo_st_wait_enable_res, - }, - [OM2K_ST_WAIT_OPINFO_ACCEPT] = { - .name = "WAIT-OPINFO-ACCEPT", - .in_event_mask = S(OM2K_MO_EVT_RX_OPINFO_ACC), - .out_state_mask = S(OM2K_ST_DONE) | - S(OM2K_ST_ERROR), - .action = om2k_mo_st_wait_opinfo_accept, - }, - [OM2K_ST_DONE] = { - .name = "DONE", - .in_event_mask = 0, - .out_state_mask = 0, - .onenter = om2k_mo_s_done_onenter, - }, - [OM2K_ST_ERROR] = { - .name = "ERROR", - .in_event_mask = 0, - .out_state_mask = 0, - .onenter = om2k_mo_s_error_onenter, - }, - -}; - -static int om2k_mo_timer_cb(struct osmo_fsm_inst *fi) -{ - osmo_fsm_inst_state_chg(fi, OM2K_ST_ERROR, 0, 0); - return 0; -} - -static struct osmo_fsm om2k_mo_fsm = { - .name = "OM2000-MO", - .states = om2k_is_states, - .num_states = ARRAY_SIZE(om2k_is_states), - .log_subsys = DNM, - .event_names = om2k_event_names, - .timer_cb = om2k_mo_timer_cb, -}; - -struct osmo_fsm_inst *om2k_mo_fsm_start(struct osmo_fsm_inst *parent, - uint32_t term_event, - struct gsm_bts_trx *trx, struct om2k_mo *mo) -{ - struct osmo_fsm_inst *fi; - struct om2k_mo_fsm_priv *omfp; - char idbuf[64]; - - snprintf(idbuf, sizeof(idbuf), "%s-%s", parent->id, - om2k_mo_name(&mo->addr)); - - fi = osmo_fsm_inst_alloc_child_id(&om2k_mo_fsm, parent, - term_event, idbuf); - if (!fi) - return NULL; - - mo->fsm = fi; - omfp = talloc_zero(fi, struct om2k_mo_fsm_priv); - omfp->mo = mo; - omfp->trx = trx; - fi->priv = omfp; - - osmo_fsm_inst_dispatch(fi, OM2K_MO_EVT_START, NULL); - - return fi; -} - -int om2k_mo_fsm_recvmsg(struct gsm_bts *bts, struct om2k_mo *mo, - struct om2k_decoded_msg *odm) -{ - switch (odm->msg_type) { - case OM2K_MSGT_CONNECT_COMPL: - case OM2K_MSGT_CONNECT_REJ: - osmo_fsm_inst_dispatch(mo->fsm, - OM2K_MO_EVT_RX_CONN_COMPL, odm); - break; - - case OM2K_MSGT_RESET_COMPL: - case OM2K_MSGT_RESET_REJ: - osmo_fsm_inst_dispatch(mo->fsm, - OM2K_MO_EVT_RX_RESET_COMPL, odm); - break; - - case OM2K_MSGT_START_REQ_ACK: - case OM2K_MSGT_START_REQ_REJ: - osmo_fsm_inst_dispatch(mo->fsm, - OM2K_MO_EVT_RX_START_REQ_ACCEPT, odm); - break; - - case OM2K_MSGT_START_RES: - osmo_fsm_inst_dispatch(mo->fsm, - OM2K_MO_EVT_RX_START_RES, odm); - break; - - case OM2K_MSGT_CON_CONF_REQ_ACK: - case OM2K_MSGT_IS_CONF_REQ_ACK: - case OM2K_MSGT_RX_CONF_REQ_ACK: - case OM2K_MSGT_TF_CONF_REQ_ACK: - case OM2K_MSGT_TS_CONF_REQ_ACK: - case OM2K_MSGT_TX_CONF_REQ_ACK: - osmo_fsm_inst_dispatch(mo->fsm, - OM2K_MO_EVT_RX_CFG_REQ_ACCEPT, odm); - break; - - case OM2K_MSGT_CON_CONF_RES: - case OM2K_MSGT_IS_CONF_RES: - case OM2K_MSGT_RX_CONF_RES: - case OM2K_MSGT_TF_CONF_RES: - case OM2K_MSGT_TS_CONF_RES: - case OM2K_MSGT_TX_CONF_RES: - osmo_fsm_inst_dispatch(mo->fsm, - OM2K_MO_EVT_RX_CFG_RES, odm); - break; - - case OM2K_MSGT_ENABLE_REQ_ACK: - case OM2K_MSGT_ENABLE_REQ_REJ: - osmo_fsm_inst_dispatch(mo->fsm, - OM2K_MO_EVT_RX_ENA_REQ_ACCEPT, odm); - break; - case OM2K_MSGT_ENABLE_RES: - osmo_fsm_inst_dispatch(mo->fsm, - OM2K_MO_EVT_RX_ENA_RES, odm); - break; - - case OM2K_MSGT_OP_INFO_ACK: - case OM2K_MSGT_OP_INFO_REJ: - osmo_fsm_inst_dispatch(mo->fsm, - OM2K_MO_EVT_RX_OPINFO_ACC, odm); - break; - default: - return -1; - } - - return 0; -} - -/*********************************************************************** - * OM2000 TRX Finite State Machine, initializes TRXC and all siblings - ***********************************************************************/ - -enum om2k_trx_event { - OM2K_TRX_EVT_START, - OM2K_TRX_EVT_TRXC_DONE, - OM2K_TRX_EVT_TX_DONE, - OM2K_TRX_EVT_RX_DONE, - OM2K_TRX_EVT_TS_DONE, - OM2K_TRX_EVT_STOP, -}; - -static struct value_string om2k_trx_events[] = { - { OM2K_TRX_EVT_START, "START" }, - { OM2K_TRX_EVT_TRXC_DONE, "TRXC-DONE" }, - { OM2K_TRX_EVT_TX_DONE, "TX-DONE" }, - { OM2K_TRX_EVT_RX_DONE, "RX-DONE" }, - { OM2K_TRX_EVT_TS_DONE, "TS-DONE" }, - { OM2K_TRX_EVT_STOP, "STOP" }, - { 0, NULL } -}; - -enum om2k_trx_state { - OM2K_TRX_S_INIT, - OM2K_TRX_S_WAIT_TRXC, - OM2K_TRX_S_WAIT_TX, - OM2K_TRX_S_WAIT_RX, - OM2K_TRX_S_WAIT_TS, - OM2K_TRX_S_DONE, - OM2K_TRX_S_ERROR -}; - -struct om2k_trx_fsm_priv { - struct gsm_bts_trx *trx; - uint8_t next_ts_nr; -}; - -static void om2k_trx_s_init(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct om2k_trx_fsm_priv *otfp = fi->priv; - - /* First initialize TRXC */ - osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_WAIT_TRXC, - TRX_FSM_TIMEOUT, 0); - om2k_mo_fsm_start(fi, OM2K_TRX_EVT_TRXC_DONE, otfp->trx, - &otfp->trx->rbs2000.trxc.om2k_mo); -} - -static void om2k_trx_s_wait_trxc(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct om2k_trx_fsm_priv *otfp = fi->priv; - - /* Initialize TX after TRXC */ - osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_WAIT_TX, - TRX_FSM_TIMEOUT, 0); - om2k_mo_fsm_start(fi, OM2K_TRX_EVT_TX_DONE, otfp->trx, - &otfp->trx->rbs2000.tx.om2k_mo); -} - -static void om2k_trx_s_wait_tx(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct om2k_trx_fsm_priv *otfp = fi->priv; - - /* Initialize RX after TX */ - osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_WAIT_RX, - TRX_FSM_TIMEOUT, 0); - om2k_mo_fsm_start(fi, OM2K_TRX_EVT_RX_DONE, otfp->trx, - &otfp->trx->rbs2000.rx.om2k_mo); -} - -static void om2k_trx_s_wait_rx(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct om2k_trx_fsm_priv *otfp = fi->priv; - struct gsm_bts_trx_ts *ts; - - /* Initialize Timeslots after TX */ - osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_WAIT_TS, - TRX_FSM_TIMEOUT, 0); - otfp->next_ts_nr = 0; - ts = &otfp->trx->ts[otfp->next_ts_nr++]; - om2k_mo_fsm_start(fi, OM2K_TRX_EVT_TS_DONE, otfp->trx, - &ts->rbs2000.om2k_mo); -} - -static void om2k_trx_s_wait_ts(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct om2k_trx_fsm_priv *otfp = fi->priv; - struct gsm_bts_trx_ts *ts; - - if (otfp->next_ts_nr < 8) { - /* iterate to the next timeslot */ - ts = &otfp->trx->ts[otfp->next_ts_nr++]; - om2k_mo_fsm_start(fi, OM2K_TRX_EVT_TS_DONE, otfp->trx, - &ts->rbs2000.om2k_mo); - } else { - /* only after all 8 TS */ - osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_DONE, 0, 0); - } -} - -static void om2k_trx_s_done_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) -{ - struct om2k_trx_fsm_priv *otfp = fi->priv; - gsm_bts_trx_set_system_infos(otfp->trx); - osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL); -} - -static const struct osmo_fsm_state om2k_trx_states[] = { - [OM2K_TRX_S_INIT] = { - .in_event_mask = S(OM2K_TRX_EVT_START), - .out_state_mask = S(OM2K_TRX_S_WAIT_TRXC), - .name = "INIT", - .action = om2k_trx_s_init, - }, - [OM2K_TRX_S_WAIT_TRXC] = { - .in_event_mask = S(OM2K_TRX_EVT_TRXC_DONE), - .out_state_mask = S(OM2K_TRX_S_ERROR) | - S(OM2K_TRX_S_WAIT_TX), - .name = "WAIT-TRXC", - .action = om2k_trx_s_wait_trxc, - }, - [OM2K_TRX_S_WAIT_TX] = { - .in_event_mask = S(OM2K_TRX_EVT_TX_DONE), - .out_state_mask = S(OM2K_TRX_S_ERROR) | - S(OM2K_TRX_S_WAIT_RX), - .name = "WAIT-TX", - .action = om2k_trx_s_wait_tx, - }, - [OM2K_TRX_S_WAIT_RX] = { - .in_event_mask = S(OM2K_TRX_EVT_RX_DONE), - .out_state_mask = S(OM2K_TRX_S_ERROR) | - S(OM2K_TRX_S_WAIT_TS), - .name = "WAIT-RX", - .action = om2k_trx_s_wait_rx, - }, - [OM2K_TRX_S_WAIT_TS] = { - .in_event_mask = S(OM2K_TRX_EVT_TS_DONE), - .out_state_mask = S(OM2K_TRX_S_ERROR) | - S(OM2K_TRX_S_DONE), - .name = "WAIT-TS", - .action = om2k_trx_s_wait_ts, - }, - [OM2K_TRX_S_DONE] = { - .name = "DONE", - .onenter = om2k_trx_s_done_onenter, - }, - [OM2K_TRX_S_ERROR] = { - .name = "ERROR", - }, -}; - -static int om2k_trx_timer_cb(struct osmo_fsm_inst *fi) -{ - osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_ERROR, 0, 0); - return 0; -} - -static struct osmo_fsm om2k_trx_fsm = { - .name = "OM2000-TRX", - .states = om2k_trx_states, - .num_states = ARRAY_SIZE(om2k_trx_states), - .log_subsys = DNM, - .event_names = om2k_trx_events, - .timer_cb = om2k_trx_timer_cb, -}; - -struct osmo_fsm_inst *om2k_trx_fsm_start(struct osmo_fsm_inst *parent, - struct gsm_bts_trx *trx, - uint32_t term_event) -{ - struct osmo_fsm_inst *fi; - struct om2k_trx_fsm_priv *otfp; - char idbuf[32]; - - snprintf(idbuf, sizeof(idbuf), "%u/%u", trx->bts->nr, trx->nr); - - fi = osmo_fsm_inst_alloc_child_id(&om2k_trx_fsm, parent, term_event, - idbuf); - if (!fi) - return NULL; - - otfp = talloc_zero(fi, struct om2k_trx_fsm_priv); - otfp->trx = trx; - fi->priv = otfp; - - osmo_fsm_inst_dispatch(fi, OM2K_TRX_EVT_START, NULL); - - return fi; -} - - -/*********************************************************************** - * OM2000 BTS Finite State Machine, initializes CF and all siblings - ***********************************************************************/ - -enum om2k_bts_event { - OM2K_BTS_EVT_START, - OM2K_BTS_EVT_CF_DONE, - OM2K_BTS_EVT_IS_DONE, - OM2K_BTS_EVT_CON_DONE, - OM2K_BTS_EVT_TF_DONE, - OM2K_BTS_EVT_TRX_DONE, - OM2K_BTS_EVT_STOP, -}; - -static const struct value_string om2k_bts_events[] = { - { OM2K_BTS_EVT_START, "START" }, - { OM2K_BTS_EVT_CF_DONE, "CF-DONE" }, - { OM2K_BTS_EVT_IS_DONE, "IS-DONE" }, - { OM2K_BTS_EVT_CON_DONE, "CON-DONE" }, - { OM2K_BTS_EVT_TF_DONE, "TF-DONE" }, - { OM2K_BTS_EVT_TRX_DONE, "TRX-DONE" }, - { OM2K_BTS_EVT_STOP, "STOP" }, - { 0, NULL } -}; - -enum om2k_bts_state { - OM2K_BTS_S_INIT, - OM2K_BTS_S_WAIT_CF, - OM2K_BTS_S_WAIT_IS, - OM2K_BTS_S_WAIT_CON, - OM2K_BTS_S_WAIT_TF, - OM2K_BTS_S_WAIT_TRX, - OM2K_BTS_S_DONE, - OM2K_BTS_S_ERROR, -}; - -struct om2k_bts_fsm_priv { - struct gsm_bts *bts; - uint8_t next_trx_nr; -}; - -static void om2k_bts_s_init(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct om2k_bts_fsm_priv *obfp = fi->priv; - struct gsm_bts *bts = obfp->bts; - - OSMO_ASSERT(event == OM2K_BTS_EVT_START); - osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_CF, - BTS_FSM_TIMEOUT, 0); - om2k_mo_fsm_start(fi, OM2K_BTS_EVT_CF_DONE, bts->c0, - &bts->rbs2000.cf.om2k_mo); -} - -static void om2k_bts_s_wait_cf(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct om2k_bts_fsm_priv *obfp = fi->priv; - struct gsm_bts *bts = obfp->bts; - - OSMO_ASSERT(event == OM2K_BTS_EVT_CF_DONE); - /* TF can take a long time to initialize, wait for 10min */ - osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_TF, 600, 0); - om2k_mo_fsm_start(fi, OM2K_BTS_EVT_TF_DONE, bts->c0, - &bts->rbs2000.tf.om2k_mo); -} - -static void om2k_bts_s_wait_tf(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct om2k_bts_fsm_priv *obfp = fi->priv; - struct gsm_bts *bts = obfp->bts; - - OSMO_ASSERT(event == OM2K_BTS_EVT_TF_DONE); - - osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_CON, - BTS_FSM_TIMEOUT, 0); - om2k_mo_fsm_start(fi, OM2K_BTS_EVT_CON_DONE, bts->c0, - &bts->rbs2000.con.om2k_mo); -} - -static void om2k_bts_s_wait_con(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct om2k_bts_fsm_priv *obfp = fi->priv; - struct gsm_bts *bts = obfp->bts; - - OSMO_ASSERT(event == OM2K_BTS_EVT_CON_DONE); - - osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_IS, - BTS_FSM_TIMEOUT, 0); - om2k_mo_fsm_start(fi, OM2K_BTS_EVT_IS_DONE, bts->c0, - &bts->rbs2000.is.om2k_mo); -} - -static void om2k_bts_s_wait_is(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct om2k_bts_fsm_priv *obfp = fi->priv; - struct gsm_bts_trx *trx; - - OSMO_ASSERT(event == OM2K_BTS_EVT_IS_DONE); - - osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_TRX, - BTS_FSM_TIMEOUT, 0); - obfp->next_trx_nr = 0; - trx = gsm_bts_trx_num(obfp->bts, obfp->next_trx_nr++); - om2k_trx_fsm_start(fi, trx, OM2K_BTS_EVT_TRX_DONE); -} - -static void om2k_bts_s_wait_trx(struct osmo_fsm_inst *fi, uint32_t event, void *data) -{ - struct om2k_bts_fsm_priv *obfp = fi->priv; - - OSMO_ASSERT(event == OM2K_BTS_EVT_TRX_DONE); - - if (obfp->next_trx_nr < obfp->bts->num_trx) { - struct gsm_bts_trx *trx; - trx = gsm_bts_trx_num(obfp->bts, obfp->next_trx_nr++); - om2k_trx_fsm_start(fi, trx, OM2K_BTS_EVT_TRX_DONE); - } else { - osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_DONE, 0, 0); - } -} - -static void om2k_bts_s_done_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) -{ - osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL); -} - -static const struct osmo_fsm_state om2k_bts_states[] = { - [OM2K_BTS_S_INIT] = { - .in_event_mask = S(OM2K_BTS_EVT_START), - .out_state_mask = S(OM2K_BTS_S_WAIT_CF), - .name = "INIT", - .action = om2k_bts_s_init, - }, - [OM2K_BTS_S_WAIT_CF] = { - .in_event_mask = S(OM2K_BTS_EVT_CF_DONE), - .out_state_mask = S(OM2K_BTS_S_ERROR) | - S(OM2K_BTS_S_WAIT_TF), - .name = "WAIT-CF", - .action = om2k_bts_s_wait_cf, - }, - [OM2K_BTS_S_WAIT_TF] = { - .in_event_mask = S(OM2K_BTS_EVT_TF_DONE), - .out_state_mask = S(OM2K_BTS_S_ERROR) | - S(OM2K_BTS_S_WAIT_CON), - .name = "WAIT-TF", - .action = om2k_bts_s_wait_tf, - }, - [OM2K_BTS_S_WAIT_CON] = { - .in_event_mask = S(OM2K_BTS_EVT_CON_DONE), - .out_state_mask = S(OM2K_BTS_S_ERROR) | - S(OM2K_BTS_S_WAIT_IS), - .name = "WAIT-CON", - .action = om2k_bts_s_wait_con, - }, - [OM2K_BTS_S_WAIT_IS] = { - .in_event_mask = S(OM2K_BTS_EVT_IS_DONE), - .out_state_mask = S(OM2K_BTS_S_ERROR) | - S(OM2K_BTS_S_WAIT_TRX), - .name = "WAIT-IS", - .action = om2k_bts_s_wait_is, - }, - [OM2K_BTS_S_WAIT_TRX] = { - .in_event_mask = S(OM2K_BTS_EVT_TRX_DONE), - .out_state_mask = S(OM2K_BTS_S_ERROR) | - S(OM2K_BTS_S_DONE), - .name = "WAIT-TRX", - .action = om2k_bts_s_wait_trx, - }, - [OM2K_BTS_S_DONE] = { - .name = "DONE", - .onenter = om2k_bts_s_done_onenter, - }, - [OM2K_BTS_S_ERROR] = { - .name = "ERROR", - }, -}; - -static int om2k_bts_timer_cb(struct osmo_fsm_inst *fi) -{ - osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_ERROR, 0, 0); - return 0; -} - -static struct osmo_fsm om2k_bts_fsm = { - .name = "OM2000-BTS", - .states = om2k_bts_states, - .num_states = ARRAY_SIZE(om2k_bts_states), - .log_subsys = DNM, - .event_names = om2k_bts_events, - .timer_cb = om2k_bts_timer_cb, -}; - -struct osmo_fsm_inst * -om2k_bts_fsm_start(struct gsm_bts *bts) -{ - struct osmo_fsm_inst *fi; - struct om2k_bts_fsm_priv *obfp; - char idbuf[16]; - - snprintf(idbuf, sizeof(idbuf), "%u", bts->nr); - - fi = osmo_fsm_inst_alloc(&om2k_bts_fsm, bts, NULL, - LOGL_DEBUG, idbuf); - if (!fi) - return NULL; - fi->priv = obfp = talloc_zero(fi, struct om2k_bts_fsm_priv); - obfp->bts = bts; - - osmo_fsm_inst_dispatch(fi, OM2K_BTS_EVT_START, NULL); - - return fi; -} - - -/*********************************************************************** - * OM2000 Negotiation - ***********************************************************************/ - -static int abis_om2k_tx_negot_req_ack(struct gsm_bts *bts, const struct abis_om2k_mo *mo, - uint8_t *data, unsigned int len) -{ - struct msgb *msg = om2k_msgb_alloc(); - struct abis_om2k_hdr *o2k; - - o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); - fill_om2k_hdr(o2k, mo, OM2K_MSGT_NEGOT_REQ_ACK); - - msgb_tlv_put(msg, OM2K_DEI_NEGOT_REC2, len, data); - - DEBUGP(DNM, "Tx MO=%s %s\n", om2k_mo_name(mo), - get_value_string(om2k_msgcode_vals, OM2K_MSGT_NEGOT_REQ_ACK)); - - return abis_om2k_sendmsg(bts, msg); -} - -struct iwd_version { - uint8_t gen_char[3+1]; - uint8_t rev_char[3+1]; -}; - -struct iwd_type { - uint8_t num_vers; - struct iwd_version v[8]; -}; - -static int om2k_rx_negot_req(struct msgb *msg) -{ - struct e1inp_sign_link *sign_link = (struct e1inp_sign_link *)msg->dst; - struct abis_om2k_hdr *o2h = msgb_l2(msg); - struct iwd_type iwd_types[16]; - uint8_t num_iwd_types = o2h->data[2]; - uint8_t *cur = o2h->data+3; - unsigned int i, v; - - uint8_t out_buf[1024]; - uint8_t *out_cur = out_buf+1; - uint8_t out_num_types = 0; - - memset(iwd_types, 0, sizeof(iwd_types)); - - /* Parse the RBS-supported IWD versions into iwd_types array */ - for (i = 0; i < num_iwd_types; i++) { - uint8_t num_versions = *cur++; - uint8_t iwd_type = *cur++; - - iwd_types[iwd_type].num_vers = num_versions; - - for (v = 0; v < num_versions; v++) { - struct iwd_version *iwd_v = &iwd_types[iwd_type].v[v]; - - memcpy(iwd_v->gen_char, cur, 3); - cur += 3; - memcpy(iwd_v->rev_char, cur, 3); - cur += 3; - - DEBUGP(DNM, "\tIWD Type %u Gen %s Rev %s\n", iwd_type, - iwd_v->gen_char, iwd_v->rev_char); - } - } - - /* Select the last version for each IWD type */ - for (i = 0; i < ARRAY_SIZE(iwd_types); i++) { - struct iwd_type *type = &iwd_types[i]; - struct iwd_version *last_v; - - if (type->num_vers == 0) - continue; - - out_num_types++; - - last_v = &type->v[type->num_vers-1]; - - *out_cur++ = i; - memcpy(out_cur, last_v->gen_char, 3); - out_cur += 3; - memcpy(out_cur, last_v->rev_char, 3); - out_cur += 3; - } - - out_buf[0] = out_num_types; - - return abis_om2k_tx_negot_req_ack(sign_link->trx->bts, &o2h->mo, out_buf, out_cur - out_buf); -} - - -/*********************************************************************** - * OM2000 Receive Message Handler - ***********************************************************************/ - -static int om2k_rx_nack(struct msgb *msg) -{ - struct abis_om2k_hdr *o2h = msgb_l2(msg); - uint16_t msg_type = ntohs(o2h->msg_type); - struct tlv_parsed tp; - - LOGP(DNM, LOGL_ERROR, "Rx MO=%s %s", om2k_mo_name(&o2h->mo), - get_value_string(om2k_msgcode_vals, msg_type)); - - abis_om2k_msg_tlv_parse(&tp, o2h); - if (TLVP_PRESENT(&tp, OM2K_DEI_REASON_CODE)) - LOGPC(DNM, LOGL_ERROR, ", Reason 0x%02x", - *TLVP_VAL(&tp, OM2K_DEI_REASON_CODE)); - - if (TLVP_PRESENT(&tp, OM2K_DEI_RESULT_CODE)) - LOGPC(DNM, LOGL_ERROR, ", Result %s", - get_value_string(om2k_result_strings, - *TLVP_VAL(&tp, OM2K_DEI_RESULT_CODE))); - LOGPC(DNM, LOGL_ERROR, "\n"); - - return 0; -} - -static int process_mo_state(struct gsm_bts *bts, struct om2k_decoded_msg *odm) -{ - uint8_t mo_state; - - if (!TLVP_PRESENT(&odm->tp, OM2K_DEI_MO_STATE)) - return -EIO; - mo_state = *TLVP_VAL(&odm->tp, OM2K_DEI_MO_STATE); - - LOGP(DNM, LOGL_DEBUG, "Rx MO=%s %s, MO State: %s\n", - om2k_mo_name(&odm->o2h.mo), - get_value_string(om2k_msgcode_vals, odm->msg_type), - get_value_string(om2k_mostate_vals, mo_state)); - - /* Throw error message in case we see an enable rsponse that does - * not yield an enabled mo-state */ - if (odm->msg_type == OM2K_MSGT_ENABLE_RES - && mo_state != OM2K_MO_S_ENABLED) { - LOGP(DNM, LOGL_ERROR, - "Rx MO=%s %s Failed to enable MO State!\n", - om2k_mo_name(&odm->o2h.mo), - get_value_string(om2k_msgcode_vals, odm->msg_type)); - } - - update_mo_state(bts, &odm->o2h.mo, mo_state); - - return 0; -} - -/* Display fault report bits (helper function of display_fault_maps()) */ -static bool display_fault_bits(const uint8_t *vect, uint16_t len, - uint8_t dei, const struct abis_om2k_mo *mo) -{ - uint16_t i; - int k; - bool faults_present = false; - int first = 1; - char string[255]; - - /* Check if errors are present at all */ - for (i = 0; i < len; i++) - if (vect[i]) - faults_present = true; - if (!faults_present) - return false; - - sprintf(string, "Fault Report: %s (", - get_value_string(om2k_attr_vals, dei)); - - for (i = 0; i < len; i++) { - for (k = 0; k < 8; k++) { - if ((vect[i] >> k) & 1) { - if (!first) - sprintf(string + strlen(string), ","); - sprintf(string + strlen(string), "%d", k + i*8); - first = 0; - } - } - } - - sprintf(string + strlen(string), ")\n"); - DEBUGP(DNM, "Rx MO=%s %s", om2k_mo_name(mo), string); - - return true; -} - -/* Display fault report maps */ -static void display_fault_maps(const uint8_t *src, unsigned int src_len, - const struct abis_om2k_mo *mo) -{ - uint8_t tag; - uint16_t tag_len; - const uint8_t *val; - int src_pos = 0; - int rc; - int tlv_count = 0; - uint16_t msg_code; - bool faults_present = false; - - /* Chop off header */ - src+=4; - src_len-=4; - - /* Check message type */ - msg_code = (*src & 0xff) << 8; - src++; - src_len--; - msg_code |= (*src & 0xff); - src++; - src_len--; - if (msg_code != OM2K_MSGT_FAULT_REP) { - LOGP(DNM, LOGL_ERROR, "Rx MO=%s Fault report: invalid message code!\n", - om2k_mo_name(mo)); - return; - } - - /* Chop off mo-interface */ - src += 4; - src_len -= 4; - - /* Iterate over each TLV element */ - while (1) { - - /* Bail if an the maximum number of TLV fields - * have been parsed */ - if (tlv_count >= 11) { - LOGP(DNM, LOGL_ERROR, - "Rx MO=%s Fault Report: too many tlv elements!\n", - om2k_mo_name(mo)); - return; - } - - /* Parse TLV field */ - rc = tlv_parse_one(&tag, &tag_len, &val, &om2k_att_tlvdef, - src + src_pos, src_len - src_pos); - if (rc > 0) - src_pos += rc; - else { - LOGP(DNM, LOGL_ERROR, - "Rx MO=%s Fault Report: invalid tlv element!\n", - om2k_mo_name(mo)); - return; - } - - switch (tag) { - case OM2K_DEI_INT_FAULT_MAP_1A: - case OM2K_DEI_INT_FAULT_MAP_1B: - case OM2K_DEI_INT_FAULT_MAP_2A: - case OM2K_DEI_EXT_COND_MAP_1: - case OM2K_DEI_EXT_COND_MAP_2: - case OM2K_DEI_REPL_UNIT_MAP: - case OM2K_DEI_INT_FAULT_MAP_2A_EXT: - case OM2K_DEI_EXT_COND_MAP_2_EXT: - case OM2K_DEI_REPL_UNIT_MAP_EXT: - faults_present |= display_fault_bits(val, tag_len, - tag, mo); - break; - } - - /* Stop when no further TLV elements can be expected */ - if (src_len - src_pos < 2) - break; - - tlv_count++; - } - - if (!faults_present) { - DEBUGP(DNM, "Rx MO=%s Fault Report: All faults ceased!\n", - om2k_mo_name(mo)); - } -} - -int abis_om2k_rcvmsg(struct msgb *msg) -{ - struct e1inp_sign_link *sign_link = (struct e1inp_sign_link *)msg->dst; - struct gsm_bts *bts = sign_link->trx->bts; - struct abis_om2k_hdr *o2h = msgb_l2(msg); - struct abis_om_hdr *oh = &o2h->om; - uint16_t msg_type = ntohs(o2h->msg_type); - struct om2k_decoded_msg odm; - struct om2k_mo *mo; - int rc = 0; - - /* Various consistency checks */ - if (oh->placement != ABIS_OM_PLACEMENT_ONLY) { - LOGP(DNM, LOGL_ERROR, "ABIS OML placement 0x%x not supported\n", - oh->placement); - if (oh->placement != ABIS_OM_PLACEMENT_FIRST) - return -EINVAL; - } - if (oh->sequence != 0) { - LOGP(DNM, LOGL_ERROR, "ABIS OML sequence 0x%x != 0x00\n", - oh->sequence); - return -EINVAL; - } - - msg->l3h = (unsigned char *)o2h + sizeof(*o2h); - - if (oh->mdisc != ABIS_OM_MDISC_FOM) { - LOGP(DNM, LOGL_ERROR, "unknown ABIS OM2000 message discriminator 0x%x\n", - oh->mdisc); - return -EINVAL; - } - - DEBUGP(DNM, "Rx MO=%s %s (%s)\n", om2k_mo_name(&o2h->mo), - get_value_string(om2k_msgcode_vals, msg_type), - osmo_hexdump(msg->l2h, msgb_l2len(msg))); - - om2k_decode_msg(&odm, msg); - - process_mo_state(bts, &odm); - - switch (msg_type) { - case OM2K_MSGT_CAL_TIME_REQ: - rc = abis_om2k_cal_time_resp(bts); - break; - case OM2K_MSGT_FAULT_REP: - display_fault_maps(msg->l2h, msgb_l2len(msg), &o2h->mo); - rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_FAULT_REP_ACK); - break; - case OM2K_MSGT_NEGOT_REQ: - rc = om2k_rx_negot_req(msg); - break; - case OM2K_MSGT_START_RES: - /* common processing here */ - rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_START_RES_ACK); - /* below we dispatch into MO */ - break; - case OM2K_MSGT_IS_CONF_RES: - rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_IS_CONF_RES_ACK); - break; - case OM2K_MSGT_CON_CONF_RES: - rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_CON_CONF_RES_ACK); - break; - case OM2K_MSGT_TX_CONF_RES: - rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_TX_CONF_RES_ACK); - break; - case OM2K_MSGT_RX_CONF_RES: - rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_RX_CONF_RES_ACK); - break; - case OM2K_MSGT_TS_CONF_RES: - rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_TS_CONF_RES_ACK); - break; - case OM2K_MSGT_TF_CONF_RES: - rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_TF_CONF_RES_ACK); - break; - case OM2K_MSGT_ENABLE_RES: - rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_ENABLE_RES_ACK); - break; - case OM2K_MSGT_DISABLE_RES: - rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_DISABLE_RES_ACK); - break; - case OM2K_MSGT_TEST_RES: - rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_TEST_RES_ACK); - break; - case OM2K_MSGT_CAPA_RES: - rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_CAPA_RES_ACK); - break; - /* ERrors */ - case OM2K_MSGT_START_REQ_REJ: - case OM2K_MSGT_CONNECT_REJ: - case OM2K_MSGT_OP_INFO_REJ: - case OM2K_MSGT_DISCONNECT_REJ: - case OM2K_MSGT_TEST_REQ_REJ: - case OM2K_MSGT_CON_CONF_REQ_REJ: - case OM2K_MSGT_IS_CONF_REQ_REJ: - case OM2K_MSGT_TX_CONF_REQ_REJ: - case OM2K_MSGT_RX_CONF_REQ_REJ: - case OM2K_MSGT_TS_CONF_REQ_REJ: - case OM2K_MSGT_TF_CONF_REQ_REJ: - case OM2K_MSGT_ENABLE_REQ_REJ: - case OM2K_MSGT_ALARM_STATUS_REQ_REJ: - case OM2K_MSGT_DISABLE_REQ_REJ: - rc = om2k_rx_nack(msg); - break; - } - - /* Resolve the MO for this message */ - mo = get_om2k_mo(bts, &o2h->mo); - if (!mo) { - LOGP(DNM, LOGL_ERROR, "Couldn't resolve MO for OM2K msg " - "%s: %s\n", get_value_string(om2k_msgcode_vals, msg_type), - msgb_hexdump(msg)); - return 0; - } - if (!mo->fsm) { - LOGP(DNM, LOGL_ERROR, "MO object should not generate any message. fsm == NULL " - "%s: %s\n", get_value_string(om2k_msgcode_vals, msg_type), - msgb_hexdump(msg)); - return 0; - } - - /* Dispatch message to that MO */ - om2k_mo_fsm_recvmsg(bts, mo, &odm); - - msgb_free(msg); - return rc; -} - -static void om2k_mo_init(struct om2k_mo *mo, uint8_t class, - uint8_t bts_nr, uint8_t assoc_so, uint8_t inst) -{ - mo->addr.class = class; - mo->addr.bts = bts_nr; - mo->addr.assoc_so = assoc_so; - mo->addr.inst = inst; -} - -/* initialize the OM2K_MO members of gsm_bts_trx and its timeslots */ -void abis_om2k_trx_init(struct gsm_bts_trx *trx) -{ - struct gsm_bts *bts = trx->bts; - unsigned int i; - - OSMO_ASSERT(bts->type == GSM_BTS_TYPE_RBS2000); - - om2k_mo_init(&trx->rbs2000.trxc.om2k_mo, OM2K_MO_CLS_TRXC, - bts->nr, 255, trx->nr); - om2k_mo_init(&trx->rbs2000.tx.om2k_mo, OM2K_MO_CLS_TX, - bts->nr, 255, trx->nr); - om2k_mo_init(&trx->rbs2000.rx.om2k_mo, OM2K_MO_CLS_RX, - bts->nr, 255, trx->nr); - - for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { - om2k_mo_init(&trx->ts[i].rbs2000.om2k_mo, OM2K_MO_CLS_TS, - bts->nr, trx->nr, i); - } -} - -/* initialize the OM2K_MO members of gsm_bts */ -void abis_om2k_bts_init(struct gsm_bts *bts) -{ - OSMO_ASSERT(bts->type == GSM_BTS_TYPE_RBS2000); - - om2k_mo_init(&bts->rbs2000.cf.om2k_mo, OM2K_MO_CLS_CF, - bts->nr, 0xFF, 0); - om2k_mo_init(&bts->rbs2000.is.om2k_mo, OM2K_MO_CLS_IS, - bts->nr, 0xFF, 0); - om2k_mo_init(&bts->rbs2000.con.om2k_mo, OM2K_MO_CLS_CON, - bts->nr, 0xFF, 0); - om2k_mo_init(&bts->rbs2000.dp.om2k_mo, OM2K_MO_CLS_DP, - bts->nr, 0xFF, 0); - om2k_mo_init(&bts->rbs2000.tf.om2k_mo, OM2K_MO_CLS_TF, - bts->nr, 0xFF, 0); -} - -static __attribute__((constructor)) void abis_om2k_init(void) -{ - osmo_fsm_register(&om2k_mo_fsm); - osmo_fsm_register(&om2k_bts_fsm); - osmo_fsm_register(&om2k_trx_fsm); -} diff --git a/openbsc/src/libbsc/abis_om2000_vty.c b/openbsc/src/libbsc/abis_om2000_vty.c deleted file mode 100644 index a6bc4c78c..000000000 --- a/openbsc/src/libbsc/abis_om2000_vty.c +++ /dev/null @@ -1,609 +0,0 @@ -/* VTY interface for A-bis OM2000 */ - -/* (C) 2010-2011 by Harald Welte <laforge@gnumonks.org> - * - * 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 <http://www.gnu.org/licenses/>. - * - */ - -#include <stdlib.h> -#include <unistd.h> -#include <errno.h> -#include <stdint.h> - -#include <arpa/inet.h> - -#include <openbsc/gsm_data.h> -#include <osmocom/core/msgb.h> -#include <osmocom/gsm/tlv.h> -#include <osmocom/core/talloc.h> -#include <openbsc/debug.h> -#include <openbsc/signal.h> -#include <openbsc/abis_om2000.h> -#include <openbsc/vty.h> - -#include <osmocom/vty/vty.h> -#include <osmocom/vty/command.h> -#include <osmocom/vty/logging.h> -#include <osmocom/vty/telnet_interface.h> - -extern struct gsm_network *bsc_gsmnet; - -static struct cmd_node om2k_node = { - OM2K_NODE, - "%s(om2k)# ", - 1, -}; - -static struct cmd_node om2k_con_group_node = { - OM2K_CON_GROUP_NODE, - "%s(om2k-con-group)# ", - 1, -}; - -struct con_group; - -struct oml_node_state { - struct gsm_bts *bts; - struct abis_om2k_mo mo; - struct con_group *cg; -}; - -static int dummy_config_write(struct vty *v) -{ - return CMD_SUCCESS; -} - -/* FIXME: auto-generate those strings from the value_string lists */ -#define OM2K_OBJCLASS_VTY "(trxc|ts|tf|is|con|dp|cf|tx|rx)" -#define OM2K_OBJCLASS_VTY_HELP "TRX Controller\n" \ - "Timeslot\n" \ - "Timing Function\n" \ - "Interface Switch\n" \ - "Abis Concentrator\n" \ - "Digital Path\n" \ - "Central Function\n" \ - "Transmitter\n" \ - "Receiver\n" - -DEFUN(om2k_class_inst, om2k_class_inst_cmd, - "bts <0-255> om2000 class " OM2K_OBJCLASS_VTY - " <0-255> <0-255> <0-255>", - "BTS related commands\n" "BTS Number\n" - "Manipulate the OM2000 managed objects\n" - "Object Class\n" OM2K_OBJCLASS_VTY_HELP - "BTS Number\n" "Associated SO Instance\n" "Instance Number\n") -{ - struct gsm_bts *bts; - struct oml_node_state *oms; - int bts_nr = atoi(argv[0]); - - bts = gsm_bts_num(gsmnet_from_vty(vty), bts_nr); - if (!bts) { - vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE); - return CMD_WARNING; - } - - if (bts->type != GSM_BTS_TYPE_RBS2000) { - vty_out(vty, "%% BTS %d not an Ericsson RBS%s", - bts_nr, VTY_NEWLINE); - return CMD_WARNING; - } - - oms = talloc_zero(tall_bsc_ctx, struct oml_node_state); - if (!oms) - return CMD_WARNING; - - oms->bts = bts; - oms->mo.class = get_string_value(om2k_mo_class_short_vals, argv[1]); - oms->mo.bts = atoi(argv[2]); - oms->mo.assoc_so = atoi(argv[3]); - oms->mo.inst = atoi(argv[4]); - - vty->index = oms; - vty->node = OM2K_NODE; - - return CMD_SUCCESS; - -} - -DEFUN(om2k_classnum_inst, om2k_classnum_inst_cmd, - "bts <0-255> om2000 class <0-255> <0-255> <0-255> <0-255>", - "BTS related commands\n" "BTS Number\n" - "Manipulate the OML managed objects\n" - "Object Class\n" "Object Class\n" - "BTS Number\n" "Associated SO Instance\n" "Instance Number\n") -{ - struct gsm_bts *bts; - struct oml_node_state *oms; - int bts_nr = atoi(argv[0]); - - bts = gsm_bts_num(gsmnet_from_vty(vty), bts_nr); - if (!bts) { - vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE); - return CMD_WARNING; - } - - oms = talloc_zero(tall_bsc_ctx, struct oml_node_state); - if (!oms) - return CMD_WARNING; - - oms->bts = bts; - oms->mo.class = atoi(argv[1]); - oms->mo.bts = atoi(argv[2]); - oms->mo.assoc_so = atoi(argv[3]); - oms->mo.inst = atoi(argv[4]); - - vty->index = oms; - vty->node = OM2K_NODE; - - return CMD_SUCCESS; -} - -DEFUN(om2k_reset, om2k_reset_cmd, - "reset-command", - "Reset the MO\n") -{ - struct oml_node_state *oms = vty->index; - - abis_om2k_tx_reset_cmd(oms->bts, &oms->mo); - return CMD_SUCCESS; -} - -DEFUN(om2k_start, om2k_start_cmd, - "start-request", - "Start the MO\n") -{ - struct oml_node_state *oms = vty->index; - - abis_om2k_tx_start_req(oms->bts, &oms->mo); - return CMD_SUCCESS; -} - -DEFUN(om2k_status, om2k_status_cmd, - "status-request", - "Get the MO Status\n") -{ - struct oml_node_state *oms = vty->index; - - abis_om2k_tx_status_req(oms->bts, &oms->mo); - return CMD_SUCCESS; -} - -DEFUN(om2k_connect, om2k_connect_cmd, - "connect-command", - "Connect the MO\n") -{ - struct oml_node_state *oms = vty->index; - - abis_om2k_tx_connect_cmd(oms->bts, &oms->mo); - return CMD_SUCCESS; -} - -DEFUN(om2k_disconnect, om2k_disconnect_cmd, - "disconnect-command", - "Disconnect the MO\n") -{ - struct oml_node_state *oms = vty->index; - - abis_om2k_tx_disconnect_cmd(oms->bts, &oms->mo); - return CMD_SUCCESS; -} - -DEFUN(om2k_enable, om2k_enable_cmd, - "enable-request", - "Enable the MO\n") -{ - struct oml_node_state *oms = vty->index; - - abis_om2k_tx_enable_req(oms->bts, &oms->mo); - return CMD_SUCCESS; -} - -DEFUN(om2k_disable, om2k_disable_cmd, - "disable-request", - "Disable the MO\n") -{ - struct oml_node_state *oms = vty->index; - - abis_om2k_tx_disable_req(oms->bts, &oms->mo); - return CMD_SUCCESS; -} - -DEFUN(om2k_op_info, om2k_op_info_cmd, - "operational-info <0-1>", - "Set operational information\n" - "Set operational info to 0 or 1\n") -{ - struct oml_node_state *oms = vty->index; - int oper = atoi(argv[0]); - - abis_om2k_tx_op_info(oms->bts, &oms->mo, oper); - return CMD_SUCCESS; -} - -DEFUN(om2k_test, om2k_test_cmd, - "test-request", - "Test the MO\n") -{ - struct oml_node_state *oms = vty->index; - - abis_om2k_tx_test_req(oms->bts, &oms->mo); - return CMD_SUCCESS; -} - -DEFUN(om2k_cap_req, om2k_cap_req_cmd, - "capabilities-request", - "Request MO capabilities\n") -{ - struct oml_node_state *oms = vty->index; - - abis_om2k_tx_cap_req(oms->bts, &oms->mo); - return CMD_SUCCESS; -} - -static struct con_group *con_group_find_or_create(struct gsm_bts *bts, uint8_t cg) -{ - struct con_group *ent; - - llist_for_each_entry(ent, &bts->rbs2000.con.conn_groups, list) { - if (ent->cg == cg) - return ent; - } - - ent = talloc_zero(bts, struct con_group); - ent->bts = bts; - ent->cg = cg; - INIT_LLIST_HEAD(&ent->paths); - llist_add_tail(&ent->list, &bts->rbs2000.con.conn_groups); - - return ent; -} - -static int con_group_del(struct gsm_bts *bts, uint8_t cg_id) -{ - struct con_group *cg, *cg2; - - llist_for_each_entry_safe(cg, cg2, &bts->rbs2000.con.conn_groups, list) { - if (cg->cg == cg_id) { - llist_del(&cg->list); - talloc_free(cg); - return 0; - }; - } - return -ENOENT; -} - -static void con_group_add_path(struct con_group *cg, uint16_t ccp, - uint8_t ci, uint8_t tag, uint8_t tei) -{ - struct con_path *cp = talloc_zero(cg, struct con_path); - - cp->ccp = ccp; - cp->ci = ci; - cp->tag = tag; - cp->tei = tei; - llist_add(&cp->list, &cg->paths); -} - -static int con_group_del_path(struct con_group *cg, uint16_t ccp, - uint8_t ci, uint8_t tag, uint8_t tei) -{ - struct con_path *cp, *cp2; - llist_for_each_entry_safe(cp, cp2, &cg->paths, list) { - if (cp->ccp == ccp && cp->ci == ci && cp->tag == tag && - cp->tei == tei) { - llist_del(&cp->list); - talloc_free(cp); - return 0; - } - } - return -ENOENT; -} - -DEFUN(cfg_om2k_con_group, cfg_om2k_con_group_cmd, - "con-connection-group <1-31>", - "Configure a CON (Concentrator) Connection Group\n" - "CON Connection Group Number\n") -{ - struct gsm_bts *bts = vty->index; - struct con_group *cg; - uint8_t cgid = atoi(argv[0]); - - if (bts->type != GSM_BTS_TYPE_RBS2000) { - vty_out(vty, "%% CON MO only exists in RBS2000%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - cg = con_group_find_or_create(bts, cgid); - if (!cg) { - vty_out(vty, "%% Cannot create CON Group%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - vty->node = OM2K_CON_GROUP_NODE; - vty->index = cg; - - return CMD_SUCCESS; -} - -DEFUN(del_om2k_con_group, del_om2k_con_group_cmd, - "del-connection-group <1-31>", - "Delete a CON (Concentrator) Connection Group\n" - "CON Connection Group Number\n") -{ - struct gsm_bts *bts = vty->index; - int rc; - uint8_t cgid = atoi(argv[0]); - - if (bts->type != GSM_BTS_TYPE_RBS2000) { - vty_out(vty, "%% CON MO only exists in RBS2000%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - rc = con_group_del(bts, cgid); - if (rc != 0) { - vty_out(vty, "%% Cannot delete CON Group%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - return CMD_SUCCESS; -} - -#define CON_PATH_HELP "CON Path (In/Out)\n" \ - "Add CON Path to Concentration Group\n" \ - "Delete CON Path from Concentration Group\n" \ - "CON Conection Point\n" \ - "Contiguity Index\n" \ - -DEFUN(cfg_om2k_con_path_dec, cfg_om2k_con_path_dec_cmd, - "con-path (add|del) <0-2047> <0-255> deconcentrated <0-63>", - CON_PATH_HELP "De-concentrated in/outlet\n" "TEI Value\n") -{ - struct con_group *cg = vty->index; - uint16_t ccp = atoi(argv[1]); - uint8_t ci = atoi(argv[2]); - uint8_t tei = atoi(argv[3]); - - if (!strcmp(argv[0], "add")) - con_group_add_path(cg, ccp, ci, 0, tei); - else { - if (con_group_del_path(cg, ccp, ci, 0, tei) < 0) { - vty_out(vty, "%% No matching CON Path%s", - VTY_NEWLINE); - return CMD_WARNING; - } - } - - return CMD_SUCCESS; -} - -DEFUN(cfg_om2k_con_path_conc, cfg_om2k_con_path_conc_cmd, - "con-path (add|del) <0-2047> <0-255> concentrated <1-16>", - CON_PATH_HELP "Concentrated in/outlet\n" "Tag Number\n") -{ - struct con_group *cg = vty->index; - uint16_t ccp = atoi(argv[1]); - uint8_t ci = atoi(argv[2]); - uint8_t tag = atoi(argv[3]); - - if (!strcmp(argv[0], "add")) - con_group_add_path(cg, ccp, ci, tag, 0xff); - else { - if (con_group_del_path(cg, ccp, ci, tag, 0xff) < 0) { - vty_out(vty, "%% No matching CON list entry%s", - VTY_NEWLINE); - return CMD_WARNING; - } - } - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_alt_mode, cfg_bts_alt_mode_cmd, - "abis-lower-transport (single-timeslot|super-channel)", - "Configure thee Abis Lower Transport\n" - "Single Timeslot (classic Abis)\n" - "SuperChannel (Packet Abis)\n") -{ - struct gsm_bts *bts = vty->index; - struct con_group *cg; - - if (bts->type != GSM_BTS_TYPE_RBS2000) { - vty_out(vty, "%% Command only works for RBS2000%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - if (!strcmp(argv[0], "super-channel")) - bts->rbs2000.use_superchannel = 1; - else - bts->rbs2000.use_superchannel = 0; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_is_conn_list, cfg_bts_is_conn_list_cmd, - "is-connection-list (add|del) <0-2047> <0-2047> <0-255>", - "Interface Switch Connection List\n" - "Add to IS list\n" "Delete from IS list\n" - "ICP1\n" "ICP2\n" "Contiguity Index\n") -{ - struct gsm_bts *bts = vty->index; - uint16_t icp1 = atoi(argv[1]); - uint16_t icp2 = atoi(argv[2]); - uint8_t ci = atoi(argv[3]); - struct is_conn_group *grp, *grp2; - - if (bts->type != GSM_BTS_TYPE_RBS2000) { - vty_out(vty, "%% IS MO only exists in RBS2000%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - if (!strcmp(argv[0], "add")) { - grp = talloc_zero(bts, struct is_conn_group); - grp->icp1 = icp1; - grp->icp2 = icp2; - grp->ci = ci; - llist_add_tail(&grp->list, &bts->rbs2000.is.conn_groups); - } else { - llist_for_each_entry_safe(grp, grp2, &bts->rbs2000.is.conn_groups, list) { - if (grp->icp1 == icp1 && grp->icp2 == icp2 - && grp->ci == ci) { - llist_del(&grp->list); - talloc_free(grp); - return CMD_SUCCESS; - } - } - vty_out(vty, "%% No matching IS Conn Group found!%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - return CMD_SUCCESS; -} - - -DEFUN(om2k_conf_req, om2k_conf_req_cmd, - "configuration-request", - "Send the configuration request for current MO\n") -{ - struct oml_node_state *oms = vty->index; - struct gsm_bts *bts = oms->bts; - struct gsm_bts_trx *trx = NULL; - struct gsm_bts_trx_ts *ts = NULL; - - switch (oms->mo.class) { - case OM2K_MO_CLS_IS: - abis_om2k_tx_is_conf_req(bts); - break; - case OM2K_MO_CLS_TS: - trx = gsm_bts_trx_by_nr(bts, oms->mo.assoc_so); - if (!trx) { - vty_out(vty, "%% BTS %u has no TRX %u%s", bts->nr, - oms->mo.assoc_so, VTY_NEWLINE); - return CMD_WARNING; - } - if (oms->mo.inst >= ARRAY_SIZE(trx->ts)) { - vty_out(vty, "%% Timeslot %u out of range%s", - oms->mo.inst, VTY_NEWLINE); - return CMD_WARNING; - } - ts = &trx->ts[oms->mo.inst]; - abis_om2k_tx_ts_conf_req(ts); - break; - case OM2K_MO_CLS_RX: - case OM2K_MO_CLS_TX: - case OM2K_MO_CLS_TRXC: - trx = gsm_bts_trx_by_nr(bts, oms->mo.inst); - if (!trx) { - vty_out(vty, "%% BTS %u has no TRX %u%s", bts->nr, - oms->mo.inst, VTY_NEWLINE); - return CMD_WARNING; - } - switch (oms->mo.class) { - case OM2K_MO_CLS_RX: - abis_om2k_tx_rx_conf_req(trx); - break; - case OM2K_MO_CLS_TX: - abis_om2k_tx_tx_conf_req(trx); - break; - default: - break; - } - break; - case OM2K_MO_CLS_TF: - abis_om2k_tx_tf_conf_req(bts); - break; - default: - vty_out(vty, "%% Don't know how to configure MO%s", - VTY_NEWLINE); - } - - return CMD_SUCCESS; -} - -static void dump_con_group(struct vty *vty, struct con_group *cg) -{ - struct con_path *cp; - - llist_for_each_entry(cp, &cg->paths, list) { - vty_out(vty, " con-path add %u %u ", cp->ccp, cp->ci); - if (cp->tei == 0xff) { - vty_out(vty, "concentrated %u%s", cp->tag, - VTY_NEWLINE); - } else { - vty_out(vty, "deconcentrated %u%s", cp->tei, - VTY_NEWLINE); - } - } -} - -void abis_om2k_config_write_bts(struct vty *vty, struct gsm_bts *bts) -{ - struct is_conn_group *igrp; - struct con_group *cgrp; - - llist_for_each_entry(igrp, &bts->rbs2000.is.conn_groups, list) - vty_out(vty, " is-connection-list add %u %u %u%s", - igrp->icp1, igrp->icp2, igrp->ci, VTY_NEWLINE); - - llist_for_each_entry(cgrp, &bts->rbs2000.con.conn_groups, list) { - vty_out(vty, " con-connection-group %u%s", cgrp->cg, - VTY_NEWLINE); - dump_con_group(vty, cgrp); - } - if (bts->rbs2000.use_superchannel) - vty_out(vty, " abis-lower-transport super-channel%s", - VTY_NEWLINE); -} - -int abis_om2k_vty_init(void) -{ - install_element(ENABLE_NODE, &om2k_class_inst_cmd); - install_element(ENABLE_NODE, &om2k_classnum_inst_cmd); - install_node(&om2k_node, dummy_config_write); - - vty_install_default(OM2K_NODE); - install_element(OM2K_NODE, &om2k_reset_cmd); - install_element(OM2K_NODE, &om2k_start_cmd); - install_element(OM2K_NODE, &om2k_status_cmd); - install_element(OM2K_NODE, &om2k_connect_cmd); - install_element(OM2K_NODE, &om2k_disconnect_cmd); - install_element(OM2K_NODE, &om2k_enable_cmd); - install_element(OM2K_NODE, &om2k_disable_cmd); - install_element(OM2K_NODE, &om2k_op_info_cmd); - install_element(OM2K_NODE, &om2k_test_cmd); - install_element(OM2K_NODE, &om2k_cap_req_cmd); - install_element(OM2K_NODE, &om2k_conf_req_cmd); - - install_node(&om2k_con_group_node, dummy_config_write); - vty_install_default(OM2K_CON_GROUP_NODE); - install_element(OM2K_CON_GROUP_NODE, &cfg_om2k_con_path_dec_cmd); - install_element(OM2K_CON_GROUP_NODE, &cfg_om2k_con_path_conc_cmd); - - install_element(BTS_NODE, &cfg_bts_is_conn_list_cmd); - install_element(BTS_NODE, &cfg_bts_alt_mode_cmd); - install_element(BTS_NODE, &cfg_om2k_con_group_cmd); - install_element(BTS_NODE, &del_om2k_con_group_cmd); - - return 0; -} diff --git a/openbsc/src/libbsc/abis_rsl.c b/openbsc/src/libbsc/abis_rsl.c deleted file mode 100644 index 6ae790f6c..000000000 --- a/openbsc/src/libbsc/abis_rsl.c +++ /dev/null @@ -1,2927 +0,0 @@ -/* GSM Radio Signalling Link messages on the A-bis interface - * 3GPP TS 08.58 version 8.6.0 Release 1999 / ETSI TS 100 596 V8.6.0 */ - -/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org> - * (C) 2012 by Holger Hans Peter Freyther - * - * 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 <http://www.gnu.org/licenses/>. - * - */ - -#include <stdio.h> -#include <stdlib.h> -#include <errno.h> -#include <netinet/in.h> -#include <arpa/inet.h> - -#include <openbsc/gsm_data.h> -#include <openbsc/gsm_04_08.h> -#include <osmocom/gsm/gsm_utils.h> -#include <openbsc/abis_rsl.h> -#include <openbsc/chan_alloc.h> -#include <openbsc/bsc_rll.h> -#include <openbsc/debug.h> -#include <osmocom/gsm/tlv.h> -#include <osmocom/gsm/protocol/gsm_04_08.h> -#include <osmocom/gsm/protocol/gsm_08_58.h> -#include <openbsc/paging.h> -#include <openbsc/signal.h> -#include <openbsc/meas_rep.h> -#include <openbsc/rtp_proxy.h> -#include <openbsc/gsm_subscriber.h> -#include <osmocom/abis/e1_input.h> -#include <osmocom/gsm/rsl.h> -#include <osmocom/core/talloc.h> -#include <openbsc/pcu_if.h> - -#define RSL_ALLOC_SIZE 1024 -#define RSL_ALLOC_HEADROOM 128 - -enum sacch_deact { - SACCH_NONE, - SACCH_DEACTIVATE, -}; - -static int rsl_send_imm_assignment(struct gsm_lchan *lchan); -static void error_timeout_cb(void *data); -static int dyn_ts_switchover_continue(struct gsm_bts_trx_ts *ts); -static int dyn_ts_switchover_failed(struct gsm_bts_trx_ts *ts, int rc); -static void dyn_ts_switchover_complete(struct gsm_lchan *lchan); - -static void send_lchan_signal(int sig_no, struct gsm_lchan *lchan, - struct gsm_meas_rep *resp) -{ - struct lchan_signal_data sig; - sig.lchan = lchan; - sig.mr = resp; - osmo_signal_dispatch(SS_LCHAN, sig_no, &sig); -} - -static void do_lchan_free(struct gsm_lchan *lchan) -{ - /* We start the error timer to make the channel available again */ - if (lchan->state == LCHAN_S_REL_ERR) { - osmo_timer_setup(&lchan->error_timer, error_timeout_cb, lchan); - osmo_timer_schedule(&lchan->error_timer, - lchan->ts->trx->bts->network->T3111 + 2, 0); - } else { - rsl_lchan_set_state(lchan, LCHAN_S_NONE); - } - lchan_free(lchan); -} - -static void count_codecs(struct gsm_bts *bts, struct gsm_lchan *lchan) -{ - OSMO_ASSERT(bts); - - if (lchan->type == GSM_LCHAN_TCH_H) { - switch (lchan->tch_mode) { - case GSM48_CMODE_SPEECH_AMR: - rate_ctr_inc(&bts->network->bsc_ctrs->ctr[BSC_CTR_CODEC_AMR_H]); - break; - case GSM48_CMODE_SPEECH_V1: - rate_ctr_inc(&bts->network->bsc_ctrs->ctr[BSC_CTR_CODEC_V1_HR]); - break; - default: - break; - } - } else if (lchan->type == GSM_LCHAN_TCH_F) { - switch (lchan->tch_mode) { - case GSM48_CMODE_SPEECH_AMR: - rate_ctr_inc(&bts->network->bsc_ctrs->ctr[BSC_CTR_CODEC_AMR_F]); - break; - case GSM48_CMODE_SPEECH_V1: - rate_ctr_inc(&bts->network->bsc_ctrs->ctr[BSC_CTR_CODEC_V1_FR]); - break; - case GSM48_CMODE_SPEECH_EFR: - rate_ctr_inc(&bts->network->bsc_ctrs->ctr[BSC_CTR_CODEC_EFR]); - break; - default: - break; - } - } -} - -static uint8_t mdisc_by_msgtype(uint8_t msg_type) -{ - /* mask off the transparent bit ? */ - msg_type &= 0xfe; - - if ((msg_type & 0xf0) == 0x00) - return ABIS_RSL_MDISC_RLL; - if ((msg_type & 0xf0) == 0x10) { - if (msg_type >= 0x19 && msg_type <= 0x22) - return ABIS_RSL_MDISC_TRX; - else - return ABIS_RSL_MDISC_COM_CHAN; - } - if ((msg_type & 0xe0) == 0x20) - return ABIS_RSL_MDISC_DED_CHAN; - - return ABIS_RSL_MDISC_LOC; -} - -static inline void init_dchan_hdr(struct abis_rsl_dchan_hdr *dh, - uint8_t msg_type) -{ - dh->c.msg_discr = mdisc_by_msgtype(msg_type); - dh->c.msg_type = msg_type; - dh->ie_chan = RSL_IE_CHAN_NR; -} - -/* call rsl_lchan_lookup and set the log context */ -static struct gsm_lchan *lchan_lookup(struct gsm_bts_trx *trx, uint8_t chan_nr, - const char *log_name) -{ - int rc; - struct gsm_lchan *lchan = rsl_lchan_lookup(trx, chan_nr, &rc); - - if (!lchan) { - LOGP(DRSL, LOGL_ERROR, "%sunknown chan_nr=0x%02x\n", - log_name, chan_nr); - return NULL; - } - - if (rc < 0) - LOGP(DRSL, LOGL_ERROR, "%s %smismatching chan_nr=0x%02x\n", - gsm_ts_and_pchan_name(lchan->ts), log_name, chan_nr); - - if (lchan->conn) - log_set_context(LOG_CTX_VLR_SUBSCR, lchan->conn->subscr); - - return lchan; -} - -/* As per TS 03.03 Section 2.2, the IMSI has 'not more than 15 digits' */ -uint64_t str_to_imsi(const char *imsi_str) -{ - uint64_t ret; - - ret = strtoull(imsi_str, NULL, 10); - - return ret; -} - -static struct msgb *rsl_msgb_alloc(void) -{ - return msgb_alloc_headroom(RSL_ALLOC_SIZE, RSL_ALLOC_HEADROOM, - "RSL"); -} - -static void pad_macblock(uint8_t *out, const uint8_t *in, int len) -{ - memcpy(out, in, len); - - if (len < GSM_MACBLOCK_LEN) - memset(out+len, 0x2b, GSM_MACBLOCK_LEN - len); -} - -/* Chapter 9.3.7: Encryption Information */ -static int build_encr_info(uint8_t *out, struct gsm_lchan *lchan) -{ - *out++ = lchan->encr.alg_id & 0xff; - if (lchan->encr.key_len) - memcpy(out, lchan->encr.key, lchan->encr.key_len); - return lchan->encr.key_len + 1; -} - -static void print_rsl_cause(int lvl, const uint8_t *cause_v, uint8_t cause_len) -{ - int i; - - LOGPC(DRSL, lvl, "CAUSE=0x%02x(%s) ", - cause_v[0], rsl_err_name(cause_v[0])); - for (i = 1; i < cause_len-1; i++) - LOGPC(DRSL, lvl, "%02x ", cause_v[i]); -} - -static void lchan_act_tmr_cb(void *data) -{ - struct gsm_lchan *lchan = data; - - rsl_lchan_mark_broken(lchan, "activation timeout"); - lchan_free(lchan); -} - -static void lchan_deact_tmr_cb(void *data) -{ - struct gsm_lchan *lchan = data; - - rsl_lchan_mark_broken(lchan, "de-activation timeout"); - lchan_free(lchan); -} - - -/* Send a BCCH_INFO message as per Chapter 8.5.1 */ -int rsl_bcch_info(const struct gsm_bts_trx *trx, enum osmo_sysinfo_type si_type, const uint8_t *data, int len) -{ - struct abis_rsl_dchan_hdr *dh; - const struct gsm_bts *bts = trx->bts; - struct msgb *msg = rsl_msgb_alloc(); - uint8_t type = osmo_sitype2rsl(si_type); - - if (bts->c0 != trx) - LOGP(DRR, LOGL_ERROR, "Attempting to set BCCH SI%s on wrong BTS%u/TRX%u\n", - get_value_string(osmo_sitype_strs, si_type), bts->nr, trx->nr); - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof*dh); - init_dchan_hdr(dh, RSL_MT_BCCH_INFO); - dh->chan_nr = RSL_CHAN_BCCH; - - if (trx->bts->type == GSM_BTS_TYPE_RBS2000 - && type == RSL_SYSTEM_INFO_13) { - /* Ericsson proprietary encoding of SI13 */ - msgb_tv_put(msg, RSL_IE_SYSINFO_TYPE, RSL_ERIC_SYSTEM_INFO_13); - msgb_tlv_put(msg, RSL_IE_FULL_BCCH_INFO, len, data); - msgb_tv_put(msg, RSL_IE_ERIC_BCCH_MAPPING, 0x00); - } else { - /* Normal encoding */ - msgb_tv_put(msg, RSL_IE_SYSINFO_TYPE, type); - msgb_tlv_put(msg, RSL_IE_FULL_BCCH_INFO, len, data); - } - - msg->dst = trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -int rsl_sacch_filling(struct gsm_bts_trx *trx, uint8_t type, - const uint8_t *data, int len) -{ - struct abis_rsl_common_hdr *ch; - struct msgb *msg = rsl_msgb_alloc(); - - ch = (struct abis_rsl_common_hdr *) msgb_put(msg, sizeof(*ch)); - ch->msg_discr = ABIS_RSL_MDISC_TRX; - ch->msg_type = RSL_MT_SACCH_FILL; - - msgb_tv_put(msg, RSL_IE_SYSINFO_TYPE, type); - msgb_tl16v_put(msg, RSL_IE_L3_INFO, len, data); - - msg->dst = trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -int rsl_sacch_info_modify(struct gsm_lchan *lchan, uint8_t type, - const uint8_t *data, int len) -{ - struct abis_rsl_dchan_hdr *dh; - struct msgb *msg = rsl_msgb_alloc(); - uint8_t chan_nr = gsm_lchan2chan_nr(lchan); - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_SACCH_INFO_MODIFY); - dh->chan_nr = chan_nr; - - msgb_tv_put(msg, RSL_IE_SYSINFO_TYPE, type); - msgb_tl16v_put(msg, RSL_IE_L3_INFO, len, data); - - msg->dst = lchan->ts->trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -int rsl_chan_bs_power_ctrl(struct gsm_lchan *lchan, unsigned int fpc, int db) -{ - struct abis_rsl_dchan_hdr *dh; - struct msgb *msg; - uint8_t chan_nr = gsm_lchan2chan_nr(lchan); - - db = abs(db); - if (db > 30) - return -EINVAL; - - msg = rsl_msgb_alloc(); - - lchan->bs_power = db/2; - if (fpc) - lchan->bs_power |= 0x10; - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_BS_POWER_CONTROL); - dh->chan_nr = chan_nr; - - msgb_tv_put(msg, RSL_IE_BS_POWER, lchan->bs_power); - - msg->dst = lchan->ts->trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -int rsl_chan_ms_power_ctrl(struct gsm_lchan *lchan, unsigned int fpc, int dbm) -{ - struct abis_rsl_dchan_hdr *dh; - struct msgb *msg; - uint8_t chan_nr = gsm_lchan2chan_nr(lchan); - int ctl_lvl; - - ctl_lvl = ms_pwr_ctl_lvl(lchan->ts->trx->bts->band, dbm); - if (ctl_lvl < 0) - return ctl_lvl; - - msg = rsl_msgb_alloc(); - - lchan->ms_power = ctl_lvl; - - if (fpc) - lchan->ms_power |= 0x20; - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_MS_POWER_CONTROL); - dh->chan_nr = chan_nr; - - msgb_tv_put(msg, RSL_IE_MS_POWER, lchan->ms_power); - - msg->dst = lchan->ts->trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -static int channel_mode_from_lchan(struct rsl_ie_chan_mode *cm, - struct gsm_lchan *lchan) -{ - memset(cm, 0, sizeof(*cm)); - - /* FIXME: what to do with data calls ? */ - cm->dtx_dtu = 0; - if (lchan->ts->trx->bts->dtxu != GSM48_DTX_SHALL_NOT_BE_USED) - cm->dtx_dtu |= RSL_CMOD_DTXu; - if (lchan->ts->trx->bts->dtxd) - cm->dtx_dtu |= RSL_CMOD_DTXd; - - /* set TCH Speech/Data */ - cm->spd_ind = lchan->rsl_cmode; - - if (lchan->rsl_cmode == RSL_CMOD_SPD_SIGN && - lchan->tch_mode != GSM48_CMODE_SIGN) - LOGP(DRSL, LOGL_ERROR, "unsupported: rsl_mode == signalling, " - "but tch_mode != signalling\n"); - - switch (lchan->type) { - case GSM_LCHAN_SDCCH: - cm->chan_rt = RSL_CMOD_CRT_SDCCH; - break; - case GSM_LCHAN_TCH_F: - cm->chan_rt = RSL_CMOD_CRT_TCH_Bm; - break; - case GSM_LCHAN_TCH_H: - cm->chan_rt = RSL_CMOD_CRT_TCH_Lm; - break; - case GSM_LCHAN_NONE: - case GSM_LCHAN_UNKNOWN: - default: - LOGP(DRSL, LOGL_ERROR, - "unsupported activation lchan->type %u %s\n", - lchan->type, gsm_lchant_name(lchan->type)); - return -EINVAL; - } - - switch (lchan->tch_mode) { - case GSM48_CMODE_SIGN: - cm->chan_rate = 0; - break; - case GSM48_CMODE_SPEECH_V1: - cm->chan_rate = RSL_CMOD_SP_GSM1; - break; - case GSM48_CMODE_SPEECH_EFR: - cm->chan_rate = RSL_CMOD_SP_GSM2; - break; - case GSM48_CMODE_SPEECH_AMR: - cm->chan_rate = RSL_CMOD_SP_GSM3; - break; - case GSM48_CMODE_DATA_14k5: - case GSM48_CMODE_DATA_12k0: - case GSM48_CMODE_DATA_6k0: - switch (lchan->csd_mode) { - case LCHAN_CSD_M_NT: - /* non-transparent CSD with RLP */ - switch (lchan->tch_mode) { - case GSM48_CMODE_DATA_14k5: - cm->chan_rate = RSL_CMOD_SP_NT_14k5; - break; - case GSM48_CMODE_DATA_12k0: - cm->chan_rate = RSL_CMOD_SP_NT_12k0; - break; - case GSM48_CMODE_DATA_6k0: - cm->chan_rate = RSL_CMOD_SP_NT_6k0; - break; - default: - LOGP(DRSL, LOGL_ERROR, - "unsupported lchan->tch_mode %u\n", - lchan->tch_mode); - return -EINVAL; - } - break; - /* transparent data services below */ - case LCHAN_CSD_M_T_1200_75: - cm->chan_rate = RSL_CMOD_CSD_T_1200_75; - break; - case LCHAN_CSD_M_T_600: - cm->chan_rate = RSL_CMOD_CSD_T_600; - break; - case LCHAN_CSD_M_T_1200: - cm->chan_rate = RSL_CMOD_CSD_T_1200; - break; - case LCHAN_CSD_M_T_2400: - cm->chan_rate = RSL_CMOD_CSD_T_2400; - break; - case LCHAN_CSD_M_T_9600: - cm->chan_rate = RSL_CMOD_CSD_T_9600; - break; - case LCHAN_CSD_M_T_14400: - cm->chan_rate = RSL_CMOD_CSD_T_14400; - break; - case LCHAN_CSD_M_T_29000: - cm->chan_rate = RSL_CMOD_CSD_T_29000; - break; - case LCHAN_CSD_M_T_32000: - cm->chan_rate = RSL_CMOD_CSD_T_32000; - break; - default: - LOGP(DRSL, LOGL_ERROR, - "unsupported lchan->csd_mode %u\n", - lchan->csd_mode); - return -EINVAL; - } - break; - default: - LOGP(DRSL, LOGL_ERROR, - "unsupported lchan->tch_mode %u\n", - lchan->tch_mode); - return -EINVAL; - } - - return 0; -} - -static void mr_config_for_bts(struct gsm_lchan *lchan, struct msgb *msg) -{ - if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) - msgb_tlv_put(msg, RSL_IE_MR_CONFIG, lchan->mr_bts_lv[0], - lchan->mr_bts_lv + 1); -} - -static enum gsm_phys_chan_config pchan_for_lchant(enum gsm_chan_t type) -{ - switch (type) { - case GSM_LCHAN_TCH_F: - return GSM_PCHAN_TCH_F; - case GSM_LCHAN_TCH_H: - return GSM_PCHAN_TCH_H; - case GSM_LCHAN_NONE: - case GSM_LCHAN_PDTCH: - /* TODO: so far lchan->type is NONE in PDCH mode. PDTCH is only - * used in osmo-bts. Maybe set PDTCH and drop the NONE case - * here. */ - return GSM_PCHAN_PDCH; - default: - return GSM_PCHAN_UNKNOWN; - } -} - -/*! Tx simplified channel activation message for non-standard PDCH type. */ -static int rsl_chan_activate_lchan_as_pdch(struct gsm_lchan *lchan) -{ - struct msgb *msg; - struct abis_rsl_dchan_hdr *dh; - - /* This might be called after release of the second lchan of a TCH/H, - * but PDCH activation must always happen on the first lchan. Make sure - * the calling code passes the correct lchan. */ - OSMO_ASSERT(lchan == lchan->ts->lchan); - - rsl_lchan_set_state(lchan, LCHAN_S_ACT_REQ); - - msg = rsl_msgb_alloc(); - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_CHAN_ACTIV); - dh->chan_nr = gsm_lchan_as_pchan2chan_nr(lchan, GSM_PCHAN_PDCH); - - msgb_tv_put(msg, RSL_IE_ACT_TYPE, RSL_ACT_OSMO_PDCH); - - if (lchan->ts->trx->bts->type == GSM_BTS_TYPE_RBS2000 && - lchan->ts->trx->bts->rbs2000.use_superchannel) { - const uint8_t eric_pgsl_tmr[] = { 30, 1 }; - msgb_tv_fixed_put(msg, RSL_IE_ERIC_PGSL_TIMERS, - sizeof(eric_pgsl_tmr), eric_pgsl_tmr); - } - - msg->dst = lchan->ts->trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -/* Chapter 8.4.1 */ -int rsl_chan_activate_lchan(struct gsm_lchan *lchan, uint8_t act_type, - uint8_t ho_ref) -{ - struct abis_rsl_dchan_hdr *dh; - struct msgb *msg; - int rc; - uint8_t *len; - uint8_t ta; - - struct rsl_ie_chan_mode cm; - struct gsm48_chan_desc cd; - - /* If a TCH_F/PDCH TS is in PDCH mode, deactivate PDCH first. */ - if (lchan->ts->pchan == GSM_PCHAN_TCH_F_PDCH - && (lchan->ts->flags & TS_F_PDCH_ACTIVE)) { - /* store activation type and handover reference */ - lchan->dyn.act_type = act_type; - lchan->dyn.ho_ref = ho_ref; - return rsl_ipacc_pdch_activate(lchan->ts, 0); - } - - /* - * If necessary, release PDCH on dynamic TS. Note that sending a - * release here is only necessary when in PDCH mode; for TCH types, an - * RSL RF Chan Release is initiated by the BTS when a voice call ends, - * so when we reach this, it will already be released. If a dyn TS is - * in PDCH mode, it is still active and we need to initiate a release - * from the BSC side here. - * - * If pchan_is != pchan_want, the PDCH has already been taken down and - * the switchover now needs to enable the TCH lchan. - * - * To switch a dyn TS between TCH/H and TCH/F, it is sufficient to send - * a chan activ with the new lchan type, because it will already be - * released. - */ - if (lchan->ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH - && lchan->ts->dyn.pchan_is == GSM_PCHAN_PDCH - && lchan->ts->dyn.pchan_is == lchan->ts->dyn.pchan_want) { - enum gsm_phys_chan_config pchan_want; - pchan_want = pchan_for_lchant(lchan->type); - if (lchan->ts->dyn.pchan_is != pchan_want) { - /* - * Make sure to record on lchan[0] so that we'll find - * it after the PDCH release. - */ - struct gsm_lchan *lchan0 = lchan->ts->lchan; - lchan0->dyn.act_type = act_type, - lchan0->dyn.ho_ref = ho_ref; - lchan0->dyn.rqd_ref = lchan->rqd_ref; - lchan0->dyn.rqd_ta = lchan->rqd_ta; - lchan->rqd_ref = NULL; - lchan->rqd_ta = 0; - DEBUGP(DRSL, "%s saved rqd_ref=%p ta=%u\n", - gsm_lchan_name(lchan0), lchan0->rqd_ref, - lchan0->rqd_ta); - return dyn_ts_switchover_start(lchan->ts, pchan_want); - } - } - - DEBUGP(DRSL, "%s Tx RSL Channel Activate with act_type=%s\n", - gsm_ts_and_pchan_name(lchan->ts), - rsl_act_type_name(act_type)); - - if (act_type == RSL_ACT_OSMO_PDCH) { - if (lchan->ts->pchan != GSM_PCHAN_TCH_F_TCH_H_PDCH) { - LOGP(DRSL, LOGL_ERROR, - "%s PDCH channel activation only allowed on %s\n", - gsm_ts_and_pchan_name(lchan->ts), - gsm_pchan_name(GSM_PCHAN_TCH_F_TCH_H_PDCH)); - return -EINVAL; - } - return rsl_chan_activate_lchan_as_pdch(lchan); - } - - if (lchan->ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH - && lchan->ts->dyn.pchan_want == GSM_PCHAN_PDCH) { - LOGP(DRSL, LOGL_ERROR, - "%s Expected PDCH activation kind\n", - gsm_ts_and_pchan_name(lchan->ts)); - return -EINVAL; - } - - rc = channel_mode_from_lchan(&cm, lchan); - if (rc < 0) { - LOGP(DRSL, LOGL_ERROR, - "%s Cannot find channel mode from lchan type\n", - gsm_ts_and_pchan_name(lchan->ts)); - return rc; - } - - rsl_lchan_set_state(lchan, LCHAN_S_ACT_REQ); - - ta = lchan->rqd_ta; - - /* BS11 requires TA shifted by 2 bits */ - if (lchan->ts->trx->bts->type == GSM_BTS_TYPE_BS11) - ta <<= 2; - - memset(&cd, 0, sizeof(cd)); - gsm48_lchan2chan_desc(&cd, lchan); - - msg = rsl_msgb_alloc(); - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_CHAN_ACTIV); - - if (lchan->ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH) - dh->chan_nr = gsm_lchan_as_pchan2chan_nr( - lchan, lchan->ts->dyn.pchan_want); - else - dh->chan_nr = gsm_lchan2chan_nr(lchan); - - msgb_tv_put(msg, RSL_IE_ACT_TYPE, act_type); - msgb_tlv_put(msg, RSL_IE_CHAN_MODE, sizeof(cm), - (uint8_t *) &cm); - - /* - * The Channel Identification is needed for Phase1 phones - * and it contains the GSM48 Channel Description and the - * Mobile Allocation. The GSM 08.58 asks for the Mobile - * Allocation to have a length of zero. We are using the - * msgb_l3len to calculate the length of both messages. - */ - msgb_v_put(msg, RSL_IE_CHAN_IDENT); - len = msgb_put(msg, 1); - msgb_tv_fixed_put(msg, GSM48_IE_CHANDESC_2, sizeof(cd), (const uint8_t *) &cd); - - if (lchan->ts->hopping.enabled) - msgb_tlv_put(msg, GSM48_IE_MA_AFTER, lchan->ts->hopping.ma_len, - lchan->ts->hopping.ma_data); - else - msgb_tlv_put(msg, GSM48_IE_MA_AFTER, 0, NULL); - - /* update the calculated size */ - msg->l3h = len + 1; - *len = msgb_l3len(msg); - - if (lchan->encr.alg_id > RSL_ENC_ALG_A5(0)) { - uint8_t encr_info[MAX_A5_KEY_LEN+2]; - rc = build_encr_info(encr_info, lchan); - if (rc > 0) - msgb_tlv_put(msg, RSL_IE_ENCR_INFO, rc, encr_info); - } - - switch (act_type) { - case RSL_ACT_INTER_ASYNC: - case RSL_ACT_INTER_SYNC: - msgb_tv_put(msg, RSL_IE_HANDO_REF, ho_ref); - break; - default: - break; - } - - msgb_tv_put(msg, RSL_IE_BS_POWER, lchan->bs_power); - msgb_tv_put(msg, RSL_IE_MS_POWER, lchan->ms_power); - msgb_tv_put(msg, RSL_IE_TIMING_ADVANCE, ta); - mr_config_for_bts(lchan, msg); - - msg->dst = lchan->ts->trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -/* Chapter 8.4.9: Modify channel mode on BTS side */ -int rsl_chan_mode_modify_req(struct gsm_lchan *lchan) -{ - struct abis_rsl_dchan_hdr *dh; - struct msgb *msg; - int rc; - - uint8_t chan_nr = gsm_lchan2chan_nr(lchan); - struct rsl_ie_chan_mode cm; - - rc = channel_mode_from_lchan(&cm, lchan); - if (rc < 0) - return rc; - - msg = rsl_msgb_alloc(); - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_MODE_MODIFY_REQ); - dh->chan_nr = chan_nr; - - msgb_tlv_put(msg, RSL_IE_CHAN_MODE, sizeof(cm), - (uint8_t *) &cm); - - if (lchan->encr.alg_id > RSL_ENC_ALG_A5(0)) { - uint8_t encr_info[MAX_A5_KEY_LEN+2]; - rc = build_encr_info(encr_info, lchan); - if (rc > 0) - msgb_tlv_put(msg, RSL_IE_ENCR_INFO, rc, encr_info); - } - - mr_config_for_bts(lchan, msg); - - msg->dst = lchan->ts->trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -/* Chapter 8.4.6: Send the encryption command with given L3 info */ -int rsl_encryption_cmd(struct msgb *msg) -{ - struct abis_rsl_dchan_hdr *dh; - struct gsm_lchan *lchan = msg->lchan; - uint8_t chan_nr = gsm_lchan2chan_nr(lchan); - uint8_t encr_info[MAX_A5_KEY_LEN+2]; - uint8_t l3_len = msg->len; - int rc; - - /* First push the L3 IE tag and length */ - msgb_tv16_push(msg, RSL_IE_L3_INFO, l3_len); - - /* then the link identifier (SAPI0, main sign link) */ - msgb_tv_push(msg, RSL_IE_LINK_IDENT, 0); - - /* then encryption information */ - rc = build_encr_info(encr_info, lchan); - if (rc <= 0) - return rc; - msgb_tlv_push(msg, RSL_IE_ENCR_INFO, rc, encr_info); - - /* and finally the DCHAN header */ - dh = (struct abis_rsl_dchan_hdr *) msgb_push(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_ENCR_CMD); - dh->chan_nr = chan_nr; - - msg->dst = lchan->ts->trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -/* Chapter 8.4.5 / 4.6: Deactivate the SACCH after 04.08 RR CHAN RELEASE */ -int rsl_deact_sacch(struct gsm_lchan *lchan) -{ - struct abis_rsl_dchan_hdr *dh; - struct msgb *msg = rsl_msgb_alloc(); - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_DEACTIVATE_SACCH); - dh->chan_nr = gsm_lchan2chan_nr(lchan); - - msg->lchan = lchan; - msg->dst = lchan->ts->trx->rsl_link; - - DEBUGP(DRSL, "%s DEACTivate SACCH CMD\n", gsm_lchan_name(lchan)); - - return abis_rsl_sendmsg(msg); -} - -static bool dyn_ts_should_switch_to_pdch(struct gsm_bts_trx_ts *ts) -{ - int ss; - - if (ts->pchan != GSM_PCHAN_TCH_F_TCH_H_PDCH) - return false; - - if (ts->trx->bts->gprs.mode == BTS_GPRS_NONE) - return false; - - /* Already in PDCH mode? */ - if (ts->dyn.pchan_is == GSM_PCHAN_PDCH) - return false; - - /* See if all lchans are released. */ - for (ss = 0; ss < ts_subslots(ts); ss++) { - struct gsm_lchan *lc = &ts->lchan[ss]; - if (lc->state != LCHAN_S_NONE) { - DEBUGP(DRSL, "%s lchan %u still in use" - " (type=%s,state=%s)\n", - gsm_ts_and_pchan_name(ts), lc->nr, - gsm_lchant_name(lc->type), - gsm_lchans_name(lc->state)); - /* An lchan is still used. */ - return false; - } - } - - /* All channels are released, go to PDCH mode. */ - DEBUGP(DRSL, "%s back to PDCH\n", - gsm_ts_and_pchan_name(ts)); - return true; -} - -static void error_timeout_cb(void *data) -{ - struct gsm_lchan *lchan = data; - if (lchan->state != LCHAN_S_REL_ERR) { - LOGP(DRSL, LOGL_ERROR, "%s error timeout but not in error state: %d\n", - gsm_lchan_name(lchan), lchan->state); - return; - } - - /* go back to the none state */ - LOGP(DRSL, LOGL_INFO, "%s is back in operation.\n", gsm_lchan_name(lchan)); - rsl_lchan_set_state(lchan, LCHAN_S_NONE); - - /* Put PDCH channel back into PDCH mode, if GPRS is enabled */ - if (lchan->ts->pchan == GSM_PCHAN_TCH_F_PDCH - && lchan->ts->trx->bts->gprs.mode != BTS_GPRS_NONE) - rsl_ipacc_pdch_activate(lchan->ts, 1); - - if (dyn_ts_should_switch_to_pdch(lchan->ts)) - dyn_ts_switchover_start(lchan->ts, GSM_PCHAN_PDCH); -} - -static int rsl_rx_rf_chan_rel_ack(struct gsm_lchan *lchan); - -/* Chapter 8.4.14 / 4.7: Tell BTS to release the radio channel */ -static int rsl_rf_chan_release(struct gsm_lchan *lchan, int error, - enum sacch_deact deact_sacch) -{ - struct abis_rsl_dchan_hdr *dh; - struct msgb *msg; - int rc; - - /* Stop timers that should lead to a channel release */ - osmo_timer_del(&lchan->T3109); - - if (lchan->state == LCHAN_S_REL_ERR) { - LOGP(DRSL, LOGL_NOTICE, "%s is in error state, not sending release.\n", - gsm_lchan_name(lchan)); - return -1; - } - - msg = rsl_msgb_alloc(); - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_RF_CHAN_REL); - dh->chan_nr = gsm_lchan2chan_nr(lchan); - - msg->lchan = lchan; - msg->dst = lchan->ts->trx->rsl_link; - - if (error) - DEBUGP(DRSL, "%s RF Channel Release due to error: %d\n", - gsm_lchan_name(lchan), error); - else - DEBUGP(DRSL, "%s RF Channel Release\n", gsm_lchan_name(lchan)); - - if (error) { - /* - * FIXME: GSM 04.08 gives us two options for the abnormal - * chanel release. This can be either like in the non-existent - * sub-lcuase 3.5.1 or for the main signalling link deactivate - * the SACCH, start timer T3109 and consider the channel as - * released. - * - * This code is doing the later for all raido links and not - * only the main link. Right now all SAPIs are released on the - * local end, the SACCH will be de-activated and right now the - * T3111 will be started. First T3109 should be started and then - * the T3111. - * - * TODO: Move this out of the function. - */ - - /* - * sacch de-activate and "local end release" - */ - if (deact_sacch == SACCH_DEACTIVATE) - rsl_deact_sacch(lchan); - rsl_release_sapis_from(lchan, 0, RSL_REL_LOCAL_END); - - /* - * TODO: start T3109 now. - */ - rsl_lchan_set_state(lchan, LCHAN_S_REL_ERR); - } - - /* Start another timer or assume the BTS sends a ACK/NACK? */ - osmo_timer_setup(&lchan->act_timer, lchan_deact_tmr_cb, lchan); - osmo_timer_schedule(&lchan->act_timer, 4, 0); - - rc = abis_rsl_sendmsg(msg); - - /* BTS will respond by RF CHAN REL ACK */ - return rc; -} - -/* - * Special handling for channel releases in the error case. - */ -static int rsl_rf_chan_release_err(struct gsm_lchan *lchan) -{ - enum sacch_deact sacch_deact; - if (lchan->state != LCHAN_S_ACTIVE) - return 0; - switch (ts_pchan(lchan->ts)) { - case GSM_PCHAN_TCH_F: - case GSM_PCHAN_TCH_H: - case GSM_PCHAN_CCCH_SDCCH4: - case GSM_PCHAN_CCCH_SDCCH4_CBCH: - case GSM_PCHAN_SDCCH8_SACCH8C: - case GSM_PCHAN_SDCCH8_SACCH8C_CBCH: - sacch_deact = SACCH_DEACTIVATE; - break; - default: - sacch_deact = SACCH_NONE; - break; - } - return rsl_rf_chan_release(lchan, 1, sacch_deact); -} - -static int rsl_rx_rf_chan_rel_ack(struct gsm_lchan *lchan) -{ - struct gsm_bts_trx_ts *ts = lchan->ts; - - DEBUGP(DRSL, "%s RF CHANNEL RELEASE ACK\n", gsm_lchan_name(lchan)); - - /* Stop all pending timers */ - osmo_timer_del(&lchan->act_timer); - osmo_timer_del(&lchan->T3111); - - /* - * The BTS didn't respond within the timeout to our channel - * release request and we have marked the channel as broken. - * Now we do receive an ACK and let's be conservative. If it - * is a sysmoBTS we know that only one RF Channel Release ACK - * will be sent. So let's "repair" the channel. - */ - if (lchan->state == LCHAN_S_BROKEN) { - int do_free = is_sysmobts_v2(ts->trx->bts); - LOGP(DRSL, LOGL_NOTICE, - "%s CHAN REL ACK for broken channel. %s.\n", - gsm_lchan_name(lchan), - do_free ? "Releasing it" : "Keeping it broken"); - if (do_free) - do_lchan_free(lchan); - if (dyn_ts_should_switch_to_pdch(lchan->ts)) - dyn_ts_switchover_start(lchan->ts, GSM_PCHAN_PDCH); - return 0; - } - - if (lchan->state != LCHAN_S_REL_REQ && lchan->state != LCHAN_S_REL_ERR) - LOGP(DRSL, LOGL_NOTICE, "%s CHAN REL ACK but state %s\n", - gsm_lchan_name(lchan), - gsm_lchans_name(lchan->state)); - - do_lchan_free(lchan); - - /* - * Check Osmocom RSL CHAN ACT style dynamic TCH/F_TCH/H_PDCH TS for pending - * transitions in these cases: - * - * a) after PDCH was released due to switchover request, activate TCH. - * BSC initiated this switchover, so dyn.pchan_is != pchan_want and - * lchan->type has been set to the desired GSM_LCHAN_*. - * - * b) Voice call ended and a TCH is released. If the TS is now unused, - * switch to PDCH. Here still dyn.pchan_is == dyn.pchan_want because - * we're only just notified and may decide to switch to PDCH now. - */ - if (ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH) { - DEBUGP(DRSL, "%s Rx RSL Channel Release ack for lchan %u\n", - gsm_ts_and_pchan_name(ts), lchan->nr); - - /* (a) */ - if (ts->dyn.pchan_is != ts->dyn.pchan_want) - return dyn_ts_switchover_continue(ts); - - /* (b) */ - if (dyn_ts_should_switch_to_pdch(ts)) - return dyn_ts_switchover_start(ts, GSM_PCHAN_PDCH); - } - - /* - * Put a dynamic TCH/F_PDCH channel back to PDCH mode iff it was - * released successfully. If in error, the PDCH ACT will follow after - * T3111 in error_timeout_cb(). - * - * Any state other than LCHAN_S_REL_ERR became LCHAN_S_NONE after above - * do_lchan_free(). Assert this, because that's what ensures a PDCH ACT - * on a TCH/F_PDCH TS in all cases. - * - * If GPRS is disabled, always skip the PDCH ACT. - */ - OSMO_ASSERT(lchan->state == LCHAN_S_NONE - || lchan->state == LCHAN_S_REL_ERR); - if (ts->trx->bts->gprs.mode == BTS_GPRS_NONE) - return 0; - if (ts->pchan == GSM_PCHAN_TCH_F_PDCH - && lchan->state == LCHAN_S_NONE) - return rsl_ipacc_pdch_activate(ts, 1); - return 0; -} - -int rsl_paging_cmd(struct gsm_bts *bts, uint8_t paging_group, uint8_t len, - uint8_t *ms_ident, uint8_t chan_needed, bool is_gprs) -{ - struct abis_rsl_dchan_hdr *dh; - struct msgb *msg = rsl_msgb_alloc(); - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_PAGING_CMD); - dh->chan_nr = RSL_CHAN_PCH_AGCH; - - msgb_tv_put(msg, RSL_IE_PAGING_GROUP, paging_group); - msgb_tlv_put(msg, RSL_IE_MS_IDENTITY, len-2, ms_ident+2); - msgb_tv_put(msg, RSL_IE_CHAN_NEEDED, chan_needed); - - /* Ericsson wants to have this IE in case a paging message - * relates to packet paging */ - if (bts->type == GSM_BTS_TYPE_RBS2000 && is_gprs) - msgb_tv_put(msg, RSL_IE_ERIC_PACKET_PAG_IND, 0); - - msg->dst = bts->c0->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -int imsi_str2bcd(uint8_t *bcd_out, const char *str_in) -{ - int i, len = strlen(str_in); - - for (i = 0; i < len; i++) { - int num = str_in[i] - 0x30; - if (num < 0 || num > 9) - return -1; - if (i % 2 == 0) - bcd_out[i/2] = num; - else - bcd_out[i/2] |= (num << 4); - } - - return 0; -} - -/* Chapter 8.5.6 */ -struct msgb *rsl_imm_assign_cmd_common(struct gsm_bts *bts, uint8_t len, uint8_t *val) -{ - struct msgb *msg = rsl_msgb_alloc(); - struct abis_rsl_dchan_hdr *dh; - uint8_t buf[GSM_MACBLOCK_LEN]; - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_IMMEDIATE_ASSIGN_CMD); - dh->chan_nr = RSL_CHAN_PCH_AGCH; - - switch (bts->type) { - case GSM_BTS_TYPE_BS11: - msgb_tlv_put(msg, RSL_IE_IMM_ASS_INFO, len, val); - break; - default: - /* If phase 2, construct a FULL_IMM_ASS_INFO */ - pad_macblock(buf, val, len); - msgb_tlv_put(msg, RSL_IE_FULL_IMM_ASS_INFO, GSM_MACBLOCK_LEN, - buf); - break; - } - - msg->dst = bts->c0->rsl_link; - return msg; -} - -/* Chapter 8.5.6 */ -int rsl_imm_assign_cmd(struct gsm_bts *bts, uint8_t len, uint8_t *val) -{ - struct msgb *msg = rsl_imm_assign_cmd_common(bts, len, val); - if (!msg) - return 1; - return abis_rsl_sendmsg(msg); -} - -/* Chapter 8.5.6 */ -int rsl_ericsson_imm_assign_cmd(struct gsm_bts *bts, uint32_t tlli, uint8_t len, uint8_t *val) -{ - struct msgb *msg = rsl_imm_assign_cmd_common(bts, len, val); - if (!msg) - return 1; - - /* ericsson can handle a reference at the end of the message which is used in - * the confirm message. The confirm message is only sent if the trailer is present */ - msgb_put_u8(msg, RSL_IE_ERIC_MOBILE_ID); - msgb_put_u32(msg, tlli); - - return abis_rsl_sendmsg(msg); -} - -/* Send Siemens specific MS RF Power Capability Indication */ -int rsl_siemens_mrpci(struct gsm_lchan *lchan, struct rsl_mrpci *mrpci) -{ - struct msgb *msg = rsl_msgb_alloc(); - struct abis_rsl_dchan_hdr *dh; - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_SIEMENS_MRPCI); - dh->c.msg_discr = ABIS_RSL_MDISC_DED_CHAN; - dh->chan_nr = gsm_lchan2chan_nr(lchan); - msgb_tv_put(msg, RSL_IE_SIEMENS_MRPCI, *(uint8_t *)mrpci); - - DEBUGP(DRSL, "%s TX Siemens MRPCI 0x%02x\n", - gsm_lchan_name(lchan), *(uint8_t *)mrpci); - - msg->dst = lchan->ts->trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - - -/* Send "DATA REQUEST" message with given L3 Info payload */ -/* Chapter 8.3.1 */ -int rsl_data_request(struct msgb *msg, uint8_t link_id) -{ - if (msg->lchan == NULL) { - LOGP(DRSL, LOGL_ERROR, "cannot send DATA REQUEST to unknown lchan\n"); - return -EINVAL; - } - - rsl_rll_push_l3(msg, RSL_MT_DATA_REQ, gsm_lchan2chan_nr(msg->lchan), - link_id, 1); - - msg->dst = msg->lchan->ts->trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -/* Send "ESTABLISH REQUEST" message with given L3 Info payload */ -/* Chapter 8.3.1 */ -int rsl_establish_request(struct gsm_lchan *lchan, uint8_t link_id) -{ - struct msgb *msg; - - msg = rsl_rll_simple(RSL_MT_EST_REQ, gsm_lchan2chan_nr(lchan), - link_id, 0); - msg->dst = lchan->ts->trx->rsl_link; - - DEBUGP(DRLL, "%s RSL RLL ESTABLISH REQ (link_id=0x%02x)\n", - gsm_lchan_name(lchan), link_id); - - return abis_rsl_sendmsg(msg); -} - -static void rsl_handle_release(struct gsm_lchan *lchan); - -/* Special work handler to handle missing RSL_MT_REL_CONF message from - * Nokia InSite BTS */ -static void lchan_rel_work_cb(void *data) -{ - struct gsm_lchan *lchan = data; - int sapi; - - for (sapi = 0; sapi < ARRAY_SIZE(lchan->sapis); ++sapi) { - if (lchan->sapis[sapi] == LCHAN_SAPI_REL) - lchan->sapis[sapi] = LCHAN_SAPI_UNUSED; - } - rsl_handle_release(lchan); -} - -/* Chapter 8.3.7 Request the release of multiframe mode of RLL connection. - This is what higher layers should call. The BTS then responds with - RELEASE CONFIRM, which we in turn use to trigger RSL CHANNEL RELEASE, - which in turn is acknowledged by RSL CHANNEL RELEASE ACK, which calls - lchan_free() */ -int rsl_release_request(struct gsm_lchan *lchan, uint8_t link_id, - enum rsl_rel_mode release_mode) -{ - - struct msgb *msg; - - msg = rsl_rll_simple(RSL_MT_REL_REQ, gsm_lchan2chan_nr(lchan), - link_id, 0); - /* 0 is normal release, 1 is local end */ - msgb_tv_put(msg, RSL_IE_RELEASE_MODE, release_mode); - - /* FIXME: start some timer in case we don't receive a REL ACK ? */ - - msg->dst = lchan->ts->trx->rsl_link; - - DEBUGP(DRLL, "%s RSL RLL RELEASE REQ (link_id=0x%02x, reason=%u)\n", - gsm_lchan_name(lchan), link_id, release_mode); - - abis_rsl_sendmsg(msg); - - /* Do not wait for Nokia BTS to send the confirm. */ - if (is_nokia_bts(lchan->ts->trx->bts) - && lchan->ts->trx->bts->nokia.no_loc_rel_cnf - && release_mode == RSL_REL_LOCAL_END) { - DEBUGP(DRLL, "Scheduling release, becasuse Nokia InSite BTS does not send a RELease CONFirm.\n"); - lchan->sapis[link_id & 0x7] = LCHAN_SAPI_REL; - osmo_timer_setup(&lchan->rel_work, lchan_rel_work_cb, lchan); - osmo_timer_schedule(&lchan->rel_work, 0, 0); - } - - return 0; -} - -int rsl_lchan_mark_broken(struct gsm_lchan *lchan, const char *reason) -{ - LOGP(DRSL, LOGL_ERROR, "%s %s lchan broken: %s\n", - gsm_lchan_name(lchan), gsm_lchant_name(lchan->type), reason); - rsl_lchan_set_state(lchan, LCHAN_S_BROKEN); - lchan->broken_reason = reason; - return 0; -} - -int rsl_lchan_set_state(struct gsm_lchan *lchan, int state) -{ - DEBUGP(DRSL, "%s state %s -> %s\n", - gsm_lchan_name(lchan), gsm_lchans_name(lchan->state), - gsm_lchans_name(state)); - lchan->state = state; - return 0; -} - -/* Chapter 8.4.2: Channel Activate Acknowledge */ -static int rsl_rx_chan_act_ack(struct msgb *msg) -{ - struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg); - struct gsm_lchan *lchan = msg->lchan; - struct gsm_bts_trx_ts *ts = lchan->ts; - - /* BTS has confirmed channel activation, we now need - * to assign the activated channel to the MS */ - if (rslh->ie_chan != RSL_IE_CHAN_NR) - return -EINVAL; - - osmo_timer_del(&lchan->act_timer); - - if (lchan->state == LCHAN_S_BROKEN) { - int do_release = is_sysmobts_v2(ts->trx->bts); - LOGP(DRSL, LOGL_NOTICE, "%s CHAN ACT ACK for broken channel. %s\n", - gsm_lchan_name(lchan), - do_release ? "Releasing it" : "Keeping it broken"); - if (do_release) { - talloc_free(lchan->rqd_ref); - lchan->rqd_ref = NULL; - lchan->rqd_ta = 0; - rsl_lchan_set_state(msg->lchan, LCHAN_S_ACTIVE); - if (ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH) { - /* - * lchan_act_tmr_cb() already called - * lchan_free() and cleared the lchan->type, so - * calling dyn_ts_switchover_complete() here - * would not have the desired effect of - * mimicking an activated lchan that we can - * release. Instead hack the dyn ts state to - * make sure that rsl_rx_rf_chan_rel_ack() will - * switch back to PDCH, i.e. have pchan_is == - * pchan_want, both != GSM_PCHAN_PDCH: - */ - ts->dyn.pchan_is = GSM_PCHAN_NONE; - ts->dyn.pchan_want = GSM_PCHAN_NONE; - } - rsl_rf_chan_release(msg->lchan, 0, SACCH_NONE); - } - return 0; - } - - if (lchan->state != LCHAN_S_ACT_REQ) - LOGP(DRSL, LOGL_NOTICE, "%s CHAN ACT ACK, but state %s\n", - gsm_lchan_name(lchan), - gsm_lchans_name(lchan->state)); - rsl_lchan_set_state(lchan, LCHAN_S_ACTIVE); - - if (ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH) - dyn_ts_switchover_complete(lchan); - - if (lchan->rqd_ref) { - rsl_send_imm_assignment(lchan); - talloc_free(lchan->rqd_ref); - lchan->rqd_ref = NULL; - lchan->rqd_ta = 0; - } - - send_lchan_signal(S_LCHAN_ACTIVATE_ACK, lchan, NULL); - - /* Update bts attributes inside the PCU */ - if (ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH || - ts->pchan == GSM_PCHAN_TCH_F_PDCH || - ts->pchan == GSM_PCHAN_PDCH) - pcu_info_update(ts->trx->bts); - - return 0; -} - -/* Chapter 8.4.3: Channel Activate NACK */ -static int rsl_rx_chan_act_nack(struct msgb *msg) -{ - struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); - struct tlv_parsed tp; - - osmo_timer_del(&msg->lchan->act_timer); - - if (msg->lchan->state == LCHAN_S_BROKEN) { - LOGP(DRSL, LOGL_ERROR, - "%s CHANNEL ACTIVATE NACK for broken channel.\n", - gsm_lchan_name(msg->lchan)); - return -1; - } - - LOGP(DRSL, LOGL_ERROR, "%s CHANNEL ACTIVATE NACK ", - gsm_lchan_name(msg->lchan)); - - /* BTS has rejected channel activation ?!? */ - if (dh->ie_chan != RSL_IE_CHAN_NR) - return -EINVAL; - - rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh)); - if (TLVP_PRESENT(&tp, RSL_IE_CAUSE)) { - const uint8_t *cause = TLVP_VAL(&tp, RSL_IE_CAUSE); - print_rsl_cause(LOGL_ERROR, cause, - TLVP_LEN(&tp, RSL_IE_CAUSE)); - msg->lchan->error_cause = *cause; - if (*cause != RSL_ERR_RCH_ALR_ACTV_ALLOC) { - rsl_lchan_mark_broken(msg->lchan, "NACK on activation"); - } else - rsl_rf_chan_release(msg->lchan, 1, SACCH_DEACTIVATE); - - } else { - rsl_lchan_mark_broken(msg->lchan, "NACK on activation no IE"); - } - - LOGPC(DRSL, LOGL_ERROR, "\n"); - - send_lchan_signal(S_LCHAN_ACTIVATE_NACK, msg->lchan, NULL); - return 0; -} - -/* Chapter 8.4.4: Connection Failure Indication */ -static int rsl_rx_conn_fail(struct msgb *msg) -{ - struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); - struct tlv_parsed tp; - - LOGP(DRSL, LOGL_NOTICE, "%s CONNECTION FAIL: RELEASING state %s ", - gsm_lchan_name(msg->lchan), - gsm_lchans_name(msg->lchan->state)); - - rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh)); - - if (TLVP_PRESENT(&tp, RSL_IE_CAUSE)) - print_rsl_cause(LOGL_NOTICE, TLVP_VAL(&tp, RSL_IE_CAUSE), - TLVP_LEN(&tp, RSL_IE_CAUSE)); - - LOGPC(DRSL, LOGL_NOTICE, "\n"); - rate_ctr_inc(&msg->lchan->ts->trx->bts->network->bsc_ctrs->ctr[BSC_CTR_CHAN_RF_FAIL]); - return rsl_rf_chan_release_err(msg->lchan); -} - -static void print_meas_rep_uni(struct gsm_meas_rep_unidir *mru, - const char *prefix) -{ - DEBUGPC(DMEAS, "RXL-FULL-%s=%3ddBm RXL-SUB-%s=%3ddBm ", - prefix, rxlev2dbm(mru->full.rx_lev), - prefix, rxlev2dbm(mru->sub.rx_lev)); - DEBUGPC(DMEAS, "RXQ-FULL-%s=%d RXQ-SUB-%s=%d ", - prefix, mru->full.rx_qual, prefix, mru->sub.rx_qual); -} - -static void print_meas_rep(struct gsm_lchan *lchan, struct gsm_meas_rep *mr) -{ - int i; - const char *name = ""; - - if (lchan && lchan->conn) { - if (lchan->conn->bsub) - name = bsc_subscr_name(lchan->conn->bsub); - else if (lchan->conn->subscr) - name = lchan->conn->subscr->imsi; - else - name = lchan->name; - } - - DEBUGP(DMEAS, "[%s] MEASUREMENT RESULT NR=%d ", name, mr->nr); - - if (mr->flags & MEAS_REP_F_DL_DTX) - DEBUGPC(DMEAS, "DTXd "); - - print_meas_rep_uni(&mr->ul, "ul"); - DEBUGPC(DMEAS, "BS_POWER=%d ", mr->bs_power); - - if (mr->flags & MEAS_REP_F_MS_TO) - DEBUGPC(DMEAS, "MS_TO=%d ", mr->ms_timing_offset); - - if (mr->flags & MEAS_REP_F_MS_L1) { - DEBUGPC(DMEAS, "L1_MS_PWR=%3ddBm ", mr->ms_l1.pwr); - DEBUGPC(DMEAS, "L1_FPC=%u ", - mr->flags & MEAS_REP_F_FPC ? 1 : 0); - DEBUGPC(DMEAS, "L1_TA=%u ", mr->ms_l1.ta); - } - - if (mr->flags & MEAS_REP_F_UL_DTX) - DEBUGPC(DMEAS, "DTXu "); - if (mr->flags & MEAS_REP_F_BA1) - DEBUGPC(DMEAS, "BA1 "); - if (!(mr->flags & MEAS_REP_F_DL_VALID)) - DEBUGPC(DMEAS, "NOT VALID "); - else - print_meas_rep_uni(&mr->dl, "dl"); - - DEBUGPC(DMEAS, "NUM_NEIGH=%u\n", mr->num_cell); - if (mr->num_cell == 7) - return; - for (i = 0; i < mr->num_cell; i++) { - struct gsm_meas_rep_cell *mrc = &mr->cell[i]; - DEBUGP(DMEAS, "IDX=%u ARFCN=%u BSIC=%u => %d dBm\n", - mrc->neigh_idx, mrc->arfcn, mrc->bsic, rxlev2dbm(mrc->rxlev)); - } -} - -static int rsl_rx_meas_res(struct msgb *msg) -{ - struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); - struct tlv_parsed tp; - struct gsm_meas_rep *mr = lchan_next_meas_rep(msg->lchan); - uint8_t len; - const uint8_t *val; - int rc; - - /* check if this channel is actually active */ - /* FIXME: maybe this check should be way more generic/centralized */ - if (msg->lchan->state != LCHAN_S_ACTIVE) { - LOGP(DRSL, LOGL_DEBUG, "%s: MEAS RES for inactive channel\n", - gsm_lchan_name(msg->lchan)); - return 0; - } - - memset(mr, 0, sizeof(*mr)); - mr->lchan = msg->lchan; - - rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh)); - - if (!TLVP_PRESENT(&tp, RSL_IE_MEAS_RES_NR) || - !TLVP_PRESENT(&tp, RSL_IE_UPLINK_MEAS) || - !TLVP_PRESENT(&tp, RSL_IE_BS_POWER)) - return -EIO; - - /* Mandatory Parts */ - mr->nr = *TLVP_VAL(&tp, RSL_IE_MEAS_RES_NR); - - len = TLVP_LEN(&tp, RSL_IE_UPLINK_MEAS); - val = TLVP_VAL(&tp, RSL_IE_UPLINK_MEAS); - if (len >= 3) { - if (val[0] & 0x40) - mr->flags |= MEAS_REP_F_DL_DTX; - mr->ul.full.rx_lev = val[0] & 0x3f; - mr->ul.sub.rx_lev = val[1] & 0x3f; - mr->ul.full.rx_qual = val[2]>>3 & 0x7; - mr->ul.sub.rx_qual = val[2] & 0x7; - } - - mr->bs_power = *TLVP_VAL(&tp, RSL_IE_BS_POWER); - - /* Optional Parts */ - if (TLVP_PRESENT(&tp, RSL_IE_MS_TIMING_OFFSET)) { - /* According to 3GPP TS 48.058 § MS Timing Offset = Timing Offset field - 63 */ - mr->ms_timing_offset = *TLVP_VAL(&tp, RSL_IE_MS_TIMING_OFFSET) - 63; - mr->flags |= MEAS_REP_F_MS_TO; - } - - if (TLVP_PRESENT(&tp, RSL_IE_L1_INFO)) { - struct e1inp_sign_link *sign_link = msg->dst; - - val = TLVP_VAL(&tp, RSL_IE_L1_INFO); - mr->flags |= MEAS_REP_F_MS_L1; - mr->ms_l1.pwr = ms_pwr_dbm(sign_link->trx->bts->band, val[0] >> 3); - if (val[0] & 0x04) - mr->flags |= MEAS_REP_F_FPC; - mr->ms_l1.ta = val[1]; - /* BS11 and Nokia reports TA shifted by 2 bits */ - if (msg->lchan->ts->trx->bts->type == GSM_BTS_TYPE_BS11 - || msg->lchan->ts->trx->bts->type == GSM_BTS_TYPE_NOKIA_SITE) - mr->ms_l1.ta >>= 2; - } - if (TLVP_PRESENT(&tp, RSL_IE_L3_INFO)) { - msg->l3h = (uint8_t *) TLVP_VAL(&tp, RSL_IE_L3_INFO); - rc = gsm48_parse_meas_rep(mr, msg); - if (rc < 0) - return rc; - } - - print_meas_rep(msg->lchan, mr); - - send_lchan_signal(S_LCHAN_MEAS_REP, msg->lchan, mr); - - return 0; -} - -/* Chapter 8.4.7 */ -static int rsl_rx_hando_det(struct msgb *msg) -{ - struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); - struct tlv_parsed tp; - - DEBUGP(DRSL, "%s HANDOVER DETECT ", gsm_lchan_name(msg->lchan)); - - rsl_tlv_parse(&tp, dh->data, msgb_l2len(msg)-sizeof(*dh)); - - if (TLVP_PRESENT(&tp, RSL_IE_ACCESS_DELAY)) - DEBUGPC(DRSL, "access delay = %u\n", - *TLVP_VAL(&tp, RSL_IE_ACCESS_DELAY)); - else - DEBUGPC(DRSL, "\n"); - - send_lchan_signal(S_LCHAN_HANDOVER_DETECT, msg->lchan, NULL); - - return 0; -} - -static bool lchan_may_change_pdch(struct gsm_lchan *lchan, bool pdch_act) -{ - struct gsm_bts_trx_ts *ts; - - OSMO_ASSERT(lchan); - - ts = lchan->ts; - OSMO_ASSERT(ts); - OSMO_ASSERT(ts->trx); - OSMO_ASSERT(ts->trx->bts); - - if (lchan->ts->pchan != GSM_PCHAN_TCH_F_PDCH) { - LOGP(DRSL, LOGL_ERROR, "%s pchan=%s Rx PDCH %s ACK" - " for channel that is no TCH/F_PDCH\n", - gsm_lchan_name(lchan), - gsm_pchan_name(ts->pchan), - pdch_act? "ACT" : "DEACT"); - return false; - } - - if (lchan->state != LCHAN_S_NONE) { - LOGP(DRSL, LOGL_ERROR, "%s pchan=%s Rx PDCH %s ACK" - " in unexpected state: %s\n", - gsm_lchan_name(lchan), - gsm_pchan_name(ts->pchan), - pdch_act? "ACT" : "DEACT", - gsm_lchans_name(lchan->state)); - return false; - } - return true; -} - -static int rsl_rx_pdch_act_ack(struct msgb *msg) -{ - if (!lchan_may_change_pdch(msg->lchan, true)) - return -EINVAL; - - msg->lchan->ts->flags |= TS_F_PDCH_ACTIVE; - msg->lchan->ts->flags &= ~TS_F_PDCH_ACT_PENDING; - - return 0; -} - -static int rsl_rx_pdch_deact_ack(struct msgb *msg) -{ - if (!lchan_may_change_pdch(msg->lchan, false)) - return -EINVAL; - - msg->lchan->ts->flags &= ~TS_F_PDCH_ACTIVE; - msg->lchan->ts->flags &= ~TS_F_PDCH_DEACT_PENDING; - - rsl_chan_activate_lchan(msg->lchan, msg->lchan->dyn.act_type, - msg->lchan->dyn.ho_ref); - - return 0; -} - -static int abis_rsl_rx_dchan(struct msgb *msg) -{ - struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg); - int rc = 0; - char *ts_name; - struct e1inp_sign_link *sign_link = msg->dst; - - msg->lchan = lchan_lookup(sign_link->trx, rslh->chan_nr, - "Abis RSL rx DCHAN: "); - if (!msg->lchan) - return -1; - ts_name = gsm_lchan_name(msg->lchan); - - switch (rslh->c.msg_type) { - case RSL_MT_CHAN_ACTIV_ACK: - DEBUGP(DRSL, "%s CHANNEL ACTIVATE ACK\n", ts_name); - rc = rsl_rx_chan_act_ack(msg); - count_codecs(sign_link->trx->bts, msg->lchan); - break; - case RSL_MT_CHAN_ACTIV_NACK: - rc = rsl_rx_chan_act_nack(msg); - break; - case RSL_MT_CONN_FAIL: - rc = rsl_rx_conn_fail(msg); - break; - case RSL_MT_MEAS_RES: - rc = rsl_rx_meas_res(msg); - break; - case RSL_MT_HANDO_DET: - rc = rsl_rx_hando_det(msg); - break; - case RSL_MT_RF_CHAN_REL_ACK: - rc = rsl_rx_rf_chan_rel_ack(msg->lchan); - break; - case RSL_MT_MODE_MODIFY_ACK: - count_codecs(sign_link->trx->bts, msg->lchan); - DEBUGP(DRSL, "%s CHANNEL MODE MODIFY ACK\n", ts_name); - break; - case RSL_MT_MODE_MODIFY_NACK: - LOGP(DRSL, LOGL_ERROR, "%s CHANNEL MODE MODIFY NACK\n", ts_name); - break; - case RSL_MT_IPAC_PDCH_ACT_ACK: - DEBUGP(DRSL, "%s IPAC PDCH ACT ACK\n", ts_name); - rc = rsl_rx_pdch_act_ack(msg); - break; - case RSL_MT_IPAC_PDCH_ACT_NACK: - LOGP(DRSL, LOGL_ERROR, "%s IPAC PDCH ACT NACK\n", ts_name); - break; - case RSL_MT_IPAC_PDCH_DEACT_ACK: - DEBUGP(DRSL, "%s IPAC PDCH DEACT ACK\n", ts_name); - rc = rsl_rx_pdch_deact_ack(msg); - break; - case RSL_MT_IPAC_PDCH_DEACT_NACK: - LOGP(DRSL, LOGL_ERROR, "%s IPAC PDCH DEACT NACK\n", ts_name); - break; - case RSL_MT_PHY_CONTEXT_CONF: - case RSL_MT_PREPROC_MEAS_RES: - case RSL_MT_TALKER_DET: - case RSL_MT_LISTENER_DET: - case RSL_MT_REMOTE_CODEC_CONF_REP: - case RSL_MT_MR_CODEC_MOD_ACK: - case RSL_MT_MR_CODEC_MOD_NACK: - case RSL_MT_MR_CODEC_MOD_PER: - LOGP(DRSL, LOGL_NOTICE, "%s Unimplemented Abis RSL DChan " - "msg 0x%02x\n", ts_name, rslh->c.msg_type); - break; - default: - LOGP(DRSL, LOGL_NOTICE, "%s unknown Abis RSL DChan msg 0x%02x\n", - ts_name, rslh->c.msg_type); - return -EINVAL; - } - - return rc; -} - -static int rsl_rx_error_rep(struct msgb *msg) -{ - struct abis_rsl_common_hdr *rslh = msgb_l2(msg); - struct tlv_parsed tp; - struct e1inp_sign_link *sign_link = msg->dst; - - LOGP(DRSL, LOGL_ERROR, "%s ERROR REPORT ", gsm_trx_name(sign_link->trx)); - - rsl_tlv_parse(&tp, rslh->data, msgb_l2len(msg)-sizeof(*rslh)); - - if (TLVP_PRESENT(&tp, RSL_IE_CAUSE)) - print_rsl_cause(LOGL_ERROR, TLVP_VAL(&tp, RSL_IE_CAUSE), - TLVP_LEN(&tp, RSL_IE_CAUSE)); - - LOGPC(DRSL, LOGL_ERROR, "\n"); - - return 0; -} - -static int abis_rsl_rx_trx(struct msgb *msg) -{ - struct abis_rsl_common_hdr *rslh = msgb_l2(msg); - struct e1inp_sign_link *sign_link = msg->dst; - int rc = 0; - - switch (rslh->msg_type) { - case RSL_MT_ERROR_REPORT: - rc = rsl_rx_error_rep(msg); - break; - case RSL_MT_RF_RES_IND: - /* interference on idle channels of TRX */ - //DEBUGP(DRSL, "%s RF Resource Indication\n", gsm_trx_name(sign_link->trx)); - break; - case RSL_MT_OVERLOAD: - /* indicate CCCH / ACCH / processor overload */ - LOGP(DRSL, LOGL_ERROR, "%s CCCH/ACCH/CPU Overload\n", - gsm_trx_name(sign_link->trx)); - break; - case 0x42: /* Nokia specific: SI End ACK */ - LOGP(DRSL, LOGL_INFO, "Nokia SI End ACK\n"); - break; - case 0x43: /* Nokia specific: SI End NACK */ - LOGP(DRSL, LOGL_INFO, "Nokia SI End NACK\n"); - break; - default: - LOGP(DRSL, LOGL_NOTICE, "%s Unknown Abis RSL TRX message " - "type 0x%02x\n", gsm_trx_name(sign_link->trx), rslh->msg_type); - return -EINVAL; - } - return rc; -} - -/* If T3101 expires, we never received a response to IMMEDIATE ASSIGN */ -static void t3101_expired(void *data) -{ - struct gsm_lchan *lchan = data; - LOGP(DRSL, LOGL_NOTICE, - "%s T3101 expired: no response to IMMEDIATE ASSIGN\n", - gsm_lchan_name(lchan)); - rsl_rf_chan_release(lchan, 1, SACCH_DEACTIVATE); -} - -/* If T3111 expires, we will send the RF Channel Request */ -static void t3111_expired(void *data) -{ - struct gsm_lchan *lchan = data; - LOGP(DRSL, LOGL_NOTICE, - "%s T3111 expired: releasing RF Channel\n", - gsm_lchan_name(lchan)); - rsl_rf_chan_release(lchan, 0, SACCH_NONE); -} - -/* If T3109 expires the MS has not send a UA/UM do the error release */ -static void t3109_expired(void *data) -{ - struct gsm_lchan *lchan = data; - - LOGP(DRSL, LOGL_ERROR, - "%s SACCH deactivation timeout.\n", gsm_lchan_name(lchan)); - rsl_rf_chan_release(lchan, 1, SACCH_NONE); -} - -/* Format an IMM ASS REJ according to 04.08 Chapter 9.1.20 */ -static int rsl_send_imm_ass_rej(struct gsm_bts *bts, - unsigned int num_req_refs, - struct gsm48_req_ref *rqd_refs, - uint8_t wait_ind) -{ - uint8_t buf[GSM_MACBLOCK_LEN]; - struct gsm48_imm_ass_rej *iar = (struct gsm48_imm_ass_rej *)buf; - - /* create IMMEDIATE ASSIGN REJECT 04.08 message */ - memset(iar, 0, sizeof(*iar)); - iar->proto_discr = GSM48_PDISC_RR; - iar->msg_type = GSM48_MT_RR_IMM_ASS_REJ; - iar->page_mode = GSM48_PM_SAME; - - memcpy(&iar->req_ref1, &rqd_refs[0], sizeof(iar->req_ref1)); - iar->wait_ind1 = wait_ind; - - if (num_req_refs >= 2) - memcpy(&iar->req_ref2, &rqd_refs[1], sizeof(iar->req_ref2)); - else - memcpy(&iar->req_ref2, &rqd_refs[0], sizeof(iar->req_ref2)); - iar->wait_ind2 = wait_ind; - - if (num_req_refs >= 3) - memcpy(&iar->req_ref3, &rqd_refs[2], sizeof(iar->req_ref3)); - else - memcpy(&iar->req_ref3, &rqd_refs[0], sizeof(iar->req_ref3)); - iar->wait_ind3 = wait_ind; - - if (num_req_refs >= 4) - memcpy(&iar->req_ref4, &rqd_refs[3], sizeof(iar->req_ref4)); - else - memcpy(&iar->req_ref4, &rqd_refs[0], sizeof(iar->req_ref4)); - iar->wait_ind4 = wait_ind; - - /* we need to subtract 1 byte from sizeof(*iar) since ia includes the l2_plen field */ - iar->l2_plen = GSM48_LEN2PLEN((sizeof(*iar)-1)); - - return rsl_imm_assign_cmd(bts, sizeof(*iar), (uint8_t *) iar); -} - -/* Handle packet channel rach requests */ -static int rsl_rx_pchan_rqd(struct msgb *msg, struct gsm_bts *bts) -{ - struct gsm48_req_ref *rqd_ref; - struct abis_rsl_dchan_hdr *rqd_hdr = msgb_l2(msg); - rqd_ref = (struct gsm48_req_ref *) &rqd_hdr->data[1]; - uint8_t ra = rqd_ref->ra; - uint8_t t1, t2, t3; - uint32_t fn; - uint8_t rqd_ta; - uint8_t is_11bit; - - /* Process rach request and forward contained information to PCU */ - if (ra == 0x7F) { - is_11bit = 1; - - /* FIXME: Also handle 11 bit rach requests */ - LOGP(DRSL, LOGL_ERROR, "BTS %d eleven bit access burst not supported yet!\n",bts->nr); - return -EINVAL; - } else { - is_11bit = 0; - t1 = rqd_ref->t1; - t2 = rqd_ref->t2; - t3 = rqd_ref->t3_low | (rqd_ref->t3_high << 3); - fn = (51 * ((t3-t2) % 26) + t3 + 51 * 26 * t1); - - rqd_ta = rqd_hdr->data[sizeof(struct gsm48_req_ref)+2]; - } - - return pcu_tx_rach_ind(bts, rqd_ta, ra, fn, is_11bit, - GSM_L1_BURST_TYPE_ACCESS_0); -} - -/* MS has requested a channel on the RACH */ -static int rsl_rx_chan_rqd(struct msgb *msg) -{ - struct e1inp_sign_link *sign_link = msg->dst; - struct gsm_bts *bts = sign_link->trx->bts; - struct abis_rsl_dchan_hdr *rqd_hdr = msgb_l2(msg); - struct gsm48_req_ref *rqd_ref; - enum gsm_chan_t lctype; - enum gsm_chreq_reason_t chreq_reason; - struct gsm_lchan *lchan; - uint8_t rqd_ta; - int is_lu; - - uint16_t arfcn; - uint8_t subch; - - /* parse request reference to be used in immediate assign */ - if (rqd_hdr->data[0] != RSL_IE_REQ_REFERENCE) - return -EINVAL; - - rqd_ref = (struct gsm48_req_ref *) &rqd_hdr->data[1]; - - /* parse access delay and use as TA */ - if (rqd_hdr->data[sizeof(struct gsm48_req_ref)+1] != RSL_IE_ACCESS_DELAY) - return -EINVAL; - rqd_ta = rqd_hdr->data[sizeof(struct gsm48_req_ref)+2]; - - /* Determine channel request cause code */ - chreq_reason = get_reason_by_chreq(rqd_ref->ra, bts->network->neci); - LOGP(DRSL, LOGL_NOTICE, "BTS %d CHAN RQD: reason: %s (ra=0x%02x, neci=0x%02x, chreq_reason=0x%02x)\n", - msg->lchan->ts->trx->bts->nr, - get_value_string(gsm_chreq_descs, chreq_reason), - rqd_ref->ra, bts->network->neci, chreq_reason); - - /* Handle PDCH related rach requests (in case of BSC-co-located-PCU */ - if (chreq_reason == GSM_CHREQ_REASON_PDCH) - return rsl_rx_pchan_rqd(msg, bts); - - /* determine channel type (SDCCH/TCH_F/TCH_H) based on - * request reference RA */ - lctype = get_ctype_by_chreq(bts->network, rqd_ref->ra); - - rate_ctr_inc(&bts->network->bsc_ctrs->ctr[BSC_CTR_CHREQ_TOTAL]); - - /* - * We want LOCATION UPDATES to succeed and will assign a TCH - * if we have no SDCCH available. - */ - is_lu = !!(chreq_reason == GSM_CHREQ_REASON_LOCATION_UPD); - - /* check availability / allocate channel */ - lchan = lchan_alloc(bts, lctype, is_lu); - if (!lchan) { - LOGP(DRSL, LOGL_NOTICE, "BTS %d CHAN RQD: no resources for %s 0x%x\n", - msg->lchan->ts->trx->bts->nr, gsm_lchant_name(lctype), rqd_ref->ra); - rate_ctr_inc(&bts->network->bsc_ctrs->ctr[BSC_CTR_CHREQ_NO_CHANNEL]); - /* FIXME gather multiple CHAN RQD and reject up to 4 at the same time */ - if (bts->network->T3122) - rsl_send_imm_ass_rej(bts, 1, rqd_ref, bts->network->T3122 & 0xff); - return 0; - } - - /* - * Expecting lchan state to be NONE, except for dyn TS in PDCH mode. - * Those are expected to be ACTIVE: the PDCH release will be sent from - * rsl_chan_activate_lchan() below. - */ - if (lchan->state != LCHAN_S_NONE - && !(lchan->ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH - && lchan->ts->dyn.pchan_is == GSM_PCHAN_PDCH - && lchan->state == LCHAN_S_ACTIVE)) - LOGP(DRSL, LOGL_NOTICE, "%s lchan_alloc() returned channel " - "in state %s\n", gsm_lchan_name(lchan), - gsm_lchans_name(lchan->state)); - - /* save the RACH data as we need it after the CHAN ACT ACK */ - lchan->rqd_ref = talloc_zero(bts, struct gsm48_req_ref); - if (!lchan->rqd_ref) { - LOGP(DRSL, LOGL_ERROR, "Failed to allocate gsm48_req_ref.\n"); - lchan_free(lchan); - return -ENOMEM; - } - - memcpy(lchan->rqd_ref, rqd_ref, sizeof(*rqd_ref)); - lchan->rqd_ta = rqd_ta; - - arfcn = lchan->ts->trx->arfcn; - subch = lchan->nr; - - lchan->encr.alg_id = RSL_ENC_ALG_A5(0); /* no encryption */ - lchan->ms_power = ms_pwr_ctl_lvl(bts->band, bts->ms_max_power); - lchan->bs_power = 0; /* 0dB reduction, output power = Pn */ - lchan->rsl_cmode = RSL_CMOD_SPD_SIGN; - lchan->tch_mode = GSM48_CMODE_SIGN; - - /* Start another timer or assume the BTS sends a ACK/NACK? */ - osmo_timer_setup(&lchan->act_timer, lchan_act_tmr_cb, lchan); - osmo_timer_schedule(&lchan->act_timer, 4, 0); - - DEBUGP(DRSL, "%s Activating ARFCN(%u) SS(%u) lctype %s " - "r=%s ra=0x%02x ta=%d\n", gsm_lchan_name(lchan), arfcn, subch, - gsm_lchant_name(lchan->type), gsm_chreq_name(chreq_reason), - rqd_ref->ra, rqd_ta); - - rsl_chan_activate_lchan(lchan, RSL_ACT_INTRA_IMM_ASS, 0); - - return 0; -} - -static int rsl_send_imm_assignment(struct gsm_lchan *lchan) -{ - struct gsm_bts *bts = lchan->ts->trx->bts; - uint8_t buf[GSM_MACBLOCK_LEN]; - struct gsm48_imm_ass *ia = (struct gsm48_imm_ass *) buf; - - /* create IMMEDIATE ASSIGN 04.08 messge */ - memset(ia, 0, sizeof(*ia)); - /* we set ia->l2_plen once we know the length of the MA below */ - ia->proto_discr = GSM48_PDISC_RR; - ia->msg_type = GSM48_MT_RR_IMM_ASS; - ia->page_mode = GSM48_PM_SAME; - gsm48_lchan2chan_desc(&ia->chan_desc, lchan); - - /* use request reference extracted from CHAN_RQD */ - memcpy(&ia->req_ref, lchan->rqd_ref, sizeof(ia->req_ref)); - ia->timing_advance = lchan->rqd_ta; - if (!lchan->ts->hopping.enabled) { - ia->mob_alloc_len = 0; - } else { - ia->mob_alloc_len = lchan->ts->hopping.ma_len; - memcpy(ia->mob_alloc, lchan->ts->hopping.ma_data, ia->mob_alloc_len); - } - /* we need to subtract 1 byte from sizeof(*ia) since ia includes the l2_plen field */ - ia->l2_plen = GSM48_LEN2PLEN((sizeof(*ia)-1) + ia->mob_alloc_len); - - /* Start timer T3101 to wait for GSM48_MT_RR_PAG_RESP */ - osmo_timer_setup(&lchan->T3101, t3101_expired, lchan); - osmo_timer_schedule(&lchan->T3101, bts->network->T3101, 0); - - /* send IMMEDIATE ASSIGN CMD on RSL to BTS (to send on CCCH to MS) */ - return rsl_imm_assign_cmd(bts, sizeof(*ia)+ia->mob_alloc_len, (uint8_t *) ia); -} - -/* current load on the CCCH */ -static int rsl_rx_ccch_load(struct msgb *msg) -{ - struct e1inp_sign_link *sign_link = msg->dst; - struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg); - struct ccch_signal_data sd; - - sd.bts = sign_link->trx->bts; - sd.rach_slot_count = -1; - sd.rach_busy_count = -1; - sd.rach_access_count = -1; - - switch (rslh->data[0]) { - case RSL_IE_PAGING_LOAD: - sd.pg_buf_space = rslh->data[1] << 8 | rslh->data[2]; - if (is_ipaccess_bts(sign_link->trx->bts) && sd.pg_buf_space == 0xffff) { - /* paging load below configured threshold, use 50 as default */ - sd.pg_buf_space = 50; - } - paging_update_buffer_space(sign_link->trx->bts, sd.pg_buf_space); - osmo_signal_dispatch(SS_CCCH, S_CCCH_PAGING_LOAD, &sd); - break; - case RSL_IE_RACH_LOAD: - if (msg->data_len >= 7) { - sd.rach_slot_count = rslh->data[2] << 8 | rslh->data[3]; - sd.rach_busy_count = rslh->data[4] << 8 | rslh->data[5]; - sd.rach_access_count = rslh->data[6] << 8 | rslh->data[7]; - osmo_signal_dispatch(SS_CCCH, S_CCCH_RACH_LOAD, &sd); - } - break; - default: - break; - } - - return 0; -} - -static int abis_rsl_rx_cchan(struct msgb *msg) -{ - struct e1inp_sign_link *sign_link = msg->dst; - struct abis_rsl_dchan_hdr *rslh = msgb_l2(msg); - int rc = 0; - uint32_t tlli; - - msg->lchan = lchan_lookup(sign_link->trx, rslh->chan_nr, - "Abis RSL rx CCHAN: "); - - switch (rslh->c.msg_type) { - case RSL_MT_CHAN_RQD: - /* MS has requested a channel on the RACH */ - rc = rsl_rx_chan_rqd(msg); - break; - case RSL_MT_CCCH_LOAD_IND: - /* current load on the CCCH */ - rc = rsl_rx_ccch_load(msg); - break; - case RSL_MT_DELETE_IND: - /* CCCH overloaded, IMM_ASSIGN was dropped */ - case RSL_MT_CBCH_LOAD_IND: - /* current load on the CBCH */ - LOGP(DRSL, LOGL_NOTICE, "Unimplemented Abis RSL TRX message " - "type 0x%02x\n", rslh->c.msg_type); - break; - case 0x10: /* Ericsson specific: Immediate Assign Sent */ - /* FIXME: Replace the messy message parsing below - * with proper TV parser */ - LOGP(DRSL, LOGL_INFO, "IMM.ass sent\n"); - if(msg->len < 9) - LOGP(DRSL, LOGL_ERROR, "short IMM.ass sent message!\n"); - else if(msg->data[4] != 0xf1) - LOGP(DRSL, LOGL_ERROR, "unsupported IMM.ass message format! (please fix)\n"); - else { - msgb_pull(msg, 5); /* drop previous data to use msg_pull_u32 */ - tlli = msgb_pull_u32(msg); - pcu_tx_imm_ass_sent(sign_link->trx->bts, tlli); - } - break; - default: - LOGP(DRSL, LOGL_NOTICE, "Unknown Abis RSL TRX message type " - "0x%02x\n", rslh->c.msg_type); - return -EINVAL; - } - - return rc; -} - -static int rsl_rx_rll_err_ind(struct msgb *msg) -{ - struct tlv_parsed tp; - struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); - uint8_t rlm_cause; - - rsl_tlv_parse(&tp, rllh->data, msgb_l2len(msg) - sizeof(*rllh)); - if (!TLVP_PRESENT(&tp, RSL_IE_RLM_CAUSE)) { - LOGP(DRLL, LOGL_ERROR, - "%s ERROR INDICATION without mandantory cause.\n", - gsm_lchan_name(msg->lchan)); - return -1; - } - - rlm_cause = *TLVP_VAL(&tp, RSL_IE_RLM_CAUSE); - LOGP(DRLL, LOGL_ERROR, "%s ERROR INDICATION cause=%s in state=%s\n", - gsm_lchan_name(msg->lchan), - rsl_rlm_cause_name(rlm_cause), - gsm_lchans_name(msg->lchan->state)); - - rll_indication(msg->lchan, rllh->link_id, BSC_RLLR_IND_ERR_IND); - - if (rlm_cause == RLL_CAUSE_T200_EXPIRED) { - rate_ctr_inc(&msg->lchan->ts->trx->bts->network->bsc_ctrs->ctr[BSC_CTR_CHAN_RLL_ERR]); - return rsl_rf_chan_release_err(msg->lchan); - } - - return 0; -} - -static void rsl_handle_release(struct gsm_lchan *lchan) -{ - int sapi; - struct gsm_bts *bts; - - /* - * Maybe only one link/SAPI was releasd or the error handling - * was activated. Just return now and let the other code handle - * it. - */ - if (lchan->state != LCHAN_S_REL_REQ) - return; - - for (sapi = 0; sapi < ARRAY_SIZE(lchan->sapis); ++sapi) { - if (lchan->sapis[sapi] == LCHAN_SAPI_UNUSED) - continue; - LOGP(DRSL, LOGL_DEBUG, "%s waiting for SAPI=%d to be released.\n", - gsm_lchan_name(lchan), sapi); - return; - } - - - /* Stop T3109 and wait for T3111 before re-using the channel */ - osmo_timer_del(&lchan->T3109); - osmo_timer_setup(&lchan->T3111, t3111_expired, lchan); - bts = lchan->ts->trx->bts; - osmo_timer_schedule(&lchan->T3111, bts->network->T3111, 0); -} - -/* ESTABLISH INDICATION, LOCATION AREA UPDATE REQUEST - 0x02, 0x06, - 0x01, 0x20, - 0x02, 0x00, - 0x0b, 0x00, 0x0f, 0x05, 0x08, ... */ - -static int abis_rsl_rx_rll(struct msgb *msg) -{ - struct e1inp_sign_link *sign_link = msg->dst; - struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); - int rc = 0; - char *ts_name; - uint8_t sapi = rllh->link_id & 7; - - msg->lchan = lchan_lookup(sign_link->trx, rllh->chan_nr, - "Abis RSL rx RLL: "); - ts_name = gsm_lchan_name(msg->lchan); - DEBUGP(DRLL, "%s SAPI=%u ", ts_name, sapi); - - switch (rllh->c.msg_type) { - case RSL_MT_DATA_IND: - DEBUGPC(DRLL, "DATA INDICATION\n"); - if (msgb_l2len(msg) > - sizeof(struct abis_rsl_common_hdr) + sizeof(*rllh) && - rllh->data[0] == RSL_IE_L3_INFO) { - msg->l3h = &rllh->data[3]; - return gsm0408_rcvmsg(msg, rllh->link_id); - } - break; - case RSL_MT_EST_IND: - DEBUGPC(DRLL, "ESTABLISH INDICATION\n"); - /* lchan is established, stop T3101 */ - msg->lchan->sapis[rllh->link_id & 0x7] = LCHAN_SAPI_MS; - osmo_timer_del(&msg->lchan->T3101); - if (msgb_l2len(msg) > - sizeof(struct abis_rsl_common_hdr) + sizeof(*rllh) && - rllh->data[0] == RSL_IE_L3_INFO) { - msg->l3h = &rllh->data[3]; - return gsm0408_rcvmsg(msg, rllh->link_id); - } - break; - case RSL_MT_EST_CONF: - DEBUGPC(DRLL, "ESTABLISH CONFIRM\n"); - msg->lchan->sapis[rllh->link_id & 0x7] = LCHAN_SAPI_NET; - rll_indication(msg->lchan, rllh->link_id, - BSC_RLLR_IND_EST_CONF); - break; - case RSL_MT_REL_IND: - /* BTS informs us of having received DISC from MS */ - DEBUGPC(DRLL, "RELEASE INDICATION\n"); - msg->lchan->sapis[rllh->link_id & 0x7] = LCHAN_SAPI_UNUSED; - rll_indication(msg->lchan, rllh->link_id, - BSC_RLLR_IND_REL_IND); - rsl_handle_release(msg->lchan); - break; - case RSL_MT_REL_CONF: - /* BTS informs us of having received UA from MS, - * in response to DISC that we've sent earlier */ - DEBUGPC(DRLL, "RELEASE CONFIRMATION\n"); - msg->lchan->sapis[rllh->link_id & 0x7] = LCHAN_SAPI_UNUSED; - rsl_handle_release(msg->lchan); - break; - case RSL_MT_ERROR_IND: - DEBUGPC(DRLL, "ERROR INDICATION\n"); - rc = rsl_rx_rll_err_ind(msg); - break; - case RSL_MT_UNIT_DATA_IND: - DEBUGPC(DRLL, "UNIT DATA INDICATION\n"); - LOGP(DRLL, LOGL_NOTICE, "unimplemented Abis RLL message " - "type 0x%02x\n", rllh->c.msg_type); - break; - default: - DEBUGPC(DRLL, "UNKNOWN\n"); - LOGP(DRLL, LOGL_NOTICE, "unknown Abis RLL message " - "type 0x%02x\n", rllh->c.msg_type); - } - return rc; -} - -static uint8_t ipa_smod_s_for_lchan(struct gsm_lchan *lchan) -{ - switch (lchan->tch_mode) { - case GSM48_CMODE_SPEECH_V1: - switch (lchan->type) { - case GSM_LCHAN_TCH_F: - return 0x00; - case GSM_LCHAN_TCH_H: - return 0x03; - default: - break; - } - break; - case GSM48_CMODE_SPEECH_EFR: - switch (lchan->type) { - case GSM_LCHAN_TCH_F: - return 0x01; - /* there's no half-rate EFR */ - default: - break; - } - break; - case GSM48_CMODE_SPEECH_AMR: - switch (lchan->type) { - case GSM_LCHAN_TCH_F: - return 0x02; - case GSM_LCHAN_TCH_H: - return 0x05; - default: - break; - } - break; - default: - break; - } - LOGP(DRSL, LOGL_ERROR, "Cannot determine ip.access speech mode for " - "tch_mode == 0x%02x\n", lchan->tch_mode); - return 0; -} - -static uint8_t ipa_rtp_pt_for_lchan(struct gsm_lchan *lchan) -{ - switch (lchan->tch_mode) { - case GSM48_CMODE_SPEECH_V1: - switch (lchan->type) { - case GSM_LCHAN_TCH_F: - return RTP_PT_GSM_FULL; - case GSM_LCHAN_TCH_H: - return RTP_PT_GSM_HALF; - default: - break; - } - break; - case GSM48_CMODE_SPEECH_EFR: - switch (lchan->type) { - case GSM_LCHAN_TCH_F: - return RTP_PT_GSM_EFR; - /* there's no half-rate EFR */ - default: - break; - } - break; - case GSM48_CMODE_SPEECH_AMR: - switch (lchan->type) { - case GSM_LCHAN_TCH_F: - case GSM_LCHAN_TCH_H: - return RTP_PT_AMR; - default: - break; - } - break; - default: - break; - } - LOGP(DRSL, LOGL_ERROR, "Cannot determine ip.access rtp payload type for " - "tch_mode == 0x%02x\n & lchan_type == %d", - lchan->tch_mode, lchan->type); - return 0; -} - -/* ip.access specific RSL extensions */ -static void ipac_parse_rtp(struct gsm_lchan *lchan, struct tlv_parsed *tv) -{ - struct in_addr ip; - uint16_t port, conn_id; - - if (TLVP_PRESENT(tv, RSL_IE_IPAC_LOCAL_IP)) { - ip.s_addr = tlvp_val32_unal(tv, RSL_IE_IPAC_LOCAL_IP); - DEBUGPC(DRSL, "LOCAL_IP=%s ", inet_ntoa(ip)); - lchan->abis_ip.bound_ip = ntohl(ip.s_addr); - } - - if (TLVP_PRESENT(tv, RSL_IE_IPAC_LOCAL_PORT)) { - port = tlvp_val16_unal(tv, RSL_IE_IPAC_LOCAL_PORT); - port = ntohs(port); - DEBUGPC(DRSL, "LOCAL_PORT=%u ", port); - lchan->abis_ip.bound_port = port; - } - - if (TLVP_PRESENT(tv, RSL_IE_IPAC_CONN_ID)) { - conn_id = tlvp_val16_unal(tv, RSL_IE_IPAC_CONN_ID); - conn_id = ntohs(conn_id); - DEBUGPC(DRSL, "CON_ID=%u ", conn_id); - lchan->abis_ip.conn_id = conn_id; - } - - if (TLVP_PRESENT(tv, RSL_IE_IPAC_RTP_PAYLOAD2)) { - lchan->abis_ip.rtp_payload2 = - *TLVP_VAL(tv, RSL_IE_IPAC_RTP_PAYLOAD2); - DEBUGPC(DRSL, "RTP_PAYLOAD2=0x%02x ", - lchan->abis_ip.rtp_payload2); - } - - if (TLVP_PRESENT(tv, RSL_IE_IPAC_SPEECH_MODE)) { - lchan->abis_ip.speech_mode = - *TLVP_VAL(tv, RSL_IE_IPAC_SPEECH_MODE); - DEBUGPC(DRSL, "speech_mode=0x%02x ", - lchan->abis_ip.speech_mode); - } - - if (TLVP_PRESENT(tv, RSL_IE_IPAC_REMOTE_IP)) { - ip.s_addr = tlvp_val32_unal(tv, RSL_IE_IPAC_REMOTE_IP); - DEBUGPC(DRSL, "REMOTE_IP=%s ", inet_ntoa(ip)); - lchan->abis_ip.connect_ip = ntohl(ip.s_addr); - } - - if (TLVP_PRESENT(tv, RSL_IE_IPAC_REMOTE_PORT)) { - port = tlvp_val16_unal(tv, RSL_IE_IPAC_REMOTE_PORT); - port = ntohs(port); - DEBUGPC(DRSL, "REMOTE_PORT=%u ", port); - lchan->abis_ip.connect_port = port; - } -} - -/*! \brief Issue IPA RSL CRCX to configure RTP on BTS side - * \param[in] lchan Logical Channel for which we issue CRCX - */ -int rsl_ipacc_crcx(struct gsm_lchan *lchan) -{ - struct msgb *msg = rsl_msgb_alloc(); - struct abis_rsl_dchan_hdr *dh; - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_IPAC_CRCX); - dh->c.msg_discr = ABIS_RSL_MDISC_IPACCESS; - dh->chan_nr = gsm_lchan2chan_nr(lchan); - - /* 0x1- == receive-only, 0x-1 == EFR codec */ - lchan->abis_ip.speech_mode = 0x10 | ipa_smod_s_for_lchan(lchan); - lchan->abis_ip.rtp_payload = ipa_rtp_pt_for_lchan(lchan); - msgb_tv_put(msg, RSL_IE_IPAC_SPEECH_MODE, lchan->abis_ip.speech_mode); - msgb_tv_put(msg, RSL_IE_IPAC_RTP_PAYLOAD, lchan->abis_ip.rtp_payload); - - DEBUGP(DRSL, "%s IPAC_BIND speech_mode=0x%02x RTP_PAYLOAD=%d\n", - gsm_lchan_name(lchan), lchan->abis_ip.speech_mode, - lchan->abis_ip.rtp_payload); - - msg->dst = lchan->ts->trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -/*! \brief Issue IPA RSL MDCX to configure MGW-side of RTP - * \param[in] lchan Logical Channel for which we issue MDCX - * \param[in] ip Remote (MGW) IP address for RTP - * \param[in] port Remote (MGW) UDP port number for RTP - * \param[in] rtp_payload2 Contents of RTP PAYLOAD 2 IE - */ -int rsl_ipacc_mdcx(struct gsm_lchan *lchan, uint32_t ip, uint16_t port, - uint8_t rtp_payload2) -{ - struct msgb *msg = rsl_msgb_alloc(); - struct abis_rsl_dchan_hdr *dh; - uint32_t *att_ip; - struct in_addr ia; - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_IPAC_MDCX); - dh->c.msg_discr = ABIS_RSL_MDISC_IPACCESS; - dh->chan_nr = gsm_lchan2chan_nr(lchan); - - /* we need to store these now as MDCX_ACK does not return them :( */ - lchan->abis_ip.rtp_payload2 = rtp_payload2; - lchan->abis_ip.connect_port = port; - lchan->abis_ip.connect_ip = ip; - - /* 0x0- == both directions, 0x-1 == EFR codec */ - lchan->abis_ip.speech_mode = 0x00 | ipa_smod_s_for_lchan(lchan); - lchan->abis_ip.rtp_payload = ipa_rtp_pt_for_lchan(lchan); - - ia.s_addr = htonl(ip); - DEBUGP(DRSL, "%s IPAC_MDCX IP=%s PORT=%d RTP_PAYLOAD=%d RTP_PAYLOAD2=%d " - "CONN_ID=%d speech_mode=0x%02x\n", gsm_lchan_name(lchan), - inet_ntoa(ia), port, lchan->abis_ip.rtp_payload, rtp_payload2, - lchan->abis_ip.conn_id, lchan->abis_ip.speech_mode); - - msgb_tv16_put(msg, RSL_IE_IPAC_CONN_ID, lchan->abis_ip.conn_id); - msgb_v_put(msg, RSL_IE_IPAC_REMOTE_IP); - att_ip = (uint32_t *) msgb_put(msg, sizeof(ip)); - *att_ip = ia.s_addr; - msgb_tv16_put(msg, RSL_IE_IPAC_REMOTE_PORT, port); - msgb_tv_put(msg, RSL_IE_IPAC_SPEECH_MODE, lchan->abis_ip.speech_mode); - msgb_tv_put(msg, RSL_IE_IPAC_RTP_PAYLOAD, lchan->abis_ip.rtp_payload); - if (rtp_payload2) - msgb_tv_put(msg, RSL_IE_IPAC_RTP_PAYLOAD2, rtp_payload2); - - msg->dst = lchan->ts->trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -/* tell BTS to connect RTP stream to our local RTP socket */ -int rsl_ipacc_mdcx_to_rtpsock(struct gsm_lchan *lchan) -{ - struct rtp_socket *rs = lchan->abis_ip.rtp_socket; - int rc; - - rc = rsl_ipacc_mdcx(lchan, ntohl(rs->rtp.sin_local.sin_addr.s_addr), - ntohs(rs->rtp.sin_local.sin_port), - /* FIXME: use RTP payload of bound socket, not BTS*/ - lchan->abis_ip.rtp_payload2); - - return rc; -} - -int rsl_ipacc_pdch_activate(struct gsm_bts_trx_ts *ts, int act) -{ - struct msgb *msg = rsl_msgb_alloc(); - struct abis_rsl_dchan_hdr *dh; - uint8_t msg_type; - - if (ts->flags & TS_F_PDCH_PENDING_MASK) { - LOGP(DRSL, LOGL_ERROR, - "%s PDCH %s requested, but a PDCH%s%s is still pending\n", - gsm_ts_name(ts), - act ? "ACT" : "DEACT", - ts->flags & TS_F_PDCH_ACT_PENDING? " ACT" : "", - ts->flags & TS_F_PDCH_DEACT_PENDING? " DEACT" : ""); - return -EINVAL; - } - - if (act){ - /* Callers should heed the GPRS mode. */ - OSMO_ASSERT(ts->trx->bts->gprs.mode != BTS_GPRS_NONE); - msg_type = RSL_MT_IPAC_PDCH_ACT; - ts->flags |= TS_F_PDCH_ACT_PENDING; - } else { - msg_type = RSL_MT_IPAC_PDCH_DEACT; - ts->flags |= TS_F_PDCH_DEACT_PENDING; - } - /* TODO add timeout to cancel PDCH DE/ACT */ - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(msg, sizeof(*dh)); - init_dchan_hdr(dh, msg_type); - dh->c.msg_discr = ABIS_RSL_MDISC_DED_CHAN; - dh->chan_nr = gsm_pchan2chan_nr(GSM_PCHAN_PDCH, ts->nr, 0); - - DEBUGP(DRSL, "%s IPAC PDCH %sACT\n", gsm_ts_name(ts), - act ? "" : "DE"); - - msg->dst = ts->trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -static int abis_rsl_rx_ipacc_crcx_ack(struct msgb *msg) -{ - struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); - struct tlv_parsed tv; - struct gsm_lchan *lchan = msg->lchan; - - /* the BTS has acknowledged a local bind, it now tells us the IP - * address and port number to which it has bound the given logical - * channel */ - - rsl_tlv_parse(&tv, dh->data, msgb_l2len(msg)-sizeof(*dh)); - if (!TLVP_PRESENT(&tv, RSL_IE_IPAC_LOCAL_PORT) || - !TLVP_PRESENT(&tv, RSL_IE_IPAC_LOCAL_IP) || - !TLVP_PRESENT(&tv, RSL_IE_IPAC_CONN_ID)) { - LOGP(DRSL, LOGL_NOTICE, "mandatory IE missing"); - return -EINVAL; - } - - ipac_parse_rtp(lchan, &tv); - - osmo_signal_dispatch(SS_ABISIP, S_ABISIP_CRCX_ACK, msg->lchan); - - return 0; -} - -static int abis_rsl_rx_ipacc_mdcx_ack(struct msgb *msg) -{ - struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); - struct tlv_parsed tv; - struct gsm_lchan *lchan = msg->lchan; - - /* the BTS has acknowledged a remote connect request and - * it now tells us the IP address and port number to which it has - * connected the given logical channel */ - - rsl_tlv_parse(&tv, dh->data, msgb_l2len(msg)-sizeof(*dh)); - ipac_parse_rtp(lchan, &tv); - osmo_signal_dispatch(SS_ABISIP, S_ABISIP_MDCX_ACK, msg->lchan); - - return 0; -} - -static int abis_rsl_rx_ipacc_dlcx_ind(struct msgb *msg) -{ - struct abis_rsl_dchan_hdr *dh = msgb_l2(msg); - struct tlv_parsed tv; - - rsl_tlv_parse(&tv, dh->data, msgb_l2len(msg)-sizeof(*dh)); - - if (TLVP_PRESENT(&tv, RSL_IE_CAUSE)) - print_rsl_cause(LOGL_DEBUG, TLVP_VAL(&tv, RSL_IE_CAUSE), - TLVP_LEN(&tv, RSL_IE_CAUSE)); - - osmo_signal_dispatch(SS_ABISIP, S_ABISIP_DLCX_IND, msg->lchan); - - return 0; -} - -static int abis_rsl_rx_ipacc(struct msgb *msg) -{ - struct e1inp_sign_link *sign_link = msg->dst; - struct abis_rsl_rll_hdr *rllh = msgb_l2(msg); - char *ts_name; - int rc = 0; - - msg->lchan = lchan_lookup(sign_link->trx, rllh->chan_nr, - "Abis RSL rx IPACC: "); - ts_name = gsm_lchan_name(msg->lchan); - - switch (rllh->c.msg_type) { - case RSL_MT_IPAC_CRCX_ACK: - DEBUGP(DRSL, "%s IPAC_CRCX_ACK ", ts_name); - rc = abis_rsl_rx_ipacc_crcx_ack(msg); - break; - case RSL_MT_IPAC_CRCX_NACK: - /* somehow the BTS was unable to bind the lchan to its local - * port?!? */ - LOGP(DRSL, LOGL_ERROR, "%s IPAC_CRCX_NACK\n", ts_name); - break; - case RSL_MT_IPAC_MDCX_ACK: - /* the BTS tells us that a connect operation was successful */ - DEBUGP(DRSL, "%s IPAC_MDCX_ACK ", ts_name); - rc = abis_rsl_rx_ipacc_mdcx_ack(msg); - break; - case RSL_MT_IPAC_MDCX_NACK: - /* somehow the BTS was unable to connect the lchan to a remote - * port */ - LOGP(DRSL, LOGL_ERROR, "%s IPAC_MDCX_NACK\n", ts_name); - break; - case RSL_MT_IPAC_DLCX_IND: - DEBUGP(DRSL, "%s IPAC_DLCX_IND ", ts_name); - rc = abis_rsl_rx_ipacc_dlcx_ind(msg); - break; - default: - LOGP(DRSL, LOGL_NOTICE, "Unknown ip.access msg_type 0x%02x\n", - rllh->c.msg_type); - break; - } - DEBUGPC(DRSL, "\n"); - - return rc; -} - -int dyn_ts_switchover_start(struct gsm_bts_trx_ts *ts, - enum gsm_phys_chan_config to_pchan) -{ - int ss; - int rc = -EIO; - - OSMO_ASSERT(ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH); - DEBUGP(DRSL, "%s starting switchover to %s\n", - gsm_ts_and_pchan_name(ts), gsm_pchan_name(to_pchan)); - - if (ts->dyn.pchan_is != ts->dyn.pchan_want) { - LOGP(DRSL, LOGL_ERROR, - "%s: Attempt to switch dynamic channel to %s," - " but is already in switchover.\n", - gsm_ts_and_pchan_name(ts), - gsm_pchan_name(to_pchan)); - return ts->dyn.pchan_want == to_pchan? 0 : -EAGAIN; - } - - if (ts->dyn.pchan_is == to_pchan) { - LOGP(DRSL, LOGL_INFO, - "%s %s Already is in %s mode, cannot switchover.\n", - gsm_ts_name(ts), gsm_pchan_name(ts->pchan), - gsm_pchan_name(to_pchan)); - return -EINVAL; - } - - /* Paranoia: let's make sure all is indeed released. */ - for (ss = 0; ss < ts_subslots(ts); ss++) { - struct gsm_lchan *lc = &ts->lchan[ss]; - if (lc->state != LCHAN_S_NONE) { - LOGP(DRSL, LOGL_ERROR, - "%s Attempt to switch dynamic channel to %s," - " but is not fully released.\n", - gsm_ts_and_pchan_name(ts), - gsm_pchan_name(to_pchan)); - return -EAGAIN; - } - } - - /* Record that we're busy switching. */ - ts->dyn.pchan_want = to_pchan; - - /* - * To switch from PDCH, we need to initiate the release from the BSC - * side. dyn_ts_switchover_continue() will be called from - * rsl_rx_rf_chan_rel_ack(). PDCH is always on lchan[0]. - */ - if (ts->dyn.pchan_is == GSM_PCHAN_PDCH) { - rsl_lchan_set_state(ts->lchan, LCHAN_S_REL_REQ); - rc = rsl_rf_chan_release(ts->lchan, 0, SACCH_NONE); - if (rc) { - LOGP(DRSL, LOGL_ERROR, - "%s RSL RF Chan Release failed\n", - gsm_ts_and_pchan_name(ts)); - return dyn_ts_switchover_failed(ts, rc); - } - return 0; - } - - /* - * To switch from TCH/F and TCH/H pchans, this has been called from - * rsl_rx_rf_chan_rel_ack(), i.e. release is complete. Go ahead and - * activate as new type. This will always be PDCH. - */ - return dyn_ts_switchover_continue(ts); -} - -static int dyn_ts_switchover_continue(struct gsm_bts_trx_ts *ts) -{ - int rc; - uint8_t act_type; - uint8_t ho_ref; - int ss; - struct gsm_lchan *lchan; - - OSMO_ASSERT(ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH); - DEBUGP(DRSL, "%s switchover: release complete," - " activating new pchan type\n", - gsm_ts_and_pchan_name(ts)); - - if (ts->dyn.pchan_is == ts->dyn.pchan_want) { - LOGP(DRSL, LOGL_ERROR, - "%s Requested to switchover dynamic channel to the" - " same type it is already in.\n", - gsm_ts_and_pchan_name(ts)); - return 0; - } - - for (ss = 0; ss < ts_subslots(ts); ss++) { - lchan = &ts->lchan[ss]; - if (lchan->rqd_ref) { - LOGP(DRSL, LOGL_ERROR, - "%s During dyn TS switchover, expecting no" - " Request Reference to be pending. Discarding!\n", - gsm_lchan_name(lchan)); - talloc_free(lchan->rqd_ref); - lchan->rqd_ref = NULL; - } - } - - /* - * When switching pchan modes, all lchans are unused. So always - * activate whatever wants to be activated on the first lchan. (We - * wouldn't remember to use lchan[1] across e.g. a PDCH deact anyway) - */ - lchan = ts->lchan; - - /* - * For TCH/x, the lchan->type has been set in lchan_alloc(), but it may - * have been lost during channel release due to dynamic switchover. - * - * For PDCH, the lchan->type will actually remain NONE. - * TODO: set GSM_LCHAN_PDTCH? - */ - switch (ts->dyn.pchan_want) { - case GSM_PCHAN_TCH_F: - lchan->type = GSM_LCHAN_TCH_F; - break; - case GSM_PCHAN_TCH_H: - lchan->type = GSM_LCHAN_TCH_H; - break; - case GSM_PCHAN_PDCH: - lchan->type = GSM_LCHAN_NONE; - break; - default: - LOGP(DRSL, LOGL_ERROR, - "%s Invalid target pchan for dynamic TS\n", - gsm_ts_and_pchan_name(ts)); - } - - act_type = (ts->dyn.pchan_want == GSM_PCHAN_PDCH) - ? RSL_ACT_OSMO_PDCH - : lchan->dyn.act_type; - ho_ref = (ts->dyn.pchan_want == GSM_PCHAN_PDCH) - ? 0 - : lchan->dyn.ho_ref; - - /* Fetch the rqd_ref back from before switchover started. */ - lchan->rqd_ref = lchan->dyn.rqd_ref; - lchan->rqd_ta = lchan->dyn.rqd_ta; - lchan->dyn.rqd_ref = NULL; - lchan->dyn.rqd_ta = 0; - - /* During switchover, we have received a release ack, which means that - * the act_timer has been stopped. Start the timer again so we mark - * this channel broken if the activation ack comes too late. */ - osmo_timer_setup(&lchan->act_timer, lchan_act_tmr_cb, lchan); - osmo_timer_schedule(&lchan->act_timer, 4, 0); - - rc = rsl_chan_activate_lchan(lchan, act_type, ho_ref); - if (rc) { - LOGP(DRSL, LOGL_ERROR, - "%s RSL Chan Activate failed\n", - gsm_ts_and_pchan_name(ts)); - return dyn_ts_switchover_failed(ts, rc); - } - return 0; -} - -static int dyn_ts_switchover_failed(struct gsm_bts_trx_ts *ts, int rc) -{ - ts->dyn.pchan_want = ts->dyn.pchan_is; - LOGP(DRSL, LOGL_ERROR, "%s Error %d during dynamic channel switchover." - " Going back to previous pchan.\n", gsm_ts_and_pchan_name(ts), - rc); - return rc; -} - -static void dyn_ts_switchover_complete(struct gsm_lchan *lchan) -{ - enum gsm_phys_chan_config pchan_act; - enum gsm_phys_chan_config pchan_was; - struct gsm_bts_trx_ts *ts = lchan->ts; - - OSMO_ASSERT(ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH); - - pchan_act = pchan_for_lchant(lchan->type); - /* - * Paranoia: do the types match? - * In case of errors: we've received an act ack already, so what to do - * about it? Logging the error should suffice for now. - */ - if (pchan_act != ts->dyn.pchan_want) - LOGP(DRSL, LOGL_ERROR, - "%s Requested transition does not match lchan type %s\n", - gsm_ts_and_pchan_name(ts), - gsm_lchant_name(lchan->type)); - - pchan_was = ts->dyn.pchan_is; - ts->dyn.pchan_is = ts->dyn.pchan_want = pchan_act; - - if (pchan_was != ts->dyn.pchan_is) - LOGP(DRSL, LOGL_INFO, "%s switchover from %s complete.\n", - gsm_ts_and_pchan_name(ts), gsm_pchan_name(pchan_was)); -} - -/* Entry-point where L2 RSL from BTS enters */ -int abis_rsl_rcvmsg(struct msgb *msg) -{ - struct abis_rsl_common_hdr *rslh; - int rc = 0; - - if (!msg) { - DEBUGP(DRSL, "Empty RSL msg?..\n"); - return -1; - } - - if (msgb_l2len(msg) < sizeof(*rslh)) { - DEBUGP(DRSL, "Truncated RSL message with l2len: %u\n", msgb_l2len(msg)); - msgb_free(msg); - return -1; - } - - rslh = msgb_l2(msg); - - switch (rslh->msg_discr & 0xfe) { - case ABIS_RSL_MDISC_RLL: - rc = abis_rsl_rx_rll(msg); - break; - case ABIS_RSL_MDISC_DED_CHAN: - rc = abis_rsl_rx_dchan(msg); - break; - case ABIS_RSL_MDISC_COM_CHAN: - rc = abis_rsl_rx_cchan(msg); - break; - case ABIS_RSL_MDISC_TRX: - rc = abis_rsl_rx_trx(msg); - break; - case ABIS_RSL_MDISC_LOC: - LOGP(DRSL, LOGL_NOTICE, "unimplemented RSL msg disc 0x%02x\n", - rslh->msg_discr); - break; - case ABIS_RSL_MDISC_IPACCESS: - rc = abis_rsl_rx_ipacc(msg); - break; - default: - LOGP(DRSL, LOGL_NOTICE, "unknown RSL message discriminator " - "0x%02x\n", rslh->msg_discr); - rc = -EINVAL; - } - msgb_free(msg); - return rc; -} - -int rsl_sms_cb_command(struct gsm_bts *bts, uint8_t chan_number, - struct rsl_ie_cb_cmd_type cb_command, - const uint8_t *data, int len) -{ - struct abis_rsl_dchan_hdr *dh; - struct msgb *cb_cmd; - - cb_cmd = rsl_msgb_alloc(); - if (!cb_cmd) - return -1; - - dh = (struct abis_rsl_dchan_hdr *) msgb_put(cb_cmd, sizeof(*dh)); - init_dchan_hdr(dh, RSL_MT_SMS_BC_CMD); - dh->c.msg_discr = ABIS_RSL_MDISC_COM_CHAN; - dh->chan_nr = chan_number; /* TODO: check the chan config */ - - msgb_tv_put(cb_cmd, RSL_IE_CB_CMD_TYPE, *(uint8_t*)&cb_command); - msgb_tlv_put(cb_cmd, RSL_IE_SMSCB_MSG, len, data); - - cb_cmd->dst = bts->c0->rsl_link; - - return abis_rsl_sendmsg(cb_cmd); -} - -int rsl_nokia_si_begin(struct gsm_bts_trx *trx) -{ - struct abis_rsl_common_hdr *ch; - struct msgb *msg = rsl_msgb_alloc(); - - ch = (struct abis_rsl_common_hdr *) msgb_put(msg, sizeof(*ch)); - ch->msg_discr = ABIS_RSL_MDISC_TRX; - ch->msg_type = 0x40; /* Nokia SI Begin */ - - msg->dst = trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -int rsl_nokia_si_end(struct gsm_bts_trx *trx) -{ - struct abis_rsl_common_hdr *ch; - struct msgb *msg = rsl_msgb_alloc(); - - ch = (struct abis_rsl_common_hdr *) msgb_put(msg, sizeof(*ch)); - ch->msg_discr = ABIS_RSL_MDISC_TRX; - ch->msg_type = 0x41; /* Nokia SI End */ - - msgb_tv_put(msg, 0xFD, 0x00); /* Nokia Pagemode Info, No paging reorganisation required */ - - msg->dst = trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -int rsl_bs_power_control(struct gsm_bts_trx *trx, uint8_t channel, uint8_t reduction) -{ - struct abis_rsl_common_hdr *ch; - struct msgb *msg = rsl_msgb_alloc(); - - ch = (struct abis_rsl_common_hdr *) msgb_put(msg, sizeof(*ch)); - ch->msg_discr = ABIS_RSL_MDISC_DED_CHAN; - ch->msg_type = RSL_MT_BS_POWER_CONTROL; - - msgb_tv_put(msg, RSL_IE_CHAN_NR, channel); - msgb_tv_put(msg, RSL_IE_BS_POWER, reduction); /* reduction in 2dB steps */ - - msg->dst = trx->rsl_link; - - return abis_rsl_sendmsg(msg); -} - -/** - * Release all allocated SAPIs starting from @param start and - * release them with the given release mode. Once the release - * confirmation arrives it will be attempted to release the - * the RF channel. - */ -int rsl_release_sapis_from(struct gsm_lchan *lchan, int start, - enum rsl_rel_mode release_mode) -{ - int no_sapi = 1; - int sapi; - - for (sapi = start; sapi < ARRAY_SIZE(lchan->sapis); ++sapi) { - uint8_t link_id; - if (lchan->sapis[sapi] == LCHAN_SAPI_UNUSED) - continue; - - link_id = sapi; - if (lchan->type == GSM_LCHAN_TCH_F || lchan->type == GSM_LCHAN_TCH_H) - link_id |= 0x40; - rsl_release_request(lchan, link_id, release_mode); - no_sapi = 0; - } - - return no_sapi; -} - -int rsl_start_t3109(struct gsm_lchan *lchan) -{ - struct gsm_bts *bts = lchan->ts->trx->bts; - - /* Disabled, mostly legacy code */ - if (bts->network->T3109 == 0) - return -1; - - osmo_timer_setup(&lchan->T3109, t3109_expired, lchan); - osmo_timer_schedule(&lchan->T3109, bts->network->T3109, 0); - return 0; -} - -/** - * \brief directly RF Channel Release the lchan - * - * When no SAPI was allocated, directly release the logical channel. This - * should only be called from chan_alloc.c on channel release handling. In - * case no SAPI was established the RF Channel can be directly released, - */ -int rsl_direct_rf_release(struct gsm_lchan *lchan) -{ - int i; - for (i = 0; i < ARRAY_SIZE(lchan->sapis); ++i) { - if (lchan->sapis[i] != LCHAN_SAPI_UNUSED) { - LOGP(DRSL, LOGL_ERROR, "%s SAPI(%d) still allocated.\n", - gsm_lchan_name(lchan), i); - return -1; - } - } - - /* Now release it */ - return rsl_rf_chan_release(lchan, 0, SACCH_NONE); -} diff --git a/openbsc/src/libbsc/arfcn_range_encode.c b/openbsc/src/libbsc/arfcn_range_encode.c deleted file mode 100644 index 9ca48407e..000000000 --- a/openbsc/src/libbsc/arfcn_range_encode.c +++ /dev/null @@ -1,330 +0,0 @@ -/* gsm 04.08 system information (si) encoding and decoding - * 3gpp ts 04.08 version 7.21.0 release 1998 / etsi ts 100 940 v7.21.0 */ - -/* - * (C) 2012 Holger Hans Peter Freyther - * (C) 2012 by On-Waves - * 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 <http://www.gnu.org/licenses/>. - */ - -#include <openbsc/arfcn_range_encode.h> -#include <openbsc/debug.h> - -#include <osmocom/gsm/protocol/gsm_04_08.h> - -#include <osmocom/core/utils.h> - -#include <errno.h> - -static inline int greatest_power_of_2_lesser_or_equal_to(int index) -{ - int power_of_2 = 1; - - do { - power_of_2 *= 2; - } while (power_of_2 <= index); - - /* now go back one step */ - return power_of_2 / 2; -} - -static inline int mod(int data, int range) -{ - int res = data % range; - while (res < 0) - res += range; - return res; -} - -/** - * Determine at which index to split the ARFCNs to create an - * equally size partition for the given range. Return -1 if - * no such partition exists. - */ -int range_enc_find_index(enum gsm48_range range, const int *freqs, const int size) -{ - int i, j, n; - - const int RANGE_DELTA = (range - 1) / 2; - - for (i = 0; i < size; ++i) { - n = 0; - for (j = 0; j < size; ++j) { - if (mod(freqs[j] - freqs[i], range) <= RANGE_DELTA) - n += 1; - } - - if (n - 1 == (size - 1) / 2) - return i; - } - - return -1; -} - -/** - * Range encode the ARFCN list. - * \param range The range to use. - * \param arfcns The list of ARFCNs - * \param size The size of the list of ARFCNs - * \param out Place to store the W(i) output. - */ -int range_enc_arfcns(enum gsm48_range range, - const int *arfcns, int size, int *out, - const int index) -{ - int split_at; - int i; - - /* - * The below is a GNU extension and we can remove it when - * we move to a quicksort like in-situ swap with the pivot. - */ - int arfcns_left[size / 2]; - int arfcns_right[size / 2]; - int l_size; - int r_size; - int l_origin; - int r_origin; - - - /* Test the two recursion anchors and stop processing */ - if (size == 0) - return 0; - - if (size == 1) { - out[index] = 1 + arfcns[0]; - return 0; - } - - /* Now do the processing */ - split_at = range_enc_find_index(range, arfcns, size); - if (split_at < 0) - return -EINVAL; - - /* we now know where to split */ - out[index] = 1 + arfcns[split_at]; - - /* calculate the work that needs to be done for the leafs */ - l_origin = mod(arfcns[split_at] + ((range - 1) / 2) + 1, range); - r_origin = mod(arfcns[split_at] + 1, range); - for (i = 0, l_size = 0, r_size = 0; i < size; ++i) { - if (mod(arfcns[i] - l_origin, range) < range / 2) - arfcns_left[l_size++] = mod(arfcns[i] - l_origin, range); - if (mod(arfcns[i] - r_origin, range) < range / 2) - arfcns_right[r_size++] = mod(arfcns[i] - r_origin, range); - } - - /* - * Now recurse and we need to make this iterative... but as the - * tree is balanced the stack will not be too deep. - */ - if (l_size) - range_enc_arfcns(range / 2, arfcns_left, l_size, - out, index + greatest_power_of_2_lesser_or_equal_to(index + 1)); - if (r_size) - range_enc_arfcns((range - 1) / 2, arfcns_right, r_size, - out, index + (2 * greatest_power_of_2_lesser_or_equal_to(index + 1))); - return 0; -} - -/* - * The easiest is to use f0 == arfcns[0]. This means that under certain - * circumstances we can encode less ARFCNs than possible with an optimal f0. - * - * TODO: Solve the optimisation problem and pick f0 so that the max distance - * is the smallest. Taking into account the modulo operation. I think picking - * size/2 will be the optimal arfcn. - */ -/** - * This implements the range determination as described in GSM 04.08 J4. The - * result will be a base frequency f0 and the range to use. Note that for range - * 1024 encoding f0 always refers to ARFCN 0 even if it is not an element of - * the arfcns list. - * - * \param[in] arfcns The input frequencies, they must be sorted, lowest number first - * \param[in] size The length of the array - * \param[out] f0 The selected F0 base frequency. It might not be inside the list - */ -int range_enc_determine_range(const int *arfcns, const int size, int *f0) -{ - int max = 0; - - /* - * Go for the easiest. And pick arfcns[0] == f0. - */ - max = arfcns[size - 1] - arfcns[0]; - *f0 = arfcns[0]; - - if (max < 128 && size <= 29) - return ARFCN_RANGE_128; - if (max < 256 && size <= 22) - return ARFCN_RANGE_256; - if (max < 512 && size <= 18) - return ARFCN_RANGE_512; - if (max < 1024 && size <= 17) { - *f0 = 0; - return ARFCN_RANGE_1024; - } - - return ARFCN_RANGE_INVALID; -} - -static void write_orig_arfcn(uint8_t *chan_list, int f0) -{ - chan_list[0] |= (f0 >> 9) & 1; - chan_list[1] = (f0 >> 1); - chan_list[2] = (f0 & 1) << 7; -} - -static void write_all_wn(uint8_t *chan_list, int bit_offs, - int *w, int w_size, int w1_len) -{ - int octet_offs = 0; /* offset into chan_list */ - int wk_len = w1_len; /* encoding size in bits of w[k] */ - int k; /* 1 based */ - int level = 0; /* tree level, top level = 0 */ - int lvl_left = 1; /* nodes per tree level */ - - /* W(2^i) to W(2^(i+1)-1) are on w1_len-i bits when present */ - - for (k = 1; k <= w_size; k++) { - int wk_left = wk_len; - DEBUGP(DRR, - "k=%d, wk_len=%d, offs=%d:%d, level=%d, " - "lvl_left=%d\n", - k, wk_len, octet_offs, bit_offs, level, lvl_left); - - while (wk_left > 0) { - int cur_bits = 8 - bit_offs; - int cur_mask; - int wk_slice; - - if (cur_bits > wk_left) - cur_bits = wk_left; - - cur_mask = ((1 << cur_bits) - 1); - - DEBUGP(DRR, - " wk_left=%d, cur_bits=%d, offs=%d:%d\n", - wk_left, cur_bits, octet_offs, bit_offs); - - /* advance */ - wk_left -= cur_bits; - bit_offs += cur_bits; - - /* right aligned wk data for current out octet */ - wk_slice = (w[k-1] >> wk_left) & cur_mask; - - /* cur_bits now contains the number of bits - * that are to be copied from wk to the chan_list. - * wk_left is set to the number of bits that must - * not yet be copied. - * bit_offs points after the bit area that is going to - * be overwritten: - * - * wk_left - * | - * v - * wk: WWWWWWWWWWW - * |||||<-- wk_slice, cur_bits=5 - * --WWWWW- - * ^ - * | - * bit_offs - */ - - DEBUGP(DRR, - " wk=%02x, slice=%02x/%02x, cl=%02x\n", - w[k-1], wk_slice, cur_mask, wk_slice << (8 - bit_offs)); - - chan_list[octet_offs] &= ~(cur_mask << (8 - bit_offs)); - chan_list[octet_offs] |= wk_slice << (8 - bit_offs); - - /* adjust output */ - if (bit_offs == 8) { - bit_offs = 0; - octet_offs += 1; - } - } - - /* adjust bit sizes */ - lvl_left -= 1; - if (!lvl_left) { - /* completed tree level, advance to next */ - level += 1; - lvl_left = 1 << level; - wk_len -= 1; - } - } -} - -int range_enc_range128(uint8_t *chan_list, int f0, int *w) -{ - chan_list[0] = 0x8C; - write_orig_arfcn(chan_list, f0); - - write_all_wn(&chan_list[2], 1, w, 28, 7); - return 0; -} - -int range_enc_range256(uint8_t *chan_list, int f0, int *w) -{ - chan_list[0] = 0x8A; - write_orig_arfcn(chan_list, f0); - - write_all_wn(&chan_list[2], 1, w, 21, 8); - return 0; -} - -int range_enc_range512(uint8_t *chan_list, int f0, int *w) -{ - chan_list[0] = 0x88; - write_orig_arfcn(chan_list, f0); - - write_all_wn(&chan_list[2], 1, w, 17, 9); - return 0; -} - -int range_enc_range1024(uint8_t *chan_list, int f0, int f0_included, int *w) -{ - chan_list[0] = 0x80 | (f0_included << 2); - - write_all_wn(&chan_list[0], 6, w, 16, 10); - return 0; -} - -int range_enc_filter_arfcns(int *arfcns, - const int size, const int f0, int *f0_included) -{ - int i, j = 0; - *f0_included = 0; - - for (i = 0; i < size; ++i) { - /* - * Appendix J.4 says the following: - * All frequencies except F(0), minus F(0) + 1. - * I assume we need to exclude it here. - */ - if (arfcns[i] == f0) { - *f0_included = 1; - continue; - } - - arfcns[j++] = mod(arfcns[i] - (f0 + 1), 1024); - } - - return j; -} diff --git a/openbsc/src/libbsc/bsc_api.c b/openbsc/src/libbsc/bsc_api.c deleted file mode 100644 index 7613cac90..000000000 --- a/openbsc/src/libbsc/bsc_api.c +++ /dev/null @@ -1,894 +0,0 @@ -/* GSM 08.08 like API for OpenBSC. The bridge from MSC to BSC */ - -/* (C) 2010-2011 by Holger Hans Peter Freyther - * (C) 2010-2011 by On-Waves - * (C) 2009 by Harald Welte <laforge@gnumonks.org> - * - * 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 <http://www.gnu.org/licenses/>. - * - */ - -#include <openbsc/bsc_api.h> -#include <openbsc/bsc_rll.h> -#include <openbsc/gsm_data.h> -#include <openbsc/gsm_subscriber.h> -#include <openbsc/signal.h> -#include <openbsc/abis_rsl.h> -#include <openbsc/chan_alloc.h> -#include <openbsc/handover.h> -#include <openbsc/debug.h> -#include <openbsc/gsm_04_08.h> -#include <openbsc/trau_mux.h> - -#include <osmocom/gsm/protocol/gsm_08_08.h> -#include <osmocom/gsm/gsm48.h> - -#include <osmocom/core/talloc.h> - -#define GSM0808_T10_VALUE 6, 0 - - -static void rll_ind_cb(struct gsm_lchan *, uint8_t, void *, enum bsc_rllr_ind); -static void send_sapi_reject(struct gsm_subscriber_connection *conn, int link_id); -static void handle_release(struct gsm_subscriber_connection *conn, struct bsc_api *bsc, struct gsm_lchan *lchan); -static void handle_chan_ack(struct gsm_subscriber_connection *conn, struct bsc_api *bsc, struct gsm_lchan *lchan); -static void handle_chan_nack(struct gsm_subscriber_connection *conn, struct bsc_api *bsc, struct gsm_lchan *lchan); - -/* GSM 08.08 3.2.2.33 */ -static uint8_t lchan_to_chosen_channel(struct gsm_lchan *lchan) -{ - uint8_t channel_mode = 0, channel = 0; - - switch (lchan->tch_mode) { - case GSM48_CMODE_SPEECH_V1: - case GSM48_CMODE_SPEECH_EFR: - case GSM48_CMODE_SPEECH_AMR: - channel_mode = 0x9; - break; - case GSM48_CMODE_SIGN: - channel_mode = 0x8; - break; - case GSM48_CMODE_DATA_14k5: - channel_mode = 0xe; - break; - case GSM48_CMODE_DATA_12k0: - channel_mode = 0xb; - break; - case GSM48_CMODE_DATA_6k0: - channel_mode = 0xc; - break; - case GSM48_CMODE_DATA_3k6: - channel_mode = 0xd; - break; - } - - switch (lchan->type) { - case GSM_LCHAN_NONE: - channel = 0x0; - break; - case GSM_LCHAN_SDCCH: - channel = 0x1; - break; - case GSM_LCHAN_TCH_F: - channel = 0x8; - break; - case GSM_LCHAN_TCH_H: - channel = 0x9; - break; - case GSM_LCHAN_UNKNOWN: - default: - LOGP(DMSC, LOGL_ERROR, "Unknown lchan type: %p\n", lchan); - break; - } - - return channel_mode << 4 | channel; -} - -static uint8_t chan_mode_to_speech(struct gsm_lchan *lchan) -{ - int mode = 0; - - switch (lchan->tch_mode) { - case GSM48_CMODE_SPEECH_V1: - mode = 1; - break; - case GSM48_CMODE_SPEECH_EFR: - mode = 0x11; - break; - case GSM48_CMODE_SPEECH_AMR: - mode = 0x21; - break; - case GSM48_CMODE_SIGN: - case GSM48_CMODE_DATA_14k5: - case GSM48_CMODE_DATA_12k0: - case GSM48_CMODE_DATA_6k0: - case GSM48_CMODE_DATA_3k6: - default: - LOGP(DMSC, LOGL_ERROR, "Using non speech mode: %d\n", mode); - return 0; - break; - } - - /* assume to always do AMR HR on any TCH type */ - if (lchan->type == GSM_LCHAN_TCH_H || - lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) - mode |= 0x4; - - return mode; -} - -static void assignment_t10_timeout(void *_conn) -{ - struct bsc_api *api; - struct gsm_subscriber_connection *conn = - (struct gsm_subscriber_connection *) _conn; - - LOGP(DMSC, LOGL_ERROR, "Assignment T10 timeout on %p\n", conn); - - /* - * normal release on the secondary channel but only if the - * secondary_channel has not been released by the handle_chan_nack. - */ - if (conn->secondary_lchan) - lchan_release(conn->secondary_lchan, 0, RSL_REL_LOCAL_END); - conn->secondary_lchan = NULL; - - /* inform them about the failure */ - api = conn->network->bsc_api; - api->assign_fail(conn, GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE, NULL); -} - -/*! \brief Determine and apply AMR multi-rate configuration to lchan - * Determine which AMR multi-rate configuration to use and apply it to - * the lchan (so it can be communicated to BTS and MS during channel - * activation. - * \param[in] conn subscriber connection (used to resolve bsc_api) - * \param[out] lchan logical channel to which to apply mr config - * \param[in] full_rate whether to use full-rate (1) or half-rate (0) config - */ -static void handle_mr_config(struct gsm_subscriber_connection *conn, - struct gsm_lchan *lchan, int full_rate) -{ - struct bsc_api *api; - api = conn->network->bsc_api; - struct amr_multirate_conf *mr; - struct gsm48_multi_rate_conf *mr_conf; - - /* BSC api override for this method, used in OsmoBSC mode with - * bsc_mr_config() to use MSC-specific/specified configuration */ - if (api->mr_config) - return api->mr_config(conn, lchan, full_rate); - - /* NITB case: use the BTS-specic multi-rate configuration from - * the vty/configuration file */ - if (full_rate) - mr = &lchan->ts->trx->bts->mr_full; - else - mr = &lchan->ts->trx->bts->mr_half; - - mr_conf = (struct gsm48_multi_rate_conf *) mr->gsm48_ie; - mr_conf->ver = 1; - - /* default, if no AMR codec defined */ - if (!mr->gsm48_ie[1]) { - mr_conf->icmi = 1; - mr_conf->m5_90 = 1; - } - /* store encoded MR config IE lchan for both MS (uplink) and BTS - * (downlink) directions */ - gsm48_multirate_config(lchan->mr_ms_lv, mr, mr->ms_mode); - gsm48_multirate_config(lchan->mr_bts_lv, mr, mr->bts_mode); -} - -/* - * Start a new assignment and make sure that it is completed within T10 either - * positively, negatively or by the timeout. - * - * 1.) allocate a new lchan - * 2.) copy the encryption key and other data from the - * old to the new channel. - * 3.) RSL Channel Activate this channel and wait - * - * -> Signal handler for the LCHAN - * 4.) Send GSM 04.08 assignment command to the MS - * - * -> Assignment Complete/Assignment Failure - * 5.) Release the SDCCH, continue signalling on the new link - */ -static int handle_new_assignment(struct gsm_subscriber_connection *conn, int chan_mode, int full_rate) -{ - struct gsm_lchan *new_lchan; - int chan_type; - - chan_type = full_rate ? GSM_LCHAN_TCH_F : GSM_LCHAN_TCH_H; - - new_lchan = lchan_alloc(conn->bts, chan_type, 0); - - if (!new_lchan) { - LOGP(DMSC, LOGL_NOTICE, "No free channel.\n"); - return -1; - } - - /* copy old data to the new channel */ - memcpy(&new_lchan->encr, &conn->lchan->encr, sizeof(new_lchan->encr)); - new_lchan->ms_power = conn->lchan->ms_power; - new_lchan->bs_power = conn->lchan->bs_power; - new_lchan->rqd_ta = conn->lchan->rqd_ta; - - /* copy new data to it */ - new_lchan->tch_mode = chan_mode; - new_lchan->rsl_cmode = RSL_CMOD_SPD_SPEECH; - - /* handle AMR correctly */ - if (chan_mode == GSM48_CMODE_SPEECH_AMR) - handle_mr_config(conn, new_lchan, full_rate); - - if (rsl_chan_activate_lchan(new_lchan, 0x1, 0) < 0) { - LOGP(DHO, LOGL_ERROR, "could not activate channel\n"); - lchan_free(new_lchan); - return -1; - } - - /* remember that we have the channel */ - conn->secondary_lchan = new_lchan; - new_lchan->conn = conn; - - rsl_lchan_set_state(new_lchan, LCHAN_S_ACT_REQ); - return 0; -} - -struct gsm_subscriber_connection *bsc_subscr_con_allocate(struct gsm_lchan *lchan) -{ - struct gsm_subscriber_connection *conn; - struct gsm_network *net = lchan->ts->trx->bts->network; - - conn = talloc_zero(net, struct gsm_subscriber_connection); - if (!conn) - return NULL; - - conn->network = net; - conn->lchan = lchan; - conn->bts = lchan->ts->trx->bts; - conn->via_ran = RAN_GERAN_A; - lchan->conn = conn; - llist_add_tail(&conn->entry, &net->subscr_conns); - return conn; -} - -void bsc_subscr_con_free(struct gsm_subscriber_connection *conn) -{ - if (!conn) - return; - - - if (conn->subscr) { - subscr_put(conn->subscr); - conn->subscr = NULL; - } - - - if (conn->ho_lchan) { - LOGP(DNM, LOGL_ERROR, "The ho_lchan should have been cleared.\n"); - conn->ho_lchan->conn = NULL; - } - - if (conn->lchan) { - LOGP(DNM, LOGL_ERROR, "The lchan should have been cleared.\n"); - conn->lchan->conn = NULL; - } - - if (conn->secondary_lchan) { - LOGP(DNM, LOGL_ERROR, "The secondary_lchan should have been cleared.\n"); - conn->secondary_lchan->conn = NULL; - } - - llist_del(&conn->entry); - talloc_free(conn); -} - -int bsc_api_init(struct gsm_network *network, struct bsc_api *api) -{ - network->bsc_api = api; - return 0; -} - -/*! \brief process incoming 08.08 DTAP from MSC (send via BTS to MS) */ -int gsm0808_submit_dtap(struct gsm_subscriber_connection *conn, - struct msgb *msg, int link_id, int allow_sacch) -{ - uint8_t sapi; - - - if (!conn->lchan) { - LOGP(DMSC, LOGL_ERROR, - "Called submit dtap without an lchan.\n"); - msgb_free(msg); - return -1; - } - - sapi = link_id & 0x7; - msg->lchan = conn->lchan; - msg->dst = msg->lchan->ts->trx->rsl_link; - - /* If we are on a TCH and need to submit a SMS (on SAPI=3) we need to use the SACH */ - if (allow_sacch && sapi != 0) { - if (conn->lchan->type == GSM_LCHAN_TCH_F || conn->lchan->type == GSM_LCHAN_TCH_H) - link_id |= 0x40; - } - - msg->l3h = msg->data; - /* is requested SAPI already up? */ - if (conn->lchan->sapis[sapi] == LCHAN_SAPI_UNUSED) { - /* Establish L2 for additional SAPI */ - OBSC_LINKID_CB(msg) = link_id; - if (rll_establish(msg->lchan, sapi, rll_ind_cb, msg) != 0) { - msgb_free(msg); - send_sapi_reject(conn, link_id); - return -1; - } - return 0; - } else { - /* Directly forward via RLL/RSL to BTS */ - return rsl_data_request(msg, link_id); - } -} - -/* - * \brief Check if the given channel is compatible with the mode/fullrate - */ -static int chan_compat_with_mode(struct gsm_lchan *lchan, int chan_mode, int full_rate) -{ - switch (chan_mode) { - case GSM48_CMODE_SIGN: - /* signalling is always possible */ - return 1; - case GSM48_CMODE_SPEECH_V1: - case GSM48_CMODE_SPEECH_AMR: - case GSM48_CMODE_DATA_3k6: - case GSM48_CMODE_DATA_6k0: - /* these services can all run on TCH/H, but we may have - * an explicit override by the 'full_rate' argument */ - switch (lchan->type) { - case GSM_LCHAN_TCH_F: - return 1; - case GSM_LCHAN_TCH_H: - if (full_rate) - return 0; - else - return 1; - break; - default: - return 0; - } - break; - case GSM48_CMODE_DATA_12k0: - case GSM48_CMODE_DATA_14k5: - case GSM48_CMODE_SPEECH_EFR: - /* these services all explicitly require a TCH/F */ - if (lchan->type == GSM_LCHAN_TCH_F) - return 1; - else - return 0; - break; - } - - return 0; -} - -/** - * Send a GSM08.08 Assignment Request. Right now this does not contain the - * audio codec type or the allowed rates for the config. It is assumed that - * this is for audio handling only. In case the current channel does not allow - * the selected mode a new one will be allocated. - * - * TODO: Add multirate configuration, make it work for more than audio. - */ -int gsm0808_assign_req(struct gsm_subscriber_connection *conn, int chan_mode, int full_rate) -{ - struct bsc_api *api; - api = conn->network->bsc_api; - - if (!chan_compat_with_mode(conn->lchan, chan_mode, full_rate)) { - if (handle_new_assignment(conn, chan_mode, full_rate) != 0) - goto error; - } else { - if (chan_mode == GSM48_CMODE_SPEECH_AMR) - handle_mr_config(conn, conn->lchan, full_rate); - - LOGP(DMSC, LOGL_NOTICE, - "Sending %s ChanModify for speech: %s on channel %s\n", - gsm_lchan_name(conn->lchan), - get_value_string(gsm48_chan_mode_names, chan_mode), - get_value_string(gsm_chan_t_names, conn->lchan->type)); - gsm48_lchan_modify(conn->lchan, chan_mode); - } - - /* we will now start the timer to complete the assignment */ - osmo_timer_setup(&conn->T10, assignment_t10_timeout, conn); - osmo_timer_schedule(&conn->T10, GSM0808_T10_VALUE); - return 0; - -error: - api->assign_fail(conn, 0, NULL); - return -1; -} - -int gsm0808_page(struct gsm_bts *bts, unsigned int page_group, unsigned int mi_len, - uint8_t *mi, int chan_type) -{ - return rsl_paging_cmd(bts, page_group, mi_len, mi, chan_type, false); -} - -static void handle_ass_compl(struct gsm_subscriber_connection *conn, - struct msgb *msg) -{ - struct gsm48_hdr *gh; - struct bsc_api *api = conn->network->bsc_api; - - if (conn->secondary_lchan != msg->lchan) { - LOGP(DMSC, LOGL_ERROR, "Assignment Compl should occur on second lchan.\n"); - return; - } - - gh = msgb_l3(msg); - if (msgb_l3len(msg) - sizeof(*gh) != 1) { - LOGP(DMSC, LOGL_ERROR, "Assignment Compl invalid: %zu\n", - msgb_l3len(msg) - sizeof(*gh)); - return; - } - - /* switch TRAU muxer for E1 based BTS from one channel to another */ - if (is_e1_bts(conn->bts)) - switch_trau_mux(conn->lchan, conn->secondary_lchan); - - /* swap channels */ - osmo_timer_del(&conn->T10); - - lchan_release(conn->lchan, 0, RSL_REL_LOCAL_END); - conn->lchan = conn->secondary_lchan; - conn->secondary_lchan = NULL; - - if (is_ipaccess_bts(conn->bts) && conn->lchan->tch_mode != GSM48_CMODE_SIGN) - rsl_ipacc_crcx(conn->lchan); - - api->assign_compl(conn, gh->data[0], - lchan_to_chosen_channel(conn->lchan), - conn->lchan->encr.alg_id, - chan_mode_to_speech(conn->lchan)); -} - -static void handle_ass_fail(struct gsm_subscriber_connection *conn, - struct msgb *msg) -{ - struct bsc_api *api = conn->network->bsc_api; - uint8_t *rr_failure; - struct gsm48_hdr *gh; - - - if (conn->lchan != msg->lchan) { - LOGP(DMSC, LOGL_ERROR, "Assignment failure should occur on primary lchan.\n"); - return; - } - - /* stop the timer and release it */ - osmo_timer_del(&conn->T10); - lchan_release(conn->secondary_lchan, 0, RSL_REL_LOCAL_END); - conn->secondary_lchan = NULL; - - gh = msgb_l3(msg); - if (msgb_l3len(msg) - sizeof(*gh) != 1) { - LOGP(DMSC, LOGL_ERROR, "assignment failure unhandled: %zu\n", - msgb_l3len(msg) - sizeof(*gh)); - rr_failure = NULL; - } else { - rr_failure = &gh->data[0]; - } - - api->assign_fail(conn, - GSM0808_CAUSE_RADIO_INTERFACE_MESSAGE_FAILURE, - rr_failure); -} - -static void handle_classmark_chg(struct gsm_subscriber_connection *conn, - struct msgb *msg) -{ - struct bsc_api *api = msg->lchan->ts->trx->bts->network->bsc_api; - struct gsm48_hdr *gh = msgb_l3(msg); - unsigned int payload_len = msgb_l3len(msg) - sizeof(*gh); - uint8_t cm2_len, cm3_len = 0; - uint8_t *cm2, *cm3 = NULL; - - DEBUGP(DRR, "CLASSMARK CHANGE "); - - /* classmark 2 */ - cm2_len = gh->data[0]; - cm2 = &gh->data[1]; - DEBUGPC(DRR, "CM2(len=%u) ", cm2_len); - - if (payload_len > cm2_len + 1) { - /* we must have a classmark3 */ - if (gh->data[cm2_len+1] != 0x20) { - DEBUGPC(DRR, "ERR CM3 TAG\n"); - return; - } - if (cm2_len > 3) { - DEBUGPC(DRR, "CM2 too long!\n"); - return; - } - - cm3_len = gh->data[cm2_len+2]; - cm3 = &gh->data[cm2_len+3]; - if (cm3_len > 14) { - DEBUGPC(DRR, "CM3 len %u too long!\n", cm3_len); - return; - } - DEBUGPC(DRR, "CM3(len=%u)\n", cm3_len); - } - api->classmark_chg(conn, cm2, cm2_len, cm3, cm3_len); -} - -/* Chapter 9.1.16 Handover complete */ -static void handle_rr_ho_compl(struct msgb *msg) -{ - struct lchan_signal_data sig; - struct gsm48_hdr *gh = msgb_l3(msg); - - DEBUGP(DRR, "HANDOVER COMPLETE cause = %s\n", - rr_cause_name(gh->data[0])); - - sig.lchan = msg->lchan; - sig.mr = NULL; - osmo_signal_dispatch(SS_LCHAN, S_LCHAN_HANDOVER_COMPL, &sig); - /* FIXME: release old channel */ -} - -/* Chapter 9.1.17 Handover Failure */ -static void handle_rr_ho_fail(struct msgb *msg) -{ - struct lchan_signal_data sig; - struct gsm48_hdr *gh = msgb_l3(msg); - - DEBUGP(DRR, "HANDOVER FAILED cause = %s\n", - rr_cause_name(gh->data[0])); - - sig.lchan = msg->lchan; - sig.mr = NULL; - osmo_signal_dispatch(SS_LCHAN, S_LCHAN_HANDOVER_FAIL, &sig); - /* FIXME: release allocated new channel */ -} - - -static void dispatch_dtap(struct gsm_subscriber_connection *conn, - uint8_t link_id, struct msgb *msg) -{ - struct bsc_api *api = msg->lchan->ts->trx->bts->network->bsc_api; - struct gsm48_hdr *gh; - uint8_t pdisc; - uint8_t msg_type; - int rc; - - if (msgb_l3len(msg) < sizeof(*gh)) { - LOGP(DMSC, LOGL_ERROR, "Message too short for a GSM48 header.\n"); - return; - } - - gh = msgb_l3(msg); - pdisc = gsm48_hdr_pdisc(gh); - msg_type = gsm48_hdr_msg_type(gh); - - /* the idea is to handle all RR messages here, and only hand - * MM/CC/SMS-CP/LCS up to the MSC. Some messages like PAGING - * RESPONSE or CM SERVICE REQUEST will not be covered here, as - * they are only possible in the first L3 message of each L2 - * channel, i.e. 'conn' will not exist and gsm0408_rcvmsg() - * will call api->compl_l3() for it */ - switch (pdisc) { - case GSM48_PDISC_RR: - switch (msg_type) { - case GSM48_MT_RR_GPRS_SUSP_REQ: - DEBUGP(DRR, "%s\n", - gsm48_rr_msg_name(GSM48_MT_RR_GPRS_SUSP_REQ)); - break; - case GSM48_MT_RR_STATUS: - LOGP(DRR, LOGL_NOTICE, "%s (cause: %s)\n", - gsm48_rr_msg_name(GSM48_MT_RR_GPRS_SUSP_REQ), - rr_cause_name(gh->data[0])); - break; - case GSM48_MT_RR_MEAS_REP: - /* This shouldn't actually end up here, as RSL treats - * L3 Info of 08.58 MEASUREMENT REPORT different by calling - * directly into gsm48_parse_meas_rep */ - LOGP(DMEAS, LOGL_ERROR, "DIRECT GSM48 MEASUREMENT REPORT ?!? "); - break; - case GSM48_MT_RR_HANDO_COMPL: - handle_rr_ho_compl(msg); - break; - case GSM48_MT_RR_HANDO_FAIL: - handle_rr_ho_fail(msg); - break; - case GSM48_MT_RR_CIPH_M_COMPL: - if (api->cipher_mode_compl) - api->cipher_mode_compl(conn, msg, - conn->lchan->encr.alg_id); - break; - case GSM48_MT_RR_ASS_COMPL: - handle_ass_compl(conn, msg); - break; - case GSM48_MT_RR_ASS_FAIL: - handle_ass_fail(conn, msg); - break; - case GSM48_MT_RR_CHAN_MODE_MODIF_ACK: - osmo_timer_del(&conn->T10); - rc = gsm48_rx_rr_modif_ack(msg); - if (rc < 0) { - api->assign_fail(conn, - GSM0808_CAUSE_NO_RADIO_RESOURCE_AVAILABLE, - NULL); - } else if (rc >= 0) { - api->assign_compl(conn, 0, - lchan_to_chosen_channel(conn->lchan), - conn->lchan->encr.alg_id, - chan_mode_to_speech(conn->lchan)); - } - break; - case GSM48_MT_RR_CLSM_CHG: - handle_classmark_chg(conn, msg); - break; - case GSM48_MT_RR_APP_INFO: - /* Passing RR APP INFO to MSC, not quite - * according to spec */ - if (api->dtap) - api->dtap(conn, link_id, msg); - break; - default: - /* Normally, a MSC should never receive RR - * messages, but we'd rather forward what we - * don't know than drop it... */ - LOGP(DRR, LOGL_NOTICE, - "BSC: Passing %s 04.08 RR message to MSC\n", - gsm48_rr_msg_name(msg_type)); - if (api->dtap) - api->dtap(conn, link_id, msg); - } - break; - default: - if (api->dtap) - api->dtap(conn, link_id, msg); - break; - } -} - -/*! \brief RSL has received a DATA INDICATION with L3 from MS */ -int gsm0408_rcvmsg(struct msgb *msg, uint8_t link_id) -{ - int rc; - struct bsc_api *api = msg->lchan->ts->trx->bts->network->bsc_api; - struct gsm_lchan *lchan; - - lchan = msg->lchan; - if (lchan->state != LCHAN_S_ACTIVE) { - LOGP(DRSL, LOGL_INFO, "Got data in non active state(%s), " - "discarding.\n", gsm_lchans_name(lchan->state)); - return -1; - } - - - if (lchan->conn) { - /* if we already have a connection, forward via DTAP to - * MSC */ - dispatch_dtap(lchan->conn, link_id, msg); - } else { - /* allocate a new connection */ - rc = BSC_API_CONN_POL_REJECT; - lchan->conn = bsc_subscr_con_allocate(msg->lchan); - if (!lchan->conn) { - lchan_release(lchan, 1, RSL_REL_NORMAL); - return -1; - } - - /* fwd via bsc_api to send COMPLETE L3 INFO to MSC */ - rc = api->compl_l3(lchan->conn, msg, 0); - - if (rc != BSC_API_CONN_POL_ACCEPT) { - lchan->conn->lchan = NULL; - bsc_subscr_con_free(lchan->conn); - lchan_release(lchan, 1, RSL_REL_NORMAL); - } - } - - return 0; -} - -/*! \brief We received a GSM 08.08 CIPHER MODE from the MSC */ -int gsm0808_cipher_mode(struct gsm_subscriber_connection *conn, int cipher, - const uint8_t *key, int len, int include_imeisv) -{ - if (cipher > 0 && key == NULL) { - LOGP(DRSL, LOGL_ERROR, "Need to have an encrytpion key.\n"); - return -1; - } - - if (len > MAX_A5_KEY_LEN) { - LOGP(DRSL, LOGL_ERROR, "The key is too long: %d\n", len); - return -1; - } - - conn->lchan->encr.alg_id = RSL_ENC_ALG_A5(cipher); - if (key) { - conn->lchan->encr.key_len = len; - memcpy(conn->lchan->encr.key, key, len); - } - - return gsm48_send_rr_ciph_mode(conn->lchan, include_imeisv); -} - -/* - * Release all occupied RF Channels but stay around for more. - */ -int gsm0808_clear(struct gsm_subscriber_connection *conn) -{ - if (conn->ho_lchan) - bsc_clear_handover(conn, 1); - - if (conn->secondary_lchan) - lchan_release(conn->secondary_lchan, 0, RSL_REL_LOCAL_END); - - if (conn->lchan) - lchan_release(conn->lchan, 1, RSL_REL_NORMAL); - - conn->lchan = NULL; - conn->secondary_lchan = NULL; - conn->ho_lchan = NULL; - conn->bts = NULL; - - osmo_timer_del(&conn->T10); - - return 0; -} - -static void send_sapi_reject(struct gsm_subscriber_connection *conn, int link_id) -{ - struct bsc_api *api; - - if (!conn) - return; - - api = conn->network->bsc_api; - if (!api || !api->sapi_n_reject) - return; - - api->sapi_n_reject(conn, link_id); -} - -static void rll_ind_cb(struct gsm_lchan *lchan, uint8_t link_id, void *_data, enum bsc_rllr_ind rllr_ind) -{ - struct msgb *msg = _data; - - /* - * There seems to be a small window that the RLL timer can - * fire after a lchan_release call and before the S_CHALLOC_FREED - * is called. Check if a conn is set before proceeding. - */ - if (!lchan->conn) - return; - - switch (rllr_ind) { - case BSC_RLLR_IND_EST_CONF: - rsl_data_request(msg, OBSC_LINKID_CB(msg)); - break; - case BSC_RLLR_IND_REL_IND: - case BSC_RLLR_IND_ERR_IND: - case BSC_RLLR_IND_TIMEOUT: - send_sapi_reject(lchan->conn, OBSC_LINKID_CB(msg)); - msgb_free(msg); - break; - } -} - -static int bsc_handle_lchan_signal(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct bsc_api *bsc; - struct gsm_lchan *lchan; - struct lchan_signal_data *lchan_data; - - if (subsys != SS_LCHAN) - return 0; - - - lchan_data = signal_data; - if (!lchan_data->lchan || !lchan_data->lchan->conn) - return 0; - - lchan = lchan_data->lchan; - bsc = lchan->ts->trx->bts->network->bsc_api; - if (!bsc) - return 0; - - switch (signal) { - case S_LCHAN_UNEXPECTED_RELEASE: - handle_release(lchan->conn, bsc, lchan); - break; - case S_LCHAN_ACTIVATE_ACK: - handle_chan_ack(lchan->conn, bsc, lchan); - break; - case S_LCHAN_ACTIVATE_NACK: - handle_chan_nack(lchan->conn, bsc, lchan); - break; - } - - return 0; -} - -static void handle_release(struct gsm_subscriber_connection *conn, - struct bsc_api *bsc, struct gsm_lchan *lchan) -{ - int destruct = 1; - - if (conn->secondary_lchan == lchan) { - osmo_timer_del(&conn->T10); - conn->secondary_lchan = NULL; - - bsc->assign_fail(conn, - GSM0808_CAUSE_RADIO_INTERFACE_FAILURE, - NULL); - } - - /* clear the connection now */ - if (bsc->clear_request) - destruct = bsc->clear_request(conn, 0); - - /* now give up all channels */ - if (conn->lchan == lchan) - conn->lchan = NULL; - if (conn->ho_lchan == lchan) { - bsc_clear_handover(conn, 0); - conn->ho_lchan = NULL; - } - lchan->conn = NULL; - - gsm0808_clear(conn); - - if (destruct) - bsc_subscr_con_free(conn); -} - -static void handle_chan_ack(struct gsm_subscriber_connection *conn, - struct bsc_api *api, struct gsm_lchan *lchan) -{ - if (conn->secondary_lchan != lchan) - return; - - LOGP(DMSC, LOGL_NOTICE, "Sending assignment on chan: %p\n", lchan); - gsm48_send_rr_ass_cmd(conn->lchan, lchan, lchan->ms_power); -} - -static void handle_chan_nack(struct gsm_subscriber_connection *conn, - struct bsc_api *api, struct gsm_lchan *lchan) -{ - if (conn->secondary_lchan != lchan) - return; - - LOGP(DMSC, LOGL_ERROR, "Channel activation failed. Waiting for timeout now\n"); - conn->secondary_lchan->conn = NULL; - conn->secondary_lchan = NULL; -} - -static __attribute__((constructor)) void on_dso_load_bsc(void) -{ - osmo_signal_register_handler(SS_LCHAN, bsc_handle_lchan_signal, NULL); -} - diff --git a/openbsc/src/libbsc/bsc_ctrl_commands.c b/openbsc/src/libbsc/bsc_ctrl_commands.c deleted file mode 100644 index 641fe2bf6..000000000 --- a/openbsc/src/libbsc/bsc_ctrl_commands.c +++ /dev/null @@ -1,459 +0,0 @@ -/* - * (C) 2013-2015 by Holger Hans Peter Freyther - * (C) 2013-2015 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 <http://www.gnu.org/licenses/>. - * - */ -#include <errno.h> -#include <time.h> - -#include <osmocom/ctrl/control_cmd.h> -#include <openbsc/ipaccess.h> -#include <openbsc/gsm_data.h> -#include <openbsc/abis_nm.h> -#include <openbsc/debug.h> -#include <openbsc/chan_alloc.h> -#include <openbsc/osmo_bsc_rf.h> -#include <openbsc/bsc_msc_data.h> - -#define CTRL_CMD_VTY_STRING(cmdname, cmdstr, dtype, element) \ - CTRL_HELPER_GET_STRING(cmdname, dtype, element) \ - CTRL_HELPER_SET_STRING(cmdname, dtype, element) \ -static struct ctrl_cmd_element cmd_##cmdname = { \ - .name = cmdstr, \ - .get = get_##cmdname, \ - .set = set_##cmdname, \ - .verify = verify_vty_description_string, \ -} - -/** - * Check that there are no newlines or comments or other things - * that could make the VTY configuration unparsable. - */ -static int verify_vty_description_string(struct ctrl_cmd *cmd, - const char *value, void *data) -{ - int i; - const size_t len = strlen(value); - - for (i = 0; i < len; ++i) { - switch(value[i]) { - case '#': - case '\n': - case '\r': - cmd->reply = "String includes illegal character"; - return -1; - default: - break; - } - } - - return 0; -} - -CTRL_CMD_DEFINE_RANGE(net_mnc, "mnc", struct gsm_network, network_code, 0, 999); -CTRL_CMD_DEFINE_RANGE(net_mcc, "mcc", struct gsm_network, country_code, 1, 999); -CTRL_CMD_VTY_STRING(net_short_name, "short-name", struct gsm_network, name_short); -CTRL_CMD_VTY_STRING(net_long_name, "long-name", struct gsm_network, name_long); - -static int set_net_apply_config(struct ctrl_cmd *cmd, void *data) -{ - struct gsm_network *net = cmd->node; - struct gsm_bts *bts; - - llist_for_each_entry(bts, &net->bts_list, list) { - if (!is_ipaccess_bts(bts)) - continue; - - /* - * The ip.access nanoBTS seems to be unrelaible on BSSGP - * so let's us just reboot it. For the sysmoBTS we can just - * restart the process as all state is gone. - */ - if (!is_sysmobts_v2(bts) && strcmp(cmd->value, "restart") == 0) { - struct gsm_bts_trx *trx; - llist_for_each_entry_reverse(trx, &bts->trx_list, list) - abis_nm_ipaccess_restart(trx); - } else - ipaccess_drop_oml(bts); - } - - cmd->reply = "Tried to drop the BTS"; - return CTRL_CMD_REPLY; -} - -CTRL_CMD_DEFINE_WO_NOVRF(net_apply_config, "apply-configuration"); - -static int verify_net_mcc_mnc_apply(struct ctrl_cmd *cmd, const char *value, void *d) -{ - char *tmp, *saveptr, *mcc, *mnc; - - tmp = talloc_strdup(cmd, value); - if (!tmp) - return 1; - - mcc = strtok_r(tmp, ",", &saveptr); - mnc = strtok_r(NULL, ",", &saveptr); - talloc_free(tmp); - - if (!mcc || !mnc) - return 1; - return 0; -} - -static int set_net_mcc_mnc_apply(struct ctrl_cmd *cmd, void *data) -{ - struct gsm_network *net = cmd->node; - char *tmp, *saveptr, *mcc_str, *mnc_str; - int mcc, mnc; - - tmp = talloc_strdup(cmd, cmd->value); - if (!tmp) - goto oom; - - - mcc_str = strtok_r(tmp, ",", &saveptr); - mnc_str = strtok_r(NULL, ",", &saveptr); - - mcc = atoi(mcc_str); - mnc = atoi(mnc_str); - - talloc_free(tmp); - - if (net->network_code == mnc && net->country_code == mcc) { - cmd->reply = "Nothing changed"; - return CTRL_CMD_REPLY; - } - - net->network_code = mnc; - net->country_code = mcc; - - return set_net_apply_config(cmd, data); - -oom: - cmd->reply = "OOM"; - return CTRL_CMD_ERROR; -} -CTRL_CMD_DEFINE_WO(net_mcc_mnc_apply, "mcc-mnc-apply"); - -/* BTS related commands below */ -CTRL_CMD_DEFINE_RANGE(bts_lac, "location-area-code", struct gsm_bts, location_area_code, 0, 65535); -CTRL_CMD_DEFINE_RANGE(bts_ci, "cell-identity", struct gsm_bts, cell_identity, 0, 65535); - -static int set_bts_apply_config(struct ctrl_cmd *cmd, void *data) -{ - struct gsm_bts *bts = cmd->node; - - if (!is_ipaccess_bts(bts)) { - cmd->reply = "BTS is not IP based"; - return CTRL_CMD_ERROR; - } - - ipaccess_drop_oml(bts); - cmd->reply = "Tried to drop the BTS"; - return CTRL_CMD_REPLY; -} - -CTRL_CMD_DEFINE_WO_NOVRF(bts_apply_config, "apply-configuration"); - -static int set_bts_si(struct ctrl_cmd *cmd, void *data) -{ - struct gsm_bts *bts = cmd->node; - int rc; - - rc = gsm_bts_set_system_infos(bts); - if (rc != 0) { - cmd->reply = "Failed to generate SI"; - return CTRL_CMD_ERROR; - } - - cmd->reply = "Generated new System Information"; - return CTRL_CMD_REPLY; -} -CTRL_CMD_DEFINE_WO_NOVRF(bts_si, "send-new-system-informations"); - -static int get_bts_chan_load(struct ctrl_cmd *cmd, void *data) -{ - int i; - struct pchan_load pl; - struct gsm_bts *bts; - const char *space = ""; - - bts = cmd->node; - memset(&pl, 0, sizeof(pl)); - bts_chan_load(&pl, bts); - - cmd->reply = talloc_strdup(cmd, ""); - - for (i = 0; i < ARRAY_SIZE(pl.pchan); ++i) { - const struct load_counter *lc = &pl.pchan[i]; - - /* These can never have user load */ - if (i == GSM_PCHAN_NONE) - continue; - if (i == GSM_PCHAN_CCCH) - continue; - if (i == GSM_PCHAN_PDCH) - continue; - if (i == GSM_PCHAN_UNKNOWN) - continue; - - cmd->reply = talloc_asprintf_append(cmd->reply, - "%s%s,%u,%u", - space, gsm_pchan_name(i), lc->used, lc->total); - if (!cmd->reply) - goto error; - space = " "; - } - - return CTRL_CMD_REPLY; - -error: - cmd->reply = "Memory allocation failure"; - return CTRL_CMD_ERROR; -} - -CTRL_CMD_DEFINE_RO(bts_chan_load, "channel-load"); - -static int get_bts_oml_conn(struct ctrl_cmd *cmd, void *data) -{ - struct gsm_bts *bts = cmd->node; - - cmd->reply = bts->oml_link ? "connected" : "disconnected"; - return CTRL_CMD_REPLY; -} - -CTRL_CMD_DEFINE_RO(bts_oml_conn, "oml-connection-state"); - -static int verify_bts_gprs_mode(struct ctrl_cmd *cmd, const char *value, void *_data) -{ - int valid; - enum bts_gprs_mode mode; - struct gsm_bts *bts = cmd->node; - - mode = bts_gprs_mode_parse(value, &valid); - if (!valid) { - cmd->reply = "Mode is not known"; - return 1; - } - - if (!bts_gprs_mode_is_compat(bts, mode)) { - cmd->reply = "bts does not support this mode"; - return 1; - } - - return 0; -} - -static int get_bts_gprs_mode(struct ctrl_cmd *cmd, void *data) -{ - struct gsm_bts *bts = cmd->node; - - cmd->reply = talloc_strdup(cmd, bts_gprs_mode_name(bts->gprs.mode)); - return CTRL_CMD_REPLY; -} - -static int set_bts_gprs_mode(struct ctrl_cmd *cmd, void *data) -{ - struct gsm_bts *bts = cmd->node; - - bts->gprs.mode = bts_gprs_mode_parse(cmd->value, NULL); - return get_bts_gprs_mode(cmd, data); -} - -CTRL_CMD_DEFINE(bts_gprs_mode, "gprs-mode"); - -static int get_bts_rf_state(struct ctrl_cmd *cmd, void *data) -{ - const char *oper, *admin, *policy; - struct gsm_bts *bts = cmd->node; - - if (!bts) { - cmd->reply = "bts not found."; - return CTRL_CMD_ERROR; - } - - oper = osmo_bsc_rf_get_opstate_name(osmo_bsc_rf_get_opstate_by_bts(bts)); - admin = osmo_bsc_rf_get_adminstate_name(osmo_bsc_rf_get_adminstate_by_bts(bts)); - policy = osmo_bsc_rf_get_policy_name(osmo_bsc_rf_get_policy_by_bts(bts)); - - cmd->reply = talloc_asprintf(cmd, "%s,%s,%s", oper, admin, policy); - if (!cmd->reply) { - cmd->reply = "OOM."; - return CTRL_CMD_ERROR; - } - - return CTRL_CMD_REPLY; -} -CTRL_CMD_DEFINE_RO(bts_rf_state, "rf_state"); - -static int get_net_rf_lock(struct ctrl_cmd *cmd, void *data) -{ - struct gsm_network *net = cmd->node; - struct gsm_bts *bts; - const char *policy_name; - - policy_name = osmo_bsc_rf_get_policy_name(net->bsc_data->rf_ctrl->policy); - - llist_for_each_entry(bts, &net->bts_list, list) { - struct gsm_bts_trx *trx; - - /* Exclude the BTS from the global lock */ - if (bts->excl_from_rf_lock) - continue; - - llist_for_each_entry(trx, &bts->trx_list, list) { - if (trx->mo.nm_state.availability == NM_AVSTATE_OK && - trx->mo.nm_state.operational != NM_OPSTATE_DISABLED) { - cmd->reply = talloc_asprintf(cmd, - "state=on,policy=%s,bts=%u,trx=%u", - policy_name, bts->nr, trx->nr); - return CTRL_CMD_REPLY; - } - } - } - - cmd->reply = talloc_asprintf(cmd, "state=off,policy=%s", - policy_name); - return CTRL_CMD_REPLY; -} - -#define TIME_FORMAT_RFC2822 "%a, %d %b %Y %T %z" - -static int set_net_rf_lock(struct ctrl_cmd *cmd, void *data) -{ - int locked = atoi(cmd->value); - struct gsm_network *net = cmd->node; - time_t now = time(NULL); - char now_buf[64]; - struct osmo_bsc_rf *rf; - - if (!net) { - cmd->reply = "net not found."; - return CTRL_CMD_ERROR; - } - - rf = net->bsc_data->rf_ctrl; - - if (!rf) { - cmd->reply = "RF Ctrl is not enabled in the BSC Configuration"; - return CTRL_CMD_ERROR; - } - - talloc_free(rf->last_rf_lock_ctrl_command); - strftime(now_buf, sizeof(now_buf), TIME_FORMAT_RFC2822, gmtime(&now)); - rf->last_rf_lock_ctrl_command = - talloc_asprintf(rf, "rf_locked %u (%s)", locked, now_buf); - - osmo_bsc_rf_schedule_lock(rf, locked == 1 ? '0' : '1'); - - cmd->reply = talloc_asprintf(cmd, "%u", locked); - if (!cmd->reply) { - cmd->reply = "OOM."; - return CTRL_CMD_ERROR; - } - - return CTRL_CMD_REPLY; -} - -static int verify_net_rf_lock(struct ctrl_cmd *cmd, const char *value, void *data) -{ - int locked = atoi(cmd->value); - - if ((locked != 0) && (locked != 1)) - return 1; - - return 0; -} -CTRL_CMD_DEFINE(net_rf_lock, "rf_locked"); - -static int get_net_bts_num(struct ctrl_cmd *cmd, void *data) -{ - struct gsm_network *net = cmd->node; - - cmd->reply = talloc_asprintf(cmd, "%u", net->num_bts); - return CTRL_CMD_REPLY; -} -CTRL_CMD_DEFINE_RO(net_bts_num, "number-of-bts"); - -/* TRX related commands below here */ -CTRL_HELPER_GET_INT(trx_max_power, struct gsm_bts_trx, max_power_red); -static int verify_trx_max_power(struct ctrl_cmd *cmd, const char *value, void *_data) -{ - int tmp = atoi(value); - - if (tmp < 0 || tmp > 22) { - cmd->reply = "Value must be between 0 and 22"; - return -1; - } - - if (tmp & 1) { - cmd->reply = "Value must be even"; - return -1; - } - - return 0; -} -CTRL_CMD_DEFINE_RANGE(trx_arfcn, "arfcn", struct gsm_bts_trx, arfcn, 0, 1023); - -static int set_trx_max_power(struct ctrl_cmd *cmd, void *_data) -{ - struct gsm_bts_trx *trx = cmd->node; - int old_power; - - /* remember the old value, set the new one */ - old_power = trx->max_power_red; - trx->max_power_red = atoi(cmd->value); - - /* Maybe update the value */ - if (old_power != trx->max_power_red) { - LOGP(DCTRL, LOGL_NOTICE, - "%s updating max_pwr_red(%d)\n", - gsm_trx_name(trx), trx->max_power_red); - abis_nm_update_max_power_red(trx); - } - - return get_trx_max_power(cmd, _data); -} -CTRL_CMD_DEFINE(trx_max_power, "max-power-reduction"); - -int bsc_base_ctrl_cmds_install(void) -{ - int rc = 0; - rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_mnc); - rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_mcc); - rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_short_name); - rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_long_name); - rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_apply_config); - rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_mcc_mnc_apply); - rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_rf_lock); - rc |= ctrl_cmd_install(CTRL_NODE_ROOT, &cmd_net_bts_num); - - rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_lac); - rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_ci); - rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_apply_config); - rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_si); - rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_chan_load); - rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_oml_conn); - rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_gprs_mode); - rc |= ctrl_cmd_install(CTRL_NODE_BTS, &cmd_bts_rf_state); - - rc |= ctrl_cmd_install(CTRL_NODE_TRX, &cmd_trx_max_power); - rc |= ctrl_cmd_install(CTRL_NODE_TRX, &cmd_trx_arfcn); - - return rc; -} diff --git a/openbsc/src/libbsc/bsc_ctrl_lookup.c b/openbsc/src/libbsc/bsc_ctrl_lookup.c deleted file mode 100644 index a8a8cf526..000000000 --- a/openbsc/src/libbsc/bsc_ctrl_lookup.c +++ /dev/null @@ -1,107 +0,0 @@ -/* SNMP-like status interface. Look-up of BTS/TRX - * - * (C) 2010-2011 by Daniel Willmann <daniel@totalueberwachung.de> - * (C) 2010-2011 by On-Waves - * - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#include <errno.h> - -#include <osmocom/vty/command.h> -#include <osmocom/ctrl/control_if.h> -#include <openbsc/debug.h> -#include <openbsc/gsm_data.h> - -extern vector ctrl_node_vec; - -/*! \brief control interface lookup function for bsc/bts gsm_data - * \param[in] data Private data passed to controlif_setup() - * \param[in] vline Vector of the line holding the command string - * \param[out] node_type type (CTRL_NODE_) that was determined - * \param[out] node_data private dta of node that was determined - * \param i Current index into vline, up to which it is parsed - */ -static int bsc_ctrl_node_lookup(void *data, vector vline, int *node_type, - void **node_data, int *i) -{ - struct gsm_network *net = data; - struct gsm_bts *bts = NULL; - struct gsm_bts_trx *trx = NULL; - struct gsm_bts_trx_ts *ts = NULL; - char *token = vector_slot(vline, *i); - long num; - - /* TODO: We need to make sure that the following chars are digits - * and/or use strtol to check if number conversion was successful - * Right now something like net.bts_stats will not work */ - if (!strcmp(token, "bts")) { - if (*node_type != CTRL_NODE_ROOT || !net) - goto err_missing; - (*i)++; - if (!ctrl_parse_get_num(vline, *i, &num)) - goto err_index; - - bts = gsm_bts_num(net, num); - if (!bts) - goto err_missing; - *node_data = bts; - *node_type = CTRL_NODE_BTS; - } else if (!strcmp(token, "trx")) { - if (*node_type != CTRL_NODE_BTS || !*node_data) - goto err_missing; - bts = *node_data; - (*i)++; - if (!ctrl_parse_get_num(vline, *i, &num)) - goto err_index; - - trx = gsm_bts_trx_num(bts, num); - if (!trx) - goto err_missing; - *node_data = trx; - *node_type = CTRL_NODE_TRX; - } else if (!strcmp(token, "ts")) { - if (*node_type != CTRL_NODE_TRX || !*node_data) - goto err_missing; - trx = *node_data; - (*i)++; - if (!ctrl_parse_get_num(vline, *i, &num)) - goto err_index; - - if ((num >= 0) && (num < TRX_NR_TS)) - ts = &trx->ts[num]; - if (!ts) - goto err_missing; - *node_data = ts; - *node_type = CTRL_NODE_TS; - } else - return 0; - - return 1; -err_missing: - return -ENODEV; -err_index: - return -ERANGE; -} - -struct ctrl_handle *bsc_controlif_setup(struct gsm_network *net, - const char *bind_addr, uint16_t port) -{ - return ctrl_interface_setup_dynip(net, bind_addr, port, - bsc_ctrl_node_lookup); -} diff --git a/openbsc/src/libbsc/bsc_dyn_ts.c b/openbsc/src/libbsc/bsc_dyn_ts.c deleted file mode 100644 index e5422fc5c..000000000 --- a/openbsc/src/libbsc/bsc_dyn_ts.c +++ /dev/null @@ -1,77 +0,0 @@ -/* Dynamic PDCH initialisation implementation shared across NM and RSL */ - -/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> - * 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 <http://www.gnu.org/licenses/>. - * - */ - -#include <osmocom/core/logging.h> -#include <openbsc/debug.h> -#include <openbsc/gsm_data.h> -#include <openbsc/abis_rsl.h> - -void tchf_pdch_ts_init(struct gsm_bts_trx_ts *ts) -{ - int rc; - - if (ts->trx->bts->gprs.mode == BTS_GPRS_NONE) { - LOGP(DRSL, LOGL_NOTICE, "%s: GPRS mode is 'none':" - " not activating PDCH.\n", - gsm_ts_and_pchan_name(ts)); - return; - } - - LOGP(DRSL, LOGL_DEBUG, "%s: trying to PDCH ACT\n", - gsm_ts_and_pchan_name(ts)); - - rc = rsl_ipacc_pdch_activate(ts, 1); - if (rc != 0) - LOGP(DRSL, LOGL_ERROR, "%s %s: PDCH ACT failed\n", - gsm_ts_name(ts), gsm_pchan_name(ts->pchan)); -} - -void tchf_tchh_pdch_ts_init(struct gsm_bts_trx_ts *ts) -{ - if (ts->trx->bts->gprs.mode == BTS_GPRS_NONE) { - LOGP(DRSL, LOGL_NOTICE, "%s: GPRS mode is 'none':" - " not activating PDCH.\n", - gsm_ts_and_pchan_name(ts)); - return; - } - - dyn_ts_switchover_start(ts, GSM_PCHAN_PDCH); -} - -void dyn_ts_init(struct gsm_bts_trx_ts *ts) -{ - /* Clear all TCH/F_PDCH flags */ - ts->flags &= ~(TS_F_PDCH_PENDING_MASK | TS_F_PDCH_ACTIVE); - - /* Clear TCH/F_TCH/H_PDCH state */ - ts->dyn.pchan_is = ts->dyn.pchan_want = GSM_PCHAN_NONE; - ts->dyn.pending_chan_activ = NULL; - - switch (ts->pchan) { - case GSM_PCHAN_TCH_F_PDCH: - tchf_pdch_ts_init(ts); - break; - case GSM_PCHAN_TCH_F_TCH_H_PDCH: - tchf_tchh_pdch_ts_init(ts); - break; - default: - break; - } -} diff --git a/openbsc/src/libbsc/bsc_init.c b/openbsc/src/libbsc/bsc_init.c deleted file mode 100644 index ec87a7bc7..000000000 --- a/openbsc/src/libbsc/bsc_init.c +++ /dev/null @@ -1,552 +0,0 @@ -/* A hackish minimal BSC (+MSC +HLR) implementation */ - -/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org> - * (C) 2009 by Holger Hans Peter Freyther <zecke@selfish.org> - * 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 <http://www.gnu.org/licenses/>. - * - */ - -#include <openbsc/gsm_data.h> -#include <osmocom/gsm/gsm_utils.h> -#include <openbsc/gsm_04_08.h> -#include <openbsc/abis_rsl.h> -#include <openbsc/abis_nm.h> -#include <openbsc/debug.h> -#include <openbsc/misdn.h> -#include <osmocom/vty/telnet_interface.h> -#include <osmocom/vty/ports.h> -#include <openbsc/system_information.h> -#include <openbsc/paging.h> -#include <openbsc/signal.h> -#include <openbsc/chan_alloc.h> -#include <osmocom/core/talloc.h> -#include <openbsc/ipaccess.h> -#include <osmocom/gsm/sysinfo.h> -#include <openbsc/e1_config.h> -#include <openbsc/common_bsc.h> -#include <openbsc/pcu_if.h> -#include <limits.h> - -/* global pointer to the gsm network data structure */ -extern struct gsm_network *bsc_gsmnet; - -/* Callback function for NACK on the OML NM */ -static int oml_msg_nack(struct nm_nack_signal_data *nack) -{ - if (nack->mt == NM_MT_GET_ATTR_NACK) { - LOGP(DNM, LOGL_ERROR, "BTS%u does not support Get Attributes " - "OML message.\n", nack->bts->nr); - return 0; - } - - if (nack->mt == NM_MT_SET_BTS_ATTR_NACK) - LOGP(DNM, LOGL_ERROR, "Failed to set BTS attributes. That is fatal. " - "Was the bts type and frequency properly specified?\n"); - else - LOGP(DNM, LOGL_ERROR, "Got %s NACK going to drop the OML links.\n", - abis_nm_nack_name(nack->mt)); - - if (!nack->bts) { - LOGP(DNM, LOGL_ERROR, "Unknown bts. Can not drop it.\n"); - return 0; - } - - if (is_ipaccess_bts(nack->bts)) - ipaccess_drop_oml(nack->bts); - - return 0; -} - -/* Callback function to be called every time we receive a signal from NM */ -static int nm_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct nm_nack_signal_data *nack; - - switch (signal) { - case S_NM_NACK: - nack = signal_data; - return oml_msg_nack(nack); - default: - break; - } - return 0; -} - -int bsc_shutdown_net(struct gsm_network *net) -{ - struct gsm_bts *bts; - - llist_for_each_entry(bts, &net->bts_list, list) { - LOGP(DNM, LOGL_NOTICE, "shutting down OML for BTS %u\n", bts->nr); - osmo_signal_dispatch(SS_L_GLOBAL, S_GLOBAL_BTS_CLOSE_OM, bts); - } - - return 0; -} - -static int rsl_si(struct gsm_bts_trx *trx, enum osmo_sysinfo_type i, int si_len) -{ - struct gsm_bts *bts = trx->bts; - int rc, j; - - DEBUGP(DRR, "SI%s: %s\n", get_value_string(osmo_sitype_strs, i), - osmo_hexdump(GSM_BTS_SI(bts, i), GSM_MACBLOCK_LEN)); - - switch (i) { - case SYSINFO_TYPE_5: - case SYSINFO_TYPE_5bis: - case SYSINFO_TYPE_5ter: - case SYSINFO_TYPE_6: - rc = rsl_sacch_filling(trx, osmo_sitype2rsl(i), - GSM_BTS_SI(bts, i), si_len); - break; - case SYSINFO_TYPE_2quater: - for (j = 0; j <= bts->si2q_count; j++) - rc = rsl_bcch_info(trx, i, (const uint8_t *)GSM_BTS_SI2Q(bts, j), GSM_MACBLOCK_LEN); - break; - default: - rc = rsl_bcch_info(trx, i, GSM_BTS_SI(bts, i), si_len); - break; - } - - return rc; -} - -/* set all system information types for a TRX */ -int gsm_bts_trx_set_system_infos(struct gsm_bts_trx *trx) -{ - int i, rc; - struct gsm_bts *bts = trx->bts; - uint8_t gen_si[_MAX_SYSINFO_TYPE], n_si = 0, n; - int si_len[_MAX_SYSINFO_TYPE]; - - bts->si_common.cell_sel_par.ms_txpwr_max_ccch = - ms_pwr_ctl_lvl(bts->band, bts->ms_max_power); - bts->si_common.cell_sel_par.neci = bts->network->neci; - - /* Zero, forget the state of the SIs */ - bts->si_valid = 0; - - /* First, we determine which of the SI messages we actually need */ - - if (trx == bts->c0) { - /* 1...4 are always present on a C0 TRX */ - gen_si[n_si++] = SYSINFO_TYPE_1; - gen_si[n_si++] = SYSINFO_TYPE_2; - gen_si[n_si++] = SYSINFO_TYPE_2bis; - gen_si[n_si++] = SYSINFO_TYPE_2ter; - gen_si[n_si++] = SYSINFO_TYPE_2quater; - gen_si[n_si++] = SYSINFO_TYPE_3; - gen_si[n_si++] = SYSINFO_TYPE_4; - - /* 13 is always present on a C0 TRX of a GPRS BTS */ - if (bts->gprs.mode != BTS_GPRS_NONE) - gen_si[n_si++] = SYSINFO_TYPE_13; - } - - /* 5 and 6 are always present on every TRX */ - gen_si[n_si++] = SYSINFO_TYPE_5; - gen_si[n_si++] = SYSINFO_TYPE_5bis; - gen_si[n_si++] = SYSINFO_TYPE_5ter; - gen_si[n_si++] = SYSINFO_TYPE_6; - - /* Second, we generate the selected SI via RSL */ - - for (n = 0; n < n_si; n++) { - i = gen_si[n]; - /* Only generate SI if this SI is not in "static" (user-defined) mode */ - if (!(bts->si_mode_static & (1 << i))) { - /* Set SI as being valid. gsm_generate_si() might unset - * it, if SI is not required. */ - bts->si_valid |= (1 << i); - rc = gsm_generate_si(bts, i); - if (rc < 0) - goto err_out; - si_len[i] = rc; - } else { - if (i == SYSINFO_TYPE_5 || i == SYSINFO_TYPE_5bis - || i == SYSINFO_TYPE_5ter) - si_len[i] = 18; - else if (i == SYSINFO_TYPE_6) - si_len[i] = 11; - else - si_len[i] = 23; - } - } - - /* Third, we send the selected SI via RSL */ - - for (n = 0; n < n_si; n++) { - i = gen_si[n]; - if (!GSM_BTS_HAS_SI(bts, i)) - continue; - rc = rsl_si(trx, i, si_len[i]); - if (rc < 0) - return rc; - } - - /* Make sure the PCU is aware (in case anything GPRS related has - * changed in SI */ - pcu_info_update(bts); - - return 0; -err_out: - LOGP(DRR, LOGL_ERROR, "Cannot generate SI%s for BTS %u: error <%s>, " - "most likely a problem with neighbor cell list generation\n", - get_value_string(osmo_sitype_strs, i), bts->nr, strerror(-rc)); - return rc; -} - -/* 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; -} - -/* Produce a MA as specified in 10.5.2.21 */ -static int generate_ma_for_ts(struct gsm_bts_trx_ts *ts) -{ - /* we have three bitvecs: the per-timeslot ARFCNs, the cell chan ARFCNs - * and the MA */ - struct bitvec *cell_chan = &ts->trx->bts->si_common.cell_alloc; - struct bitvec *ts_arfcn = &ts->hopping.arfcns; - struct bitvec *ma = &ts->hopping.ma; - unsigned int num_cell_arfcns, bitnum, n_chan; - int i; - - /* re-set the MA to all-zero */ - ma->cur_bit = 0; - ts->hopping.ma_len = 0; - memset(ma->data, 0, ma->data_len); - - if (!ts->hopping.enabled) - return 0; - - /* count the number of ARFCNs in the cell channel allocation */ - num_cell_arfcns = 0; - for (i = 0; i < 1024; i++) { - if (bitvec_get_bit_pos(cell_chan, i)) - num_cell_arfcns++; - } - - /* pad it to octet-aligned number of bits */ - ts->hopping.ma_len = num_cell_arfcns / 8; - if (num_cell_arfcns % 8) - ts->hopping.ma_len++; - - n_chan = 0; - for (i = 0; i < 1024; i++) { - if (!bitvec_get_bit_pos(cell_chan, i)) - continue; - /* set the corresponding bit in the MA */ - bitnum = (ts->hopping.ma_len * 8) - 1 - n_chan; - if (bitvec_get_bit_pos(ts_arfcn, i)) - bitvec_set_bit_pos(ma, bitnum, 1); - else - bitvec_set_bit_pos(ma, bitnum, 0); - n_chan++; - } - - /* ARFCN 0 is special: It is coded last in the bitmask */ - if (bitvec_get_bit_pos(cell_chan, 0)) { - n_chan++; - /* set the corresponding bit in the MA */ - bitnum = (ts->hopping.ma_len * 8) - 1 - n_chan; - if (bitvec_get_bit_pos(ts_arfcn, 0)) - bitvec_set_bit_pos(ma, bitnum, 1); - else - bitvec_set_bit_pos(ma, bitnum, 0); - } - - return 0; -} - -static void bootstrap_rsl(struct gsm_bts_trx *trx) -{ - unsigned int i; - - LOGP(DRSL, LOGL_NOTICE, "bootstrapping RSL for BTS/TRX (%u/%u) " - "on ARFCN %u using MCC=%u MNC=%u LAC=%u CID=%u BSIC=%u\n", - trx->bts->nr, trx->nr, trx->arfcn, bsc_gsmnet->country_code, - bsc_gsmnet->network_code, trx->bts->location_area_code, - trx->bts->cell_identity, trx->bts->bsic); - - if (trx->bts->type == GSM_BTS_TYPE_NOKIA_SITE) { - rsl_nokia_si_begin(trx); - } - - gsm_bts_trx_set_system_infos(trx); - - if (trx->bts->type == GSM_BTS_TYPE_NOKIA_SITE) { - /* channel unspecific, power reduction in 2 dB steps */ - rsl_bs_power_control(trx, 0xFF, trx->max_power_red / 2); - rsl_nokia_si_end(trx); - } - - for (i = 0; i < ARRAY_SIZE(trx->ts); i++) - generate_ma_for_ts(&trx->ts[i]); -} - -/* Callback function to be called every time we receive a signal from INPUT */ -static int inp_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct input_signal_data *isd = signal_data; - struct gsm_bts_trx *trx = isd->trx; - int ts_no, lchan_no; - /* N. B: we rely on attribute order when parsing response in abis_nm_rx_get_attr_resp() */ - const uint8_t bts_attr[] = { NM_ATT_MANUF_ID, NM_ATT_SW_CONFIG, }; - const uint8_t trx_attr[] = { NM_ATT_MANUF_STATE, NM_ATT_SW_CONFIG, }; - - /* we should not request more attributes than we're ready to handle */ - OSMO_ASSERT(sizeof(bts_attr) < MAX_BTS_ATTR); - OSMO_ASSERT(sizeof(trx_attr) < MAX_BTS_ATTR); - - if (subsys != SS_L_INPUT) - return -EINVAL; - - LOGP(DLMI, LOGL_DEBUG, "%s(): Input signal '%s' received\n", __func__, - get_value_string(e1inp_signal_names, signal)); - switch (signal) { - case S_L_INP_TEI_UP: - if (isd->link_type == E1INP_SIGN_OML) { - /* TODO: this is required for the Nokia BTS, hopping is configured - during OML, other MA is not set. */ - struct gsm_bts_trx *cur_trx; - /* was static in system_information.c */ - extern int generate_cell_chan_list(uint8_t *chan_list, struct gsm_bts *bts); - uint8_t ca[20]; - /* has to be called before generate_ma_for_ts to - set bts->si_common.cell_alloc */ - generate_cell_chan_list(ca, trx->bts); - - /* Request generic BTS-level attributes */ - abis_nm_get_attr(trx->bts, NM_OC_BTS, trx->bts->nr, trx->nr, 0xFF, bts_attr, sizeof(bts_attr)); - - llist_for_each_entry(cur_trx, &trx->bts->trx_list, list) { - int i; - /* Request TRX-level attributes */ - abis_nm_get_attr(cur_trx->bts, NM_OC_BASEB_TRANSC, cur_trx->bts->nr, cur_trx->nr, 0xFF, - trx_attr, sizeof(trx_attr)); - for (i = 0; i < ARRAY_SIZE(cur_trx->ts); i++) - generate_ma_for_ts(&cur_trx->ts[i]); - } - } - if (isd->link_type == E1INP_SIGN_RSL) - bootstrap_rsl(trx); - break; - case S_L_INP_TEI_DN: - LOGP(DLMI, LOGL_ERROR, "Lost some E1 TEI link: %d %p\n", isd->link_type, trx); - - if (isd->link_type == E1INP_SIGN_OML) - rate_ctr_inc(&trx->bts->network->bsc_ctrs->ctr[BSC_CTR_BTS_OML_FAIL]); - else if (isd->link_type == E1INP_SIGN_RSL) - rate_ctr_inc(&trx->bts->network->bsc_ctrs->ctr[BSC_CTR_BTS_RSL_FAIL]); - - /* - * free all allocated channels. change the nm_state so the - * trx and trx_ts becomes unusable and chan_alloc.c can not - * allocate from it. - */ - for (ts_no = 0; ts_no < ARRAY_SIZE(trx->ts); ++ts_no) { - struct gsm_bts_trx_ts *ts = &trx->ts[ts_no]; - - for (lchan_no = 0; lchan_no < ARRAY_SIZE(ts->lchan); ++lchan_no) { - if (ts->lchan[lchan_no].state != LCHAN_S_NONE) - lchan_free(&ts->lchan[lchan_no]); - lchan_reset(&ts->lchan[lchan_no]); - } - } - - gsm_bts_mo_reset(trx->bts); - - abis_nm_clear_queue(trx->bts); - break; - default: - break; - } - - return 0; -} - -static int bootstrap_bts(struct gsm_bts *bts) -{ - int i, n; - - if (!bts->model) - return -EFAULT; - - if (bts->model->start && !bts->model->started) { - int ret = bts->model->start(bts->network); - if (ret < 0) - return ret; - - bts->model->started = true; - } - - /* FIXME: What about secondary TRX of a BTS? What about a BTS that has TRX - * in different bands? Why is 'band' a parameter of the BTS and not of the TRX? */ - switch (bts->band) { - case GSM_BAND_1800: - if (bts->c0->arfcn < 512 || bts->c0->arfcn > 885) { - LOGP(DNM, LOGL_ERROR, "GSM1800 channel must be between 512-885.\n"); - return -EINVAL; - } - break; - case GSM_BAND_1900: - if (bts->c0->arfcn < 512 || bts->c0->arfcn > 810) { - LOGP(DNM, LOGL_ERROR, "GSM1900 channel must be between 512-810.\n"); - return -EINVAL; - } - break; - case GSM_BAND_900: - if ((bts->c0->arfcn > 124 && bts->c0->arfcn < 955) || - bts->c0->arfcn > 1023) { - LOGP(DNM, LOGL_ERROR, "GSM900 channel must be between 0-124, 955-1023.\n"); - return -EINVAL; - } - break; - case GSM_BAND_850: - if (bts->c0->arfcn < 128 || bts->c0->arfcn > 251) { - LOGP(DNM, LOGL_ERROR, "GSM850 channel must be between 128-251.\n"); - return -EINVAL; - } - break; - default: - LOGP(DNM, LOGL_ERROR, "Unsupported frequency band.\n"); - return -EINVAL; - } - - if (bts->network->auth_policy == GSM_AUTH_POLICY_ACCEPT_ALL && - !bts->si_common.rach_control.cell_bar) - LOGP(DNM, LOGL_ERROR, "\nWARNING: You are running an 'accept-all' " - "network on a BTS that is not barred. This " - "configuration is likely to interfere with production " - "GSM networks and should only be used in a RF " - "shielded environment such as a faraday cage!\n\n"); - - /* Control Channel Description is set from vty/config */ - - /* T3212 is set from vty/config */ - - /* Set ccch config by looking at ts config */ - for (n=0, i=0; i<8; i++) - n += bts->c0->ts[i].pchan == GSM_PCHAN_CCCH ? 1 : 0; - - switch (n) { - case 0: - bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_1_C; - /* Limit reserved block to 2 on combined channel according to - 3GPP TS 44.018 Table 10.5.2.11.1 */ - if (bts->si_common.chan_desc.bs_ag_blks_res > 2) { - LOGP(DNM, LOGL_NOTICE, "CCCH is combined with SDCCHs, " - "reducing BS-AG-BLKS-RES value %d -> 2\n", - bts->si_common.chan_desc.bs_ag_blks_res); - bts->si_common.chan_desc.bs_ag_blks_res = 2; - } - break; - case 1: - bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_1_NC; - break; - case 2: - bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_2_NC; - break; - case 3: - bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_3_NC; - break; - case 4: - bts->si_common.chan_desc.ccch_conf = RSL_BCCH_CCCH_CONF_4_NC; - break; - default: - LOGP(DNM, LOGL_ERROR, "Unsupported CCCH timeslot configuration\n"); - return -EINVAL; - } - - bts->si_common.cell_options.pwrc = 0; /* PWRC not set */ - - bts->si_common.cell_sel_par.acs = 0; - - bts->si_common.ncc_permitted = 0xff; - - /* Initialize the BTS state */ - gsm_bts_mo_reset(bts); - - return 0; -} - -int bsc_network_alloc(mncc_recv_cb_t mncc_recv) -{ - /* initialize our data structures */ - bsc_gsmnet = bsc_network_init(tall_bsc_ctx, 1, 1, mncc_recv); - if (!bsc_gsmnet) - return -ENOMEM; - - bsc_gsmnet->name_long = talloc_strdup(bsc_gsmnet, "OpenBSC"); - bsc_gsmnet->name_short = talloc_strdup(bsc_gsmnet, "OpenBSC"); - - return 0; -} - -int bsc_network_configure(const char *config_file) -{ - struct gsm_bts *bts; - int rc; - - rc = vty_read_config_file(config_file, NULL); - if (rc < 0) { - LOGP(DNM, LOGL_FATAL, "Failed to parse the config file: '%s'\n", config_file); - return rc; - } - - /* start telnet after reading config for vty_get_bind_addr() */ - rc = telnet_init_dynif(tall_bsc_ctx, bsc_gsmnet, vty_get_bind_addr(), - OSMO_VTY_PORT_NITB_BSC); - if (rc < 0) - return rc; - - osmo_signal_register_handler(SS_NM, nm_sig_cb, NULL); - osmo_signal_register_handler(SS_L_INPUT, inp_sig_cb, NULL); - - llist_for_each_entry(bts, &bsc_gsmnet->bts_list, list) { - rc = bootstrap_bts(bts); - if (rc < 0) { - LOGP(DNM, LOGL_FATAL, "Error bootstrapping BTS\n"); - return rc; - } - rc = e1_reconfig_bts(bts); - if (rc < 0) { - LOGP(DNM, LOGL_FATAL, "Error enabling E1 input driver\n"); - return rc; - } - } - - return 0; -} diff --git a/openbsc/src/libbsc/bsc_msc.c b/openbsc/src/libbsc/bsc_msc.c deleted file mode 100644 index 82a572dbe..000000000 --- a/openbsc/src/libbsc/bsc_msc.c +++ /dev/null @@ -1,320 +0,0 @@ -/* Routines to talk to the MSC using the IPA Protocol */ -/* - * (C) 2010 by Holger Hans Peter Freyther <zecke@selfish.org> - * (C) 2010 by On-Waves - * 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 <http://www.gnu.org/licenses/>. - * - */ - -#include <openbsc/bsc_msc.h> -#include <openbsc/debug.h> -#include <osmocom/abis/ipaccess.h> - -#include <osmocom/core/write_queue.h> -#include <osmocom/core/talloc.h> - -#include <osmocom/gsm/tlv.h> - -#include <arpa/inet.h> -#include <sys/socket.h> -#include <errno.h> -#include <fcntl.h> -#include <stdio.h> -#include <string.h> -#include <unistd.h> - -static void connection_loss(struct bsc_msc_connection *con) -{ - struct osmo_fd *fd; - - fd = &con->write_queue.bfd; - - if (con->pending_msg) { - LOGP(DMSC, LOGL_ERROR, - "MSC(%s) dropping incomplete message.\n", con->name); - msgb_free(con->pending_msg); - con->pending_msg = NULL; - } - - close(fd->fd); - fd->fd = -1; - fd->cb = osmo_wqueue_bfd_cb; - fd->when = 0; - - con->is_connected = 0; - con->first_contact = 0; - con->connection_loss(con); -} - -static void msc_con_timeout(void *_con) -{ - struct bsc_msc_connection *con = _con; - - LOGP(DMSC, LOGL_ERROR, - "MSC(%s) Connection timeout.\n", con->name); - bsc_msc_lost(con); -} - -/* called in the case of a non blocking connect */ -static int msc_connection_connect(struct osmo_fd *fd, unsigned int what) -{ - int rc; - int val; - struct bsc_msc_connection *con; - struct osmo_wqueue *queue; - - socklen_t len = sizeof(val); - - queue = container_of(fd, struct osmo_wqueue, bfd); - con = container_of(queue, struct bsc_msc_connection, write_queue); - - if ((what & BSC_FD_WRITE) == 0) { - LOGP(DMSC, LOGL_ERROR, - "MSC(%s) Callback but not writable.\n", con->name); - return -1; - } - - /* From here on we will either be connected or reconnect */ - osmo_timer_del(&con->timeout_timer); - - /* check the socket state */ - rc = getsockopt(fd->fd, SOL_SOCKET, SO_ERROR, &val, &len); - if (rc != 0) { - LOGP(DMSC, LOGL_ERROR, - "getsockopt for the MSC(%s) socket failed.\n", con->name); - goto error; - } - if (val != 0) { - LOGP(DMSC, LOGL_ERROR, - "Not connected to the MSC(%s): %d\n", - con->name, val); - goto error; - } - - - /* go to full operation */ - fd->cb = osmo_wqueue_bfd_cb; - fd->when = BSC_FD_READ | BSC_FD_EXCEPT; - - con->is_connected = 1; - LOGP(DMSC, LOGL_NOTICE, - "(Re)Connected to the MSC(%s).\n", con->name); - if (con->connected) - con->connected(con); - return 0; - -error: - osmo_fd_unregister(fd); - connection_loss(con); - return -1; -} -static void setnonblocking(struct osmo_fd *fd) -{ - int flags; - - flags = fcntl(fd->fd, F_GETFL); - if (flags < 0) { - perror("fcntl get failed"); - close(fd->fd); - fd->fd = -1; - return; - } - - flags |= O_NONBLOCK; - flags = fcntl(fd->fd, F_SETFL, flags); - if (flags < 0) { - perror("fcntl get failed"); - close(fd->fd); - fd->fd = -1; - return; - } -} - -int bsc_msc_connect(struct bsc_msc_connection *con) -{ - struct bsc_msc_dest *dest; - struct osmo_fd *fd; - struct sockaddr_in sin; - int on = 1, ret; - - if (llist_empty(con->dests)) { - LOGP(DMSC, LOGL_ERROR, - "No MSC(%s) connections configured.\n", - con->name); - connection_loss(con); - return -1; - } - - /* TODO: Why are we not using the libosmocore soecket - * abstraction, or libosmo-netif? */ - - /* move to the next connection */ - dest = (struct bsc_msc_dest *) con->dests->next; - llist_del(&dest->list); - llist_add_tail(&dest->list, con->dests); - - LOGP(DMSC, LOGL_NOTICE, - "Attempting to connect MSC(%s) at %s:%d\n", - con->name, dest->ip, dest->port); - - con->is_connected = 0; - - msgb_free(con->pending_msg); - con->pending_msg = NULL; - - fd = &con->write_queue.bfd; - fd->fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - fd->priv_nr = 1; - - if (fd->fd < 0) { - perror("Creating TCP socket failed"); - return fd->fd; - } - - /* make it non blocking */ - setnonblocking(fd); - - /* set the socket priority */ - ret = setsockopt(fd->fd, IPPROTO_IP, IP_TOS, - &dest->dscp, sizeof(dest->dscp)); - if (ret != 0) - LOGP(DMSC, LOGL_ERROR, - "Failed to set DSCP to %d on MSC(%s). %s\n", - dest->dscp, con->name, strerror(errno)); - - memset(&sin, 0, sizeof(sin)); - sin.sin_family = AF_INET; - sin.sin_port = htons(dest->port); - inet_aton(dest->ip, &sin.sin_addr); - - ret = setsockopt(fd->fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)); - if (ret != 0) - LOGP(DMSC, LOGL_ERROR, - "Failed to set SO_REUSEADDR socket option\n"); - ret = connect(fd->fd, (struct sockaddr *) &sin, sizeof(sin)); - - if (ret == -1 && errno == EINPROGRESS) { - LOGP(DMSC, LOGL_ERROR, - "MSC(%s) Connection in progress\n", con->name); - fd->when = BSC_FD_WRITE; - fd->cb = msc_connection_connect; - osmo_timer_setup(&con->timeout_timer, msc_con_timeout, con); - osmo_timer_schedule(&con->timeout_timer, 20, 0); - } else if (ret < 0) { - perror("Connection failed"); - connection_loss(con); - return ret; - } else { - fd->when = BSC_FD_READ | BSC_FD_EXCEPT; - fd->cb = osmo_wqueue_bfd_cb; - con->is_connected = 1; - if (con->connected) - con->connected(con); - } - - ret = osmo_fd_register(fd); - if (ret < 0) { - perror("Registering the fd failed"); - close(fd->fd); - return ret; - } - - return ret; -} - -struct bsc_msc_connection *bsc_msc_create(void *ctx, struct llist_head *dests) -{ - struct bsc_msc_connection *con; - - con = talloc_zero(NULL, struct bsc_msc_connection); - if (!con) { - LOGP(DMSC, LOGL_FATAL, "Failed to create the MSC connection.\n"); - return NULL; - } - - con->dests = dests; - con->write_queue.bfd.fd = -1; - con->name = ""; - osmo_wqueue_init(&con->write_queue, 100); - return con; -} - -void bsc_msc_lost(struct bsc_msc_connection *con) -{ - osmo_wqueue_clear(&con->write_queue); - osmo_timer_del(&con->timeout_timer); - osmo_timer_del(&con->reconnect_timer); - - if (con->write_queue.bfd.fd >= 0) - osmo_fd_unregister(&con->write_queue.bfd); - connection_loss(con); -} - -static void reconnect_msc(void *_msc) -{ - struct bsc_msc_connection *con = _msc; - - LOGP(DMSC, LOGL_NOTICE, - "Attempting to reconnect to the MSC(%s).\n", con->name); - bsc_msc_connect(con); -} - -void bsc_msc_schedule_connect(struct bsc_msc_connection *con) -{ - LOGP(DMSC, LOGL_NOTICE, - "Attempting to reconnect to the MSC(%s)\n", con->name); - osmo_timer_setup(&con->reconnect_timer, reconnect_msc, con); - osmo_timer_schedule(&con->reconnect_timer, 5, 0); -} - -struct msgb *bsc_msc_id_get_resp(int fixed, const char *token, const uint8_t *res, int len) -{ - struct msgb *msg; - - if (!token) { - LOGP(DMSC, LOGL_ERROR, "No token specified.\n"); - return NULL; - } - - msg = msgb_alloc_headroom(4096, 128, "id resp"); - if (!msg) { - LOGP(DMSC, LOGL_ERROR, "Failed to create the message.\n"); - return NULL; - } - - /* - * The situation is bizarre. The encoding doesn't follow the - * TLV structure. It is more like a LV and old versions had - * it wrong but we want new versions to old servers so we - * introduce the quirk here. - */ - msg->l2h = msgb_v_put(msg, IPAC_MSGT_ID_RESP); - if (fixed) { - msgb_put_u8(msg, 0); - msgb_put_u8(msg, strlen(token) + 2); - msgb_tv_fixed_put(msg, IPAC_IDTAG_UNITNAME, strlen(token) + 1, (uint8_t *) token); - if (len > 0) { - msgb_put_u8(msg, 0); - msgb_put_u8(msg, len + 1); - msgb_tv_fixed_put(msg, 0x24, len, res); - } - } else { - msgb_l16tv_put(msg, strlen(token) + 1, - IPAC_IDTAG_UNITNAME, (uint8_t *) token); - } - - return msg; -} diff --git a/openbsc/src/libbsc/bsc_rf_ctrl.c b/openbsc/src/libbsc/bsc_rf_ctrl.c deleted file mode 100644 index b7b6fc819..000000000 --- a/openbsc/src/libbsc/bsc_rf_ctrl.c +++ /dev/null @@ -1,534 +0,0 @@ -/* RF Ctl handling socket */ - -/* (C) 2010 by Harald Welte <laforge@gnumonks.org> - * (C) 2010-2014 by Holger Hans Peter Freyther <zecke@selfish.org> - * (C) 2010-2014 by On-Waves - * 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 <http://www.gnu.org/licenses/>. - * - */ - -#include <openbsc/osmo_bsc_rf.h> -#include <openbsc/debug.h> -#include <openbsc/gsm_data.h> -#include <openbsc/signal.h> -#include <openbsc/bsc_msc_data.h> -#include <openbsc/ipaccess.h> - -#include <osmocom/core/talloc.h> -#include <osmocom/core/utils.h> -#include <osmocom/gsm/protocol/gsm_12_21.h> - -#include <sys/socket.h> -#include <sys/un.h> - -#include <errno.h> -#include <unistd.h> - -#define RF_CMD_QUERY '?' -#define RF_CMD_OFF '0' -#define RF_CMD_ON '1' -#define RF_CMD_D_OFF 'd' -#define RF_CMD_ON_G 'g' - -static const struct value_string opstate_names[] = { - { OSMO_BSC_RF_OPSTATE_INOPERATIONAL, "inoperational" }, - { OSMO_BSC_RF_OPSTATE_OPERATIONAL, "operational" }, - { 0, NULL } -}; - -static const struct value_string adminstate_names[] = { - { OSMO_BSC_RF_ADMINSTATE_UNLOCKED, "unlocked" }, - { OSMO_BSC_RF_ADMINSTATE_LOCKED, "locked" }, - { 0, NULL } -}; - -static const struct value_string policy_names[] = { - { OSMO_BSC_RF_POLICY_OFF, "off" }, - { OSMO_BSC_RF_POLICY_ON, "on" }, - { OSMO_BSC_RF_POLICY_GRACE, "grace" }, - { OSMO_BSC_RF_POLICY_UNKNOWN, "unknown" }, - { 0, NULL } -}; - -const char *osmo_bsc_rf_get_opstate_name(enum osmo_bsc_rf_opstate opstate) -{ - return get_value_string(opstate_names, opstate); -} - -const char *osmo_bsc_rf_get_adminstate_name(enum osmo_bsc_rf_adminstate adminstate) -{ - return get_value_string(adminstate_names, adminstate); -} - -const char *osmo_bsc_rf_get_policy_name(enum osmo_bsc_rf_policy policy) -{ - return get_value_string(policy_names, policy); -} - -enum osmo_bsc_rf_opstate osmo_bsc_rf_get_opstate_by_bts(struct gsm_bts *bts) -{ - struct gsm_bts_trx *trx; - - llist_for_each_entry(trx, &bts->trx_list, list) { - if (trx->mo.nm_state.operational == NM_OPSTATE_ENABLED) - return OSMO_BSC_RF_OPSTATE_OPERATIONAL; - } - - /* No trx were active, so this bts is disabled */ - return OSMO_BSC_RF_OPSTATE_INOPERATIONAL; -} - -enum osmo_bsc_rf_adminstate osmo_bsc_rf_get_adminstate_by_bts(struct gsm_bts *bts) -{ - struct gsm_bts_trx *trx; - - llist_for_each_entry(trx, &bts->trx_list, list) { - if (trx->mo.nm_state.administrative == NM_STATE_UNLOCKED) - return OSMO_BSC_RF_ADMINSTATE_UNLOCKED; - } - - /* All trx administrative states were locked */ - return OSMO_BSC_RF_ADMINSTATE_LOCKED; -} - -enum osmo_bsc_rf_policy osmo_bsc_rf_get_policy_by_bts(struct gsm_bts *bts) -{ - struct osmo_bsc_data *bsc_data = bts->network->bsc_data; - - if (!bsc_data) - return OSMO_BSC_RF_POLICY_UNKNOWN; - - switch (bsc_data->rf_ctrl->policy) { - case S_RF_ON: - return OSMO_BSC_RF_POLICY_ON; - case S_RF_OFF: - return OSMO_BSC_RF_POLICY_OFF; - case S_RF_GRACE: - return OSMO_BSC_RF_POLICY_GRACE; - default: - return OSMO_BSC_RF_POLICY_UNKNOWN; - } -} - -static int lock_each_trx(struct gsm_network *net, int lock) -{ - struct gsm_bts *bts; - - llist_for_each_entry(bts, &net->bts_list, list) { - struct gsm_bts_trx *trx; - - /* Exclude the BTS from the global lock */ - if (bts->excl_from_rf_lock) { - LOGP(DLINP, LOGL_DEBUG, - "Excluding BTS(%d) from trx lock.\n", bts->nr); - continue; - } - - llist_for_each_entry(trx, &bts->trx_list, list) { - gsm_trx_lock_rf(trx, lock); - } - } - - return 0; -} - -static void send_resp(struct osmo_bsc_rf_conn *conn, char send) -{ - struct msgb *msg; - - msg = msgb_alloc(10, "RF Query"); - if (!msg) { - LOGP(DLINP, LOGL_ERROR, "Failed to allocate response msg.\n"); - return; - } - - msg->l2h = msgb_put(msg, 1); - msg->l2h[0] = send; - - if (osmo_wqueue_enqueue(&conn->queue, msg) != 0) { - LOGP(DLINP, LOGL_ERROR, "Failed to enqueue the answer.\n"); - msgb_free(msg); - return; - } - - return; -} - - -/* - * Send a - * 'g' when we are in grace mode - * '1' when one TRX is online, - * '0' otherwise - */ -static void handle_query(struct osmo_bsc_rf_conn *conn) -{ - struct gsm_bts *bts; - char send = RF_CMD_OFF; - - if (conn->rf->policy == S_RF_GRACE) - return send_resp(conn, RF_CMD_ON_G); - - llist_for_each_entry(bts, &conn->rf->gsm_network->bts_list, list) { - struct gsm_bts_trx *trx; - - /* Exclude the BTS from the global lock */ - if (bts->excl_from_rf_lock) { - LOGP(DLINP, LOGL_DEBUG, - "Excluding BTS(%d) from query.\n", bts->nr); - continue; - } - llist_for_each_entry(trx, &bts->trx_list, list) { - if (trx->mo.nm_state.availability == NM_AVSTATE_OK && - trx->mo.nm_state.operational != NM_OPSTATE_DISABLED) { - send = RF_CMD_ON; - break; - } - } - } - - send_resp(conn, send); -} - -static void rf_check_cb(void *_data) -{ - struct gsm_bts *bts; - struct osmo_bsc_rf *rf = _data; - - llist_for_each_entry(bts, &rf->gsm_network->bts_list, list) { - struct gsm_bts_trx *trx; - - /* don't bother to check a booting or missing BTS */ - if (!bts->oml_link || !is_ipaccess_bts(bts)) - continue; - - /* Exclude the BTS from the global lock */ - if (bts->excl_from_rf_lock) { - LOGP(DLINP, LOGL_DEBUG, - "Excluding BTS(%d) from query.\n", bts->nr); - continue; - } - - llist_for_each_entry(trx, &bts->trx_list, list) { - if (trx->mo.nm_state.availability != NM_AVSTATE_OK || - trx->mo.nm_state.operational != NM_OPSTATE_ENABLED || - trx->mo.nm_state.administrative != NM_STATE_UNLOCKED) { - LOGP(DNM, LOGL_ERROR, "RF activation failed. Starting again.\n"); - ipaccess_drop_oml(bts); - break; - } - } - } -} - -static void send_signal(struct osmo_bsc_rf *rf, int val) -{ - struct rf_signal_data sig; - sig.net = rf->gsm_network; - - rf->policy = val; - osmo_signal_dispatch(SS_RF, val, &sig); -} - -static int switch_rf_off(struct osmo_bsc_rf *rf) -{ - lock_each_trx(rf->gsm_network, 1); - send_signal(rf, S_RF_OFF); - - return 0; -} - -static void grace_timeout(void *_data) -{ - struct osmo_bsc_rf *rf = (struct osmo_bsc_rf *) _data; - - LOGP(DLINP, LOGL_NOTICE, "Grace timeout. Going to disable all BTS/TRX.\n"); - switch_rf_off(rf); -} - -static int enter_grace(struct osmo_bsc_rf *rf) -{ - if (osmo_timer_pending(&rf->grace_timeout)) { - LOGP(DLINP, LOGL_NOTICE, "RF Grace timer is pending. Not restarting.\n"); - return 0; - } - - osmo_timer_setup(&rf->grace_timeout, grace_timeout, rf); - osmo_timer_schedule(&rf->grace_timeout, rf->gsm_network->bsc_data->mid_call_timeout, 0); - LOGP(DLINP, LOGL_NOTICE, "Going to switch RF off in %d seconds.\n", - rf->gsm_network->bsc_data->mid_call_timeout); - - send_signal(rf, S_RF_GRACE); - return 0; -} - -static void rf_delay_cmd_cb(void *data) -{ - struct osmo_bsc_rf *rf = data; - - switch (rf->last_request) { - case RF_CMD_D_OFF: - rf->last_state_command = "RF Direct Off"; - osmo_timer_del(&rf->rf_check); - osmo_timer_del(&rf->grace_timeout); - switch_rf_off(rf); - break; - case RF_CMD_ON: - rf->last_state_command = "RF Direct On"; - osmo_timer_del(&rf->grace_timeout); - lock_each_trx(rf->gsm_network, 0); - send_signal(rf, S_RF_ON); - osmo_timer_schedule(&rf->rf_check, 3, 0); - break; - case RF_CMD_OFF: - rf->last_state_command = "RF Scheduled Off"; - osmo_timer_del(&rf->rf_check); - enter_grace(rf); - break; - } -} - -static int rf_read_cmd(struct osmo_fd *fd) -{ - struct osmo_bsc_rf_conn *conn = fd->data; - char buf[1]; - int rc; - - rc = read(fd->fd, buf, sizeof(buf)); - if (rc != sizeof(buf)) { - LOGP(DLINP, LOGL_ERROR, "Short read %d/%s\n", errno, strerror(errno)); - osmo_fd_unregister(fd); - close(fd->fd); - osmo_wqueue_clear(&conn->queue); - talloc_free(conn); - return -1; - } - - switch (buf[0]) { - case RF_CMD_QUERY: - handle_query(conn); - break; - case RF_CMD_D_OFF: - case RF_CMD_ON: - case RF_CMD_OFF: - osmo_bsc_rf_schedule_lock(conn->rf, buf[0]); - break; - default: - conn->rf->last_state_command = "Unknown command"; - LOGP(DLINP, LOGL_ERROR, "Unknown command %d\n", buf[0]); - break; - } - - return 0; -} - -static int rf_write_cmd(struct osmo_fd *fd, struct msgb *msg) -{ - int rc; - - rc = write(fd->fd, msg->data, msg->len); - if (rc != msg->len) { - LOGP(DLINP, LOGL_ERROR, "Short write %d/%s\n", errno, strerror(errno)); - return -1; - } - - return 0; -} - -static int rf_ctrl_accept(struct osmo_fd *bfd, unsigned int what) -{ - struct osmo_bsc_rf_conn *conn; - struct osmo_bsc_rf *rf = bfd->data; - struct sockaddr_un addr; - socklen_t len = sizeof(addr); - int fd; - - fd = accept(bfd->fd, (struct sockaddr *) &addr, &len); - if (fd < 0) { - LOGP(DLINP, LOGL_ERROR, "Failed to accept. errno: %d/%s\n", - errno, strerror(errno)); - return -1; - } - - conn = talloc_zero(rf, struct osmo_bsc_rf_conn); - if (!conn) { - LOGP(DLINP, LOGL_ERROR, "Failed to allocate mem.\n"); - close(fd); - return -1; - } - - osmo_wqueue_init(&conn->queue, 10); - conn->queue.bfd.data = conn; - conn->queue.bfd.fd = fd; - conn->queue.bfd.when = BSC_FD_READ | BSC_FD_WRITE; - conn->queue.read_cb = rf_read_cmd; - conn->queue.write_cb = rf_write_cmd; - conn->rf = rf; - - if (osmo_fd_register(&conn->queue.bfd) != 0) { - close(fd); - talloc_free(conn); - return -1; - } - - return 0; -} - -static void rf_auto_off_cb(void *_timer) -{ - struct osmo_bsc_rf *rf = _timer; - - LOGP(DLINP, LOGL_NOTICE, - "Going to switch off RF due lack of a MSC connection.\n"); - osmo_bsc_rf_schedule_lock(rf, RF_CMD_D_OFF); -} - -static int msc_signal_handler(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct gsm_network *net; - struct msc_signal_data *msc; - struct osmo_bsc_rf *rf; - - /* check if we want to handle this signal */ - if (subsys != SS_MSC) - return 0; - - net = handler_data; - msc = signal_data; - - /* check if we have the needed information */ - if (!net->bsc_data) - return 0; - if (msc->data->type != MSC_CON_TYPE_NORMAL) - return 0; - - rf = net->bsc_data->rf_ctrl; - switch (signal) { - case S_MSC_LOST: - if (net->bsc_data->auto_off_timeout < 0) - return 0; - if (osmo_timer_pending(&rf->auto_off_timer)) - return 0; - osmo_timer_schedule(&rf->auto_off_timer, - net->bsc_data->auto_off_timeout, 0); - break; - case S_MSC_CONNECTED: - osmo_timer_del(&rf->auto_off_timer); - break; - } - - return 0; -} - -static int rf_create_socket(struct osmo_bsc_rf *rf, const char *path) -{ - unsigned int namelen; - struct sockaddr_un local; - struct osmo_fd *bfd; - int rc; - - bfd = &rf->listen; - bfd->fd = socket(AF_UNIX, SOCK_STREAM, 0); - if (bfd->fd < 0) { - LOGP(DLINP, LOGL_ERROR, "Can not create socket. %d/%s\n", - errno, strerror(errno)); - return -1; - } - - local.sun_family = AF_UNIX; - osmo_strlcpy(local.sun_path, path, sizeof(local.sun_path)); - unlink(local.sun_path); - - /* we use the same magic that X11 uses in Xtranssock.c for - * calculating the proper length of the sockaddr */ -#if defined(BSD44SOCKETS) || defined(__UNIXWARE__) - local.sun_len = strlen(local.sun_path); -#endif -#if defined(BSD44SOCKETS) || defined(SUN_LEN) - namelen = SUN_LEN(&local); -#else - namelen = strlen(local.sun_path) + - offsetof(struct sockaddr_un, sun_path); -#endif - - rc = bind(bfd->fd, (struct sockaddr *) &local, namelen); - if (rc != 0) { - LOGP(DLINP, LOGL_ERROR, "Failed to bind '%s' errno: %d/%s\n", - local.sun_path, errno, strerror(errno)); - close(bfd->fd); - return -1; - } - - if (listen(bfd->fd, 0) != 0) { - LOGP(DLINP, LOGL_ERROR, "Failed to listen: %d/%s\n", errno, strerror(errno)); - close(bfd->fd); - return -1; - } - - bfd->when = BSC_FD_READ; - bfd->cb = rf_ctrl_accept; - bfd->data = rf; - - if (osmo_fd_register(bfd) != 0) { - LOGP(DLINP, LOGL_ERROR, "Failed to register bfd.\n"); - close(bfd->fd); - return -1; - } - - return 0; -} - -struct osmo_bsc_rf *osmo_bsc_rf_create(const char *path, struct gsm_network *net) -{ - struct osmo_bsc_rf *rf; - - rf = talloc_zero(NULL, struct osmo_bsc_rf); - if (!rf) { - LOGP(DLINP, LOGL_ERROR, "Failed to create osmo_bsc_rf.\n"); - return NULL; - } - - if (path && rf_create_socket(rf, path) != 0) { - talloc_free(rf); - return NULL; - } - - rf->gsm_network = net; - rf->policy = S_RF_ON; - rf->last_state_command = ""; - rf->last_rf_lock_ctrl_command = talloc_strdup(rf, ""); - - /* check the rf state */ - osmo_timer_setup(&rf->rf_check, rf_check_cb, rf); - - /* delay cmd handling */ - osmo_timer_setup(&rf->delay_cmd, rf_delay_cmd_cb, rf); - - osmo_timer_setup(&rf->auto_off_timer, rf_auto_off_cb, rf); - - /* listen to RF signals */ - osmo_signal_register_handler(SS_MSC, msc_signal_handler, net); - - return rf; -} - -void osmo_bsc_rf_schedule_lock(struct osmo_bsc_rf *rf, char cmd) -{ - rf->last_request = cmd; - if (!osmo_timer_pending(&rf->delay_cmd)) - osmo_timer_schedule(&rf->delay_cmd, 1, 0); -} diff --git a/openbsc/src/libbsc/bsc_rll.c b/openbsc/src/libbsc/bsc_rll.c deleted file mode 100644 index bb488da15..000000000 --- a/openbsc/src/libbsc/bsc_rll.c +++ /dev/null @@ -1,139 +0,0 @@ -/* GSM BSC Radio Link Layer API - * 3GPP TS 08.58 version 8.6.0 Release 1999 / ETSI TS 100 596 V8.6.0 */ - -/* (C) 2009 by Harald Welte <laforge@gnumonks.org> - * - * 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 <http://www.gnu.org/licenses/>. - * - */ - -#include <errno.h> - -#include <openbsc/debug.h> -#include <osmocom/core/talloc.h> -#include <osmocom/core/timer.h> -#include <osmocom/core/linuxlist.h> -#include <openbsc/bsc_rll.h> -#include <openbsc/gsm_data.h> -#include <openbsc/chan_alloc.h> -#include <openbsc/abis_rsl.h> -#include <openbsc/signal.h> - -struct bsc_rll_req { - struct llist_head list; - struct osmo_timer_list timer; - - struct gsm_lchan *lchan; - uint8_t link_id; - - void (*cb)(struct gsm_lchan *lchan, uint8_t link_id, - void *data, enum bsc_rllr_ind); - void *data; -}; - -/* we only compare C1, C2 and SAPI */ -#define LINKID_MASK 0xC7 - -static LLIST_HEAD(bsc_rll_reqs); - -static void complete_rllr(struct bsc_rll_req *rllr, enum bsc_rllr_ind type) -{ - llist_del(&rllr->list); - rllr->cb(rllr->lchan, rllr->link_id, rllr->data, type); - talloc_free(rllr); -} - -static void timer_cb(void *_rllr) -{ - struct bsc_rll_req *rllr = _rllr; - - complete_rllr(rllr, BSC_RLLR_IND_TIMEOUT); -} - -/* establish a RLL connection with given SAPI / priority */ -int rll_establish(struct gsm_lchan *lchan, uint8_t sapi, - void (*cb)(struct gsm_lchan *, uint8_t, void *, - enum bsc_rllr_ind), - void *data) -{ - struct bsc_rll_req *rllr = talloc_zero(tall_bsc_ctx, struct bsc_rll_req); - uint8_t link_id; - if (!rllr) - return -ENOMEM; - - link_id = sapi; - - /* If we are a TCH and not in signalling mode, we need to - * indicate that the new RLL connection is to be made on the SACCH */ - if ((lchan->type == GSM_LCHAN_TCH_F || - lchan->type == GSM_LCHAN_TCH_H) && sapi != 0) - link_id |= 0x40; - - rllr->lchan = lchan; - rllr->link_id = link_id; - rllr->cb = cb; - rllr->data = data; - - llist_add(&rllr->list, &bsc_rll_reqs); - - osmo_timer_setup(&rllr->timer, timer_cb, rllr); - osmo_timer_schedule(&rllr->timer, 7, 0); - - /* send the RSL RLL ESTablish REQuest */ - return rsl_establish_request(rllr->lchan, rllr->link_id); -} - -/* Called from RSL code in case we have received an indication regarding - * any RLL link */ -void rll_indication(struct gsm_lchan *lchan, uint8_t link_id, uint8_t type) -{ - struct bsc_rll_req *rllr, *rllr2; - - llist_for_each_entry_safe(rllr, rllr2, &bsc_rll_reqs, list) { - if (rllr->lchan == lchan && - (rllr->link_id & LINKID_MASK) == (link_id & LINKID_MASK)) { - osmo_timer_del(&rllr->timer); - complete_rllr(rllr, type); - return; - } - } -} - -static int rll_lchan_signal(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct challoc_signal_data *challoc; - struct bsc_rll_req *rllr, *rllr2; - - if (subsys != SS_CHALLOC || signal != S_CHALLOC_FREED) - return 0; - - challoc = (struct challoc_signal_data *) signal_data; - - llist_for_each_entry_safe(rllr, rllr2, &bsc_rll_reqs, list) { - if (rllr->lchan == challoc->lchan) { - osmo_timer_del(&rllr->timer); - complete_rllr(rllr, BSC_RLLR_IND_ERR_IND); - } - } - - return 0; -} - -static __attribute__((constructor)) void on_dso_load_rll(void) -{ - osmo_signal_register_handler(SS_CHALLOC, rll_lchan_signal, NULL); -} diff --git a/openbsc/src/libbsc/bsc_subscriber.c b/openbsc/src/libbsc/bsc_subscriber.c deleted file mode 100644 index 73e61e801..000000000 --- a/openbsc/src/libbsc/bsc_subscriber.c +++ /dev/null @@ -1,168 +0,0 @@ -/* GSM subscriber details for use in BSC land */ - -/* - * (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> - * - * Author: Neels Hofmeyr <nhofmeyr@sysmocom.de> - * - * 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 <http://www.gnu.org/licenses/>. - * - */ - -#include <talloc.h> -#include <string.h> -#include <limits.h> - -#include <osmocom/gsm/gsm48.h> -#include <osmocom/core/logging.h> - -#include <openbsc/bsc_subscriber.h> -#include <openbsc/debug.h> - -static struct bsc_subscr *bsc_subscr_alloc(struct llist_head *list) -{ - struct bsc_subscr *bsub; - - bsub = talloc_zero(list, struct bsc_subscr); - if (!bsub) - return NULL; - - llist_add_tail(&bsub->entry, list); - bsub->use_count = 1; - - return bsub; -} - -struct bsc_subscr *bsc_subscr_find_by_imsi(struct llist_head *list, - const char *imsi) -{ - struct bsc_subscr *bsub; - - if (!imsi || !*imsi) - return NULL; - - llist_for_each_entry(bsub, list, entry) { - if (!strcmp(bsub->imsi, imsi)) - return bsc_subscr_get(bsub); - } - return NULL; -} - -struct bsc_subscr *bsc_subscr_find_by_tmsi(struct llist_head *list, - uint32_t tmsi) -{ - struct bsc_subscr *bsub; - - if (tmsi == GSM_RESERVED_TMSI) - return NULL; - - llist_for_each_entry(bsub, list, entry) { - if (bsub->tmsi == tmsi) - return bsc_subscr_get(bsub); - } - return NULL; -} - -void bsc_subscr_set_imsi(struct bsc_subscr *bsub, const char *imsi) -{ - if (!bsub) - return; - osmo_strlcpy(bsub->imsi, imsi, sizeof(bsub->imsi)); -} - -struct bsc_subscr *bsc_subscr_find_or_create_by_imsi(struct llist_head *list, - const char *imsi) -{ - struct bsc_subscr *bsub; - bsub = bsc_subscr_find_by_imsi(list, imsi); - if (bsub) - return bsub; - bsub = bsc_subscr_alloc(list); - bsc_subscr_set_imsi(bsub, imsi); - return bsub; -} - -struct bsc_subscr *bsc_subscr_find_or_create_by_tmsi(struct llist_head *list, - uint32_t tmsi) -{ - struct bsc_subscr *bsub; - bsub = bsc_subscr_find_by_tmsi(list, tmsi); - if (bsub) - return bsub; - bsub = bsc_subscr_alloc(list); - bsub->tmsi = tmsi; - return bsub; -} - -const char *bsc_subscr_name(struct bsc_subscr *bsub) -{ - static char buf[32]; - if (!bsub) - return "unknown"; - if (bsub->imsi[0]) - snprintf(buf, sizeof(buf), "IMSI:%s", bsub->imsi); - else - snprintf(buf, sizeof(buf), "TMSI:0x%08x", bsub->tmsi); - return buf; -} - -static void bsc_subscr_free(struct bsc_subscr *bsub) -{ - llist_del(&bsub->entry); - talloc_free(bsub); -} - -struct bsc_subscr *_bsc_subscr_get(struct bsc_subscr *bsub, - const char *file, int line) -{ - OSMO_ASSERT(bsub->use_count < INT_MAX); - bsub->use_count++; - LOGPSRC(DREF, LOGL_DEBUG, file, line, - "BSC subscr %s usage increases to: %d\n", - bsc_subscr_name(bsub), bsub->use_count); - return bsub; -} - -struct bsc_subscr *_bsc_subscr_put(struct bsc_subscr *bsub, - const char *file, int line) -{ - bsub->use_count--; - LOGPSRC(DREF, bsub->use_count >= 0? LOGL_DEBUG : LOGL_ERROR, - file, line, - "BSC subscr %s usage decreases to: %d\n", - bsc_subscr_name(bsub), bsub->use_count); - if (bsub->use_count <= 0) - bsc_subscr_free(bsub); - return NULL; -} - -void log_set_filter_bsc_subscr(struct log_target *target, - struct bsc_subscr *bsc_subscr) -{ - struct bsc_subscr **fsub = (void*)&target->filter_data[LOG_FLT_BSC_SUBSCR]; - - /* free the old data */ - if (*fsub) { - bsc_subscr_put(*fsub); - *fsub = NULL; - } - - if (bsc_subscr) { - target->filter_map |= (1 << LOG_FLT_BSC_SUBSCR); - *fsub = bsc_subscr_get(bsc_subscr); - } else - target->filter_map &= ~(1 << LOG_FLT_BSC_SUBSCR); -} diff --git a/openbsc/src/libbsc/bsc_vty.c b/openbsc/src/libbsc/bsc_vty.c deleted file mode 100644 index 9fc289503..000000000 --- a/openbsc/src/libbsc/bsc_vty.c +++ /dev/null @@ -1,4304 +0,0 @@ -/* OpenBSC interface to quagga VTY */ -/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org> - * 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 <http://www.gnu.org/licenses/>. - * - */ - -#include <stdlib.h> -#include <stdbool.h> -#include <unistd.h> - -#include <osmocom/vty/command.h> -#include <osmocom/vty/buffer.h> -#include <osmocom/vty/vty.h> -#include <osmocom/vty/logging.h> -#include <osmocom/vty/stats.h> -#include <osmocom/vty/telnet_interface.h> -#include <osmocom/vty/misc.h> -#include <osmocom/gsm/protocol/gsm_04_08.h> -#include <osmocom/gsm/gsm0502.h> - -#include <arpa/inet.h> - -#include <osmocom/core/linuxlist.h> -#include <openbsc/gsm_data.h> -#include <osmocom/abis/e1_input.h> -#include <openbsc/abis_nm.h> -#include <openbsc/abis_om2000.h> -#include <osmocom/core/utils.h> -#include <osmocom/gsm/gsm_utils.h> -#include <osmocom/gsm/abis_nm.h> -#include <openbsc/chan_alloc.h> -#include <openbsc/meas_rep.h> -#include <openbsc/db.h> -#include <openbsc/vty.h> -#include <osmocom/gprs/gprs_ns.h> -#include <openbsc/system_information.h> -#include <openbsc/debug.h> -#include <openbsc/paging.h> -#include <openbsc/ipaccess.h> -#include <openbsc/abis_rsl.h> -#include <openbsc/bsc_msc_data.h> -#include <openbsc/osmo_bsc_rf.h> -#include <openbsc/pcu_if.h> - -#include <openbsc/common_cs.h> - -#include <inttypes.h> - -#include "../../bscconfig.h" - - -#define LCHAN_NR_STR "Logical Channel Number\n" - - -/* FIXME: this should go to some common file */ -static const struct value_string gprs_ns_timer_strs[] = { - { 0, "tns-block" }, - { 1, "tns-block-retries" }, - { 2, "tns-reset" }, - { 3, "tns-reset-retries" }, - { 4, "tns-test" }, - { 5, "tns-alive" }, - { 6, "tns-alive-retries" }, - { 0, NULL } -}; - -static const struct value_string gprs_bssgp_cfg_strs[] = { - { 0, "blocking-timer" }, - { 1, "blocking-retries" }, - { 2, "unblocking-retries" }, - { 3, "reset-timer" }, - { 4, "reset-retries" }, - { 5, "suspend-timer" }, - { 6, "suspend-retries" }, - { 7, "resume-timer" }, - { 8, "resume-retries" }, - { 9, "capability-update-timer" }, - { 10, "capability-update-retries" }, - { 0, NULL } -}; - -static const struct value_string bts_neigh_mode_strs[] = { - { NL_MODE_AUTOMATIC, "automatic" }, - { NL_MODE_MANUAL, "manual" }, - { NL_MODE_MANUAL_SI5SEP, "manual-si5" }, - { 0, NULL } -}; - -const struct value_string bts_loc_fix_names[] = { - { BTS_LOC_FIX_INVALID, "invalid" }, - { BTS_LOC_FIX_2D, "fix2d" }, - { BTS_LOC_FIX_3D, "fix3d" }, - { 0, NULL } -}; - -struct cmd_node bts_node = { - BTS_NODE, - "%s(config-net-bts)# ", - 1, -}; - -struct cmd_node trx_node = { - TRX_NODE, - "%s(config-net-bts-trx)# ", - 1, -}; - -struct cmd_node ts_node = { - TS_NODE, - "%s(config-net-bts-trx-ts)# ", - 1, -}; - -static int dummy_config_write(struct vty *v) -{ - return CMD_SUCCESS; -} - -static void net_dump_nmstate(struct vty *vty, struct gsm_nm_state *nms) -{ - vty_out(vty,"Oper '%s', Admin '%s', Avail '%s'%s", - abis_nm_opstate_name(nms->operational), - get_value_string(abis_nm_adm_state_names, nms->administrative), - abis_nm_avail_name(nms->availability), VTY_NEWLINE); -} - -static void dump_pchan_load_vty(struct vty *vty, char *prefix, - const struct pchan_load *pl) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(pl->pchan); i++) { - const struct load_counter *lc = &pl->pchan[i]; - unsigned int percent; - - if (lc->total == 0) - continue; - - percent = (lc->used * 100) / lc->total; - - vty_out(vty, "%s%20s: %3u%% (%u/%u)%s", prefix, - gsm_pchan_name(i), percent, lc->used, lc->total, - VTY_NEWLINE); - } -} - -static void net_dump_vty(struct vty *vty, struct gsm_network *net) -{ - struct pchan_load pl; - - vty_out(vty, "BSC is on Country Code %u, Network Code %u " - "and has %u BTS%s", net->country_code, net->network_code, - net->num_bts, VTY_NEWLINE); - vty_out(vty, " Long network name: '%s'%s", - net->name_long, VTY_NEWLINE); - vty_out(vty, " Short network name: '%s'%s", - net->name_short, VTY_NEWLINE); - vty_out(vty, " Authentication policy: %s", - gsm_auth_policy_name(net->auth_policy)); - if (net->authorized_reg_str) - vty_out(vty, ", authorized regexp: %s", net->authorized_reg_str); - vty_out(vty, "%s", VTY_NEWLINE); - vty_out(vty, " Auto create subscriber: %s%s", - net->auto_create_subscr ? "yes" : "no", VTY_NEWLINE); - vty_out(vty, " Auto assign extension: %s%s", - net->auto_assign_exten ? "yes" : "no", VTY_NEWLINE); - vty_out(vty, " Location updating reject cause: %u%s", - net->reject_cause, VTY_NEWLINE); - vty_out(vty, " Encryption: A5/%u%s", net->a5_encryption, - VTY_NEWLINE); - vty_out(vty, " NECI (TCH/H): %u%s", net->neci, - VTY_NEWLINE); - vty_out(vty, " Use TCH for Paging any: %d%s", net->pag_any_tch, - VTY_NEWLINE); - vty_out(vty, " RRLP Mode: %s%s", rrlp_mode_name(net->rrlp.mode), - VTY_NEWLINE); - vty_out(vty, " MM Info: %s%s", net->send_mm_info ? "On" : "Off", - VTY_NEWLINE); - vty_out(vty, " Handover: %s%s", net->handover.active ? "On" : "Off", - VTY_NEWLINE); - network_chan_load(&pl, net); - vty_out(vty, " Current Channel Load:%s", VTY_NEWLINE); - dump_pchan_load_vty(vty, " ", &pl); - - /* show rf */ - if (net->bsc_data) - vty_out(vty, " Last RF Command: %s%s", - net->bsc_data->rf_ctrl->last_state_command, - VTY_NEWLINE); - if (net->bsc_data) - vty_out(vty, " Last RF Lock Command: %s%s", - net->bsc_data->rf_ctrl->last_rf_lock_ctrl_command, - VTY_NEWLINE); -} - -DEFUN(bsc_show_net, bsc_show_net_cmd, "show network", - SHOW_STR "Display information about a GSM NETWORK\n") -{ - struct gsm_network *net = gsmnet_from_vty(vty); - net_dump_vty(vty, net); - - return CMD_SUCCESS; -} - -static 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); -} - -static void bts_dump_vty(struct vty *vty, struct gsm_bts *bts) -{ - struct pchan_load pl; - - vty_out(vty, "BTS %u is of %s type in band %s, has CI %u LAC %u, " - "BSIC %u (NCC=%u, BCC=%u) and %u TRX%s", - bts->nr, btstype2str(bts->type), gsm_band_name(bts->band), - bts->cell_identity, - bts->location_area_code, bts->bsic, - bts->bsic >> 3, bts->bsic & 7, - bts->num_trx, VTY_NEWLINE); - vty_out(vty, "Description: %s%s", - bts->description ? bts->description : "(null)", VTY_NEWLINE); - if (strnlen(bts->pcu_version, MAX_VERSION_LENGTH)) - vty_out(vty, "PCU version %s connected%s", bts->pcu_version, - VTY_NEWLINE); - vty_out(vty, "MS Max power: %u dBm%s", bts->ms_max_power, VTY_NEWLINE); - vty_out(vty, "Minimum Rx Level for Access: %i dBm%s", - rxlev2dbm(bts->si_common.cell_sel_par.rxlev_acc_min), - VTY_NEWLINE); - vty_out(vty, "Cell Reselection Hysteresis: %u dBm%s", - bts->si_common.cell_sel_par.cell_resel_hyst*2, VTY_NEWLINE); - vty_out(vty, "RACH TX-Integer: %u%s", bts->si_common.rach_control.tx_integer, - VTY_NEWLINE); - vty_out(vty, "RACH Max transmissions: %u%s", - rach_max_trans_raw2val(bts->si_common.rach_control.max_trans), - VTY_NEWLINE); - if (bts->si_common.rach_control.cell_bar) - vty_out(vty, " CELL IS BARRED%s", VTY_NEWLINE); - if (bts->dtxu != GSM48_DTX_SHALL_NOT_BE_USED) - vty_out(vty, "Uplink DTX: %s%s", - (bts->dtxu != GSM48_DTX_SHALL_BE_USED) ? - "enabled" : "forced", VTY_NEWLINE); - else - vty_out(vty, "Uplink DTX: not enabled%s", VTY_NEWLINE); - vty_out(vty, "Downlink DTX: %senabled%s", bts->dtxd ? "" : "not ", - VTY_NEWLINE); - vty_out(vty, "Channel Description Attachment: %s%s", - (bts->si_common.chan_desc.att) ? "yes" : "no", VTY_NEWLINE); - vty_out(vty, "Channel Description BS-PA-MFRMS: %u%s", - bts->si_common.chan_desc.bs_pa_mfrms + 2, VTY_NEWLINE); - vty_out(vty, "Channel Description BS-AG_BLKS-RES: %u%s", - bts->si_common.chan_desc.bs_ag_blks_res, VTY_NEWLINE); - vty_out(vty, "System Information present: 0x%08x, static: 0x%08x%s", - bts->si_valid, bts->si_mode_static, VTY_NEWLINE); - vty_out(vty, "Early Classmark Sending: %s%s", - bts->early_classmark_allowed ? "allowed" : "forbidden", - VTY_NEWLINE); - if (bts->pcu_sock_path) - vty_out(vty, "PCU Socket Path: %s%s", bts->pcu_sock_path, VTY_NEWLINE); - if (is_ipaccess_bts(bts)) - vty_out(vty, " Unit ID: %u/%u/0, OML Stream ID 0x%02x%s", - bts->ip_access.site_id, bts->ip_access.bts_id, - bts->oml_tei, VTY_NEWLINE); - else if (bts->type == GSM_BTS_TYPE_NOKIA_SITE) - vty_out(vty, " Skip Reset: %d%s", - bts->nokia.skip_reset, VTY_NEWLINE); - vty_out(vty, " NM State: "); - net_dump_nmstate(vty, &bts->mo.nm_state); - vty_out(vty, " Site Mgr NM State: "); - net_dump_nmstate(vty, &bts->site_mgr.mo.nm_state); - vty_out(vty, " GPRS NSE: "); - net_dump_nmstate(vty, &bts->gprs.nse.mo.nm_state); - vty_out(vty, " GPRS CELL: "); - net_dump_nmstate(vty, &bts->gprs.cell.mo.nm_state); - vty_out(vty, " GPRS NSVC0: "); - net_dump_nmstate(vty, &bts->gprs.nsvc[0].mo.nm_state); - vty_out(vty, " GPRS NSVC1: "); - net_dump_nmstate(vty, &bts->gprs.nsvc[1].mo.nm_state); - vty_out(vty, " Paging: %u pending requests, %u free slots%s", - paging_pending_requests_nr(bts), - bts->paging.available_slots, VTY_NEWLINE); - if (is_ipaccess_bts(bts)) { - vty_out(vty, " OML Link state: %s.%s", - bts->oml_link ? "connected" : "disconnected", VTY_NEWLINE); - } else { - vty_out(vty, " E1 Signalling Link:%s", VTY_NEWLINE); - e1isl_dump_vty(vty, bts->oml_link); - } - - /* FIXME: chan_desc */ - memset(&pl, 0, sizeof(pl)); - bts_chan_load(&pl, bts); - vty_out(vty, " Current Channel Load:%s", VTY_NEWLINE); - dump_pchan_load_vty(vty, " ", &pl); -} - -DEFUN(show_bts, show_bts_cmd, "show bts [<0-255>]", - SHOW_STR "Display information about a BTS\n" - "BTS number") -{ - struct gsm_network *net = gsmnet_from_vty(vty); - int bts_nr; - - if (argc != 0) { - /* use the BTS number that the user has specified */ - bts_nr = atoi(argv[0]); - if (bts_nr >= net->num_bts) { - vty_out(vty, "%% can't find BTS '%s'%s", argv[0], - VTY_NEWLINE); - return CMD_WARNING; - } - bts_dump_vty(vty, gsm_bts_num(net, bts_nr)); - return CMD_SUCCESS; - } - /* print all BTS's */ - for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) - bts_dump_vty(vty, gsm_bts_num(net, bts_nr)); - - return CMD_SUCCESS; -} - -/* utility functions */ -static 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 = 255; - else - e1_link->e1_ts_ss = atoi(ss); -} - -static 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 == 255) - 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 != GSM_PCHAN_NONE) - vty_out(vty, " phys_chan_config %s%s", - gsm_pchan_name(ts->pchan), 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); -} - -static 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); - if (trx->description) - vty_out(vty, " description %s%s", trx->description, - VTY_NEWLINE); - vty_out(vty, " rf_locked %u%s", - trx->mo.nm_state.administrative == NM_STATE_LOCKED ? 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, 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]); -} - -static void config_write_bts_gprs(struct vty *vty, struct gsm_bts *bts) -{ - unsigned int i; - vty_out(vty, " gprs mode %s%s", bts_gprs_mode_name(bts->gprs.mode), - VTY_NEWLINE); - if (bts->gprs.mode == BTS_GPRS_NONE) - return; - - vty_out(vty, " gprs 11bit_rach_support_for_egprs %u%s", - bts->gprs.supports_egprs_11bit_rach, VTY_NEWLINE); - - vty_out(vty, " gprs routing area %u%s", bts->gprs.rac, - VTY_NEWLINE); - vty_out(vty, " gprs network-control-order nc%u%s", - bts->gprs.net_ctrl_ord, VTY_NEWLINE); - if (!bts->gprs.ctrl_ack_type_use_block) - vty_out(vty, " gprs control-ack-type-rach%s", VTY_NEWLINE); - vty_out(vty, " gprs cell bvci %u%s", bts->gprs.cell.bvci, - VTY_NEWLINE); - for (i = 0; i < ARRAY_SIZE(bts->gprs.cell.timer); i++) - vty_out(vty, " gprs cell timer %s %u%s", - get_value_string(gprs_bssgp_cfg_strs, i), - bts->gprs.cell.timer[i], VTY_NEWLINE); - vty_out(vty, " gprs nsei %u%s", bts->gprs.nse.nsei, - VTY_NEWLINE); - for (i = 0; i < ARRAY_SIZE(bts->gprs.nse.timer); i++) - vty_out(vty, " gprs ns timer %s %u%s", - get_value_string(gprs_ns_timer_strs, i), - bts->gprs.nse.timer[i], VTY_NEWLINE); - for (i = 0; i < ARRAY_SIZE(bts->gprs.nsvc); i++) { - struct gsm_bts_gprs_nsvc *nsvc = - &bts->gprs.nsvc[i]; - struct in_addr ia; - - ia.s_addr = htonl(nsvc->remote_ip); - vty_out(vty, " gprs nsvc %u nsvci %u%s", i, - nsvc->nsvci, VTY_NEWLINE); - vty_out(vty, " gprs nsvc %u local udp port %u%s", i, - nsvc->local_port, VTY_NEWLINE); - vty_out(vty, " gprs nsvc %u remote udp port %u%s", i, - nsvc->remote_port, VTY_NEWLINE); - vty_out(vty, " gprs nsvc %u remote ip %s%s", i, - inet_ntoa(ia), VTY_NEWLINE); - } -} - -/* Write the model data if there is one */ -static void config_write_bts_model(struct vty *vty, struct gsm_bts *bts) -{ - struct gsm_bts_trx *trx; - - if (!bts->model) - return; - - if (bts->model->config_write_bts) - bts->model->config_write_bts(vty, bts); - - llist_for_each_entry(trx, &bts->trx_list, list) - config_write_trx_single(vty, trx); -} - -static void write_amr_modes(struct vty *vty, const char *prefix, - const char *name, struct amr_mode *modes, int num) -{ - int i; - - vty_out(vty, " %s threshold %s", prefix, name); - for (i = 0; i < num - 1; i++) - vty_out(vty, " %d", modes[i].threshold); - vty_out(vty, "%s", VTY_NEWLINE); - vty_out(vty, " %s hysteresis %s", prefix, name); - for (i = 0; i < num - 1; i++) - vty_out(vty, " %d", modes[i].hysteresis); - vty_out(vty, "%s", VTY_NEWLINE); -} - -static void config_write_bts_amr(struct vty *vty, struct gsm_bts *bts, - struct amr_multirate_conf *mr, int full) -{ - struct gsm48_multi_rate_conf *mr_conf; - const char *prefix = (full) ? "amr tch-f" : "amr tch-h"; - int i, num; - - if (!(mr->gsm48_ie[1])) - return; - - mr_conf = (struct gsm48_multi_rate_conf *) mr->gsm48_ie; - - num = 0; - vty_out(vty, " %s modes", prefix); - for (i = 0; i < ((full) ? 8 : 6); i++) { - if ((mr->gsm48_ie[1] & (1 << i))) { - vty_out(vty, " %d", i); - num++; - } - } - vty_out(vty, "%s", VTY_NEWLINE); - if (num > 4) - num = 4; - if (num > 1) { - write_amr_modes(vty, prefix, "ms", mr->ms_mode, num); - write_amr_modes(vty, prefix, "bts", mr->bts_mode, num); - } - vty_out(vty, " %s start-mode ", prefix); - if (mr_conf->icmi) { - num = 0; - for (i = 0; i < ((full) ? 8 : 6) && num < 4; i++) { - if ((mr->gsm48_ie[1] & (1 << i))) - num++; - if (mr_conf->smod == num - 1) { - vty_out(vty, "%d%s", num, VTY_NEWLINE); - break; - } - } - } else - vty_out(vty, "auto%s", VTY_NEWLINE); -} - -static void config_write_bts_single(struct vty *vty, struct gsm_bts *bts) -{ - int i; - uint8_t tmp; - - vty_out(vty, " bts %u%s", bts->nr, VTY_NEWLINE); - vty_out(vty, " type %s%s", btstype2str(bts->type), VTY_NEWLINE); - if (bts->description) - vty_out(vty, " description %s%s", bts->description, VTY_NEWLINE); - vty_out(vty, " band %s%s", gsm_band_name(bts->band), VTY_NEWLINE); - vty_out(vty, " cell_identity %u%s", bts->cell_identity, VTY_NEWLINE); - vty_out(vty, " location_area_code %u%s", bts->location_area_code, - VTY_NEWLINE); - if (bts->dtxu != GSM48_DTX_SHALL_NOT_BE_USED) - vty_out(vty, " dtx uplink%s%s", - (bts->dtxu != GSM48_DTX_SHALL_BE_USED) ? "" : " force", - VTY_NEWLINE); - if (bts->dtxd) - vty_out(vty, " dtx downlink%s", VTY_NEWLINE); - vty_out(vty, " base_station_id_code %u%s", bts->bsic, VTY_NEWLINE); - vty_out(vty, " ms max power %u%s", bts->ms_max_power, VTY_NEWLINE); - vty_out(vty, " cell reselection hysteresis %u%s", - bts->si_common.cell_sel_par.cell_resel_hyst*2, VTY_NEWLINE); - vty_out(vty, " rxlev access min %u%s", - bts->si_common.cell_sel_par.rxlev_acc_min, VTY_NEWLINE); - - if (bts->si_common.cell_ro_sel_par.present) { - struct gsm48_si_selection_params *sp; - sp = &bts->si_common.cell_ro_sel_par; - - if (sp->cbq) - vty_out(vty, " cell bar qualify %u%s", - sp->cbq, VTY_NEWLINE); - - if (sp->cell_resel_off) - vty_out(vty, " cell reselection offset %u%s", - sp->cell_resel_off*2, VTY_NEWLINE); - - if (sp->temp_offs == 7) - vty_out(vty, " temporary offset infinite%s", - VTY_NEWLINE); - else if (sp->temp_offs) - vty_out(vty, " temporary offset %u%s", - sp->temp_offs*10, VTY_NEWLINE); - - if (sp->penalty_time == 31) - vty_out(vty, " penalty time reserved%s", - VTY_NEWLINE); - else if (sp->penalty_time) - vty_out(vty, " penalty time %u%s", - (sp->penalty_time*20)+20, VTY_NEWLINE); - } - - /* Is periodic LU enabled or disabled? */ - if (bts->si_common.chan_desc.t3212 == 0) - vty_out(vty, " no periodic location update%s", VTY_NEWLINE); - else - vty_out(vty, " periodic location update %u%s", - bts->si_common.chan_desc.t3212 * 6, VTY_NEWLINE); - - if (gsm_bts_get_radio_link_timeout(bts) < 0) - vty_out(vty, " radio-link-timeout infinite%s", VTY_NEWLINE); - else - vty_out(vty, " radio-link-timeout %d%s", - gsm_bts_get_radio_link_timeout(bts), VTY_NEWLINE); - vty_out(vty, " channel allocator %s%s", - bts->chan_alloc_reverse ? "descending" : "ascending", - VTY_NEWLINE); - vty_out(vty, " rach tx integer %u%s", - bts->si_common.rach_control.tx_integer, VTY_NEWLINE); - vty_out(vty, " rach max transmission %u%s", - rach_max_trans_raw2val(bts->si_common.rach_control.max_trans), - VTY_NEWLINE); - - vty_out(vty, " channel-descrption attach %u%s", - bts->si_common.chan_desc.att, VTY_NEWLINE); - vty_out(vty, " channel-descrption bs-pa-mfrms %u%s", - bts->si_common.chan_desc.bs_pa_mfrms + 2, VTY_NEWLINE); - vty_out(vty, " channel-descrption bs-ag-blks-res %u%s", - bts->si_common.chan_desc.bs_ag_blks_res, VTY_NEWLINE); - - if (bts->rach_b_thresh != -1) - vty_out(vty, " rach nm busy threshold %u%s", - bts->rach_b_thresh, VTY_NEWLINE); - if (bts->rach_ldavg_slots != -1) - vty_out(vty, " rach nm load average %u%s", - bts->rach_ldavg_slots, VTY_NEWLINE); - if (bts->si_common.rach_control.cell_bar) - vty_out(vty, " cell barred 1%s", VTY_NEWLINE); - if ((bts->si_common.rach_control.t2 & 0x4) == 0) - vty_out(vty, " rach emergency call allowed 1%s", VTY_NEWLINE); - if ((bts->si_common.rach_control.t3) != 0) - for (i = 0; i < 8; i++) - if (bts->si_common.rach_control.t3 & (0x1 << i)) - vty_out(vty, " rach access-control-class %d barred%s", i, VTY_NEWLINE); - if ((bts->si_common.rach_control.t2 & 0xfb) != 0) - for (i = 0; i < 8; i++) - if ((i != 2) && (bts->si_common.rach_control.t2 & (0x1 << i))) - vty_out(vty, " rach access-control-class %d barred%s", i+8, VTY_NEWLINE); - for (i = SYSINFO_TYPE_1; i < _MAX_SYSINFO_TYPE; i++) { - if (bts->si_mode_static & (1 << i)) { - vty_out(vty, " system-information %s mode static%s", - get_value_string(osmo_sitype_strs, i), VTY_NEWLINE); - vty_out(vty, " system-information %s static %s%s", - get_value_string(osmo_sitype_strs, i), - osmo_hexdump_nospc(GSM_BTS_SI(bts, i), GSM_MACBLOCK_LEN), - VTY_NEWLINE); - } - } - vty_out(vty, " early-classmark-sending %s%s", - bts->early_classmark_allowed ? "allowed" : "forbidden", VTY_NEWLINE); - switch (bts->type) { - case GSM_BTS_TYPE_NANOBTS: - case GSM_BTS_TYPE_OSMOBTS: - vty_out(vty, " ip.access unit_id %u %u%s", - bts->ip_access.site_id, bts->ip_access.bts_id, VTY_NEWLINE); - if (bts->ip_access.rsl_ip) { - struct in_addr ia; - ia.s_addr = htonl(bts->ip_access.rsl_ip); - vty_out(vty, " ip.access rsl-ip %s%s", inet_ntoa(ia), - VTY_NEWLINE); - } - vty_out(vty, " oml ip.access stream_id %u line %u%s", - bts->oml_tei, bts->oml_e1_link.e1_nr, VTY_NEWLINE); - break; - case GSM_BTS_TYPE_NOKIA_SITE: - vty_out(vty, " nokia_site skip-reset %d%s", bts->nokia.skip_reset, VTY_NEWLINE); - vty_out(vty, " nokia_site no-local-rel-conf %d%s", - bts->nokia.no_loc_rel_cnf, VTY_NEWLINE); - vty_out(vty, " nokia_site bts-reset-timer %d%s", bts->nokia.bts_reset_timer_cnf, VTY_NEWLINE); - /* fall through: Nokia requires "oml e1" parameters also */ - default: - config_write_e1_link(vty, &bts->oml_e1_link, " oml "); - vty_out(vty, " oml e1 tei %u%s", bts->oml_tei, VTY_NEWLINE); - break; - } - - /* if we have a limit, write it */ - if (bts->paging.free_chans_need >= 0) - vty_out(vty, " paging free %d%s", bts->paging.free_chans_need, VTY_NEWLINE); - - vty_out(vty, " neighbor-list mode %s%s", - get_value_string(bts_neigh_mode_strs, bts->neigh_list_manual_mode), VTY_NEWLINE); - if (bts->neigh_list_manual_mode != NL_MODE_AUTOMATIC) { - for (i = 0; i < 1024; i++) { - if (bitvec_get_bit_pos(&bts->si_common.neigh_list, i)) - vty_out(vty, " neighbor-list add arfcn %u%s", - i, VTY_NEWLINE); - } - } - if (bts->neigh_list_manual_mode == NL_MODE_MANUAL_SI5SEP) { - for (i = 0; i < 1024; i++) { - if (bitvec_get_bit_pos(&bts->si_common.si5_neigh_list, i)) - vty_out(vty, " si5 neighbor-list add arfcn %u%s", - i, VTY_NEWLINE); - } - } - - for (i = 0; i < MAX_EARFCN_LIST; i++) { - struct osmo_earfcn_si2q *e = &bts->si_common.si2quater_neigh_list; - if (e->arfcn[i] != OSMO_EARFCN_INVALID) { - vty_out(vty, " si2quater neighbor-list add earfcn %u " - "thresh-hi %u", e->arfcn[i], e->thresh_hi); - - vty_out(vty, " thresh-lo %u", - e->thresh_lo_valid ? e->thresh_lo : 32); - - vty_out(vty, " prio %u", - e->prio_valid ? e->prio : 8); - - vty_out(vty, " qrxlv %u", - e->qrxlm_valid ? e->qrxlm : 32); - - tmp = e->meas_bw[i]; - vty_out(vty, " meas %u", - (tmp != OSMO_EARFCN_MEAS_INVALID) ? tmp : 8); - - vty_out(vty, "%s", VTY_NEWLINE); - } - } - - for (i = 0; i < bts->si_common.uarfcn_length; i++) { - vty_out(vty, " si2quater neighbor-list add uarfcn %u %u %u%s", - bts->si_common.data.uarfcn_list[i], - bts->si_common.data.scramble_list[i] & ~(1 << 9), - (bts->si_common.data.scramble_list[i] >> 9) & 1, - VTY_NEWLINE); - } - - vty_out(vty, " codec-support fr"); - if (bts->codec.hr) - vty_out(vty, " hr"); - if (bts->codec.efr) - vty_out(vty, " efr"); - if (bts->codec.amr) - vty_out(vty, " amr"); - vty_out(vty, "%s", VTY_NEWLINE); - - config_write_bts_amr(vty, bts, &bts->mr_full, 1); - config_write_bts_amr(vty, bts, &bts->mr_half, 0); - - config_write_bts_gprs(vty, bts); - - if (bts->excl_from_rf_lock) - vty_out(vty, " rf-lock-exclude%s", VTY_NEWLINE); - - vty_out(vty, " %sforce-combined-si%s", - bts->force_combined_si ? "" : "no ", VTY_NEWLINE); - - for (i = 0; i < ARRAY_SIZE(bts->depends_on); ++i) { - int j; - - if (bts->depends_on[i] == 0) - continue; - - for (j = 0; j < sizeof(bts->depends_on[i]) * 8; ++j) { - int bts_nr; - - if ((bts->depends_on[i] & (1<<j)) == 0) - continue; - - bts_nr = (i * sizeof(bts->depends_on[i]) * 8) + j; - vty_out(vty, " depends-on-bts %d%s", bts_nr, VTY_NEWLINE); - } - } - if (bts->pcu_sock_path) - vty_out(vty, " pcu-socket %s%s", bts->pcu_sock_path, VTY_NEWLINE); - - config_write_bts_model(vty, bts); -} - -static int config_write_bts(struct vty *v) -{ - struct gsm_network *gsmnet = gsmnet_from_vty(v); - struct gsm_bts *bts; - - llist_for_each_entry(bts, &gsmnet->bts_list, list) - config_write_bts_single(v, bts); - - return CMD_SUCCESS; -} - -static int config_write_net(struct vty *vty) -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - - vty_out(vty, "network%s", VTY_NEWLINE); - vty_out(vty, " network country code %u%s", gsmnet->country_code, VTY_NEWLINE); - vty_out(vty, " mobile network code %u%s", gsmnet->network_code, VTY_NEWLINE); - vty_out(vty, " short name %s%s", gsmnet->name_short, VTY_NEWLINE); - vty_out(vty, " long name %s%s", gsmnet->name_long, VTY_NEWLINE); - vty_out(vty, " auth policy %s%s", gsm_auth_policy_name(gsmnet->auth_policy), VTY_NEWLINE); - if (gsmnet->authorized_reg_str) - vty_out(vty, " authorized-regexp %s%s", gsmnet->authorized_reg_str, VTY_NEWLINE); - vty_out(vty, " location updating reject cause %u%s", - gsmnet->reject_cause, VTY_NEWLINE); - vty_out(vty, " encryption a5 %u%s", gsmnet->a5_encryption, VTY_NEWLINE); - vty_out(vty, " neci %u%s", gsmnet->neci, VTY_NEWLINE); - vty_out(vty, " paging any use tch %d%s", gsmnet->pag_any_tch, VTY_NEWLINE); - vty_out(vty, " rrlp mode %s%s", rrlp_mode_name(gsmnet->rrlp.mode), - VTY_NEWLINE); - vty_out(vty, " mm info %u%s", gsmnet->send_mm_info, VTY_NEWLINE); - vty_out(vty, " handover %u%s", gsmnet->handover.active, VTY_NEWLINE); - vty_out(vty, " handover window rxlev averaging %u%s", - gsmnet->handover.win_rxlev_avg, VTY_NEWLINE); - vty_out(vty, " handover window rxqual averaging %u%s", - gsmnet->handover.win_rxqual_avg, VTY_NEWLINE); - vty_out(vty, " handover window rxlev neighbor averaging %u%s", - gsmnet->handover.win_rxlev_avg_neigh, VTY_NEWLINE); - vty_out(vty, " handover power budget interval %u%s", - gsmnet->handover.pwr_interval, VTY_NEWLINE); - vty_out(vty, " handover power budget hysteresis %u%s", - gsmnet->handover.pwr_hysteresis, VTY_NEWLINE); - vty_out(vty, " handover maximum distance %u%s", - gsmnet->handover.max_distance, VTY_NEWLINE); - vty_out(vty, " timer t3101 %u%s", gsmnet->T3101, VTY_NEWLINE); - vty_out(vty, " timer t3103 %u%s", gsmnet->T3103, VTY_NEWLINE); - vty_out(vty, " timer t3105 %u%s", gsmnet->T3105, VTY_NEWLINE); - vty_out(vty, " timer t3107 %u%s", gsmnet->T3107, VTY_NEWLINE); - vty_out(vty, " timer t3109 %u%s", gsmnet->T3109, VTY_NEWLINE); - vty_out(vty, " timer t3111 %u%s", gsmnet->T3111, VTY_NEWLINE); - vty_out(vty, " timer t3113 %u%s", gsmnet->T3113, VTY_NEWLINE); - vty_out(vty, " timer t3115 %u%s", gsmnet->T3115, VTY_NEWLINE); - vty_out(vty, " timer t3117 %u%s", gsmnet->T3117, VTY_NEWLINE); - vty_out(vty, " timer t3119 %u%s", gsmnet->T3119, VTY_NEWLINE); - vty_out(vty, " timer t3122 %u%s", gsmnet->T3122, VTY_NEWLINE); - vty_out(vty, " timer t3141 %u%s", gsmnet->T3141, VTY_NEWLINE); - vty_out(vty, " dyn_ts_allow_tch_f %d%s", - gsmnet->dyn_ts_allow_tch_f ? 1 : 0, VTY_NEWLINE); - vty_out(vty, " subscriber-keep-in-ram %d%s", - gsmnet->subscr_group->keep_subscr, VTY_NEWLINE); - if (gsmnet->tz.override != 0) { - if (gsmnet->tz.dst) - vty_out(vty, " timezone %d %d %d%s", - gsmnet->tz.hr, gsmnet->tz.mn, gsmnet->tz.dst, - VTY_NEWLINE); - else - vty_out(vty, " timezone %d %d%s", - gsmnet->tz.hr, gsmnet->tz.mn, VTY_NEWLINE); - } - - return CMD_SUCCESS; -} - -static void trx_dump_vty(struct vty *vty, struct gsm_bts_trx *trx) -{ - 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, "Description: %s%s", - trx->description ? trx->description : "(null)", 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, " NM State: "); - net_dump_nmstate(vty, &trx->mo.nm_state); - vty_out(vty, " Baseband Transceiver NM State: "); - net_dump_nmstate(vty, &trx->bb_transc.mo.nm_state); - if (is_ipaccess_bts(trx->bts)) { - vty_out(vty, " ip.access stream ID: 0x%02x%s", - trx->rsl_tei, VTY_NEWLINE); - } else { - vty_out(vty, " E1 Signalling Link:%s", VTY_NEWLINE); - e1isl_dump_vty(vty, trx->rsl_link); - } -} - -DEFUN(show_trx, - show_trx_cmd, - "show trx [<0-255>] [<0-255>]", - SHOW_STR "Display information about a TRX\n" - "BTS Number\n" - "TRX Number\n") -{ - struct gsm_network *net = gsmnet_from_vty(vty); - struct gsm_bts *bts = NULL; - struct gsm_bts_trx *trx; - int bts_nr, trx_nr; - - if (argc >= 1) { - /* use the BTS number that the user has specified */ - bts_nr = atoi(argv[0]); - if (bts_nr >= net->num_bts) { - vty_out(vty, "%% can't find BTS '%s'%s", argv[0], - VTY_NEWLINE); - return CMD_WARNING; - } - bts = gsm_bts_num(net, bts_nr); - } - if (argc >= 2) { - trx_nr = atoi(argv[1]); - if (trx_nr >= bts->num_trx) { - vty_out(vty, "%% can't find TRX '%s'%s", argv[1], - VTY_NEWLINE); - return CMD_WARNING; - } - trx = gsm_bts_trx_num(bts, trx_nr); - trx_dump_vty(vty, trx); - return CMD_SUCCESS; - } - if (bts) { - /* print all TRX in this BTS */ - for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) { - trx = gsm_bts_trx_num(bts, trx_nr); - trx_dump_vty(vty, trx); - } - return CMD_SUCCESS; - } - - for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) { - bts = gsm_bts_num(net, bts_nr); - for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) { - trx = gsm_bts_trx_num(bts, trx_nr); - trx_dump_vty(vty, trx); - } - } - - return CMD_SUCCESS; -} - - -static 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, TSC %u", - ts->trx->bts->nr, ts->trx->nr, ts->nr, - gsm_pchan_name(ts->pchan), gsm_ts_tsc(ts)); - if (ts->pchan == GSM_PCHAN_TCH_F_PDCH) - vty_out(vty, " (%s mode)", - ts->flags & TS_F_PDCH_ACTIVE ? "PDCH" : "TCH/F"); - vty_out(vty, "%s", VTY_NEWLINE); - vty_out(vty, " NM State: "); - net_dump_nmstate(vty, &ts->mo.nm_state); - if (!is_ipaccess_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); -} - -DEFUN(show_ts, - show_ts_cmd, - "show timeslot [<0-255>] [<0-255>] [<0-7>]", - SHOW_STR "Display information about a TS\n" - "BTS Number\n" "TRX Number\n" "Timeslot Number\n") -{ - struct gsm_network *net = gsmnet_from_vty(vty); - struct gsm_bts *bts = NULL; - struct gsm_bts_trx *trx = NULL; - struct gsm_bts_trx_ts *ts = NULL; - int bts_nr, trx_nr, ts_nr; - - if (argc >= 1) { - /* use the BTS number that the user has specified */ - bts_nr = atoi(argv[0]); - if (bts_nr >= net->num_bts) { - vty_out(vty, "%% can't find BTS '%s'%s", argv[0], - VTY_NEWLINE); - return CMD_WARNING; - } - bts = gsm_bts_num(net, bts_nr); - } - if (argc >= 2) { - trx_nr = atoi(argv[1]); - if (trx_nr >= bts->num_trx) { - vty_out(vty, "%% can't find TRX '%s'%s", argv[1], - VTY_NEWLINE); - return CMD_WARNING; - } - trx = gsm_bts_trx_num(bts, trx_nr); - } - if (argc >= 3) { - ts_nr = atoi(argv[2]); - if (ts_nr >= TRX_NR_TS) { - vty_out(vty, "%% can't find TS '%s'%s", argv[2], - VTY_NEWLINE); - return CMD_WARNING; - } - /* Fully Specified: print and exit */ - ts = &trx->ts[ts_nr]; - ts_dump_vty(vty, ts); - return CMD_SUCCESS; - } - - if (bts && trx) { - /* Iterate over all TS in this TRX */ - for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) { - ts = &trx->ts[ts_nr]; - ts_dump_vty(vty, ts); - } - } else if (bts) { - /* Iterate over all TRX in this BTS, TS in each TRX */ - for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) { - trx = gsm_bts_trx_num(bts, trx_nr); - for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) { - ts = &trx->ts[ts_nr]; - ts_dump_vty(vty, ts); - } - } - } else { - /* Iterate over all BTS, TRX in each BTS, TS in each TRX */ - for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) { - bts = gsm_bts_num(net, bts_nr); - for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) { - trx = gsm_bts_trx_num(bts, trx_nr); - for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) { - ts = &trx->ts[ts_nr]; - ts_dump_vty(vty, ts); - } - } - } - } - - return CMD_SUCCESS; -} - -static void subscr_dump_vty(struct vty *vty, struct gsm_subscriber *subscr) -{ - vty_out(vty, " ID: %llu, Authorized: %d%s", subscr->id, - subscr->authorized, VTY_NEWLINE); - if (strlen(subscr->name)) - vty_out(vty, " Name: '%s'%s", subscr->name, VTY_NEWLINE); - if (strlen(subscr->extension)) - vty_out(vty, " Extension: %s%s", subscr->extension, - VTY_NEWLINE); - vty_out(vty, " IMSI: %s%s", subscr->imsi, VTY_NEWLINE); - if (subscr->tmsi != GSM_RESERVED_TMSI) - vty_out(vty, " TMSI: %08X%s", subscr->tmsi, - VTY_NEWLINE); - - vty_out(vty, " Use count: %u%s", subscr->use_count, VTY_NEWLINE); -} - -static void bsc_subscr_dump_vty(struct vty *vty, struct bsc_subscr *bsub) -{ - if (strlen(bsub->imsi)) - vty_out(vty, " IMSI: %s%s", bsub->imsi, VTY_NEWLINE); - if (bsub->tmsi != GSM_RESERVED_TMSI) - vty_out(vty, " TMSI: 0x%08x%s", bsub->tmsi, - VTY_NEWLINE); - vty_out(vty, " Use count: %d%s", bsub->use_count, VTY_NEWLINE); -} - -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"); -} - -/* FIXME: move this to libosmogsm */ -static const struct value_string gsm48_cmode_names[] = { - { GSM48_CMODE_SIGN, "signalling" }, - { GSM48_CMODE_SPEECH_V1, "FR or HR" }, - { GSM48_CMODE_SPEECH_EFR, "EFR" }, - { GSM48_CMODE_SPEECH_AMR, "AMR" }, - { GSM48_CMODE_DATA_14k5, "CSD(14k5)" }, - { GSM48_CMODE_DATA_12k0, "CSD(12k0)" }, - { GSM48_CMODE_DATA_6k0, "CSD(6k0)" }, - { GSM48_CMODE_DATA_3k6, "CSD(3k6)" }, - { 0, NULL } -}; - -/* 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) -{ - switch (ts->pchan) { - case GSM_PCHAN_TCH_F_TCH_H_PDCH: - if (ts->dyn.pchan_is == ts->dyn.pchan_want) - vty_out(vty, " as %s", - gsm_pchan_name(ts->dyn.pchan_is)); - else - vty_out(vty, " switching %s -> %s", - gsm_pchan_name(ts->dyn.pchan_is), - gsm_pchan_name(ts->dyn.pchan_want)); - break; - case GSM_PCHAN_TCH_F_PDCH: - if ((ts->flags & TS_F_PDCH_PENDING_MASK) == 0) - vty_out(vty, " as %s", - (ts->flags & TS_F_PDCH_ACTIVE)? "PDCH" - : "TCH/F"); - else - vty_out(vty, " switching %s -> %s", - (ts->flags & TS_F_PDCH_ACTIVE)? "PDCH" - : "TCH/F", - (ts->flags & TS_F_PDCH_ACT_PENDING)? "PDCH" - : "TCH/F"); - break; - default: - /* no dyn ts */ - break; - } -} - -static 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_lchant_name(lchan->type), VTY_NEWLINE); - /* show dyn TS details, if applicable */ - switch (lchan->ts->pchan) { - case GSM_PCHAN_TCH_F_TCH_H_PDCH: - vty_out(vty, " Osmocom Dyn TS:"); - vty_out_dyn_ts_status(vty, lchan->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, lchan->ts); - vty_out(vty, VTY_NEWLINE); - break; - default: - /* no dyn ts */ - break; - } - vty_out(vty, " Connection: %u, State: %s%s%s%s", - lchan->conn ? 1: 0, - gsm_lchans_name(lchan->state), - lchan->state == LCHAN_S_BROKEN ? " Error reason: " : "", - lchan->state == LCHAN_S_BROKEN ? lchan->broken_reason : "", - 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*2, - ms_pwr_dbm(lchan->ts->trx->bts->band, lchan->ms_power), - VTY_NEWLINE); - vty_out(vty, " Channel Mode / Codec: %s%s", - get_value_string(gsm48_cmode_names, lchan->tch_mode), - VTY_NEWLINE); - if (lchan->conn && lchan->conn->subscr) { - vty_out(vty, " Subscriber:%s", VTY_NEWLINE); - subscr_dump_vty(vty, lchan->conn->subscr); - } else - vty_out(vty, " No Subscriber%s", VTY_NEWLINE); - if (is_ipaccess_bts(lchan->ts->trx->bts)) { - struct in_addr ia; - ia.s_addr = htonl(lchan->abis_ip.bound_ip); - vty_out(vty, " Bound IP: %s Port %u RTP_TYPE2=%u CONN_ID=%u%s", - inet_ntoa(ia), lchan->abis_ip.bound_port, - lchan->abis_ip.rtp_payload2, lchan->abis_ip.conn_id, - 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], " "); -} - -static 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)); - vty_out_dyn_ts_status(vty, lchan->ts); - vty_out(vty, ", Lchan %u, Type %s, State %s - " - "L1 MS Power: %u dBm RXL-FULL-dl: %4d dBm RXL-FULL-ul: %4d dBm%s", - lchan->nr, - gsm_lchant_name(lchan->type), gsm_lchans_name(lchan->state), - mr->ms_l1.pwr, - rxlev2dbm(mr->dl.full.rx_lev), - rxlev2dbm(mr->ul.full.rx_lev), - VTY_NEWLINE); -} - - -static int dump_lchan_trx_ts(struct gsm_bts_trx_ts *ts, struct vty *vty, - void (*dump_cb)(struct vty *, struct gsm_lchan *)) -{ - int lchan_nr; - for (lchan_nr = 0; lchan_nr < TS_MAX_LCHAN; lchan_nr++) { - struct gsm_lchan *lchan = &ts->lchan[lchan_nr]; - if ((lchan->type == GSM_LCHAN_NONE) && (lchan->state == LCHAN_S_NONE)) - continue; - dump_cb(vty, lchan); - } - - return CMD_SUCCESS; -} - -static int dump_lchan_trx(struct gsm_bts_trx *trx, struct vty *vty, - void (*dump_cb)(struct vty *, struct gsm_lchan *)) -{ - int ts_nr; - - for (ts_nr = 0; ts_nr < TRX_NR_TS; ts_nr++) { - struct gsm_bts_trx_ts *ts = &trx->ts[ts_nr]; - dump_lchan_trx_ts(ts, vty, dump_cb); - } - - return CMD_SUCCESS; -} - -static int dump_lchan_bts(struct gsm_bts *bts, struct vty *vty, - void (*dump_cb)(struct vty *, struct gsm_lchan *)) -{ - int trx_nr; - - for (trx_nr = 0; trx_nr < bts->num_trx; trx_nr++) { - struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, trx_nr); - dump_lchan_trx(trx, vty, dump_cb); - } - - return CMD_SUCCESS; -} - -static int lchan_summary(struct vty *vty, int argc, const char **argv, - void (*dump_cb)(struct vty *, struct gsm_lchan *)) -{ - struct gsm_network *net = gsmnet_from_vty(vty); - struct gsm_bts *bts; - struct gsm_bts_trx *trx; - struct gsm_bts_trx_ts *ts; - struct gsm_lchan *lchan; - int bts_nr, trx_nr, ts_nr, lchan_nr; - - if (argc >= 1) { - /* use the BTS number that the user has specified */ - bts_nr = atoi(argv[0]); - if (bts_nr >= net->num_bts) { - vty_out(vty, "%% can't find BTS %s%s", argv[0], - VTY_NEWLINE); - return CMD_WARNING; - } - bts = gsm_bts_num(net, bts_nr); - - if (argc == 1) - return dump_lchan_bts(bts, vty, dump_cb); - } - if (argc >= 2) { - trx_nr = atoi(argv[1]); - if (trx_nr >= bts->num_trx) { - vty_out(vty, "%% can't find TRX %s%s", argv[1], - VTY_NEWLINE); - return CMD_WARNING; - } - trx = gsm_bts_trx_num(bts, trx_nr); - - if (argc == 2) - return dump_lchan_trx(trx, vty, dump_cb); - } - if (argc >= 3) { - ts_nr = atoi(argv[2]); - if (ts_nr >= TRX_NR_TS) { - vty_out(vty, "%% can't find TS %s%s", argv[2], - VTY_NEWLINE); - return CMD_WARNING; - } - ts = &trx->ts[ts_nr]; - - if (argc == 3) - return dump_lchan_trx_ts(ts, vty, dump_cb); - } - if (argc >= 4) { - lchan_nr = atoi(argv[3]); - if (lchan_nr >= TS_MAX_LCHAN) { - vty_out(vty, "%% can't find LCHAN %s%s", argv[3], - VTY_NEWLINE); - return CMD_WARNING; - } - lchan = &ts->lchan[lchan_nr]; - dump_cb(vty, lchan); - return CMD_SUCCESS; - } - - - for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) { - bts = gsm_bts_num(net, bts_nr); - dump_lchan_bts(bts, vty, dump_cb); - } - - return CMD_SUCCESS; -} - - -DEFUN(show_lchan, - show_lchan_cmd, - "show lchan [<0-255>] [<0-255>] [<0-7>] [lchan_nr]", - SHOW_STR "Display information about a logical channel\n" - "BTS Number\n" "TRX Number\n" "Timeslot Number\n" - LCHAN_NR_STR) - -{ - return lchan_summary(vty, argc, argv, lchan_dump_full_vty); -} - -DEFUN(show_lchan_summary, - show_lchan_summary_cmd, - "show lchan summary [<0-255>] [<0-255>] [<0-7>] [lchan_nr]", - SHOW_STR "Display information about a logical channel\n" - "Short summary\n" - "BTS Number\n" "TRX Number\n" "Timeslot Number\n" - LCHAN_NR_STR) -{ - return lchan_summary(vty, argc, argv, lchan_dump_short_vty); -} - -static void paging_dump_vty(struct vty *vty, struct gsm_paging_request *pag) -{ - vty_out(vty, "Paging on BTS %u%s", pag->bts->nr, VTY_NEWLINE); - bsc_subscr_dump_vty(vty, pag->bsub); -} - -static void bts_paging_dump_vty(struct vty *vty, struct gsm_bts *bts) -{ - struct gsm_paging_request *pag; - - if (!bts->paging.bts) - return; - - llist_for_each_entry(pag, &bts->paging.pending_requests, entry) - paging_dump_vty(vty, pag); -} - -DEFUN(show_paging, - show_paging_cmd, - "show paging [<0-255>]", - SHOW_STR "Display information about paging reuqests of a BTS\n" - "BTS Number\n") -{ - struct gsm_network *net = gsmnet_from_vty(vty); - struct gsm_bts *bts; - int bts_nr; - - if (argc >= 1) { - /* use the BTS number that the user has specified */ - bts_nr = atoi(argv[0]); - if (bts_nr >= net->num_bts) { - vty_out(vty, "%% can't find BTS %s%s", argv[0], - VTY_NEWLINE); - return CMD_WARNING; - } - bts = gsm_bts_num(net, bts_nr); - bts_paging_dump_vty(vty, bts); - - return CMD_SUCCESS; - } - for (bts_nr = 0; bts_nr < net->num_bts; bts_nr++) { - bts = gsm_bts_num(net, bts_nr); - bts_paging_dump_vty(vty, bts); - } - - return CMD_SUCCESS; -} - -DEFUN(show_paging_group, - show_paging_group_cmd, - "show paging-group <0-255> IMSI", - SHOW_STR "Display the paging group\n" - "BTS Number\n" "IMSI\n") -{ - struct gsm_network *net = gsmnet_from_vty(vty); - struct gsm_bts *bts; - unsigned int page_group; - int bts_nr = atoi(argv[0]); - - if (bts_nr >= net->num_bts) { - vty_out(vty, "%% can't find BTS %s%s", argv[0], VTY_NEWLINE); - return CMD_WARNING; - } - - bts = gsm_bts_num(net, bts_nr); - if (!bts) { - vty_out(vty, "%% can't find BTS %s%s", argv[0], VTY_NEWLINE); - return CMD_WARNING; - } - - page_group = gsm0502_calc_paging_group(&bts->si_common.chan_desc, - str_to_imsi(argv[1])); - vty_out(vty, "%%Paging group for IMSI %" PRIu64 " on BTS #%d is %u%s", - str_to_imsi(argv[1]), bts->nr, - page_group, VTY_NEWLINE); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_neci, - cfg_net_neci_cmd, - "neci (0|1)", - "New Establish Cause Indication\n" - "Don't set the NECI bit\n" "Set the NECI bit\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - - gsmnet->neci = atoi(argv[0]); - gsm_net_update_ctype(gsmnet); - return CMD_SUCCESS; -} - -#define HANDOVER_STR "Handover Options\n" - -DEFUN(cfg_net_handover, cfg_net_handover_cmd, - "handover (0|1)", - HANDOVER_STR - "Don't perform in-call handover\n" - "Perform in-call handover\n") -{ - int enable = atoi(argv[0]); - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - - if (enable && ipacc_rtp_direct) { - vty_out(vty, "%% Cannot enable handover unless RTP Proxy mode " - "is enabled by using the -P command line option%s", - VTY_NEWLINE); - return CMD_WARNING; - } - gsmnet->handover.active = enable; - - return CMD_SUCCESS; -} - -#define HO_WIN_STR HANDOVER_STR "Measurement Window\n" -#define HO_WIN_RXLEV_STR HO_WIN_STR "Received Level Averaging\n" -#define HO_WIN_RXQUAL_STR HO_WIN_STR "Received Quality Averaging\n" -#define HO_PBUDGET_STR HANDOVER_STR "Power Budget\n" -#define HO_AVG_COUNT_STR "Amount to use for Averaging\n" - -DEFUN(cfg_net_ho_win_rxlev_avg, cfg_net_ho_win_rxlev_avg_cmd, - "handover window rxlev averaging <1-10>", - HO_WIN_RXLEV_STR - "How many RxLev measurements are used for averaging\n" - HO_AVG_COUNT_STR) -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - gsmnet->handover.win_rxlev_avg = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_ho_win_rxqual_avg, cfg_net_ho_win_rxqual_avg_cmd, - "handover window rxqual averaging <1-10>", - HO_WIN_RXQUAL_STR - "How many RxQual measurements are used for averaging\n" - HO_AVG_COUNT_STR) -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - gsmnet->handover.win_rxqual_avg = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_ho_win_rxlev_neigh_avg, cfg_net_ho_win_rxlev_avg_neigh_cmd, - "handover window rxlev neighbor averaging <1-10>", - HO_WIN_RXLEV_STR "Neighbor\n" - "How many RxQual measurements are used for averaging\n" - HO_AVG_COUNT_STR) -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - gsmnet->handover.win_rxlev_avg_neigh = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_ho_pwr_interval, cfg_net_ho_pwr_interval_cmd, - "handover power budget interval <1-99>", - HO_PBUDGET_STR - "How often to check if we have a better cell (SACCH frames)\n" - "Interval\n" "Number\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - gsmnet->handover.pwr_interval = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_ho_pwr_hysteresis, cfg_net_ho_pwr_hysteresis_cmd, - "handover power budget hysteresis <0-999>", - HO_PBUDGET_STR - "How many dB does a neighbor to be stronger to become a HO candidate\n" - "Hysteresis\n" "Number\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - gsmnet->handover.pwr_hysteresis = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_ho_max_distance, cfg_net_ho_max_distance_cmd, - "handover maximum distance <0-9999>", - HANDOVER_STR - "How big is the maximum timing advance before HO is forced\n" - "Distance\n" "Number\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - gsmnet->handover.max_distance = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_net_pag_any_tch, - cfg_net_pag_any_tch_cmd, - "paging any use tch (0|1)", - "Assign a TCH when receiving a Paging Any request\n" - "Any Channel\n" "Use\n" "TCH\n" - "Do not use TCH for Paging Request Any\n" - "Do use TCH for Paging Request Any\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - gsmnet->pag_any_tch = atoi(argv[0]); - gsm_net_update_ctype(gsmnet); - return CMD_SUCCESS; -} - -#define DECLARE_TIMER(number, doc) \ - DEFUN(cfg_net_T##number, \ - cfg_net_T##number##_cmd, \ - "timer t" #number " <0-65535>", \ - "Configure GSM Timers\n" \ - doc "Timer Value in seconds\n") \ -{ \ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); \ - int value = atoi(argv[0]); \ - \ - if (value < 0 || value > 65535) { \ - vty_out(vty, "Timer value %s out of range.%s", \ - argv[0], VTY_NEWLINE); \ - return CMD_WARNING; \ - } \ - \ - gsmnet->T##number = value; \ - return CMD_SUCCESS; \ -} - -DECLARE_TIMER(3101, "Set the timeout value for IMMEDIATE ASSIGNMENT.\n") -DECLARE_TIMER(3103, "Set the timeout value for HANDOVER.\n") -DECLARE_TIMER(3105, "Set the timer for repetition of PHYSICAL INFORMATION.\n") -DECLARE_TIMER(3107, "Currently not used.\n") -DECLARE_TIMER(3109, "Set the RSL SACCH deactivation timeout.\n") -DECLARE_TIMER(3111, "Set the RSL timeout to wait before releasing the RF Channel.\n") -DECLARE_TIMER(3113, "Set the time to try paging a subscriber.\n") -DECLARE_TIMER(3115, "Currently not used.\n") -DECLARE_TIMER(3117, "Currently not used.\n") -DECLARE_TIMER(3119, "Currently not used.\n") -DECLARE_TIMER(3122, "Waiting time (seconds) after IMM ASS REJECT\n") -DECLARE_TIMER(3141, "Currently not used.\n") - -DEFUN_DEPRECATED(cfg_net_dtx, - cfg_net_dtx_cmd, - "dtx-used (0|1)", - ".HIDDEN\n""Obsolete\n""Obsolete\n") -{ - vty_out(vty, "%% 'dtx-used' is now deprecated: use dtx * " - "configuration options of BTS instead%s", VTY_NEWLINE); - return CMD_SUCCESS; -} - -/* per-BTS configuration */ -DEFUN(cfg_bts, - cfg_bts_cmd, - "bts <0-255>", - "Select a BTS to configure\n" - "BTS Number\n") -{ - struct gsm_network *gsmnet = gsmnet_from_vty(vty); - int bts_nr = atoi(argv[0]); - struct gsm_bts *bts; - - if (bts_nr > gsmnet->num_bts) { - vty_out(vty, "%% The next unused BTS number is %u%s", - gsmnet->num_bts, VTY_NEWLINE); - return CMD_WARNING; - } else if (bts_nr == gsmnet->num_bts) { - /* allocate a new one */ - bts = gsm_bts_alloc_register(gsmnet, GSM_BTS_TYPE_UNKNOWN, - HARDCODED_BSIC); - } else - bts = gsm_bts_num(gsmnet, bts_nr); - - if (!bts) { - vty_out(vty, "%% Unable to allocate BTS %u%s", - gsmnet->num_bts, VTY_NEWLINE); - return CMD_WARNING; - } - - vty->index = bts; - vty->index_sub = &bts->description; - vty->node = BTS_NODE; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_type, - cfg_bts_type_cmd, - "type TYPE", /* dynamically created */ - "Set the BTS type\n" "Type\n") -{ - struct gsm_bts *bts = vty->index; - int rc; - - rc = gsm_set_bts_type(bts, str2btstype(argv[0])); - if (rc < 0) - return CMD_WARNING; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_band, - cfg_bts_band_cmd, - "band BAND", - "Set the frequency band of this BTS\n" "Frequency band\n") -{ - struct gsm_bts *bts = vty->index; - int band = gsm_band_parse(argv[0]); - - if (band < 0) { - vty_out(vty, "%% BAND %d is not a valid GSM band%s", - band, VTY_NEWLINE); - return CMD_WARNING; - } - - bts->band = band; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_dtxu, cfg_bts_dtxu_cmd, "dtx uplink [force]", - "Configure discontinuous transmission\n" - "Enable Uplink DTX for this BTS\n" - "MS 'shall' use DTXu instead of 'may' use (might not be supported by " - "older phones).\n") -{ - struct gsm_bts *bts = vty->index; - - bts->dtxu = (argc > 0) ? GSM48_DTX_SHALL_BE_USED : GSM48_DTX_MAY_BE_USED; - if (!is_ipaccess_bts(bts)) - vty_out(vty, "%% DTX enabled on non-IP BTS: this configuration " - "neither supported nor tested!%s", VTY_NEWLINE); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_no_dtxu, cfg_bts_no_dtxu_cmd, "no dtx uplink", - NO_STR - "Configure discontinuous transmission\n" - "Disable Uplink DTX for this BTS\n") -{ - struct gsm_bts *bts = vty->index; - - bts->dtxu = GSM48_DTX_SHALL_NOT_BE_USED; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_dtxd, cfg_bts_dtxd_cmd, "dtx downlink", - "Configure discontinuous transmission\n" - "Enable Downlink DTX for this BTS\n") -{ - struct gsm_bts *bts = vty->index; - - bts->dtxd = true; - if (!is_ipaccess_bts(bts)) - vty_out(vty, "%% DTX enabled on non-IP BTS: this configuration " - "neither supported nor tested!%s", VTY_NEWLINE); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_no_dtxd, cfg_bts_no_dtxd_cmd, "no dtx downlink", - NO_STR - "Configure discontinuous transmission\n" - "Disable Downlink DTX for this BTS\n") -{ - struct gsm_bts *bts = vty->index; - - bts->dtxd = false; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_ci, - cfg_bts_ci_cmd, - "cell_identity <0-65535>", - "Set the Cell identity of this BTS\n" "Cell Identity\n") -{ - struct gsm_bts *bts = vty->index; - int ci = atoi(argv[0]); - - if (ci < 0 || ci > 0xffff) { - vty_out(vty, "%% CI %d is not in the valid range (0-65535)%s", - ci, VTY_NEWLINE); - return CMD_WARNING; - } - bts->cell_identity = ci; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_lac, - cfg_bts_lac_cmd, - "location_area_code <0-65535>", - "Set the Location Area Code (LAC) of this BTS\n" "LAC\n") -{ - struct gsm_bts *bts = vty->index; - int lac = atoi(argv[0]); - - if (lac < 0 || lac > 0xffff) { - vty_out(vty, "%% LAC %d is not in the valid range (0-65535)%s", - lac, VTY_NEWLINE); - return CMD_WARNING; - } - - if (lac == GSM_LAC_RESERVED_DETACHED || lac == GSM_LAC_RESERVED_ALL_BTS) { - vty_out(vty, "%% LAC %d is reserved by GSM 04.08%s", - lac, VTY_NEWLINE); - return CMD_WARNING; - } - - bts->location_area_code = lac; - - return CMD_SUCCESS; -} - - -/* compatibility wrapper for old config files */ -DEFUN_HIDDEN(cfg_bts_tsc, - cfg_bts_tsc_cmd, - "training_sequence_code <0-7>", - "Set the Training Sequence Code (TSC) of this BTS\n" "TSC\n") -{ - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_bsic, - cfg_bts_bsic_cmd, - "base_station_id_code <0-63>", - "Set the Base Station Identity Code (BSIC) of this BTS\n" - "BSIC of this BTS\n") -{ - struct gsm_bts *bts = vty->index; - int bsic = atoi(argv[0]); - - if (bsic < 0 || bsic > 0x3f) { - vty_out(vty, "%% BSIC %d is not in the valid range (0-255)%s", - bsic, VTY_NEWLINE); - return CMD_WARNING; - } - bts->bsic = bsic; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_unit_id, - cfg_bts_unit_id_cmd, - "ip.access unit_id <0-65534> <0-255>", - "Abis/IP specific options\n" - "Set the IPA BTS Unit ID\n" - "Unit ID (Site)\n" - "Unit ID (BTS)\n") -{ - struct gsm_bts *bts = vty->index; - int site_id = atoi(argv[0]); - int bts_id = atoi(argv[1]); - - if (!is_ipaccess_bts(bts)) { - vty_out(vty, "%% BTS is not of ip.access type%s", VTY_NEWLINE); - return CMD_WARNING; - } - - bts->ip_access.site_id = site_id; - bts->ip_access.bts_id = bts_id; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_rsl_ip, - cfg_bts_rsl_ip_cmd, - "ip.access rsl-ip A.B.C.D", - "Abis/IP specific options\n" - "Set the IPA RSL IP Address of the BSC\n" - "Destination IP address for RSL connection\n") -{ - struct gsm_bts *bts = vty->index; - struct in_addr ia; - - if (!is_ipaccess_bts(bts)) { - vty_out(vty, "%% BTS is not of ip.access type%s", VTY_NEWLINE); - return CMD_WARNING; - } - - inet_aton(argv[0], &ia); - bts->ip_access.rsl_ip = ntohl(ia.s_addr); - - return CMD_SUCCESS; -} - -#define NOKIA_STR "Nokia *Site related commands\n" - -DEFUN(cfg_bts_nokia_site_skip_reset, - cfg_bts_nokia_site_skip_reset_cmd, - "nokia_site skip-reset (0|1)", - NOKIA_STR - "Skip the reset step during bootstrap process of this BTS\n" - "Do NOT skip the reset\n" "Skip the reset\n") -{ - struct gsm_bts *bts = vty->index; - - if (bts->type != GSM_BTS_TYPE_NOKIA_SITE) { - vty_out(vty, "%% BTS is not of Nokia *Site type%s", VTY_NEWLINE); - return CMD_WARNING; - } - - bts->nokia.skip_reset = atoi(argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_nokia_site_no_loc_rel_cnf, - cfg_bts_nokia_site_no_loc_rel_cnf_cmd, - "nokia_site no-local-rel-conf (0|1)", - NOKIA_STR - "Do not wait for RELease CONFirm message when releasing channel locally\n" - "Wait for RELease CONFirm\n" "Do not wait for RELease CONFirm\n") -{ - struct gsm_bts *bts = vty->index; - - if (!is_nokia_bts(bts)) { - vty_out(vty, "%% BTS is not of Nokia *Site type%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - bts->nokia.no_loc_rel_cnf = atoi(argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_nokia_site_bts_reset_timer_cnf, - cfg_bts_nokia_site_bts_reset_timer_cnf_cmd, - "nokia_site bts-reset-timer <15-100>", - NOKIA_STR - "The amount of time (in sec.) between BTS_RESET is sent,\n" - "and the BTS is being bootstrapped.\n") -{ - struct gsm_bts *bts = vty->index; - - if (!is_nokia_bts(bts)) { - vty_out(vty, "%% BTS is not of Nokia *Site type%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - bts->nokia.bts_reset_timer_cnf = atoi(argv[0]); - - return CMD_SUCCESS; -} -#define OML_STR "Organization & Maintenance Link\n" -#define IPA_STR "A-bis/IP Specific Options\n" - -DEFUN(cfg_bts_stream_id, - cfg_bts_stream_id_cmd, - "oml ip.access stream_id <0-255> line E1_LINE", - OML_STR IPA_STR - "Set the ip.access Stream ID of the OML link of this BTS\n" - "Stream Identifier\n" "Virtual E1 Line Number\n" "Virtual E1 Line Number\n") -{ - struct gsm_bts *bts = vty->index; - int stream_id = atoi(argv[0]), linenr = atoi(argv[1]); - - if (!is_ipaccess_bts(bts)) { - vty_out(vty, "%% BTS is not of ip.access type%s", VTY_NEWLINE); - return CMD_WARNING; - } - - bts->oml_tei = stream_id; - /* This is used by e1inp_bind_ops callback for each BTS model. */ - bts->oml_e1_link.e1_nr = linenr; - - return CMD_SUCCESS; -} - -#define OML_E1_STR OML_STR "OML E1/T1 Configuration\n" - -DEFUN(cfg_bts_oml_e1, - cfg_bts_oml_e1_cmd, - "oml e1 line E1_LINE timeslot <1-31> sub-slot (0|1|2|3|full)", - OML_E1_STR - "E1/T1 line number to be used for OML\n" - "E1/T1 line number to be used for OML\n" - "E1/T1 timeslot to be used for OML\n" - "E1/T1 timeslot to be used for OML\n" - "E1/T1 sub-slot to be used for OML\n" - "Use E1/T1 sub-slot 0\n" - "Use E1/T1 sub-slot 1\n" - "Use E1/T1 sub-slot 2\n" - "Use E1/T1 sub-slot 3\n" - "Use full E1 slot 3\n" - ) -{ - struct gsm_bts *bts = vty->index; - - parse_e1_link(&bts->oml_e1_link, argv[0], argv[1], argv[2]); - - return CMD_SUCCESS; -} - - -DEFUN(cfg_bts_oml_e1_tei, - cfg_bts_oml_e1_tei_cmd, - "oml e1 tei <0-63>", - OML_E1_STR - "Set the TEI to be used for OML\n" - "TEI Number\n") -{ - struct gsm_bts *bts = vty->index; - - bts->oml_tei = atoi(argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_challoc, cfg_bts_challoc_cmd, - "channel allocator (ascending|descending)", - "Channnel Allocator\n" "Channel Allocator\n" - "Allocate Timeslots and Transceivers in ascending order\n" - "Allocate Timeslots and Transceivers in descending order\n") -{ - struct gsm_bts *bts = vty->index; - - if (!strcmp(argv[0], "ascending")) - bts->chan_alloc_reverse = 0; - else - bts->chan_alloc_reverse = 1; - - return CMD_SUCCESS; -} - -#define RACH_STR "Random Access Control Channel\n" - -DEFUN(cfg_bts_rach_tx_integer, - cfg_bts_rach_tx_integer_cmd, - "rach tx integer <0-15>", - RACH_STR - "Set the raw tx integer value in RACH Control parameters IE\n" - "Set the raw tx integer value in RACH Control parameters IE\n" - "Raw tx integer value in RACH Control parameters IE\n") -{ - struct gsm_bts *bts = vty->index; - bts->si_common.rach_control.tx_integer = atoi(argv[0]) & 0xf; - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_rach_max_trans, - cfg_bts_rach_max_trans_cmd, - "rach max transmission (1|2|4|7)", - RACH_STR - "Set the maximum number of RACH burst transmissions\n" - "Set the maximum number of RACH burst transmissions\n" - "Maximum number of 1 RACH burst transmissions\n" - "Maximum number of 2 RACH burst transmissions\n" - "Maximum number of 4 RACH burst transmissions\n" - "Maximum number of 7 RACH burst transmissions\n") -{ - struct gsm_bts *bts = vty->index; - bts->si_common.rach_control.max_trans = rach_max_trans_val2raw(atoi(argv[0])); - return CMD_SUCCESS; -} - -#define CD_STR "Channel Description\n" - -DEFUN(cfg_bts_chan_desc_att, - cfg_bts_chan_desc_att_cmd, - "channel-descrption attach (0|1)", - CD_STR - "Set if attachment is required\n" - "Attachment is NOT required\n" - "Attachment is required (standard)\n") -{ - struct gsm_bts *bts = vty->index; - bts->si_common.chan_desc.att = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_chan_desc_bs_pa_mfrms, - cfg_bts_chan_desc_bs_pa_mfrms_cmd, - "channel-descrption bs-pa-mfrms <2-9>", - CD_STR - "Set number of multiframe periods for paging groups\n" - "Number of multiframe periods for paging groups\n") -{ - struct gsm_bts *bts = vty->index; - int bs_pa_mfrms = atoi(argv[0]); - - bts->si_common.chan_desc.bs_pa_mfrms = bs_pa_mfrms - 2; - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_chan_desc_bs_ag_blks_res, - cfg_bts_chan_desc_bs_ag_blks_res_cmd, - "channel-descrption bs-ag-blks-res <0-7>", - CD_STR - "Set number of blocks reserved for access grant\n" - "Number of blocks reserved for access grant\n") -{ - struct gsm_bts *bts = vty->index; - int bs_ag_blks_res = atoi(argv[0]); - - bts->si_common.chan_desc.bs_ag_blks_res = bs_ag_blks_res; - return CMD_SUCCESS; -} - -#define NM_STR "Network Management\n" - -DEFUN(cfg_bts_rach_nm_b_thresh, - cfg_bts_rach_nm_b_thresh_cmd, - "rach nm busy threshold <0-255>", - RACH_STR NM_STR - "Set the NM Busy Threshold\n" - "Set the NM Busy Threshold\n" - "NM Busy Threshold in dB") -{ - struct gsm_bts *bts = vty->index; - bts->rach_b_thresh = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_rach_nm_ldavg, - cfg_bts_rach_nm_ldavg_cmd, - "rach nm load average <0-65535>", - RACH_STR NM_STR - "Set the NM Loadaverage Slots value\n" - "Set the NM Loadaverage Slots value\n" - "NM Loadaverage Slots value\n") -{ - struct gsm_bts *bts = vty->index; - bts->rach_ldavg_slots = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_cell_barred, cfg_bts_cell_barred_cmd, - "cell barred (0|1)", - "Should this cell be barred from access?\n" - "Should this cell be barred from access?\n" - "Cell should NOT be barred\n" - "Cell should be barred\n") - -{ - struct gsm_bts *bts = vty->index; - - bts->si_common.rach_control.cell_bar = atoi(argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_rach_ec_allowed, cfg_bts_rach_ec_allowed_cmd, - "rach emergency call allowed (0|1)", - RACH_STR - "Should this cell allow emergency calls?\n" - "Should this cell allow emergency calls?\n" - "Should this cell allow emergency calls?\n" - "Do NOT allow emergency calls\n" - "Allow emergency calls\n") -{ - struct gsm_bts *bts = vty->index; - - if (atoi(argv[0]) == 0) - bts->si_common.rach_control.t2 |= 0x4; - else - bts->si_common.rach_control.t2 &= ~0x4; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_rach_ac_class, cfg_bts_rach_ac_class_cmd, - "rach access-control-class (0|1|2|3|4|5|6|7|8|9|11|12|13|14|15) (barred|allowed)", - RACH_STR - "Set access control class\n" - "Access control class 0\n" - "Access control class 1\n" - "Access control class 2\n" - "Access control class 3\n" - "Access control class 4\n" - "Access control class 5\n" - "Access control class 6\n" - "Access control class 7\n" - "Access control class 8\n" - "Access control class 9\n" - "Access control class 11 for PLMN use\n" - "Access control class 12 for security services\n" - "Access control class 13 for public utilities (e.g. water/gas suppliers)\n" - "Access control class 14 for emergency services\n" - "Access control class 15 for PLMN staff\n" - "barred to use access control class\n" - "allowed to use access control class\n") -{ - struct gsm_bts *bts = vty->index; - - uint8_t control_class; - uint8_t allowed = 0; - - if (strcmp(argv[1], "allowed") == 0) - allowed = 1; - - control_class = atoi(argv[0]); - if (control_class < 8) - if (allowed) - bts->si_common.rach_control.t3 &= ~(0x1 << control_class); - else - bts->si_common.rach_control.t3 |= (0x1 << control_class); - else - if (allowed) - bts->si_common.rach_control.t2 &= ~(0x1 << (control_class - 8)); - else - bts->si_common.rach_control.t2 |= (0x1 << (control_class - 8)); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_ms_max_power, cfg_bts_ms_max_power_cmd, - "ms max power <0-40>", - "MS Options\n" - "Maximum transmit power of the MS\n" - "Maximum transmit power of the MS\n" - "Maximum transmit power of the MS in dBm") -{ - struct gsm_bts *bts = vty->index; - - bts->ms_max_power = atoi(argv[0]); - - return CMD_SUCCESS; -} - -#define CELL_STR "Cell Parameters\n" - -DEFUN(cfg_bts_cell_resel_hyst, cfg_bts_cell_resel_hyst_cmd, - "cell reselection hysteresis <0-14>", - CELL_STR "Cell re-selection parameters\n" - "Cell Re-Selection Hysteresis in dB\n" - "Cell Re-Selection Hysteresis in dB") -{ - struct gsm_bts *bts = vty->index; - - bts->si_common.cell_sel_par.cell_resel_hyst = atoi(argv[0])/2; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_rxlev_acc_min, cfg_bts_rxlev_acc_min_cmd, - "rxlev access min <0-63>", - "Minimum RxLev needed for cell access\n" - "Minimum RxLev needed for cell access\n" - "Minimum RxLev needed for cell access\n" - "Minimum RxLev needed for cell access (better than -110dBm)") -{ - struct gsm_bts *bts = vty->index; - - bts->si_common.cell_sel_par.rxlev_acc_min = atoi(argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_cell_bar_qualify, cfg_bts_cell_bar_qualify_cmd, - "cell bar qualify (0|1)", - CELL_STR "Cell Bar Qualify\n" "Cell Bar Qualify\n" - "Set CBQ to 0\n" "Set CBQ to 1\n") -{ - struct gsm_bts *bts = vty->index; - - bts->si_common.cell_ro_sel_par.present = 1; - bts->si_common.cell_ro_sel_par.cbq = atoi(argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_cell_resel_ofs, cfg_bts_cell_resel_ofs_cmd, - "cell reselection offset <0-126>", - CELL_STR "Cell Re-Selection Parameters\n" - "Cell Re-Selection Offset (CRO) in dB\n" - "Cell Re-Selection Offset (CRO) in dB\n" - ) -{ - struct gsm_bts *bts = vty->index; - - bts->si_common.cell_ro_sel_par.present = 1; - bts->si_common.cell_ro_sel_par.cell_resel_off = atoi(argv[0])/2; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_temp_ofs, cfg_bts_temp_ofs_cmd, - "temporary offset <0-60>", - "Cell selection temporary negative offset\n" - "Cell selection temporary negative offset\n" - "Cell selection temporary negative offset in dB") -{ - struct gsm_bts *bts = vty->index; - - bts->si_common.cell_ro_sel_par.present = 1; - bts->si_common.cell_ro_sel_par.temp_offs = atoi(argv[0])/10; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_temp_ofs_inf, cfg_bts_temp_ofs_inf_cmd, - "temporary offset infinite", - "Cell selection temporary negative offset\n" - "Cell selection temporary negative offset\n" - "Sets cell selection temporary negative offset to infinity") -{ - struct gsm_bts *bts = vty->index; - - bts->si_common.cell_ro_sel_par.present = 1; - bts->si_common.cell_ro_sel_par.temp_offs = 7; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_penalty_time, cfg_bts_penalty_time_cmd, - "penalty time <20-620>", - "Cell selection penalty time\n" - "Cell selection penalty time\n" - "Cell selection penalty time in seconds (by 20s increments)\n") -{ - struct gsm_bts *bts = vty->index; - - bts->si_common.cell_ro_sel_par.present = 1; - bts->si_common.cell_ro_sel_par.penalty_time = (atoi(argv[0])-20)/20; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_penalty_time_rsvd, cfg_bts_penalty_time_rsvd_cmd, - "penalty time reserved", - "Cell selection penalty time\n" - "Cell selection penalty time\n" - "Set cell selection penalty time to reserved value 31, " - "(indicate that CELL_RESELECT_OFFSET is subtracted from C2 " - "and TEMPORARY_OFFSET is ignored)") -{ - struct gsm_bts *bts = vty->index; - - bts->si_common.cell_ro_sel_par.present = 1; - bts->si_common.cell_ro_sel_par.penalty_time = 31; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_per_loc_upd, cfg_bts_per_loc_upd_cmd, - "periodic location update <6-1530>", - "Periodic Location Updating Interval\n" - "Periodic Location Updating Interval\n" - "Periodic Location Updating Interval\n" - "Periodic Location Updating Interval in Minutes\n") -{ - struct gsm_bts *bts = vty->index; - - bts->si_common.chan_desc.t3212 = atoi(argv[0]) / 6; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_no_per_loc_upd, cfg_bts_no_per_loc_upd_cmd, - "no periodic location update", - NO_STR - "Periodic Location Updating Interval\n" - "Periodic Location Updating Interval\n" - "Periodic Location Updating Interval\n") -{ - struct gsm_bts *bts = vty->index; - - bts->si_common.chan_desc.t3212 = 0; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_radio_link_timeout, cfg_bts_radio_link_timeout_cmd, - "radio-link-timeout <4-64>", - "Radio link timeout criterion (BTS side)\n" - "Radio link timeout value (lost SACCH block)\n") -{ - struct gsm_bts *bts = vty->index; - - gsm_bts_set_radio_link_timeout(bts, atoi(argv[0])); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_radio_link_timeout_inf, cfg_bts_radio_link_timeout_inf_cmd, - "radio-link-timeout infinite", - "Radio link timeout criterion (BTS side)\n" - "Infinite Radio link timeout value (use only for BTS RF testing)\n") -{ - struct gsm_bts *bts = vty->index; - - if (bts->type != GSM_BTS_TYPE_OSMOBTS) { - vty_out(vty, "%% infinite radio link timeout not supported by this BTS%s", VTY_NEWLINE); - return CMD_WARNING; - } - - vty_out(vty, "%% INFINITE RADIO LINK TIMEOUT, USE ONLY FOR BTS RF TESTING%s", VTY_NEWLINE); - gsm_bts_set_radio_link_timeout(bts, -1); - - return CMD_SUCCESS; -} - -#define GPRS_TEXT "GPRS Packet Network\n" - -DEFUN(cfg_bts_prs_bvci, cfg_bts_gprs_bvci_cmd, - "gprs cell bvci <2-65535>", - GPRS_TEXT - "GPRS Cell Settings\n" - "GPRS BSSGP VC Identifier\n" - "GPRS BSSGP VC Identifier") -{ - struct gsm_bts *bts = vty->index; - - if (bts->gprs.mode == BTS_GPRS_NONE) { - vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); - return CMD_WARNING; - } - - bts->gprs.cell.bvci = atoi(argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_gprs_nsei, cfg_bts_gprs_nsei_cmd, - "gprs nsei <0-65535>", - GPRS_TEXT - "GPRS NS Entity Identifier\n" - "GPRS NS Entity Identifier") -{ - struct gsm_bts *bts = vty->index; - - if (bts->gprs.mode == BTS_GPRS_NONE) { - vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); - return CMD_WARNING; - } - - bts->gprs.nse.nsei = atoi(argv[0]); - - return CMD_SUCCESS; -} - -#define NSVC_TEXT "Network Service Virtual Connection (NS-VC)\n" \ - "NSVC Logical Number\n" - -DEFUN(cfg_bts_gprs_nsvci, cfg_bts_gprs_nsvci_cmd, - "gprs nsvc <0-1> nsvci <0-65535>", - GPRS_TEXT NSVC_TEXT - "NS Virtual Connection Identifier\n" - "GPRS NS VC Identifier") -{ - struct gsm_bts *bts = vty->index; - int idx = atoi(argv[0]); - - if (bts->gprs.mode == BTS_GPRS_NONE) { - vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); - return CMD_WARNING; - } - - bts->gprs.nsvc[idx].nsvci = atoi(argv[1]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_gprs_nsvc_lport, cfg_bts_gprs_nsvc_lport_cmd, - "gprs nsvc <0-1> local udp port <0-65535>", - GPRS_TEXT NSVC_TEXT - "GPRS NS Local UDP Port\n" - "GPRS NS Local UDP Port\n" - "GPRS NS Local UDP Port\n" - "GPRS NS Local UDP Port Number\n") -{ - struct gsm_bts *bts = vty->index; - int idx = atoi(argv[0]); - - if (bts->gprs.mode == BTS_GPRS_NONE) { - vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); - return CMD_WARNING; - } - - bts->gprs.nsvc[idx].local_port = atoi(argv[1]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_gprs_nsvc_rport, cfg_bts_gprs_nsvc_rport_cmd, - "gprs nsvc <0-1> remote udp port <0-65535>", - GPRS_TEXT NSVC_TEXT - "GPRS NS Remote UDP Port\n" - "GPRS NS Remote UDP Port\n" - "GPRS NS Remote UDP Port\n" - "GPRS NS Remote UDP Port Number\n") -{ - struct gsm_bts *bts = vty->index; - int idx = atoi(argv[0]); - - if (bts->gprs.mode == BTS_GPRS_NONE) { - vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); - return CMD_WARNING; - } - - bts->gprs.nsvc[idx].remote_port = atoi(argv[1]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_gprs_nsvc_rip, cfg_bts_gprs_nsvc_rip_cmd, - "gprs nsvc <0-1> remote ip A.B.C.D", - GPRS_TEXT NSVC_TEXT - "GPRS NS Remote IP Address\n" - "GPRS NS Remote IP Address\n" - "GPRS NS Remote IP Address\n") -{ - struct gsm_bts *bts = vty->index; - int idx = atoi(argv[0]); - struct in_addr ia; - - if (bts->gprs.mode == BTS_GPRS_NONE) { - vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); - return CMD_WARNING; - } - - inet_aton(argv[1], &ia); - bts->gprs.nsvc[idx].remote_ip = ntohl(ia.s_addr); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_pag_free, cfg_bts_pag_free_cmd, - "paging free <-1-1024>", - "Paging options\n" - "Only page when having a certain amount of free slots\n" - "amount of required free paging slots. -1 to disable\n") -{ - struct gsm_bts *bts = vty->index; - - bts->paging.free_chans_need = atoi(argv[0]); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_gprs_ns_timer, cfg_bts_gprs_ns_timer_cmd, - "gprs ns timer " NS_TIMERS " <0-255>", - GPRS_TEXT "Network Service\n" - "Network Service Timer\n" - NS_TIMERS_HELP "Timer Value\n") -{ - struct gsm_bts *bts = vty->index; - int idx = get_string_value(gprs_ns_timer_strs, argv[0]); - int val = atoi(argv[1]); - - if (bts->gprs.mode == BTS_GPRS_NONE) { - vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); - return CMD_WARNING; - } - - if (idx < 0 || idx >= ARRAY_SIZE(bts->gprs.nse.timer)) - return CMD_WARNING; - - bts->gprs.nse.timer[idx] = val; - - return CMD_SUCCESS; -} - -#define BSSGP_TIMERS "(blocking-timer|blocking-retries|unblocking-retries|reset-timer|reset-retries|suspend-timer|suspend-retries|resume-timer|resume-retries|capability-update-timer|capability-update-retries)" -#define BSSGP_TIMERS_HELP \ - "Tbvc-block timeout\n" \ - "Tbvc-block retries\n" \ - "Tbvc-unblock retries\n" \ - "Tbvcc-reset timeout\n" \ - "Tbvc-reset retries\n" \ - "Tbvc-suspend timeout\n" \ - "Tbvc-suspend retries\n" \ - "Tbvc-resume timeout\n" \ - "Tbvc-resume retries\n" \ - "Tbvc-capa-update timeout\n" \ - "Tbvc-capa-update retries\n" - -DEFUN(cfg_bts_gprs_cell_timer, cfg_bts_gprs_cell_timer_cmd, - "gprs cell timer " BSSGP_TIMERS " <0-255>", - GPRS_TEXT "Cell / BSSGP\n" - "Cell/BSSGP Timer\n" - BSSGP_TIMERS_HELP "Timer Value\n") -{ - struct gsm_bts *bts = vty->index; - int idx = get_string_value(gprs_bssgp_cfg_strs, argv[0]); - int val = atoi(argv[1]); - - if (bts->gprs.mode == BTS_GPRS_NONE) { - vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); - return CMD_WARNING; - } - - if (idx < 0 || idx >= ARRAY_SIZE(bts->gprs.cell.timer)) - return CMD_WARNING; - - bts->gprs.cell.timer[idx] = val; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_gprs_rac, cfg_bts_gprs_rac_cmd, - "gprs routing area <0-255>", - GPRS_TEXT - "GPRS Routing Area Code\n" - "GPRS Routing Area Code\n" - "GPRS Routing Area Code\n") -{ - struct gsm_bts *bts = vty->index; - - if (bts->gprs.mode == BTS_GPRS_NONE) { - vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); - return CMD_WARNING; - } - - bts->gprs.rac = atoi(argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_gprs_ctrl_ack, cfg_bts_gprs_ctrl_ack_cmd, - "gprs control-ack-type-rach", GPRS_TEXT - "Set GPRS Control Ack Type for PACKET CONTROL ACKNOWLEDGMENT message to " - "four access bursts format instead of default RLC/MAC control block\n") -{ - struct gsm_bts *bts = vty->index; - - if (bts->gprs.mode == BTS_GPRS_NONE) { - vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); - return CMD_WARNING; - } - - bts->gprs.ctrl_ack_type_use_block = false; - - return CMD_SUCCESS; -} - -DEFUN(cfg_no_bts_gprs_ctrl_ack, cfg_no_bts_gprs_ctrl_ack_cmd, - "no gprs control-ack-type-rach", NO_STR GPRS_TEXT - "Set GPRS Control Ack Type for PACKET CONTROL ACKNOWLEDGMENT message to " - "four access bursts format instead of default RLC/MAC control block\n") -{ - struct gsm_bts *bts = vty->index; - - if (bts->gprs.mode == BTS_GPRS_NONE) { - vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); - return CMD_WARNING; - } - - bts->gprs.ctrl_ack_type_use_block = true; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_gprs_net_ctrl_ord, cfg_bts_gprs_net_ctrl_ord_cmd, - "gprs network-control-order (nc0|nc1|nc2)", - GPRS_TEXT - "GPRS Network Control Order\n" - "MS controlled cell re-selection, no measurement reporting\n" - "MS controlled cell re-selection, MS sends measurement reports\n" - "Network controlled cell re-selection, MS sends measurement reports\n") -{ - struct gsm_bts *bts = vty->index; - - if (bts->gprs.mode == BTS_GPRS_NONE) { - vty_out(vty, "%% GPRS not enabled on this BTS%s", VTY_NEWLINE); - return CMD_WARNING; - } - - bts->gprs.net_ctrl_ord = atoi(argv[0] + 2); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_gprs_mode, cfg_bts_gprs_mode_cmd, - "gprs mode (none|gprs|egprs)", - GPRS_TEXT - "GPRS Mode for this BTS\n" - "GPRS Disabled on this BTS\n" - "GPRS Enabled on this BTS\n" - "EGPRS (EDGE) Enabled on this BTS\n") -{ - struct gsm_bts *bts = vty->index; - enum bts_gprs_mode mode = bts_gprs_mode_parse(argv[0], NULL); - - if (!bts_gprs_mode_is_compat(bts, mode)) { - vty_out(vty, "This BTS type does not support %s%s", argv[0], - VTY_NEWLINE); - return CMD_WARNING; - } - - bts->gprs.mode = mode; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_gprs_11bit_rach_support_for_egprs, - cfg_bts_gprs_11bit_rach_support_for_egprs_cmd, - "gprs 11bit_rach_support_for_egprs (0|1)", - GPRS_TEXT "11 bit RACH options\n" - "Disable 11 bit RACH for EGPRS\n" - "Enable 11 bit RACH for EGPRS") -{ - struct gsm_bts *bts = vty->index; - - bts->gprs.supports_egprs_11bit_rach = atoi(argv[0]); - - if (bts->gprs.supports_egprs_11bit_rach > 1) { - vty_out(vty, "Error in RACH type%s", VTY_NEWLINE); - return CMD_WARNING; - } - - if ((bts->gprs.mode == BTS_GPRS_NONE) && - (bts->gprs.supports_egprs_11bit_rach == 1)) { - vty_out(vty, "Error:gprs mode is none and 11bit rach is" - " enabled%s", VTY_NEWLINE); - return CMD_WARNING; - } - - return CMD_SUCCESS; -} - -#define SI_TEXT "System Information Messages\n" -#define SI_TYPE_TEXT "(1|2|3|4|5|6|7|8|9|10|13|16|17|18|19|20|2bis|2ter|2quater|5bis|5ter)" -#define SI_TYPE_HELP "System Information Type 1\n" \ - "System Information Type 2\n" \ - "System Information Type 3\n" \ - "System Information Type 4\n" \ - "System Information Type 5\n" \ - "System Information Type 6\n" \ - "System Information Type 7\n" \ - "System Information Type 8\n" \ - "System Information Type 9\n" \ - "System Information Type 10\n" \ - "System Information Type 13\n" \ - "System Information Type 16\n" \ - "System Information Type 17\n" \ - "System Information Type 18\n" \ - "System Information Type 19\n" \ - "System Information Type 20\n" \ - "System Information Type 2bis\n" \ - "System Information Type 2ter\n" \ - "System Information Type 2quater\n" \ - "System Information Type 5bis\n" \ - "System Information Type 5ter\n" - -DEFUN(cfg_bts_si_mode, cfg_bts_si_mode_cmd, - "system-information " SI_TYPE_TEXT " mode (static|computed)", - SI_TEXT SI_TYPE_HELP - "System Information Mode\n" - "Static user-specified\n" - "Dynamic, BSC-computed\n") -{ - struct gsm_bts *bts = vty->index; - int type; - - type = get_string_value(osmo_sitype_strs, argv[0]); - if (type < 0) { - vty_out(vty, "Error SI Type%s", VTY_NEWLINE); - return CMD_WARNING; - } - - if (!strcmp(argv[1], "static")) - bts->si_mode_static |= (1 << type); - else - bts->si_mode_static &= ~(1 << type); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_si_static, cfg_bts_si_static_cmd, - "system-information " SI_TYPE_TEXT " static HEXSTRING", - SI_TEXT SI_TYPE_HELP - "Static System Information filling\n" - "Static user-specified SI content in HEX notation\n") -{ - struct gsm_bts *bts = vty->index; - int rc, type; - - type = get_string_value(osmo_sitype_strs, argv[0]); - if (type < 0) { - vty_out(vty, "Error SI Type%s", VTY_NEWLINE); - return CMD_WARNING; - } - - if (!(bts->si_mode_static & (1 << type))) { - vty_out(vty, "SI Type %s is not configured in static mode%s", - get_value_string(osmo_sitype_strs, type), VTY_NEWLINE); - return CMD_WARNING; - } - - /* Fill buffer with padding pattern */ - memset(GSM_BTS_SI(bts, type), 0x2b, GSM_MACBLOCK_LEN); - - /* Parse the user-specified SI in hex format, [partially] overwriting padding */ - rc = osmo_hexparse(argv[1], GSM_BTS_SI(bts, type), GSM_MACBLOCK_LEN); - if (rc < 0 || rc > GSM_MACBLOCK_LEN) { - vty_out(vty, "Error parsing HEXSTRING%s", VTY_NEWLINE); - return CMD_WARNING; - } - - /* Mark this SI as present */ - bts->si_valid |= (1 << type); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_early_cm, cfg_bts_early_cm_cmd, - "early-classmark-sending (allowed|forbidden)", - "Early Classmark Sending\n" - "Early Classmark Sending is allowed\n" - "Early Classmark Sending is forbidden\n") -{ - struct gsm_bts *bts = vty->index; - - if (!strcmp(argv[0], "allowed")) - bts->early_classmark_allowed = true; - else - bts->early_classmark_allowed = false; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_neigh_mode, cfg_bts_neigh_mode_cmd, - "neighbor-list mode (automatic|manual|manual-si5)", - "Neighbor List\n" "Mode of Neighbor List generation\n" - "Automatically from all BTS in this OpenBSC\n" "Manual\n" - "Manual with different lists for SI2 and SI5\n") -{ - struct gsm_bts *bts = vty->index; - int mode = get_string_value(bts_neigh_mode_strs, argv[0]); - - switch (mode) { - case NL_MODE_MANUAL_SI5SEP: - case NL_MODE_MANUAL: - /* make sure we clear the current list when switching to - * manual mode */ - if (bts->neigh_list_manual_mode == 0) - memset(&bts->si_common.data.neigh_list, 0, - sizeof(bts->si_common.data.neigh_list)); - break; - default: - break; - } - - bts->neigh_list_manual_mode = mode; - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_neigh, cfg_bts_neigh_cmd, - "neighbor-list (add|del) arfcn <0-1023>", - "Neighbor List\n" "Add to manual neighbor list\n" - "Delete from manual neighbor list\n" "ARFCN of neighbor\n" - "ARFCN of neighbor\n") -{ - struct gsm_bts *bts = vty->index; - struct bitvec *bv = &bts->si_common.neigh_list; - uint16_t arfcn = atoi(argv[1]); - - if (!bts->neigh_list_manual_mode) { - vty_out(vty, "%% Cannot configure neighbor list in " - "automatic mode%s", VTY_NEWLINE); - return CMD_WARNING; - } - - if (!strcmp(argv[0], "add")) - bitvec_set_bit_pos(bv, arfcn, 1); - else - bitvec_set_bit_pos(bv, arfcn, 0); - - return CMD_SUCCESS; -} - -/* help text should be kept in sync with EARFCN_*_INVALID defines */ -DEFUN(cfg_bts_si2quater_neigh_add, cfg_bts_si2quater_neigh_add_cmd, - "si2quater neighbor-list add earfcn <0-65535> thresh-hi <0-31> " - "thresh-lo <0-32> prio <0-8> qrxlv <0-32> meas <0-8>", - "SI2quater Neighbor List\n" "SI2quater Neighbor List\n" - "Add to manual SI2quater neighbor list\n" - "EARFCN of neighbor\n" "EARFCN of neighbor\n" - "threshold high bits\n" "threshold high bits\n" - "threshold low bits\n" "threshold low bits (32 means NA)\n" - "priority\n" "priority (8 means NA)\n" - "QRXLEVMIN\n" "QRXLEVMIN (32 means NA)\n" - "measurement bandwidth\n" "measurement bandwidth (8 means NA)\n") -{ - struct gsm_bts *bts = vty->index; - struct osmo_earfcn_si2q *e = &bts->si_common.si2quater_neigh_list; - uint16_t arfcn = atoi(argv[0]); - uint8_t thresh_hi = atoi(argv[1]), thresh_lo = atoi(argv[2]), - prio = atoi(argv[3]), qrx = atoi(argv[4]), meas = atoi(argv[5]); - int r = bts_earfcn_add(bts, arfcn, thresh_hi, thresh_lo, prio, qrx, meas); - - switch (r) { - case 1: - vty_out(vty, "Warning: multiple threshold-high are not supported, overriding with %u%s", - thresh_hi, VTY_NEWLINE); - break; - case EARFCN_THRESH_LOW_INVALID: - vty_out(vty, "Warning: multiple threshold-low are not supported, overriding with %u%s", - thresh_lo, VTY_NEWLINE); - break; - case EARFCN_QRXLV_INVALID + 1: - vty_out(vty, "Warning: multiple QRXLEVMIN are not supported, overriding with %u%s", - qrx, VTY_NEWLINE); - break; - case EARFCN_PRIO_INVALID: - vty_out(vty, "Warning: multiple priorities are not supported, overriding with %u%s", - prio, VTY_NEWLINE); - break; - default: - if (r < 0) { - vty_out(vty, "Unable to add ARFCN %u: %s%s", arfcn, strerror(-r), VTY_NEWLINE); - return CMD_WARNING; - } - } - - if (si2q_num(bts) <= SI2Q_MAX_NUM) - return CMD_SUCCESS; - - vty_out(vty, "Warning: not enough space in SI2quater (%u/%u used) for a given EARFCN %u%s", - bts->si2q_count, SI2Q_MAX_NUM, arfcn, VTY_NEWLINE); - osmo_earfcn_del(e, arfcn); - - return CMD_WARNING; -} - -DEFUN(cfg_bts_si2quater_neigh_del, cfg_bts_si2quater_neigh_del_cmd, - "si2quater neighbor-list del earfcn <0-65535>", - "SI2quater Neighbor List\n" - "SI2quater Neighbor List\n" - "Delete from SI2quater manual neighbor list\n" - "EARFCN of neighbor\n" - "EARFCN\n") -{ - struct gsm_bts *bts = vty->index; - struct osmo_earfcn_si2q *e = &bts->si_common.si2quater_neigh_list; - uint16_t arfcn = atoi(argv[0]); - int r = osmo_earfcn_del(e, arfcn); - if (r < 0) { - vty_out(vty, "Unable to delete arfcn %u: %s%s", arfcn, - strerror(-r), VTY_NEWLINE); - return CMD_WARNING; - } - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_si2quater_uarfcn_add, cfg_bts_si2quater_uarfcn_add_cmd, - "si2quater neighbor-list add uarfcn <0-16383> <0-511> <0-1>", - "SI2quater Neighbor List\n" - "SI2quater Neighbor List\n" "Add to manual SI2quater neighbor list\n" - "UARFCN of neighbor\n" "UARFCN of neighbor\n" "scrambling code\n" - "diversity bit\n") -{ - struct gsm_bts *bts = vty->index; - uint16_t arfcn = atoi(argv[0]), scramble = atoi(argv[1]); - - switch(bts_uarfcn_add(bts, arfcn, scramble, atoi(argv[2]))) { - case -ENOMEM: - vty_out(vty, "Unable to add UARFCN: max number of UARFCNs (%u) reached%s", MAX_EARFCN_LIST, VTY_NEWLINE); - return CMD_WARNING; - case -ENOSPC: - vty_out(vty, "Warning: not enough space in SI2quater for a given UARFCN (%u, %u)%s", - arfcn, scramble, VTY_NEWLINE); - return CMD_WARNING; - case -EADDRINUSE: - vty_out(vty, "Unable to add UARFCN: (%u, %u) is already added%s", arfcn, scramble, VTY_NEWLINE); - return CMD_WARNING; - } - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_si2quater_uarfcn_del, cfg_bts_si2quater_uarfcn_del_cmd, - "si2quater neighbor-list del uarfcn <0-16383> <0-511>", - "SI2quater Neighbor List\n" - "SI2quater Neighbor List\n" - "Delete from SI2quater manual neighbor list\n" - "UARFCN of neighbor\n" - "UARFCN\n" - "scrambling code\n") -{ - struct gsm_bts *bts = vty->index; - - if (bts_uarfcn_del(bts, atoi(argv[0]), atoi(argv[1])) < 0) { - vty_out(vty, "Unable to delete uarfcn: pair not found%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_si5_neigh, cfg_bts_si5_neigh_cmd, - "si5 neighbor-list (add|del) arfcn <0-1023>", - "SI5 Neighbor List\n" - "SI5 Neighbor List\n" "Add to manual SI5 neighbor list\n" - "Delete from SI5 manual neighbor list\n" "ARFCN of neighbor\n" - "ARFCN of neighbor\n") -{ - struct gsm_bts *bts = vty->index; - struct bitvec *bv = &bts->si_common.si5_neigh_list; - uint16_t arfcn = atoi(argv[1]); - - if (!bts->neigh_list_manual_mode) { - vty_out(vty, "%% Cannot configure neighbor list in " - "automatic mode%s", VTY_NEWLINE); - return CMD_WARNING; - } - - if (!strcmp(argv[0], "add")) - bitvec_set_bit_pos(bv, arfcn, 1); - else - bitvec_set_bit_pos(bv, arfcn, 0); - - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_pcu_sock, cfg_bts_pcu_sock_cmd, - "pcu-socket PATH", - "PCU Socket Path for using OsmoPCU co-located with BSC (legacy BTS)\n" - "Path in the file system for the unix-domain PCU socket\n") -{ - struct gsm_bts *bts = vty->index; - int rc; - - osmo_talloc_replace_string(bts, &bts->pcu_sock_path, argv[0]); - pcu_sock_exit(bts); - rc = pcu_sock_init(bts->pcu_sock_path, bts); - if (rc < 0) { - vty_out(vty, "%% Error creating PCU socket `%s' for BTS %u%s", - bts->pcu_sock_path, bts->nr, VTY_NEWLINE); - return CMD_WARNING; - } - - return CMD_SUCCESS; -} - -#define EXCL_RFLOCK_STR "Exclude this BTS from the global RF Lock\n" - -DEFUN(cfg_bts_excl_rf_lock, - cfg_bts_excl_rf_lock_cmd, - "rf-lock-exclude", - EXCL_RFLOCK_STR) -{ - struct gsm_bts *bts = vty->index; - bts->excl_from_rf_lock = 1; - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_no_excl_rf_lock, - cfg_bts_no_excl_rf_lock_cmd, - "no rf-lock-exclude", - NO_STR EXCL_RFLOCK_STR) -{ - struct gsm_bts *bts = vty->index; - bts->excl_from_rf_lock = 0; - return CMD_SUCCESS; -} - -#define FORCE_COMB_SI_STR "Force the generation of a single SI (no ter/bis)\n" - -DEFUN(cfg_bts_force_comb_si, - cfg_bts_force_comb_si_cmd, - "force-combined-si", - FORCE_COMB_SI_STR) -{ - struct gsm_bts *bts = vty->index; - bts->force_combined_si = 1; - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_no_force_comb_si, - cfg_bts_no_force_comb_si_cmd, - "no force-combined-si", - NO_STR FORCE_COMB_SI_STR) -{ - struct gsm_bts *bts = vty->index; - bts->force_combined_si = 0; - return CMD_SUCCESS; -} - -static void _get_codec_from_arg(struct vty *vty, int argc, const char *argv[]) -{ - struct gsm_bts *bts = vty->index; - struct bts_codec_conf *codec = &bts->codec; - int i; - - codec->hr = 0; - codec->efr = 0; - codec->amr = 0; - for (i = 0; i < argc; i++) { - if (!strcmp(argv[i], "hr")) - codec->hr = 1; - if (!strcmp(argv[i], "efr")) - codec->efr = 1; - if (!strcmp(argv[i], "amr")) - codec->amr = 1; - } -} - -#define CODEC_PAR_STR " (hr|efr|amr)" -#define CODEC_HELP_STR "Half Rate\n" \ - "Enhanced Full Rate\nAdaptive Multirate\n" - -DEFUN(cfg_bts_codec0, cfg_bts_codec0_cmd, - "codec-support fr", - "Codec Support settings\nFullrate\n") -{ - _get_codec_from_arg(vty, 0, argv); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_codec1, cfg_bts_codec1_cmd, - "codec-support fr" CODEC_PAR_STR, - "Codec Support settings\nFullrate\n" - CODEC_HELP_STR) -{ - _get_codec_from_arg(vty, 1, argv); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_codec2, cfg_bts_codec2_cmd, - "codec-support fr" CODEC_PAR_STR CODEC_PAR_STR, - "Codec Support settings\nFullrate\n" - CODEC_HELP_STR CODEC_HELP_STR) -{ - _get_codec_from_arg(vty, 2, argv); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_codec3, cfg_bts_codec3_cmd, - "codec-support fr" CODEC_PAR_STR CODEC_PAR_STR CODEC_PAR_STR, - "Codec Support settings\nFullrate\n" - CODEC_HELP_STR CODEC_HELP_STR CODEC_HELP_STR) -{ - _get_codec_from_arg(vty, 3, argv); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_codec4, cfg_bts_codec4_cmd, - "codec-support fr" CODEC_PAR_STR CODEC_PAR_STR CODEC_PAR_STR CODEC_PAR_STR, - "Codec Support settings\nFullrate\n" - CODEC_HELP_STR CODEC_HELP_STR CODEC_HELP_STR CODEC_HELP_STR) -{ - _get_codec_from_arg(vty, 4, argv); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_depends_on, cfg_bts_depends_on_cmd, - "depends-on-bts <0-255>", - "This BTS can only be started if another one is up\n" "BTS Number\n") -{ - struct gsm_bts *bts = vty->index; - struct gsm_bts *other_bts; - int dep = atoi(argv[0]); - - - if (!is_ipaccess_bts(bts)) { - vty_out(vty, "This feature is only available for IP systems.%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - other_bts = gsm_bts_num(bts->network, dep); - if (!other_bts || !is_ipaccess_bts(other_bts)) { - vty_out(vty, "This feature is only available for IP systems.%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - if (dep >= bts->nr) { - vty_out(vty, "%%Need to depend on an already declared unit.%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - bts_depend_mark(bts, dep); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_no_depends_on, cfg_bts_no_depends_on_cmd, - "depeneds-on-bts <0-255>", - NO_STR "This BTS can only be started if another one is up\n" - "BTS Number\n") -{ - struct gsm_bts *bts = vty->index; - int dep = atoi(argv[0]); - - bts_depend_clear(bts, dep); - return CMD_SUCCESS; -} - -#define AMR_TEXT "Adaptive Multi Rate settings\n" -#define AMR_MODE_TEXT "Codec modes to use with AMR codec\n" -#define AMR_START_TEXT "Initial codec to use with AMR\n" \ - "Automatically\nFirst codec\nSecond codec\nThird codec\nFourth codec\n" -#define AMR_TH_TEXT "AMR threshold between codecs\nMS side\nBTS side\n" -#define AMR_HY_TEXT "AMR hysteresis between codecs\nMS side\nBTS side\n" - -static void get_amr_from_arg(struct vty *vty, int argc, const char *argv[], int full) -{ - struct gsm_bts *bts = vty->index; - struct amr_multirate_conf *mr = (full) ? &bts->mr_full: &bts->mr_half; - struct gsm48_multi_rate_conf *mr_conf = - (struct gsm48_multi_rate_conf *) mr->gsm48_ie; - int i; - - mr->gsm48_ie[1] = 0; - for (i = 0; i < argc; i++) - mr->gsm48_ie[1] |= 1 << atoi(argv[i]); - mr_conf->icmi = 0; -} - -static void get_amr_th_from_arg(struct vty *vty, int argc, const char *argv[], int full) -{ - struct gsm_bts *bts = vty->index; - struct amr_multirate_conf *mr = (full) ? &bts->mr_full: &bts->mr_half; - struct amr_mode *modes; - int i; - - modes = argv[0][0]=='m' ? mr->ms_mode : mr->bts_mode; - for (i = 0; i < argc - 1; i++) - modes[i].threshold = atoi(argv[i + 1]); -} - -static void get_amr_hy_from_arg(struct vty *vty, int argc, const char *argv[], int full) -{ - struct gsm_bts *bts = vty->index; - struct amr_multirate_conf *mr = (full) ? &bts->mr_full: &bts->mr_half; - struct amr_mode *modes; - int i; - - modes = argv[0][0]=='m' ? mr->ms_mode : mr->bts_mode; - for (i = 0; i < argc - 1; i++) - modes[i].hysteresis = atoi(argv[i + 1]); -} - -static void get_amr_start_from_arg(struct vty *vty, const char *argv[], int full) -{ - struct gsm_bts *bts = vty->index; - struct amr_multirate_conf *mr = (full) ? &bts->mr_full: &bts->mr_half; - struct gsm48_multi_rate_conf *mr_conf = - (struct gsm48_multi_rate_conf *) mr->gsm48_ie; - int num = 0, i; - - for (i = 0; i < ((full) ? 8 : 6); i++) { - if ((mr->gsm48_ie[1] & (1 << i))) { - num++; - } - } - - if (argv[0][0] == 'a' || num == 0) - mr_conf->icmi = 0; - else { - mr_conf->icmi = 1; - if (num < atoi(argv[0])) - mr_conf->smod = num - 1; - else - mr_conf->smod = atoi(argv[0]) - 1; - } -} - -#define AMR_TCHF_PAR_STR " (0|1|2|3|4|5|6|7)" -#define AMR_TCHF_HELP_STR "4,75k\n5,15k\n5,90k\n6,70k\n7,40k\n7,95k\n" \ - "10,2k\n12,2k\n" - -#define AMR_TCHH_PAR_STR " (0|1|2|3|4|5)" -#define AMR_TCHH_HELP_STR "4,75k\n5,15k\n5,90k\n6,70k\n7,40k\n7,95k\n" - -#define AMR_TH_HELP_STR "Threshold between codec 1 and 2\n" -#define AMR_HY_HELP_STR "Hysteresis between codec 1 and 2\n" - -DEFUN(cfg_bts_amr_fr_modes1, cfg_bts_amr_fr_modes1_cmd, - "amr tch-f modes" AMR_TCHF_PAR_STR, - AMR_TEXT "Full Rate\n" AMR_MODE_TEXT - AMR_TCHF_HELP_STR) -{ - get_amr_from_arg(vty, 1, argv, 1); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_amr_fr_modes2, cfg_bts_amr_fr_modes2_cmd, - "amr tch-f modes" AMR_TCHF_PAR_STR AMR_TCHF_PAR_STR, - AMR_TEXT "Full Rate\n" AMR_MODE_TEXT - AMR_TCHF_HELP_STR AMR_TCHF_HELP_STR) -{ - get_amr_from_arg(vty, 2, argv, 1); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_amr_fr_modes3, cfg_bts_amr_fr_modes3_cmd, - "amr tch-f modes" AMR_TCHF_PAR_STR AMR_TCHF_PAR_STR AMR_TCHF_PAR_STR, - AMR_TEXT "Full Rate\n" AMR_MODE_TEXT - AMR_TCHF_HELP_STR AMR_TCHF_HELP_STR AMR_TCHF_HELP_STR) -{ - get_amr_from_arg(vty, 3, argv, 1); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_amr_fr_modes4, cfg_bts_amr_fr_modes4_cmd, - "amr tch-f modes" AMR_TCHF_PAR_STR AMR_TCHF_PAR_STR AMR_TCHF_PAR_STR AMR_TCHF_PAR_STR, - AMR_TEXT "Full Rate\n" AMR_MODE_TEXT - AMR_TCHF_HELP_STR AMR_TCHF_HELP_STR AMR_TCHF_HELP_STR AMR_TCHF_HELP_STR) -{ - get_amr_from_arg(vty, 4, argv, 1); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_amr_fr_start_mode, cfg_bts_amr_fr_start_mode_cmd, - "amr tch-f start-mode (auto|1|2|3|4)", - AMR_TEXT "Full Rate\n" AMR_START_TEXT) -{ - get_amr_start_from_arg(vty, argv, 1); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_amr_fr_thres1, cfg_bts_amr_fr_thres1_cmd, - "amr tch-f threshold (ms|bts) <0-63>", - AMR_TEXT "Full Rate\n" AMR_TH_TEXT - AMR_TH_HELP_STR) -{ - get_amr_th_from_arg(vty, 2, argv, 1); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_amr_fr_thres2, cfg_bts_amr_fr_thres2_cmd, - "amr tch-f threshold (ms|bts) <0-63> <0-63>", - AMR_TEXT "Full Rate\n" AMR_TH_TEXT - AMR_TH_HELP_STR AMR_TH_HELP_STR) -{ - get_amr_th_from_arg(vty, 3, argv, 1); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_amr_fr_thres3, cfg_bts_amr_fr_thres3_cmd, - "amr tch-f threshold (ms|bts) <0-63> <0-63> <0-63>", - AMR_TEXT "Full Rate\n" AMR_TH_TEXT - AMR_TH_HELP_STR AMR_TH_HELP_STR AMR_TH_HELP_STR) -{ - get_amr_th_from_arg(vty, 4, argv, 1); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_amr_fr_hyst1, cfg_bts_amr_fr_hyst1_cmd, - "amr tch-f hysteresis (ms|bts) <0-15>", - AMR_TEXT "Full Rate\n" AMR_HY_TEXT - AMR_HY_HELP_STR) -{ - get_amr_hy_from_arg(vty, 2, argv, 1); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_amr_fr_hyst2, cfg_bts_amr_fr_hyst2_cmd, - "amr tch-f hysteresis (ms|bts) <0-15> <0-15>", - AMR_TEXT "Full Rate\n" AMR_HY_TEXT - AMR_HY_HELP_STR AMR_HY_HELP_STR) -{ - get_amr_hy_from_arg(vty, 3, argv, 1); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_amr_fr_hyst3, cfg_bts_amr_fr_hyst3_cmd, - "amr tch-f hysteresis (ms|bts) <0-15> <0-15> <0-15>", - AMR_TEXT "Full Rate\n" AMR_HY_TEXT - AMR_HY_HELP_STR AMR_HY_HELP_STR AMR_HY_HELP_STR) -{ - get_amr_hy_from_arg(vty, 4, argv, 1); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_amr_hr_modes1, cfg_bts_amr_hr_modes1_cmd, - "amr tch-h modes" AMR_TCHH_PAR_STR, - AMR_TEXT "Half Rate\n" AMR_MODE_TEXT - AMR_TCHH_HELP_STR) -{ - get_amr_from_arg(vty, 1, argv, 0); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_amr_hr_modes2, cfg_bts_amr_hr_modes2_cmd, - "amr tch-h modes" AMR_TCHH_PAR_STR AMR_TCHH_PAR_STR, - AMR_TEXT "Half Rate\n" AMR_MODE_TEXT - AMR_TCHH_HELP_STR AMR_TCHH_HELP_STR) -{ - get_amr_from_arg(vty, 2, argv, 0); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_amr_hr_modes3, cfg_bts_amr_hr_modes3_cmd, - "amr tch-h modes" AMR_TCHH_PAR_STR AMR_TCHH_PAR_STR AMR_TCHH_PAR_STR, - AMR_TEXT "Half Rate\n" AMR_MODE_TEXT - AMR_TCHH_HELP_STR AMR_TCHH_HELP_STR AMR_TCHH_HELP_STR) -{ - get_amr_from_arg(vty, 3, argv, 0); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_amr_hr_modes4, cfg_bts_amr_hr_modes4_cmd, - "amr tch-h modes" AMR_TCHH_PAR_STR AMR_TCHH_PAR_STR AMR_TCHH_PAR_STR AMR_TCHH_PAR_STR, - AMR_TEXT "Half Rate\n" AMR_MODE_TEXT - AMR_TCHH_HELP_STR AMR_TCHH_HELP_STR AMR_TCHH_HELP_STR AMR_TCHH_HELP_STR) -{ - get_amr_from_arg(vty, 4, argv, 0); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_amr_hr_start_mode, cfg_bts_amr_hr_start_mode_cmd, - "amr tch-h start-mode (auto|1|2|3|4)", - AMR_TEXT "Half Rate\n" AMR_START_TEXT) -{ - get_amr_start_from_arg(vty, argv, 0); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_amr_hr_thres1, cfg_bts_amr_hr_thres1_cmd, - "amr tch-h threshold (ms|bts) <0-63>", - AMR_TEXT "Half Rate\n" AMR_TH_TEXT - AMR_TH_HELP_STR) -{ - get_amr_th_from_arg(vty, 2, argv, 0); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_amr_hr_thres2, cfg_bts_amr_hr_thres2_cmd, - "amr tch-h threshold (ms|bts) <0-63> <0-63>", - AMR_TEXT "Half Rate\n" AMR_TH_TEXT - AMR_TH_HELP_STR AMR_TH_HELP_STR) -{ - get_amr_th_from_arg(vty, 3, argv, 0); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_amr_hr_thres3, cfg_bts_amr_hr_thres3_cmd, - "amr tch-h threshold (ms|bts) <0-63> <0-63> <0-63>", - AMR_TEXT "Half Rate\n" AMR_TH_TEXT - AMR_TH_HELP_STR AMR_TH_HELP_STR AMR_TH_HELP_STR) -{ - get_amr_th_from_arg(vty, 4, argv, 0); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_amr_hr_hyst1, cfg_bts_amr_hr_hyst1_cmd, - "amr tch-h hysteresis (ms|bts) <0-15>", - AMR_TEXT "Half Rate\n" AMR_HY_TEXT - AMR_HY_HELP_STR) -{ - get_amr_hy_from_arg(vty, 2, argv, 0); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_amr_hr_hyst2, cfg_bts_amr_hr_hyst2_cmd, - "amr tch-h hysteresis (ms|bts) <0-15> <0-15>", - AMR_TEXT "Half Rate\n" AMR_HY_TEXT - AMR_HY_HELP_STR AMR_HY_HELP_STR) -{ - get_amr_hy_from_arg(vty, 3, argv, 0); - return CMD_SUCCESS; -} - -DEFUN(cfg_bts_amr_hr_hyst3, cfg_bts_amr_hr_hyst3_cmd, - "amr tch-h hysteresis (ms|bts) <0-15> <0-15> <0-15>", - AMR_TEXT "Half Rate\n" AMR_HY_TEXT - AMR_HY_HELP_STR AMR_HY_HELP_STR AMR_HY_HELP_STR) -{ - get_amr_hy_from_arg(vty, 4, argv, 0); - return CMD_SUCCESS; -} - -#define TRX_TEXT "Radio Transceiver\n" - -/* per TRX configuration */ -DEFUN(cfg_trx, - cfg_trx_cmd, - "trx <0-255>", - TRX_TEXT - "Select a TRX to configure") -{ - 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->index_sub = &trx->description; - vty->node = TRX_NODE; - - return CMD_SUCCESS; -} - -DEFUN(cfg_trx_arfcn, - cfg_trx_arfcn_cmd, - "arfcn <0-1023>", - "Set the ARFCN for this TRX\n" - "Absolute Radio Frequency Channel Number\n") -{ - int arfcn = atoi(argv[0]); - struct gsm_bts_trx *trx = vty->index; - - /* FIXME: check if this ARFCN is supported by this TRX */ - - trx->arfcn = arfcn; - - /* 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(cfg_trx_nominal_power, - cfg_trx_nominal_power_cmd, - "nominal power <0-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(cfg_trx_max_power_red, - cfg_trx_max_power_red_cmd, - "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; -} - -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(cfg_trx_rsl_e1_tei, - cfg_trx_rsl_e1_tei_cmd, - "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 = atoi(argv[0]); - - return CMD_SUCCESS; -} - -DEFUN(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") -{ - int locked = atoi(argv[0]); - struct gsm_bts_trx *trx = vty->index; - - gsm_trx_lock_rf(trx, locked); - return CMD_SUCCESS; -} - -/* per TS configuration */ -DEFUN(cfg_ts, - cfg_ts_cmd, - "timeslot <0-7>", - "Select a Timeslot to configure\n" - "Timeslot number\n") -{ - 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(cfg_ts_pchan, - cfg_ts_pchan_cmd, - "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 = pchanc; - - return CMD_SUCCESS; -} - -/* used for backwards compatibility with old config files that still - * have uppercase pchan type names */ -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) - return CMD_WARNING; - - ts->pchan = pchanc; - - return CMD_SUCCESS; -} - - - -DEFUN(cfg_ts_tsc, - cfg_ts_tsc_cmd, - "training_sequence_code <0-7>", - "Training Sequence Code of the Timeslot\n" "TSC\n") -{ - struct gsm_bts_trx_ts *ts = vty->index; - - if (!gsm_btsmodel_has_feature(ts->trx->bts->model, 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(cfg_ts_hopping, - cfg_ts_hopping_cmd, - "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; - int enabled = atoi(argv[0]); - - if (enabled && !gsm_btsmodel_has_feature(ts->trx->bts->model, BTS_FEAT_HOPPING)) { - vty_out(vty, "BTS model does not support hopping%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - ts->hopping.enabled = enabled; - - return CMD_SUCCESS; -} - -DEFUN(cfg_ts_hsn, - cfg_ts_hsn_cmd, - "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(cfg_ts_maio, - cfg_ts_maio_cmd, - "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(cfg_ts_arfcn_add, - cfg_ts_arfcn_add_cmd, - "hopping arfcn add <0-1023>", - HOPPING_STR "Configure hopping ARFCN list\n" - "Add an entry to the hopping ARFCN list\n" "ARFCN\n") -{ - struct gsm_bts_trx_ts *ts = vty->index; - int arfcn = atoi(argv[0]); - - bitvec_set_bit_pos(&ts->hopping.arfcns, arfcn, 1); - - return CMD_SUCCESS; -} - -DEFUN(cfg_ts_arfcn_del, - cfg_ts_arfcn_del_cmd, - "hopping arfcn del <0-1023>", - HOPPING_STR "Configure hopping ARFCN list\n" - "Delete an entry to the hopping ARFCN list\n" "ARFCN\n") -{ - struct gsm_bts_trx_ts *ts = vty->index; - int arfcn = atoi(argv[0]); - - bitvec_set_bit_pos(&ts->hopping.arfcns, arfcn, 0); - - return CMD_SUCCESS; -} - -DEFUN(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") -{ - struct gsm_bts_trx_ts *ts = vty->index; - - parse_e1_link(&ts->e1_link, argv[0], argv[1], argv[2]); - - return CMD_SUCCESS; -} - -void openbsc_vty_print_statistics(struct vty *vty, struct gsm_network *net) -{ - vty_out(vty, "Channel Requests : %lu total, %lu no channel%s", - net->bsc_ctrs->ctr[BSC_CTR_CHREQ_TOTAL].current, - net->bsc_ctrs->ctr[BSC_CTR_CHREQ_NO_CHANNEL].current, - VTY_NEWLINE); - vty_out(vty, "Channel Failures : %lu rf_failures, %lu rll failures%s", - net->bsc_ctrs->ctr[BSC_CTR_CHAN_RF_FAIL].current, - net->bsc_ctrs->ctr[BSC_CTR_CHAN_RLL_ERR].current, - VTY_NEWLINE); - vty_out(vty, "Paging : %lu attempted, %lu complete, %lu expired%s", - net->bsc_ctrs->ctr[BSC_CTR_PAGING_ATTEMPTED].current, - net->bsc_ctrs->ctr[BSC_CTR_PAGING_COMPLETED].current, - net->bsc_ctrs->ctr[BSC_CTR_PAGING_EXPIRED].current, - VTY_NEWLINE); - vty_out(vty, "BTS failures : %lu OML, %lu RSL%s", - net->bsc_ctrs->ctr[BSC_CTR_BTS_OML_FAIL].current, - net->bsc_ctrs->ctr[BSC_CTR_BTS_RSL_FAIL].current, - VTY_NEWLINE); -} - -DEFUN(drop_bts, - drop_bts_cmd, - "drop bts connection <0-65535> (oml|rsl)", - "Debug/Simulation command to drop Abis/IP BTS\n" - "Debug/Simulation command to drop Abis/IP BTS\n" - "Debug/Simulation command to drop Abis/IP BTS\n" - "BTS NR\n" "Drop OML Connection\n" "Drop RSL Connection\n") -{ - struct gsm_network *gsmnet; - struct gsm_bts_trx *trx; - struct gsm_bts *bts; - unsigned int bts_nr; - - gsmnet = gsmnet_from_vty(vty); - - bts_nr = atoi(argv[0]); - if (bts_nr >= gsmnet->num_bts) { - vty_out(vty, "BTS number must be between 0 and %d. It was %d.%s", - gsmnet->num_bts, bts_nr, VTY_NEWLINE); - return CMD_WARNING; - } - - bts = gsm_bts_num(gsmnet, bts_nr); - if (!bts) { - vty_out(vty, "BTS Nr. %d could not be found.%s", bts_nr, VTY_NEWLINE); - return CMD_WARNING; - } - - if (!is_ipaccess_bts(bts)) { - vty_out(vty, "This command only works for ipaccess.%s", VTY_NEWLINE); - return CMD_WARNING; - } - - - /* close all connections */ - if (strcmp(argv[1], "oml") == 0) { - ipaccess_drop_oml(bts); - } else if (strcmp(argv[1], "rsl") == 0) { - /* close all rsl connections */ - llist_for_each_entry(trx, &bts->trx_list, list) { - ipaccess_drop_rsl(trx); - } - } else { - vty_out(vty, "Argument must be 'oml# or 'rsl'.%s", VTY_NEWLINE); - return CMD_WARNING; - } - - return CMD_SUCCESS; -} - -DEFUN(restart_bts, restart_bts_cmd, - "restart-bts <0-65535>", - "Restart ip.access nanoBTS through OML\n" - "BTS Number\n") -{ - struct gsm_network *gsmnet; - struct gsm_bts_trx *trx; - struct gsm_bts *bts; - unsigned int bts_nr; - - gsmnet = gsmnet_from_vty(vty); - - bts_nr = atoi(argv[0]); - if (bts_nr >= gsmnet->num_bts) { - vty_out(vty, "BTS number must be between 0 and %d. It was %d.%s", - gsmnet->num_bts, bts_nr, VTY_NEWLINE); - return CMD_WARNING; - } - - bts = gsm_bts_num(gsmnet, bts_nr); - if (!bts) { - vty_out(vty, "BTS Nr. %d could not be found.%s", bts_nr, VTY_NEWLINE); - return CMD_WARNING; - } - - if (!is_ipaccess_bts(bts) || is_sysmobts_v2(bts)) { - vty_out(vty, "This command only works for ipaccess nanoBTS.%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - /* go from last TRX to c0 */ - llist_for_each_entry_reverse(trx, &bts->trx_list, list) - abis_nm_ipaccess_restart(trx); - - return CMD_SUCCESS; -} - -DEFUN(smscb_cmd, smscb_cmd_cmd, - "bts <0-255> smscb-command <1-4> HEXSTRING", - "BTS related commands\n" "BTS Number\n" - "SMS Cell Broadcast\n" "Last Valid Block\n" - "Hex Encoded SMSCB message (up to 88 octets)\n") -{ - struct gsm_bts *bts; - int bts_nr = atoi(argv[0]); - int last_block = atoi(argv[1]); - struct rsl_ie_cb_cmd_type cb_cmd; - uint8_t buf[88]; - int rc; - - bts = gsm_bts_num(gsmnet_from_vty(vty), bts_nr); - if (!bts) { - vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE); - return CMD_WARNING; - } - rc = osmo_hexparse(argv[2], buf, sizeof(buf)); - if (rc < 0 || rc > sizeof(buf)) { - vty_out(vty, "Error parsing HEXSTRING%s", VTY_NEWLINE); - return CMD_WARNING; - } - - cb_cmd.spare = 0; - cb_cmd.def_bcast = 0; - cb_cmd.command = RSL_CB_CMD_TYPE_NORMAL; - - switch (last_block) { - case 1: - cb_cmd.last_block = RSL_CB_CMD_LASTBLOCK_1; - break; - case 2: - cb_cmd.last_block = RSL_CB_CMD_LASTBLOCK_2; - break; - case 3: - cb_cmd.last_block = RSL_CB_CMD_LASTBLOCK_3; - break; - case 4: - cb_cmd.last_block = RSL_CB_CMD_LASTBLOCK_4; - break; - } - - rsl_sms_cb_command(bts, RSL_CHAN_SDCCH4_ACCH, cb_cmd, buf, rc); - - return CMD_SUCCESS; -} - -/* resolve a gsm_bts_trx_ts basd on the given numeric identifiers */ -static struct gsm_bts_trx_ts *vty_get_ts(struct vty *vty, const char *bts_str, const char *trx_str, - const char *ts_str) -{ - int bts_nr = atoi(bts_str); - int trx_nr = atoi(trx_str); - int ts_nr = atoi(ts_str); - struct gsm_bts *bts; - struct gsm_bts_trx *trx; - struct gsm_bts_trx_ts *ts; - - bts = gsm_bts_num(gsmnet_from_vty(vty), bts_nr); - if (!bts) { - vty_out(vty, "%% No such BTS (%d)%s", bts_nr, VTY_NEWLINE); - return NULL; - } - - trx = gsm_bts_trx_num(bts, trx_nr); - if (!trx) { - vty_out(vty, "%% No such TRX (%d)%s", trx_nr, VTY_NEWLINE); - return NULL; - } - - ts = &trx->ts[ts_nr]; - - return ts; -} - -DEFUN(pdch_act, pdch_act_cmd, - "bts <0-255> trx <0-255> timeslot <0-7> pdch (activate|deactivate)", - "BTS related commands\n" "BTS Number\n" "Transceiver\n" "Transceiver Number\n" - "TRX Timeslot\n" "Timeslot Number\n" "Packet Data Channel\n" - "Activate Dynamic PDCH/TCH (-> PDCH mode)\n" - "Deactivate Dynamic PDCH/TCH (-> TCH mode)\n") -{ - struct gsm_bts_trx_ts *ts; - int activate; - - ts = vty_get_ts(vty, argv[0], argv[1], argv[2]); - if (!ts) - return CMD_WARNING; - - if (!is_ipaccess_bts(ts->trx->bts)) { - vty_out(vty, "%% This command only works for ipaccess BTS%s", - VTY_NEWLINE); - return CMD_WARNING; - } - - if (ts->pchan != GSM_PCHAN_TCH_F_PDCH) { - vty_out(vty, "%% Timeslot %u is not in dynamic TCH_F/PDCH " - "mode%s", ts->nr, VTY_NEWLINE); - return CMD_WARNING; - } - - if (!strcmp(argv[3], "activate")) - activate = 1; - else - activate = 0; - - rsl_ipacc_pdch_activate(ts, activate); - - return CMD_SUCCESS; - -} - -/* determine the logical channel type based on the physical channel type */ -static int lchan_type_by_pchan(enum gsm_phys_chan_config pchan) -{ - switch (pchan) { - case GSM_PCHAN_TCH_F: - return GSM_LCHAN_TCH_F; - case GSM_PCHAN_TCH_H: - return GSM_LCHAN_TCH_H; - case GSM_PCHAN_SDCCH8_SACCH8C: - case GSM_PCHAN_SDCCH8_SACCH8C_CBCH: - case GSM_PCHAN_CCCH_SDCCH4: - case GSM_PCHAN_CCCH_SDCCH4_CBCH: - return GSM_LCHAN_SDCCH; - default: - return -1; - } -} - -/* configure the lchan for a single AMR mode (as specified) */ -static int lchan_set_single_amr_mode(struct gsm_lchan *lchan, uint8_t amr_mode) -{ - struct amr_multirate_conf mr; - struct gsm48_multi_rate_conf *mr_conf; - mr_conf = (struct gsm48_multi_rate_conf *) &mr.gsm48_ie; - - if (amr_mode > 7) - return -1; - - memset(&mr, 0, sizeof(mr)); - mr_conf->ver = 1; - /* bit-mask of supported modes, only one bit is set. Reflects - * Figure 10.5.2.47a where there are no thershold and only a - * single mode */ - mr.gsm48_ie[1] = 1 << amr_mode; - - mr.ms_mode[0].mode = amr_mode; - mr.bts_mode[0].mode = amr_mode; - - /* encode this configuration into the lchan for both uplink and - * downlink direction */ - gsm48_multirate_config(lchan->mr_ms_lv, &mr, mr.ms_mode); - gsm48_multirate_config(lchan->mr_bts_lv, &mr, mr.bts_mode); - - return 0; -} - -/* Debug/Measurement command to activate a given logical channel - * manually in a given mode/codec. This is useful for receiver - * performance testing (FER/RBER/...) */ -DEFUN(lchan_act, lchan_act_cmd, - "bts <0-255> trx <0-255> timeslot <0-7> sub-slot <0-7> (activate|deactivate) (hr|fr|efr|amr) [<0-7>]", - "BTS related commands\n" "BTS Number\n" "Transceiver\n" "Transceiver Number\n" - "TRX Timeslot\n" "Timeslot Number\n" "Sub-Slot Number\n" "Sub-Slot Number\n" - "Manual Channel Activation (e.g. for BER test)\n" - "Manual Channel Deactivation (e.g. for BER test)\n" - "Half-Rate v1\n" "Full-Rate\n" "Enhanced Full Rate\n" "Adaptive Multi-Rate\n" "AMR Mode\n") -{ - struct gsm_bts_trx_ts *ts; - struct gsm_lchan *lchan; - int ss_nr = atoi(argv[3]); - const char *act_str = argv[4]; - const char *codec_str = argv[5]; - int activate; - - ts = vty_get_ts(vty, argv[0], argv[1], argv[2]); - if (!ts) - return CMD_WARNING; - - lchan = &ts->lchan[ss_nr]; - - if (!strcmp(act_str, "activate")) - activate = 1; - else - activate = 0; - - if (ss_nr >= ts_subslots(ts)) { - vty_out(vty, "%% subslot %d >= permitted %d for physical channel %s%s", - ss_nr, ts_subslots(ts), gsm_pchan_name(ts->pchan), VTY_NEWLINE); - return CMD_WARNING; - } - - if (activate) { - int lchan_t; - if (lchan->state != LCHAN_S_NONE) { - vty_out(vty, "%% Cannot activate: Channel busy!%s", VTY_NEWLINE); - return CMD_WARNING; - } - lchan_t = lchan_type_by_pchan(ts->pchan); - if (lchan_t < 0) - return CMD_WARNING; - /* configure the lchan */ - lchan->type = lchan_t; - lchan->rsl_cmode = RSL_CMOD_SPD_SPEECH; - if (!strcmp(codec_str, "hr") || !strcmp(codec_str, "fr")) - lchan->tch_mode = GSM48_CMODE_SPEECH_V1; - else if (!strcmp(codec_str, "efr")) - lchan->tch_mode = GSM48_CMODE_SPEECH_EFR; - else if (!strcmp(codec_str, "amr")) { - int amr_mode; - if (argc < 7) { - vty_out(vty, "%% AMR requires specification of AMR mode%s", VTY_NEWLINE); - return CMD_WARNING; - } - amr_mode = atoi(argv[6]); - lchan->tch_mode = GSM48_CMODE_SPEECH_AMR; - lchan_set_single_amr_mode(lchan, amr_mode); - } - vty_out(vty, "%% activating lchan %s%s", gsm_lchan_name(lchan), VTY_NEWLINE); - rsl_chan_activate_lchan(lchan, RSL_ACT_TYPE_INITIAL, 0); - rsl_ipacc_crcx(lchan); - } else { - rsl_direct_rf_release(lchan); - } - - return CMD_SUCCESS; -} - -DEFUN(lchan_mdcx, lchan_mdcx_cmd, - "bts <0-255> trx <0-255> timeslot <0-7> sub-slot <0-7> mdcx A.B.C.D <0-65535>", - "BTS related commands\n" "BTS Number\n" "Transceiver\n" "Transceiver Number\n" - "TRX Timeslot\n" "Timeslot Number\n" "Sub-Slot\n" "Sub-Slot Number\n" - "Modify RTP Connection\n" "MGW IP Address\n" "MGW UDP Port\n") -{ - struct gsm_bts_trx_ts *ts; - struct gsm_lchan *lchan; - int ss_nr = atoi(argv[3]); - int port = atoi(argv[5]); - struct in_addr ia; - inet_aton(argv[4], &ia); - - ts = vty_get_ts(vty, argv[0], argv[1], argv[2]); - if (!ts) - return CMD_WARNING; - - lchan = &ts->lchan[ss_nr]; - - if (ss_nr >= ts_subslots(ts)) { - vty_out(vty, "%% subslot %d >= permitted %d for physical channel %s%s", - ss_nr, ts_subslots(ts), gsm_pchan_name(ts->pchan), VTY_NEWLINE); - return CMD_WARNING; - } - - vty_out(vty, "%% connecting RTP of %s to %s:%u%s", gsm_lchan_name(lchan), - inet_ntoa(ia), port, VTY_NEWLINE); - rsl_ipacc_mdcx(lchan, ntohl(ia.s_addr), port, 0); - return CMD_SUCCESS; -} -extern int bsc_vty_init_extra(void); - -int bsc_vty_init(struct gsm_network *network) -{ - 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); - - cfg_bts_type_cmd.string = - vty_cmd_string_from_valstr(tall_bsc_ctx, - bts_type_names, - "type (", "|", ")", - VTY_DO_LOWER); - cfg_bts_type_cmd.doc = - vty_cmd_string_from_valstr(tall_bsc_ctx, - bts_type_descs, - "BTS Vendor/Type\n", - "\n", "", 0); - - common_cs_vty_init(network, config_write_net); - - install_element_ve(&bsc_show_net_cmd); - install_element_ve(&show_bts_cmd); - install_element_ve(&show_trx_cmd); - install_element_ve(&show_ts_cmd); - install_element_ve(&show_lchan_cmd); - install_element_ve(&show_lchan_summary_cmd); - - install_element_ve(&show_paging_cmd); - install_element_ve(&show_paging_group_cmd); - - logging_vty_add_cmds(NULL); - osmo_stats_vty_add_cmds(); - - install_element(GSMNET_NODE, &cfg_net_neci_cmd); - install_element(GSMNET_NODE, &cfg_net_handover_cmd); - install_element(GSMNET_NODE, &cfg_net_ho_win_rxlev_avg_cmd); - install_element(GSMNET_NODE, &cfg_net_ho_win_rxqual_avg_cmd); - install_element(GSMNET_NODE, &cfg_net_ho_win_rxlev_avg_neigh_cmd); - install_element(GSMNET_NODE, &cfg_net_ho_pwr_interval_cmd); - install_element(GSMNET_NODE, &cfg_net_ho_pwr_hysteresis_cmd); - install_element(GSMNET_NODE, &cfg_net_ho_max_distance_cmd); - install_element(GSMNET_NODE, &cfg_net_T3101_cmd); - install_element(GSMNET_NODE, &cfg_net_T3103_cmd); - install_element(GSMNET_NODE, &cfg_net_T3105_cmd); - install_element(GSMNET_NODE, &cfg_net_T3107_cmd); - install_element(GSMNET_NODE, &cfg_net_T3109_cmd); - install_element(GSMNET_NODE, &cfg_net_T3111_cmd); - install_element(GSMNET_NODE, &cfg_net_T3113_cmd); - install_element(GSMNET_NODE, &cfg_net_T3115_cmd); - install_element(GSMNET_NODE, &cfg_net_T3117_cmd); - install_element(GSMNET_NODE, &cfg_net_T3119_cmd); - install_element(GSMNET_NODE, &cfg_net_T3122_cmd); - install_element(GSMNET_NODE, &cfg_net_T3141_cmd); - install_element(GSMNET_NODE, &cfg_net_dtx_cmd); - install_element(GSMNET_NODE, &cfg_net_pag_any_tch_cmd); - - install_element(GSMNET_NODE, &cfg_bts_cmd); - install_node(&bts_node, config_write_bts); - vty_install_default(BTS_NODE); - install_element(BTS_NODE, &cfg_bts_type_cmd); - install_element(BTS_NODE, &cfg_description_cmd); - install_element(BTS_NODE, &cfg_no_description_cmd); - install_element(BTS_NODE, &cfg_bts_band_cmd); - install_element(BTS_NODE, &cfg_bts_ci_cmd); - install_element(BTS_NODE, &cfg_bts_dtxu_cmd); - install_element(BTS_NODE, &cfg_bts_dtxd_cmd); - install_element(BTS_NODE, &cfg_bts_no_dtxu_cmd); - install_element(BTS_NODE, &cfg_bts_no_dtxd_cmd); - install_element(BTS_NODE, &cfg_bts_lac_cmd); - install_element(BTS_NODE, &cfg_bts_tsc_cmd); - install_element(BTS_NODE, &cfg_bts_bsic_cmd); - install_element(BTS_NODE, &cfg_bts_unit_id_cmd); - install_element(BTS_NODE, &cfg_bts_rsl_ip_cmd); - install_element(BTS_NODE, &cfg_bts_nokia_site_skip_reset_cmd); - install_element(BTS_NODE, &cfg_bts_nokia_site_no_loc_rel_cnf_cmd); - install_element(BTS_NODE, &cfg_bts_nokia_site_bts_reset_timer_cnf_cmd); - install_element(BTS_NODE, &cfg_bts_stream_id_cmd); - install_element(BTS_NODE, &cfg_bts_oml_e1_cmd); - install_element(BTS_NODE, &cfg_bts_oml_e1_tei_cmd); - install_element(BTS_NODE, &cfg_bts_challoc_cmd); - install_element(BTS_NODE, &cfg_bts_rach_tx_integer_cmd); - install_element(BTS_NODE, &cfg_bts_rach_max_trans_cmd); - install_element(BTS_NODE, &cfg_bts_chan_desc_att_cmd); - install_element(BTS_NODE, &cfg_bts_chan_desc_bs_pa_mfrms_cmd); - install_element(BTS_NODE, &cfg_bts_chan_desc_bs_ag_blks_res_cmd); - install_element(BTS_NODE, &cfg_bts_rach_nm_b_thresh_cmd); - install_element(BTS_NODE, &cfg_bts_rach_nm_ldavg_cmd); - install_element(BTS_NODE, &cfg_bts_cell_barred_cmd); - install_element(BTS_NODE, &cfg_bts_rach_ec_allowed_cmd); - install_element(BTS_NODE, &cfg_bts_rach_ac_class_cmd); - install_element(BTS_NODE, &cfg_bts_ms_max_power_cmd); - install_element(BTS_NODE, &cfg_bts_per_loc_upd_cmd); - install_element(BTS_NODE, &cfg_bts_no_per_loc_upd_cmd); - install_element(BTS_NODE, &cfg_bts_cell_resel_hyst_cmd); - install_element(BTS_NODE, &cfg_bts_rxlev_acc_min_cmd); - install_element(BTS_NODE, &cfg_bts_cell_bar_qualify_cmd); - install_element(BTS_NODE, &cfg_bts_cell_resel_ofs_cmd); - install_element(BTS_NODE, &cfg_bts_temp_ofs_cmd); - install_element(BTS_NODE, &cfg_bts_temp_ofs_inf_cmd); - install_element(BTS_NODE, &cfg_bts_penalty_time_cmd); - install_element(BTS_NODE, &cfg_bts_penalty_time_rsvd_cmd); - install_element(BTS_NODE, &cfg_bts_radio_link_timeout_cmd); - install_element(BTS_NODE, &cfg_bts_radio_link_timeout_inf_cmd); - install_element(BTS_NODE, &cfg_bts_gprs_mode_cmd); - install_element(BTS_NODE, &cfg_bts_gprs_11bit_rach_support_for_egprs_cmd); - install_element(BTS_NODE, &cfg_bts_gprs_ns_timer_cmd); - install_element(BTS_NODE, &cfg_bts_gprs_rac_cmd); - install_element(BTS_NODE, &cfg_bts_gprs_net_ctrl_ord_cmd); - install_element(BTS_NODE, &cfg_bts_gprs_ctrl_ack_cmd); - install_element(BTS_NODE, &cfg_no_bts_gprs_ctrl_ack_cmd); - install_element(BTS_NODE, &cfg_bts_gprs_bvci_cmd); - install_element(BTS_NODE, &cfg_bts_gprs_cell_timer_cmd); - install_element(BTS_NODE, &cfg_bts_gprs_nsei_cmd); - install_element(BTS_NODE, &cfg_bts_gprs_nsvci_cmd); - install_element(BTS_NODE, &cfg_bts_gprs_nsvc_lport_cmd); - install_element(BTS_NODE, &cfg_bts_gprs_nsvc_rport_cmd); - install_element(BTS_NODE, &cfg_bts_gprs_nsvc_rip_cmd); - install_element(BTS_NODE, &cfg_bts_pag_free_cmd); - install_element(BTS_NODE, &cfg_bts_si_mode_cmd); - install_element(BTS_NODE, &cfg_bts_si_static_cmd); - install_element(BTS_NODE, &cfg_bts_early_cm_cmd); - install_element(BTS_NODE, &cfg_bts_neigh_mode_cmd); - install_element(BTS_NODE, &cfg_bts_neigh_cmd); - install_element(BTS_NODE, &cfg_bts_si5_neigh_cmd); - install_element(BTS_NODE, &cfg_bts_si2quater_neigh_add_cmd); - install_element(BTS_NODE, &cfg_bts_si2quater_neigh_del_cmd); - install_element(BTS_NODE, &cfg_bts_si2quater_uarfcn_add_cmd); - install_element(BTS_NODE, &cfg_bts_si2quater_uarfcn_del_cmd); - install_element(BTS_NODE, &cfg_bts_excl_rf_lock_cmd); - install_element(BTS_NODE, &cfg_bts_no_excl_rf_lock_cmd); - install_element(BTS_NODE, &cfg_bts_force_comb_si_cmd); - install_element(BTS_NODE, &cfg_bts_no_force_comb_si_cmd); - install_element(BTS_NODE, &cfg_bts_codec0_cmd); - install_element(BTS_NODE, &cfg_bts_codec1_cmd); - install_element(BTS_NODE, &cfg_bts_codec2_cmd); - install_element(BTS_NODE, &cfg_bts_codec3_cmd); - install_element(BTS_NODE, &cfg_bts_codec4_cmd); - install_element(BTS_NODE, &cfg_bts_depends_on_cmd); - install_element(BTS_NODE, &cfg_bts_no_depends_on_cmd); - install_element(BTS_NODE, &cfg_bts_amr_fr_modes1_cmd); - install_element(BTS_NODE, &cfg_bts_amr_fr_modes2_cmd); - install_element(BTS_NODE, &cfg_bts_amr_fr_modes3_cmd); - install_element(BTS_NODE, &cfg_bts_amr_fr_modes4_cmd); - install_element(BTS_NODE, &cfg_bts_amr_fr_thres1_cmd); - install_element(BTS_NODE, &cfg_bts_amr_fr_thres2_cmd); - install_element(BTS_NODE, &cfg_bts_amr_fr_thres3_cmd); - install_element(BTS_NODE, &cfg_bts_amr_fr_hyst1_cmd); - install_element(BTS_NODE, &cfg_bts_amr_fr_hyst2_cmd); - install_element(BTS_NODE, &cfg_bts_amr_fr_hyst3_cmd); - install_element(BTS_NODE, &cfg_bts_amr_fr_start_mode_cmd); - install_element(BTS_NODE, &cfg_bts_amr_hr_modes1_cmd); - install_element(BTS_NODE, &cfg_bts_amr_hr_modes2_cmd); - install_element(BTS_NODE, &cfg_bts_amr_hr_modes3_cmd); - install_element(BTS_NODE, &cfg_bts_amr_hr_modes4_cmd); - install_element(BTS_NODE, &cfg_bts_amr_hr_thres1_cmd); - install_element(BTS_NODE, &cfg_bts_amr_hr_thres2_cmd); - install_element(BTS_NODE, &cfg_bts_amr_hr_thres3_cmd); - install_element(BTS_NODE, &cfg_bts_amr_hr_hyst1_cmd); - install_element(BTS_NODE, &cfg_bts_amr_hr_hyst2_cmd); - install_element(BTS_NODE, &cfg_bts_amr_hr_hyst3_cmd); - install_element(BTS_NODE, &cfg_bts_amr_hr_start_mode_cmd); - install_element(BTS_NODE, &cfg_bts_pcu_sock_cmd); - - install_element(BTS_NODE, &cfg_trx_cmd); - install_node(&trx_node, dummy_config_write); - vty_install_default(TRX_NODE); - 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); - vty_install_default(TS_NODE); - 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_e1_subslot_cmd); - - install_element(ENABLE_NODE, &drop_bts_cmd); - install_element(ENABLE_NODE, &restart_bts_cmd); - install_element(ENABLE_NODE, &pdch_act_cmd); - install_element(ENABLE_NODE, &lchan_act_cmd); - install_element(ENABLE_NODE, &lchan_mdcx_cmd); - install_element(ENABLE_NODE, &smscb_cmd_cmd); - - abis_nm_vty_init(); - abis_om2k_vty_init(); - e1inp_vty_init(); - osmo_fsm_vty_add_cmds(); - - bsc_vty_init_extra(); - - return 0; -} diff --git a/openbsc/src/libbsc/bts_ericsson_rbs2000.c b/openbsc/src/libbsc/bts_ericsson_rbs2000.c deleted file mode 100644 index 99da4e75f..000000000 --- a/openbsc/src/libbsc/bts_ericsson_rbs2000.c +++ /dev/null @@ -1,204 +0,0 @@ -/* Ericsson RBS-2xxx specific code */ - -/* (C) 2011 by Harald Welte <laforge@gnumonks.org> - * - * 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 <http://www.gnu.org/licenses/>. - * - */ - - -#include <osmocom/gsm/tlv.h> - -#include <openbsc/debug.h> -#include <openbsc/gsm_data.h> -#include <openbsc/abis_om2000.h> -#include <openbsc/abis_nm.h> -#include <osmocom/abis/e1_input.h> -#include <openbsc/signal.h> - -#include <osmocom/abis/lapd.h> - -static void bootstrap_om_bts(struct gsm_bts *bts) -{ - LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for BTS %u\n", bts->nr); - - /* FIXME: this is global init, not bootstrapping */ - abis_om2k_bts_init(bts); - abis_om2k_trx_init(bts->c0); - - /* TODO: Should we wait for a Failure report? */ - om2k_bts_fsm_start(bts); -} - -static void bootstrap_om_trx(struct gsm_bts_trx *trx) -{ - LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for TRX %u/%u\n", - trx->bts->nr, trx->nr); - /* FIXME */ -} - -static int shutdown_om(struct gsm_bts *bts) -{ - /* FIXME */ - return 0; -} - - -/* Tell LAPD to start start the SAP (send SABM requests) for all signalling - * timeslots in this line */ -static void start_sabm_in_line(struct e1inp_line *line, int start) -{ - struct e1inp_sign_link *link; - int i; - - for (i = 0; i < ARRAY_SIZE(line->ts); i++) { - struct e1inp_ts *ts = &line->ts[i]; - - if (ts->type != E1INP_TS_TYPE_SIGN) - continue; - - llist_for_each_entry(link, &ts->sign.sign_links, list) { - if (!ts->lapd) - continue; - lapd_instance_set_profile(ts->lapd, - &lapd_profile_abis_ericsson); - - if (start) - lapd_sap_start(ts->lapd, link->tei, link->sapi); - else - lapd_sap_stop(ts->lapd, link->tei, link->sapi); - } - } -} - -/* Callback function to be called every time we receive a signal from INPUT */ -static int gbl_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct gsm_bts *bts; - - if (subsys != SS_L_GLOBAL) - return 0; - - switch (signal) { - case S_GLOBAL_BTS_CLOSE_OM: - bts = signal_data; - if (bts->type == GSM_BTS_TYPE_RBS2000) - shutdown_om(signal_data); - break; - } - - return 0; -} - -/* Callback function to be called every time we receive a signal from INPUT */ -static int inp_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct input_signal_data *isd = signal_data; - struct e1inp_ts *e1i_ts; - - if (subsys != SS_L_INPUT) - return 0; - - LOGP(DNM, LOGL_DEBUG, "%s(): Input signal '%s' received\n", __func__, - get_value_string(e1inp_signal_names, signal)); - switch (signal) { - case S_L_INP_TEI_UP: - switch (isd->link_type) { - case E1INP_SIGN_OML: - if (isd->trx->bts->type != GSM_BTS_TYPE_RBS2000) - break; - if (isd->tei == isd->trx->bts->oml_tei) - bootstrap_om_bts(isd->trx->bts); - else - bootstrap_om_trx(isd->trx); - break; - } - break; - case S_L_INP_TEI_DN: - if (isd->trx->bts->type != GSM_BTS_TYPE_RBS2000) - break; - LOGP(DNM, LOGL_NOTICE, "Line-%u TS-%u TEI-%u SAPI-%u: Link " - "Lost for Ericsson RBS2000. Re-starting DL Establishment\n", - isd->line->num, isd->ts_nr, isd->tei, isd->sapi); - /* Some datalink for a given TEI/SAPI went down, try to re-start it */ - e1i_ts = &isd->line->ts[isd->ts_nr-1]; - OSMO_ASSERT(e1i_ts->type == E1INP_TS_TYPE_SIGN); - lapd_sap_start(e1i_ts->lapd, isd->tei, isd->sapi); - break; - case S_L_INP_LINE_INIT: - case S_L_INP_LINE_NOALARM: - if (strcasecmp(isd->line->driver->name, "DAHDI") - && strcasecmp(isd->line->driver->name, "MISDN_LAPD") - && strcasecmp(isd->line->driver->name, "UNIXSOCKET")) - break; - start_sabm_in_line(isd->line, 1); - break; - case S_L_INP_LINE_ALARM: - if (strcasecmp(isd->line->driver->name, "DAHDI") - && strcasecmp(isd->line->driver->name, "MISDN_LAPD") - && strcasecmp(isd->line->driver->name, "UNIXSOCKET")) - break; - start_sabm_in_line(isd->line, 0); - break; - } - - return 0; -} - -static void config_write_bts(struct vty *vty, struct gsm_bts *bts) -{ - abis_om2k_config_write_bts(vty, bts); -} - -static int bts_model_rbs2k_start(struct gsm_network *net); - -static void bts_model_rbs2k_e1line_bind_ops(struct e1inp_line *line) -{ - e1inp_line_bind_ops(line, &bts_isdn_e1inp_line_ops); -} - -static struct gsm_bts_model model_rbs2k = { - .type = GSM_BTS_TYPE_RBS2000, - .name = "rbs2000", - .start = bts_model_rbs2k_start, - .oml_rcvmsg = &abis_om2k_rcvmsg, - .config_write_bts = &config_write_bts, - .e1line_bind_ops = &bts_model_rbs2k_e1line_bind_ops, -}; - -static int bts_model_rbs2k_start(struct gsm_network *net) -{ - model_rbs2k.features.data = &model_rbs2k._features_data[0]; - model_rbs2k.features.data_len = sizeof(model_rbs2k._features_data); - - gsm_btsmodel_set_feature(&model_rbs2k, BTS_FEAT_GPRS); - gsm_btsmodel_set_feature(&model_rbs2k, BTS_FEAT_EGPRS); - gsm_btsmodel_set_feature(&model_rbs2k, BTS_FEAT_HOPPING); - gsm_btsmodel_set_feature(&model_rbs2k, BTS_FEAT_HSCSD); - gsm_btsmodel_set_feature(&model_rbs2k, BTS_FEAT_MULTI_TSC); - - osmo_signal_register_handler(SS_L_INPUT, inp_sig_cb, NULL); - osmo_signal_register_handler(SS_L_GLOBAL, gbl_sig_cb, NULL); - - return 0; -} - -int bts_model_rbs2k_init(void) -{ - return gsm_bts_model_register(&model_rbs2k); -} diff --git a/openbsc/src/libbsc/bts_init.c b/openbsc/src/libbsc/bts_init.c deleted file mode 100644 index d6b152a79..000000000 --- a/openbsc/src/libbsc/bts_init.c +++ /dev/null @@ -1,30 +0,0 @@ -/* (C) 2011 by Harald Welte <laforge@gnumonks.org> - * - * 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 <http://www.gnu.org/licenses/>. - * - */ -#include <openbsc/bss.h> - -int bts_init(void) -{ - bts_model_bs11_init(); - bts_model_rbs2k_init(); - bts_model_nanobts_init(); - bts_model_nokia_site_init(); - bts_model_sysmobts_init(); - /* Your new BTS here. */ - return 0; -} diff --git a/openbsc/src/libbsc/bts_ipaccess_nanobts.c b/openbsc/src/libbsc/bts_ipaccess_nanobts.c deleted file mode 100644 index a1bde778f..000000000 --- a/openbsc/src/libbsc/bts_ipaccess_nanobts.c +++ /dev/null @@ -1,520 +0,0 @@ -/* ip.access nanoBTS specific code */ - -/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org> - * - * 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 <http://www.gnu.org/licenses/>. - * - */ - -#include <arpa/inet.h> - -#include <osmocom/gsm/tlv.h> - -#include <openbsc/gsm_data.h> -#include <openbsc/signal.h> -#include <openbsc/abis_nm.h> -#include <osmocom/abis/e1_input.h> -#include <osmocom/gsm/tlv.h> -#include <osmocom/core/msgb.h> -#include <osmocom/core/talloc.h> -#include <openbsc/gsm_data.h> -#include <openbsc/abis_nm.h> -#include <openbsc/abis_rsl.h> -#include <openbsc/debug.h> -#include <osmocom/abis/subchan_demux.h> -#include <osmocom/gsm/ipa.h> -#include <osmocom/abis/ipaccess.h> -#include <osmocom/core/logging.h> -#include <openbsc/ipaccess.h> -#include <openbsc/bts_ipaccess_nanobts_omlattr.h> - -extern struct gsm_network *bsc_gsmnet; - -static int bts_model_nanobts_start(struct gsm_network *net); -static void bts_model_nanobts_e1line_bind_ops(struct e1inp_line *line); - -struct gsm_bts_model bts_model_nanobts = { - .type = GSM_BTS_TYPE_NANOBTS, - .name = "nanobts", - .start = bts_model_nanobts_start, - .oml_rcvmsg = &abis_nm_rcvmsg, - .e1line_bind_ops = bts_model_nanobts_e1line_bind_ops, - .nm_att_tlvdef = { - .def = { - /* ip.access specifics */ - [NM_ATT_IPACC_DST_IP] = { TLV_TYPE_FIXED, 4 }, - [NM_ATT_IPACC_DST_IP_PORT] = { TLV_TYPE_FIXED, 2 }, - [NM_ATT_IPACC_STREAM_ID] = { TLV_TYPE_TV, }, - [NM_ATT_IPACC_SEC_OML_CFG] = { TLV_TYPE_FIXED, 6 }, - [NM_ATT_IPACC_IP_IF_CFG] = { TLV_TYPE_FIXED, 8 }, - [NM_ATT_IPACC_IP_GW_CFG] = { TLV_TYPE_FIXED, 12 }, - [NM_ATT_IPACC_IN_SERV_TIME] = { TLV_TYPE_FIXED, 4 }, - [NM_ATT_IPACC_LOCATION] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_PAGING_CFG] = { TLV_TYPE_FIXED, 2 }, - [NM_ATT_IPACC_UNIT_ID] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_UNIT_NAME] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_SNMP_CFG] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_PRIM_OML_CFG_LIST] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_NV_FLAGS] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_FREQ_CTRL] = { TLV_TYPE_FIXED, 2 }, - [NM_ATT_IPACC_PRIM_OML_FB_TOUT] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_CUR_SW_CFG] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_TIMING_BUS] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_CGI] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_RAC] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_OBJ_VERSION] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_GPRS_PAGING_CFG]= { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_NSEI] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_BVCI] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_NSVCI] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_NS_CFG] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_BSSGP_CFG] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_NS_LINK_CFG] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_RLC_CFG] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_ALM_THRESH_LIST]= { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_MONIT_VAL_LIST] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_TIB_CONTROL] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_SUPP_FEATURES] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_CODING_SCHEMES] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_RLC_CFG_2] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_HEARTB_TOUT] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_UPTIME] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_RLC_CFG_3] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_SSL_CFG] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_SEC_POSSIBLE] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_IML_SSL_STATE] = { TLV_TYPE_TL16V }, - [NM_ATT_IPACC_REVOC_DATE] = { TLV_TYPE_TL16V }, - }, - }, -}; - - -/* Callback function to be called whenever we get a GSM 12.21 state change event */ -static int nm_statechg_event(int evt, struct nm_statechg_signal_data *nsd) -{ - uint8_t obj_class = nsd->obj_class; - void *obj = nsd->obj; - struct gsm_nm_state *new_state = nsd->new_state; - - struct gsm_bts *bts; - struct gsm_bts_trx *trx; - struct gsm_bts_trx_ts *ts; - struct gsm_bts_gprs_nsvc *nsvc; - - struct msgb *msgb; - - if (!is_ipaccess_bts(nsd->bts)) - return 0; - - /* This event-driven BTS setup is currently only required on nanoBTS */ - - /* S_NM_STATECHG_ADM is called after we call chg_adm_state() and would create - * endless loop */ - if (evt != S_NM_STATECHG_OPER) - return 0; - - switch (obj_class) { - case NM_OC_SITE_MANAGER: - bts = container_of(obj, struct gsm_bts, site_mgr); - if ((new_state->operational == NM_OPSTATE_ENABLED && - new_state->availability == NM_AVSTATE_OK) || - (new_state->operational == NM_OPSTATE_DISABLED && - new_state->availability == NM_AVSTATE_OFF_LINE)) - abis_nm_opstart(bts, obj_class, 0xff, 0xff, 0xff); - break; - case NM_OC_BTS: - bts = obj; - if (new_state->availability == NM_AVSTATE_DEPENDENCY) { - msgb = nanobts_attr_bts_get(bts); - abis_nm_set_bts_attr(bts, msgb->data, msgb->len); - msgb_free(msgb); - abis_nm_chg_adm_state(bts, obj_class, - bts->bts_nr, 0xff, 0xff, - NM_STATE_UNLOCKED); - abis_nm_opstart(bts, obj_class, - bts->bts_nr, 0xff, 0xff); - } - break; - case NM_OC_CHANNEL: - ts = obj; - trx = ts->trx; - if (new_state->operational == NM_OPSTATE_DISABLED && - new_state->availability == NM_AVSTATE_DEPENDENCY) { - enum abis_nm_chan_comb ccomb = - abis_nm_chcomb4pchan(ts->pchan); - if (abis_nm_set_channel_attr(ts, ccomb) == -EINVAL) { - ipaccess_drop_oml(trx->bts); - return -1; - } - abis_nm_chg_adm_state(trx->bts, obj_class, - trx->bts->bts_nr, trx->nr, ts->nr, - NM_STATE_UNLOCKED); - abis_nm_opstart(trx->bts, obj_class, - trx->bts->bts_nr, trx->nr, ts->nr); - } - if (new_state->operational == NM_OPSTATE_ENABLED - && new_state->availability == NM_AVSTATE_OK) - dyn_ts_init(ts); - break; - case NM_OC_RADIO_CARRIER: - trx = obj; - if (new_state->operational == NM_OPSTATE_DISABLED && - new_state->availability == NM_AVSTATE_OK) - abis_nm_opstart(trx->bts, obj_class, trx->bts->bts_nr, - trx->nr, 0xff); - break; - case NM_OC_GPRS_NSE: - bts = container_of(obj, struct gsm_bts, gprs.nse); - if (bts->gprs.mode == BTS_GPRS_NONE) - break; - if (new_state->availability == NM_AVSTATE_DEPENDENCY) { - msgb = nanobts_attr_nse_get(bts); - abis_nm_ipaccess_set_attr(bts, obj_class, bts->bts_nr, - 0xff, 0xff, msgb->data, - msgb->len); - msgb_free(msgb); - abis_nm_opstart(bts, obj_class, bts->bts_nr, - 0xff, 0xff); - } - break; - case NM_OC_GPRS_CELL: - bts = container_of(obj, struct gsm_bts, gprs.cell); - if (bts->gprs.mode == BTS_GPRS_NONE) - break; - if (new_state->availability == NM_AVSTATE_DEPENDENCY) { - msgb = nanobts_attr_cell_get(bts); - abis_nm_ipaccess_set_attr(bts, obj_class, bts->bts_nr, - 0, 0xff, msgb->data, - msgb->len); - msgb_free(msgb); - abis_nm_opstart(bts, obj_class, bts->bts_nr, - 0, 0xff); - abis_nm_chg_adm_state(bts, obj_class, bts->bts_nr, - 0, 0xff, NM_STATE_UNLOCKED); - abis_nm_chg_adm_state(bts, NM_OC_GPRS_NSE, bts->bts_nr, - 0xff, 0xff, NM_STATE_UNLOCKED); - } - break; - case NM_OC_GPRS_NSVC: - nsvc = obj; - bts = nsvc->bts; - if (bts->gprs.mode == BTS_GPRS_NONE) - break; - /* We skip NSVC1 since we only use NSVC0 */ - if (nsvc->id == 1) - break; - if ((new_state->availability == NM_AVSTATE_OFF_LINE) || - (new_state->availability == NM_AVSTATE_DEPENDENCY)) { - msgb = nanobts_attr_nscv_get(bts); - abis_nm_ipaccess_set_attr(bts, obj_class, bts->bts_nr, - nsvc->id, 0xff, - msgb->data, msgb->len); - msgb_free(msgb); - abis_nm_opstart(bts, obj_class, bts->bts_nr, - nsvc->id, 0xff); - abis_nm_chg_adm_state(bts, obj_class, bts->bts_nr, - nsvc->id, 0xff, - NM_STATE_UNLOCKED); - } - default: - break; - } - return 0; -} - -/* Callback function to be called every time we receive a 12.21 SW activated report */ -static int sw_activ_rep(struct msgb *mb) -{ - struct abis_om_fom_hdr *foh = msgb_l3(mb); - struct e1inp_sign_link *sign_link = mb->dst; - struct gsm_bts *bts = sign_link->trx->bts; - struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, foh->obj_inst.trx_nr); - - if (!trx) - return -EINVAL; - - if (!is_ipaccess_bts(trx->bts)) - return 0; - - switch (foh->obj_class) { - case NM_OC_BASEB_TRANSC: - abis_nm_chg_adm_state(trx->bts, foh->obj_class, - trx->bts->bts_nr, trx->nr, 0xff, - NM_STATE_UNLOCKED); - abis_nm_opstart(trx->bts, foh->obj_class, - trx->bts->bts_nr, trx->nr, 0xff); - /* TRX software is active, tell it to initiate RSL Link */ - abis_nm_ipaccess_rsl_connect(trx, trx->bts->ip_access.rsl_ip, - 3003, trx->rsl_tei); - break; - case NM_OC_RADIO_CARRIER: { - /* - * Locking the radio carrier will make it go - * offline again and we would come here. The - * framework should determine that there was - * no change and avoid recursion. - * - * This code is here to make sure that on start - * a TRX remains locked. - */ - int rc_state = trx->mo.nm_state.administrative; - /* Patch ARFCN into radio attribute */ - struct msgb *msgb = nanobts_attr_radio_get(trx->bts, trx); - abis_nm_set_radio_attr(trx, msgb->data, msgb->len); - msgb_free(msgb); - abis_nm_chg_adm_state(trx->bts, foh->obj_class, - trx->bts->bts_nr, trx->nr, 0xff, - rc_state); - abis_nm_opstart(trx->bts, foh->obj_class, trx->bts->bts_nr, - trx->nr, 0xff); - break; - } - } - return 0; -} - -/* Callback function to be called every time we receive a signal from NM */ -static int bts_ipa_nm_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - if (subsys != SS_NM) - return 0; - - switch (signal) { - case S_NM_SW_ACTIV_REP: - return sw_activ_rep(signal_data); - case S_NM_STATECHG_OPER: - case S_NM_STATECHG_ADM: - return nm_statechg_event(signal, signal_data); - default: - break; - } - return 0; -} - -static int bts_model_nanobts_start(struct gsm_network *net) -{ - osmo_signal_unregister_handler(SS_NM, bts_ipa_nm_sig_cb, NULL); - osmo_signal_register_handler(SS_NM, bts_ipa_nm_sig_cb, NULL); - return 0; -} - -int bts_model_nanobts_init(void) -{ - bts_model_nanobts.features.data = &bts_model_nanobts._features_data[0]; - bts_model_nanobts.features.data_len = - sizeof(bts_model_nanobts._features_data); - - gsm_btsmodel_set_feature(&bts_model_nanobts, BTS_FEAT_GPRS); - gsm_btsmodel_set_feature(&bts_model_nanobts, BTS_FEAT_EGPRS); - gsm_btsmodel_set_feature(&bts_model_nanobts, BTS_FEAT_MULTI_TSC); - - return gsm_bts_model_register(&bts_model_nanobts); -} - -#define OML_UP 0x0001 -#define RSL_UP 0x0002 - -static struct gsm_bts * -find_bts_by_unitid(struct gsm_network *net, uint16_t site_id, uint16_t bts_id) -{ - struct gsm_bts *bts; - - llist_for_each_entry(bts, &net->bts_list, list) { - if (!is_ipaccess_bts(bts)) - continue; - - if (bts->ip_access.site_id == site_id && - bts->ip_access.bts_id == bts_id) - return bts; - } - return NULL; -} - -/* These are exported because they are used by the VTY interface. */ -void ipaccess_drop_rsl(struct gsm_bts_trx *trx) -{ - if (!trx->rsl_link) - return; - - e1inp_sign_link_destroy(trx->rsl_link); - trx->rsl_link = NULL; -} - -void ipaccess_drop_oml(struct gsm_bts *bts) -{ - struct gsm_bts *rdep_bts; - struct gsm_bts_trx *trx; - - if (!bts->oml_link) - return; - - e1inp_sign_link_destroy(bts->oml_link); - bts->oml_link = NULL; - - /* we have issues reconnecting RSL, drop everything. */ - llist_for_each_entry(trx, &bts->trx_list, list) - ipaccess_drop_rsl(trx); - - bts->ip_access.flags = 0; - - /* - * Go through the list and see if we are the depndency of a BTS - * and then drop the BTS. This can lead to some recursion but it - * should be fine in userspace. - * The oml_link is serving as recursion anchor for us and - * it is set to NULL some lines above. - */ - llist_for_each_entry(rdep_bts, &bts->network->bts_list, list) { - if (!bts_depend_is_depedency(rdep_bts, bts)) - continue; - LOGP(DLINP, LOGL_NOTICE, "Dropping BTS(%u) due BTS(%u).\n", - rdep_bts->nr, bts->nr); - ipaccess_drop_oml(rdep_bts); - } -} - -/* This function is called once the OML/RSL link becomes up. */ -static struct e1inp_sign_link * -ipaccess_sign_link_up(void *unit_data, struct e1inp_line *line, - enum e1inp_sign_type type) -{ - struct gsm_bts *bts; - struct ipaccess_unit *dev = unit_data; - struct e1inp_sign_link *sign_link = NULL; - - bts = find_bts_by_unitid(bsc_gsmnet, dev->site_id, dev->bts_id); - if (!bts) { - LOGP(DLINP, LOGL_ERROR, "Unable to find BTS configuration for " - " %u/%u/%u, disconnecting\n", dev->site_id, - dev->bts_id, dev->trx_id); - return NULL; - } - DEBUGP(DLINP, "Identified BTS %u/%u/%u\n", - dev->site_id, dev->bts_id, dev->trx_id); - - switch(type) { - case E1INP_SIGN_OML: - /* remove old OML signal link for this BTS. */ - ipaccess_drop_oml(bts); - - if (!bts_depend_check(bts)) { - LOGP(DLINP, LOGL_NOTICE, - "Dependency not full-filled for %u/%u/%u\n", - dev->site_id, dev->bts_id, dev->trx_id); - return NULL; - } - - /* create new OML link. */ - sign_link = bts->oml_link = - e1inp_sign_link_create(&line->ts[E1INP_SIGN_OML - 1], - E1INP_SIGN_OML, bts->c0, - bts->oml_tei, 0); - break; - case E1INP_SIGN_RSL: { - struct e1inp_ts *ts; - struct gsm_bts_trx *trx = gsm_bts_trx_num(bts, dev->trx_id); - - /* no OML link set yet? give up. */ - if (!bts->oml_link || !trx) - return NULL; - - /* remove old RSL link for this TRX. */ - ipaccess_drop_rsl(trx); - - /* set new RSL link for this TRX. */ - line = bts->oml_link->ts->line; - ts = &line->ts[E1INP_SIGN_RSL + dev->trx_id - 1]; - e1inp_ts_config_sign(ts, line); - sign_link = trx->rsl_link = - e1inp_sign_link_create(ts, E1INP_SIGN_RSL, - trx, trx->rsl_tei, 0); - trx->rsl_link->ts->sign.delay = 0; - break; - } - default: - break; - } - return sign_link; -} - -static void ipaccess_sign_link_down(struct e1inp_line *line) -{ - /* No matter what link went down, we close both signal links. */ - struct e1inp_ts *ts = &line->ts[E1INP_SIGN_OML-1]; - struct e1inp_sign_link *link; - - llist_for_each_entry(link, &ts->sign.sign_links, list) { - struct gsm_bts *bts = link->trx->bts; - - ipaccess_drop_oml(bts); - /* Yes, we only use the first element of the list. */ - break; - } -} - -/* This function is called if we receive one OML/RSL message. */ -static int ipaccess_sign_link(struct msgb *msg) -{ - int ret = 0; - struct e1inp_sign_link *link = msg->dst; - struct e1inp_ts *e1i_ts = link->ts; - - switch (link->type) { - case E1INP_SIGN_RSL: - if (!(link->trx->bts->ip_access.flags & - (RSL_UP << link->trx->nr))) { - e1inp_event(e1i_ts, S_L_INP_TEI_UP, - link->tei, link->sapi); - link->trx->bts->ip_access.flags |= - (RSL_UP << link->trx->nr); - } - ret = abis_rsl_rcvmsg(msg); - break; - case E1INP_SIGN_OML: - if (!(link->trx->bts->ip_access.flags & OML_UP)) { - e1inp_event(e1i_ts, S_L_INP_TEI_UP, - link->tei, link->sapi); - link->trx->bts->ip_access.flags |= OML_UP; - } - ret = abis_nm_rcvmsg(msg); - break; - default: - LOGP(DLINP, LOGL_ERROR, "Unknown signal link type %d\n", - link->type); - msgb_free(msg); - break; - } - return ret; -} - -/* not static, ipaccess-config needs it. */ -struct e1inp_line_ops ipaccess_e1inp_line_ops = { - .cfg = { - .ipa = { - .addr = "0.0.0.0", - .role = E1INP_LINE_R_BSC, - }, - }, - .sign_link_up = ipaccess_sign_link_up, - .sign_link_down = ipaccess_sign_link_down, - .sign_link = ipaccess_sign_link, -}; - -static void bts_model_nanobts_e1line_bind_ops(struct e1inp_line *line) -{ - e1inp_line_bind_ops(line, &ipaccess_e1inp_line_ops); -} diff --git a/openbsc/src/libbsc/bts_ipaccess_nanobts_omlattr.c b/openbsc/src/libbsc/bts_ipaccess_nanobts_omlattr.c deleted file mode 100644 index 473e1caea..000000000 --- a/openbsc/src/libbsc/bts_ipaccess_nanobts_omlattr.c +++ /dev/null @@ -1,240 +0,0 @@ -/* ip.access nanoBTS specific code, OML attribute table generator */ - -/* (C) 2016 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> - * All Rights Reserved - * - * Author: Philipp Maier - * - * 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 <http://www.gnu.org/licenses/>. - */ - -#include <arpa/inet.h> -#include <osmocom/core/msgb.h> -#include <openbsc/gsm_data.h> -#include <openbsc/abis_nm.h> - -static void patch_16(uint8_t *data, const uint16_t val) -{ - memcpy(data, &val, sizeof(val)); -} - -static void patch_32(uint8_t *data, const uint32_t val) -{ - memcpy(data, &val, sizeof(val)); -} - -struct msgb *nanobts_attr_bts_get(struct gsm_bts *bts) -{ - struct msgb *msgb; - uint8_t buf[256]; - int rlt; - msgb = msgb_alloc(1024, "nanobts_attr_bts"); - - memcpy(buf, "\x55\x5b\x61\x67\x6d\x73", 6); - msgb_tv_fixed_put(msgb, NM_ATT_INTERF_BOUND, 6, buf); - - /* interference avg. period in numbers of SACCH multifr */ - msgb_tv_put(msgb, NM_ATT_INTAVE_PARAM, 0x06); - - rlt = gsm_bts_get_radio_link_timeout(bts); - if (rlt == -1) { - /* Osmocom extension: Use infinite radio link timeout */ - buf[0] = 0xFF; - buf[1] = 0x00; - } else { - /* conn fail based on SACCH error rate */ - buf[0] = 0x01; - buf[1] = rlt; - } - msgb_tl16v_put(msgb, NM_ATT_CONN_FAIL_CRIT, 2, buf); - - memcpy(buf, "\x1e\x24\x24\xa8\x34\x21\xa8", 7); - msgb_tv_fixed_put(msgb, NM_ATT_T200, 7, buf); - - msgb_tv_put(msgb, NM_ATT_MAX_TA, 0x3f); - - /* seconds */ - memcpy(buf, "\x00\x01\x0a", 3); - msgb_tv_fixed_put(msgb, NM_ATT_OVERL_PERIOD, 3, buf); - - /* percent */ - msgb_tv_put(msgb, NM_ATT_CCCH_L_T, 10); - - /* seconds */ - msgb_tv_put(msgb, NM_ATT_CCCH_L_I_P, 1); - - /* busy threshold in - dBm */ - buf[0] = 10; - if (bts->rach_b_thresh != -1) - buf[0] = bts->rach_b_thresh & 0xff; - msgb_tv_put(msgb, NM_ATT_RACH_B_THRESH, buf[0]); - - /* rach load averaging 1000 slots */ - buf[0] = 0x03; - buf[1] = 0xe8; - if (bts->rach_ldavg_slots != -1) { - buf[0] = (bts->rach_ldavg_slots >> 8) & 0x0f; - buf[1] = bts->rach_ldavg_slots & 0xff; - } - msgb_tv_fixed_put(msgb, NM_ATT_LDAVG_SLOTS, 2, buf); - - /* miliseconds */ - msgb_tv_put(msgb, NM_ATT_BTS_AIR_TIMER, 128); - - /* 10 retransmissions of physical config */ - msgb_tv_put(msgb, NM_ATT_NY1, 10); - - buf[0] = (bts->c0->arfcn >> 8) & 0x0f; - buf[1] = bts->c0->arfcn & 0xff; - msgb_tv_fixed_put(msgb, NM_ATT_BCCH_ARFCN, 2, buf); - - msgb_tv_put(msgb, NM_ATT_BSIC, bts->bsic); - - abis_nm_ipaccess_cgi(buf, bts); - msgb_tl16v_put(msgb, NM_ATT_IPACC_CGI, 7, buf); - - return msgb; -} - -struct msgb *nanobts_attr_nse_get(struct gsm_bts *bts) -{ - struct msgb *msgb; - uint8_t buf[256]; - msgb = msgb_alloc(1024, "nanobts_attr_bts"); - - /* NSEI 925 */ - buf[0] = bts->gprs.nse.nsei >> 8; - buf[1] = bts->gprs.nse.nsei & 0xff; - msgb_tl16v_put(msgb, NM_ATT_IPACC_NSEI, 2, buf); - - /* all timers in seconds */ - OSMO_ASSERT(ARRAY_SIZE(bts->gprs.nse.timer) < sizeof(buf)); - memcpy(buf, bts->gprs.nse.timer, ARRAY_SIZE(bts->gprs.nse.timer)); - msgb_tl16v_put(msgb, NM_ATT_IPACC_NS_CFG, 7, buf); - - /* all timers in seconds */ - buf[0] = 3; /* blockimg timer (T1) */ - buf[1] = 3; /* blocking retries */ - buf[2] = 3; /* unblocking retries */ - buf[3] = 3; /* reset timer (T2) */ - buf[4] = 3; /* reset retries */ - buf[5] = 10; /* suspend timer (T3) in 100ms */ - buf[6] = 3; /* suspend retries */ - buf[7] = 10; /* resume timer (T4) in 100ms */ - buf[8] = 3; /* resume retries */ - buf[9] = 10; /* capability update timer (T5) */ - buf[10] = 3; /* capability update retries */ - - OSMO_ASSERT(ARRAY_SIZE(bts->gprs.cell.timer) < sizeof(buf)); - memcpy(buf, bts->gprs.cell.timer, ARRAY_SIZE(bts->gprs.cell.timer)); - msgb_tl16v_put(msgb, NM_ATT_IPACC_BSSGP_CFG, 11, buf); - - return msgb; -} - -struct msgb *nanobts_attr_cell_get(struct gsm_bts *bts) -{ - struct msgb *msgb; - uint8_t buf[256]; - msgb = msgb_alloc(1024, "nanobts_attr_bts"); - - /* routing area code */ - buf[0] = bts->gprs.rac; - msgb_tl16v_put(msgb, NM_ATT_IPACC_RAC, 1, buf); - - buf[0] = 5; /* repeat time (50ms) */ - buf[1] = 3; /* repeat count */ - msgb_tl16v_put(msgb, NM_ATT_IPACC_GPRS_PAGING_CFG, 2, buf); - - /* BVCI 925 */ - buf[0] = bts->gprs.cell.bvci >> 8; - buf[1] = bts->gprs.cell.bvci & 0xff; - msgb_tl16v_put(msgb, NM_ATT_IPACC_BVCI, 2, buf); - - /* all timers in seconds, unless otherwise stated */ - buf[0] = 20; /* T3142 */ - buf[1] = 5; /* T3169 */ - buf[2] = 5; /* T3191 */ - buf[3] = 160; /* T3193 (units of 10ms) */ - buf[4] = 5; /* T3195 */ - buf[5] = 10; /* N3101 */ - buf[6] = 4; /* N3103 */ - buf[7] = 8; /* N3105 */ - buf[8] = 15; /* RLC CV countdown */ - msgb_tl16v_put(msgb, NM_ATT_IPACC_RLC_CFG, 9, buf); - - if (bts->gprs.mode == BTS_GPRS_EGPRS) { - buf[0] = 0x8f; - buf[1] = 0xff; - } else { - buf[0] = 0x0f; - buf[1] = 0x00; - } - msgb_tl16v_put(msgb, NM_ATT_IPACC_CODING_SCHEMES, 2, buf); - - buf[0] = 0; /* T downlink TBF extension (0..500, high byte) */ - buf[1] = 250; /* T downlink TBF extension (0..500, low byte) */ - buf[2] = 0; /* T uplink TBF extension (0..500, high byte) */ - buf[3] = 250; /* T uplink TBF extension (0..500, low byte) */ - buf[4] = 2; /* CS2 */ - msgb_tl16v_put(msgb, NM_ATT_IPACC_RLC_CFG_2, 5, buf); - -#if 0 - /* EDGE model only, breaks older models. - * Should inquire the BTS capabilities */ - buf[0] = 2; /* MCS2 */ - msgb_tl16v_put(msgb, NM_ATT_IPACC_RLC_CFG_3, 1, buf); -#endif - - return msgb; -} - -struct msgb *nanobts_attr_nscv_get(struct gsm_bts *bts) -{ - struct msgb *msgb; - uint8_t buf[256]; - msgb = msgb_alloc(1024, "nanobts_attr_bts"); - - /* 925 */ - buf[0] = bts->gprs.nsvc[0].nsvci >> 8; - buf[1] = bts->gprs.nsvc[0].nsvci & 0xff; - msgb_tl16v_put(msgb, NM_ATT_IPACC_NSVCI, 2, buf); - - /* remote udp port */ - patch_16(&buf[0], htons(bts->gprs.nsvc[0].remote_port)); - /* remote ip address */ - patch_32(&buf[2], htonl(bts->gprs.nsvc[0].remote_ip)); - /* local udp port */ - patch_16(&buf[6], htons(bts->gprs.nsvc[0].local_port)); - msgb_tl16v_put(msgb, NM_ATT_IPACC_NS_LINK_CFG, 8, buf); - - return msgb; -} - -struct msgb *nanobts_attr_radio_get(struct gsm_bts *bts, - struct gsm_bts_trx *trx) -{ - struct msgb *msgb; - uint8_t buf[256]; - msgb = msgb_alloc(1024, "nanobts_attr_bts"); - - /* number of -2dB reduction steps / Pn */ - msgb_tv_put(msgb, NM_ATT_RF_MAXPOWR_R, trx->max_power_red / 2); - - buf[0] = trx->arfcn >> 8; - buf[1] = trx->arfcn & 0xff; - msgb_tl16v_put(msgb, NM_ATT_ARFCN_LIST, 2, buf); - - return msgb; -} diff --git a/openbsc/src/libbsc/bts_nokia_site.c b/openbsc/src/libbsc/bts_nokia_site.c deleted file mode 100644 index 3ca76c017..000000000 --- a/openbsc/src/libbsc/bts_nokia_site.c +++ /dev/null @@ -1,1739 +0,0 @@ -/* Nokia XXXsite family specific code */ - -/* (C) 2011 by Dieter Spaar <spaar@mirider.augusta.de> - * - * 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 <http://www.gnu.org/licenses/>. - * - */ - -/* - TODO: Attention: There are some static variables used for states during - configuration. Those variables have to be moved to a BTS specific context, - otherwise there will most certainly be problems if more than one Nokia BTS - is used. -*/ - -#include <time.h> - -#include <osmocom/gsm/tlv.h> - -#include <openbsc/debug.h> -#include <openbsc/gsm_data.h> -#include <openbsc/abis_nm.h> -#include <osmocom/abis/e1_input.h> -#include <openbsc/signal.h> - -#include <osmocom/core/timer.h> - -#include <osmocom/abis/lapd.h> - -/* TODO: put in a separate file ? */ - -extern int abis_nm_sendmsg(struct gsm_bts *bts, struct msgb *msg); -/* was static in system_information.c */ -extern int generate_cell_chan_list(uint8_t * chan_list, struct gsm_bts *bts); - -static void nokia_abis_nm_queue_send_next(struct gsm_bts *bts); -static void reset_timer_cb(void *_bts); -static int abis_nm_reset(struct gsm_bts *bts, uint16_t ref); -static int dump_elements(uint8_t * data, int len) __attribute__((unused)); - -static void bootstrap_om_bts(struct gsm_bts *bts) -{ - LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for BTS %u\n", bts->nr); - - if (!bts->nokia.skip_reset) { - if (!bts->nokia.did_reset) - abis_nm_reset(bts, 1); - } else - bts->nokia.did_reset = 1; -} - -static void bootstrap_om_trx(struct gsm_bts_trx *trx) -{ - LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for TRX %u/%u\n", - trx->bts->nr, trx->nr); -} - -static int shutdown_om(struct gsm_bts *bts) -{ - /* TODO !? */ - return 0; -} - -#define SAPI_OML 62 -#define SAPI_RSL 0 - -/* - - Tell LAPD to start start the SAP (send SABM requests) for all signalling - timeslots in this line - - Attention: this has to be adapted for mISDN -*/ - -static void start_sabm_in_line(struct e1inp_line *line, int start, int sapi) -{ - struct e1inp_sign_link *link; - int i; - - for (i = 0; i < ARRAY_SIZE(line->ts); i++) { - struct e1inp_ts *ts = &line->ts[i]; - - if (ts->type != E1INP_TS_TYPE_SIGN) - continue; - - llist_for_each_entry(link, &ts->sign.sign_links, list) { - if (sapi != -1 && link->sapi != sapi) - continue; - -#if 0 /* debugging */ - printf("sap start/stop (%d): %d tei=%d sapi=%d\n", - start, i + 1, link->tei, link->sapi); -#endif - - if (start) { - ts->lapd->profile.t200_sec = 1; - ts->lapd->profile.t200_usec = 0; - lapd_sap_start(ts->lapd, link->tei, - link->sapi); - } else - lapd_sap_stop(ts->lapd, link->tei, - link->sapi); - } - } -} - -/* Callback function to be called every time we receive a signal from INPUT */ -static int gbl_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct gsm_bts *bts; - - if (subsys != SS_L_GLOBAL) - return 0; - - switch (signal) { - case S_GLOBAL_BTS_CLOSE_OM: - bts = signal_data; - if (bts->type == GSM_BTS_TYPE_NOKIA_SITE) - shutdown_om(signal_data); - break; - } - - return 0; -} - -/* Callback function to be called every time we receive a signal from INPUT */ -static int inp_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct input_signal_data *isd = signal_data; - - if (subsys != SS_L_INPUT) - return 0; - - switch (signal) { - case S_L_INP_LINE_INIT: - start_sabm_in_line(isd->line, 1, SAPI_OML); /* start only OML */ - break; - case S_L_INP_TEI_DN: - break; - case S_L_INP_TEI_UP: - switch (isd->link_type) { - case E1INP_SIGN_OML: - if (isd->trx->bts->type != GSM_BTS_TYPE_NOKIA_SITE) - break; - - if (isd->tei == isd->trx->bts->oml_tei) - bootstrap_om_bts(isd->trx->bts); - else - bootstrap_om_trx(isd->trx); - break; - } - break; - case S_L_INP_TEI_UNKNOWN: - /* We are receiving LAPD frames with one TEI that we do not - * seem to know, likely that we (the BSC) stopped working - * and lost our local states. However, the BTS is already - * configured, we try to take over the RSL links. */ - start_sabm_in_line(isd->line, 1, SAPI_RSL); - break; - } - - return 0; -} - -static void nm_statechg_evt(unsigned int signal, - struct nm_statechg_signal_data *nsd) -{ - if (nsd->bts->type != GSM_BTS_TYPE_NOKIA_SITE) - return; -} - -static int nm_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - if (subsys != SS_NM) - return 0; - - switch (signal) { - case S_NM_STATECHG_OPER: - case S_NM_STATECHG_ADM: - nm_statechg_evt(signal, signal_data); - break; - default: - break; - } - - return 0; -} - -/* TODO: put in a separate file ? */ - -static const struct value_string nokia_msgt_name[] = { - { 0x80, "NOKIA_BTS_CONF_DATA" }, - { 0x81, "NOKIA_BTS_ACK" }, - { 0x82, "NOKIA_BTS_OMU_STARTED" }, - { 0x83, "NOKIA_BTS_START_DOWNLOAD_REQ" }, - { 0x84, "NOKIA_BTS_MF_REQ" }, - { 0x85, "NOKIA_BTS_AF_REQ" }, - { 0x86, "NOKIA_BTS_RESET_REQ" }, - { 0x87, "NOKIA_reserved" }, - { 0x88, "NOKIA_BTS_CONF_REQ" }, - { 0x89, "NOKIA_BTS_TEST_REQ" }, - { 0x8A, "NOKIA_BTS_TEST_REPORT" }, - { 0x8B, "NOKIA_reserved" }, - { 0x8C, "NOKIA_reserved" }, - { 0x8D, "NOKIA_reserved" }, - { 0x8E, "NOKIA_BTS_CONF_COMPL" }, - { 0x8F, "NOKIA_reserved" }, - { 0x90, "NOKIA_BTS_STM_TEST_REQ" }, - { 0x91, "NOKIA_BTS_STM_TEST_REPORT" }, - { 0x92, "NOKIA_BTS_TRANSMISSION_COMMAND" }, - { 0x93, "NOKIA_BTS_TRANSMISSION_ANSWER" }, - { 0x94, "NOKIA_BTS_HW_DB_UPLOAD_REQ" }, - { 0x95, "NOKIA_BTS_START_HW_DB_DOWNLOAD_REQ" }, - { 0x96, "NOKIA_BTS_HW_DB_SAVE_REQ" }, - { 0x97, "NOKIA_BTS_FLASH_ERASURE_REQ" }, - { 0x98, "NOKIA_BTS_HW_DB_DOWNLOAD_REQ" }, - { 0x99, "NOKIA_BTS_PWR_SUPPLY_CONTROL" }, - { 0x9A, "NOKIA_BTS_ATTRIBUTE_REQ" }, - { 0x9B, "NOKIA_BTS_ATTRIBUTE_REPORT" }, - { 0x9C, "NOKIA_BTS_HW_REQ" }, - { 0x9D, "NOKIA_BTS_HW_REPORT" }, - { 0x9E, "NOKIA_BTS_RTE_TEST_REQ" }, - { 0x9F, "NOKIA_BTS_RTE_TEST_REPORT" }, - { 0xA0, "NOKIA_BTS_HW_DB_VERIFICATION_REQ" }, - { 0xA1, "NOKIA_BTS_CLOCK_REQ" }, - { 0xA2, "NOKIA_AC_CIRCUIT_REQ_NACK" }, - { 0xA3, "NOKIA_AC_INTERRUPTED" }, - { 0xA4, "NOKIA_BTS_NEW_TRE_INFO" }, - { 0xA5, "NOKIA_AC_BSC_CIRCUITS_ALLOCATED" }, - { 0xA6, "NOKIA_BTS_TRE_POLL_LIST" }, - { 0xA7, "NOKIA_AC_CIRCUIT_REQ" }, - { 0xA8, "NOKIA_BTS_BLOCK_CTRL_REQ" }, - { 0xA9, "NOKIA_BTS_GSM_TIME_REQ" }, - { 0xAA, "NOKIA_BTS_GSM_TIME" }, - { 0xAB, "NOKIA_BTS_OUTPUT_CONTROL" }, - { 0xAC, "NOKIA_BTS_STATE_CHANGED" }, - { 0xAD, "NOKIA_BTS_SW_SAVE_REQ" }, - { 0xAE, "NOKIA_BTS_ALARM" }, - { 0xAF, "NOKIA_BTS_CHA_ADM_STATE" }, - { 0xB0, "NOKIA_AC_POOL_SIZE_REPORT" }, - { 0xB1, "NOKIA_AC_POOL_SIZE_INQUIRY" }, - { 0xB2, "NOKIA_BTS_COMMISS_TEST_COMPLETED" }, - { 0xB3, "NOKIA_BTS_COMMISS_TEST_REQ" }, - { 0xB4, "NOKIA_BTS_TRANSP_BTS_TO_BSC" }, - { 0xB5, "NOKIA_BTS_TRANSP_BSC_TO_BTS" }, - { 0xB6, "NOKIA_BTS_LCS_COMMAND" }, - { 0xB7, "NOKIA_BTS_LCS_ANSWER" }, - { 0xB8, "NOKIA_BTS_LMU_FN_OFFSET_COMMAND" }, - { 0xB9, "NOKIA_BTS_LMU_FN_OFFSET_ANSWER" }, - { 0, NULL } -}; - -static const char *get_msg_type_name_string(uint8_t msg_type) -{ - return get_value_string(nokia_msgt_name, msg_type); -} - -static const struct value_string nokia_element_name[] = { - { 0x01, "Ny1" }, - { 0x02, "T3105_F" }, - { 0x03, "Interference band limits" }, - { 0x04, "Interference report timer in secs" }, - { 0x05, "Channel configuration per TS" }, - { 0x06, "BSIC" }, - { 0x07, "RACH report timer in secs" }, - { 0x08, "Hardware database status" }, - { 0x09, "BTS RX level" }, - { 0x0A, "ARFN" }, - { 0x0B, "STM antenna attenuation" }, - { 0x0C, "Cell allocation bitmap" }, - { 0x0D, "Radio definition per TS" }, - { 0x0E, "Frame number" }, - { 0x0F, "Antenna diversity" }, - { 0x10, "T3105_D" }, - { 0x11, "File format" }, - { 0x12, "Last File" }, - { 0x13, "BTS type" }, - { 0x14, "Erasure mode" }, - { 0x15, "Hopping mode" }, - { 0x16, "Floating TRX" }, - { 0x17, "Power supplies" }, - { 0x18, "Reset type" }, - { 0x19, "Averaging period" }, - { 0x1A, "RBER2" }, - { 0x1B, "LAC" }, - { 0x1C, "CI" }, - { 0x1D, "Failure parameters" }, - { 0x1E, "(RF max power reduction)" }, - { 0x1F, "Measured RX_SENS" }, - { 0x20, "Extended cell radius" }, - { 0x21, "reserved" }, - { 0x22, "Success-Failure" }, - { 0x23, "Ack-Nack" }, - { 0x24, "OMU test results" }, - { 0x25, "File identity" }, - { 0x26, "Generation and version code" }, - { 0x27, "SW description" }, - { 0x28, "BCCH LEV" }, - { 0x29, "Test type" }, - { 0x2A, "Subscriber number" }, - { 0x2B, "reserved" }, - { 0x2C, "HSN" }, - { 0x2D, "reserved" }, - { 0x2E, "MS RXLEV" }, - { 0x2F, "MS TXLEV" }, - { 0x30, "RXQUAL" }, - { 0x31, "RX SENS" }, - { 0x32, "Alarm block" }, - { 0x33, "Neighbouring BCCH levels" }, - { 0x34, "STM report type" }, - { 0x35, "MA" }, - { 0x36, "MAIO" }, - { 0x37, "H_FLAG" }, - { 0x38, "TCH_ARFN" }, - { 0x39, "Clock output" }, - { 0x3A, "Transmitted power" }, - { 0x3B, "Clock sync" }, - { 0x3C, "TMS protocol discriminator" }, - { 0x3D, "TMS protocol data" }, - { 0x3E, "FER" }, - { 0x3F, "SWR result" }, - { 0x40, "Object identity" }, - { 0x41, "STM RX Antenna Test" }, - { 0x42, "reserved" }, - { 0x43, "reserved" }, - { 0x44, "Object current state" }, - { 0x45, "reserved" }, - { 0x46, "FU channel configuration" }, - { 0x47, "reserved" }, - { 0x48, "ARFN of a CU" }, - { 0x49, "FU radio definition" }, - { 0x4A, "reserved" }, - { 0x4B, "Severity" }, - { 0x4C, "Diversity selection" }, - { 0x4D, "RX antenna test" }, - { 0x4E, "RX antenna supervision period" }, - { 0x4F, "RX antenna state" }, - { 0x50, "Sector configuration" }, - { 0x51, "Additional info" }, - { 0x52, "SWR parameters" }, - { 0x53, "HW inquiry mode" }, - { 0x54, "reserved" }, - { 0x55, "Availability status" }, - { 0x56, "reserved" }, - { 0x57, "EAC inputs" }, - { 0x58, "EAC outputs" }, - { 0x59, "reserved" }, - { 0x5A, "Position" }, - { 0x5B, "HW unit identity" }, - { 0x5C, "RF test signal attenuation" }, - { 0x5D, "Operational state" }, - { 0x5E, "Logical object identity" }, - { 0x5F, "reserved" }, - { 0x60, "BS_TXPWR_OM" }, - { 0x61, "Loop_Duration" }, - { 0x62, "LNA_Path_Selection" }, - { 0x63, "Serial number" }, - { 0x64, "HW version" }, - { 0x65, "Obj. identity and obj. state" }, - { 0x66, "reserved" }, - { 0x67, "EAC input definition" }, - { 0x68, "EAC id and text" }, - { 0x69, "HW unit status" }, - { 0x6A, "SW release version" }, - { 0x6B, "FW version" }, - { 0x6C, "Bit_Error_Ratio" }, - { 0x6D, "RXLEV_with_Attenuation" }, - { 0x6E, "RXLEV_without_Attenuation" }, - { 0x6F, "reserved" }, - { 0x70, "CU_Results" }, - { 0x71, "reserved" }, - { 0x72, "LNA_Path_Results" }, - { 0x73, "RTE Results" }, - { 0x74, "Real Time" }, - { 0x75, "RX diversity selection" }, - { 0x76, "EAC input config" }, - { 0x77, "Feature support" }, - { 0x78, "File version" }, - { 0x79, "Outputs" }, - { 0x7A, "FU parameters" }, - { 0x7B, "Diagnostic info" }, - { 0x7C, "FU BSIC" }, - { 0x7D, "TRX Configuration" }, - { 0x7E, "Download status" }, - { 0x7F, "RX difference limit" }, - { 0x80, "TRX HW capability" }, - { 0x81, "Common HW config" }, - { 0x82, "Autoconfiguration pool size" }, - { 0x83, "TRE diagnostic info" }, - { 0x84, "TRE object identity" }, - { 0x85, "New TRE Info" }, - { 0x86, "Acknowledgement period" }, - { 0x87, "Synchronization mode" }, - { 0x88, "reserved" }, - { 0x89, "Block Control Data" }, - { 0x8A, "SW load mode" }, - { 0x8B, "Recommended recovery action" }, - { 0x8C, "BSC BCF id" }, - { 0x8D, "Q1 baud rate" }, - { 0x8E, "Allocation status" }, - { 0x8F, "Functional entity number" }, - { 0x90, "Transmission delay" }, - { 0x91, "Loop Duration ms" }, - { 0x92, "Logical channel" }, - { 0x93, "Q1 address" }, - { 0x94, "Alarm detail" }, - { 0x95, "Cabinet type" }, - { 0x96, "HW unit existence" }, - { 0x97, "RF power parameters" }, - { 0x98, "Message scenario" }, - { 0x99, "HW unit max amount" }, - { 0x9A, "Master TRX" }, - { 0x9B, "Transparent data" }, - { 0x9C, "BSC topology info" }, - { 0x9D, "Air i/f modulation" }, - { 0x9E, "LCS Q1 command data" }, - { 0x9F, "Frame number offset" }, - { 0xA0, "Abis TSL" }, - { 0xA1, "Dynamic pool info" }, - { 0xA2, "LCS LLP data" }, - { 0xA3, "LCS Q1 answer data" }, - { 0xA4, "DFCA FU Radio Definition" }, - { 0xA5, "Antenna hopping" }, - { 0xA6, "Field record sequence number" }, - { 0xA7, "Timeslot offslot" }, - { 0xA8, "EPCR capability" }, - { 0xA9, "Connectsite optional element" }, - { 0xAA, "TSC" }, - { 0xAB, "Special TX Power Setting" }, - { 0xAC, "Optional sync settings" }, - { 0xFA, "Abis If parameters" }, - { 0, NULL } -}; - -static const char *get_element_name_string(uint16_t element) -{ - return get_value_string(nokia_element_name, element); -} - -static const struct value_string nokia_bts_types[] = { - { 0x0a, "MetroSite GSM 900" }, - { 0x0b, "MetroSite GSM 1800" }, - { 0x0c, "MetroSite GSM 1900 (PCS)" }, - { 0x0d, "MetroSite GSM 900 & 1800" }, - { 0x0e, "InSite GSM 900" }, - { 0x0f, "InSite GSM 1800" }, - { 0x10, "InSite GSM 1900" }, - { 0x11, "UltraSite GSM 900" }, - { 0x12, "UltraSite GSM 1800" }, - { 0x13, "UltraSite GSM/US-TDMA 1900" }, - { 0x14, "UltraSite GSM 900 & 1800" }, - { 0x16, "UltraSite GSM/US-TDMA 850" }, - { 0x18, "MetroSite GSM/US-TDMA 850" }, - { 0x19, "UltraSite GSM 800/1900" }, - { 0, NULL } -}; - -static const char *get_bts_type_string(uint8_t type) -{ - return get_value_string(nokia_bts_types, type); -} - -static const struct value_string nokia_severity[] = { - { 0, "indeterminate" }, - { 1, "critical" }, - { 2, "major" }, - { 3, "minor" }, - { 4, "warning" }, - { 0, NULL } -}; - -static const char *get_severity_string(uint8_t severity) -{ - return get_value_string(nokia_severity, severity); -} - -/* TODO: put in a separate file ? */ - -/* some message IDs */ - -#define NOKIA_MSG_CONF_DATA 128 -#define NOKIA_MSG_ACK 129 -#define NOKIA_MSG_OMU_STARTED 130 -#define NOKIA_MSG_START_DOWNLOAD_REQ 131 -#define NOKIA_MSG_MF_REQ 132 -#define NOKIA_MSG_RESET_REQ 134 -#define NOKIA_MSG_CONF_REQ 136 -#define NOKIA_MSG_CONF_COMPLETE 142 -#define NOKIA_MSG_BLOCK_CTRL_REQ 168 -#define NOKIA_MSG_STATE_CHANGED 172 -#define NOKIA_MSG_ALARM 174 - -/* some element IDs */ - -#define NOKIA_EI_BTS_TYPE 0x13 -#define NOKIA_EI_ACK 0x23 -#define NOKIA_EI_ADD_INFO 0x51 -#define NOKIA_EI_SEVERITY 0x4B -#define NOKIA_EI_ALARM_DETAIL 0x94 - -#define OM_ALLOC_SIZE 1024 -#define OM_HEADROOM_SIZE 128 - -static uint8_t fu_config_template[] = { - 0x7F, 0x7A, 0x39, - /* ID = 0x7A (FU parameters) ## constructed ## */ - /* length = 57 */ - /* [3] */ - - 0x5F, 0x40, 0x04, - /* ID = 0x40 (Object identity) */ - /* length = 4 */ - /* [6] */ - 0x00, 0x07, 0x01, 0xFF, - - 0x41, 0x02, - /* ID = 0x01 (Ny1) */ - /* length = 2 */ - /* [12] */ - 0x00, 0x05, - - 0x42, 0x02, - /* ID = 0x02 (T3105_F) */ - /* length = 2 */ - /* [16] */ - 0x00, 0x28, /* FIXME: use net->T3105 */ - - 0x50, 0x02, - /* ID = 0x10 (T3105_D) */ - /* length = 2 */ - /* [20] */ - 0x00, 0x28, /* FIXME: use net->T3105 */ - - 0x43, 0x05, - /* ID = 0x03 (Interference band limits) */ - /* length = 5 */ - /* [24] */ - 0x0F, 0x1B, 0x27, 0x33, 0x3F, - - 0x44, 0x02, - /* ID = 0x04 (Interference report timer in secs) */ - /* length = 2 */ - /* [31] */ - 0x00, 0x10, - - 0x47, 0x01, - /* ID = 0x07 (RACH report timer in secs) */ - /* length = 1 */ - /* [35] */ - 0x1E, - - 0x4C, 0x10, - /* ID = 0x0C (Cell allocation bitmap) ####### */ - /* length = 16 */ - /* [38] */ - 0x8F, 0xB1, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x59, 0x01, - /* ID = 0x19 (Averaging period) */ - /* length = 1 */ - /* [56] */ - 0x01, - - 0x5E, 0x01, - /* ID = 0x1E ((RF max power reduction)) */ - /* length = 1 */ - /* [59] */ - 0x00, - - 0x7F, 0x46, 0x11, - /* ID = 0x46 (FU channel configuration) ## constructed ## */ - /* length = 17 */ - /* [63] */ - - 0x5F, 0x40, 0x04, - /* ID = 0x40 (Object identity) */ - /* length = 4 */ - /* [66] */ - 0x00, 0x07, 0x01, 0xFF, - - 0x45, 0x08, - /* ID = 0x05 (Channel configuration per TS) */ - /* length = 8 */ - /* [72] */ - 0x01, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, 0x09, - - 0x7F, 0x65, 0x0B, - /* ID = 0x65 (Obj. identity and obj. state) ## constructed ## */ - /* length = 11 */ - /* [83] */ - - 0x5F, 0x40, 0x04, - /* ID = 0x40 (Object identity) */ - /* length = 4 */ - /* [86] */ - 0x00, 0x04, 0x01, 0xFF, - - 0x5F, 0x44, 0x01, - /* ID = 0x44 (Object current state) */ - /* length = 1 */ - /* [93] */ - 0x03, - - 0x7F, 0x7C, 0x0A, - /* ID = 0x7C (FU BSIC) ## constructed ## */ - /* length = 10 */ - /* [97] */ - - 0x5F, 0x40, 0x04, - /* ID = 0x40 (Object identity) */ - /* length = 4 */ - /* [100] */ - 0x00, 0x07, 0x01, 0xFF, - - 0x46, 0x01, - /* ID = 0x06 (BSIC) */ - /* length = 1 */ - /* [106] */ - 0x00, - - 0x7F, 0x48, 0x0B, - /* ID = 0x48 (ARFN of a CU) ## constructed ## */ - /* length = 11 */ - /* [110] */ - - 0x5F, 0x40, 0x04, - /* ID = 0x40 (Object identity) */ - /* length = 4 */ - /* [113] */ - 0x00, 0x08, 0x01, 0xFF, - - 0x4A, 0x02, - /* ID = 0x0A (ARFN) ####### */ - /* length = 2 */ - /* [119] */ - 0x03, 0x62, - - 0x7F, 0x49, 0x59, - /* ID = 0x49 (FU radio definition) ## constructed ## */ - /* length = 89 */ - /* [124] */ - - 0x5F, 0x40, 0x04, - /* ID = 0x40 (Object identity) */ - /* length = 4 */ - /* [127] */ - 0x00, 0x07, 0x01, 0xFF, - - 0x4D, 0x50, - /* ID = 0x0D (Radio definition per TS) ####### */ - /* length = 80 */ - /* [133] */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MA */ - 0x03, 0x62, /* HSN, MAIO or ARFCN if no hopping */ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x62, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x62, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x62, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x62, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x62, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x62, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x03, 0x62, -}; - -/* TODO: put in a separate file ? */ - -/* - build the configuration for each TRX -*/ - -static int make_fu_config(struct gsm_bts_trx *trx, uint8_t id, - uint8_t * fu_config, int *hopping) -{ - int i; - - *hopping = 0; - - memcpy(fu_config, fu_config_template, sizeof(fu_config_template)); - - /* set ID */ - - fu_config[6 + 2] = id; - fu_config[66 + 2] = id; - fu_config[86 + 2] = id; - fu_config[100 + 2] = id; - fu_config[113 + 2] = id; - fu_config[127 + 2] = id; - - /* set ARFCN */ - - uint16_t arfcn = trx->arfcn; - - fu_config[119] = arfcn >> 8; - fu_config[119 + 1] = arfcn & 0xFF; - - for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { - struct gsm_bts_trx_ts *ts = &trx->ts[i]; - - if (ts->hopping.enabled) { - /* reverse order */ - int j; - for (j = 0; j < ts->hopping.ma_len; j++) - fu_config[133 + (i * 10) + (7 - j)] = - ts->hopping.ma_data[j]; - fu_config[133 + 8 + (i * 10)] = ts->hopping.hsn; - fu_config[133 + 8 + 1 + (i * 10)] = ts->hopping.maio; - *hopping = 1; - } else { - fu_config[133 + 8 + (i * 10)] = arfcn >> 8; - fu_config[133 + 8 + 1 + (i * 10)] = arfcn & 0xFF; - } - } - - /* set BSIC */ - - /* - Attention: all TRX except the first one seem to get the TSC - from the CHANNEL ACTIVATION command (in CHANNEL IDENTIFICATION, - GSM 04.08 CHANNEL DESCRIPTION). - There was a bug in rsl_chan_activate_lchan() setting this parameter. - */ - - uint8_t bsic = trx->bts->bsic; - - fu_config[106] = bsic; - - /* set CA */ - - if (generate_cell_chan_list(&fu_config[38], trx->bts) != 0) { - fprintf(stderr, "generate_cell_chan_list failed\n"); - return 0; - } - - /* set channel configuration */ - - for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { - struct gsm_bts_trx_ts *ts = &trx->ts[i]; - uint8_t chan_config; - - /* - 0 = FCCH + SCH + BCCH + CCCH - 1 = FCCH + SCH + BCCH + CCCH + SDCCH/4 + SACCH/4 - 2 = BCCH + CCCH (This combination is not used in any BTS) - 3 = FCCH + SCH + BCCH + CCCH + SDCCH/4 with SDCCH2 used as CBCH - 4 = SDCCH/8 + SACCH/8 - 5 = SDCCH/8 with SDCCH2 used as CBCH - 6 = TCH/F + FACCH/F + SACCH/F - 7 = E-RACH (Talk family) - 9 = Dual rate (capability for TCH/F and TCH/H) - 10 = reserved for BTS internal use - 11 = PBCCH + PCCCH + PDTCH + PACCH + PTCCH (can be used in GPRS release 2). - 0xFF = spare TS - */ - - if (ts->pchan == GSM_PCHAN_NONE) - chan_config = 0xFF; - else if (ts->pchan == GSM_PCHAN_CCCH) - chan_config = 0; - else if (ts->pchan == GSM_PCHAN_CCCH_SDCCH4) - chan_config = 1; - else if (ts->pchan == GSM_PCHAN_TCH_F) - chan_config = 6; /* 9 should work too */ - else if (ts->pchan == GSM_PCHAN_TCH_H) - chan_config = 9; - else if (ts->pchan == GSM_PCHAN_SDCCH8_SACCH8C) - chan_config = 4; - else if (ts->pchan == GSM_PCHAN_PDCH) - chan_config = 11; - else { - fprintf(stderr, - "unsupported channel config %d for timeslot %d\n", - ts->pchan, i); - return 0; - } - - fu_config[72 + i] = chan_config; - } - return sizeof(fu_config_template); -} - -/* TODO: put in a separate file ? */ - -static uint8_t bts_config_1[] = { - 0x4E, 0x02, - /* ID = 0x0E (Frame number) */ - /* length = 2 */ - /* [2] */ - 0xFF, 0xFF, - - 0x5F, 0x4E, 0x02, - /* ID = 0x4E (RX antenna supervision period) */ - /* length = 2 */ - /* [7] */ - 0xFF, 0xFF, - - 0x5F, 0x50, 0x02, - /* ID = 0x50 (Sector configuration) */ - /* length = 2 */ - /* [12] */ - 0x01, 0x01, -}; - -static uint8_t bts_config_2[] = { - 0x55, 0x02, - /* ID = 0x15 (Hopping mode) */ - /* length = 2 */ - /* [2] */ - 0x01, 0x00, - - 0x5F, 0x75, 0x02, - /* ID = 0x75 (RX diversity selection) */ - /* length = 2 */ - /* [7] */ - 0x01, 0x01, -}; - -static uint8_t bts_config_3[] = { - 0x5F, 0x20, 0x02, - /* ID = 0x20 (Extended cell radius) */ - /* length = 2 */ - /* [3] */ - 0x01, 0x00, -}; - -static uint8_t bts_config_4[] = { - 0x5F, 0x74, 0x09, - /* ID = 0x74 (Real Time) */ - /* length = 9 */ - /* [3] year-high, year-low, month, day, hour, minute, second, msec-high, msec-low */ - 0x07, 0xDB, 0x06, 0x02, 0x0B, 0x20, 0x0C, 0x00, - 0x00, - - 0x5F, 0x76, 0x03, - /* ID = 0x76 (EAC input config) */ - /* length = 3 */ - /* [15] */ - 0x01, 0x01, 0x00, - - 0x5F, 0x76, 0x03, - /* ID = 0x76 (EAC input config) */ - /* length = 3 */ - /* [21] */ - 0x02, 0x01, 0x00, - - 0x5F, 0x76, 0x03, - /* ID = 0x76 (EAC input config) */ - /* length = 3 */ - /* [27] */ - 0x03, 0x01, 0x00, - - 0x5F, 0x76, 0x03, - /* ID = 0x76 (EAC input config) */ - /* length = 3 */ - /* [33] */ - 0x04, 0x01, 0x00, - - 0x5F, 0x76, 0x03, - /* ID = 0x76 (EAC input config) */ - /* length = 3 */ - /* [39] */ - 0x05, 0x01, 0x00, - - 0x5F, 0x76, 0x03, - /* ID = 0x76 (EAC input config) */ - /* length = 3 */ - /* [45] */ - 0x06, 0x01, 0x00, - - 0x5F, 0x76, 0x03, - /* ID = 0x76 (EAC input config) */ - /* length = 3 */ - /* [51] */ - 0x07, 0x01, 0x00, - - 0x5F, 0x76, 0x03, - /* ID = 0x76 (EAC input config) */ - /* length = 3 */ - /* [57] */ - 0x08, 0x01, 0x00, - - 0x5F, 0x76, 0x03, - /* ID = 0x76 (EAC input config) */ - /* length = 3 */ - /* [63] */ - 0x09, 0x01, 0x00, - - 0x5F, 0x76, 0x03, - /* ID = 0x76 (EAC input config) */ - /* length = 3 */ - /* [69] */ - 0x0A, 0x01, 0x00, -}; - -static uint8_t bts_config_insite[] = { - 0x4E, 0x02, - /* ID = 0x0E (Frame number) */ - /* length = 2 */ - /* [2] */ - 0xFF, 0xFF, - - 0x5F, 0x4E, 0x02, - /* ID = 0x4E (RX antenna supervision period) */ - /* length = 2 */ - /* [7] */ - 0xFF, 0xFF, - - 0x5F, 0x50, 0x02, - /* ID = 0x50 (Sector configuration) */ - /* length = 2 */ - /* [12] */ - 0x01, 0x01, - - 0x55, 0x02, - /* ID = 0x15 (Hopping mode) */ - /* length = 2 */ - /* [16] */ - 0x01, 0x00, - - 0x5F, 0x20, 0x02, - /* ID = 0x20 (Extended cell radius) */ - /* length = 2 */ - /* [21] */ - 0x01, 0x00, - - 0x5F, 0x74, 0x09, - /* ID = 0x74 (Real Time) */ - /* length = 9 */ - /* [26] */ - 0x07, 0xDB, 0x07, 0x0A, 0x0F, 0x09, 0x0B, 0x00, - 0x00, -}; - -void set_real_time(uint8_t * real_time) -{ - time_t t; - struct tm *tm; - - t = time(NULL); - tm = localtime(&t); - - /* year-high, year-low, month, day, hour, minute, second, msec-high, msec-low */ - - real_time[0] = (1900 + tm->tm_year) >> 8; - real_time[1] = (1900 + tm->tm_year) & 0xFF; - real_time[2] = tm->tm_mon + 1; - real_time[3] = tm->tm_mday; - real_time[4] = tm->tm_hour; - real_time[5] = tm->tm_min; - real_time[6] = tm->tm_sec; - real_time[7] = 0; - real_time[8] = 0; -} - -/* TODO: put in a separate file ? */ - -/* - build the configuration data -*/ - -static int make_bts_config(uint8_t bts_type, int n_trx, uint8_t * fu_config, - int need_hopping) -{ - /* is it an InSite BTS ? */ - if (bts_type == 0x0E || bts_type == 0x0F || bts_type == 0x10) { /* TODO */ - if (n_trx != 1) { - fprintf(stderr, "InSite has only one TRX\n"); - return 0; - } - if (need_hopping != 0) { - fprintf(stderr, "InSite does not support hopping\n"); - return 0; - } - memcpy(fu_config, bts_config_insite, sizeof(bts_config_insite)); - set_real_time(&fu_config[26]); - return sizeof(bts_config_insite); - } - - int len = 0; - int i; - - memcpy(fu_config + len, bts_config_1, sizeof(bts_config_1)); - - /* set sector configuration */ - fu_config[len + 12 - 1] = 1 + n_trx; /* len */ - for (i = 0; i < n_trx; i++) - fu_config[len + 12 + 1 + i] = ((i + 1) & 0xFF); - - len += (sizeof(bts_config_1) + (n_trx - 1)); - - memcpy(fu_config + len, bts_config_2, sizeof(bts_config_2)); - /* set hopping mode (Baseband and RF hopping work for the MetroSite) */ - if (need_hopping) - fu_config[len + 2 + 1] = 1; /* 0: no hopping, 1: Baseband hopping, 2: RF hopping */ - len += sizeof(bts_config_2); - - /* set extended cell radius for each TRX */ - for (i = 0; i < n_trx; i++) { - memcpy(fu_config + len, bts_config_3, sizeof(bts_config_3)); - fu_config[len + 3] = ((i + 1) & 0xFF); - len += sizeof(bts_config_3); - } - - memcpy(fu_config + len, bts_config_4, sizeof(bts_config_4)); - set_real_time(&fu_config[len + 3]); - len += sizeof(bts_config_4); - - return len; -} - -/* TODO: put in a separate file ? */ - -static struct msgb *nm_msgb_alloc(void) -{ - return msgb_alloc_headroom(OM_ALLOC_SIZE, OM_HEADROOM_SIZE, "OML"); -} - -/* TODO: put in a separate file ? */ - -struct abis_om_nokia_hdr { - uint8_t msg_type; - uint8_t spare; - uint16_t reference; - uint8_t data[0]; -} __attribute__ ((packed)); - -#define ABIS_OM_NOKIA_HDR_SIZE (sizeof(struct abis_om_hdr) + sizeof(struct abis_om_nokia_hdr)) - -static int abis_nm_send(struct gsm_bts *bts, uint8_t msg_type, uint16_t ref, - uint8_t * data, int len_data) -{ - struct abis_om_hdr *oh; - struct abis_om_nokia_hdr *noh; - struct msgb *msg = nm_msgb_alloc(); - - oh = (struct abis_om_hdr *)msgb_put(msg, - ABIS_OM_NOKIA_HDR_SIZE + len_data); - - oh->mdisc = ABIS_OM_MDISC_FOM; - oh->placement = ABIS_OM_PLACEMENT_ONLY; - oh->sequence = 0; - oh->length = sizeof(struct abis_om_nokia_hdr) + len_data; - - noh = (struct abis_om_nokia_hdr *)oh->data; - - noh->msg_type = msg_type; - noh->spare = 0; - noh->reference = htons(ref); - memcpy(noh->data, data, len_data); - - DEBUGPC(DNM, "Sending %s\n", get_msg_type_name_string(msg_type)); - - return abis_nm_sendmsg(bts, msg); -} - -/* TODO: put in a separate file ? */ - -static uint8_t download_req[] = { - 0x5F, 0x25, 0x0B, - /* ID = 0x25 (File identity) */ - /* length = 11 */ - /* [3] */ - 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, 0x2A, - 0x2A, 0x2A, 0x2A, - - 0x5F, 0x78, 0x03, - /* ID = 0x78 (File version) */ - /* length = 3 */ - /* [17] */ - 0x2A, 0x2A, 0x2A, - - 0x5F, 0x81, 0x0A, 0x01, - /* ID = 0x8A (SW load mode) */ - /* length = 1 */ - /* [24] */ - 0x01, - - 0x5F, 0x81, 0x06, 0x01, - /* ID = 0x86 (Acknowledgement period) */ - /* length = 1 */ - /* [29] */ - 0x01, -}; - -static int abis_nm_download_req(struct gsm_bts *bts, uint16_t ref) -{ - uint8_t *data = download_req; - int len_data = sizeof(download_req); - - return abis_nm_send(bts, NOKIA_MSG_START_DOWNLOAD_REQ, ref, data, - len_data); -} - -/* TODO: put in a separate file ? */ - -static uint8_t ack[] = { - 0x5F, 0x23, 0x01, - /* ID = 0x23 (Ack-Nack) */ - /* length = 1 */ - /* [3] */ - 0x01, -}; - -static int abis_nm_ack(struct gsm_bts *bts, uint16_t ref) -{ - uint8_t *data = ack; - int len_data = sizeof(ack); - - return abis_nm_send(bts, NOKIA_MSG_ACK, ref, data, len_data); -} - -/* TODO: put in a separate file ? */ - -static uint8_t reset[] = { - 0x5F, 0x40, 0x04, - /* ID = 0x40 (Object identity) */ - /* length = 4 */ - /* [3] */ - 0x00, 0x01, 0xFF, 0xFF, -}; - -static int abis_nm_reset(struct gsm_bts *bts, uint16_t ref) -{ - uint8_t *data = reset; - int len_data = sizeof(reset); - LOGP(DLINP, LOGL_INFO, "Nokia BTS reset timer: %d\n", bts->nokia.bts_reset_timer_cnf); - return abis_nm_send(bts, NOKIA_MSG_RESET_REQ, ref, data, len_data); -} - -/* TODO: put in a separate file ? */ - -static int abis_nm_send_multi_segments(struct gsm_bts *bts, uint8_t msg_type, - uint16_t ref, uint8_t * data, int len) -{ - int len_remain, len_to_send, max_send; - int seq = 0; - int ret; - - len_remain = len; - - while (len_remain) { - struct abis_om_hdr *oh; - struct abis_om_nokia_hdr *noh; - struct msgb *msg = nm_msgb_alloc(); - - if (seq == 0) - max_send = 256 - sizeof(struct abis_om_nokia_hdr); - else - max_send = 256; - - if (len_remain > max_send) { - len_to_send = max_send; - - if (seq == 0) { - /* first segment */ - oh = (struct abis_om_hdr *)msgb_put(msg, - ABIS_OM_NOKIA_HDR_SIZE - + - len_to_send); - - oh->mdisc = ABIS_OM_MDISC_FOM; - oh->placement = ABIS_OM_PLACEMENT_FIRST; /* first segment of multi-segment message */ - oh->sequence = seq; - oh->length = 0; /* 256 bytes */ - - noh = (struct abis_om_nokia_hdr *)oh->data; - - noh->msg_type = msg_type; - noh->spare = 0; - noh->reference = htons(ref); - memcpy(noh->data, data, len_to_send); - } else { - /* segment in between */ - oh = (struct abis_om_hdr *)msgb_put(msg, - sizeof - (struct - abis_om_hdr) - + - len_to_send); - - oh->mdisc = ABIS_OM_MDISC_FOM; - oh->placement = ABIS_OM_PLACEMENT_MIDDLE; /* segment of multi-segment message */ - oh->sequence = seq; - oh->length = 0; /* 256 bytes */ - - memcpy(oh->data, data, len_to_send); - } - } else { - - len_to_send = len_remain; - - /* check if message fits in a single segment */ - - if (seq == 0) - return abis_nm_send(bts, msg_type, ref, data, - len_to_send); - - /* last segment */ - - oh = (struct abis_om_hdr *)msgb_put(msg, - sizeof(struct - abis_om_hdr) - + len_to_send); - - oh->mdisc = ABIS_OM_MDISC_FOM; - oh->placement = ABIS_OM_PLACEMENT_LAST; /* last segment of multi-segment message */ - oh->sequence = seq; - oh->length = len_to_send; - - memcpy(oh->data, data, len_to_send); - } - - DEBUGPC(DNM, "Sending multi-segment %d\n", seq); - - ret = abis_nm_sendmsg(bts, msg); - if (ret < 0) - return ret; - - nokia_abis_nm_queue_send_next(bts); - - /* next segment */ - len_remain -= len_to_send; - data += len_to_send; - seq++; - } - return ret; -} - -/* TODO: put in a separate file ? */ - -static int abis_nm_send_config(struct gsm_bts *bts, uint8_t bts_type) -{ - struct gsm_bts_trx *trx; - uint8_t config[2048]; /* TODO: might be too small if lots of TRX are used */ - int len = 0; - int idx = 0; - int ret; - int hopping = 0; - int need_hopping = 0; - - memset(config, 0, sizeof(config)); - - llist_for_each_entry(trx, &bts->trx_list, list) { -#if 0 /* debugging */ - printf("TRX\n"); - printf(" arfcn: %d\n", trx->arfcn); - printf(" bsic: %d\n", trx->bts->bsic); - uint8_t ca[20]; - memset(ca, 0xFF, sizeof(ca)); - ret = generate_cell_chan_list(ca, trx->bts); - printf(" ca (%d): %s\n", ret, osmo_hexdump(ca, sizeof(ca))); - int i; - for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { - struct gsm_bts_trx_ts *ts = &trx->ts[i]; - - printf(" pchan %d: %d\n", i, ts->pchan); - } -#endif - ret = make_fu_config(trx, idx + 1, config + len, &hopping); - need_hopping |= hopping; - len += ret; - - idx++; - } - - ret = make_bts_config(bts_type, idx, config + len, need_hopping); - len += ret; - -#if 0 /* debugging */ - dump_elements(config, len); -#endif - - return abis_nm_send_multi_segments(bts, NOKIA_MSG_CONF_DATA, 1, config, - len); -} - -#define GET_NEXT_BYTE if(idx >= len) return 0; \ - ub = data[idx++]; - -static int find_element(uint8_t * data, int len, uint16_t id, uint8_t * value, - int max_value) -{ - uint8_t ub; - int idx = 0; - int found = 0; - int constructed __attribute__((unused)); - uint16_t id_value; - - for (;;) { - - GET_NEXT_BYTE; - - /* encoding bit, construced means that other elements are contained */ - constructed = ((ub & 0x20) ? 1 : 0); - - if ((ub & 0x1F) == 0x1F) { - /* fixed pattern, ID follows */ - GET_NEXT_BYTE; /* ID */ - id_value = ub & 0x7F; - if (ub & 0x80) { - /* extension bit */ - GET_NEXT_BYTE; /* ID low part */ - id_value = (id_value << 7) | (ub & 0x7F); - } - if (id_value == id) - found = 1; - } else { - id_value = (ub & 0x3F); - if (id_value == id) - found = 1; - } - - GET_NEXT_BYTE; /* length */ - - if (found) { - /* get data */ - uint8_t n = ub; - uint8_t i; - for (i = 0; i < n; i++) { - GET_NEXT_BYTE; - if (max_value <= 0) - return -1; /* buffer too small */ - *value = ub; - value++; - max_value--; - } - return n; /* length */ - } else { - /* skip data */ - uint8_t n = ub; - uint8_t i; - for (i = 0; i < n; i++) { - GET_NEXT_BYTE; - } - } - } - return 0; /* not found */ -} - -static int dump_elements(uint8_t * data, int len) -{ - uint8_t ub; - int idx = 0; - int constructed; - uint16_t id_value; - static char indent[100] = ""; /* TODO: move static to BTS context */ - - for (;;) { - - GET_NEXT_BYTE; - - /* encoding bit, construced means that other elements are contained */ - constructed = ((ub & 0x20) ? 1 : 0); - - if ((ub & 0x1F) == 0x1F) { - /* fixed pattern, ID follows */ - GET_NEXT_BYTE; /* ID */ - id_value = ub & 0x7F; - if (ub & 0x80) { - /* extension bit */ - GET_NEXT_BYTE; /* ID low part */ - id_value = (id_value << 7) | (ub & 0x7F); - } - - } else { - id_value = (ub & 0x3F); - } - - GET_NEXT_BYTE; /* length */ - - printf("%s--ID = 0x%02X (%s) %s\n", indent, id_value, - get_element_name_string(id_value), - constructed ? "** constructed **" : ""); - printf("%s length = %d\n", indent, ub); - printf("%s %s\n", indent, osmo_hexdump(data + idx, ub)); - - if (constructed) { - int indent_len = strlen(indent); - strcat(indent, " "); - - dump_elements(data + idx, ub); - - indent[indent_len] = 0; - } - /* skip data */ - uint8_t n = ub; - uint8_t i; - for (i = 0; i < n; i++) { - GET_NEXT_BYTE; - } - } - return 0; -} - -/* TODO: put in a separate file ? */ - -/* taken from abis_nm.c */ - -static void nokia_abis_nm_queue_send_next(struct gsm_bts *bts) -{ - int wait = 0; - struct msgb *msg; - /* the queue is empty */ - while (!llist_empty(&bts->abis_queue)) { - msg = msgb_dequeue(&bts->abis_queue); - wait = OBSC_NM_W_ACK_CB(msg); - abis_sendmsg(msg); - - if (wait) - break; - } - - bts->abis_nm_pend = wait; -} - -/* TODO: put in a separate file ? */ - -/* timer for restarting OML after BTS reset */ - -static void reset_timer_cb(void *_bts) -{ - struct gsm_bts *bts = _bts; - struct gsm_e1_subslot *e1_link = &bts->oml_e1_link; - struct e1inp_line *line; - - bts->nokia.wait_reset = 0; - - /* OML link */ - line = e1inp_line_find(e1_link->e1_nr); - if (!line) { - LOGP(DLINP, LOGL_ERROR, "BTS %u OML link referring to " - "non-existing E1 line %u\n", bts->nr, e1_link->e1_nr); - return; - } - - start_sabm_in_line(line, 0, -1); /* stop all first */ - start_sabm_in_line(line, 1, SAPI_OML); /* start only OML */ -} - -/* TODO: put in a separate file ? */ - -/* - This is how the configuration is done: - - start OML link - - reset BTS - - receive ACK, wait some time and restart OML link - - receive OMU STARTED message, send START DOWNLOAD REQ - - receive CNF REQ message, send CONF DATA - - receive ACK, start RSL link(s) - ACK some other messages received from the BTS. - - Probably its also possible to configure the BTS without a reset, this - has not been tested yet. -*/ - -static int abis_nm_rcvmsg_fom(struct msgb *mb) -{ - struct e1inp_sign_link *sign_link = (struct e1inp_sign_link *)mb->dst; - struct gsm_bts *bts = sign_link->trx->bts; - struct abis_om_hdr *oh = msgb_l2(mb); - struct abis_om_nokia_hdr *noh = msgb_l3(mb); - uint8_t mt = noh->msg_type; - int ret = 0; - uint16_t ref = ntohs(noh->reference); - uint8_t info[256]; - uint8_t ack = 0xFF; - uint8_t severity = 0xFF; - int str_len; - int len_data; - - if (bts->nokia.wait_reset) { - LOGP(DNM, LOGL_INFO, - "Ignore message while waiting for reset\n"); - return ret; - } - - if (oh->length < sizeof(struct abis_om_nokia_hdr)) { - LOGP(DNM, LOGL_ERROR, "Message too short\n"); - return -EINVAL; - } - - len_data = oh->length - sizeof(struct abis_om_nokia_hdr); - LOGP(DNM, LOGL_INFO, "(0x%02X) %s\n", mt, get_msg_type_name_string(mt)); -#if 0 /* debugging */ - dump_elements(noh->data, len_data); -#endif - - switch (mt) { - case NOKIA_MSG_OMU_STARTED: - if (find_element(noh->data, len_data, - NOKIA_EI_BTS_TYPE, &bts->nokia.bts_type, - sizeof(uint8_t)) == sizeof(uint8_t)) - LOGP(DNM, LOGL_INFO, "BTS type = %d (%s)\n", - bts->nokia.bts_type, - get_bts_type_string(bts->nokia.bts_type)); - else - LOGP(DNM, LOGL_ERROR, "BTS type not found\n"); - /* send START_DOWNLOAD_REQ */ - abis_nm_download_req(bts, ref); - break; - case NOKIA_MSG_MF_REQ: - break; - case NOKIA_MSG_CONF_REQ: - /* send ACK */ - abis_nm_ack(bts, ref); - nokia_abis_nm_queue_send_next(bts); - /* send CONF_DATA */ - abis_nm_send_config(bts, bts->nokia.bts_type); - bts->nokia.configured = 1; - break; - case NOKIA_MSG_ACK: - if (find_element - (noh->data, len_data, NOKIA_EI_ACK, &ack, - sizeof(uint8_t)) == sizeof(uint8_t)) { - LOGP(DNM, LOGL_INFO, "ACK = %d\n", ack); - if (ack != 1) { - LOGP(DNM, LOGL_ERROR, "No ACK received (%d)\n", - ack); - /* TODO: properly handle failures (NACK) */ - } - } else - LOGP(DNM, LOGL_ERROR, "ACK not found\n"); - - /* TODO: the assumption for the following is that no NACK was received */ - - /* ACK for reset message ? */ - if (!bts->nokia.did_reset) { - bts->nokia.did_reset = 1; - - /* - TODO: For the InSite processing the received data is - blocked in the driver during reset. - Otherwise the LAPD module might assert because the InSite - sends garbage on the E1 line during reset. - This is done by looking at "wait_reset" in the driver - (function handle_ts1_read()) and ignoring the received data. - It seems to be necessary for the MetroSite too. - */ - bts->nokia.wait_reset = 1; - - osmo_timer_setup(&bts->nokia.reset_timer, - reset_timer_cb, bts); - osmo_timer_schedule(&bts->nokia.reset_timer, bts->nokia.bts_reset_timer_cnf, 0); - - struct gsm_e1_subslot *e1_link = &bts->oml_e1_link; - struct e1inp_line *line; - /* OML link */ - line = e1inp_line_find(e1_link->e1_nr); - if (!line) { - LOGP(DLINP, LOGL_ERROR, - "BTS %u OML link referring to " - "non-existing E1 line %u\n", bts->nr, - e1_link->e1_nr); - return -ENOMEM; - } - - start_sabm_in_line(line, 0, -1); /* stop all first */ - } - - /* ACK for CONF DATA message ? */ - if (bts->nokia.configured != 0) { - /* start TRX (RSL link) */ - - struct gsm_e1_subslot *e1_link = - &sign_link->trx->rsl_e1_link; - struct e1inp_line *line; - - bts->nokia.configured = 0; - - /* RSL Link */ - line = e1inp_line_find(e1_link->e1_nr); - if (!line) { - LOGP(DLINP, LOGL_ERROR, - "TRX (%u/%u) RSL link referring " - "to non-existing E1 line %u\n", - sign_link->trx->bts->nr, sign_link->trx->nr, - e1_link->e1_nr); - return -ENOMEM; - } - /* start TRX */ - start_sabm_in_line(line, 1, SAPI_RSL); /* start only RSL */ - } - break; - case NOKIA_MSG_STATE_CHANGED: - /* send ACK */ - abis_nm_ack(bts, ref); - break; - case NOKIA_MSG_CONF_COMPLETE: - /* send ACK */ - abis_nm_ack(bts, ref); - break; - case NOKIA_MSG_BLOCK_CTRL_REQ: /* seems to be send when something goes wrong !? */ - /* send ACK (do we have to send an ACK ?) */ - abis_nm_ack(bts, ref); - break; - case NOKIA_MSG_ALARM: - find_element(noh->data, len_data, NOKIA_EI_SEVERITY, &severity, - sizeof(severity)); - /* TODO: there might be alarms with both elements set */ - str_len = - find_element(noh->data, len_data, NOKIA_EI_ADD_INFO, info, - sizeof(info)); - if (str_len > 0) { - info[str_len] = 0; - LOGP(DNM, LOGL_INFO, "ALARM Severity %s (%d) : %s\n", - get_severity_string(severity), severity, info); - } else { /* nothing found, try details */ - str_len = - find_element(noh->data, len_data, - NOKIA_EI_ALARM_DETAIL, info, - sizeof(info)); - if (str_len > 0) { - uint16_t code; - info[str_len] = 0; - code = (info[0] << 8) + info[1]; - LOGP(DNM, LOGL_INFO, - "ALARM Severity %s (%d), code 0x%X : %s\n", - get_severity_string(severity), severity, - code, info + 2); - } - } - /* send ACK */ - abis_nm_ack(bts, ref); - break; - } - - nokia_abis_nm_queue_send_next(bts); - - return ret; -} - -/* TODO: put in a separate file ? */ - -int abis_nokia_rcvmsg(struct msgb *msg) -{ - struct abis_om_hdr *oh = msgb_l2(msg); - int rc = 0; - - /* Various consistency checks */ - if (oh->placement != ABIS_OM_PLACEMENT_ONLY) { - LOGP(DNM, LOGL_ERROR, "ABIS OML placement 0x%x not supported\n", - oh->placement); - if (oh->placement != ABIS_OM_PLACEMENT_FIRST) - return -EINVAL; - } - if (oh->sequence != 0) { - LOGP(DNM, LOGL_ERROR, "ABIS OML sequence 0x%x != 0x00\n", - oh->sequence); - return -EINVAL; - } - msg->l3h = (unsigned char *)oh + sizeof(*oh); - - switch (oh->mdisc) { - case ABIS_OM_MDISC_FOM: - LOGP(DNM, LOGL_INFO, "ABIS_OM_MDISC_FOM\n"); - rc = abis_nm_rcvmsg_fom(msg); - break; - case ABIS_OM_MDISC_MANUF: - LOGP(DNM, LOGL_INFO, "ABIS_OM_MDISC_MANUF\n"); - break; - case ABIS_OM_MDISC_MMI: - case ABIS_OM_MDISC_TRAU: - LOGP(DNM, LOGL_ERROR, - "unimplemented ABIS OML message discriminator 0x%x\n", - oh->mdisc); - break; - default: - LOGP(DNM, LOGL_ERROR, - "unknown ABIS OML message discriminator 0x%x\n", - oh->mdisc); - return -EINVAL; - } - - msgb_free(msg); - return rc; -} - -static int bts_model_nokia_site_start(struct gsm_network *net); - -static void bts_model_nokia_site_e1line_bind_ops(struct e1inp_line *line) -{ - e1inp_line_bind_ops(line, &bts_isdn_e1inp_line_ops); -} - -static struct gsm_bts_model model_nokia_site = { - .type = GSM_BTS_TYPE_NOKIA_SITE, - .name = "nokia_site", - .start = bts_model_nokia_site_start, - .oml_rcvmsg = &abis_nokia_rcvmsg, - .e1line_bind_ops = &bts_model_nokia_site_e1line_bind_ops, -}; - -static struct gsm_network *my_net; - -static int bts_model_nokia_site_start(struct gsm_network *net) -{ - model_nokia_site.features.data = &model_nokia_site._features_data[0]; - model_nokia_site.features.data_len = - sizeof(model_nokia_site._features_data); - - gsm_btsmodel_set_feature(&model_nokia_site, BTS_FEAT_HOPPING); - gsm_btsmodel_set_feature(&model_nokia_site, BTS_FEAT_HSCSD); - gsm_btsmodel_set_feature(&model_nokia_site, BTS_FEAT_MULTI_TSC); - - osmo_signal_register_handler(SS_L_INPUT, inp_sig_cb, NULL); - osmo_signal_register_handler(SS_L_GLOBAL, gbl_sig_cb, NULL); - osmo_signal_register_handler(SS_NM, nm_sig_cb, NULL); - - my_net = net; - - return 0; -} - -int bts_model_nokia_site_init(void) -{ - return gsm_bts_model_register(&model_nokia_site); -} diff --git a/openbsc/src/libbsc/bts_siemens_bs11.c b/openbsc/src/libbsc/bts_siemens_bs11.c deleted file mode 100644 index c083b1e06..000000000 --- a/openbsc/src/libbsc/bts_siemens_bs11.c +++ /dev/null @@ -1,602 +0,0 @@ -/* Siemens BS-11 specific code */ - -/* (C) 2009-2010 by Harald Welte <laforge@gnumonks.org> - * - * 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 <http://www.gnu.org/licenses/>. - * - */ - - -#include <osmocom/gsm/tlv.h> - -#include <openbsc/debug.h> -#include <openbsc/gsm_data.h> -#include <openbsc/abis_nm.h> -#include <osmocom/abis/e1_input.h> -#include <openbsc/signal.h> - -static int bts_model_bs11_start(struct gsm_network *net); - -static void bts_model_bs11_e1line_bind_ops(struct e1inp_line *line) -{ - e1inp_line_bind_ops(line, &bts_isdn_e1inp_line_ops); -} - -static struct gsm_bts_model model_bs11 = { - .type = GSM_BTS_TYPE_BS11, - .name = "bs11", - .start = bts_model_bs11_start, - .oml_rcvmsg = &abis_nm_rcvmsg, - .e1line_bind_ops = bts_model_bs11_e1line_bind_ops, - .nm_att_tlvdef = { - .def = { - [NM_ATT_AVAIL_STATUS] = { TLV_TYPE_TLV }, - /* BS11 specifics */ - [NM_ATT_BS11_ESN_FW_CODE_NO] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_ESN_HW_CODE_NO] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_ESN_PCB_SERIAL] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_BOOT_SW_VERS] = { TLV_TYPE_TLV }, - [0xd5] = { TLV_TYPE_TLV }, - [0xa8] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_PASSWORD] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_TXPWR] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_RSSI_OFFS] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_LINE_CFG] = { TLV_TYPE_TV }, - [NM_ATT_BS11_L1_PROT_TYPE] = { TLV_TYPE_TV }, - [NM_ATT_BS11_BIT_ERR_THESH] = { TLV_TYPE_FIXED, 2 }, - [NM_ATT_BS11_DIVERSITY] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_LMT_LOGON_SESSION]={ TLV_TYPE_TLV }, - [NM_ATT_BS11_LMT_LOGIN_TIME] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_LMT_USER_ACC_LEV] ={ TLV_TYPE_TLV }, - [NM_ATT_BS11_LMT_USER_NAME] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_BTS_STATE] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_E1_STATE] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_PLL_MODE] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_PLL] = { TLV_TYPE_TLV }, - [NM_ATT_BS11_CCLK_ACCURACY] = { TLV_TYPE_TV }, - [NM_ATT_BS11_CCLK_TYPE] = { TLV_TYPE_TV }, - [0x95] = { TLV_TYPE_FIXED, 2 }, - }, - }, -}; - -/* The following definitions are for OM and NM packets that we cannot yet - * generate by code but we just pass on */ - -// BTS Site Manager, SET ATTRIBUTES - -/* - Object Class: BTS Site Manager - Instance 1: FF - Instance 2: FF - Instance 3: FF -SET ATTRIBUTES - sAbisExternalTime: 2007/09/08 14:36:11 - omLAPDRelTimer: 30sec - shortLAPDIntTimer: 5sec - emergencyTimer1: 10 minutes - emergencyTimer2: 0 minutes -*/ - -unsigned char msg_1[] = -{ - NM_MT_BS11_SET_ATTR, NM_OC_SITE_MANAGER, 0xFF, 0xFF, 0xFF, - NM_ATT_BS11_ABIS_EXT_TIME, 0x07, - 0xD7, 0x09, 0x08, 0x0E, 0x24, 0x0B, 0xCE, - 0x02, - 0x00, 0x1E, - NM_ATT_BS11_SH_LAPD_INT_TIMER, - 0x01, 0x05, - 0x42, 0x02, 0x00, 0x0A, - 0x44, 0x02, 0x00, 0x00 -}; - -// BTS, SET BTS ATTRIBUTES - -/* - Object Class: BTS - BTS relat. Number: 0 - Instance 2: FF - Instance 3: FF -SET BTS ATTRIBUTES - bsIdentityCode / BSIC: - PLMN_colour_code: 7h - BS_colour_code: 7h - BTS Air Timer T3105: 4 ,unit 10 ms - btsIsHopping: FALSE - periodCCCHLoadIndication: 1sec - thresholdCCCHLoadIndication: 0% - cellAllocationNumber: 00h = GSM 900 - enableInterferenceClass: 00h = Disabled - fACCHQual: 6 (FACCH stealing flags minus 1) - intaveParameter: 31 SACCH multiframes - interferenceLevelBoundaries: - Interference Boundary 1: 0Ah - Interference Boundary 2: 0Fh - Interference Boundary 3: 14h - Interference Boundary 4: 19h - Interference Boundary 5: 1Eh - mSTxPwrMax: 11 - GSM range: 2=39dBm, 15=13dBm, stepsize 2 dBm - DCS1800 range: 0=30dBm, 15=0dBm, stepsize 2 dBm - PCS1900 range: 0=30dBm, 15=0dBm, stepsize 2 dBm - 30=33dBm, 31=32dBm - ny1: - Maximum number of repetitions for PHYSICAL INFORMATION message (GSM 04.08): 20 - powerOutputThresholds: - Out Power Fault Threshold: -10 dB - Red Out Power Threshold: - 6 dB - Excessive Out Power Threshold: 5 dB - rACHBusyThreshold: -127 dBm - rACHLoadAveragingSlots: 250 ,number of RACH burst periods - rfResourceIndicationPeriod: 125 SACCH multiframes - T200: - SDCCH: 044 in 5 ms - FACCH/Full rate: 031 in 5 ms - FACCH/Half rate: 041 in 5 ms - SACCH with TCH SAPI0: 090 in 10 ms - SACCH with SDCCH: 090 in 10 ms - SDCCH with SAPI3: 090 in 5 ms - SACCH with TCH SAPI3: 135 in 10 ms - tSync: 9000 units of 10 msec - tTrau: 9000 units of 10 msec - enableUmLoopTest: 00h = disabled - enableExcessiveDistance: 00h = Disabled - excessiveDistance: 64km - hoppingMode: 00h = baseband hopping - cellType: 00h = Standard Cell - BCCH ARFCN / bCCHFrequency: 1 -*/ - -static unsigned char bs11_attr_bts[] = -{ - NM_ATT_BSIC, HARDCODED_BSIC, - NM_ATT_BTS_AIR_TIMER, 0x04, - NM_ATT_BS11_BTSLS_HOPPING, 0x00, - NM_ATT_CCCH_L_I_P, 0x01, - NM_ATT_CCCH_L_T, 0x00, - NM_ATT_BS11_CELL_ALLOC_NR, NM_BS11_CANR_GSM, - NM_ATT_BS11_ENA_INTERF_CLASS, 0x01, - NM_ATT_BS11_FACCH_QUAL, 0x06, - /* interference avg. period in numbers of SACCH multifr */ - NM_ATT_INTAVE_PARAM, 0x1F, - NM_ATT_INTERF_BOUND, 0x0A, 0x0F, 0x14, 0x19, 0x1E, 0x7B, - NM_ATT_CCCH_L_T, 0x23, - NM_ATT_GSM_TIME, 0x28, 0x00, - NM_ATT_ADM_STATE, 0x03, - NM_ATT_RACH_B_THRESH, 0x7F, - NM_ATT_LDAVG_SLOTS, 0x00, 0xFA, - NM_ATT_BS11_RF_RES_IND_PER, 0x7D, - NM_ATT_T200, 0x2C, 0x1F, 0x29, 0x5A, 0x5A, 0x5A, 0x87, - NM_ATT_BS11_TSYNC, 0x23, 0x28, - NM_ATT_BS11_TTRAU, 0x23, 0x28, - NM_ATT_TEST_DUR, 0x01, 0x00, - NM_ATT_OUTST_ALARM, 0x01, 0x00, - NM_ATT_BS11_EXCESSIVE_DISTANCE, 0x01, 0x40, - NM_ATT_BS11_HOPPING_MODE, 0x01, 0x00, - NM_ATT_BS11_PLL, 0x01, 0x00, - NM_ATT_BCCH_ARFCN, 0x00, HARDCODED_ARFCN/*0x01*/, -}; - -// Handover Recognition, SET ATTRIBUTES - -/* -Illegal Contents GSM Formatted O&M Msg - Object Class: Handover Recognition - BTS relat. Number: 0 - Instance 2: FF - Instance 3: FF -SET ATTRIBUTES - enableDelayPowerBudgetHO: 00h = Disabled - enableDistanceHO: 00h = Disabled - enableInternalInterCellHandover: 00h = Disabled - enableInternalIntraCellHandover: 00h = Disabled - enablePowerBudgetHO: 00h = Disabled - enableRXLEVHO: 00h = Disabled - enableRXQUALHO: 00h = Disabled - hoAveragingDistance: 8 SACCH multiframes - hoAveragingLev: - A_LEV_HO: 8 SACCH multiframes - W_LEV_HO: 1 SACCH multiframes - hoAveragingPowerBudget: 16 SACCH multiframes - hoAveragingQual: - A_QUAL_HO: 8 SACCH multiframes - W_QUAL_HO: 2 SACCH multiframes - hoLowerThresholdLevDL: (10 - 110) dBm - hoLowerThresholdLevUL: (5 - 110) dBm - hoLowerThresholdQualDL: 06h = 6.4% < BER < 12.8% - hoLowerThresholdQualUL: 06h = 6.4% < BER < 12.8% - hoThresholdLevDLintra : (20 - 110) dBm - hoThresholdLevULintra: (20 - 110) dBm - hoThresholdMsRangeMax: 20 km - nCell: 06h - timerHORequest: 3 ,unit 2 SACCH multiframes -*/ - -unsigned char msg_3[] = -{ - NM_MT_BS11_SET_ATTR, NM_OC_BS11_HANDOVER, 0x00, 0xFF, 0xFF, - 0xD0, 0x00, /* enableDelayPowerBudgetHO */ - 0x64, 0x00, /* enableDistanceHO */ - 0x67, 0x00, /* enableInternalInterCellHandover */ - 0x68, 0x00, /* enableInternalInterCellHandover */ - 0x6A, 0x00, /* enablePowerBudgetHO */ - 0x6C, 0x00, /* enableRXLEVHO */ - 0x6D, 0x00, /* enableRXQUALHO */ - 0x6F, 0x08, /* hoAveragingDistance */ - 0x70, 0x08, 0x01, /* hoAveragingLev */ - 0x71, 0x10, 0x10, 0x10, - 0x72, 0x08, 0x02, /* hoAveragingQual */ - 0x73, 0x0A, /* hoLowerThresholdLevDL */ - 0x74, 0x05, /* hoLowerThresholdLevUL */ - 0x75, 0x06, /* hoLowerThresholdQualDL */ - 0x76, 0x06, /* hoLowerThresholdQualUL */ - 0x78, 0x14, /* hoThresholdLevDLintra */ - 0x79, 0x14, /* hoThresholdLevULintra */ - 0x7A, 0x14, /* hoThresholdMsRangeMax */ - 0x7D, 0x06, /* nCell */ - NM_ATT_BS11_TIMER_HO_REQUEST, 0x03, - 0x20, 0x01, 0x00, - 0x45, 0x01, 0x00, - 0x48, 0x01, 0x00, - 0x5A, 0x01, 0x00, - 0x5B, 0x01, 0x05, - 0x5E, 0x01, 0x1A, - 0x5F, 0x01, 0x20, - 0x9D, 0x01, 0x00, - 0x47, 0x01, 0x00, - 0x5C, 0x01, 0x64, - 0x5D, 0x01, 0x1E, - 0x97, 0x01, 0x20, - 0xF7, 0x01, 0x3C, -}; - -// Power Control, SET ATTRIBUTES - -/* - Object Class: Power Control - BTS relat. Number: 0 - Instance 2: FF - Instance 3: FF -SET ATTRIBUTES - enableMsPowerControl: 00h = Disabled - enablePowerControlRLFW: 00h = Disabled - pcAveragingLev: - A_LEV_PC: 4 SACCH multiframes - W_LEV_PC: 1 SACCH multiframes - pcAveragingQual: - A_QUAL_PC: 4 SACCH multiframes - W_QUAL_PC: 2 SACCH multiframes - pcLowerThresholdLevDL: 0Fh - pcLowerThresholdLevUL: 0Ah - pcLowerThresholdQualDL: 05h = 3.2% < BER < 6.4% - pcLowerThresholdQualUL: 05h = 3.2% < BER < 6.4% - pcRLFThreshold: 0Ch - pcUpperThresholdLevDL: 14h - pcUpperThresholdLevUL: 0Fh - pcUpperThresholdQualDL: 04h = 1.6% < BER < 3.2% - pcUpperThresholdQualUL: 04h = 1.6% < BER < 3.2% - powerConfirm: 2 ,unit 2 SACCH multiframes - powerControlInterval: 2 ,unit 2 SACCH multiframes - powerIncrStepSize: 02h = 4 dB - powerRedStepSize: 01h = 2 dB - radioLinkTimeoutBs: 64 SACCH multiframes - enableBSPowerControl: 00h = disabled -*/ - -unsigned char msg_4[] = -{ - NM_MT_BS11_SET_ATTR, NM_OC_BS11_PWR_CTRL, 0x00, 0xFF, 0xFF, - NM_ATT_BS11_ENA_MS_PWR_CTRL, 0x00, - NM_ATT_BS11_ENA_PWR_CTRL_RLFW, 0x00, - 0x7E, 0x04, 0x01, /* pcAveragingLev */ - 0x7F, 0x04, 0x02, /* pcAveragingQual */ - 0x80, 0x0F, /* pcLowerThresholdLevDL */ - 0x81, 0x0A, /* pcLowerThresholdLevUL */ - 0x82, 0x05, /* pcLowerThresholdQualDL */ - 0x83, 0x05, /* pcLowerThresholdQualUL */ - 0x84, 0x0C, /* pcRLFThreshold */ - 0x85, 0x14, /* pcUpperThresholdLevDL */ - 0x86, 0x0F, /* pcUpperThresholdLevUL */ - 0x87, 0x04, /* pcUpperThresholdQualDL */ - 0x88, 0x04, /* pcUpperThresholdQualUL */ - 0x89, 0x02, /* powerConfirm */ - 0x8A, 0x02, /* powerConfirmInterval */ - 0x8B, 0x02, /* powerIncrStepSize */ - 0x8C, 0x01, /* powerRedStepSize */ - 0x8D, 0x40, /* radioLinkTimeoutBs */ - 0x65, 0x01, 0x00 // set to 0x01 to enable BSPowerControl -}; - - -// Transceiver, SET TRX ATTRIBUTES (TRX 0) - -/* - Object Class: Transceiver - BTS relat. Number: 0 - Tranceiver number: 0 - Instance 3: FF -SET TRX ATTRIBUTES - aRFCNList (HEX): 0001 - txPwrMaxReduction: 00h = 30dB - radioMeasGran: 254 SACCH multiframes - radioMeasRep: 01h = enabled - memberOfEmergencyConfig: 01h = TRUE - trxArea: 00h = TRX doesn't belong to a concentric cell -*/ - -static unsigned char bs11_attr_radio[] = -{ - NM_ATT_ARFCN_LIST, 0x01, 0x00, HARDCODED_ARFCN /*0x01*/, - NM_ATT_RF_MAXPOWR_R, 0x00, - NM_ATT_BS11_RADIO_MEAS_GRAN, 0x01, 0x05, - NM_ATT_BS11_RADIO_MEAS_REP, 0x01, 0x01, - NM_ATT_BS11_EMRG_CFG_MEMBER, 0x01, 0x01, - NM_ATT_BS11_TRX_AREA, 0x01, 0x00, -}; - -/* - * Patch the various SYSTEM INFORMATION tables to update - * the LAI - */ -static void patch_nm_tables(struct gsm_bts *bts) -{ - uint8_t arfcn_low = bts->c0->arfcn & 0xff; - uint8_t arfcn_high = (bts->c0->arfcn >> 8) & 0x0f; - - /* T3105 attribute in units of 10ms */ - bs11_attr_bts[2] = bts->network->T3105 / 10; - - /* patch ARFCN into BTS Attributes */ - bs11_attr_bts[69] &= 0xf0; - bs11_attr_bts[69] |= arfcn_high; - bs11_attr_bts[70] = arfcn_low; - - /* patch ARFCN into TRX Attributes */ - bs11_attr_radio[2] &= 0xf0; - bs11_attr_radio[2] |= arfcn_high; - bs11_attr_radio[3] = arfcn_low; - - /* patch the RACH attributes */ - if (bts->rach_b_thresh != -1) - bs11_attr_bts[33] = bts->rach_b_thresh & 0xff; - - if (bts->rach_ldavg_slots != -1) { - uint8_t avg_high = bts->rach_ldavg_slots & 0xff; - uint8_t avg_low = (bts->rach_ldavg_slots >> 8) & 0x0f; - - bs11_attr_bts[35] = avg_high; - bs11_attr_bts[36] = avg_low; - } - - /* patch BSIC */ - bs11_attr_bts[1] = bts->bsic; - - /* patch the power reduction */ - bs11_attr_radio[5] = bts->c0->max_power_red / 2; -} - - -static void nm_reconfig_ts(struct gsm_bts_trx_ts *ts) -{ - enum abis_nm_chan_comb ccomb = abis_nm_chcomb4pchan(ts->pchan); - struct gsm_e1_subslot *e1l = &ts->e1_link; - - abis_nm_set_channel_attr(ts, ccomb); - - if (is_ipaccess_bts(ts->trx->bts)) - return; - - if (ts_is_tch(ts)) - abis_nm_conn_terr_traf(ts, e1l->e1_nr, e1l->e1_ts, - e1l->e1_ts_ss); -} - -static void nm_reconfig_trx(struct gsm_bts_trx *trx) -{ - struct gsm_e1_subslot *e1l = &trx->rsl_e1_link; - int i; - - patch_nm_tables(trx->bts); - - switch (trx->bts->type) { - case GSM_BTS_TYPE_BS11: - /* FIXME: discover this by fetching an attribute */ -#if 0 - trx->nominal_power = 15; /* 15dBm == 30mW PA configuration */ -#else - trx->nominal_power = 24; /* 24dBm == 250mW PA configuration */ -#endif - abis_nm_conn_terr_sign(trx, e1l->e1_nr, e1l->e1_ts, - e1l->e1_ts_ss); - abis_nm_establish_tei(trx->bts, trx->nr, e1l->e1_nr, - e1l->e1_ts, e1l->e1_ts_ss, trx->rsl_tei); - - /* Set Radio Attributes */ - if (trx == trx->bts->c0) - abis_nm_set_radio_attr(trx, bs11_attr_radio, - sizeof(bs11_attr_radio)); - else { - uint8_t trx1_attr_radio[sizeof(bs11_attr_radio)]; - uint8_t arfcn_low = trx->arfcn & 0xff; - uint8_t arfcn_high = (trx->arfcn >> 8) & 0x0f; - memcpy(trx1_attr_radio, bs11_attr_radio, - sizeof(trx1_attr_radio)); - - /* patch ARFCN into TRX Attributes */ - trx1_attr_radio[2] &= 0xf0; - trx1_attr_radio[2] |= arfcn_high; - trx1_attr_radio[3] = arfcn_low; - - abis_nm_set_radio_attr(trx, trx1_attr_radio, - sizeof(trx1_attr_radio)); - } - break; - case GSM_BTS_TYPE_NANOBTS: - switch (trx->bts->band) { - case GSM_BAND_850: - case GSM_BAND_900: - trx->nominal_power = 20; - break; - case GSM_BAND_1800: - case GSM_BAND_1900: - trx->nominal_power = 23; - break; - default: - LOGP(DNM, LOGL_ERROR, "Unsupported nanoBTS GSM band %s\n", - gsm_band_name(trx->bts->band)); - break; - } - break; - default: - break; - } - - for (i = 0; i < TRX_NR_TS; i++) - nm_reconfig_ts(&trx->ts[i]); -} - -static void nm_reconfig_bts(struct gsm_bts *bts) -{ - struct gsm_bts_trx *trx; - - switch (bts->type) { - case GSM_BTS_TYPE_BS11: - patch_nm_tables(bts); - abis_nm_raw_msg(bts, sizeof(msg_1), msg_1); /* set BTS SiteMgr attr*/ - abis_nm_set_bts_attr(bts, bs11_attr_bts, sizeof(bs11_attr_bts)); - abis_nm_raw_msg(bts, sizeof(msg_3), msg_3); /* set BTS handover attr */ - abis_nm_raw_msg(bts, sizeof(msg_4), msg_4); /* set BTS power control attr */ - break; - default: - break; - } - - llist_for_each_entry(trx, &bts->trx_list, list) - nm_reconfig_trx(trx); -} - - -static void bootstrap_om_bs11(struct gsm_bts *bts) -{ - LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for BTS %u\n", bts->nr); - - /* stop sending event reports */ - abis_nm_event_reports(bts, 0); - - /* begin DB transmission */ - abis_nm_bs11_db_transmission(bts, 1); - - /* end DB transmission */ - abis_nm_bs11_db_transmission(bts, 0); - - /* Reset BTS Site manager resource */ - abis_nm_bs11_reset_resource(bts); - - /* begin DB transmission */ - abis_nm_bs11_db_transmission(bts, 1); - - /* reconfigure BTS with all TRX and all TS */ - nm_reconfig_bts(bts); - - /* end DB transmission */ - abis_nm_bs11_db_transmission(bts, 0); - - /* Reset BTS Site manager resource */ - abis_nm_bs11_reset_resource(bts); - - /* restart sending event reports */ - abis_nm_event_reports(bts, 1); -} - -static int shutdown_om(struct gsm_bts *bts) -{ - /* stop sending event reports */ - abis_nm_event_reports(bts, 0); - - /* begin DB transmission */ - abis_nm_bs11_db_transmission(bts, 1); - - /* end DB transmission */ - abis_nm_bs11_db_transmission(bts, 0); - - /* Reset BTS Site manager resource */ - abis_nm_bs11_reset_resource(bts); - - return 0; -} - -/* Callback function to be called every time we receive a signal from INPUT */ -static int gbl_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct gsm_bts *bts; - - if (subsys != SS_L_GLOBAL) - return 0; - - switch (signal) { - case S_GLOBAL_BTS_CLOSE_OM: - bts = signal_data; - if (bts->type == GSM_BTS_TYPE_BS11) - shutdown_om(signal_data); - break; - } - - return 0; -} - -/* Callback function to be called every time we receive a signal from INPUT */ -static int inp_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct input_signal_data *isd = signal_data; - - if (subsys != SS_L_INPUT) - return 0; - - switch (signal) { - case S_L_INP_TEI_UP: - switch (isd->link_type) { - case E1INP_SIGN_OML: - if (isd->trx->bts->type == GSM_BTS_TYPE_BS11) - bootstrap_om_bs11(isd->trx->bts); - break; - } - } - - return 0; -} - -static int bts_model_bs11_start(struct gsm_network *net) -{ - model_bs11.features.data = &model_bs11._features_data[0]; - model_bs11.features.data_len = sizeof(model_bs11._features_data); - - gsm_btsmodel_set_feature(&model_bs11, BTS_FEAT_HOPPING); - gsm_btsmodel_set_feature(&model_bs11, BTS_FEAT_HSCSD); - gsm_btsmodel_set_feature(&model_bs11, BTS_FEAT_MULTI_TSC); - - osmo_signal_register_handler(SS_L_INPUT, inp_sig_cb, NULL); - osmo_signal_register_handler(SS_L_GLOBAL, gbl_sig_cb, NULL); - - return 0; -} - -int bts_model_bs11_init(void) -{ - return gsm_bts_model_register(&model_bs11); -} diff --git a/openbsc/src/libbsc/bts_sysmobts.c b/openbsc/src/libbsc/bts_sysmobts.c deleted file mode 100644 index e4b6cdc78..000000000 --- a/openbsc/src/libbsc/bts_sysmobts.c +++ /dev/null @@ -1,60 +0,0 @@ -/* sysmocom sysmoBTS specific code */ - -/* (C) 2010-2012 by Harald Welte <laforge@gnumonks.org> - * - * 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 <http://www.gnu.org/licenses/>. - * - */ - -#include <arpa/inet.h> - -#include <osmocom/gsm/tlv.h> - -#include <openbsc/gsm_data.h> -#include <openbsc/signal.h> -#include <openbsc/abis_nm.h> -#include <osmocom/abis/e1_input.h> -#include <osmocom/gsm/tlv.h> -#include <osmocom/core/msgb.h> -#include <osmocom/core/talloc.h> -#include <openbsc/gsm_data.h> -#include <openbsc/abis_nm.h> -#include <openbsc/abis_rsl.h> -#include <openbsc/debug.h> -#include <osmocom/abis/subchan_demux.h> -#include <osmocom/abis/ipaccess.h> -#include <osmocom/core/logging.h> - -extern struct gsm_bts_model bts_model_nanobts; - -static struct gsm_bts_model model_sysmobts; - -int bts_model_sysmobts_init(void) -{ - model_sysmobts = bts_model_nanobts; - model_sysmobts.name = "sysmobts"; - model_sysmobts.type = GSM_BTS_TYPE_OSMOBTS; - - model_sysmobts.features.data = &model_sysmobts._features_data[0]; - model_sysmobts.features.data_len = - sizeof(model_sysmobts._features_data); - memset(model_sysmobts.features.data, 0, sizeof(model_sysmobts.features.data_len)); - - gsm_btsmodel_set_feature(&model_sysmobts, BTS_FEAT_GPRS); - gsm_btsmodel_set_feature(&model_sysmobts, BTS_FEAT_EGPRS); - - return gsm_bts_model_register(&model_sysmobts); -} diff --git a/openbsc/src/libbsc/bts_unknown.c b/openbsc/src/libbsc/bts_unknown.c deleted file mode 100644 index f1135294f..000000000 --- a/openbsc/src/libbsc/bts_unknown.c +++ /dev/null @@ -1,40 +0,0 @@ -/* Generic BTS - VTY code tries to allocate this BTS before type is known */ - -/* (C) 2010 by Daniel Willmann <daniel@totalueberwachung.de> - * - * 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 <http://www.gnu.org/licenses/>. - * - */ - - -#include <openbsc/gsm_data.h> -#include <osmocom/gsm/tlv.h> -#include <openbsc/abis_nm.h> - -static struct gsm_bts_model model_unknown = { - .type = GSM_BTS_TYPE_UNKNOWN, - .name = "unknown", - .oml_rcvmsg = &abis_nm_rcvmsg, - .nm_att_tlvdef = { - .def = { - }, - }, -}; - -int bts_model_unknown_init(void) -{ - return gsm_bts_model_register(&model_unknown); -} diff --git a/openbsc/src/libbsc/chan_alloc.c b/openbsc/src/libbsc/chan_alloc.c deleted file mode 100644 index 33b79a0b2..000000000 --- a/openbsc/src/libbsc/chan_alloc.c +++ /dev/null @@ -1,543 +0,0 @@ -/* GSM Channel allocation routines - * - * (C) 2008 by Harald Welte <laforge@gnumonks.org> - * (C) 2008, 2009 by Holger Hans Peter Freyther <zecke@selfish.org> - * - * 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 <http://www.gnu.org/licenses/>. - * - */ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> - -#include <openbsc/gsm_subscriber.h> -#include <openbsc/chan_alloc.h> -#include <openbsc/abis_nm.h> -#include <openbsc/abis_rsl.h> -#include <openbsc/debug.h> -#include <openbsc/rtp_proxy.h> -#include <openbsc/signal.h> - -#include <osmocom/core/talloc.h> - -static int ts_is_usable(struct gsm_bts_trx_ts *ts) -{ - /* FIXME: How does this behave for BS-11 ? */ - if (is_ipaccess_bts(ts->trx->bts)) { - if (!nm_is_running(&ts->mo.nm_state)) - return 0; - } - - /* If a TCH/F_PDCH TS is busy changing, it is already taken or not - * yet available. */ - if (ts->pchan == GSM_PCHAN_TCH_F_PDCH) { - if (ts->flags & TS_F_PDCH_PENDING_MASK) - return 0; - } - - /* If a dynamic channel is busy changing, it is already taken or not - * yet available. */ - if (ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH) { - if (ts->dyn.pchan_is != ts->dyn.pchan_want) - return 0; - } - - return 1; -} - -int trx_is_usable(struct gsm_bts_trx *trx) -{ - /* FIXME: How does this behave for BS-11 ? */ - if (is_ipaccess_bts(trx->bts)) { - if (!nm_is_running(&trx->mo.nm_state) || - !nm_is_running(&trx->bb_transc.mo.nm_state)) - return 0; - } - - return 1; -} - -static struct gsm_lchan * -_lc_find_trx(struct gsm_bts_trx *trx, enum gsm_phys_chan_config pchan, - enum gsm_phys_chan_config dyn_as_pchan) -{ - struct gsm_bts_trx_ts *ts; - int j, start, stop, dir, ss; - int check_subslots; - - if (!trx_is_usable(trx)) - return NULL; - - if (trx->bts->chan_alloc_reverse) { - /* check TS 7..0 */ - start = 7; - stop = -1; - dir = -1; - } else { - /* check TS 0..7 */ - start = 0; - stop = 8; - dir = 1; - } - - for (j = start; j != stop; j += dir) { - ts = &trx->ts[j]; - if (!ts_is_usable(ts)) - continue; - if (ts->pchan != pchan) - continue; - - /* - * Allocation for fully dynamic timeslots - * (does not apply for ip.access style GSM_PCHAN_TCH_F_PDCH) - * - * Note the special nature of a dynamic timeslot in PDCH mode: - * in PDCH mode, typically, lchan->type is GSM_LCHAN_NONE and - * lchan->state is LCHAN_S_NONE -- an otherwise unused slot - * becomes PDCH implicitly. In the same sense, this channel - * allocator will never be asked to find an available PDCH - * slot; only TCH/F or TCH/H will be requested, and PDCH mode - * means that it is available for switchover. - * - * A dynamic timeslot in PDCH mode may be switched to TCH/F or - * TCH/H. If a dyn TS is already in TCH/F or TCH/H mode, it - * means that it is in use and its mode can't be switched. - * - * The logic concerning channels for TCH/F is trivial: there is - * only one channel, so a dynamic TS in TCH/F mode is already - * taken and not available for allocation. For TCH/H, we need - * to check whether a dynamic timeslot is already in TCH/H mode - * and whether one of the two channels is still available. - */ - switch (pchan) { - case GSM_PCHAN_TCH_F_TCH_H_PDCH: - if (ts->dyn.pchan_is != ts->dyn.pchan_want) { - /* The TS's mode is being switched. Not - * available anymore/yet. */ - DEBUGP(DRLL, "%s already in switchover\n", - gsm_ts_and_pchan_name(ts)); - continue; - } - if (ts->dyn.pchan_is == GSM_PCHAN_PDCH) { - /* This slot is available. Still check for - * error states to be sure; in all cases the - * first lchan will be used. */ - if (ts->lchan->state != LCHAN_S_NONE - && ts->lchan->state != LCHAN_S_ACTIVE) - continue; - return ts->lchan; - } - if (ts->dyn.pchan_is != dyn_as_pchan) - /* not applicable. */ - continue; - /* The requested type matches the dynamic timeslot's - * current mode. A channel may still be available - * (think TCH/H). */ - check_subslots = ts_subslots(ts); - break; - - case GSM_PCHAN_TCH_F_PDCH: - /* Available for voice when in PDCH mode */ - if (ts_pchan(ts) != GSM_PCHAN_PDCH) - continue; - /* Subslots of a PDCH ts don't need to be checked. */ - return ts->lchan; - - default: - /* Not a dynamic channel, there is only one pchan kind: */ - check_subslots = ts_subslots(ts); - break; - } - - /* Is a sub-slot still available? */ - for (ss = 0; ss < check_subslots; ss++) { - struct gsm_lchan *lc = &ts->lchan[ss]; - if (lc->type == GSM_LCHAN_NONE && - lc->state == LCHAN_S_NONE) - return lc; - } - } - - return NULL; -} - -static struct gsm_lchan * -_lc_dyn_find_bts(struct gsm_bts *bts, enum gsm_phys_chan_config pchan, - enum gsm_phys_chan_config dyn_as_pchan) -{ - struct gsm_bts_trx *trx; - struct gsm_lchan *lc; - - if (bts->chan_alloc_reverse) { - llist_for_each_entry_reverse(trx, &bts->trx_list, list) { - lc = _lc_find_trx(trx, pchan, dyn_as_pchan); - if (lc) - return lc; - } - } else { - llist_for_each_entry(trx, &bts->trx_list, list) { - lc = _lc_find_trx(trx, pchan, dyn_as_pchan); - if (lc) - return lc; - } - } - - return NULL; -} - -static struct gsm_lchan * -_lc_find_bts(struct gsm_bts *bts, enum gsm_phys_chan_config pchan) -{ - return _lc_dyn_find_bts(bts, pchan, GSM_PCHAN_NONE); -} - -/* Allocate a logical channel. - * - * Dynamic channel types: we always prefer a dedicated TS, and only pick + - * switch a dynamic TS if no pure TS of the requested PCHAN is available. - * - * TCH_F/PDCH: if we pick a PDCH ACT style dynamic TS as TCH/F channel, PDCH - * will be disabled in rsl_chan_activate_lchan(); there is no need to check - * whether PDCH mode is currently active, here. - */ -struct gsm_lchan *lchan_alloc(struct gsm_bts *bts, enum gsm_chan_t type, - int allow_bigger) -{ - struct gsm_lchan *lchan = NULL; - enum gsm_phys_chan_config first, first_cbch, second, second_cbch; - - switch (type) { - case GSM_LCHAN_SDCCH: - if (bts->chan_alloc_reverse) { - first = GSM_PCHAN_SDCCH8_SACCH8C; - first_cbch = GSM_PCHAN_SDCCH8_SACCH8C_CBCH; - second = GSM_PCHAN_CCCH_SDCCH4; - second_cbch = GSM_PCHAN_CCCH_SDCCH4_CBCH; - } else { - first = GSM_PCHAN_CCCH_SDCCH4; - first_cbch = GSM_PCHAN_CCCH_SDCCH4_CBCH; - second = GSM_PCHAN_SDCCH8_SACCH8C; - second_cbch = GSM_PCHAN_SDCCH8_SACCH8C_CBCH; - } - - lchan = _lc_find_bts(bts, first); - if (lchan == NULL) - lchan = _lc_find_bts(bts, first_cbch); - if (lchan == NULL) - lchan = _lc_find_bts(bts, second); - if (lchan == NULL) - lchan = _lc_find_bts(bts, second_cbch); - - /* allow to assign bigger channels */ - if (allow_bigger) { - if (lchan == NULL) { - lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_H); - if (lchan) - type = GSM_LCHAN_TCH_H; - } - - if (lchan == NULL) { - lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F); - if (lchan) - type = GSM_LCHAN_TCH_F; - } - - /* try dynamic TCH/F_PDCH */ - if (lchan == NULL) { - lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F_PDCH); - /* TCH/F_PDCH will be used as TCH/F */ - if (lchan) - type = GSM_LCHAN_TCH_F; - } - - /* try fully dynamic TCH/F_TCH/H_PDCH */ - if (lchan == NULL) { - lchan = _lc_dyn_find_bts(bts, GSM_PCHAN_TCH_F_TCH_H_PDCH, - GSM_PCHAN_TCH_H); - if (lchan) - type = GSM_LCHAN_TCH_H; - } - /* - * No need to check fully dynamic channels for TCH/F: - * if no TCH/H was available, neither will be TCH/F. - */ - } - break; - case GSM_LCHAN_TCH_F: - lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F); - /* If we don't have TCH/F available, fall-back to TCH/H */ - if (!lchan) { - lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_H); - if (lchan) - type = GSM_LCHAN_TCH_H; - } - /* If we don't have TCH/H either, try dynamic TCH/F_PDCH */ - if (!lchan) { - lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F_PDCH); - /* TCH/F_PDCH used as TCH/F -- here, type is already - * set to GSM_LCHAN_TCH_F, but for clarity's sake... */ - if (lchan) - type = GSM_LCHAN_TCH_F; - } - - /* Try fully dynamic TCH/F_TCH/H_PDCH as TCH/F... */ - if (!lchan && bts->network->dyn_ts_allow_tch_f) { - lchan = _lc_dyn_find_bts(bts, - GSM_PCHAN_TCH_F_TCH_H_PDCH, - GSM_PCHAN_TCH_F); - if (lchan) - type = GSM_LCHAN_TCH_F; - } - /* ...and as TCH/H. */ - if (!lchan) { - lchan = _lc_dyn_find_bts(bts, - GSM_PCHAN_TCH_F_TCH_H_PDCH, - GSM_PCHAN_TCH_H); - if (lchan) - type = GSM_LCHAN_TCH_H; - } - break; - case GSM_LCHAN_TCH_H: - lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_H); - /* If we don't have TCH/H available, fall-back to TCH/F */ - if (!lchan) { - lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F); - if (lchan) - type = GSM_LCHAN_TCH_F; - } - /* No dedicated TCH/x available -- try fully dynamic - * TCH/F_TCH/H_PDCH */ - if (!lchan) { - lchan = _lc_dyn_find_bts(bts, - GSM_PCHAN_TCH_F_TCH_H_PDCH, - GSM_PCHAN_TCH_H); - if (lchan) - type = GSM_LCHAN_TCH_H; - } - /* - * No need to check TCH/F_TCH/H_PDCH channels for TCH/F: - * if no TCH/H was available, neither will be TCH/F. - */ - /* If we don't have TCH/F either, try dynamic TCH/F_PDCH */ - if (!lchan) { - lchan = _lc_find_bts(bts, GSM_PCHAN_TCH_F_PDCH); - if (lchan) - type = GSM_LCHAN_TCH_F; - } - break; - default: - LOGP(DRLL, LOGL_ERROR, "Unknown gsm_chan_t %u\n", type); - } - - if (lchan) { - lchan->type = type; - - LOGP(DRLL, LOGL_INFO, "%s Allocating lchan=%u as %s\n", - gsm_ts_and_pchan_name(lchan->ts), - lchan->nr, gsm_lchant_name(lchan->type)); - - /* clear sapis */ - memset(lchan->sapis, 0, ARRAY_SIZE(lchan->sapis)); - - /* clear multi rate config */ - memset(&lchan->mr_ms_lv, 0, sizeof(lchan->mr_ms_lv)); - memset(&lchan->mr_bts_lv, 0, sizeof(lchan->mr_bts_lv)); - lchan->broken_reason = ""; - } else { - struct challoc_signal_data sig; - - LOGP(DRLL, LOGL_ERROR, "Failed to allocate %s channel\n", - gsm_lchant_name(type)); - - sig.bts = bts; - sig.type = type; - osmo_signal_dispatch(SS_CHALLOC, S_CHALLOC_ALLOC_FAIL, &sig); - } - - return lchan; -} - -/* Free a logical channel */ -void lchan_free(struct gsm_lchan *lchan) -{ - struct challoc_signal_data sig; - int i; - - sig.type = lchan->type; - lchan->type = GSM_LCHAN_NONE; - - - if (lchan->conn) { - struct lchan_signal_data sig; - - /* We might kill an active channel... */ - sig.lchan = lchan; - sig.mr = NULL; - osmo_signal_dispatch(SS_LCHAN, S_LCHAN_UNEXPECTED_RELEASE, &sig); - } - - if (lchan->abis_ip.rtp_socket) { - LOGP(DRLL, LOGL_ERROR, "%s RTP Proxy Socket remained open.\n", - gsm_lchan_name(lchan)); - rtp_socket_free(lchan->abis_ip.rtp_socket); - lchan->abis_ip.rtp_socket = NULL; - } - - /* stop the timer */ - osmo_timer_del(&lchan->T3101); - - /* clear cached measuement reports */ - lchan->meas_rep_idx = 0; - for (i = 0; i < ARRAY_SIZE(lchan->meas_rep); i++) { - lchan->meas_rep[i].flags = 0; - lchan->meas_rep[i].nr = 0; - } - for (i = 0; i < ARRAY_SIZE(lchan->neigh_meas); i++) - lchan->neigh_meas[i].arfcn = 0; - - if (lchan->rqd_ref) { - talloc_free(lchan->rqd_ref); - lchan->rqd_ref = NULL; - lchan->rqd_ta = 0; - } - - sig.lchan = lchan; - sig.bts = lchan->ts->trx->bts; - osmo_signal_dispatch(SS_CHALLOC, S_CHALLOC_FREED, &sig); - - if (lchan->conn) { - LOGP(DRLL, LOGL_ERROR, "the subscriber connection should be gone.\n"); - lchan->conn = NULL; - } - - /* FIXME: ts_free() the timeslot, if we're the last logical - * channel using it */ -} - -/* - * There was an error with the TRX and we need to forget - * any state so that a lchan can be allocated again after - * the trx is fully usable. - * - * This should be called after lchan_free to force a channel - * be available for allocation again. This means that this - * method will stop the "delay after error"-timer and set the - * state to LCHAN_S_NONE. - */ -void lchan_reset(struct gsm_lchan *lchan) -{ - osmo_timer_del(&lchan->T3101); - osmo_timer_del(&lchan->T3109); - osmo_timer_del(&lchan->T3111); - osmo_timer_del(&lchan->error_timer); - - lchan->type = GSM_LCHAN_NONE; - lchan->state = LCHAN_S_NONE; - - if (lchan->abis_ip.rtp_socket) { - rtp_socket_free(lchan->abis_ip.rtp_socket); - lchan->abis_ip.rtp_socket = NULL; - } -} - -/* Drive the release process of the lchan */ -static void _lchan_handle_release(struct gsm_lchan *lchan, - int sacch_deact, int mode) -{ - /* Release all SAPIs on the local end and continue */ - rsl_release_sapis_from(lchan, 1, RSL_REL_LOCAL_END); - - /* - * Shall we send a RR Release, start T3109 and wait for the - * release indication from the BTS or just take it down (e.g. - * on assignment requests) - */ - if (sacch_deact) { - gsm48_send_rr_release(lchan); - - /* Deactivate the SACCH on the BTS side */ - rsl_deact_sacch(lchan); - rsl_start_t3109(lchan); - } else if (lchan->sapis[0] == LCHAN_SAPI_UNUSED) { - rsl_direct_rf_release(lchan); - } else { - rsl_release_request(lchan, 0, mode); - } -} - -/* Consider releasing the channel now */ -int lchan_release(struct gsm_lchan *lchan, int sacch_deact, enum rsl_rel_mode mode) -{ - DEBUGP(DRLL, "%s starting release sequence\n", gsm_lchan_name(lchan)); - rsl_lchan_set_state(lchan, LCHAN_S_REL_REQ); - - lchan->conn = NULL; - _lchan_handle_release(lchan, sacch_deact, mode); - return 1; -} - -void bts_chan_load(struct pchan_load *cl, const struct gsm_bts *bts) -{ - struct gsm_bts_trx *trx; - - llist_for_each_entry(trx, &bts->trx_list, list) { - int i; - - /* skip administratively deactivated tranxsceivers */ - if (!nm_is_running(&trx->mo.nm_state) || - !nm_is_running(&trx->bb_transc.mo.nm_state)) - continue; - - for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { - struct gsm_bts_trx_ts *ts = &trx->ts[i]; - struct load_counter *pl = &cl->pchan[ts->pchan]; - int j; - int subslots; - - /* skip administratively deactivated timeslots */ - if (!nm_is_running(&ts->mo.nm_state)) - continue; - - subslots = ts_subslots(ts); - for (j = 0; j < subslots; j++) { - struct gsm_lchan *lchan = &ts->lchan[j]; - - pl->total++; - - switch (lchan->state) { - case LCHAN_S_NONE: - break; - default: - pl->used++; - break; - } - } - } - } -} - -void network_chan_load(struct pchan_load *pl, struct gsm_network *net) -{ - struct gsm_bts *bts; - - memset(pl, 0, sizeof(*pl)); - - llist_for_each_entry(bts, &net->bts_list, list) - bts_chan_load(pl, bts); -} - diff --git a/openbsc/src/libbsc/e1_config.c b/openbsc/src/libbsc/e1_config.c deleted file mode 100644 index d57dec57e..000000000 --- a/openbsc/src/libbsc/e1_config.c +++ /dev/null @@ -1,297 +0,0 @@ -/* OpenBSC E1 Input code */ - -/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org> - * 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 <http://www.gnu.org/licenses/>. - * - */ - -#include <string.h> -#include <errno.h> - -#include <netinet/in.h> - -#include <openbsc/gsm_data.h> -#include <osmocom/abis/e1_input.h> -#include <osmocom/abis/trau_frame.h> -#include <openbsc/trau_mux.h> -#include <openbsc/misdn.h> -#include <osmocom/abis/ipaccess.h> -#include <osmocom/core/talloc.h> -#include <openbsc/debug.h> -#include <openbsc/abis_rsl.h> - -#define SAPI_L2ML 0 -#define SAPI_OML 62 -#define SAPI_RSL 0 /* 63 ? */ - -/* The e1_reconfig_*() functions below take the configuration present in the - * bts/trx/ts data structures and ensure the E1 configuration reflects the - * timeslot/subslot/TEI configuration */ - -int e1_reconfig_ts(struct gsm_bts_trx_ts *ts) -{ - struct gsm_e1_subslot *e1_link = &ts->e1_link; - struct e1inp_line *line; - struct e1inp_ts *e1_ts; - - DEBUGP(DLMI, "e1_reconfig_ts(%u,%u,%u)\n", ts->trx->bts->nr, ts->trx->nr, ts->nr); - - if (!e1_link->e1_ts) { - LOGP(DLINP, LOGL_ERROR, "TS (%u/%u/%u) without E1 timeslot?\n", - ts->nr, ts->trx->nr, ts->trx->bts->nr); - return 0; - } - - line = e1inp_line_find(e1_link->e1_nr); - if (!line) { - LOGP(DLINP, LOGL_ERROR, "TS (%u/%u/%u) referring to " - "non-existing E1 line %u\n", ts->nr, ts->trx->nr, - ts->trx->bts->nr, e1_link->e1_nr); - return -ENOMEM; - } - - if (ts_is_tch(ts)) { - e1_ts = &line->ts[e1_link->e1_ts-1]; - e1inp_ts_config_trau(e1_ts, line, subch_cb); - subch_demux_activate(&e1_ts->trau.demux, e1_link->e1_ts_ss); - } - - return 0; -} - -int e1_reconfig_trx(struct gsm_bts_trx *trx) -{ - struct gsm_e1_subslot *e1_link = &trx->rsl_e1_link; - struct e1inp_ts *sign_ts; - struct e1inp_line *line; - struct e1inp_sign_link *rsl_link; - int i; - - if (!e1_link->e1_ts) { - LOGP(DLINP, LOGL_ERROR, "TRX (%u/%u) RSL link without " - "timeslot?\n", trx->bts->nr, trx->nr); - return -EINVAL; - } - - /* RSL Link */ - line = e1inp_line_find(e1_link->e1_nr); - if (!line) { - LOGP(DLINP, LOGL_ERROR, "TRX (%u/%u) RSL link referring " - "to non-existing E1 line %u\n", trx->bts->nr, - trx->nr, e1_link->e1_nr); - return -ENOMEM; - } - sign_ts = &line->ts[e1_link->e1_ts-1]; - e1inp_ts_config_sign(sign_ts, line); - /* Ericsson RBS have a per-TRX OML link in parallel to RSL */ - if (trx->bts->type == GSM_BTS_TYPE_RBS2000) { - struct e1inp_sign_link *oml_link; - oml_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OML, trx, - trx->rsl_tei, SAPI_OML); - if (!oml_link) { - LOGP(DLINP, LOGL_ERROR, "TRX (%u/%u) OML link creation " - "failed\n", trx->bts->nr, trx->nr); - return -ENOMEM; - } - if (trx->oml_link) - e1inp_sign_link_destroy(trx->oml_link); - trx->oml_link = oml_link; - } - rsl_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_RSL, - trx, trx->rsl_tei, SAPI_RSL); - if (!rsl_link) { - LOGP(DLINP, LOGL_ERROR, "TRX (%u/%u) RSL link creation " - "failed\n", trx->bts->nr, trx->nr); - return -ENOMEM; - } - if (trx->rsl_link) - e1inp_sign_link_destroy(trx->rsl_link); - trx->rsl_link = rsl_link; - - for (i = 0; i < TRX_NR_TS; i++) - e1_reconfig_ts(&trx->ts[i]); - - return 0; -} - -/* this is the generic callback for all ISDN-based BTS. */ -static int bts_isdn_sign_link(struct msgb *msg) -{ - int ret = -EINVAL; - struct e1inp_sign_link *link = msg->dst; - struct gsm_bts *bts; - - switch (link->type) { - case E1INP_SIGN_OML: - bts = link->trx->bts; - ret = bts->model->oml_rcvmsg(msg); - break; - case E1INP_SIGN_RSL: - ret = abis_rsl_rcvmsg(msg); - break; - default: - LOGP(DLMI, LOGL_ERROR, "unknown link type %u\n", link->type); - break; - } - return ret; -} - -struct e1inp_line_ops bts_isdn_e1inp_line_ops = { - .sign_link = bts_isdn_sign_link, -}; - -int e1_reconfig_bts(struct gsm_bts *bts) -{ - struct gsm_e1_subslot *e1_link = &bts->oml_e1_link; - struct e1inp_ts *sign_ts; - struct e1inp_line *line; - struct e1inp_sign_link *oml_link; - struct gsm_bts_trx *trx; - - DEBUGP(DLMI, "e1_reconfig_bts(%u)\n", bts->nr); - - line = e1inp_line_find(e1_link->e1_nr); - if (!line) { - LOGP(DLINP, LOGL_ERROR, "BTS %u OML link referring to " - "non-existing E1 line %u\n", bts->nr, e1_link->e1_nr); - return -ENOMEM; - } - - if (!bts->model->e1line_bind_ops) { - LOGP(DLINP, LOGL_ERROR, "no callback to bind E1 line operations\n"); - return -EINVAL; - } - if (!line->ops) - bts->model->e1line_bind_ops(line); - - /* skip signal link initialization, this is done later for these BTS. */ - if (bts->type == GSM_BTS_TYPE_NANOBTS || - bts->type == GSM_BTS_TYPE_OSMOBTS) - return e1inp_line_update(line); - - /* OML link */ - if (!e1_link->e1_ts) { - LOGP(DLINP, LOGL_ERROR, "BTS %u OML link without timeslot?\n", - bts->nr); - return -EINVAL; - } - - sign_ts = &line->ts[e1_link->e1_ts-1]; - e1inp_ts_config_sign(sign_ts, line); - oml_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OML, - bts->c0, bts->oml_tei, SAPI_OML); - if (!oml_link) { - LOGP(DLINP, LOGL_ERROR, "BTS %u OML link creation failed\n", - bts->nr); - return -ENOMEM; - } - if (bts->oml_link) - e1inp_sign_link_destroy(bts->oml_link); - bts->oml_link = oml_link; - - llist_for_each_entry(trx, &bts->trx_list, list) - e1_reconfig_trx(trx); - - /* notify E1 input something has changed */ - return e1inp_line_update(line); -} - -#if 0 -/* do some compiled-in configuration for our BTS/E1 setup */ -int e1_config(struct gsm_bts *bts, int cardnr, int release_l2) -{ - struct e1inp_line *line; - struct e1inp_ts *sign_ts; - struct e1inp_sign_link *oml_link, *rsl_link; - struct gsm_bts_trx *trx = bts->c0; - int base_ts; - - switch (bts->nr) { - case 0: - /* First BTS uses E1 TS 01,02,03,04,05 */ - base_ts = HARDCODED_BTS0_TS - 1; - break; - case 1: - /* Second BTS uses E1 TS 06,07,08,09,10 */ - base_ts = HARDCODED_BTS1_TS - 1; - break; - case 2: - /* Third BTS uses E1 TS 11,12,13,14,15 */ - base_ts = HARDCODED_BTS2_TS - 1; - default: - return -EINVAL; - } - - line = talloc_zero(tall_bsc_ctx, struct e1inp_line); - if (!line) - return -ENOMEM; - - /* create E1 timeslots for signalling and TRAU frames */ - e1inp_ts_config(&line->ts[base_ts+1-1], line, E1INP_TS_TYPE_SIGN); - e1inp_ts_config(&line->ts[base_ts+2-1], line, E1INP_TS_TYPE_TRAU); - e1inp_ts_config(&line->ts[base_ts+3-1], line, E1INP_TS_TYPE_TRAU); - - /* create signalling links for TS1 */ - sign_ts = &line->ts[base_ts+1-1]; - oml_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_OML, - trx, TEI_OML, SAPI_OML); - rsl_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_RSL, - trx, TEI_RSL, SAPI_RSL); - - /* create back-links from bts/trx */ - bts->oml_link = oml_link; - trx->rsl_link = rsl_link; - - /* enable subchannel demuxer on TS2 */ - subch_demux_activate(&line->ts[base_ts+2-1].trau.demux, 1); - subch_demux_activate(&line->ts[base_ts+2-1].trau.demux, 2); - subch_demux_activate(&line->ts[base_ts+2-1].trau.demux, 3); - - /* enable subchannel demuxer on TS3 */ - subch_demux_activate(&line->ts[base_ts+3-1].trau.demux, 0); - subch_demux_activate(&line->ts[base_ts+3-1].trau.demux, 1); - subch_demux_activate(&line->ts[base_ts+3-1].trau.demux, 2); - subch_demux_activate(&line->ts[base_ts+3-1].trau.demux, 3); - - trx = gsm_bts_trx_num(bts, 1); - if (trx) { - /* create E1 timeslots for TRAU frames of TRX1 */ - e1inp_ts_config(&line->ts[base_ts+4-1], line, E1INP_TS_TYPE_TRAU); - e1inp_ts_config(&line->ts[base_ts+5-1], line, E1INP_TS_TYPE_TRAU); - - /* create RSL signalling link for TRX1 */ - sign_ts = &line->ts[base_ts+1-1]; - rsl_link = e1inp_sign_link_create(sign_ts, E1INP_SIGN_RSL, - trx, TEI_RSL+1, SAPI_RSL); - /* create back-links from trx */ - trx->rsl_link = rsl_link; - - /* enable subchannel demuxer on TS2 */ - subch_demux_activate(&line->ts[base_ts+4-1].trau.demux, 0); - subch_demux_activate(&line->ts[base_ts+4-1].trau.demux, 1); - subch_demux_activate(&line->ts[base_ts+4-1].trau.demux, 2); - subch_demux_activate(&line->ts[base_ts+4-1].trau.demux, 3); - - /* enable subchannel demuxer on TS3 */ - subch_demux_activate(&line->ts[base_ts+5-1].trau.demux, 0); - subch_demux_activate(&line->ts[base_ts+5-1].trau.demux, 1); - subch_demux_activate(&line->ts[base_ts+5-1].trau.demux, 2); - subch_demux_activate(&line->ts[base_ts+5-1].trau.demux, 3); - } - - return mi_setup(cardnr, line, release_l2); -} -#endif diff --git a/openbsc/src/libbsc/gsm_04_08_utils.c b/openbsc/src/libbsc/gsm_04_08_utils.c deleted file mode 100644 index 3447d27cb..000000000 --- a/openbsc/src/libbsc/gsm_04_08_utils.c +++ /dev/null @@ -1,687 +0,0 @@ -/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface - * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 - * utility functions - */ - -/* (C) 2008-2009 by Harald Welte <laforge@gnumonks.org> - * (C) 2008, 2009 by Holger Hans Peter Freyther <zecke@selfish.org> - * - * 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 <http://www.gnu.org/licenses/>. - * - */ -#include <stdio.h> -#include <stdlib.h> -#include <errno.h> -#include <netinet/in.h> - -#include <osmocom/core/msgb.h> -#include <osmocom/gsm/gsm48.h> - -#include <openbsc/abis_rsl.h> -#include <openbsc/debug.h> -#include <openbsc/gsm_04_08.h> -#include <openbsc/transaction.h> -#include <openbsc/paging.h> -#include <openbsc/signal.h> -#include <openbsc/bsc_api.h> - -/* should ip.access BTS use direct RTP streams between each other (1), - * or should OpenBSC always act as RTP relay/proxy in between (0) ? */ -int ipacc_rtp_direct = 1; - -static int gsm48_sendmsg(struct msgb *msg) -{ - if (msg->lchan) - msg->dst = msg->lchan->ts->trx->rsl_link; - - msg->l3h = msg->data; - return rsl_data_request(msg, 0); -} - -/* Section 9.1.8 / Table 9.9 */ -struct chreq { - uint8_t val; - uint8_t mask; - enum chreq_type type; -}; - -/* If SYSTEM INFORMATION TYPE 4 NECI bit == 1 */ -static const struct chreq chreq_type_neci1[] = { - { 0xa0, 0xe0, CHREQ_T_EMERG_CALL }, - { 0xc0, 0xe0, CHREQ_T_CALL_REEST_TCH_F }, - { 0x68, 0xfc, CHREQ_T_CALL_REEST_TCH_H }, - { 0x6c, 0xfc, CHREQ_T_CALL_REEST_TCH_H_DBL }, - { 0xe0, 0xe0, CHREQ_T_TCH_F }, - { 0x40, 0xf0, CHREQ_T_VOICE_CALL_TCH_H }, - { 0x50, 0xf0, CHREQ_T_DATA_CALL_TCH_H }, - { 0x00, 0xf0, CHREQ_T_LOCATION_UPD }, - { 0x10, 0xf0, CHREQ_T_SDCCH }, - { 0x80, 0xe0, CHREQ_T_PAG_R_ANY_NECI1 }, - { 0x20, 0xf0, CHREQ_T_PAG_R_TCH_F }, - { 0x30, 0xf0, CHREQ_T_PAG_R_TCH_FH }, - { 0x67, 0xff, CHREQ_T_LMU }, - { 0x60, 0xf9, CHREQ_T_RESERVED_SDCCH }, - { 0x61, 0xfb, CHREQ_T_RESERVED_SDCCH }, - { 0x63, 0xff, CHREQ_T_RESERVED_SDCCH }, - { 0x70, 0xf8, CHREQ_T_PDCH_TWO_PHASE }, - { 0x78, 0xfc, CHREQ_T_PDCH_ONE_PHASE }, - { 0x78, 0xfa, CHREQ_T_PDCH_ONE_PHASE }, - { 0x78, 0xf9, CHREQ_T_PDCH_ONE_PHASE }, - { 0x7f, 0xff, CHREQ_T_RESERVED_IGNORE }, -}; - -/* If SYSTEM INFORMATION TYPE 4 NECI bit == 0 */ -static const struct chreq chreq_type_neci0[] = { - { 0xa0, 0xe0, CHREQ_T_EMERG_CALL }, - { 0xc0, 0xe0, CHREQ_T_CALL_REEST_TCH_H }, - { 0xe0, 0xe0, CHREQ_T_TCH_F }, - { 0x50, 0xf0, CHREQ_T_DATA_CALL_TCH_H }, - { 0x00, 0xe0, CHREQ_T_LOCATION_UPD }, - { 0x80, 0xe0, CHREQ_T_PAG_R_ANY_NECI0 }, - { 0x20, 0xf0, CHREQ_T_PAG_R_TCH_F }, - { 0x30, 0xf0, CHREQ_T_PAG_R_TCH_FH }, - { 0x67, 0xff, CHREQ_T_LMU }, - { 0x60, 0xf9, CHREQ_T_RESERVED_SDCCH }, - { 0x61, 0xfb, CHREQ_T_RESERVED_SDCCH }, - { 0x63, 0xff, CHREQ_T_RESERVED_SDCCH }, - { 0x70, 0xf8, CHREQ_T_PDCH_TWO_PHASE }, - { 0x78, 0xfc, CHREQ_T_PDCH_ONE_PHASE }, - { 0x78, 0xfa, CHREQ_T_PDCH_ONE_PHASE }, - { 0x78, 0xf9, CHREQ_T_PDCH_ONE_PHASE }, - { 0x7f, 0xff, CHREQ_T_RESERVED_IGNORE }, -}; - -static const enum gsm_chan_t ctype_by_chreq[] = { - [CHREQ_T_EMERG_CALL] = GSM_LCHAN_TCH_F, - [CHREQ_T_CALL_REEST_TCH_F] = GSM_LCHAN_TCH_F, - [CHREQ_T_CALL_REEST_TCH_H] = GSM_LCHAN_TCH_H, - [CHREQ_T_CALL_REEST_TCH_H_DBL] = GSM_LCHAN_TCH_H, - [CHREQ_T_SDCCH] = GSM_LCHAN_SDCCH, - [CHREQ_T_TCH_F] = GSM_LCHAN_TCH_F, - [CHREQ_T_VOICE_CALL_TCH_H] = GSM_LCHAN_TCH_H, - [CHREQ_T_DATA_CALL_TCH_H] = GSM_LCHAN_TCH_H, - [CHREQ_T_LOCATION_UPD] = GSM_LCHAN_SDCCH, - [CHREQ_T_PAG_R_ANY_NECI1] = GSM_LCHAN_SDCCH, - [CHREQ_T_PAG_R_ANY_NECI0] = GSM_LCHAN_SDCCH, - [CHREQ_T_PAG_R_TCH_F] = GSM_LCHAN_TCH_F, - [CHREQ_T_PAG_R_TCH_FH] = GSM_LCHAN_TCH_F, - [CHREQ_T_LMU] = GSM_LCHAN_SDCCH, - [CHREQ_T_RESERVED_SDCCH] = GSM_LCHAN_SDCCH, - [CHREQ_T_PDCH_ONE_PHASE] = GSM_LCHAN_PDTCH, - [CHREQ_T_PDCH_TWO_PHASE] = GSM_LCHAN_PDTCH, - [CHREQ_T_RESERVED_IGNORE] = GSM_LCHAN_UNKNOWN, -}; - -static const enum gsm_chreq_reason_t reason_by_chreq[] = { - [CHREQ_T_EMERG_CALL] = GSM_CHREQ_REASON_EMERG, - [CHREQ_T_CALL_REEST_TCH_F] = GSM_CHREQ_REASON_CALL, - [CHREQ_T_CALL_REEST_TCH_H] = GSM_CHREQ_REASON_CALL, - [CHREQ_T_CALL_REEST_TCH_H_DBL] = GSM_CHREQ_REASON_CALL, - [CHREQ_T_SDCCH] = GSM_CHREQ_REASON_OTHER, - [CHREQ_T_TCH_F] = GSM_CHREQ_REASON_OTHER, - [CHREQ_T_VOICE_CALL_TCH_H] = GSM_CHREQ_REASON_CALL, - [CHREQ_T_DATA_CALL_TCH_H] = GSM_CHREQ_REASON_OTHER, - [CHREQ_T_LOCATION_UPD] = GSM_CHREQ_REASON_LOCATION_UPD, - [CHREQ_T_PAG_R_ANY_NECI1] = GSM_CHREQ_REASON_PAG, - [CHREQ_T_PAG_R_ANY_NECI0] = GSM_CHREQ_REASON_PAG, - [CHREQ_T_PAG_R_TCH_F] = GSM_CHREQ_REASON_PAG, - [CHREQ_T_PAG_R_TCH_FH] = GSM_CHREQ_REASON_PAG, - [CHREQ_T_LMU] = GSM_CHREQ_REASON_OTHER, - [CHREQ_T_PDCH_ONE_PHASE] = GSM_CHREQ_REASON_PDCH, - [CHREQ_T_PDCH_TWO_PHASE] = GSM_CHREQ_REASON_PDCH, - [CHREQ_T_RESERVED_SDCCH] = GSM_CHREQ_REASON_OTHER, - [CHREQ_T_RESERVED_IGNORE] = GSM_CHREQ_REASON_OTHER, -}; - -/* verify that the two tables match */ -osmo_static_assert(sizeof(ctype_by_chreq) == - sizeof(((struct gsm_network *) NULL)->ctype_by_chreq), assert_size); - -/* - * Update channel types for request based on policy. E.g. in the - * case of a TCH/H network/bsc use TCH/H for the emergency calls, - * for early assignment assign a SDCCH and some other options. - */ -void gsm_net_update_ctype(struct gsm_network *network) -{ - /* copy over the data */ - memcpy(network->ctype_by_chreq, ctype_by_chreq, sizeof(ctype_by_chreq)); - - /* - * Use TCH/H for emergency calls when this cell allows TCH/H. Maybe it - * is better to iterate over the BTS/TRX and check if no TCH/F is available - * and then set it to TCH/H. - */ - if (network->neci) - network->ctype_by_chreq[CHREQ_T_EMERG_CALL] = GSM_LCHAN_TCH_H; - - if (network->pag_any_tch) { - if (network->neci) { - network->ctype_by_chreq[CHREQ_T_PAG_R_ANY_NECI0] = GSM_LCHAN_TCH_H; - network->ctype_by_chreq[CHREQ_T_PAG_R_ANY_NECI1] = GSM_LCHAN_TCH_H; - } else { - network->ctype_by_chreq[CHREQ_T_PAG_R_ANY_NECI0] = GSM_LCHAN_TCH_F; - network->ctype_by_chreq[CHREQ_T_PAG_R_ANY_NECI1] = GSM_LCHAN_TCH_F; - } - } -} - -enum gsm_chan_t get_ctype_by_chreq(struct gsm_network *network, uint8_t ra) -{ - int i; - int length; - const struct chreq *chreq; - - if (network->neci) { - chreq = chreq_type_neci1; - length = ARRAY_SIZE(chreq_type_neci1); - } else { - chreq = chreq_type_neci0; - length = ARRAY_SIZE(chreq_type_neci0); - } - - - for (i = 0; i < length; i++) { - const struct chreq *chr = &chreq[i]; - if ((ra & chr->mask) == chr->val) - return network->ctype_by_chreq[chr->type]; - } - LOGP(DRR, LOGL_ERROR, "Unknown CHANNEL REQUEST RQD 0x%02x\n", ra); - return GSM_LCHAN_SDCCH; -} - -int get_reason_by_chreq(uint8_t ra, int neci) -{ - int i; - int length; - const struct chreq *chreq; - - if (neci) { - chreq = chreq_type_neci1; - length = ARRAY_SIZE(chreq_type_neci1); - } else { - chreq = chreq_type_neci0; - length = ARRAY_SIZE(chreq_type_neci0); - } - - for (i = 0; i < length; i++) { - const struct chreq *chr = &chreq[i]; - if ((ra & chr->mask) == chr->val) - return reason_by_chreq[chr->type]; - } - LOGP(DRR, LOGL_ERROR, "Unknown CHANNEL REQUEST REASON 0x%02x\n", ra); - return GSM_CHREQ_REASON_OTHER; -} - -static void mr_config_for_ms(struct gsm_lchan *lchan, struct msgb *msg) -{ - if (lchan->tch_mode == GSM48_CMODE_SPEECH_AMR) - msgb_tlv_put(msg, GSM48_IE_MUL_RATE_CFG, lchan->mr_ms_lv[0], - lchan->mr_ms_lv + 1); -} - -/* 7.1.7 and 9.1.7: RR CHANnel RELease */ -int gsm48_send_rr_release(struct gsm_lchan *lchan) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 RR REL"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - uint8_t *cause; - - msg->lchan = lchan; - gh->proto_discr = GSM48_PDISC_RR; - gh->msg_type = GSM48_MT_RR_CHAN_REL; - - cause = msgb_put(msg, 1); - cause[0] = GSM48_RR_CAUSE_NORMAL; - - DEBUGP(DRR, "Sending Channel Release: Chan: Number: %d Type: %d\n", - lchan->nr, lchan->type); - - /* Send actual release request to MS */ - return gsm48_sendmsg(msg); -} - -int send_siemens_mrpci(struct gsm_lchan *lchan, - uint8_t *classmark2_lv) -{ - struct rsl_mrpci mrpci; - - if (classmark2_lv[0] < 2) - return -EINVAL; - - mrpci.power_class = classmark2_lv[1] & 0x7; - mrpci.vgcs_capable = classmark2_lv[2] & (1 << 1); - mrpci.vbs_capable = classmark2_lv[2] & (1 <<2); - mrpci.gsm_phase = (classmark2_lv[1]) >> 5 & 0x3; - - return rsl_siemens_mrpci(lchan, &mrpci); -} - -int gsm48_extract_mi(uint8_t *classmark2_lv, int length, char *mi_string, uint8_t *mi_type) -{ - /* Check the size for the classmark */ - if (length < 1 + *classmark2_lv) - return -1; - - uint8_t *mi_lv = classmark2_lv + *classmark2_lv + 1; - if (length < 2 + *classmark2_lv + mi_lv[0]) - return -2; - - *mi_type = mi_lv[1] & GSM_MI_TYPE_MASK; - return gsm48_mi_to_string(mi_string, GSM48_MI_SIZE, mi_lv+1, *mi_lv); -} - -int gsm48_paging_extract_mi(struct gsm48_pag_resp *resp, int length, - char *mi_string, uint8_t *mi_type) -{ - static const uint32_t classmark_offset = - offsetof(struct gsm48_pag_resp, classmark2); - uint8_t *classmark2_lv = (uint8_t *) &resp->classmark2; - return gsm48_extract_mi(classmark2_lv, length - classmark_offset, - mi_string, mi_type); -} - -int gsm48_handle_paging_resp(struct gsm_subscriber_connection *conn, - struct msgb *msg, struct bsc_subscr *bsub) -{ - struct gsm_bts *bts = msg->lchan->ts->trx->bts; - struct gsm48_hdr *gh = msgb_l3(msg); - uint8_t *classmark2_lv = gh->data + 1; - - if (is_siemens_bts(bts)) - send_siemens_mrpci(msg->lchan, classmark2_lv); - - if (!conn->bsub) { - conn->bsub = bsub; - } else if (conn->bsub != bsub) { - LOGP(DRR, LOGL_ERROR, - "<- Channel already owned by someone else?\n"); - bsc_subscr_put(bsub); - return -EINVAL; - } else { - DEBUGP(DRR, "<- Channel already owned by us\n"); - bsc_subscr_put(bsub); - bsub = conn->bsub; - } - - rate_ctr_inc(&bts->network->bsc_ctrs->ctr[BSC_CTR_PAGING_COMPLETED]); - - /* Stop paging on the bts we received the paging response */ - paging_request_stop(&bts->network->bts_list, conn->bts, bsub, conn, - msg); - return 0; -} - -/* Chapter 9.1.9: Ciphering Mode Command */ -int gsm48_send_rr_ciph_mode(struct gsm_lchan *lchan, int want_imeisv) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CIPH"); - struct gsm48_hdr *gh; - uint8_t ciph_mod_set; - - msg->lchan = lchan; - - DEBUGP(DRR, "TX CIPHERING MODE CMD\n"); - - if (lchan->encr.alg_id <= RSL_ENC_ALG_A5(0)) - ciph_mod_set = 0; - else - ciph_mod_set = (lchan->encr.alg_id-2)<<1 | 1; - - gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh) + 1); - gh->proto_discr = GSM48_PDISC_RR; - gh->msg_type = GSM48_MT_RR_CIPH_M_CMD; - gh->data[0] = (want_imeisv & 0x1) << 4 | (ciph_mod_set & 0xf); - - return rsl_encryption_cmd(msg); -} - -static void gsm48_cell_desc(struct gsm48_cell_desc *cd, - const struct gsm_bts *bts) -{ - cd->ncc = (bts->bsic >> 3 & 0x7); - cd->bcc = (bts->bsic & 0x7); - cd->arfcn_hi = bts->c0->arfcn >> 8; - cd->arfcn_lo = bts->c0->arfcn & 0xff; -} - -void gsm48_lchan2chan_desc(struct gsm48_chan_desc *cd, - const struct gsm_lchan *lchan) -{ - uint16_t arfcn = lchan->ts->trx->arfcn & 0x3ff; - - cd->chan_nr = gsm_lchan2chan_nr(lchan); - if (!lchan->ts->hopping.enabled) { - cd->h0.tsc = gsm_ts_tsc(lchan->ts); - cd->h0.h = 0; - cd->h0.arfcn_high = arfcn >> 8; - cd->h0.arfcn_low = arfcn & 0xff; - } else { - cd->h1.tsc = gsm_ts_tsc(lchan->ts); - cd->h1.h = 1; - cd->h1.maio_high = lchan->ts->hopping.maio >> 2; - cd->h1.maio_low = lchan->ts->hopping.maio & 0x03; - cd->h1.hsn = lchan->ts->hopping.hsn; - } -} - -/*! \brief Encode a TS 04.08 multirate config LV according to 10.5.2.21aa - * \param[out] lv caller-allocated buffer of 7 bytes. First octet is IS length - * \param[in] mr multi-rate configuration to encode - * \param[in] modes array describing the AMR modes - * \returns 0 on success */ -int gsm48_multirate_config(uint8_t *lv, const struct amr_multirate_conf *mr, const struct amr_mode *modes) -{ - int num = 0, i; - - for (i = 0; i < 8; i++) { - if (((mr->gsm48_ie[1] >> i) & 1)) - num++; - } - if (num > 4) { - LOGP(DRR, LOGL_ERROR, "BUG: Using multirate codec with too " - "many modes in config.\n"); - num = 4; - } - if (num < 1) { - LOGP(DRR, LOGL_ERROR, "BUG: Using multirate codec with no " - "mode in config.\n"); - num = 1; - } - - lv[0] = (num == 1) ? 2 : (num + 2); - memcpy(lv + 1, mr->gsm48_ie, 2); - if (num == 1) - return 0; - - lv[3] = modes[0].threshold & 0x3f; - lv[4] = modes[0].hysteresis << 4; - if (num == 2) - return 0; - lv[4] |= (modes[1].threshold & 0x3f) >> 2; - lv[5] = modes[1].threshold << 6; - lv[5] |= (modes[1].hysteresis & 0x0f) << 2; - if (num == 3) - return 0; - lv[5] |= (modes[2].threshold & 0x3f) >> 4; - lv[6] = modes[2].threshold << 4; - lv[6] |= modes[2].hysteresis & 0x0f; - - return 0; -} - -#define GSM48_HOCMD_CCHDESC_LEN 16 - -/* Chapter 9.1.15: Handover Command */ -int gsm48_send_ho_cmd(struct gsm_lchan *old_lchan, struct gsm_lchan *new_lchan, - uint8_t power_command, uint8_t ho_ref) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 HO CMD"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - struct gsm48_ho_cmd *ho = - (struct gsm48_ho_cmd *) msgb_put(msg, sizeof(*ho)); - - msg->lchan = old_lchan; - gh->proto_discr = GSM48_PDISC_RR; - gh->msg_type = GSM48_MT_RR_HANDO_CMD; - - /* mandatory bits */ - gsm48_cell_desc(&ho->cell_desc, new_lchan->ts->trx->bts); - gsm48_lchan2chan_desc(&ho->chan_desc, new_lchan); - ho->ho_ref = ho_ref; - ho->power_command = power_command; - - if (new_lchan->ts->hopping.enabled) { - struct gsm_bts *bts = new_lchan->ts->trx->bts; - struct gsm48_system_information_type_1 *si1; - uint8_t *cur; - - si1 = GSM_BTS_SI(bts, SYSINFO_TYPE_1); - /* Copy the Cell Chan Desc (ARFCNS in this cell) */ - msgb_put_u8(msg, GSM48_IE_CELL_CH_DESC); - cur = msgb_put(msg, GSM48_HOCMD_CCHDESC_LEN); - memcpy(cur, si1->cell_channel_description, - GSM48_HOCMD_CCHDESC_LEN); - /* Copy the Mobile Allocation */ - msgb_tlv_put(msg, GSM48_IE_MA_BEFORE, - new_lchan->ts->hopping.ma_len, - new_lchan->ts->hopping.ma_data); - } - /* FIXME: optional bits for type of synchronization? */ - - return gsm48_sendmsg(msg); -} - -/* Chapter 9.1.2: Assignment Command */ -int gsm48_send_rr_ass_cmd(struct gsm_lchan *dest_lchan, struct gsm_lchan *lchan, uint8_t power_command) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 ASS CMD"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - struct gsm48_ass_cmd *ass = - (struct gsm48_ass_cmd *) msgb_put(msg, sizeof(*ass)); - - DEBUGP(DRR, "-> ASSIGNMENT COMMAND tch_mode=0x%02x\n", lchan->tch_mode); - - msg->lchan = dest_lchan; - gh->proto_discr = GSM48_PDISC_RR; - gh->msg_type = GSM48_MT_RR_ASS_CMD; - - /* - * fill the channel information element, this code - * should probably be shared with rsl_rx_chan_rqd(), - * gsm48_lchan_modify(). But beware that 10.5.2.5 - * 10.5.2.5.a have slightly different semantic for - * the chan_desc. But as long as multi-slot configurations - * are not used we seem to be fine. - */ - gsm48_lchan2chan_desc(&ass->chan_desc, lchan); - ass->power_command = power_command; - - /* optional: cell channel description */ - - msgb_tv_put(msg, GSM48_IE_CHANMODE_1, lchan->tch_mode); - - /* mobile allocation in case of hopping */ - if (lchan->ts->hopping.enabled) { - msgb_tlv_put(msg, GSM48_IE_MA_BEFORE, lchan->ts->hopping.ma_len, - lchan->ts->hopping.ma_data); - } - - /* in case of multi rate we need to attach a config */ - mr_config_for_ms(lchan, msg); - - return gsm48_sendmsg(msg); -} - -/* 9.1.5 Channel mode modify: Modify the mode on the MS side */ -int gsm48_lchan_modify(struct gsm_lchan *lchan, uint8_t mode) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 CHN MOD"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - struct gsm48_chan_mode_modify *cmm = - (struct gsm48_chan_mode_modify *) msgb_put(msg, sizeof(*cmm)); - - DEBUGP(DRR, "-> CHANNEL MODE MODIFY mode=0x%02x\n", mode); - - lchan->tch_mode = mode; - msg->lchan = lchan; - gh->proto_discr = GSM48_PDISC_RR; - gh->msg_type = GSM48_MT_RR_CHAN_MODE_MODIF; - - /* fill the channel information element, this code - * should probably be shared with rsl_rx_chan_rqd() */ - gsm48_lchan2chan_desc(&cmm->chan_desc, lchan); - cmm->mode = mode; - - /* in case of multi rate we need to attach a config */ - mr_config_for_ms(lchan, msg); - - return gsm48_sendmsg(msg); -} - -int gsm48_rx_rr_modif_ack(struct msgb *msg) -{ - int rc; - struct gsm48_hdr *gh = msgb_l3(msg); - struct gsm48_chan_mode_modify *mod = - (struct gsm48_chan_mode_modify *) gh->data; - - DEBUGP(DRR, "CHANNEL MODE MODIFY ACK\n"); - - if (mod->mode != msg->lchan->tch_mode) { - LOGP(DRR, LOGL_ERROR, "CHANNEL MODE change failed. Wanted: %d Got: %d\n", - msg->lchan->tch_mode, mod->mode); - return -1; - } - - /* update the channel type */ - switch (mod->mode) { - case GSM48_CMODE_SIGN: - msg->lchan->rsl_cmode = RSL_CMOD_SPD_SIGN; - break; - case GSM48_CMODE_SPEECH_V1: - case GSM48_CMODE_SPEECH_EFR: - case GSM48_CMODE_SPEECH_AMR: - msg->lchan->rsl_cmode = RSL_CMOD_SPD_SPEECH; - break; - case GSM48_CMODE_DATA_14k5: - case GSM48_CMODE_DATA_12k0: - case GSM48_CMODE_DATA_6k0: - case GSM48_CMODE_DATA_3k6: - msg->lchan->rsl_cmode = RSL_CMOD_SPD_DATA; - break; - } - - /* We've successfully modified the MS side of the channel, - * now go on to modify the BTS side of the channel */ - rc = rsl_chan_mode_modify_req(msg->lchan); - - /* FIXME: we not only need to do this after mode modify, but - * also after channel activation */ - if (is_ipaccess_bts(msg->lchan->ts->trx->bts) && mod->mode != GSM48_CMODE_SIGN) - rsl_ipacc_crcx(msg->lchan); - return rc; -} - -int gsm48_parse_meas_rep(struct gsm_meas_rep *rep, struct msgb *msg) -{ - struct gsm48_hdr *gh = msgb_l3(msg); - uint8_t *data = gh->data; - struct gsm_bts *bts = msg->lchan->ts->trx->bts; - struct bitvec *nbv = &bts->si_common.neigh_list; - struct gsm_meas_rep_cell *mrc; - - if (gh->msg_type != GSM48_MT_RR_MEAS_REP) - return -EINVAL; - - if (data[0] & 0x80) - rep->flags |= MEAS_REP_F_BA1; - if (data[0] & 0x40) - rep->flags |= MEAS_REP_F_UL_DTX; - if ((data[1] & 0x40) == 0x00) - rep->flags |= MEAS_REP_F_DL_VALID; - - rep->dl.full.rx_lev = data[0] & 0x3f; - rep->dl.sub.rx_lev = data[1] & 0x3f; - rep->dl.full.rx_qual = (data[2] >> 4) & 0x7; - rep->dl.sub.rx_qual = (data[2] >> 1) & 0x7; - - rep->num_cell = ((data[3] >> 6) & 0x3) | ((data[2] & 0x01) << 2); - if (rep->num_cell < 1 || rep->num_cell > 6) - return 0; - - /* an encoding nightmare in perfection */ - mrc = &rep->cell[0]; - mrc->rxlev = data[3] & 0x3f; - mrc->neigh_idx = data[4] >> 3; - mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); - mrc->bsic = ((data[4] & 0x07) << 3) | (data[5] >> 5); - if (rep->num_cell < 2) - return 0; - - mrc = &rep->cell[1]; - mrc->rxlev = ((data[5] & 0x1f) << 1) | (data[6] >> 7); - mrc->neigh_idx = (data[6] >> 2) & 0x1f; - mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); - mrc->bsic = ((data[6] & 0x03) << 4) | (data[7] >> 4); - if (rep->num_cell < 3) - return 0; - - mrc = &rep->cell[2]; - mrc->rxlev = ((data[7] & 0x0f) << 2) | (data[8] >> 6); - mrc->neigh_idx = (data[8] >> 1) & 0x1f; - mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); - mrc->bsic = ((data[8] & 0x01) << 5) | (data[9] >> 3); - if (rep->num_cell < 4) - return 0; - - mrc = &rep->cell[3]; - mrc->rxlev = ((data[9] & 0x07) << 3) | (data[10] >> 5); - mrc->neigh_idx = data[10] & 0x1f; - mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); - mrc->bsic = data[11] >> 2; - if (rep->num_cell < 5) - return 0; - - mrc = &rep->cell[4]; - mrc->rxlev = ((data[11] & 0x03) << 4) | (data[12] >> 4); - mrc->neigh_idx = ((data[12] & 0xf) << 1) | (data[13] >> 7); - mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); - mrc->bsic = (data[13] >> 1) & 0x3f; - if (rep->num_cell < 6) - return 0; - - mrc = &rep->cell[5]; - mrc->rxlev = ((data[13] & 0x01) << 5) | (data[14] >> 3); - mrc->neigh_idx = ((data[14] & 0x07) << 2) | (data[15] >> 6); - mrc->arfcn = bitvec_get_nth_set_bit(nbv, mrc->neigh_idx + 1); - mrc->bsic = data[15] & 0x3f; - - return 0; -} - -/* 9.2.5 CM service accept */ -int gsm48_tx_mm_serv_ack(struct gsm_subscriber_connection *conn) -{ - struct msgb *msg = gsm48_msgb_alloc_name("GSM 04.08 SERV ACK"); - struct gsm48_hdr *gh = (struct gsm48_hdr *) msgb_put(msg, sizeof(*gh)); - - msg->lchan = conn->lchan; - - gh->proto_discr = GSM48_PDISC_MM; - gh->msg_type = GSM48_MT_MM_CM_SERV_ACC; - - DEBUGP(DMM, "-> CM SERVICE ACK\n"); - - return gsm0808_submit_dtap(conn, msg, 0, 0); -} - -/* 9.2.6 CM service reject */ -int gsm48_tx_mm_serv_rej(struct gsm_subscriber_connection *conn, - enum gsm48_reject_value value) -{ - struct msgb *msg; - - msg = gsm48_create_mm_serv_rej(value); - if (!msg) { - LOGP(DMM, LOGL_ERROR, "Failed to allocate CM Service Reject.\n"); - return -1; - } - - DEBUGP(DMM, "-> CM SERVICE Reject cause: %d\n", value); - - return gsm0808_submit_dtap(conn, msg, 0, 0); -} diff --git a/openbsc/src/libbsc/gsm_04_80_utils.c b/openbsc/src/libbsc/gsm_04_80_utils.c deleted file mode 100644 index e0db81edf..000000000 --- a/openbsc/src/libbsc/gsm_04_80_utils.c +++ /dev/null @@ -1,40 +0,0 @@ -/* OpenBSC utility functions for 3GPP TS 04.80 */ - -/* (C) 2016 by sysmocom s.m.f.c. GmbH <info@sysmocom.de> - * - * 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 <http://www.gnu.org/licenses/>. - * - */ - -#include <osmocom/gsm/gsm0480.h> -#include <openbsc/bsc_api.h> - -int bsc_send_ussd_notify(struct gsm_subscriber_connection *conn, int level, - const char *text) -{ - struct msgb *msg = gsm0480_create_ussd_notify(level, text); - if (!msg) - return -1; - return gsm0808_submit_dtap(conn, msg, 0, 0); -} - -int bsc_send_ussd_release_complete(struct gsm_subscriber_connection *conn) -{ - struct msgb *msg = gsm0480_create_ussd_release_complete(); - if (!msg) - return -1; - return gsm0808_submit_dtap(conn, msg, 0, 0); -} diff --git a/openbsc/src/libbsc/handover_decision.c b/openbsc/src/libbsc/handover_decision.c deleted file mode 100644 index 0f07bcac6..000000000 --- a/openbsc/src/libbsc/handover_decision.c +++ /dev/null @@ -1,304 +0,0 @@ -/* Handover Decision making for Inter-BTS (Intra-BSC) Handover. This - * only implements the handover algorithm/decision, but not execution - * of it */ - -/* (C) 2009 by Harald Welte <laforge@gnumonks.org> - * - * 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 <http://www.gnu.org/licenses/>. - * - */ - -#include <stdlib.h> -#include <errno.h> - -#include <osmocom/core/msgb.h> -#include <openbsc/debug.h> -#include <openbsc/gsm_data.h> -#include <openbsc/meas_rep.h> -#include <openbsc/signal.h> -#include <osmocom/core/talloc.h> -#include <openbsc/handover.h> -#include <osmocom/gsm/gsm_utils.h> - -/* issue handover to a cell identified by ARFCN and BSIC */ -static int handover_to_arfcn_bsic(struct gsm_lchan *lchan, - uint16_t arfcn, uint8_t bsic) -{ - struct gsm_bts *new_bts; - - /* resolve the gsm_bts structure for the best neighbor */ - new_bts = gsm_bts_neighbor(lchan->ts->trx->bts, arfcn, bsic); - if (!new_bts) { - LOGP(DHO, LOGL_NOTICE, "unable to determine neighbor BTS " - "for ARFCN %u BSIC %u ?!?\n", arfcn, bsic); - return -EINVAL; - } - - /* and actually try to handover to that cell */ - return bsc_handover_start(lchan, new_bts); -} - -/* did we get a RXLEV for a given cell in the given report? */ -static int rxlev_for_cell_in_rep(struct gsm_meas_rep *mr, - uint16_t arfcn, uint8_t bsic) -{ - int i; - - for (i = 0; i < mr->num_cell; i++) { - struct gsm_meas_rep_cell *mrc = &mr->cell[i]; - - /* search for matching report */ - if (!(mrc->arfcn == arfcn && mrc->bsic == bsic)) - continue; - - mrc->flags |= MRC_F_PROCESSED; - return mrc->rxlev; - } - return -ENODEV; -} - -/* obtain averaged rxlev for given neighbor */ -static int neigh_meas_avg(struct neigh_meas_proc *nmp, int window) -{ - unsigned int i, idx; - int avg = 0; - - idx = calc_initial_idx(ARRAY_SIZE(nmp->rxlev), - nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev), - window); - - for (i = 0; i < window; i++) { - int j = (idx+i) % ARRAY_SIZE(nmp->rxlev); - - avg += nmp->rxlev[j]; - } - - return avg / window; -} - -/* find empty or evict bad neighbor */ -static struct neigh_meas_proc *find_evict_neigh(struct gsm_lchan *lchan) -{ - int j, worst = 999999; - struct neigh_meas_proc *nmp_worst = NULL; - - /* first try to find an empty/unused slot */ - for (j = 0; j < ARRAY_SIZE(lchan->neigh_meas); j++) { - struct neigh_meas_proc *nmp = &lchan->neigh_meas[j]; - if (!nmp->arfcn) - return nmp; - } - - /* no empty slot found. evict worst neighbor from list */ - for (j = 0; j < ARRAY_SIZE(lchan->neigh_meas); j++) { - struct neigh_meas_proc *nmp = &lchan->neigh_meas[j]; - int avg = neigh_meas_avg(nmp, MAX_WIN_NEIGH_AVG); - if (!nmp_worst || avg < worst) { - worst = avg; - nmp_worst = nmp; - } - } - - return nmp_worst; -} - -/* process neighbor cell measurement reports */ -static void process_meas_neigh(struct gsm_meas_rep *mr) -{ - int i, j, idx; - - /* for each reported cell, try to update global state */ - for (j = 0; j < ARRAY_SIZE(mr->lchan->neigh_meas); j++) { - struct neigh_meas_proc *nmp = &mr->lchan->neigh_meas[j]; - unsigned int idx; - int rxlev; - - /* skip unused entries */ - if (!nmp->arfcn) - continue; - - rxlev = rxlev_for_cell_in_rep(mr, nmp->arfcn, nmp->bsic); - idx = nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev); - if (rxlev >= 0) { - nmp->rxlev[idx] = rxlev; - nmp->last_seen_nr = mr->nr; - } else - nmp->rxlev[idx] = 0; - nmp->rxlev_cnt++; - } - - /* iterate over list of reported cells, check if we did not - * process all of them */ - for (i = 0; i < mr->num_cell; i++) { - struct gsm_meas_rep_cell *mrc = &mr->cell[i]; - struct neigh_meas_proc *nmp; - - if (mrc->flags & MRC_F_PROCESSED) - continue; - - nmp = find_evict_neigh(mr->lchan); - - nmp->arfcn = mrc->arfcn; - nmp->bsic = mrc->bsic; - - idx = nmp->rxlev_cnt % ARRAY_SIZE(nmp->rxlev); - nmp->rxlev[idx] = mrc->rxlev; - nmp->rxlev_cnt++; - nmp->last_seen_nr = mr->nr; - - mrc->flags |= MRC_F_PROCESSED; - } -} - -/* attempt to do a handover */ -static int attempt_handover(struct gsm_meas_rep *mr) -{ - struct gsm_network *net = mr->lchan->ts->trx->bts->network; - struct neigh_meas_proc *best_cell = NULL; - unsigned int best_better_db = 0; - int i, rc; - - /* find the best cell in this report that is at least RXLEV_HYST - * better than the current serving cell */ - - for (i = 0; i < ARRAY_SIZE(mr->lchan->neigh_meas); i++) { - struct neigh_meas_proc *nmp = &mr->lchan->neigh_meas[i]; - int avg, better; - - /* skip empty slots */ - if (nmp->arfcn == 0) - continue; - - /* caculate average rxlev for this cell over the window */ - avg = neigh_meas_avg(nmp, net->handover.win_rxlev_avg_neigh); - - /* check if hysteresis is fulfilled */ - if (avg < mr->dl.full.rx_lev + net->handover.pwr_hysteresis) - continue; - - better = avg - mr->dl.full.rx_lev; - if (better > best_better_db) { - best_cell = nmp; - best_better_db = better; - } - } - - if (!best_cell) - return 0; - - LOGP(DHO, LOGL_INFO, "%s: Cell on ARFCN %u is better: ", - gsm_ts_name(mr->lchan->ts), best_cell->arfcn); - if (!net->handover.active) { - LOGPC(DHO, LOGL_INFO, "Skipping, Handover disabled\n"); - return 0; - } - - rc = handover_to_arfcn_bsic(mr->lchan, best_cell->arfcn, best_cell->bsic); - switch (rc) { - case 0: - LOGPC(DHO, LOGL_INFO, "Starting handover\n"); - break; - case -ENOSPC: - LOGPC(DHO, LOGL_INFO, "No channel available\n"); - break; - case -EBUSY: - LOGPC(DHO, LOGL_INFO, "Handover already active\n"); - break; - default: - LOGPC(DHO, LOGL_ERROR, "Unknown error\n"); - } - return rc; -} - -/* process an already parsed measurement report and decide if we want to - * attempt a handover */ -static int process_meas_rep(struct gsm_meas_rep *mr) -{ - struct gsm_network *net = mr->lchan->ts->trx->bts->network; - enum meas_rep_field dlev, dqual; - int av_rxlev; - - /* we currently only do handover for TCH channels */ - switch (mr->lchan->type) { - case GSM_LCHAN_TCH_F: - case GSM_LCHAN_TCH_H: - break; - default: - return 0; - } - - if (mr->flags & MEAS_REP_F_DL_DTX) { - dlev = MEAS_REP_DL_RXLEV_SUB; - dqual = MEAS_REP_DL_RXQUAL_SUB; - } else { - dlev = MEAS_REP_DL_RXLEV_FULL; - dqual = MEAS_REP_DL_RXQUAL_FULL; - } - - /* parse actual neighbor cell info */ - if (mr->num_cell > 0 && mr->num_cell < 7) - process_meas_neigh(mr); - - av_rxlev = get_meas_rep_avg(mr->lchan, dlev, - net->handover.win_rxlev_avg); - - /* Interference HO */ - if (rxlev2dbm(av_rxlev) > -85 && - meas_rep_n_out_of_m_be(mr->lchan, dqual, 3, 4, 5)) - return attempt_handover(mr); - - /* Bad Quality */ - if (meas_rep_n_out_of_m_be(mr->lchan, dqual, 3, 4, 5)) - return attempt_handover(mr); - - /* Low Level */ - if (rxlev2dbm(av_rxlev) <= -110) - return attempt_handover(mr); - - /* Distance */ - if (mr->ms_l1.ta > net->handover.max_distance) - return attempt_handover(mr); - - /* Power Budget AKA Better Cell */ - if ((mr->nr % net->handover.pwr_interval) == 0) - return attempt_handover(mr); - - return 0; - -} - -static int ho_dec_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct lchan_signal_data *lchan_data; - - if (subsys != SS_LCHAN) - return 0; - - lchan_data = signal_data; - switch (signal) { - case S_LCHAN_MEAS_REP: - process_meas_rep(lchan_data->mr); - break; - } - - return 0; -} - -void on_dso_load_ho_dec(void) -{ - osmo_signal_register_handler(SS_LCHAN, ho_dec_sig_cb, NULL); -} diff --git a/openbsc/src/libbsc/handover_logic.c b/openbsc/src/libbsc/handover_logic.c deleted file mode 100644 index 4dd913b15..000000000 --- a/openbsc/src/libbsc/handover_logic.c +++ /dev/null @@ -1,378 +0,0 @@ -/* Handover Logic for Inter-BTS (Intra-BSC) Handover. This does not - * actually implement the handover algorithm/decision, but executes a - * handover decision */ - -/* (C) 2009 by Harald Welte <laforge@gnumonks.org> - * - * 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 <http://www.gnu.org/licenses/>. - * - */ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <time.h> -#include <netinet/in.h> - -#include <osmocom/core/msgb.h> -#include <openbsc/debug.h> -#include <openbsc/gsm_data.h> -#include <osmocom/gsm/gsm_utils.h> -#include <openbsc/gsm_subscriber.h> -#include <openbsc/gsm_04_08.h> -#include <openbsc/abis_rsl.h> -#include <openbsc/chan_alloc.h> -#include <openbsc/signal.h> -#include <osmocom/core/talloc.h> -#include <openbsc/transaction.h> -#include <openbsc/trau_mux.h> - -struct bsc_handover { - struct llist_head list; - - struct gsm_lchan *old_lchan; - struct gsm_lchan *new_lchan; - - struct osmo_timer_list T3103; - - uint8_t ho_ref; -}; - -static LLIST_HEAD(bsc_handovers); - -static void handover_free(struct bsc_handover *ho) -{ - osmo_timer_del(&ho->T3103); - llist_del(&ho->list); - talloc_free(ho); -} - -static struct bsc_handover *bsc_ho_by_new_lchan(struct gsm_lchan *new_lchan) -{ - struct bsc_handover *ho; - - llist_for_each_entry(ho, &bsc_handovers, list) { - if (ho->new_lchan == new_lchan) - return ho; - } - - return NULL; -} - -static struct bsc_handover *bsc_ho_by_old_lchan(struct gsm_lchan *old_lchan) -{ - struct bsc_handover *ho; - - llist_for_each_entry(ho, &bsc_handovers, list) { - if (ho->old_lchan == old_lchan) - return ho; - } - - return NULL; -} - -/*! \brief Hand over the specified logical channel to the specified new BTS. - * This is the main entry point for the actual handover algorithm, after the - * decision whether to initiate HO to a specific BTS. */ -int bsc_handover_start(struct gsm_lchan *old_lchan, struct gsm_bts *bts) -{ - struct gsm_lchan *new_lchan; - struct bsc_handover *ho; - static uint8_t ho_ref; - int rc; - - /* don't attempt multiple handovers for the same lchan at - * the same time */ - if (bsc_ho_by_old_lchan(old_lchan)) - return -EBUSY; - - DEBUGP(DHO, "(old_lchan on BTS %u, new BTS %u)\n", - old_lchan->ts->trx->bts->nr, bts->nr); - - rate_ctr_inc(&bts->network->bsc_ctrs->ctr[BSC_CTR_HANDOVER_ATTEMPTED]); - - if (!old_lchan->conn) { - LOGP(DHO, LOGL_ERROR, "Old lchan lacks connection data.\n"); - return -ENOSPC; - } - - new_lchan = lchan_alloc(bts, old_lchan->type, 0); - if (!new_lchan) { - LOGP(DHO, LOGL_NOTICE, "No free channel\n"); - rate_ctr_inc(&bts->network->bsc_ctrs->ctr[BSC_CTR_HANDOVER_NO_CHANNEL]); - return -ENOSPC; - } - - ho = talloc_zero(tall_bsc_ctx, struct bsc_handover); - if (!ho) { - LOGP(DHO, LOGL_FATAL, "Out of Memory\n"); - lchan_free(new_lchan); - return -ENOMEM; - } - ho->old_lchan = old_lchan; - ho->new_lchan = new_lchan; - ho->ho_ref = ho_ref++; - - /* copy some parameters from old lchan */ - memcpy(&new_lchan->encr, &old_lchan->encr, sizeof(new_lchan->encr)); - new_lchan->ms_power = old_lchan->ms_power; - new_lchan->bs_power = old_lchan->bs_power; - new_lchan->rsl_cmode = old_lchan->rsl_cmode; - new_lchan->tch_mode = old_lchan->tch_mode; - memcpy(&new_lchan->mr_ms_lv, &old_lchan->mr_ms_lv, ARRAY_SIZE(new_lchan->mr_ms_lv)); - memcpy(&new_lchan->mr_bts_lv, &old_lchan->mr_bts_lv, ARRAY_SIZE(new_lchan->mr_bts_lv)); - - new_lchan->conn = old_lchan->conn; - new_lchan->conn->ho_lchan = new_lchan; - - /* FIXME: do we have a better idea of the timing advance? */ - rc = rsl_chan_activate_lchan(new_lchan, RSL_ACT_INTER_ASYNC, ho->ho_ref); - if (rc < 0) { - LOGP(DHO, LOGL_ERROR, "could not activate channel\n"); - new_lchan->conn->ho_lchan = NULL; - new_lchan->conn = NULL; - talloc_free(ho); - lchan_free(new_lchan); - return rc; - } - - rsl_lchan_set_state(new_lchan, LCHAN_S_ACT_REQ); - llist_add(&ho->list, &bsc_handovers); - /* we continue in the SS_LCHAN handler / ho_chan_activ_ack */ - - return 0; -} - -void bsc_clear_handover(struct gsm_subscriber_connection *conn, int free_lchan) -{ - struct bsc_handover *ho; - - ho = bsc_ho_by_new_lchan(conn->ho_lchan); - - - if (!ho && conn->ho_lchan) - LOGP(DHO, LOGL_ERROR, "BUG: We lost some state.\n"); - - if (!ho) { - LOGP(DHO, LOGL_ERROR, "unable to find HO record\n"); - return; - } - - conn->ho_lchan->conn = NULL; - conn->ho_lchan = NULL; - - if (free_lchan) - lchan_release(ho->new_lchan, 0, RSL_REL_LOCAL_END); - - handover_free(ho); -} - -/* T3103 expired: Handover has failed without HO COMPLETE or HO FAIL */ -static void ho_T3103_cb(void *_ho) -{ - struct bsc_handover *ho = _ho; - struct gsm_network *net = ho->new_lchan->ts->trx->bts->network; - - DEBUGP(DHO, "HO T3103 expired\n"); - rate_ctr_inc(&net->bsc_ctrs->ctr[BSC_CTR_HANDOVER_TIMEOUT]); - - ho->new_lchan->conn->ho_lchan = NULL; - ho->new_lchan->conn = NULL; - lchan_release(ho->new_lchan, 0, RSL_REL_LOCAL_END); - handover_free(ho); -} - -/* RSL has acknowledged activation of the new lchan */ -static int ho_chan_activ_ack(struct gsm_lchan *new_lchan) -{ - struct bsc_handover *ho; - - /* we need to check if this channel activation is related to - * a handover at all (and if, which particular handover) */ - ho = bsc_ho_by_new_lchan(new_lchan); - if (!ho) - return -ENODEV; - - DEBUGP(DHO, "handover activate ack, send HO Command\n"); - - /* we can now send the 04.08 HANDOVER COMMAND to the MS - * using the old lchan */ - - gsm48_send_ho_cmd(ho->old_lchan, new_lchan, 0, ho->ho_ref); - - /* start T3103. We can continue either with T3103 expiration, - * 04.08 HANDOVER COMPLETE or 04.08 HANDOVER FAIL */ - osmo_timer_setup(&ho->T3103, ho_T3103_cb, ho); - osmo_timer_schedule(&ho->T3103, 10, 0); - - /* create a RTP connection */ - if (is_ipaccess_bts(new_lchan->ts->trx->bts)) - rsl_ipacc_crcx(new_lchan); - - return 0; -} - -/* RSL has not acknowledged activation of the new lchan */ -static int ho_chan_activ_nack(struct gsm_lchan *new_lchan) -{ - struct bsc_handover *ho; - - ho = bsc_ho_by_new_lchan(new_lchan); - if (!ho) { - LOGP(DHO, LOGL_INFO, "ACT NACK: unable to find HO record\n"); - return -ENODEV; - } - - new_lchan->conn->ho_lchan = NULL; - new_lchan->conn = NULL; - handover_free(ho); - - /* FIXME: maybe we should try to allocate a new LCHAN here? */ - - return 0; -} - -/* GSM 04.08 HANDOVER COMPLETE has been received on new channel */ -static int ho_gsm48_ho_compl(struct gsm_lchan *new_lchan) -{ - struct gsm_network *net; - struct bsc_handover *ho; - - ho = bsc_ho_by_new_lchan(new_lchan); - if (!ho) { - LOGP(DHO, LOGL_ERROR, "unable to find HO record\n"); - return -ENODEV; - } - - net = new_lchan->ts->trx->bts->network; - LOGP(DHO, LOGL_INFO, "Subscriber %s HO from BTS %u->%u on ARFCN " - "%u->%u\n", subscr_name(ho->old_lchan->conn->subscr), - ho->old_lchan->ts->trx->bts->nr, new_lchan->ts->trx->bts->nr, - ho->old_lchan->ts->trx->arfcn, new_lchan->ts->trx->arfcn); - - rate_ctr_inc(&net->bsc_ctrs->ctr[BSC_CTR_HANDOVER_COMPLETED]); - - osmo_timer_del(&ho->T3103); - - /* switch TRAU muxer for E1 based BTS from one channel to another */ - if (is_e1_bts(new_lchan->conn->bts)) - switch_trau_mux(ho->old_lchan, new_lchan); - - /* Replace the ho lchan with the primary one */ - if (ho->old_lchan != new_lchan->conn->lchan) - LOGP(DHO, LOGL_ERROR, "Primary lchan changed during handover.\n"); - - if (new_lchan != new_lchan->conn->ho_lchan) - LOGP(DHO, LOGL_ERROR, "Handover channel changed during this handover.\n"); - - new_lchan->conn->ho_lchan = NULL; - new_lchan->conn->lchan = new_lchan; - ho->old_lchan->conn = NULL; - - lchan_release(ho->old_lchan, 0, RSL_REL_LOCAL_END); - - handover_free(ho); - return 0; -} - -/* GSM 04.08 HANDOVER FAIL has been received */ -static int ho_gsm48_ho_fail(struct gsm_lchan *old_lchan) -{ - struct gsm_network *net = old_lchan->ts->trx->bts->network; - struct bsc_handover *ho; - struct gsm_lchan *new_lchan; - - ho = bsc_ho_by_old_lchan(old_lchan); - if (!ho) { - LOGP(DHO, LOGL_ERROR, "unable to find HO record\n"); - return -ENODEV; - } - - rate_ctr_inc(&net->bsc_ctrs->ctr[BSC_CTR_HANDOVER_FAILED]); - - new_lchan = ho->new_lchan; - - /* release the channel and forget about it */ - ho->new_lchan->conn->ho_lchan = NULL; - ho->new_lchan->conn = NULL; - handover_free(ho); - - lchan_release(new_lchan, 0, RSL_REL_LOCAL_END); - - - return 0; -} - -/* GSM 08.58 HANDOVER DETECT has been received */ -static int ho_rsl_detect(struct gsm_lchan *new_lchan) -{ - struct bsc_handover *ho; - - ho = bsc_ho_by_new_lchan(new_lchan); - if (!ho) { - LOGP(DHO, LOGL_ERROR, "unable to find HO record\n"); - return -ENODEV; - } - - /* FIXME: do we actually want to do something here ? */ - - return 0; -} - -static int ho_logic_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct lchan_signal_data *lchan_data; - struct gsm_lchan *lchan; - - lchan_data = signal_data; - switch (subsys) { - case SS_LCHAN: - lchan = lchan_data->lchan; - switch (signal) { - case S_LCHAN_ACTIVATE_ACK: - return ho_chan_activ_ack(lchan); - case S_LCHAN_ACTIVATE_NACK: - return ho_chan_activ_nack(lchan); - case S_LCHAN_HANDOVER_DETECT: - return ho_rsl_detect(lchan); - case S_LCHAN_HANDOVER_COMPL: - return ho_gsm48_ho_compl(lchan); - case S_LCHAN_HANDOVER_FAIL: - return ho_gsm48_ho_fail(lchan); - } - break; - default: - break; - } - - return 0; -} - -struct gsm_lchan *bsc_handover_pending(struct gsm_lchan *new_lchan) -{ - struct bsc_handover *ho; - ho = bsc_ho_by_new_lchan(new_lchan); - if (!ho) - return NULL; - return ho->old_lchan; -} - -static __attribute__((constructor)) void on_dso_load_ho_logic(void) -{ - osmo_signal_register_handler(SS_LCHAN, ho_logic_sig_cb, NULL); -} diff --git a/openbsc/src/libbsc/meas_proc.c b/openbsc/src/libbsc/meas_proc.c deleted file mode 100644 index 5b97e74ee..000000000 --- a/openbsc/src/libbsc/meas_proc.c +++ /dev/null @@ -1,84 +0,0 @@ -/* Measurement Processing */ - -/* (C) 2009 by Harald Welte <laforge@gnumonks.org> - * - * 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 <http://www.gnu.org/licenses/>. - * - */ - -#include <stdlib.h> -#include <errno.h> - -#include <osmocom/core/talloc.h> -#include <osmocom/core/msgb.h> -#include <openbsc/debug.h> -#include <openbsc/gsm_data.h> -#include <openbsc/meas_rep.h> -#include <openbsc/signal.h> - -/* process an already parsed measurement report */ -static int process_meas_rep(struct gsm_meas_rep *mr) -{ - struct gsm_meas_rep_cell *mr_cell = NULL; - unsigned int best_better_db; - int i; - - /* FIXME: implement actual averaging over multiple measurement - * reports */ - - /* find the best cell in this report that is at least RXLEV_HYST - * better than the current serving cell */ - for (i = 0; i < mr->num_cell; i++) { - unsigned int better; - if (mr->cell[i].rxlev < mr->dl.full.rx_lev + RXLEV_HYST) - continue; - - better = mr->cell[i].rxlev - mr->dl.full.rx_lev; - if (better > best_better_db) { - mr_cell = &mr->cell[i]; - best_better_db = better; - } - } - - if (mr_cell) - return handover_to_arfcn_bsic(mr->lchan, mr_cell->arfcn, - mr_cell->bsic); - return 0; -} - -static int meas_proc_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - struct gsm_lchan *lchan; - struct gsm_meas_rep *mr; - - if (subsys != SS_LCHAN) - return 0; - - switch (signal) { - case S_LCHAN_MEAS_REP: - mr = signal_data; - process_meas_rep(mr); - break; - } - - return 0; -} - -static __attribute__((constructor)) void on_dso_load_meas(void) -{ - osmo_signal_register_handler(SS_LCHAN, meas_proc_sig_cb, NULL); -} diff --git a/openbsc/src/libbsc/meas_rep.c b/openbsc/src/libbsc/meas_rep.c deleted file mode 100644 index 808103d28..000000000 --- a/openbsc/src/libbsc/meas_rep.c +++ /dev/null @@ -1,115 +0,0 @@ -/* Measurement Report Processing */ - -/* (C) 2009 by Harald Welte <laforge@gnumonks.org> - * - * 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 <http://www.gnu.org/licenses/>. - * - */ - - -#include <openbsc/gsm_data.h> -#include <openbsc/meas_rep.h> - -static int get_field(const struct gsm_meas_rep *rep, - enum meas_rep_field field) -{ - switch (field) { - case MEAS_REP_DL_RXLEV_FULL: - return rep->dl.full.rx_lev; - case MEAS_REP_DL_RXLEV_SUB: - return rep->dl.sub.rx_lev; - case MEAS_REP_DL_RXQUAL_FULL: - return rep->dl.full.rx_qual; - case MEAS_REP_DL_RXQUAL_SUB: - return rep->dl.sub.rx_qual; - case MEAS_REP_UL_RXLEV_FULL: - return rep->ul.full.rx_lev; - case MEAS_REP_UL_RXLEV_SUB: - return rep->ul.sub.rx_lev; - case MEAS_REP_UL_RXQUAL_FULL: - return rep->ul.full.rx_qual; - case MEAS_REP_UL_RXQUAL_SUB: - return rep->ul.sub.rx_qual; - } - - return 0; -} - - -unsigned int calc_initial_idx(unsigned int array_size, - unsigned int meas_rep_idx, - unsigned int num_values) -{ - int offs, idx; - - /* from which element do we need to start if we're interested - * in an average of 'num' elements */ - offs = meas_rep_idx - num_values; - - if (offs < 0) - idx = array_size + offs; - else - idx = offs; - - return idx; -} - -/* obtain an average over the last 'num' fields in the meas reps */ -int get_meas_rep_avg(const struct gsm_lchan *lchan, - enum meas_rep_field field, unsigned int num) -{ - unsigned int i, idx; - int avg = 0; - - if (num < 1) - return 0; - - idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep), - lchan->meas_rep_idx, num); - - for (i = 0; i < num; i++) { - int j = (idx+i) % ARRAY_SIZE(lchan->meas_rep); - - avg += get_field(&lchan->meas_rep[j], field); - } - - return avg / num; -} - -/* Check if N out of M last values for FIELD are >= bd */ -int meas_rep_n_out_of_m_be(const struct gsm_lchan *lchan, - enum meas_rep_field field, - unsigned int n, unsigned int m, int be) -{ - unsigned int i, idx; - int count = 0; - - idx = calc_initial_idx(ARRAY_SIZE(lchan->meas_rep), - lchan->meas_rep_idx, m); - - for (i = 0; i < m; i++) { - int j = (idx + i) % ARRAY_SIZE(lchan->meas_rep); - int val = get_field(&lchan->meas_rep[j], field); - - if (val >= be) - count++; - - if (count >= n) - return 1; - } - - return 0; -} diff --git a/openbsc/src/libbsc/net_init.c b/openbsc/src/libbsc/net_init.c deleted file mode 100644 index bc5ed3510..000000000 --- a/openbsc/src/libbsc/net_init.c +++ /dev/null @@ -1,69 +0,0 @@ -/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org> - * - * 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 <http://www.gnu.org/licenses/>. - * - */ - -#include <openbsc/common_cs.h> -#include <openbsc/osmo_bsc.h> -#include <openbsc/bsc_msc_data.h> - -struct gsm_network *bsc_network_init(void *ctx, - uint16_t country_code, - uint16_t network_code, - mncc_recv_cb_t mncc_recv) -{ - struct gsm_network *net; - - net = gsm_network_init(ctx, country_code, network_code, mncc_recv); - - net->bsc_data = talloc_zero(net, struct osmo_bsc_data); - if (!net->bsc_data) { - talloc_free(net); - return NULL; - } - - /* Init back pointer */ - net->bsc_data->auto_off_timeout = -1; - net->bsc_data->network = net; - INIT_LLIST_HEAD(&net->bsc_data->mscs); - - net->num_bts = 0; - net->reject_cause = GSM48_REJECT_ROAMING_NOT_ALLOWED; - net->T3101 = GSM_T3101_DEFAULT; - net->T3105 = GSM_T3105_DEFAULT; - net->T3113 = GSM_T3113_DEFAULT; - net->T3122 = GSM_T3122_DEFAULT; - /* FIXME: initialize all other timers! */ - - /* default set of handover parameters */ - net->handover.win_rxlev_avg = 10; - net->handover.win_rxqual_avg = 1; - net->handover.win_rxlev_avg_neigh = 10; - net->handover.pwr_interval = 6; - net->handover.pwr_hysteresis = 3; - net->handover.max_distance = 9999; - - INIT_LLIST_HEAD(&net->bts_list); - - /* init statistics */ - net->bsc_ctrs = rate_ctr_group_alloc(net, &bsc_ctrg_desc, 0); - - gsm_net_update_ctype(net); - - return net; -} - diff --git a/openbsc/src/libbsc/paging.c b/openbsc/src/libbsc/paging.c deleted file mode 100644 index 78e39c558..000000000 --- a/openbsc/src/libbsc/paging.c +++ /dev/null @@ -1,449 +0,0 @@ -/* Paging helper and manager.... */ -/* (C) 2009,2013 by Holger Hans Peter Freyther <zecke@selfish.org> - * 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 <http://www.gnu.org/licenses/>. - * - */ - -/* - * Relevant specs: - * 12.21: - * - 9.4.12 for CCCH Local Threshold - * - * 05.58: - * - 8.5.2 CCCH Load indication - * - 9.3.15 Paging Load - * - * Approach: - * - Send paging command to subscriber - * - On Channel Request we will remember the reason - * - After the ACK we will request the identity - * - Then we will send assign the gsm_subscriber and - * - and call a callback - */ - -#include <stdio.h> -#include <stdlib.h> -#include <assert.h> - -#include <osmocom/core/talloc.h> -#include <osmocom/gsm/gsm48.h> -#include <osmocom/gsm/gsm0502.h> - -#include <openbsc/bsc_subscriber.h> -#include <openbsc/paging.h> -#include <openbsc/debug.h> -#include <openbsc/signal.h> -#include <openbsc/abis_rsl.h> -#include <openbsc/gsm_data.h> -#include <openbsc/chan_alloc.h> -#include <openbsc/bsc_api.h> - -void *tall_paging_ctx; - -#define PAGING_TIMER 0, 500000 - -/* - * Kill one paging request update the internal list... - */ -static void paging_remove_request(struct gsm_bts_paging_state *paging_bts, - struct gsm_paging_request *to_be_deleted) -{ - osmo_timer_del(&to_be_deleted->T3113); - llist_del(&to_be_deleted->entry); - bsc_subscr_put(to_be_deleted->bsub); - talloc_free(to_be_deleted); -} - -static void page_ms(struct gsm_paging_request *request) -{ - uint8_t mi[128]; - unsigned int mi_len; - unsigned int page_group; - struct gsm_bts *bts = request->bts; - - /* the bts is down.. we will just wait for the paging to expire */ - if (!bts->oml_link) - return; - - log_set_context(LOG_CTX_BSC_SUBSCR, request->bsub); - - LOGP(DPAG, LOGL_INFO, "Going to send paging commands: imsi: %s tmsi: " - "0x%08x for ch. type %d (attempt %d)\n", request->bsub->imsi, - request->bsub->tmsi, request->chan_type, request->attempts); - - if (request->bsub->tmsi == GSM_RESERVED_TMSI) - mi_len = gsm48_generate_mid_from_imsi(mi, request->bsub->imsi); - else - mi_len = gsm48_generate_mid_from_tmsi(mi, request->bsub->tmsi); - - page_group = gsm0502_calc_paging_group(&bts->si_common.chan_desc, - str_to_imsi(request->bsub->imsi)); - gsm0808_page(bts, page_group, mi_len, mi, request->chan_type); - log_set_context(LOG_CTX_BSC_SUBSCR, NULL); -} - -static void paging_schedule_if_needed(struct gsm_bts_paging_state *paging_bts) -{ - if (llist_empty(&paging_bts->pending_requests)) - return; - - if (!osmo_timer_pending(&paging_bts->work_timer)) - osmo_timer_schedule(&paging_bts->work_timer, PAGING_TIMER); -} - - -static void paging_handle_pending_requests(struct gsm_bts_paging_state *paging_bts); -static void paging_give_credit(void *data) -{ - struct gsm_bts_paging_state *paging_bts = data; - - LOGP(DPAG, LOGL_NOTICE, "No slots available on bts nr %d\n", paging_bts->bts->nr); - paging_bts->available_slots = 20; - paging_handle_pending_requests(paging_bts); -} - -static int can_send_pag_req(struct gsm_bts *bts, int rsl_type) -{ - struct pchan_load pl; - int count; - - memset(&pl, 0, sizeof(pl)); - bts_chan_load(&pl, bts); - - switch (rsl_type) { - case RSL_CHANNEED_TCH_F: - case RSL_CHANNEED_TCH_ForH: - goto count_tch; - break; - case RSL_CHANNEED_SDCCH: - goto count_sdcch; - break; - case RSL_CHANNEED_ANY: - default: - if (bts->network->pag_any_tch) - goto count_tch; - else - goto count_sdcch; - break; - } - - return 0; - - /* could available SDCCH */ -count_sdcch: - count = 0; - count += pl.pchan[GSM_PCHAN_SDCCH8_SACCH8C].total - - pl.pchan[GSM_PCHAN_SDCCH8_SACCH8C].used; - count += pl.pchan[GSM_PCHAN_CCCH_SDCCH4].total - - pl.pchan[GSM_PCHAN_CCCH_SDCCH4].used; - return bts->paging.free_chans_need > count; - -count_tch: - count = 0; - count += pl.pchan[GSM_PCHAN_TCH_F].total - - pl.pchan[GSM_PCHAN_TCH_F].used; - if (bts->network->neci) - count += pl.pchan[GSM_PCHAN_TCH_H].total - - pl.pchan[GSM_PCHAN_TCH_H].used; - return bts->paging.free_chans_need > count; -} - -/* - * This is kicked by the periodic PAGING LOAD Indicator - * coming from abis_rsl.c - * - * We attempt to iterate once over the list of items but - * only upto available_slots. - */ -static void paging_handle_pending_requests(struct gsm_bts_paging_state *paging_bts) -{ - struct gsm_paging_request *request = NULL; - - /* - * Determine if the pending_requests list is empty and - * return then. - */ - if (llist_empty(&paging_bts->pending_requests)) { - /* since the list is empty, no need to reschedule the timer */ - return; - } - - /* - * In case the BTS does not provide us with load indication and we - * ran out of slots, call an autofill routine. It might be that the - * BTS did not like our paging messages and then we have counted down - * to zero and we do not get any messages. - */ - if (paging_bts->available_slots == 0) { - osmo_timer_setup(&paging_bts->credit_timer, paging_give_credit, - paging_bts); - osmo_timer_schedule(&paging_bts->credit_timer, 5, 0); - return; - } - - request = llist_entry(paging_bts->pending_requests.next, - struct gsm_paging_request, entry); - - /* we need to determine the number of free channels */ - if (paging_bts->free_chans_need != -1) { - if (can_send_pag_req(request->bts, request->chan_type) != 0) - goto skip_paging; - } - - /* handle the paging request now */ - page_ms(request); - paging_bts->available_slots--; - request->attempts++; - - /* take the current and add it to the back */ - llist_del(&request->entry); - llist_add_tail(&request->entry, &paging_bts->pending_requests); - -skip_paging: - osmo_timer_schedule(&paging_bts->work_timer, PAGING_TIMER); -} - -static void paging_worker(void *data) -{ - struct gsm_bts_paging_state *paging_bts = data; - - paging_handle_pending_requests(paging_bts); -} - -static void paging_init_if_needed(struct gsm_bts *bts) -{ - if (bts->paging.bts) - return; - - bts->paging.bts = bts; - INIT_LLIST_HEAD(&bts->paging.pending_requests); - osmo_timer_setup(&bts->paging.work_timer, paging_worker, - &bts->paging); - - /* Large number, until we get a proper message */ - bts->paging.available_slots = 20; -} - -static int paging_pending_request(struct gsm_bts_paging_state *bts, - struct bsc_subscr *bsub) -{ - struct gsm_paging_request *req; - - llist_for_each_entry(req, &bts->pending_requests, entry) { - if (bsub == req->bsub) - return 1; - } - - return 0; -} - -static void paging_T3113_expired(void *data) -{ - struct gsm_paging_request *req = (struct gsm_paging_request *)data; - void *cbfn_param; - gsm_cbfn *cbfn; - int msg; - - log_set_context(LOG_CTX_BSC_SUBSCR, req->bsub); - - LOGP(DPAG, LOGL_INFO, "T3113 expired for request %p (%s)\n", - req, bsc_subscr_name(req->bsub)); - - /* must be destroyed before calling cbfn, to prevent double free */ - rate_ctr_inc(&req->bts->network->bsc_ctrs->ctr[BSC_CTR_PAGING_EXPIRED]); - cbfn_param = req->cbfn_param; - cbfn = req->cbfn; - - /* did we ever manage to page the subscriber */ - msg = req->attempts > 0 ? GSM_PAGING_EXPIRED : GSM_PAGING_BUSY; - - /* destroy it now. Do not access req afterwards */ - paging_remove_request(&req->bts->paging, req); - - if (cbfn) - cbfn(GSM_HOOK_RR_PAGING, msg, NULL, NULL, - cbfn_param); - -} - -static int _paging_request(struct gsm_bts *bts, struct bsc_subscr *bsub, - int type, gsm_cbfn *cbfn, void *data) -{ - struct gsm_bts_paging_state *bts_entry = &bts->paging; - struct gsm_paging_request *req; - - if (paging_pending_request(bts_entry, bsub)) { - LOGP(DPAG, LOGL_INFO, "Paging request already pending for %s\n", - bsc_subscr_name(bsub)); - return -EEXIST; - } - - LOGP(DPAG, LOGL_DEBUG, "Start paging of subscriber %s on bts %d.\n", - bsc_subscr_name(bsub), bts->nr); - req = talloc_zero(tall_paging_ctx, struct gsm_paging_request); - req->bsub = bsc_subscr_get(bsub); - req->bts = bts; - req->chan_type = type; - req->cbfn = cbfn; - req->cbfn_param = data; - osmo_timer_setup(&req->T3113, paging_T3113_expired, req); - osmo_timer_schedule(&req->T3113, bts->network->T3113, 0); - llist_add_tail(&req->entry, &bts_entry->pending_requests); - paging_schedule_if_needed(bts_entry); - - return 0; -} - -int paging_request_bts(struct gsm_bts *bts, struct bsc_subscr *bsub, - int type, gsm_cbfn *cbfn, void *data) -{ - int rc; - - /* skip all currently inactive TRX */ - if (!trx_is_usable(bts->c0)) - return 0; - - /* maybe it is the first time we use it */ - paging_init_if_needed(bts); - - /* Trigger paging, pass any error to the caller */ - rc = _paging_request(bts, bsub, type, cbfn, data); - if (rc < 0) - return rc; - return 1; -} - -int paging_request(struct gsm_network *network, struct bsc_subscr *bsub, - int type, gsm_cbfn *cbfn, void *data) -{ - struct gsm_bts *bts = NULL; - int num_pages = 0; - - rate_ctr_inc(&network->bsc_ctrs->ctr[BSC_CTR_PAGING_ATTEMPTED]); - - /* start paging subscriber on all BTS within Location Area */ - do { - int rc; - - bts = gsm_bts_by_lac(network, bsub->lac, bts); - if (!bts) - break; - - rc = paging_request_bts(bts, bsub, type, cbfn, data); - if (rc < 0) { - paging_request_stop(&network->bts_list, NULL, bsub, - NULL, NULL); - return rc; - } - num_pages += rc; - } while (1); - - if (num_pages == 0) - rate_ctr_inc(&network->bsc_ctrs->ctr[BSC_CTR_PAGING_DETACHED]); - - return num_pages; -} - - -/* we consciously ignore the type of the request here */ -static void _paging_request_stop(struct gsm_bts *bts, struct bsc_subscr *bsub, - struct gsm_subscriber_connection *conn, - struct msgb *msg) -{ - struct gsm_bts_paging_state *bts_entry = &bts->paging; - struct gsm_paging_request *req, *req2; - - paging_init_if_needed(bts); - - llist_for_each_entry_safe(req, req2, &bts_entry->pending_requests, - entry) { - if (req->bsub == bsub) { - gsm_cbfn *cbfn = req->cbfn; - void *param = req->cbfn_param; - - /* now give up the data structure */ - paging_remove_request(&bts->paging, req); - req = NULL; - - if (conn && cbfn) { - LOGP(DPAG, LOGL_DEBUG, "Stop paging %s on bts %d, calling cbfn.\n", bsub->imsi, bts->nr); - cbfn(GSM_HOOK_RR_PAGING, GSM_PAGING_SUCCEEDED, - msg, conn, param); - } else - LOGP(DPAG, LOGL_DEBUG, "Stop paging %s on bts %d silently.\n", bsub->imsi, bts->nr); - break; - } - } -} - -/* Stop paging on all other bts' */ -void paging_request_stop(struct llist_head *bts_list, - struct gsm_bts *_bts, struct bsc_subscr *bsub, - struct gsm_subscriber_connection *conn, - struct msgb *msg) -{ - struct gsm_bts *bts; - - log_set_context(LOG_CTX_BSC_SUBSCR, bsub); - - /* Stop this first and dispatch the request */ - if (_bts) - _paging_request_stop(_bts, bsub, conn, msg); - - /* Make sure to cancel this everywhere else */ - llist_for_each_entry(bts, bts_list, list) { - /* Sort of an optimization. */ - if (bts == _bts) - continue; - _paging_request_stop(bts, bsub, NULL, NULL); - } -} - -void paging_update_buffer_space(struct gsm_bts *bts, uint16_t free_slots) -{ - paging_init_if_needed(bts); - - osmo_timer_del(&bts->paging.credit_timer); - bts->paging.available_slots = free_slots; - paging_schedule_if_needed(&bts->paging); -} - -unsigned int paging_pending_requests_nr(struct gsm_bts *bts) -{ - unsigned int requests = 0; - struct gsm_paging_request *req; - - paging_init_if_needed(bts); - - llist_for_each_entry(req, &bts->paging.pending_requests, entry) - ++requests; - - return requests; -} - -/** - * Find any paging data for the given subscriber at the given BTS. - */ -void *paging_get_data(struct gsm_bts *bts, struct bsc_subscr *bsub) -{ - struct gsm_paging_request *req; - - llist_for_each_entry(req, &bts->paging.pending_requests, entry) - if (req->bsub == bsub) - return req->cbfn_param; - - return NULL; -} diff --git a/openbsc/src/libbsc/pcu_sock.c b/openbsc/src/libbsc/pcu_sock.c deleted file mode 100644 index 98e12fad4..000000000 --- a/openbsc/src/libbsc/pcu_sock.c +++ /dev/null @@ -1,742 +0,0 @@ -/* pcu_sock.c: Connect from PCU via unix domain socket */ - -/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org> - * (C) 2009-2012 by Andreas Eversberg <jolly@eversberg.eu> - * (C) 2012 by Holger Hans Peter Freyther - * All Rights Reserved - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - */ - -#include <stdio.h> -#include <unistd.h> -#include <stdlib.h> -#include <string.h> -#include <errno.h> -#include <assert.h> -#include <sys/socket.h> -#include <sys/un.h> - -#include <osmocom/core/talloc.h> -#include <osmocom/core/select.h> -#include <osmocom/core/socket.h> -#include <osmocom/core/logging.h> -#include <osmocom/gsm/l1sap.h> -#include <osmocom/gsm/gsm0502.h> - -#include <openbsc/gsm_data.h> -#include <openbsc/pcu_if.h> -#include <openbsc/pcuif_proto.h> -#include <openbsc/signal.h> -#include <openbsc/debug.h> -#include <openbsc/abis_rsl.h> - -static int pcu_sock_send(struct gsm_bts *bts, struct msgb *msg); -uint32_t trx_get_hlayer1(struct gsm_bts_trx *trx); -int pcu_direct = 1; - -static const char *sapi_string[] = { - [PCU_IF_SAPI_RACH] = "RACH", - [PCU_IF_SAPI_AGCH] = "AGCH", - [PCU_IF_SAPI_PCH] = "PCH", - [PCU_IF_SAPI_BCCH] = "BCCH", - [PCU_IF_SAPI_PDTCH] = "PDTCH", - [PCU_IF_SAPI_PRACH] = "PRACH", - [PCU_IF_SAPI_PTCCH] = "PTCCH", - [PCU_IF_SAPI_AGCH_DT] = "AGCH_DT", -}; - -/* Check if BTS has a PCU connection */ -static bool pcu_connected(struct gsm_bts *bts) -{ - struct pcu_sock_state *state = bts->pcu_state; - - if (!state) - return false; - if (state->conn_bfd.fd <= 0) - return false; - return true; -} - -/* - * PCU messages - */ - -/* Set up an message buffer to package an pcu interface message */ -struct msgb *pcu_msgb_alloc(uint8_t msg_type, uint8_t bts_nr) -{ - struct msgb *msg; - struct gsm_pcu_if *pcu_prim; - - msg = msgb_alloc(sizeof(struct gsm_pcu_if), "pcu_sock_tx"); - if (!msg) - return NULL; - - msgb_put(msg, sizeof(struct gsm_pcu_if)); - pcu_prim = (struct gsm_pcu_if *) msg->data; - pcu_prim->msg_type = msg_type; - pcu_prim->bts_nr = bts_nr; - - return msg; -} - -/* Helper function exclusivly used by pcu_if_signal_cb() */ -static bool ts_should_be_pdch(struct gsm_bts_trx_ts *ts) { - if (ts->pchan == GSM_PCHAN_PDCH) - return true; - if (ts->pchan == GSM_PCHAN_TCH_F_PDCH) { - /* When we're busy deactivating the PDCH, we first set - * DEACT_PENDING, tell the PCU about it and wait for a - * response. So DEACT_PENDING means "no PDCH" to the PCU. - * Similarly, when we're activating PDCH, we set the - * ACT_PENDING and wait for an activation response from the - * PCU, so ACT_PENDING means "is PDCH". */ - if (ts->flags & TS_F_PDCH_ACTIVE) - return !(ts->flags & TS_F_PDCH_DEACT_PENDING); - else - return (ts->flags & TS_F_PDCH_ACT_PENDING); - } - if (ts->pchan == GSM_PCHAN_TCH_F_TCH_H_PDCH) { - /* - * When we're busy de-/activating the PDCH, we first set - * ts->dyn.pchan_want, tell the PCU about it and wait for a - * response. So only care about dyn.pchan_want here. - */ - return ts->dyn.pchan_want == GSM_PCHAN_PDCH; - } - return false; -} - -/* Send BTS properties to the PCU */ -static int pcu_tx_info_ind(struct gsm_bts *bts) -{ - struct msgb *msg; - struct gsm_pcu_if *pcu_prim; - struct gsm_pcu_if_info_ind *info_ind; - struct gprs_rlc_cfg *rlcc; - struct gsm_bts_gprs_nsvc *nsvc; - struct gsm_bts_trx *trx; - struct gsm_bts_trx_ts *ts; - int i, j; - - OSMO_ASSERT(bts); - OSMO_ASSERT(bts->network); - - LOGP(DPCU, LOGL_INFO, "Sending info for BTS %d\n",bts->nr); - - rlcc = &bts->gprs.cell.rlc_cfg; - - msg = pcu_msgb_alloc(PCU_IF_MSG_INFO_IND, bts->nr); - if (!msg) - return -ENOMEM; - - pcu_prim = (struct gsm_pcu_if *) msg->data; - info_ind = &pcu_prim->u.info_ind; - info_ind->version = PCU_IF_VERSION; - info_ind->flags |= PCU_IF_FLAG_ACTIVE; - - if (pcu_direct) - info_ind->flags |= PCU_IF_FLAG_SYSMO; - - /* RAI */ - info_ind->mcc = bts->network->country_code; - info_ind->mnc = bts->network->network_code; - info_ind->lac = bts->location_area_code; - info_ind->rac = bts->gprs.rac; - - /* NSE */ - info_ind->nsei = bts->gprs.nse.nsei; - memcpy(info_ind->nse_timer, bts->gprs.nse.timer, 7); - memcpy(info_ind->cell_timer, bts->gprs.cell.timer, 11); - - /* cell attributes */ - info_ind->cell_id = bts->cell_identity; - info_ind->repeat_time = rlcc->paging.repeat_time; - info_ind->repeat_count = rlcc->paging.repeat_count; - info_ind->bvci = bts->gprs.cell.bvci; - info_ind->t3142 = rlcc->parameter[RLC_T3142]; - info_ind->t3169 = rlcc->parameter[RLC_T3169]; - info_ind->t3191 = rlcc->parameter[RLC_T3191]; - info_ind->t3193_10ms = rlcc->parameter[RLC_T3193]; - info_ind->t3195 = rlcc->parameter[RLC_T3195]; - info_ind->n3101 = rlcc->parameter[RLC_N3101]; - info_ind->n3103 = rlcc->parameter[RLC_N3103]; - info_ind->n3105 = rlcc->parameter[RLC_N3105]; - info_ind->cv_countdown = rlcc->parameter[CV_COUNTDOWN]; - if (rlcc->cs_mask & (1 << GPRS_CS1)) - info_ind->flags |= PCU_IF_FLAG_CS1; - if (rlcc->cs_mask & (1 << GPRS_CS2)) - info_ind->flags |= PCU_IF_FLAG_CS2; - if (rlcc->cs_mask & (1 << GPRS_CS3)) - info_ind->flags |= PCU_IF_FLAG_CS3; - if (rlcc->cs_mask & (1 << GPRS_CS4)) - info_ind->flags |= PCU_IF_FLAG_CS4; - if (bts->gprs.mode == BTS_GPRS_EGPRS) { - if (rlcc->cs_mask & (1 << GPRS_MCS1)) - info_ind->flags |= PCU_IF_FLAG_MCS1; - if (rlcc->cs_mask & (1 << GPRS_MCS2)) - info_ind->flags |= PCU_IF_FLAG_MCS2; - if (rlcc->cs_mask & (1 << GPRS_MCS3)) - info_ind->flags |= PCU_IF_FLAG_MCS3; - if (rlcc->cs_mask & (1 << GPRS_MCS4)) - info_ind->flags |= PCU_IF_FLAG_MCS4; - if (rlcc->cs_mask & (1 << GPRS_MCS5)) - info_ind->flags |= PCU_IF_FLAG_MCS5; - if (rlcc->cs_mask & (1 << GPRS_MCS6)) - info_ind->flags |= PCU_IF_FLAG_MCS6; - if (rlcc->cs_mask & (1 << GPRS_MCS7)) - info_ind->flags |= PCU_IF_FLAG_MCS7; - if (rlcc->cs_mask & (1 << GPRS_MCS8)) - info_ind->flags |= PCU_IF_FLAG_MCS8; - if (rlcc->cs_mask & (1 << GPRS_MCS9)) - info_ind->flags |= PCU_IF_FLAG_MCS9; - } -#warning "isn't dl_tbf_ext wrong?: * 10 and no ntohs" - info_ind->dl_tbf_ext = rlcc->parameter[T_DL_TBF_EXT]; -#warning "isn't ul_tbf_ext wrong?: * 10 and no ntohs" - info_ind->ul_tbf_ext = rlcc->parameter[T_UL_TBF_EXT]; - info_ind->initial_cs = rlcc->initial_cs; - info_ind->initial_mcs = rlcc->initial_mcs; - - /* NSVC */ - for (i = 0; i < ARRAY_SIZE(info_ind->nsvci); i++) { - nsvc = &bts->gprs.nsvc[i]; - info_ind->nsvci[i] = nsvc->nsvci; - info_ind->local_port[i] = nsvc->local_port; - info_ind->remote_port[i] = nsvc->remote_port; - info_ind->remote_ip[i] = nsvc->remote_ip; - } - - for (i = 0; i < ARRAY_SIZE(info_ind->trx); i++) { - trx = gsm_bts_trx_num(bts, i); - if (!trx) - continue; - info_ind->trx[i].hlayer1 = 0x2342; - info_ind->trx[i].pdch_mask = 0; - info_ind->trx[i].arfcn = trx->arfcn; - for (j = 0; j < ARRAY_SIZE(trx->ts); j++) { - ts = &trx->ts[j]; - if (ts->mo.nm_state.operational == NM_OPSTATE_ENABLED - && ts_should_be_pdch(ts)) { - info_ind->trx[i].pdch_mask |= (1 << j); - info_ind->trx[i].tsc[j] = - (ts->tsc >= 0) ? ts->tsc : bts->bsic & 7; - LOGP(DPCU, LOGL_INFO, "trx=%d ts=%d: " - "available (tsc=%d arfcn=%d)\n", - trx->nr, ts->nr, - info_ind->trx[i].tsc[j], - info_ind->trx[i].arfcn); - } - } - } - - return pcu_sock_send(bts, msg); -} - -void pcu_info_update(struct gsm_bts *bts) -{ - if (pcu_connected(bts)) - pcu_tx_info_ind(bts); -} - -/* Forward rach indication to PCU */ -int pcu_tx_rach_ind(struct gsm_bts *bts, int16_t qta, uint16_t ra, uint32_t fn, - uint8_t is_11bit, enum ph_burst_type burst_type) -{ - struct msgb *msg; - struct gsm_pcu_if *pcu_prim; - struct gsm_pcu_if_rach_ind *rach_ind; - - /* Bail if no PCU is connected */ - if (!pcu_connected(bts)) { - LOGP(DRSL, LOGL_ERROR, "BTS %d CHAN RQD(GPRS) but PCU not " - "connected!\n", bts->nr); - return -ENODEV; - } - - LOGP(DPCU, LOGL_INFO, "Sending RACH indication: qta=%d, ra=%d, " - "fn=%d\n", qta, ra, fn); - - msg = pcu_msgb_alloc(PCU_IF_MSG_RACH_IND, bts->nr); - if (!msg) - return -ENOMEM; - pcu_prim = (struct gsm_pcu_if *) msg->data; - rach_ind = &pcu_prim->u.rach_ind; - - rach_ind->sapi = PCU_IF_SAPI_RACH; - rach_ind->ra = ra; - rach_ind->qta = qta; - rach_ind->fn = fn; - rach_ind->is_11bit = is_11bit; - rach_ind->burst_type = burst_type; - - return pcu_sock_send(bts, msg); -} - -/* Confirm the sending of an immediate assignment to the pcu */ -int pcu_tx_imm_ass_sent(struct gsm_bts *bts, uint32_t tlli) -{ - struct msgb *msg; - struct gsm_pcu_if *pcu_prim; - struct gsm_pcu_if_data_cnf_dt *data_cnf_dt; - - LOGP(DPCU, LOGL_INFO, "Sending PCH confirm with direct TLLI\n"); - - msg = pcu_msgb_alloc(PCU_IF_MSG_DATA_CNF_DT, bts->nr); - if (!msg) - return -ENOMEM; - pcu_prim = (struct gsm_pcu_if *) msg->data; - data_cnf_dt = &pcu_prim->u.data_cnf_dt; - - data_cnf_dt->sapi = PCU_IF_SAPI_PCH; - data_cnf_dt->tlli = tlli; - - return pcu_sock_send(bts, msg); -} - -/* we need to decode the raw RR paging messsage (see PCU code - * Encoding::write_paging_request) and extract the mobile identity - * (P-TMSI) from it */ -static int pcu_rx_rr_paging(struct gsm_bts *bts, uint8_t paging_group, - const uint8_t *raw_rr_msg) -{ - struct gsm48_paging1 *p1 = (struct gsm48_paging1 *) raw_rr_msg; - uint8_t chan_needed; - unsigned int mi_len; - uint8_t *mi; - int rc; - - switch (p1->msg_type) { - case GSM48_MT_RR_PAG_REQ_1: - chan_needed = (p1->cneed2 << 2) | p1->cneed1; - mi_len = p1->data[0]; - mi = p1->data+1; - LOGP(DPCU, LOGL_ERROR, "PCU Sends paging " - "request type %02x (chan_needed=%02x, mi_len=%u, mi=%s)\n", - p1->msg_type, chan_needed, mi_len, - osmo_hexdump_nospc(mi,mi_len)); - /* NOTE: We will have to add 2 to mi_len and subtract 2 from - * the mi pointer because rsl_paging_cmd() will perform the - * reverse operations. This is because rsl_paging_cmd() is - * normally expected to chop off the element identifier (0xC0) - * and the length field. In our parameter, we do not have - * those fields included. */ - rc = rsl_paging_cmd(bts, paging_group, mi_len+2, mi-2, - chan_needed, true); - break; - case GSM48_MT_RR_PAG_REQ_2: - case GSM48_MT_RR_PAG_REQ_3: - LOGP(DPCU, LOGL_ERROR, "PCU Sends unsupported paging " - "request type %02x\n", p1->msg_type); - rc = -EINVAL; - break; - default: - LOGP(DPCU, LOGL_ERROR, "PCU Sends unknown paging " - "request type %02x\n", p1->msg_type); - rc = -EINVAL; - break; - } - - return rc; -} - -static int pcu_rx_data_req(struct gsm_bts *bts, uint8_t msg_type, - struct gsm_pcu_if_data *data_req) -{ - uint8_t is_ptcch; - struct gsm_bts_trx *trx; - struct gsm_bts_trx_ts *ts; - struct msgb *msg; - char imsi_digit_buf[4]; - uint32_t tlli = -1; - uint8_t pag_grp; - int rc = 0; - - LOGP(DPCU, LOGL_DEBUG, "Data request received: sapi=%s arfcn=%d " - "block=%d data=%s\n", sapi_string[data_req->sapi], - data_req->arfcn, data_req->block_nr, - osmo_hexdump(data_req->data, data_req->len)); - - switch (data_req->sapi) { - case PCU_IF_SAPI_PCH: - /* the first three bytes are the last three digits of - * the IMSI, which we need to compute the paging group */ - imsi_digit_buf[0] = data_req->data[0]; - imsi_digit_buf[1] = data_req->data[1]; - imsi_digit_buf[2] = data_req->data[2]; - imsi_digit_buf[3] = '\0'; - LOGP(DPCU, LOGL_DEBUG, "SAPI PCH imsi %s\n", imsi_digit_buf); - pag_grp = gsm0502_calc_paging_group(&bts->si_common.chan_desc, - str_to_imsi(imsi_digit_buf)); - pcu_rx_rr_paging(bts, pag_grp, data_req->data+3); - break; - case PCU_IF_SAPI_AGCH: - msg = msgb_alloc(data_req->len, "pcu_agch"); - if (!msg) { - rc = -ENOMEM; - break; - } - msg->l3h = msgb_put(msg, data_req->len); - memcpy(msg->l3h, data_req->data, data_req->len); - - if (rsl_imm_assign_cmd(bts, msg->len, msg->data)) { - msgb_free(msg); - rc = -EIO; - } - break; - case PCU_IF_SAPI_AGCH_DT: - /* DT = direct tlli. A tlli is prefixed */ - - if (data_req->len < 5) { - LOGP(DPCU, LOGL_ERROR, "Received PCU data request with " - "invalid/small length %d\n", data_req->len); - break; - } - tlli = *((uint32_t *)data_req->data); - - msg = msgb_alloc(data_req->len - 4, "pcu_agch"); - if (!msg) { - rc = -ENOMEM; - break; - } - msg->l3h = msgb_put(msg, data_req->len - 4); - memcpy(msg->l3h, data_req->data + 4, data_req->len - 4); - - if (bts->type == GSM_BTS_TYPE_RBS2000) - rc = rsl_ericsson_imm_assign_cmd(bts, tlli, msg->len, msg->data); - else - rc = rsl_imm_assign_cmd(bts, msg->len, msg->data); - - if (rc) { - msgb_free(msg); - rc = -EIO; - } - break; - default: - LOGP(DPCU, LOGL_ERROR, "Received PCU data request with " - "unsupported sapi %d\n", data_req->sapi); - rc = -EINVAL; - } - - return rc; -} - -static int pcu_rx(struct gsm_network *net, uint8_t msg_type, - struct gsm_pcu_if *pcu_prim) -{ - int rc = 0; - struct gsm_bts *bts; - - /* FIXME: allow multiple BTS */ - bts = llist_entry(net->bts_list.next, struct gsm_bts, list); - - switch (msg_type) { - case PCU_IF_MSG_DATA_REQ: - case PCU_IF_MSG_PAG_REQ: - rc = pcu_rx_data_req(bts, msg_type, &pcu_prim->u.data_req); - break; - default: - LOGP(DPCU, LOGL_ERROR, "Received unknwon PCU msg type %d\n", - msg_type); - rc = -EINVAL; - } - - return rc; -} - -/* - * PCU socket interface - */ - -static int pcu_sock_send(struct gsm_bts *bts, struct msgb *msg) -{ - struct pcu_sock_state *state = bts->pcu_state; - struct osmo_fd *conn_bfd; - struct gsm_pcu_if *pcu_prim = (struct gsm_pcu_if *) msg->data; - - if (!state) { - if (pcu_prim->msg_type != PCU_IF_MSG_TIME_IND) - LOGP(DPCU, LOGL_INFO, "PCU socket not created, " - "dropping message\n"); - msgb_free(msg); - return -EINVAL; - } - conn_bfd = &state->conn_bfd; - if (conn_bfd->fd <= 0) { - if (pcu_prim->msg_type != PCU_IF_MSG_TIME_IND) - LOGP(DPCU, LOGL_NOTICE, "PCU socket not connected, " - "dropping message\n"); - msgb_free(msg); - return -EIO; - } - msgb_enqueue(&state->upqueue, msg); - conn_bfd->when |= BSC_FD_WRITE; - - return 0; -} - -static void pcu_sock_close(struct pcu_sock_state *state) -{ - struct osmo_fd *bfd = &state->conn_bfd; - struct gsm_bts *bts; - struct gsm_bts_trx *trx; - struct gsm_bts_trx_ts *ts; - int i, j; - - /* FIXME: allow multiple BTS */ - bts = llist_entry(state->net->bts_list.next, struct gsm_bts, list); - - LOGP(DPCU, LOGL_NOTICE, "PCU socket has LOST connection\n"); - - close(bfd->fd); - bfd->fd = -1; - osmo_fd_unregister(bfd); - - /* re-enable the generation of ACCEPT for new connections */ - state->listen_bfd.when |= BSC_FD_READ; - -#if 0 - /* remove si13, ... */ - bts->si_valid &= ~(1 << SYSINFO_TYPE_13); - osmo_signal_dispatch(SS_GLOBAL, S_NEW_SYSINFO, bts); -#endif - - /* release PDCH */ - for (i = 0; i < 8; i++) { - trx = gsm_bts_trx_num(bts, i); - if (!trx) - break; - for (j = 0; j < 8; j++) { - ts = &trx->ts[j]; - if (ts->mo.nm_state.operational == NM_OPSTATE_ENABLED - && ts->pchan == GSM_PCHAN_PDCH) { - printf("l1sap_chan_rel(trx,gsm_lchan2chan_nr(ts->lchan));\n"); - } - } - } - - /* flush the queue */ - while (!llist_empty(&state->upqueue)) { - struct msgb *msg = msgb_dequeue(&state->upqueue); - msgb_free(msg); - } -} - -static int pcu_sock_read(struct osmo_fd *bfd) -{ - struct pcu_sock_state *state = (struct pcu_sock_state *)bfd->data; - struct gsm_pcu_if *pcu_prim; - struct msgb *msg; - int rc; - - msg = msgb_alloc(sizeof(*pcu_prim), "pcu_sock_rx"); - if (!msg) - return -ENOMEM; - - pcu_prim = (struct gsm_pcu_if *) msg->tail; - - rc = recv(bfd->fd, msg->tail, msgb_tailroom(msg), 0); - if (rc == 0) - goto close; - - if (rc < 0) { - if (errno == EAGAIN) - return 0; - goto close; - } - - rc = pcu_rx(state->net, pcu_prim->msg_type, pcu_prim); - - /* as we always synchronously process the message in pcu_rx() and - * its callbacks, we can free the message here. */ - msgb_free(msg); - - return rc; - -close: - msgb_free(msg); - pcu_sock_close(state); - return -1; -} - -static int pcu_sock_write(struct osmo_fd *bfd) -{ - struct pcu_sock_state *state = bfd->data; - int rc; - - while (!llist_empty(&state->upqueue)) { - struct msgb *msg, *msg2; - struct gsm_pcu_if *pcu_prim; - - /* peek at the beginning of the queue */ - msg = llist_entry(state->upqueue.next, struct msgb, list); - pcu_prim = (struct gsm_pcu_if *)msg->data; - - bfd->when &= ~BSC_FD_WRITE; - - /* bug hunter 8-): maybe someone forgot msgb_put(...) ? */ - if (!msgb_length(msg)) { - LOGP(DPCU, LOGL_ERROR, "message type (%d) with ZERO " - "bytes!\n", pcu_prim->msg_type); - goto dontsend; - } - - /* try to send it over the socket */ - rc = write(bfd->fd, msgb_data(msg), msgb_length(msg)); - if (rc == 0) - goto close; - if (rc < 0) { - if (errno == EAGAIN) { - bfd->when |= BSC_FD_WRITE; - break; - } - goto close; - } - -dontsend: - /* _after_ we send it, we can deueue */ - msg2 = msgb_dequeue(&state->upqueue); - assert(msg == msg2); - msgb_free(msg); - } - return 0; - -close: - pcu_sock_close(state); - - return -1; -} - -static int pcu_sock_cb(struct osmo_fd *bfd, unsigned int flags) -{ - int rc = 0; - - if (flags & BSC_FD_READ) - rc = pcu_sock_read(bfd); - if (rc < 0) - return rc; - - if (flags & BSC_FD_WRITE) - rc = pcu_sock_write(bfd); - - return rc; -} - -/* accept connection comming from PCU */ -static int pcu_sock_accept(struct osmo_fd *bfd, unsigned int flags) -{ - struct pcu_sock_state *state = (struct pcu_sock_state *)bfd->data; - struct osmo_fd *conn_bfd = &state->conn_bfd; - struct sockaddr_un un_addr; - socklen_t len; - int rc; - - len = sizeof(un_addr); - rc = accept(bfd->fd, (struct sockaddr *) &un_addr, &len); - if (rc < 0) { - LOGP(DPCU, LOGL_ERROR, "Failed to accept a new connection\n"); - return -1; - } - - if (conn_bfd->fd >= 0) { - LOGP(DPCU, LOGL_NOTICE, "PCU connects but we already have " - "another active connection ?!?\n"); - /* We already have one PCU connected, this is all we support */ - state->listen_bfd.when &= ~BSC_FD_READ; - close(rc); - return 0; - } - - conn_bfd->fd = rc; - conn_bfd->when = BSC_FD_READ; - conn_bfd->cb = pcu_sock_cb; - conn_bfd->data = state; - - if (osmo_fd_register(conn_bfd) != 0) { - LOGP(DPCU, LOGL_ERROR, "Failed to register new connection " - "fd\n"); - close(conn_bfd->fd); - conn_bfd->fd = -1; - return -1; - } - - LOGP(DPCU, LOGL_NOTICE, "PCU socket connected to external PCU\n"); - - return 0; -} - -/* Open connection to PCU */ -int pcu_sock_init(const char *path, struct gsm_bts *bts) -{ - struct pcu_sock_state *state; - struct osmo_fd *bfd; - int rc; - - state = talloc_zero(NULL, struct pcu_sock_state); - if (!state) - return -ENOMEM; - - INIT_LLIST_HEAD(&state->upqueue); - state->net = bts->network; - state->conn_bfd.fd = -1; - - bfd = &state->listen_bfd; - - bfd->fd = osmo_sock_unix_init(SOCK_SEQPACKET, 0, path, - OSMO_SOCK_F_BIND); - if (bfd->fd < 0) { - LOGP(DPCU, LOGL_ERROR, "Could not create unix socket: %s\n", - strerror(errno)); - talloc_free(state); - return -1; - } - - bfd->when = BSC_FD_READ; - bfd->cb = pcu_sock_accept; - bfd->data = state; - - rc = osmo_fd_register(bfd); - if (rc < 0) { - LOGP(DPCU, LOGL_ERROR, "Could not register listen fd: %d\n", - rc); - close(bfd->fd); - talloc_free(state); - return rc; - } - - bts->pcu_state = state; - return 0; -} - -/* Close connection to PCU */ -void pcu_sock_exit(struct gsm_bts *bts) -{ - struct pcu_sock_state *state = bts->pcu_state; - struct osmo_fd *bfd, *conn_bfd; - - if (!state) - return; - - conn_bfd = &state->conn_bfd; - if (conn_bfd->fd > 0) - pcu_sock_close(state); - bfd = &state->listen_bfd; - close(bfd->fd); - osmo_fd_unregister(bfd); - talloc_free(state); - bts->pcu_state = NULL; -} - diff --git a/openbsc/src/libbsc/rest_octets.c b/openbsc/src/libbsc/rest_octets.c deleted file mode 100644 index fdab70a0c..000000000 --- a/openbsc/src/libbsc/rest_octets.c +++ /dev/null @@ -1,860 +0,0 @@ -/* GSM Mobile Radio Interface Layer 3 messages on the A-bis interface, - * rest octet handling according to - * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */ - -/* (C) 2009 by Harald Welte <laforge@gnumonks.org> - * - * 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 <http://www.gnu.org/licenses/>. - * - */ - -#include <string.h> -#include <stdlib.h> -#include <errno.h> -#include <stdbool.h> - -#include <openbsc/debug.h> -#include <openbsc/gsm_data.h> -#include <osmocom/core/bitvec.h> -#include <osmocom/gsm/bitvec_gsm.h> -#include <openbsc/rest_octets.h> -#include <openbsc/arfcn_range_encode.h> -#include <openbsc/system_information.h> - -/* generate SI1 rest octets */ -int rest_octets_si1(uint8_t *data, uint8_t *nch_pos, int is1800_net) -{ - struct bitvec bv; - - memset(&bv, 0, sizeof(bv)); - bv.data = data; - bv.data_len = 1; - - if (nch_pos) { - bitvec_set_bit(&bv, H); - bitvec_set_uint(&bv, *nch_pos, 5); - } else - bitvec_set_bit(&bv, L); - - if (is1800_net) - bitvec_set_bit(&bv, L); - else - bitvec_set_bit(&bv, H); - - bitvec_spare_padding(&bv, 6); - return bv.data_len; -} - -/* Append Repeated E-UTRAN Neighbour Cell to bitvec: see 3GPP TS 44.018 Table 10.5.2.33b.1 */ -static inline void append_eutran_neib_cell(struct bitvec *bv, struct gsm_bts *bts, uint8_t budget) -{ - const struct osmo_earfcn_si2q *e = &bts->si_common.si2quater_neigh_list; - unsigned i, skip = 0; - size_t offset = bts->e_offset; - uint8_t rem = budget - 6, earfcn_budget; /* account for mandatory stop bit and THRESH_E-UTRAN_high */ - - if (budget <= 6) - return; - - OSMO_ASSERT(budget <= SI2Q_MAX_LEN); - - /* first we have to properly adjust budget requirements */ - if (e->prio_valid) /* E-UTRAN_PRIORITY: 3GPP TS 45.008*/ - rem -= 4; - else - rem--; - - if (e->thresh_lo_valid) /* THRESH_E-UTRAN_low: */ - rem -= 6; - else - rem--; - - if (e->qrxlm_valid) /* E-UTRAN_QRXLEVMIN: */ - rem -= 6; - else - rem--; - - /* now we can proceed with actually adding EARFCNs within adjusted budget limit */ - for (i = 0; i < e->length; i++) { - if (e->arfcn[i] != OSMO_EARFCN_INVALID) { - if (skip < offset) { - skip++; /* ignore EARFCNs added on previous calls */ - } else { - earfcn_budget = 17; /* compute budget per-EARFCN */ - if (OSMO_EARFCN_MEAS_INVALID == e->meas_bw[i]) - earfcn_budget++; - else - earfcn_budget += 4; - - if (rem - earfcn_budget < 0) - break; - else { - bts->e_offset++; - rem -= earfcn_budget; - bitvec_set_bit(bv, 1); /* EARFCN: */ - bitvec_set_uint(bv, e->arfcn[i], 16); - - if (OSMO_EARFCN_MEAS_INVALID == e->meas_bw[i]) - bitvec_set_bit(bv, 0); - else { /* Measurement Bandwidth: 9.1.54 */ - bitvec_set_bit(bv, 1); - bitvec_set_uint(bv, e->meas_bw[i], 3); - } - } - } - } - } - - /* stop bit - end of EARFCN + Measurement Bandwidth sequence */ - bitvec_set_bit(bv, 0); - - /* Note: we don't support different EARFCN arrays each with different priority, threshold etc. */ - - if (e->prio_valid) { - /* E-UTRAN_PRIORITY: 3GPP TS 45.008*/ - bitvec_set_bit(bv, 1); - bitvec_set_uint(bv, e->prio, 3); - } else - bitvec_set_bit(bv, 0); - - /* THRESH_E-UTRAN_high */ - bitvec_set_uint(bv, e->thresh_hi, 5); - - if (e->thresh_lo_valid) { - /* THRESH_E-UTRAN_low: */ - bitvec_set_bit(bv, 1); - bitvec_set_uint(bv, e->thresh_lo, 5); - } else - bitvec_set_bit(bv, 0); - - if (e->qrxlm_valid) { - /* E-UTRAN_QRXLEVMIN: */ - bitvec_set_bit(bv, 1); - bitvec_set_uint(bv, e->qrxlm, 5); - } else - bitvec_set_bit(bv, 0); -} - -static inline void append_earfcn(struct bitvec *bv, struct gsm_bts *bts, uint8_t budget) -{ - int rem = budget - 25; - if (rem <= 0) - return; - - OSMO_ASSERT(budget <= SI2Q_MAX_LEN); - - /* Additions in Rel-5: */ - bitvec_set_bit(bv, H); - /* No 3G Additional Measurement Param. Descr. */ - bitvec_set_bit(bv, 0); - /* No 3G ADDITIONAL MEASUREMENT Param. Descr. 2 */ - bitvec_set_bit(bv, 0); - /* Additions in Rel-6: */ - bitvec_set_bit(bv, H); - /* 3G_CCN_ACTIVE */ - bitvec_set_bit(bv, 0); - /* Additions in Rel-7: */ - bitvec_set_bit(bv, H); - /* No 700_REPORTING_OFFSET */ - bitvec_set_bit(bv, 0); - /* No 810_REPORTING_OFFSET */ - bitvec_set_bit(bv, 0); - /* Additions in Rel-8: */ - bitvec_set_bit(bv, H); - - /* Priority and E-UTRAN Parameters Description */ - bitvec_set_bit(bv, 1); - - /* No Serving Cell Priority Parameters Descr. */ - bitvec_set_bit(bv, 0); - /* No 3G Priority Parameters Description */ - bitvec_set_bit(bv, 0); - /* E-UTRAN Parameters Description */ - bitvec_set_bit(bv, 1); - - /* E-UTRAN_CCN_ACTIVE */ - bitvec_set_bit(bv, 0); - /* E-UTRAN_Start: 9.1.54 */ - bitvec_set_bit(bv, 1); - /* E-UTRAN_Stop: 9.1.54 */ - bitvec_set_bit(bv, 1); - - /* No E-UTRAN Measurement Parameters Descr. */ - bitvec_set_bit(bv, 0); - /* No GPRS E-UTRAN Measurement Param. Descr. */ - bitvec_set_bit(bv, 0); - - /* Note: each of next 3 "repeated" structures might be repeated any - (0, 1, 2...) times - we only support 1 and 0 */ - - /* Repeated E-UTRAN Neighbour Cells */ - bitvec_set_bit(bv, 1); - - /* N. B: 25 bits are set in append_earfcn() - keep it in sync with budget adjustment below: */ - append_eutran_neib_cell(bv, bts, rem); - - /* stop bit - end of Repeated E-UTRAN Neighbour Cells sequence: */ - bitvec_set_bit(bv, 0); - - /* Note: following 2 repeated structs are not supported ATM */ - /* stop bit - end of Repeated E-UTRAN Not Allowed Cells sequence: */ - bitvec_set_bit(bv, 0); - /* stop bit - end of Repeated E-UTRAN PCID to TA mapping sequence: */ - bitvec_set_bit(bv, 0); - - /* Priority and E-UTRAN Parameters Description ends here */ - /* No 3G CSG Description */ - bitvec_set_bit(bv, 0); - /* No E-UTRAN CSG Description */ - bitvec_set_bit(bv, 0); - /* No Additions in Rel-9: */ - bitvec_set_bit(bv, L); -} - -static inline int f0_helper(int *sc, size_t length, uint8_t *chan_list) -{ - int w[RANGE_ENC_MAX_ARFCNS] = { 0 }; - - return range_encode(ARFCN_RANGE_1024, sc, length, w, 0, chan_list); -} - -/* Estimate how many bits it'll take to append single FDD UARFCN */ -static inline int append_utran_fdd_length(uint16_t u, int *sc, size_t sc_len, size_t length) -{ - uint8_t chan_list[16] = { 0 }; - int tmp[sc_len], f0; - - memcpy(tmp, sc, sizeof(tmp)); - - f0 = f0_helper(tmp, length, chan_list); - if (f0 < 0) - return f0; - - return 21 + range1024_p(length); -} - -/* Append single FDD UARFCN */ -static inline int append_utran_fdd(struct bitvec *bv, uint16_t u, int *sc, size_t length) -{ - uint8_t chan_list[16] = { 0 }; - int f0 = f0_helper(sc, length, chan_list); - - if (f0 < 0) - return f0; - - /* Repeated UTRAN FDD Neighbour Cells */ - bitvec_set_bit(bv, 1); - - /* FDD-ARFCN */ - bitvec_set_bit(bv, 0); - bitvec_set_uint(bv, u, 14); - - /* FDD_Indic0: parameter value '0000000000' is a member of the set? */ - bitvec_set_bit(bv, f0); - /* NR_OF_FDD_CELLS */ - bitvec_set_uint(bv, length, 5); - - f0 = bv->cur_bit; - bitvec_add_range1024(bv, (struct gsm48_range_1024 *)chan_list); - bv->cur_bit = f0 + range1024_p(length); - - return 21 + range1024_p(length); -} - -/* Append multiple FDD UARFCNs */ -static inline int append_uarfcns(struct bitvec *bv, struct gsm_bts *bts, uint8_t budget) -{ - const uint16_t *u = bts->si_common.data.uarfcn_list, *sc = bts->si_common.data.scramble_list; - int i, j, k, rc, st = 0, a[bts->si_common.uarfcn_length]; - uint16_t cu = u[bts->u_offset]; /* caller ensures that length is positive */ - uint8_t rem = budget - 7, offset_diff; /* account for constant bits right away */ - - OSMO_ASSERT(budget <= SI2Q_MAX_LEN); - - if (budget <= 7) - return -ENOMEM; - - /* 3G Neighbour Cell Description */ - bitvec_set_bit(bv, 1); - /* No Index_Start_3G */ - bitvec_set_bit(bv, 0); - /* No Absolute_Index_Start_EMR */ - bitvec_set_bit(bv, 0); - - /* UTRAN FDD Description */ - bitvec_set_bit(bv, 1); - /* No Bandwidth_FDD */ - bitvec_set_bit(bv, 0); - - for (i = bts->u_offset; i < bts->si_common.uarfcn_length; i++) { - offset_diff = 0; - for (j = st, k = 0; j < i; j++) { - a[k++] = sc[j]; /* copy corresponding SCs */ - offset_diff++; /* compute proper offset step */ - } - if (u[i] != cu) { /* we've reached new UARFCN */ - rc = append_utran_fdd_length(cu, a, bts->si_common.uarfcn_length, k); - if (rc < 0) { /* estimate bit length requirements */ - return rc; - } - - if (rem - rc <= 0) - break; /* we have ran out of budget in current SI2q */ - else { - rem -= append_utran_fdd(bv, cu, a, k); - bts->u_offset += offset_diff; - } - cu = u[i]; - st = i; /* update start position */ - } - } - - if (rem > 22) { /* add last UARFCN not covered by previous cycle if it could possibly fit into budget */ - offset_diff = 0; - for (i = st, k = 0; i < bts->si_common.uarfcn_length; i++) { - a[k++] = sc[i]; - offset_diff++; - } - rc = append_utran_fdd_length(cu, a, bts->si_common.uarfcn_length, k); - if (rc < 0) { - return rc; - } - - if (rem - rc >= 0) { - rem -= append_utran_fdd(bv, cu, a, k); - bts->u_offset += offset_diff; - } - } - - /* stop bit - end of Repeated UTRAN FDD Neighbour Cells */ - bitvec_set_bit(bv, 0); - - /* UTRAN TDD Description */ - bitvec_set_bit(bv, 0); - - return 0; -} - -/* generate SI2quater rest octets: 3GPP TS 44.018 § 10.5.2.33b */ -int rest_octets_si2quater(uint8_t *data, struct gsm_bts *bts) -{ - int rc; - struct bitvec bv; - - if (bts->si2q_count < bts->si2q_index) - return -EINVAL; - - bv.data = data; - bv.data_len = 20; - bitvec_zero(&bv); - - /* BA_IND */ - bitvec_set_bit(&bv, 1); - /* 3G_BA_IND */ - bitvec_set_bit(&bv, 1); - /* MP_CHANGE_MARK */ - bitvec_set_bit(&bv, 0); - - /* SI2quater_INDEX */ - bitvec_set_uint(&bv, bts->si2q_index, 4); - /* SI2quater_COUNT */ - bitvec_set_uint(&bv, bts->si2q_count, 4); - - /* No Measurement_Parameters Description */ - bitvec_set_bit(&bv, 0); - /* No GPRS_Real Time Difference Description */ - bitvec_set_bit(&bv, 0); - /* No GPRS_BSIC Description */ - bitvec_set_bit(&bv, 0); - /* No GPRS_REPORT PRIORITY Description */ - bitvec_set_bit(&bv, 0); - /* No GPRS_MEASUREMENT_Parameters Description */ - bitvec_set_bit(&bv, 0); - /* No NC Measurement Parameters */ - bitvec_set_bit(&bv, 0); - /* No extension (length) */ - bitvec_set_bit(&bv, 0); - - rc = SI2Q_MAX_LEN - (bv.cur_bit + 3); - if (rc > 0 && bts->si_common.uarfcn_length - bts->u_offset > 0) { - rc = append_uarfcns(&bv, bts, rc); - if (rc < 0) { - LOGP(DRR, LOGL_ERROR, "SI2quater [%u/%u]: failed to append %zu UARFCNs due to range encoding " - "failure: %s\n", - bts->si2q_index, bts->si2q_count, bts->si_common.uarfcn_length, strerror(-rc)); - return rc; - } - } else /* No 3G Neighbour Cell Description */ - bitvec_set_bit(&bv, 0); - - /* No 3G Measurement Parameters Description */ - bitvec_set_bit(&bv, 0); - /* No GPRS_3G_MEASUREMENT Parameters Descr. */ - bitvec_set_bit(&bv, 0); - - rc = SI2Q_MAX_LEN - bv.cur_bit; - if (rc > 0 && si2q_earfcn_count(&bts->si_common.si2quater_neigh_list) - bts->e_offset > 0) - append_earfcn(&bv, bts, rc); - else /* No Additions in Rel-5: */ - bitvec_set_bit(&bv, L); - - bitvec_spare_padding(&bv, (bv.data_len * 8) - 1); - return bv.data_len; -} - -/* Append selection parameters to bitvec */ -static void append_selection_params(struct bitvec *bv, - const struct gsm48_si_selection_params *sp) -{ - if (sp->present) { - bitvec_set_bit(bv, H); - bitvec_set_bit(bv, sp->cbq); - bitvec_set_uint(bv, sp->cell_resel_off, 6); - bitvec_set_uint(bv, sp->temp_offs, 3); - bitvec_set_uint(bv, sp->penalty_time, 5); - } else - bitvec_set_bit(bv, L); -} - -/* Append power offset to bitvec */ -static void append_power_offset(struct bitvec *bv, - const struct gsm48_si_power_offset *po) -{ - if (po->present) { - bitvec_set_bit(bv, H); - bitvec_set_uint(bv, po->power_offset, 2); - } else - bitvec_set_bit(bv, L); -} - -/* Append GPRS indicator to bitvec */ -static void append_gprs_ind(struct bitvec *bv, - const struct gsm48_si3_gprs_ind *gi) -{ - if (gi->present) { - bitvec_set_bit(bv, H); - bitvec_set_uint(bv, gi->ra_colour, 3); - /* 0 == SI13 in BCCH Norm, 1 == SI13 sent on BCCH Ext */ - bitvec_set_bit(bv, gi->si13_position); - } else - bitvec_set_bit(bv, L); -} - - -/* Generate SI3 Rest Octests (Chapter 10.5.2.34 / Table 10.4.72) */ -int rest_octets_si3(uint8_t *data, const struct gsm48_si_ro_info *si3) -{ - struct bitvec bv; - - memset(&bv, 0, sizeof(bv)); - bv.data = data; - bv.data_len = 4; - - /* Optional Selection Parameters */ - append_selection_params(&bv, &si3->selection_params); - - /* Optional Power Offset */ - append_power_offset(&bv, &si3->power_offset); - - /* Do we have a SI2ter on the BCCH? */ - if (si3->si2ter_indicator) - bitvec_set_bit(&bv, H); - else - bitvec_set_bit(&bv, L); - - /* Early Classmark Sending Control */ - if (si3->early_cm_ctrl) - bitvec_set_bit(&bv, H); - else - bitvec_set_bit(&bv, L); - - /* Do we have a SI Type 9 on the BCCH? */ - if (si3->scheduling.present) { - bitvec_set_bit(&bv, H); - bitvec_set_uint(&bv, si3->scheduling.where, 3); - } else - bitvec_set_bit(&bv, L); - - /* GPRS Indicator */ - append_gprs_ind(&bv, &si3->gprs_ind); - - /* 3G Early Classmark Sending Restriction controlled by - * early_cm_ctrl above */ - bitvec_set_bit(&bv, H); - - if (si3->si2quater_indicator) { - bitvec_set_bit(&bv, H); /* indicator struct present */ - bitvec_set_uint(&bv, 0, 1); /* message is sent on BCCH Norm */ - } - - bitvec_spare_padding(&bv, (bv.data_len*8)-1); - return bv.data_len; -} - -static int append_lsa_params(struct bitvec *bv, - const struct gsm48_lsa_params *lsa_params) -{ - /* FIXME */ - return -1; -} - -/* Generate SI4 Rest Octets (Chapter 10.5.2.35) */ -int rest_octets_si4(uint8_t *data, const struct gsm48_si_ro_info *si4, int len) -{ - struct bitvec bv; - - memset(&bv, 0, sizeof(bv)); - bv.data = data; - bv.data_len = len; - - /* SI4 Rest Octets O */ - append_selection_params(&bv, &si4->selection_params); - append_power_offset(&bv, &si4->power_offset); - append_gprs_ind(&bv, &si4->gprs_ind); - - if (0 /* FIXME */) { - /* H and SI4 Rest Octets S */ - bitvec_set_bit(&bv, H); - - /* LSA Parameters */ - if (si4->lsa_params.present) { - bitvec_set_bit(&bv, H); - append_lsa_params(&bv, &si4->lsa_params); - } else - bitvec_set_bit(&bv, L); - - /* Cell Identity */ - if (1) { - bitvec_set_bit(&bv, H); - bitvec_set_uint(&bv, si4->cell_id, 16); - } else - bitvec_set_bit(&bv, L); - - /* LSA ID Information */ - if (0) { - bitvec_set_bit(&bv, H); - /* FIXME */ - } else - bitvec_set_bit(&bv, L); - } else { - /* L and break indicator */ - bitvec_set_bit(&bv, L); - bitvec_set_bit(&bv, si4->break_ind ? H : L); - } - - return bv.data_len; -} - - -/* GSM 04.18 ETSI TS 101 503 V8.27.0 (2006-05) - -<SI6 rest octets> ::= -{L | H <PCH and NCH info>} -{L | H <VBS/VGCS options : bit(2)>} -{ < DTM_support : bit == L > I < DTM_support : bit == H > -< RAC : bit (8) > -< MAX_LAPDm : bit (3) > } -< Band indicator > -{ L | H < GPRS_MS_TXPWR_MAX_CCH : bit (5) > } -<implicit spare >; -*/ -int rest_octets_si6(uint8_t *data, bool is1800_net) -{ - struct bitvec bv; - - memset(&bv, 0, sizeof(bv)); - bv.data = data; - bv.data_len = 1; - - /* no PCH/NCH info */ - bitvec_set_bit(&bv, L); - /* no VBS/VGCS options */ - bitvec_set_bit(&bv, L); - /* no DTM_support */ - bitvec_set_bit(&bv, L); - /* band indicator */ - if (is1800_net) - bitvec_set_bit(&bv, L); - else - bitvec_set_bit(&bv, H); - /* no GPRS_MS_TXPWR_MAX_CCH */ - bitvec_set_bit(&bv, L); - - bitvec_spare_padding(&bv, (bv.data_len * 8) - 1); - return bv.data_len; -} - -/* GPRS Mobile Allocation as per TS 04.60 Chapter 12.10a: - < GPRS Mobile Allocation IE > ::= - < HSN : bit (6) > - { 0 | 1 < RFL number list : < RFL number list struct > > } - { 0 < MA_LENGTH : bit (6) > - < MA_BITMAP: bit (val(MA_LENGTH) + 1) > - | 1 { 0 | 1 <ARFCN index list : < ARFCN index list struct > > } } ; - - < RFL number list struct > :: = - < RFL_NUMBER : bit (4) > - { 0 | 1 < RFL number list struct > } ; - < ARFCN index list struct > ::= - < ARFCN_INDEX : bit(6) > - { 0 | 1 < ARFCN index list struct > } ; - */ -static int append_gprs_mobile_alloc(struct bitvec *bv) -{ - /* Hopping Sequence Number */ - bitvec_set_uint(bv, 0, 6); - - if (0) { - /* We want to use a RFL number list */ - bitvec_set_bit(bv, 1); - /* FIXME: RFL number list */ - } else - bitvec_set_bit(bv, 0); - - if (0) { - /* We want to use a MA_BITMAP */ - bitvec_set_bit(bv, 0); - /* FIXME: MA_LENGTH, MA_BITMAP, ... */ - } else { - bitvec_set_bit(bv, 1); - if (0) { - /* We want to provide an ARFCN index list */ - bitvec_set_bit(bv, 1); - /* FIXME */ - } else - bitvec_set_bit(bv, 0); - } - return 0; -} - -static int encode_t3192(unsigned int t3192) -{ - /* See also 3GPP TS 44.060 - Table 12.24.2: GPRS Cell Options information element details */ - if (t3192 == 0) - return 3; - else if (t3192 <= 80) - return 4; - else if (t3192 <= 120) - return 5; - else if (t3192 <= 160) - return 6; - else if (t3192 <= 200) - return 7; - else if (t3192 <= 500) - return 0; - else if (t3192 <= 1000) - return 1; - else if (t3192 <= 1500) - return 2; - else - return -EINVAL; -} - -static int encode_drx_timer(unsigned int drx) -{ - if (drx == 0) - return 0; - else if (drx == 1) - return 1; - else if (drx == 2) - return 2; - else if (drx <= 4) - return 3; - else if (drx <= 8) - return 4; - else if (drx <= 16) - return 5; - else if (drx <= 32) - return 6; - else if (drx <= 64) - return 7; - else - return -EINVAL; -} - -/* GPRS Cell Options as per TS 04.60 Chapter 12.24 - < GPRS Cell Options IE > ::= - < NMO : bit(2) > - < T3168 : bit(3) > - < T3192 : bit(3) > - < DRX_TIMER_MAX: bit(3) > - < ACCESS_BURST_TYPE: bit > - < CONTROL_ACK_TYPE : bit > - < BS_CV_MAX: bit(4) > - { 0 | 1 < PAN_DEC : bit(3) > - < PAN_INC : bit(3) > - < PAN_MAX : bit(3) > - { 0 | 1 < Extension Length : bit(6) > - < bit (val(Extension Length) + 1 - & { < Extension Information > ! { bit ** = <no string> } } ; - < Extension Information > ::= - { 0 | 1 < EGPRS_PACKET_CHANNEL_REQUEST : bit > - < BEP_PERIOD : bit(4) > } - < PFC_FEATURE_MODE : bit > - < DTM_SUPPORT : bit > - <BSS_PAGING_COORDINATION: bit > - <spare bit > ** ; - */ -static int append_gprs_cell_opt(struct bitvec *bv, - const struct gprs_cell_options *gco) -{ - int t3192, drx_timer_max; - - t3192 = encode_t3192(gco->t3192); - if (t3192 < 0) - return t3192; - - drx_timer_max = encode_drx_timer(gco->drx_timer_max); - if (drx_timer_max < 0) - return drx_timer_max; - - bitvec_set_uint(bv, gco->nmo, 2); - - /* See also 3GPP TS 44.060 - Table 12.24.2: GPRS Cell Options information element details */ - bitvec_set_uint(bv, gco->t3168 / 500 - 1, 3); - - bitvec_set_uint(bv, t3192, 3); - bitvec_set_uint(bv, drx_timer_max, 3); - /* ACCESS_BURST_TYPE: Hard-code 8bit */ - bitvec_set_bit(bv, 0); - /* CONTROL_ACK_TYPE: */ - bitvec_set_bit(bv, gco->ctrl_ack_type_use_block); - bitvec_set_uint(bv, gco->bs_cv_max, 4); - - if (0) { - /* hard-code no PAN_{DEC,INC,MAX} */ - bitvec_set_bit(bv, 0); - } else { - /* copied from ip.access BSC protocol trace */ - bitvec_set_bit(bv, 1); - bitvec_set_uint(bv, 1, 3); /* DEC */ - bitvec_set_uint(bv, 1, 3); /* INC */ - bitvec_set_uint(bv, 15, 3); /* MAX */ - } - - if (!gco->ext_info_present) { - /* no extension information */ - bitvec_set_bit(bv, 0); - } else { - /* extension information */ - bitvec_set_bit(bv, 1); - if (!gco->ext_info.egprs_supported) { - /* 6bit length of extension */ - bitvec_set_uint(bv, (1 + 3)-1, 6); - /* EGPRS supported in the cell */ - bitvec_set_bit(bv, 0); - } else { - /* 6bit length of extension */ - bitvec_set_uint(bv, (1 + 5 + 3)-1, 6); - /* EGPRS supported in the cell */ - bitvec_set_bit(bv, 1); - - /* 1bit EGPRS PACKET CHANNEL REQUEST */ - if (gco->supports_egprs_11bit_rach == 0) { - bitvec_set_bit(bv, - gco->ext_info.use_egprs_p_ch_req); - } else { - bitvec_set_bit(bv, 0); - } - - /* 4bit BEP PERIOD */ - bitvec_set_uint(bv, gco->ext_info.bep_period, 4); - } - bitvec_set_bit(bv, gco->ext_info.pfc_supported); - bitvec_set_bit(bv, gco->ext_info.dtm_supported); - bitvec_set_bit(bv, gco->ext_info.bss_paging_coordination); - } - - return 0; -} - -static void append_gprs_pwr_ctrl_pars(struct bitvec *bv, - const struct gprs_power_ctrl_pars *pcp) -{ - bitvec_set_uint(bv, pcp->alpha, 4); - bitvec_set_uint(bv, pcp->t_avg_w, 5); - bitvec_set_uint(bv, pcp->t_avg_t, 5); - bitvec_set_uint(bv, pcp->pc_meas_chan, 1); - bitvec_set_uint(bv, pcp->n_avg_i, 4); -} - -/* Generate SI13 Rest Octests (04.08 Chapter 10.5.2.37b) */ -int rest_octets_si13(uint8_t *data, const struct gsm48_si13_info *si13) -{ - struct bitvec bv; - - memset(&bv, 0, sizeof(bv)); - bv.data = data; - bv.data_len = 20; - - if (0) { - /* No rest octets */ - bitvec_set_bit(&bv, L); - } else { - bitvec_set_bit(&bv, H); - bitvec_set_uint(&bv, si13->bcch_change_mark, 3); - bitvec_set_uint(&bv, si13->si_change_field, 4); - if (1) { - bitvec_set_bit(&bv, 0); - } else { - bitvec_set_bit(&bv, 1); - bitvec_set_uint(&bv, si13->bcch_change_mark, 2); - append_gprs_mobile_alloc(&bv); - } - if (!si13->pbcch_present) { - /* PBCCH not present in cell */ - bitvec_set_bit(&bv, 0); - bitvec_set_uint(&bv, si13->no_pbcch.rac, 8); - bitvec_set_bit(&bv, si13->no_pbcch.spgc_ccch_sup); - bitvec_set_uint(&bv, si13->no_pbcch.prio_acc_thr, 3); - bitvec_set_uint(&bv, si13->no_pbcch.net_ctrl_ord, 2); - append_gprs_cell_opt(&bv, &si13->cell_opts); - append_gprs_pwr_ctrl_pars(&bv, &si13->pwr_ctrl_pars); - } else { - /* PBCCH present in cell */ - bitvec_set_bit(&bv, 1); - bitvec_set_uint(&bv, si13->pbcch.psi1_rep_per, 4); - /* PBCCH Descripiton */ - bitvec_set_uint(&bv, si13->pbcch.pb, 4); - bitvec_set_uint(&bv, si13->pbcch.tsc, 3); - bitvec_set_uint(&bv, si13->pbcch.tn, 3); - switch (si13->pbcch.carrier_type) { - case PBCCH_BCCH: - bitvec_set_bit(&bv, 0); - bitvec_set_bit(&bv, 0); - break; - case PBCCH_ARFCN: - bitvec_set_bit(&bv, 0); - bitvec_set_bit(&bv, 1); - bitvec_set_uint(&bv, si13->pbcch.arfcn, 10); - break; - case PBCCH_MAIO: - bitvec_set_bit(&bv, 1); - bitvec_set_uint(&bv, si13->pbcch.maio, 6); - break; - } - } - /* 3GPP TS 44.018 Release 6 / 10.5.2.37b */ - bitvec_set_bit(&bv, H); /* added Release 99 */ - /* claim our SGSN is compatible with Release 99, as EDGE and EGPRS - * was only added in this Release */ - bitvec_set_bit(&bv, 1); - } - bitvec_spare_padding(&bv, (bv.data_len*8)-1); - return bv.data_len; -} diff --git a/openbsc/src/libbsc/system_information.c b/openbsc/src/libbsc/system_information.c deleted file mode 100644 index dcabbbdd1..000000000 --- a/openbsc/src/libbsc/system_information.c +++ /dev/null @@ -1,1169 +0,0 @@ -/* GSM 04.08 System Information (SI) encoding and decoding - * 3GPP TS 04.08 version 7.21.0 Release 1998 / ETSI TS 100 940 V7.21.0 */ - -/* (C) 2008-2010 by Harald Welte <laforge@gnumonks.org> - * (C) 2012 Holger Hans Peter Freyther - * - * 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 <http://www.gnu.org/licenses/>. - * - */ - -#include <errno.h> -#include <string.h> -#include <stdio.h> -#include <netinet/in.h> -#include <stdbool.h> - -#include <osmocom/core/bitvec.h> -#include <osmocom/core/utils.h> -#include <osmocom/gsm/sysinfo.h> - -#include <openbsc/debug.h> -#include <openbsc/gsm_04_08.h> -#include <openbsc/gsm_data.h> -#include <openbsc/abis_rsl.h> -#include <openbsc/rest_octets.h> -#include <openbsc/arfcn_range_encode.h> - -/* - * DCS1800 and PCS1900 have overlapping ARFCNs. We would need to set the - * ARFCN_PCS flag on the 1900 ARFCNs but this would increase cell_alloc - * and other arrays to make sure (ARFCN_PCS + 1024)/8 ARFCNs fit into the - * array. DCS1800 and PCS1900 can not be used at the same time so conserve - * memory and do the below. - */ -static int band_compatible(const struct gsm_bts *bts, int arfcn) -{ - enum gsm_band band = gsm_arfcn2band(arfcn); - - /* normal case */ - if (band == bts->band) - return 1; - /* deal with ARFCN_PCS not set */ - if (band == GSM_BAND_1800 && bts->band == GSM_BAND_1900) - return 1; - - return 0; -} - -static int is_dcs_net(const struct gsm_bts *bts) -{ - if (bts->band == GSM_BAND_850) - return 0; - if (bts->band == GSM_BAND_1900) - return 0; - return 1; -} - -/* Return p(n) for given NR_OF_TDD_CELLS - see Table 9.1.54.1a, 3GPP TS 44.018 */ -unsigned range1024_p(unsigned n) -{ - switch (n) { - case 0: return 0; - case 1: return 10; - case 2: return 19; - case 3: return 28; - case 4: return 36; - case 5: return 44; - case 6: return 52; - case 7: return 60; - case 8: return 67; - case 9: return 74; - case 10: return 81; - case 11: return 88; - case 12: return 95; - case 13: return 102; - case 14: return 109; - case 15: return 116; - case 16: return 122; - default: return 0; - } -} - -/* Return q(m) for given NR_OF_TDD_CELLS - see Table 9.1.54.1b, 3GPP TS 44.018 */ -unsigned range512_q(unsigned m) -{ - switch (m) { - case 0: return 0; - case 1: return 9; - case 2: return 17; - case 3: return 25; - case 4: return 32; - case 5: return 39; - case 6: return 46; - case 7: return 53; - case 8: return 59; - case 9: return 65; - case 10: return 71; - case 11: return 77; - case 12: return 83; - case 13: return 89; - case 14: return 95; - case 15: return 101; - case 16: return 106; - case 17: return 111; - case 18: return 116; - case 19: return 121; - case 20: return 126; - default: return 0; - } -} - -size_t si2q_earfcn_count(const struct osmo_earfcn_si2q *e) -{ - unsigned i, ret = 0; - - if (!e) - return 0; - - for (i = 0; i < e->length; i++) - if (e->arfcn[i] != OSMO_EARFCN_INVALID) - ret++; - - return ret; -} - -/* generate SI2quater messages, return rest octets length of last generated message or negative error code */ -static int make_si2quaters(struct gsm_bts *bts, bool counting) -{ - int rc; - bool memory_exceeded = true; - struct gsm48_system_information_type_2quater *si2q; - - for (bts->si2q_index = 0; bts->si2q_index < SI2Q_MAX_NUM; bts->si2q_index++) { - si2q = GSM_BTS_SI2Q(bts, bts->si2q_index); - if (counting) { /* that's legitimate if we're called for counting purpose: */ - if (bts->si2q_count < bts->si2q_index) - bts->si2q_count = bts->si2q_index; - } else { - memset(si2q, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); - - si2q->header.l2_plen = GSM48_LEN2PLEN(22); - si2q->header.rr_protocol_discriminator = GSM48_PDISC_RR; - si2q->header.skip_indicator = 0; - si2q->header.system_information = GSM48_MT_RR_SYSINFO_2quater; - } - - rc = rest_octets_si2quater(si2q->rest_octets, bts); - if (rc < 0) - return rc; - - if (bts->u_offset >= bts->si_common.uarfcn_length && - bts->e_offset >= si2q_earfcn_count(&bts->si_common.si2quater_neigh_list)) { - memory_exceeded = false; - break; - } - } - - if (memory_exceeded) - return -ENOMEM; - - return rc; -} - -/* we generate SI2q rest octets twice to get proper estimation but it's one time cost anyway */ -uint8_t si2q_num(struct gsm_bts *bts) -{ - int rc = make_si2quaters(bts, true); - uint8_t num = bts->si2q_index + 1; /* number of SI2quater messages */ - - /* N. B: si2q_num() should NEVER be called during actualSI2q rest octets generation - we're not re-entrant because of the following code: */ - bts->u_offset = 0; - bts->e_offset = 0; - - if (rc < 0) - return 0xFF; /* return impossible index as an indicator of error in generating SI2quater */ - - return num; -} - -/* 3GPP TS 44.018, Table 9.1.54.1 - prepend diversity bit to scrambling code */ -static inline uint16_t encode_fdd(uint16_t scramble, bool diversity) -{ - if (diversity) - return scramble | (1 << 9); - return scramble; -} - -int bts_earfcn_add(struct gsm_bts *bts, uint16_t earfcn, uint8_t thresh_hi, uint8_t thresh_lo, uint8_t prio, - uint8_t qrx, uint8_t meas_bw) -{ - struct osmo_earfcn_si2q *e = &bts->si_common.si2quater_neigh_list; - int r = osmo_earfcn_add(e, earfcn, (meas_bw < EARFCN_MEAS_BW_INVALID) ? meas_bw : OSMO_EARFCN_MEAS_INVALID); - - if (r < 0) - return r; - - if (e->thresh_hi && thresh_hi != e->thresh_hi) - r = 1; - - e->thresh_hi = thresh_hi; - - if (thresh_lo != EARFCN_THRESH_LOW_INVALID) { - if (e->thresh_lo_valid && e->thresh_lo != thresh_lo) - r = EARFCN_THRESH_LOW_INVALID; - e->thresh_lo = thresh_lo; - e->thresh_lo_valid = true; - } - - if (qrx != EARFCN_QRXLV_INVALID) { - if (e->qrxlm_valid && e->qrxlm != qrx) - r = EARFCN_QRXLV_INVALID + 1; - e->qrxlm = qrx; - e->qrxlm_valid = true; - } - - if (prio != EARFCN_PRIO_INVALID) { - if (e->prio_valid && e->prio != prio) - r = EARFCN_PRIO_INVALID; - e->prio = prio; - e->prio_valid = true; - } - - return r; -} - -int bts_uarfcn_del(struct gsm_bts *bts, uint16_t arfcn, uint16_t scramble) -{ - uint16_t sc0 = encode_fdd(scramble, false), sc1 = encode_fdd(scramble, true), - *ual = bts->si_common.data.uarfcn_list, - *scl = bts->si_common.data.scramble_list; - size_t len = bts->si_common.uarfcn_length, i; - for (i = 0; i < len; i++) { - if (arfcn == ual[i] && (sc0 == scl[i] || sc1 == scl[i])) { - /* we rely on the assumption that (uarfcn, scramble) - tuple is unique in the lists */ - if (i != len - 1) { /* move the tail if necessary */ - memmove(ual + i, ual + i + 1, 2 * (len - i + 1)); - memmove(scl + i, scl + i + 1, 2 * (len - i + 1)); - } - break; - } - } - - if (i == len) - return -EINVAL; - - bts->si_common.uarfcn_length--; - return 0; -} - -int bts_uarfcn_add(struct gsm_bts *bts, uint16_t arfcn, uint16_t scramble, bool diversity) -{ - size_t len = bts->si_common.uarfcn_length, i, k = 0; - uint16_t scr, chk, - *ual = bts->si_common.data.uarfcn_list, - *scl = bts->si_common.data.scramble_list, - scramble1 = encode_fdd(scramble, true), - scramble0 = encode_fdd(scramble, false); - - scr = diversity ? scramble1 : scramble0; - chk = diversity ? scramble0 : scramble1; - - if (len == MAX_EARFCN_LIST) - return -ENOMEM; - - for (i = 0; i < len; i++) /* find the position of arfcn if any */ - if (arfcn == ual[i]) - break; - - for (k = 0; i < len; i++) { - if (arfcn == ual[i] && (scr == scl[i] || chk == scl[i])) - return -EADDRINUSE; - if (scr > scl[i]) - k = i + 1; - } - /* we keep lists sorted by scramble code: - insert into appropriate position and move the tail */ - if (len - k) { - memmove(ual + k + 1, ual + k, (len - k) * 2); - memmove(scl + k + 1, scl + k, (len - k) * 2); - } - - ual[k] = arfcn; - scl[k] = scr; - bts->si_common.uarfcn_length++; - - if (si2q_num(bts) <= SI2Q_MAX_NUM) { - bts->si2q_count = si2q_num(bts) - 1; - return 0; - } - - bts_uarfcn_del(bts, arfcn, scramble); - return -ENOSPC; -} - -static inline int use_arfcn(const struct gsm_bts *bts, const bool bis, const bool ter, - const bool pgsm, const int arfcn) -{ - if (bts->force_combined_si) - return !bis && !ter; - if (!bis && !ter && band_compatible(bts, arfcn)) - return 1; - /* Correct but somehow broken with either the nanoBTS or the iPhone5 */ - if (bis && pgsm && band_compatible(bts, arfcn) && (arfcn < 1 || arfcn > 124)) - return 1; - if (ter && !band_compatible(bts, arfcn)) - return 1; - return 0; -} - -/* Frequency Lists as per TS 04.08 10.5.2.13 */ - -/* 10.5.2.13.2: Bit map 0 format */ -static int freq_list_bm0_set_arfcn(uint8_t *chan_list, unsigned int arfcn) -{ - unsigned int byte, bit; - - if (arfcn > 124 || arfcn < 1) { - LOGP(DRR, LOGL_ERROR, "Bitmap 0 only supports ARFCN 1...124\n"); - return -EINVAL; - } - - /* the bitmask is from 1..124, not from 0..123 */ - arfcn--; - - byte = arfcn / 8; - bit = arfcn % 8; - - chan_list[GSM48_CELL_CHAN_DESC_SIZE-1-byte] |= (1 << bit); - - return 0; -} - -/* 10.5.2.13.7: Variable bit map format */ -static int freq_list_bmrel_set_arfcn(uint8_t *chan_list, unsigned int arfcn) -{ - unsigned int byte, bit; - unsigned int min_arfcn; - unsigned int bitno; - - min_arfcn = (chan_list[0] & 1) << 9; - min_arfcn |= chan_list[1] << 1; - min_arfcn |= (chan_list[2] >> 7) & 1; - - /* The lower end of our bitmaks is always implicitly included */ - if (arfcn == min_arfcn) - return 0; - - if (((arfcn - min_arfcn) & 1023) > 111) { - LOGP(DRR, LOGL_ERROR, "arfcn(%u) > min(%u) + 111\n", arfcn, min_arfcn); - return -EINVAL; - } - - bitno = (arfcn - min_arfcn) & 1023; - byte = bitno / 8; - bit = bitno % 8; - - chan_list[2 + byte] |= 1 << (7 - bit); - - return 0; -} - -/* generate a variable bitmap */ -static inline int enc_freq_lst_var_bitmap(uint8_t *chan_list, - struct bitvec *bv, const struct gsm_bts *bts, - bool bis, bool ter, int min, bool pgsm) -{ - int i; - - /* set it to 'Variable bitmap format' */ - chan_list[0] = 0x8e; - - chan_list[0] |= (min >> 9) & 1; - chan_list[1] = (min >> 1); - chan_list[2] = (min & 1) << 7; - - for (i = 0; i < bv->data_len*8; i++) { - /* see notes in bitvec2freq_list */ - if (bitvec_get_bit_pos(bv, i) - && ((!bis && !ter && band_compatible(bts,i)) - || (bis && pgsm && band_compatible(bts,i) && (i < 1 || i > 124)) - || (ter && !band_compatible(bts, i)))) { - int rc = freq_list_bmrel_set_arfcn(chan_list, i); - if (rc < 0) - return rc; - } - } - - return 0; -} - -int range_encode(enum gsm48_range r, int *arfcns, int arfcns_used, int *w, - int f0, uint8_t *chan_list) -{ - /* - * Manipulate the ARFCN list according to the rules in J4 depending - * on the selected range. - */ - int rc, f0_included; - - range_enc_filter_arfcns(arfcns, arfcns_used, f0, &f0_included); - - rc = range_enc_arfcns(r, arfcns, arfcns_used, w, 0); - if (rc < 0) - return rc; - - /* Select the range and the amount of bits needed */ - switch (r) { - case ARFCN_RANGE_128: - return range_enc_range128(chan_list, f0, w); - case ARFCN_RANGE_256: - return range_enc_range256(chan_list, f0, w); - case ARFCN_RANGE_512: - return range_enc_range512(chan_list, f0, w); - case ARFCN_RANGE_1024: - return range_enc_range1024(chan_list, f0, f0_included, w); - default: - return -ERANGE; - }; - - return f0_included; -} - -/* generate a frequency list with the range 512 format */ -static inline int enc_freq_lst_range(uint8_t *chan_list, - struct bitvec *bv, const struct gsm_bts *bts, - bool bis, bool ter, bool pgsm) -{ - int arfcns[RANGE_ENC_MAX_ARFCNS]; - int w[RANGE_ENC_MAX_ARFCNS]; - int arfcns_used = 0; - int i, range, f0; - - /* - * Select ARFCNs according to the rules in bitvec2freq_list - */ - for (i = 0; i < bv->data_len * 8; ++i) { - /* More ARFCNs than the maximum */ - if (arfcns_used > ARRAY_SIZE(arfcns)) - return -1; - /* Check if we can select it? */ - if (bitvec_get_bit_pos(bv, i) && use_arfcn(bts, bis, ter, pgsm, i)) - arfcns[arfcns_used++] = i; - } - - /* - * Check if the given list of ARFCNs can be encoded. - */ - range = range_enc_determine_range(arfcns, arfcns_used, &f0); - if (range == ARFCN_RANGE_INVALID) - return -2; - - memset(w, 0, sizeof(w)); - return range_encode(range, arfcns, arfcns_used, w, f0, chan_list); -} - -/* generate a cell channel list as per Section 10.5.2.1b of 04.08 */ -static int bitvec2freq_list(uint8_t *chan_list, struct bitvec *bv, - const struct gsm_bts *bts, bool bis, bool ter) -{ - int i, rc, min = -1, max = -1, arfcns = 0; - bool pgsm = false; - memset(chan_list, 0, 16); - - if (bts->band == GSM_BAND_900 - && bts->c0->arfcn >= 1 && bts->c0->arfcn <= 124) - pgsm = true; - /* P-GSM-only handsets only support 'bit map 0 format' */ - if (!bis && !ter && pgsm) { - chan_list[0] = 0; - - for (i = 0; i < bv->data_len*8; i++) { - if (i >= 1 && i <= 124 - && bitvec_get_bit_pos(bv, i)) { - rc = freq_list_bm0_set_arfcn(chan_list, i); - if (rc < 0) - return rc; - } - } - return 0; - } - - for (i = 0; i < bv->data_len*8; i++) { - /* in case of SI2 or SI5 allow all neighbours in same band - * in case of SI*bis, allow neighbours in same band ouside pgsm - * in case of SI*ter, allow neighbours in different bands - */ - if (!bitvec_get_bit_pos(bv, i)) - continue; - if (!use_arfcn(bts, bis, ter, pgsm, i)) - continue; - /* count the arfcns we want to carry */ - arfcns += 1; - - /* 955..1023 < 0..885 */ - if (min < 0) - min = i; - if (i >= 955 && min < 955) - min = i; - if (i >= 955 && min >= 955 && i < min) - min = i; - if (i < 955 && min < 955 && i < min) - min = i; - if (max < 0) - max = i; - if (i < 955 && max >= 955) - max = i; - if (i >= 955 && max >= 955 && i > max) - max = i; - if (i < 955 && max < 955 && i > max) - max = i; - } - - if (max == -1) { - /* Empty set, use 'bit map 0 format' */ - chan_list[0] = 0; - return 0; - } - - /* Now find the best encoding */ - if (((max - min) & 1023) <= 111) - return enc_freq_lst_var_bitmap(chan_list, bv, bts, bis, - ter, min, pgsm); - - /* Attempt to do the range encoding */ - rc = enc_freq_lst_range(chan_list, bv, bts, bis, ter, pgsm); - if (rc >= 0) - return 0; - - LOGP(DRR, LOGL_ERROR, "min_arfcn=%u, max_arfcn=%u, arfcns=%d " - "can not generate ARFCN list", min, max, arfcns); - return -EINVAL; -} - -/* generate a cell channel list as per Section 10.5.2.1b of 04.08 */ -/* static*/ int generate_cell_chan_list(uint8_t *chan_list, struct gsm_bts *bts) -{ - struct gsm_bts_trx *trx; - struct bitvec *bv = &bts->si_common.cell_alloc; - - /* Zero-initialize the bit-vector */ - memset(bv->data, 0, bv->data_len); - - /* first we generate a bitvec of all TRX ARFCN's in our BTS */ - llist_for_each_entry(trx, &bts->trx_list, list) { - unsigned int i, j; - /* Always add the TRX's ARFCN */ - bitvec_set_bit_pos(bv, trx->arfcn, 1); - for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { - struct gsm_bts_trx_ts *ts = &trx->ts[i]; - /* Add any ARFCNs present in hopping channels */ - for (j = 0; j < 1024; j++) { - if (bitvec_get_bit_pos(&ts->hopping.arfcns, j)) - bitvec_set_bit_pos(bv, j, 1); - } - } - } - - /* then we generate a GSM 04.08 frequency list from the bitvec */ - return bitvec2freq_list(chan_list, bv, bts, false, false); -} - -/* generate a cell channel list as per Section 10.5.2.1b of 04.08 */ -static int generate_bcch_chan_list(uint8_t *chan_list, struct gsm_bts *bts, - bool si5, bool bis, bool ter) -{ - struct gsm_bts *cur_bts; - struct bitvec *bv; - - if (si5 && bts->neigh_list_manual_mode == NL_MODE_MANUAL_SI5SEP) - bv = &bts->si_common.si5_neigh_list; - else - bv = &bts->si_common.neigh_list; - - /* Generate list of neighbor cells if we are in automatic mode */ - if (bts->neigh_list_manual_mode == NL_MODE_AUTOMATIC) { - /* Zero-initialize the bit-vector */ - memset(bv->data, 0, bv->data_len); - - /* first we generate a bitvec of the BCCH ARFCN's in our BSC */ - llist_for_each_entry(cur_bts, &bts->network->bts_list, list) { - if (cur_bts == bts) - continue; - bitvec_set_bit_pos(bv, cur_bts->c0->arfcn, 1); - } - } - - /* then we generate a GSM 04.08 frequency list from the bitvec */ - return bitvec2freq_list(chan_list, bv, bts, bis, ter); -} - -static int list_arfcn(uint8_t *chan_list, uint8_t mask, char *text) -{ - int n = 0, i; - struct gsm_sysinfo_freq freq[1024]; - - memset(freq, 0, sizeof(freq)); - gsm48_decode_freq_list(freq, chan_list, 16, 0xce, 1); - for (i = 0; i < 1024; i++) { - if (freq[i].mask) { - if (!n) - LOGP(DRR, LOGL_INFO, "%s", text); - LOGPC(DRR, LOGL_INFO, " %d", i); - n++; - } - } - if (n) - LOGPC(DRR, LOGL_INFO, "\n"); - - return n; -} - -static int generate_si1(enum osmo_sysinfo_type t, struct gsm_bts *bts) -{ - int rc; - struct gsm48_system_information_type_1 *si1 = (struct gsm48_system_information_type_1 *) GSM_BTS_SI(bts, t); - - memset(si1, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); - - si1->header.l2_plen = GSM48_LEN2PLEN(21); - si1->header.rr_protocol_discriminator = GSM48_PDISC_RR; - si1->header.skip_indicator = 0; - si1->header.system_information = GSM48_MT_RR_SYSINFO_1; - - rc = generate_cell_chan_list(si1->cell_channel_description, bts); - if (rc < 0) - return rc; - list_arfcn(si1->cell_channel_description, 0xce, "Serving cell:"); - - si1->rach_control = bts->si_common.rach_control; - - /* - * SI1 Rest Octets (10.5.2.32), contains NCH position and band - * indicator but that is not in the 04.08. - */ - rc = rest_octets_si1(si1->rest_octets, NULL, is_dcs_net(bts)); - - return sizeof(*si1) + rc; -} - -static int generate_si2(enum osmo_sysinfo_type t, struct gsm_bts *bts) -{ - int rc; - struct gsm48_system_information_type_2 *si2 = (struct gsm48_system_information_type_2 *) GSM_BTS_SI(bts, t); - - memset(si2, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); - - si2->header.l2_plen = GSM48_LEN2PLEN(22); - si2->header.rr_protocol_discriminator = GSM48_PDISC_RR; - si2->header.skip_indicator = 0; - si2->header.system_information = GSM48_MT_RR_SYSINFO_2; - - rc = generate_bcch_chan_list(si2->bcch_frequency_list, bts, false, false, false); - if (rc < 0) - return rc; - list_arfcn(si2->bcch_frequency_list, 0xce, - "SI2 Neighbour cells in same band:"); - - si2->ncc_permitted = bts->si_common.ncc_permitted; - si2->rach_control = bts->si_common.rach_control; - - return sizeof(*si2); -} - -static int generate_si2bis(enum osmo_sysinfo_type t, struct gsm_bts *bts) -{ - int rc; - struct gsm48_system_information_type_2bis *si2b = - (struct gsm48_system_information_type_2bis *) GSM_BTS_SI(bts, t); - int n; - - memset(si2b, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); - - si2b->header.l2_plen = GSM48_LEN2PLEN(22); - si2b->header.rr_protocol_discriminator = GSM48_PDISC_RR; - si2b->header.skip_indicator = 0; - si2b->header.system_information = GSM48_MT_RR_SYSINFO_2bis; - - rc = generate_bcch_chan_list(si2b->bcch_frequency_list, bts, false, true, false); - if (rc < 0) - return rc; - n = list_arfcn(si2b->bcch_frequency_list, 0xce, - "Neighbour cells in same band, but outside P-GSM:"); - if (n) { - /* indicate in SI2 and SI2bis: there is an extension */ - struct gsm48_system_information_type_2 *si2 = - (struct gsm48_system_information_type_2 *) GSM_BTS_SI(bts, SYSINFO_TYPE_2); - si2->bcch_frequency_list[0] |= 0x20; - si2b->bcch_frequency_list[0] |= 0x20; - } else - bts->si_valid &= ~(1 << SYSINFO_TYPE_2bis); - - si2b->rach_control = bts->si_common.rach_control; - - return sizeof(*si2b); -} - -static int generate_si2ter(enum osmo_sysinfo_type t, struct gsm_bts *bts) -{ - int rc; - struct gsm48_system_information_type_2ter *si2t = - (struct gsm48_system_information_type_2ter *) GSM_BTS_SI(bts, t); - int n; - - memset(si2t, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); - - si2t->header.l2_plen = GSM48_LEN2PLEN(22); - si2t->header.rr_protocol_discriminator = GSM48_PDISC_RR; - si2t->header.skip_indicator = 0; - si2t->header.system_information = GSM48_MT_RR_SYSINFO_2ter; - - rc = generate_bcch_chan_list(si2t->ext_bcch_frequency_list, bts, false, false, true); - if (rc < 0) - return rc; - n = list_arfcn(si2t->ext_bcch_frequency_list, 0x8e, - "Neighbour cells in different band:"); - if (!n) - bts->si_valid &= ~(1 << SYSINFO_TYPE_2ter); - - return sizeof(*si2t); -} - -/* SI2quater messages are optional - we only generate them when neighbor UARFCNs or EARFCNs are configured */ -static inline bool si2quater_not_needed(struct gsm_bts *bts) -{ - unsigned i = MAX_EARFCN_LIST; - - if (bts->si_common.si2quater_neigh_list.arfcn) - for (i = 0; i < MAX_EARFCN_LIST; i++) - if (bts->si_common.si2quater_neigh_list.arfcn[i] != OSMO_EARFCN_INVALID) - break; - - if (!bts->si_common.uarfcn_length && i == MAX_EARFCN_LIST) { - bts->si_valid &= ~(1 << SYSINFO_TYPE_2quater); /* mark SI2q as invalid if no (E|U)ARFCNs are present */ - return true; - } - - return false; -} - -static int generate_si2quater(enum osmo_sysinfo_type t, struct gsm_bts *bts) -{ - int rc; - struct gsm48_system_information_type_2quater *si2q; - - if (si2quater_not_needed(bts)) /* generate rest_octets for SI2q only when necessary */ - return GSM_MACBLOCK_LEN; - - bts->u_offset = 0; - bts->e_offset = 0; - bts->si2q_index = 0; - bts->si2q_count = si2q_num(bts) - 1; - - rc = make_si2quaters(bts, false); - if (rc < 0) - return rc; - - OSMO_ASSERT(bts->si2q_count == bts->si2q_index); - OSMO_ASSERT(bts->si2q_count <= SI2Q_MAX_NUM); - - return sizeof(*si2q) + rc; -} - -static struct gsm48_si_ro_info si_info = { - .selection_params = { - .present = 0, - }, - .power_offset = { - .present = 0, - }, - .si2ter_indicator = 0, - .early_cm_ctrl = 1, - .scheduling = { - .present = 0, - }, - .gprs_ind = { - .si13_position = 0, - .ra_colour = 0, - .present = 1, - }, - .si2quater_indicator = 0, - .lsa_params = { - .present = 0, - }, - .cell_id = 0, /* FIXME: doesn't the bts have this? */ - .break_ind = 0, -}; - -static int generate_si3(enum osmo_sysinfo_type t, struct gsm_bts *bts) -{ - int rc; - struct gsm48_system_information_type_3 *si3 = (struct gsm48_system_information_type_3 *) GSM_BTS_SI(bts, t); - - memset(si3, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); - - si3->header.l2_plen = GSM48_LEN2PLEN(18); - si3->header.rr_protocol_discriminator = GSM48_PDISC_RR; - si3->header.skip_indicator = 0; - si3->header.system_information = GSM48_MT_RR_SYSINFO_3; - - si3->cell_identity = htons(bts->cell_identity); - gsm48_generate_lai(&si3->lai, bts->network->country_code, - bts->network->network_code, - bts->location_area_code); - si3->control_channel_desc = bts->si_common.chan_desc; - si3->cell_options = bts->si_common.cell_options; - si3->cell_sel_par = bts->si_common.cell_sel_par; - si3->rach_control = bts->si_common.rach_control; - - /* allow/disallow DTXu */ - gsm48_set_dtx(&si3->cell_options, bts->dtxu, bts->dtxu, true); - - if (GSM_BTS_HAS_SI(bts, SYSINFO_TYPE_2ter)) { - LOGP(DRR, LOGL_INFO, "SI 2ter is included.\n"); - si_info.si2ter_indicator = 1; - } else { - si_info.si2ter_indicator = 0; - } - if (GSM_BTS_HAS_SI(bts, SYSINFO_TYPE_2quater)) { - LOGP(DRR, LOGL_INFO, "SI 2quater is included, based on %zu EARFCNs and %zu UARFCNs.\n", - si2q_earfcn_count(&bts->si_common.si2quater_neigh_list), bts->si_common.uarfcn_length); - si_info.si2quater_indicator = 1; - } else { - si_info.si2quater_indicator = 0; - } - si_info.early_cm_ctrl = bts->early_classmark_allowed; - - /* SI3 Rest Octets (10.5.2.34), containing - CBQ, CELL_RESELECT_OFFSET, TEMPORARY_OFFSET, PENALTY_TIME - Power Offset, 2ter Indicator, Early Classmark Sending, - Scheduling if and WHERE, GPRS Indicator, SI13 position */ - rc = rest_octets_si3(si3->rest_octets, &si_info); - - return sizeof(*si3) + rc; -} - -static int generate_si4(enum osmo_sysinfo_type t, struct gsm_bts *bts) -{ - int rc; - struct gsm48_system_information_type_4 *si4 = (struct gsm48_system_information_type_4 *) GSM_BTS_SI(bts, t); - struct gsm_lchan *cbch_lchan; - uint8_t *restoct = si4->data; - - /* length of all IEs present except SI4 rest octets and l2_plen */ - int l2_plen = sizeof(*si4) - 1; - - memset(si4, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); - - si4->header.rr_protocol_discriminator = GSM48_PDISC_RR; - si4->header.skip_indicator = 0; - si4->header.system_information = GSM48_MT_RR_SYSINFO_4; - - gsm48_generate_lai(&si4->lai, bts->network->country_code, - bts->network->network_code, - bts->location_area_code); - si4->cell_sel_par = bts->si_common.cell_sel_par; - si4->rach_control = bts->si_common.rach_control; - - /* Optional: CBCH Channel Description + CBCH Mobile Allocation */ - cbch_lchan = gsm_bts_get_cbch(bts); - if (cbch_lchan) { - struct gsm48_chan_desc cd; - gsm48_lchan2chan_desc(&cd, cbch_lchan); - tv_fixed_put(si4->data, GSM48_IE_CBCH_CHAN_DESC, 3, - (uint8_t *) &cd); - l2_plen += 3 + 1; - restoct += 3 + 1; - /* we don't use hopping and thus don't need a CBCH MA */ - } - - si4->header.l2_plen = GSM48_LEN2PLEN(l2_plen); - - /* SI4 Rest Octets (10.5.2.35), containing - Optional Power offset, GPRS Indicator, - Cell Identity, LSA ID, Selection Parameter */ - rc = rest_octets_si4(restoct, &si_info, (uint8_t *)GSM_BTS_SI(bts, t) + GSM_MACBLOCK_LEN - restoct); - - return l2_plen + 1 + rc; -} - -static int generate_si5(enum osmo_sysinfo_type t, struct gsm_bts *bts) -{ - struct gsm48_system_information_type_5 *si5; - uint8_t *output = GSM_BTS_SI(bts, t); - int rc, l2_plen = 18; - - memset(output, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); - - /* ip.access nanoBTS needs l2_plen!! */ - switch (bts->type) { - case GSM_BTS_TYPE_NANOBTS: - case GSM_BTS_TYPE_OSMOBTS: - *output++ = GSM48_LEN2PLEN(l2_plen); - l2_plen++; - break; - default: - break; - } - - si5 = (struct gsm48_system_information_type_5 *) GSM_BTS_SI(bts, t); - - /* l2 pseudo length, not part of msg: 18 */ - si5->rr_protocol_discriminator = GSM48_PDISC_RR; - si5->skip_indicator = 0; - si5->system_information = GSM48_MT_RR_SYSINFO_5; - rc = generate_bcch_chan_list(si5->bcch_frequency_list, bts, true, false, false); - if (rc < 0) - return rc; - list_arfcn(si5->bcch_frequency_list, 0xce, - "SI5 Neighbour cells in same band:"); - - /* 04.08 9.1.37: L2 Pseudo Length of 18 */ - return l2_plen; -} - -static int generate_si5bis(enum osmo_sysinfo_type t, struct gsm_bts *bts) -{ - struct gsm48_system_information_type_5bis *si5b; - uint8_t *output = GSM_BTS_SI(bts, t); - int rc, l2_plen = 18; - int n; - - memset(output, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); - - /* ip.access nanoBTS needs l2_plen!! */ - switch (bts->type) { - case GSM_BTS_TYPE_NANOBTS: - case GSM_BTS_TYPE_OSMOBTS: - *output++ = GSM48_LEN2PLEN(l2_plen); - l2_plen++; - break; - default: - break; - } - - si5b = (struct gsm48_system_information_type_5bis *) GSM_BTS_SI(bts, t); - - /* l2 pseudo length, not part of msg: 18 */ - si5b->rr_protocol_discriminator = GSM48_PDISC_RR; - si5b->skip_indicator = 0; - si5b->system_information = GSM48_MT_RR_SYSINFO_5bis; - rc = generate_bcch_chan_list(si5b->bcch_frequency_list, bts, true, true, false); - if (rc < 0) - return rc; - n = list_arfcn(si5b->bcch_frequency_list, 0xce, - "Neighbour cells in same band, but outside P-GSM:"); - if (n) { - /* indicate in SI5 and SI5bis: there is an extension */ - struct gsm48_system_information_type_5 *si5 = - (struct gsm48_system_information_type_5 *) GSM_BTS_SI(bts, SYSINFO_TYPE_5); - si5->bcch_frequency_list[0] |= 0x20; - si5b->bcch_frequency_list[0] |= 0x20; - } else - bts->si_valid &= ~(1 << SYSINFO_TYPE_5bis); - - /* 04.08 9.1.37: L2 Pseudo Length of 18 */ - return l2_plen; -} - -static int generate_si5ter(enum osmo_sysinfo_type t, struct gsm_bts *bts) -{ - struct gsm48_system_information_type_5ter *si5t; - uint8_t *output = GSM_BTS_SI(bts, t); - int rc, l2_plen = 18; - int n; - - memset(output, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); - - /* ip.access nanoBTS needs l2_plen!! */ - switch (bts->type) { - case GSM_BTS_TYPE_NANOBTS: - case GSM_BTS_TYPE_OSMOBTS: - *output++ = GSM48_LEN2PLEN(l2_plen); - l2_plen++; - break; - default: - break; - } - - si5t = (struct gsm48_system_information_type_5ter *) GSM_BTS_SI(bts, t); - - /* l2 pseudo length, not part of msg: 18 */ - si5t->rr_protocol_discriminator = GSM48_PDISC_RR; - si5t->skip_indicator = 0; - si5t->system_information = GSM48_MT_RR_SYSINFO_5ter; - rc = generate_bcch_chan_list(si5t->bcch_frequency_list, bts, true, false, true); - if (rc < 0) - return rc; - n = list_arfcn(si5t->bcch_frequency_list, 0x8e, - "Neighbour cells in different band:"); - if (!n) - bts->si_valid &= ~(1 << SYSINFO_TYPE_5ter); - - /* 04.08 9.1.37: L2 Pseudo Length of 18 */ - return l2_plen; -} - -static int generate_si6(enum osmo_sysinfo_type t, struct gsm_bts *bts) -{ - struct gsm48_system_information_type_6 *si6; - uint8_t *output = GSM_BTS_SI(bts, t); - int l2_plen = 11; - int rc; - - memset(output, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); - - /* ip.access nanoBTS needs l2_plen!! */ - switch (bts->type) { - case GSM_BTS_TYPE_NANOBTS: - case GSM_BTS_TYPE_OSMOBTS: - *output++ = GSM48_LEN2PLEN(l2_plen); - l2_plen++; - break; - default: - break; - } - - si6 = (struct gsm48_system_information_type_6 *) GSM_BTS_SI(bts, t); - - /* l2 pseudo length, not part of msg: 11 */ - si6->rr_protocol_discriminator = GSM48_PDISC_RR; - si6->skip_indicator = 0; - si6->system_information = GSM48_MT_RR_SYSINFO_6; - si6->cell_identity = htons(bts->cell_identity); - gsm48_generate_lai(&si6->lai, bts->network->country_code, - bts->network->network_code, - bts->location_area_code); - si6->cell_options = bts->si_common.cell_options; - si6->ncc_permitted = bts->si_common.ncc_permitted; - /* allow/disallow DTXu */ - gsm48_set_dtx(&si6->cell_options, bts->dtxu, bts->dtxu, false); - - /* SI6 Rest Octets: 10.5.2.35a: PCH / NCH info, VBS/VGCS options */ - rc = rest_octets_si6(si6->rest_octets, is_dcs_net(bts)); - - return l2_plen + rc; -} - -static struct gsm48_si13_info si13_default = { - .cell_opts = { - .nmo = GPRS_NMO_II, - .t3168 = 2000, - .t3192 = 1500, - .drx_timer_max = 3, - .bs_cv_max = 15, - .ctrl_ack_type_use_block = true, - .ext_info_present = 0, - .supports_egprs_11bit_rach = 0, - .ext_info = { - /* The values below are just guesses ! */ - .egprs_supported = 0, - .use_egprs_p_ch_req = 1, - .bep_period = 5, - .pfc_supported = 0, - .dtm_supported = 0, - .bss_paging_coordination = 0, - }, - }, - .pwr_ctrl_pars = { - .alpha = 0, /* a = 0.0 */ - .t_avg_w = 16, - .t_avg_t = 16, - .pc_meas_chan = 0, /* downling measured on CCCH */ - .n_avg_i = 8, - }, - .bcch_change_mark = 1, - .si_change_field = 0, - .pbcch_present = 0, - { - .no_pbcch = { - .rac = 0, /* needs to be patched */ - .spgc_ccch_sup = 0, - .net_ctrl_ord = 0, - .prio_acc_thr = 6, - }, - }, -}; - -static int generate_si13(enum osmo_sysinfo_type t, struct gsm_bts *bts) -{ - struct gsm48_system_information_type_13 *si13 = - (struct gsm48_system_information_type_13 *) GSM_BTS_SI(bts, t); - int ret; - - memset(si13, GSM_MACBLOCK_PADDING, GSM_MACBLOCK_LEN); - - si13->header.rr_protocol_discriminator = GSM48_PDISC_RR; - si13->header.skip_indicator = 0; - si13->header.system_information = GSM48_MT_RR_SYSINFO_13; - - si13_default.no_pbcch.rac = bts->gprs.rac; - si13_default.no_pbcch.net_ctrl_ord = bts->gprs.net_ctrl_ord; - - si13_default.cell_opts.ctrl_ack_type_use_block = - bts->gprs.ctrl_ack_type_use_block; - - /* Information about the other SIs */ - si13_default.bcch_change_mark = bts->bcch_change_mark; - si13_default.cell_opts.supports_egprs_11bit_rach = - bts->gprs.supports_egprs_11bit_rach; - - ret = rest_octets_si13(si13->rest_octets, &si13_default); - if (ret < 0) - return ret; - - /* length is coded in bit 2 an up */ - si13->header.l2_plen = 0x01; - - return sizeof (*si13) + ret; -} - -typedef int (*gen_si_fn_t)(enum osmo_sysinfo_type t, struct gsm_bts *bts); - -static const gen_si_fn_t gen_si_fn[_MAX_SYSINFO_TYPE] = { - [SYSINFO_TYPE_1] = &generate_si1, - [SYSINFO_TYPE_2] = &generate_si2, - [SYSINFO_TYPE_2bis] = &generate_si2bis, - [SYSINFO_TYPE_2ter] = &generate_si2ter, - [SYSINFO_TYPE_2quater] = &generate_si2quater, - [SYSINFO_TYPE_3] = &generate_si3, - [SYSINFO_TYPE_4] = &generate_si4, - [SYSINFO_TYPE_5] = &generate_si5, - [SYSINFO_TYPE_5bis] = &generate_si5bis, - [SYSINFO_TYPE_5ter] = &generate_si5ter, - [SYSINFO_TYPE_6] = &generate_si6, - [SYSINFO_TYPE_13] = &generate_si13, -}; - -int gsm_generate_si(struct gsm_bts *bts, enum osmo_sysinfo_type si_type) -{ - gen_si_fn_t gen_si; - - switch (bts->gprs.mode) { - case BTS_GPRS_EGPRS: - si13_default.cell_opts.ext_info_present = 1; - si13_default.cell_opts.ext_info.egprs_supported = 1; - /* fallthrough */ - case BTS_GPRS_GPRS: - si_info.gprs_ind.present = 1; - break; - case BTS_GPRS_NONE: - si_info.gprs_ind.present = 0; - break; - } - - memcpy(&si_info.selection_params, - &bts->si_common.cell_ro_sel_par, - sizeof(struct gsm48_si_selection_params)); - - gen_si = gen_si_fn[si_type]; - if (!gen_si) - return -EINVAL; - - return gen_si(si_type, bts); -} |