aboutsummaryrefslogtreecommitdiffstats
path: root/src/common/oml.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/oml.c')
-rw-r--r--src/common/oml.c900
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;
+}