diff mbox

[1/7] TRX: factor out the scheduler from remaining code

Message ID 1454523846-13022-1-git-send-email-laforge@gnumonks.org
State Superseded
Headers show

Commit Message

Harald Welte Feb. 3, 2016, 6:24 p.m. UTC
From: Harald Welte <laforge@gnumonks.org>

The L1 scheduler is a generally useful component that is unfortunately
tied quite a bit into the OsmoTRX support.  Let's try to separate it out
by having separate per-trx/per-ts/per-chan data structures pre-fixed
with l1sched_

Using this patch it should be one step easier to use the scheduler for
other BTS models, such as the intended upcoming virtual BTS.
---
 src/osmo-bts-trx/l1_if.c     |  35 ++--
 src/osmo-bts-trx/l1_if.h     | 121 +----------
 src/osmo-bts-trx/loops.c     |  83 ++++----
 src/osmo-bts-trx/loops.h     |  14 +-
 src/osmo-bts-trx/scheduler.c | 486 +++++++++++++++++++++++--------------------
 src/osmo-bts-trx/scheduler.h | 143 ++++++++++++-
 src/osmo-bts-trx/trx_if.c    |   2 +-
 7 files changed, 473 insertions(+), 411 deletions(-)
diff mbox

Patch

diff --git a/src/osmo-bts-trx/l1_if.c b/src/osmo-bts-trx/l1_if.c
index a45ba05..70a5c59 100644
--- a/src/osmo-bts-trx/l1_if.c
+++ b/src/osmo-bts-trx/l1_if.c
@@ -68,9 +68,10 @@  struct trx_l1h *l1if_open(struct gsm_bts_trx *trx)
 	if (!l1h)
 		return NULL;
 	l1h->trx = trx;
+	l1h->l1s.trx = trx;
 	trx->role_bts.l1h = l1h;
 
