Change in osmo-bts[master]: WIP: Introduce NM Channel 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
Mon Sep 28 13:00:42 UTC 2020


pespin has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-bts/+/20311 )


Change subject: WIP: Introduce NM Channel FSM
......................................................................

WIP: Introduce NM Channel FSM

Change-Id: I288cbfb4730b25a334ef1c3d6b9679d6f1d4cfc5
---
M include/osmo-bts/Makefile.am
M include/osmo-bts/gsm_data.h
A include/osmo-bts/nm_channel_fsm.h
M src/common/Makefile.am
M src/common/bts.c
M src/common/bts_trx.c
M src/common/nm_bb_transc_fsm.c
A src/common/nm_channel_fsm.c
M src/common/oml.c
M src/common/pcu_sock.c
M src/common/vty.c
M src/osmo-bts-litecell15/oml.c
M src/osmo-bts-oc2g/oml.c
M src/osmo-bts-octphy/l1_if.c
M src/osmo-bts-octphy/l1_oml.c
M src/osmo-bts-omldummy/bts_model.c
M src/osmo-bts-sysmo/l1_if.c
M src/osmo-bts-sysmo/oml.c
M src/osmo-bts-trx/l1_if.c
M src/osmo-bts-virtual/bts_model.c
20 files changed, 450 insertions(+), 147 deletions(-)



  git pull ssh://gerrit.osmocom.org:29418/osmo-bts refs/changes/11/20311/1

diff --git a/include/osmo-bts/Makefile.am b/include/osmo-bts/Makefile.am
index 4593f7a..45961e0 100644
--- a/include/osmo-bts/Makefile.am
+++ b/include/osmo-bts/Makefile.am
@@ -28,5 +28,6 @@
 	dtx_dl_amr_fsm.h \
 	ta_control.h \
 	nm_bb_transc_fsm.h \
+	nm_channel_fsm.h \
 	nm_radio_carrier_fsm.h \
 	$(NULL)
diff --git a/include/osmo-bts/gsm_data.h b/include/osmo-bts/gsm_data.h
index 3d01d55..98fb481 100644
--- a/include/osmo-bts/gsm_data.h
+++ b/include/osmo-bts/gsm_data.h
@@ -306,6 +306,13 @@
 			TS_F_PDCH_ACT_PENDING | TS_F_PDCH_DEACT_PENDING */
 };
 
+struct nm_chan {
+	/* NM Channel FSM */
+	struct osmo_fsm_inst *fi;
+	bool opstart_success; /* OPSTART went OK in lower layers and was acked */
+	struct gsm_abis_mo mo;
+};
+
 /* One Timeslot in a TRX */
 struct gsm_bts_trx_ts {
 	struct gsm_bts_trx *trx;
@@ -321,7 +328,7 @@
 	} dyn;
 
 	unsigned int flags;
-	struct gsm_abis_mo mo;
+	struct nm_chan nm_chan;
 	int tsc;		/* -1 == use BTS TSC */
 
 	/* Frequency hopping parameters (configured via OML) */
