diff options
-rw-r--r-- | include/openbsc/abis_nm.h | 46 | ||||
-rw-r--r-- | include/openbsc/tlv.h | 21 | ||||
-rw-r--r-- | src/abis_nm.c | 328 | ||||
-rw-r--r-- | src/tlv_parser.c | 46 |
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; } + |