aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPau Espin Pedrol <pespin@sysmocom.de>2020-07-06 19:56:08 +0200
committerPau Espin Pedrol <pespin@sysmocom.de>2020-07-14 11:44:30 +0200
commit1272af452932e00243d6fea5e073228e59a46ed7 (patch)
tree9c587315613ee084421d2e077e75e56545bebfd1
parent414d649e01cbe45e81750c7a6fa91cf0de3c18a3 (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.am2
-rw-r--r--src/osmo-bts-trx/l1_if.c232
-rw-r--r--src/osmo-bts-trx/l1_if.h3
-rw-r--r--src/osmo-bts-trx/trx_if.c10
-rw-r--r--src/osmo-bts-trx/trx_provision_fsm.c459
-rw-r--r--src/osmo-bts-trx/trx_provision_fsm.h55
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;