diff options
-rw-r--r-- | openbsc/include/openbsc/abis_om2000.h | 11 | ||||
-rw-r--r-- | openbsc/include/openbsc/gsm_data_shared.h | 39 | ||||
-rw-r--r-- | openbsc/src/libbsc/abis_om2000.c | 1256 | ||||
-rw-r--r-- | openbsc/src/libbsc/bts_ericsson_rbs2000.c | 112 |
4 files changed, 1132 insertions, 286 deletions
diff --git a/openbsc/include/openbsc/abis_om2000.h b/openbsc/include/openbsc/abis_om2000.h index 2ff72705b..5a7344f40 100644 --- a/openbsc/include/openbsc/abis_om2000.h +++ b/openbsc/include/openbsc/abis_om2000.h @@ -41,13 +41,6 @@ enum om2k_mo_state { OM2K_MO_S_DISABLED, }; -struct abis_om2k_mo { - uint8_t class; - uint8_t bts; - uint8_t assoc_so; - uint8_t inst; -} __attribute__ ((packed)); - /* on-wire format for IS conn group */ struct om2k_is_conn_grp { uint16_t icp1; @@ -90,6 +83,10 @@ int abis_om2k_tx_rx_conf_req(struct gsm_bts_trx *trx); int abis_om2k_tx_tx_conf_req(struct gsm_bts_trx *trx); int abis_om2k_tx_ts_conf_req(struct gsm_bts_trx_ts *ts); +struct osmo_fsm_inst *om2k_bts_fsm_start(struct gsm_bts *bts); +void abis_om2k_bts_init(struct gsm_bts *bts); +void abis_om2k_trx_init(struct gsm_bts_trx *trx); + int abis_om2k_vty_init(void); struct vty; diff --git a/openbsc/include/openbsc/gsm_data_shared.h b/openbsc/include/openbsc/gsm_data_shared.h index 4472310e3..97fde835f 100644 --- a/openbsc/include/openbsc/gsm_data_shared.h +++ b/openbsc/include/openbsc/gsm_data_shared.h @@ -104,6 +104,19 @@ struct gsm_abis_mo { struct gsm_bts *bts; }; +/* Ericsson OM2000 Managed Object */ +struct abis_om2k_mo { + uint8_t class; + uint8_t bts; + uint8_t assoc_so; + uint8_t inst; +} __attribute__ ((packed)); + +struct om2k_mo { + struct abis_om2k_mo addr; + struct osmo_fsm_inst *fsm; +}; + #define MAX_A5_KEY_LEN (128/8) #define A38_XOR_MIN_KEY_LEN 12 #define A38_XOR_MAX_KEY_LEN 16 @@ -387,6 +400,12 @@ struct gsm_bts_trx_ts { /* To which E1 subslot are we connected */ struct gsm_e1_subslot e1_link; + union { + struct { + struct om2k_mo om2k_mo; + } rbs2000; + }; + struct gsm_lchan lchan[TS_MAX_LCHAN]; }; @@ -441,6 +460,17 @@ struct gsm_bts_trx { uint8_t test_nr; struct rxlev_stats rxlev_stat; } ipaccess; + struct { + struct { + struct om2k_mo om2k_mo; + } trxc; + struct { + struct om2k_mo om2k_mo; + } rx; + struct { + struct om2k_mo om2k_mo; + } tx; + } rbs2000; }; struct gsm_bts_trx_ts ts[TRX_NR_TS]; }; @@ -679,17 +709,26 @@ struct gsm_bts { } bs11; struct { struct { + struct om2k_mo om2k_mo; + struct gsm_abis_mo mo; + struct llist_head conn_groups; + } cf; + struct { + struct om2k_mo om2k_mo; struct gsm_abis_mo mo; struct llist_head conn_groups; } is; struct { + struct om2k_mo om2k_mo; struct gsm_abis_mo mo; struct llist_head conn_groups; } con; struct { + struct om2k_mo om2k_mo; struct gsm_abis_mo mo; } dp; struct { + struct om2k_mo om2k_mo; struct gsm_abis_mo mo; } tf; } rbs2000; diff --git a/openbsc/src/libbsc/abis_om2000.c b/openbsc/src/libbsc/abis_om2000.c index 1745a72e0..58d86c64a 100644 --- a/openbsc/src/libbsc/abis_om2000.c +++ b/openbsc/src/libbsc/abis_om2000.c @@ -1,7 +1,7 @@ /* Ericsson RBS 2xxx GSM O&M (OM2000) messages on the A-bis interface * implemented based on protocol trace analysis, no formal documentation */ -/* (C) 2010-2011 by Harald Welte <laforge@gnumonks.org> +/* (C) 2010-2011,2016 by Harald Welte <laforge@gnumonks.org> * * All Rights Reserved * @@ -34,6 +34,7 @@ #include <osmocom/gsm/tlv.h> #include <osmocom/core/talloc.h> #include <osmocom/core/utils.h> +#include <osmocom/core/fsm.h> #include <openbsc/gsm_data.h> #include <openbsc/debug.h> @@ -42,9 +43,39 @@ #include <openbsc/signal.h> #include <osmocom/abis/e1_input.h> +/* FIXME: move to libosmocore */ +struct osmo_fsm_inst *osmo_fsm_inst_alloc_child_id(struct osmo_fsm *fsm, + struct osmo_fsm_inst *parent, + uint32_t parent_term_event, + const char *id) +{ + struct osmo_fsm_inst *fi; + + fi = osmo_fsm_inst_alloc(fsm, parent, NULL, parent->log_level, + id ? id : parent->id); + if (!fi) { + /* indicate immediate termination to caller */ + osmo_fsm_inst_dispatch(parent, parent_term_event, NULL); + return NULL; + } + + LOGPFSM(fi, "is child of %s\n", osmo_fsm_inst_name(parent)); + + fi->proc.parent = parent; + fi->proc.parent_term_event = parent_term_event; + llist_add(&fi->proc.child, &parent->proc.children); + + return fi; +} + + #define OM_ALLOC_SIZE 1024 #define OM_HEADROOM_SIZE 128 +#define OM2K_TIMEOUT 10 +#define TRX_FSM_TIMEOUT 60 +#define BTS_FSM_TIMEOUT 60 + /* use following functions from abis_nm.c: * om2k_msgb_alloc() * abis_om2k_sendmsg() @@ -625,6 +656,96 @@ const struct value_string om2k_mo_class_short_vals[] = { { 0, NULL } }; +const struct value_string om2k_result_strings[] = { + { 0x02, "Wrong state or out of sequence" }, + { 0x03, "File error" }, + { 0x04, "Fault, unspecified" }, + { 0x05, "Tuning fault" }, + { 0x06, "Protocol error" }, + { 0x07, "MO not connected" }, + { 0x08, "Parameter error" }, + { 0x09, "Optional function not supported" }, + { 0x0a, "Local access state LOCALLY DISCONNECTED" }, + { 0, NULL } +}; + +const struct value_string om2k_accordance_strings[] = { + { 0x00, "Data according to request" }, + { 0x01, "Data not according to request" }, + { 0x02, "Inconsistent MO data" }, + { 0x03, "Capability constraint violation" }, + { 0, NULL } +}; + +const struct value_string om2k_mostate_vals[] = { + { 0x00, "RESET" }, + { 0x01, "STARTED" }, + { 0x02, "ENABLED" }, + { 0x03, "DISABLED" }, + { 0, NULL } +}; + +/* entire decoded OM2K message (header + parsed TLV) */ +struct om2k_decoded_msg { + struct abis_om2k_hdr o2h; + uint16_t msg_type; + struct tlv_parsed tp; +}; + +/* resolve the OM2000 Managed Object by BTS + MO Address */ +static struct om2k_mo * +get_om2k_mo(struct gsm_bts *bts, const struct abis_om2k_mo *abis_mo) +{ + struct om2k_mo *mo = NULL; + struct gsm_bts_trx *trx; + + switch (abis_mo->class) { + case OM2K_MO_CLS_CF: + mo = &bts->rbs2000.cf.om2k_mo; + break; + case OM2K_MO_CLS_CON: + mo = &bts->rbs2000.con.om2k_mo; + break; + case OM2K_MO_CLS_IS: + mo = &bts->rbs2000.is.om2k_mo; + break; + case OM2K_MO_CLS_TF: + mo = &bts->rbs2000.tf.om2k_mo; + break; + + case OM2K_MO_CLS_TRXC: + trx = gsm_bts_trx_num(bts, abis_mo->inst); + if (!trx) + return NULL; + mo = &trx->rbs2000.trxc.om2k_mo; + break; + case OM2K_MO_CLS_TX: + trx = gsm_bts_trx_num(bts, abis_mo->inst); + if (!trx) + return NULL; + mo = &trx->rbs2000.tx.om2k_mo; + break; + case OM2K_MO_CLS_RX: + trx = gsm_bts_trx_num(bts, abis_mo->inst); + if (!trx) + return NULL; + mo = &trx->rbs2000.rx.om2k_mo; + break; + case OM2K_MO_CLS_TS: + trx = gsm_bts_trx_num(bts, abis_mo->assoc_so); + if (!trx) + return NULL; + if (abis_mo->inst >= ARRAY_SIZE(trx->ts)) + return NULL; + mo = &trx->ts[abis_mo->inst].rbs2000.om2k_mo; + break; + default: + return NULL; + }; + + return mo; +} + static struct msgb *om2k_msgb_alloc(void) { return msgb_alloc_headroom(OM_ALLOC_SIZE, OM_HEADROOM_SIZE, @@ -641,6 +762,15 @@ static int abis_om2k_msg_tlv_parse(struct tlv_parsed *tp, struct abis_om2k_hdr * return abis_om2k_tlv_parse(tp, oh->data, oh->om.length - 6); } +/* decode/parse the message */ +static int om2k_decode_msg(struct om2k_decoded_msg *odm, struct msgb *msg) +{ + struct abis_om2k_hdr *o2h = msgb_l2(msg); + odm->msg_type = ntohs(o2h->msg_type); + odm->o2h = *o2h; + return abis_om2k_msg_tlv_parse(&odm->tp, o2h); +} + static char *om2k_mo_name(const struct abis_om2k_mo *mo) { static char mo_buf[64]; @@ -783,23 +913,6 @@ static void update_op_state(struct gsm_bts *bts, const struct abis_om2k_mo *mo, nm_state->operational = new_state.operational; } -static void signal_op_state(struct gsm_bts *bts, struct abis_om2k_mo *mo) -{ - struct gsm_nm_state *nm_state = mo2nm_state(bts, mo); - struct nm_statechg_signal_data nsd; - - if (!nm_state) - return; - - nsd.bts = bts; - nsd.obj = mo2obj(bts, mo); - nsd.old_state = nm_state; - nsd.new_state = nm_state; - nsd.om2k_mo = mo; - - osmo_signal_dispatch(SS_NM, S_NM_STATECHG_OPER, &nsd); -} - static int abis_om2k_sendmsg(struct gsm_bts *bts, struct msgb *msg) { struct abis_om2k_hdr *o2h; @@ -854,11 +967,6 @@ static void fill_om2k_hdr(struct abis_om2k_hdr *o2h, const struct abis_om2k_mo * memcpy(&o2h->mo, mo, sizeof(o2h->mo)); } -const struct abis_om2k_mo om2k_mo_cf = { OM2K_MO_CLS_CF, 0, 0xFF, 0 }; -const struct abis_om2k_mo om2k_mo_is = { OM2K_MO_CLS_IS, 0, 0xFF, 0 }; -const struct abis_om2k_mo om2k_mo_con = { OM2K_MO_CLS_CON, 0, 0xFF, 0 }; -const struct abis_om2k_mo om2k_mo_tf = { OM2K_MO_CLS_TF, 0, 0xFF, 0 }; - static int abis_om2k_cal_time_resp(struct gsm_bts *bts) { struct msgb *msg = om2k_msgb_alloc(); @@ -867,7 +975,8 @@ static int abis_om2k_cal_time_resp(struct gsm_bts *bts) struct tm *tm; o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); - fill_om2k_hdr(o2k, &om2k_mo_cf, OM2K_MSGT_CAL_TIME_RESP); + fill_om2k_hdr(o2k, &bts->rbs2000.cf.om2k_mo.addr, + OM2K_MSGT_CAL_TIME_RESP); tm_t = time(NULL); tm = localtime(&tm_t); @@ -989,7 +1098,8 @@ int abis_om2k_tx_is_conf_req(struct gsm_bts *bts) om2k_fill_is_conn_grp(&cg[i++], grp->icp1, grp->icp2, grp->ci); o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); - fill_om2k_hdr(o2k, &om2k_mo_is, OM2K_MSGT_IS_CONF_REQ); + fill_om2k_hdr(o2k, &bts->rbs2000.is.om2k_mo.addr, + OM2K_MSGT_IS_CONF_REQ); msgb_tv_put(msg, OM2K_DEI_LIST_NR, 1); msgb_tv_put(msg, OM2K_DEI_END_LIST_NR, 1); @@ -999,7 +1109,8 @@ int abis_om2k_tx_is_conf_req(struct gsm_bts *bts) talloc_free(cg); - DEBUGP(DNM, "Tx MO=%s %s\n", om2k_mo_name(&om2k_mo_is), + DEBUGP(DNM, "Tx MO=%s %s\n", + om2k_mo_name(&bts->rbs2000.is.om2k_mo.addr), get_value_string(om2k_msgcode_vals, OM2K_MSGT_IS_CONF_REQ)); return abis_om2k_sendmsg(bts, msg); @@ -1012,14 +1123,16 @@ int abis_om2k_tx_con_conf_req(struct gsm_bts *bts, uint8_t *data, struct abis_om2k_hdr *o2k; o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); - fill_om2k_hdr(o2k, &om2k_mo_con, OM2K_MSGT_CON_CONF_REQ); + fill_om2k_hdr(o2k, &bts->rbs2000.con.om2k_mo.addr, + OM2K_MSGT_CON_CONF_REQ); msgb_tv_put(msg, OM2K_DEI_LIST_NR, 1); msgb_tv_put(msg, OM2K_DEI_END_LIST_NR, 1); msgb_tlv_put(msg, OM2K_DEI_CON_CONN_LIST, len, data); - DEBUGP(DNM, "Tx MO=%s %s\n", om2k_mo_name(&om2k_mo_con), + DEBUGP(DNM, "Tx MO=%s %s\n", + om2k_mo_name(&bts->rbs2000.con.om2k_mo.addr), get_value_string(om2k_msgcode_vals, OM2K_MSGT_CON_CONF_REQ)); return abis_om2k_sendmsg(bts, msg); @@ -1098,14 +1211,16 @@ int abis_om2k_tx_tf_conf_req(struct gsm_bts *bts) struct abis_om2k_hdr *o2k; o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); - fill_om2k_hdr(o2k, &om2k_mo_tf, OM2K_MSGT_TF_CONF_REQ); + fill_om2k_hdr(o2k, &bts->rbs2000.tf.om2k_mo.addr, + OM2K_MSGT_TF_CONF_REQ); msgb_tv_put(msg, OM2K_DEI_TF_MODE, OM2K_TF_MODE_STANDALONE); msgb_tv_put(msg, OM2K_DEI_TF_SYNC_SRC, 0x00); msgb_tv_fixed_put(msg, OM2K_DEI_FS_OFFSET, sizeof(fs_offset_undef), fs_offset_undef); - DEBUGP(DNM, "Tx MO=%s %s\n", om2k_mo_name(&om2k_mo_tf), + DEBUGP(DNM, "Tx MO=%s %s\n", + om2k_mo_name(&bts->rbs2000.tf.om2k_mo.addr), get_value_string(om2k_msgcode_vals, OM2K_MSGT_TF_CONF_REQ)); return abis_om2k_sendmsg(bts, msg); @@ -1256,9 +1371,860 @@ int abis_om2k_tx_ts_conf_req(struct gsm_bts_trx_ts *ts) break; } + DEBUGP(DNM, "Tx MO=%s %s\n", + om2k_mo_name(&mo), + get_value_string(om2k_msgcode_vals, OM2K_MSGT_TS_CONF_REQ)); + return abis_om2k_sendmsg(ts->trx->bts, msg); } + +/*********************************************************************** + * OM2000 Managed Object (MO) FSM + ***********************************************************************/ + +#define S(x) (1 << (x)) + +enum om2k_event_name { + OM2K_MO_EVT_START, + OM2K_MO_EVT_RX_CONN_COMPL, + OM2K_MO_EVT_RX_RESET_COMPL, + OM2K_MO_EVT_RX_START_REQ_ACCEPT, + OM2K_MO_EVT_RX_START_RES, + OM2K_MO_EVT_RX_CFG_REQ_ACCEPT, + OM2K_MO_EVT_RX_CFG_RES, + OM2K_MO_EVT_RX_ENA_REQ_ACCEPT, + OM2K_MO_EVT_RX_ENA_RES, + OM2K_MO_EVT_RX_OPINFO_ACC, +}; + +static const struct value_string om2k_event_names[] = { + { OM2K_MO_EVT_START, "START" }, + { OM2K_MO_EVT_RX_CONN_COMPL, "RX-CONN-COMPL" }, + { OM2K_MO_EVT_RX_RESET_COMPL, "RX-RESET-COMPL" }, + { OM2K_MO_EVT_RX_START_REQ_ACCEPT, "RX-RESET-REQ-ACCEPT" }, + { OM2K_MO_EVT_RX_START_RES, "RX-START-RESULT" }, + { OM2K_MO_EVT_RX_CFG_REQ_ACCEPT, "RX-CFG-REQ-ACCEPT" }, + { OM2K_MO_EVT_RX_CFG_RES, "RX-CFG-RESULT" }, + { OM2K_MO_EVT_RX_ENA_REQ_ACCEPT, "RX-ENABLE-REQ-ACCEPT" }, + { OM2K_MO_EVT_RX_ENA_RES, "RX-ENABLE-RESULT" }, + { OM2K_MO_EVT_RX_OPINFO_ACC, "RX-OPINFO-ACCEPT" }, + { 0, NULL } +}; + +enum om2k_mo_fsm_state { + OM2K_ST_INIT, + OM2K_ST_WAIT_CONN_COMPL, + OM2K_ST_WAIT_RES_COMPL, + OM2K_ST_WAIT_START_ACCEPT, + OM2K_ST_WAIT_START_RES, + OM2K_ST_WAIT_CFG_ACCEPT, + OM2K_ST_WAIT_CFG_RES, + OM2K_ST_WAIT_ENABLE_ACCEPT, + OM2K_ST_WAIT_ENABLE_RES, + OM2K_ST_WAIT_OPINFO_ACCEPT, + OM2K_ST_DONE, + OM2K_ST_ERROR, +}; + +struct om2k_mo_fsm_priv { + struct gsm_bts_trx *trx; + struct om2k_mo *mo; + uint8_t ts_nr; +}; + +static void om2k_mo_st_init(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct om2k_mo_fsm_priv *omfp = fi->priv; + + OSMO_ASSERT(event == OM2K_MO_EVT_START); + + switch (omfp->mo->addr.class) { + case OM2K_MO_CLS_CF: + /* no Connect required, is always connected */ + osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_START_ACCEPT, + OM2K_TIMEOUT, 0); + abis_om2k_tx_start_req(omfp->trx->bts, &omfp->mo->addr); + break; + case OM2K_MO_CLS_TRXC: + /* no Connect required, start with Reset */ + osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_RES_COMPL, + OM2K_TIMEOUT, 0); + abis_om2k_tx_reset_cmd(omfp->trx->bts, &omfp->mo->addr); + break; + default: + /* start with Connect */ + osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_CONN_COMPL, + OM2K_TIMEOUT, 0); + abis_om2k_tx_connect_cmd(omfp->trx->bts, &omfp->mo->addr); + break; + } +} + +static void om2k_mo_st_wait_conn_compl(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct om2k_mo_fsm_priv *omfp = fi->priv; + + switch (omfp->mo->addr.class) { +#if 0 + case OM2K_MO_CLS_TF: + /* skip the reset, hope that helps */ + osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_START_ACCEPT, + OM2K_TIMEOUT, 0); + abis_om2k_tx_start_req(omfp->trx->bts, &omfp->mo->addr); + break; +#endif + default: + osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_RES_COMPL, + OM2K_TIMEOUT, 0); + abis_om2k_tx_reset_cmd(omfp->trx->bts, &omfp->mo->addr); + break; + } +} + +static void om2k_mo_st_wait_res_compl(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct om2k_mo_fsm_priv *omfp = fi->priv; + + osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_START_ACCEPT, + OM2K_TIMEOUT, 0); + abis_om2k_tx_start_req(omfp->trx->bts, &omfp->mo->addr); +} + +static void om2k_mo_st_wait_start_accept(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct om2k_decoded_msg *omd = data; + + switch (omd->msg_type) { + case OM2K_MSGT_START_REQ_ACK: + osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_START_RES, + OM2K_TIMEOUT, 0); + break; + case OM2K_MSGT_START_REQ_REJ: + osmo_fsm_inst_state_chg(fi, OM2K_ST_ERROR, 0, 0); + break; + } +} + +static void om2k_mo_st_wait_start_res(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct om2k_mo_fsm_priv *omfp = fi->priv; + struct gsm_bts_trx_ts *ts; + + switch (omfp->mo->addr.class) { + case OM2K_MO_CLS_CF: + case OM2K_MO_CLS_TRXC: + /* Transition directly to Operational Info */ + osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_OPINFO_ACCEPT, + OM2K_TIMEOUT, 0); + abis_om2k_tx_op_info(omfp->trx->bts, &omfp->mo->addr, 1); + break; + case OM2K_MO_CLS_DP: + /* Transition directoy to WAIT_ENABLE_ACCEPT */ + osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_ENABLE_ACCEPT, + OM2K_TIMEOUT, 0); + abis_om2k_tx_enable_req(omfp->trx->bts, &omfp->mo->addr); + return; +#if 0 + case OM2K_MO_CLS_TF: + /* skip the config, hope that helps speeding things up */ + osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_ENABLE_ACCEPT, + OM2K_TIMEOUT, 0); + abis_om2k_tx_enable_req(omfp->trx->bts, &omfp->mo->addr); + return; +#endif + } + + osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_CFG_ACCEPT, + OM2K_TIMEOUT, 0); + switch (omfp->mo->addr.class) { + case OM2K_MO_CLS_TF: + abis_om2k_tx_tf_conf_req(omfp->trx->bts); + break; + case OM2K_MO_CLS_IS: + abis_om2k_tx_is_conf_req(omfp->trx->bts); + break; + case OM2K_MO_CLS_CON: + /* TODO */ + //abis_om2k_tx_con_conf_req(omfp->trx->bts, data, len); + break; + case OM2K_MO_CLS_TX: + abis_om2k_tx_tx_conf_req(omfp->trx); + break; + case OM2K_MO_CLS_RX: + abis_om2k_tx_rx_conf_req(omfp->trx); + break; + case OM2K_MO_CLS_TS: + ts = mo2obj(omfp->trx->bts, &omfp->mo->addr); + abis_om2k_tx_ts_conf_req(ts); + break; + } +} + +static void om2k_mo_st_wait_cfg_accept(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct om2k_mo_fsm_priv *omfp = fi->priv; + uint32_t timeout = OM2K_TIMEOUT; + + if (omfp->mo->addr.class == OM2K_MO_CLS_TF) + timeout = 600; + + osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_CFG_RES, timeout, 0); +} + +static void om2k_mo_st_wait_cfg_res(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct om2k_mo_fsm_priv *omfp = fi->priv; + struct om2k_decoded_msg *omd = data; + uint8_t accordance; + + if (!TLVP_PRESENT(&omd->tp, OM2K_DEI_ACCORDANCE_IND)) { + osmo_fsm_inst_state_chg(fi, OM2K_ST_ERROR, 0, 0); + return; + } + accordance = *TLVP_VAL(&omd->tp, OM2K_DEI_ACCORDANCE_IND); + + if (accordance != 0) { + /* accordance not OK */ + osmo_fsm_inst_state_chg(fi, OM2K_ST_ERROR, 0, 0); + return; + } + + osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_ENABLE_ACCEPT, + OM2K_TIMEOUT, 0); + abis_om2k_tx_enable_req(omfp->trx->bts, &omfp->mo->addr); +} + +static void om2k_mo_st_wait_enable_accept(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct om2k_decoded_msg *omd = data; + + switch (omd->msg_type) { + case OM2K_MSGT_ENABLE_REQ_REJ: + osmo_fsm_inst_state_chg(fi, OM2K_ST_ERROR, 0, 0); + break; + case OM2K_MSGT_ENABLE_REQ_ACK: + osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_ENABLE_RES, + OM2K_TIMEOUT, 0); + } +} + +static void om2k_mo_st_wait_enable_res(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct om2k_mo_fsm_priv *omfp = fi->priv; + //struct om2k_decoded_msg *omd = data; + /* TODO: check if state is actually enabled now? */ + + osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_OPINFO_ACCEPT, + OM2K_TIMEOUT, 0); + abis_om2k_tx_op_info(omfp->trx->bts, &omfp->mo->addr, 1); +} + +static void om2k_mo_st_wait_opinfo_accept(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + osmo_fsm_inst_state_chg(fi, OM2K_ST_DONE, 0, 0); +} + +static void om2k_mo_s_done_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL); +} + +static const struct osmo_fsm_state om2k_is_states[] = { + [OM2K_ST_INIT] = { + .name = "INIT", + .in_event_mask = S(OM2K_MO_EVT_START), + .out_state_mask = S(OM2K_ST_DONE) | + S(OM2K_ST_ERROR) | + S(OM2K_ST_WAIT_CONN_COMPL) | + S(OM2K_ST_WAIT_START_ACCEPT) | + S(OM2K_ST_WAIT_RES_COMPL), + .action = om2k_mo_st_init, + }, + [OM2K_ST_WAIT_CONN_COMPL] = { + .name = "WAIT-CONN-COMPL", + .in_event_mask = S(OM2K_MO_EVT_RX_CONN_COMPL), + .out_state_mask = S(OM2K_ST_DONE) | + S(OM2K_ST_ERROR) | + S(OM2K_ST_WAIT_START_ACCEPT) | + S(OM2K_ST_WAIT_RES_COMPL), + .action = om2k_mo_st_wait_conn_compl, + }, + [OM2K_ST_WAIT_RES_COMPL] = { + .name = "WAIT-RES-COMPL", + .in_event_mask = S(OM2K_MO_EVT_RX_RESET_COMPL), + .out_state_mask = S(OM2K_ST_DONE) | + S(OM2K_ST_ERROR) | + S(OM2K_ST_WAIT_START_ACCEPT), + .action = om2k_mo_st_wait_res_compl, + }, + [OM2K_ST_WAIT_START_ACCEPT] = { + .name = "WAIT-START-ACCEPT", + .in_event_mask = S(OM2K_MO_EVT_RX_START_REQ_ACCEPT), + .out_state_mask = S(OM2K_ST_DONE) | + S(OM2K_ST_ERROR) | + S(OM2K_ST_WAIT_START_RES), + .action =om2k_mo_st_wait_start_accept, + }, + [OM2K_ST_WAIT_START_RES] = { + .name = "WAIT-START-RES", + .in_event_mask = S(OM2K_MO_EVT_RX_START_RES), + .out_state_mask = S(OM2K_ST_DONE) | + S(OM2K_ST_ERROR) | + S(OM2K_ST_WAIT_CFG_ACCEPT) | + S(OM2K_ST_WAIT_OPINFO_ACCEPT), + .action = om2k_mo_st_wait_start_res, + }, + [OM2K_ST_WAIT_CFG_ACCEPT] = { + .name = "WAIT-CFG-ACCEPT", + .in_event_mask = S(OM2K_MO_EVT_RX_CFG_REQ_ACCEPT), + .out_state_mask = S(OM2K_ST_DONE) | + S(OM2K_ST_ERROR) | + S(OM2K_ST_WAIT_CFG_RES), + .action = om2k_mo_st_wait_cfg_accept, + }, + [OM2K_ST_WAIT_CFG_RES] = { + .name = "WAIT-CFG-RES", + .in_event_mask = S(OM2K_MO_EVT_RX_CFG_RES), + .out_state_mask = S(OM2K_ST_DONE) | + S(OM2K_ST_ERROR) | + S(OM2K_ST_WAIT_ENABLE_ACCEPT), + .action = om2k_mo_st_wait_cfg_res, + }, + [OM2K_ST_WAIT_ENABLE_ACCEPT] = { + .name = "WAIT-ENABLE-ACCEPT", + .in_event_mask = S(OM2K_MO_EVT_RX_ENA_REQ_ACCEPT), + .out_state_mask = S(OM2K_ST_DONE) | + S(OM2K_ST_ERROR) | + S(OM2K_ST_WAIT_ENABLE_RES), + .action = om2k_mo_st_wait_enable_accept, + }, + [OM2K_ST_WAIT_ENABLE_RES] = { + .name = "WAIT-ENABLE-RES", + .in_event_mask = S(OM2K_MO_EVT_RX_ENA_RES), + .out_state_mask = S(OM2K_ST_DONE) | + S(OM2K_ST_ERROR) | + S(OM2K_ST_WAIT_OPINFO_ACCEPT), + .action = om2k_mo_st_wait_enable_res, + }, + [OM2K_ST_WAIT_OPINFO_ACCEPT] = { + .name = "WAIT-OPINFO-ACCEPT", + .in_event_mask = S(OM2K_MO_EVT_RX_OPINFO_ACC), + .out_state_mask = S(OM2K_ST_DONE) | + S(OM2K_ST_ERROR), + .action = om2k_mo_st_wait_opinfo_accept, + }, + [OM2K_ST_DONE] = { + .name = "DONE", + .in_event_mask = 0, + .out_state_mask = 0, + .onenter = om2k_mo_s_done_onenter, + }, + [OM2K_ST_ERROR] = { + .name = "ERROR", + .in_event_mask = 0, + .out_state_mask = 0, + .onenter = om2k_mo_s_done_onenter, + }, + +}; + +static int om2k_mo_timer_cb(struct osmo_fsm_inst *fi) +{ + osmo_fsm_inst_state_chg(fi, OM2K_ST_ERROR, 0, 0); + return 0; +} + +static struct osmo_fsm om2k_mo_fsm = { + .name = "OM2000-MO", + .states = om2k_is_states, + .num_states = ARRAY_SIZE(om2k_is_states), + .log_subsys = DNM, + .event_names = om2k_event_names, + .timer_cb = om2k_mo_timer_cb, +}; + +struct osmo_fsm_inst *om2k_mo_fsm_start(struct osmo_fsm_inst *parent, + uint32_t term_event, + struct gsm_bts_trx *trx, struct om2k_mo *mo) +{ + struct osmo_fsm_inst *fi; + struct om2k_mo_fsm_priv *omfp; + char idbuf[64]; + + snprintf(idbuf, sizeof(idbuf), "%s-%s", parent->id, + om2k_mo_name(&mo->addr)); + + fi = osmo_fsm_inst_alloc_child_id(&om2k_mo_fsm, parent, + term_event, idbuf); + if (!fi) + return NULL; + + mo->fsm = fi; + omfp = talloc_zero(fi, struct om2k_mo_fsm_priv); + omfp->mo = mo; + omfp->trx = trx; + fi->priv = omfp; + + osmo_fsm_inst_dispatch(fi, OM2K_MO_EVT_START, NULL); + + return fi; +} + +int om2k_mo_fsm_recvmsg(struct gsm_bts *bts, struct om2k_mo *mo, + struct om2k_decoded_msg *odm) +{ + switch (odm->msg_type) { + case OM2K_MSGT_CONNECT_COMPL: + case OM2K_MSGT_CONNECT_REJ: + osmo_fsm_inst_dispatch(mo->fsm, + OM2K_MO_EVT_RX_CONN_COMPL, odm); + break; + + case OM2K_MSGT_RESET_COMPL: + case OM2K_MSGT_RESET_REJ: + osmo_fsm_inst_dispatch(mo->fsm, + OM2K_MO_EVT_RX_RESET_COMPL, odm); + break; + + case OM2K_MSGT_START_REQ_ACK: + case OM2K_MSGT_START_REQ_REJ: + osmo_fsm_inst_dispatch(mo->fsm, + OM2K_MO_EVT_RX_START_REQ_ACCEPT, odm); + break; + + case OM2K_MSGT_START_RES: + osmo_fsm_inst_dispatch(mo->fsm, + OM2K_MO_EVT_RX_START_RES, odm); + break; + + case OM2K_MSGT_CON_CONF_REQ_ACK: + case OM2K_MSGT_IS_CONF_REQ_ACK: + case OM2K_MSGT_RX_CONF_REQ_ACK: + case OM2K_MSGT_TF_CONF_REQ_ACK: + case OM2K_MSGT_TS_CONF_REQ_ACK: + case OM2K_MSGT_TX_CONF_REQ_ACK: + osmo_fsm_inst_dispatch(mo->fsm, + OM2K_MO_EVT_RX_CFG_REQ_ACCEPT, odm); + break; + + case OM2K_MSGT_CON_CONF_RES: + case OM2K_MSGT_IS_CONF_RES: + case OM2K_MSGT_RX_CONF_RES: + case OM2K_MSGT_TF_CONF_RES: + case OM2K_MSGT_TS_CONF_RES: + case OM2K_MSGT_TX_CONF_RES: + osmo_fsm_inst_dispatch(mo->fsm, + OM2K_MO_EVT_RX_CFG_RES, odm); + break; + + case OM2K_MSGT_ENABLE_REQ_ACK: + case OM2K_MSGT_ENABLE_REQ_REJ: + osmo_fsm_inst_dispatch(mo->fsm, + OM2K_MO_EVT_RX_ENA_REQ_ACCEPT, odm); + break; + case OM2K_MSGT_ENABLE_RES: + osmo_fsm_inst_dispatch(mo->fsm, + OM2K_MO_EVT_RX_ENA_RES, odm); + break; + + case OM2K_MSGT_OP_INFO_ACK: + case OM2K_MSGT_OP_INFO_REJ: + osmo_fsm_inst_dispatch(mo->fsm, + OM2K_MO_EVT_RX_OPINFO_ACC, odm); + break; + default: + return -1; + } + + return 0; +} + +/*********************************************************************** + * OM2000 TRX Finite State Machine, initializes TRXC and all siblings + ***********************************************************************/ + +enum om2k_trx_event { + OM2K_TRX_EVT_START, + OM2K_TRX_EVT_TRXC_DONE, + OM2K_TRX_EVT_TX_DONE, + OM2K_TRX_EVT_RX_DONE, + OM2K_TRX_EVT_TS_DONE, + OM2K_TRX_EVT_STOP, +}; + +static struct value_string om2k_trx_events[] = { + { OM2K_TRX_EVT_START, "START" }, + { OM2K_TRX_EVT_TRXC_DONE, "TRXC-DONE" }, + { OM2K_TRX_EVT_TX_DONE, "TX-DONE" }, + { OM2K_TRX_EVT_RX_DONE, "RX-DONE" }, + { OM2K_TRX_EVT_TS_DONE, "TS-DONE" }, + { OM2K_TRX_EVT_STOP, "STOP" }, + { 0, NULL } +}; + +enum om2k_trx_state { + OM2K_TRX_S_INIT, + OM2K_TRX_S_WAIT_TRXC, + OM2K_TRX_S_WAIT_TX, + OM2K_TRX_S_WAIT_RX, + OM2K_TRX_S_WAIT_TS, + OM2K_TRX_S_DONE, + OM2K_TRX_S_ERROR +}; + +struct om2k_trx_fsm_priv { + struct gsm_bts_trx *trx; + uint8_t next_ts_nr; +}; + +static void om2k_trx_s_init(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct om2k_trx_fsm_priv *otfp = fi->priv; + + /* First initialize TRXC */ + osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_WAIT_TRXC, + TRX_FSM_TIMEOUT, 0); + om2k_mo_fsm_start(fi, OM2K_TRX_EVT_TRXC_DONE, otfp->trx, + &otfp->trx->rbs2000.trxc.om2k_mo); +} + +static void om2k_trx_s_wait_trxc(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct om2k_trx_fsm_priv *otfp = fi->priv; + + /* Initialize TX after TRXC */ + osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_WAIT_TX, + TRX_FSM_TIMEOUT, 0); + om2k_mo_fsm_start(fi, OM2K_TRX_EVT_TX_DONE, otfp->trx, + &otfp->trx->rbs2000.tx.om2k_mo); +} + +static void om2k_trx_s_wait_tx(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct om2k_trx_fsm_priv *otfp = fi->priv; + + /* Initialize RX after TX */ + osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_WAIT_RX, + TRX_FSM_TIMEOUT, 0); + om2k_mo_fsm_start(fi, OM2K_TRX_EVT_RX_DONE, otfp->trx, + &otfp->trx->rbs2000.rx.om2k_mo); +} + +static void om2k_trx_s_wait_rx(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct om2k_trx_fsm_priv *otfp = fi->priv; + struct gsm_bts_trx_ts *ts; + + /* Initialize Timeslots after TX */ + osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_WAIT_TS, + TRX_FSM_TIMEOUT, 0); + otfp->next_ts_nr = 0; + ts = &otfp->trx->ts[otfp->next_ts_nr++]; + om2k_mo_fsm_start(fi, OM2K_TRX_EVT_TS_DONE, otfp->trx, + &ts->rbs2000.om2k_mo); +} + +static void om2k_trx_s_wait_ts(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct om2k_trx_fsm_priv *otfp = fi->priv; + struct gsm_bts_trx_ts *ts; + + if (otfp->next_ts_nr < 8) { + /* iterate to the next timeslot */ + ts = &otfp->trx->ts[otfp->next_ts_nr++]; + om2k_mo_fsm_start(fi, OM2K_TRX_EVT_TS_DONE, otfp->trx, + &ts->rbs2000.om2k_mo); + } else { + /* only after all 8 TS */ + osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_DONE, 0, 0); + } +} + +static void om2k_trx_s_done_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + struct om2k_trx_fsm_priv *otfp = fi->priv; + gsm_bts_trx_set_system_infos(otfp->trx); + osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL); +} + +static const struct osmo_fsm_state om2k_trx_states[] = { + [OM2K_TRX_S_INIT] = { + .in_event_mask = S(OM2K_TRX_EVT_START), + .out_state_mask = S(OM2K_TRX_S_WAIT_TRXC), + .name = "INIT", + .action = om2k_trx_s_init, + }, + [OM2K_TRX_S_WAIT_TRXC] = { + .in_event_mask = S(OM2K_TRX_EVT_TRXC_DONE), + .out_state_mask = S(OM2K_TRX_S_ERROR) | + S(OM2K_TRX_S_WAIT_TX), + .name = "WAIT-TRXC", + .action = om2k_trx_s_wait_trxc, + }, + [OM2K_TRX_S_WAIT_TX] = { + .in_event_mask = S(OM2K_TRX_EVT_TX_DONE), + .out_state_mask = S(OM2K_TRX_S_ERROR) | + S(OM2K_TRX_S_WAIT_RX), + .name = "WAIT-TX", + .action = om2k_trx_s_wait_tx, + }, + [OM2K_TRX_S_WAIT_RX] = { + .in_event_mask = S(OM2K_TRX_EVT_RX_DONE), + .out_state_mask = S(OM2K_TRX_S_ERROR) | + S(OM2K_TRX_S_WAIT_TS), + .name = "WAIT-RX", + .action = om2k_trx_s_wait_rx, + }, + [OM2K_TRX_S_WAIT_TS] = { + .in_event_mask = S(OM2K_TRX_EVT_TS_DONE), + .out_state_mask = S(OM2K_TRX_S_ERROR) | + S(OM2K_TRX_S_DONE), + .name = "WAIT-TS", + .action = om2k_trx_s_wait_ts, + }, + [OM2K_TRX_S_DONE] = { + .name = "DONE", + .onenter = om2k_trx_s_done_onenter, + }, + [OM2K_TRX_S_ERROR] = { + .name = "ERROR", + }, +}; + +static int om2k_trx_timer_cb(struct osmo_fsm_inst *fi) +{ + osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_ERROR, 0, 0); + return 0; +} + +static struct osmo_fsm om2k_trx_fsm = { + .name = "OM2000-TRX", + .states = om2k_trx_states, + .num_states = ARRAY_SIZE(om2k_trx_states), + .log_subsys = DNM, + .event_names = om2k_trx_events, + .timer_cb = om2k_trx_timer_cb, +}; + +struct osmo_fsm_inst *om2k_trx_fsm_start(struct osmo_fsm_inst *parent, + struct gsm_bts_trx *trx, + uint32_t term_event) +{ + struct osmo_fsm_inst *fi; + struct om2k_trx_fsm_priv *otfp; + char idbuf[32]; + + snprintf(idbuf, sizeof(idbuf), "%u/%u", trx->bts->nr, trx->nr); + + fi = osmo_fsm_inst_alloc_child_id(&om2k_trx_fsm, parent, term_event, + idbuf); + if (!fi) + return NULL; + + otfp = talloc_zero(fi, struct om2k_trx_fsm_priv); + otfp->trx = trx; + fi->priv = otfp; + + osmo_fsm_inst_dispatch(fi, OM2K_TRX_EVT_START, NULL); + + return fi; +} + + +/*********************************************************************** + * OM2000 BTS Finite State Machine, initializes CF and all siblings + ***********************************************************************/ + +enum om2k_bts_event { + OM2K_BTS_EVT_START, + OM2K_BTS_EVT_CF_DONE, + OM2K_BTS_EVT_IS_DONE, + OM2K_BTS_EVT_TF_DONE, + OM2K_BTS_EVT_TRX_DONE, + OM2K_BTS_EVT_STOP, +}; + +static const struct value_string om2k_bts_events[] = { + { OM2K_BTS_EVT_START, "START" }, + { OM2K_BTS_EVT_CF_DONE, "CF-DONE" }, + { OM2K_BTS_EVT_IS_DONE, "IS-DONE" }, + { OM2K_BTS_EVT_TF_DONE, "TF-DONE" }, + { OM2K_BTS_EVT_TRX_DONE, "TRX-DONE" }, + { OM2K_BTS_EVT_STOP, "STOP" }, + { 0, NULL } +}; + +enum om2k_bts_state { + OM2K_BTS_S_INIT, + OM2K_BTS_S_WAIT_CF, + OM2K_BTS_S_WAIT_IS, + OM2K_BTS_S_WAIT_TF, + OM2K_BTS_S_WAIT_TRX, + OM2K_BTS_S_DONE, + OM2K_BTS_S_ERROR, +}; + +struct om2k_bts_fsm_priv { + struct gsm_bts *bts; + uint8_t next_trx_nr; +}; + +static void om2k_bts_s_init(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct om2k_bts_fsm_priv *obfp = fi->priv; + struct gsm_bts *bts = obfp->bts; + + OSMO_ASSERT(event == OM2K_BTS_EVT_START); + osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_CF, + BTS_FSM_TIMEOUT, 0); + om2k_mo_fsm_start(fi, OM2K_BTS_EVT_CF_DONE, bts->c0, + &bts->rbs2000.cf.om2k_mo); +} + +static void om2k_bts_s_wait_cf(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct om2k_bts_fsm_priv *obfp = fi->priv; + struct gsm_bts *bts = obfp->bts; + + OSMO_ASSERT(event == OM2K_BTS_EVT_CF_DONE); + osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_IS, + BTS_FSM_TIMEOUT, 0); + om2k_mo_fsm_start(fi, OM2K_BTS_EVT_IS_DONE, bts->c0, + &bts->rbs2000.is.om2k_mo); +} + +static void om2k_bts_s_wait_is(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct om2k_bts_fsm_priv *obfp = fi->priv; + struct gsm_bts *bts = obfp->bts; + + OSMO_ASSERT(event == OM2K_BTS_EVT_IS_DONE); + /* TF can take a long time to initialize, wait for 10min */ + osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_TF, 600, 0); + om2k_mo_fsm_start(fi, OM2K_BTS_EVT_TF_DONE, bts->c0, + &bts->rbs2000.tf.om2k_mo); +} + +static void om2k_bts_s_wait_tf(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct om2k_bts_fsm_priv *obfp = fi->priv; + struct gsm_bts_trx *trx; + + OSMO_ASSERT(event == OM2K_BTS_EVT_TF_DONE); + + osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_TRX, + BTS_FSM_TIMEOUT, 0); + obfp->next_trx_nr = 0; + trx = gsm_bts_trx_num(obfp->bts, obfp->next_trx_nr++); + om2k_trx_fsm_start(fi, trx, OM2K_BTS_EVT_TRX_DONE); +} + +static void om2k_bts_s_wait_trx(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct om2k_bts_fsm_priv *obfp = fi->priv; + + OSMO_ASSERT(event == OM2K_BTS_EVT_TRX_DONE); + + if (obfp->next_trx_nr < obfp->bts->num_trx) { + struct gsm_bts_trx *trx; + trx = gsm_bts_trx_num(obfp->bts, obfp->next_trx_nr++); + om2k_trx_fsm_start(fi, trx, OM2K_BTS_EVT_TRX_DONE); + } else { + osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_DONE, 0, 0); + } +} + +static void om2k_bts_s_done_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) +{ + osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL); +} + +static const struct osmo_fsm_state om2k_bts_states[] = { + [OM2K_BTS_S_INIT] = { + .in_event_mask = S(OM2K_BTS_EVT_START), + .out_state_mask = S(OM2K_BTS_S_WAIT_CF), + .name = "INIT", + .action = om2k_bts_s_init, + }, + [OM2K_BTS_S_WAIT_CF] = { + .in_event_mask = S(OM2K_BTS_EVT_CF_DONE), + .out_state_mask = S(OM2K_BTS_S_ERROR) | + S(OM2K_BTS_S_WAIT_IS), + .name = "WAIT-CF", + .action = om2k_bts_s_wait_cf, + }, + [OM2K_BTS_S_WAIT_IS] = { + .in_event_mask = S(OM2K_BTS_EVT_IS_DONE), + .out_state_mask = S(OM2K_BTS_S_ERROR) | + S(OM2K_BTS_S_WAIT_TF), + .name = "WAIT-IS", + .action = om2k_bts_s_wait_is, + }, + [OM2K_BTS_S_WAIT_TF] = { + .in_event_mask = S(OM2K_BTS_EVT_TF_DONE), + .out_state_mask = S(OM2K_BTS_S_ERROR) | + S(OM2K_BTS_S_WAIT_TRX), + .name = "WAIT-TF", + .action = om2k_bts_s_wait_tf, + }, + [OM2K_BTS_S_WAIT_TRX] = { + .in_event_mask = S(OM2K_BTS_EVT_TRX_DONE), + .out_state_mask = S(OM2K_BTS_S_ERROR) | + S(OM2K_BTS_S_DONE), + .name = "WAIT-TRX", + .action = om2k_bts_s_wait_trx, + }, + [OM2K_BTS_S_DONE] = { + .name = "DONE", + .onenter = om2k_bts_s_done_onenter, + }, + [OM2K_BTS_S_ERROR] = { + .name = "ERROR", + }, +}; + +static int om2k_bts_timer_cb(struct osmo_fsm_inst *fi) +{ + osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_ERROR, 0, 0); + return 0; +} + +static struct osmo_fsm om2k_bts_fsm = { + .name = "OM2000-BTS", + .states = om2k_bts_states, + .num_states = ARRAY_SIZE(om2k_bts_states), + .log_subsys = DNM, + .event_names = om2k_bts_events, + .timer_cb = om2k_bts_timer_cb, +}; + +struct osmo_fsm_inst * +om2k_bts_fsm_start(struct gsm_bts *bts) +{ + struct osmo_fsm_inst *fi; + struct om2k_bts_fsm_priv *obfp; + char idbuf[16]; + + snprintf(idbuf, sizeof(idbuf), "%u", bts->nr); + + fi = osmo_fsm_inst_alloc(&om2k_bts_fsm, bts, NULL, + LOGL_DEBUG, idbuf); + if (!fi) + return NULL; + fi->priv = obfp = talloc_zero(fi, struct om2k_bts_fsm_priv); + obfp->bts = bts; + + osmo_fsm_inst_dispatch(fi, OM2K_BTS_EVT_START, NULL); + + return fi; +} + + +/*********************************************************************** + * OM2000 Negotiation + ***********************************************************************/ + static int abis_om2k_tx_negot_req_ack(struct gsm_bts *bts, const struct abis_om2k_mo *mo, uint8_t *data, unsigned int len) { @@ -1345,60 +2311,10 @@ static int om2k_rx_negot_req(struct msgb *msg) return abis_om2k_tx_negot_req_ack(sign_link->trx->bts, &o2h->mo, out_buf, out_cur - out_buf); } -static int om2k_rx_start_res(struct msgb *msg) -{ - struct e1inp_sign_link *sign_link = (struct e1inp_sign_link *)msg->dst; - struct abis_om2k_hdr *o2h = msgb_l2(msg); - int rc; - - rc = abis_om2k_tx_simple(sign_link->trx->bts, &o2h->mo, OM2K_MSGT_START_RES_ACK); - rc = abis_om2k_tx_op_info(sign_link->trx->bts, &o2h->mo, 1); - - return rc; -} - -static int om2k_rx_op_info_ack(struct msgb *msg) -{ - struct e1inp_sign_link *sign_link = (struct e1inp_sign_link *)msg->dst; - struct abis_om2k_hdr *o2h = msgb_l2(msg); - - /* This Acknowledgement does not contain the actual operational state, - * so we signal whatever state we saved when we sent the Op Info - * request */ - signal_op_state(sign_link->trx->bts, &o2h->mo); - - return 0; -} - -const struct value_string om2k_result_strings[] = { - { 0x02, "Wrong state or out of sequence" }, - { 0x03, "File error" }, - { 0x04, "Fault, unspecified" }, - { 0x05, "Tuning fault" }, - { 0x06, "Protocol error" }, - { 0x07, "MO not connected" }, - { 0x08, "Parameter error" }, - { 0x09, "Optional function not supported" }, - { 0x0a, "Local access state LOCALLY DISCONNECTED" }, - { 0, NULL } -}; - -const struct value_string om2k_accordance_strings[] = { - { 0x00, "Data according to request" }, - { 0x01, "Data not according to request" }, - { 0x02, "Inconsistent MO data" }, - { 0x03, "Capability constraint violation" }, - { 0, NULL } -}; - -const struct value_string om2k_mostate_vals[] = { - { 0x00, "RESET" }, - { 0x01, "STARTED" }, - { 0x02, "ENABLED" }, - { 0x03, "DISABLED" }, - { 0, NULL } -}; +/*********************************************************************** + * OM2000 Receive Message Handler + ***********************************************************************/ static int om2k_rx_nack(struct msgb *msg) { @@ -1423,68 +2339,20 @@ static int om2k_rx_nack(struct msgb *msg) return 0; } -/* Process a Configuration Result message */ -static int process_conf_res(struct gsm_bts *bts, struct msgb *msg) +static int process_mo_state(struct gsm_bts *bts, struct om2k_decoded_msg *odm) { - struct abis_om2k_hdr *o2h = msgb_l2(msg); - uint16_t msg_type = ntohs(o2h->msg_type); - struct nm_om2k_signal_data nsd; - struct tlv_parsed tp; - uint8_t acc; - unsigned int log_level; - int ret; - - memset(&nsd, 0, sizeof(nsd)); - - abis_om2k_msg_tlv_parse(&tp, o2h); - if (!TLVP_PRESENT(&tp, OM2K_DEI_ACCORDANCE_IND)) - return -EIO; - - acc = *TLVP_VAL(&tp, OM2K_DEI_ACCORDANCE_IND); - - switch (acc) { - case 0: - log_level = LOGL_DEBUG; - ret = 0; - break; - default: - log_level = LOGL_ERROR; - ret = -EINVAL; - break; - } - - LOGP(DNM, log_level, "Rx MO=%s %s, Accordance: %s\n", - om2k_mo_name(&o2h->mo), - get_value_string(om2k_msgcode_vals, msg_type), - get_value_string(om2k_accordance_strings, acc)); - - nsd.bts = bts; - nsd.obj = mo2obj(bts, &o2h->mo); - nsd.om2k_mo = &o2h->mo; - nsd.accordance_ind = acc; - osmo_signal_dispatch(SS_NM, S_NM_OM2K_CONF_RES, &nsd); - - return ret; -} - -static int process_mo_state(struct gsm_bts *bts, struct msgb *msg) -{ - struct abis_om2k_hdr *o2h = msgb_l2(msg); - uint16_t msg_type = ntohs(o2h->msg_type); - struct tlv_parsed tp; uint8_t mo_state; - abis_om2k_msg_tlv_parse(&tp, o2h); - if (!TLVP_PRESENT(&tp, OM2K_DEI_MO_STATE)) + if (!TLVP_PRESENT(&odm->tp, OM2K_DEI_MO_STATE)) return -EIO; - mo_state = *TLVP_VAL(&tp, OM2K_DEI_MO_STATE); + mo_state = *TLVP_VAL(&odm->tp, OM2K_DEI_MO_STATE); LOGP(DNM, LOGL_DEBUG, "Rx MO=%s %s, MO State: %s\n", - om2k_mo_name(&o2h->mo), - get_value_string(om2k_msgcode_vals, msg_type), + om2k_mo_name(&odm->o2h.mo), + get_value_string(om2k_msgcode_vals, odm->msg_type), get_value_string(om2k_mostate_vals, mo_state)); - update_mo_state(bts, &o2h->mo, mo_state); + update_mo_state(bts, &odm->o2h.mo, mo_state); return 0; } @@ -1496,6 +2364,8 @@ int abis_om2k_rcvmsg(struct msgb *msg) struct abis_om2k_hdr *o2h = msgb_l2(msg); struct abis_om_hdr *oh = &o2h->om; uint16_t msg_type = ntohs(o2h->msg_type); + struct om2k_decoded_msg odm; + struct om2k_mo *mo; int rc = 0; /* Various consistency checks */ @@ -1523,80 +2393,53 @@ int abis_om2k_rcvmsg(struct msgb *msg) get_value_string(om2k_msgcode_vals, msg_type), osmo_hexdump(msg->l2h, msgb_l2len(msg))); + om2k_decode_msg(&odm, msg); + + process_mo_state(bts, &odm); + switch (msg_type) { case OM2K_MSGT_CAL_TIME_REQ: rc = abis_om2k_cal_time_resp(bts); break; case OM2K_MSGT_FAULT_REP: rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_FAULT_REP_ACK); - process_mo_state(bts, msg); break; case OM2K_MSGT_NEGOT_REQ: rc = om2k_rx_negot_req(msg); break; case OM2K_MSGT_START_RES: - rc = om2k_rx_start_res(msg); - process_mo_state(bts, msg); - break; - case OM2K_MSGT_OP_INFO_ACK: - rc = om2k_rx_op_info_ack(msg); + /* common processing here */ + rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_START_RES_ACK); + /* below we dispatch into MO */ break; case OM2K_MSGT_IS_CONF_RES: rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_IS_CONF_RES_ACK); - process_conf_res(bts, msg); break; case OM2K_MSGT_CON_CONF_RES: rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_CON_CONF_RES_ACK); - process_conf_res(bts, msg); break; case OM2K_MSGT_TX_CONF_RES: rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_TX_CONF_RES_ACK); - process_conf_res(bts, msg); break; case OM2K_MSGT_RX_CONF_RES: rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_RX_CONF_RES_ACK); - process_conf_res(bts, msg); break; case OM2K_MSGT_TS_CONF_RES: rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_TS_CONF_RES_ACK); - process_conf_res(bts, msg); break; case OM2K_MSGT_TF_CONF_RES: rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_TF_CONF_RES_ACK); - process_conf_res(bts, msg); - break; - case OM2K_MSGT_CONNECT_COMPL: - rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_RESET_CMD); - break; - case OM2K_MSGT_RESET_COMPL: - rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_START_REQ); break; case OM2K_MSGT_ENABLE_RES: rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_ENABLE_RES_ACK); - process_mo_state(bts, msg); break; case OM2K_MSGT_DISABLE_RES: rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_DISABLE_RES_ACK); - process_mo_state(bts, msg); break; case OM2K_MSGT_TEST_RES: rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_TEST_RES_ACK); - process_mo_state(bts, msg); - break; - case OM2K_MSGT_STATUS_RESP: - process_mo_state(bts, msg); - break; - case OM2K_MSGT_START_REQ_ACK: - case OM2K_MSGT_CON_CONF_REQ_ACK: - case OM2K_MSGT_IS_CONF_REQ_ACK: - case OM2K_MSGT_TX_CONF_REQ_ACK: - case OM2K_MSGT_RX_CONF_REQ_ACK: - case OM2K_MSGT_TS_CONF_REQ_ACK: - case OM2K_MSGT_TF_CONF_REQ_ACK: - case OM2K_MSGT_ENABLE_REQ_ACK: - case OM2K_MSGT_ALARM_STATUS_REQ_ACK: - case OM2K_MSGT_DISABLE_REQ_ACK: break; + /* ERrors */ case OM2K_MSGT_START_REQ_REJ: case OM2K_MSGT_CONNECT_REJ: case OM2K_MSGT_OP_INFO_REJ: @@ -1613,11 +2456,74 @@ int abis_om2k_rcvmsg(struct msgb *msg) case OM2K_MSGT_DISABLE_REQ_REJ: rc = om2k_rx_nack(msg); break; - default: - LOGP(DNM, LOGL_NOTICE, "Rx unhandled OM2000 msg %s\n", - get_value_string(om2k_msgcode_vals, msg_type)); } + /* Resolve the MO for this message */ + mo = get_om2k_mo(bts, &o2h->mo); + if (!mo) { + LOGP(DNM, LOGL_ERROR, "Couldn't resolve MO for OM2K msg " + "%s: %s\n", get_value_string(om2k_msgcode_vals, msg_type), + msgb_hexdump(msg)); + return 0; + } + + /* Dispatch message to that MO */ + om2k_mo_fsm_recvmsg(bts, mo, &odm); + msgb_free(msg); return rc; } + +static void om2k_mo_init(struct om2k_mo *mo, uint8_t class, + uint8_t bts_nr, uint8_t assoc_so, uint8_t inst) +{ + mo->addr.class = class; + mo->addr.bts = bts_nr; + mo->addr.assoc_so = assoc_so; + mo->addr.inst = inst; +} + +/* initialize the OM2K_MO members of gsm_bts_trx and its timeslots */ +void abis_om2k_trx_init(struct gsm_bts_trx *trx) +{ + struct gsm_bts *bts = trx->bts; + unsigned int i; + + OSMO_ASSERT(bts->type == GSM_BTS_TYPE_RBS2000); + + om2k_mo_init(&trx->rbs2000.trxc.om2k_mo, OM2K_MO_CLS_TRXC, + bts->nr, 255, trx->nr); + om2k_mo_init(&trx->rbs2000.tx.om2k_mo, OM2K_MO_CLS_TX, + bts->nr, 255, trx->nr); + om2k_mo_init(&trx->rbs2000.rx.om2k_mo, OM2K_MO_CLS_RX, + bts->nr, 255, trx->nr); + + for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { + om2k_mo_init(&trx->ts[i].rbs2000.om2k_mo, OM2K_MO_CLS_TS, + bts->nr, trx->nr, i); + } +} + +/* initialize the OM2K_MO members of gsm_bts */ +void abis_om2k_bts_init(struct gsm_bts *bts) +{ + OSMO_ASSERT(bts->type == GSM_BTS_TYPE_RBS2000); + + om2k_mo_init(&bts->rbs2000.cf.om2k_mo, OM2K_MO_CLS_CF, + bts->nr, 0xFF, 0); + om2k_mo_init(&bts->rbs2000.is.om2k_mo, OM2K_MO_CLS_IS, + bts->nr, 0xFF, 0); + om2k_mo_init(&bts->rbs2000.con.om2k_mo, OM2K_MO_CLS_CON, + bts->nr, 0xFF, 0); + om2k_mo_init(&bts->rbs2000.dp.om2k_mo, OM2K_MO_CLS_DP, + bts->nr, 0xFF, 0); + om2k_mo_init(&bts->rbs2000.tf.om2k_mo, OM2K_MO_CLS_TF, + bts->nr, 0xFF, 0); +} + +static __attribute__((constructor)) void abis_om2k_init(void) +{ + osmo_fsm_register(&om2k_mo_fsm); + osmo_fsm_register(&om2k_bts_fsm); + osmo_fsm_register(&om2k_trx_fsm); +} diff --git a/openbsc/src/libbsc/bts_ericsson_rbs2000.c b/openbsc/src/libbsc/bts_ericsson_rbs2000.c index 7f056f12d..a82893729 100644 --- a/openbsc/src/libbsc/bts_ericsson_rbs2000.c +++ b/openbsc/src/libbsc/bts_ericsson_rbs2000.c @@ -34,18 +34,20 @@ static void bootstrap_om_bts(struct gsm_bts *bts) { LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for BTS %u\n", bts->nr); - abis_om2k_tx_start_req(bts, &om2k_mo_cf); - /* FIXME */ + + /* FIXME: this is global init, not bootstrapping */ + abis_om2k_bts_init(bts); + abis_om2k_trx_init(bts->c0); + + /* TODO: Should we wait for a Failure report? */ + om2k_bts_fsm_start(bts); } static void bootstrap_om_trx(struct gsm_bts_trx *trx) { - struct abis_om2k_mo trx_mo = { OM2K_MO_CLS_TRXC, 0, 255, trx->nr }; - LOGP(DNM, LOGL_NOTICE, "bootstrapping OML for TRX %u/%u\n", trx->bts->nr, trx->nr); - - abis_om2k_tx_reset_cmd(trx->bts, &trx_mo); + /* FIXME */ } static int shutdown_om(struct gsm_bts *bts) @@ -137,103 +139,6 @@ static int inp_sig_cb(unsigned int subsys, unsigned int signal, return 0; } -static void nm_statechg_evt(unsigned int signal, - struct nm_statechg_signal_data *nsd) -{ - struct abis_om2k_mo mo; - - if (nsd->bts->type != GSM_BTS_TYPE_RBS2000) - return; - - switch (nsd->om2k_mo->class) { - case OM2K_MO_CLS_CF: - if (nsd->new_state->operational != NM_OPSTATE_ENABLED || - nsd->new_state->availability != OM2K_MO_S_STARTED) - break; - /* CF has started, we can trigger IS and TF start */ - abis_om2k_tx_connect_cmd(nsd->bts, &om2k_mo_is); - abis_om2k_tx_connect_cmd(nsd->bts, &om2k_mo_tf); - break; - case OM2K_MO_CLS_IS: - if (nsd->new_state->availability == OM2K_MO_S_ENABLED) { - /* IS is enabled, we can proceed with TRXC/RX/TX/TS */ - break; - } - if (nsd->new_state->operational != NM_OPSTATE_ENABLED) - break; - /* IS has started, we can configure + enable it */ - abis_om2k_tx_is_conf_req(nsd->bts); - break; - case OM2K_MO_CLS_TF: - if (nsd->new_state->operational != NM_OPSTATE_ENABLED || - nsd->new_state->availability == OM2K_MO_S_DISABLED) - break; - if (nsd->new_state->availability == OM2K_MO_S_STARTED) { - /* TF has started, configure + enable it */ - abis_om2k_tx_tf_conf_req(nsd->bts); - } - break; - case OM2K_MO_CLS_TRXC: - if (nsd->new_state->availability != OM2K_MO_S_STARTED) - break; - /* TRXC is started, connect the TX and RX objects */ - memcpy(&mo, nsd->om2k_mo, sizeof(mo)); - mo.class = OM2K_MO_CLS_TX; - abis_om2k_tx_connect_cmd(nsd->bts, &mo); - mo.class = OM2K_MO_CLS_RX; - abis_om2k_tx_connect_cmd(nsd->bts, &mo); - break; - case OM2K_MO_CLS_RX: - if (nsd->new_state->operational != NM_OPSTATE_ENABLED || - nsd->new_state->availability != OM2K_MO_S_STARTED) - break; - /* RX is started, configure + enable it */ - abis_om2k_tx_rx_conf_req(nsd->obj); - break; - case OM2K_MO_CLS_TX: - if (nsd->new_state->operational != NM_OPSTATE_ENABLED || - nsd->new_state->availability != OM2K_MO_S_STARTED) - break; - /* RX is started, configure + enable it */ - abis_om2k_tx_tx_conf_req(nsd->obj); - break; - } -} - -static void nm_conf_res(struct nm_om2k_signal_data *nsd) -{ - switch (nsd->om2k_mo->class) { - case OM2K_MO_CLS_IS: - case OM2K_MO_CLS_TF: - case OM2K_MO_CLS_RX: - case OM2K_MO_CLS_TX: - /* If configuration was a success, enable it */ - abis_om2k_tx_enable_req(nsd->bts, nsd->om2k_mo); - break; - } -} - -static int nm_sig_cb(unsigned int subsys, unsigned int signal, - void *handler_data, void *signal_data) -{ - if (subsys != SS_NM) - return 0; - - switch (signal) { - case S_NM_STATECHG_OPER: - case S_NM_STATECHG_ADM: - nm_statechg_evt(signal, signal_data); - break; - case S_NM_OM2K_CONF_RES: - nm_conf_res(signal_data); - break; - default: - break; - } - - return 0; -} - static void config_write_bts(struct vty *vty, struct gsm_bts *bts) { abis_om2k_config_write_bts(vty, bts); @@ -266,7 +171,6 @@ static int bts_model_rbs2k_start(struct gsm_network *net) osmo_signal_register_handler(SS_L_INPUT, inp_sig_cb, NULL); osmo_signal_register_handler(SS_L_GLOBAL, gbl_sig_cb, NULL); - osmo_signal_register_handler(SS_NM, nm_sig_cb, NULL); return 0; } |