-	trx_sched_init(l1h);
+	trx_sched_init(&l1h->l1s);
 
 	rc = trx_if_open(l1h);
 	if (rc < 0) {
@@ -89,7 +90,7 @@  err:
 void l1if_close(struct trx_l1h *l1h)
 {
 	trx_if_close(l1h);
-	trx_sched_exit(l1h);
+	trx_sched_exit(&l1h->l1s);
 	talloc_free(l1h);
 }
 
@@ -267,7 +268,7 @@  int bts_model_trx_close(struct gsm_bts_trx *trx)
 	enum gsm_phys_chan_config pchan = trx->ts[0].pchan;
 
 	/* close all logical channels and reset timeslots */
-	trx_sched_reset(l1h);
+	trx_sched_reset(&l1h->l1s);
 
 	/* deactivate lchan for CCCH */
 	if (pchan == GSM_PCHAN_CCCH || pchan == GSM_PCHAN_CCCH_SDCCH4) {
@@ -374,8 +375,12 @@  static uint8_t trx_set_ts(struct gsm_bts_trx_ts *ts)
 		l1if_provision_transceiver_trx(l1h);
 	}
 
+	/* ignore disabled slots */
+	if (!(l1h->config.slotmask & (1 << tn)))
+		return NM_NACK_RES_NOTAVAIL;
+
 	/* set physical channel */
-	rc = trx_sched_set_pchan(l1h, tn, pchan);
+	rc = trx_sched_set_pchan(&l1h->l1s, tn, pchan);
 	if (rc)
 		return NM_NACK_RES_NOTAVAIL;
 
@@ -413,17 +418,17 @@  static int l1if_set_ciphering(struct trx_l1h *l1h, struct gsm_lchan *lchan,
 
 	if (!downlink) {
 		/* set uplink */
-		trx_sched_set_cipher(l1h, chan_nr, 0, lchan->encr.alg_id - 1,
+		trx_sched_set_cipher(&l1h->l1s, chan_nr, 0, lchan->encr.alg_id - 1,
 			lchan->encr.key, lchan->encr.key_len);
 		lchan->ciph_state = LCHAN_CIPH_RX_CONF;
 	} else {
 		/* set downlink and also set uplink, if not already */
 		if (lchan->ciph_state != LCHAN_CIPH_RX_CONF) {
-			trx_sched_set_cipher(l1h, chan_nr, 0,
+			trx_sched_set_cipher(&l1h->l1s, chan_nr, 0,
 				lchan->encr.alg_id - 1, lchan->encr.key,
 				lchan->encr.key_len);
 		}
-		trx_sched_set_cipher(l1h, chan_nr, 1, lchan->encr.alg_id - 1,
+		trx_sched_set_cipher(&l1h->l1s, chan_nr, 1, lchan->encr.alg_id - 1,
 			lchan->encr.key, lchan->encr.key_len);
 		lchan->ciph_state = LCHAN_CIPH_RXTX_CONF;
 	}
@@ -510,12 +515,12 @@  int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
 		if (!msg)
 			break;
 		/* put data into scheduler's queue */
-		return trx_sched_ph_data_req(l1h, l1sap);
+		return trx_sched_ph_data_req(&l1h->l1s, l1sap);
 	case OSMO_PRIM(PRIM_TCH, PRIM_OP_REQUEST):
 		if (!msg)
 			break;
 		/* put data into scheduler's queue */
-		return trx_sched_tch_req(l1h, l1sap);
+		return trx_sched_tch_req(&l1h->l1s, l1sap);
 	case OSMO_PRIM(PRIM_MPH_INFO, PRIM_OP_REQUEST):
 		switch (l1sap->u.info.type) {
 		case PRIM_INFO_ACT_CIPH:
@@ -542,11 +547,11 @@  int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
 					break;
 				}
 				/* activate dedicated channel */
-				trx_sched_set_lchan(l1h, chan_nr, 0x00, 1);
+				trx_sched_set_lchan(&l1h->l1s, chan_nr, 0x00, 1);
 				/* activate associated channel */
-				trx_sched_set_lchan(l1h, chan_nr, 0x40, 1);
+				trx_sched_set_lchan(&l1h->l1s, chan_nr, 0x40, 1);
 				/* set mode */
-				trx_sched_set_mode(l1h, chan_nr,
+				trx_sched_set_mode(&l1h->l1s, chan_nr,
 					lchan->rsl_cmode, lchan->tch_mode,
 					lchan->tch.amr_mr.num_modes,
 					lchan->tch.amr_mr.bts_mode[0].mode,
@@ -574,7 +579,7 @@  int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
 			}
 			if (l1sap->u.info.type == PRIM_INFO_MODIFY) {
 				/* change mode */
-				trx_sched_set_mode(l1h, chan_nr,
+				trx_sched_set_mode(&l1h->l1s, chan_nr,
 					lchan->rsl_cmode, lchan->tch_mode,
 					lchan->tch.amr_mr.num_modes,
 					lchan->tch.amr_mr.bts_mode[0].mode,
@@ -591,12 +596,12 @@  int bts_model_l1sap_down(struct gsm_bts_trx *trx, struct osmo_phsap_prim *l1sap)
 				break;
 			}
 			/* deactivate associated channel */
-			trx_sched_set_lchan(l1h, chan_nr, 0x40, 0);
+			trx_sched_set_lchan(&l1h->l1s, chan_nr, 0x40, 0);
 			if (!l1sap->u.info.u.act_req.sacch_only) {
 				/* set lchan inactive */
 				lchan_set_state(lchan, LCHAN_S_NONE);
 				/* deactivate dedicated channel */
-				trx_sched_set_lchan(l1h, chan_nr, 0x00, 0);
+				trx_sched_set_lchan(&l1h->l1s, chan_nr, 0x00, 0);
 				/* confirm only on dedicated channel */
 				mph_info_chan_confirm(l1h, chan_nr,
 					PRIM_INFO_DEACTIVATE, 0);
diff --git a/src/osmo-bts-trx/l1_if.h b/src/osmo-bts-trx/l1_if.h
index 2c672cb..6dec273 100644
--- a/src/osmo-bts-trx/l1_if.h
+++ b/src/osmo-bts-trx/l1_if.h
@@ -1,109 +1,7 @@ 
 #ifndef L1_IF_H_TRX
 #define L1_IF_H_TRX
 
-/* These types define the different channels on a multiframe.
- * Each channel has queues and can be activated individually.
- */
-enum trx_chan_type {
-	TRXC_IDLE = 0,
-	TRXC_FCCH,
-	TRXC_SCH,
-	TRXC_BCCH,
-	TRXC_RACH,
-	TRXC_CCCH,
-	TRXC_TCHF,
-	TRXC_TCHH_0,
-	TRXC_TCHH_1,
-	TRXC_SDCCH4_0,
-	TRXC_SDCCH4_1,
-	TRXC_SDCCH4_2,
-	TRXC_SDCCH4_3,
-	TRXC_SDCCH8_0,
-	TRXC_SDCCH8_1,
-	TRXC_SDCCH8_2,
-	TRXC_SDCCH8_3,
-	TRXC_SDCCH8_4,
-	TRXC_SDCCH8_5,
-	TRXC_SDCCH8_6,
-	TRXC_SDCCH8_7,
-	TRXC_SACCHTF,
-	TRXC_SACCHTH_0,
-	TRXC_SACCHTH_1,
-	TRXC_SACCH4_0,
-	TRXC_SACCH4_1,
-	TRXC_SACCH4_2,
-	TRXC_SACCH4_3,
-	TRXC_SACCH8_0,
-	TRXC_SACCH8_1,
-	TRXC_SACCH8_2,
-	TRXC_SACCH8_3,
-	TRXC_SACCH8_4,
-	TRXC_SACCH8_5,
-	TRXC_SACCH8_6,
-	TRXC_SACCH8_7,
-	TRXC_PDTCH,
-	TRXC_PTCCH,
-	_TRX_CHAN_MAX
-};
-
-/* States each channel on a multiframe */
-struct trx_chan_state {
-	/* scheduler */
-	uint8_t			active;		/* Channel is active */
-	ubit_t			*dl_bursts;	/* burst buffer for TX */
-	sbit_t			*ul_bursts;	/* burst buffer for RX */
-	uint32_t		ul_first_fn;	/* fn of first burst */
-	uint8_t			ul_mask;	/* mask of received bursts */
-
-	/* RSSI / TOA */
-	uint8_t			rssi_num;	/* number of RSSI values */
-	float			rssi_sum;	/* sum of RSSI values */
-	uint8_t			toa_num;	/* number of TOA values */
-	float			toa_sum;	/* sum of TOA values */
-
-	/* loss detection */
-	uint8_t			lost;		/* (SACCH) loss detection */
-
-	/* mode */
-	uint8_t			rsl_cmode, tch_mode; /* mode for TCH channels */
-
-	/* AMR */
-	uint8_t			codec[4];	/* 4 possible codecs for amr */
-	int			codecs;		/* number of possible codecs */
-	float			ber_sum;	/* sum of bit error rates */
-	int			ber_num;	/* number of bit error rates */
-	uint8_t			ul_ft;		/* current uplink FT index */
-	uint8_t			dl_ft;		/* current downlink FT index */
-	uint8_t			ul_cmr;		/* current uplink CMR index */
-	uint8_t			dl_cmr;		/* current downlink CMR index */
-	uint8_t			amr_loop;	/* if AMR loop is enabled */
-
-	/* TCH/H */
-	uint8_t			dl_ongoing_facch; /* FACCH/H on downlink */
-	uint8_t			ul_ongoing_facch; /* FACCH/H on uplink */
-
-	/* encryption */
-	int			ul_encr_algo;	/* A5/x encry algo downlink */
-	int			dl_encr_algo;	/* A5/x encry algo uplink */
-	int			ul_encr_key_len;
-	int			dl_encr_key_len;
-	uint8_t			ul_encr_key[MAX_A5_KEY_LEN];
-	uint8_t			dl_encr_key[MAX_A5_KEY_LEN];
-
-	/* measurements */
-	struct {
-		uint8_t		clock;		/* cyclic clock counter */
-		int8_t		rssi[32];	/* last RSSI values */
-		int		rssi_count;	/* received RSSI values */
-		int		rssi_valid_count; /* number of stored value */
-		int		rssi_got_burst; /* any burst received so far */
-		float		toa_sum;	/* sum of TOA values */
-		int		toa_num;	/* number of TOA value */
-	} meas;
-
-	/* handover */
-	uint8_t			ho_rach_detect;	/* if rach detection is on */
-};
+#include "scheduler.h"
 
 struct trx_config {
 	uint8_t			poweron;	/* poweron(1) or poweroff(0) */
@@ -152,16 +50,9 @@  struct trx_l1h {
 
 	/* transceiver config */
 	struct trx_config	config;
-
-	uint8_t			mf_index[TRX_NR_TS];	/* selected multiframe index */
-	uint32_t		mf_last_fn[TRX_NR_TS];	/* last received frame */
-	uint8_t			mf_period[TRX_NR_TS];	/* period of multiframe */
-	const struct trx_sched_frame *mf_frames[TRX_NR_TS];	/* pointer to frame layout */
-
-	/* Channel states for all channels on all timeslots */
-	struct trx_chan_state	chan_states[TRX_NR_TS][_TRX_CHAN_MAX];
-	struct llist_head	dl_prims[TRX_NR_TS];	/* Queue primitves for TX */
 	uint8_t			ho_rach_detect[TRX_NR_TS][TS_MAX_LCHAN];
+
+	struct l1sched_trx	l1s;
 };
 
 struct trx_l1h *l1if_open(struct gsm_bts_trx *trx);
@@ -176,4 +67,10 @@  void l1if_fill_meas_res(struct osmo_phsap_prim *l1sap, uint8_t chan_nr, float ta
 int l1if_process_meas_res(struct gsm_bts_trx *trx, uint8_t tn, uint32_t fn, uint8_t chan_nr,
 	int n_errors, int n_bits_total, float rssi, float toa);
 
+static inline struct l1sched_trx *trx_l1sched_hdl(struct gsm_bts_trx *trx)
+{
+	struct trx_l1h *l1h = trx_l1h_hdl(trx);
+	return &l1h->l1s;
+}
+
 #endif /* L1_IF_H_TRX */
diff --git a/src/osmo-bts-trx/loops.c b/src/osmo-bts-trx/loops.c
index 52ac170..8070e80 100644
--- a/src/osmo-bts-trx/loops.c
+++ b/src/osmo-bts-trx/loops.c
@@ -33,7 +33,7 @@ 
 #include "l1_if.h"
 #include "loops.h"
 
-#define MS_PWR_DBM(lvl) ms_pwr_dbm(gsm_arfcn2band(l1h->config.arfcn), lvl)
+#define MS_PWR_DBM(arfcn, lvl) ms_pwr_dbm(gsm_arfcn2band(arfcn), lvl)
 
 /*
  * MS Power loop
@@ -42,11 +42,12 @@ 
 int trx_ms_power_loop = 0;
 int8_t trx_target_rssi = -10;
 
-static int ms_power_diff(struct trx_l1h *l1h, struct gsm_lchan *lchan,
-	uint8_t chan_nr, struct trx_chan_state *chan_state, int8_t diff)
+static int ms_power_diff(struct gsm_lchan *lchan, uint8_t chan_nr, int8_t diff)
 {
+	struct gsm_bts_trx *trx = lchan->ts->trx;
+	uint16_t arfcn = trx->arfcn;
 	int8_t new_power;
-	
+
 	new_power = lchan->ms_power - (diff >> 1);
 
 	if (diff == 0)
@@ -56,7 +57,7 @@  static int ms_power_diff(struct trx_l1h *l1h, struct gsm_lchan *lchan,
 		new_power = 0;
 
 	// FIXME: to go above 1W, we need to know classmark of MS
-	if (l1h->config.arfcn >= 512 && l1h->config.arfcn <= 885) {
+	if (arfcn >= 512 && arfcn <= 885) {
 		if (new_power > 15)
 			new_power = 15;
 	} else {
@@ -73,8 +74,8 @@  static int ms_power_diff(struct trx_l1h *l1h, struct gsm_lchan *lchan,
 	if (lchan->ms_power == new_power) {
 		LOGP(DLOOP, LOGL_INFO, "Keeping MS new_power of trx=%u "
 			"chan_nr=0x%02x at control level %d (%d dBm)\n",
-			l1h->trx->nr, chan_nr, new_power,
-			MS_PWR_DBM(new_power));
+			trx->nr, chan_nr, new_power,
+			MS_PWR_DBM(arfcn, new_power));
 
 		return 0;
 	}
@@ -82,15 +83,16 @@  static int ms_power_diff(struct trx_l1h *l1h, struct gsm_lchan *lchan,
 	LOGP(DLOOP, LOGL_INFO, "%s MS new_power of trx=%u chan_nr=0x%02x from "
 		"control level %d (%d dBm) to %d (%d dBm)\n",
 		(diff > 0) ? "Raising" : "Lowering",
-		l1h->trx->nr, chan_nr, lchan->ms_power,
-		MS_PWR_DBM(lchan->ms_power), new_power, MS_PWR_DBM(new_power));
+		trx->nr, chan_nr, lchan->ms_power,
+		MS_PWR_DBM(arfcn, lchan->ms_power), new_power,
+		MS_PWR_DBM(arfcn, new_power));
 
 	lchan->ms_power = new_power;
 
 	return 0;
 }
 
-static int ms_power_val(struct trx_chan_state *chan_state, int8_t rssi)
+static int ms_power_val(struct l1sched_chan_state *chan_state, int8_t rssi)
 {
 	/* ignore inserted dummy frames, treat as lost frames */
 	if (rssi < -127)
@@ -112,9 +114,10 @@  static int ms_power_val(struct trx_chan_state *chan_state, int8_t rssi)
 	return 0;
 }
 
-static int ms_power_clock(struct trx_l1h *l1h, struct gsm_lchan *lchan,
-	uint8_t chan_nr, struct trx_chan_state *chan_state)
+static int ms_power_clock(struct gsm_lchan *lchan,
+	uint8_t chan_nr, struct l1sched_chan_state *chan_state)
 {
+	struct gsm_bts_trx *trx = lchan->ts->trx;
 	int rssi;
 	int i;
 
@@ -134,9 +137,8 @@  static int ms_power_clock(struct trx_l1h *l1h, struct gsm_lchan *lchan,
 	if (chan_state->meas.rssi_count == 0) {
 		LOGP(DLOOP, LOGL_NOTICE, "LOST SACCH frame of trx=%u "
 			"chan_nr=0x%02x, so we raise MS power\n",
-			l1h->trx->nr, chan_nr);
-		return ms_power_diff(l1h, lchan, chan_nr, chan_state,
-			MS_RAISE_MAX);
+			trx->nr, chan_nr);
+		return ms_power_diff(lchan, chan_nr, MS_RAISE_MAX);
 	}
 
 	/* reset total counter */
@@ -157,9 +159,10 @@  static int ms_power_clock(struct trx_l1h *l1h, struct gsm_lchan *lchan,
 	/* change RSSI */
 	LOGP(DLOOP, LOGL_DEBUG, "Lowest RSSI: %d Target RSSI: %d Current "
 		"MS power: %d (%d dBm) of trx=%u chan_nr=0x%02x\n", rssi,
-		trx_target_rssi, lchan->ms_power, MS_PWR_DBM(lchan->ms_power),
-		l1h->trx->nr, chan_nr);
-	ms_power_diff(l1h, lchan, chan_nr, chan_state, trx_target_rssi - rssi);
+		trx_target_rssi, lchan->ms_power,
+		MS_PWR_DBM(trx->arfcn, lchan->ms_power),
+		trx->nr, chan_nr);
+	ms_power_diff(lchan, chan_nr, trx_target_rssi - rssi);
 
 	return 0;
 }
@@ -171,9 +174,11 @@  static int ms_power_clock(struct trx_l1h *l1h, struct gsm_lchan *lchan,
 
 int trx_ta_loop = 1;
 
-int ta_val(struct trx_l1h *l1h, struct gsm_lchan *lchan, uint8_t chan_nr,
-	struct trx_chan_state *chan_state, float toa)
+int ta_val(struct gsm_lchan *lchan, uint8_t chan_nr,
+	struct l1sched_chan_state *chan_state, float toa)
 {
+	struct gsm_bts_trx *trx = lchan->ts->trx;
+
 	/* check if the current L1 header acks to the current ordered TA */
 	if (lchan->meas.l1_info[1] != lchan->rqd_ta)
 		return 0;
@@ -190,19 +195,19 @@  int ta_val(struct trx_l1h *l1h, struct gsm_lchan *lchan, uint8_t chan_nr,
 	if (toa < -0.9F && lchan->rqd_ta > 0) {
 		LOGP(DLOOP, LOGL_INFO, "TOA of trx=%u chan_nr=0x%02x is too "
 			"early (%.2f), now lowering TA from %d to %d\n",
-			l1h->trx->nr, chan_nr, toa, lchan->rqd_ta,
+			trx->nr, chan_nr, toa, lchan->rqd_ta,
 			lchan->rqd_ta - 1);
 		lchan->rqd_ta--;
 	} else if (toa > 0.9F && lchan->rqd_ta < 63) {
 		LOGP(DLOOP, LOGL_INFO, "TOA of trx=%u chan_nr=0x%02x is too "
 			"late (%.2f), now raising TA from %d to %d\n",
-			l1h->trx->nr, chan_nr, toa, lchan->rqd_ta,
+			trx->nr, chan_nr, toa, lchan->rqd_ta,
 			lchan->rqd_ta + 1);
 		lchan->rqd_ta++;
 	} else
 		LOGP(DLOOP, LOGL_INFO, "TOA of trx=%u chan_nr=0x%02x is "
 			"correct (%.2f), keeping current TA of %d\n",
-			l1h->trx->nr, chan_nr, toa, lchan->rqd_ta);
+			trx->nr, chan_nr, toa, lchan->rqd_ta);
 
 	chan_state->meas.toa_num = 0;
 	chan_state->meas.toa_sum = 0;
@@ -210,29 +215,29 @@  int ta_val(struct trx_l1h *l1h, struct gsm_lchan *lchan, uint8_t chan_nr,
 	return 0;
 }
 
-int trx_loop_sacch_input(struct trx_l1h *l1h, uint8_t chan_nr,
-	struct trx_chan_state *chan_state, int8_t rssi, float toa)
+int trx_loop_sacch_input(struct l1sched_trx *l1t, uint8_t chan_nr,
+	struct l1sched_chan_state *chan_state, int8_t rssi, float toa)
 {
-	struct gsm_lchan *lchan = &l1h->trx->ts[L1SAP_CHAN2TS(chan_nr)]
+	struct gsm_lchan *lchan = &l1t->trx->ts[L1SAP_CHAN2TS(chan_nr)]
 					.lchan[l1sap_chan2ss(chan_nr)];
 
 	if (trx_ms_power_loop)
 		ms_power_val(chan_state, rssi);
 
 	if (trx_ta_loop)
-		ta_val(l1h, lchan, chan_nr, chan_state, toa);
+		ta_val(lchan, chan_nr, chan_state, toa);
 
 	return 0;
 }
 
-int trx_loop_sacch_clock(struct trx_l1h *l1h, uint8_t chan_nr,
-	struct trx_chan_state *chan_state)
+int trx_loop_sacch_clock(struct l1sched_trx *l1t, uint8_t chan_nr,
+	struct l1sched_chan_state *chan_state)
 {
-	struct gsm_lchan *lchan = &l1h->trx->ts[L1SAP_CHAN2TS(chan_nr)]
+	struct gsm_lchan *lchan = &l1t->trx->ts[L1SAP_CHAN2TS(chan_nr)]
 					.lchan[l1sap_chan2ss(chan_nr)];
 
 	if (trx_ms_power_loop)
-		ms_power_clock(l1h, lchan, chan_nr, chan_state);
+		ms_power_clock(lchan, chan_nr, chan_state);
 
 	/* count the number of SACCH clocks */
 	chan_state->meas.clock++;
@@ -240,10 +245,11 @@  int trx_loop_sacch_clock(struct trx_l1h *l1h, uint8_t chan_nr,
 	return 0;
 }
 
-int trx_loop_amr_input(struct trx_l1h *l1h, uint8_t chan_nr,
-	struct trx_chan_state *chan_state, float ber)
+int trx_loop_amr_input(struct l1sched_trx *l1t, uint8_t chan_nr,
+	struct l1sched_chan_state *chan_state, float ber)
 {
-	struct gsm_lchan *lchan = &l1h->trx->ts[L1SAP_CHAN2TS(chan_nr)]
+	struct gsm_bts_trx *trx = l1t->trx;
+	struct gsm_lchan *lchan = &trx->ts[L1SAP_CHAN2TS(chan_nr)]
 					.lchan[l1sap_chan2ss(chan_nr)];
 	int c_i;
 
@@ -280,7 +286,7 @@  int trx_loop_amr_input(struct trx_l1h *l1h, uint8_t chan_nr,
 
 	LOGP(DLOOP, LOGL_DEBUG, "Current bit error rate (BER) %.6f "
 		"codec id %d of trx=%u chan_nr=0x%02x\n", ber,
-		chan_state->ul_ft, l1h->trx->nr, chan_nr);
+		chan_state->ul_ft, trx->nr, chan_nr);
 
 	/* degrade */
 	if (chan_state->dl_cmr > 0) {
@@ -290,7 +296,7 @@  int trx_loop_amr_input(struct trx_l1h *l1h, uint8_t chan_nr,
 			LOGP(DLOOP, LOGL_DEBUG, "Degrading due to BER %.6f "
 				"from codec id %d to %d of trx=%u "
 				"chan_nr=0x%02x\n", ber, chan_state->dl_cmr,
-				chan_state->dl_cmr - 1, l1h->trx->nr, chan_nr);
+				chan_state->dl_cmr - 1, trx->nr, chan_nr);
 			chan_state->dl_cmr--;
 		}
 
@@ -306,7 +312,7 @@  int trx_loop_amr_input(struct trx_l1h *l1h, uint8_t chan_nr,
 			LOGP(DLOOP, LOGL_DEBUG, "Upgrading due to BER %.6f "
 				"from codec id %d to %d of trx=%u "
 				"chan_nr=0x%02x\n", ber, chan_state->dl_cmr,
-				chan_state->dl_cmr + 1, l1h->trx->nr, chan_nr);
+				chan_state->dl_cmr + 1, trx->nr, chan_nr);
 			chan_state->dl_cmr++;
 		}
 
@@ -316,7 +322,7 @@  int trx_loop_amr_input(struct trx_l1h *l1h, uint8_t chan_nr,
 	return 0;
 }
 
-int trx_loop_amr_set(struct trx_chan_state *chan_state, int loop)
+int trx_loop_amr_set(struct l1sched_chan_state *chan_state, int loop)
 {
 	if (chan_state->amr_loop && !loop) {
 		chan_state->amr_loop = 0;
@@ -336,4 +342,3 @@  int trx_loop_amr_set(struct trx_chan_state *chan_state, int loop)
 
 	return 0;
 }
-
diff --git a/src/osmo-bts-trx/loops.h b/src/osmo-bts-trx/loops.h
index 27b0ef2..613d2d0 100644
--- a/src/osmo-bts-trx/loops.h
+++ b/src/osmo-bts-trx/loops.h
@@ -17,15 +17,15 @@  extern int trx_ms_power_loop;
 extern int8_t trx_target_rssi;
 extern int trx_ta_loop;
 
-int trx_loop_sacch_input(struct trx_l1h *l1h, uint8_t chan_nr,
-	struct trx_chan_state *chan_state, int8_t rssi, float toa);
+int trx_loop_sacch_input(struct l1sched_trx *l1t, uint8_t chan_nr,
+	struct l1sched_chan_state *chan_state, int8_t rssi, float toa);
 
-int trx_loop_sacch_clock(struct trx_l1h *l1h, uint8_t chan_nr,
-        struct trx_chan_state *chan_state);
+int trx_loop_sacch_clock(struct l1sched_trx *l1t, uint8_t chan_nr,
+        struct l1sched_chan_state *chan_state);
 
-int trx_loop_amr_input(struct trx_l1h *l1h, uint8_t chan_nr,
-        struct trx_chan_state *chan_state, float ber);
+int trx_loop_amr_input(struct l1sched_trx *l1t, uint8_t chan_nr,
+        struct l1sched_chan_state *chan_state, float ber);
 
-int trx_loop_amr_set(struct trx_chan_state *chan_state, int loop);
+int trx_loop_amr_set(struct l1sched_chan_state *chan_state, int loop);
 
 #endif /* _TRX_LOOPS_H */
diff --git a/src/osmo-bts-trx/scheduler.c b/src/osmo-bts-trx/scheduler.c
index 1710b4e..6d0f180 100644
--- a/src/osmo-bts-trx/scheduler.c
+++ b/src/osmo-bts-trx/scheduler.c
@@ -2,6 +2,7 @@ 
 
 /* (C) 2013 by Andreas Eversberg <jolly@eversberg.eu>
  * (C) 2015 by Alexander Chemeris <Alexander.Chemeris@fairwaves.co>
+ * (C) 2015 by Harald Welte <laforge@gnumonks.org>
  *
  * All Rights Reserved
  *
@@ -67,47 +68,47 @@  uint32_t trx_clock_advance = 20;
 /* advance RTS to give some time for data processing. (especially PCU) */
 uint32_t trx_rts_advance = 5; /* about 20ms */
 
-typedef int trx_sched_rts_func(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+typedef int trx_sched_rts_func(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
 	enum trx_chan_type chan);
-typedef ubit_t *trx_sched_dl_func(struct trx_l1h *l1h, uint8_t tn,
+typedef ubit_t *trx_sched_dl_func(struct l1sched_trx *l1t, uint8_t tn,
 	uint32_t fn, enum trx_chan_type chan, uint8_t bid);
-typedef int trx_sched_ul_func(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+typedef int trx_sched_ul_func(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
 	enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi,
 	float toa);
 
-static int rts_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+static int rts_data_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
 	enum trx_chan_type chan);
-static int rts_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+static int rts_tchf_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
 	enum trx_chan_type chan);
-static int rts_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+static int rts_tchh_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
 	enum trx_chan_type chan);
-static ubit_t *tx_idle_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+static ubit_t *tx_idle_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
 	enum trx_chan_type chan, uint8_t bid);
-static ubit_t *tx_fcch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+static ubit_t *tx_fcch_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
 	enum trx_chan_type chan, uint8_t bid);
-static ubit_t *tx_sch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+static ubit_t *tx_sch_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
 	enum trx_chan_type chan, uint8_t bid);
-static ubit_t *tx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+static ubit_t *tx_data_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
 	enum trx_chan_type chan, uint8_t bid);
-static ubit_t *tx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+static ubit_t *tx_pdtch_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
 	enum trx_chan_type chan, uint8_t bid);
-static ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+static ubit_t *tx_tchf_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
 	enum trx_chan_type chan, uint8_t bid);
-static ubit_t *tx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+static ubit_t *tx_tchh_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
 	enum trx_chan_type chan, uint8_t bid);
-static int rx_rach_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+static int rx_rach_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
 	enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi,
 	float toa);
-static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+static int rx_data_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
 	enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi,
 	float toa);
-static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+static int rx_pdtch_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
 	enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi,
 	float toa);
-static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+static int rx_tchf_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
 	enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi,
 	float toa);
-static int rx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+static int rx_tchh_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
 	enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi,
 	float toa);
 
@@ -219,23 +220,22 @@  static const struct trx_chan_desc trx_chan_desc[_TRX_CHAN_MAX] = {
  * init / exit
  */
 
-int trx_sched_init(struct trx_l1h *l1h)
+int trx_sched_init(struct l1sched_trx *l1t)
 {
 	uint8_t tn;
 	int i;
-	struct trx_chan_state *chan_state;
 
-	LOGP(DL1C, LOGL_NOTICE, "Init scheduler for trx=%u\n", l1h->trx->nr);
+	LOGP(DL1C, LOGL_NOTICE, "Init scheduler for trx=%u\n", l1t->trx->nr);
 
-	/* hack to get bts */
-	bts = l1h->trx->bts;
+	for (tn = 0; tn < ARRAY_SIZE(l1t->ts); tn++) {
+		struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
 
-	for (tn = 0; tn < TRX_NR_TS; tn++) {
-		l1h->mf_index[tn] = 0;
-		l1h->mf_last_fn[tn] = 0;
-		INIT_LLIST_HEAD(&l1h->dl_prims[tn]);
-		for (i = 0; i < _TRX_CHAN_MAX; i++) {
-			chan_state = &l1h->chan_states[tn][i];
+		l1ts->mf_index = 0;
+		l1ts->mf_last_fn = 0;
+		INIT_LLIST_HEAD(&l1ts->dl_prims);
+		for (i = 0; i < ARRAY_SIZE(&l1ts->chan_state); i++) {
+			struct l1sched_chan_state *chan_state;
+			chan_state = &l1ts->chan_state[i];
 			chan_state->active = 0;
 		}
 	}
@@ -243,18 +243,20 @@  int trx_sched_init(struct trx_l1h *l1h)
 	return 0;
 }
 
-void trx_sched_exit(struct trx_l1h *l1h)
+void trx_sched_exit(struct l1sched_trx *l1t)
 {
+	struct gsm_bts_trx_ts *ts;
 	uint8_t tn;
 	int i;
-	struct trx_chan_state *chan_state;
 
-	LOGP(DL1C, LOGL_NOTICE, "Exit scheduler for trx=%u\n", l1h->trx->nr);
+	LOGP(DL1C, LOGL_NOTICE, "Exit scheduler for trx=%u\n", l1t->trx->nr);
 
-	for (tn = 0; tn < TRX_NR_TS; tn++) {
-		msgb_queue_flush(&l1h->dl_prims[tn]);
+	for (tn = 0; tn < ARRAY_SIZE(l1t->ts); tn++) {
+		struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
+		msgb_queue_flush(&l1ts->dl_prims);
 		for (i = 0; i < _TRX_CHAN_MAX; i++) {
-			chan_state = &l1h->chan_states[tn][i];
+			struct l1sched_chan_state *chan_state;
+			chan_state = &l1ts->chan_state[i];
 			if (chan_state->dl_bursts) {
 				talloc_free(chan_state->dl_bursts);
 				chan_state->dl_bursts = NULL;
@@ -265,16 +267,17 @@  void trx_sched_exit(struct trx_l1h *l1h)
 			}
 		}
 		/* clear lchan channel states */
-		for (i = 0; i < TRX_NR_TS; i++)
-			l1h->trx->ts[tn].lchan[i].state = LCHAN_S_NONE;
+		ts = &l1t->trx->ts[tn];
+		for (i = 0; i < ARRAY_SIZE(ts->lchan); i++)
+			lchan_set_state(&ts->lchan[i], LCHAN_S_NONE);
 	}
 }
 
 /* close all logical channels and reset timeslots */
-void trx_sched_reset(struct trx_l1h *l1h)
+void trx_sched_reset(struct l1sched_trx *l1t)
 {
-	trx_sched_exit(l1h);
-	trx_sched_init(l1h);
+	trx_sched_exit(l1t);
+	trx_sched_init(l1t);
 }
 
 
@@ -282,13 +285,14 @@  void trx_sched_reset(struct trx_l1h *l1h)
  * data request (from upper layer)
  */
 
-int trx_sched_ph_data_req(struct trx_l1h *l1h, struct osmo_phsap_prim *l1sap)
+int trx_sched_ph_data_req(struct l1sched_trx *l1t, struct osmo_phsap_prim *l1sap)
 {
 	uint8_t tn = l1sap->u.data.chan_nr & 7;
+	struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
 
 	LOGP(DL1C, LOGL_INFO, "PH-DATA.req: chan_nr=0x%02x link_id=0x%02x "
 		"fn=%u ts=%u trx=%u\n", l1sap->u.data.chan_nr,
-		l1sap->u.data.link_id, l1sap->u.data.fn, tn, l1h->trx->nr);
+		l1sap->u.data.link_id, l1sap->u.data.fn, tn, l1t->trx->nr);
 
 	if (!l1sap->oph.msg)
 		abort();
@@ -299,18 +303,19 @@  int trx_sched_ph_data_req(struct trx_l1h *l1h, struct osmo_phsap_prim *l1sap)
 		return 0;
 	}
 
-	msgb_enqueue(&l1h->dl_prims[tn], l1sap->oph.msg);
+	msgb_enqueue(&l1ts->dl_prims, l1sap->oph.msg);
 
 	return 0;
 }
 
-int trx_sched_tch_req(struct trx_l1h *l1h, struct osmo_phsap_prim *l1sap)
+int trx_sched_tch_req(struct l1sched_trx *l1t, struct osmo_phsap_prim *l1sap)
 {
 	uint8_t tn = l1sap->u.tch.chan_nr & 7;
+	struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
 
 	LOGP(DL1C, LOGL_INFO, "TCH.req: chan_nr=0x%02x "
 		"fn=%u ts=%u trx=%u\n", l1sap->u.tch.chan_nr,
-		l1sap->u.tch.fn, tn, l1h->trx->nr);
+		l1sap->u.tch.fn, tn, l1t->trx->nr);
 
 	if (!l1sap->oph.msg)
 		abort();
@@ -321,7 +326,7 @@  int trx_sched_tch_req(struct trx_l1h *l1h, struct osmo_phsap_prim *l1sap)
 		return 0;
 	}
 
-	msgb_enqueue(&l1h->dl_prims[tn], l1sap->oph.msg);
+	msgb_enqueue(&l1ts->dl_prims, l1sap->oph.msg);
 
 	return 0;
 }
@@ -332,12 +337,13 @@  int trx_sched_tch_req(struct trx_l1h *l1h, struct osmo_phsap_prim *l1sap)
  */
 
 /* RTS for data frame */
-static int rts_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+static int rts_data_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
 	enum trx_chan_type chan)
 {
 	uint8_t chan_nr, link_id;
 	struct msgb *msg;
 	struct osmo_phsap_prim *l1sap;
+	struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
 
 	/* get data for RTS indication */
 	chan_nr = trx_chan_desc[chan].chan_nr | tn;
@@ -351,11 +357,11 @@  static int rts_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
 
 	LOGP(DL1C, LOGL_INFO, "PH-RTS.ind: chan=%s chan_nr=0x%02x "
 		"link_id=0x%02x fn=%u ts=%u trx=%u\n", trx_chan_desc[chan].name,
-		chan_nr, link_id, fn, tn, l1h->trx->nr);
+		chan_nr, link_id, fn, tn, l1t->trx->nr);
 
 	/* send clock information to loops process */
 	if (L1SAP_IS_LINK_SACCH(link_id))
-		trx_loop_sacch_clock(l1h, chan_nr, &l1h->chan_states[tn][chan]);
+		trx_loop_sacch_clock(l1t, chan_nr, &l1ts->chan_state[chan]);
 
 	/* generate prim */
 	msg = l1sap_msgb_alloc(200);
@@ -368,15 +374,16 @@  static int rts_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
 	l1sap->u.data.link_id = link_id;
 	l1sap->u.data.fn = fn;
 
-	return l1sap_up(l1h->trx, l1sap);
+	return l1sap_up(l1t->trx, l1sap);
 }
 
