osmo-bts/src/osmo-bts-trx/trx_provision_fsm.c

735 lines
22 KiB
C

/* TRX provision FSM */
/* (C) 2020 by sysmocom - s.m.f.c. GmbH <info@sysmocom.de>
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <errno.h>
#include <unistd.h>
#include <inttypes.h>
#include <osmocom/core/fsm.h>
#include <osmocom/core/tdef.h>
#include <osmocom/gsm/protocol/gsm_12_21.h>
#include <osmo-bts/logging.h>
#include <osmo-bts/gsm_data.h>
#include <osmo-bts/bts_model.h>
#include <osmo-bts/bts.h>
#include <osmo-bts/rsl.h>
#include <osmo-bts/nm_common_fsm.h>
#include <osmo-bts/signal.h>
#include "l1_if.h"
#include "trx_provision_fsm.h"
#define X(s) (1 << (s))
#define trx_prov_fsm_state_chg(fi, NEXT_STATE) \
osmo_fsm_inst_state_chg(fi, NEXT_STATE, 0, 0)
static void l1if_poweronoff_cb(struct trx_l1h *l1h, bool poweronoff, int rc)
{
struct phy_instance *pinst = l1h->phy_inst;
struct phy_link *plink = pinst->phy_link;
plink->u.osmotrx.powered = poweronoff;
if (poweronoff) {
plink->u.osmotrx.poweron_sent = false;
osmo_fsm_inst_dispatch(l1h->provision_fi, TRX_PROV_EV_POWERON_CNF, (void*)(intptr_t)rc);
} else {
plink->u.osmotrx.poweroff_sent = false;
osmo_fsm_inst_dispatch(l1h->provision_fi, TRX_PROV_EV_POWEROFF_CNF, (void*)(intptr_t)rc);
}
}
void l1if_rxtune_cb(struct trx_l1h *l1h, int rc)
{
osmo_fsm_inst_dispatch(l1h->provision_fi, TRX_PROV_EV_RXTUNE_CNF, (void*)(intptr_t)rc);
}
void l1if_txtune_cb(struct trx_l1h *l1h, int rc)
{
osmo_fsm_inst_dispatch(l1h->provision_fi, TRX_PROV_EV_TXTUNE_CNF, (void*)(intptr_t)rc);
}
void l1if_settsc_cb(struct trx_l1h *l1h, int rc)
{
osmo_fsm_inst_dispatch(l1h->provision_fi, TRX_PROV_EV_SETTSC_CNF, (void*)(intptr_t)rc);
}
void l1if_setbsic_cb(struct trx_l1h *l1h, int rc)
{
osmo_fsm_inst_dispatch(l1h->provision_fi, TRX_PROV_EV_SETBSIC_CNF, (void*)(intptr_t)rc);
}
static void l1if_getnompower_cb(struct trx_l1h *l1h, int nominal_power, int rc)
{
struct phy_instance *pinst = l1h->phy_inst;
LOGPPHI(pinst, DL1C, LOGL_DEBUG, "l1if_getnompower_cb(nominal_power=%d, rc=%d)\n", nominal_power, rc);
osmo_fsm_inst_dispatch(l1h->provision_fi, TRX_PROV_EV_NOMTXPOWER_CNF, (void*)(intptr_t)nominal_power);
}
void l1if_setformat_cb(struct trx_l1h *l1h, int rc)
{
osmo_fsm_inst_dispatch(l1h->provision_fi, TRX_PROV_EV_SETFORMAT_CNF, (void*)(intptr_t)rc);
}
/*
* transceiver provisioning
*/
static void trx_provision_reset(struct trx_l1h *l1h)
{
struct phy_instance *pinst = l1h->phy_inst;
uint8_t tn;
l1h->config.trxd_pdu_ver_req = pinst->phy_link->u.osmotrx.trxd_pdu_ver_max;
l1h->config.trxd_pdu_ver_use = 0;
l1h->config.setformat_sent = false;
l1h->config.setformat_acked = false;
l1h->config.enabled = false;
l1h->config.arfcn_valid = false;
l1h->config.arfcn = 0;
l1h->config.rxtune_sent = false;
l1h->config.rxtune_acked = false;
l1h->config.txtune_sent = false;
l1h->config.txtune_acked = false;
l1h->config.tsc_valid = false;
l1h->config.tsc = 0;
l1h->config.tsc_sent = false;
l1h->config.tsc_acked = false;
l1h->config.bsic_valid = false;
l1h->config.bsic = 0;
l1h->config.bsic_sent = false;
l1h->config.bsic_acked = false;
l1h->config.rxgain_sent = false;
l1h->config.nomtxpower_sent = false;
l1h->config.nomtxpower_acked = false;
l1h->config.maxdly_sent = false;
l1h->config.maxdlynb_sent = false;
for (tn = 0; tn < TRX_NR_TS; tn++) {
l1h->config.setslot_valid[tn] = false;
l1h->config.setslot_sent[tn] = false;
l1h->config.setslot[tn].slottype = 0;
l1h->config.setslot[tn].tsc_set = 0;
l1h->config.setslot[tn].tsc_val = 0;
l1h->config.setslot[tn].tsc_valid = 0;
}
}
int l1if_provision_transceiver_trx(struct trx_l1h *l1h)
{
struct phy_instance *pinst = l1h->phy_inst;
struct phy_link *plink = pinst->phy_link;
/* During setup, pinst may still not be associated to a TRX nr */
if (!pinst->trx) {
LOGPPHI(pinst, DL1C, LOGL_INFO,
"Delaying provision, TRX not yet assigned to phy instance\n");
return -EIO;
}
if (phy_link_state_get(plink) == PHY_LINK_SHUTDOWN) {
LOGPPHI(pinst, DL1C, LOGL_INFO,
"Delaying provision, TRX not yet available\n");
return -EIO;
}
/* before power on */
if (l1h->config.arfcn_valid) {
if (!l1h->config.rxtune_sent) {
trx_if_cmd_rxtune(l1h, l1h->config.arfcn, l1if_rxtune_cb);
l1h->config.rxtune_sent = true;
l1h->config.rxtune_acked = false;
}
if (!l1h->config.txtune_sent) {
trx_if_cmd_txtune(l1h, l1h->config.arfcn, l1if_txtune_cb);
l1h->config.txtune_sent = true;
l1h->config.txtune_acked = false;
}
if (l1h->config.txtune_acked) {
/* After TXTUNE is sent to TRX, get the tx nominal power
* (which may vary precisly on band/arfcn. Avoid sending
* it if we are forced by VTY to use a specific nominal
* power (because TRX may not support the command or
* provide broken values) */
if (!l1h->config.nominal_power_set_by_vty && !l1h->config.nomtxpower_sent) {
trx_if_cmd_getnompower(l1h, l1if_getnompower_cb);
l1h->config.nomtxpower_sent = true;
l1h->config.nomtxpower_acked = false;
}
}
}
if (!pinst->phy_link->u.osmotrx.use_legacy_setbsic &&
l1h->config.tsc_valid && !l1h->config.tsc_sent) {
trx_if_cmd_settsc(l1h, l1h->config.tsc, l1if_settsc_cb);
l1h->config.tsc_sent = true;
l1h->config.tsc_acked = false;
}
if (pinst->phy_link->u.osmotrx.use_legacy_setbsic &&
l1h->config.bsic_valid && !l1h->config.bsic_sent) {
trx_if_cmd_setbsic(l1h, l1h->config.bsic, l1if_setbsic_cb);
l1h->config.bsic_sent = true;
l1h->config.bsic_acked = false;
}
/* Ask transceiver to use the newest TRXD PDU version if not using it yet */
if (!l1h->config.setformat_sent) {
l1h->config.setformat_sent = true;
if (plink->u.osmotrx.trxd_pdu_ver_max == 0) {
LOGPPHI(pinst, DL1C, LOGL_INFO,
"No need to negotiate max TRXD version 0");
l1h->config.trxd_pdu_ver_use = 0;
l1h->config.setformat_acked = true;
} else {
trx_if_cmd_setformat(l1h, l1h->config.trxd_pdu_ver_req, l1if_setformat_cb);
l1h->config.setformat_acked = false;
}
}
return 0;
}
static void l1if_setslot_cb(struct trx_l1h *l1h, uint8_t tn, uint8_t type, int rc)
{
struct phy_instance *pinst = l1h->phy_inst;
struct gsm_bts_trx *trx = pinst->trx;
struct gsm_bts_trx_ts *ts;
enum gsm_phys_chan_config pchan;
if (tn >= TRX_NR_TS) {
LOGPPHI(pinst, DL1C, LOGL_ERROR, "transceiver SETSLOT invalid param TN (%" PRIu8 ")\n",
tn);
return;
}
pchan = transceiver_chan_type_2_pchan(type);
if (pchan == GSM_PCHAN_UNKNOWN) {
LOGPPHI(pinst, DL1C, LOGL_ERROR, "transceiver SETSLOT invalid param TS_TYPE (%" PRIu8 ")\n",
type);
return;
}
ts = &trx->ts[tn];
LOGPPHI(pinst, DL1C, LOGL_DEBUG, "%s l1if_setslot_cb(as_pchan=%s),"
" calling cb_ts_connected(rc=%d)\n",
gsm_ts_name(ts), gsm_pchan_name(pchan), rc);
cb_ts_connected(ts, rc);
}
static void update_ts_data(struct trx_l1h *l1h, struct trx_prov_ev_cfg_ts_data *data)
{
l1h->config.setslot[data->tn].slottype = data->slottype;
l1h->config.setslot[data->tn].tsc_set = data->tsc_set;
l1h->config.setslot[data->tn].tsc_val = data->tsc_val;
l1h->config.setslot[data->tn].tsc_valid = data->tsc_valid;
l1h->config.setslot_valid[data->tn] = true;
l1h->config.setslot_sent[data->tn] = false;
}
/* Whether a given TRX is fully configured and can be powered on */
static bool trx_is_provisioned(struct trx_l1h *l1h)
{
struct phy_instance *pinst = l1h->phy_inst;
if (l1h->config.enabled && l1h->config.rxtune_acked && l1h->config.txtune_acked &&
(l1h->config.bsic_acked || !pinst->phy_link->u.osmotrx.use_legacy_setbsic) &&
(l1h->config.tsc_acked || pinst->phy_link->u.osmotrx.use_legacy_setbsic) &&
(l1h->config.nomtxpower_acked || l1h->config.nominal_power_set_by_vty) &&
(l1h->config.setformat_acked)) {
return true;
}
return false;
}
static void trx_signal_ready_trx0(struct trx_l1h *l1h)
{
struct phy_instance *pinst = l1h->phy_inst;
struct phy_instance *pinst_it;
llist_for_each_entry(pinst_it, &pinst->phy_link->instances, list) {
struct trx_l1h *l1h_it = pinst_it->u.osmotrx.hdl;
if (l1h_it->phy_inst->num != 0)
continue;
osmo_fsm_inst_dispatch(l1h_it->provision_fi, TRX_PROV_EV_OTHER_TRX_READY, NULL);
return;
}
}
/* Called from TRX0 to check if other TRX are already configured and powered so POWERON can be sent */
static bool trx_other_trx0_ready(struct trx_l1h *l1h)
{
struct phy_instance *pinst = l1h->phy_inst;
struct phy_instance *pinst_it;
/* Don't POWERON until all trx are ready */
llist_for_each_entry(pinst_it, &pinst->phy_link->instances, list) {
struct trx_l1h *l1h_it = pinst_it->u.osmotrx.hdl;
if (l1h_it->phy_inst->num == 0)
continue;
if (l1h_it->provision_fi->state != TRX_PROV_ST_OPEN_POWERON)
return false;
}
return true;
}
/* Closes a phy_link and all its associated TRX */
static void trx_prov_fsm_apply_close(struct phy_link *plink, int rc)
{
struct trx_l1h *l1h;
struct phy_instance *pinst;
if (plink->state == PHY_LINK_SHUTDOWN)
return;
bts_model_phy_link_close(plink);
/* Notify TRX close on all TRX associated with this phy */
llist_for_each_entry(pinst, &plink->instances, list) {
l1h = pinst->u.osmotrx.hdl;
trx_prov_fsm_state_chg(l1h->provision_fi, TRX_PROV_ST_CLOSED);
bts_model_trx_close_cb(pinst->trx, rc);
}
}
static int trx_prov_fsm_signal_cb(unsigned int subsys, unsigned int signal,
void *hdlr_data, void *signal_data)
{
struct nm_statechg_signal_data *nsd;
struct gsm_bts_trx *trx;
if (subsys != SS_GLOBAL)
return -EINVAL;
if (signal != S_NEW_OP_STATE)
return 0;
nsd = (struct nm_statechg_signal_data *)signal_data;
if (nsd->mo->obj_class != NM_OC_RADIO_CARRIER)
return 0;
if (nsd->old_state != NM_OPSTATE_ENABLED && nsd->new_state == NM_OPSTATE_ENABLED) {
trx = gsm_objclass2obj(nsd->mo->bts, nsd->mo->obj_class, &nsd->mo->obj_inst);
l1if_trx_start_power_ramp(trx, NULL);
}
return 0;
}
//////////////////////////
// FSM STATE ACTIONS
//////////////////////////
static void st_closed(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct trx_l1h *l1h = (struct trx_l1h *)fi->priv;
switch (event) {
case TRX_PROV_EV_OPEN:
/* enable all slots */
l1h->config.slotmask = 0xff;
if (l1h->phy_inst->num == 0)
trx_if_cmd_poweroff(l1h, NULL); /* TODO: jump to poweroff upon cb received */
trx_prov_fsm_state_chg(fi, TRX_PROV_ST_OPEN_POWEROFF);
break;
default:
OSMO_ASSERT(0);
}
}
static void st_open_poweroff_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct trx_l1h *l1h = (struct trx_l1h *)fi->priv;
struct phy_instance *pinst = l1h->phy_inst;
trx_provision_reset(l1h);
if (pinst->trx == NULL) {
trx_if_cmd_rfmute(l1h, true);
return;
}
/* Apply initial RFMUTE state */
trx_if_cmd_rfmute(l1h, pinst->trx->mo.nm_state.administrative != NM_STATE_UNLOCKED);
osmo_fsm_inst_dispatch(pinst->trx->mo.fi, NM_EV_SW_ACT, NULL);
osmo_fsm_inst_dispatch(pinst->trx->bb_transc.mo.fi, NM_EV_SW_ACT, NULL);
}
static void st_open_poweroff(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct trx_l1h *l1h = (struct trx_l1h *)fi->priv;
struct phy_instance *pinst = l1h->phy_inst;
struct gsm_bts_trx *trx = pinst->trx;
uint16_t arfcn;
int nominal_power;
int status;
bool others_ready;
switch (event) {
case TRX_PROV_EV_CLOSE:
/* In this state, we didn't for sure send a POWERON yet, hence we
are save directly applying the close as if we received a
POWEROFF RSP: */
if (pinst->num == 0)
trx_prov_fsm_apply_close(pinst->phy_link, 0);
return;
case TRX_PROV_EV_CFG_ENABLE:
l1h->config.enabled =(bool)data;
break;
case TRX_PROV_EV_CFG_BSIC:
/* We always get BSIC from the BSC, TSC can be derived from the BCC */
if (!pinst->phy_link->u.osmotrx.use_legacy_setbsic) {
const uint8_t tsc = BSIC2BCC((uint8_t)(intptr_t)data);
if (l1h->config.tsc != tsc || !l1h->config.tsc_valid) {
l1h->config.tsc = tsc;
l1h->config.tsc_valid = true;
l1h->config.tsc_sent = false;
}
} else {
const uint8_t bsic = (uint8_t)(intptr_t)data;
if (l1h->config.bsic != bsic || !l1h->config.bsic_valid) {
l1h->config.bsic = bsic;
l1h->config.bsic_valid = true;
l1h->config.bsic_sent = false;
}
}
break;
case TRX_PROV_EV_CFG_ARFCN:
arfcn = (uint16_t)(intptr_t)data;
if (l1h->config.arfcn != arfcn || !l1h->config.arfcn_valid) {
l1h->config.arfcn = arfcn;
l1h->config.arfcn_valid = true;
l1h->config.txtune_sent = false;
l1h->config.rxtune_sent = false;
l1h->config.nomtxpower_sent = false;
}
break;
case TRX_PROV_EV_CFG_TS:
update_ts_data(l1h, (struct trx_prov_ev_cfg_ts_data*)data);
break;
/* CONFIRMATIONS FROM TRXC */
case TRX_PROV_EV_RXTUNE_CNF:
if (l1h->config.rxtune_sent)
l1h->config.rxtune_acked = true;
break;
case TRX_PROV_EV_TXTUNE_CNF:
if (l1h->config.txtune_sent)
l1h->config.txtune_acked = true;
break;
case TRX_PROV_EV_NOMTXPOWER_CNF:
nominal_power = (int)(intptr_t)data;
if (l1h->config.nomtxpower_sent)
l1h->config.nomtxpower_acked = true;
l1if_trx_set_nominal_power(trx, nominal_power);
break;
case TRX_PROV_EV_SETBSIC_CNF:
if (l1h->config.bsic_sent)
l1h->config.bsic_acked = true;
break;
case TRX_PROV_EV_SETTSC_CNF:
if (l1h->config.tsc_sent)
l1h->config.tsc_acked = true;
break;
case TRX_PROV_EV_SETFORMAT_CNF:
status = (int)(intptr_t)data;
/* Transceiver may suggest a lower version (than requested) */
if (status == l1h->config.trxd_pdu_ver_req) {
l1h->config.trxd_pdu_ver_use = status;
l1h->config.setformat_acked = true;
LOGPPHI(l1h->phy_inst, DTRX, LOGL_INFO,
"Using TRXD PDU version %u\n",
l1h->config.trxd_pdu_ver_use);
} else {
LOGPPHI(l1h->phy_inst, DTRX, LOGL_DEBUG,
"Transceiver suggests TRXD PDU version %u (requested %u)\n",
status, l1h->config.trxd_pdu_ver_req);
/* Send another SETFORMAT with suggested version */
l1h->config.trxd_pdu_ver_req = status;
l1h->config.setformat_sent = false;
}
break;
case TRX_PROV_EV_OTHER_TRX_READY:
OSMO_ASSERT(pinst->num == 0);
/* Do nothing here, we were triggered to see if we can finally poweron TRX0 below */
break;
default:
OSMO_ASSERT(0);
}
l1if_provision_transceiver_trx(l1h);
if (l1h->phy_inst->num == 0)
others_ready = trx_other_trx0_ready(l1h);
else
others_ready = true; /* we don't care about others in TRX!=0 */
/* if we gathered all data and could go forward. For TRX0, only after
* all other TRX are prepared, since it will send POWERON commad */
if (trx_is_provisioned(l1h) &&
(l1h->phy_inst->num != 0 || others_ready)) {
if (l1h->phy_inst->num != 0) {
trx_prov_fsm_state_chg(fi, TRX_PROV_ST_OPEN_POWERON);
/* Once we are powered on, signal TRX0 in case it was waiting for us */
trx_signal_ready_trx0(l1h);
} else {
trx_prov_fsm_state_chg(fi, TRX_PROV_ST_OPEN_WAIT_POWERON_CNF);
}
} else {
LOGPFSML(fi, LOGL_INFO, "Delay poweron, wait for:%s%s%s%s%s%s%s%s\n",
l1h->config.enabled ? "" :" enable",
pinst->phy_link->u.osmotrx.use_legacy_setbsic ?
(l1h->config.bsic_valid ? (l1h->config.bsic_acked ? "" : " bsic-ack") : " bsic") :
(l1h->config.tsc_valid ? (l1h->config.tsc_acked ? "" : " tsc-ack") : " tsc"),
l1h->config.arfcn_valid ? "" : " arfcn",
l1h->config.rxtune_acked ? "" : " rxtune-ack",
l1h->config.txtune_acked ? "" : " txtune-ack",
l1h->config.nominal_power_set_by_vty ? "" : (l1h->config.nomtxpower_acked ? "" : " nomtxpower-ack"),
l1h->config.setformat_acked ? "" : " setformat-ack",
(l1h->phy_inst->num != 0 || others_ready) ? "" : " other-trx"
);
}
}
static void st_open_wait_power_cnf_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct trx_l1h *l1h = (struct trx_l1h *)fi->priv;
struct phy_instance *pinst = l1h->phy_inst;
trx_if_cmd_poweron(l1h, l1if_poweronoff_cb);
pinst->phy_link->u.osmotrx.poweron_sent = true;
}
static void st_open_wait_power_cnf(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct trx_l1h *l1h = (struct trx_l1h *)fi->priv;
struct phy_instance *pinst = l1h->phy_inst;
struct phy_link *plink = pinst->phy_link;
int rc;
switch (event) {
case TRX_PROV_EV_POWERON_CNF:
rc = (uint16_t)(intptr_t)data;
if (rc == 0 && plink->state != PHY_LINK_CONNECTED) {
trx_sched_clock_started(pinst->trx->bts);
phy_link_state_set(plink, PHY_LINK_CONNECTED);
trx_prov_fsm_state_chg(fi, TRX_PROV_ST_OPEN_POWERON);
} else if (rc != 0 && plink->state != PHY_LINK_SHUTDOWN) {
trx_sched_clock_stopped(pinst->trx->bts);
phy_link_state_set(plink, PHY_LINK_SHUTDOWN);
}
break;
case TRX_PROV_EV_CFG_TS:
update_ts_data(l1h, (struct trx_prov_ev_cfg_ts_data*)data);
break;
case TRX_PROV_EV_CLOSE:
/* power off transceiver, if not already */
if (pinst->num == 0 && !plink->u.osmotrx.poweroff_sent) {
trx_if_cmd_poweroff(l1h, l1if_poweronoff_cb);
plink->u.osmotrx.poweroff_sent = true;
}
trx_prov_fsm_state_chg(fi, TRX_PROV_ST_OPEN_WAIT_POWEROFF_CNF);
break;
default:
OSMO_ASSERT(0);
}
}
static void st_open_poweron_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
{
struct trx_l1h *l1h = (struct trx_l1h *)fi->priv;
uint8_t tn;
/* after power on */
if (l1h->config.rxgain_valid && !l1h->config.rxgain_sent) {
trx_if_cmd_setrxgain(l1h, l1h->config.rxgain);
l1h->config.rxgain_sent = true;
}
if (l1h->config.maxdly_valid && !l1h->config.maxdly_sent) {
trx_if_cmd_setmaxdly(l1h, l1h->config.maxdly);
l1h->config.maxdly_sent = true;
}
if (l1h->config.maxdlynb_valid && !l1h->config.maxdlynb_sent) {
trx_if_cmd_setmaxdlynb(l1h, l1h->config.maxdlynb);
l1h->config.maxdlynb_sent = true;
}
for (tn = 0; tn < TRX_NR_TS; tn++) {
if (l1h->config.setslot_valid[tn]
&& !l1h->config.setslot_sent[tn]) {
trx_if_cmd_setslot(l1h, tn, l1if_setslot_cb);
l1h->config.setslot_sent[tn] = true;
}
}
}
static void st_open_poweron(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct trx_l1h *l1h = (struct trx_l1h *)fi->priv;
struct phy_instance *pinst = l1h->phy_inst;
struct phy_link *plink = pinst->phy_link;
struct trx_prov_ev_cfg_ts_data* ts_data;
switch (event) {
case TRX_PROV_EV_CLOSE:
/* power off transceiver, if not already */
if (pinst->num == 0 && plink->u.osmotrx.powered && !plink->u.osmotrx.poweroff_sent) {
trx_if_cmd_poweroff(l1h, l1if_poweronoff_cb);
plink->u.osmotrx.poweroff_sent = true;
}
trx_prov_fsm_state_chg(fi, TRX_PROV_ST_OPEN_WAIT_POWEROFF_CNF);
break;
case TRX_PROV_EV_CFG_TS:
ts_data = (struct trx_prov_ev_cfg_ts_data*)data;
update_ts_data(l1h, ts_data);
/* While in this state we can send SETSLOT immediately */
trx_if_cmd_setslot(l1h, ts_data->tn, l1if_setslot_cb);
l1h->config.setslot_sent[ts_data->tn] = true;
break;
default:
OSMO_ASSERT(0);
}
}
static void st_open_wait_poweroff_cnf(struct osmo_fsm_inst *fi, uint32_t event, void *data)
{
struct trx_l1h *l1h = (struct trx_l1h *)fi->priv;
struct phy_instance *pinst = l1h->phy_inst;
struct phy_link *plink = pinst->phy_link;
int rc;
switch (event) {
case TRX_PROV_EV_POWEROFF_CNF:
rc = (uint16_t)(intptr_t)data;
trx_prov_fsm_apply_close(plink, rc);
break;
default:
OSMO_ASSERT(0);
}
}
static struct osmo_fsm_state trx_prov_fsm_states[] = {
[TRX_PROV_ST_CLOSED] = {
.in_event_mask =
X(TRX_PROV_EV_OPEN),
.out_state_mask =
X(TRX_PROV_ST_OPEN_POWEROFF),
.name = "CLOSED",
.action = st_closed,
},
[TRX_PROV_ST_OPEN_POWEROFF] = {
.in_event_mask =
X(TRX_PROV_EV_CLOSE) |
X(TRX_PROV_EV_OTHER_TRX_READY) |
X(TRX_PROV_EV_CFG_ENABLE) |
X(TRX_PROV_EV_CFG_BSIC) |
X(TRX_PROV_EV_CFG_ARFCN) |
X(TRX_PROV_EV_CFG_TS) |
X(TRX_PROV_EV_RXTUNE_CNF) |
X(TRX_PROV_EV_TXTUNE_CNF) |
X(TRX_PROV_EV_NOMTXPOWER_CNF) |
X(TRX_PROV_EV_SETBSIC_CNF) |
X(TRX_PROV_EV_SETTSC_CNF) |
X(TRX_PROV_EV_SETFORMAT_CNF),
.out_state_mask =
X(TRX_PROV_ST_CLOSED) |
X(TRX_PROV_ST_OPEN_WAIT_POWERON_CNF) |
X(TRX_PROV_ST_OPEN_POWERON),
.name = "OPEN_POWEROFF",
.onenter = st_open_poweroff_on_enter,
.action = st_open_poweroff,
},
[TRX_PROV_ST_OPEN_WAIT_POWERON_CNF] = {
.in_event_mask =
X(TRX_PROV_EV_CLOSE) |
X(TRX_PROV_EV_POWERON_CNF) |
X(TRX_PROV_EV_CFG_TS),
.out_state_mask =
X(TRX_PROV_ST_OPEN_POWERON) |
X(TRX_PROV_ST_OPEN_WAIT_POWEROFF_CNF),
.name = "OPEN_WAIT_POWERON_CNF",
.onenter = st_open_wait_power_cnf_on_enter,
.action = st_open_wait_power_cnf,
},
[TRX_PROV_ST_OPEN_POWERON] = {
.in_event_mask =
X(TRX_PROV_EV_CLOSE) |
X(TRX_PROV_EV_CFG_TS),
.out_state_mask =
X(TRX_PROV_ST_OPEN_WAIT_POWEROFF_CNF),
.name = "OPEN_POWERON",
.onenter = st_open_poweron_on_enter,
.action = st_open_poweron,
},
[TRX_PROV_ST_OPEN_WAIT_POWEROFF_CNF] = {
.in_event_mask =
X(TRX_PROV_EV_POWEROFF_CNF),
.out_state_mask =
X(TRX_PROV_ST_CLOSED),
.name = "OPEN_WAIT_POWEROFF_CNF",
.action = st_open_wait_poweroff_cnf,
},
};
const struct value_string trx_prov_fsm_event_names[] = {
OSMO_VALUE_STRING(TRX_PROV_EV_OTHER_TRX_READY),
OSMO_VALUE_STRING(TRX_PROV_EV_OPEN),
OSMO_VALUE_STRING(TRX_PROV_EV_CFG_ENABLE),
OSMO_VALUE_STRING(TRX_PROV_EV_CFG_BSIC),
OSMO_VALUE_STRING(TRX_PROV_EV_CFG_ARFCN),
OSMO_VALUE_STRING(TRX_PROV_EV_CFG_TS),
OSMO_VALUE_STRING(TRX_PROV_EV_CFG_RXGAIN),
OSMO_VALUE_STRING(TRX_PROV_EV_CFG_SETMAXDLY),
OSMO_VALUE_STRING(TRX_PROV_EV_RXTUNE_CNF),
OSMO_VALUE_STRING(TRX_PROV_EV_TXTUNE_CNF),
OSMO_VALUE_STRING(TRX_PROV_EV_NOMTXPOWER_CNF),
OSMO_VALUE_STRING(TRX_PROV_EV_SETBSIC_CNF),
OSMO_VALUE_STRING(TRX_PROV_EV_SETTSC_CNF),
OSMO_VALUE_STRING(TRX_PROV_EV_SETFORMAT_CNF),
OSMO_VALUE_STRING(TRX_PROV_EV_POWERON_CNF),
OSMO_VALUE_STRING(TRX_PROV_EV_POWEROFF_CNF),
OSMO_VALUE_STRING(TRX_PROV_EV_CLOSE),
{ 0, NULL }
};
struct osmo_fsm trx_prov_fsm = {
.name = "TRX_PROV",
.states = trx_prov_fsm_states,
.num_states = ARRAY_SIZE(trx_prov_fsm_states),
.event_names = trx_prov_fsm_event_names,
.log_subsys = DL1C,
};
static __attribute__((constructor)) void trx_prov_fsm_init(void)
{
OSMO_ASSERT(osmo_fsm_register(&trx_prov_fsm) == 0);
OSMO_ASSERT(osmo_signal_register_handler(SS_GLOBAL, trx_prov_fsm_signal_cb, NULL) == 0);
}