aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/openbsc/abis_nm.h46
-rw-r--r--include/openbsc/tlv.h21
-rw-r--r--src/abis_nm.c328
-rw-r--r--src/tlv_parser.c46
4 files changed, 418 insertions, 23 deletions
diff --git a/include/openbsc/abis_nm.h b/include/openbsc/abis_nm.h
index e8837f0b8..dfe0bf7b8 100644
--- a/include/openbsc/abis_nm.h
+++ b/include/openbsc/abis_nm.h
@@ -374,6 +374,44 @@ enum abis_nm_chan_comb {
NM_CHANC_SDCCH_CBCH = 0x08,
};
+/* Section 9.4.36: NACK Causes */
+enum abis_nm_nack_cause {
+ /* General Nack Causes */
+ NM_NACK_INCORR_STRUCT = 0x01,
+ NM_NACK_MSGTYPE_INVAL = 0x02,
+ NM_NACK_OBJCLASS_INVAL = 0x05,
+ NM_NACK_OBJCLASS_NOTSUPP = 0x06,
+ NM_NACK_BTSNR_UNKN = 0x07,
+ NM_NACK_TRXNR_UNKN = 0x08,
+ NM_NACK_OBJINST_UNKN = 0x09,
+ NM_NACK_ATTRID_INVAL = 0x0c,
+ NM_NACK_ATTRID_NOTSUPP = 0x0d,
+ NM_NACK_PARAM_RANGE = 0x0e,
+ NM_NACK_ATTRLIST_INCONSISTENT = 0x0f,
+ NM_NACK_SPEC_IMPL_NOTSUPP = 0x10,
+ NM_NACK_CANT_PERFORM = 0x11,
+ /* Specific Nack Causes */
+ NM_NACK_RES_NOTIMPL = 0x19,
+ NM_NACK_RES_NOTAVAIL = 0x1a,
+ NM_NACK_FREQ_NOTAVAIL = 0x1b,
+ NM_NACK_TEST_NOTSUPP = 0x1c,
+ NM_NACK_CAPACITY_RESTR = 0x1d,
+ NM_NACK_PHYSCFG_NOTPERFORM = 0x1e,
+ NM_NACK_TEST_NOTINIT = 0x1f,
+ NM_NACK_PHYSCFG_NOTRESTORE = 0x20,
+ NM_NACK_TEST_NOSUCH = 0x21,
+ NM_NACK_TEST_NOSTOP = 0x22,
+ NM_NACK_MSGINCONSIST_PHYSCFG = 0x23,
+ NM_NACK_FILE_INCOMPLETE = 0x25,
+ NM_NACK_FILE_NOTAVAIL = 0x26,
+ MN_NACK_FILE_NOTACTIVATE = 0x27,
+ NM_NACK_REQ_NOT_GRANT = 0x28,
+ NM_NACK_WAIT = 0x29,
+ NM_NACK_NOTH_REPORT_EXIST = 0x2a,
+ NM_NACK_MEAS_NOTSUPP = 0x2b,
+ NM_NACK_MEAS_NOTSTART = 0x2c,
+};
+
/* Section 9.4.1 */
struct abis_nm_channel {
u_int8_t attrib;
@@ -484,4 +522,12 @@ int abis_nm_bs11_set_ext_time(struct gsm_bts *bts);
int abis_nm_bs11_bsc_disconnect(struct gsm_bts *bts, int reconnect);
int abis_nm_bs11_restart(struct gsm_bts *bts);
+/* Functions calling into other code parts */
+enum nm_evt {
+ EVT_STATECHG_OPER,
+ EVT_STATECHG_ADM,
+};
+int nm_state_event(enum nm_evt evt, u_int8_t obj_class, void *obj,
+ struct gsm_nm_state *old_state, struct gsm_nm_state *new_state);
+
#endif /* _NM_H */
diff --git a/include/openbsc/tlv.h b/include/openbsc/tlv.h
index 3bb586225..e21511685 100644
--- a/include/openbsc/tlv.h
+++ b/include/openbsc/tlv.h
@@ -109,15 +109,32 @@ static inline u_int8_t *msgb_tv16_push(struct msgb *msg, u_int8_t tag, u_int16_t
/* TLV parsing */
struct tlv_p_entry {
- u_int8_t len;
+ u_int16_t len;
u_int8_t *val;
};
+enum tlv_type {
+ TLV_TYPE_FIXED,
+ TLV_TYPE_T,
+ TLV_TYPE_TV,
+ TLV_TYPE_TLV,
+ TLV_TYPE_TL16V,
+};
+
+struct tlv_def {
+ enum tlv_type type;
+ u_int8_t fixed_len;
+};
+
+struct tlv_definition {
+ struct tlv_def def[0xff];
+};
+
struct tlv_parsed {
struct tlv_p_entry lv[0xff];
};
-int tlv_parse(struct tlv_parsed *dec, u_int8_t *buf, int buf_len);
+int tlv_parse(struct tlv_parsed *dec, const struct tlv_definition *def, u_int8_t *buf, int buf_len);
#define TLVP_PRESENT(x, y) ((x)->lv[y].val)
#define TLVP_LEN(x, y) (x)->lv[y].len
diff --git a/src/abis_nm.c b/src/abis_nm.c
index 4f5841a9c..159424aa8 100644
--- a/src/abis_nm.c
+++ b/src/abis_nm.c
@@ -74,6 +74,41 @@ static const enum abis_nm_msgtype sw_load_msgs[] = {
NM_MT_SW_ACTIVATED_REP,
};
+static const enum abis_nm_msgtype nacks[] = {
+ NM_MT_LOAD_INIT_NACK,
+ NM_MT_LOAD_END_NACK,
+ NM_MT_SW_ACT_REQ_NACK,
+ NM_MT_ACTIVATE_SW_NACK,
+ NM_MT_ESTABLISH_TEI_NACK,
+ NM_MT_CONN_TERR_SIGN_NACK,
+ NM_MT_DISC_TERR_SIGN_NACK,
+ NM_MT_CONN_TERR_TRAF_NACK,
+ NM_MT_DISC_TERR_TRAF_NACK,
+ NM_MT_CONN_MDROP_LINK_NACK,
+ NM_MT_DISC_MDROP_LINK_NACK,
+ NM_MT_SET_BTS_ATTR_NACK,
+ NM_MT_SET_RADIO_ATTR_NACK,
+ NM_MT_SET_CHAN_ATTR_NACK,
+ NM_MT_PERF_TEST_NACK,
+ NM_MT_SEND_TEST_REP_NACK,
+ NM_MT_STOP_TEST_NACK,
+ NM_MT_STOP_EVENT_REP_NACK,
+ NM_MT_REST_EVENT_REP_NACK,
+ NM_MT_CHG_ADM_STATE_NACK,
+ NM_MT_CHG_ADM_STATE_REQ_NACK,
+ NM_MT_REP_OUTST_ALARMS_NACK,
+ NM_MT_CHANGEOVER_NACK,
+ NM_MT_OPSTART_NACK,
+ NM_MT_REINIT_NACK,
+ NM_MT_SET_SITE_OUT_NACK,
+ NM_MT_CHG_HW_CONF_NACK,
+ NM_MT_GET_ATTR_NACK,
+ NM_MT_SET_ALARM_THRES_NACK,
+ NM_MT_BS11_BEGIN_DB_TX_NACK,
+ NM_MT_BS11_END_DB_TX_NACK,
+ NM_MT_BS11_CREATE_OBJ_NACK,
+ NM_MT_BS11_DELETE_OBJ_NACK,
+};
/* Attributes that the BSC can set, not only get, according to Section 9.4 */
static const enum abis_nm_attr nm_att_settable[] = {
NM_ATT_ADD_INFO,
@@ -103,6 +138,88 @@ static const enum abis_nm_attr nm_att_settable[] = {
NM_ATT_MEAS_TYPE,
};
+static 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_FILE_DATA] = { TLV_TYPE_TL16V },
+ [NM_ATT_MEAS_RES] = { TLV_TYPE_TL16V },
+ /* BS11 specifics */
+ [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_LOGIN_TIME] = { TLV_TYPE_TLV },
+ [NM_ATT_BS11_LMT_USER_ACC_LEV] ={ TLV_TYPE_TLV },
+ [NM_ATT_BS11_LMT_USER_NAME] = { TLV_TYPE_TLV },
+ },
+};
+#define nm_tlv_parse(dec, buf, len) tlv_parse(dec, &nm_att_tlvdef, buf, len)
+
static int is_in_arr(enum abis_nm_msgtype mt, const enum abis_nm_msgtype *arr, int size)
{
int i;
@@ -199,28 +316,168 @@ static const char *opstate_name(u_int8_t os)
}
}
+/* Chapter 9.4.7 */
+const char *avail_names[] = {
+ "In test",
+ "Failed",
+ "Power off",
+ "Off line",
+ "<not used>",
+ "Dependency",
+ "Degraded",
+ "Not installed",
+};
+
+static const char *avail_name(u_int8_t avail)
+{
+ if (avail >= ARRAY_SIZE(avail_names))
+ return "UNKNOWN";
+ return avail_names[avail];
+}
+
+
+/* obtain the gsm_nm_state data structure for a given object instance */
+static struct gsm_nm_state *
+objclass2nmstate(struct gsm_bts *bts, u_int8_t obj_class,
+ struct abis_om_obj_inst *obj_inst)
+{
+ struct gsm_bts_trx *trx;
+ struct gsm_nm_state *nm_state = NULL;
+
+ switch (obj_class) {
+ case NM_OC_BTS:
+ nm_state = &bts->nm_state;
+ break;
+ case NM_OC_RADIO_CARRIER:
+ if (obj_inst->trx_nr >= bts->num_trx)
+ return NULL;
+ trx = &bts->trx[obj_inst->trx_nr];
+ nm_state = &trx->nm_state;
+ break;
+ case NM_OC_BASEB_TRANSC:
+ if (obj_inst->trx_nr >= bts->num_trx)
+ return NULL;
+ trx = &bts->trx[obj_inst->trx_nr];
+ nm_state = &trx->bb_transc.nm_state;
+ break;
+ case NM_OC_CHANNEL:
+ if (obj_inst->trx_nr > bts->num_trx)
+ return NULL;
+ trx = &bts->trx[obj_inst->trx_nr];
+ if (obj_inst->ts_nr >= TRX_NR_TS)
+ return NULL;
+ nm_state = &trx->ts[obj_inst->ts_nr].nm_state;
+ break;
+ case NM_OC_SITE_MANAGER:
+ nm_state = &bts->site_mgr.nm_state;
+ break;
+ }
+ return nm_state;
+}
+
+/* obtain the in-memory data structure of a given object instance */
+static void *
+objclass2obj(struct gsm_bts *bts, u_int8_t obj_class,
+ struct abis_om_obj_inst *obj_inst)
+{
+ struct gsm_bts_trx *trx;
+ void *obj = NULL;
+
+ switch (obj_class) {
+ case NM_OC_BTS:
+ obj = bts;
+ break;
+ case NM_OC_RADIO_CARRIER:
+ if (obj_inst->trx_nr >= bts->num_trx)
+ return NULL;
+ trx = &bts->trx[obj_inst->trx_nr];
+ obj = trx;
+ break;
+ case NM_OC_BASEB_TRANSC:
+ if (obj_inst->trx_nr >= bts->num_trx)
+ return NULL;
+ trx = &bts->trx[obj_inst->trx_nr];
+ obj = &trx->bb_transc;
+ break;
+ case NM_OC_CHANNEL:
+ if (obj_inst->trx_nr > bts->num_trx)
+ return NULL;
+ trx = &bts->trx[obj_inst->trx_nr];
+ if (obj_inst->ts_nr >= TRX_NR_TS)
+ return NULL;
+ obj = &trx->ts[obj_inst->ts_nr];
+ break;
+ case NM_OC_SITE_MANAGER:
+ obj = &bts->site_mgr;
+ break;
+ }
+ return obj;
+}
+
+/* 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, u_int8_t obj_class,
+ struct abis_om_obj_inst *obj_inst, u_int8_t adm_state)
+{
+ struct gsm_nm_state *nm_state;
+ void *obj;
+ int rc;
+
+ nm_state = objclass2nmstate(bts, obj_class, obj_inst);
+ obj = objclass2obj(bts, obj_class, obj_inst);
+ rc = nm_state_event(EVT_STATECHG_ADM, obj_class, obj, nm_state, adm_state);
+ if (nm_state)
+ nm_state->administrative = adm_state;
+
+ return rc;
+}
+
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);
- u_int8_t *data = &foh->data[0];
struct gsm_bts *bts = mb->trx->bts;
- u_int8_t op_state = 0;
-
+ struct tlv_parsed tp;
+ struct gsm_nm_state *nm_state, new_state;
+ int rc;
+
DEBUGP(DNM, "STATE CHG: OC=%s(%02x) INST=(%02x,%02x,%02x) ",
obj_class_name(foh->obj_class), foh->obj_class,
foh->obj_inst.bts_nr, foh->obj_inst.trx_nr,
foh->obj_inst.ts_nr);
- if (*data++ == NM_ATT_OPER_STATE) {
- u_int8_t op_state = *data++;
- DEBUGPC(DNM, "OP_STATE=%s ", opstate_name(op_state));
-
+
+ nm_state = objclass2nmstate(bts, foh->obj_class, &foh->obj_inst);
+ if (!nm_state) {
+ DEBUGPC(DNM, "\n");
+ return -EINVAL;
+ }
+
+ new_state = *nm_state;
+
+ nm_tlv_parse(&tp, 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 ", opstate_name(new_state.operational));
+ }
+ if (TLVP_PRESENT(&tp, NM_ATT_AVAIL_STATUS)) {
+ new_state.availability = *TLVP_VAL(&tp, NM_ATT_AVAIL_STATUS);
+ DEBUGPC(DNM, "AVAIL=%s(%02x) ", avail_name(new_state.availability),
+ new_state.availability);
}
- if (*data++ == NM_ATT_AVAIL_STATUS) {
- u_int8_t att_len = *data++;
- while (att_len--)
- DEBUGPC(DNM, "AVAIL=%02x ", *data++);
+ if (TLVP_PRESENT(&tp, NM_ATT_ADM_STATE)) {
+ new_state.administrative = *TLVP_VAL(&tp, NM_ATT_ADM_STATE);
+ DEBUGPC(DNM, "ADM=%02x ", new_state.administrative);
}
DEBUGPC(DNM, "\n");
+
+ if (memcmp(&new_state, nm_state, sizeof(new_state))) {
+ /* Update the operational state of a given object in our in-memory data
+ * structures and send an event to the higher layer */
+ void *obj = objclass2obj(bts, foh->obj_class, &foh->obj_inst);
+ rc = nm_state_event(EVT_STATECHG_OPER, foh->obj_class, obj, nm_state, &new_state);
+ *nm_state = new_state;
+ }
+#if 0
if (op_state == 1) {
/* try to enable objects that are disabled */
abis_nm_opstart(bts, foh->obj_class,
@@ -228,6 +485,7 @@ static int abis_nm_rx_statechg_rep(struct msgb *mb)
foh->obj_inst.trx_nr,
foh->obj_inst.ts_nr);
}
+#endif
return 0;
}
@@ -245,10 +503,15 @@ static int abis_nm_rcvmsg_report(struct msgb *mb)
case NM_MT_SW_ACTIVATED_REP:
DEBUGP(DNM, "Software Activated Report\n");
break;
+ case NM_MT_FAILURE_EVENT_REP:
+ DEBUGP(DNM, "Failure Event Report\n");
+ break;
+ default:
+ DEBUGP(DNM, "reporting NM MT 0x%02x\n", mt);
+ break;
+
};
- DEBUGP(DNM, "reporting NM MT 0x%02x\n", mt);
-
return 0;
}
@@ -292,6 +555,23 @@ static int abis_nm_rx_sw_act_req(struct msgb *mb)
foh->data + oh->length-sizeof(*foh)-22, 22);
}
+/* 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 tlv_parsed tp;
+ u_int8_t adm_state;
+
+ nm_tlv_parse(&tp, 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(mb->trx->bts, foh->obj_class, &foh->obj_inst, adm_state);
+}
+
/* Receive a OML NM Message from BTS */
static int abis_nm_rcvmsg_fom(struct msgb *mb)
{
@@ -305,6 +585,8 @@ static int abis_nm_rcvmsg_fom(struct msgb *mb)
if (is_in_arr(mt, sw_load_msgs, ARRAY_SIZE(sw_load_msgs)))
return abis_nm_rcvmsg_sw(mb);
+ if (is_in_arr(mt, nacks, ARRAY_SIZE(nacks)))
+ DEBUGP(DNM, "NACK 0x%02x\n", mt);
#if 0
/* check if last message is to be acked */
if (is_ack_nack(nmh->last_msgtype)) {
@@ -325,6 +607,9 @@ static int abis_nm_rcvmsg_fom(struct msgb *mb)
#endif
switch (mt) {
+ case NM_MT_CHG_ADM_STATE_ACK:
+ return abis_nm_rx_chg_adm_state_ack(mb);
+ break;
case NM_MT_SW_ACT_REQ:
return abis_nm_rx_sw_act_req(mb);
break;
@@ -978,7 +1263,10 @@ int abis_nm_set_channel_attr(struct gsm_bts_trx_ts *ts, u_int8_t chan_comb)
u_int16_t arfcn = htons(ts->trx->arfcn);
u_int8_t zero = 0x00;
struct msgb *msg = nm_msgb_alloc();
- u_int8_t len = 4 + 2 + 2 + 2 + 2 +3;
+ u_int8_t len = 2 + 2;
+
+ if (bts->type == GSM_BTS_TYPE_BS11)
+ len += 4 + 2 + 2 + 3;
DEBUGP(DNM, "Set Chan Attr (bts=%d,trx=%d,ts=%d)\n", bts->nr, ts->trx->nr, ts->nr);
@@ -987,12 +1275,16 @@ int abis_nm_set_channel_attr(struct gsm_bts_trx_ts *ts, u_int8_t chan_comb)
NM_OC_CHANNEL, bts->bts_nr,
ts->trx->nr, ts->nr);
/* FIXME: don't send ARFCN list, hopping sequence, mAIO, ...*/
- msgb_tlv16_put(msg, NM_ATT_ARFCN_LIST, 1, &arfcn);
+ if (bts->type == GSM_BTS_TYPE_BS11)
+ msgb_tlv16_put(msg, NM_ATT_ARFCN_LIST, 1, &arfcn);
msgb_tv_put(msg, NM_ATT_CHAN_COMB, chan_comb);
- msgb_tv_put(msg, NM_ATT_HSN, 0x00);
- msgb_tv_put(msg, NM_ATT_MAIO, 0x00);
+ if (bts->type == GSM_BTS_TYPE_BS11) {
+ msgb_tv_put(msg, NM_ATT_HSN, 0x00);
+ msgb_tv_put(msg, NM_ATT_MAIO, 0x00);
+ }
msgb_tv_put(msg, NM_ATT_TSC, 0x07); /* training sequence */
- msgb_tlv_put(msg, 0x59, 1, &zero);
+ if (bts->type == GSM_BTS_TYPE_BS11)
+ msgb_tlv_put(msg, 0x59, 1, &zero);
return abis_nm_sendmsg(bts, msg);
}
diff --git a/src/tlv_parser.c b/src/tlv_parser.c
index b790d3cd8..f047d0bbc 100644
--- a/src/tlv_parser.c
+++ b/src/tlv_parser.c
@@ -1,6 +1,19 @@
+#include <stdio.h>
#include <openbsc/tlv.h>
-int tlv_parse(struct tlv_parsed *dec, u_int8_t *buf, int buf_len)
+int tlv_dump(struct tlv_parsed *dec)
+{
+ int i;
+
+ for (i = 0; i <= 0xff; i++) {
+ if (!dec->lv[i].val)
+ continue;
+ printf("T=%02x L=%d\n", i, dec->lv[i].len);
+ }
+ return 0;
+}
+
+int tlv_parse(struct tlv_parsed *dec, const struct tlv_definition *def, u_int8_t *buf, int buf_len)
{
u_int8_t tag, len = 1;
u_int8_t *pos;
@@ -11,13 +24,27 @@ int tlv_parse(struct tlv_parsed *dec, u_int8_t *buf, int buf_len)
for (pos = buf; pos < buf+buf_len; pos += len) {
tag = *pos;
/* FIXME: use tables for knwon IEI */
- if (tag & 0x80) {
+ switch (def->def[tag].type) {
+ case TLV_TYPE_T:
/* GSM TS 04.07 11.2.4: Type 1 TV or Type 2 T */
dec->lv[tag].val = pos;
dec->lv[tag].len = 0;
len = 1;
num_parsed++;
- } else {
+ break;
+ case TLV_TYPE_TV:
+ dec->lv[tag].val = pos+1;
+ dec->lv[tag].len = 1;
+ len = 2;
+ num_parsed++;
+ break;
+ case TLV_TYPE_FIXED:
+ dec->lv[tag].val = pos+1;
+ dec->lv[tag].len = def->def[tag].fixed_len;
+ len = def->def[tag].fixed_len + 1;
+ num_parsed++;
+ break;
+ case TLV_TYPE_TLV:
/* GSM TS 04.07 11.2.4: Type 4 TLV */
if (pos + 1 > buf + buf_len)
return -1;
@@ -27,7 +54,20 @@ int tlv_parse(struct tlv_parsed *dec, u_int8_t *buf, int buf_len)
if (pos + len > buf + buf_len)
return -2;
num_parsed++;
+ break;
+ case TLV_TYPE_TL16V:
+ if (pos + 2 > buf + buf_len)
+ return -1;
+ dec->lv[tag].val = pos+3;
+ dec->lv[tag].len = *(pos+1) << 8 | *(pos+2);
+ len = dec->lv[tag].len + 3;
+ if (pos + len > buf + buf_len)
+ return -2;
+ num_parsed++;
+ break;
}
}
+ //tlv_dump(dec);
return num_parsed;
}
+