diff options
author | Pau Espin Pedrol <pespin@sysmocom.de> | 2020-07-06 19:56:08 +0200 |
---|---|---|
committer | Pau Espin Pedrol <pespin@sysmocom.de> | 2020-07-14 11:44:30 +0200 |
commit | 1272af452932e00243d6fea5e073228e59a46ed7 (patch) | |
tree | 9c587315613ee084421d2e077e75e56545bebfd1 | |
parent | 414d649e01cbe45e81750c7a6fa91cf0de3c18a3 (diff) |
bts-trx: introduce TRX provisioning FSM
With prior code state managing the TRXC side of osmo-bts-trx, there are
plenty o cases (race conditions) where things can go wrong/unexpected,
because there's really no infrastructure to wait and synchronize between
different TRXs (eg wait until all are configured to POWERON), or to
simply keep well known per-trx state regarding lower layers.
In order to fix in the future all of those issues and to sanitize
current code, a new per-trx FSM is introduced, which takes care of
submitting TRXC commands and waiting for response when needed to manage
the state of the TRX.
Related: OS#4364
Change-Id: I2a00c23df15840e33fbb232c9e1dd6db128f63f6
-rw-r--r-- | src/osmo-bts-trx/Makefile.am | 2 | ||||
-rw-r--r-- | src/osmo-bts-trx/l1_if.c | 232 | ||||
-rw-r--r-- | src/osmo-bts-trx/l1_if.h | 3 | ||||
-rw-r--r-- | src/osmo-bts-trx/trx_if.c | 10 | ||||
-rw-r--r-- | src/osmo-bts-trx/trx_provision_fsm.c | 459 | ||||
-rw-r--r-- | src/osmo-bts-trx/trx_provision_fsm.h | 55 |
6 files changed, 537 insertions, 224 deletions
diff --git a/src/osmo-bts-trx/Makefile.am b/src/osmo-bts-trx/Makefile.am index ae690007..54d1af9e 100644 --- a/src/osmo-bts-trx/Makefile.am +++ b/src/osmo-bts-trx/Makefile.am @@ -32,6 +32,7 @@ noinst_HEADERS = \ trx_if.h \ l1_if.h \ loops.h \ + trx_provision_fsm.h \ $(NULL) bin_PROGRAMS = osmo-bts-trx @@ -47,6 +48,7 @@ osmo_bts_trx_SOURCES = \ sched_lchan_pdtch.c \ sched_lchan_tchf.c \ sched_lchan_tchh.c \ + trx_provision_fsm.c \ trx_vty.c \ loops.c \ $(NULL) diff --git a/src/osmo-bts-trx/l1_if.c b/src/osmo-bts-trx/l1_if.c index a3bdf012..d65900fe 100644 --- a/src/osmo-bts-trx/l1_if.c +++ b/src/osmo-bts-trx/l1_if.c @@ -29,6 +29,7 @@ #include <osmocom/core/talloc.h> #include <osmocom/core/bits.h> +#include <osmocom/core/fsm.h> #include <osmocom/codec/ecu.h> #include <osmocom/gsm/abis_nm.h> @@ -45,6 +46,7 @@ #include "l1_if.h" #include "trx_if.h" +#include "trx_provision_fsm.h" #define RF_DISABLED_mdB to_mdB(-10) @@ -62,7 +64,7 @@ static const uint8_t transceiver_chan_types[_GSM_PCHAN_MAX] = { [GSM_PCHAN_UNKNOWN] = 0, }; -static enum gsm_phys_chan_config transceiver_chan_type_2_pchan(uint8_t type) +enum gsm_phys_chan_config transceiver_chan_type_2_pchan(uint8_t type) { int i; for (i = 0; i < _GSM_PCHAN_MAX; i++) { @@ -77,6 +79,8 @@ struct trx_l1h *trx_l1h_alloc(void *tall_ctx, struct phy_instance *pinst) struct trx_l1h *l1h; l1h = talloc_zero(tall_ctx, struct trx_l1h); l1h->phy_inst = pinst; + l1h->provision_fi = osmo_fsm_inst_alloc(&trx_prov_fsm, l1h, l1h, LOGL_INFO, NULL); + OSMO_ASSERT(osmo_fsm_inst_update_id_f_sanitize(l1h->provision_fi, '-', phy_instance_name(pinst)) == 0); trx_if_init(l1h); return l1h; } @@ -140,7 +144,7 @@ int bts_model_lchan_deactivate_sacch(struct gsm_lchan *lchan) return trx_sched_set_lchan(&l1h->l1s, gsm_lchan2chan_nr(lchan), LID_SACCH, false); } -static int l1if_trx_start_power_ramp(struct gsm_bts_trx *trx, ramp_compl_cb_t ramp_compl_cb) +int l1if_trx_start_power_ramp(struct gsm_bts_trx *trx, ramp_compl_cb_t ramp_compl_cb) { struct phy_instance *pinst = trx_phy_instance(trx); struct trx_l1h *l1h = pinst->u.osmotrx.hdl; @@ -174,78 +178,6 @@ void l1if_trx_set_nominal_power(struct gsm_bts_trx *trx, int nominal_power) l1if_trx_start_power_ramp(trx, NULL); } -static void l1if_getnompower_cb(struct trx_l1h *l1h, int nominal_power, int rc) -{ - struct phy_instance *pinst = l1h->phy_inst; - struct gsm_bts_trx *trx = pinst->trx; - - LOGPPHI(pinst, DL1C, LOGL_DEBUG, "l1if_getnompower_cb(nominal_power=%d, rc=%d)\n", nominal_power, rc); - - l1if_trx_set_nominal_power(trx, nominal_power); -} - -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 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; - plink->u.osmotrx.poweronoff_sent = false; - - if (poweronoff) { - if (rc == 0 && plink->state != PHY_LINK_CONNECTED) { - trx_sched_clock_started(pinst->trx->bts); - phy_link_state_set(plink, PHY_LINK_CONNECTED); - - /* Begin to ramp up the power on all TRX associated with this phy */ - llist_for_each_entry(pinst, &plink->instances, list) { - if (pinst->trx->mo.nm_state.administrative == NM_STATE_UNLOCKED) - l1if_trx_start_power_ramp(pinst->trx, NULL); - } - } else if (rc != 0 && plink->state != PHY_LINK_SHUTDOWN) { - trx_sched_clock_stopped(pinst->trx->bts); - phy_link_state_set(plink, PHY_LINK_SHUTDOWN); - } - } else { - if (plink->state != PHY_LINK_SHUTDOWN) { - trx_sched_clock_stopped(pinst->trx->bts); - phy_link_state_set(plink, PHY_LINK_SHUTDOWN); - - /* Notify TRX close on all TRX associated with this phy */ - llist_for_each_entry(pinst, &plink->instances, list) { - bts_model_trx_close_cb(pinst->trx, rc); - } - } - } -} - static void l1if_setpower_att_cb(struct trx_l1h *l1h, int power_att_db, int rc) { struct phy_instance *pinst = l1h->phy_inst; @@ -257,113 +189,6 @@ static void l1if_setpower_att_cb(struct trx_l1h *l1h, int power_att_db, int rc) } /* - * transceiver provisioning - */ -int l1if_provision_transceiver_trx(struct trx_l1h *l1h) -{ - uint8_t tn; - 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; - } - - if (l1h->config.enabled - && l1h->config.tsc_valid - && l1h->config.bsic_valid - && l1h->config.arfcn_valid) { - /* before power on */ - if (!l1h->config.arfcn_sent) { - trx_if_cmd_rxtune(l1h, l1h->config.arfcn); - trx_if_cmd_txtune(l1h, l1h->config.arfcn); - /* 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) - trx_if_cmd_getnompower(l1h, l1if_getnompower_cb); - l1h->config.arfcn_sent = 1; - } - if (!l1h->config.tsc_sent) { - trx_if_cmd_settsc(l1h, l1h->config.tsc); - l1h->config.tsc_sent = 1; - } - if (!l1h->config.bsic_sent) { - trx_if_cmd_setbsic(l1h, l1h->config.bsic); - l1h->config.bsic_sent = 1; - } - - /* Ask transceiver to use the newest TRXD header version if not using it yet */ - if (!l1h->config.setformat_sent) { - if (l1h->config.trxd_hdr_ver_use != plink->u.osmotrx.trxd_hdr_ver_max) { - trx_if_cmd_setformat(l1h, plink->u.osmotrx.trxd_hdr_ver_max); - l1h->config.trxd_hdr_ver_req = plink->u.osmotrx.trxd_hdr_ver_max; - } else { - LOGPPHI(pinst, DL1C, LOGL_INFO, - "No need to negotiate TRXD version, " - "already using maximum configured one: %" PRIu8 "\n", - l1h->config.trxd_hdr_ver_use); - } - l1h->config.setformat_sent = 1; - } - - if (pinst->num == 0 && !plink->u.osmotrx.powered && !plink->u.osmotrx.poweronoff_sent) { - trx_if_cmd_poweron(l1h, l1if_poweronoff_cb); - plink->u.osmotrx.poweronoff_sent = true; - } - - /* after power on */ - if (l1h->config.rxgain_valid && !l1h->config.rxgain_sent) { - trx_if_cmd_setrxgain(l1h, l1h->config.rxgain); - l1h->config.rxgain_sent = 1; - } - if (l1h->config.maxdly_valid && !l1h->config.maxdly_sent) { - trx_if_cmd_setmaxdly(l1h, l1h->config.maxdly); - l1h->config.maxdly_sent = 1; - } - if (l1h->config.maxdlynb_valid && !l1h->config.maxdlynb_sent) { - trx_if_cmd_setmaxdlynb(l1h, l1h->config.maxdlynb); - l1h->config.maxdlynb_sent = 1; - } - - for (tn = 0; tn < TRX_NR_TS; tn++) { - if (l1h->config.slottype_valid[tn] - && !l1h->config.slottype_sent[tn]) { - trx_if_cmd_setslot(l1h, tn, - l1h->config.slottype[tn], l1if_setslot_cb); - l1h->config.slottype_sent[tn] = 1; - } - } - return 0; - } - - if (!l1h->config.enabled) { - if (pinst->num == 0 && plink->u.osmotrx.powered && !plink->u.osmotrx.poweronoff_sent) { - trx_if_cmd_poweroff(l1h, l1if_poweronoff_cb); - plink->u.osmotrx.poweronoff_sent = true; - } - l1h->config.rxgain_sent = 0; - l1h->config.maxdly_sent = 0; - l1h->config.maxdlynb_sent = 0; - for (tn = 0; tn < TRX_NR_TS; tn++) - l1h->config.slottype_sent[tn] = 0; - } - - return 0; -} - -/* * activation/configuration/deactivation of transceiver's TRX */ @@ -373,11 +198,7 @@ static int trx_init(struct gsm_bts_trx *trx) struct phy_instance *pinst = trx_phy_instance(trx); struct trx_l1h *l1h = pinst->u.osmotrx.hdl; - /* power on transceiver, if not already */ - if (!l1h->config.enabled) { - l1h->config.enabled = true; - l1if_provision_transceiver_trx(l1h); - } + osmo_fsm_inst_dispatch(l1h->provision_fi, TRX_PROV_EV_CFG_ENABLE, (void*)(intptr_t)true); if (trx == trx->bts->c0) lchan_init_lapdm(&trx->ts[0].lchan[CCCH_LCHAN]); @@ -411,13 +232,7 @@ void bts_model_trx_close(struct gsm_bts_trx *trx) struct phy_instance *pinst = trx_phy_instance(trx); struct trx_l1h *l1h = pinst->u.osmotrx.hdl; - /* power off transceiver, if not already */ - if (l1h->config.enabled) { - l1h->config.enabled = false; - l1if_provision_transceiver_trx(l1h); - } else if (!pinst->phy_link->u.osmotrx.poweronoff_sent) { - bts_model_trx_close_cb(trx, 0); - } /* else: poweroff in progress, cb will be called upon TRXC RSP */ + osmo_fsm_inst_dispatch(l1h->provision_fi, TRX_PROV_EV_POWEROFF, NULL); /* Set to Operational State: Disabled */ check_transceiver_availability_trx(l1h, 0); @@ -446,12 +261,7 @@ static uint8_t trx_set_bts(struct gsm_bts *bts, struct tlv_parsed *new_attr) struct phy_instance *pinst = trx_phy_instance(trx); struct phy_link *plink = pinst->phy_link; struct trx_l1h *l1h = pinst->u.osmotrx.hdl; - if (l1h->config.bsic != bsic || !l1h->config.bsic_valid) { - l1h->config.bsic = bsic; - l1h->config.bsic_valid = 1; - l1h->config.bsic_sent = 0; - l1if_provision_transceiver_trx(l1h); - } + osmo_fsm_inst_dispatch(l1h->provision_fi, TRX_PROV_EV_CFG_BSIC, (void*)(intptr_t)bsic); check_transceiver_availability_trx(l1h, phy_link_state_get(plink) != PHY_LINK_SHUTDOWN); } @@ -466,12 +276,7 @@ static uint8_t trx_set_trx(struct gsm_bts_trx *trx) struct phy_link *plink = pinst->phy_link; uint16_t arfcn = trx->arfcn; - if (l1h->config.arfcn != arfcn || !l1h->config.arfcn_valid) { - l1h->config.arfcn = arfcn; - l1h->config.arfcn_valid = 1; - l1h->config.arfcn_sent = 0; - l1if_provision_transceiver_trx(l1h); - } + osmo_fsm_inst_dispatch(l1h->provision_fi, TRX_PROV_EV_CFG_ARFCN, (void*)(intptr_t)arfcn); /* Begin to ramp up the power if power reduction is set by OML and TRX is already running. Otherwise skip, power ramping will be started @@ -497,12 +302,7 @@ static uint8_t trx_set_ts_as_pchan(struct gsm_bts_trx_ts *ts, /* all TSC of all timeslots must be equal, because transceiver only * supports one TSC per TRX */ - if (l1h->config.tsc != tsc || !l1h->config.tsc_valid) { - l1h->config.tsc = tsc; - l1h->config.tsc_valid = 1; - l1h->config.tsc_sent = 0; - l1if_provision_transceiver_trx(l1h); - } + osmo_fsm_inst_dispatch(l1h->provision_fi, TRX_PROV_EV_CFG_TSC, (void*)(intptr_t)tsc); /* ignore disabled slots */ if (!(l1h->config.slotmask & (1 << tn))) @@ -525,13 +325,9 @@ static uint8_t trx_set_ts_as_pchan(struct gsm_bts_trx_ts *ts, slottype = transceiver_chan_types[pchan]; - if (l1h->config.slottype[tn] != slottype - || !l1h->config.slottype_valid[tn]) { - l1h->config.slottype[tn] = slottype; - l1h->config.slottype_valid[tn] = 1; - l1h->config.slottype_sent[tn] = 0; - l1if_provision_transceiver_trx(l1h); - } + + struct trx_prov_ev_cfg_ts_data data = { .tn = tn, .slottype = slottype }; + osmo_fsm_inst_dispatch(l1h->provision_fi, TRX_PROV_EV_CFG_TS, &data); return 0; } diff --git a/src/osmo-bts-trx/l1_if.h b/src/osmo-bts-trx/l1_if.h index d2fdf06f..07751f21 100644 --- a/src/osmo-bts-trx/l1_if.h +++ b/src/osmo-bts-trx/l1_if.h @@ -114,6 +114,7 @@ struct trx_l1h { /* transceiver config */ struct trx_config config; + struct osmo_fsm_inst *provision_fi; struct l1sched_trx l1s; }; @@ -122,6 +123,8 @@ struct trx_l1h *trx_l1h_alloc(void *tall_ctx, struct phy_instance *pinst); int l1if_provision_transceiver_trx(struct trx_l1h *l1h); int l1if_mph_time_ind(struct gsm_bts *bts, uint32_t fn); void l1if_trx_set_nominal_power(struct gsm_bts_trx *trx, int nominal_power); +int l1if_trx_start_power_ramp(struct gsm_bts_trx *trx, ramp_compl_cb_t ramp_compl_cb); +enum gsm_phys_chan_config transceiver_chan_type_2_pchan(uint8_t type); static inline struct l1sched_trx *trx_l1sched_hdl(struct gsm_bts_trx *trx) { diff --git a/src/osmo-bts-trx/trx_if.c b/src/osmo-bts-trx/trx_if.c index 75086068..e650d794 100644 --- a/src/osmo-bts-trx/trx_if.c +++ b/src/osmo-bts-trx/trx_if.c @@ -39,6 +39,7 @@ #include <osmocom/core/timer.h> #include <osmocom/core/talloc.h> #include <osmocom/core/bits.h> +#include <osmocom/core/fsm.h> #include <osmo-bts/phy_link.h> #include <osmo-bts/logging.h> @@ -47,6 +48,7 @@ #include "l1_if.h" #include "trx_if.h" +#include "trx_provision_fsm.h" /* * socket helper functions @@ -1231,12 +1233,6 @@ static int trx_if_open(struct trx_l1h *l1h) if (rc < 0) goto err; - /* enable all slots */ - l1h->config.slotmask = 0xff; - - if (pinst->num == 0) - trx_if_cmd_poweroff(l1h, NULL); - return 0; err: @@ -1301,8 +1297,10 @@ int bts_model_phy_link_open(struct phy_link *plink) /* open the individual instances with their ctrl+data sockets */ llist_for_each_entry(pinst, &plink->instances, list) { + struct trx_l1h *l1h = pinst->u.osmotrx.hdl; if (trx_phy_inst_open(pinst) < 0) goto cleanup; + osmo_fsm_inst_dispatch(l1h->provision_fi, TRX_PROV_EV_OPEN, NULL); } return 0; diff --git a/src/osmo-bts-trx/trx_provision_fsm.c b/src/osmo-bts-trx/trx_provision_fsm.c new file mode 100644 index 00000000..c3b266d8 --- /dev/null +++ b/src/osmo-bts-trx/trx_provision_fsm.c @@ -0,0 +1,459 @@ +/* BTS shutdown 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 "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; + plink->u.osmotrx.poweronoff_sent = false; + + if (poweronoff) + osmo_fsm_inst_dispatch(l1h->provision_fi, TRX_PROV_EV_POWERON_CNF, (void*)(intptr_t)rc); + else + osmo_fsm_inst_dispatch(l1h->provision_fi, TRX_PROV_EV_POWEROFF_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; + struct gsm_bts_trx *trx = pinst->trx; + + LOGPPHI(pinst, DL1C, LOGL_DEBUG, "l1if_getnompower_cb(nominal_power=%d, rc=%d)\n", nominal_power, rc); + + l1if_trx_set_nominal_power(trx, nominal_power); +} + +/* + * transceiver provisioning + */ +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; + } + + if (l1h->config.enabled + && l1h->config.tsc_valid + && l1h->config.bsic_valid + && l1h->config.arfcn_valid) { + /* before power on */ + if (!l1h->config.arfcn_sent) { + trx_if_cmd_rxtune(l1h, l1h->config.arfcn); + trx_if_cmd_txtune(l1h, l1h->config.arfcn); + /* 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) + trx_if_cmd_getnompower(l1h, l1if_getnompower_cb); + l1h->config.arfcn_sent = 1; + } + if (!l1h->config.tsc_sent) { + trx_if_cmd_settsc(l1h, l1h->config.tsc); + l1h->config.tsc_sent = 1; + } + if (!l1h->config.bsic_sent) { + trx_if_cmd_setbsic(l1h, l1h->config.bsic); + l1h->config.bsic_sent = 1; + } + + /* Ask transceiver to use the newest TRXD header version if not using it yet */ + if (!l1h->config.setformat_sent) { + if (l1h->config.trxd_hdr_ver_use != plink->u.osmotrx.trxd_hdr_ver_max) { + trx_if_cmd_setformat(l1h, plink->u.osmotrx.trxd_hdr_ver_max); + l1h->config.trxd_hdr_ver_req = plink->u.osmotrx.trxd_hdr_ver_max; + } else { + LOGPPHI(pinst, DL1C, LOGL_INFO, + "No need to negotiate TRXD version, " + "already using maximum configured one: %" PRIu8 "\n", + l1h->config.trxd_hdr_ver_use); + } + l1h->config.setformat_sent = 1; + } + + if (pinst->num == 0 && !plink->u.osmotrx.powered && !plink->u.osmotrx.poweronoff_sent) { + trx_if_cmd_poweron(l1h, l1if_poweronoff_cb); + plink->u.osmotrx.poweronoff_sent = true; + } + + return 0; + } + LOGPPHI(pinst, DL1C, LOGL_INFO, "Delaying provision, TRX attributes not yet received from BSC:%s%s%s%s\n", + l1h->config.enabled ? "" :" enable", + l1h->config.tsc_valid ? "" : " tsc", + l1h->config.bsic_valid ? "" : " bsic", + l1h->config.arfcn_valid ? "" : " arfcn"); + return 1; +} + +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); +} + +/* Returns true if any TS changed, false otherwise */ +static bool update_ts_data(struct trx_l1h *l1h, struct trx_prov_ev_cfg_ts_data* ts_data) { + + if (l1h->config.slottype[ts_data->tn] != ts_data->slottype || + !l1h->config.slottype_valid[ts_data->tn]) { + l1h->config.slottype[ts_data->tn] = ts_data->slottype; + l1h->config.slottype_valid[ts_data->tn] = 1; + l1h->config.slottype_sent[ts_data->tn] = 0; + return true; + } + return false; +} + + +////////////////////////// +// 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; + } +} + +static void st_open_poweroff(struct osmo_fsm_inst *fi, uint32_t event, void *data) +{ + struct trx_l1h *l1h = (struct trx_l1h *)fi->priv; + uint8_t bsic; + uint16_t arfcn; + uint16_t tsc; + + switch(event) { + case TRX_PROV_EV_CFG_ENABLE: + l1h->config.enabled =(bool)data; + break; + case TRX_PROV_EV_CFG_BSIC: + bsic = (uint8_t)(intptr_t)data; + if (l1h->config.bsic != bsic || !l1h->config.bsic_valid) { + l1h->config.bsic = bsic; + l1h->config.bsic_valid = 1; + l1h->config.bsic_sent = 0; + } + 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 = 1; + l1h->config.arfcn_sent = 0; + } + break; + case TRX_PROV_EV_CFG_TSC: + tsc = (uint16_t)(intptr_t)data; + if (l1h->config.tsc != tsc || !l1h->config.tsc_valid) { + l1h->config.tsc = tsc; + l1h->config.tsc_valid = 1; + l1h->config.tsc_sent = 0; + } + break; + case TRX_PROV_EV_CFG_TS: + update_ts_data(l1h, (struct trx_prov_ev_cfg_ts_data*)data); + break; + } + + /* 0 = if we gathered all date and could go forward :*/ + if (l1if_provision_transceiver_trx(l1h) == 0) { + if (l1h->phy_inst->num == 0) + trx_prov_fsm_state_chg(fi, TRX_PROV_ST_OPEN_WAIT_POWERON_CNF); + else + trx_prov_fsm_state_chg(fi, TRX_PROV_ST_OPEN_POWERON); + } +} + +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); + + /* Begin to ramp up the power on all TRX associated with this phy */ + llist_for_each_entry(pinst, &plink->instances, list) { + if (pinst->trx->mo.nm_state.administrative == NM_STATE_UNLOCKED) + l1if_trx_start_power_ramp(pinst->trx, NULL); + } + + 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; + } +} + +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 = 1; + } + if (l1h->config.maxdly_valid && !l1h->config.maxdly_sent) { + trx_if_cmd_setmaxdly(l1h, l1h->config.maxdly); + l1h->config.maxdly_sent = 1; + } + if (l1h->config.maxdlynb_valid && !l1h->config.maxdlynb_sent) { + trx_if_cmd_setmaxdlynb(l1h, l1h->config.maxdlynb); + l1h->config.maxdlynb_sent = 1; + } + + for (tn = 0; tn < TRX_NR_TS; tn++) { + if (l1h->config.slottype_valid[tn] + && !l1h->config.slottype_sent[tn]) { + trx_if_cmd_setslot(l1h, tn, + l1h->config.slottype[tn], l1if_setslot_cb); + l1h->config.slottype_sent[tn] = 1; + } + } +} + +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; + uint8_t tn; + + switch(event) { + case TRX_PROV_EV_CLOSE: + /* power off transceiver, if not already */ + if (l1h->config.enabled) { + if (pinst->num == 0 && plink->u.osmotrx.powered && !plink->u.osmotrx.poweronoff_sent) { + trx_if_cmd_poweroff(l1h, l1if_poweronoff_cb); + plink->u.osmotrx.poweronoff_sent = true; + } + l1h->config.rxgain_sent = 0; + l1h->config.maxdly_sent = 0; + l1h->config.maxdlynb_sent = 0; + for (tn = 0; tn < TRX_NR_TS; tn++) + l1h->config.slottype_sent[tn] = 0; + } else if (!pinst->phy_link->u.osmotrx.poweronoff_sent) { + bts_model_trx_close_cb(pinst->trx, 0); + } /* else: poweroff in progress, cb will be called upon TRXC RSP */ + + if (pinst->num == 0) + trx_prov_fsm_state_chg(fi, TRX_PROV_ST_OPEN_WAIT_POWEROFF_CNF); + else + trx_prov_fsm_state_chg(fi, TRX_PROV_ST_OPEN_POWEROFF); + break; + case TRX_PROV_EV_CFG_TS: + ts_data = (struct trx_prov_ev_cfg_ts_data*)data; + if (update_ts_data(l1h, ts_data)) { + trx_if_cmd_setslot(l1h, ts_data->tn, + l1h->config.slottype[ ts_data->tn], l1if_setslot_cb); + l1h->config.slottype_sent[ ts_data->tn] = 1; + } + + break; + } +} + +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; + if (plink->state != PHY_LINK_SHUTDOWN) { + trx_sched_clock_stopped(pinst->trx->bts); + phy_link_state_set(plink, PHY_LINK_SHUTDOWN); + + /* Notify TRX close on all TRX associated with this phy */ + llist_for_each_entry(pinst, &plink->instances, list) { + bts_model_trx_close_cb(pinst->trx, rc); + } + } + break; + } +} + +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_CFG_ENABLE) | + X(TRX_PROV_EV_CFG_BSIC) | + X(TRX_PROV_EV_CFG_ARFCN) | + X(TRX_PROV_EV_CFG_TSC) | + X(TRX_PROV_EV_CFG_TS), + .out_state_mask = + X(TRX_PROV_ST_OPEN_WAIT_POWERON_CNF) | + X(TRX_PROV_ST_OPEN_POWERON), + .name = "OPEN_POWEROFF", + .action = st_open_poweroff, + }, + [TRX_PROV_ST_OPEN_WAIT_POWERON_CNF] = { + .in_event_mask = + X(TRX_PROV_EV_POWERON_CNF) | + X(TRX_PROV_EV_CFG_TS), + .out_state_mask = + X(TRX_PROV_ST_OPEN_POWERON), + .name = "OPEN_WAIT_POWERON_CNF", + .action = st_open_wait_power_cnf, + }, + [TRX_PROV_ST_OPEN_POWERON] = { + .in_event_mask = + X(TRX_PROV_EV_POWEROFF) | + X(TRX_PROV_EV_CFG_TS), + .out_state_mask = + X(TRX_PROV_ST_OPEN_WAIT_POWEROFF_CNF) | + X(TRX_PROV_ST_OPEN_POWEROFF), + .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_OPEN_POWEROFF), + .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_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_TSC), + 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_POWERON_CNF), + OSMO_VALUE_STRING(TRX_PROV_EV_POWEROFF), + 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); +} diff --git a/src/osmo-bts-trx/trx_provision_fsm.h b/src/osmo-bts-trx/trx_provision_fsm.h new file mode 100644 index 00000000..7fede35c --- /dev/null +++ b/src/osmo-bts-trx/trx_provision_fsm.h @@ -0,0 +1,55 @@ +/* Provision TRX over TRXC protocol 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/>. + * + */ + +#pragma once + +#include <osmocom/core/fsm.h> + +enum trx_provision_fsm_states { + TRX_PROV_ST_CLOSED, + TRX_PROV_ST_OPEN_POWEROFF, + TRX_PROV_ST_OPEN_WAIT_POWERON_CNF, + TRX_PROV_ST_OPEN_POWERON, + TRX_PROV_ST_OPEN_WAIT_POWEROFF_CNF, +}; + +struct trx_prov_ev_cfg_ts_data { + uint8_t tn; + uint8_t slottype; +}; + +enum trx_provision_fsm_events { + TRX_PROV_EV_OPEN, + TRX_PROV_EV_CFG_ENABLE, + TRX_PROV_EV_CFG_BSIC, + TRX_PROV_EV_CFG_ARFCN, + TRX_PROV_EV_CFG_TSC, + TRX_PROV_EV_CFG_TS, + TRX_PROV_EV_CFG_RXGAIN, + TRX_PROV_EV_CFG_SETMAXDLY, + TRX_PROV_EV_POWERON_CNF, + TRX_PROV_EV_POWEROFF, + TRX_PROV_EV_POWEROFF_CNF, + TRX_PROV_EV_CLOSE, +}; + +extern struct osmo_fsm trx_prov_fsm; |