-static int rts_tch_common(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+static int rts_tch_common(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
 	enum trx_chan_type chan, int facch)
 {
 	uint8_t chan_nr, link_id;
 	struct msgb *msg;
 	struct osmo_phsap_prim *l1sap;
+	struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
 	int rc = 0;
 
 	/* get data for RTS indication */
@@ -391,7 +398,7 @@  static int rts_tch_common(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
 
 	LOGP(DL1C, LOGL_INFO, "TCH RTS.ind: chan=%s chan_nr=0x%02x "
 		"fn=%u ts=%u trx=%u\n", trx_chan_desc[chan].name,
-		chan_nr, fn, tn, l1h->trx->nr);
+		chan_nr, fn, tn, l1t->trx->nr);
 
 	/* only send, if FACCH is selected */
 	if (facch) {
@@ -406,11 +413,11 @@  static int rts_tch_common(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
 		l1sap->u.data.link_id = link_id;
 		l1sap->u.data.fn = fn;
 
-		rc = l1sap_up(l1h->trx, l1sap);
+		rc = l1sap_up(l1t->trx, l1sap);
 	}
 
 	/* dont send, if TCH is in signalling only mode */
-	if (l1h->chan_states[tn][chan].rsl_cmode != RSL_CMOD_SPD_SIGN) {
+	if (l1ts->chan_state[chan].rsl_cmode != RSL_CMOD_SPD_SIGN) {
 		/* generate prim */
 		msg = l1sap_msgb_alloc(200);
 		if (!msg)
@@ -421,27 +428,27 @@  static int rts_tch_common(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
 		l1sap->u.tch.chan_nr = chan_nr;
 		l1sap->u.tch.fn = fn;
 
-		return l1sap_up(l1h->trx, l1sap);
+		return l1sap_up(l1t->trx, l1sap);
 	}
 
 	return rc;
 }
 
 /* RTS for full rate traffic frame */
-static int rts_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+static int rts_tchf_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
 	enum trx_chan_type chan)
 {
 	/* TCH/F may include FACCH on every 4th burst */
-	return rts_tch_common(l1h, tn, fn, chan, 1);
+	return rts_tch_common(l1t, tn, fn, chan, 1);
 }
 
 
 /* RTS for half rate traffic frame */
-static int rts_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+static int rts_tchh_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
 	enum trx_chan_type chan)
 {
 	/* the FN 4/5, 13/14, 21/22 defines that FACCH may be included. */
-	return rts_tch_common(l1h, tn, fn, chan, ((fn % 26) >> 2) & 1);
+	return rts_tch_common(l1t, tn, fn, chan, ((fn % 26) >> 2) & 1);
 }
 
 
@@ -450,25 +457,25 @@  static int rts_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
  */
 
 /* an IDLE burst returns nothing. on C0 it is replaced by dummy burst */
-static ubit_t *tx_idle_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+static ubit_t *tx_idle_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
 	enum trx_chan_type chan, uint8_t bid)
 {
 	LOGP(DL1C, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u trx=%u\n",
-		trx_chan_desc[chan].name, fn, tn, l1h->trx->nr);
+		trx_chan_desc[chan].name, fn, tn, l1t->trx->nr);
 
 	return NULL;
 }
 
