Change in osmo-bts[master]: bts-trx: introduce TRX provisioning FSM

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.

pespin gerrit-no-reply at lists.osmocom.org
Tue Jul 14 15:33:19 UTC 2020


pespin has submitted this change. ( https://gerrit.osmocom.org/c/osmo-bts/+/19167 )

Change subject: bts-trx: introduce TRX provisioning FSM
......................................................................

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
---
M src/osmo-bts-trx/Makefile.am
M src/osmo-bts-trx/l1_if.c
M src/osmo-bts-trx/l1_if.h
M src/osmo-bts-trx/trx_if.c
A src/osmo-bts-trx/trx_provision_fsm.c
A src/osmo-bts-trx/trx_provision_fsm.h
6 files changed, 537 insertions(+), 224 deletions(-)

Approvals:
  Jenkins Builder: Verified
  Hoernchen: Looks good to me, but someone else must approve
  pespin: Looks good to me, approved
  laforge: Looks good to me, but someone else must approve



diff --git a/src/osmo-bts-trx/Makefile.am b/src/osmo-bts-trx/Makefile.am
index ae69000..54d1af9 100644
--- a/src/osmo-bts-trx/Makefile.am
+++ b/src/osmo-bts-trx/Makefile.am
@@ -32,6 +32,7 @@
 	trx_if.h \
 	l1_if.h \
 	loops.h \
+	trx_provision_fsm.h \
 	$(NULL)
 
 bin_PROGRAMS = osmo-bts-trx
@@ -47,6 +48,7 @@
 	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 a3bdf01..d65900f 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 @@
 	[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 *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 @@
 	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 @@
 	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 @@
 }
 
 /*
- * 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 @@
 	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 @@
 	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 @@
 		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 @@
 	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 @@
 	/* 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 @@
 
 	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 d2fdf06..07751f2 100644
--- a/src/osmo-bts-trx/l1_if.h
+++ b/src/osmo-bts-trx/l1_if.h
@@ -114,6 +114,7 @@
 
 	/* transceiver config */
 	struct trx_config	config;
+	struct osmo_fsm_inst	*provision_fi;
 
 	struct l1sched_trx	l1s;
 };
@@ -122,6 +123,8 @@
 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 7508606..e650d79 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 @@
 	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 @@
 
 	/* 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 0000000..c3b266d
--- /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 at sysmocom.de>
+ * Author: Pau Espin Pedrol <pespin at 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 0000000..7fede35
--- /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 at sysmocom.de>
+ * Author: Pau Espin Pedrol <pespin at 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;

-- 
To view, visit https://gerrit.osmocom.org/c/osmo-bts/+/19167
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings

Gerrit-Project: osmo-bts
Gerrit-Branch: master
Gerrit-Change-Id: I2a00c23df15840e33fbb232c9e1dd6db128f63f6
Gerrit-Change-Number: 19167
Gerrit-PatchSet: 4
Gerrit-Owner: pespin <pespin at sysmocom.de>
Gerrit-Reviewer: Hoernchen <ewild at sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: daniel <dwillmann at sysmocom.de>
Gerrit-Reviewer: laforge <laforge at osmocom.org>
Gerrit-Reviewer: pespin <pespin at sysmocom.de>
Gerrit-MessageType: merged
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20200714/0870d7fc/attachment.htm>


More information about the gerrit-log mailing list