diff --git a/include/osmo-bts/nm_channel_fsm.h b/include/osmo-bts/nm_channel_fsm.h
new file mode 100644
index 0000000..4f76db6
--- /dev/null
+++ b/include/osmo-bts/nm_channel_fsm.h
@@ -0,0 +1,44 @@
+/* NM Channel FSM. Following 3GPP TS 12.21 Figure 2/GSM 12.21:
+  GSM 12.21 Objects' Operational state and availability status behaviour during initialization */
+
+/* (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 nm_chan_op_fsm_states {
+	NM_CHAN_ST_OP_DISABLED_NOTINSTALLED,
+	NM_CHAN_ST_OP_DISABLED_DEPENDENCY,
+	NM_CHAN_ST_OP_DISABLED_OFFLINE,
+	NM_CHAN_ST_OP_ENABLED,
+};
+
+enum nm_chan_op_fsm_events {
+	NM_CHAN_EV_BBTRANSC_INSTALLED,
+	NM_CHAN_EV_BBTRANSC_ENABLED,
+	NM_CHAN_EV_BBTRANSC_DISABLED,
+	NM_CHAN_EV_OPSTART_ACK,
+	NM_CHAN_EV_OPSTART_NACK,
+	NM_CHAN_EV_DISABLE,
+};
+
+extern struct osmo_fsm nm_chan_fsm;
diff --git a/src/common/Makefile.am b/src/common/Makefile.am
index fe8de46..f5ec188 100644
--- a/src/common/Makefile.am
+++ b/src/common/Makefile.am
@@ -38,6 +38,7 @@
 	scheduler_mframe.c \
 	ta_control.c \
 	nm_bb_transc_fsm.c \
+	nm_channel_fsm.c \
 	nm_radio_carrier_fsm.c \
 	$(NULL)
 
diff --git a/src/common/bts.c b/src/common/bts.c
index 4d8445c..0ba3ce0 100644
--- a/src/common/bts.c
+++ b/src/common/bts.c
@@ -410,7 +410,7 @@
 		for (j = 0; j < ARRAY_SIZE(trx->ts); j++) {
 			struct gsm_bts_trx_ts *ts = &trx->ts[j];
 
-			oml_tx_state_changed(&ts->mo);
+			oml_tx_state_changed(&ts->nm_chan.mo);
 		}
 	}
 
diff --git a/src/common/bts_trx.c b/src/common/bts_trx.c
index 6ffae9e..9551b73 100644
--- a/src/common/bts_trx.c
+++ b/src/common/bts_trx.c
@@ -30,6 +30,7 @@
 #include <osmo-bts/phy_link.h>
 #include <osmo-bts/nm_radio_carrier_fsm.h>
 #include <osmo-bts/nm_bb_transc_fsm.h>
+#include <osmo-bts/nm_channel_fsm.h>
 
 struct gsm_bts_trx *gsm_bts_trx_alloc(struct gsm_bts *bts)
 {
@@ -65,7 +66,11 @@
 		ts->dyn.pchan_want = GSM_PCHAN_NONE;
 		ts->tsc = -1;
 
-		gsm_mo_init(&ts->mo, bts, NM_OC_CHANNEL,
+		ts->nm_chan.fi = osmo_fsm_inst_alloc(&nm_chan_fsm, trx, ts,
+						     LOGL_INFO, NULL);
+		osmo_fsm_inst_update_id_f(ts->nm_chan.fi, "bts%d-trx%d-ts%d",
+					  bts->nr, trx->nr, ts->nr);
+		gsm_mo_init(&ts->nm_chan.mo, bts, NM_OC_CHANNEL,
 			    bts->nr, trx->nr, ts->nr);
 
 		for (l = 0; l < TS_MAX_LCHAN; l++) {
diff --git a/src/common/nm_bb_transc_fsm.c b/src/common/nm_bb_transc_fsm.c
index 6dfb2b3..57d5557 100644
--- a/src/common/nm_bb_transc_fsm.c
+++ b/src/common/nm_bb_transc_fsm.c
@@ -34,6 +34,7 @@
 #include <osmo-bts/bts.h>
 #include <osmo-bts/rsl.h>
 #include <osmo-bts/nm_bb_transc_fsm.h>
+#include <osmo-bts/nm_channel_fsm.h>
 #include <osmo-bts/phy_link.h>
 
 #define X(s) (1 << (s))
@@ -55,11 +56,16 @@
 static void st_op_disabled_notinstalled(struct osmo_fsm_inst *fi, uint32_t event, void *data)
 {
 	struct gsm_bts_trx *trx = (struct gsm_bts_trx *)fi->priv;
+	int i;
 
 	switch (event) {
 	case NM_BBTRANSC_EV_SW_ACT:
 		oml_mo_tx_sw_act_rep(&trx->bb_transc.mo);
 		nm_bb_transc_fsm_state_chg(fi, NM_BBTRANSC_ST_OP_DISABLED_OFFLINE);
+		for (i = 0; i < TRX_NR_TS; i++) {
+			struct gsm_bts_trx_ts *ts = &trx->ts[i];
+			osmo_fsm_inst_dispatch(ts->nm_chan.fi, NM_CHAN_EV_BBTRANSC_INSTALLED, NULL);
+		}
 		return;
 	case NM_BBTRANSC_EV_RSL_UP:
 		return;
@@ -79,8 +85,17 @@
 static void st_op_disabled_offline_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
 {
 	struct gsm_bts_trx *trx = (struct gsm_bts_trx *)fi->priv;
+	int i;
+
 	trx->bb_transc.opstart_success = false;
 	oml_mo_state_chg(&trx->bb_transc.mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OFF_LINE);
+
+	if (prev_state == NM_BBTRANSC_ST_OP_ENABLED) {
+		for (i = 0; i < TRX_NR_TS; i++) {
+			struct gsm_bts_trx_ts *ts = &trx->ts[i];
+			osmo_fsm_inst_dispatch(ts->nm_chan.fi, NM_CHAN_EV_BBTRANSC_DISABLED, NULL);
+		}
+	}
 }
 
 static void st_op_disabled_offline(struct osmo_fsm_inst *fi, uint32_t event, void *data)
@@ -127,7 +142,13 @@
 static void st_op_enabled_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
 {
 	struct gsm_bts_trx *trx = (struct gsm_bts_trx *)fi->priv;
+	int i;
+
 	oml_mo_state_chg(&trx->bb_transc.mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK);
+	for (i = 0; i < TRX_NR_TS; i++) {
+		struct gsm_bts_trx_ts *ts = &trx->ts[i];
+		osmo_fsm_inst_dispatch(ts->nm_chan.fi, NM_CHAN_EV_BBTRANSC_ENABLED, NULL);
+	}
 }
 
 static void st_op_enabled(struct osmo_fsm_inst *fi, uint32_t event, void *data)
diff --git a/src/common/nm_channel_fsm.c b/src/common/nm_channel_fsm.c
new file mode 100644
index 0000000..f2cdfb4
--- /dev/null
+++ b/src/common/nm_channel_fsm.c
@@ -0,0 +1,220 @@
+/* NM Radio Carrier 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 <osmo-bts/nm_channel_fsm.h>
+
+#define X(s) (1 << (s))
+
+#define nm_chan_fsm_state_chg(fi, NEXT_STATE) \
+	osmo_fsm_inst_state_chg(fi, NEXT_STATE, 0, 0)
+
+//////////////////////////
+// FSM STATE ACTIONS
+//////////////////////////
+
+static void st_op_disabled_notinstalled_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+	struct gsm_bts_trx_ts *ts = (struct gsm_bts_trx_ts *)fi->priv;
+	ts->nm_chan.opstart_success = false;
+	oml_mo_state_chg(&ts->nm_chan.mo, NM_OPSTATE_DISABLED, NM_AVSTATE_NOT_INSTALLED);
+}
+
+static void st_op_disabled_notinstalled(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+	struct gsm_bts_trx_ts *ts = (struct gsm_bts_trx_ts *)fi->priv;
+
+	switch (event) {
+	case NM_CHAN_EV_BBTRANSC_INSTALLED:
+		oml_mo_tx_sw_act_rep(&ts->nm_chan.mo);
+		if (ts->trx->bb_transc.mo.nm_state.operational == NM_OPSTATE_ENABLED)
+			nm_chan_fsm_state_chg(fi, NM_CHAN_ST_OP_DISABLED_OFFLINE);
+		else
+			nm_chan_fsm_state_chg(fi, NM_CHAN_ST_OP_DISABLED_DEPENDENCY);
+		return;
+	default:
+		OSMO_ASSERT(0);
+	}
+}
+
+static void st_op_disabled_dependency_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+	struct gsm_bts_trx_ts *ts = (struct gsm_bts_trx_ts *)fi->priv;
+	ts->nm_chan.opstart_success = false;
+	oml_mo_state_chg(&ts->nm_chan.mo, NM_OPSTATE_DISABLED, NM_AVSTATE_DEPENDENCY);
+}
+
+static void st_op_disabled_dependency(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+	struct gsm_bts_trx_ts *ts = (struct gsm_bts_trx_ts *)fi->priv;
+
+	switch (event) {
+	case NM_CHAN_EV_OPSTART_ACK:
+		 LOGPFSML(fi, LOGL_NOTICE, "BSC trying to activate TS while still in avail=dependency. "
+		 	  "Allowing it to stay backward-compatible with older osmo-bts versions, but BSC is wrong.\n");
+		ts->nm_chan.opstart_success = true;
+		oml_mo_opstart_ack(&ts->nm_chan.mo);
+		nm_chan_fsm_state_chg(fi, NM_CHAN_ST_OP_ENABLED);
+		return;
+	case NM_CHAN_EV_OPSTART_NACK:
+		ts->nm_chan.opstart_success = false;
+		oml_mo_opstart_nack(&ts->nm_chan.mo, (int)(intptr_t)data);
+		return;
+	case NM_CHAN_EV_BBTRANSC_ENABLED:
+		oml_mo_state_chg(&ts->nm_chan.mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OFF_LINE);
+		return;
+	default:
+		OSMO_ASSERT(0);
+	}
+}
+
+static void st_op_disabled_offline_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+	struct gsm_bts_trx_ts *ts = (struct gsm_bts_trx_ts *)fi->priv;
+	ts->nm_chan.opstart_success = false;
+	oml_mo_state_chg(&ts->nm_chan.mo, NM_OPSTATE_DISABLED, NM_AVSTATE_OFF_LINE);
+}
+
+static void st_op_disabled_offline(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+	struct gsm_bts_trx_ts *ts = (struct gsm_bts_trx_ts *)fi->priv;
+
+	switch (event) {
+	case NM_CHAN_EV_OPSTART_ACK:
+		ts->nm_chan.opstart_success = true;
+		oml_mo_opstart_ack(&ts->nm_chan.mo);
+		nm_chan_fsm_state_chg(fi, NM_CHAN_ST_OP_ENABLED);
+		return;
+	case NM_CHAN_EV_OPSTART_NACK:
+		ts->nm_chan.opstart_success = false;
+		oml_mo_opstart_nack(&ts->nm_chan.mo, (int)(intptr_t)data);
+		return;
+	case NM_CHAN_EV_BBTRANSC_DISABLED:
+		oml_mo_state_chg(&ts->nm_chan.mo, NM_OPSTATE_DISABLED, NM_AVSTATE_DEPENDENCY);
+		return;
+	default:
+		OSMO_ASSERT(0);
+	}
+}
+
+static void st_op_enabled_on_enter(struct osmo_fsm_inst *fi, uint32_t prev_state)
+{
+	struct gsm_bts_trx_ts *ts = (struct gsm_bts_trx_ts *)fi->priv;
+	oml_mo_state_chg(&ts->nm_chan.mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK);
+}
+
+static void st_op_enabled(struct osmo_fsm_inst *fi, uint32_t event, void *data)
+{
+	switch (event) {
+	case NM_CHAN_EV_BBTRANSC_DISABLED:
+		nm_chan_fsm_state_chg(fi, NM_CHAN_ST_OP_DISABLED_DEPENDENCY);
+		return;
+	case NM_CHAN_EV_DISABLE:
+		nm_chan_fsm_state_chg(fi, NM_CHAN_ST_OP_DISABLED_OFFLINE);
+		return;
+	default:
+		OSMO_ASSERT(0);
+	}
+}
+
+static struct osmo_fsm_state nm_chan_fsm_states[] = {
+	[NM_CHAN_ST_OP_DISABLED_NOTINSTALLED] = {
+		.in_event_mask =
+			X(NM_CHAN_EV_BBTRANSC_INSTALLED),
+		.out_state_mask =
+			X(NM_CHAN_ST_OP_DISABLED_OFFLINE) |
+			X(NM_CHAN_ST_OP_DISABLED_DEPENDENCY),
+		.name = "DISABLED_NOTINSTALLED",
+		.onenter = st_op_disabled_notinstalled_on_enter,
+		.action = st_op_disabled_notinstalled,
+	},
+	[NM_CHAN_ST_OP_DISABLED_DEPENDENCY] = {
+		.in_event_mask =
+			X(NM_CHAN_EV_OPSTART_ACK) |  /* backward compatibility, buggy BSC */
+			X(NM_CHAN_EV_OPSTART_NACK) |
+			X(NM_CHAN_EV_BBTRANSC_ENABLED),
+		.out_state_mask =
+			X(NM_CHAN_ST_OP_DISABLED_OFFLINE) |
+			X(NM_CHAN_ST_OP_ENABLED), /* backward compatibility, buggy BSC */
+		.name = "DISABLED_DEPENDENCY",
+		.onenter = st_op_disabled_dependency_on_enter,
+		.action = st_op_disabled_dependency,
+	},
+	[NM_CHAN_ST_OP_DISABLED_OFFLINE] = {
+		.in_event_mask =
+			X(NM_CHAN_EV_OPSTART_ACK) |
+			X(NM_CHAN_EV_OPSTART_NACK) |
+			X(NM_CHAN_EV_BBTRANSC_DISABLED),
+		.out_state_mask =
+			X(NM_CHAN_ST_OP_ENABLED) |
+			X(NM_CHAN_ST_OP_DISABLED_DEPENDENCY),
+		.name = "DISABLED_OFFLINE",
+		.onenter = st_op_disabled_offline_on_enter,
+		.action = st_op_disabled_offline,
+	},
+	[NM_CHAN_ST_OP_ENABLED] = {
+		.in_event_mask =
+			X(NM_CHAN_EV_BBTRANSC_DISABLED) |
+			X(NM_CHAN_EV_DISABLE),
+		.out_state_mask =
+			X(NM_CHAN_ST_OP_DISABLED_OFFLINE) |
+			X(NM_CHAN_ST_OP_DISABLED_DEPENDENCY),
+		.name = "ENABLED",
+		.onenter = st_op_enabled_on_enter,
+		.action = st_op_enabled,
+	},
+};
+
+const struct value_string nm_chan_fsm_event_names[] = {
+	{ NM_CHAN_EV_BBTRANSC_INSTALLED, "BBTRANSC_INSTALLED" },
+	{ NM_CHAN_EV_BBTRANSC_ENABLED, "BBTRANSC_ENABLED" },
+	{ NM_CHAN_EV_BBTRANSC_DISABLED, "BBTRANSC_DISABLED" },
+	{ NM_CHAN_EV_OPSTART_ACK, "OPSTART_ACK" },
+	{ NM_CHAN_EV_OPSTART_NACK, "OPSTART_NACK" },
+	{ NM_CHAN_EV_DISABLE, "DISABLE" },
+	{ 0, NULL }
+};
+
+struct osmo_fsm nm_chan_fsm = {
+	.name = "NM_CHAN_OP",
+	.states = nm_chan_fsm_states,
+	.num_states = ARRAY_SIZE(nm_chan_fsm_states),
+	.event_names = nm_chan_fsm_event_names,
+	.log_subsys = DOML,
+};
+
+static __attribute__((constructor)) void nm_chan_fsm_init(void)
+{
+	OSMO_ASSERT(osmo_fsm_register(&nm_chan_fsm) == 0);
+}
diff --git a/src/common/oml.c b/src/common/oml.c
index 19559b8..370cf2d 100644
--- a/src/common/oml.c
+++ b/src/common/oml.c
@@ -880,7 +880,7 @@
 
 	rc = oml_tlv_parse(&tp, foh->data, msgb_l3len(msg) - sizeof(*foh));
 	if (rc < 0) {
-		oml_tx_failure_event_rep(&ts->mo, NM_SEVER_MAJOR, OSMO_EVT_MAJ_UNSUP_ATTR,
+		oml_tx_failure_event_rep(&ts->nm_chan.mo, NM_SEVER_MAJOR, OSMO_EVT_MAJ_UNSUP_ATTR,
 					 "New value for Set Channel Attribute not supported");
 		return oml_fom_ack_nack(msg, NM_NACK_INCORR_STRUCT);
 	}
@@ -927,11 +927,11 @@
 	}
 
 	/* merge existing BTS attributes with new attributes */
