[PATCH] osmocom-bb[master]: VIRT-PHY: Major rewrite to deal with muliple L1CTL clients

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/.

Harald Welte gerrit-no-reply at lists.osmocom.org
Wed Jul 19 11:54:39 UTC 2017


Review at  https://gerrit.osmocom.org/3286

VIRT-PHY: Major rewrite to deal with muliple L1CTL clients

Change-Id: Ibfb2a93f8b45a95215c01368b1a52d92283474e6
---
M src/host/virt_phy/include/virtphy/gsmtapl1_if.h
M src/host/virt_phy/include/virtphy/l1ctl_sap.h
M src/host/virt_phy/include/virtphy/l1ctl_sock.h
M src/host/virt_phy/include/virtphy/virt_l1_model.h
M src/host/virt_phy/include/virtphy/virt_l1_sched.h
M src/host/virt_phy/src/gsmtapl1_if.c
M src/host/virt_phy/src/l1ctl_sap.c
M src/host/virt_phy/src/l1ctl_sock.c
M src/host/virt_phy/src/virt_l1_model.c
M src/host/virt_phy/src/virt_l1_sched_simple.c
M src/host/virt_phy/src/virt_prim_data.c
M src/host/virt_phy/src/virt_prim_fbsb.c
M src/host/virt_phy/src/virt_prim_pm.c
M src/host/virt_phy/src/virt_prim_rach.c
M src/host/virt_phy/src/virt_prim_traffic.c
M src/host/virt_phy/src/virtphy.c
16 files changed, 388 insertions(+), 428 deletions(-)


  git pull ssh://gerrit.osmocom.org:29418/osmocom-bb refs/changes/86/3286/1

diff --git a/src/host/virt_phy/include/virtphy/gsmtapl1_if.h b/src/host/virt_phy/include/virtphy/gsmtapl1_if.h
index d8a4367..125ec11 100644
--- a/src/host/virt_phy/include/virtphy/gsmtapl1_if.h
+++ b/src/host/virt_phy/include/virtphy/gsmtapl1_if.h
@@ -9,8 +9,4 @@
 void gsmtapl1_init(struct l1_model_ms *model);
 void gsmtapl1_rx_from_virt_um_inst_cb(struct virt_um_inst *vui,
                                       struct msgb *msg);
-void gsmtapl1_rx_from_virt_um(struct msgb *msg);
-void gsmtapl1_tx_to_virt_um_inst(uint32_t fn, struct virt_um_inst *vui, struct msgb *msg);
-void gsmtapl1_tx_to_virt_um(uint32_t fn, struct msgb *msg);
-void chantype_gsmtap2rsl(uint8_t gsmtap_chantype, uint8_t *rsl_chantype,
-                         uint8_t *link_id);
+void gsmtapl1_tx_to_virt_um_inst(struct l1_model_ms *ms, uint32_t fn, struct msgb *msg);
diff --git a/src/host/virt_phy/include/virtphy/l1ctl_sap.h b/src/host/virt_phy/include/virtphy/l1ctl_sap.h
index b84c3e9..5903a02 100644
--- a/src/host/virt_phy/include/virtphy/l1ctl_sap.h
+++ b/src/host/virt_phy/include/virtphy/l1ctl_sap.h
@@ -27,17 +27,11 @@
 
 
 void l1ctl_sap_init(struct l1_model_ms *model);
-void prim_rach_init(struct l1_model_ms *model);
-void prim_data_init(struct l1_model_ms *model);
-void prim_traffic_init(struct l1_model_ms *model);
-void prim_fbsb_init(struct l1_model_ms *model);
 void prim_pm_init(struct l1_model_ms *model);
-void l1ctl_sap_tx_to_l23_inst(struct l1ctl_sock_inst *lsi, struct msgb *msg);
-void l1ctl_sap_tx_to_l23(struct msgb *msg);
-void l1ctl_sap_rx_from_l23_inst_cb(struct l1ctl_sock_inst *lsi,
-                                   struct msgb *msg);
+void l1ctl_sap_tx_to_l23_inst(struct l1_model_ms *model, struct msgb *msg);
+void l1ctl_sap_rx_from_l23_inst_cb(struct l1ctl_sock_client *lsc, struct msgb *msg);
 void l1ctl_sap_rx_from_l23(struct msgb *msg);
-void l1ctl_sap_handler(struct msgb *msg);
+void l1ctl_sap_handler(struct l1_model_ms *ms, struct msgb *msg);
 
 /* utility methods */
 struct msgb *l1ctl_msgb_alloc(uint8_t msg_type);
@@ -45,39 +39,39 @@
                                  uint16_t arfcn);
 
 /* receive routines */
-void l1ctl_rx_fbsb_req(struct msgb *msg);
-void l1ctl_rx_dm_est_req(struct msgb *msg);
-void l1ctl_rx_dm_rel_req(struct msgb *msg);
-void l1ctl_rx_param_req(struct msgb *msg);
-void l1ctl_rx_dm_freq_req(struct msgb *msg);
-void l1ctl_rx_crypto_req(struct msgb *msg);
-void l1ctl_rx_rach_req(struct msgb *msg);
-void l1ctl_rx_data_req(struct msgb *msg);
-void l1ctl_rx_pm_req(struct msgb *msg);
-void l1ctl_rx_reset_req(struct msgb *msg);
-void l1ctl_rx_ccch_mode_req(struct msgb *msg);
-void l1ctl_rx_tch_mode_req(struct msgb *msg);
-void l1ctl_rx_neigh_pm_req(struct msgb *msg);
-void l1ctl_rx_traffic_req(struct msgb *msg);
-void l1ctl_rx_sim_req(struct msgb *msg);
+void l1ctl_rx_fbsb_req(struct l1_model_ms *, struct msgb *msg);
+void l1ctl_rx_dm_est_req(struct l1_model_ms *, struct msgb *msg);
+void l1ctl_rx_dm_rel_req(struct l1_model_ms *, struct msgb *msg);
+void l1ctl_rx_param_req(struct l1_model_ms *, struct msgb *msg);
+void l1ctl_rx_dm_freq_req(struct l1_model_ms *, struct msgb *msg);
+void l1ctl_rx_crypto_req(struct l1_model_ms *, struct msgb *msg);
+void l1ctl_rx_rach_req(struct l1_model_ms *, struct msgb *msg);
+void l1ctl_rx_data_req(struct l1_model_ms *, struct msgb *msg);
+void l1ctl_rx_pm_req(struct l1_model_ms *, struct msgb *msg);
+void l1ctl_rx_reset_req(struct l1_model_ms *, struct msgb *msg);
+void l1ctl_rx_ccch_mode_req(struct l1_model_ms *, struct msgb *msg);
+void l1ctl_rx_tch_mode_req(struct l1_model_ms *, struct msgb *msg);
+void l1ctl_rx_neigh_pm_req(struct l1_model_ms *, struct msgb *msg);
+void l1ctl_rx_traffic_req(struct l1_model_ms *, struct msgb *msg);
+void l1ctl_rx_sim_req(struct l1_model_ms *, struct msgb *msg);
 
 /* transmit routines */