-static ubit_t *tx_fcch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+static ubit_t *tx_fcch_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
 	enum trx_chan_type chan, uint8_t bid)
 {
 	LOGP(DL1C, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u trx=%u\n",
-		trx_chan_desc[chan].name, fn, tn, l1h->trx->nr);
+		trx_chan_desc[chan].name, fn, tn, l1t->trx->nr);
 
 	return (ubit_t *) fcch_burst;
 }
 
-static ubit_t *tx_sch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+static ubit_t *tx_sch_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
 	enum trx_chan_type chan, uint8_t bid)
 {
 	static ubit_t bits[148], burst[78];
@@ -477,12 +484,12 @@  static ubit_t *tx_sch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
 	uint8_t t3p, bsic;
 
 	LOGP(DL1C, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u trx=%u\n",
-		trx_chan_desc[chan].name, fn, tn, l1h->trx->nr);
+		trx_chan_desc[chan].name, fn, tn, l1t->trx->nr);
 
 	/* create SB info from GSM time and BSIC */
 	gsm_fn2gsmtime(&t, fn);
 	t3p = t.t3 / 10;
-	bsic = l1h->trx->bts->bsic;
+	bsic = l1t->trx->bts->bsic;
 	sb_info[0] =
 		((bsic &  0x3f) << 2) |
 		((t.t1 & 0x600) >> 9);
@@ -508,16 +515,17 @@  static ubit_t *tx_sch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
 	return bits;
 }
 
-static struct msgb *dequeue_prim(struct trx_l1h *l1h, int8_t tn,uint32_t fn,
+static struct msgb *dequeue_prim(struct l1sched_trx *l1t, int8_t tn, uint32_t fn,
 	enum trx_chan_type chan)
 {
 	struct msgb *msg, *msg2;
 	struct osmo_phsap_prim *l1sap;
 	uint32_t prim_fn;
 	uint8_t chan_nr, link_id;
+	struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
 
 	/* get prim of current fn from queue */
-	llist_for_each_entry_safe(msg, msg2, &l1h->dl_prims[tn], list) {
+	llist_for_each_entry_safe(msg, msg2, &l1ts->dl_prims, list) {
 		l1sap = msgb_l1sap_prim(msg);
 		if (l1sap->oph.operation != PRIM_OP_REQUEST) {
 wrong_type:
@@ -548,7 +556,7 @@  free_msg:
 				"is out of range, or channel already disabled. "
 				"If this happens in conjunction with PCU, "
 				"increase 'rts-advance' by 5. (current fn=%u)\n",
-				l1h->trx->nr, tn, l1sap->u.data.fn, fn);
+				l1t->trx->nr, tn, l1sap->u.data.fn, fn);
 			/* unlink and free message */
 			llist_del(&msg->list);
 			msgb_free(msg);
@@ -578,12 +586,13 @@  found_msg:
 	return msg;
 }
 
-static int compose_ph_data_ind(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+static int compose_ph_data_ind(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
 	enum trx_chan_type chan, uint8_t *l2, uint8_t l2_len, float rssi)
 {
 	struct msgb *msg;
 	struct osmo_phsap_prim *l1sap;
 	uint8_t chan_nr = trx_chan_desc[chan].chan_nr | tn;
+	struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
 
 	/* compose primitive */
 	msg = l1sap_msgb_alloc(l2_len);
@@ -599,19 +608,20 @@  static int compose_ph_data_ind(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
 		memcpy(msg->l2h, l2, l2_len);
 
 	if (L1SAP_IS_LINK_SACCH(trx_chan_desc[chan].link_id))
-		l1h->chan_states[tn][chan].lost = 0;
+		l1ts->chan_state[chan].lost = 0;
 
 	/* forward primitive */
-	l1sap_up(l1h->trx, l1sap);
+	l1sap_up(l1t->trx, l1sap);
 
 	return 0;
 }
 
-static int compose_tch_ind(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+static int compose_tch_ind(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
 	enum trx_chan_type chan, uint8_t *tch, uint8_t tch_len)
 {
 	struct msgb *msg;
 	struct osmo_phsap_prim *l1sap;
+	struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
 
 	/* compose primitive */
 	msg = l1sap_msgb_alloc(tch_len);
@@ -624,20 +634,22 @@  static int compose_tch_ind(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
 	if (tch_len)
 		memcpy(msg->l2h, tch, tch_len);
 
-	if (l1h->chan_states[tn][chan].lost)
-		l1h->chan_states[tn][chan].lost--;
+	if (l1ts->chan_state[chan].lost)
+		l1ts->chan_state[chan].lost--;
 
 	/* forward primitive */
-	l1sap_up(l1h->trx, l1sap);
+	l1sap_up(l1t->trx, l1sap);
 
 	return 0;
 }
 
-static ubit_t *tx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+static ubit_t *tx_data_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
 	enum trx_chan_type chan, uint8_t bid)
 {
+	struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
+	struct gsm_bts_trx_ts *ts = &l1t->trx->ts[tn];
 	struct msgb *msg = NULL; /* make GCC happy */
-	ubit_t *burst, **bursts_p = &l1h->chan_states[tn][chan].dl_bursts;
+	ubit_t *burst, **bursts_p = &l1ts->chan_state[chan].dl_bursts;
 	static ubit_t bits[148];
 
 	/* send burst, if we already got a frame */
@@ -648,13 +660,13 @@  static ubit_t *tx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
 	}
 
 	/* get mac block from queue */
-	msg = dequeue_prim(l1h, tn, fn, chan);
+	msg = dequeue_prim(l1t, tn, fn, chan);
 	if (msg)
 		goto got_msg;
 
 	LOGP(DL1C, LOGL_INFO, "%s has not been served !! No prim for "
 		"trx=%u ts=%u at fn=%u to transmit.\n", 
-		trx_chan_desc[chan].name, l1h->trx->nr, tn, fn);
+		trx_chan_desc[chan].name, l1t->trx->nr, tn, fn);
 
 no_msg:
 	/* free burst memory */
@@ -677,15 +689,15 @@  got_msg:
 	/* handle loss detection of sacch */
 	if (L1SAP_IS_LINK_SACCH(trx_chan_desc[chan].link_id)) {
 		/* count and send BFI */
-		if (++(l1h->chan_states[tn][chan].lost) > 1) {
+		if (++(l1ts->chan_state[chan].lost) > 1) {
 			/* TODO: Should we pass old TOA here? Otherwise we risk
 			 * unnecessary decreasing TA */
 
 			/* Send uplnk measurement information to L2 */
-			l1if_process_meas_res(l1h->trx, tn, fn, trx_chan_desc[chan].chan_nr | tn,
+			l1if_process_meas_res(l1t->trx, tn, fn, trx_chan_desc[chan].chan_nr | tn,
 				456, 456, -110, 0);
 
-			compose_ph_data_ind(l1h, tn, 0, chan, NULL, 0, -110);
+			compose_ph_data_ind(l1t, tn, 0, chan, NULL, 0, -110);
 		}
 	}
 
@@ -707,21 +719,23 @@  send_burst:
 	burst = *bursts_p + bid * 116;
 	memset(bits, 0, 3);
 	memcpy(bits + 3, burst, 58);
-	memcpy(bits + 61, tsc[l1h->config.tsc], 26);
+	memcpy(bits + 61, tsc[gsm_ts_tsc(ts)], 26);
 	memcpy(bits + 87, burst + 58, 58);
 	memset(bits + 145, 0, 3);
 
 	LOGP(DL1C, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u trx=%u burst=%u\n",
-		trx_chan_desc[chan].name, fn, tn, l1h->trx->nr, bid);
+		trx_chan_desc[chan].name, fn, tn, l1t->trx->nr, bid);
 
 	return bits;
 }
 
-static ubit_t *tx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+static ubit_t *tx_pdtch_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
 	enum trx_chan_type chan, uint8_t bid)
 {
+	struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
+	struct gsm_bts_trx_ts *ts = &l1t->trx->ts[tn];
 	struct msgb *msg = NULL; /* make GCC happy */
-	ubit_t *burst, **bursts_p = &l1h->chan_states[tn][chan].dl_bursts;
+	ubit_t *burst, **bursts_p = &l1ts->chan_state[chan].dl_bursts;
 	static ubit_t bits[148];
 	int rc;
 
@@ -733,13 +747,13 @@  static ubit_t *tx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
 	}
 
 	/* get mac block from queue */
-	msg = dequeue_prim(l1h, tn, fn, chan);
+	msg = dequeue_prim(l1t, tn, fn, chan);
 	if (msg)
 		goto got_msg;
 
 	LOGP(DL1C, LOGL_INFO, "%s has not been served !! No prim for "
 		"trx=%u ts=%u at fn=%u to transmit.\n", 
-		trx_chan_desc[chan].name, l1h->trx->nr, tn, fn);
+		trx_chan_desc[chan].name, l1t->trx->nr, tn, fn);
 
 no_msg:
 	/* free burst memory */
@@ -777,29 +791,30 @@  send_burst:
 	burst = *bursts_p + bid * 116;
 	memset(bits, 0, 3);
 	memcpy(bits + 3, burst, 58);
-	memcpy(bits + 61, tsc[l1h->config.tsc], 26);
+	memcpy(bits + 61, tsc[gsm_ts_tsc(ts)], 26);
 	memcpy(bits + 87, burst + 58, 58);
 	memset(bits + 145, 0, 3);
 
 	LOGP(DL1C, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u trx=%u burst=%u\n",
-		trx_chan_desc[chan].name, fn, tn, l1h->trx->nr, bid);
+		trx_chan_desc[chan].name, fn, tn, l1t->trx->nr, bid);
 
 	return bits;
 }
 