-	tp_merged = osmo_tlvp_copy(ts->mo.nm_attr, bts);
+	tp_merged = osmo_tlvp_copy(ts->nm_chan.mo.nm_attr, bts);
 	osmo_tlvp_merge(tp_merged, &tp);
 
 	/* Call into BTS driver to check attribute values */
-	rc = bts_model_check_oml(bts, foh->msg_type, ts->mo.nm_attr, tp_merged, ts);
+	rc = bts_model_check_oml(bts, foh->msg_type, ts->nm_chan.mo.nm_attr, tp_merged, ts);
 	if (rc < 0) {
 		LOGPFOH(DOML, LOGL_ERROR, foh, "SET CHAN ATTR: invalid attribute value, rc=%d\n", rc);
 		talloc_free(tp_merged);
@@ -940,8 +940,8 @@
 	}
 
 	/* Success: replace old BTS attributes with new */
-	talloc_free(ts->mo.nm_attr);
-	ts->mo.nm_attr = tp_merged;
+	talloc_free(ts->nm_chan.mo.nm_attr);
+	ts->nm_chan.mo.nm_attr = tp_merged;
 
 	/* 9.4.13 Channel Combination */
 	if (TLVP_PRES_LEN(&tp, NM_ATT_CHAN_COMB, 1)) {
@@ -1551,7 +1551,7 @@
 		trx = gsm_bts_trx_num(bts, obj_inst->trx_nr);
 		if (obj_inst->ts_nr >= TRX_NR_TS)
 			return NULL;
-		mo = &trx->ts[obj_inst->ts_nr].mo;
+		mo = &trx->ts[obj_inst->ts_nr].nm_chan.mo;
 		break;
 	case NM_OC_SITE_MANAGER:
 		mo = &bts->site_mgr.mo;
diff --git a/src/common/pcu_sock.c b/src/common/pcu_sock.c
index 45047ba..0ecc526 100644
--- a/src/common/pcu_sock.c
+++ b/src/common/pcu_sock.c
@@ -211,7 +211,7 @@
 	for (tn = 0; tn < 8; tn++) {
 		const struct gsm_bts_trx_ts *ts = &trx->ts[tn];
 
-		if (ts->mo.nm_state.operational != NM_OPSTATE_ENABLED)
+		if (ts->nm_chan.mo.nm_state.operational != NM_OPSTATE_ENABLED)
 			continue;
 		if (!ts_should_be_pdch(ts))
 			continue;
@@ -907,7 +907,7 @@
 			break;
 		for (j = 0; j < 8; j++) {
 			ts = &trx->ts[j];
-			if (ts->mo.nm_state.operational == NM_OPSTATE_ENABLED
+			if (ts->nm_chan.mo.nm_state.operational == NM_OPSTATE_ENABLED
 			 && ts->pchan == GSM_PCHAN_PDCH) {
 				ts->lchan[0].rel_act_kind = LCHAN_REL_ACT_PCU;
 				l1sap_chan_rel(trx,
diff --git a/src/common/vty.c b/src/common/vty.c
index b442f83..b7132a9 100644
--- a/src/common/vty.c
+++ b/src/common/vty.c
@@ -1045,7 +1045,7 @@
 			ts->flags & TS_F_PDCH_ACTIVE ? "PDCH" : "TCH/F");
 	vty_out(vty, "%s", VTY_NEWLINE);
 	vty_out(vty, "  NM State: ");
-	net_dump_nmstate(vty, &ts->mo.nm_state);
+	net_dump_nmstate(vty, &ts->nm_chan.mo.nm_state);
 }
 
 DEFUN(show_ts,
diff --git a/src/osmo-bts-litecell15/oml.c b/src/osmo-bts-litecell15/oml.c
index a01df68..e10f818 100644
--- a/src/osmo-bts-litecell15/oml.c
+++ b/src/osmo-bts-litecell15/oml.c
@@ -45,6 +45,7 @@
 #include <osmo-bts/l1sap.h>
 #include <osmo-bts/nm_radio_carrier_fsm.h>
 #include <osmo-bts/nm_bb_transc_fsm.h>
+#include <osmo-bts/nm_channel_fsm.h>
 
 #include "l1_if.h"
 #include "lc15bts.h"
@@ -278,38 +279,41 @@
 			get_value_string(lc15bts_l1prim_names, l1p->id),
 			get_value_string(lc15bts_l1status_names, status));
 		msgb_free(l1_msg);
-		if (mo->obj_class == NM_OC_RADIO_CARRIER)
+		switch (mo->obj_class) {
+		case NM_OC_RADIO_CARRIER:
 			return osmo_fsm_inst_dispatch(trx->rc.fi, NM_RCARRIER_EV_OPSTART_NACK,
 						      (void*)(intptr_t)NM_NACK_CANT_PERFORM);
-		else
-			return oml_mo_opstart_nack(mo, NM_NACK_CANT_PERFORM);
-	}
-
-	msgb_free(l1_msg);
-
-	/* We already have a FSM for Radio Carrier, handle it there */
-	if (mo->obj_class == NM_OC_RADIO_CARRIER)
-		return osmo_fsm_inst_dispatch(trx->rc.fi, NM_RCARRIER_EV_OPSTART_ACK, NULL);
-
-	/* Set to Operational State: Enabled */
-	oml_mo_state_chg(mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK);
-
-	/* ugly hack to auto-activate all SAPIs for the BCCH/CCCH on TS0 */
-	if (mo->obj_class == NM_OC_CHANNEL && mo->obj_inst.trx_nr == 0 &&
-	    mo->obj_inst.ts_nr == 0) {
-		struct gsm_lchan *cbch = gsm_bts_get_cbch(mo->bts);
-		DEBUGP(DL1C, "====> trying to activate lchans of BCCH\n");
-		mo->bts->c0->ts[0].lchan[CCCH_LCHAN].rel_act_kind =
-			LCHAN_REL_ACT_OML;
-		lchan_activate(&mo->bts->c0->ts[0].lchan[CCCH_LCHAN]);
-		if (cbch) {
-			cbch->rel_act_kind = LCHAN_REL_ACT_OML;
-			lchan_activate(cbch);
+		case NM_OC_CHANNEL:
+			return osmo_fsm_inst_dispatch(trx->ts[mo->obj_inst.ts_nr].nm_chan.fi, NM_CHAN_EV_OPSTART_NACK,
+						      (void*)(intptr_t)NM_NACK_CANT_PERFORM);
+		default:
+			OSMO_ASSERT(0);
 		}
 	}
 
-	/* Send OPSTART ack */
-	return oml_mo_opstart_ack(mo);
+	msgb_free(l1_msg);
+	switch (mo->obj_class) {
+	case NM_OC_RADIO_CARRIER:
+		return osmo_fsm_inst_dispatch(trx->rc.fi, NM_RCARRIER_EV_OPSTART_ACK, NULL);
+	case NM_OC_CHANNEL:
+		/* ugly hack to auto-activate all SAPIs for the BCCH/CCCH on TS0 */
+		if (mo->obj_inst.trx_nr == 0 &&
+		    mo->obj_inst.ts_nr == 0) {
+			struct gsm_lchan *cbch = gsm_bts_get_cbch(mo->bts);
+			DEBUGP(DL1C, "====> trying to activate lchans of BCCH\n");
+			mo->bts->c0->ts[0].lchan[CCCH_LCHAN].rel_act_kind =
+				LCHAN_REL_ACT_OML;
+			lchan_activate(&mo->bts->c0->ts[0].lchan[CCCH_LCHAN]);
+			if (cbch) {
+				cbch->rel_act_kind = LCHAN_REL_ACT_OML;
+				lchan_activate(cbch);
+			}
+		}
+		return osmo_fsm_inst_dispatch(trx->ts[mo->obj_inst.ts_nr].nm_chan.fi,
+					      NM_CHAN_EV_OPSTART_ACK, NULL);
+	default:
+		OSMO_ASSERT(0);
+	}
 }
 
 static int opstart_compl_cb(struct gsm_bts_trx *trx, struct msgb *l1_msg,
@@ -319,7 +323,7 @@
 	GsmL1_Prim_t *l1p = msgb_l1prim(l1_msg);
 	GsmL1_MphConnectCnf_t *cnf = &l1p->u.mphConnectCnf;
 
-	mo = &trx->ts[cnf->u8Tn].mo;
+	mo = &trx->ts[cnf->u8Tn].nm_chan.mo;
 	return opstart_compl(mo, l1_msg);
 }
 
@@ -1886,6 +1890,7 @@
 		      void *obj)
 {
 	struct gsm_bts_trx *trx;
+	struct gsm_bts_trx_ts *ts;
 	int rc;
 
 	switch (mo->obj_class) {
@@ -1898,7 +1903,8 @@
 		rc = osmo_fsm_inst_dispatch(trx->bb_transc.fi, NM_BBTRANSC_EV_OPSTART_ACK, NULL);
 		break;
 	case NM_OC_CHANNEL:
-		rc = ts_opstart(obj);
+		ts = (struct gsm_bts_trx_ts*) obj;
+		rc = ts_opstart(ts);
 		break;
 	case NM_OC_BTS:
 	case NM_OC_SITE_MANAGER:
diff --git a/src/osmo-bts-oc2g/oml.c b/src/osmo-bts-oc2g/oml.c
index 2f2730e..0464225 100644
--- a/src/osmo-bts-oc2g/oml.c
+++ b/src/osmo-bts-oc2g/oml.c
@@ -278,38 +278,41 @@
 			get_value_string(oc2gbts_l1prim_names, l1p->id),
 			get_value_string(oc2gbts_l1status_names, status));
 		msgb_free(l1_msg);
-		if (mo->obj_class == NM_OC_RADIO_CARRIER)
+		switch (mo->obj_class) {
+		case NM_OC_RADIO_CARRIER:
 			return osmo_fsm_inst_dispatch(trx->rc.fi, NM_RCARRIER_EV_OPSTART_NACK,
 						      (void*)(intptr_t)NM_NACK_CANT_PERFORM);
-		else
-			return oml_mo_opstart_nack(mo, NM_NACK_CANT_PERFORM);
-	}
-
-	msgb_free(l1_msg);
-
-	/* We already have a FSM for Radio Carrier, handle it there */
-	if (mo->obj_class == NM_OC_RADIO_CARRIER)
-		return osmo_fsm_inst_dispatch(trx->rc.fi, NM_RCARRIER_EV_OPSTART_ACK, NULL);
-
-	/* Set to Operational State: Enabled */
-	oml_mo_state_chg(mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK);
-
-	/* ugly hack to auto-activate all SAPIs for the BCCH/CCCH on TS0 */
-	if (mo->obj_class == NM_OC_CHANNEL && mo->obj_inst.trx_nr == 0 &&
-	    mo->obj_inst.ts_nr == 0) {
-		struct gsm_lchan *cbch = gsm_bts_get_cbch(mo->bts);
-		DEBUGP(DL1C, "====> trying to activate lchans of BCCH\n");
-		mo->bts->c0->ts[0].lchan[CCCH_LCHAN].rel_act_kind =
-			LCHAN_REL_ACT_OML;
-		lchan_activate(&mo->bts->c0->ts[0].lchan[CCCH_LCHAN]);
-		if (cbch) {
-			cbch->rel_act_kind = LCHAN_REL_ACT_OML;
-			lchan_activate(cbch);
+		case NM_OC_CHANNEL:
+			return osmo_fsm_inst_dispatch(trx->ts[mo->obj_inst.ts_nr].nm_chan.fi, NM_CHAN_EV_OPSTART_NACK,
+						      (void*)(intptr_t)NM_NACK_CANT_PERFORM);
+		default:
+			OSMO_ASSERT(0);
 		}
 	}
 
-	/* Send OPSTART ack */
-	return oml_mo_opstart_ack(mo);
+	msgb_free(l1_msg);
+	switch (mo->obj_class) {
+	case NM_OC_RADIO_CARRIER:
+		return osmo_fsm_inst_dispatch(trx->rc.fi, NM_RCARRIER_EV_OPSTART_ACK, NULL);
+	case NM_OC_CHANNEL:
+		/* ugly hack to auto-activate all SAPIs for the BCCH/CCCH on TS0 */
+		if (mo->obj_inst.trx_nr == 0 &&
+		    mo->obj_inst.ts_nr == 0) {
+			struct gsm_lchan *cbch = gsm_bts_get_cbch(mo->bts);
+			DEBUGP(DL1C, "====> trying to activate lchans of BCCH\n");
+			mo->bts->c0->ts[0].lchan[CCCH_LCHAN].rel_act_kind =
+				LCHAN_REL_ACT_OML;
+			lchan_activate(&mo->bts->c0->ts[0].lchan[CCCH_LCHAN]);
+			if (cbch) {
+				cbch->rel_act_kind = LCHAN_REL_ACT_OML;
+				lchan_activate(cbch);
+			}
+		}
+		return osmo_fsm_inst_dispatch(trx->ts[mo->obj_inst.ts_nr].nm_chan.fi,
+					      NM_CHAN_EV_OPSTART_ACK, NULL);
+	default:
+		OSMO_ASSERT(0);
+	}
 }
 
 static int opstart_compl_cb(struct gsm_bts_trx *trx, struct msgb *l1_msg,
@@ -1894,6 +1897,7 @@
 		      void *obj)
 {
 	struct gsm_bts_trx* trx;
+	struct gsm_bts_trx_ts *ts;
 	int rc;
 
 	switch (mo->obj_class) {
@@ -1906,6 +1910,7 @@
 		rc = osmo_fsm_inst_dispatch(trx->bb_transc.fi, NM_BBTRANSC_EV_OPSTART_ACK, NULL);
 		break;
 	case NM_OC_CHANNEL:
+		ts = (struct gsm_bts_trx_ts*) obj;
 		rc = ts_opstart(obj);
 		break;
 	case NM_OC_BTS:
diff --git a/src/osmo-bts-octphy/l1_if.c b/src/osmo-bts-octphy/l1_if.c
index 37a3e65..15353d0 100644
--- a/src/osmo-bts-octphy/l1_if.c
+++ b/src/osmo-bts-octphy/l1_if.c
@@ -311,15 +311,10 @@
 /* For OctPHY, this only about sending state changes to BSC */
 int l1if_activate_rf(struct gsm_bts_trx *trx, int on)
 {
-	int i;
 	if (on) {
 		/* signal availability */
 		osmo_fsm_inst_dispatch(trx->rc.fi, NM_RCARRIER_EV_SW_ACT, NULL);
 		osmo_fsm_inst_dispatch(trx->bb_transc.fi, NM_BBTRANSC_EV_SW_ACT, NULL);
-
-		for (i = 0; i < ARRAY_SIZE(trx->ts); i++)
-			oml_mo_state_chg(&trx->ts[i].mo, NM_OPSTATE_DISABLED,
-					 NM_AVSTATE_DEPENDENCY);
 	} else {
 		osmo_fsm_inst_dispatch(trx->rc.fi, NM_RCARRIER_EV_DISABLE, NULL);
 		osmo_fsm_inst_dispatch(trx->bb_transc.fi, NM_BBTRANSC_EV_DISABLE, NULL);
diff --git a/src/osmo-bts-octphy/l1_oml.c b/src/osmo-bts-octphy/l1_oml.c
index 50116b5..f1d873d 100644
--- a/src/osmo-bts-octphy/l1_oml.c
+++ b/src/osmo-bts-octphy/l1_oml.c
@@ -41,6 +41,7 @@
 #include <osmo-bts/l1sap.h>
 #include <osmo-bts/nm_radio_carrier_fsm.h>
 #include <osmo-bts/nm_bb_transc_fsm.h>
+#include <osmo-bts/nm_channel_fsm.h>
 
 #include "l1_if.h"
 #include "l1_oml.h"
@@ -191,28 +192,28 @@
 	/* TODO: Send NACK in case of error! */
 	struct gsm_bts_trx *trx = gsm_bts_trx_num(mo->bts, mo->obj_inst.trx_nr);
 
-	/* We already have a FSM for Radio Carrier, handle it there */
-	if (mo->obj_class == NM_OC_RADIO_CARRIER)
+	switch (mo->obj_class) {
+	case NM_OC_RADIO_CARRIER:
 		return osmo_fsm_inst_dispatch(trx->rc.fi, NM_RCARRIER_EV_OPSTART_ACK, NULL);
-
-	/* Set to Operational State: Enabled */
-	oml_mo_state_chg(mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK);
-
-	/* hack to auto-activate all SAPIs for the BCCH/CCCH on TS0 */
-	if (mo->obj_class == NM_OC_CHANNEL && mo->obj_inst.trx_nr == 0 &&
-	    mo->obj_inst.ts_nr == 7) {
-		struct gsm_lchan *cbch = gsm_bts_get_cbch(mo->bts);
-		mo->bts->c0->ts[0].lchan[CCCH_LCHAN].rel_act_kind =
-			LCHAN_REL_ACT_OML;
-		lchan_activate(&mo->bts->c0->ts[0].lchan[CCCH_LCHAN]);
-		if (cbch) {
-			cbch->rel_act_kind = LCHAN_REL_ACT_OML;
-			lchan_activate(cbch);
+	case NM_OC_CHANNEL:
+		/* ugly hack to auto-activate all SAPIs for the BCCH/CCCH on TS0 */
+		if (mo->obj_inst.trx_nr == 0 &&
+		    mo->obj_inst.ts_nr == 0) {
+			struct gsm_lchan *cbch = gsm_bts_get_cbch(mo->bts);
+			DEBUGP(DL1C, "====> trying to activate lchans of BCCH\n");
+			mo->bts->c0->ts[0].lchan[CCCH_LCHAN].rel_act_kind =
+				LCHAN_REL_ACT_OML;
+			lchan_activate(&mo->bts->c0->ts[0].lchan[CCCH_LCHAN]);
+			if (cbch) {
+				cbch->rel_act_kind = LCHAN_REL_ACT_OML;
+				lchan_activate(cbch);
+			}
 		}
+		return osmo_fsm_inst_dispatch(trx->ts[mo->obj_inst.ts_nr].nm_chan.fi,
+					      NM_CHAN_EV_OPSTART_ACK, NULL);
+	default:
+		OSMO_ASSERT(0);
 	}
-
-	/* Send OPSTART ack */
-	return oml_mo_opstart_ack(mo);
 }
 
 static
@@ -1502,7 +1503,7 @@
 	}
 
 	trx = ts->trx;
-	mo = &trx->ts[ar->PchId.byTimeslotNb].mo;
+	mo = &trx->ts[ar->PchId.byTimeslotNb].nm_chan.mo;
 
 	msgb_free(resp);
 
diff --git a/src/osmo-bts-omldummy/bts_model.c b/src/osmo-bts-omldummy/bts_model.c
index 11b71bd..f55b881 100644
--- a/src/osmo-bts-omldummy/bts_model.c
+++ b/src/osmo-bts-omldummy/bts_model.c
@@ -37,6 +37,7 @@
 #include <osmo-bts/l1sap.h>
 #include <osmo-bts/nm_radio_carrier_fsm.h>
 #include <osmo-bts/nm_bb_transc_fsm.h>
+#include <osmo-bts/nm_channel_fsm.h>
 
 /* TODO: check if dummy method is sufficient, else implement */
 int bts_model_lchan_deactivate(struct gsm_lchan *lchan)
@@ -77,13 +78,8 @@
 static uint8_t vbts_set_bts(struct gsm_bts *bts)
 {
 	struct gsm_bts_trx *trx;
-	uint8_t tn;
 
 	llist_for_each_entry(trx, &bts->trx_list, list) {
-
-		for (tn = 0; tn < TRX_NR_TS; tn++)
-			oml_mo_state_chg(&trx->ts[tn].mo, NM_OPSTATE_DISABLED, NM_AVSTATE_DEPENDENCY);
-
 		/* report availability of trx to the bts. this will trigger the rsl connection */
 		osmo_fsm_inst_dispatch(trx->rc.fi, NM_RCARRIER_EV_SW_ACT, NULL);
 		osmo_fsm_inst_dispatch(trx->bb_transc.fi, NM_BBTRANSC_EV_SW_ACT, NULL);
@@ -127,6 +123,7 @@
 {
 	int rc;
 	struct gsm_bts_trx* trx;
+	struct gsm_bts_trx_ts* ts;
 
 	switch (mo->obj_class) {
 	case NM_OC_RADIO_CARRIER:
@@ -138,6 +135,9 @@
 		rc = osmo_fsm_inst_dispatch(trx->bb_transc.fi, NM_BBTRANSC_EV_OPSTART_ACK, NULL);
 		break;
 	case NM_OC_CHANNEL:
+		ts = (struct gsm_bts_trx_ts *) obj;
+		rc = osmo_fsm_inst_dispatch(ts->nm_chan.fi, NM_CHAN_EV_OPSTART_ACK, NULL);
+		break;
 	case NM_OC_SITE_MANAGER:
 	case NM_OC_BTS:
 	case NM_OC_GPRS_NSE:
diff --git a/src/osmo-bts-sysmo/l1_if.c b/src/osmo-bts-sysmo/l1_if.c
index e766cea..99b17e8 100644
--- a/src/osmo-bts-sysmo/l1_if.c
+++ b/src/osmo-bts-sysmo/l1_if.c
@@ -1219,7 +1219,6 @@
 	SuperFemto_Prim_t *sysp = msgb_sysprim(resp);
 	GsmL1_Status_t status;
 	int on = 0;
-	unsigned int i;
 
 	if (sysp->id == SuperFemto_PrimId_ActivateRfCnf)
 		on = 1;
@@ -1244,9 +1243,6 @@
 		/* signal availability */
 		osmo_fsm_inst_dispatch(trx->rc.fi, NM_RCARRIER_EV_SW_ACT, NULL);
 		osmo_fsm_inst_dispatch(trx->bb_transc.fi, NM_BBTRANSC_EV_SW_ACT, NULL);
-
-		for (i = 0; i < ARRAY_SIZE(trx->ts); i++)
-			oml_mo_state_chg(&trx->ts[i].mo, NM_OPSTATE_DISABLED, NM_AVSTATE_DEPENDENCY);
 	} else {
 		bts_update_status(BTS_STATUS_RF_ACTIVE, 0);
 		osmo_fsm_inst_dispatch(trx->rc.fi, NM_RCARRIER_EV_DISABLE, NULL);
diff --git a/src/osmo-bts-sysmo/oml.c b/src/osmo-bts-sysmo/oml.c
index e14bea3..0a5fd0a 100644
--- a/src/osmo-bts-sysmo/oml.c
+++ b/src/osmo-bts-sysmo/oml.c
@@ -42,6 +42,8 @@
 #include <osmo-bts/l1sap.h>
 #include <osmo-bts/nm_radio_carrier_fsm.h>
 #include <osmo-bts/nm_bb_transc_fsm.h>
+#include <osmo-bts/nm_channel_fsm.h>
+#include <osmo-bts/nm_channel_fsm.h>
 
 #include "l1_if.h"
 #include "femtobts.h"
@@ -277,38 +279,41 @@
 			get_value_string(femtobts_l1prim_names, l1p->id),
 			get_value_string(femtobts_l1status_names, status));
 		msgb_free(l1_msg);
-		if (mo->obj_class == NM_OC_RADIO_CARRIER)
+		switch (mo->obj_class) {
+		case NM_OC_RADIO_CARRIER:
 			return osmo_fsm_inst_dispatch(trx->rc.fi, NM_RCARRIER_EV_OPSTART_NACK,
 						      (void*)(intptr_t)NM_NACK_CANT_PERFORM);
-		else
-			return oml_mo_opstart_nack(mo, NM_NACK_CANT_PERFORM);
-	}
-
-	msgb_free(l1_msg);
-
-	/* We already have a FSM for Radio Carrier, handle it there */
-	if (mo->obj_class == NM_OC_RADIO_CARRIER)
-		return osmo_fsm_inst_dispatch(trx->rc.fi, NM_RCARRIER_EV_OPSTART_ACK, NULL);
-
-	/* Set to Operational State: Enabled */
-	oml_mo_state_chg(mo, NM_OPSTATE_ENABLED, NM_AVSTATE_OK);
-
-	/* ugly hack to auto-activate all SAPIs for the BCCH/CCCH on TS0 */
-	if (mo->obj_class == NM_OC_CHANNEL && mo->obj_inst.trx_nr == 0 &&
-	    mo->obj_inst.ts_nr == 0) {
-		struct gsm_lchan *cbch = gsm_bts_get_cbch(mo->bts);
-		DEBUGP(DL1C, "====> trying to activate lchans of BCCH\n");
-		mo->bts->c0->ts[0].lchan[CCCH_LCHAN].rel_act_kind =
-			LCHAN_REL_ACT_OML;
-		lchan_activate(&mo->bts->c0->ts[0].lchan[CCCH_LCHAN]);
-		if (cbch) {
-			cbch->rel_act_kind = LCHAN_REL_ACT_OML;
-			lchan_activate(cbch);
+		case NM_OC_CHANNEL:
+			return osmo_fsm_inst_dispatch(trx->ts[mo->obj_inst.ts_nr].nm_chan.fi, NM_CHAN_EV_OPSTART_NACK,
+						      (void*)(intptr_t)NM_NACK_CANT_PERFORM);
+		default:
+			OSMO_ASSERT(0);
 		}
 	}
 
-	/* Send OPSTART ack */
-	return oml_mo_opstart_ack(mo);
+	msgb_free(l1_msg);
+	switch (mo->obj_class) {
+	case NM_OC_RADIO_CARRIER:
+		return osmo_fsm_inst_dispatch(trx->rc.fi, NM_RCARRIER_EV_OPSTART_ACK, NULL);
+	case NM_OC_CHANNEL:
+		/* ugly hack to auto-activate all SAPIs for the BCCH/CCCH on TS0 */
+		if (mo->obj_inst.trx_nr == 0 &&
+		    mo->obj_inst.ts_nr == 0) {
+			struct gsm_lchan *cbch = gsm_bts_get_cbch(mo->bts);
+			DEBUGP(DL1C, "====> trying to activate lchans of BCCH\n");
+			mo->bts->c0->ts[0].lchan[CCCH_LCHAN].rel_act_kind =
+				LCHAN_REL_ACT_OML;
+			lchan_activate(&mo->bts->c0->ts[0].lchan[CCCH_LCHAN]);
+			if (cbch) {
+				cbch->rel_act_kind = LCHAN_REL_ACT_OML;
+				lchan_activate(cbch);
+			}
+		}
+		return osmo_fsm_inst_dispatch(trx->ts[mo->obj_inst.ts_nr].nm_chan.fi,
+					      NM_CHAN_EV_OPSTART_ACK, NULL);
+	default:
+		OSMO_ASSERT(0);
+	}
 }
 
 static int opstart_compl_cb(struct gsm_bts_trx *trx, struct msgb *l1_msg,
@@ -318,7 +323,7 @@
 	GsmL1_Prim_t *l1p = msgb_l1prim(l1_msg);
 	GsmL1_MphConnectCnf_t *cnf = &l1p->u.mphConnectCnf;
 
-	mo = &trx->ts[cnf->u8Tn].mo;
+	mo = &trx->ts[cnf->u8Tn].nm_chan.mo;
 	return opstart_compl(mo, l1_msg);
 }
 
@@ -1772,6 +1777,7 @@
 		      void *obj)
 {
 	struct gsm_bts_trx* trx;
+	struct gsm_bts_trx_ts *ts;
 	int rc;
 
 	switch (mo->obj_class) {
@@ -1784,7 +1790,8 @@
 		rc = osmo_fsm_inst_dispatch(trx->bb_transc.fi, NM_BBTRANSC_EV_OPSTART_ACK, NULL);
 		break;
 	case NM_OC_CHANNEL:
-		rc = ts_opstart(obj);
+		ts = (struct gsm_bts_trx_ts*) obj;
+		rc = ts_opstart(ts);
 		break;
 	case NM_OC_BTS:
 	case NM_OC_SITE_MANAGER:
diff --git a/src/osmo-bts-trx/l1_if.c b/src/osmo-bts-trx/l1_if.c
index 9797188..72aa74e 100644
--- a/src/osmo-bts-trx/l1_if.c
+++ b/src/osmo-bts-trx/l1_if.c
@@ -46,6 +46,7 @@
 #include <osmo-bts/pcu_if.h>
 #include <osmo-bts/nm_radio_carrier_fsm.h>
 #include <osmo-bts/nm_bb_transc_fsm.h>
+#include <osmo-bts/nm_channel_fsm.h>
 
 #include "l1_if.h"
 #include "trx_if.h"
@@ -92,7 +93,6 @@
 {
 	struct phy_instance *pinst = l1h->phy_inst;
 	struct gsm_bts_trx *trx = pinst->trx;
-	uint8_t tn;
 
 	/* HACK, we should change state when we receive first clock from
 	 * transceiver */
@@ -103,19 +103,9 @@
 			osmo_fsm_inst_dispatch(trx->bb_transc.fi, NM_BBTRANSC_EV_SW_ACT, NULL);
 			pinst->u.osmotrx.sw_act_reported = true;
 		}
-
-		for (tn = 0; tn < TRX_NR_TS; tn++)
-			oml_mo_state_chg(&trx->ts[tn].mo, NM_OPSTATE_DISABLED,
-				(l1h->config.slotmask & (1 << tn)) ?
-					NM_AVSTATE_DEPENDENCY :
-					NM_AVSTATE_NOT_INSTALLED);
 	} else {
 		osmo_fsm_inst_dispatch(trx->rc.fi, NM_RCARRIER_EV_DISABLE, NULL);
 		osmo_fsm_inst_dispatch(trx->bb_transc.fi, NM_BBTRANSC_EV_DISABLE, NULL);
-
-		for (tn = 0; tn < TRX_NR_TS; tn++)
-			oml_mo_state_chg(&trx->ts[tn].mo, NM_OPSTATE_DISABLED,
-				NM_AVSTATE_OFF_LINE);
 	}
 }
 
@@ -604,6 +594,7 @@
 		      void *obj)
 {
 	struct gsm_bts_trx *trx;
+	struct gsm_bts_trx_ts *ts;
 	int rc;
 
 	switch (mo->obj_class) {
@@ -617,6 +608,9 @@
 		rc = osmo_fsm_inst_dispatch(trx->bb_transc.fi, NM_BBTRANSC_EV_OPSTART_ACK, NULL);
 		break;
 	case NM_OC_CHANNEL:
+		ts = (struct gsm_bts_trx_ts *) obj;
+		rc = osmo_fsm_inst_dispatch(ts->nm_chan.fi, NM_CHAN_EV_OPSTART_ACK, NULL);
+		break;
 	case NM_OC_BTS:
 	case NM_OC_SITE_MANAGER:
 	case NM_OC_GPRS_NSE:
diff --git a/src/osmo-bts-virtual/bts_model.c b/src/osmo-bts-virtual/bts_model.c
index 218697b..b89013b 100644
--- a/src/osmo-bts-virtual/bts_model.c
+++ b/src/osmo-bts-virtual/bts_model.c
@@ -37,6 +37,7 @@
 #include <osmo-bts/l1sap.h>
 #include <osmo-bts/nm_radio_carrier_fsm.h>
 #include <osmo-bts/nm_bb_transc_fsm.h>
+#include <osmo-bts/nm_channel_fsm.h>
 
 #include "virtual_um.h"
 
@@ -84,13 +85,8 @@
 static uint8_t vbts_set_bts(struct gsm_bts *bts)
 {
 	struct gsm_bts_trx *trx;
-	uint8_t tn;
 
 	llist_for_each_entry(trx, &bts->trx_list, list) {
-
-		for (tn = 0; tn < TRX_NR_TS; tn++)
-			oml_mo_state_chg(&trx->ts[tn].mo, NM_OPSTATE_DISABLED, NM_AVSTATE_DEPENDENCY);
-
 		/* report availability of trx to the bts. this will trigger the rsl connection */
 		osmo_fsm_inst_dispatch(trx->rc.fi, NM_RCARRIER_EV_SW_ACT, NULL);
 		osmo_fsm_inst_dispatch(trx->bb_transc.fi, NM_BBTRANSC_EV_SW_ACT, NULL);
@@ -141,6 +137,7 @@
 {
 	int rc;
 	struct gsm_bts_trx* trx;
+	struct gsm_bts_trx_ts *ts;
 
 	switch (mo->obj_class) {
 	case NM_OC_RADIO_CARRIER:
@@ -152,6 +149,9 @@
 		rc = osmo_fsm_inst_dispatch(trx->bb_transc.fi, NM_BBTRANSC_EV_OPSTART_ACK, NULL);
 		break;
 	case NM_OC_CHANNEL:
+		ts = (struct gsm_bts_trx_ts *) obj;
+		rc = osmo_fsm_inst_dispatch(ts->nm_chan.fi, NM_CHAN_EV_OPSTART_ACK, NULL);
+		break;
 	case NM_OC_SITE_MANAGER:
 	case NM_OC_BTS:
 	case NM_OC_GPRS_NSE:

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

Gerrit-Project: osmo-bts
Gerrit-Branch: master
Gerrit-Change-Id: I288cbfb4730b25a334ef1c3d6b9679d6f1d4cfc5
Gerrit-Change-Number: 20311
Gerrit-PatchSet: 1
Gerrit-Owner: pespin <pespin at sysmocom.de>
Gerrit-MessageType: newchange
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20200928/3ba6a45a/attachment.htm>


More information about the gerrit-log mailing list