-void l1ctl_tx_reset(uint8_t msg_type, uint8_t reset_type);
-void l1ctl_tx_rach_conf(uint32_t fn, uint16_t arfcn);
-void l1ctl_tx_data_conf(uint32_t fn, uint16_t snr, uint16_t arfcn);
-void l1ctl_tx_data_ind(struct msgb *msg, uint16_t arfcn, uint8_t link_id,
+void l1ctl_tx_reset(struct l1_model_ms *, uint8_t msg_type, uint8_t reset_type);
+void l1ctl_tx_rach_conf(struct l1_model_ms *, uint32_t fn, uint16_t arfcn);
+void l1ctl_tx_data_conf(struct l1_model_ms *, uint32_t fn, uint16_t snr, uint16_t arfcn);
+void l1ctl_tx_data_ind(struct l1_model_ms *, struct msgb *msg, uint16_t arfcn, uint8_t link_id,
                        uint8_t chan_nr, uint32_t fn, uint8_t snr,
                        uint8_t signal_dbm, uint8_t num_biterr,
                        uint8_t fire_crc);
-void l1ctl_tx_traffic_conf(uint32_t fn, uint16_t snr, uint16_t arfcn);
-void l1ctl_tx_traffic_ind(struct msgb *msg, uint16_t arfcn, uint8_t link_id,
+void l1ctl_tx_traffic_conf(struct l1_model_ms *, uint32_t fn, uint16_t snr, uint16_t arfcn);
+void l1ctl_tx_traffic_ind(struct l1_model_ms *, struct msgb *msg, uint16_t arfcn, uint8_t link_id,
                           uint8_t chan_nr, uint32_t fn, uint8_t snr,
                           uint8_t signal_dbm, uint8_t num_biterr,
                           uint8_t fire_crc);
-void l1ctl_tx_pm_conf(struct l1ctl_pm_req *pm_req);
-void l1ctl_tx_fbsb_conf(uint8_t res, uint16_t arfcn);
-void l1ctl_tx_ccch_mode_conf(uint8_t ccch_mode);
-void l1ctl_tx_tch_mode_conf(uint8_t tch_mode, uint8_t audio_mode);
+void l1ctl_tx_pm_conf(struct l1_model_ms *, struct l1ctl_pm_req *pm_req);
+void l1ctl_tx_fbsb_conf(struct l1_model_ms *, uint8_t res, uint16_t arfcn);
+void l1ctl_tx_ccch_mode_conf(struct l1_model_ms *, uint8_t ccch_mode);
+void l1ctl_tx_tch_mode_conf(struct l1_model_ms *, uint8_t tch_mode, uint8_t audio_mode);
 
 /* scheduler functions */
 uint32_t sched_fn_ul(struct gsm_time cur_time, uint8_t chan_nr,
diff --git a/src/host/virt_phy/include/virtphy/l1ctl_sock.h b/src/host/virt_phy/include/virtphy/l1ctl_sock.h
index 8c96dc8..82ee5ca 100644
--- a/src/host/virt_phy/include/virtphy/l1ctl_sock.h
+++ b/src/host/virt_phy/include/virtphy/l1ctl_sock.h
@@ -1,17 +1,33 @@
 #pragma once
 
 #include <osmocom/core/msgb.h>
+#include <osmocom/core/linuxlist.h>
 #include <osmocom/core/select.h>
 
 #define L1CTL_SOCK_PATH	"/tmp/osmocom_l2"
 
+struct l1ctl_sock_inst;
+
+struct l1ctl_sock_client {
+	/* list head in l1ctl_sock_inst.clients */
+	struct llist_head list;
+	/* pointer back to the server socket that accepted us */
+	struct l1ctl_sock_inst *l1ctl_sock;
+	/* Osmo FD for the client socket */
+	struct osmo_fd ofd;
+	/* private data, can be set in accept_cb */
+	void *priv;
+};
+
 /* L1CTL socket instance contains socket data. */
 struct l1ctl_sock_inst {
 	void *priv; /* Will be appended after osmo-fd's data pointer. */
-	struct osmo_fd connection; /* L1CTL connection to l2 app */
+	struct llist_head clients;
 	char* l1ctl_sock_path; /* Socket path used to connect to l23 */
 	struct osmo_fd ofd; /* Osmocom file descriptor to accept L1CTL connections. */
-	void (*recv_cb)(struct l1ctl_sock_inst *vui, struct msgb *msg); /* Callback function called for incoming data from l2 app. */
+	void (*recv_cb)(struct l1ctl_sock_client *lsc, struct msgb *msg); /* Callback function called for incoming data from l2 app. */
+	/* Callback function called for new client after accept() */
+	int (*accept_cb)(struct l1ctl_sock_client *lsc);
 };
 
 /**
@@ -19,20 +35,16 @@
  */
 struct l1ctl_sock_inst *l1ctl_sock_init(
                 void *ctx,
-                void (*recv_cb)(struct l1ctl_sock_inst *lsi, struct msgb *msg),
+                void (*recv_cb)(struct l1ctl_sock_client *lsc, struct msgb *msg),
+                int (*accept_cb)(struct l1ctl_sock_client *lsc),
                 char *path);
 
 /**
  * @brief Transmit message to l2.
  */
-int l1ctl_sock_write_msg(struct l1ctl_sock_inst *lsi, struct msgb *msg);
+int l1ctl_sock_write_msg(struct l1ctl_sock_client *lsc, struct msgb *msg);
 
 /**
  * @brief Destroy instance.
  */
-void l1ctl_sock_destroy();
-
-/**
- * @brief Disconnect current connection.
- */
-void l1ctl_sock_disconnect(struct l1ctl_sock_inst *lsi);
+void l1ctl_sock_destroy(struct l1ctl_sock_inst *lsi);
diff --git a/src/host/virt_phy/include/virtphy/virt_l1_model.h b/src/host/virt_phy/include/virtphy/virt_l1_model.h
index 61776ec..0cb5381 100644
--- a/src/host/virt_phy/include/virtphy/virt_l1_model.h
+++ b/src/host/virt_phy/include/virtphy/virt_l1_model.h
@@ -45,6 +45,10 @@
 
 	struct gsm_time	downlink_time;	/* current GSM time received on downlink */
 	struct gsm_time current_time; /* GSM time used internally for scheduling */
+	struct {
+		uint32_t last_exec_fn;
+		struct llist_head mframe_items;
+	} sched;
 
 	enum ms_state state;
 
@@ -89,13 +93,16 @@
 };
 
 struct l1_model_ms {
-	struct l1ctl_sock_inst *lsi;
+	/* pointer to the L1CTL socket client associated with this specific MS */
+	struct l1ctl_sock_client *lsc;
+	/* pointer to the (shared) GSMTAP/VirtUM socket to talk to BTS(s) */
 	struct virt_um_inst *vui;
+	/* actual per-MS state */
 	struct l1_state_ms state;
 };
 
 
-struct l1_model_ms *l1_model_ms_init(void *ctx);
+struct l1_model_ms *l1_model_ms_init(void *ctx, struct l1ctl_sock_client *lsc, struct virt_um_inst *vui);
 
 void l1_model_ms_destroy(struct l1_model_ms *model);
 
diff --git a/src/host/virt_phy/include/virtphy/virt_l1_sched.h b/src/host/virt_phy/include/virtphy/virt_l1_sched.h
index de08550..f3e9b84 100644
--- a/src/host/virt_phy/include/virtphy/virt_l1_sched.h
+++ b/src/host/virt_phy/include/virtphy/virt_l1_sched.h
@@ -5,7 +5,9 @@
 #include <osmocom/gsm/gsm_utils.h>
 #include <virtphy/virt_l1_sched.h>
 
-typedef void virt_l1_sched_cb(uint32_t fn, struct msgb * msg);
+struct l1_model_ms;
+
+typedef void virt_l1_sched_cb(struct l1_model_ms *ms, uint32_t fn, struct msgb * msg);
 
 /* bucket containing items to be executed for a specific mframe number */
 struct virt_l1_sched_mframe_item {
@@ -22,11 +24,9 @@
 	virt_l1_sched_cb * handler_cb; /* handler callback */
 };
 
-void virt_l1_sched_init(struct l1_model_ms * model);
-int virt_l1_sched_start(struct gsm_time time);
-int virt_l1_sched_restart(struct gsm_time time);
-void virt_l1_sched_sync_time(struct gsm_time time, uint8_t hard_reset);
-void virt_l1_sched_stop();
-void virt_l1_sched_execute(uint32_t fn);
-void virt_l1_sched_schedule(struct msgb * msg, uint32_t fn, uint8_t ts,
+int virt_l1_sched_restart(struct l1_model_ms *ms, struct gsm_time time);
+void virt_l1_sched_sync_time(struct l1_model_ms *ms, struct gsm_time time, uint8_t hard_reset);
+void virt_l1_sched_stop(struct l1_model_ms *ms);
+void virt_l1_sched_execute(struct l1_model_ms *ms, uint32_t fn);
+void virt_l1_sched_schedule(struct l1_model_ms *ms, struct msgb *msg, uint32_t fn, uint8_t ts,
                             virt_l1_sched_cb * handler_cb);
diff --git a/src/host/virt_phy/src/gsmtapl1_if.c b/src/host/virt_phy/src/gsmtapl1_if.c
index 18580b4..dfcd5a8 100644
--- a/src/host/virt_phy/src/gsmtapl1_if.c
+++ b/src/host/virt_phy/src/gsmtapl1_if.c
@@ -1,6 +1,7 @@
 /* GSMTAP layer1 is transmits gsmtap messages over a virtual layer 1.*/
 
 /* (C) 2016 by Sebastian Stumpf <sebastian.stumpf87 at googlemail.com>
+ * (C) 2017 by Harald Welte <laforge at gnumonks.org>
  *
  * All Rights Reserved
  *
@@ -38,24 +39,16 @@
 #include <virtphy/logging.h>
 #include <virtphy/virt_l1_sched.h>
 
-static struct l1_model_ms *l1_model_ms = NULL;
-
-void gsmtapl1_init(struct l1_model_ms *model)
-{
-	l1_model_ms = model;
-}
-
 /**
  * Replace l11 header of given msgb by a gsmtap header and send it over the virt um.
  */
-void gsmtapl1_tx_to_virt_um_inst(uint32_t fn, struct virt_um_inst *vui,
-				 struct msgb *msg)
+void gsmtapl1_tx_to_virt_um_inst(struct l1_model_ms *ms, uint32_t fn, struct msgb *msg)
 {
 	struct l1ctl_hdr *l1h = (struct l1ctl_hdr *)msg->data;
 	struct l1ctl_info_ul *ul = (struct l1ctl_info_ul *)l1h->data;
 	struct gsmtap_hdr *gh;
 	struct msgb *outmsg;	/* msg to send with gsmtap header prepended */
-	uint16_t arfcn = l1_model_ms->state.serving_cell.arfcn;	/* arfcn of the cell we currently camp on */
+	uint16_t arfcn = ms->state.serving_cell.arfcn;	/* arfcn of the cell we currently camp on */
 	uint8_t signal_dbm = 63;	/* signal strength */
 	uint8_t snr = 63;	/* signal noise ratio, 63 is best */
 	uint8_t *data = msgb_l2(msg);	/* data to transmit (whole message without l1 header) */
@@ -76,7 +69,7 @@
 	if (outmsg) {
 		outmsg->l1h = msgb_data(outmsg);
 		gh = msgb_l1(outmsg);
-		if (virt_um_write_msg(l1_model_ms->vui, outmsg) == -1) {
+		if (virt_um_write_msg(ms->vui, outmsg) == -1) {
 			LOGP(DVIRPHY, LOGL_ERROR, "Gsmtap msg could not send to virt um - "
 			     "(arfcn=%u, type=%u, subtype=%u, timeslot=%u, subslot=%u)\n",
 			     gh->arfcn, gh->type, gh->sub_type, gh->timeslot,
@@ -95,86 +88,44 @@
 }
 
 /**
- * @see void gsmtapl1_tx_to_virt_um(struct virt_um_inst *vui, uint32_t fn, struct msgb *msg).
- */
-void gsmtapl1_tx_to_virt_um(uint32_t fn, struct msgb *msg)
-{
-	gsmtapl1_tx_to_virt_um_inst(fn, l1_model_ms->vui, msg);
-}
-
-/**
  * @see virt_prim_fbsb.c
  */
-extern void prim_fbsb_sync(struct msgb *msg);
+extern void prim_fbsb_sync(struct l1_model_ms *ms, struct msgb *msg);
 
 /**
  * @see virt_prim_pm.c
  */
-extern uint16_t prim_pm_set_sig_strength(uint16_t arfcn, int16_t sig_lev);
+extern uint16_t prim_pm_set_sig_strength(struct l1_model_ms *ms, uint16_t arfcn, int16_t sig_lev);
 
-/**
- * Receive a gsmtap message from the virt um.
- *
- * As we do not have a downlink scheduler, but not all dl messages must be processed and thus forwarded to l2, this function also implements some message filtering.
- * E.g. we do not forward:
- * - uplink messages
- * - messages with a wrong arfcn
- * - if in MS_STATE_IDLE_SEARCHING
- */
-void gsmtapl1_rx_from_virt_um_inst_cb(struct virt_um_inst *vui,
-				      struct msgb *msg)
+static void l1ctl_from_virt_um(struct l1ctl_sock_client *lsc, struct msgb *msg, uint32_t fn,
+				uint16_t arfcn, uint8_t timeslot, uint8_t subslot,
+				uint8_t gsmtap_chantype, uint8_t chan_nr, uint8_t link_id,
+				uint8_t snr_db)
 {
-	if (!msg)
+	struct l1_model_ms *ms = lsc->priv;
+	uint8_t signal_dbm = dbm2rxlev(prim_pm_set_sig_strength(ms, arfcn & GSMTAP_ARFCN_MASK, MAX_SIG_LEV_DBM));	/* Power measurement with each received massage */
+
+	gsm_fn2gsmtime(&ms->state.downlink_time, fn);
+
+	/* we do not forward messages to l23 if we are in network search state */
+	if (ms->state.state == MS_STATE_IDLE_SEARCHING)
 		return;
 
-	struct gsmtap_hdr *gh = msgb_l1(msg);
-	uint32_t fn = ntohl(gh->frame_number);	/* frame number of the rcv msg */
-	uint16_t arfcn = ntohs(gh->arfcn);	/* arfcn of the received msg */
-	uint8_t gsmtap_chantype = gh->sub_type;	/* gsmtap channel type */
-	uint8_t signal_dbm = dbm2rxlev(prim_pm_set_sig_strength(arfcn & GSMTAP_ARFCN_MASK, MAX_SIG_LEV_DBM));	/* Power measurement with each received massage */
-	uint8_t snr = gh->snr_db;	/* signal noise ratio */
-	uint8_t subslot = gh->sub_slot;	/* multiframe subslot to send msg in (tch -> 0-26, bcch/ccch -> 0-51) */
-	uint8_t timeslot = gh->timeslot;	/* tdma timeslot to send in (0-7) */
-	uint8_t rsl_chantype;	/* rsl chan type (8.58, 9.3.1) */
-	uint8_t link_id;	/* rsl link id tells if this is an ssociated or dedicated link */
-	uint8_t chan_nr;	/* encoded rsl channel type, timeslot and mf subslot */
-
-	/* generally ignore all uplink messages received */
-	if (arfcn & GSMTAP_ARFCN_F_UPLINK) {
-		LOGP(DVIRPHY, LOGL_NOTICE, "Ignoring gsmtap msg from virt um - uplink flag set!\n");
-		goto freemsg;
-	}
-	/* we do not forward messages to l23 if we are in network search state */
-	if (l1_model_ms->state.state == MS_STATE_IDLE_SEARCHING)
-		goto freemsg;
-
 	/* forward downlink msg to fbsb sync routine if we are in sync state */
-	if (l1_model_ms->state.state == MS_STATE_IDLE_SYNCING) {
-		prim_fbsb_sync(msg);
+	if (ms->state.state == MS_STATE_IDLE_SYNCING) {
+		prim_fbsb_sync(ms, msg);
 		return;
 	}
 	/* generally ignore all messages coming from another arfcn than the camped one */
-	if (l1_model_ms->state.serving_cell.arfcn != arfcn) {
+	if (ms->state.serving_cell.arfcn != arfcn) {
 		LOGP(DVIRPHY, LOGL_NOTICE,
 		     "Ignoring gsmtap msg from virt um - msg arfcn=%d not equal synced arfcn=%d!\n",
-		     arfcn, l1_model_ms->state.serving_cell.arfcn);
-		goto freemsg;
+		     arfcn, ms->state.serving_cell.arfcn);
+		return;
 	}
 
-	msg->l2h = msgb_pull(msg, sizeof(*gh));
-	chantype_gsmtap2rsl(gsmtap_chantype, &rsl_chantype, &link_id);
-	/* see TS 08.58 -> 9.3.1 for channel number encoding */
-	chan_nr = rsl_enc_chan_nr(rsl_chantype, subslot, timeslot);
-
-	gsm_fn2gsmtime(&l1_model_ms->state.downlink_time, fn);
-	virt_l1_sched_sync_time(l1_model_ms->state.downlink_time, 0);
-	virt_l1_sched_execute(fn);
-
-	DEBUGP(DVIRPHY, "Receiving gsmtap msg from virt um - "
-	       "(arfcn=%u, framenumber=%u, type=%s, subtype=%s, timeslot=%u, subslot=%u, rsl_chan_type=0x%2x, link_id=0x%2x, chan_nr=0x%2x)\n",
-	       arfcn, fn, get_value_string(gsmtap_type_names, gh->type),
-	       get_value_string(gsmtap_gsm_channel_names, gsmtap_chantype), timeslot,
-	       subslot, rsl_chantype, link_id, chan_nr);
+	virt_l1_sched_sync_time(ms, ms->state.downlink_time, 0);
+	virt_l1_sched_execute(ms, fn);
 
 	/* switch case with removed ACCH flag */
 	switch (gsmtap_chantype & ~GSMTAP_CHANNEL_ACCH & 0xff) {
@@ -191,9 +142,9 @@
 	case GSMTAP_CHANNEL_SDCCH8:
 		/* only forward messages on dedicated channels to l2, if
 		 * the timeslot and subslot is fitting */
-		if (l1_model_ms->state.dedicated.tn == timeslot
-		    && l1_model_ms->state.dedicated.subslot == subslot) {
-			l1ctl_tx_data_ind(msg, arfcn, link_id, chan_nr, fn, snr, signal_dbm, 0, 0);
+		if (ms->state.dedicated.tn == timeslot
+		    && ms->state.dedicated.subslot == subslot) {
+			l1ctl_tx_data_ind(ms, msg, arfcn, link_id, chan_nr, fn, snr_db, signal_dbm, 0, 0);
 		}
 		break;
 	case GSMTAP_CHANNEL_AGCH:
@@ -201,7 +152,7 @@
 	case GSMTAP_CHANNEL_BCCH:
 		/* save to just forward here, as upper layer ignores messages that
 		 * do not fit the current state (e.g.  gsm48_rr.c:2159) */
-		l1ctl_tx_data_ind(msg, arfcn, link_id, chan_nr, fn, snr, signal_dbm, 0, 0);
+		l1ctl_tx_data_ind(ms, msg, arfcn, link_id, chan_nr, fn, snr_db, signal_dbm, 0, 0);
 		break;
 	case GSMTAP_CHANNEL_RACH:
 		LOGP(DVIRPHY, LOGL_NOTICE,
@@ -222,15 +173,60 @@
 		     "Ignoring gsmtap msg from virt um - channel type unknown.\n");
 		break;
 	}
-
-freemsg:
-	talloc_free(msg);
 }
 
 /**
- * @see void gsmtapl1_rx_from_virt_um_cb(struct virt_um_inst *vui, struct msgb msg).
+ * Receive a gsmtap message from the virt um.
+ *
+ * As we do not have a downlink scheduler, but not all dl messages must be processed and thus forwarded to l2, this function also implements some message filtering.
+ * E.g. we do not forward:
+ * - uplink messages
+ * - messages with a wrong arfcn
+ * - if in MS_STATE_IDLE_SEARCHING
  */
-void gsmtapl1_rx_from_virt_um(struct msgb *msg)
+void gsmtapl1_rx_from_virt_um_inst_cb(struct virt_um_inst *vui,
+				      struct msgb *msg)
 {
-	gsmtapl1_rx_from_virt_um_inst_cb(l1_model_ms->vui, msg);
+	struct l1ctl_sock_inst *lsi = vui->priv;
+	struct l1ctl_sock_client *lsc;
+
+	if (!msg)
+		return;
+
+	struct gsmtap_hdr *gh = msgb_l1(msg);
+	uint32_t fn = ntohl(gh->frame_number);	/* frame number of the rcv msg */
+	uint16_t arfcn = ntohs(gh->arfcn);	/* arfcn of the received msg */
+	uint8_t gsmtap_chantype = gh->sub_type;	/* gsmtap channel type */
+	uint8_t snr = gh->snr_db;	/* signal noise ratio */
+	uint8_t subslot = gh->sub_slot;	/* multiframe subslot to send msg in (tch -> 0-26, bcch/ccch -> 0-51) */
+	uint8_t timeslot = gh->timeslot;	/* tdma timeslot to send in (0-7) */
+	uint8_t rsl_chantype;	/* rsl chan type (8.58, 9.3.1) */
+	uint8_t link_id;	/* rsl link id tells if this is an ssociated or dedicated link */
+	uint8_t chan_nr;	/* encoded rsl channel type, timeslot and mf subslot */
+
+	msg->l2h = msgb_pull(msg, sizeof(*gh));
+	chantype_gsmtap2rsl(gsmtap_chantype, &rsl_chantype, &link_id);
+	/* see TS 08.58 -> 9.3.1 for channel number encoding */
+	chan_nr = rsl_enc_chan_nr(rsl_chantype, subslot, timeslot);
+
+	/* generally ignore all uplink messages received */
+	if (arfcn & GSMTAP_ARFCN_F_UPLINK) {
+		LOGP(DVIRPHY, LOGL_NOTICE, "Ignoring gsmtap msg from virt um - uplink flag set!\n");
+		goto freemsg;
+	}
+
+	DEBUGP(DVIRPHY, "Receiving gsmtap msg from virt um - "
+	       "(arfcn=%u, framenumber=%u, type=%s, subtype=%s, timeslot=%u, subslot=%u, rsl_chan_type=0x%2x, link_id=0x%2x, chan_nr=0x%2x)\n",
+	       arfcn, fn, get_value_string(gsmtap_type_names, gh->type),
+	       get_value_string(gsmtap_gsm_channel_names, gsmtap_chantype), timeslot,
+	       subslot, rsl_chantype, link_id, chan_nr);
+
+	/* dispatch the incoming DL message from GSMTAP to each of the registered L1CTL instances */
+	llist_for_each_entry(lsc, &lsi->clients, list) {
+		l1ctl_from_virt_um(lsc, msg, fn, arfcn, timeslot, subslot, gsmtap_chantype,
+				   chan_nr, link_id, snr);
+	}
+
+freemsg:
+	talloc_free(msg);
 }
diff --git a/src/host/virt_phy/src/l1ctl_sap.c b/src/host/virt_phy/src/l1ctl_sap.c
index 8965ac1..be5557d 100644
--- a/src/host/virt_phy/src/l1ctl_sap.c
+++ b/src/host/virt_phy/src/l1ctl_sap.c
@@ -39,15 +39,13 @@
 #include <virtphy/logging.h>
 #include <virtphy/virt_l1_sched.h>
 
-static struct l1_model_ms *l1_model_ms = NULL;
-
-static void l1_model_tch_mode_set(uint8_t tch_mode)
+static void l1_model_tch_mode_set(struct l1_model_ms *ms, uint8_t tch_mode)
 {
 	if (tch_mode == GSM48_CMODE_SPEECH_V1 || tch_mode == GSM48_CMODE_SPEECH_EFR)
-		l1_model_ms->state.tch_mode = tch_mode;
+		ms->state.tch_mode = tch_mode;
 	else {
 		/* set default value if no proper mode was assigned by l23 */
-		l1_model_ms->state.tch_mode = GSM48_CMODE_SIGN;
+		ms->state.tch_mode = GSM48_CMODE_SIGN;
 	}
 }
 
@@ -56,11 +54,7 @@
  */
 void l1ctl_sap_init(struct l1_model_ms *model)
 {
-	l1_model_ms = model;
-	prim_rach_init(model);
-	prim_fbsb_init(model);
-	prim_data_init(model);
-	prim_traffic_init(model);
+	INIT_LLIST_HEAD(&model->state.sched.mframe_items);
 	prim_pm_init(model);
 }
 
@@ -69,20 +63,15 @@
  *
  * Enqueues the message into the rx queue.
  */
-void l1ctl_sap_rx_from_l23_inst_cb(struct l1ctl_sock_inst *lsi, struct msgb *msg)
+void l1ctl_sap_rx_from_l23_inst_cb(struct l1ctl_sock_client *lsc, struct msgb *msg)
 {
+	struct l1_model_ms *ms = lsc->priv;
 	/* check if the received msg is not empty */
-	if (msg) {
-		DEBUGP(DL1C, "Message incoming from layer 2: %s\n", osmo_hexdump(msg->data, msg->len));
-		l1ctl_sap_handler(msg);
-	}
-}
-/**
- * @see l1ctl_sap_rx_from_l23_cb(struct l1ctl_sock_inst *lsi, struct msgb *msg).
- */
-void l1ctl_sap_rx_from_l23(struct msgb *msg)
-{
-	l1ctl_sap_rx_from_l23_inst_cb(l1_model_ms->lsi, msg);
+	if (!msg)
+		return;
+
+	DEBUGP(DL1C, "Message incoming from layer 2: %s\n", osmo_hexdump(msg->data, msg->len));
+	l1ctl_sap_handler(ms, msg);
 }
 
 /**
@@ -90,19 +79,11 @@
  *
  * This will forward the message as it is to the upper layer.
  */
-void l1ctl_sap_tx_to_l23_inst(struct l1ctl_sock_inst *lsi, struct msgb *msg)
+void l1ctl_sap_tx_to_l23_inst(struct l1_model_ms *ms, struct msgb *msg)
 {
 	/* prepend 16bit length before sending */
 	msgb_push_u16(msg, msg->len);
-	l1ctl_sock_write_msg(lsi, msg);
-}
-
-/**
- * @see void l1ctl_sap_tx_to_l23(struct l1ctl_sock_inst *lsi, struct msgb *msg).
- */
-void l1ctl_sap_tx_to_l23(struct msgb *msg)
-{
-	l1ctl_sap_tx_to_l23_inst(l1_model_ms->lsi, msg);
+	l1ctl_sock_write_msg(ms->lsc, msg);
 }
 
 /**
@@ -170,7 +151,7 @@
  *
  * This handler will call the specific routine dependent on the L1CTL message type.
  */
-void l1ctl_sap_handler(struct msgb *msg)
+void l1ctl_sap_handler(struct l1_model_ms *ms, struct msgb *msg)
 {
 	struct l1ctl_hdr *l1h;
 
@@ -186,49 +167,49 @@
 
 	switch (l1h->msg_type) {
 	case L1CTL_FBSB_REQ:
-		l1ctl_rx_fbsb_req(msg);
+		l1ctl_rx_fbsb_req(ms, msg);
 		break;
 	case L1CTL_DM_EST_REQ:
-		l1ctl_rx_dm_est_req(msg);
+		l1ctl_rx_dm_est_req(ms, msg);
 		break;
 	case L1CTL_DM_REL_REQ:
-		l1ctl_rx_dm_rel_req(msg);
+		l1ctl_rx_dm_rel_req(ms, msg);
 		break;
 	case L1CTL_PARAM_REQ:
-		l1ctl_rx_param_req(msg);
+		l1ctl_rx_param_req(ms, msg);
 		break;
 	case L1CTL_DM_FREQ_REQ:
-		l1ctl_rx_dm_freq_req(msg);
+		l1ctl_rx_dm_freq_req(ms,msg);
 		break;
 	case L1CTL_CRYPTO_REQ:
-		l1ctl_rx_crypto_req(msg);
+		l1ctl_rx_crypto_req(ms, msg);
 		break;
 	case L1CTL_RACH_REQ:
-		l1ctl_rx_rach_req(msg);
+		l1ctl_rx_rach_req(ms, msg);
 		goto exit_nofree;
 	case L1CTL_DATA_REQ:
-		l1ctl_rx_data_req(msg);
+		l1ctl_rx_data_req(ms, msg);
 		goto exit_nofree;
 	case L1CTL_PM_REQ:
-		l1ctl_rx_pm_req(msg);
+		l1ctl_rx_pm_req(ms, msg);
 		break;
 	case L1CTL_RESET_REQ:
-		l1ctl_rx_reset_req(msg);
+		l1ctl_rx_reset_req(ms, msg);
 		break;
 	case L1CTL_CCCH_MODE_REQ:
-		l1ctl_rx_ccch_mode_req(msg);
+		l1ctl_rx_ccch_mode_req(ms, msg);
 		break;
 	case L1CTL_TCH_MODE_REQ:
-		l1ctl_rx_tch_mode_req(msg);
+		l1ctl_rx_tch_mode_req(ms, msg);
 		break;
 	case L1CTL_NEIGH_PM_REQ:
-		l1ctl_rx_neigh_pm_req(msg);
+		l1ctl_rx_neigh_pm_req(ms, msg);
 		break;
 	case L1CTL_TRAFFIC_REQ:
-		l1ctl_rx_traffic_req(msg);
+		l1ctl_rx_traffic_req(ms, msg);
 		goto exit_nofree;
 	case L1CTL_SIM_REQ:
-		l1ctl_rx_sim_req(msg);
+		l1ctl_rx_sim_req(ms, msg);
 		break;
 	}
 
@@ -255,7 +236,7 @@
  * Handle state change from idle to dedicated mode.
  *
  */
-void l1ctl_rx_dm_est_req(struct msgb *msg)
+void l1ctl_rx_dm_est_req(struct l1_model_ms *ms, struct msgb *msg)
 {
 	struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data;
 	struct l1ctl_info_ul *ul = (struct l1ctl_info_ul *) l1h->data;
@@ -267,16 +248,16 @@
 	DEBUGP(DL1C, "Received and handled from l23 - L1CTL_DM_EST_REQ (chan_nr=0x%02x, tn=%u, ss=%u)\n",
 		ul->chan_nr, timeslot, subslot);
 
-	l1_model_ms->state.dedicated.chan_type = rsl_chantype;
-	l1_model_ms->state.dedicated.tn = timeslot;
-	l1_model_ms->state.dedicated.subslot = subslot;
-	l1_model_ms->state.state = MS_STATE_DEDICATED;
+	ms->state.dedicated.chan_type = rsl_chantype;
+	ms->state.dedicated.tn = timeslot;
+	ms->state.dedicated.subslot = subslot;
+	ms->state.state = MS_STATE_DEDICATED;
 
 	/* TCH config */
 	if (rsl_chantype == RSL_CHAN_Bm_ACCHs || rsl_chantype == RSL_CHAN_Lm_ACCHs) {
-		l1_model_ms->state.tch_mode = est_req->tch_mode;
-		l1_model_tch_mode_set(est_req->tch_mode);
-		l1_model_ms->state.audio_mode = est_req->audio_mode;
+		ms->state.tch_mode = est_req->tch_mode;
+		l1_model_tch_mode_set(ms, est_req->tch_mode);
+		ms->state.audio_mode = est_req->audio_mode;
 		/* TODO: configure audio hardware for encoding /
 		 * decoding / recording / playing voice */
 	}
@@ -293,7 +274,7 @@
  *
  * Note: Not needed for virtual physical layer as freqency hopping is generally disabled.
  */
-void l1ctl_rx_dm_freq_req(struct msgb *msg)
+void l1ctl_rx_dm_freq_req(struct l1_model_ms *ms, struct msgb *msg)
 {
 	struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data;
 	struct l1ctl_info_ul *ul = (struct l1ctl_info_ul *) l1h->data;
@@ -317,7 +298,7 @@
  * TODO: Implement cryptographic operations for virtual um!
  * TODO: Implement this handler routine!
  */
-void l1ctl_rx_crypto_req(struct msgb *msg)
+void l1ctl_rx_crypto_req(struct l1_model_ms *ms, struct msgb *msg)
 {
 	struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data;
 	struct l1ctl_info_ul *ul = (struct l1ctl_info_ul *) l1h->data;
@@ -332,8 +313,8 @@
 		return;
 	}
 
-	l1_model_ms->state.crypto_inf.algo = cr->algo;
-	memcpy(l1_model_ms->state.crypto_inf.key, cr->key, sizeof(uint8_t) * A5_KEY_LEN);
+	ms->state.crypto_inf.algo = cr->algo;
+	memcpy(ms->state.crypto_inf.key, cr->key, sizeof(uint8_t) * A5_KEY_LEN);
 }
 
 /**
@@ -346,15 +327,15 @@
  * Handle state change from dedicated to idle mode. Flush message buffers of dedicated channel.
  *
  */
-void l1ctl_rx_dm_rel_req(struct msgb *msg)
+void l1ctl_rx_dm_rel_req(struct l1_model_ms *ms, struct msgb *msg)
 {
 	DEBUGP(DL1C, "Received and handled from l23 - L1CTL_DM_REL_REQ\n");
 
-	l1_model_ms->state.dedicated.chan_type = 0;
-	l1_model_ms->state.dedicated.tn = 0;
-	l1_model_ms->state.dedicated.subslot = 0;
-	l1_model_ms->state.tch_mode = GSM48_CMODE_SIGN;
-	l1_model_ms->state.state = MS_STATE_IDLE_CAMPING;
+	ms->state.dedicated.chan_type = 0;
+	ms->state.dedicated.tn = 0;
+	ms->state.dedicated.subslot = 0;
+	ms->state.tch_mode = GSM48_CMODE_SIGN;
+	ms->state.state = MS_STATE_IDLE_CAMPING;
 
 	/* TODO: disable ciphering */
 	/* TODO: disable audio recording / playing */
@@ -371,7 +352,7 @@
  *
  * Note: Not needed for virtual physical layer.
  */
-void l1ctl_rx_param_req(struct msgb *msg)
+void l1ctl_rx_param_req(struct l1_model_ms *ms, struct msgb *msg)
 {
 	struct l1ctl_hdr *l1h = (struct l1ctl_hdr *)msg->data;
 	struct l1ctl_info_ul *ul = (struct l1ctl_info_ul *)l1h->data;
@@ -394,7 +375,7 @@
  * to just tell l2 that we are rdy.
  *
  */
-void l1ctl_rx_reset_req(struct msgb *msg)
+void l1ctl_rx_reset_req(struct l1_model_ms *ms, struct msgb *msg)
 {
 	struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data;
 	struct l1ctl_reset *reset_req = (struct l1ctl_reset *) l1h->data;
@@ -402,14 +383,14 @@
 	switch (reset_req->type) {
 	case L1CTL_RES_T_FULL:
 		DEBUGP(DL1C, "Received and handled from l23 - L1CTL_RESET_REQ (type=FULL)\n");
-		l1_model_ms->state.state = MS_STATE_IDLE_SEARCHING;
-		virt_l1_sched_stop();
-		l1ctl_tx_reset(L1CTL_RESET_CONF, reset_req->type);
+		ms->state.state = MS_STATE_IDLE_SEARCHING;
+		virt_l1_sched_stop(ms);
+		l1ctl_tx_reset(ms, L1CTL_RESET_CONF, reset_req->type);
 		break;
 	case L1CTL_RES_T_SCHED:
-		virt_l1_sched_restart(l1_model_ms->state.downlink_time);
+		virt_l1_sched_restart(ms, ms->state.downlink_time);
 		DEBUGP(DL1C, "Received and handled from l23 - L1CTL_RESET_REQ (type=SCHED)\n");
-		l1ctl_tx_reset(L1CTL_RESET_CONF, reset_req->type);
+		l1ctl_tx_reset(ms, L1CTL_RESET_CONF, reset_req->type);
 		break;
 	default:
 		LOGP(DL1C, LOGL_ERROR, "Received and ignored from l23 - L1CTL_RESET_REQ (type=unknown)\n");
@@ -430,7 +411,7 @@
  *
  * TODO: Implement this handler routine!
  */
-void l1ctl_rx_ccch_mode_req(struct msgb *msg)
+void l1ctl_rx_ccch_mode_req(struct l1_model_ms *ms, struct msgb *msg)
 {
 	struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data;
 	struct l1ctl_ccch_mode_req *ccch_mode_req = (struct l1ctl_ccch_mode_req *) l1h->data;
@@ -438,10 +419,10 @@
 
 	DEBUGP(DL1C, "Received and handled from l23 - L1CTL_CCCH_MODE_REQ\n");
 
-	l1_model_ms->state.serving_cell.ccch_mode = ccch_mode;
+	ms->state.serving_cell.ccch_mode = ccch_mode;
 
 	/* check if more has to be done here */
-	l1ctl_tx_ccch_mode_conf(ccch_mode);
+	l1ctl_tx_ccch_mode_conf(ms, ccch_mode);
 }
 
 /**
@@ -455,20 +436,20 @@
  *
  * TODO: Implement this handler routine!
  */
-void l1ctl_rx_tch_mode_req(struct msgb *msg)
+void l1ctl_rx_tch_mode_req(struct l1_model_ms *ms, struct msgb *msg)
 {
 	struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data;
 	struct l1ctl_tch_mode_req *tch_mode_req = (struct l1ctl_tch_mode_req *) l1h->data;
 
-	l1_model_tch_mode_set(tch_mode_req->tch_mode);
-	l1_model_ms->state.audio_mode = tch_mode_req->audio_mode;
+	l1_model_tch_mode_set(ms, tch_mode_req->tch_mode);
+	ms->state.audio_mode = tch_mode_req->audio_mode;
 
 	DEBUGP(DL1C, "Received and handled from l23 - L1CTL_TCH_MODE_REQ (tch_mode=0x%02x audio_mode=0x%02x)\n",
 		tch_mode_req->tch_mode, tch_mode_req->audio_mode);
 
 	/* TODO: configure audio hardware for encoding / decoding / recording / playing voice */
 
-	l1ctl_tx_tch_mode_conf(l1_model_ms->state.tch_mode, l1_model_ms->state.audio_mode);
+	l1ctl_tx_tch_mode_conf(ms, ms->state.tch_mode, ms->state.audio_mode);
 }
 
 /**
@@ -484,7 +465,7 @@
  *
  * Note: Not needed for virtual physical layer as we dont maintain neigbors.
  */
-void l1ctl_rx_neigh_pm_req(struct msgb *msg)
+void l1ctl_rx_neigh_pm_req(struct l1_model_ms *ms, struct msgb *msg)
 {
 	struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data;
 	struct l1ctl_neigh_pm_req *pm_req = (struct l1ctl_neigh_pm_req *) l1h->data;
@@ -512,7 +493,7 @@
  *  ki comp128 <xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx xx>
  * --------
  */
-void l1ctl_rx_sim_req(struct msgb *msg)
+void l1ctl_rx_sim_req(struct l1_model_ms *ms, struct msgb *msg)
 {
 	uint16_t len = msg->len - sizeof(struct l1ctl_hdr);
 	uint8_t *data = msg->data + sizeof(struct l1ctl_hdr);
@@ -536,7 +517,7 @@
  * @param [in] msg_type L1CTL primitive message type.
  * @param [in] reset_type reset type (full, boot or just scheduler reset).
  */
-void l1ctl_tx_reset(uint8_t msg_type, uint8_t reset_type)
+void l1ctl_tx_reset(struct l1_model_ms *ms, uint8_t msg_type, uint8_t reset_type)
 {
 	struct msgb *msg = l1ctl_msgb_alloc(msg_type);
 	struct l1ctl_reset *reset_resp = (struct l1ctl_reset *) msgb_put(msg, sizeof(*reset_resp));
@@ -544,7 +525,7 @@
 	reset_resp->type = reset_type;
 	DEBUGP(DL1C, "Sending to l23 - %s (reset_type: %u)\n", getL1ctlPrimName(msg_type), reset_type);
 
-	l1ctl_sap_tx_to_l23(msg);
+	l1ctl_sap_tx_to_l23_inst(ms, msg);
 }
 
 /**
@@ -556,7 +537,7 @@
  *
  * Called by layer 1 to inform layer 2 that the ccch mode was successfully changed.
  */
-void l1ctl_tx_ccch_mode_conf(uint8_t ccch_mode)
+void l1ctl_tx_ccch_mode_conf(struct l1_model_ms *ms, uint8_t ccch_mode)
 {
 	struct msgb *msg = l1ctl_msgb_alloc(L1CTL_CCCH_MODE_CONF);
 	struct l1ctl_ccch_mode_conf *mode_conf;
@@ -565,7 +546,7 @@
 	mode_conf->ccch_mode = ccch_mode;
 
 	DEBUGP(DL1C, "Sending to l23 - L1CTL_CCCH_MODE_CONF (mode: %u)\n", ccch_mode);
-	l1ctl_sap_tx_to_l23(msg);
+	l1ctl_sap_tx_to_l23_inst(ms, msg);
 }
 
 /**
@@ -578,7 +559,7 @@
  *
  * Called by layer 1 to inform layer 23 that the traffic channel mode was successfully changed.
  */
-void l1ctl_tx_tch_mode_conf(uint8_t tch_mode, uint8_t audio_mode)
+void l1ctl_tx_tch_mode_conf(struct l1_model_ms *ms, uint8_t tch_mode, uint8_t audio_mode)
 {
 	struct msgb *msg = l1ctl_msgb_alloc(L1CTL_TCH_MODE_CONF);
 	struct l1ctl_tch_mode_conf *mode_conf;
@@ -589,7 +570,7 @@
 
 	DEBUGP(DL1C, "Sending to l23 - L1CTL_TCH_MODE_CONF (tch_mode: %u, audio_mode: %u)\n",
 		tch_mode, audio_mode);
-	l1ctl_sap_tx_to_l23(msg);
+	l1ctl_sap_tx_to_l23_inst(ms, msg);
 }
 
 /**
diff --git a/src/host/virt_phy/src/l1ctl_sock.c b/src/host/virt_phy/src/l1ctl_sock.c
index 370d3f3..d654c21 100644
--- a/src/host/virt_phy/src/l1ctl_sock.c
+++ b/src/host/virt_phy/src/l1ctl_sock.c
@@ -1,6 +1,7 @@
 /* Socket based Layer1 <-> Layer23 communication over L1CTL primitives. */
 
 /* (C) 2016 by Sebastian Stumpf <sebastian.stumpf87 at googlemail.com>
+ * (C) 2017 by Harald Welte <laforge at gnumonks.org>
  *
  * All Rights Reserved
  *
@@ -32,23 +33,24 @@
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <sys/stat.h>
+#include <arpa/inet.h>
 
 #include <osmocom/core/linuxlist.h>
 #include <osmocom/core/select.h>
-#include <osmocom/core/serial.h>
 #include <osmocom/core/talloc.h>
-#include <osmocom/core/timer.h>
 #include <osmocom/core/socket.h>
 
-#include <arpa/inet.h>
-
-#include <l1ctl_proto.h>
-
-#include <virtphy/virtual_um.h>
 #include <virtphy/l1ctl_sock.h>
 #include <virtphy/logging.h>
 
 #define L1CTL_SOCK_MSGB_SIZE	256
+
+static void l1ctl_client_destroy(struct l1ctl_sock_client *lsc)
+{
+	osmo_fd_close(&lsc->ofd);
+	llist_del(&lsc->list);
+	talloc_free(lsc);
+}
 
 /**
  * @brief L1CTL socket file descriptor callback function.
@@ -60,7 +62,7 @@
  */
 static int l1ctl_sock_data_cb(struct osmo_fd *ofd, unsigned int what)
 {
-	struct l1ctl_sock_inst *lsi = ofd->data;
+	struct l1ctl_sock_client *lsc = ofd->data;
 	struct l1ctl_hdr *l1h;
 	struct msgb *msg;
 	uint16_t len;
@@ -87,44 +89,67 @@
 		msgb_put(msg, rc);
 		l1h = (void *) msgb_data(msg);
 		msg->l1h = (void *) l1h;
-		lsi->recv_cb(lsi, msg);
+		lsc->l1ctl_sock->recv_cb(lsc, msg);
 		return 0;
 	}
 err_close:
 	perror("Failed to receive msg from l2. Connection will be closed.\n");
-	l1ctl_sock_disconnect(lsi);
+	l1ctl_client_destroy(lsc);
 
 	return 0;
 
 }
 
+/* called for the master (listening) socket of the instance, allocates a new client */
 static int l1ctl_sock_accept_cb(struct osmo_fd *ofd, unsigned int what)
 {
 
 	struct l1ctl_sock_inst *lsi = ofd->data;
-	int fd;
+	struct l1ctl_sock_client *lsc;
+	int fd, rc;
 
 	fd = accept(ofd->fd, NULL, NULL);
 	if (fd < 0) {
 		fprintf(stderr, "Failed to accept connection to l2.\n");
 		return -1;
 	}
+	printf("Accepted client (fd=%u) from server (fd=%u)\n", fd, ofd->fd);
 
-	lsi->connection.fd = fd;
-	lsi->connection.when = BSC_FD_READ;
-	lsi->connection.cb = l1ctl_sock_data_cb;
-	lsi->connection.data = lsi;
-
-	if (osmo_fd_register(&lsi->connection) != 0) {
-		fprintf(stderr, "Failed to register the l2 connection fd.\n");
+	lsc = talloc_zero(lsi, struct l1ctl_sock_client);
+	if (!lsc) {
+		close(fd);
+		fprintf(stderr, "Failed to allocate L1CTL client\n");
 		return -1;
 	}
+
+	lsc->l1ctl_sock = lsi;
+	lsc->ofd.fd = fd;
+	lsc->ofd.when = BSC_FD_READ;
+	lsc->ofd.cb = l1ctl_sock_data_cb;
+	lsc->ofd.data = lsc;
+	if (lsi->accept_cb) {
+		rc = lsi->accept_cb(lsc);
+		if (rc < 0) {
+			talloc_free(lsc);
+			close(fd);
+			return rc;
+		}
+	}
+
+	printf("Accepted L1CTL connection, lsc=%p, lsc->priv=%p\n", lsc, lsc->priv);
+	if (osmo_fd_register(&lsc->ofd) != 0) {
+		fprintf(stderr, "Failed to register the l2 connection fd.\n");
+		talloc_free(lsc);
+		return -1;
+	}
+	llist_add_tail(&lsc->list, &lsi->clients);
 	return 0;
 }
 
 struct l1ctl_sock_inst *l1ctl_sock_init(
                 void *ctx,
-                void (*recv_cb)(struct l1ctl_sock_inst *lsi, struct msgb *msg),
+                void (*recv_cb)(struct l1ctl_sock_client *lsc, struct msgb *msg),
+                int (*accept_cb)(struct l1ctl_sock_client *lsc),
                 char *path)
 {
 	struct l1ctl_sock_inst *lsi;
@@ -146,40 +171,28 @@
 	}
 
 	lsi->recv_cb = recv_cb;
-	/* no connection -> invalid filedescriptor and not 0 (==std_in) */
-	lsi->connection.fd = -1;
+	lsi->accept_cb = accept_cb;
 	lsi->l1ctl_sock_path = path;
-
-	osmo_fd_register(&lsi->ofd);
+	INIT_LLIST_HEAD(&lsi->clients);
 
 	return lsi;
 }
 
 void l1ctl_sock_destroy(struct l1ctl_sock_inst *lsi)
 {
-	struct osmo_fd *ofd = &lsi->ofd;
+	struct l1ctl_sock_client *lsc, *lsc2;
 
-	osmo_fd_unregister(ofd);
-	close(ofd->fd);
-	ofd->fd = -1;
-	ofd->when = 0;
+	llist_for_each_entry_safe(lsc, lsc2, &lsi->clients, list)
+		l1ctl_client_destroy(lsc);
 
+	osmo_fd_close(&lsi->ofd);
 	talloc_free(lsi);
 }
 
-void l1ctl_sock_disconnect(struct l1ctl_sock_inst *lsi)
-{
-	struct osmo_fd *ofd = &lsi->connection;
-	osmo_fd_unregister(ofd);
-	close(ofd->fd);
-	ofd->fd = -1;
-	ofd->when = 0;
-}
-
-int l1ctl_sock_write_msg(struct l1ctl_sock_inst *lsi, struct msgb *msg)
+int l1ctl_sock_write_msg(struct l1ctl_sock_client *lsc, struct msgb *msg)
 {
 	int rc;
-	rc = write(lsi->connection.fd, msgb_data(msg), msgb_length(msg));
+	rc = write(lsc->ofd.fd, msgb_data(msg), msgb_length(msg));
 	msgb_free(msg);
 	return rc;
 }
diff --git a/src/host/virt_phy/src/virt_l1_model.c b/src/host/virt_phy/src/virt_l1_model.c
index 056c9eb..da0c314 100644
--- a/src/host/virt_phy/src/virt_l1_model.c
+++ b/src/host/virt_phy/src/virt_l1_model.c
@@ -19,17 +19,24 @@
  */
 
 #include <virtphy/virt_l1_model.h>
+#include <virtphy/l1ctl_sap.h>
 #include <talloc.h>
 
-struct l1_model_ms* l1_model_ms_init(void *ctx)
+struct l1_model_ms *l1_model_ms_init(void *ctx, struct l1ctl_sock_client *lsc, struct virt_um_inst *vui)
 {
 	struct l1_model_ms *model = talloc_zero(ctx, struct l1_model_ms);
+	if (!model)
+		return NULL;
+
+	model->lsc = lsc;
+	model->vui = vui;
+
+	l1ctl_sap_init(model);
+
 	return model;
 }
 
 void l1_model_ms_destroy(struct l1_model_ms *model)
 {
-	virt_um_destroy(model->vui);
-	l1ctl_sock_destroy(model->lsi);
 	talloc_free(model);
 }
diff --git a/src/host/virt_phy/src/virt_l1_sched_simple.c b/src/host/virt_phy/src/virt_l1_sched_simple.c
index 20481df..7d1cdd4 100644
--- a/src/host/virt_phy/src/virt_l1_sched_simple.c
+++ b/src/host/virt_phy/src/virt_l1_sched_simple.c
@@ -1,4 +1,5 @@
 /* (C) 2016 by Sebastian Stumpf <sebastian.stumpf87 at googlemail.com>
+ * (C) 2017 by Harald Welte <laforge at gnumonks.org>
  *
  * All Rights Reserved
  *
@@ -24,55 +25,41 @@
 #include <time.h>
 #include <talloc.h>
 
-static struct l1_model_ms *l1_model_ms = NULL;
-
-static LLIST_HEAD(mframe_item_list);
-
-static uint32_t last_exec_fn = 0;
-
 /**
- * @brief Initialize schedulers data structures.
+ * @brief Start scheduler thread based on current gsm time from model
  */
-void virt_l1_sched_init(struct l1_model_ms *model)
+static int virt_l1_sched_start(struct l1_model_ms *ms, struct gsm_time time)
 {
-	l1_model_ms = model;
+	virt_l1_sched_sync_time(ms, time, 1);
+	return 0;
 }
 
 /**
  * @brief Clear scheduler queue and completely restart scheduler.
  */
-int virt_l1_sched_restart(struct gsm_time time)
+int virt_l1_sched_restart(struct l1_model_ms *ms, struct gsm_time time)
 {
-	virt_l1_sched_stop();
-	return virt_l1_sched_start(time);
-}
-
-/**
- * @brief Start scheduler thread based on current gsm time from model
- */
-int virt_l1_sched_start(struct gsm_time time)
-{
-	virt_l1_sched_sync_time(time, 1);
-	return 0;
+	virt_l1_sched_stop(ms);
+	return virt_l1_sched_start(ms, time);
 }
 
 /**
  * @brief Sync scheduler with given time.
  */
-void virt_l1_sched_sync_time(struct gsm_time time, uint8_t hard_reset)
+void virt_l1_sched_sync_time(struct l1_model_ms *ms, struct gsm_time time, uint8_t hard_reset)
 {
-	l1_model_ms->state.current_time = time;
+	ms->state.current_time = time;
 }
 
 /**
  * @brief Stop the scheduler thread and cleanup mframe items queue
  */
-void virt_l1_sched_stop()
+void virt_l1_sched_stop(struct l1_model_ms *ms)
 {
 	struct virt_l1_sched_mframe_item *mi_next, *mi_tmp;
 
 	/* Empty tdma and mframe sched items lists */
-	llist_for_each_entry_safe(mi_next, mi_tmp, &mframe_item_list, mframe_item_entry) {
+	llist_for_each_entry_safe(mi_next, mi_tmp, &ms->state.sched.mframe_items, mframe_item_entry) {
 		struct virt_l1_sched_tdma_item *ti_next, *ti_tmp;
 
 		llist_for_each_entry_safe(ti_next, ti_tmp, &mi_next->tdma_item_list, tdma_item_entry) {
@@ -87,14 +74,15 @@
 /**
  * @brief Handle all pending scheduled items for the current frame number.
  */
-void virt_l1_sched_execute(uint32_t fn)
+void virt_l1_sched_execute(struct l1_model_ms *ms, uint32_t fn)
 {
+	struct l1_state_ms *l1s = &ms->state;
 	struct virt_l1_sched_mframe_item *mi_next, *mi_tmp;
-	uint8_t hyperframe_restart = fn < last_exec_fn;
+	uint8_t hyperframe_restart = fn < l1s->sched.last_exec_fn;
 
-	llist_for_each_entry_safe(mi_next, mi_tmp, &mframe_item_list, mframe_item_entry) {
+	llist_for_each_entry_safe(mi_next, mi_tmp, &l1s->sched.mframe_items, mframe_item_entry) {
 		/* execute all registered handler for current mf sched item */
-		uint8_t exec_now = mi_next->fn <= fn || (hyperframe_restart && mi_next->fn > last_exec_fn);
+		uint8_t exec_now = mi_next->fn <= fn || (hyperframe_restart && mi_next->fn > l1s->sched.last_exec_fn);
 		/* break loop, as we have an ordered list in case the hyperframe had not been reset */
 		uint8_t break_now = mi_next->fn > fn && !hyperframe_restart;
 
@@ -106,7 +94,7 @@
 				/* exec tdma sched item's handler callback */
 				/* TODO: we do not have a TDMA scheduler currently and execute
 				 * all scheduled tdma items here at once */
-				ti_next->handler_cb(mi_next->fn, ti_next->msg);
+				ti_next->handler_cb(ms, mi_next->fn, ti_next->msg);
 				/* remove handled tdma sched item */
 				llist_del(&ti_next->tdma_item_entry);
 			}
@@ -118,19 +106,19 @@
 		if (break_now)
 			break;
 	}
-	last_exec_fn = fn;
+	l1s->sched.last_exec_fn = fn;
 }
 
 /**
  * @brief Schedule a msg to the given framenumber and timeslot.
  */
-void virt_l1_sched_schedule(struct msgb * msg, uint32_t fn, uint8_t ts,
-                            virt_l1_sched_cb * handler_cb)
+void virt_l1_sched_schedule(struct l1_model_ms *ms, struct msgb *msg, uint32_t fn, uint8_t ts,
+                            virt_l1_sched_cb *handler_cb)
 {
 	struct virt_l1_sched_mframe_item *mi_next = NULL, *mi_tmp = NULL, *mi_fn = NULL;
 	struct virt_l1_sched_tdma_item *ti_new = NULL;
 
-	llist_for_each_entry_safe(mi_next, mi_tmp, &mframe_item_list, mframe_item_entry) {
+	llist_for_each_entry_safe(mi_next, mi_tmp, &ms->state.sched.mframe_items, mframe_item_entry) {
 		if (mi_next->fn == fn) {
 			mi_fn = mi_next;
 			break;
diff --git a/src/host/virt_phy/src/virt_prim_data.c b/src/host/virt_phy/src/virt_prim_data.c
index 72c58b7..4b07ca6 100644
--- a/src/host/virt_phy/src/virt_prim_data.c
+++ b/src/host/virt_phy/src/virt_prim_data.c
@@ -1,7 +1,7 @@
 /* Layer 1 normal data burst uplink handling and scheduling */
 
 /* (C) 2010 by Dieter Spaar <spaar at mirider.augusta.de>
- * (C) 2010 by Harald Welte <laforge at gnumonks.org>
+ * (C) 2010,2017 by Harald Welte <laforge at gnumonks.org>
  * (C) 2016 by Sebastian Stumpf <sebastian.stumpf87 at googlemail.com>
  *
  * All Rights Reserved
@@ -38,19 +38,16 @@
 
 #include <l1ctl_proto.h>
 
-static struct l1_model_ms *l1_model_ms = NULL;
-static void virt_l1_sched_handler_cb(uint32_t fn, struct msgb * msg);
-
 /**
  * @brief Handler callback function for DATA request.
  *
  * @param [in] fn frame number
  * @param [in] msg the msg to sent over virtual um.
  */
-static void virt_l1_sched_handler_cb(uint32_t fn, struct msgb * msg)
+static void virt_l1_sched_handler_cb(struct l1_model_ms *ms, uint32_t fn, struct msgb * msg)
 {
-	gsmtapl1_tx_to_virt_um(fn, msg);
-	l1ctl_tx_data_conf(fn, 0, l1_model_ms->state.serving_cell.arfcn);
+	gsmtapl1_tx_to_virt_um_inst(ms, fn, msg);
+	l1ctl_tx_data_conf(ms, fn, 0, ms->state.serving_cell.arfcn);
 }
 
 /**
@@ -64,13 +61,13 @@
  *
  * TODO: Check if a msg on FACCH needs special handling.
  */
-void l1ctl_rx_data_req(struct msgb *msg)
+void l1ctl_rx_data_req(struct l1_model_ms *ms, struct msgb *msg)
 {
 	struct l1ctl_hdr *l1h = (struct l1ctl_hdr *)msg->data;
 	struct l1ctl_info_ul *ul = (struct l1ctl_info_ul *)l1h->data;
 	struct l1ctl_data_ind *data_ind = (struct l1ctl_data_ind *)ul->payload;
 	uint8_t rsl_chantype, subslot, timeslot;
-	uint32_t fn_sched = sched_fn_ul(l1_model_ms->state.current_time,
+	uint32_t fn_sched = sched_fn_ul(ms->state.current_time,
 	                                ul->chan_nr, ul->link_id);
 
 	rsl_dec_chan_nr(ul->chan_nr, &rsl_chantype, &subslot, &timeslot);
@@ -81,10 +78,10 @@
 
 	msg->l2h = data_ind->data;
 
-	virt_l1_sched_schedule(msg, fn_sched, timeslot, &virt_l1_sched_handler_cb);
+	virt_l1_sched_schedule(ms, msg, fn_sched, timeslot, &virt_l1_sched_handler_cb);
 }
 
-void l1ctl_tx_data_ind(struct msgb *msg, uint16_t arfcn, uint8_t link_id,
+void l1ctl_tx_data_ind(struct l1_model_ms *ms, struct msgb *msg, uint16_t arfcn, uint8_t link_id,
                        uint8_t chan_nr, uint32_t fn, uint8_t snr,
                        uint8_t signal_dbm, uint8_t num_biterr, uint8_t fire_crc)
 {
@@ -111,7 +108,7 @@
 	memcpy(l1di->data, msgb_data(msg), msgb_length(msg));
 
 	DEBUGP(DL1C, "Sending signaling-data to l23.\n");
-	l1ctl_sap_tx_to_l23(l1ctl_msg);
+	l1ctl_sap_tx_to_l23_inst(ms, l1ctl_msg);
 }
 
 /**
@@ -122,20 +119,10 @@
  * @param [in] arfcn arfcn of the cell the message was send on
  *
  */
-void l1ctl_tx_data_conf(uint32_t fn, uint16_t snr, uint16_t arfcn)
+void l1ctl_tx_data_conf(struct l1_model_ms *ms, uint32_t fn, uint16_t snr, uint16_t arfcn)
 {
 	struct msgb * l1ctl_msg;
 	l1ctl_msg = l1ctl_create_l2_msg(L1CTL_DATA_CONF, fn, snr, arfcn);
 	/* send confirm to layer23 */
-	l1ctl_sap_tx_to_l23(l1ctl_msg);
-}
-
-/**
- * @brief Initialize virtual prim data.
- *
- * @param [in] model the l1 model instance
- */
-void prim_data_init(struct l1_model_ms *model)
-{
-	l1_model_ms = model;
+	l1ctl_sap_tx_to_l23_inst(ms, l1ctl_msg);
 }
diff --git a/src/host/virt_phy/src/virt_prim_fbsb.c b/src/host/virt_phy/src/virt_prim_fbsb.c
index 7f99821..59fda35 100644
--- a/src/host/virt_phy/src/virt_prim_fbsb.c
+++ b/src/host/virt_phy/src/virt_prim_fbsb.c
@@ -1,6 +1,6 @@
 
 /* (C) 2010 by Dieter Spaar <spaar at mirider.augusta.de>
- * (C) 2010 by Harald Welte <laforge at gnumonks.org>
+ * (C) 2010,2017 by Harald Welte <laforge at gnumonks.org>
  * (C) 2016 by Sebastian Stumpf <sebastian.stumpf87 at googlemail.com>
  *
  * All Rights Reserved
@@ -35,7 +35,6 @@
 #include <virtphy/logging.h>
 #include <l1ctl_proto.h>
 
-static struct l1_model_ms *l1_model_ms = NULL;
 static uint16_t sync_count = 0;
 
 /**
@@ -52,9 +51,9 @@
  * Note: virt bts does not broadcast freq and sync bursts.
  *
  */
-void l1ctl_rx_fbsb_req(struct msgb *msg)
+void l1ctl_rx_fbsb_req(struct l1_model_ms *ms, struct msgb *msg)
 {
-	struct l1_state_ms *l1s = &l1_model_ms->state;
+	struct l1_state_ms *l1s = &ms->state;
 	struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data;
 	struct l1ctl_fbsb_req *sync_req = (struct l1ctl_fbsb_req *) l1h->data;
 
@@ -70,22 +69,21 @@
  *
  * Note: for virtual layer 1 this can be a random downlink message, as we can parse the fn from the gsmtap header.
  */
-void prim_fbsb_sync(struct msgb *msg)
+void prim_fbsb_sync(struct l1_model_ms *ms, struct msgb *msg)
 {
-	struct l1_state_ms *l1s = &l1_model_ms->state;
+	struct l1_state_ms *l1s = &ms->state;
 	struct gsmtap_hdr *gh = msgb_l1(msg);
 	uint32_t fn = ntohl(gh->frame_number); /* frame number of the rcv msg */
 	uint16_t arfcn = ntohs(gh->arfcn); /* arfcn of the received msg */
 
 	/* ignore messages from other arfcns as the one requested to sync to by l23 */
 	if (l1s->fbsb.arfcn != arfcn) {
-		talloc_free(msg);
 		/* cancel sync if we did not receive a msg on dl from
 		 * the requested arfcn that we can sync to */
 		if (sync_count++ > 20) {
 			sync_count = 0;
 			l1s->state = MS_STATE_IDLE_SEARCHING;
-			l1ctl_tx_fbsb_conf(1, (l1s->fbsb.arfcn));
+			l1ctl_tx_fbsb_conf(ms, 1, (l1s->fbsb.arfcn));
 		}
 		return;
 	}
@@ -98,9 +96,8 @@
 	/* Update current gsm time each time we receive a message on the virt um */
 	gsm_fn2gsmtime(&l1s->downlink_time, fn);
 	/* Restart scheduler */
-	virt_l1_sched_restart(l1s->downlink_time);
-	talloc_free(msg);
-	l1ctl_tx_fbsb_conf(0, arfcn);
+	virt_l1_sched_restart(ms, l1s->downlink_time);
+	l1ctl_tx_fbsb_conf(ms, 0, arfcn);
 }
 
 /**
@@ -113,7 +110,7 @@
  *
  * No calculation needed for virtual pyh -> uses dummy values for a good link quality.
  */
-void l1ctl_tx_fbsb_conf(uint8_t res, uint16_t arfcn)
+void l1ctl_tx_fbsb_conf(struct l1_model_ms *ms, uint8_t res, uint16_t arfcn)
 {
 	struct msgb *msg;
 	struct l1ctl_fbsb_conf *resp;
@@ -131,14 +128,5 @@
 
 	DEBUGP(DL1C, "Sending to l23 - %s (res: %u)\n", getL1ctlPrimName(L1CTL_FBSB_CONF), res);
 
-	l1ctl_sap_tx_to_l23(msg);
-}
-/**
- * @brief Initialize virtual prim pm.
- *
- * @param [in] model the l1 model instance
- */
-void prim_fbsb_init(struct l1_model_ms *model)
-{
-	l1_model_ms = model;
+	l1ctl_sap_tx_to_l23_inst(ms, msg);
 }
diff --git a/src/host/virt_phy/src/virt_prim_pm.c b/src/host/virt_phy/src/virt_prim_pm.c
index 6335c61..96b7f07 100644
--- a/src/host/virt_phy/src/virt_prim_pm.c
+++ b/src/host/virt_phy/src/virt_prim_pm.c
@@ -1,6 +1,6 @@
 
 /* (C) 2010 by Dieter Spaar <spaar at mirider.augusta.de>
- * (C) 2010 by Harald Welte <laforge at gnumonks.org>
+ * (C) 2010,2017 by Harald Welte <laforge at gnumonks.org>
  * (C) 2016 by Sebastian Stumpf <sebastian.stumpf87 at googlemail.com>
  *
  * All Rights Reserved
@@ -35,8 +35,6 @@
 #include <virtphy/logging.h>
 #include <l1ctl_proto.h>
 
-static struct l1_model_ms *l1_model_ms = NULL;
-
 /**
  * @brief Change the signal strength for a given arfcn.
  *
@@ -45,9 +43,9 @@
  * @param [in] arfcn to change sig str for.
  * @param [in] sig_lev the measured signal level value.
  */
-uint16_t prim_pm_set_sig_strength(uint16_t arfcn, int16_t sig_lev)
+uint16_t prim_pm_set_sig_strength(struct l1_model_ms *ms, uint16_t arfcn, int16_t sig_lev)
 {
-	struct l1_state_ms *l1s = &l1_model_ms->state;
+	struct l1_state_ms *l1s = &ms->state;
 
 	if (l1s->pm.timeout_s > 0 || l1s->pm.timeout_us > 0) {
 		osmo_timer_schedule(&l1s->pm.meas.arfcn_sig_lev_timers[arfcn],
@@ -81,9 +79,9 @@
  * Note: This should only be called after a certain time so some
  * messages have already been received.
  */
-void l1ctl_rx_pm_req(struct msgb *msg)
+void l1ctl_rx_pm_req(struct l1_model_ms *ms, struct msgb *msg)
 {
-	struct l1_state_ms *l1s = &l1_model_ms->state;
+	struct l1_state_ms *l1s = &ms->state;
 	struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data;
 	struct l1ctl_pm_req *pm_req = (struct l1ctl_pm_req *) l1h->data;
 	struct msgb *resp_msg = l1ctl_msgb_alloc(L1CTL_PM_CONF);
@@ -110,13 +108,13 @@
 		}
 		/* no more space to hold mor pm info in msgb, flush to l23 */
 		if (msgb_tailroom(resp_msg) < sizeof(*pm_conf)) {
-			l1ctl_sap_tx_to_l23(resp_msg);
+			l1ctl_sap_tx_to_l23_inst(ms, resp_msg);
 			resp_msg = l1ctl_msgb_alloc(L1CTL_PM_CONF);
 		}
 	}
 	/* transmit the remaining part of pm response to l23 */
 	if (resp_msg)
-		l1ctl_sap_tx_to_l23(resp_msg);
+		l1ctl_sap_tx_to_l23_inst(ms, resp_msg);
 }
 
 /**
@@ -129,7 +127,6 @@
 	struct l1_state_ms *l1s = &model->state;
 	int i;
 
-	l1_model_ms = model;
 	/* init the signal level of all arfcns with the lowest value possible */
 	memset(l1s->pm.meas.arfcn_sig_lev_dbm, MIN_SIG_LEV_DBM, sizeof (int16_t) * 1024);
 	/* init timers */
diff --git a/src/host/virt_phy/src/virt_prim_rach.c b/src/host/virt_phy/src/virt_prim_rach.c
index bff777d..88e8206 100644
--- a/src/host/virt_phy/src/virt_prim_rach.c
+++ b/src/host/virt_phy/src/virt_prim_rach.c
@@ -1,7 +1,7 @@
 /* Layer 1 Random Access Channel Burst */
 
 /* (C) 2010 by Dieter Spaar <spaar at mirider.augusta.de>
- * (C) 2010 by Harald Welte <laforge at gnumonks.org>
+ * (C) 2010,2017 by Harald Welte <laforge at gnumonks.org>
  * (C) 2016 by Sebastian Stumpf <sebastian.stumpf87 at googlemail.com>
  *
  * All Rights Reserved
@@ -38,9 +38,6 @@
 
 #include <l1ctl_proto.h>
 
-static struct l1_model_ms *l1_model_ms = NULL;
-static void virt_l1_sched_handler_cb(uint32_t fn, struct msgb * msg);
-
 /* use if we have a combined uplink (RACH, SDCCH, ...) (see
  * http://www.rfwireless-world.com/Terminology/GSM-combined-channel-configuration.html)
  * if we have no combined channel config, uplink consists of only RACH * */
@@ -59,10 +56,10 @@
  *
  * @param [in] msg the msg to sent over virtual um.
  */
-static void virt_l1_sched_handler_cb(uint32_t fn, struct msgb *msg)
+static void virt_l1_sched_handler_cb(struct l1_model_ms *ms, uint32_t fn, struct msgb *msg)
 {
-	gsmtapl1_tx_to_virt_um(fn, msg);
-	l1ctl_tx_rach_conf(fn, l1_model_ms->state.serving_cell.arfcn);
+	gsmtapl1_tx_to_virt_um_inst(ms, fn, msg);
+	l1ctl_tx_rach_conf(ms, fn, ms->state.serving_cell.arfcn);
 }
 
 /**
@@ -75,9 +72,9 @@
  * Transmit RACH request on RACH. Refer to 04.08 - 9.1.8 - Channel request.
  *
  */
-void l1ctl_rx_rach_req(struct msgb *msg)
+void l1ctl_rx_rach_req(struct l1_model_ms *ms, struct msgb *msg)
 {
-	struct l1_state_ms *l1s = &l1_model_ms->state;
+	struct l1_state_ms *l1s = &ms->state;
 	struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data;
 	struct l1ctl_info_ul *ul = (struct l1ctl_info_ul *) l1h->data;
 	struct l1ctl_rach_req *rach_req = (struct l1ctl_rach_req *) ul->payload;
@@ -111,7 +108,7 @@
 	} else
 		fn_sched = l1s->current_time.fn + offset;
 
-	virt_l1_sched_schedule(msg, fn_sched, ts, &virt_l1_sched_handler_cb);
+	virt_l1_sched_schedule(ms, msg, fn_sched, ts, &virt_l1_sched_handler_cb);
 }
 
 /**
@@ -122,21 +119,11 @@
  * @param [in] fn the fn on which the rach was sent
  * @param [in] arfcn arfcn on which the rach was sent
  */
-void l1ctl_tx_rach_conf(uint32_t fn, uint16_t arfcn)
+void l1ctl_tx_rach_conf(struct l1_model_ms *ms, uint32_t fn, uint16_t arfcn)
 {
-	struct msgb * msg = l1ctl_create_l2_msg(L1CTL_RACH_CONF, fn, 0, arfcn);
+	struct msgb *msg = l1ctl_create_l2_msg(L1CTL_RACH_CONF, fn, 0, arfcn);
 
 	DEBUGP(DL1C, "Sending to l23 - %s (fn: %u, arfcn: %u)\n",
 	       getL1ctlPrimName(L1CTL_RACH_CONF), fn, arfcn);
-	l1ctl_sap_tx_to_l23(msg);
-}
-
-/**
- * @brief Initialize virtual prim rach.
- *
- * @param [in] model the l1 model instance
- */
-void prim_rach_init(struct l1_model_ms *model)
-{
-	l1_model_ms = model;
+	l1ctl_sap_tx_to_l23_inst(ms, msg);
 }
diff --git a/src/host/virt_phy/src/virt_prim_traffic.c b/src/host/virt_phy/src/virt_prim_traffic.c
index acfd493..9e87db9 100644
--- a/src/host/virt_phy/src/virt_prim_traffic.c
+++ b/src/host/virt_phy/src/virt_prim_traffic.c
@@ -1,7 +1,7 @@
 /* Layer 1 normal data burst tx handling */
 
 /* (C) 2010 by Dieter Spaar <spaar at mirider.augusta.de>
- * (C) 2010 by Harald Welte <laforge at gnumonks.org>
+ * (C) 2010,2017 by Harald Welte <laforge at gnumonks.org>
  * (C) 2016 by Sebastian Stumpf <sebastian.stumpf87 at googlemail.com>
  *
  * All Rights Reserved
@@ -38,19 +38,16 @@
 
 #include <l1ctl_proto.h>
 
-static struct l1_model_ms *l1_model_ms = NULL;
-static void virt_l1_sched_handler_cb(uint32_t fn, struct msgb * msg);
-
 /**
  * @brief Handler callback function for TRAFFIC request.
  *
  * @param [in] fn frame number
  * @param [in] msg the msg to sent over virtual um.
  */
-static void virt_l1_sched_handler_cb(uint32_t fn, struct msgb * msg)
+static void virt_l1_sched_handler_cb(struct l1_model_ms *ms, uint32_t fn, struct msgb * msg)
 {
-	gsmtapl1_tx_to_virt_um(fn, msg);
-	l1ctl_tx_traffic_conf(fn, 0, l1_model_ms->state.serving_cell.arfcn);
+	gsmtapl1_tx_to_virt_um_inst(ms, fn, msg);
+	l1ctl_tx_traffic_conf(ms, fn, 0, ms->state.serving_cell.arfcn);
 }
 
 /**
@@ -63,26 +60,25 @@
  * Enqueue the message (traffic frame) to the L1 state machine's transmit queue. In virtual layer1 just submit it to the virt um.
  *
  */
-void l1ctl_rx_traffic_req(struct msgb *msg)
+void l1ctl_rx_traffic_req(struct l1_model_ms *ms, struct msgb *msg)
 {
 	struct l1ctl_hdr *l1h = (struct l1ctl_hdr *) msg->data;
 	struct l1ctl_info_ul *ul = (struct l1ctl_info_ul *) l1h->data;
 	struct l1ctl_traffic_req *tr = (struct l1ctl_traffic_req *) ul->payload;
 	uint8_t rsl_chantype, subslot, timeslot;
-	uint32_t fn_sched = sched_fn_ul(l1_model_ms->state.current_time, ul->chan_nr, ul->link_id);
+	uint32_t fn_sched = sched_fn_ul(ms->state.current_time, ul->chan_nr, ul->link_id);
 
 	rsl_dec_chan_nr(ul->chan_nr, &rsl_chantype, &subslot, &timeslot);
 	DEBUGP(DL1C, "Received and handled from l23 - L1CTL_TRAFFIC_REQ\n");
 
 	msg->l2h = tr->data;
 
-	virt_l1_sched_schedule(msg, fn_sched, timeslot, &virt_l1_sched_handler_cb);
+	virt_l1_sched_schedule(ms, msg, fn_sched, timeslot, &virt_l1_sched_handler_cb);
 }
 
-void l1ctl_tx_traffic_ind(struct msgb *msg, uint16_t arfcn, uint8_t link_id,
-                          uint8_t chan_nr, uint32_t fn, uint8_t snr,
-                          uint8_t signal_dbm, uint8_t num_biterr,
-                          uint8_t fire_crc)
+void l1ctl_tx_traffic_ind(struct l1_model_ms *ms, struct msgb *msg, uint16_t arfcn, uint8_t link_id,
+			  uint8_t chan_nr, uint32_t fn, uint8_t snr, uint8_t signal_dbm,
+			  uint8_t num_biterr, uint8_t fire_crc)
 {
 	struct msgb *l1ctl_msg = NULL;
 	struct l1ctl_traffic_ind * l1ti;
@@ -107,7 +103,7 @@
 
 	memcpy(l1ti->data, msgb_data(msg), msgb_length(msg));
 	DEBUGP(DL1C, "Sending to l23 - L1CTL_TRAFFIC_IND\n");
-	l1ctl_sap_tx_to_l23(l1ctl_msg);
+	l1ctl_sap_tx_to_l23_inst(ms, l1ctl_msg);
 }
 
 /**
@@ -118,20 +114,10 @@
  * @param [in] arfcn arfcn of the cell the message was send on
  *
  */
-void l1ctl_tx_traffic_conf(uint32_t fn, uint16_t snr, uint16_t arfcn)
+void l1ctl_tx_traffic_conf(struct l1_model_ms *ms, uint32_t fn, uint16_t snr, uint16_t arfcn)
 {
 	struct msgb * l1ctl_msg;
 	l1ctl_msg = l1ctl_create_l2_msg(L1CTL_TRAFFIC_CONF, fn, snr, arfcn);
 	/* send confirm to layer23 */
-	l1ctl_sap_tx_to_l23(l1ctl_msg);
-}
-
-/**
- * @brief Initialize virtual prim traffic.
- *
- * @param [in] model the l1 model instance
- */
-void prim_traffic_init(struct l1_model_ms *model)
-{
-	l1_model_ms = model;
+	l1ctl_sap_tx_to_l23_inst(ms, l1ctl_msg);
 }
diff --git a/src/host/virt_phy/src/virtphy.c b/src/host/virt_phy/src/virtphy.c
index 28dd685..c7e11ca 100644
--- a/src/host/virt_phy/src/virtphy.c
+++ b/src/host/virt_phy/src/virtphy.c
@@ -1,6 +1,7 @@
 /* osmocom includes */
 
 /* (C) 2016 by Sebastian Stumpf <sebastian.stumpf87 at googlemail.com>
+ * (C) 2017 by Harald Welte <laforge at gnumonks.org>
  *
  * All Rights Reserved
  *
@@ -27,6 +28,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <getopt.h>
+#include <errno.h>
 #include <virtphy/virtual_um.h>
 #include <virtphy/l1ctl_sock.h>
 #include <virtphy/virt_l1_model.h>
@@ -36,6 +38,17 @@
 #include <virtphy/virt_l1_sched.h>
 
 #define DEFAULT_LOG_MASK "DL1C,1:DVIRPHY,1"
+
+/* this exists once in the program, and contains the state that we
+ * only keep once:  L1CTL server socket, GSMTAP/VirtUM socket */
+struct virtphy_context {
+	/* L1CTL socket server */
+	struct l1ctl_sock_inst *l1ctl_sock;
+	/* Virtual Um layer based on GSMTAP multicast */
+	struct virt_um_inst *virt_um;
+};
+
+static struct virtphy_context g_vphy;
 
 static char *dl_rx_grp = DEFAULT_MS_MCAST_GROUP;
 static char *ul_tx_grp = DEFAULT_BTS_MCAST_GROUP;
@@ -132,31 +145,38 @@
 	} while ((token = strtok(NULL, ":")));
 }
 
+/* create a new l1_model_ms instance when L1CTL socket accept()s new connection */
+static int l1ctl_accept_cb(struct l1ctl_sock_client *lsc)
+{
+	struct l1_model_ms *ms = l1_model_ms_init(lsc, lsc, g_vphy.virt_um);
+
+	if (!ms)
+		return -ENOMEM;
+
+	/* apply timeout and arfcn reduction value config to model */
+	parse_pm_timeout(ms, pm_timeout);
+	parse_arfcn_sig_lev_red(ms, arfcn_sig_lev_red_mask);
+
+	lsc->priv = ms;
+
+	return 0;
+}
+
 int main(int argc, char *argv[])
 {
 	/* init loginfo */
-	static struct l1_model_ms *model;
-
 	handle_options(argc, argv);
 
 	ms_log_init(log_mask);
 
 	LOGP(DVIRPHY, LOGL_INFO, "Virtual physical layer starting up...\n");
 
-	model = l1_model_ms_init(NULL);
+	g_vphy.virt_um = virt_um_init(NULL, ul_tx_grp, port, dl_rx_grp, port,
+					gsmtapl1_rx_from_virt_um_inst_cb);
 
-	model->vui = virt_um_init(NULL, ul_tx_grp, port, dl_rx_grp, port,
-	                          gsmtapl1_rx_from_virt_um_inst_cb);
-	model->lsi = l1ctl_sock_init(NULL, l1ctl_sap_rx_from_l23_inst_cb,
-	                             l1ctl_sock_path);
-
-	gsmtapl1_init(model);
-	l1ctl_sap_init(model);
-	virt_l1_sched_init(model);
-
-	/* apply timeout and arfcn reduction value config to model */
-	parse_pm_timeout(model, pm_timeout);
-	parse_arfcn_sig_lev_red(model, arfcn_sig_lev_red_mask);
+	g_vphy.l1ctl_sock = l1ctl_sock_init(NULL, l1ctl_sap_rx_from_l23_inst_cb,
+					    l1ctl_accept_cb, l1ctl_sock_path);
+	g_vphy.virt_um->priv = g_vphy.l1ctl_sock;
 
 	LOGP(DVIRPHY, LOGL_INFO,
 	     "Virtual physical layer ready...\n \
@@ -168,7 +188,8 @@
 		osmo_select_main(0);
 	}
 
-	l1_model_ms_destroy(model);
+	l1ctl_sock_destroy(g_vphy.l1ctl_sock);
+	virt_um_destroy(g_vphy.virt_um);
 
 	/* not reached */
 	return EXIT_FAILURE;

-- 
To view, visit https://gerrit.osmocom.org/3286
To unsubscribe, visit https://gerrit.osmocom.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: Ibfb2a93f8b45a95215c01368b1a52d92283474e6
Gerrit-PatchSet: 1
Gerrit-Project: osmocom-bb
Gerrit-Branch: master
Gerrit-Owner: Harald Welte <laforge at gnumonks.org>



More information about the gerrit-log mailing list