-static void tx_tch_common(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+static void tx_tch_common(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
 	enum trx_chan_type chan, uint8_t bid, struct msgb **_msg_tch,
 	struct msgb **_msg_facch, int codec_mode_request)
 {
+	struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
 	struct msgb *msg1, *msg2, *msg_tch = NULL, *msg_facch = NULL;
-	struct trx_chan_state *chan_state = &l1h->chan_states[tn][chan];
+	struct l1sched_chan_state *chan_state = &l1ts->chan_state[chan];
 	uint8_t rsl_cmode = chan_state->rsl_cmode;
 	uint8_t tch_mode = chan_state->tch_mode;
 	struct osmo_phsap_prim *l1sap;
 
 	/* handle loss detection of received TCH frames */
 	if (rsl_cmode == RSL_CMOD_SPD_SPEECH
-	 && ++(l1h->chan_states[tn][chan].lost) > 5) {
+	 && ++(chan_state->lost) > 5) {
 		uint8_t tch_data[GSM_FR_BYTES];
 		int len;
 
@@ -831,7 +846,7 @@  static void tx_tch_common(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
 			if (len < 2)
 				break;
 			memset(tch_data + 2, 0, len - 2);
-			compose_tch_ind(l1h, tn, 0, chan, tch_data, len);
+			compose_tch_ind(l1t, tn, 0, chan, tch_data, len);
 			break;
 		default:
 inval_mode1:
@@ -840,12 +855,12 @@  inval_mode1:
 			len = 0;
 		}
 		if (len)
-			compose_tch_ind(l1h, tn, 0, chan, tch_data, len);
+			compose_tch_ind(l1t, tn, 0, chan, tch_data, len);
 	}
 
 	/* get frame and unlink from queue */
-	msg1 = dequeue_prim(l1h, tn, fn, chan);
-	msg2 = dequeue_prim(l1h, tn, fn, chan);
+	msg1 = dequeue_prim(l1t, tn, fn, chan);
+	msg2 = dequeue_prim(l1t, tn, fn, chan);
 	if (msg1) {
 		l1sap = msgb_l1sap_prim(msg1);
 		if (l1sap->oph.primitive == PRIM_TCH) {
@@ -898,7 +913,7 @@  inval_mode1:
 			LOGP(DL1C, LOGL_NOTICE, "%s Dropping speech frame, "
 				"because we are not in speech mode trx=%u "
 				"ts=%u at fn=%u.\n", trx_chan_desc[chan].name,
-				l1h->trx->nr, tn, fn);
+				l1t->trx->nr, tn, fn);
 			goto free_bad_msg;
 		}
 
@@ -913,7 +928,7 @@  inval_mode1:
 						"HR frame' trx=%u ts=%u at "
 						"fn=%u.\n",
 						trx_chan_desc[chan].name,
-						l1h->trx->nr, tn, fn);
+						l1t->trx->nr, tn, fn);
 					goto free_bad_msg;
 				}
 				break;
@@ -924,7 +939,7 @@  inval_mode1:
 				LOGP(DL1C, LOGL_NOTICE, "%s Transmitting 'bad "
 					"FR frame' trx=%u ts=%u at fn=%u.\n",
 					trx_chan_desc[chan].name,
-					l1h->trx->nr, tn, fn);
+					l1t->trx->nr, tn, fn);
 				goto free_bad_msg;
 			}
 			break;
@@ -937,7 +952,7 @@  inval_mode1:
 				LOGP(DL1C, LOGL_NOTICE, "%s Transmitting 'bad "
 					"EFR frame' trx=%u ts=%u at fn=%u.\n",
 					trx_chan_desc[chan].name,
-					l1h->trx->nr, tn, fn);
+					l1t->trx->nr, tn, fn);
 				goto free_bad_msg;
 			}
 			break;
@@ -966,7 +981,7 @@  inval_mode1:
 					" of RTP frame not in list. "
 					"trx=%u ts=%u\n",
 					trx_chan_desc[chan].name, ft_codec,
-					l1h->trx->nr, tn);
+					l1t->trx->nr, tn);
 				goto free_bad_msg;
 			}
 			if (codec_mode_request && chan_state->dl_ft != ft) {
@@ -974,7 +989,7 @@  inval_mode1:
 					" of RTP cannot be changed now, but in "
 					"next frame. trx=%u ts=%u\n",
 					trx_chan_desc[chan].name, ft_codec,
-					l1h->trx->nr, tn);
+					l1t->trx->nr, tn);
 				goto free_bad_msg;
 			}
 			chan_state->dl_ft = ft;
@@ -982,7 +997,7 @@  inval_mode1:
 				LOGP(DL1C, LOGL_NOTICE, "%s Transmitting 'bad "
 					"AMR frame' trx=%u ts=%u at fn=%u.\n",
 					trx_chan_desc[chan].name,
-					l1h->trx->nr, tn, fn);
+					l1t->trx->nr, tn, fn);
 				goto free_bad_msg;
 			}
 			break;
@@ -1014,11 +1029,13 @@  send_frame:
 	*_msg_facch = msg_facch;
 }
 
-static ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+static ubit_t *tx_tchf_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
 	enum trx_chan_type chan, uint8_t bid)
 {
 	struct msgb *msg_tch = NULL, *msg_facch = NULL;
-	struct trx_chan_state *chan_state = &l1h->chan_states[tn][chan];
+	struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
+	struct gsm_bts_trx_ts *ts = &l1t->trx->ts[tn];
+	struct l1sched_chan_state *chan_state = &l1ts->chan_state[chan];
 	uint8_t tch_mode = chan_state->tch_mode;
 	ubit_t *burst, **bursts_p = &chan_state->dl_bursts;
 	static ubit_t bits[148];
@@ -1030,7 +1047,7 @@  static ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
 		goto send_burst;
 	}
 
-	tx_tch_common(l1h, tn, fn, chan, bid, &msg_tch, &msg_facch,
+	tx_tch_common(l1t, tn, fn, chan, bid, &msg_tch, &msg_facch,
 		(((fn + 4) % 26) >> 2) & 1);
 
 	/* alloc burst memory, if not already,
@@ -1048,7 +1065,7 @@  static ubit_t *tx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
 	if (!msg_tch && !msg_facch) {
 		LOGP(DL1C, LOGL_INFO, "%s has not been served !! No prim for "
 			"trx=%u ts=%u at fn=%u to transmit.\n", 
-			trx_chan_desc[chan].name, l1h->trx->nr, tn, fn);
+			trx_chan_desc[chan].name, l1t->trx->nr, tn, fn);
 		goto send_burst;
 	}
 
@@ -1079,21 +1096,23 @@  send_burst:
 	burst = *bursts_p + bid * 116;
 	memset(bits, 0, 3);
 	memcpy(bits + 3, burst, 58);
-	memcpy(bits + 61, tsc[l1h->config.tsc], 26);
+	memcpy(bits + 61, tsc[gsm_ts_tsc(ts)], 26);
 	memcpy(bits + 87, burst + 58, 58);
 	memset(bits + 145, 0, 3);
 
 	LOGP(DL1C, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u trx=%u burst=%u\n",
-		trx_chan_desc[chan].name, fn, tn, l1h->trx->nr, bid);
+		trx_chan_desc[chan].name, fn, tn, l1t->trx->nr, bid);
 
 	return bits;
 }
 
-static ubit_t *tx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+static ubit_t *tx_tchh_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
 	enum trx_chan_type chan, uint8_t bid)
 {
 	struct msgb *msg_tch = NULL, *msg_facch = NULL;
-	struct trx_chan_state *chan_state = &l1h->chan_states[tn][chan];
+	struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
+	struct gsm_bts_trx_ts *ts = &l1t->trx->ts[tn];
+	struct l1sched_chan_state *chan_state = &l1ts->chan_state[chan];
 	uint8_t tch_mode = chan_state->tch_mode;
 	ubit_t *burst, **bursts_p = &chan_state->dl_bursts;
 	static ubit_t bits[148];
@@ -1106,7 +1125,7 @@  static ubit_t *tx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
 	}
 
 	/* get TCH and/or FACCH */
-	tx_tch_common(l1h, tn, fn, chan, bid, &msg_tch, &msg_facch,
+	tx_tch_common(l1t, tn, fn, chan, bid, &msg_tch, &msg_facch,
 		(((fn + 4) % 26) >> 2) & 1);
 
 	/* check for FACCH alignment */
@@ -1138,7 +1157,7 @@  static ubit_t *tx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
 	if (!msg_tch && !msg_facch && !chan_state->dl_ongoing_facch) {
 		LOGP(DL1C, LOGL_INFO, "%s has not been served !! No prim for "
 			"trx=%u ts=%u at fn=%u to transmit.\n", 
-			trx_chan_desc[chan].name, l1h->trx->nr, tn, fn);
+			trx_chan_desc[chan].name, l1t->trx->nr, tn, fn);
 		goto send_burst;
 	}
 
@@ -1171,12 +1190,12 @@  send_burst:
 	burst = *bursts_p + bid * 116;
 	memset(bits, 0, 3);
 	memcpy(bits + 3, burst, 58);
-	memcpy(bits + 61, tsc[l1h->config.tsc], 26);
+	memcpy(bits + 61, tsc[gsm_ts_tsc(ts)], 26);
 	memcpy(bits + 87, burst + 58, 58);
 	memset(bits + 145, 0, 3);
 
 	LOGP(DL1C, LOGL_DEBUG, "Transmitting %s fn=%u ts=%u trx=%u burst=%u\n",
-		trx_chan_desc[chan].name, fn, tn, l1h->trx->nr, bid);
+		trx_chan_desc[chan].name, fn, tn, l1t->trx->nr, bid);
 
 	return bits;
 }
@@ -1186,7 +1205,7 @@  send_burst:
  * RX on uplink (indication to upper layer)
  */
 
-static int rx_rach_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+static int rx_rach_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
 	enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi,
 	float toa)
 {
@@ -1201,7 +1220,7 @@  static int rx_rach_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
 		trx_chan_desc[chan].name, fn, toa);
 
 	/* decode */
-	rc = rach_decode(&ra, bits + 8 + 41, l1h->trx->bts->bsic);
+	rc = rach_decode(&ra, bits + 8 + 41, l1t->trx->bts->bsic);
 	if (rc) {
 		LOGP(DL1C, LOGL_NOTICE, "Received bad AB frame at fn=%u "
 			"(%u/51)\n", fn, fn % 51);
@@ -1223,17 +1242,18 @@  static int rx_rach_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
 	l1sap.u.rach_ind.fn = fn;
 
 	/* forward primitive */
-	l1sap_up(l1h->trx, &l1sap);
+	l1sap_up(l1t->trx, &l1sap);
 
 	return 0;
 }
 
 /*! \brief a single burst was received by the PHY, process it */
