diff options
Diffstat (limited to 'src/osmo-bsc/abis_om2000.c')
-rw-r--r-- | src/osmo-bsc/abis_om2000.c | 783 |
1 files changed, 480 insertions, 303 deletions
diff --git a/src/osmo-bsc/abis_om2000.c b/src/osmo-bsc/abis_om2000.c index a3f689add..2a653bf97 100644 --- a/src/osmo-bsc/abis_om2000.c +++ b/src/osmo-bsc/abis_om2000.c @@ -44,9 +44,22 @@ #include <osmocom/bsc/abis_om2000.h> #include <osmocom/bsc/signal.h> #include <osmocom/bsc/timeslot_fsm.h> +#include <osmocom/bsc/nm_common_fsm.h> #include <osmocom/bsc/bts.h> #include <osmocom/abis/e1_input.h> +static inline void abis_om2000_fsm_transc_becomes_enabled(struct gsm_bts_trx *trx) +{ + nm_obj_fsm_becomes_enabled_disabled(trx->bts, trx, NM_OC_RADIO_CARRIER, true); + nm_obj_fsm_becomes_enabled_disabled(trx->bts, &trx->bb_transc, NM_OC_BASEB_TRANSC, true); +} + +static inline void abis_om2000_fsm_transc_becomes_disabled(struct gsm_bts_trx *trx) +{ + nm_obj_fsm_becomes_enabled_disabled(trx->bts, trx, NM_OC_RADIO_CARRIER, false); + nm_obj_fsm_becomes_enabled_disabled(trx->bts, &trx->bb_transc, NM_OC_BASEB_TRANSC, false); +} + /* FIXME: move to libosmocore */ struct osmo_fsm_inst *osmo_fsm_inst_alloc_child_id(struct osmo_fsm *fsm, struct osmo_fsm_inst *parent, @@ -310,6 +323,7 @@ enum abis_om2k_dei { OM2K_DEI_MAX_ALLOWED_POWER = 0xa9, OM2K_DEI_MAX_ALLOWED_NUM_TRXCS = 0xaa, OM2K_DEI_MCTR_FEAT_STATUS_BMAP = 0xab, + OM2K_DEI_SEEN_UNKNOWN_D2 = 0xd2, }; enum abis_om2k_mostate { @@ -388,6 +402,7 @@ const struct tlv_definition om2k_att_tlvdef = { [OM2K_DEI_FS_OFFSET] = { TLV_TYPE_FIXED, 5 }, [OM2K_DEI_EXT_COND_MAP_2_EXT] = { TLV_TYPE_FIXED, 4 }, [OM2K_DEI_TSS_MO_STATE] = { TLV_TYPE_FIXED, 4 }, + [OM2K_DEI_SEEN_UNKNOWN_D2] = { TLV_TYPE_FIXED, 6 }, }, }; @@ -776,6 +791,9 @@ get_om2k_mo(struct gsm_bts *bts, const struct abis_om2k_mo *abis_mo) struct gsm_bts_trx *trx; switch (abis_mo->class) { + case OM2K_MO_CLS_DP: + mo = &bts->rbs2000.dp.om2k_mo; + break; case OM2K_MO_CLS_CF: mo = &bts->rbs2000.cf.om2k_mo; break; @@ -849,7 +867,7 @@ static int om2k_decode_msg(struct om2k_decoded_msg *odm, struct msgb *msg) return abis_om2k_msg_tlv_parse(&odm->tp, o2h); } -static char *om2k_mo_name(const struct abis_om2k_mo *mo) +const char *abis_om2k_mo_name(const struct abis_om2k_mo *mo) { static char mo_buf[64]; @@ -861,8 +879,7 @@ static char *om2k_mo_name(const struct abis_om2k_mo *mo) } /* resolve the gsm_nm_state data structure for a given MO */ -static struct gsm_nm_state * -mo2nm_state(struct gsm_bts *bts, const struct abis_om2k_mo *mo) +static struct gsm_nm_state *mo2nm_state(struct gsm_bts *bts, const struct abis_om2k_mo *mo) { struct gsm_bts_trx *trx; struct gsm_nm_state *nm_state = NULL; @@ -917,7 +934,7 @@ mo2nm_state(struct gsm_bts *bts, const struct abis_om2k_mo *mo) return nm_state; } -static void *mo2obj(struct gsm_bts *bts, struct abis_om2k_mo *mo) +static void *mo2obj(struct gsm_bts *bts, const struct abis_om2k_mo *mo) { struct gsm_bts_trx *trx; @@ -945,17 +962,11 @@ static void *mo2obj(struct gsm_bts *bts, struct abis_om2k_mo *mo) return NULL; } -static void update_mo_state(struct gsm_bts *bts, struct abis_om2k_mo *mo, - uint8_t mo_state) +/* Derive an OML Availability state from an OM2000 MO state */ +static enum abis_nm_avail_state abis_nm_av_state_from_om2k_av_state(struct abis_om2k_mo *mo, uint8_t mo_state) { - struct gsm_nm_state *nm_state = mo2nm_state(bts, mo); - struct gsm_nm_state new_state; - struct nm_statechg_signal_data nsd; bool has_enabled_state; - if (!nm_state) - return; - switch (mo->class) { case OM2K_MO_CLS_CF: case OM2K_MO_CLS_TRXC: @@ -966,61 +977,132 @@ static void update_mo_state(struct gsm_bts *bts, struct abis_om2k_mo *mo, break; } - new_state = *nm_state; switch (mo_state) { case OM2K_MOSTATE_RESET: - new_state.availability = NM_AVSTATE_POWER_OFF; - break; + return NM_AVSTATE_POWER_OFF; case OM2K_MOSTATE_STARTED: - new_state.availability = has_enabled_state ? NM_AVSTATE_OFF_LINE : NM_AVSTATE_OK; - break; + return has_enabled_state ? NM_AVSTATE_OFF_LINE : NM_AVSTATE_OK; case OM2K_MOSTATE_ENABLED: - new_state.availability = NM_AVSTATE_OK; - break; + return NM_AVSTATE_OK; case OM2K_MOSTATE_DISABLED: - new_state.availability = NM_AVSTATE_POWER_OFF; - break; + return NM_AVSTATE_POWER_OFF; default: - new_state.availability = NM_AVSTATE_DEGRADED; - break; + return NM_AVSTATE_DEGRADED; } +} + +/* The OM2000 -> 12.21 mapping we do doesn't have a separate bb_transc MO, + * for compatibility reasons we pretend to have it anyway. */ +static void update_bb_trxc_mo_state(struct gsm_bts *bts, struct abis_om2k_mo *mo, uint8_t mo_state) +{ + struct nm_statechg_signal_data nsd; + struct gsm_bts_trx *trx; + + trx = gsm_bts_trx_num(bts, mo->inst); + if (!trx) + return; memset(&nsd, 0, sizeof(nsd)); nsd.bts = bts; nsd.obj = mo2obj(bts, mo); - nsd.old_state = nm_state; - nsd.new_state = &new_state; + nsd.old_state = trx->bb_transc.mo.nm_state; + nsd.new_state = trx->bb_transc.mo.nm_state; nsd.om2k_mo = mo; - osmo_signal_dispatch(SS_NM, S_NM_STATECHG_ADM, &nsd); - - nm_state->availability = new_state.availability; + nsd.new_state.availability = abis_nm_av_state_from_om2k_av_state(mo, mo_state); + trx->bb_transc.mo.nm_state.availability = nsd.new_state.availability; + osmo_signal_dispatch(SS_NM, S_NM_STATECHG, &nsd); } -static void update_op_state(struct gsm_bts *bts, const struct abis_om2k_mo *mo, - uint8_t op_state) +static void update_mo_state(struct gsm_bts *bts, struct abis_om2k_mo *mo, uint8_t mo_state) { struct gsm_nm_state *nm_state = mo2nm_state(bts, mo); - struct gsm_nm_state new_state; + struct nm_statechg_signal_data nsd; if (!nm_state) return; - new_state = *nm_state; + memset(&nsd, 0, sizeof(nsd)); + + nsd.bts = bts; + nsd.obj = mo2obj(bts, mo); + nsd.old_state = *nm_state; + nsd.new_state = *nm_state; + nsd.om2k_mo = mo; + + nsd.new_state.availability = abis_nm_av_state_from_om2k_av_state(mo, mo_state); + + /* Update current state before emitting signal: */ + nm_state->availability = nsd.new_state.availability; + osmo_signal_dispatch(SS_NM, S_NM_STATECHG, &nsd); + + /* When the TRXC MO is updated, also update the BB TRXC accordingly. */ + if (mo->class == OM2K_MO_CLS_TRXC) + update_bb_trxc_mo_state(bts, mo, mo_state); +} + +/* Derive an OML Operational state from an OM2000 OP state */ +static enum abis_nm_op_state abis_nm_op_state_from_om2k_op_state(uint8_t op_state) +{ switch (op_state) { case 1: - new_state.operational = NM_OPSTATE_ENABLED; - break; + return NM_OPSTATE_ENABLED; case 0: - new_state.operational = NM_OPSTATE_DISABLED; - break; + return NM_OPSTATE_DISABLED; default: - new_state.operational = NM_OPSTATE_NULL; - break; + return NM_OPSTATE_NULL; } +} + +/* (see comment in update_bb_trxc_mo_state() above) */ +static void update_bb_trxc_op_state(struct gsm_bts *bts, const struct abis_om2k_mo *mo, uint8_t op_state) +{ + struct nm_statechg_signal_data nsd; + struct gsm_bts_trx *trx; + + trx = gsm_bts_trx_num(bts, mo->inst); + if (!trx) + return; + + memset(&nsd, 0, sizeof(nsd)); + + nsd.bts = bts; + nsd.obj = mo2obj(bts, mo); + nsd.old_state = trx->bb_transc.mo.nm_state; + nsd.new_state = trx->bb_transc.mo.nm_state; + nsd.om2k_mo = mo; + + nsd.new_state.operational = abis_nm_op_state_from_om2k_op_state(op_state); + trx->bb_transc.mo.nm_state.operational = nsd.new_state.operational; + osmo_signal_dispatch(SS_NM, S_NM_STATECHG, &nsd); +} + +static void update_op_state(struct gsm_bts *bts, const struct abis_om2k_mo *mo, uint8_t op_state) +{ + struct gsm_nm_state *nm_state = mo2nm_state(bts, mo); + struct nm_statechg_signal_data nsd; - nm_state->operational = new_state.operational; + if (!nm_state) + return; + + memset(&nsd, 0, sizeof(nsd)); + + nsd.bts = bts; + nsd.obj = mo2obj(bts, mo); + nsd.old_state = *nm_state; + nsd.new_state = *nm_state; + nsd.om2k_mo = mo; + + nsd.new_state.operational = abis_nm_op_state_from_om2k_op_state(op_state); + + /* Update current state before emitting signal: */ + nm_state->operational = nsd.new_state.operational; + osmo_signal_dispatch(SS_NM, S_NM_STATECHG, &nsd); + + /* When the TRXC MO is updated, also update the BB TRXC accordingly. */ + if (mo->class == OM2K_MO_CLS_TRXC) + update_bb_trxc_op_state(bts, mo, op_state); } static int abis_om2k_sendmsg(struct gsm_bts *bts, struct msgb *msg) @@ -1041,8 +1123,8 @@ static int abis_om2k_sendmsg(struct gsm_bts *bts, struct msgb *msg) /* Route through per-TRX OML Link to the appropriate TRX */ trx = gsm_bts_trx_num(bts, o2h->mo.inst); if (!trx) { - LOGP(DNM, LOGL_ERROR, "MO=%s Tx Dropping msg to " - "non-existing TRX\n", om2k_mo_name(&o2h->mo)); + LOGP(DNM, LOGL_ERROR, "MO=%s Tx Dropping msg to non-existing TRX\n", + abis_om2k_mo_name(&o2h->mo)); return -ENODEV; } msg->dst = trx->oml_link; @@ -1051,8 +1133,8 @@ static int abis_om2k_sendmsg(struct gsm_bts *bts, struct msgb *msg) /* Route through per-TRX OML Link to the appropriate TRX */ trx = gsm_bts_trx_num(bts, o2h->mo.assoc_so); if (!trx) { - LOGP(DNM, LOGL_ERROR, "MO=%s Tx Dropping msg to " - "non-existing TRX\n", om2k_mo_name(&o2h->mo)); + LOGP(DNM, LOGL_ERROR, "MO=%s Tx Dropping msg to non-existing TRX\n", + abis_om2k_mo_name(&o2h->mo)); return -ENODEV; } msg->dst = trx->oml_link; @@ -1066,8 +1148,7 @@ static int abis_om2k_sendmsg(struct gsm_bts *bts, struct msgb *msg) return _abis_nm_sendmsg(msg); } -static void fill_om2k_hdr(struct abis_om2k_hdr *o2h, const struct abis_om2k_mo *mo, - uint16_t msg_type) +static void fill_om2k_hdr(struct abis_om2k_hdr *o2h, const struct abis_om2k_mo *mo, uint16_t msg_type) { o2h->om.mdisc = ABIS_OM_MDISC_FOM; o2h->om.placement = ABIS_OM_PLACEMENT_ONLY; @@ -1085,8 +1166,7 @@ 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, &bts->rbs2000.cf.om2k_mo.addr, - 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); @@ -1102,8 +1182,7 @@ static int abis_om2k_cal_time_resp(struct gsm_bts *bts) return abis_om2k_sendmsg(bts, msg); } -static int abis_om2k_tx_simple(struct gsm_bts *bts, const struct abis_om2k_mo *mo, - uint16_t msg_type) +static int abis_om2k_tx_simple(struct gsm_bts *bts, const struct abis_om2k_mo *mo, uint16_t msg_type) { struct msgb *msg = om2k_msgb_alloc(); struct abis_om2k_hdr *o2k; @@ -1111,8 +1190,7 @@ static int abis_om2k_tx_simple(struct gsm_bts *bts, const struct abis_om2k_mo *m o2k = (struct abis_om2k_hdr *) msgb_put(msg, sizeof(*o2k)); fill_om2k_hdr(o2k, mo, msg_type); - DEBUGP(DNM, "Tx MO=%s %s\n", om2k_mo_name(mo), - get_value_string(om2k_msgcode_vals, msg_type)); + DEBUGP(DNM, "Tx MO=%s %s\n", abis_om2k_mo_name(mo), get_value_string(om2k_msgcode_vals, msg_type)); return abis_om2k_sendmsg(bts, msg); } @@ -1157,8 +1235,7 @@ int abis_om2k_tx_disable_req(struct gsm_bts *bts, const struct abis_om2k_mo *mo) return abis_om2k_tx_simple(bts, mo, OM2K_MSGT_DISABLE_REQ); } -int abis_om2k_tx_op_info(struct gsm_bts *bts, const struct abis_om2k_mo *mo, - uint8_t operational) +int abis_om2k_tx_op_info(struct gsm_bts *bts, const struct abis_om2k_mo *mo, uint8_t operational) { struct msgb *msg = om2k_msgb_alloc(); struct abis_om2k_hdr *o2k; @@ -1168,7 +1245,7 @@ int abis_om2k_tx_op_info(struct gsm_bts *bts, const struct abis_om2k_mo *mo, msgb_tv_put(msg, OM2K_DEI_OP_INFO, operational); - DEBUGP(DNM, "Tx MO=%s %s\n", om2k_mo_name(mo), + DEBUGP(DNM, "Tx MO=%s %s\n", abis_om2k_mo_name(mo), get_value_string(om2k_msgcode_vals, OM2K_MSGT_OP_INFO)); /* we update the state here... and send the signal at ACK */ @@ -1228,19 +1305,16 @@ 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, &bts->rbs2000.is.om2k_mo.addr, - 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); - msgb_tlv_put(msg, OM2K_DEI_IS_CONN_LIST, - num_grps * sizeof(*cg), (uint8_t *)cg); + msgb_tlv_put(msg, OM2K_DEI_IS_CONN_LIST, num_grps * sizeof(*cg), (uint8_t *)cg); talloc_free(cg); - DEBUGP(DNM, "Tx MO=%s %s\n", - om2k_mo_name(&bts->rbs2000.is.om2k_mo.addr), + DEBUGP(DNM, "Tx MO=%s %s\n", abis_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); @@ -1286,11 +1360,9 @@ int abis_om2k_tx_con_conf_req(struct gsm_bts *bts) /* pre-pend the OM2K header */ o2k = (struct abis_om2k_hdr *) msgb_push(msg, sizeof(*o2k)); - fill_om2k_hdr(o2k, &bts->rbs2000.con.om2k_mo.addr, - OM2K_MSGT_CON_CONF_REQ); + fill_om2k_hdr(o2k, &bts->rbs2000.con.om2k_mo.addr, OM2K_MSGT_CON_CONF_REQ); - DEBUGP(DNM, "Tx MO=%s %s\n", - om2k_mo_name(&bts->rbs2000.con.om2k_mo.addr), + DEBUGP(DNM, "Tx MO=%s %s\n", abis_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); @@ -1316,17 +1388,14 @@ int abis_om2k_tx_mctr_conf_req(struct gsm_bts *bts) /* pre-pend the OM2K header */ o2k = (struct abis_om2k_hdr *) msgb_push(msg, sizeof(*o2k)); - fill_om2k_hdr(o2k, &bts->rbs2000.mctr.om2k_mo.addr, - OM2K_MSGT_MCTR_CONF_REQ); - DEBUGP(DNM, "Tx MO=%s %s\n", - om2k_mo_name(&bts->rbs2000.mctr.om2k_mo.addr), + fill_om2k_hdr(o2k, &bts->rbs2000.mctr.om2k_mo.addr, OM2K_MSGT_MCTR_CONF_REQ); + DEBUGP(DNM, "Tx MO=%s %s\n", abis_om2k_mo_name(&bts->rbs2000.mctr.om2k_mo.addr), get_value_string(om2k_msgcode_vals, OM2K_MSGT_MCTR_CONF_REQ)); return abis_om2k_sendmsg(bts, msg); } -static void om2k_trx_to_mo(struct abis_om2k_mo *mo, - const struct gsm_bts_trx *trx, +static void om2k_trx_to_mo(struct abis_om2k_mo *mo, const struct gsm_bts_trx *trx, enum abis_om2k_mo_cls cls) { mo->class = cls; @@ -1335,8 +1404,7 @@ static void om2k_trx_to_mo(struct abis_om2k_mo *mo, mo->assoc_so = 255; } -static void om2k_ts_to_mo(struct abis_om2k_mo *mo, - const struct gsm_bts_trx_ts *ts) +static void om2k_ts_to_mo(struct abis_om2k_mo *mo, const struct gsm_bts_trx_ts *ts) { mo->class = OM2K_MO_CLS_TS; mo->bts = 0; @@ -1358,7 +1426,7 @@ int abis_om2k_tx_rx_conf_req(struct gsm_bts_trx *trx) /* OM2K_DEI_FREQ_SPEC_RX: Using trx_nr as "RX address" only works for single MCTR case */ msgb_tv16_put(msg, OM2K_DEI_FREQ_SPEC_RX, 0x8000 | ((uint16_t)trx->nr << 10)); - msgb_tv_put(msg, OM2K_DEI_RX_DIVERSITY, 0x02); /* A */ + msgb_tv_put(msg, OM2K_DEI_RX_DIVERSITY, trx->rbs2000.rx_diversity); return abis_om2k_sendmsg(trx->bts, msg); } @@ -1400,16 +1468,13 @@ 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, &bts->rbs2000.tf.om2k_mo.addr, - 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); + msgb_tv_put(msg, OM2K_DEI_TF_SYNC_SRC, bts->rbs2000.sync_src); + 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(&bts->rbs2000.tf.om2k_mo.addr), + DEBUGP(DNM, "Tx MO=%s %s\n", abis_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); @@ -1428,7 +1493,7 @@ static uint8_t pchan2comb(enum gsm_phys_chan_config pchan) case GSM_PCHAN_TCH_H: case GSM_PCHAN_PDCH: case GSM_PCHAN_TCH_F_PDCH: - case GSM_PCHAN_TCH_F_TCH_H_PDCH: + case GSM_PCHAN_OSMO_DYN: return 8; default: return 0; @@ -1438,11 +1503,9 @@ static uint8_t pchan2comb(enum gsm_phys_chan_config pchan) static uint8_t ts2comb(struct gsm_bts_trx_ts *ts) { if (ts->pchan_on_init == GSM_PCHAN_TCH_F_PDCH) { - LOGP(DNM, LOGL_ERROR, "%s pchan %s not intended for use" - " with OM2000, use %s instead\n", - gsm_ts_and_pchan_name(ts), - gsm_pchan_name(GSM_PCHAN_TCH_F_PDCH), - gsm_pchan_name(GSM_PCHAN_TCH_F_TCH_H_PDCH)); + LOGP(DNM, LOGL_ERROR, "%s pchan %s not intended for use with OM2000, use %s instead\n", + gsm_ts_and_pchan_name(ts), gsm_pchan_name(GSM_PCHAN_TCH_F_PDCH), + gsm_pchan_name(GSM_PCHAN_OSMO_DYN)); /* If we allowed initialization of TCH/F_PDCH, it would fail * when we try to send the ip.access specific RSL PDCH Act * message for it. Rather fail completely right now: */ @@ -1537,7 +1600,7 @@ int abis_om2k_tx_ts_conf_req(struct gsm_bts_trx_ts *ts) msgb_tv_put(msg, OM2K_DEI_HSN, ts->hopping.hsn); msgb_tv_put(msg, OM2K_DEI_MAIO, ts->hopping.maio); msgb_tv_put(msg, OM2K_DEI_BSIC, ts->trx->bts->bsic); - msgb_tv_put(msg, OM2K_DEI_RX_DIVERSITY, 0x02); /* A */ + msgb_tv_put(msg, OM2K_DEI_RX_DIVERSITY, ts->trx->rbs2000.rx_diversity); msgb_tv16_put(msg, OM2K_DEI_FN_OFFSET, 0); msgb_tv_put(msg, OM2K_DEI_EXT_RANGE, 0); /* Off */ /* Optional: Interference Rejection Combining */ @@ -1606,7 +1669,7 @@ int abis_om2k_tx_ts_conf_req(struct gsm_bts_trx_ts *ts) } DEBUGP(DNM, "Tx MO=%s %s\n", - om2k_mo_name(&mo), + abis_om2k_mo_name(&mo), get_value_string(om2k_msgcode_vals, OM2K_MSGT_TS_CONF_REQ)); return abis_om2k_sendmsg(ts->trx->bts, msg); @@ -1620,7 +1683,9 @@ int abis_om2k_tx_ts_conf_req(struct gsm_bts_trx_ts *ts) #define S(x) (1 << (x)) enum om2k_event_name { + OM2K_MO_EVT_RESET, OM2K_MO_EVT_START, + OM2K_MO_EVT_CHILD_TERM, OM2K_MO_EVT_RX_CONN_COMPL, OM2K_MO_EVT_RX_RESET_COMPL, OM2K_MO_EVT_RX_START_REQ_ACCEPT, @@ -1633,7 +1698,9 @@ enum om2k_event_name { }; static const struct value_string om2k_event_names[] = { + { OM2K_MO_EVT_RESET, "RESET" }, { OM2K_MO_EVT_START, "START" }, + { OM2K_MO_EVT_CHILD_TERM, "CHILD-TERM" }, { 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" }, @@ -1665,6 +1732,7 @@ struct om2k_mo_fsm_priv { struct gsm_bts_trx *trx; struct om2k_mo *mo; uint8_t ts_nr; + uint32_t done_event; }; static void om2k_mo_st_init(struct osmo_fsm_inst *fi, uint32_t event, void *data) @@ -1676,20 +1744,17 @@ static void om2k_mo_st_init(struct osmo_fsm_inst *fi, uint32_t event, void *data 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); + 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); + 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); + 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; } @@ -1703,14 +1768,12 @@ static void om2k_mo_st_wait_conn_compl(struct osmo_fsm_inst *fi, uint32_t event, #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); + 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); + 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; } @@ -1720,8 +1783,7 @@ static void om2k_mo_st_wait_res_compl(struct osmo_fsm_inst *fi, uint32_t event, { struct om2k_mo_fsm_priv *omfp = fi->priv; - osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_START_ACCEPT, - OM2K_TIMEOUT, 0); + 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); } @@ -1731,8 +1793,7 @@ static void om2k_mo_st_wait_start_accept(struct osmo_fsm_inst *fi, uint32_t even switch (omd->msg_type) { case OM2K_MSGT_START_REQ_ACK: - osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_START_RES, - OM2K_TIMEOUT, 0); + 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); @@ -1749,21 +1810,18 @@ static void om2k_mo_st_wait_start_res(struct osmo_fsm_inst *fi, uint32_t event, 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); + 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); return; case OM2K_MO_CLS_DP: /* Transition directory to WAIT_ENABLE_ACCEPT */ - osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_ENABLE_ACCEPT, - OM2K_TIMEOUT, 0); + 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); + 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 @@ -1826,8 +1884,7 @@ static void om2k_mo_st_wait_cfg_res(struct osmo_fsm_inst *fi, uint32_t event, vo return; } - osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_ENABLE_ACCEPT, - OM2K_TIMEOUT, 0); + 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); } @@ -1841,11 +1898,9 @@ static void om2k_mo_st_wait_enable_accept(struct osmo_fsm_inst *fi, uint32_t eve osmo_fsm_inst_state_chg(fi, OM2K_ST_ERROR, 0, 0); break; case OM2K_MSGT_ENABLE_REQ_ACK: - if (omfp->mo->addr.class == OM2K_MO_CLS_IS && - omfp->trx->bts->rbs2000.use_superchannel) + if (omfp->mo->addr.class == OM2K_MO_CLS_IS && omfp->trx->bts->rbs2000.use_superchannel) e1inp_ericsson_set_altc(omfp->trx->bts->oml_link->ts->line, 1); - osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_ENABLE_RES, - OM2K_TIMEOUT, 0); + osmo_fsm_inst_state_chg(fi, OM2K_ST_WAIT_ENABLE_RES, OM2K_TIMEOUT, 0); } } @@ -1855,8 +1910,7 @@ static void om2k_mo_st_wait_enable_res(struct osmo_fsm_inst *fi, uint32_t event, //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); + 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); } @@ -1868,8 +1922,9 @@ static void om2k_mo_st_wait_opinfo_accept(struct osmo_fsm_inst *fi, uint32_t eve static void om2k_mo_s_done_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) { struct om2k_mo_fsm_priv *omfp = fi->priv; - omfp->mo->fsm = NULL; - osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL); + + if (fi->proc.parent) + osmo_fsm_inst_dispatch(fi->proc.parent, omfp->done_event, NULL); } static void om2k_mo_s_error_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) @@ -1880,11 +1935,24 @@ static void om2k_mo_s_error_onenter(struct osmo_fsm_inst *fi, uint32_t prev_stat osmo_fsm_inst_term(fi, OSMO_FSM_TERM_ERROR, NULL); } +static void om2k_mo_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + switch (event) { + case OM2K_MO_EVT_RESET: + osmo_fsm_inst_broadcast_children(fi, event, data); + osmo_fsm_inst_state_chg(fi, OM2K_ST_INIT, 0, 0); + break; + default: + OSMO_ASSERT(0); + } +} + 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_INIT) | S(OM2K_ST_ERROR) | S(OM2K_ST_WAIT_CONN_COMPL) | S(OM2K_ST_WAIT_START_ACCEPT) | @@ -1895,6 +1963,7 @@ static const struct osmo_fsm_state om2k_is_states[] = { .name = "WAIT-CONN-COMPL", .in_event_mask = S(OM2K_MO_EVT_RX_CONN_COMPL), .out_state_mask = S(OM2K_ST_DONE) | + S(OM2K_ST_INIT) | S(OM2K_ST_ERROR) | S(OM2K_ST_WAIT_START_ACCEPT) | S(OM2K_ST_WAIT_RES_COMPL), @@ -1904,6 +1973,7 @@ static const struct osmo_fsm_state om2k_is_states[] = { .name = "WAIT-RES-COMPL", .in_event_mask = S(OM2K_MO_EVT_RX_RESET_COMPL), .out_state_mask = S(OM2K_ST_DONE) | + S(OM2K_ST_INIT) | S(OM2K_ST_ERROR) | S(OM2K_ST_WAIT_START_ACCEPT), .action = om2k_mo_st_wait_res_compl, @@ -1912,6 +1982,7 @@ static const struct osmo_fsm_state om2k_is_states[] = { .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_INIT) | S(OM2K_ST_ERROR) | S(OM2K_ST_WAIT_START_RES), .action =om2k_mo_st_wait_start_accept, @@ -1920,15 +1991,18 @@ static const struct osmo_fsm_state om2k_is_states[] = { .name = "WAIT-START-RES", .in_event_mask = S(OM2K_MO_EVT_RX_START_RES), .out_state_mask = S(OM2K_ST_DONE) | + S(OM2K_ST_INIT) | S(OM2K_ST_ERROR) | S(OM2K_ST_WAIT_CFG_ACCEPT) | - S(OM2K_ST_WAIT_OPINFO_ACCEPT), + S(OM2K_ST_WAIT_OPINFO_ACCEPT) | + S(OM2K_ST_WAIT_ENABLE_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_INIT) | S(OM2K_ST_ERROR) | S(OM2K_ST_WAIT_CFG_RES), .action = om2k_mo_st_wait_cfg_accept, @@ -1937,6 +2011,7 @@ static const struct osmo_fsm_state om2k_is_states[] = { .name = "WAIT-CFG-RES", .in_event_mask = S(OM2K_MO_EVT_RX_CFG_RES), .out_state_mask = S(OM2K_ST_DONE) | + S(OM2K_ST_INIT) | S(OM2K_ST_ERROR) | S(OM2K_ST_WAIT_ENABLE_ACCEPT), .action = om2k_mo_st_wait_cfg_res, @@ -1945,6 +2020,7 @@ static const struct osmo_fsm_state om2k_is_states[] = { .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_INIT) | S(OM2K_ST_ERROR) | S(OM2K_ST_WAIT_ENABLE_RES), .action = om2k_mo_st_wait_enable_accept, @@ -1953,6 +2029,7 @@ static const struct osmo_fsm_state om2k_is_states[] = { .name = "WAIT-ENABLE-RES", .in_event_mask = S(OM2K_MO_EVT_RX_ENA_RES), .out_state_mask = S(OM2K_ST_DONE) | + S(OM2K_ST_INIT) | S(OM2K_ST_ERROR) | S(OM2K_ST_WAIT_OPINFO_ACCEPT), .action = om2k_mo_st_wait_enable_res, @@ -1961,19 +2038,20 @@ static const struct osmo_fsm_state om2k_is_states[] = { .name = "WAIT-OPINFO-ACCEPT", .in_event_mask = S(OM2K_MO_EVT_RX_OPINFO_ACC), .out_state_mask = S(OM2K_ST_DONE) | + S(OM2K_ST_INIT) | S(OM2K_ST_ERROR), .action = om2k_mo_st_wait_opinfo_accept, }, [OM2K_ST_DONE] = { .name = "DONE", .in_event_mask = 0, - .out_state_mask = 0, + .out_state_mask = S(OM2K_ST_INIT), .onenter = om2k_mo_s_done_onenter, }, [OM2K_ST_ERROR] = { .name = "ERROR", .in_event_mask = 0, - .out_state_mask = 0, + .out_state_mask = S(OM2K_ST_INIT), .onenter = om2k_mo_s_error_onenter, }, @@ -1990,13 +2068,14 @@ static struct osmo_fsm om2k_mo_fsm = { .states = om2k_is_states, .num_states = ARRAY_SIZE(om2k_is_states), .log_subsys = DNM, + .allstate_event_mask = S(OM2K_MO_EVT_RESET), + .allstate_action = om2k_mo_allstate, .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) +static struct osmo_fsm_inst *om2k_mo_fsm_alloc(struct osmo_fsm_inst *parent, uint32_t done_event, + struct gsm_bts_trx *trx, struct om2k_mo *mo) { struct osmo_fsm_inst *fi; struct om2k_mo_fsm_priv *omfp; @@ -2006,8 +2085,7 @@ struct osmo_fsm_inst *om2k_mo_fsm_start(struct osmo_fsm_inst *parent, get_value_string(om2k_mo_class_short_vals, mo->addr.class), mo->addr.bts, mo->addr.assoc_so, mo->addr.inst); - fi = osmo_fsm_inst_alloc_child_id(&om2k_mo_fsm, parent, - term_event, idbuf); + fi = osmo_fsm_inst_alloc_child_id(&om2k_mo_fsm, parent, OM2K_MO_EVT_CHILD_TERM, idbuf); if (!fi) return NULL; @@ -2015,38 +2093,38 @@ struct osmo_fsm_inst *om2k_mo_fsm_start(struct osmo_fsm_inst *parent, omfp = talloc_zero(fi, struct om2k_mo_fsm_priv); omfp->mo = mo; omfp->trx = trx; + omfp->done_event = done_event; fi->priv = omfp; - osmo_fsm_inst_dispatch(fi, OM2K_MO_EVT_START, NULL); - return fi; } +static void om2k_mo_fsm_start(struct om2k_mo *mo) +{ + osmo_fsm_inst_dispatch(mo->fsm, OM2K_MO_EVT_START, NULL); +} + 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); + 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); + 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); + 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); + osmo_fsm_inst_dispatch(mo->fsm, OM2K_MO_EVT_RX_START_RES, odm); break; case OM2K_MSGT_CON_CONF_REQ_ACK: @@ -2056,8 +2134,7 @@ int om2k_mo_fsm_recvmsg(struct gsm_bts *bts, struct om2k_mo *mo, 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); + osmo_fsm_inst_dispatch(mo->fsm, OM2K_MO_EVT_RX_CFG_REQ_ACCEPT, odm); break; case OM2K_MSGT_CON_CONF_RES: @@ -2067,24 +2144,20 @@ int om2k_mo_fsm_recvmsg(struct gsm_bts *bts, struct om2k_mo *mo, 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); + 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); + 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); + 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); + osmo_fsm_inst_dispatch(mo->fsm, OM2K_MO_EVT_RX_OPINFO_ACC, odm); break; default: return -1; @@ -2098,7 +2171,9 @@ int om2k_mo_fsm_recvmsg(struct gsm_bts *bts, struct om2k_mo *mo, ***********************************************************************/ enum om2k_trx_event { - OM2K_TRX_EVT_START, + OM2K_TRX_EVT_RESET = OM2K_MO_EVT_RESET, + OM2K_TRX_EVT_START = OM2K_MO_EVT_START, + OM2K_TRX_EVT_CHILD_TERM = OM2K_MO_EVT_CHILD_TERM, OM2K_TRX_EVT_TRXC_DONE, OM2K_TRX_EVT_TX_DONE, OM2K_TRX_EVT_RX_DONE, @@ -2107,7 +2182,9 @@ enum om2k_trx_event { }; static struct value_string om2k_trx_events[] = { + { OM2K_TRX_EVT_RESET, "RESET" }, { OM2K_TRX_EVT_START, "START" }, + { OM2K_TRX_EVT_CHILD_TERM, "CHILD-TERM" }, { OM2K_TRX_EVT_TRXC_DONE, "TRXC-DONE" }, { OM2K_TRX_EVT_TX_DONE, "TX-DONE" }, { OM2K_TRX_EVT_RX_DONE, "RX-DONE" }, @@ -2130,6 +2207,7 @@ enum om2k_trx_state { struct om2k_trx_fsm_priv { struct gsm_bts_trx *trx; uint8_t cur_ts_nr; + uint32_t done_event; }; static void om2k_trx_s_init(struct osmo_fsm_inst *fi, uint32_t event, void *data) @@ -2137,10 +2215,8 @@ 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); + osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_WAIT_TRXC, TRX_FSM_TIMEOUT, 0); + om2k_mo_fsm_start(&otfp->trx->rbs2000.trxc.om2k_mo); } static void om2k_trx_s_wait_trxc(struct osmo_fsm_inst *fi, uint32_t event, void *data) @@ -2148,10 +2224,8 @@ static void om2k_trx_s_wait_trxc(struct osmo_fsm_inst *fi, uint32_t event, void 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); + osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_WAIT_TX, TRX_FSM_TIMEOUT, 0); + om2k_mo_fsm_start(&otfp->trx->rbs2000.tx.om2k_mo); } static void om2k_trx_s_wait_tx(struct osmo_fsm_inst *fi, uint32_t event, void *data) @@ -2159,10 +2233,8 @@ static void om2k_trx_s_wait_tx(struct osmo_fsm_inst *fi, uint32_t event, void *d 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); + osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_WAIT_RX, TRX_FSM_TIMEOUT, 0); + om2k_mo_fsm_start(&otfp->trx->rbs2000.rx.om2k_mo); } static void om2k_trx_s_wait_rx(struct osmo_fsm_inst *fi, uint32_t event, void *data) @@ -2171,12 +2243,10 @@ static void om2k_trx_s_wait_rx(struct osmo_fsm_inst *fi, uint32_t event, void *d 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); + osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_WAIT_TS, TRX_FSM_TIMEOUT, 0); otfp->cur_ts_nr = 0; ts = &otfp->trx->ts[otfp->cur_ts_nr]; - om2k_mo_fsm_start(fi, OM2K_TRX_EVT_TS_DONE, otfp->trx, - &ts->rbs2000.om2k_mo); + om2k_mo_fsm_start(&ts->rbs2000.om2k_mo); } static void om2k_trx_s_wait_ts(struct osmo_fsm_inst *fi, uint32_t event, void *data) @@ -2184,16 +2254,11 @@ static void om2k_trx_s_wait_ts(struct osmo_fsm_inst *fi, uint32_t event, void *d struct om2k_trx_fsm_priv *otfp = fi->priv; struct gsm_bts_trx_ts *ts; - /* notify TS is ready */ - ts = &otfp->trx->ts[otfp->cur_ts_nr]; - osmo_fsm_inst_dispatch(ts->fi, TS_EV_OML_READY, NULL); - /* next ? */ if (++otfp->cur_ts_nr < 8) { /* iterate to the next timeslot */ ts = &otfp->trx->ts[otfp->cur_ts_nr]; - om2k_mo_fsm_start(fi, OM2K_TRX_EVT_TS_DONE, otfp->trx, - &ts->rbs2000.om2k_mo); + om2k_mo_fsm_start(&ts->rbs2000.om2k_mo); } else { /* only after all 8 TS */ osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_SEND_SI, 0, 0); @@ -2212,55 +2277,117 @@ static void om2k_trx_s_send_si(struct osmo_fsm_inst *fi, uint32_t prev_state) static void om2k_trx_s_done_onenter(struct osmo_fsm_inst *fi, uint32_t prev_state) { - osmo_fsm_inst_term(fi, OSMO_FSM_TERM_REGULAR, NULL); + struct om2k_trx_fsm_priv *otfp = fi->priv; + struct nm_statechg_signal_data nsd; + struct nm_statechg_signal_data nsd_bb_transc; + struct gsm_bts_trx *trx = otfp->trx; + unsigned int i; + + memset(&nsd, 0, sizeof(nsd)); + + nsd.bts = trx->bts; + nsd.obj = trx; + nsd.old_state = trx->mo.nm_state; + nsd.new_state = trx->mo.nm_state; + nsd.om2k_mo = &trx->rbs2000.trxc.om2k_mo.addr; + + /* See e1_config:bts_isdn_sign_link() / OS#4914 */ + nsd.new_state.administrative = NM_STATE_UNLOCKED; + trx->mo.nm_state.administrative = nsd.new_state.administrative; + osmo_signal_dispatch(SS_NM, S_NM_STATECHG, &nsd); + + /* The OM2000 -> 12.21 mapping we do doesn't have separate bb_transc MO, + * for compatibility reasons we pretend to have it anyway and mirror the + * mo state of the TRXC on it. */ + memset(&nsd_bb_transc, 0, sizeof(nsd)); + nsd_bb_transc.bts = trx->bts; + nsd_bb_transc.obj = &trx->bb_transc; + nsd_bb_transc.old_state = trx->bb_transc.mo.nm_state; + nsd_bb_transc.new_state = trx->bb_transc.mo.nm_state; + nsd_bb_transc.om2k_mo = &trx->rbs2000.trxc.om2k_mo.addr; + nsd_bb_transc.new_state.administrative = NM_STATE_UNLOCKED; + trx->bb_transc.mo.nm_state.administrative = nsd_bb_transc.new_state.administrative; + osmo_signal_dispatch(SS_NM, S_NM_STATECHG, &nsd_bb_transc); + + abis_om2000_fsm_transc_becomes_enabled(trx); + + if (fi->proc.parent) + osmo_fsm_inst_dispatch(fi->proc.parent, otfp->done_event, NULL); + + /* Notify the timeslot FSM that all TRX initialization steps are done. */ + for (i = 0; i < ARRAY_SIZE(trx->ts); i++) + osmo_fsm_inst_dispatch(trx->ts[i].fi, TS_EV_OML_READY, NULL); +} + +static void om2k_trx_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct om2k_trx_fsm_priv *otfp = fi->priv; + + switch (event) { + case OM2K_TRX_EVT_RESET: + abis_om2000_fsm_transc_becomes_disabled(otfp->trx); + osmo_fsm_inst_broadcast_children(fi, event, data); + osmo_fsm_inst_state_chg(fi, OM2K_TRX_S_INIT, 0, 0); + break; + default: + OSMO_ASSERT(0); + } } 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), + .out_state_mask = S(OM2K_TRX_S_WAIT_TRXC) | + S(OM2K_TRX_S_INIT), .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), + S(OM2K_TRX_S_WAIT_TX) | + S(OM2K_TRX_S_INIT), .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), + S(OM2K_TRX_S_WAIT_RX) | + S(OM2K_TRX_S_INIT), .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), + S(OM2K_TRX_S_WAIT_TS) | + S(OM2K_TRX_S_INIT), .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_SEND_SI), + S(OM2K_TRX_S_SEND_SI) | + S(OM2K_TRX_S_INIT), .name = "WAIT-TS", .action = om2k_trx_s_wait_ts, }, [OM2K_TRX_S_SEND_SI] = { .out_state_mask = S(OM2K_TRX_S_ERROR) | - S(OM2K_TRX_S_DONE), + S(OM2K_TRX_S_DONE) | + S(OM2K_TRX_S_INIT), .name = "SEND-SI", .onenter = om2k_trx_s_send_si, }, [OM2K_TRX_S_DONE] = { + .out_state_mask = S(OM2K_TRX_S_INIT), .name = "DONE", .onenter = om2k_trx_s_done_onenter, }, [OM2K_TRX_S_ERROR] = { + .out_state_mask = S(OM2K_TRX_S_INIT), .name = "ERROR", }, }; @@ -2276,41 +2403,68 @@ static struct osmo_fsm om2k_trx_fsm = { .states = om2k_trx_states, .num_states = ARRAY_SIZE(om2k_trx_states), .log_subsys = DNM, + .allstate_event_mask = S(OM2K_TRX_EVT_RESET), + .allstate_action = om2k_trx_allstate, .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) +static struct osmo_fsm_inst *om2k_trx_fsm_alloc(struct osmo_fsm_inst *parent, + struct gsm_bts_trx *trx, uint32_t done_event) { struct osmo_fsm_inst *fi; struct om2k_trx_fsm_priv *otfp; char idbuf[32]; + OSMO_ASSERT(!trx->rbs2000.trx_fi); + 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); + fi = osmo_fsm_inst_alloc_child_id(&om2k_trx_fsm, parent, OM2K_MO_EVT_CHILD_TERM, idbuf); if (!fi) return NULL; otfp = talloc_zero(fi, struct om2k_trx_fsm_priv); otfp->trx = trx; + otfp->done_event = done_event; fi->priv = otfp; - osmo_fsm_inst_dispatch(fi, OM2K_TRX_EVT_START, NULL); - return fi; } +void om2k_trx_fsm_start(struct gsm_bts_trx *trx) +{ + struct osmo_fsm_inst *bts_fi = trx->bts->rbs2000.bts_fi; + OSMO_ASSERT(trx->rbs2000.trx_fi); + + /* suppress if BTS is not yet brought up */ + if (bts_fi->state == OM2K_BTS_S_DONE || bts_fi->state == OM2K_BTS_S_WAIT_TRX) + return; + + osmo_fsm_inst_dispatch(trx->rbs2000.trx_fi, OM2K_TRX_EVT_START, NULL); +} + +void om2k_trx_fsm_reset(struct gsm_bts_trx *trx) +{ + struct osmo_fsm_inst *bts_fi = trx->bts->rbs2000.bts_fi; + OSMO_ASSERT(trx->rbs2000.trx_fi); + OSMO_ASSERT(trx->rbs2000.trx_fi); + + /* suppress if BTS is not yet brought up */ + if (bts_fi->state == OM2K_BTS_S_DONE || bts_fi->state == OM2K_BTS_S_WAIT_TRX) + return; + + osmo_fsm_inst_dispatch(trx->rbs2000.trx_fi, OM2K_TRX_EVT_RESET, NULL); +} /*********************************************************************** * OM2000 BTS Finite State Machine, initializes CF and all siblings ***********************************************************************/ enum om2k_bts_event { - OM2K_BTS_EVT_START, + OM2K_BTS_EVT_RESET = OM2K_MO_EVT_RESET, + OM2K_BTS_EVT_START = OM2K_MO_EVT_START, + OM2K_BTS_EVT_CHILD_TERM = OM2K_MO_EVT_CHILD_TERM, OM2K_BTS_EVT_CF_DONE, OM2K_BTS_EVT_IS_DONE, OM2K_BTS_EVT_CON_DONE, @@ -2318,11 +2472,14 @@ enum om2k_bts_event { OM2K_BTS_EVT_MCTR_DONE, OM2K_BTS_EVT_TRX_LAPD_UP, OM2K_BTS_EVT_TRX_DONE, + OM2K_BTS_EVT_TRX_TERM, OM2K_BTS_EVT_STOP, }; static const struct value_string om2k_bts_events[] = { + { OM2K_BTS_EVT_RESET, "RESET" }, { OM2K_BTS_EVT_START, "START" }, + { OM2K_BTS_EVT_CHILD_TERM, "CHILD-TERM" }, { OM2K_BTS_EVT_CF_DONE, "CF-DONE" }, { OM2K_BTS_EVT_IS_DONE, "IS-DONE" }, { OM2K_BTS_EVT_CON_DONE, "CON-DONE" }, @@ -2334,19 +2491,6 @@ static const struct value_string om2k_bts_events[] = { { 0, NULL } }; -enum om2k_bts_state { - OM2K_BTS_S_INIT, - OM2K_BTS_S_WAIT_CF, - OM2K_BTS_S_WAIT_IS, - OM2K_BTS_S_WAIT_CON, - OM2K_BTS_S_WAIT_TF, - OM2K_BTS_S_WAIT_MCTR, - OM2K_BTS_S_WAIT_TRX_LAPD, - 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; @@ -2358,10 +2502,8 @@ static void om2k_bts_s_init(struct osmo_fsm_inst *fi, uint32_t event, void *data 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); + osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_CF, BTS_FSM_TIMEOUT, 0); + om2k_mo_fsm_start(&bts->rbs2000.cf.om2k_mo); } static void om2k_bts_s_wait_cf(struct osmo_fsm_inst *fi, uint32_t event, void *data) @@ -2372,8 +2514,7 @@ static void om2k_bts_s_wait_cf(struct osmo_fsm_inst *fi, uint32_t event, void *d OSMO_ASSERT(event == OM2K_BTS_EVT_CF_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); + om2k_mo_fsm_start(&bts->rbs2000.tf.om2k_mo); } static void om2k_bts_s_wait_tf(struct osmo_fsm_inst *fi, uint32_t event, void *data) @@ -2383,10 +2524,14 @@ static void om2k_bts_s_wait_tf(struct osmo_fsm_inst *fi, uint32_t event, void *d OSMO_ASSERT(event == OM2K_BTS_EVT_TF_DONE); - osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_CON, - BTS_FSM_TIMEOUT, 0); - om2k_mo_fsm_start(fi, OM2K_BTS_EVT_CON_DONE, bts->c0, - &bts->rbs2000.con.om2k_mo); + if (!llist_count(&bts->rbs2000.con.conn_groups)) { + /* skip CON object if we have no configuration for it */ + osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_IS, BTS_FSM_TIMEOUT, 0); + om2k_mo_fsm_start(&bts->rbs2000.is.om2k_mo); + } else { + osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_CON, BTS_FSM_TIMEOUT, 0); + om2k_mo_fsm_start(&bts->rbs2000.con.om2k_mo); + } } static void om2k_bts_s_wait_con(struct osmo_fsm_inst *fi, uint32_t event, void *data) @@ -2396,10 +2541,8 @@ static void om2k_bts_s_wait_con(struct osmo_fsm_inst *fi, uint32_t event, void * OSMO_ASSERT(event == OM2K_BTS_EVT_CON_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); + osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_IS, BTS_FSM_TIMEOUT, 0); + om2k_mo_fsm_start(&bts->rbs2000.is.om2k_mo); } static void om2k_bts_s_wait_is(struct osmo_fsm_inst *fi, uint32_t event, void *data) @@ -2411,13 +2554,10 @@ static void om2k_bts_s_wait_is(struct osmo_fsm_inst *fi, uint32_t event, void *d /* If we're running OML >= G12R13, start MCTR, else skip directly to TRX */ if (bts->rbs2000.om2k_version[0].active >= 0x0c0d) { - osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_MCTR, - BTS_FSM_TIMEOUT, 0); - om2k_mo_fsm_start(fi, OM2K_BTS_EVT_MCTR_DONE, bts->c0, - &bts->rbs2000.mctr.om2k_mo); + osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_MCTR, BTS_FSM_TIMEOUT, 0); + om2k_mo_fsm_start(&bts->rbs2000.mctr.om2k_mo); } else { - osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_TRX_LAPD, - TRX_LAPD_TIMEOUT, 0); + osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_TRX_LAPD, TRX_LAPD_TIMEOUT, 0); } } @@ -2425,8 +2565,7 @@ static void om2k_bts_s_wait_mctr(struct osmo_fsm_inst *fi, uint32_t event, void { OSMO_ASSERT(event == OM2K_BTS_EVT_MCTR_DONE); - osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_TRX_LAPD, - TRX_LAPD_TIMEOUT, 0); + osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_TRX_LAPD, TRX_LAPD_TIMEOUT, 0); } static void om2k_bts_s_wait_trx_lapd(struct osmo_fsm_inst *fi, uint32_t event, void *data) @@ -2436,11 +2575,10 @@ static void om2k_bts_s_wait_trx_lapd(struct osmo_fsm_inst *fi, uint32_t event, v OSMO_ASSERT(event == OM2K_BTS_EVT_TRX_LAPD_UP); - osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_WAIT_TRX, - BTS_FSM_TIMEOUT, 0); + 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); + om2k_trx_fsm_start(trx); } static void om2k_bts_s_wait_trx(struct osmo_fsm_inst *fi, uint32_t event, void *data) @@ -2452,7 +2590,7 @@ static void om2k_bts_s_wait_trx(struct osmo_fsm_inst *fi, uint32_t event, void * 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); + om2k_trx_fsm_start(trx); } else { osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_DONE, 0, 0); } @@ -2460,34 +2598,50 @@ static void om2k_bts_s_wait_trx(struct osmo_fsm_inst *fi, uint32_t event, void * 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 void om2k_bts_allstate(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + switch (event) { + case OM2K_BTS_EVT_RESET: + osmo_fsm_inst_broadcast_children(fi, event, data); + osmo_fsm_inst_state_chg(fi, OM2K_BTS_S_INIT, 0, 0); + break; + default: + OSMO_ASSERT(0); + } } 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), + .out_state_mask = S(OM2K_BTS_S_WAIT_CF) | + S(OM2K_BTS_S_INIT), .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_TF), + S(OM2K_BTS_S_WAIT_TF) | + S(OM2K_BTS_S_INIT), .name = "WAIT-CF", .action = om2k_bts_s_wait_cf, }, [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_CON), + S(OM2K_BTS_S_WAIT_CON) | + S(OM2K_BTS_S_WAIT_IS) | + S(OM2K_BTS_S_INIT), .name = "WAIT-TF", .action = om2k_bts_s_wait_tf, }, [OM2K_BTS_S_WAIT_CON] = { .in_event_mask = S(OM2K_BTS_EVT_CON_DONE), .out_state_mask = S(OM2K_BTS_S_ERROR) | - S(OM2K_BTS_S_WAIT_IS), + S(OM2K_BTS_S_WAIT_IS) | + S(OM2K_BTS_S_INIT), .name = "WAIT-CON", .action = om2k_bts_s_wait_con, }, @@ -2495,35 +2649,41 @@ static const struct osmo_fsm_state om2k_bts_states[] = { .in_event_mask = S(OM2K_BTS_EVT_IS_DONE), .out_state_mask = S(OM2K_BTS_S_ERROR) | S(OM2K_BTS_S_WAIT_MCTR) | - S(OM2K_BTS_S_WAIT_TRX_LAPD), + S(OM2K_BTS_S_WAIT_TRX_LAPD) | + S(OM2K_BTS_S_INIT), .name = "WAIT-IS", .action = om2k_bts_s_wait_is, }, [OM2K_BTS_S_WAIT_MCTR] = { .in_event_mask = S(OM2K_BTS_EVT_MCTR_DONE), .out_state_mask = S(OM2K_BTS_S_ERROR) | - S(OM2K_BTS_S_WAIT_TRX_LAPD), + S(OM2K_BTS_S_WAIT_TRX_LAPD) | + S(OM2K_BTS_S_INIT), .name = "WAIT-MCTR", .action = om2k_bts_s_wait_mctr, }, [OM2K_BTS_S_WAIT_TRX_LAPD] = { .in_event_mask = S(OM2K_BTS_EVT_TRX_LAPD_UP), - .out_state_mask = S(OM2K_BTS_S_WAIT_TRX), + .out_state_mask = S(OM2K_BTS_S_WAIT_TRX) | + S(OM2K_BTS_S_INIT), .name = "WAIT-TRX-LAPD", .action = om2k_bts_s_wait_trx_lapd, }, [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), + S(OM2K_BTS_S_DONE) | + S(OM2K_BTS_S_INIT), .name = "WAIT-TRX", .action = om2k_bts_s_wait_trx, }, [OM2K_BTS_S_DONE] = { + .out_state_mask = S(OM2K_BTS_S_INIT), .name = "DONE", .onenter = om2k_bts_s_done_onenter, }, [OM2K_BTS_S_ERROR] = { + .out_state_mask = S(OM2K_BTS_S_INIT), .name = "ERROR", }, }; @@ -2546,31 +2706,44 @@ static struct osmo_fsm om2k_bts_fsm = { .states = om2k_bts_states, .num_states = ARRAY_SIZE(om2k_bts_states), .log_subsys = DNM, + .allstate_event_mask = S(OM2K_BTS_EVT_RESET), + .allstate_action = om2k_bts_allstate, .event_names = om2k_bts_events, .timer_cb = om2k_bts_timer_cb, }; -struct osmo_fsm_inst * -om2k_bts_fsm_start(struct gsm_bts *bts) +static struct osmo_fsm_inst * +om2k_bts_fsm_alloc(struct gsm_bts *bts) { struct osmo_fsm_inst *fi; struct om2k_bts_fsm_priv *obfp; char idbuf[16]; + OSMO_ASSERT(!bts->rbs2000.bts_fi); + snprintf(idbuf, sizeof(idbuf), "%u", bts->nr); - fi = osmo_fsm_inst_alloc(&om2k_bts_fsm, bts, NULL, - LOGL_DEBUG, idbuf); + 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; } +void om2k_bts_fsm_start(struct gsm_bts *bts) +{ + OSMO_ASSERT(bts->rbs2000.bts_fi); + osmo_fsm_inst_dispatch(bts->rbs2000.bts_fi, OM2K_BTS_EVT_START, NULL); +} + +void om2k_bts_fsm_reset(struct gsm_bts *bts) +{ + OSMO_ASSERT(bts->rbs2000.bts_fi); + osmo_fsm_inst_dispatch(bts->rbs2000.bts_fi, OM2K_BTS_EVT_RESET, NULL); +} /*********************************************************************** * OM2000 Negotiation @@ -2587,7 +2760,7 @@ static int abis_om2k_tx_negot_req_ack(struct gsm_bts *bts, const struct abis_om2 msgb_tlv_put(msg, OM2K_DEI_NEGOT_REC2, len, data); - DEBUGP(DNM, "Tx MO=%s %s\n", om2k_mo_name(mo), + DEBUGP(DNM, "Tx MO=%s %s\n", abis_om2k_mo_name(mo), get_value_string(om2k_msgcode_vals, OM2K_MSGT_NEGOT_REQ_ACK)); return abis_om2k_sendmsg(bts, msg); @@ -2709,18 +2882,16 @@ static int om2k_rx_nack(struct msgb *msg) uint16_t msg_type = ntohs(o2h->msg_type); struct tlv_parsed tp; - LOGP(DNM, LOGL_ERROR, "Rx MO=%s %s", om2k_mo_name(&o2h->mo), + LOGP(DNM, LOGL_ERROR, "Rx MO=%s %s", abis_om2k_mo_name(&o2h->mo), get_value_string(om2k_msgcode_vals, msg_type)); abis_om2k_msg_tlv_parse(&tp, o2h); if (TLVP_PRESENT(&tp, OM2K_DEI_REASON_CODE)) - LOGPC(DNM, LOGL_ERROR, ", Reason 0x%02x", - *TLVP_VAL(&tp, OM2K_DEI_REASON_CODE)); + LOGPC(DNM, LOGL_ERROR, ", Reason 0x%02x", *TLVP_VAL(&tp, OM2K_DEI_REASON_CODE)); if (TLVP_PRESENT(&tp, OM2K_DEI_RESULT_CODE)) LOGPC(DNM, LOGL_ERROR, ", Result %s", - get_value_string(om2k_result_strings, - *TLVP_VAL(&tp, OM2K_DEI_RESULT_CODE))); + get_value_string(om2k_result_strings, *TLVP_VAL(&tp, OM2K_DEI_RESULT_CODE))); LOGPC(DNM, LOGL_ERROR, "\n"); return 0; @@ -2734,8 +2905,7 @@ static int process_mo_state(struct gsm_bts *bts, struct om2k_decoded_msg *odm) return -EIO; 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(&odm->o2h.mo), + LOGP(DNM, LOGL_DEBUG, "Rx MO=%s %s, MO State: %s\n", abis_om2k_mo_name(&odm->o2h.mo), get_value_string(om2k_msgcode_vals, odm->msg_type), get_value_string(om2k_mostate_vals, mo_state)); @@ -2743,10 +2913,8 @@ static int process_mo_state(struct gsm_bts *bts, struct om2k_decoded_msg *odm) * not yield an enabled mo-state */ if (odm->msg_type == OM2K_MSGT_ENABLE_RES && mo_state != OM2K_MO_S_ENABLED) { - LOGP(DNM, LOGL_ERROR, - "Rx MO=%s %s Failed to enable MO State!\n", - om2k_mo_name(&odm->o2h.mo), - get_value_string(om2k_msgcode_vals, odm->msg_type)); + LOGP(DNM, LOGL_ERROR, "Rx MO=%s %s Failed to enable MO State!\n", + abis_om2k_mo_name(&odm->o2h.mo), get_value_string(om2k_msgcode_vals, odm->msg_type)); } update_mo_state(bts, &odm->o2h.mo, mo_state); @@ -2786,7 +2954,7 @@ static bool display_fault_bits(const uint8_t *vect, uint16_t len, } sprintf(string + strlen(string), ")\n"); - DEBUGP(DNM, "Rx MO=%s %s", om2k_mo_name(mo), string); + DEBUGP(DNM, "Rx MO=%s %s", abis_om2k_mo_name(mo), string); return true; } @@ -2816,8 +2984,7 @@ static void display_fault_maps(const uint8_t *src, unsigned int src_len, src++; src_len--; if (msg_code != OM2K_MSGT_FAULT_REP) { - LOGP(DNM, LOGL_ERROR, "Rx MO=%s Fault report: invalid message code!\n", - om2k_mo_name(mo)); + LOGP(DNM, LOGL_ERROR, "Rx MO=%s Fault report: invalid message code!\n", abis_om2k_mo_name(mo)); return; } @@ -2830,22 +2997,19 @@ static void display_fault_maps(const uint8_t *src, unsigned int src_len, /* Bail if an the maximum number of TLV fields * have been parsed */ - if (tlv_count >= 11) { - LOGP(DNM, LOGL_ERROR, - "Rx MO=%s Fault Report: too many tlv elements!\n", - om2k_mo_name(mo)); + if (tlv_count >= 20) { + LOGP(DNM, LOGL_ERROR, "Rx MO=%s Fault Report: too many tlv elements!\n", + abis_om2k_mo_name(mo)); return; } /* Parse TLV field */ - rc = tlv_parse_one(&tag, &tag_len, &val, &om2k_att_tlvdef, - src + src_pos, src_len - src_pos); + rc = tlv_parse_one(&tag, &tag_len, &val, &om2k_att_tlvdef, src + src_pos, src_len - src_pos); if (rc > 0) src_pos += rc; else { - LOGP(DNM, LOGL_ERROR, - "Rx MO=%s Fault Report: invalid tlv element!\n", - om2k_mo_name(mo)); + LOGP(DNM, LOGL_ERROR, "Rx MO=%s Fault Report: invalid tlv element!\n", + abis_om2k_mo_name(mo)); return; } @@ -2872,8 +3036,7 @@ static void display_fault_maps(const uint8_t *src, unsigned int src_len, } if (!faults_present) { - DEBUGP(DNM, "Rx MO=%s Fault Report: All faults ceased!\n", - om2k_mo_name(mo)); + DEBUGP(DNM, "Rx MO=%s Fault Report: All faults ceased!\n", abis_om2k_mo_name(mo)); } } @@ -2890,28 +3053,24 @@ int abis_om2k_rcvmsg(struct msgb *msg) /* Various consistency checks */ if (oh->placement != ABIS_OM_PLACEMENT_ONLY) { - LOGP(DNM, LOGL_ERROR, "ABIS OML placement 0x%x not supported\n", - oh->placement); + LOGP(DNM, LOGL_ERROR, "ABIS OML placement 0x%x not supported\n", oh->placement); if (oh->placement != ABIS_OM_PLACEMENT_FIRST) return -EINVAL; } if (oh->sequence != 0) { - LOGP(DNM, LOGL_ERROR, "ABIS OML sequence 0x%x != 0x00\n", - oh->sequence); + LOGP(DNM, LOGL_ERROR, "ABIS OML sequence 0x%x != 0x00\n", oh->sequence); return -EINVAL; } msg->l3h = (unsigned char *)o2h + sizeof(*o2h); if (oh->mdisc != ABIS_OM_MDISC_FOM) { - LOGP(DNM, LOGL_ERROR, "unknown ABIS OM2000 message discriminator 0x%x\n", - oh->mdisc); + LOGP(DNM, LOGL_ERROR, "unknown ABIS OM2000 message discriminator 0x%x\n", oh->mdisc); return -EINVAL; } - DEBUGP(DNM, "Rx MO=%s %s (%s)\n", om2k_mo_name(&o2h->mo), - get_value_string(om2k_msgcode_vals, msg_type), - osmo_hexdump(msg->l2h, msgb_l2len(msg))); + DEBUGP(DNM, "Rx MO=%s %s (%s)\n", abis_om2k_mo_name(&o2h->mo), + get_value_string(om2k_msgcode_vals, msg_type), osmo_hexdump(msg->l2h, msgb_l2len(msg))); om2k_decode_msg(&odm, msg); @@ -2920,11 +3079,13 @@ int abis_om2k_rcvmsg(struct msgb *msg) switch (msg_type) { case OM2K_MSGT_CAL_TIME_REQ: rc = abis_om2k_cal_time_resp(bts); - break; + /* we receive this from MOs without FSM (https://osmocom.org/issues/4670) */ + goto no_mo; case OM2K_MSGT_FAULT_REP: display_fault_maps(msg->l2h, msgb_l2len(msg), &o2h->mo); rc = abis_om2k_tx_simple(bts, &o2h->mo, OM2K_MSGT_FAULT_REP_ACK); - break; + /* we receive this from MOs without FSM (https://osmocom.org/issues/4643) */ + goto no_mo; case OM2K_MSGT_NEGOT_REQ: rc = om2k_rx_negot_req(msg); break; @@ -2996,26 +3157,24 @@ int abis_om2k_rcvmsg(struct msgb *msg) 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; + "%s: %s\n", get_value_string(om2k_msgcode_vals, msg_type), msgb_hexdump(msg)); + goto no_mo; } if (!mo->fsm) { LOGP(DNM, LOGL_ERROR, "MO object should not generate any message. fsm == NULL " - "%s: %s\n", get_value_string(om2k_msgcode_vals, msg_type), - msgb_hexdump(msg)); - return 0; + "%s: %s\n", get_value_string(om2k_msgcode_vals, msg_type), msgb_hexdump(msg)); + goto no_mo; } /* Dispatch message to that MO */ om2k_mo_fsm_recvmsg(bts, mo, &odm); +no_mo: 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) +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; @@ -3027,21 +3186,28 @@ static void om2k_mo_init(struct om2k_mo *mo, uint8_t class, void abis_om2k_trx_init(struct gsm_bts_trx *trx) { struct gsm_bts *bts = trx->bts; + struct osmo_fsm_inst *trx_fi; 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); + trx_fi = om2k_trx_fsm_alloc(trx->bts->rbs2000.bts_fi, trx, OM2K_BTS_EVT_TRX_DONE); + trx->rbs2000.trx_fi = trx_fi; + trx->rbs2000.rx_diversity = OM2K_RX_DIVERSITY_A; + + om2k_mo_init(&trx->rbs2000.trxc.om2k_mo, OM2K_MO_CLS_TRXC, bts->nr, 255, trx->nr); + om2k_mo_fsm_alloc(trx_fi, OM2K_TRX_EVT_TRXC_DONE, trx, &trx->rbs2000.trxc.om2k_mo); + + om2k_mo_init(&trx->rbs2000.tx.om2k_mo, OM2K_MO_CLS_TX, bts->nr, 255, trx->nr); + om2k_mo_fsm_alloc(trx_fi, OM2K_TRX_EVT_TX_DONE, trx, &trx->rbs2000.tx.om2k_mo); + + om2k_mo_init(&trx->rbs2000.rx.om2k_mo, OM2K_MO_CLS_RX, bts->nr, 255, trx->nr); + om2k_mo_fsm_alloc(trx_fi, OM2K_TRX_EVT_RX_DONE, trx, &trx->rbs2000.rx.om2k_mo); for (i = 0; i < ARRAY_SIZE(trx->ts); i++) { struct gsm_bts_trx_ts *ts = &trx->ts[i]; - om2k_mo_init(&ts->rbs2000.om2k_mo, OM2K_MO_CLS_TS, - bts->nr, trx->nr, i); + om2k_mo_init(&ts->rbs2000.om2k_mo, OM2K_MO_CLS_TS, bts->nr, trx->nr, i); + om2k_mo_fsm_alloc(trx_fi, OM2K_TRX_EVT_TS_DONE, trx, &ts->rbs2000.om2k_mo); OSMO_ASSERT(ts->fi); } } @@ -3049,20 +3215,31 @@ void abis_om2k_trx_init(struct gsm_bts_trx *trx) /* initialize the OM2K_MO members of gsm_bts */ void abis_om2k_bts_init(struct gsm_bts *bts) { + struct osmo_fsm_inst *bts_fi; + 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); - om2k_mo_init(&bts->rbs2000.mctr.om2k_mo, OM2K_MO_CLS_MCTR, - bts->nr, 0xFF, 0); // FIXME: There can be multiple MCTRs ... + bts_fi = om2k_bts_fsm_alloc(bts); + bts->rbs2000.bts_fi = bts_fi; + bts->rbs2000.sync_src = OM2K_SYNC_SRC_INTERNAL; + + om2k_mo_init(&bts->rbs2000.cf.om2k_mo, OM2K_MO_CLS_CF, bts->nr, 0xFF, 0); + om2k_mo_fsm_alloc(bts_fi, OM2K_BTS_EVT_CF_DONE, bts->c0, &bts->rbs2000.cf.om2k_mo); + + om2k_mo_init(&bts->rbs2000.is.om2k_mo, OM2K_MO_CLS_IS, bts->nr, 0xFF, 0); + om2k_mo_fsm_alloc(bts_fi, OM2K_BTS_EVT_IS_DONE, bts->c0, &bts->rbs2000.is.om2k_mo); + + om2k_mo_init(&bts->rbs2000.con.om2k_mo, OM2K_MO_CLS_CON, bts->nr, 0xFF, 0); + om2k_mo_fsm_alloc(bts_fi, OM2K_BTS_EVT_CON_DONE, bts->c0, &bts->rbs2000.con.om2k_mo); + + 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); + om2k_mo_fsm_alloc(bts_fi, OM2K_BTS_EVT_TF_DONE, bts->c0, &bts->rbs2000.tf.om2k_mo); + + om2k_mo_init(&bts->rbs2000.mctr.om2k_mo, OM2K_MO_CLS_MCTR, bts->nr, 0xFF, 0); + om2k_mo_fsm_alloc(bts_fi, OM2K_BTS_EVT_MCTR_DONE, bts->c0, &bts->rbs2000.mctr.om2k_mo); + // FIXME: There can be multiple MCTRs ... } static __attribute__((constructor)) void abis_om2k_init(void) |