diff options
Diffstat (limited to 'src/common/oml.c')
-rw-r--r-- | src/common/oml.c | 900 |
1 files changed, 666 insertions, 234 deletions
diff --git a/src/common/oml.c b/src/common/oml.c index 9af09395..92c7848d 100644 --- a/src/common/oml.c +++ b/src/common/oml.c @@ -1,12 +1,13 @@ -/* - * (C) 2011 by Andreas Eversberg <jolly@eversberg.eu> +/* GSM TS 12.21 O&M / OML, BTS side */ + +/* (C) 2011 by Andreas Eversberg <jolly@eversberg.eu> * (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 General Public License as published by - * the Free Software Foundation; either version 2 of the License, or + * 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, @@ -14,9 +15,8 @@ * 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. + * 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/>. * */ @@ -28,95 +28,294 @@ #include <sys/types.h> #include <arpa/inet.h> +#include <osmocom/core/talloc.h> #include <osmocom/gsm/protocol/gsm_12_21.h> #include <osmocom/gsm/abis_nm.h> + #include <osmo-bts/logging.h> -//#include <osmocom/bb/common/osmocom_data.h> -#include <osmo-bts/support.h> +#include <osmo-bts/gsm_data.h> #include <osmo-bts/abis.h> -#include <osmo-bts/rtp.h> -#include <osmo-bts/bts.h> -#include <osmo-bts/rsl.h> #include <osmo-bts/oml.h> +#include <osmo-bts/bts_model.h> + +/* FIXME: move this to libosmocore */ +static struct tlv_definition abis_nm_att_tlvdef_ipa = { + .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 }, + }, +}; + +/* ip.access nanoBTS specific commands */ +static const char ipaccess_magic[] = "com.ipaccess"; /* * support */ -struct osmobts_trx *get_trx_by_nr(struct osmocom_bts *bts, uint8_t trx_nr) +struct tlv_parsed *tlvp_copy(const struct tlv_parsed *tp_orig, void *ctx) { - int max = sizeof(bts->trx) / sizeof(bts->trx[0]); - struct osmobts_trx *trx; + struct tlv_parsed *tp_out; + unsigned int i; - if (trx_nr >= max) { - LOGP(DOML, LOGL_NOTICE, "Indicated TRX #%d is out of range. (max #%d)\n", trx_nr, max - 1); + tp_out = talloc_zero(ctx, struct tlv_parsed); + if (!tp_out) return NULL; + + /* if the original is NULL, return empty tlvp */ + if (!tp_orig) + return tp_out; + + for (i = 0; i < ARRAY_SIZE(tp_orig->lv); i++) { + unsigned int len = tp_orig->lv[i].len; + tp_out->lv[i].len = len; + if (len && tp_out->lv[i].val) { + tp_out->lv[i].val = talloc_zero_size(tp_out, len); + if (!tp_out->lv[i].val) { + talloc_free(tp_out); + return NULL; + } + memcpy((uint8_t *)tp_out->lv[i].val, tp_orig->lv[i].val, len); + } } - trx = bts->trx[trx_nr]; - if (!trx) { - LOGP(DOML, LOGL_NOTICE, "Indicated TRX #%d does not exist.\n", trx_nr); - return NULL; + return tp_out; +} + +/* merge all attributes of 'new' into 'out' */ +int tlvp_merge(struct tlv_parsed *out, const struct tlv_parsed *new) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(out->lv); i++) { + unsigned int len = new->lv[i].len; + if (len == 0 || new->lv[i].val == NULL) + continue; + if (out->lv[i].val) { + talloc_free((uint8_t *) out->lv[i].val); + out->lv[i].len = 0; + } + out->lv[i].val = talloc_zero_size(out, len); + if (!out->lv[i].val) + return -ENOMEM; + memcpy((uint8_t *) out->lv[i].val, new->lv[i].val, len); } + return 0; +} - return trx; +static int oml_tlv_parse(struct tlv_parsed *tp, const uint8_t *buf, int len) +{ + return tlv_parse(tp, &abis_nm_att_tlvdef_ipa, buf, len, 0, 0); } -struct osmobts_slot *get_slot_by_nr(struct osmobts_trx *trx, uint8_t slot_nr) +struct msgb *oml_msgb_alloc(void) { - struct osmobts_slot *slot; + return msgb_alloc_headroom(1024, 128, "OML"); +} - if (slot_nr >= 8) { - LOGP(DOML, LOGL_NOTICE, "Indicated Slot #%d is out of range. (max #8)\n", slot_nr); - return NULL; +int oml_send_msg(struct msgb *msg, int is_manuf) +{ + struct abis_om_hdr *omh; + + if (is_manuf) { + /* length byte, string + 0 termination */ + uint8_t *manuf = msgb_push(msg, 1 + sizeof(ipaccess_magic)); + manuf[0] = strlen(ipaccess_magic)+1; + memcpy(manuf+1, ipaccess_magic, strlen(ipaccess_magic)); } - slot = &trx->slot[slot_nr]; + /* Push the main OML header and send it off */ + omh = (struct abis_om_hdr *) msgb_push(msg, sizeof(*omh)); + if (is_manuf) + omh->mdisc = ABIS_OM_MDISC_MANUF; + else + omh->mdisc = ABIS_OM_MDISC_FOM; + omh->placement = ABIS_OM_PLACEMENT_ONLY; + omh->sequence = 0; + omh->length = msgb_l3len(msg); + + msg->l2h = (uint8_t *)omh; + + return abis_oml_sendmsg(msg); +} + +int oml_mo_send_msg(struct gsm_abis_mo *mo, struct msgb *msg, uint8_t msg_type) +{ + struct abis_om_fom_hdr *foh; + + msg->l3h = msgb_push(msg, sizeof(*foh)); + foh = (struct abis_om_fom_hdr *) msg->l3h; + foh->msg_type = msg_type; + foh->obj_class = mo->obj_class; + memcpy(&foh->obj_inst, &mo->obj_inst, sizeof(foh->obj_inst)); + + /* FIXME: This assumption may not always be correct */ + msg->trx = mo->bts->c0; - return slot; + return oml_send_msg(msg, 0); } -static struct msgb *fom_msgb_alloc(void) +/* FIXME: move to gsm_data_shared */ +static char mo_buf[128]; +char *gsm_abis_mo_name(const struct gsm_abis_mo *mo) +{ + snprintf(mo_buf, sizeof(mo_buf), "OC=%s INST=(%02x,%02x,%02x)", + get_value_string(abis_nm_obj_class_names, mo->obj_class), + mo->obj_inst.bts_nr, mo->obj_inst.trx_nr, mo->obj_inst.ts_nr); + return mo_buf; +} + +/* 8.8.1 sending State Changed Event Report */ +int oml_tx_state_changed(struct gsm_abis_mo *mo, + uint8_t op_state, uint8_t avail_status) { struct msgb *nmsg; - nmsg = abis_msgb_alloc(sizeof(struct abis_om_hdr)); + LOGP(DOML, LOGL_INFO, "%s Tx STATE CHG REP\n", gsm_abis_mo_name(mo)); + + nmsg = oml_msgb_alloc(); if (!nmsg) - return NULL; - return nmsg; + return -ENOMEM; + + /* 9.4.38 Operational State */ + msgb_tv_put(nmsg, NM_ATT_OPER_STATE, op_state); + + /* 9.4.7 Availability Status */ + msgb_tl16v_put(nmsg, NM_ATT_AVAIL_STATUS, 1, &avail_status); + + return oml_mo_send_msg(mo, nmsg, NM_MT_STATECHG_EVENT_REP); } -static void fom_push_om(struct msgb *msg, uint8_t mdisc, uint8_t placement, uint8_t sequence) +int oml_mo_state_chg(struct gsm_abis_mo *mo, int op_state, int avail_state) { - 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); + int rc = 0; + + if ((op_state != -1 && mo->nm_state.operational != op_state) || + (avail_state != -1 && mo->nm_state.availability != avail_state)) { + if (avail_state != -1) { + LOGP(DOML, LOGL_INFO, "%s AVAIL STATE %s -> %s\n", + gsm_abis_mo_name(mo), + abis_nm_avail_name(mo->nm_state.availability), + abis_nm_avail_name(avail_state)); + mo->nm_state.availability = avail_state; + } + if (op_state != -1) { + LOGP(DOML, LOGL_INFO, "%s OPER STATE %s -> %s\n", + gsm_abis_mo_name(mo), + abis_nm_opstate_name(mo->nm_state.operational), + abis_nm_opstate_name(op_state)); + mo->nm_state.operational = op_state; + } + + /* send state change report */ + rc = oml_tx_state_changed(mo, op_state, avail_state); + } + return rc; } -static int fom_ack_nack(struct ipabis_link *link, struct msgb *msg, uint8_t cause) +int oml_mo_fom_ack_nack(struct gsm_abis_mo *mo, uint8_t orig_msg_type, + uint8_t cause) { - struct abis_om_fom_hdr *foh = msgb_l3(msg); - uint8_t *ie; + struct msgb *msg; + uint8_t new_msg_type; + + msg = oml_msgb_alloc(); + if (!msg) + return -ENOMEM; + + if (cause) { + new_msg_type = orig_msg_type + 2; + msgb_tv_put(msg, NM_ATT_NACK_CAUSES, cause); + } else { + new_msg_type = orig_msg_type + 1; + } + + return oml_mo_send_msg(mo, msg, new_msg_type); +} + +int oml_mo_opstart_ack(struct gsm_abis_mo *mo) +{ + return oml_mo_fom_ack_nack(mo, NM_MT_OPSTART, 0); +} + +int oml_mo_opstart_nack(struct gsm_abis_mo *mo, uint8_t nack_cause) +{ + return oml_mo_fom_ack_nack(mo, NM_MT_OPSTART, nack_cause); +} + +int oml_fom_ack_nack(struct msgb *old_msg, uint8_t cause) +{ + struct abis_om_hdr *old_oh = msgb_l2(old_msg); + struct abis_om_fom_hdr *old_foh = msgb_l3(old_msg); + struct msgb *msg = oml_msgb_alloc(); + struct abis_om_fom_hdr *foh; + int is_manuf = 0; + + /* make sure to respond with MANUF if request was MANUF */ + if (old_oh->mdisc == ABIS_OM_MDISC_MANUF) + is_manuf = 1; + + msg->trx = old_msg->trx; + + /* copy over old FOM-Header and later only change the msg_type */ + msg->l3h = msgb_push(msg, sizeof(*foh)); + foh = (struct abis_om_fom_hdr *) msg->l3h; + memcpy(foh, old_foh, sizeof(*foh)); /* alter message type */ if (cause) { - LOGP(DOML, LOGL_NOTICE, "Sending FOM NACK with cause %d.\n", cause); + LOGP(DOML, LOGL_NOTICE, "Sending FOM NACK with cause %s.\n", + abis_nm_nack_cause_name(cause)); foh->msg_type += 2; /* nack */ /* add cause */ - ie = msgb_put(msg, 2); - ie[0] = NM_ATT_NACK_CAUSES; - ie[1] = cause; + msgb_tv_put(msg, NM_ATT_NACK_CAUSES, cause); } else { LOGP(DOML, LOGL_NOTICE, "Sending FOM ACK.\n"); foh->msg_type++; /* ack */ } - return abis_tx(link, msg); + return oml_send_msg(msg, is_manuf); } /* @@ -124,246 +323,396 @@ static int fom_ack_nack(struct ipabis_link *link, struct msgb *msg, uint8_t caus */ /* 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) +int oml_mo_tx_sw_act_rep(struct gsm_abis_mo *mo) { 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); + LOGP(DOML, LOGL_INFO, "%s Tx SW ACT REP\n", gsm_abis_mo_name(mo)); - nmsg = fom_msgb_alloc(); + nmsg = oml_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); + + return oml_mo_send_msg(mo, nmsg, NM_MT_SW_ACTIVATED_REP); } -/* 8.6.1 Set BTS Attributes is received */ -int oml_rx_set_bts_attr(struct osmocom_bts *bts, struct msgb *msg) +/* TS 12.21 9.4.53 */ +enum abis_nm_t200_idx { + T200_SDCCH = 0, + T200_FACCH_F = 1, + T200_FACCH_H = 2, + T200_SACCH_TCH_SAPI0 = 3, + T200_SACCH_SDCCH = 4, + T200_SDCCH_SAPI3 = 5, + T200_SACCH_TCH_SAPI3 = 6 +}; + +/* TS 12.21 9.4.53 */ +static const uint8_t abis_nm_t200_mult[] = { + [T200_SDCCH] = 5, + [T200_FACCH_F] = 5, + [T200_FACCH_H] = 5, + [T200_SACCH_TCH_SAPI0] = 10, + [T200_SACCH_SDCCH] = 10, + [T200_SDCCH_SAPI3] = 5, + [T200_SACCH_TCH_SAPI3] = 10 +}; + +/* 8.6.1 Set BTS Attributes has been received */ +static int oml_rx_set_bts_attr(struct gsm_bts *bts, struct msgb *msg) { struct abis_om_fom_hdr *foh = msgb_l3(msg); - struct tlv_parsed tp; - struct bts_support *sup = &bts_support; + struct tlv_parsed tp, *tp_merged; + struct gsm_bts_role_bts *btsb = bts_role_bts(bts); + int rc, i; + const uint8_t *payload; - LOGP(DOML, LOGL_INFO, "BSC is setting BTS attributes:\n"); + abis_nm_debugp_foh(DOML, foh); + DEBUGPC(DOML, "Rx SET BTS ATTR\n"); - tlv_parse(&tp, &abis_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 */ + rc = oml_tlv_parse(&tp, foh->data, msgb_l3len(msg) - sizeof(*foh)); + if (rc < 0) + return oml_fom_ack_nack(msg, NM_NACK_INCORR_STRUCT); + + /* Test for globally unsupported stuff here */ if (TLVP_PRESENT(&tp, NM_ATT_BCCH_ARFCN)) { - uint16_t *value = (uint16_t *) TLVP_VAL(&tp, NM_ATT_BCCH_ARFCN); + const 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)))) { + if (arfcn > 1024) { LOGP(DOML, LOGL_NOTICE, "Given ARFCN %d is not supported.\n", arfcn); - return fom_ack_nack(&bts->link, msg, NM_NACK_FREQ_NOTAVAIL); + return oml_fom_ack_nack(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 oml_fom_ack_nack(msg, NM_NACK_SPEC_IMPL_NOTSUPP); } - return fom_ack_nack(&bts->link, msg, 0); + /* merge existing BTS attributes with new attributes */ + tp_merged = tlvp_copy(bts->mo.nm_attr, bts); + tlvp_merge(tp_merged, &tp); + + /* Ask BTS driver to validate new merged attributes */ + rc = bts_model_check_oml(bts, foh->msg_type, bts->mo.nm_attr, tp_merged, bts); + if (rc < 0) { + talloc_free(tp_merged); + /* FIXME: send nack? */ + return rc; + } + + /* Success: replace old BTS attributes with new */ + talloc_free(bts->mo.nm_attr); + bts->mo.nm_attr = tp_merged; + + /* ... and actually still parse them */ + + /* 9.4.25 Interference Level Boundaries */ + if (TLVP_PRESENT(&tp, NM_ATT_INTERF_BOUND)) { + payload = TLVP_VAL(&tp, NM_ATT_INTERF_BOUND); + for (i = 0; i < 6; i++) { + int16_t boundary = *payload; + btsb->interference.boundary[i] = -1 * boundary; + } + /* 9.4.24 Intave Parameter */ + if (TLVP_PRESENT(&tp, NM_ATT_INTAVE_PARAM)) + btsb->interference.intave = *TLVP_VAL(&tp, NM_ATT_INTAVE_PARAM); + + /* 9.4.14 Connection Failure Criterion */ + /* ... can be 'operator dependent' and needs to be parsed by bts driver */ + + /* 9.4.53 T200 */ + if (TLVP_PRESENT(&tp, NM_ATT_T200)) { + payload = TLVP_VAL(&tp, NM_ATT_T200); + for (i = 0; i < ARRAY_SIZE(btsb->t200_ms); i++) + btsb->t200_ms[i] = payload[i] * abis_nm_t200_mult[i]; + } + + /* 9.4.31 Maximum Timing Advance */ + if (TLVP_PRESENT(&tp, NM_ATT_MAX_TA)) { + uint16_t *fn = (uint16_t *) TLVP_VAL(&tp, NM_ATT_MAX_TA); + btsb->max_ta = ntohs(*fn); + } + + /* 9.4.39 Overload Period */ + if (TLVP_PRESENT(&tp, NM_ATT_OVERL_PERIOD)) + btsb->load.overload_period = *TLVP_VAL(&tp, NM_ATT_OVERL_PERIOD); + + /* 9.4.12 CCCH Load Threshold */ + if (TLVP_PRESENT(&tp, NM_ATT_CCCH_L_T)) + btsb->load.ccch.load_ind_thresh = *TLVP_VAL(&tp, NM_ATT_CCCH_L_T); + + /* 9.4.11 CCCH Load Indication Period */ + if (TLVP_PRESENT(&tp, NM_ATT_CCCH_L_I_P)) + btsb->load.ccch.load_ind_period = *TLVP_VAL(&tp, NM_ATT_CCCH_L_I_P); + + /* 9.4.44 RACH Busy Threshold */ + if (TLVP_PRESENT(&tp, NM_ATT_RACH_B_THRESH)) { + int16_t thresh = *TLVP_VAL(&tp, NM_ATT_RACH_B_THRESH); + btsb->load.rach.busy_thresh = -1 * thresh; + } + + /* 9.4.45 RACH Load Averaging Slots */ + if (TLVP_PRESENT(&tp, NM_ATT_LDAVG_SLOTS)) + payload = TLVP_VAL(&tp, NM_ATT_LDAVG_SLOTS); + btsb->load.rach.averaging_slots = ntohs(*(uint16_t *)payload); + } + + /* 9.4.10 BTS Air Timer */ + if (TLVP_PRESENT(&tp, NM_ATT_BTS_AIR_TIMER)) + btsb->t3105_ms = *TLVP_VAL(&tp, NM_ATT_BTS_AIR_TIMER) * 10; + + /* 9.4.37 NY1 */ + if (TLVP_PRESENT(&tp, NM_ATT_NY1)) + btsb->ny1 = *TLVP_VAL(&tp, NM_ATT_NY1); + + /* 9.4.8 BCCH ARFCN */ + if (TLVP_PRESENT(&tp, NM_ATT_BCCH_ARFCN)) { + const uint16_t *value = (uint16_t *) TLVP_VAL(&tp, NM_ATT_BCCH_ARFCN); + bts->c0->arfcn = ntohs(*value); + } + /* 9.4.9 BSIC */ + if (TLVP_PRESENT(&tp, NM_ATT_BSIC)) + bts->bsic = *TLVP_VAL(&tp, NM_ATT_BSIC); + + /* call into BTS driver to apply new attributes to hardware */ + return bts_model_apply_oml(bts, msg, tp_merged, bts); } -/* 8.6.2 Set Radio Attributes is received */ -int oml_rx_set_radio_attr(struct osmocom_bts *bts, struct msgb *msg) +/* 8.6.2 Set Radio Attributes has been received */ +static int oml_rx_set_radio_attr(struct gsm_bts_trx *trx, 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; + struct tlv_parsed tp, *tp_merged; + int rc; + + abis_nm_debugp_foh(DOML, foh); + DEBUGPC(DOML, "Rx SET RADIO CARRIER ATTR\n"); + + rc = oml_tlv_parse(&tp, foh->data, msgb_l3len(msg) - sizeof(*foh)); + if (rc < 0) + return oml_fom_ack_nack(msg, NM_NACK_INCORR_STRUCT); + + /* merge existing BTS attributes with new attributes */ + tp_merged = tlvp_copy(trx->mo.nm_attr, trx->bts); + tlvp_merge(tp_merged, &tp); + + /* Ask BTS driver to validate new merged attributes */ + rc = bts_model_check_oml(trx->bts, foh->msg_type, trx->mo.nm_attr, tp_merged, trx); + if (rc < 0) { + talloc_free(tp_merged); + /* FIXME: send NACK */ + return rc; + } - trx = get_trx_by_nr(bts, foh->obj_inst.trx_nr); - if (!trx) - return fom_ack_nack(&bts->link, msg, NM_NACK_TRXNR_UNKN); + /* Success: replace old BTS attributes with new */ + talloc_free(trx->mo.nm_attr); + trx->mo.nm_attr = tp_merged; - LOGP(DOML, LOGL_INFO, "BSC is setting radio attributes:\n"); + /* ... and actually still parse them */ - tlv_parse(&tp, &abis_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; + trx->max_power_red = *TLVP_VAL(&tp, NM_ATT_RF_MAXPOWR_R); + LOGP(DOML, LOGL_INFO, " RF Max Power Reduction = %d\n", trx->max_power_red); + } /* 9.4.5 ARFCN List */ +#if 0 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 length = TLVP_LEN(&tp, NM_ATT_ARFCN_LIST); 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); + if (arfcn > 1024) + return oml_fom_ack_nack(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; +#endif + /* call into BTS driver to apply new attributes to hardware */ + return bts_model_apply_oml(trx->bts, msg, tp_merged, trx); +} - return fom_ack_nack(&bts->link, msg, 0); +static int conf_lchans_for_pchan(struct gsm_bts_trx_ts *ts) +{ + struct gsm_lchan *lchan; + unsigned int i; + + switch (ts->pchan) { + case GSM_PCHAN_CCCH_SDCCH4: + for (i = 0; i < 4; i++) { + lchan = &ts->lchan[i+1]; + lchan->type = GSM_LCHAN_SDCCH; + } + /* fallthrough */ + case GSM_PCHAN_CCCH: + lchan = &ts->lchan[0]; + lchan->type = GSM_LCHAN_CCCH; + break; + case GSM_PCHAN_TCH_F: + lchan = &ts->lchan[0]; + lchan->type = GSM_LCHAN_TCH_F; + break; + case GSM_PCHAN_TCH_H: + for (i = 0; i < 2; i++) { + lchan = &ts->lchan[i]; + lchan->type = GSM_LCHAN_TCH_H; + } + break; + case GSM_PCHAN_SDCCH8_SACCH8C: + for (i = 0; i < 8; i++) { + lchan = &ts->lchan[i]; + lchan->type = GSM_LCHAN_SDCCH; + } + break; + default: + /* FIXME */ + break; + } + return 0; } -/* 8.6.3 Set Channel Attributes is received */ -int oml_rx_set_chan_attr(struct osmocom_bts *bts, struct msgb *msg) +/* 8.6.3 Set Channel Attributes has been received */ +static int oml_rx_set_chan_attr(struct gsm_bts_trx_ts *ts, 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; + struct gsm_bts *bts = ts->trx->bts; + struct tlv_parsed tp, *tp_merged; + int rc; + + abis_nm_debugp_foh(DOML, foh); + DEBUGPC(DOML, "Rx SET CHAN ATTR\n"); - 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); + rc = oml_tlv_parse(&tp, foh->data, msgb_l3len(msg) - sizeof(*foh)); + if (rc < 0) + return oml_fom_ack_nack(msg, NM_NACK_INCORR_STRUCT); - LOGP(DOML, LOGL_INFO, "BSC is setting channel attributes:\n"); + /* 9.4.21 HSN... */ + /* 9.4.27 MAIO */ + if (TLVP_PRESENT(&tp, NM_ATT_HSN) || TLVP_PRESENT(&tp, NM_ATT_MAIO)) { + LOGP(DOML, LOGL_NOTICE, "SET CHAN ATTR: Frequency hopping not supported.\n"); + return oml_fom_ack_nack(msg, NM_NACK_SPEC_IMPL_NOTSUPP); + } + + /* 9.4.52 Starting Time */ + if (TLVP_PRESENT(&tp, NM_ATT_START_TIME)) { + LOGP(DOML, LOGL_NOTICE, "SET CHAN ATTR: Starting time not supported.\n"); + return oml_fom_ack_nack(msg, NM_NACK_SPEC_IMPL_NOTSUPP); + } + + /* merge existing BTS attributes with new attributes */ + tp_merged = tlvp_copy(bts->mo.nm_attr, bts); + tlvp_merge(tp_merged, &tp); + + /* Call into BTS driver to check attribute values */ + rc = bts_model_check_oml(bts, foh->msg_type, ts->mo.nm_attr, tp_merged, ts); + if (rc < 0) { + talloc_free(&tp_merged); + /* FIXME: Send NACK */ + return rc; + } - tlv_parse(&tp, &abis_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; + ts->pchan = abis_nm_pchan4chcomb(comb); + conf_lchans_for_pchan(ts); } - /* 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); + + /* 9.4.5 ARFCN List */ + + /* 9.4.60 TSC */ + if (TLVP_PRESENT(&tp, NM_ATT_TSC) && TLVP_LEN(&tp, NM_ATT_TSC) >= 1) { + ts->tsc = *TLVP_VAL(&tp, NM_ATT_TSC); + } else { + /* If there is no TSC specified, use the BCC */ + ts->tsc = bts->bsic & 0x3; } + DEBUGP(DOML, "TS %u, settig TSC = %u\n", ts->nr, ts->tsc); - return fom_ack_nack(&bts->link, msg, 0); + /* call into BTS driver to apply new attributes to hardware */ + return bts_model_apply_oml(bts, msg, tp_merged, ts); } -/* 8.9.2 Opstart is received */ -int oml_rx_opstart(struct osmocom_bts *bts, struct msgb *msg) +/* 8.9.2 Opstart has been received */ +static int oml_rx_opstart(struct gsm_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); + struct gsm_abis_mo *mo; + void *obj; + + abis_nm_debugp_foh(DOML, foh); + DEBUGPC(DOML, "Rx OPSTART\n"); + + /* Step 1: Resolve MO by obj_class/obj_inst */ + mo = gsm_objclass2mo(bts, foh->obj_class, &foh->obj_inst); + obj = gsm_objclass2obj(bts, foh->obj_class, &foh->obj_inst); + if (!mo || !obj) + return oml_fom_ack_nack(msg, NM_NACK_OBJINST_UNKN); + + /* Step 2: Do some global dependency/consistency checking */ + if (mo->nm_state.operational == NM_OPSTATE_ENABLED) { + DEBUGP(DOML, "... automatic ACK, OP state already was Enabled\n"); + return oml_mo_opstart_ack(mo); + } + + /* Step 3: Ask BTS driver to apply the opstart */ + return bts_model_opstart(bts, mo, obj); +} + +static int oml_rx_chg_adm_state(struct gsm_bts *bts, struct msgb *msg) +{ + struct abis_om_fom_hdr *foh = msgb_l3(msg); + struct tlv_parsed tp; + struct gsm_abis_mo *mo; + uint8_t adm_state; + void *obj; + int rc; + + abis_nm_debugp_foh(DOML, foh); + DEBUGPC(DOML, "Rx CHG ADM STATE\n"); + + rc = oml_tlv_parse(&tp, foh->data, msgb_l3len(msg) - sizeof(*foh)); + if (rc < 0) { + LOGP(DOML, LOGL_ERROR, "Rx CHG ADM STATE: error during TLV parse\n"); + return oml_fom_ack_nack(msg, NM_NACK_INCORR_STRUCT); } - /* 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); + if (!TLVP_PRESENT(&tp, NM_ATT_ADM_STATE)) { + LOGP(DOML, LOGL_ERROR, "Rx CHG ADM STATE: no ADM state attribute\n"); + return oml_fom_ack_nack(msg, NM_NACK_INCORR_STRUCT); + } + + adm_state = *TLVP_VAL(&tp, NM_ATT_ADM_STATE); + + /* Step 1: Resolve MO by obj_class/obj_inst */ + mo = gsm_objclass2mo(bts, foh->obj_class, &foh->obj_inst); + obj = gsm_objclass2obj(bts, foh->obj_class, &foh->obj_inst); + if (!mo || !obj) + return oml_fom_ack_nack(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); + /* Step 2: Do some global dependency/consistency checking */ + if (mo->nm_state.administrative == adm_state) { + DEBUGP(DOML, "... automatic ACK, ADM state already was %s\n", + get_value_string(abis_nm_adm_state_names, adm_state)); + return oml_fom_ack_nack(msg, 0); + } + + /* Step 3: Ask BTS driver to apply the state chg */ + return bts_model_chg_adm_state(bts, mo, obj, adm_state); } -static int down_fom(struct osmocom_bts *bts, struct msgb *msg) +static int down_fom(struct gsm_bts *bts, struct msgb *msg) { struct abis_om_fom_hdr *foh = msgb_l3(msg); + struct gsm_bts_trx *trx; int ret; if (msgb_l2len(msg) < sizeof(*foh)) { @@ -374,7 +723,7 @@ static int down_fom(struct osmocom_bts *bts, struct msgb *msg) 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); + return oml_fom_ack_nack(msg, NM_NACK_BTSNR_UNKN); } switch (foh->msg_type) { @@ -382,22 +731,29 @@ static int down_fom(struct osmocom_bts *bts, struct msgb *msg) ret = oml_rx_set_bts_attr(bts, msg); break; case NM_MT_SET_RADIO_ATTR: - ret = oml_rx_set_radio_attr(bts, msg); + trx = gsm_bts_trx_num(bts, foh->obj_inst.trx_nr); + if (!trx) + return oml_fom_ack_nack(msg, NM_NACK_TRXNR_UNKN); + ret = oml_rx_set_radio_attr(trx, msg); break; case NM_MT_SET_CHAN_ATTR: - ret = oml_rx_set_chan_attr(bts, msg); + trx = gsm_bts_trx_num(bts, foh->obj_inst.trx_nr); + if (!trx) + return oml_fom_ack_nack(msg, NM_NACK_TRXNR_UNKN); + if (foh->obj_inst.ts_nr >= ARRAY_SIZE(trx->ts)) + return oml_fom_ack_nack(msg, NM_NACK_OBJINST_UNKN); + ret = oml_rx_set_chan_attr(&trx->ts[foh->obj_inst.ts_nr], 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); + ret = oml_rx_chg_adm_state(bts, msg); 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); + ret = oml_fom_ack_nack(msg, NM_NACK_MSGTYPE_INVAL); } return ret; @@ -407,37 +763,107 @@ static int down_fom(struct osmocom_bts *bts, struct msgb *msg) * manufacturer related messages */ -static int down_mom(struct osmocom_bts *bts, struct msgb *msg) + +static int rx_oml_ipa_rsl_connect(struct gsm_bts_trx *trx, struct msgb *msg, + struct tlv_parsed *tp) { - struct abis_om_fom_hdr *foh = msgb_l3(msg); + struct ipabis_link *oml_link = (struct ipabis_link *) trx->bts->oml_link; + uint16_t port = IPA_TCP_PORT_RSL; + uint32_t ip = oml_link->ip; + struct in_addr in; + int rc; + + uint8_t stream_id = 0; + + DEBUGP(DOML, "Rx IPA RSL CONNECT "); + + if (TLVP_PRESENT(tp, NM_ATT_IPACC_DST_IP)) { + const uint8_t *ptr = TLVP_VAL(tp, NM_ATT_IPACC_DST_IP); + ip = ntohl(*(uint32_t *)ptr); + } + if (TLVP_PRESENT(tp, NM_ATT_IPACC_DST_IP_PORT)) { + const uint8_t *ptr = TLVP_VAL(tp, NM_ATT_IPACC_DST_IP_PORT); + port = ntohs(*(uint16_t *)ptr); + } + if (TLVP_PRESENT(tp, NM_ATT_IPACC_STREAM_ID)) { + stream_id = *TLVP_VAL(tp, NM_ATT_IPACC_STREAM_ID); + } + + in.s_addr = htonl(ip); + DEBUGPC(DOML, "IP=%s PORT=%u STREAM=0x%02x\n", inet_ntoa(in), + port, stream_id); + + if (!trx->rsl_link) { + struct ipabis_link *rsl_link = talloc_zero(trx, struct ipabis_link); + rsl_link->trx = trx; + trx->rsl_link = rsl_link; + } + + /* FIXME: we cannot even use a non-standard port here */ + rc = abis_open(trx->rsl_link, ip); + if (rc < 0) { + LOGP(DOML, LOGL_ERROR, "Error in abis_open(RSL): %d\n", rc); + return oml_fom_ack_nack(msg, NM_NACK_CANT_PERFORM); + } + + return oml_fom_ack_nack(msg, 0); +} + +static int down_mom(struct gsm_bts *bts, struct msgb *msg) +{ + struct abis_om_hdr *oh = msgb_l2(msg); + struct abis_om_fom_hdr *foh; + struct gsm_bts_trx *trx; + uint8_t idstrlen = oh->data[0]; + struct tlv_parsed tp; int ret; + DEBUGP(DOML, "Manufacturer OML message\n"); + if (msgb_l2len(msg) < sizeof(*foh)) { LOGP(DOML, LOGL_NOTICE, "Manufacturer O&M message too short\n"); msgb_free(msg); return -EIO; } + if (strncmp((char *)&oh->data[1], ipaccess_magic, idstrlen)) { + LOGP(DOML, LOGL_ERROR, "Manufacturer OML message != ipaccess not supported\n"); + return -EINVAL; + } + + msg->l3h = oh->data + 1 + idstrlen; + foh = (struct abis_om_fom_hdr *) msg->l3h; + 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); + return oml_fom_ack_nack(msg, NM_NACK_BTSNR_UNKN); + } + + ret = oml_tlv_parse(&tp, foh->data, oh->length - sizeof(*foh)); + if (ret < 0) { + LOGP(DOML, LOGL_ERROR, "TLV parse error %d\n", ret); + return oml_fom_ack_nack(msg, NM_NACK_BTSNR_UNKN); } + abis_nm_debugp_foh(DOML, foh); + DEBUGPC(DOML, "IPACCESS(0x%02x): ", foh->msg_type); + switch (foh->msg_type) { + case NM_MT_IPACC_RSL_CONNECT: + trx = gsm_bts_trx_num(bts, foh->obj_inst.trx_nr); + ret = rx_oml_ipa_rsl_connect(trx, msg, &tp); + break; 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); + ret = oml_fom_ack_nack(msg, NM_NACK_MSGTYPE_INVAL); } return ret; } -/* - * selecting messages - */ - -int down_oml(struct osmocom_bts *bts, struct msgb *msg) +/* incoming OML message from BSC */ +int down_oml(struct gsm_bts *bts, struct msgb *msg) { struct abis_om_hdr *oh = msgb_l2(msg); int ret = 0; @@ -478,4 +904,10 @@ int down_oml(struct osmocom_bts *bts, struct msgb *msg) return ret; } +int oml_init(void) +{ + DEBUGP(DOML, "Initializing OML attribute definitions\n"); + tlv_def_patch(&abis_nm_att_tlvdef_ipa, &abis_nm_att_tlvdef); + return 0; +} |