-static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+static int rx_data_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
 	enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi,
 	float toa)
 {
-	struct trx_chan_state *chan_state = &l1h->chan_states[tn][chan];
+	struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
+	struct l1sched_chan_state *chan_state = &l1ts->chan_state[chan];
 	sbit_t *burst, **bursts_p = &chan_state->ul_bursts;
 	uint32_t *first_fn = &chan_state->ul_first_fn;
 	uint8_t *mask = &chan_state->ul_mask;
@@ -1247,10 +1267,10 @@  static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
 
 	/* handle rach, if handover rach detection is turned on */
 	if (chan_state->ho_rach_detect == 1)
-		return rx_rach_fn(l1h, tn, fn, chan, bid, bits, rssi, toa);
+		return rx_rach_fn(l1t, tn, fn, chan, bid, bits, rssi, toa);
 
 	LOGP(DL1C, LOGL_DEBUG, "Data received %s fn=%u ts=%u trx=%u bid=%u\n",
-		trx_chan_desc[chan].name, fn, tn, l1h->trx->nr, bid);
+		trx_chan_desc[chan].name, fn, tn, l1t->trx->nr, bid);
 
 	/* alloc burst memory, if not already */
 	if (!*bursts_p) {
@@ -1284,7 +1304,7 @@  static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
 
 	/* send burst information to loops process */
 	if (L1SAP_IS_LINK_SACCH(trx_chan_desc[chan].link_id)) {
-		trx_loop_sacch_input(l1h, trx_chan_desc[chan].chan_nr | tn,
+		trx_loop_sacch_input(l1t, trx_chan_desc[chan].chan_nr | tn,
 			chan_state, rssi, toa);
 	}
 
@@ -1296,7 +1316,7 @@  static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
 	if ((*mask & 0xf) != 0xf) {
 		LOGP(DL1C, LOGL_NOTICE, "Received incomplete data frame at "
 			"fn=%u (%u/%u) for %s\n", *first_fn,
-			(*first_fn) % l1h->mf_period[tn], l1h->mf_period[tn],
+			(*first_fn) % l1ts->mf_period, l1ts->mf_period,
 			trx_chan_desc[chan].name);
 
 		/* we require first burst to have correct FN */
@@ -1312,24 +1332,25 @@  static int rx_data_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
 	if (rc) {
 		LOGP(DL1C, LOGL_NOTICE, "Received bad data frame at fn=%u "
 			"(%u/%u) for %s\n", *first_fn,
-			(*first_fn) % l1h->mf_period[tn], l1h->mf_period[tn],
+			(*first_fn) % l1ts->mf_period, l1ts->mf_period,
 			trx_chan_desc[chan].name);
 		l2_len = 0;
 	} else
 		l2_len = GSM_MACBLOCK_LEN;
 
 	/* Send uplnk measurement information to L2 */
-	l1if_process_meas_res(l1h->trx, tn, fn, trx_chan_desc[chan].chan_nr | tn,
+	l1if_process_meas_res(l1t->trx, tn, fn, trx_chan_desc[chan].chan_nr | tn,
 		n_errors, n_bits_total, *rssi_sum / *rssi_num, *toa_sum / *toa_num);
 
-	return compose_ph_data_ind(l1h, tn, *first_fn, chan, l2, l2_len, *rssi_sum / *rssi_num);
+	return compose_ph_data_ind(l1t, tn, *first_fn, chan, l2, l2_len, *rssi_sum / *rssi_num);
 }
 
-static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+static int rx_pdtch_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
 	enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi,
 	float toa)
 {
-	struct trx_chan_state *chan_state = &l1h->chan_states[tn][chan];
+	struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
+	struct l1sched_chan_state *chan_state = &l1ts->chan_state[chan];
 	sbit_t *burst, **bursts_p = &chan_state->ul_bursts;
 	uint8_t *mask = &chan_state->ul_mask;
 	float *rssi_sum = &chan_state->rssi_sum;
@@ -1341,7 +1362,7 @@  static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
 	int rc;
 
 	LOGP(DL1C, LOGL_DEBUG, "PDTCH received %s fn=%u ts=%u trx=%u bid=%u\n", 
-		trx_chan_desc[chan].name, fn, tn, l1h->trx->nr, bid);
+		trx_chan_desc[chan].name, fn, tn, l1t->trx->nr, bid);
 
 	/* alloc burst memory, if not already */
 	if (!*bursts_p) {
@@ -1380,7 +1401,7 @@  static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
 	if ((*mask & 0xf) != 0xf) {
 		LOGP(DL1C, LOGL_NOTICE, "Received incomplete PDTCH block "
 			"ending at fn=%u (%u/%u) for %s\n", fn,
-			fn % l1h->mf_period[tn], l1h->mf_period[tn],
+			fn % l1ts->mf_period, l1ts->mf_period,
 			trx_chan_desc[chan].name);
 	}
 	*mask = 0x0;
@@ -1389,27 +1410,28 @@  static int rx_pdtch_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
 	rc = pdtch_decode(l2 + 1, *bursts_p, NULL, &n_errors, &n_bits_total);
 
 	/* Send uplnk measurement information to L2 */
-	l1if_process_meas_res(l1h->trx, tn, fn, trx_chan_desc[chan].chan_nr | tn,
+	l1if_process_meas_res(l1t->trx, tn, fn, trx_chan_desc[chan].chan_nr | tn,
 		n_errors, n_bits_total, *rssi_sum / *rssi_num, *toa_sum / *toa_num);
 
 	if (rc <= 0) {
 		LOGP(DL1C, LOGL_NOTICE, "Received bad PDTCH block ending at "
-			"fn=%u (%u/%u) for %s\n", fn, fn % l1h->mf_period[tn],
-			l1h->mf_period[tn], trx_chan_desc[chan].name);
+			"fn=%u (%u/%u) for %s\n", fn, fn % l1ts->mf_period,
+			l1ts->mf_period, trx_chan_desc[chan].name);
 		return 0;
 	}
 
 	l2[0] = 7; /* valid frame */
 
-	return compose_ph_data_ind(l1h, tn, (fn + GSM_HYPERFRAME - 3) % GSM_HYPERFRAME, chan,
+	return compose_ph_data_ind(l1t, tn, (fn + GSM_HYPERFRAME - 3) % GSM_HYPERFRAME, chan,
 		l2, rc + 1, *rssi_sum / *rssi_num);
 }
 
-static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+static int rx_tchf_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
 	enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi,
 	float toa)
 {
-	struct trx_chan_state *chan_state = &l1h->chan_states[tn][chan];
+	struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
+	struct l1sched_chan_state *chan_state = &l1ts->chan_state[chan];
 	sbit_t *burst, **bursts_p = &chan_state->ul_bursts;
 	uint8_t *mask = &chan_state->ul_mask;
 	uint8_t rsl_cmode = chan_state->rsl_cmode;
@@ -1420,10 +1442,10 @@  static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
 
 	/* handle rach, if handover rach detection is turned on */
 	if (chan_state->ho_rach_detect == 1)
-		return rx_rach_fn(l1h, tn, fn, chan, bid, bits, rssi, toa);
+		return rx_rach_fn(l1t, tn, fn, chan, bid, bits, rssi, toa);
 
 	LOGP(DL1C, LOGL_DEBUG, "TCH/F received %s fn=%u ts=%u trx=%u bid=%u\n", 
-		trx_chan_desc[chan].name, fn, tn, l1h->trx->nr, bid);
+		trx_chan_desc[chan].name, fn, tn, l1t->trx->nr, bid);
 
 	/* alloc burst memory, if not already */
 	if (!*bursts_p) {
@@ -1454,7 +1476,7 @@  static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
 	if ((*mask & 0xf) != 0xf) {
 		LOGP(DL1C, LOGL_NOTICE, "Received incomplete TCH frame ending "
 			"at fn=%u (%u/%u) for %s\n", fn,
-			fn % l1h->mf_period[tn], l1h->mf_period[tn],
+			fn % l1ts->mf_period, l1ts->mf_period,
 			trx_chan_desc[chan].name);
 	}
 	*mask = 0x0;
@@ -1479,7 +1501,7 @@  static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
 			chan_state->codecs, &chan_state->ul_ft,
 			&chan_state->ul_cmr, &n_errors, &n_bits_total);
 		if (rc)
-			trx_loop_amr_input(l1h,
+			trx_loop_amr_input(l1t,
 				trx_chan_desc[chan].chan_nr | tn, chan_state,
 				(float)n_errors/(float)n_bits_total);
 		amr = 2; /* we store tch_data + 2 header bytes */
@@ -1498,7 +1520,7 @@  static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
 	memcpy(*bursts_p, *bursts_p + 464, 464);
 
 	/* Send uplnk measurement information to L2 */
-	l1if_process_meas_res(l1h->trx, tn, fn, trx_chan_desc[chan].chan_nr|tn,
+	l1if_process_meas_res(l1t->trx, tn, fn, trx_chan_desc[chan].chan_nr|tn,
 		n_errors, n_bits_total, rssi, toa);
 
 	/* Check if the frame is bad */
@@ -1516,7 +1538,7 @@  static int rx_tchf_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
 
 	/* FACCH */
 	if (rc == GSM_MACBLOCK_LEN) {
-		compose_ph_data_ind(l1h, tn, (fn + GSM_HYPERFRAME - 7) % GSM_HYPERFRAME, chan,
+		compose_ph_data_ind(l1t, tn, (fn + GSM_HYPERFRAME - 7) % GSM_HYPERFRAME, chan,
 			tch_data + amr, GSM_MACBLOCK_LEN, rssi);
 bfi:
 		if (rsl_cmode == RSL_CMOD_SPD_SPEECH) {
@@ -1551,15 +1573,16 @@  bfi:
 		return 0;
 
 	/* TCH or BFI */
-	return compose_tch_ind(l1h, tn, (fn + GSM_HYPERFRAME - 7) % GSM_HYPERFRAME, chan,
+	return compose_tch_ind(l1t, tn, (fn + GSM_HYPERFRAME - 7) % GSM_HYPERFRAME, chan,
 		tch_data, rc);
 }
 
-static int rx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+static int rx_tchh_fn(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
 	enum trx_chan_type chan, uint8_t bid, sbit_t *bits, int8_t rssi,
 	float toa)
 {
-	struct trx_chan_state *chan_state = &l1h->chan_states[tn][chan];
+	struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
+	struct l1sched_chan_state *chan_state = &l1ts->chan_state[chan];
 	sbit_t *burst, **bursts_p = &chan_state->ul_bursts;
 	uint8_t *mask = &chan_state->ul_mask;
 	uint8_t rsl_cmode = chan_state->rsl_cmode;
@@ -1570,10 +1593,10 @@  static int rx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
 
 	/* handle rach, if handover rach detection is turned on */
 	if (chan_state->ho_rach_detect == 1)
-		return rx_rach_fn(l1h, tn, fn, chan, bid, bits, rssi, toa);
+		return rx_rach_fn(l1t, tn, fn, chan, bid, bits, rssi, toa);
 
 	LOGP(DL1C, LOGL_DEBUG, "TCH/H received %s fn=%u ts=%u trx=%u bid=%u\n",
-		trx_chan_desc[chan].name, fn, tn, l1h->trx->nr, bid);
+		trx_chan_desc[chan].name, fn, tn, l1t->trx->nr, bid);
 
 	/* alloc burst memory, if not already */
 	if (!*bursts_p) {
@@ -1604,7 +1627,7 @@  static int rx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
 	if ((*mask & 0x3) != 0x3) {
 		LOGP(DL1C, LOGL_NOTICE, "Received incomplete TCH frame ending "
 			"at fn=%u (%u/%u) for %s\n", fn,
-			fn % l1h->mf_period[tn], l1h->mf_period[tn],
+			fn % l1ts->mf_period, l1ts->mf_period,
 			trx_chan_desc[chan].name);
 	}
 	*mask = 0x0;
@@ -1641,7 +1664,7 @@  static int rx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
 			chan_state->codecs, &chan_state->ul_ft,
 			&chan_state->ul_cmr, &n_errors, &n_bits_total);
 		if (rc)
-			trx_loop_amr_input(l1h,
+			trx_loop_amr_input(l1t,
 				trx_chan_desc[chan].chan_nr | tn, chan_state,
 				(float)n_errors/(float)n_bits_total);
 		amr = 2; /* we store tch_data + 2 two */
@@ -1661,7 +1684,7 @@  static int rx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
 	memcpy(*bursts_p + 232, *bursts_p + 464, 232);
 
 	/* Send uplnk measurement information to L2 */
-	l1if_process_meas_res(l1h->trx, tn, fn, trx_chan_desc[chan].chan_nr|tn,
+	l1if_process_meas_res(l1t->trx, tn, fn, trx_chan_desc[chan].chan_nr|tn,
 		n_errors, n_bits_total, rssi, toa);
 
 	/* Check if the frame is bad */
@@ -1680,7 +1703,7 @@  static int rx_tchh_fn(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
 	/* FACCH */
 	if (rc == GSM_MACBLOCK_LEN) {
 		chan_state->ul_ongoing_facch = 1;
-		compose_ph_data_ind(l1h, tn,
+		compose_ph_data_ind(l1t, tn,
 			(fn + GSM_HYPERFRAME - 10 - ((fn % 26) >= 19)) % GSM_HYPERFRAME, chan,
 			tch_data + amr, GSM_MACBLOCK_LEN, rssi);
 bfi:
@@ -1718,7 +1741,7 @@  bfi:
 	 * with the slot 12, so an extra FN must be substracted to get correct
 	 * start of frame.
 	 */
-	return compose_tch_ind(l1h, tn,
+	return compose_tch_ind(l1t, tn,
 		(fn + GSM_HYPERFRAME - 10 - ((fn%26)==19) - ((fn%26)==20)) % GSM_HYPERFRAME,
 		chan, tch_data, rc);
 }
@@ -2487,61 +2510,59 @@  static const struct trx_sched_multiframe trx_sched_multiframes[] = {
  */
 
 /* set multiframe scheduler to given pchan */
-int trx_sched_set_pchan(struct trx_l1h *l1h, uint8_t tn,
+int trx_sched_set_pchan(struct l1sched_trx *l1t, uint8_t tn,
 	enum gsm_phys_chan_config pchan)
 {
+	struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
 	int i;
 
-	/* ignore disabled slots */
-	if (!(l1h->config.slotmask & (1 << tn)))
-		return -ENOTSUP;
-
 	for (i = 0; i < ARRAY_SIZE(trx_sched_multiframes); i++) {
 		if (trx_sched_multiframes[i].pchan == pchan
 		 && (trx_sched_multiframes[i].slotmask & (1 << tn))) {
-			l1h->mf_index[tn] = i;
-			l1h->mf_period[tn] = trx_sched_multiframes[i].period;
-			l1h->mf_frames[tn] = trx_sched_multiframes[i].frames;
+			l1ts->mf_index = i;
+			l1ts->mf_period = trx_sched_multiframes[i].period;
+			l1ts->mf_frames = trx_sched_multiframes[i].frames;
 			LOGP(DL1C, LOGL_NOTICE, "Configuring multiframe with "
 				"%s trx=%d ts=%d\n",
 				trx_sched_multiframes[i].name,
-				l1h->trx->nr, tn);
+				l1t->trx->nr, tn);
 			return 0;
 		}
 	}
 
 	LOGP(DL1C, LOGL_NOTICE, "Failed to configuring multiframe "
-		"trx=%d ts=%d\n", l1h->trx->nr, tn);
+		"trx=%d ts=%d\n", l1t->trx->nr, tn);
 
 	return -ENOTSUP;
 }
 
 /* setting all logical channels given attributes to active/inactive */
-int trx_sched_set_lchan(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t link_id,
+int trx_sched_set_lchan(struct l1sched_trx *l1t, uint8_t chan_nr, uint8_t link_id,
 	int active)
 {
 	uint8_t tn = L1SAP_CHAN2TS(chan_nr);
+	struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
 	uint8_t ss = l1sap_chan2ss(chan_nr);
 	int i;
 	int rc = -EINVAL;
-	struct trx_chan_state *chan_state;
 
 	/* look for all matching chan_nr/link_id */
 	for (i = 0; i < _TRX_CHAN_MAX; i++) {
+		struct l1sched_chan_state *chan_state;
+		chan_state = &l1ts->chan_state[i];
 		/* skip if pchan type does not match pdch flag */
-		if ((trx_sched_multiframes[l1h->mf_index[tn]].pchan
+		if ((trx_sched_multiframes[l1ts->mf_index].pchan
 							== GSM_PCHAN_PDCH)
 						!= trx_chan_desc[i].pdch)
 			continue;
 		if (trx_chan_desc[i].chan_nr == (chan_nr & 0xf8)
 		 && trx_chan_desc[i].link_id == link_id) {
-			chan_state = &l1h->chan_states[tn][i];
 			rc = 0;
 			if (chan_state->active == active)
 				continue;
 			LOGP(DL1C, LOGL_NOTICE, "%s %s on trx=%d ts=%d\n",
 				(active) ? "Activating" : "Deactivating",
-				trx_chan_desc[i].name, l1h->trx->nr, tn);
+				trx_chan_desc[i].name, l1t->trx->nr, tn);
 			if (active)
 				memset(chan_state, 0, sizeof(*chan_state));
 			chan_state->active = active;
@@ -2554,12 +2575,14 @@  int trx_sched_set_lchan(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t link_id,
 				talloc_free(chan_state->ul_bursts);
 				chan_state->ul_bursts = NULL;
 			}
+			if (!active)
+				chan_state->ho_rach_detect = 0;
 		}
 	}
 
 	/* disable handover detection (on deactivation) */
-	if (l1h->ho_rach_detect[tn][ss]) {
-		l1h->ho_rach_detect[tn][ss] = 0;
+	if (!active) {
+		struct trx_l1h *l1h = trx_l1h_hdl(l1t->trx);
 		trx_if_cmd_nohandover(l1h, tn, ss);
 	}
 
@@ -2567,28 +2590,30 @@  int trx_sched_set_lchan(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t link_id,
 }
 
 /* setting all logical channels given attributes to active/inactive */
-int trx_sched_set_mode(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t rsl_cmode,
+int trx_sched_set_mode(struct l1sched_trx *l1t, uint8_t chan_nr, uint8_t rsl_cmode,
 	uint8_t tch_mode, int codecs, uint8_t codec0, uint8_t codec1,
 	uint8_t codec2, uint8_t codec3, uint8_t initial_id, uint8_t handover)
 {
 	uint8_t tn = L1SAP_CHAN2TS(chan_nr);
+	struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
+	struct trx_l1h *l1h = trx_l1h_hdl(l1t->trx);
 	uint8_t ss = l1sap_chan2ss(chan_nr);
 	int i;
 	int rc = -EINVAL;
-	struct trx_chan_state *chan_state;
+	struct l1sched_chan_state *chan_state;
 
 	/* no mode for PDCH */
-	if (trx_sched_multiframes[l1h->mf_index[tn]].pchan == GSM_PCHAN_PDCH)
+	if (trx_sched_multiframes[l1ts->mf_index].pchan == GSM_PCHAN_PDCH)
 		return 0;
 
 	/* look for all matching chan_nr/link_id */
 	for (i = 0; i < _TRX_CHAN_MAX; i++) {
 		if (trx_chan_desc[i].chan_nr == (chan_nr & 0xf8)
 		 && trx_chan_desc[i].link_id == 0x00) {
-			chan_state = &l1h->chan_states[tn][i];
+			chan_state = &l1ts->chan_state[i];
 			LOGP(DL1C, LOGL_NOTICE, "Set mode %u, %u, handover %u "
 				"on %s of trx=%d ts=%d\n", rsl_cmode, tch_mode,
-				handover, trx_chan_desc[i].name, l1h->trx->nr,
+				handover, trx_chan_desc[i].name, l1t->trx->nr,
 				tn);
 			chan_state->rsl_cmode = rsl_cmode;
 			chan_state->tch_mode = tch_mode;
@@ -2617,10 +2642,8 @@  int trx_sched_set_mode(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t rsl_cmode,
 	 * disable handover, if state is still set, since we might not know
 	 * the actual state of transceiver (due to loss of link) */
 	if (handover) {
-		l1h->ho_rach_detect[tn][ss] = 1;
 		trx_if_cmd_handover(l1h, tn, ss);
-	} else if (l1h->ho_rach_detect[tn][ss]) {
-		l1h->ho_rach_detect[tn][ss] = 0;
+	} else {
 		trx_if_cmd_nohandover(l1h, tn, ss);
 	}
 
@@ -2628,16 +2651,17 @@  int trx_sched_set_mode(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t rsl_cmode,
 }
 
 /* setting cipher on logical channels */
-int trx_sched_set_cipher(struct trx_l1h *l1h, uint8_t chan_nr, int downlink,
+int trx_sched_set_cipher(struct l1sched_trx *l1t, uint8_t chan_nr, int downlink,
 	int algo, uint8_t *key, int key_len)
 {
 	uint8_t tn = L1SAP_CHAN2TS(chan_nr);
+	struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
 	int i;
 	int rc = -EINVAL;
-	struct trx_chan_state *chan_state;
+	struct l1sched_chan_state *chan_state;
 
 	/* no cipher for PDCH */
-	if (trx_sched_multiframes[l1h->mf_index[tn]].pchan == GSM_PCHAN_PDCH)
+	if (trx_sched_multiframes[l1ts->mf_index].pchan == GSM_PCHAN_PDCH)
 		return 0;
 
 	/* no algorithm given means a5/0 */
@@ -2655,11 +2679,11 @@  int trx_sched_set_cipher(struct trx_l1h *l1h, uint8_t chan_nr, int downlink,
 		if (trx_chan_desc[i].pdch)
 			continue;
 		if (trx_chan_desc[i].chan_nr == (chan_nr & 0xf8)) {
-			chan_state = &l1h->chan_states[tn][i];
+			chan_state = &l1ts->chan_state[i];
 			LOGP(DL1C, LOGL_NOTICE, "Set a5/%d %s for %s on trx=%d "
 				"ts=%d\n", algo,
 				(downlink) ? "downlink" : "uplink",
-				trx_chan_desc[i].name, l1h->trx->nr, tn);
+				trx_chan_desc[i].name, l1t->trx->nr, tn);
 			if (downlink) {
 				chan_state->dl_encr_algo = algo;
 				memcpy(chan_state->dl_encr_key, key, key_len);
@@ -2677,21 +2701,22 @@  int trx_sched_set_cipher(struct trx_l1h *l1h, uint8_t chan_nr, int downlink,
 }
 
 /* process ready-to-send */
-static int trx_sched_rts(struct trx_l1h *l1h, uint8_t tn, uint32_t fn)
+static int trx_sched_rts(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn)
 {
+	struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
 	const struct trx_sched_frame *frame;
 	uint8_t offset, period, bid;
 	trx_sched_rts_func *func;
 	enum trx_chan_type chan;
 
 	/* no multiframe set */
-	if (!l1h->mf_index[tn])
+	if (!l1ts->mf_index)
 		return 0;
 
 	/* get frame from multiframe */
-	period = l1h->mf_period[tn];
+	period = l1ts->mf_period;
 	offset = fn % period;
-	frame = l1h->mf_frames[tn] + offset;
+	frame = l1ts->mf_frames + offset;
 
 	chan = frame->dl_chan;
 	bid = frame->dl_bid;
@@ -2707,49 +2732,51 @@  static int trx_sched_rts(struct trx_l1h *l1h, uint8_t tn, uint32_t fn)
 
 	/* check if channel is active */
 	if (!trx_chan_desc[chan].auto_active
-	 && !l1h->chan_states[tn][chan].active)
+	 && !l1ts->chan_state[chan].active)
 	 	return -EINVAL;
 
-	return func(l1h, tn, fn, frame->dl_chan);
+	return func(l1t, tn, fn, frame->dl_chan);
 }
 
 /* process downlink burst */
-static const ubit_t *trx_sched_dl_burst(struct trx_l1h *l1h, uint8_t tn,
+static const ubit_t *trx_sched_dl_burst(struct l1sched_trx *l1t, uint8_t tn,
 	uint32_t fn)
 {
+	struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
+	struct l1sched_chan_state *l1cs;
 	const struct trx_sched_frame *frame;
 	uint8_t offset, period, bid;
 	trx_sched_dl_func *func;
 	enum trx_chan_type chan;
 	ubit_t *bits = NULL;
 
-	if (!l1h->mf_index[tn])
+	if (!l1ts->mf_index)
 		goto no_data;
 
 	/* get frame from multiframe */
-	period = l1h->mf_period[tn];
+	period = l1ts->mf_period;
 	offset = fn % period;
-	frame = l1h->mf_frames[tn] + offset;
+	frame = l1ts->mf_frames + offset;
 
 	chan = frame->dl_chan;
 	bid = frame->dl_bid;
 	func = trx_chan_desc[chan].dl_fn;
 
+	l1cs = &l1ts->chan_state[chan];
+
 	/* check if channel is active */
-	if (!trx_chan_desc[chan].auto_active
-	 && !l1h->chan_states[tn][chan].active)
+	if (!trx_chan_desc[chan].auto_active && !l1cs->active)
 	 	goto no_data;
 
 	/* get burst from function */
-	bits = func(l1h, tn, fn, chan, bid);
+	bits = func(l1t, tn, fn, chan, bid);
 
 	/* encrypt */
-	if (bits && l1h->chan_states[tn][chan].dl_encr_algo) {
+	if (bits && l1cs->dl_encr_algo) {
 		ubit_t ks[114];
 		int i;
 
-		osmo_a5(l1h->chan_states[tn][chan].dl_encr_algo,
-			l1h->chan_states[tn][chan].dl_encr_key, fn, ks, NULL);
+		osmo_a5(l1cs->dl_encr_algo, l1cs->dl_encr_key, fn, ks, NULL);
 		for (i = 0; i < 57; i++) {
 			bits[i + 3] ^= ks[i];
 			bits[i + 88] ^= ks[i + 57];
@@ -2758,7 +2785,7 @@  static const ubit_t *trx_sched_dl_burst(struct trx_l1h *l1h, uint8_t tn,
 
 no_data:
 	/* in case of C0, we need a dummy burst to maintain RF power */
-	if (bits == NULL && l1h->trx == l1h->trx->bts->c0) {
+	if (bits == NULL && l1t->trx == l1t->trx->bts->c0) {
 if (0)		if (chan != TRXC_IDLE) // hack
 		LOGP(DL1C, LOGL_DEBUG, "No burst data for %s fn=%u ts=%u "
 			"burst=%d on C0, so filling with dummy burst\n",
@@ -2770,41 +2797,44 @@  if (0)		if (chan != TRXC_IDLE) // hack
 }
 
 /* process uplink burst */
-int trx_sched_ul_burst(struct trx_l1h *l1h, uint8_t tn, uint32_t current_fn,
+int trx_sched_ul_burst(struct l1sched_trx *l1t, uint8_t tn, uint32_t current_fn,
 	sbit_t *bits, int8_t rssi, float toa)
 {
+	struct l1sched_ts *l1ts = l1sched_trx_get_ts(l1t, tn);
+	struct l1sched_chan_state *l1cs;
 	const struct trx_sched_frame *frame;
 	uint8_t offset, period, bid;
 	trx_sched_ul_func *func;
 	enum trx_chan_type chan;
 	uint32_t fn, elapsed;
 
-	if (!l1h->mf_index[tn])
+	if (!l1ts->mf_index)
 		return -EINVAL;
 
 	/* calculate how many frames have been elapsed */
-	elapsed = (current_fn + GSM_HYPERFRAME - l1h->mf_last_fn[tn]) % GSM_HYPERFRAME;
+	elapsed = (current_fn + GSM_HYPERFRAME - l1ts->mf_last_fn) % GSM_HYPERFRAME;
 
 	/* start counting from last fn + 1, but only if not too many fn have
 	 * been elapsed */
 	if (elapsed < 10)
-		fn = (l1h->mf_last_fn[tn] + 1) % GSM_HYPERFRAME;
+		fn = (l1ts->mf_last_fn + 1) % GSM_HYPERFRAME;
 	else
 		fn = current_fn;
 
 	while (42) {
 		/* get frame from multiframe */
-		period = l1h->mf_period[tn];
+		period = l1ts->mf_period;
 		offset = fn % period;
-		frame = l1h->mf_frames[tn] + offset;
+		frame = l1ts->mf_frames + offset;
 
 		chan = frame->ul_chan;
 		bid = frame->ul_bid;
 		func = trx_chan_desc[chan].ul_fn;
 
+		l1cs = &l1ts->chan_state[chan];
+
 		/* check if channel is active */
-		if (!trx_chan_desc[chan].auto_active
-		 && !l1h->chan_states[tn][chan].active)
+		if (!trx_chan_desc[chan].auto_active && !l1cs->active)
 			goto next_frame;
 
 		/* omit bursts which have no handler, like IDLE bursts */
@@ -2814,12 +2844,12 @@  int trx_sched_ul_burst(struct trx_l1h *l1h, uint8_t tn, uint32_t current_fn,
 		/* put burst to function */
 		if (fn == current_fn) {
 			/* decrypt */
-			if (bits && l1h->chan_states[tn][chan].ul_encr_algo) {
+			if (bits && l1cs->ul_encr_algo) {
 				ubit_t ks[114];
 				int i;
 
-				osmo_a5(l1h->chan_states[tn][chan].ul_encr_algo,
-					l1h->chan_states[tn][chan].ul_encr_key,
+				osmo_a5(l1cs->ul_encr_algo,
+					l1cs->ul_encr_key,
 					fn, NULL, ks);
 				for (i = 0; i < 57; i++) {
 					if (ks[i])
@@ -2829,13 +2859,12 @@  int trx_sched_ul_burst(struct trx_l1h *l1h, uint8_t tn, uint32_t current_fn,
 				}
 			}
 
-			func(l1h, tn, fn, chan, bid, bits, rssi, toa);
-		} else if (chan != TRXC_RACH
-		        && !l1h->chan_states[tn][chan].ho_rach_detect) {
+			func(l1t, tn, fn, chan, bid, bits, rssi, toa);
+		} else if (chan != TRXC_RACH && !l1cs->ho_rach_detect) {
 			sbit_t spare[148];
 
 			memset(spare, 0, 148);
-			func(l1h, tn, fn, chan, bid, spare, -128, 0);
+			func(l1t, tn, fn, chan, bid, spare, -128, 0);
 		}
 
 next_frame:
@@ -2846,7 +2875,7 @@  next_frame:
 		fn = (fn + 1) % GSM_HYPERFRAME;
 	}
 
-	l1h->mf_last_fn[tn] = fn;
+	l1ts->mf_last_fn = fn;
 
 	return 0;
 }
@@ -2855,7 +2884,6 @@  next_frame:
 static int trx_sched_fn(uint32_t fn)
 {
 	struct gsm_bts_trx *trx;
-	struct trx_l1h *l1h;
 	uint8_t tn;
 	const ubit_t *bits;
 	uint8_t gain;
@@ -2869,22 +2897,20 @@  static int trx_sched_fn(uint32_t fn)
 
 	/* process every TRX */
 	llist_for_each_entry(trx, &bts->trx_list, list) {
-		l1h = trx_l1h_hdl(trx);
+		struct trx_l1h *l1h = trx_l1h_hdl(trx);
+		struct l1sched_trx *l1t = trx_l1sched_hdl(trx);
 
 		/* we don't schedule, if power is off */
 		if (!l1h->config.poweron)
 			continue;
 
 		/* process every TS of TRX */
-		for (tn = 0; tn < TRX_NR_TS; tn++) {
-			/* ignore disabled slots */
-			if (!(l1h->config.slotmask & (1 << tn)))
-				continue;
+		for (tn = 0; tn < ARRAY_SIZE(l1t->ts); tn++) {
 			/* ready-to-send */
-			trx_sched_rts(l1h, tn,
+			trx_sched_rts(l1t, tn,
 				(fn + trx_rts_advance) % GSM_HYPERFRAME);
 			/* get burst for FN */
-			bits = trx_sched_dl_burst(l1h, tn, fn);
+			bits = trx_sched_dl_burst(l1t, tn, fn);
 			if (!bits) {
 				/* if no bits, send no burst */
 				continue;
@@ -2910,6 +2936,7 @@  extern int quit;
 /* this timer fires for every FN to be processed */
 static void trx_ctrl_timer_cb(void *data)
 {
+	struct gsm_bts *bts = data;
 	struct timeval tv_now, *tv_clock = &transceiver_clock_tv;
 	int32_t elapsed;
 
@@ -2926,7 +2953,7 @@  no_clock:
 		/* close all logical channels and reset timeslots */
 		llist_for_each_entry(trx, &bts->trx_list, list) {
 			trx_if_flush(trx_l1h_hdl(trx));
-			trx_sched_reset(trx_l1h_hdl(trx));
+			trx_sched_reset(trx_l1sched_hdl(trx));
 			if (trx->nr == 0)
 				trx_if_cmd_poweroff(trx_l1h_hdl(trx));
 		}
@@ -3061,3 +3088,8 @@  new_clock:
 	return 0;
 }
 
+struct l1sched_ts *l1sched_trx_get_ts(struct l1sched_trx *l1t, uint8_t tn)
+{
+	OSMO_ASSERT(tn < ARRAY_SIZE(l1t->ts));
+	return &l1t->ts[tn];
+}
diff --git a/src/osmo-bts-trx/scheduler.h b/src/osmo-bts-trx/scheduler.h
index 3e30693..c153b9e 100644
--- a/src/osmo-bts-trx/scheduler.h
+++ b/src/osmo-bts-trx/scheduler.h
@@ -1,6 +1,129 @@ 
 #ifndef TRX_SCHEDULER_H
 #define TRX_SCHEDULER_H
 
+/* These types define the different channels on a multiframe.
+ * Each channel has queues and can be activated individually.
+ */
+enum trx_chan_type {
+	TRXC_IDLE = 0,
+	TRXC_FCCH,
+	TRXC_SCH,
+	TRXC_BCCH,
+	TRXC_RACH,
+	TRXC_CCCH,
+	TRXC_TCHF,
+	TRXC_TCHH_0,
+	TRXC_TCHH_1,
+	TRXC_SDCCH4_0,
+	TRXC_SDCCH4_1,
+	TRXC_SDCCH4_2,
+	TRXC_SDCCH4_3,
+	TRXC_SDCCH8_0,
+	TRXC_SDCCH8_1,
+	TRXC_SDCCH8_2,
+	TRXC_SDCCH8_3,
+	TRXC_SDCCH8_4,
+	TRXC_SDCCH8_5,
+	TRXC_SDCCH8_6,
+	TRXC_SDCCH8_7,
+	TRXC_SACCHTF,
+	TRXC_SACCHTH_0,
+	TRXC_SACCHTH_1,
+	TRXC_SACCH4_0,
+	TRXC_SACCH4_1,
+	TRXC_SACCH4_2,
+	TRXC_SACCH4_3,
+	TRXC_SACCH8_0,
+	TRXC_SACCH8_1,
+	TRXC_SACCH8_2,
+	TRXC_SACCH8_3,
+	TRXC_SACCH8_4,
+	TRXC_SACCH8_5,
+	TRXC_SACCH8_6,
+	TRXC_SACCH8_7,
+	TRXC_PDTCH,
+	TRXC_PTCCH,
+	_TRX_CHAN_MAX
+};
+
+/* States each channel on a multiframe */
+struct l1sched_chan_state {
+	/* scheduler */
+	uint8_t			active;		/* Channel is active */
+	ubit_t			*dl_bursts;	/* burst buffer for TX */
+	sbit_t			*ul_bursts;	/* burst buffer for RX */
+	uint32_t		ul_first_fn;	/* fn of first burst */
+	uint8_t			ul_mask;	/* mask of received bursts */
+
+	/* RSSI / TOA */
+	uint8_t			rssi_num;	/* number of RSSI values */
+	float			rssi_sum;	/* sum of RSSI values */
+	uint8_t			toa_num;	/* number of TOA values */
+	float			toa_sum;	/* sum of TOA values */
+
+	/* loss detection */
+	uint8_t			lost;		/* (SACCH) loss detection */
+
+	/* mode */
+	uint8_t			rsl_cmode, tch_mode; /* mode for TCH channels */
+
+	/* AMR */
+	uint8_t			codec[4];	/* 4 possible codecs for amr */
+	int			codecs;		/* number of possible codecs */
+	float			ber_sum;	/* sum of bit error rates */
+	int			ber_num;	/* number of bit error rates */
+	uint8_t			ul_ft;		/* current uplink FT index */
+	uint8_t			dl_ft;		/* current downlink FT index */
+	uint8_t			ul_cmr;		/* current uplink CMR index */
+	uint8_t			dl_cmr;		/* current downlink CMR index */
+	uint8_t			amr_loop;	/* if AMR loop is enabled */
+
+	/* TCH/H */
+	uint8_t			dl_ongoing_facch; /* FACCH/H on downlink */
+	uint8_t			ul_ongoing_facch; /* FACCH/H on uplink */
+
+	/* encryption */
+	int			ul_encr_algo;	/* A5/x encry algo downlink */
+	int			dl_encr_algo;	/* A5/x encry algo uplink */
+	int			ul_encr_key_len;
+	int			dl_encr_key_len;
+	uint8_t			ul_encr_key[MAX_A5_KEY_LEN];
+	uint8_t			dl_encr_key[MAX_A5_KEY_LEN];
+
+	/* measurements */
+	struct {
+		uint8_t		clock;		/* cyclic clock counter */
+		int8_t		rssi[32];	/* last RSSI values */
+		int		rssi_count;	/* received RSSI values */
+		int		rssi_valid_count; /* number of stored value */
+		int		rssi_got_burst; /* any burst received so far */
+		float		toa_sum;	/* sum of TOA values */
+		int		toa_num;	/* number of TOA value */
+	} meas;
+
+	/* handover */
+	uint8_t			ho_rach_detect;	/* if rach detection is on */
+};
+
+struct l1sched_ts {
+	uint8_t 		mf_index;	/* selected multiframe index */
+	uint32_t 		mf_last_fn;	/* last received frame number */
+	uint8_t			mf_period;	/* period of multiframe */
+	const struct trx_sched_frame *mf_frames; /* pointer to frame layout */
+
+	struct llist_head	dl_prims;	/* Queue primitves for TX */
+
+	/* Channel states for all logical channels */
+	struct l1sched_chan_state chan_state[_TRX_CHAN_MAX];
+};
+
+struct l1sched_trx {
+	struct gsm_bts_trx	*trx;
+	struct l1sched_ts       ts[TRX_NR_TS];
+};
+
+struct l1sched_ts *l1sched_trx_get_ts(struct l1sched_trx *l1t, uint8_t tn);
+
 /*! \brief how many frame numbers in advance we should send bursts to PHY */
 extern uint32_t trx_clock_advance;
 /*! \brief advance RTS.ind to L2 by that many clocks */
@@ -10,43 +133,43 @@  extern uint32_t transceiver_last_fn;
 
 
 /*! \brief Initialize the scheudler data structures */
-int trx_sched_init(struct trx_l1h *l1h);
+int trx_sched_init(struct l1sched_trx *l1t);
 
 /*! \brief De-initialize the scheudler data structures */
-void trx_sched_exit(struct trx_l1h *l1h);
+void trx_sched_exit(struct l1sched_trx *l1t);
 
 /*! \brief Handle a PH-DATA.req from L2 down to L1 */
-int trx_sched_ph_data_req(struct trx_l1h *l1h, struct osmo_phsap_prim *l1sap);
+int trx_sched_ph_data_req(struct l1sched_trx *l1t, struct osmo_phsap_prim *l1sap);
 
 /*! \brief Handle a PH-TCH.req from L2 down to L1 */
-int trx_sched_tch_req(struct trx_l1h *l1h, struct osmo_phsap_prim *l1sap);
+int trx_sched_tch_req(struct l1sched_trx *l1t, struct osmo_phsap_prim *l1sap);
 
 /*! \brief PHY informs us of new (current) GSM freme nunmber */
 int trx_sched_clock(uint32_t fn);
 
 /*! \brief handle an UL burst received by PHY */
-int trx_sched_ul_burst(struct trx_l1h *l1h, uint8_t tn, uint32_t fn,
+int trx_sched_ul_burst(struct l1sched_trx *l1t, uint8_t tn, uint32_t fn,
         sbit_t *bits, int8_t rssi, float toa);
 
 /*! \brief set multiframe scheduler to given physical channel config */
-int trx_sched_set_pchan(struct trx_l1h *l1h, uint8_t tn,
+int trx_sched_set_pchan(struct l1sched_trx *l1t, uint8_t tn,
         enum gsm_phys_chan_config pchan);
 
 /*! \brief set all matching logical channels active/inactive */
-int trx_sched_set_lchan(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t link_id,
+int trx_sched_set_lchan(struct l1sched_trx *l1t, uint8_t chan_nr, uint8_t link_id,
 	int active);
 
 /*! \brief set mode of all matching logical channels to given mode(s) */
-int trx_sched_set_mode(struct trx_l1h *l1h, uint8_t chan_nr, uint8_t rsl_cmode,
+int trx_sched_set_mode(struct l1sched_trx *l1t, uint8_t chan_nr, uint8_t rsl_cmode,
 	uint8_t tch_mode, int codecs, uint8_t codec0, uint8_t codec1,
 	uint8_t codec2, uint8_t codec3, uint8_t initial_codec,
 	uint8_t handover);
 
 /*! \brief set ciphering on given logical channels */
-int trx_sched_set_cipher(struct trx_l1h *l1h, uint8_t chan_nr, int downlink,
+int trx_sched_set_cipher(struct l1sched_trx *l1t, uint8_t chan_nr, int downlink,
         int algo, uint8_t *key, int key_len);
 
 /* \brief close all logical channels and reset timeslots */
-void trx_sched_reset(struct trx_l1h *l1h);
+void trx_sched_reset(struct l1sched_trx *l1t);
 
 #endif /* TRX_SCHEDULER_H */
diff --git a/src/osmo-bts-trx/trx_if.c b/src/osmo-bts-trx/trx_if.c
index 419fea6..fef2064 100644
--- a/src/osmo-bts-trx/trx_if.c
+++ b/src/osmo-bts-trx/trx_if.c
@@ -454,7 +454,7 @@  static int trx_data_read_cb(struct osmo_fd *ofd, unsigned int what)
 	fprintf(stderr, "%s\n", deb);
 #endif
 
-	trx_sched_ul_burst(l1h, tn, fn, bits, rssi, toa);
+	trx_sched_ul_burst(&l1h->l1s, tn, fn, bits, rssi, toa);
 
 	return 0;
 }