aboutsummaryrefslogtreecommitdiffstats
path: root/src/common/oml.c
diff options
context:
space:
mode:
authorHarald Welte <laforge@gnumonks.org>2011-03-04 14:05:20 +0100
committerHarald Welte <laforge@gnumonks.org>2011-03-04 14:05:20 +0100
commit4b45ae1636990a15934f77f37033cf8ed5804b57 (patch)
tree892d687a5c689fdb7813a3f94dd9a0926d80b892 /src/common/oml.c
Import all C and Header files from jolly/bts branch of osmocom-bb.git
The BTS code shall reside in a separate git repository, thus I'm importing the C and H files here.
Diffstat (limited to 'src/common/oml.c')
-rw-r--r--src/common/oml.c549
1 files changed, 549 insertions, 0 deletions
diff --git a/src/common/oml.c b/src/common/oml.c
new file mode 100644
index 00000000..62be30b5
--- /dev/null
+++ b/src/common/oml.c
@@ -0,0 +1,549 @@
+/*
+ * (C) 2011 by Andreas Eversberg <jolly@eversberg.eu>
+ *
+ * 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.
+ *
+ */
+
+/*
+ * Operation and Maintainance Messages
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <arpa/inet.h>
+
+#include <osmocore/protocol/gsm_12_21.h>
+#include <osmocom/bb/common/logging.h>
+#include <osmocom/bb/common/osmocom_data.h>
+#include <osmocom/bb/bts/support.h>
+#include <osmocom/bb/bts/abis.h>
+#include <osmocom/bb/bts/rtp.h>
+#include <osmocom/bb/bts/bts.h>
+#include <osmocom/bb/bts/rsl.h>
+#include <osmocom/bb/bts/oml.h>
+
+/*
+ * support
+ */
+
+/* FIXME: move this to osmocore */
+const struct tlv_definition nm_att_tlvdef = {
+ .def = {
+ [NM_ATT_ABIS_CHANNEL] = { TLV_TYPE_FIXED, 3 },
+ [NM_ATT_ADD_INFO] = { TLV_TYPE_TL16V },
+ [NM_ATT_ADD_TEXT] = { TLV_TYPE_TL16V },
+ [NM_ATT_ADM_STATE] = { TLV_TYPE_TV },
+ [NM_ATT_ARFCN_LIST]= { TLV_TYPE_TL16V },
+ [NM_ATT_AUTON_REPORT] = { TLV_TYPE_TV },
+ [NM_ATT_AVAIL_STATUS] = { TLV_TYPE_TL16V },
+ [NM_ATT_BCCH_ARFCN] = { TLV_TYPE_FIXED, 2 },
+ [NM_ATT_BSIC] = { TLV_TYPE_TV },
+ [NM_ATT_BTS_AIR_TIMER] = { TLV_TYPE_TV },
+ [NM_ATT_CCCH_L_I_P] = { TLV_TYPE_TV },
+ [NM_ATT_CCCH_L_T] = { TLV_TYPE_TV },
+ [NM_ATT_CHAN_COMB] = { TLV_TYPE_TV },
+ [NM_ATT_CONN_FAIL_CRIT] = { TLV_TYPE_TL16V },
+ [NM_ATT_DEST] = { TLV_TYPE_TL16V },
+ [NM_ATT_EVENT_TYPE] = { TLV_TYPE_TV },
+ [NM_ATT_FILE_DATA] = { TLV_TYPE_TL16V },
+ [NM_ATT_FILE_ID] = { TLV_TYPE_TL16V },
+ [NM_ATT_FILE_VERSION] = { TLV_TYPE_TL16V },
+ [NM_ATT_GSM_TIME] = { TLV_TYPE_FIXED, 2 },
+ [NM_ATT_HSN] = { TLV_TYPE_TV },
+ [NM_ATT_HW_CONFIG] = { TLV_TYPE_TL16V },
+ [NM_ATT_HW_DESC] = { TLV_TYPE_TL16V },
+ [NM_ATT_INTAVE_PARAM] = { TLV_TYPE_TV },
+ [NM_ATT_INTERF_BOUND] = { TLV_TYPE_FIXED, 6 },
+ [NM_ATT_LIST_REQ_ATTR] = { TLV_TYPE_TL16V },
+ [NM_ATT_MAIO] = { TLV_TYPE_TV },
+ [NM_ATT_MANUF_STATE] = { TLV_TYPE_TV },
+ [NM_ATT_MANUF_THRESH] = { TLV_TYPE_TL16V },
+ [NM_ATT_MANUF_ID] = { TLV_TYPE_TL16V },
+ [NM_ATT_MAX_TA] = { TLV_TYPE_TV },
+ [NM_ATT_MDROP_LINK] = { TLV_TYPE_FIXED, 2 },
+ [NM_ATT_MDROP_NEXT] = { TLV_TYPE_FIXED, 2 },
+ [NM_ATT_NACK_CAUSES] = { TLV_TYPE_TV },
+ [NM_ATT_NY1] = { TLV_TYPE_TV },
+ [NM_ATT_OPER_STATE] = { TLV_TYPE_TV },
+ [NM_ATT_OVERL_PERIOD] = { TLV_TYPE_TL16V },
+ [NM_ATT_PHYS_CONF] = { TLV_TYPE_TL16V },
+ [NM_ATT_POWER_CLASS] = { TLV_TYPE_TV },
+ [NM_ATT_POWER_THRESH] = { TLV_TYPE_FIXED, 3 },
+ [NM_ATT_PROB_CAUSE] = { TLV_TYPE_FIXED, 3 },
+ [NM_ATT_RACH_B_THRESH] = { TLV_TYPE_TV },
+ [NM_ATT_LDAVG_SLOTS] = { TLV_TYPE_FIXED, 2 },
+ [NM_ATT_RAD_SUBC] = { TLV_TYPE_TV },
+ [NM_ATT_RF_MAXPOWR_R] = { TLV_TYPE_TV },
+ [NM_ATT_SITE_INPUTS] = { TLV_TYPE_TL16V },
+ [NM_ATT_SITE_OUTPUTS] = { TLV_TYPE_TL16V },
+ [NM_ATT_SOURCE] = { TLV_TYPE_TL16V },
+ [NM_ATT_SPEC_PROB] = { TLV_TYPE_TV },
+ [NM_ATT_START_TIME] = { TLV_TYPE_FIXED, 2 },
+ [NM_ATT_T200] = { TLV_TYPE_FIXED, 7 },
+ [NM_ATT_TEI] = { TLV_TYPE_TV },
+ [NM_ATT_TEST_DUR] = { TLV_TYPE_FIXED, 2 },
+ [NM_ATT_TEST_NO] = { TLV_TYPE_TV },
+ [NM_ATT_TEST_REPORT] = { TLV_TYPE_TL16V },
+ [NM_ATT_VSWR_THRESH] = { TLV_TYPE_FIXED, 2 },
+ [NM_ATT_WINDOW_SIZE] = { TLV_TYPE_TV },
+ [NM_ATT_TSC] = { TLV_TYPE_TV },
+ [NM_ATT_SW_CONFIG] = { TLV_TYPE_TL16V },
+ [NM_ATT_SEVERITY] = { TLV_TYPE_TV },
+ [NM_ATT_GET_ARI] = { TLV_TYPE_TL16V },
+ [NM_ATT_HW_CONF_CHG] = { TLV_TYPE_TL16V },
+ [NM_ATT_OUTST_ALARM] = { TLV_TYPE_TV },
+ [NM_ATT_MEAS_RES] = { TLV_TYPE_TL16V },
+ },
+};
+
+struct osmobts_trx *get_trx_by_nr(struct osmocom_bts *bts, uint8_t trx_nr)
+{
+ int max = sizeof(bts->trx) / sizeof(bts->trx[0]);
+ struct osmobts_trx *trx;
+
+ if (trx_nr >= max) {
+ LOGP(DOML, LOGL_NOTICE, "Indicated TRX #%d is out of range. (max #%d)\n", trx_nr, max - 1);
+ return NULL;
+ }
+
+ trx = bts->trx[trx_nr];
+ if (!trx) {
+ LOGP(DOML, LOGL_NOTICE, "Indicated TRX #%d does not exist.\n", trx_nr);
+ return NULL;
+ }
+
+ return trx;
+}
+
+struct osmobts_slot *get_slot_by_nr(struct osmobts_trx *trx, uint8_t slot_nr)
+{
+ struct osmobts_slot *slot;
+
+ if (slot_nr >= 8) {
+ LOGP(DOML, LOGL_NOTICE, "Indicated Slot #%d is out of range. (max #8)\n", slot_nr);
+ return NULL;
+ }
+
+ slot = &trx->slot[slot_nr];
+
+ return slot;
+}
+
+static struct msgb *fom_msgb_alloc(void)
+{
+ struct msgb *nmsg;
+
+ nmsg = abis_msgb_alloc(sizeof(struct abis_om_hdr));
+ if (!nmsg)
+ return NULL;
+ return nmsg;
+}
+
+static void fom_push_om(struct msgb *msg, uint8_t mdisc, uint8_t placement, uint8_t sequence)
+{
+ struct abis_om_hdr *noh;
+
+ msg->l3h = msg->data;
+ noh = (struct abis_om_hdr *) msgb_push(msg, sizeof(*noh));
+ noh->mdisc = mdisc;
+ noh->placement = placement;
+ noh->sequence = sequence;
+ noh->length = msgb_l3len(msg);
+}
+
+static int fom_ack_nack(struct ipabis_link *link, struct msgb *msg, uint8_t cause)
+{
+ struct abis_om_fom_hdr *foh = msgb_l3(msg);
+ uint8_t *ie;
+
+ /* alter message type */
+ if (cause) {
+ LOGP(DOML, LOGL_NOTICE, "Sending FOM NACK with cause %d.\n", cause);
+ foh->msg_type += 2; /* nack */
+ /* add cause */
+ ie = msgb_put(msg, 2);
+ ie[0] = NM_ATT_NACK_CAUSES;
+ ie[1] = cause;
+ } else {
+ LOGP(DOML, LOGL_NOTICE, "Sending FOM ACK.\n");
+ foh->msg_type++; /* ack */
+ }
+
+ return abis_tx(link, msg);
+}
+
+/*
+ * Formatted O&M messages
+ */
+
+/* 8.3.7 sending SW Activated Report */
+int oml_tx_sw_act_rep(struct ipabis_link *link, uint8_t obj_class, uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr)
+{
+ struct msgb *nmsg;
+ struct abis_om_fom_hdr *nofh;
+
+ LOGP(DOML, LOGL_INFO, "Sending SW Activated Report (%02x,%02x,%02x).\n", bts_nr, trx_nr, ts_nr);
+
+ nmsg = fom_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ nofh = (struct abis_om_fom_hdr *) msgb_put(nmsg, sizeof(*nofh));
+ nofh->msg_type = NM_MT_SW_ACTIVATED_REP;
+ nofh->obj_class = obj_class;
+ nofh->obj_inst.bts_nr = bts_nr;
+ nofh->obj_inst.trx_nr = trx_nr;
+ nofh->obj_inst.ts_nr = ts_nr;
+ fom_push_om(nmsg, ABIS_OM_MDISC_FOM, ABIS_OM_PLACEMENT_ONLY, 0);
+ abis_push_ipa(nmsg, IPA_PROTO_OML);
+
+ return abis_tx(link, nmsg);
+}
+
+/* 8.8.1 sending State Changed Event Report */
+int oml_tx_state_changed(struct ipabis_link *link, uint8_t op_state, uint8_t avail_status, uint8_t obj_class, uint8_t bts_nr, uint8_t trx_nr, uint8_t ts_nr)
+{
+ struct msgb *nmsg;
+ struct abis_om_fom_hdr *nofh;
+ uint8_t *ie;
+
+ LOGP(DOML, LOGL_INFO, "Sending state change (bts=%02x trx=%02x ts=%02x).\n", bts_nr, trx_nr, ts_nr);
+
+ nmsg = fom_msgb_alloc();
+ if (!nmsg)
+ return -ENOMEM;
+ nofh = (struct abis_om_fom_hdr *) msgb_put(nmsg, sizeof(*nofh));
+ nofh->msg_type = NM_MT_STATECHG_EVENT_REP;
+ nofh->obj_class = obj_class;
+ nofh->obj_inst.bts_nr = bts_nr;
+ nofh->obj_inst.trx_nr = trx_nr;
+ nofh->obj_inst.ts_nr = ts_nr;
+ /* 9.4.38 Operational State */
+ ie = msgb_put(nmsg, 2);
+ ie[0] = NM_ATT_OPER_STATE;
+ ie[1] = op_state;
+ /* 9.4.7 Availability Status */
+ ie = msgb_put(nmsg, 4);
+ ie[0] = NM_ATT_AVAIL_STATUS;
+ ie[1] = 0;
+ ie[2] = 1;
+ ie[3] = avail_status;
+ fom_push_om(nmsg, ABIS_OM_MDISC_FOM, ABIS_OM_PLACEMENT_ONLY, 0);
+ abis_push_ipa(nmsg, IPA_PROTO_OML);
+
+ return abis_tx(link, nmsg);
+}
+
+/* 8.6.1 Set BTS Attributes is received */
+int oml_rx_set_bts_attr(struct osmocom_bts *bts, struct msgb *msg)
+{
+ struct abis_om_fom_hdr *foh = msgb_l3(msg);
+ struct tlv_parsed tp;
+ struct bts_support *sup = &bts_support;
+
+ LOGP(DOML, LOGL_INFO, "BSC is setting BTS attributes:\n");
+
+ tlv_parse(&tp, &nm_att_tlvdef, foh->data, msgb_l3len(msg) - sizeof(*foh), 0, 0);
+ /* 9.4.31 Maximum Timing Advance */
+ if (TLVP_PRESENT(&tp, NM_ATT_MAX_TA)) {
+ uint16_t *fn = (uint16_t *) TLVP_VAL(&tp, NM_ATT_START_TIME);
+ bts->max_ta = ntohs(*fn);
+ LOGP(DOML, LOGL_INFO, " Maximum TA = %d\n", bts->max_ta);
+ }
+ /* 9.4.8 BCCH ARFCN */
+ if (TLVP_PRESENT(&tp, NM_ATT_BCCH_ARFCN)) {
+ uint16_t *value = (uint16_t *) TLVP_VAL(&tp, NM_ATT_BCCH_ARFCN);
+ uint16_t arfcn = ntohs(*value);
+ LOGP(DOML, LOGL_INFO, " ARFCN = %d", bts->bcch_arfcn);
+ if (arfcn > 1023 || !(sup->freq_map[arfcn >> 3] & (1 << (arfcn & 0x7)))) {
+ LOGP(DOML, LOGL_NOTICE, "Given ARFCN %d is not supported.\n", arfcn);
+ return fom_ack_nack(&bts->link, msg, NM_NACK_FREQ_NOTAVAIL);
+ }
+ bts->bcch_arfcn = arfcn;
+ }
+ /* 9.4.9 BSIC */
+ if (TLVP_PRESENT(&tp, NM_ATT_BSIC)) {
+ uint8_t *bsic = (uint8_t *) TLVP_VAL(&tp, NM_ATT_BSIC);
+ bts->bcc = *bsic & 0x7;
+ bts->ncc = (*bsic >> 3) & 0x7;
+ LOGP(DOML, LOGL_INFO, " BCC = %d\n", bts->bcc);
+ LOGP(DOML, LOGL_INFO, " NCC = %d\n", bts->ncc);
+ }
+ /* 9.4.52 Starting Time */
+ if (TLVP_PRESENT(&tp, NM_ATT_START_TIME)) {
+ uint16_t *fn = (uint16_t *) TLVP_VAL(&tp, NM_ATT_START_TIME);
+ bts->start_time = ntohs(*fn);
+ LOGP(DOML, LOGL_INFO, " Starting Time = %d\n", bts->start_time);
+ }
+
+ return fom_ack_nack(&bts->link, msg, 0);
+}
+
+/* 8.6.2 Set Radio Attributes is received */
+int oml_rx_set_radio_attr(struct osmocom_bts *bts, struct msgb *msg)
+{
+ struct abis_om_fom_hdr *foh = msgb_l3(msg);
+ struct tlv_parsed tp;
+ struct osmobts_trx *trx;
+ struct bts_support *sup = &bts_support;
+
+ trx = get_trx_by_nr(bts, foh->obj_inst.trx_nr);
+ if (!trx)
+ return fom_ack_nack(&bts->link, msg, NM_NACK_TRXNR_UNKN);
+
+ LOGP(DOML, LOGL_INFO, "BSC is setting radio attributes:\n");
+
+ tlv_parse(&tp, &nm_att_tlvdef, foh->data, msgb_l3len(msg) - sizeof(*foh), 0, 0);
+ /* 9.4.47 RF Max Power Reduction */
+ if (TLVP_PRESENT(&tp, NM_ATT_RF_MAXPOWR_R)) {
+ trx->rf_red = *TLVP_VAL(&tp, NM_ATT_RF_MAXPOWR_R);
+ LOGP(DOML, LOGL_INFO, " RF Max Power Reduction = %d\n", trx->rf_red);
+ } else
+ trx->rf_red = 0;
+ /* 9.4.5 ARFCN List */
+ if (TLVP_PRESENT(&tp, NM_ATT_ARFCN_LIST)) {
+ uint16_t *value = (uint16_t *) TLVP_VAL(&tp, NM_ATT_ARFCN_LIST);
+ uint16_t length = *(TLVP_VAL(&tp, NM_ATT_ARFCN_LIST) - 1);
+ uint16_t arfcn;
+ int max = (sizeof(trx->arfcn_list) / sizeof(trx->arfcn_list[0]));
+ int i;
+ if (length > max) {
+ LOGP(DOML, LOGL_NOTICE, "Too many ARFCN given. (max #%d)\n", max);
+ return fom_ack_nack(&bts->link, msg, NM_NACK_PARAM_RANGE);
+ }
+ for (i = 0; i < length; i++) {
+ arfcn = ntohs(*value++);
+ if (arfcn > 1023 || !(sup->freq_map[arfcn >> 3] & (1 << (arfcn & 0x7))))
+ return fom_ack_nack(&bts->link, msg, NM_NACK_FREQ_NOTAVAIL);
+ trx->arfcn_list[i] = arfcn;
+ LOGP(DOML, LOGL_INFO, " ARFCN list = %d\n", trx->arfcn_list[i]);
+ }
+ trx->arfcn_num = length;
+ } else
+ trx->arfcn_num = 0;
+
+ return fom_ack_nack(&bts->link, msg, 0);
+}
+
+/* 8.6.3 Set Channel Attributes is received */
+int oml_rx_set_chan_attr(struct osmocom_bts *bts, struct msgb *msg)
+{
+ struct abis_om_fom_hdr *foh = msgb_l3(msg);
+ struct tlv_parsed tp;
+ struct osmobts_trx *trx;
+ struct osmobts_slot *slot;
+ struct bts_support *sup = &bts_support;
+
+ trx = get_trx_by_nr(bts, foh->obj_inst.trx_nr);
+ if (!trx)
+ return fom_ack_nack(&bts->link, msg, NM_NACK_TRXNR_UNKN);
+ slot = get_slot_by_nr(trx, foh->obj_inst.ts_nr);
+ if (!slot)
+ return fom_ack_nack(&bts->link, msg, NM_NACK_OBJINST_UNKN);
+
+ LOGP(DOML, LOGL_INFO, "BSC is setting channel attributes:\n");
+
+ tlv_parse(&tp, &nm_att_tlvdef, foh->data, msgb_l3len(msg) - sizeof(*foh), 0, 0);
+ /* 9.4.13 Channel Combination */
+ if (TLVP_PRESENT(&tp, NM_ATT_CHAN_COMB)) {
+ uint8_t comb = *TLVP_VAL(&tp, NM_ATT_CHAN_COMB);
+ if (!sup->chan_comb[comb]) {
+ LOGP(DOML, LOGL_NOTICE, " channel combination %d (not supported).\n", comb);
+ return fom_ack_nack(&bts->link, msg, NM_NACK_SPEC_IMPL_NOTSUPP);
+ }
+ LOGP(DOML, LOGL_INFO, " channel combination = %s\n", bts_support_comb_name(comb));
+ bts_setup_slot(slot, comb);
+ slot->chan_comb = comb;
+ }
+ /* 9.4.21 HSN... */
+ if (TLVP_PRESENT(&tp, NM_ATT_HSN)) {
+ LOGP(DOML, LOGL_NOTICE, "Frequency hopping not supported.\n");
+ return fom_ack_nack(&bts->link, msg, NM_NACK_SPEC_IMPL_NOTSUPP);
+ }
+
+ return fom_ack_nack(&bts->link, msg, 0);
+}
+
+/* 8.9.2 Opstart is received */
+int oml_rx_opstart(struct osmocom_bts *bts, struct msgb *msg)
+{
+ struct abis_om_fom_hdr *foh = msgb_l3(msg);
+ struct osmobts_trx *trx;
+ struct osmobts_slot *slot;
+
+
+ /* site manager */
+ if (foh->obj_inst.bts_nr == 0xff) {
+ LOGP(DOML, LOGL_INFO, "BSC is sending Opstart. (Site Manager)\n");
+ oml_tx_state_changed(&bts->link, NM_OPSTATE_ENABLED, NM_AVSTATE_OK, NM_OC_SITE_MANAGER, foh->obj_inst.bts_nr, foh->obj_inst.trx_nr, foh->obj_inst.ts_nr);
+ return fom_ack_nack(&bts->link, msg, 0);
+ }
+
+#warning todo: change state
+ /* BTS */
+ if (foh->obj_inst.trx_nr == 0xff) {
+ LOGP(DOML, LOGL_INFO, "BSC is sending Opstart. (BTS)\n");
+ return fom_ack_nack(&bts->link, msg, 0);
+ }
+
+ /* TRX */
+ trx = get_trx_by_nr(bts, foh->obj_inst.trx_nr);
+ if (!trx)
+ return fom_ack_nack(&bts->link, msg, NM_NACK_TRXNR_UNKN);
+ if (foh->obj_inst.ts_nr == 0xff) {
+ LOGP(DOML, LOGL_INFO, "BSC is sending Opstart. (TRX %d)\n", trx->trx_nr);
+ if (trx->link.state == LINK_STATE_IDLE) {
+ int ret;
+
+ /* connecting TRX */
+ ret = abis_open(&trx->link, bts->link.ip);
+ if (ret <= 0) {
+ LOGP(DOML, LOGL_ERROR, "Failed to connect TRX.\n");
+ return fom_ack_nack(&bts->link, msg, NM_NACK_CANT_PERFORM);
+ }
+ }
+ return fom_ack_nack(&bts->link, msg, 0);
+ }
+
+ /* slot */
+ slot = get_slot_by_nr(trx, foh->obj_inst.ts_nr);
+ if (!slot)
+ return fom_ack_nack(&bts->link, msg, NM_NACK_OBJINST_UNKN);
+
+ LOGP(DOML, LOGL_INFO, "BSC is sending Opstart. (trx=%d ts=%d)\n", trx->trx_nr, slot->slot_nr);
+ oml_tx_state_changed(&bts->link, NM_OPSTATE_ENABLED, NM_AVSTATE_OK, NM_OC_CHANNEL, foh->obj_inst.bts_nr, foh->obj_inst.trx_nr, foh->obj_inst.ts_nr);
+ return fom_ack_nack(&bts->link, msg, 0);
+}
+
+static int down_fom(struct osmocom_bts *bts, struct msgb *msg)
+{
+ struct abis_om_fom_hdr *foh = msgb_l3(msg);
+ int ret;
+
+ if (msgb_l2len(msg) < sizeof(*foh)) {
+ LOGP(DOML, LOGL_NOTICE, "Formatted O&M message too short\n");
+ msgb_free(msg);
+ return -EIO;
+ }
+
+ if (foh->obj_inst.bts_nr != 0 && foh->obj_inst.bts_nr != 0xff) {
+ LOGP(DOML, LOGL_INFO, "Formatted O&M with BTS %d out of range.\n", foh->obj_inst.bts_nr);
+ return fom_ack_nack(&bts->link, msg, NM_NACK_BTSNR_UNKN);
+ }
+
+ switch (foh->msg_type) {
+ case NM_MT_SET_BTS_ATTR:
+ ret = oml_rx_set_bts_attr(bts, msg);
+ break;
+ case NM_MT_SET_RADIO_ATTR:
+ ret = oml_rx_set_radio_attr(bts, msg);
+ break;
+ case NM_MT_SET_CHAN_ATTR:
+ ret = oml_rx_set_chan_attr(bts, msg);
+ break;
+ case NM_MT_OPSTART:
+ ret = oml_rx_opstart(bts, msg);
+ break;
+ case NM_MT_CHG_ADM_STATE:
+ LOGP(DOML, LOGL_INFO, "BSC is changing ADM state.\n");
+ ret = fom_ack_nack(&bts->link, msg, 0);
+ break;
+ default:
+ LOGP(DOML, LOGL_INFO, "unknown Formatted O&M msg_type 0x%02x\n",
+ foh->msg_type);
+ ret = fom_ack_nack(&bts->link, msg, NM_NACK_MSGTYPE_INVAL);
+ }
+
+ return ret;
+}
+
+/*
+ * manufacturer related messages
+ */
+
+static int down_mom(struct osmocom_bts *bts, struct msgb *msg)
+{
+ struct abis_om_fom_hdr *foh = msgb_l3(msg);
+ int ret;
+
+ if (msgb_l2len(msg) < sizeof(*foh)) {
+ LOGP(DOML, LOGL_NOTICE, "Manufacturer O&M message too short\n");
+ msgb_free(msg);
+ return -EIO;
+ }
+
+ if (foh->obj_inst.bts_nr != 0 && foh->obj_inst.bts_nr != 0xff) {
+ LOGP(DOML, LOGL_INFO, "Manufacturer O&M with BTS %d out of range.\n", foh->obj_inst.bts_nr);
+ return fom_ack_nack(&bts->link, msg, NM_NACK_BTSNR_UNKN);
+ }
+
+ switch (foh->msg_type) {
+ default:
+ LOGP(DOML, LOGL_INFO, "Manufacturer Formatted O&M msg_type 0x%02x\n",
+ foh->msg_type);
+ ret = fom_ack_nack(&bts->link, msg, NM_NACK_MSGTYPE_INVAL);
+ }
+
+ return ret;
+}
+
+/*
+ * selecting messages
+ */
+
+int down_oml(struct osmocom_bts *bts, struct msgb *msg)
+{
+ struct abis_om_hdr *oh = msgb_l2(msg);
+ int ret = 0;
+
+ if (msgb_l2len(msg) < 1) {
+ LOGP(DOML, LOGL_NOTICE, "OML message too short\n");
+ msgb_free(msg);
+ return -EIO;
+ }
+ msg->l3h = (unsigned char *)oh + sizeof(*oh);
+
+ switch (oh->mdisc) {
+ case ABIS_OM_MDISC_FOM:
+ if (msgb_l2len(msg) < sizeof(*oh)) {
+ LOGP(DOML, LOGL_NOTICE, "Formatted O&M message too short\n");
+ msgb_free(msg);
+ ret = -EIO;
+ break;
+ }
+ ret = down_fom(bts, msg);
+ break;
+ case ABIS_OM_MDISC_MANUF:
+ if (msgb_l2len(msg) < sizeof(*oh)) {
+ LOGP(DOML, LOGL_NOTICE, "Manufacturer O&M message too short\n");
+ msgb_free(msg);
+ ret = -EIO;
+ break;
+ }
+ ret = down_mom(bts, msg);
+ break;
+ default:
+ LOGP(DOML, LOGL_NOTICE, "unknown OML msg_discr 0x%02x\n",
+ oh->mdisc);
+ msgb_free(msg);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+