Change in osmo-pcu[master]: Unify BTS into a C usable structure

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

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

pespin gerrit-no-reply at lists.osmocom.org
Tue Jan 19 16:40:05 UTC 2021


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

Change subject: Unify BTS into a C usable structure
......................................................................

Unify BTS into a C usable structure

Previous work on BTS class started to get stuff out of the C++ struct
 into a C struct (BTS -> struct gprs_glcmac_bts) so that some parts of
it were accessible from C code. Doing so, however, ended up being messy
too, since all code needs to be switching from one object to another,
which actually refer to the same logical component.

Let's instead rejoin the structures and make sure the struct is
accessible and usable from both C and C++ code by rewriting all methods
to be C compatible and converting 3 allocated suboject as pointers.
This way BTS can internally still use those C++ objects while providing
a clean APi to both C and C++ code.

Change-Id: I7d12c896c5ded659ca9d3bff4cf3a3fc857db9dd
---
M src/bts.cpp
M src/bts.h
M src/encoding.cpp
M src/gprs_bssgp_pcu.cpp
M src/gprs_ms.c
M src/gprs_ms.h
M src/gprs_ms_storage.cpp
M src/gprs_ms_storage.h
M src/gprs_pcu.c
M src/gprs_pcu.h
M src/gprs_rlcmac_sched.cpp
M src/gprs_rlcmac_ts_alloc.cpp
M src/gsm_timer.cpp
M src/llc.cpp
M src/llc.h
M src/pcu_l1_if.cpp
M src/pcu_main.cpp
M src/pcu_vty_functions.cpp
M src/pdch.cpp
M src/pdch.h
M src/poll_controller.cpp
M src/poll_controller.h
M src/rlc.cpp
M src/rlc.h
M src/sba.cpp
M src/sba.h
M src/tbf.cpp
M src/tbf.h
M src/tbf_dl.cpp
M src/tbf_dl.h
M src/tbf_ul.cpp
M src/tbf_ul.h
M tests/alloc/AllocTest.cpp
M tests/alloc/MslotTest.cpp
M tests/app_info/AppInfoTest.cpp
M tests/edge/EdgeTest.cpp
M tests/emu/pcu_emu.cpp
M tests/fn/FnTest.cpp
M tests/ms/MsTest.cpp
M tests/tbf/TbfTest.cpp
M tests/types/TypesTest.cpp
41 files changed, 1,048 insertions(+), 1,194 deletions(-)

Approvals:
  osmith: Looks good to me, approved
  Jenkins Builder: Verified



diff --git a/src/bts.cpp b/src/bts.cpp
index a50e8ae..294739c 100644
--- a/src/bts.cpp
+++ b/src/bts.cpp
@@ -31,6 +31,8 @@
 #include <gprs_debug.h>
 #include <cxx_linuxlist.h>
 #include <pdch.h>
+#include <gprs_ms_storage.h>
+#include <sba.h>
 
 extern "C" {
 	#include <osmocom/core/talloc.h>
@@ -190,9 +192,20 @@
 	bts_stat_item_description,
 };
 
-static void bts_init(struct gprs_rlcmac_bts *bts, BTS* bts_obj)
+static void bts_init(struct gprs_rlcmac_bts *bts, struct gprs_pcu *pcu)
 {
-	memset(bts, 0, sizeof(*bts));
+	bts->pcu = pcu;
+
+	bts->pollController = new PollController(*bts);
+	bts->sba = new SBAController(*bts);
+	bts->ms_store = new GprsMsStorage(bts);
+
+	bts->cur_fn = 0;
+	bts->cur_blk_fn = -1;
+	bts->max_cs_dl = MAX_GPRS_CS;
+	bts->max_cs_ul = MAX_GPRS_CS;
+	bts->max_mcs_dl = MAX_EDGE_MCS;
+	bts->max_mcs_ul = MAX_EDGE_MCS;
 	bts->initial_cs_dl = bts->initial_cs_ul = 1;
 	bts->initial_mcs_dl = bts->initial_mcs_ul = 1;
 	bts->cs_mask = 1 << 0;  /* CS-1 always enabled by default */
@@ -202,15 +215,17 @@
 	bts->si13_is_set = false;
 
 	bts->app_info = NULL;
-	bts->bts = bts_obj;
 	bts->T_defs_bts = T_defs_bts;
 	osmo_tdefs_reset(bts->T_defs_bts);
 
+	INIT_LLIST_HEAD(&bts->ul_tbfs);
+	INIT_LLIST_HEAD(&bts->dl_tbfs);
+
 	/* initialize back pointers */
 	for (size_t trx_no = 0; trx_no < ARRAY_SIZE(bts->trx); ++trx_no) {
 		struct gprs_rlcmac_trx *trx = &bts->trx[trx_no];
 		trx->trx_no = trx_no;
-		trx->bts = bts_obj;
+		trx->bts = bts;
 
 		for (size_t ts_no = 0; ts_no < ARRAY_SIZE(trx->pdch); ++ts_no) {
 			struct gprs_rlcmac_pdch *pdch = &trx->pdch[ts_no];
@@ -219,89 +234,57 @@
 			pdch->trx = trx;
 		}
 	}
-}
-
-BTS* BTS::main_bts()
-{
-	return the_pcu->bts;
-}
-
-struct gprs_rlcmac_bts *BTS::bts_data()
-{
-	return &m_bts;
-}
-
-struct gprs_rlcmac_bts *bts_main_data()
-{
-	return BTS::main_bts()->bts_data();
-}
-
-void bts_cleanup()
-{
-	return BTS::main_bts()->cleanup();
-}
-
-struct rate_ctr_group *bts_main_data_stats()
-{
-	return BTS::main_bts()->rate_counters();
-}
-
-BTS::BTS(struct gprs_pcu *pcu)
-	: pcu(pcu)
-	, m_cur_fn(0)
-	, m_cur_blk_fn(-1)
-	, m_max_cs_dl(MAX_GPRS_CS)
-	, m_max_cs_ul(MAX_GPRS_CS)
-	, m_max_mcs_dl(MAX_EDGE_MCS)
-	, m_max_mcs_ul(MAX_EDGE_MCS)
-	, m_pollController(*this)
-	, m_sba(*this)
-	, m_ms_store(this)
-{
-	bts_init(&m_bts, this);
 
 	/* The static allocator might have already registered the counter group.
 	   If this happens and we still called explicitly (in tests/ for example)
 	   than just allocate the group with different index.
 	   This shall be removed once weget rid of BTS singleton */
 	if (rate_ctr_get_group_by_name_idx(bts_ctrg_desc.group_name_prefix, 0))
-		m_ratectrs = rate_ctr_group_alloc(tall_pcu_ctx, &bts_ctrg_desc, 1);
+		bts->ratectrs = rate_ctr_group_alloc(tall_pcu_ctx, &bts_ctrg_desc, 1);
 	else
-		m_ratectrs = rate_ctr_group_alloc(tall_pcu_ctx, &bts_ctrg_desc, 0);
-	OSMO_ASSERT(m_ratectrs);
+		bts->ratectrs = rate_ctr_group_alloc(tall_pcu_ctx, &bts_ctrg_desc, 0);
+	OSMO_ASSERT(bts->ratectrs);
 
-	m_statg = osmo_stat_item_group_alloc(tall_pcu_ctx, &bts_statg_desc, 0);
-	OSMO_ASSERT(m_statg);
+	bts->statg = osmo_stat_item_group_alloc(tall_pcu_ctx, &bts_statg_desc, 0);
+	OSMO_ASSERT(bts->statg);
 }
 
-void BTS::cleanup()
+struct gprs_rlcmac_bts *bts_main_data()
+{
+	return the_pcu->bts;
+}
+
+struct rate_ctr_group *bts_main_data_stats()
+{
+	return bts_rate_counters(the_pcu->bts);
+}
+
+static void bts_cleanup(gprs_rlcmac_bts *bts)
 {
 	/* this can cause counter updates and must not be left to the
 	 * m_ms_store's destructor */
-	m_ms_store.cleanup();
+	bts->ms_store->cleanup();
+	delete bts->ms_store;
+	delete bts->sba;
+	delete bts->pollController;
 
-	if (m_ratectrs) {
-		rate_ctr_group_free(m_ratectrs);
-		m_ratectrs = NULL;
+	if (bts->ratectrs) {
+		rate_ctr_group_free(bts->ratectrs);
+		bts->ratectrs = NULL;
 	}
 
-	if (m_statg) {
-		osmo_stat_item_group_free(m_statg);
-		m_statg = NULL;
+	if (bts->statg) {
+		osmo_stat_item_group_free(bts->statg);
+		bts->statg = NULL;
 	}
 
-	if (m_bts.app_info) {
-		msgb_free(m_bts.app_info);
-		m_bts.app_info = NULL;
+	if (bts->app_info) {
+		msgb_free(bts->app_info);
+		bts->app_info = NULL;
 	}
 }
 
-BTS::~BTS()
-{
-	cleanup();
-}
-
-void BTS::set_current_frame_number(int fn)
+void bts_set_current_frame_number(struct gprs_rlcmac_bts *bts, int fn)
 {
 	/* The UL frame numbers lag 3 behind the DL frames and the data
 	 * indication is only sent after all 4 frames of the block have been
@@ -314,8 +297,8 @@
 	 * Values up to 50 frames have been observed under load. */
 	const static int max_delay = 60;
 
-	m_cur_fn = fn;
-	m_pollController.expireTimedout(m_cur_fn, max_delay);
+	bts->cur_fn = fn;
+	bts->pollController->expireTimedout(bts->cur_fn, max_delay);
 }
 
 static inline int delta_fn(int fn, int to)
@@ -323,7 +306,7 @@
 	return (fn + GSM_MAX_FN * 3 / 2 - to) % GSM_MAX_FN - GSM_MAX_FN/2;
 }
 
-void BTS::set_current_block_frame_number(int fn, unsigned max_delay)
+void bts_set_current_block_frame_number(struct gprs_rlcmac_bts *bts, int fn, unsigned max_delay)
 {
 	int delay = 0;
 	const int late_block_delay_thresh = 13;
@@ -332,41 +315,41 @@
 
 	/* frame numbers in the received blocks are assumed to be strongly
 	 * monotonic. */
-	if (m_cur_blk_fn >= 0) {
-		int delta = delta_fn(fn, m_cur_blk_fn);
+	if (bts->cur_blk_fn >= 0) {
+		int delta = delta_fn(fn, bts->cur_blk_fn);
 		if (delta <= 0)
 			return;
 	}
 
 	/* Check block delay vs. the current frame number */
-	if (current_frame_number() != 0)
-		delay = delta_fn(fn, current_frame_number());
+	if (bts_current_frame_number(bts) != 0)
+		delay = delta_fn(fn, bts_current_frame_number(bts));
 	if (delay <= -late_block_delay_thresh) {
 		LOGP(DRLCMAC, LOGL_NOTICE,
 			"Late RLC block, FN delta: %d FN: %d curFN: %d\n",
-			delay, fn, current_frame_number());
-		do_rate_ctr_inc(CTR_RLC_LATE_BLOCK);
+			delay, fn, bts_current_frame_number(bts));
+		bts_do_rate_ctr_inc(bts, CTR_RLC_LATE_BLOCK);
 	}
 
-	m_cur_blk_fn = fn;
+	bts->cur_blk_fn = fn;
 	if (delay < fn_update_ok_min_delay || delay > fn_update_ok_max_delay ||
-		current_frame_number() == 0)
-		m_cur_fn = fn;
+		bts_current_frame_number(bts) == 0)
+		bts->cur_fn = fn;
 
-	m_pollController.expireTimedout(fn, max_delay);
+	bts->pollController->expireTimedout(fn, max_delay);
 }
 
-int BTS::add_paging(uint8_t chan_needed, const struct osmo_mobile_identity *mi)
+int bts_add_paging(struct gprs_rlcmac_bts *bts, uint8_t chan_needed, const struct osmo_mobile_identity *mi)
 {
 	uint8_t l, trx, ts, any_tbf = 0;
 	struct gprs_rlcmac_tbf *tbf;
-	LListHead<gprs_rlcmac_tbf> *pos;
+	struct llist_item *pos;
 	uint8_t slot_mask[8];
 	int8_t first_ts; /* must be signed */
 
-	LListHead<gprs_rlcmac_tbf> *tbfs_lists[] = {
-		&m_ul_tbfs,
-		&m_dl_tbfs,
+	struct llist_head *tbfs_lists[] = {
+		&bts->ul_tbfs,
+		&bts->dl_tbfs,
 		NULL
 	};
 
@@ -382,8 +365,8 @@
 	 * Don't mark, if TBF uses a different slot that is already marked. */
 	memset(slot_mask, 0, sizeof(slot_mask));
 	for (l = 0; tbfs_lists[l]; l++) {
-		llist_for_each(pos, tbfs_lists[l]) {
-			tbf = pos->entry();
+		llist_for_each_entry(pos, tbfs_lists[l], list) {
+			tbf = (struct gprs_rlcmac_tbf *)pos->entry;
 			first_ts = -1;
 			for (ts = 0; ts < 8; ts++) {
 				if (tbf->pdch[ts]) {
@@ -418,7 +401,7 @@
 		for (ts = 0; ts < 8; ts++) {
 			if ((slot_mask[trx] & (1 << ts))) {
 				/* schedule */
-				if (!m_bts.trx[trx].pdch[ts].add_paging(chan_needed, mi))
+				if (!bts->trx[trx].pdch[ts].add_paging(chan_needed, mi))
 					return -ENOMEM;
 
 				LOGP(DRLCMAC, LOGL_INFO, "Paging on PACCH of TRX=%d TS=%d\n", trx, ts);
@@ -433,8 +416,9 @@
 	return 0;
 }
 
-void BTS::send_gsmtap_rach(enum pcu_gsmtap_category categ, uint8_t channel,
-			   const struct rach_ind_params *rip)
+void bts_send_gsmtap_rach(struct gprs_rlcmac_bts *bts,
+			  enum pcu_gsmtap_category categ, uint8_t channel,
+			  const struct rach_ind_params *rip)
 {
 	struct pcu_l1_meas meas = { 0 };
 	uint8_t ra_buf[2];
@@ -450,37 +434,39 @@
 		ra_buf[0] = (uint8_t) (rip->ra & 0xff);
 	}
 
-	send_gsmtap_meas(categ, true, rip->trx_nr, rip->ts_nr, channel,
-			 rfn_to_fn(rip->rfn), ra_buf,
+	bts_send_gsmtap_meas(bts, categ, true, rip->trx_nr, rip->ts_nr, channel,
+			 bts_rfn_to_fn(bts, rip->rfn), ra_buf,
 			 rip->is_11bit ? 2 : 1, &meas);
 }
 
-void BTS::send_gsmtap(enum pcu_gsmtap_category categ, bool uplink, uint8_t trx_no,
-		      uint8_t ts_no, uint8_t channel, uint32_t fn,
-		      const uint8_t *data, unsigned int len)
+void bts_send_gsmtap(struct gprs_rlcmac_bts *bts,
+		     enum pcu_gsmtap_category categ, bool uplink, uint8_t trx_no,
+		     uint8_t ts_no, uint8_t channel, uint32_t fn,
+		     const uint8_t *data, unsigned int len)
 {
 	struct pcu_l1_meas meas = { 0 };
-	send_gsmtap_meas(categ, uplink, trx_no, ts_no, channel, fn, data, len, &meas);
+	bts_send_gsmtap_meas(bts, categ, uplink, trx_no, ts_no, channel, fn, data, len, &meas);
 }
 
-void BTS::send_gsmtap_meas(enum pcu_gsmtap_category categ, bool uplink, uint8_t trx_no,
-		      uint8_t ts_no, uint8_t channel, uint32_t fn,
-		      const uint8_t *data, unsigned int len, struct pcu_l1_meas *meas)
+void bts_send_gsmtap_meas(struct gprs_rlcmac_bts *bts,
+			  enum pcu_gsmtap_category categ, bool uplink, uint8_t trx_no,
+			  uint8_t ts_no, uint8_t channel, uint32_t fn,
+			  const uint8_t *data, unsigned int len, struct pcu_l1_meas *meas)
 {
 	uint16_t arfcn;
 
 	/* check if category is activated at all */
-	if (!(pcu->gsmtap_categ_mask & (1 << categ)))
+	if (!(bts->pcu->gsmtap_categ_mask & (1 << categ)))
 		return;
 
-	arfcn = m_bts.trx[trx_no].arfcn;
+	arfcn = bts->trx[trx_no].arfcn;
 	if (uplink)
 		arfcn |= GSMTAP_ARFCN_F_UPLINK;
 
 	/* GSMTAP needs the SNR here, but we only have C/I (meas->link_qual).
 	   Those are not the same, but there is no known way to convert them,
 	   let's pass C/I instead of nothing */
-	gsmtap_send(pcu->gsmtap, arfcn, ts_no, channel, 0, fn,
+	gsmtap_send(bts->pcu->gsmtap, arfcn, ts_no, channel, 0, fn,
 		    meas->rssi, meas->link_qual, data, len);
 }
 
@@ -493,48 +479,52 @@
 	return false;
 }
 
-gprs_rlcmac_dl_tbf *BTS::dl_tbf_by_poll_fn(uint32_t fn, uint8_t trx, uint8_t ts)
+struct gprs_rlcmac_dl_tbf *bts_dl_tbf_by_poll_fn(struct gprs_rlcmac_bts *bts, uint32_t fn, uint8_t trx, uint8_t ts)
 {
-	LListHead<gprs_rlcmac_tbf> *pos;
+	struct llist_item *pos;
+	struct gprs_rlcmac_tbf *tbf;
 
 	/* only one TBF can poll on specific TS/FN, because scheduler can only
 	 * schedule one downlink control block (with polling) at a FN per TS */
-	llist_for_each(pos, &m_dl_tbfs) {
-		if (tbf_check(pos->entry(), fn, trx, ts))
-			return as_dl_tbf(pos->entry());
+	llist_for_each_entry(pos, &bts->dl_tbfs, list) {
+		tbf = (struct gprs_rlcmac_tbf *)pos->entry;
+		if (tbf_check(tbf, fn, trx, ts))
+			return as_dl_tbf(tbf);
 	}
 	return NULL;
 }
 
-gprs_rlcmac_ul_tbf *BTS::ul_tbf_by_poll_fn(uint32_t fn, uint8_t trx, uint8_t ts)
+struct gprs_rlcmac_ul_tbf *bts_ul_tbf_by_poll_fn(struct gprs_rlcmac_bts *bts, uint32_t fn, uint8_t trx, uint8_t ts)
 {
-	LListHead<gprs_rlcmac_tbf> *pos;
+	struct llist_item *pos;
+	struct gprs_rlcmac_tbf *tbf;
 
 	/* only one TBF can poll on specific TS/FN, because scheduler can only
 	 * schedule one downlink control block (with polling) at a FN per TS */
-	llist_for_each(pos, &m_ul_tbfs) {
-		if (tbf_check(pos->entry(), fn, trx, ts))
-			return as_ul_tbf(pos->entry());
+	llist_for_each_entry(pos, &bts->ul_tbfs, list) {
+		tbf = (struct gprs_rlcmac_tbf *)pos->entry;
+		if (tbf_check(tbf, fn, trx, ts))
+			return as_ul_tbf(tbf);
 	}
 	return NULL;
 }
 
 /* lookup downlink TBF Entity (by TFI) */
-gprs_rlcmac_dl_tbf *BTS::dl_tbf_by_tfi(uint8_t tfi, uint8_t trx, uint8_t ts)
+struct gprs_rlcmac_dl_tbf *bts_dl_tbf_by_tfi(struct gprs_rlcmac_bts *bts, uint8_t tfi, uint8_t trx, uint8_t ts)
 {
 	if (trx >= 8 || ts >= 8)
 		return NULL;
 
-	return m_bts.trx[trx].pdch[ts].dl_tbf_by_tfi(tfi);
+	return bts->trx[trx].pdch[ts].dl_tbf_by_tfi(tfi);
 }
 
 /* lookup uplink TBF Entity (by TFI) */
-gprs_rlcmac_ul_tbf *BTS::ul_tbf_by_tfi(uint8_t tfi, uint8_t trx, uint8_t ts)
+struct gprs_rlcmac_ul_tbf *bts_ul_tbf_by_tfi(struct gprs_rlcmac_bts *bts, uint8_t tfi, uint8_t trx, uint8_t ts)
 {
 	if (trx >= 8 || ts >= 8)
 		return NULL;
 
-	return m_bts.trx[trx].pdch[ts].ul_tbf_by_tfi(tfi);
+	return bts->trx[trx].pdch[ts].ul_tbf_by_tfi(tfi);
 }
 
 static unsigned int trx_count_free_tfi(const struct gprs_rlcmac_trx *trx, enum gprs_rlcmac_tbf_direction dir, uint8_t *first_free_tfi)
@@ -576,7 +566,8 @@
  * that is currently not used in any PDCH of a the TRX with least TFIs currently
  * assigned. Negative values indicate errors.
  */
-int BTS::tfi_find_free(enum gprs_rlcmac_tbf_direction dir, uint8_t *_trx, int8_t use_trx) const
+int bts_tfi_find_free(const struct gprs_rlcmac_bts *bts, enum gprs_rlcmac_tbf_direction dir,
+		      uint8_t *_trx, int8_t use_trx)
 {
 	uint8_t trx_from, trx_to, trx;
 	uint8_t best_trx_nr = 0xff;
@@ -594,7 +585,7 @@
 	for (trx = trx_from; trx <= trx_to; trx++) {
 		uint8_t tmp_first_tfi;
 		unsigned int tmp_cnt;
-		tmp_cnt = trx_count_free_tfi(&m_bts.trx[trx], dir, &tmp_first_tfi);
+		tmp_cnt = trx_count_free_tfi(&bts->trx[trx], dir, &tmp_first_tfi);
 		if (tmp_cnt > best_cnt) {
 			best_cnt = tmp_cnt;
 			best_first_tfi = tmp_first_tfi;
@@ -615,7 +606,7 @@
 	return best_first_tfi;
 }
 
-int BTS::rcv_imm_ass_cnf(const uint8_t *data, uint32_t fn)
+int bts_rcv_imm_ass_cnf(struct gprs_rlcmac_bts *bts, const uint8_t *data, uint32_t fn)
 {
 	struct gprs_rlcmac_dl_tbf *dl_tbf = NULL;
 	uint8_t plen;
@@ -640,7 +631,7 @@
 	tlli |= (*data++) << 4;
 	tlli |= (*data++) >> 4;
 
-	ms = ms_by_tlli(tlli);
+	ms = bts_ms_by_tlli(bts, tlli, GSM_RESERVED_TMSI);
 	if (ms)
 		dl_tbf = ms_dl_tbf(ms);
 	if (!dl_tbf) {
@@ -658,7 +649,7 @@
 }
 
 /* Determine the full frame number from a relative frame number */
-uint32_t BTS::rfn_to_fn(int32_t rfn)
+uint32_t bts_rfn_to_fn(const struct gprs_rlcmac_bts *bts, int32_t rfn)
 {
 	int32_t m_cur_rfn;
 	int32_t fn;
@@ -682,11 +673,11 @@
 
 	/* Compute an internal relative frame number from the full internal
 	   frame number */
-	m_cur_rfn = m_cur_fn % RFN_MODULUS;
+	m_cur_rfn = bts->cur_fn % RFN_MODULUS;
 
 	/* Compute a "rounded" version of the internal frame number, which
 	 * exactly fits in the RFN_MODULUS raster */
-	fn_rounded = m_cur_fn - m_cur_rfn;
+	fn_rounded = bts->cur_fn - m_cur_rfn;
 
 	/* If the delta between the internal and the external relative frame
 	 * number exceeds a certain limit, we need to assume that the incoming
@@ -695,7 +686,7 @@
 	if (abs(rfn - m_cur_rfn) > RFN_THRESHOLD) {
 		LOGP(DRLCMAC, LOGL_DEBUG,
 		     "Race condition between rfn (%u) and m_cur_fn (%u) detected: rfn belongs to the previous modulus %u cycle, wrapping...\n",
-		     rfn, m_cur_fn, RFN_MODULUS);
+		     rfn, bts->cur_fn, RFN_MODULUS);
 		if (fn_rounded < RFN_MODULUS) {
 			LOGP(DRLCMAC, LOGL_DEBUG,
 			"Cornercase detected: wrapping crosses %u border\n",
@@ -819,7 +810,7 @@
 	return 0;
 }
 
-int BTS::rcv_rach(const struct rach_ind_params *rip)
+int bts_rcv_rach(struct gprs_rlcmac_bts *bts, const struct rach_ind_params *rip)
 {
 	struct chan_req_params chan_req = { 0 };
 	struct gprs_rlcmac_ul_tbf *tbf = NULL;
@@ -829,16 +820,16 @@
 	uint8_t tsc = 0;
 	int plen, rc;
 
-	do_rate_ctr_inc(CTR_RACH_REQUESTS);
+	bts_do_rate_ctr_inc(bts, CTR_RACH_REQUESTS);
 
 	if (rip->is_11bit)
-		do_rate_ctr_inc(CTR_11BIT_RACH_REQUESTS);
+		bts_do_rate_ctr_inc(bts, CTR_11BIT_RACH_REQUESTS);
 
 	/* Determine full frame number */
-	uint32_t Fn = rfn_to_fn(rip->rfn);
+	uint32_t Fn = bts_rfn_to_fn(bts, rip->rfn);
 	uint8_t ta = qta2ta(rip->qta);
 
-	send_gsmtap_rach(PCU_GSMTAP_C_UL_RACH, GSMTAP_CHANNEL_RACH, rip);
+	bts_send_gsmtap_rach(bts, PCU_GSMTAP_C_UL_RACH, GSMTAP_CHANNEL_RACH, rip);
 
 	LOGP(DRLCMAC, LOGL_DEBUG, "MS requests Uplink resource on CCCH/RACH: "
 	     "ra=0x%02x (%d bit) Fn=%u qta=%d\n", rip->ra,
@@ -851,7 +842,7 @@
 
 	if (chan_req.single_block)
 		LOGP(DRLCMAC, LOGL_DEBUG, "MS requests single block allocation\n");
-	else if (pcu->vty.force_two_phase) {
+	else if (bts->pcu->vty.force_two_phase) {
 		LOGP(DRLCMAC, LOGL_DEBUG, "MS requests single block allocation, "
 		     "but we force two phase access\n");
 		chan_req.single_block = true;
@@ -864,7 +855,7 @@
 
 	/* Should we allocate a single block or an Uplink TBF? */
 	if (chan_req.single_block) {
-		rc = sba()->alloc(&trx_no, &ts_no, &sb_fn, ta);
+		rc = bts_sba(bts)->alloc(&trx_no, &ts_no, &sb_fn, ta);
 		if (rc < 0) {
 			LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH resource for "
 			     "single block allocation: rc=%d\n", rc);
@@ -872,12 +863,12 @@
 			goto send_imm_ass_rej;
 		}
 
-		tsc = m_bts.trx[trx_no].pdch[ts_no].tsc;
+		tsc = bts->trx[trx_no].pdch[ts_no].tsc;
 		LOGP(DRLCMAC, LOGL_DEBUG, "Allocated a single block at "
 		     "SBFn=%u TRX=%u TS=%u\n", sb_fn, trx_no, ts_no);
 	} else {
-		GprsMs *ms = ms_alloc(0, chan_req.egprs_mslot_class);
-		tbf = tbf_alloc_ul_tbf(&m_bts, ms, -1, true);
+		GprsMs *ms = bts_alloc_ms(bts, 0, chan_req.egprs_mslot_class);
+		tbf = tbf_alloc_ul_tbf(bts, ms, -1, true);
 		if (!tbf) {
 			LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH resource for Uplink TBF\n");
 			/* Send RR Immediate Assignment Reject */
@@ -905,18 +896,18 @@
 		LOGP(DRLCMAC, LOGL_DEBUG, "Tx Immediate Assignment Reject on AGCH\n");
 		plen = Encoding::write_immediate_assignment_reject(
 			bv, rip->ra, Fn, rip->burst_type);
-		do_rate_ctr_inc(CTR_IMMEDIATE_ASSIGN_REJ);
+		bts_do_rate_ctr_inc(bts, CTR_IMMEDIATE_ASSIGN_REJ);
 	} else {
 		LOGP(DRLCMAC, LOGL_DEBUG, "Tx Immediate Assignment on AGCH: "
 		     "TRX=%u (ARFCN %u) TS=%u TA=%u TSC=%u TFI=%d USF=%d\n",
-		     trx_no, m_bts.trx[trx_no].arfcn & ~ARFCN_FLAG_MASK,
+		     trx_no, bts->trx[trx_no].arfcn & ~ARFCN_FLAG_MASK,
 		     ts_no, ta, tsc, tbf ? tbf->tfi() : -1, usf);
 		plen = Encoding::write_immediate_assignment(
-			&m_bts.trx[trx_no].pdch[ts_no], tbf, bv,
+			&bts->trx[trx_no].pdch[ts_no], tbf, bv,
 			false, rip->ra, Fn, ta, usf, false, sb_fn,
-			pcu->vty.alpha, pcu->vty.gamma, -1,
+			bts->pcu->vty.alpha, bts->pcu->vty.gamma, -1,
 			rip->burst_type);
-		do_rate_ctr_inc(CTR_IMMEDIATE_ASSIGN_UL_TBF);
+		bts_do_rate_ctr_inc(bts, CTR_IMMEDIATE_ASSIGN_UL_TBF);
 	}
 
 	if (plen >= 0)
@@ -937,14 +928,13 @@
 	324, 350, 376, 402,
 };
 
-int BTS::rcv_ptcch_rach(const struct rach_ind_params *rip)
+int bts_rcv_ptcch_rach(struct gprs_rlcmac_bts *bts, const struct rach_ind_params *rip)
 {
-	uint32_t fn416 = rfn_to_fn(rip->rfn) % 416;
-	struct gprs_rlcmac_bts *bts = bts_data();
+	uint32_t fn416 = bts_rfn_to_fn(bts, rip->rfn) % 416;
 	struct gprs_rlcmac_pdch *pdch;
 	uint8_t ss;
 
-	send_gsmtap_rach(PCU_GSMTAP_C_UL_PTCCH, GSMTAP_CHANNEL_PTCCH, rip);
+	bts_send_gsmtap_rach(bts, PCU_GSMTAP_C_UL_PTCCH, GSMTAP_CHANNEL_PTCCH, rip);
 
 	/* Prevent buffer overflow */
 	if (rip->trx_nr >= ARRAY_SIZE(bts->trx) || rip->ts_nr >= 8) {
@@ -979,7 +969,7 @@
 	return 0;
 }
 
-void BTS::snd_dl_ass(gprs_rlcmac_tbf *tbf, bool poll, uint16_t pgroup)
+void bts_snd_dl_ass(struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_tbf *tbf, bool poll, uint16_t pgroup)
 {
 	uint8_t trx_no = tbf->trx->trx_no;
 	uint8_t ts_no = tbf->first_ts;
@@ -992,14 +982,14 @@
 	 * so the assignment will not conflict with possible RACH requests. */
 	LOGP(DRLCMAC, LOGL_DEBUG, " - TRX=%d (%d) TS=%d TA=%d pollFN=%d\n",
 		trx_no, tbf->trx->arfcn, ts_no, tbf->ta(), poll ? tbf->poll_fn : -1);
-	plen = Encoding::write_immediate_assignment(&m_bts.trx[trx_no].pdch[ts_no],
+	plen = Encoding::write_immediate_assignment(&bts->trx[trx_no].pdch[ts_no],
 						    tbf, immediate_assignment, true, 125,
 						    (tbf->pdch[ts_no]->last_rts_fn + 21216) % GSM_MAX_FN,
 						    tbf->ta(), 7, poll, tbf->poll_fn,
-						    pcu->vty.alpha, pcu->vty.gamma, -1,
+						    bts->pcu->vty.alpha, bts->pcu->vty.gamma, -1,
 						    GSM_L1_BURST_TYPE_ACCESS_0);
 	if (plen >= 0) {
-		do_rate_ctr_inc(CTR_IMMEDIATE_ASSIGN_DL_TBF);
+		bts_do_rate_ctr_inc(bts, CTR_IMMEDIATE_ASSIGN_DL_TBF);
 		pcu_l1if_tx_pch(immediate_assignment, plen, pgroup);
 	}
 
@@ -1007,70 +997,70 @@
 }
 
 /* return maximum DL CS supported by BTS and allowed by VTY */
-uint8_t BTS::max_cs_dl(void) const
+uint8_t bts_max_cs_dl(const struct gprs_rlcmac_bts* bts)
 {
-	return m_max_cs_dl;
+	return bts->max_cs_dl;
 }
 
 /* return maximum UL CS supported by BTS and allowed by VTY */
-uint8_t BTS::max_cs_ul(void) const
+uint8_t bts_max_cs_ul(const struct gprs_rlcmac_bts* bts)
 {
-	return m_max_cs_ul;
+	return bts->max_cs_ul;
 }
 
 /* return maximum DL MCS supported by BTS and allowed by VTY */
-uint8_t BTS::max_mcs_dl(void) const
+uint8_t bts_max_mcs_dl(const struct gprs_rlcmac_bts* bts)
 {
-	return m_max_mcs_dl;
+	return bts->max_mcs_dl;
 }
 
 /* return maximum UL MCS supported by BTS and allowed by VTY */
-uint8_t BTS::max_mcs_ul(void) const
+uint8_t bts_max_mcs_ul(const struct gprs_rlcmac_bts* bts)
 {
-	return m_max_mcs_ul;
+	return bts->max_mcs_ul;
 }
 
 /* Set maximum DL CS supported by BTS and allowed by VTY */
-void BTS::set_max_cs_dl(uint8_t cs_dl)
+void bts_set_max_cs_dl(struct gprs_rlcmac_bts* bts, uint8_t cs_dl)
 {
-	m_max_cs_dl = cs_dl;
+	bts->max_cs_dl = cs_dl;
 }
 
 /* Set maximum UL CS supported by BTS and allowed by VTY */
-void BTS::set_max_cs_ul(uint8_t cs_ul)
+void bts_set_max_cs_ul(struct gprs_rlcmac_bts* bts, uint8_t cs_ul)
 {
-	m_max_cs_ul = cs_ul;
+	bts->max_cs_ul = cs_ul;
 }
 
 /* Set maximum DL MCS supported by BTS and allowed by VTY */
-void BTS::set_max_mcs_dl(uint8_t mcs_dl)
+void bts_set_max_mcs_dl(struct gprs_rlcmac_bts* bts, uint8_t mcs_dl)
 {
-	m_max_mcs_dl = mcs_dl;
+	bts->max_mcs_dl = mcs_dl;
 }
 
 /* Set maximum UL MCS supported by BTS and allowed by VTY */
-void BTS::set_max_mcs_ul(uint8_t mcs_ul)
+void bts_set_max_mcs_ul(struct gprs_rlcmac_bts* bts, uint8_t mcs_ul)
 {
-	m_max_mcs_ul = mcs_ul;
+	bts->max_mcs_ul = mcs_ul;
 }
 
-bool BTS::cs_dl_is_supported(CodingScheme cs)
+bool bts_cs_dl_is_supported(const struct gprs_rlcmac_bts* bts, CodingScheme cs)
 {
 	OSMO_ASSERT(mcs_is_valid(cs));
 	uint8_t num = mcs_chan_code(cs);
 	if (mcs_is_gprs(cs)) {
-		return (max_cs_dl() >= num) && (m_bts.cs_mask & (1U << num));
+		return (bts_max_cs_dl(bts) >= num) && (bts->cs_mask & (1U << num));
 	} else {
-		return (max_mcs_dl() >= num) && (m_bts.mcs_mask & (1U << num));
+		return (bts_max_mcs_dl(bts) >= num) && (bts->mcs_mask & (1U << num));
 	}
 }
 
-GprsMs *BTS::ms_alloc(uint8_t ms_class, uint8_t egprs_ms_class)
+GprsMs *bts_alloc_ms(struct gprs_rlcmac_bts* bts, uint8_t ms_class, uint8_t egprs_ms_class)
 {
 	GprsMs *ms;
-	ms = ms_store().create_ms();
+	ms = bts_ms_store(bts)->create_ms();
 
-	ms_set_timeout(ms, osmo_tdef_get(pcu->T_defs, -2030, OSMO_TDEF_S, -1));
+	ms_set_timeout(ms, osmo_tdef_get(bts->pcu->T_defs, -2030, OSMO_TDEF_S, -1));
 	ms_set_ms_class(ms, ms_class);
 	ms_set_egprs_ms_class(ms, egprs_ms_class);
 
@@ -1078,23 +1068,38 @@
 }
 
 
-static int bts_talloc_destructor(struct BTS* bts)
+static int bts_talloc_destructor(struct gprs_rlcmac_bts* bts)
 {
-	bts->~BTS();
+	bts_cleanup(bts);
 	return 0;
 }
 
-struct BTS* bts_alloc(struct gprs_pcu *pcu)
+struct gprs_rlcmac_bts* bts_alloc(struct gprs_pcu *pcu)
 {
-	struct BTS* bts;
-	bts = talloc(pcu, struct BTS);
+	struct gprs_rlcmac_bts* bts;
+	bts = talloc_zero(pcu, struct gprs_rlcmac_bts);
 	if (!bts)
 		return bts;
 	talloc_set_destructor(bts, bts_talloc_destructor);
-	new (bts) BTS(pcu);
+	bts_init(bts, pcu);
 	return bts;
 }
 
+struct SBAController *bts_sba(struct gprs_rlcmac_bts *bts)
+{
+	return bts->sba;
+}
+
+struct GprsMsStorage *bts_ms_store(struct gprs_rlcmac_bts *bts)
+{
+	return bts->ms_store;
+}
+
+struct GprsMs *bts_ms_by_tlli(struct gprs_rlcmac_bts *bts, uint32_t tlli, uint32_t old_tlli)
+{
+	return bts_ms_store(bts)->get_ms(tlli, old_tlli);
+}
+
 /* update TA based on TA provided by PH-DATA-IND */
 void update_tbf_ta(struct gprs_rlcmac_ul_tbf *tbf, int8_t ta_delta)
 {
@@ -1136,7 +1141,7 @@
 void bts_update_tbf_ta(const char *p, uint32_t fn, uint8_t trx_no, uint8_t ts, int8_t ta, bool is_rach)
 {
 	struct gprs_rlcmac_ul_tbf *tbf =
-		bts_main_data()->bts->ul_tbf_by_poll_fn(fn, trx_no, ts);
+		bts_ul_tbf_by_poll_fn(the_pcu->bts, fn, trx_no, ts);
 	if (!tbf)
 		LOGP(DL1IF, LOGL_DEBUG, "[%s] update TA = %u ignored due to "
 		     "unknown UL TBF on TRX = %d, TS = %d, FN = %d\n",
@@ -1182,7 +1187,7 @@
 		return;
 	}
 
-	max_cs_dl = bts->bts->max_cs_dl();
+	max_cs_dl = bts_max_cs_dl(bts);
 	if (bts->pcuif_info_ind.initial_cs > max_cs_dl) {
 		LOGP(DL1IF, LOGL_DEBUG, " downgrading initial_cs_dl to %d\n", max_cs_dl);
 		bts->initial_cs_dl = max_cs_dl;
@@ -1192,7 +1197,7 @@
 	if (bts->initial_cs_dl == 0)
 		bts->initial_cs_dl = 1; /* CS1 Must always be supported */
 
-	max_cs_ul = bts->bts->max_cs_ul();
+	max_cs_ul = bts_max_cs_ul(bts);
 	if (bts->pcuif_info_ind.initial_cs > max_cs_ul) {
 		LOGP(DL1IF, LOGL_DEBUG, " downgrading initial_cs_ul to %d\n", max_cs_ul);
 		bts->initial_cs_ul = max_cs_ul;
@@ -1212,14 +1217,14 @@
 		return;
 	}
 
-	max_mcs_dl = bts->bts->max_mcs_dl();
+	max_mcs_dl = bts_max_mcs_dl(bts);
 	if (bts->pcuif_info_ind.initial_mcs > max_mcs_dl) {
 		LOGP(DL1IF, LOGL_DEBUG, " downgrading initial_mcs_dl to %d\n", max_mcs_dl);
 		bts->initial_mcs_dl = max_mcs_dl;
 	} else {
 		bts->initial_mcs_dl = bts->pcuif_info_ind.initial_mcs;
 	}
-	max_mcs_ul = bts->bts->max_mcs_ul();
+	max_mcs_ul = bts_max_mcs_ul(bts);
 	if (bts->pcuif_info_ind.initial_mcs > max_mcs_ul) {
 		LOGP(DL1IF, LOGL_DEBUG, " downgrading initial_mcs_ul to %d\n", max_mcs_ul);
 		bts->initial_mcs_ul = max_mcs_ul;
@@ -1232,7 +1237,7 @@
 {
 	int i;
 	uint8_t cs_dl, cs_ul;
-	struct gprs_pcu *pcu = bts->bts->pcu;
+	struct gprs_pcu *pcu = bts->pcu;
 
 	cs_dl = 0;
 	for (i = pcu->vty.max_cs_dl - 1; i >= 0; i--) {
@@ -1251,15 +1256,15 @@
 	}
 
 	LOGP(DRLCMAC, LOGL_DEBUG, "New max CS: DL=%u UL=%u\n", cs_dl, cs_ul);
-	bts->bts->set_max_cs_dl(cs_dl);
-	bts->bts->set_max_cs_ul(cs_ul);
+	bts_set_max_cs_dl(bts, cs_dl);
+	bts_set_max_cs_ul(bts, cs_ul);
 }
 
 void bts_recalc_max_mcs(struct gprs_rlcmac_bts *bts)
 {
 	int i;
 	uint8_t mcs_dl, mcs_ul;
-	struct gprs_pcu *pcu = bts->bts->pcu;
+	struct gprs_pcu *pcu = bts->pcu;
 
 	mcs_dl = 0;
 	for (i = pcu->vty.max_mcs_dl - 1; i >= 0; i--) {
@@ -1278,37 +1283,11 @@
 	}
 
 	LOGP(DRLCMAC, LOGL_DEBUG, "New max MCS: DL=%u UL=%u\n", mcs_dl, mcs_ul);
-	bts->bts->set_max_mcs_dl(mcs_dl);
-	bts->bts->set_max_mcs_ul(mcs_ul);
+	bts_set_max_mcs_dl(bts, mcs_dl);
+	bts_set_max_mcs_ul(bts, mcs_ul);
 }
 
-
-struct gprs_rlcmac_bts *bts_data(struct BTS *bts)
+struct GprsMs *bts_ms_by_imsi(struct gprs_rlcmac_bts *bts, const char *imsi)
 {
-	return &bts->m_bts;
-}
-
-struct GprsMs *bts_ms_by_imsi(struct BTS *bts, const char *imsi)
-{
-	return bts->ms_by_imsi(imsi);
-}
-
-uint8_t bts_max_cs_dl(const struct BTS *bts)
-{
-	return bts->max_cs_dl();
-}
-
-uint8_t bts_max_cs_ul(const struct BTS *bts)
-{
-	return bts->max_cs_ul();
-}
-
-uint8_t bts_max_mcs_dl(const struct BTS *bts)
-{
-	return bts->max_mcs_dl();
-}
-
-uint8_t bts_max_mcs_ul(const struct BTS *bts)
-{
-	return bts->max_mcs_ul();
+	return bts_ms_store(bts)->get_ms(0, 0, imsi);
 }
diff --git a/src/bts.h b/src/bts.h
index b1f73b4..d316370 100644
--- a/src/bts.h
+++ b/src/bts.h
@@ -20,11 +20,13 @@
 
 #pragma once
 
+#include <pdch.h>
+#include <stdint.h>
+#include <stdbool.h>
 
 #ifdef __cplusplus
 extern "C" {
 #endif
-
 #include <osmocom/core/linuxlist.h>
 #include <osmocom/core/rate_ctr.h>
 #include <osmocom/core/stat_item.h>
@@ -40,21 +42,11 @@
 }
 #endif
 
-#ifdef __cplusplus
-#include "poll_controller.h"
-#include "sba.h"
 #include "tbf.h"
-#include "gprs_ms_storage.h"
 #include "coding_scheme.h"
-#include <cxx_linuxlist.h>
-#endif
 
-#include <pdch.h>
-#include <stdint.h>
-#include <stdbool.h>
-
-struct BTS;
 struct GprsMs;
+struct gprs_rlcmac_bts;
 
 struct gprs_rlcmac_trx {
 	void *fl1h;
@@ -62,11 +54,12 @@
 	struct gprs_rlcmac_pdch pdch[8];
 
 	/* back pointers */
-	struct BTS *bts;
+	struct gprs_rlcmac_bts *bts;
 	uint8_t trx_no;
 
 };
 
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -78,48 +71,7 @@
 }
 #endif
 
-/**
- * This is the data from C. As soon as our minimal compiler is gcc 4.7
- * we can start to compile pcu_vty.c with c++ and remove the split.
- */
-struct gprs_rlcmac_bts {
-	bool active;
-	uint8_t bsic;
-	uint8_t cs_mask; /* Allowed CS mask from BTS */
-	uint16_t mcs_mask;  /* Allowed MCS mask from BTS */
-	struct { /* information stored from last received PCUIF info_ind message */
-		uint8_t initial_cs;
-		uint8_t initial_mcs;
-	} pcuif_info_ind;
-	uint8_t initial_cs_dl, initial_cs_ul;
-	uint8_t initial_mcs_dl, initial_mcs_ul;
-	/* Timer defintions */
-	struct osmo_tdef *T_defs_bts; /* timers controlled by BTS, received through PCUIF */
-	uint8_t n3101;
-	uint8_t n3103;
-	uint8_t n3105;
-	struct gprs_rlcmac_trx trx[8];
 
-	uint8_t si13[GSM_MACBLOCK_LEN];
-	bool si13_is_set;
-
-	/* State for dynamic algorithm selection */
-	int multislot_disabled;
-
-	/**
-	 * Point back to the C++ object. This is used during the transition
-	 * period.
-	 */
-	struct BTS *bts;
-
-	/* Packet Application Information (3GPP TS 44.060 11.2.47, usually ETWS primary message). We don't need to store
-	 * more than one message, because they get sent so rarely. */
-	struct msgb *app_info;
-	uint32_t app_info_pending; /* Count of MS with active TBF, to which we did not send app_info yet */
-
-	/* main nsei */
-	struct gprs_ns2_nse *nse;
-};
 
 enum {
 	CTR_TBF_DL_ALLOCATED,
@@ -230,189 +182,159 @@
 	bool single_block;
 };
 
-#ifdef __cplusplus
+struct PollController;
+struct SBAController;
+struct GprsMsStorage;
+struct pcu_l1_meas;
+
 /**
  * I represent a GSM BTS. I have one or more TRX, I know the current
  * GSM time and I have controllers that help with allocating resources
  * on my TRXs.
  */
-struct BTS {
-public:
-	BTS(struct gprs_pcu *pcu);
-	~BTS();
-	void cleanup();
+struct gprs_rlcmac_bts {
+	bool active;
+	uint8_t bsic;
+	uint8_t cs_mask; /* Allowed CS mask from BTS */
+	uint16_t mcs_mask;  /* Allowed MCS mask from BTS */
+	struct { /* information stored from last received PCUIF info_ind message */
+		uint8_t initial_cs;
+		uint8_t initial_mcs;
+	} pcuif_info_ind;
+	uint8_t initial_cs_dl, initial_cs_ul;
+	uint8_t initial_mcs_dl, initial_mcs_ul;
+	/* Timer defintions */
+	struct osmo_tdef *T_defs_bts; /* timers controlled by BTS, received through PCUIF */
+	uint8_t n3101;
+	uint8_t n3103;
+	uint8_t n3105;
+	struct gprs_rlcmac_trx trx[8];
 
-	static BTS* main_bts();
+	uint8_t si13[GSM_MACBLOCK_LEN];
+	bool si13_is_set;
 
-	struct gprs_rlcmac_bts *bts_data();
-	SBAController *sba();
+	/* State for dynamic algorithm selection */
+	int multislot_disabled;
 
-	/** TODO: change the number to unsigned */
-	void set_current_frame_number(int frame_number);
-	void set_current_block_frame_number(int frame_number, unsigned max_delay);
-	int current_frame_number() const;
+	/* Packet Application Information (3GPP TS 44.060 11.2.47, usually ETWS primary message). We don't need to store
+	 * more than one message, because they get sent so rarely. */
+	struct msgb *app_info;
+	uint32_t app_info_pending; /* Count of MS with active TBF, to which we did not send app_info yet */
 
-	/** add paging to paging queue(s) */
-	int add_paging(uint8_t chan_needed, const struct osmo_mobile_identity *mi);
-
-	gprs_rlcmac_dl_tbf *dl_tbf_by_poll_fn(uint32_t fn, uint8_t trx, uint8_t ts);
-	gprs_rlcmac_ul_tbf *ul_tbf_by_poll_fn(uint32_t fn, uint8_t trx, uint8_t ts);
-	gprs_rlcmac_dl_tbf *dl_tbf_by_tfi(uint8_t tfi, uint8_t trx, uint8_t ts);
-	gprs_rlcmac_ul_tbf *ul_tbf_by_tfi(uint8_t tfi, uint8_t trx, uint8_t ts);
-
-	int tfi_find_free(enum gprs_rlcmac_tbf_direction dir, uint8_t *_trx, int8_t use_trx) const;
-
-	int rcv_imm_ass_cnf(const uint8_t *data, uint32_t fn);
-
-	uint32_t rfn_to_fn(int32_t rfn);
-	int rcv_rach(const struct rach_ind_params *rip);
-	int rcv_ptcch_rach(const struct rach_ind_params *rip);
-
-	void snd_dl_ass(gprs_rlcmac_tbf *tbf, bool poll, uint16_t pgroup);
-
-	uint8_t max_cs_dl(void) const;
-	uint8_t max_cs_ul(void) const;
-	uint8_t max_mcs_dl(void) const;
-	uint8_t max_mcs_ul(void) const;
-	void set_max_cs_dl(uint8_t cs_dl);
-	void set_max_cs_ul(uint8_t cs_ul);
-	void set_max_mcs_dl(uint8_t mcs_dl);
-	void set_max_mcs_ul(uint8_t mcs_ul);
-	bool cs_dl_is_supported(CodingScheme cs);
-
-	GprsMsStorage &ms_store();
-	GprsMs *ms_by_tlli(uint32_t tlli, uint32_t old_tlli = GSM_RESERVED_TMSI);
-	GprsMs *ms_by_imsi(const char *imsi);
-	GprsMs *ms_alloc(uint8_t ms_class, uint8_t egprs_ms_class = 0);
-
-	void send_gsmtap(enum pcu_gsmtap_category categ, bool uplink, uint8_t trx_no,
-			      uint8_t ts_no, uint8_t channel, uint32_t fn,
-			      const uint8_t *data, unsigned int len);
-	void send_gsmtap_meas(enum pcu_gsmtap_category categ, bool uplink, uint8_t trx_no,
-			      uint8_t ts_no, uint8_t channel, uint32_t fn,
-			      const uint8_t *data, unsigned int len, struct pcu_l1_meas *meas);
-	void send_gsmtap_rach(enum pcu_gsmtap_category categ, uint8_t channel,
-			      const struct rach_ind_params *rip);
-
-	/*
-	 * Below for C interface for the VTY
-	 */
-	struct rate_ctr_group *rate_counters() const;
-	struct osmo_stat_item_group *stat_items() const;
-	void do_rate_ctr_inc(unsigned int ctr_id);
-	void do_rate_ctr_add(unsigned int ctr_id, int inc);
-	void stat_item_add(unsigned int stat_id, int inc);
-
-	LListHead<gprs_rlcmac_tbf>& ul_tbfs();
-	LListHead<gprs_rlcmac_tbf>& dl_tbfs();
-
-	struct gprs_rlcmac_bts m_bts;
+	/* main nsei */
+	struct gprs_ns2_nse *nse;
 
 	/* back pointer to PCU object */
 	struct gprs_pcu *pcu;
-private:
 
-	int m_cur_fn;
-	int m_cur_blk_fn;
-	uint8_t m_max_cs_dl, m_max_cs_ul;
-	uint8_t m_max_mcs_dl, m_max_mcs_ul;
-	PollController m_pollController;
-	SBAController m_sba;
-	struct rate_ctr_group *m_ratectrs;
-	struct osmo_stat_item_group *m_statg;
+	int cur_fn;
+	int cur_blk_fn;
+	uint8_t max_cs_dl, max_cs_ul;
+	uint8_t max_mcs_dl, max_mcs_ul;
+	struct PollController *pollController;
+	struct SBAController *sba;
+	struct rate_ctr_group *ratectrs;
+	struct osmo_stat_item_group *statg;
 
-	GprsMsStorage m_ms_store;
+	struct GprsMsStorage *ms_store;
 
 	/* list of uplink TBFs */
-	LListHead<gprs_rlcmac_tbf> m_ul_tbfs;
+	struct llist_head ul_tbfs; /* list of gprs_rlcmac_tbf */
 	/* list of downlink TBFs */
-	LListHead<gprs_rlcmac_tbf> m_dl_tbfs;
-
-	/* disable copying to avoid slicing */
-	BTS(const BTS&);
-	BTS& operator=(const BTS&);
+	struct llist_head dl_tbfs; /* list of gprs_rlcmac_tbf */
 };
 
-inline int BTS::current_frame_number() const
-{
-	return m_cur_fn;
-}
-
-inline SBAController *BTS::sba()
-{
-	return &m_sba;
-}
-
-inline GprsMsStorage &BTS::ms_store()
-{
-	return m_ms_store;
-}
-
-inline GprsMs *BTS::ms_by_tlli(uint32_t tlli, uint32_t old_tlli)
-{
-	return ms_store().get_ms(tlli, old_tlli);
-}
-
-inline GprsMs *BTS::ms_by_imsi(const char *imsi)
-{
-	return ms_store().get_ms(0, 0, imsi);
-}
-
-inline LListHead<gprs_rlcmac_tbf>& BTS::ul_tbfs()
-{
-	return m_ul_tbfs;
-}
-
-inline LListHead<gprs_rlcmac_tbf>& BTS::dl_tbfs()
-{
-	return m_dl_tbfs;
-}
-
-inline struct rate_ctr_group *BTS::rate_counters() const
-{
-	return m_ratectrs;
-}
-
-inline struct osmo_stat_item_group *BTS::stat_items() const
-{
-	return m_statg;
-}
-
-inline void BTS::do_rate_ctr_inc(unsigned int ctr_id) {
-	rate_ctr_inc(&m_ratectrs->ctr[ctr_id]);
-}
-
-inline void BTS::do_rate_ctr_add(unsigned int ctr_id, int inc) {
-	rate_ctr_add(&m_ratectrs->ctr[ctr_id], inc);
-}
-
-inline void BTS::stat_item_add(unsigned int stat_id, int inc) {
-	int32_t val = osmo_stat_item_get_last(m_statg->items[stat_id]);
-	osmo_stat_item_set(m_statg->items[stat_id], val + inc);
-}
-
-struct gprs_pcu;
-struct BTS* bts_alloc(struct gprs_pcu *pcu);
-#endif
-
 #ifdef __cplusplus
 extern "C" {
 #endif
-	void bts_cleanup();
-	struct gprs_rlcmac_bts *bts_data(struct BTS *bts);
-	struct gprs_rlcmac_bts *bts_main_data();
-	struct rate_ctr_group *bts_main_data_stats();
-	struct osmo_stat_item_group *bts_main_data_stat_items();
-	void bts_recalc_initial_cs(struct gprs_rlcmac_bts *bts);
-	void bts_recalc_initial_mcs(struct gprs_rlcmac_bts *bts);
-	void bts_recalc_max_cs(struct gprs_rlcmac_bts *bts);
-	void bts_recalc_max_mcs(struct gprs_rlcmac_bts *bts);
-	struct GprsMs *bts_ms_by_imsi(struct BTS *bts, const char *imsi);
-	uint8_t bts_max_cs_dl(const struct BTS *bts);
-	uint8_t bts_max_cs_ul(const struct BTS *bts);
-	uint8_t bts_max_mcs_dl(const struct BTS *bts);
-	uint8_t bts_max_mcs_ul(const struct BTS *bts);
-#ifdef __cplusplus
+
+struct GprsMs *bts_alloc_ms(struct gprs_rlcmac_bts *bts, uint8_t ms_class, uint8_t egprs_ms_class);
+int bts_add_paging(struct gprs_rlcmac_bts *bts, uint8_t chan_needed, const struct osmo_mobile_identity *mi);
+
+uint32_t bts_rfn_to_fn(const struct gprs_rlcmac_bts *bts, int32_t rfn);
+
+struct gprs_rlcmac_dl_tbf *bts_dl_tbf_by_poll_fn(struct gprs_rlcmac_bts *bts, uint32_t fn, uint8_t trx, uint8_t ts);
+struct gprs_rlcmac_ul_tbf *bts_ul_tbf_by_poll_fn(struct gprs_rlcmac_bts *bts, uint32_t fn, uint8_t trx, uint8_t ts);
+struct gprs_rlcmac_dl_tbf *bts_dl_tbf_by_tfi(struct gprs_rlcmac_bts *bts, uint8_t tfi, uint8_t trx, uint8_t ts);
+struct gprs_rlcmac_ul_tbf *bts_ul_tbf_by_tfi(struct gprs_rlcmac_bts *bts, uint8_t tfi, uint8_t trx, uint8_t ts);
+
+void bts_snd_dl_ass(struct gprs_rlcmac_bts *bts, struct gprs_rlcmac_tbf *tbf, bool poll, uint16_t pgroup);
+
+/** TODO: change the number to unsigned */
+void bts_set_current_frame_number(struct gprs_rlcmac_bts *bts, int frame_number);
+void bts_set_current_block_frame_number(struct gprs_rlcmac_bts *bts, int frame_number, unsigned max_delay);
+static inline int bts_current_frame_number(const struct gprs_rlcmac_bts *bts)
+{
+	return bts->cur_fn;
 }
 
+int bts_tfi_find_free(const struct gprs_rlcmac_bts *bts, enum gprs_rlcmac_tbf_direction dir,
+		      uint8_t *_trx, int8_t use_trx);
+
+int bts_rcv_rach(struct gprs_rlcmac_bts *bts, const struct rach_ind_params *rip);
+int bts_rcv_ptcch_rach(struct gprs_rlcmac_bts *bts, const struct rach_ind_params *rip);
+int bts_rcv_imm_ass_cnf(struct gprs_rlcmac_bts *bts, const uint8_t *data, uint32_t fn);
+
+void bts_send_gsmtap(struct gprs_rlcmac_bts *bts,
+		     enum pcu_gsmtap_category categ, bool uplink, uint8_t trx_no,
+		     uint8_t ts_no, uint8_t channel, uint32_t fn,
+		     const uint8_t *data, unsigned int len);
+void bts_send_gsmtap_meas(struct gprs_rlcmac_bts *bts,
+			  enum pcu_gsmtap_category categ, bool uplink, uint8_t trx_no,
+			  uint8_t ts_no, uint8_t channel, uint32_t fn,
+			  const uint8_t *data, unsigned int len, struct pcu_l1_meas *meas);
+void bts_send_gsmtap_rach(struct gprs_rlcmac_bts *bts,
+			  enum pcu_gsmtap_category categ, uint8_t channel,
+			  const struct rach_ind_params *rip);
+
+struct SBAController *bts_sba(struct gprs_rlcmac_bts *bts);
+
+struct GprsMsStorage *bts_ms_store(struct gprs_rlcmac_bts *bts);
+
+struct GprsMs *bts_ms_by_tlli(struct gprs_rlcmac_bts *bts, uint32_t tlli, uint32_t old_tlli);
+
+static inline struct rate_ctr_group *bts_rate_counters(struct gprs_rlcmac_bts *bts)
+{
+	return bts->ratectrs;
+}
+
+static inline struct osmo_stat_item_group *bts_stat_items(struct gprs_rlcmac_bts *bts)
+{
+	return bts->statg;
+}
+
+static inline void bts_do_rate_ctr_inc(struct gprs_rlcmac_bts *bts, unsigned int ctr_id) {
+	rate_ctr_inc(&bts->ratectrs->ctr[ctr_id]);
+}
+
+static inline void bts_do_rate_ctr_add(struct gprs_rlcmac_bts *bts, unsigned int ctr_id, int inc) {
+	rate_ctr_add(&bts->ratectrs->ctr[ctr_id], inc);
+}
+
+static inline void bts_stat_item_add(struct gprs_rlcmac_bts *bts, unsigned int stat_id, int inc) {
+	int32_t val = osmo_stat_item_get_last(bts->statg->items[stat_id]);
+	osmo_stat_item_set(bts->statg->items[stat_id], val + inc);
+}
+
+struct gprs_rlcmac_bts *bts_alloc(struct gprs_pcu *pcu);
+
+struct gprs_rlcmac_bts *bts_main_data();
+struct rate_ctr_group *bts_main_data_stats();
+struct osmo_stat_item_group *bts_main_data_stat_items();
+void bts_recalc_initial_cs(struct gprs_rlcmac_bts *bts);
+void bts_recalc_initial_mcs(struct gprs_rlcmac_bts *bts);
+void bts_recalc_max_cs(struct gprs_rlcmac_bts *bts);
+void bts_recalc_max_mcs(struct gprs_rlcmac_bts *bts);
+struct GprsMs *bts_ms_by_imsi(struct gprs_rlcmac_bts *bts, const char *imsi);
+uint8_t bts_max_cs_dl(const struct gprs_rlcmac_bts *bts);
+uint8_t bts_max_cs_ul(const struct gprs_rlcmac_bts *bts);
+uint8_t bts_max_mcs_dl(const struct gprs_rlcmac_bts *bts);
+uint8_t bts_max_mcs_ul(const struct gprs_rlcmac_bts *bts);
+void bts_set_max_cs_dl(struct gprs_rlcmac_bts *bts, uint8_t cs_dl);
+void bts_set_max_cs_ul(struct gprs_rlcmac_bts *bts, uint8_t cs_ul);
+void bts_set_max_mcs_dl(struct gprs_rlcmac_bts *bts, uint8_t mcs_dl);
+void bts_set_max_mcs_ul(struct gprs_rlcmac_bts *bts, uint8_t mcs_ul);
+bool bts_cs_dl_is_supported(const struct gprs_rlcmac_bts *bts, enum CodingScheme cs);
+#ifdef __cplusplus
+}
 #endif
diff --git a/src/encoding.cpp b/src/encoding.cpp
index e7b1fb4..f605ca2 100644
--- a/src/encoding.cpp
+++ b/src/encoding.cpp
@@ -24,6 +24,7 @@
 #include <bts.h>
 #include <tbf.h>
 #include <tbf_ul.h>
+#include <tbf_dl.h>
 #include <gprs_debug.h>
 #include <egprs_rlc_compression.h>
 
diff --git a/src/gprs_bssgp_pcu.cpp b/src/gprs_bssgp_pcu.cpp
index 1596d67..92fa845 100644
--- a/src/gprs_bssgp_pcu.cpp
+++ b/src/gprs_bssgp_pcu.cpp
@@ -34,6 +34,7 @@
 	#include <osmocom/core/utils.h>
 	#include <osmocom/gsm/gsm48.h>
 	#include "coding_scheme.h"
+	#include "tbf_dl.h"
 }
 
 /* Tuning parameters for BSSGP flow control */
@@ -208,7 +209,7 @@
 	if ((rc = get_paging_mi(&mi, tp)) > 0)
 		return bssgp_tx_status((enum gprs_bssgp_cause) rc, NULL, msg);
 
-	return BTS::main_bts()->add_paging(tlvp_val8(tp, BSSGP_IE_CHAN_NEEDED, 0), &mi);
+	return bts_add_paging(the_pcu->bts, tlvp_val8(tp, BSSGP_IE_CHAN_NEEDED, 0), &mi);
 }
 
 static int gprs_bssgp_pcu_rx_paging_ps(struct msgb *msg, const struct tlv_parsed *tp)
@@ -761,8 +762,8 @@
 			} else {
 				/* We found "num" for free in the loop above */
 			}
-		} else if (bts->bts->max_mcs_dl()) {
-			num = bts->bts->max_mcs_dl();
+		} else if (bts_max_mcs_dl(bts)) {
+			num = bts_max_mcs_dl(bts);
 		} else {
 			num = 9;
 		}
@@ -782,8 +783,8 @@
 				}
 			}
 		}
-	} else if (bts->bts->max_cs_dl()) {
-		num = bts->bts->max_cs_dl();
+	} else if (bts_max_cs_dl(bts)) {
+		num = bts_max_cs_dl(bts);
 	}
 
 	if (!num)
diff --git a/src/gprs_ms.c b/src/gprs_ms.c
index 9d303d6..ea497a3 100644
--- a/src/gprs_ms.c
+++ b/src/gprs_ms.c
@@ -91,7 +91,7 @@
 }
 
 static int ms_talloc_destructor(struct GprsMs *ms);
-struct GprsMs *ms_alloc(struct BTS *bts, uint32_t tlli)
+struct GprsMs *ms_alloc(struct gprs_rlcmac_bts *bts, uint32_t tlli)
 {
 	struct GprsMs *ms = talloc_zero(tall_pcu_ctx, struct GprsMs);
 
@@ -249,13 +249,13 @@
 	case GPRS:
 		if (!mcs_is_gprs(ms->current_cs_ul)) {
 			ms->current_cs_ul = mcs_get_gprs_by_num(
-				bts_data(ms->bts)->initial_cs_ul);
+				ms->bts->initial_cs_ul);
 			if (!mcs_is_valid(ms->current_cs_ul))
 				ms->current_cs_ul = CS1;
 		}
 		if (!mcs_is_gprs(ms->current_cs_dl)) {
 			ms->current_cs_dl = mcs_get_gprs_by_num(
-				bts_data(ms->bts)->initial_cs_dl);
+				ms->bts->initial_cs_dl);
 			if (!mcs_is_valid(ms->current_cs_dl))
 				ms->current_cs_dl = CS1;
 		}
@@ -265,13 +265,13 @@
 	case EGPRS:
 		if (!mcs_is_edge(ms->current_cs_ul)) {
 			ms->current_cs_ul = mcs_get_egprs_by_num(
-				bts_data(ms->bts)->initial_mcs_ul);
+				ms->bts->initial_mcs_ul);
 			if (!mcs_is_valid(ms->current_cs_ul))
 				ms->current_cs_ul = MCS1;
 		}
 		if (!mcs_is_edge(ms->current_cs_dl)) {
 			ms->current_cs_dl = mcs_get_egprs_by_num(
-				bts_data(ms->bts)->initial_mcs_dl);
+				ms->bts->initial_mcs_dl);
 			if (!mcs_is_valid(ms->current_cs_dl))
 				ms->current_cs_dl = MCS1;
 		}
diff --git a/src/gprs_ms.h b/src/gprs_ms.h
index 6391f72..12809f1 100644
--- a/src/gprs_ms.h
+++ b/src/gprs_ms.h
@@ -49,7 +49,7 @@
 	MS_CTR_DL_CTRL_MSG_SCHED,
 };
 
-struct BTS;
+struct gprs_rlcmac_bts;
 struct gprs_rlcmac_trx;
 struct GprsMs;
 
@@ -63,7 +63,7 @@
 	struct gpr_ms_callback cb;
 	bool app_info_pending;
 
-	struct BTS *bts;
+	struct gprs_rlcmac_bts *bts;
 	struct gprs_rlcmac_ul_tbf *ul_tbf;
 	struct gprs_rlcmac_dl_tbf *dl_tbf;
 	struct llist_head old_tbfs; /* list of gprs_rlcmac_tbf */
@@ -102,7 +102,7 @@
 	struct rate_ctr_group *ctrs;
 };
 
-struct GprsMs *ms_alloc(struct BTS *bts, uint32_t tlli);
+struct GprsMs *ms_alloc(struct gprs_rlcmac_bts *bts, uint32_t tlli);
 
 int ms_first_common_ts(const struct GprsMs *ms);
 void ms_set_reserved_slots(struct GprsMs *ms, struct gprs_rlcmac_trx *trx,
diff --git a/src/gprs_ms_storage.cpp b/src/gprs_ms_storage.cpp
index 6d5b09e..6245ed9 100644
--- a/src/gprs_ms_storage.cpp
+++ b/src/gprs_ms_storage.cpp
@@ -35,7 +35,7 @@
 {
 	llist_del(&ms->list);
 	if (ms->bts)
-		ms->bts->stat_item_add(STAT_MS_PRESENT, -1);
+		bts_stat_item_add(ms->bts, STAT_MS_PRESENT, -1);
 	if (ms_is_idle(ms))
 		talloc_free(ms);
 }
@@ -50,7 +50,7 @@
 	.ms_active = ms_storage_ms_active_cb,
 };
 
-GprsMsStorage::GprsMsStorage(BTS *bts) :
+GprsMsStorage::GprsMsStorage(struct gprs_rlcmac_bts *bts) :
 	m_bts(bts)
 {
 	INIT_LLIST_HEAD(&m_list);
@@ -109,7 +109,7 @@
 	ms_set_callback(ms, &ms_storage_ms_cb);
 	llist_add(&ms->list, &m_list);
 	if (m_bts)
-		m_bts->stat_item_add(STAT_MS_PRESENT, 1);
+		bts_stat_item_add(m_bts, STAT_MS_PRESENT, 1);
 
 	return ms;
 }
diff --git a/src/gprs_ms_storage.h b/src/gprs_ms_storage.h
index af49688..dcb6d8d 100644
--- a/src/gprs_ms_storage.h
+++ b/src/gprs_ms_storage.h
@@ -25,11 +25,11 @@
 #include <stdint.h>
 #include <stddef.h>
 
-struct BTS;
+struct gprs_rlcmac_bts;
 
-class GprsMsStorage {
+struct GprsMsStorage {
 public:
-	GprsMsStorage(BTS *bts);
+	GprsMsStorage(struct gprs_rlcmac_bts *bts);
 	~GprsMsStorage();
 
 	void cleanup();
@@ -39,6 +39,6 @@
 
 	const struct llist_head* ms_list() const {return &m_list;}
 private:
-	BTS *m_bts;
+	struct gprs_rlcmac_bts *m_bts;
 	struct llist_head m_list; /* list of struct GprsMs */
 };
diff --git a/src/gprs_pcu.c b/src/gprs_pcu.c
index bc9b350..b280652 100644
--- a/src/gprs_pcu.c
+++ b/src/gprs_pcu.c
@@ -109,8 +109,7 @@
 	the_pcu->vty.initial_cs_ul = cs_ul;
 
 	/*TODO: once we support multiple bts, foreach(bts) apply */
-	struct gprs_rlcmac_bts *bts = bts_data(pcu->bts);
-	bts_recalc_initial_cs(bts);
+	bts_recalc_initial_cs(pcu->bts);
 }
 void gprs_pcu_set_initial_mcs(struct gprs_pcu *pcu, uint8_t mcs_dl, uint8_t mcs_ul)
 {
@@ -118,8 +117,7 @@
 	the_pcu->vty.initial_mcs_ul = mcs_ul;
 
 	/*TODO: once we support multiple bts, foreach(bts) apply */
-	struct gprs_rlcmac_bts *bts = bts_data(pcu->bts);
-	bts_recalc_initial_mcs(bts);
+	bts_recalc_initial_mcs(pcu->bts);
 }
 
 void gprs_pcu_set_max_cs(struct gprs_pcu *pcu, uint8_t cs_dl, uint8_t cs_ul)
@@ -127,14 +125,12 @@
 	the_pcu->vty.max_cs_dl = cs_dl;
 	the_pcu->vty.max_cs_ul = cs_ul;
 	/*TODO: once we support multiple bts, foreach(bts) apply */
-	struct gprs_rlcmac_bts *bts = bts_data(pcu->bts);
-	bts_recalc_max_cs(bts);
+	bts_recalc_max_cs(pcu->bts);
 }
 void gprs_pcu_set_max_mcs(struct gprs_pcu *pcu, uint8_t mcs_dl, uint8_t mcs_ul)
 {
 	the_pcu->vty.max_mcs_dl = mcs_dl;
 	the_pcu->vty.max_mcs_ul = mcs_ul;
 	/* TODO: once we support multiple bts, foreach(bts) apply */
-	struct gprs_rlcmac_bts *bts = bts_data(pcu->bts);
-	bts_recalc_max_mcs(bts);
+	bts_recalc_max_mcs(pcu->bts);
 }
diff --git a/src/gprs_pcu.h b/src/gprs_pcu.h
index 8913001..37f6e07 100644
--- a/src/gprs_pcu.h
+++ b/src/gprs_pcu.h
@@ -55,7 +55,6 @@
 	PCU_GSMTAP_C_UL_PTCCH		= 21,	/* uplink PTCCH bursts */
 };
 
-struct BTS;
 struct gprs_rlcmac_bts;
 struct GprsMs;
 struct gprs_rlcmac_tbf;
@@ -108,7 +107,7 @@
 	struct gsmtap_inst *gsmtap;
 	uint32_t gsmtap_categ_mask;
 
-	struct BTS *bts;
+	struct gprs_rlcmac_bts *bts;
 
 	struct gprs_ns2_inst *nsi;
 
diff --git a/src/gprs_rlcmac_sched.cpp b/src/gprs_rlcmac_sched.cpp
index b5709c9..39e22c0 100644
--- a/src/gprs_rlcmac_sched.cpp
+++ b/src/gprs_rlcmac_sched.cpp
@@ -41,13 +41,13 @@
 	struct gprs_rlcmac_ul_tbf *ul_ack;
 };
 
-static uint32_t sched_poll(BTS *bts,
+static uint32_t sched_poll(struct gprs_rlcmac_bts *bts,
 		    uint8_t trx, uint8_t ts, uint32_t fn, uint8_t block_nr,
 		    struct tbf_sched_candidates *tbf_cand)
 {
 	struct gprs_rlcmac_ul_tbf *ul_tbf;
 	struct gprs_rlcmac_dl_tbf *dl_tbf;
-	LListHead<gprs_rlcmac_tbf> *pos;
+	struct llist_item *pos;
 	uint32_t poll_fn;
 
 	/* check special TBF for events */
@@ -55,8 +55,8 @@
 	if ((block_nr % 3) == 2)
 		poll_fn ++;
 	poll_fn = poll_fn % GSM_MAX_FN;
-	llist_for_each(pos, &bts->ul_tbfs()) {
-		ul_tbf = as_ul_tbf(pos->entry());
+	llist_for_each_entry(pos, &bts->ul_tbfs, list) {
+		ul_tbf = as_ul_tbf((struct gprs_rlcmac_tbf *)pos->entry);
 		OSMO_ASSERT(ul_tbf);
 		/* this trx, this ts */
 		if (ul_tbf->trx->trx_no != trx || !ul_tbf->is_control_ts(ts))
@@ -74,8 +74,8 @@
 /* FIXME: Is this supposed to be fair? The last TBF for each wins? Maybe use llist_add_tail and skip once we have all
 states? */
 	}
-	llist_for_each(pos, &bts->dl_tbfs()) {
-		dl_tbf = as_dl_tbf(pos->entry());
+	llist_for_each_entry(pos, &bts->dl_tbfs, list) {
+		dl_tbf = as_dl_tbf((struct gprs_rlcmac_tbf *)pos->entry);
 		OSMO_ASSERT(dl_tbf);
 		/* this trx, this ts */
 		if (dl_tbf->trx->trx_no != trx || !dl_tbf->is_control_ts(ts))
@@ -136,7 +136,7 @@
 	if (!tbf || !tbf->ms()->app_info_pending)
 		return NULL;
 
-	bts_data = BTS::main_bts()->bts_data();
+	bts_data = the_pcu->bts;
 
 	if (bts_data->app_info) {
 		LOGP(DRLCMACSCHED, LOGL_DEBUG, "Sending Packet Application Information message\n");
@@ -367,7 +367,7 @@
 	return msg;
 }
 
-static inline void tap_n_acc(const struct msgb *msg, const struct gprs_rlcmac_bts *bts, uint8_t trx, uint8_t ts,
+static inline void tap_n_acc(const struct msgb *msg, struct gprs_rlcmac_bts *bts, uint8_t trx, uint8_t ts,
 			     uint32_t fn, enum pcu_gsmtap_category cat)
 {
 	if (!msg)
@@ -375,19 +375,19 @@
 
 	switch(cat) {
 	case PCU_GSMTAP_C_DL_CTRL:
-		bts->bts->do_rate_ctr_inc(CTR_RLC_SENT_CONTROL);
-		bts->bts->send_gsmtap(PCU_GSMTAP_C_DL_CTRL, false, trx, ts, GSMTAP_CHANNEL_PACCH, fn, msg->data,
+		bts_do_rate_ctr_inc(bts, CTR_RLC_SENT_CONTROL);
+		bts_send_gsmtap(bts, PCU_GSMTAP_C_DL_CTRL, false, trx, ts, GSMTAP_CHANNEL_PACCH, fn, msg->data,
 				      msg->len);
 		break;
 	case PCU_GSMTAP_C_DL_DATA_GPRS:
 	case PCU_GSMTAP_C_DL_DATA_EGPRS:
-		bts->bts->do_rate_ctr_inc(CTR_RLC_SENT);
-		bts->bts->send_gsmtap(cat, false, trx, ts, GSMTAP_CHANNEL_PDTCH, fn, msg->data,
+		bts_do_rate_ctr_inc(bts, CTR_RLC_SENT);
+		bts_send_gsmtap(bts, cat, false, trx, ts, GSMTAP_CHANNEL_PDTCH, fn, msg->data,
 				      msg->len);
 		break;
 	case PCU_GSMTAP_C_DL_DUMMY:
-		bts->bts->do_rate_ctr_inc(CTR_RLC_SENT_DUMMY);
-		bts->bts->send_gsmtap(PCU_GSMTAP_C_DL_DUMMY, false, trx, ts, GSMTAP_CHANNEL_PACCH, fn, msg->data,
+		bts_do_rate_ctr_inc(bts, CTR_RLC_SENT_DUMMY);
+		bts_send_gsmtap(bts, PCU_GSMTAP_C_DL_DUMMY, false, trx, ts, GSMTAP_CHANNEL_PACCH, fn, msg->data,
 				      msg->len);
 		break;
 	default:
@@ -438,7 +438,7 @@
 		req_mcs_kind = EGPRS; /* all kinds are fine */
 	}
 
-	poll_fn = sched_poll(bts->bts, trx, ts, fn, block_nr, &tbf_cand);
+	poll_fn = sched_poll(bts, trx, ts, fn, block_nr, &tbf_cand);
 	/* check uplink resource for polling */
 	if (tbf_cand.poll) {
 		LOGP(DRLCMACSCHED, LOGL_DEBUG, "Received RTS for PDCH: TRX=%d "
@@ -447,7 +447,7 @@
 			block_nr, poll_fn, tbf_name(tbf_cand.poll));
 		usf = USF_UNUSED;
 	/* else. check for sba */
-	} else if ((sba_fn = bts->bts->sba()->sched(trx, ts, fn, block_nr)) != 0xffffffff) {
+} else if ((sba_fn = bts_sba(bts)->sched(trx, ts, fn, block_nr)) != 0xffffffff) {
 		LOGP(DRLCMACSCHED, LOGL_DEBUG, "Received RTS for PDCH: TRX=%d "
 			"TS=%d FN=%d block_nr=%d scheduling free USF for "
 			"single block allocation at FN=%d\n", trx, ts, fn,
@@ -493,7 +493,7 @@
 	}
 
 	/* msg is now available */
-	bts->bts->do_rate_ctr_add(CTR_RLC_DL_BYTES, msg->data_len);
+	bts_do_rate_ctr_add(bts, CTR_RLC_DL_BYTES, msg->data_len);
 
 	/* set USF */
 	OSMO_ASSERT(msgb_length(msg) > 0);
diff --git a/src/gprs_rlcmac_ts_alloc.cpp b/src/gprs_rlcmac_ts_alloc.cpp
index 7c2e828..f8b1c1f 100644
--- a/src/gprs_rlcmac_ts_alloc.cpp
+++ b/src/gprs_rlcmac_ts_alloc.cpp
@@ -305,7 +305,7 @@
  *  \param[out] trx_no_ TRX number on which TFI was found
  *  \returns negative error code or 0 on success
  */
-static int tfi_find_free(const BTS *bts, const gprs_rlcmac_trx *trx, const GprsMs *ms,
+static int tfi_find_free(const struct gprs_rlcmac_bts *bts, const gprs_rlcmac_trx *trx, const GprsMs *ms,
 			 enum gprs_rlcmac_tbf_direction dir, int8_t use_trx, uint8_t *trx_no_)
 {
 	int tfi;
@@ -323,7 +323,7 @@
 	if (use_trx == -1 && ms_current_trx(ms))
 		use_trx = ms_current_trx(ms)->trx_no;
 
-	tfi = bts->tfi_find_free(dir, &trx_no, use_trx);
+	tfi = bts_tfi_find_free(bts, dir, &trx_no, use_trx);
 	if (tfi < 0)
 		return -EBUSY;
 
@@ -423,7 +423,7 @@
 	ms_set_reserved_slots(ms_, trx, 1 << ts, 1 << ts);
 
 	tbf_->upgrade_to_multislot = 0;
-	bts->bts->do_rate_ctr_inc(CTR_TBF_ALLOC_ALGO_A);
+	bts_do_rate_ctr_inc(bts, CTR_TBF_ALLOC_ALGO_A);
 	return 0;
 }
 
@@ -888,7 +888,7 @@
 	trx = ms_current_trx(ms);
 
 	/* Step 2a: Find usable TRX and TFI */
-	tfi = tfi_find_free(bts->bts, trx, ms, tbf->direction, use_trx, &trx_no);
+	tfi = tfi_find_free(bts, trx, ms, tbf->direction, use_trx, &trx_no);
 	if (tfi < 0) {
 		LOGPAL(tbf, "B", single, use_trx, LOGL_NOTICE, "failed to allocate a TFI\n");
 		return tfi;
@@ -966,7 +966,7 @@
 	else
 		assign_ul_tbf_slots(as_ul_tbf(tbf_), trx, ul_slots, tfi, usf);
 
-	bts->bts->do_rate_ctr_inc(CTR_TBF_ALLOC_ALGO_B);
+	bts_do_rate_ctr_inc(bts, CTR_TBF_ALLOC_ALGO_B);
 
 	return 0;
 }
diff --git a/src/gsm_timer.cpp b/src/gsm_timer.cpp
index cefe520..0627753 100644
--- a/src/gsm_timer.cpp
+++ b/src/gsm_timer.cpp
@@ -16,7 +16,7 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  */
- 
+
 /* These store the amount of frame number that we wait until next timer expires. */
 static int nearest;
 static int *nearest_p;
@@ -45,7 +45,7 @@
  */
 int get_current_fn()
 {
-	return BTS::main_bts()->current_frame_number();
+	return bts_current_frame_number(the_pcu->bts);
 }
 
 static void __add_gsm_timer(struct osmo_gsm_timer_list *timer)
@@ -232,4 +232,3 @@
 }
 
 /*! }@ */
-
diff --git a/src/llc.cpp b/src/llc.cpp
index 51cb15a..470e154 100644
--- a/src/llc.cpp
+++ b/src/llc.cpp
@@ -122,13 +122,13 @@
 	msgb_enqueue(&m_queue, llc_msg);
 }
 
-void llc_queue_clear(struct gprs_llc_queue *q, struct BTS *bts)
+void llc_queue_clear(struct gprs_llc_queue *q, struct gprs_rlcmac_bts *bts)
 {
 	struct msgb *msg;
 
 	while ((msg = msgb_dequeue(&q->m_queue))) {
 		if (bts)
-			bts->do_rate_ctr_inc(CTR_LLC_FRAME_DROPPED);
+			bts_do_rate_ctr_inc(bts, CTR_LLC_FRAME_DROPPED);
 		msgb_free(msg);
 	}
 
@@ -221,7 +221,7 @@
 	return msg;
 }
 
-void gprs_llc_queue::calc_pdu_lifetime(BTS *bts, const uint16_t pdu_delay_csec, struct timespec *tv)
+void gprs_llc_queue::calc_pdu_lifetime(struct gprs_rlcmac_bts *bts, const uint16_t pdu_delay_csec, struct timespec *tv)
 {
 	uint16_t delay_csec;
 	if (bts->pcu->vty.force_llc_lifetime)
diff --git a/src/llc.h b/src/llc.h
index 72fa62e..13662d8 100644
--- a/src/llc.h
+++ b/src/llc.h
@@ -32,7 +32,7 @@
 
 #define LLC_MAX_LEN 1543
 
-struct BTS;
+struct gprs_rlcmac_bts;
 
 /**
  * I represent the LLC data to a MS
@@ -65,7 +65,7 @@
  */
 struct gprs_llc_queue {
 #ifdef __cplusplus
-	static void calc_pdu_lifetime(BTS *bts, const uint16_t pdu_delay_csec,
+	static void calc_pdu_lifetime(struct gprs_rlcmac_bts *bts, const uint16_t pdu_delay_csec,
 		struct timespec *tv);
 	static bool is_frame_expired(const struct timespec *now,
 		const struct timespec *tv);
@@ -84,7 +84,7 @@
 extern "C" {
 #endif
 void llc_queue_init(struct gprs_llc_queue *q);
-void llc_queue_clear(struct gprs_llc_queue *q, struct BTS *bts);
+void llc_queue_clear(struct gprs_llc_queue *q, struct gprs_rlcmac_bts *bts);
 void llc_queue_move_and_merge(struct gprs_llc_queue *q, struct gprs_llc_queue *o);
 
 static inline uint16_t llc_chunk_size(const struct gprs_llc *llc)
diff --git a/src/pcu_l1_if.cpp b/src/pcu_l1_if.cpp
index a11ec7e..1a47b37 100644
--- a/src/pcu_l1_if.cpp
+++ b/src/pcu_l1_if.cpp
@@ -53,6 +53,7 @@
 #include <pdch.h>
 #include <tbf_ul.h>
 #include <tbf_dl.h>
+#include <gprs_ms_storage.h>
 
 // FIXME: move this, when changed from c++ to c.
 extern "C" {
@@ -271,13 +272,13 @@
 
 extern "C" void pcu_rx_block_time(uint16_t arfcn, uint32_t fn, uint8_t ts_no)
 {
-	BTS::main_bts()->set_current_block_frame_number(fn, 0);
+	bts_set_current_block_frame_number(the_pcu->bts,fn, 0);
 }
 
 extern "C" void pcu_rx_ra_time(uint16_t arfcn, uint32_t fn, uint8_t ts_no)
 {
 	/* access bursts may arrive some bursts earlier */
-	BTS::main_bts()->set_current_block_frame_number(fn, 5);
+	bts_set_current_block_frame_number(the_pcu->bts,fn, 5);
 }
 
 extern "C" int pcu_rx_data_ind_pdtch(uint8_t trx_no, uint8_t ts_no, uint8_t *data,
@@ -368,7 +369,7 @@
 	switch (data_cnf->sapi) {
 	case PCU_IF_SAPI_PCH:
 		if (data_cnf->data[2] == 0x3f)
-			BTS::main_bts()->rcv_imm_ass_cnf(data_cnf->data, data_cnf->fn);
+			bts_rcv_imm_ass_cnf(the_pcu->bts, data_cnf->data, data_cnf->fn);
 		break;
 	default:
 		LOGP(DL1IF, LOGL_ERROR, "Received PCU data confirm with "
@@ -447,7 +448,7 @@
 		.qta = qta,
 	};
 
-	return BTS::main_bts()->rcv_ptcch_rach(&rip);
+	return bts_rcv_ptcch_rach(the_pcu->bts, &rip);
 }
 
 static int pcu_rx_rach_ind(const struct gsm_pcu_if_rach_ind *rach_ind)
@@ -471,10 +472,10 @@
 
 	switch (rach_ind->sapi) {
 	case PCU_IF_SAPI_RACH:
-		rc = BTS::main_bts()->rcv_rach(&rip);
+		rc = bts_rcv_rach(the_pcu->bts, &rip);
 		break;
 	case PCU_IF_SAPI_PTCCH:
-		rc = BTS::main_bts()->rcv_ptcch_rach(&rip);
+		rc = bts_rcv_ptcch_rach(the_pcu->bts, &rip);
 		break;
 	default:
 		LOGP(DL1IF, LOGL_ERROR, "Received PCU rach request with "
@@ -751,7 +752,7 @@
 
 	LOGP(DL1IF, LOGL_DEBUG, "Time indication received: %d\n", time_ind->fn % 52);
 
-	BTS::main_bts()->set_current_frame_number(time_ind->fn);
+	bts_set_current_frame_number(the_pcu->bts, time_ind->fn);
 	return 0;
 }
 
@@ -776,12 +777,12 @@
 		return -EINVAL;
 	}
 
-	return BTS::main_bts()->add_paging(pag_req->chan_needed, &mi);
+	return bts_add_paging(the_pcu->bts, pag_req->chan_needed, &mi);
 }
 
 static int pcu_rx_susp_req(struct gsm_pcu_if_susp_req *susp_req)
 {
-	BTS *bts = BTS::main_bts();
+	struct gprs_rlcmac_bts *bts = the_pcu->bts;
 	struct bssgp_bvc_ctx *bctx = gprs_bssgp_pcu_current_bctx();
 	GprsMs *ms;
 	struct gprs_rlcmac_dl_tbf *dl_tbf;
@@ -793,7 +794,7 @@
 	LOGP(DL1IF, LOGL_INFO, "GPRS Suspend request received: TLLI=0x%08x RAI=%s\n",
 		susp_req->tlli, osmo_rai_name(&ra_id));
 
-	if ((ms = bts->ms_store().get_ms(susp_req->tlli))) {
+	if ((ms = bts_ms_store(bts)->get_ms(susp_req->tlli))) {
 		/* We need to catch both pointers here since MS may become freed
 		   after first tbf_free(dl_tbf) if only DL TBF was available */
 		dl_tbf = ms_dl_tbf(ms);
@@ -812,15 +813,15 @@
 
 static int pcu_rx_app_info_req(struct gsm_pcu_if_app_info_req *app_info_req)
 {
-	BTS *bts = BTS::main_bts();
-	struct gprs_rlcmac_bts *bts_data = bts->bts_data();
+	struct gprs_rlcmac_bts *bts = the_pcu->bts;
+	struct gprs_rlcmac_bts *bts_data = bts;
 	struct llist_head *tmp;
 
 	LOGP(DL1IF, LOGL_DEBUG, "Application Information Request received: type=0x%08x len=%i\n",
 	     app_info_req->application_type, app_info_req->len);
 
 	bts_data->app_info_pending = 0;
-	llist_for_each(tmp, bts->ms_store().ms_list()) {
+	llist_for_each(tmp, bts_ms_store(bts)->ms_list()) {
 		GprsMs *ms = llist_entry(tmp, typeof(*ms), list);
 		if (!ms_dl_tbf(ms))
 			continue;
diff --git a/src/pcu_main.cpp b/src/pcu_main.cpp
index e953d43..8bb7c1f 100644
--- a/src/pcu_main.cpp
+++ b/src/pcu_main.cpp
@@ -342,7 +342,7 @@
 
 	pcu_l1if_close();
 
-	bts_cleanup();
+	TALLOC_FREE(the_pcu);
 	talloc_report_full(tall_pcu_ctx, stderr);
 	talloc_free(tall_pcu_ctx);
 
diff --git a/src/pcu_vty_functions.cpp b/src/pcu_vty_functions.cpp
index 0276b3e..0e9cc00 100644
--- a/src/pcu_vty_functions.cpp
+++ b/src/pcu_vty_functions.cpp
@@ -101,21 +101,23 @@
 	vty_out(vty, "%s%s", VTY_NEWLINE, VTY_NEWLINE);
 }
 
-int pcu_vty_show_tbf_all(struct vty *vty, struct gprs_rlcmac_bts *bts_data, uint32_t flags)
+int pcu_vty_show_tbf_all(struct vty *vty, struct gprs_rlcmac_bts *bts, uint32_t flags)
 {
-	BTS *bts = bts_data->bts;
-	LListHead<gprs_rlcmac_tbf> *iter;
+	struct llist_item *iter;
+	struct gprs_rlcmac_tbf *tbf;
 
 	vty_out(vty, "UL TBFs%s", VTY_NEWLINE);
-	llist_for_each(iter, &bts->ul_tbfs()) {
-		if (iter->entry()->state_flags & flags)
-			tbf_print_vty_info(vty, iter->entry());
+	llist_for_each_entry(iter, &bts->ul_tbfs, list) {
+		tbf = (struct gprs_rlcmac_tbf *)iter->entry;
+		if (tbf->state_flags & flags)
+			tbf_print_vty_info(vty, tbf);
 	}
 
 	vty_out(vty, "%sDL TBFs%s", VTY_NEWLINE, VTY_NEWLINE);
-	llist_for_each(iter, &bts->dl_tbfs()) {
-		if (iter->entry()->state_flags & flags)
-			tbf_print_vty_info(vty, iter->entry());
+	llist_for_each_entry(iter, &bts->dl_tbfs, list) {
+		tbf = (struct gprs_rlcmac_tbf *)iter->entry;
+		if (tbf->state_flags & flags)
+			tbf_print_vty_info(vty, tbf);
 	}
 
 	return CMD_SUCCESS;
@@ -204,12 +206,11 @@
 	return CMD_SUCCESS;
 }
 
-int pcu_vty_show_ms_all(struct vty *vty, struct gprs_rlcmac_bts *bts_data)
+int pcu_vty_show_ms_all(struct vty *vty, struct gprs_rlcmac_bts *bts)
 {
-	BTS *bts = bts_data->bts;
 	struct llist_head *tmp;
 
-	llist_for_each(tmp, bts->ms_store().ms_list()) {
+	llist_for_each(tmp, bts_ms_store(bts)->ms_list()) {
 		GprsMs *ms_iter = llist_entry(tmp, typeof(*ms_iter), list);
 		show_ms(vty, ms_iter);
 	}
@@ -217,11 +218,10 @@
 	return CMD_SUCCESS;
 }
 
-int pcu_vty_show_ms_by_tlli(struct vty *vty, struct gprs_rlcmac_bts *bts_data,
+int pcu_vty_show_ms_by_tlli(struct vty *vty, struct gprs_rlcmac_bts *bts,
 	uint32_t tlli)
 {
-	BTS *bts = bts_data->bts;
-	GprsMs *ms = bts->ms_store().get_ms(tlli);
+	GprsMs *ms = bts_ms_store(bts)->get_ms(tlli);
 	if (!ms) {
 		vty_out(vty, "Unknown TLLI %08x.%s", tlli, VTY_NEWLINE);
 		return CMD_WARNING;
@@ -230,11 +230,10 @@
 	return show_ms(vty, ms);
 }
 
-int pcu_vty_show_ms_by_imsi(struct vty *vty, struct gprs_rlcmac_bts *bts_data,
+int pcu_vty_show_ms_by_imsi(struct vty *vty, struct gprs_rlcmac_bts *bts,
 	const char *imsi)
 {
-	BTS *bts = bts_data->bts;
-	GprsMs *ms = bts->ms_store().get_ms(0, 0, imsi);
+	GprsMs *ms = bts_ms_store(bts)->get_ms(0, 0, imsi);
 	if (!ms) {
 		vty_out(vty, "Unknown IMSI '%s'.%s", imsi, VTY_NEWLINE);
 		return CMD_WARNING;
diff --git a/src/pdch.cpp b/src/pdch.cpp
index 49cce8d..49f0b85 100644
--- a/src/pdch.cpp
+++ b/src/pdch.cpp
@@ -113,9 +113,9 @@
 	}
 }
 
-static inline void sched_ul_ass_or_rej(BTS *bts, gprs_rlcmac_bts *bts_data, struct gprs_rlcmac_dl_tbf *tbf)
+static inline void sched_ul_ass_or_rej(struct gprs_rlcmac_bts *bts, gprs_rlcmac_bts *bts_data, struct gprs_rlcmac_dl_tbf *tbf)
 {
-	bts->do_rate_ctr_inc(CTR_CHANNEL_REQUEST_DESCRIPTION);
+	bts_do_rate_ctr_inc(bts, CTR_CHANNEL_REQUEST_DESCRIPTION);
 
 	/* This call will register the new TBF with the MS on success */
 	gprs_rlcmac_ul_tbf *ul_tbf = tbf_alloc_ul(bts_data, tbf->ms(), tbf->trx->trx_no, tbf->tlli());
@@ -158,7 +158,7 @@
 	while ((pag = dequeue_paging()))
 		talloc_free(pag);
 
-	trx->bts->sba()->free_resources(this);
+	bts_sba(trx->bts)->free_resources(this);
 }
 
 struct gprs_rlcmac_paging *gprs_rlcmac_pdch::dequeue_paging()
@@ -292,12 +292,12 @@
 {
 	struct gprs_rlcmac_tbf *tbf, *new_tbf;
 	uint32_t tlli = packet->TLLI;
-	GprsMs *ms = bts()->ms_by_tlli(tlli);
+	GprsMs *ms = bts_ms_by_tlli(bts(), tlli, GSM_RESERVED_TMSI);
 	gprs_rlcmac_ul_tbf *ul_tbf;
 
-	tbf = bts()->ul_tbf_by_poll_fn(fn, trx_no(), ts_no);
+	tbf = bts_ul_tbf_by_poll_fn(bts(), fn, trx_no(), ts_no);
 	if (!tbf)
-		tbf = bts()->dl_tbf_by_poll_fn(fn, trx_no(), ts_no);
+		tbf = bts_dl_tbf_by_poll_fn(bts(), fn, trx_no(), ts_no);
 
 	if (!tbf) {
 		LOGP(DRLCMAC, LOGL_NOTICE, "PACKET CONTROL ACK with "
@@ -410,7 +410,7 @@
 	char show_bits[RLC_GPRS_WS + 1];
 
 	tfi = ack_nack->DOWNLINK_TFI;
-	tbf = bts()->dl_tbf_by_poll_fn(fn, trx_no(), ts_no);
+	tbf = bts_dl_tbf_by_poll_fn(bts(), fn, trx_no(), ts_no);
 	if (!tbf) {
 		LOGP(DRLCMAC, LOGL_NOTICE, "PACKET DOWNLINK ACK with "
 			"unknown FN=%u TFI=%d (TRX %d TS %d)\n",
@@ -477,7 +477,7 @@
 	int bsn_begin, bsn_end;
 
 	tfi = ack_nack->DOWNLINK_TFI;
-	tbf = bts()->dl_tbf_by_poll_fn(fn, trx_no(), ts_no);
+	tbf = bts_dl_tbf_by_poll_fn(bts(), fn, trx_no(), ts_no);
 	if (!tbf) {
 		LOGP(DRLCMAC, LOGL_NOTICE, "EGPRS PACKET DOWNLINK ACK with "
 			"unknown FN=%u TFI=%d (TRX %d TS %d)\n",
@@ -566,10 +566,10 @@
 		uint32_t tlli = request->ID.u.TLLI;
 		bool ms_found = true;
 
-		GprsMs *ms = bts()->ms_by_tlli(tlli);
+		GprsMs *ms = bts_ms_by_tlli(bts(), tlli, GSM_RESERVED_TMSI);
 		if (!ms) {
 			ms_found = false;
-			ms = bts()->ms_alloc(0, 0); /* ms class updated later */
+			ms = bts_alloc_ms(bts(), 0, 0); /* ms class updated later */
 			ms_set_tlli(ms, tlli);
 		}
 		ul_tbf = ms_ul_tbf(ms); /* hence ul_tbf may be NULL */
@@ -580,10 +580,10 @@
 		LOGP(DRLCMAC, LOGL_DEBUG, "MS requests UL TBF "
 			"in packet resource request of single "
 			"block, so we provide one:\n");
-		sba = bts()->sba()->find(this, fn);
+		sba = bts_sba(bts())->find(this, fn);
 		if (sba) {
 			ms_set_ta(ms, sba->ta);
-			bts()->sba()->free_sba(sba);
+			bts_sba(bts())->free_sba(sba);
 		} else if (!ul_tbf || !ul_tbf->state_is(GPRS_RLCMAC_FINISHED)) {
 			LOGPTBFUL(ul_tbf, LOGL_NOTICE,
 				  "MS requests UL TBF in PACKET RESOURCE REQ of "
@@ -640,7 +640,7 @@
 	if (request->ID.u.Global_TFI.UnionType) {
 		struct gprs_rlcmac_dl_tbf *dl_tbf;
 		int8_t tfi = request->ID.u.Global_TFI.u.DOWNLINK_TFI;
-		dl_tbf = bts()->dl_tbf_by_tfi(tfi, trx_no(), ts_no);
+		dl_tbf = bts_dl_tbf_by_tfi(bts(), tfi, trx_no(), ts_no);
 		if (!dl_tbf) {
 			LOGP(DRLCMAC, LOGL_NOTICE, "PACKET RESOURCE REQ unknown downlink TFI=%d\n", tfi);
 			return;
@@ -653,7 +653,7 @@
 	} else {
 		struct gprs_rlcmac_ul_tbf *ul_tbf;
 		int8_t tfi = request->ID.u.Global_TFI.u.UPLINK_TFI;
-		ul_tbf = bts()->ul_tbf_by_tfi(tfi, trx_no(), ts_no);
+		ul_tbf = bts_ul_tbf_by_tfi(bts(), tfi, trx_no(), ts_no);
 		if (!ul_tbf) {
 			LOGP(DRLCMAC, LOGL_NOTICE, "PACKET RESOURCE REQ unknown uplink TFI=%d\n", tfi);
 			return;
@@ -671,16 +671,16 @@
 	struct gprs_rlcmac_sba *sba;
 	GprsMs *ms;
 
-	ms = bts()->ms_by_tlli(report->TLLI);
+	ms = bts_ms_by_tlli(bts(), report->TLLI, GSM_RESERVED_TMSI);
 	if (!ms) {
 		LOGP(DRLCMAC, LOGL_NOTICE, "MS send measurement "
 		     "but TLLI 0x%08x is unknown\n", report->TLLI);
-		ms = bts()->ms_alloc(0, 0);
+		ms = bts_alloc_ms(bts(), 0, 0);
 		ms_set_tlli(ms, report->TLLI);
 	}
-	if ((sba = bts()->sba()->find(this, fn))) {
+	if ((sba = bts_sba(bts())->find(this, fn))) {
 		ms_set_ta(ms, sba->ta);
-		bts()->sba()->free_sba(sba);
+		bts_sba(bts())->free_sba(sba);
 	}
 	gprs_rlcmac_meas_rep(ms, report);
 }
@@ -703,9 +703,9 @@
 
 	rc = decode_gsm_rlcmac_uplink(rlc_block, ul_control_block);
 	if (ul_control_block->u.MESSAGE_TYPE == MT_PACKET_UPLINK_DUMMY_CONTROL_BLOCK)
-		bts()->send_gsmtap_meas(PCU_GSMTAP_C_UL_DUMMY, true, trx_no(), ts_no, GSMTAP_CHANNEL_PACCH, fn, data, data_len, meas);
+		bts_send_gsmtap_meas(bts(), PCU_GSMTAP_C_UL_DUMMY, true, trx_no(), ts_no, GSMTAP_CHANNEL_PACCH, fn, data, data_len, meas);
 	else
-		bts()->send_gsmtap_meas(PCU_GSMTAP_C_UL_CTRL, true, trx_no(), ts_no, GSMTAP_CHANNEL_PACCH, fn, data, data_len, meas);
+		bts_send_gsmtap_meas(bts(), PCU_GSMTAP_C_UL_CTRL, true, trx_no(), ts_no, GSMTAP_CHANNEL_PACCH, fn, data, data_len, meas);
 
 	if (rc < 0) {
 		LOGP(DRLCMACUL, LOGL_ERROR, "Dropping Uplink Control Block with invalid "
@@ -714,7 +714,7 @@
 	}
 	LOGP(DRLCMAC, LOGL_DEBUG, "------------------------- RX : Uplink Control Block -------------------------\n");
 
-	bts()->do_rate_ctr_inc(CTR_RLC_RECV_CONTROL);
+	bts_do_rate_ctr_inc(bts(), CTR_RLC_RECV_CONTROL);
 	switch (ul_control_block->u.MESSAGE_TYPE) {
 	case MT_PACKET_CONTROL_ACK:
 		rcv_control_ack(&ul_control_block->u.Packet_Control_Acknowledgement, fn);
@@ -735,7 +735,7 @@
 		/* ignoring it. change the SI to not force sending these? */
 		break;
 	default:
-		bts()->do_rate_ctr_inc(CTR_DECODE_ERRORS);
+		bts_do_rate_ctr_inc(bts(), CTR_DECODE_ERRORS);
 		LOGP(DRLCMAC, LOGL_NOTICE,
 			"RX: [PCU <- BTS] unknown control block(%d) received\n",
 			ul_control_block->u.MESSAGE_TYPE);
@@ -752,13 +752,13 @@
 {
 	enum CodingScheme cs = mcs_get_by_size_ul(len);
 	if (!cs) {
-		bts()->do_rate_ctr_inc(CTR_DECODE_ERRORS);
+		bts_do_rate_ctr_inc(bts(), CTR_DECODE_ERRORS);
 		LOGP(DRLCMACUL, LOGL_ERROR, "Dropping data block with invalid "
 		     "length %d: %s\n", len, osmo_hexdump(data, len));
 		return -EINVAL;
 	}
 
-	bts()->do_rate_ctr_add(CTR_RLC_UL_BYTES, len);
+	bts_do_rate_ctr_add(bts(), CTR_RLC_UL_BYTES, len);
 
 	LOGP(DRLCMACUL, LOGL_DEBUG, "Got RLC block, coding scheme: %s, "
 		"length: %d (%d))\n", mcs_name(cs), len, mcs_used_size_ul(cs));
@@ -769,7 +769,7 @@
 	if (mcs_is_edge(cs))
 		return rcv_data_block(data, len, fn, meas, cs);
 
-	bts()->do_rate_ctr_inc(CTR_DECODE_ERRORS);
+	bts_do_rate_ctr_inc(bts(), CTR_DECODE_ERRORS);
 	LOGP(DRLCMACUL, LOGL_ERROR, "Unsupported coding scheme %s\n",
 		mcs_name(cs));
 	return -EINVAL;
@@ -788,11 +788,11 @@
 	 * control blocks (see 44.060, section 10.3, 1st par.)
 	 */
 	if (mcs_is_edge(cs)) {
-		bts()->send_gsmtap_meas(PCU_GSMTAP_C_UL_DATA_EGPRS, true,
+		bts_send_gsmtap_meas(bts(), PCU_GSMTAP_C_UL_DATA_EGPRS, true,
 					trx_no(), ts_no, GSMTAP_CHANNEL_PDTCH, fn,
 					data, data_len, meas);
 	} else {
-		bts()->send_gsmtap_meas(PCU_GSMTAP_C_UL_DATA_GPRS, true,
+		bts_send_gsmtap_meas(bts(), PCU_GSMTAP_C_UL_DATA_GPRS, true,
 					trx_no(), ts_no, GSMTAP_CHANNEL_PDTCH, fn,
 					data, data_len, meas);
 	}
@@ -804,7 +804,7 @@
 		LOGP(DRLCMACUL, LOGL_ERROR,
 			"Got %s RLC block but header parsing has failed\n",
 			mcs_name(cs));
-		bts()->do_rate_ctr_inc(CTR_DECODE_ERRORS);
+		bts_do_rate_ctr_inc(bts(), CTR_DECODE_ERRORS);
 		return rc;
 	}
 
@@ -974,7 +974,7 @@
 	m_num_reserved[dir] -= 1;
 }
 
-inline BTS *gprs_rlcmac_pdch::bts() const
+inline struct gprs_rlcmac_bts *gprs_rlcmac_pdch::bts() const
 {
 	return trx->bts;
 }
@@ -986,7 +986,7 @@
 
 inline gprs_rlcmac_bts *gprs_rlcmac_pdch::bts_data() const
 {
-	return trx->bts->bts_data();
+	return trx->bts;
 }
 
 /* PTCCH (Packet Timing Advance Control Channel) */
diff --git a/src/pdch.h b/src/pdch.h
index 5185045..7a544fe 100644
--- a/src/pdch.h
+++ b/src/pdch.h
@@ -70,7 +70,7 @@
 		struct pcu_l1_meas *meas, enum CodingScheme cs);
 
 	gprs_rlcmac_bts *bts_data() const;
-	BTS *bts() const;
+	struct gprs_rlcmac_bts *bts() const;
 	uint8_t trx_no() const;
 
 	struct gprs_rlcmac_ul_tbf *ul_tbf_by_tfi(uint8_t tfi);
diff --git a/src/poll_controller.cpp b/src/poll_controller.cpp
index ac79510..04ec4fd 100644
--- a/src/poll_controller.cpp
+++ b/src/poll_controller.cpp
@@ -24,6 +24,7 @@
 #include <bts.h>
 #include <tbf.h>
 #include <tbf_ul.h>
+#include <tbf_dl.h>
 #include <cxx_linuxlist.h>
 #include <sba.h>
 
@@ -32,7 +33,7 @@
 #include <osmocom/gsm/gsm_utils.h>
 }
 
-PollController::PollController(BTS& bts)
+PollController::PollController(struct gprs_rlcmac_bts& bts)
 	: m_bts(bts)
 {}
 
@@ -51,26 +52,26 @@
 	struct gprs_rlcmac_dl_tbf *dl_tbf;
 	struct gprs_rlcmac_ul_tbf *ul_tbf;
 	struct gprs_rlcmac_sba *sba, *sba2;
-	LListHead<gprs_rlcmac_tbf> *pos;
+	struct llist_item *pos;
 
-	llist_for_each(pos, &m_bts.ul_tbfs()) {
-		ul_tbf = as_ul_tbf(pos->entry());
+	llist_for_each_entry(pos, &m_bts.ul_tbfs, list) {
+		ul_tbf = as_ul_tbf((struct gprs_rlcmac_tbf *)pos->entry);
 		if (ul_tbf->poll_scheduled()) {
 			if (elapsed_fn_check(max_delay, frame_number, ul_tbf->poll_fn))
 				ul_tbf->poll_timeout();
 		}
 	}
-	llist_for_each(pos, &m_bts.dl_tbfs()) {
-		dl_tbf = as_dl_tbf(pos->entry());
+	llist_for_each_entry(pos, &m_bts.dl_tbfs, list) {
+		dl_tbf = as_dl_tbf((struct gprs_rlcmac_tbf *)pos->entry);
 		if (dl_tbf->poll_scheduled()) {
 			if (elapsed_fn_check(max_delay, frame_number, dl_tbf->poll_fn))
 				dl_tbf->poll_timeout();
 		}
 	}
-	llist_for_each_entry_safe(sba, sba2, &m_bts.sba()->m_sbas, list) {
+	llist_for_each_entry_safe(sba, sba2, &bts_sba(&m_bts)->m_sbas, list) {
 		if (elapsed_fn_check(max_delay, frame_number, sba->fn)) {
 			/* sba will be freed here */
-			m_bts.sba()->timeout(sba);
+			bts_sba(&m_bts)->timeout(sba);
 		}
 	}
 
diff --git a/src/poll_controller.h b/src/poll_controller.h
index 65d1fee..8e709a3 100644
--- a/src/poll_controller.h
+++ b/src/poll_controller.h
@@ -21,22 +21,22 @@
 
 #pragma once
 
-struct BTS;
+struct gprs_rlcmac_bts;
 
 /**
  * I belong to a BTS and I am responsible for finding TBFs and
  * SBAs that should have been polled and execute the timeout
  * action on them.
  */
-class PollController {
+struct PollController {
 public:
-	PollController(BTS& bts);
+	PollController(struct gprs_rlcmac_bts& bts);
 
 	/* check for poll timeout */
 	void expireTimedout(int frame_number, unsigned max_delay);
 
 private:
-	BTS& m_bts;
+	struct gprs_rlcmac_bts& m_bts;
 
 private:
 	/* disable copying to avoid slicing */
diff --git a/src/rlc.cpp b/src/rlc.cpp
index 8f56a8e..a2cc52c 100644
--- a/src/rlc.cpp
+++ b/src/rlc.cpp
@@ -127,7 +127,7 @@
 	return (ssn - 1 - bitnum);
 }
 
-void gprs_rlc_dl_window::update(BTS *bts, const struct bitvec *rbb,
+void gprs_rlc_dl_window::update(struct gprs_rlcmac_bts *bts, const struct bitvec *rbb,
 			uint16_t first_bsn, uint16_t *lost,
 			uint16_t *received)
 {
@@ -154,13 +154,13 @@
 		} else {
 			LOGP(DRLCMACDL, LOGL_DEBUG, "- got NACK for BSN=%d\n", bsn);
 			m_v_b.mark_nacked(bsn);
-			bts->do_rate_ctr_inc(CTR_RLC_NACKED);
+			bts_do_rate_ctr_inc(bts, CTR_RLC_NACKED);
 			*lost += 1;
 		}
 	}
 }
 
-void gprs_rlc_dl_window::update(BTS *bts, char *show_rbb, uint16_t ssn,
+void gprs_rlc_dl_window::update(struct gprs_rlcmac_bts *bts, char *show_rbb, uint16_t ssn,
 			uint16_t *lost, uint16_t *received)
 {
 	/* SSN - 1 is in range V(A)..V(S)-1 */
@@ -178,7 +178,7 @@
 		} else {
 			LOGP(DRLCMACDL, LOGL_DEBUG, "- got NACK for BSN=%d\n", bsn);
 			m_v_b.mark_nacked(bsn);
-			bts->do_rate_ctr_inc(CTR_RLC_NACKED);
+			bts_do_rate_ctr_inc(bts, CTR_RLC_NACKED);
 			*lost += 1;
 		}
 	}
diff --git a/src/rlc.h b/src/rlc.h
index 707f305..eb79430 100644
--- a/src/rlc.h
+++ b/src/rlc.h
@@ -38,7 +38,7 @@
 #define RLC_MAX_WS   RLC_EGPRS_MAX_WS
 #define RLC_MAX_LEN 74 /* MCS-9 data unit */
 
-struct BTS;
+struct gprs_rlcmac_bts;
 
 /* The state of a BSN in the send/receive window */
 enum gprs_rlc_ul_bsn_state {
@@ -307,9 +307,9 @@
 	/* Methods to manage reception */
 	int resend_needed() const;
 	int mark_for_resend();
-	void update(BTS *bts, char *show_rbb, uint16_t ssn,
+	void update(struct gprs_rlcmac_bts *bts, char *show_rbb, uint16_t ssn,
 			uint16_t *lost, uint16_t *received);
-	void update(BTS *bts, const struct bitvec *rbb,
+	void update(struct gprs_rlcmac_bts *bts, const struct bitvec *rbb,
 			uint16_t first_bsn, uint16_t *lost,
 			uint16_t *received);
 	int move_window();
diff --git a/src/sba.cpp b/src/sba.cpp
index cc58405..53eb847 100644
--- a/src/sba.cpp
+++ b/src/sba.cpp
@@ -40,7 +40,7 @@
  * This offset must be a multiple of 13. */
 #define AGCH_START_OFFSET 52
 
-SBAController::SBAController(BTS &bts)
+SBAController::SBAController(struct gprs_rlcmac_bts &bts)
 	: m_bts(bts)
 {
 	INIT_LLIST_HEAD(&m_sbas);
@@ -64,7 +64,7 @@
 
 	for (trx = 0; trx < 8; trx++) {
 		for (ts = 7; ts >= 0; ts--) {
-			pdch = &m_bts.bts_data()->trx[trx].pdch[ts];
+			pdch = &m_bts.trx[trx].pdch[ts];
 			if (!pdch->is_enabled())
 				continue;
 			break;
@@ -86,7 +86,7 @@
 	sba->ta = ta;
 
 	llist_add(&sba->list, &m_sbas);
-	m_bts.do_rate_ctr_inc(CTR_SBA_ALLOCATED);
+	bts_do_rate_ctr_inc(&m_bts, CTR_SBA_ALLOCATED);
 
 	*_trx = trx;
 	*_ts = ts;
@@ -132,14 +132,14 @@
 	LOGP(DRLCMAC, LOGL_NOTICE,
 	     "Poll timeout for SBA (TRX=%u, TS=%u, FN=%u, TA=%u)\n", sba->trx_no,
 	     sba->ts_no, sba->fn, sba->ta);
-	m_bts.do_rate_ctr_inc(CTR_SBA_TIMEDOUT);
+	bts_do_rate_ctr_inc(&m_bts, CTR_SBA_TIMEDOUT);
 	free_sba(sba);
 	return 0;
 }
 
 void SBAController::free_sba(gprs_rlcmac_sba *sba)
 {
-	m_bts.do_rate_ctr_inc(CTR_SBA_FREED);
+	bts_do_rate_ctr_inc(&m_bts, CTR_SBA_FREED);
 	llist_del(&sba->list);
 	talloc_free(sba);
 }
diff --git a/src/sba.h b/src/sba.h
index 27dae21..a6e3f82 100644
--- a/src/sba.h
+++ b/src/sba.h
@@ -26,7 +26,7 @@
 #include <osmocom/core/linuxlist.h>
 }
 
-struct BTS;
+struct gprs_rlcmac_bts;
 struct gprs_rlcmac_pdch;
 
 /*
@@ -45,10 +45,10 @@
  *
  * TODO: Add a flush method..
  */
-class SBAController {
+struct SBAController {
 	friend class PollController;
 public:
-	SBAController(BTS &bts);
+	SBAController(struct gprs_rlcmac_bts &bts);
 
 	int alloc(uint8_t *_trx, uint8_t *_ts, uint32_t *_fn, uint8_t ta);
 	gprs_rlcmac_sba *find(uint8_t trx, uint8_t ts, uint32_t fn);
@@ -62,6 +62,6 @@
 	void free_sba(gprs_rlcmac_sba *sba);
 
 private:
-	BTS &m_bts;
+	struct gprs_rlcmac_bts &m_bts;
 	llist_head m_sbas;
 };
diff --git a/src/tbf.cpp b/src/tbf.cpp
index 43c8cbf..05f4e2c 100644
--- a/src/tbf.cpp
+++ b/src/tbf.cpp
@@ -124,7 +124,7 @@
 	timespecclear(&rssi_tv);
 }
 
-gprs_rlcmac_tbf::gprs_rlcmac_tbf(BTS *bts_, GprsMs *ms, gprs_rlcmac_tbf_direction dir) :
+gprs_rlcmac_tbf::gprs_rlcmac_tbf(struct gprs_rlcmac_bts *bts_, GprsMs *ms, gprs_rlcmac_tbf_direction dir) :
 	state_flags(0),
 	direction(dir),
 	trx(NULL),
@@ -147,7 +147,6 @@
 	ul_ass_state(GPRS_RLCMAC_UL_ASS_NONE),
 	ul_ack_state(GPRS_RLCMAC_UL_ACK_NONE),
 	poll_state(GPRS_RLCMAC_POLL_NONE),
-	m_list(this),
 	m_egprs_enabled(false)
 {
 	/* The classes of these members do not have proper constructors yet.
@@ -160,6 +159,9 @@
 	memset(&m_ms_list, 0, sizeof(m_ms_list));
 	m_ms_list.entry = this;
 
+	memset(&m_bts_list, 0, sizeof(m_bts_list));
+	m_bts_list.entry = this;
+
 	m_rlc.init();
 	m_llc.init();
 
@@ -168,7 +170,7 @@
 
 gprs_rlcmac_bts *gprs_rlcmac_tbf::bts_data() const
 {
-	return bts->bts_data();
+	return bts;
 }
 
 uint32_t gprs_rlcmac_tbf::tlli() const
@@ -245,7 +247,7 @@
 	if (!ms_check_tlli(ms(), tlli)) {
 		GprsMs *old_ms;
 
-		old_ms = bts->ms_store().get_ms(tlli, 0, NULL);
+		old_ms = bts_ms_store(bts)->get_ms(tlli, 0, NULL);
 		if (old_ms)
 			ms_merge_and_clear_ms(ms(), old_ms);
 	}
@@ -274,9 +276,9 @@
 	/* update counters */
 	if (tbf->direction == GPRS_RLCMAC_UL_TBF) {
 		gprs_rlcmac_ul_tbf *ul_tbf = as_ul_tbf(tbf);
-		tbf->bts->do_rate_ctr_inc(CTR_TBF_UL_FREED);
+		bts_do_rate_ctr_inc(tbf->bts, CTR_TBF_UL_FREED);
 		if (tbf->state_is(GPRS_RLCMAC_FLOW))
-			tbf->bts->do_rate_ctr_inc(CTR_TBF_UL_ABORTED);
+			bts_do_rate_ctr_inc(tbf->bts, CTR_TBF_UL_ABORTED);
 		rate_ctr_group_free(ul_tbf->m_ul_egprs_ctrs);
 		rate_ctr_group_free(ul_tbf->m_ul_gprs_ctrs);
 	} else {
@@ -286,9 +288,9 @@
 		} else {
 			rate_ctr_group_free(dl_tbf->m_dl_gprs_ctrs);
 		}
-		tbf->bts->do_rate_ctr_inc(CTR_TBF_DL_FREED);
+		bts_do_rate_ctr_inc(tbf->bts, CTR_TBF_DL_FREED);
 		if (tbf->state_is(GPRS_RLCMAC_FLOW))
-			tbf->bts->do_rate_ctr_inc(CTR_TBF_DL_ABORTED);
+			bts_do_rate_ctr_inc(tbf->bts, CTR_TBF_DL_ABORTED);
 	}
 
 	/* Give final measurement report */
@@ -304,7 +306,7 @@
 	tbf->stop_timers("freeing TBF");
 	/* TODO: Could/Should generate  bssgp_tx_llc_discarded */
 	tbf_unlink_pdch(tbf);
-	llist_del(&tbf->list());
+	llist_del(tbf_bts_list(tbf));
 
 	if (tbf->ms())
 		tbf->set_ms(NULL);
@@ -326,7 +328,7 @@
 
 int gprs_rlcmac_tbf::update()
 {
-	struct gprs_rlcmac_bts *bts_data = bts->bts_data();
+	struct gprs_rlcmac_bts *bts_data = bts;
 	int rc;
 
 	if (direction != GPRS_RLCMAC_DL_TBF)
@@ -399,13 +401,13 @@
 
 	switch(n) {
 	case N3101:
-		chk = bts->bts_data()->n3101;
+		chk = bts->n3101;
 		break;
 	case N3103:
-		chk = bts->bts_data()->n3103;
+		chk = bts->n3103;
 		break;
 	case N3105:
-		chk = bts->bts_data()->n3105;
+		chk = bts->n3105;
 		break;
 	default:
 		LOGPTBF(this, LOGL_ERROR, "unhandled counter %s\n",
@@ -492,7 +494,7 @@
 	int microsec;
 	struct osmo_tdef *tdef;
 
-	if (!(tdef = osmo_tdef_get_entry(bts->bts_data()->T_defs_bts, T)))
+	if (!(tdef = osmo_tdef_get_entry(bts->T_defs_bts, T)))
 		tdef = osmo_tdef_get_entry(bts->pcu->T_defs, T);
 
 	if (t >= T_MAX || !tdef) {
@@ -563,7 +565,7 @@
 		LOGPTBF(this, LOGL_DEBUG, "Polling is already scheduled\n");
 		return -EBUSY;
 	}
-	if (bts->sba()->find(trx->trx_no, ts, next_fn(fn, 13))) {
+	if (bts_sba(bts)->find(trx->trx_no, ts, next_fn(fn, 13))) {
 		LOGPTBF(this, LOGL_DEBUG, "Polling is already scheduled "
 			"for single block allocation at FN %d TS %d ...\n",
 			new_poll_fn, ts);
@@ -628,7 +630,7 @@
 	gprs_rlcmac_ul_tbf *ul_tbf = as_ul_tbf(this);
 
 	LOGPTBF(this, LOGL_NOTICE, "poll timeout for FN=%d, TS=%d (curr FN %d)\n",
-		poll_fn, poll_ts, bts->current_frame_number());
+		poll_fn, poll_ts, bts_current_frame_number(bts));
 
 	poll_state = GPRS_RLCMAC_POLL_NONE;
 
@@ -644,11 +646,11 @@
 				"Timeout for polling PACKET CONTROL ACK for PACKET UPLINK ACK: %s\n",
 				rlcmac_diag().c_str());
 		}
-		bts->do_rate_ctr_inc(CTR_RLC_ACK_TIMEDOUT);
-		bts->do_rate_ctr_inc(CTR_PUAN_POLL_TIMEDOUT);
+		bts_do_rate_ctr_inc(bts, CTR_RLC_ACK_TIMEDOUT);
+		bts_do_rate_ctr_inc(bts, CTR_PUAN_POLL_TIMEDOUT);
 		if (state_is(GPRS_RLCMAC_FINISHED)) {
 			if (ul_tbf->n_inc(N3103)) {
-				bts->do_rate_ctr_inc(CTR_PUAN_POLL_FAILED);
+				bts_do_rate_ctr_inc(bts, CTR_PUAN_POLL_FAILED);
 				TBF_SET_STATE(ul_tbf, GPRS_RLCMAC_RELEASING);
 				T_START(ul_tbf, T3169, 3169, "MAX N3103 reached", false);
 				return;
@@ -665,13 +667,13 @@
 			state_flags |= (1 << GPRS_RLCMAC_FLAG_TO_UL_ASS);
 		}
 		ul_ass_state = GPRS_RLCMAC_UL_ASS_NONE;
-		bts->do_rate_ctr_inc(CTR_RLC_ASS_TIMEDOUT);
-		bts->do_rate_ctr_inc(CTR_PUA_POLL_TIMEDOUT);
+		bts_do_rate_ctr_inc(bts, CTR_RLC_ASS_TIMEDOUT);
+		bts_do_rate_ctr_inc(bts, CTR_PUA_POLL_TIMEDOUT);
 		if (n_inc(N3105)) {
 			TBF_SET_STATE(this, GPRS_RLCMAC_RELEASING);
 			T_START(this, T3195, 3195, "MAX N3105 reached", true);
-			bts->do_rate_ctr_inc(CTR_RLC_ASS_FAILED);
-			bts->do_rate_ctr_inc(CTR_PUA_POLL_FAILED);
+			bts_do_rate_ctr_inc(bts, CTR_RLC_ASS_FAILED);
+			bts_do_rate_ctr_inc(bts, CTR_PUA_POLL_FAILED);
 			return;
 		}
 		/* reschedule UL assignment */
@@ -684,13 +686,13 @@
 			state_flags |= (1 << GPRS_RLCMAC_FLAG_TO_DL_ASS);
 		}
 		dl_ass_state = GPRS_RLCMAC_DL_ASS_NONE;
-		bts->do_rate_ctr_inc(CTR_RLC_ASS_TIMEDOUT);
-		bts->do_rate_ctr_inc(CTR_PDA_POLL_TIMEDOUT);
+		bts_do_rate_ctr_inc(bts, CTR_RLC_ASS_TIMEDOUT);
+		bts_do_rate_ctr_inc(bts, CTR_PDA_POLL_TIMEDOUT);
 		if (n_inc(N3105)) {
 			TBF_SET_STATE(this, GPRS_RLCMAC_RELEASING);
 			T_START(this, T3195, 3195, "MAX N3105 reached", true);
-			bts->do_rate_ctr_inc(CTR_RLC_ASS_FAILED);
-			bts->do_rate_ctr_inc(CTR_PDA_POLL_FAILED);
+			bts_do_rate_ctr_inc(bts, CTR_RLC_ASS_FAILED);
+			bts_do_rate_ctr_inc(bts, CTR_PDA_POLL_FAILED);
 			return;
 		}
 		/* reschedule DL assignment */
@@ -706,17 +708,17 @@
 		}
 
 		if (dl_tbf->state_is(GPRS_RLCMAC_RELEASING))
-			bts->do_rate_ctr_inc(CTR_RLC_REL_TIMEDOUT);
+			bts_do_rate_ctr_inc(bts, CTR_RLC_REL_TIMEDOUT);
 		else {
-			bts->do_rate_ctr_inc(CTR_RLC_ACK_TIMEDOUT);
-			bts->do_rate_ctr_inc(CTR_PDAN_POLL_TIMEDOUT);
+			bts_do_rate_ctr_inc(bts, CTR_RLC_ACK_TIMEDOUT);
+			bts_do_rate_ctr_inc(bts, CTR_PDAN_POLL_TIMEDOUT);
 		}
 
 		if (dl_tbf->n_inc(N3105)) {
 			TBF_SET_STATE(dl_tbf, GPRS_RLCMAC_RELEASING);
 			T_START(dl_tbf, T3195, 3195, "MAX N3105 reached", true);
-			bts->do_rate_ctr_inc(CTR_PDAN_POLL_FAILED);
-			bts->do_rate_ctr_inc(CTR_RLC_ACK_FAILED);
+			bts_do_rate_ctr_inc(bts, CTR_PDAN_POLL_FAILED);
+			bts_do_rate_ctr_inc(bts, CTR_RLC_ACK_FAILED);
 			return;
 		}
 		/* resend IMM.ASS on CCCH on timeout */
@@ -727,7 +729,7 @@
 			/* send immediate assignment */
 			if ((pgroup = imsi2paging_group(imsi())) > 999)
 				LOGPTBF(dl_tbf, LOGL_ERROR, "IMSI to paging group failed! (%s)\n", imsi());
-			dl_tbf->bts->snd_dl_ass(dl_tbf, false, pgroup);
+			bts_snd_dl_ass(dl_tbf->bts, dl_tbf, false, pgroup);
 			dl_tbf->m_wait_confirm = 1;
 		}
 	} else
@@ -736,7 +738,7 @@
 
 int gprs_rlcmac_tbf::setup(int8_t use_trx, bool single_slot)
 {
-	struct gprs_rlcmac_bts *bts_data = bts->bts_data();
+	struct gprs_rlcmac_bts *bts_data = bts;
 	int rc;
 
 	if (ms_mode(m_ms) != GPRS)
@@ -935,7 +937,7 @@
 		goto free_ret;
 	}
 	LOGP(DTBF, LOGL_DEBUG, "------------------------- TX : Packet Downlink Assignment -------------------------\n");
-	bts->do_rate_ctr_inc(CTR_PKT_DL_ASSIGNMENT);
+	bts_do_rate_ctr_inc(bts, CTR_PKT_DL_ASSIGNMENT);
 
 	if (poll_ass_dl) {
 		set_polling(new_poll_fn, ts, GPRS_RLCMAC_POLL_DL_ASS);
@@ -970,7 +972,7 @@
 	Encoding::write_packet_access_reject(
 		packet_access_rej, tlli());
 
-	bts->do_rate_ctr_inc(CTR_PKT_ACCESS_REJ);
+	bts_do_rate_ctr_inc(bts, CTR_PKT_ACCESS_REJ);
 
 	bitvec_pack(packet_access_rej, msgb_put(msg, GSM_MACBLOCK_LEN));
 
@@ -1039,7 +1041,7 @@
 		goto free_ret;
 	}
 	LOGP(DTBF, LOGL_DEBUG, "------------------------- TX : Packet Uplink Assignment -------------------------\n");
-	bts->do_rate_ctr_inc(CTR_PKT_UL_ASSIGNMENT);
+	bts_do_rate_ctr_inc(bts, CTR_PKT_UL_ASSIGNMENT);
 
 	set_polling(new_poll_fn, ts, GPRS_RLCMAC_POLL_UL_ASS);
 
@@ -1076,9 +1078,9 @@
 {
 	struct gprs_rlcmac_dl_tbf *new_tbf = NULL;
 
-	bts->do_rate_ctr_inc(CTR_TBF_REUSED);
+	bts_do_rate_ctr_inc(bts, CTR_TBF_REUSED);
 
-	new_tbf = tbf_alloc_dl_tbf(bts->bts_data(), ms(),
+	new_tbf = tbf_alloc_dl_tbf(bts, ms(),
 		this->trx->trx_no, false);
 
 	if (!new_tbf) {
@@ -1112,11 +1114,11 @@
 
 void gprs_rlcmac_tbf::rotate_in_list()
 {
-	llist_del(&list());
+	llist_del(tbf_bts_list((struct gprs_rlcmac_tbf *)this));
 	if (direction == GPRS_RLCMAC_UL_TBF)
-		llist_add(&list(), &bts->ul_tbfs());
+		llist_add(tbf_bts_list((struct gprs_rlcmac_tbf *)this), &bts->ul_tbfs);
 	else
-		llist_add(&list(), &bts->dl_tbfs());
+		llist_add(tbf_bts_list((struct gprs_rlcmac_tbf *)this), &bts->dl_tbfs);
 }
 
 uint8_t gprs_rlcmac_tbf::tsc() const
@@ -1186,6 +1188,11 @@
 	return &tbf->m_ms_list.list;
 }
 
+struct llist_head *tbf_bts_list(struct gprs_rlcmac_tbf *tbf)
+{
+	return &tbf->m_bts_list.list;
+}
+
 struct GprsMs *tbf_ms(struct gprs_rlcmac_tbf *tbf)
 {
 	return tbf->ms();
diff --git a/src/tbf.h b/src/tbf.h
index d767547..cb4c9b9 100644
--- a/src/tbf.h
+++ b/src/tbf.h
@@ -196,6 +196,7 @@
 enum gprs_rlcmac_tbf_direction tbf_direction(const struct gprs_rlcmac_tbf *tbf);
 void tbf_set_ms(struct gprs_rlcmac_tbf *tbf, struct GprsMs *ms);
 struct llist_head *tbf_ms_list(struct gprs_rlcmac_tbf *tbf);
+struct llist_head *tbf_bts_list(struct gprs_rlcmac_tbf *tbf);
 struct GprsMs *tbf_ms(struct gprs_rlcmac_tbf *tbf);
 bool tbf_timers_pending(struct gprs_rlcmac_tbf *tbf, enum tbf_timers t);
 void tbf_free(struct gprs_rlcmac_tbf *tbf);
@@ -212,7 +213,7 @@
 #ifdef __cplusplus
 
 struct gprs_rlcmac_tbf {
-	gprs_rlcmac_tbf(BTS *bts_, GprsMs *ms, gprs_rlcmac_tbf_direction dir);
+	gprs_rlcmac_tbf(struct gprs_rlcmac_bts *bts_, GprsMs *ms, gprs_rlcmac_tbf_direction dir);
 	virtual ~gprs_rlcmac_tbf() {}
 
 	static void free_all(struct gprs_rlcmac_trx *trx);
@@ -296,9 +297,6 @@
 	/* attempt to make things a bit more fair */
 	void rotate_in_list();
 
-	LListHead<gprs_rlcmac_tbf>& list();
-	const LListHead<gprs_rlcmac_tbf>& list() const;
-
 	uint32_t state_flags;
 	enum gprs_rlcmac_tbf_direction direction;
 	struct gprs_rlcmac_trx *trx;
@@ -335,7 +333,7 @@
 	uint8_t upgrade_to_multislot;
 
 	/* store the BTS this TBF belongs to */
-	BTS *bts;
+	struct gprs_rlcmac_bts *bts;
 
 	/*
 	 * private fields. We can't make it private as it is breaking the
@@ -347,6 +345,7 @@
 	struct rate_ctr_group *m_ctrs;
 	enum gprs_rlcmac_tbf_state state;
 	struct llist_item m_ms_list;
+	struct llist_item m_bts_list;
 
 protected:
 	gprs_rlcmac_bts *bts_data() const;
@@ -364,7 +363,6 @@
 	enum gprs_rlcmac_tbf_ul_ass_state ul_ass_state;
 	enum gprs_rlcmac_tbf_ul_ack_state ul_ack_state;
 	enum gprs_rlcmac_tbf_poll_state poll_state;
-	LListHead<gprs_rlcmac_tbf> m_list;
 	bool m_egprs_enabled;
 	struct osmo_timer_list Tarr[T_MAX];
 	uint8_t Narr[N_MAX];
@@ -522,16 +520,6 @@
 	return false;
 }
 
-inline LListHead<gprs_rlcmac_tbf>& gprs_rlcmac_tbf::list()
-{
-	return this->m_list;
-}
-
-inline const LListHead<gprs_rlcmac_tbf>& gprs_rlcmac_tbf::list() const
-{
-	return this->m_list;
-}
-
 inline GprsMs *gprs_rlcmac_tbf::ms() const
 {
 	return m_ms;
diff --git a/src/tbf_dl.cpp b/src/tbf_dl.cpp
index ab34ea1..962c31d 100644
--- a/src/tbf_dl.cpp
+++ b/src/tbf_dl.cpp
@@ -139,7 +139,7 @@
 		return NULL;
 
 	talloc_set_destructor(tbf, dl_tbf_dtor);
-	new (tbf) gprs_rlcmac_dl_tbf(bts->bts, ms);
+	new (tbf) gprs_rlcmac_dl_tbf(bts, ms);
 
 	rc = tbf->setup(use_trx, single_slot);
 	/* if no resource */
@@ -169,8 +169,8 @@
 		}
 	}
 
-	llist_add(&tbf->list(), &bts->bts->dl_tbfs());
-	tbf->bts->do_rate_ctr_inc(CTR_TBF_DL_ALLOCATED);
+	llist_add(tbf_bts_list((struct gprs_rlcmac_tbf *)tbf), &bts->dl_tbfs);
+	bts_do_rate_ctr_inc(tbf->bts, CTR_TBF_DL_ALLOCATED);
 
 	tbf->m_last_dl_poll_fn = -1;
 	tbf->m_last_dl_drained_fn = -1;
@@ -181,7 +181,7 @@
 	return tbf;
 }
 
-gprs_rlcmac_dl_tbf::gprs_rlcmac_dl_tbf(BTS *bts_, GprsMs *ms) :
+gprs_rlcmac_dl_tbf::gprs_rlcmac_dl_tbf(struct gprs_rlcmac_bts *bts_, GprsMs *ms) :
 	gprs_rlcmac_tbf(bts_, ms, GPRS_RLCMAC_DL_TBF),
 	m_tx_counter(0),
 	m_wait_confirm(0),
@@ -289,10 +289,10 @@
 	GprsMs *ms, *ms_old;
 
 	/* check for existing TBF */
-	ms = bts->bts->ms_store().get_ms(tlli, tlli_old, imsi);
+	ms = bts_ms_store(bts)->get_ms(tlli, tlli_old, imsi);
 
 	if (ms && strlen(ms_imsi(ms)) == 0) {
-		ms_old = bts->bts->ms_store().get_ms(0, 0, imsi);
+		ms_old = bts_ms_store(bts)->get_ms(0, 0, imsi);
 		if (ms_old && ms_old != ms) {
 			/* The TLLI has changed (RAU), so there are two MS
 			 * objects for the same MS */
@@ -317,7 +317,7 @@
 	}
 
 	if (!ms)
-		ms = bts->bts->ms_alloc(ms_class, egprs_ms_class);
+		ms = bts_alloc_ms(bts, ms_class, egprs_ms_class);
 	ms_set_imsi(ms, imsi);
 	ms_confirm_tlli(ms, tlli);
 	if (!ms_ms_class(ms) && ms_class) {
@@ -391,12 +391,12 @@
 				break;
 		}
 
-		bts->do_rate_ctr_inc(CTR_LLC_FRAME_TIMEDOUT);
+		bts_do_rate_ctr_inc(bts, CTR_LLC_FRAME_TIMEDOUT);
 drop_frame:
 		frames++;
 		octets += msg->len;
 		msgb_free(msg);
-		bts->do_rate_ctr_inc(CTR_LLC_FRAME_DROPPED);
+		bts_do_rate_ctr_inc(bts, CTR_LLC_FRAME_DROPPED);
 		continue;
 	}
 
@@ -463,9 +463,9 @@
 		 * MCS1-4, because USF for GPRS-only MS will be sent */
 		force_cs = ms_current_cs_dl(m_ms);
 		if (force_cs > MCS4) {
-			force_cs = bts->cs_dl_is_supported(MCS4) ? MCS4 :
-				   bts->cs_dl_is_supported(MCS3) ? MCS3 :
-				   bts->cs_dl_is_supported(MCS2) ? MCS2 :
+			force_cs = bts_cs_dl_is_supported(bts, MCS4) ? MCS4 :
+				   bts_cs_dl_is_supported(bts, MCS3) ? MCS3 :
+				   bts_cs_dl_is_supported(bts, MCS2) ? MCS2 :
 				   MCS1;
 			LOGPTBFDL(this, LOGL_DEBUG,
 				  "Force downgrading DL %s -> %s due to USF for GPRS-only MS\n",
@@ -515,7 +515,7 @@
 		LOGPTBFDL(this, LOGL_DEBUG, "Resending BSN %d\n", bsn);
 		/* re-send block with negative aknowlegement */
 		m_window.m_v_b.mark_unacked(bsn);
-		bts->do_rate_ctr_inc(CTR_RLC_RESENT);
+		bts_do_rate_ctr_inc(bts, CTR_RLC_RESENT);
 	} else if (state_is(GPRS_RLCMAC_FINISHED)) {
 		/* If the TBF is in finished, we already sent all packages at least once.
 		 * If any packages could have been sent (because of unacked) it should have
@@ -523,7 +523,7 @@
 		LOGPTBFDL(this, LOGL_DEBUG,
 			  "Restarting at BSN %d, because all blocks have been transmitted.\n",
 			  m_window.v_a());
-		bts->do_rate_ctr_inc(CTR_RLC_RESTARTED);
+		bts_do_rate_ctr_inc(bts, CTR_RLC_RESTARTED);
 		if (restart_bsn_cycle())
 			return take_next_bsn(fn, previous_bsn, req_mcs_kind, may_combine);
 	} else if (dl_window_stalled()) {
@@ -532,7 +532,7 @@
 		LOGPTBFDL(this, LOGL_NOTICE,
 			  "Restarting at BSN %d, because the window is stalled.\n",
 			  m_window.v_a());
-		bts->do_rate_ctr_inc(CTR_RLC_STALLED);
+		bts_do_rate_ctr_inc(bts, CTR_RLC_STALLED);
 		if (restart_bsn_cycle())
 			return take_next_bsn(fn, previous_bsn, req_mcs_kind, may_combine);
 	} else if (have_data()) {
@@ -551,7 +551,7 @@
 		LOGPTBFDL(this, LOGL_DEBUG,
 			  "Restarting at BSN %d, because all blocks have been transmitted (FLOW).\n",
 			  m_window.v_a());
-		bts->do_rate_ctr_inc(CTR_RLC_RESTARTED);
+		bts_do_rate_ctr_inc(bts, CTR_RLC_RESTARTED);
 		if (restart_bsn_cycle())
 			return take_next_bsn(fn, previous_bsn, req_mcs_kind, may_combine);
 	} else {
@@ -568,8 +568,8 @@
 		LOGPTBFDL(this, LOGL_DEBUG,
 			  "Nothing else to send, Re-transmit final block!\n");
 		bsn = m_window.v_s_mod(-1);
-		bts->do_rate_ctr_inc(CTR_RLC_FINAL_BLOCK_RESENT);
-		bts->do_rate_ctr_inc(CTR_RLC_RESENT);
+		bts_do_rate_ctr_inc(bts, CTR_RLC_FINAL_BLOCK_RESENT);
+		bts_do_rate_ctr_inc(bts, CTR_RLC_RESENT);
 	}
 
 	*may_combine = num_data_blocks(mcs_header_type(m_rlc.block(bsn)->cs_current_trans)) > 1;
@@ -628,7 +628,7 @@
 		/* send immediate assignment */
 		if ((pgroup = imsi2paging_group(imsi())) > 999)
 			LOGPTBFDL(this, LOGL_ERROR, "IMSI to paging group failed! (%s)\n", imsi());
-		bts->snd_dl_ass(this, false, pgroup);
+		bts_snd_dl_ass(bts, this, false, pgroup);
 		m_wait_confirm = 1;
 	}
 }
@@ -648,7 +648,7 @@
 	LOGPTBFDL(this, LOGL_DEBUG, "Dequeue next LLC (len=%d)\n", msg->len);
 
 	m_llc.put_frame(msg->data, msg->len);
-	bts->do_rate_ctr_inc(CTR_LLC_FRAME_SCHED);
+	bts_do_rate_ctr_inc(bts, CTR_LLC_FRAME_SCHED);
 	msgb_free(msg);
 	m_last_dl_drained_fn = -1;
 }
@@ -742,14 +742,14 @@
 			&m_llc, &write_offset, &num_chunks, data, is_final, &payload_written);
 
 		if (payload_written > 0)
-			bts->do_rate_ctr_add(CTR_RLC_DL_PAYLOAD_BYTES, payload_written);
+			bts_do_rate_ctr_add(bts, CTR_RLC_DL_PAYLOAD_BYTES, payload_written);
 
 		if (ar == Encoding::AR_NEED_MORE_BLOCKS)
 			break;
 
 		LOGPTBFDL(this, LOGL_DEBUG, "Complete DL frame, len=%d\n", llc_frame_length(&m_llc));
 		gprs_rlcmac_dl_bw(this, llc_frame_length(&m_llc));
-		bts->do_rate_ctr_add(CTR_LLC_DL_BYTES, llc_frame_length(&m_llc));
+		bts_do_rate_ctr_add(bts, CTR_LLC_DL_BYTES, llc_frame_length(&m_llc));
 		m_llc.reset();
 
 		if (is_final) {
@@ -1472,15 +1472,15 @@
 		 */
 		if (block_status_dl == EGPRS_RESEG_FIRST_SEG_SENT) {
 			/* statistics */
-			bts->do_rate_ctr_inc(CTR_SPB_DL_SECOND_SEGMENT);
+			bts_do_rate_ctr_inc(bts, CTR_SPB_DL_SECOND_SEGMENT);
 			return EGPRS_RLCMAC_DL_SEC_SEG;
 		} else if ((ht_cs_init == HEADER_EGPRS_DATA_TYPE_1) ||
 			   (ht_cs_init == HEADER_EGPRS_DATA_TYPE_2)) {
-			bts->do_rate_ctr_inc(CTR_SPB_DL_FIRST_SEGMENT);
+			bts_do_rate_ctr_inc(bts, CTR_SPB_DL_FIRST_SEGMENT);
 			return EGPRS_RLCMAC_DL_FIRST_SEG;
 		} else if ((cs_init == MCS4) &&
 			   (cs_current_trans == MCS1)) {
-			bts->do_rate_ctr_inc(CTR_SPB_DL_FIRST_SEGMENT);
+			bts_do_rate_ctr_inc(bts, CTR_SPB_DL_FIRST_SEGMENT);
 			return EGPRS_RLCMAC_DL_FIRST_SEG;
 		}
 	}
@@ -1490,7 +1490,7 @@
 
 void gprs_rlcmac_dl_tbf::set_window_size()
 {
-	const struct gprs_rlcmac_bts *b = bts->bts_data();
+	const struct gprs_rlcmac_bts *b = bts;
 	uint16_t ws = egprs_window_size(b, dl_slots());
 
 	LOGPTBFDL(this, LOGL_INFO, "setting EGPRS DL window size to %u, base(%u) slots(%u) ws_pdch(%u)\n",
@@ -1502,55 +1502,55 @@
 {
 	switch (cs) {
 	case CS1:
-		bts->do_rate_ctr_inc(CTR_GPRS_DL_CS1);
+		bts_do_rate_ctr_inc(bts, CTR_GPRS_DL_CS1);
 		rate_ctr_inc(&m_dl_gprs_ctrs->ctr[TBF_CTR_GPRS_DL_CS1]);
 		break;
 	case CS2:
-		bts->do_rate_ctr_inc(CTR_GPRS_DL_CS2);
+		bts_do_rate_ctr_inc(bts, CTR_GPRS_DL_CS2);
 		rate_ctr_inc(&m_dl_gprs_ctrs->ctr[TBF_CTR_GPRS_DL_CS2]);
 		break;
 	case CS3:
-		bts->do_rate_ctr_inc(CTR_GPRS_DL_CS3);
+		bts_do_rate_ctr_inc(bts, CTR_GPRS_DL_CS3);
 		rate_ctr_inc(&m_dl_gprs_ctrs->ctr[TBF_CTR_GPRS_DL_CS3]);
 		break;
 	case CS4:
-		bts->do_rate_ctr_inc(CTR_GPRS_DL_CS4);
+		bts_do_rate_ctr_inc(bts, CTR_GPRS_DL_CS4);
 		rate_ctr_inc(&m_dl_gprs_ctrs->ctr[TBF_CTR_GPRS_DL_CS4]);
 		break;
 	case MCS1:
-		bts->do_rate_ctr_inc(CTR_EGPRS_DL_MCS1);
+		bts_do_rate_ctr_inc(bts, CTR_EGPRS_DL_MCS1);
 		rate_ctr_inc(&m_dl_egprs_ctrs->ctr[TBF_CTR_EGPRS_DL_MCS1]);
 		break;
 	case MCS2:
-		bts->do_rate_ctr_inc(CTR_EGPRS_DL_MCS2);
+		bts_do_rate_ctr_inc(bts, CTR_EGPRS_DL_MCS2);
 		rate_ctr_inc(&m_dl_egprs_ctrs->ctr[TBF_CTR_EGPRS_DL_MCS2]);
 		break;
 	case MCS3:
-		bts->do_rate_ctr_inc(CTR_EGPRS_DL_MCS3);
+		bts_do_rate_ctr_inc(bts, CTR_EGPRS_DL_MCS3);
 		rate_ctr_inc(&m_dl_egprs_ctrs->ctr[TBF_CTR_EGPRS_DL_MCS3]);
 		break;
 	case MCS4:
-		bts->do_rate_ctr_inc(CTR_EGPRS_DL_MCS4);
+		bts_do_rate_ctr_inc(bts, CTR_EGPRS_DL_MCS4);
 		rate_ctr_inc(&m_dl_egprs_ctrs->ctr[TBF_CTR_EGPRS_DL_MCS4]);
 		break;
 	case MCS5:
-		bts->do_rate_ctr_inc(CTR_EGPRS_DL_MCS5);
+		bts_do_rate_ctr_inc(bts, CTR_EGPRS_DL_MCS5);
 		rate_ctr_inc(&m_dl_egprs_ctrs->ctr[TBF_CTR_EGPRS_DL_MCS5]);
 		break;
 	case MCS6:
-		bts->do_rate_ctr_inc(CTR_EGPRS_DL_MCS6);
+		bts_do_rate_ctr_inc(bts, CTR_EGPRS_DL_MCS6);
 		rate_ctr_inc(&m_dl_egprs_ctrs->ctr[TBF_CTR_EGPRS_DL_MCS6]);
 		break;
 	case MCS7:
-		bts->do_rate_ctr_inc(CTR_EGPRS_DL_MCS7);
+		bts_do_rate_ctr_inc(bts, CTR_EGPRS_DL_MCS7);
 		rate_ctr_inc(&m_dl_egprs_ctrs->ctr[TBF_CTR_EGPRS_DL_MCS7]);
 		break;
 	case MCS8:
-		bts->do_rate_ctr_inc(CTR_EGPRS_DL_MCS8);
+		bts_do_rate_ctr_inc(bts, CTR_EGPRS_DL_MCS8);
 		rate_ctr_inc(&m_dl_egprs_ctrs->ctr[TBF_CTR_EGPRS_DL_MCS8]);
 		break;
 	case MCS9:
-		bts->do_rate_ctr_inc(CTR_EGPRS_DL_MCS9);
+		bts_do_rate_ctr_inc(bts, CTR_EGPRS_DL_MCS9);
 		rate_ctr_inc(&m_dl_egprs_ctrs->ctr[TBF_CTR_EGPRS_DL_MCS9]);
 		break;
 	default:
diff --git a/src/tbf_dl.h b/src/tbf_dl.h
index 3cd88c9..e29bb3f 100644
--- a/src/tbf_dl.h
+++ b/src/tbf_dl.h
@@ -39,7 +39,7 @@
 #define LOGPTBFDL(tbf, level, fmt, args...) LOGP(DTBFDL, level, "%s " fmt, tbf_name(tbf), ## args)
 
 struct gprs_rlcmac_dl_tbf : public gprs_rlcmac_tbf {
-	gprs_rlcmac_dl_tbf(BTS *bts, GprsMs *ms);
+	gprs_rlcmac_dl_tbf(struct gprs_rlcmac_bts *bts, GprsMs *ms);
 	gprs_rlc_window *window();
 	void cleanup();
 	/* dispatch Unitdata.DL messages */
diff --git a/src/tbf_ul.cpp b/src/tbf_ul.cpp
index 1f3cb01..0c59a50 100644
--- a/src/tbf_ul.cpp
+++ b/src/tbf_ul.cpp
@@ -111,7 +111,7 @@
 	if (!tbf)
 		return NULL;
 	talloc_set_destructor(tbf, ul_tbf_dtor);
-	new (tbf) gprs_rlcmac_ul_tbf(bts->bts, ms);
+	new (tbf) gprs_rlcmac_ul_tbf(bts, ms);
 
 	rc = tbf->setup(use_trx, single_slot);
 
@@ -134,8 +134,8 @@
 		return NULL;
 	}
 
-	llist_add(&tbf->list(), &bts->bts->ul_tbfs());
-	tbf->bts->do_rate_ctr_inc(CTR_TBF_UL_ALLOCATED);
+	llist_add_tail(tbf_bts_list(tbf), &bts->ul_tbfs);
+	bts_do_rate_ctr_inc(tbf->bts, CTR_TBF_UL_ALLOCATED);
 
 	return tbf;
 }
@@ -171,7 +171,7 @@
 	struct gprs_rlcmac_trx *trx = &bts->trx[trx_no];
 
 	if (!ms)
-		ms = bts->bts->ms_alloc(0, 0);
+		ms = bts_alloc_ms(bts, 0, 0);
 	ms_set_tlli(ms, tlli);
 
 	ul_tbf = talloc(tall_pcu_ctx, struct gprs_rlcmac_ul_tbf);
@@ -179,10 +179,10 @@
 		return ul_tbf;
 
 	talloc_set_destructor(ul_tbf, ul_tbf_dtor);
-	new (ul_tbf) gprs_rlcmac_ul_tbf(bts->bts, ms);
+	new (ul_tbf) gprs_rlcmac_ul_tbf(bts, ms);
 
-	llist_add(&ul_tbf->list(), &bts->bts->ul_tbfs());
-	ul_tbf->bts->do_rate_ctr_inc(CTR_TBF_UL_ALLOCATED);
+	llist_add(tbf_bts_list((struct gprs_rlcmac_tbf *)ul_tbf), &bts->ul_tbfs);
+	bts_do_rate_ctr_inc(ul_tbf->bts, CTR_TBF_UL_ALLOCATED);
 	TBF_SET_ASS_ON(ul_tbf, GPRS_RLCMAC_FLAG_PACCH, false);
 
 	ms_attach_tbf(ms, ul_tbf);
@@ -206,7 +206,7 @@
 	return ul_tbf;
 }
 
-gprs_rlcmac_ul_tbf::gprs_rlcmac_ul_tbf(BTS *bts_, GprsMs *ms) :
+gprs_rlcmac_ul_tbf::gprs_rlcmac_ul_tbf(struct gprs_rlcmac_bts *bts_, GprsMs *ms) :
 	gprs_rlcmac_tbf(bts_, ms, GPRS_RLCMAC_UL_TBF),
 	m_rx_counter(0),
 	m_contention_resolution_done(0),
@@ -243,7 +243,7 @@
 		frame = frames + i;
 
 		if (frame->length) {
-			bts->do_rate_ctr_add(CTR_RLC_UL_PAYLOAD_BYTES, frame->length);
+			bts_do_rate_ctr_add(bts, CTR_RLC_UL_PAYLOAD_BYTES, frame->length);
 
 			LOGPTBFUL(this, LOGL_DEBUG, "Frame %d "
 				"starts at offset %d, "
@@ -259,7 +259,7 @@
 			/* send frame to SGSN */
 			LOGPTBFUL(this, LOGL_DEBUG, "complete UL frame len=%d\n", llc_frame_length(&m_llc));
 			snd_ul_ud();
-			bts->do_rate_ctr_add(CTR_LLC_UL_BYTES, llc_frame_length(&m_llc));
+			bts_do_rate_ctr_add(bts, CTR_LLC_UL_BYTES, llc_frame_length(&m_llc));
 			m_llc.reset();
 		}
 	}
@@ -440,7 +440,7 @@
 				rdbi, rlc->cs, rlc_data, NULL, 0, &new_tlli);
 
 			if (num_chunks < 0) {
-				bts->do_rate_ctr_inc(CTR_DECODE_ERRORS);
+				bts_do_rate_ctr_inc(bts, CTR_DECODE_ERRORS);
 				LOGPTBFUL(this, LOGL_NOTICE,
 					  "Failed to decode TLLI of %s UL DATA TFI=%d.\n",
 					  mcs_name(rlc->cs), rlc->tfi);
@@ -589,7 +589,7 @@
 	union split_block_status *spb_status = &block->spb_status;
 	uint8_t *rlc_data = &block->block[0];
 
-        bts->do_rate_ctr_inc(CTR_SPB_UL_SECOND_SEGMENT);
+        bts_do_rate_ctr_inc(bts, CTR_SPB_UL_SECOND_SEGMENT);
 
 	if (spb_status->block_status_ul &
 				EGPRS_RESEG_FIRST_SEG_RXD) {
@@ -622,7 +622,7 @@
 	uint8_t *rlc_data = &block->block[0];
 	union split_block_status *spb_status = &block->spb_status;
 
-	bts->do_rate_ctr_inc(CTR_SPB_UL_FIRST_SEGMENT);
+	bts_do_rate_ctr_inc(bts, CTR_SPB_UL_FIRST_SEGMENT);
 
 	if (spb_status->block_status_ul & EGPRS_RESEG_SECOND_SEG_RXD) {
 		LOGPTBFUL(this, LOGL_DEBUG,
@@ -702,55 +702,55 @@
 {
 	switch (cs) {
 	case CS1:
-		bts->do_rate_ctr_inc(CTR_GPRS_UL_CS1);
+		bts_do_rate_ctr_inc(bts, CTR_GPRS_UL_CS1);
 		rate_ctr_inc(&m_ul_gprs_ctrs->ctr[TBF_CTR_GPRS_UL_CS1]);
 		break;
 	case CS2:
-		bts->do_rate_ctr_inc(CTR_GPRS_UL_CS2);
+		bts_do_rate_ctr_inc(bts, CTR_GPRS_UL_CS2);
 		rate_ctr_inc(&m_ul_gprs_ctrs->ctr[TBF_CTR_GPRS_UL_CS2]);
 		break;
 	case CS3:
-		bts->do_rate_ctr_inc(CTR_GPRS_UL_CS3);
+		bts_do_rate_ctr_inc(bts, CTR_GPRS_UL_CS3);
 		rate_ctr_inc(&m_ul_gprs_ctrs->ctr[TBF_CTR_GPRS_UL_CS3]);
 		break;
 	case CS4:
-		bts->do_rate_ctr_inc(CTR_GPRS_UL_CS4);
+		bts_do_rate_ctr_inc(bts, CTR_GPRS_UL_CS4);
 		rate_ctr_inc(&m_ul_gprs_ctrs->ctr[TBF_CTR_GPRS_UL_CS4]);
 		break;
 	case MCS1:
-		bts->do_rate_ctr_inc(CTR_EGPRS_UL_MCS1);
+		bts_do_rate_ctr_inc(bts, CTR_EGPRS_UL_MCS1);
 		rate_ctr_inc(&m_ul_egprs_ctrs->ctr[TBF_CTR_EGPRS_UL_MCS1]);
 		break;
 	case MCS2:
-		bts->do_rate_ctr_inc(CTR_EGPRS_UL_MCS2);
+		bts_do_rate_ctr_inc(bts, CTR_EGPRS_UL_MCS2);
 		rate_ctr_inc(&m_ul_egprs_ctrs->ctr[TBF_CTR_EGPRS_UL_MCS2]);
 		break;
 	case MCS3:
-		bts->do_rate_ctr_inc(CTR_EGPRS_UL_MCS3);
+		bts_do_rate_ctr_inc(bts, CTR_EGPRS_UL_MCS3);
 		rate_ctr_inc(&m_ul_egprs_ctrs->ctr[TBF_CTR_EGPRS_UL_MCS3]);
 		break;
 	case MCS4:
-		bts->do_rate_ctr_inc(CTR_EGPRS_UL_MCS4);
+		bts_do_rate_ctr_inc(bts, CTR_EGPRS_UL_MCS4);
 		rate_ctr_inc(&m_ul_egprs_ctrs->ctr[TBF_CTR_EGPRS_UL_MCS4]);
 		break;
 	case MCS5:
-		bts->do_rate_ctr_inc(CTR_EGPRS_UL_MCS5);
+		bts_do_rate_ctr_inc(bts, CTR_EGPRS_UL_MCS5);
 		rate_ctr_inc(&m_ul_egprs_ctrs->ctr[TBF_CTR_EGPRS_UL_MCS5]);
 		break;
 	case MCS6:
-		bts->do_rate_ctr_inc(CTR_EGPRS_UL_MCS6);
+		bts_do_rate_ctr_inc(bts, CTR_EGPRS_UL_MCS6);
 		rate_ctr_inc(&m_ul_egprs_ctrs->ctr[TBF_CTR_EGPRS_UL_MCS6]);
 		break;
 	case MCS7:
-		bts->do_rate_ctr_inc(CTR_EGPRS_UL_MCS7);
+		bts_do_rate_ctr_inc(bts, CTR_EGPRS_UL_MCS7);
 		rate_ctr_inc(&m_ul_egprs_ctrs->ctr[TBF_CTR_EGPRS_UL_MCS7]);
 		break;
 	case MCS8:
-		bts->do_rate_ctr_inc(CTR_EGPRS_UL_MCS8);
+		bts_do_rate_ctr_inc(bts, CTR_EGPRS_UL_MCS8);
 		rate_ctr_inc(&m_ul_egprs_ctrs->ctr[TBF_CTR_EGPRS_UL_MCS8]);
 		break;
 	case MCS9:
-		bts->do_rate_ctr_inc(CTR_EGPRS_UL_MCS9);
+		bts_do_rate_ctr_inc(bts, CTR_EGPRS_UL_MCS9);
 		rate_ctr_inc(&m_ul_egprs_ctrs->ctr[TBF_CTR_EGPRS_UL_MCS9]);
 		break;
 	default:
@@ -761,7 +761,7 @@
 
 void gprs_rlcmac_ul_tbf::set_window_size()
 {
-	const struct gprs_rlcmac_bts *b = bts->bts_data();
+	const struct gprs_rlcmac_bts *b = bts;
 	uint16_t ws = egprs_window_size(b, ul_slots());
 	LOGPTBFUL(this, LOGL_INFO, "setting EGPRS UL window size to %u, base(%u) slots(%u) ws_pdch(%u)\n",
 		  ws, bts->pcu->vty.ws_base, pcu_bitcount(ul_slots()), bts->pcu->vty.ws_pdch);
diff --git a/src/tbf_ul.h b/src/tbf_ul.h
index 1d9cf50..8713596 100644
--- a/src/tbf_ul.h
+++ b/src/tbf_ul.h
@@ -50,7 +50,7 @@
 #define LOGPTBFUL(tbf, level, fmt, args...) LOGP(DTBFUL, level, "%s " fmt, tbf_name(tbf), ## args)
 
 struct gprs_rlcmac_ul_tbf : public gprs_rlcmac_tbf {
-	gprs_rlcmac_ul_tbf(BTS *bts, GprsMs *ms);
+	gprs_rlcmac_ul_tbf(struct gprs_rlcmac_bts *bts, GprsMs *ms);
 	gprs_rlc_window *window();
 	struct msgb *create_ul_ack(uint32_t fn, uint8_t ts);
 	bool ctrl_ack_to_toggle();
diff --git a/tests/alloc/AllocTest.cpp b/tests/alloc/AllocTest.cpp
index 249c268..20f5286 100644
--- a/tests/alloc/AllocTest.cpp
+++ b/tests/alloc/AllocTest.cpp
@@ -21,7 +21,9 @@
 #include "gprs_debug.h"
 #include "tbf.h"
 #include "tbf_ul.h"
+#include "tbf_dl.h"
 #include "bts.h"
+#include "gprs_ms.h"
 
 #include <string.h>
 #include <stdio.h>
@@ -51,17 +53,17 @@
 		return tbf_alloc_dl_tbf(bts, ms, use_trx, single_slot);
 }
 
-static void check_tfi_usage(BTS *the_bts)
+static void check_tfi_usage(struct gprs_rlcmac_bts *bts)
 {
 	int pdch_no;
 
 	struct gprs_rlcmac_tbf *tfi_usage[8][8][2][32] = {{{{NULL}}}};
-	LListHead<gprs_rlcmac_tbf> *tbf_lists[2] = {
-		&the_bts->ul_tbfs(),
-		&the_bts->dl_tbfs()
+	struct llist_head *tbf_lists[2] = {
+		&bts->ul_tbfs,
+		&bts->dl_tbfs
 	};
 
-	LListHead<gprs_rlcmac_tbf> *pos;
+	struct llist_item *pos;
 	gprs_rlcmac_tbf *tbf;
 	unsigned list_idx;
 	struct gprs_rlcmac_tbf **tbf_var;
@@ -69,8 +71,8 @@
 	for (list_idx = 0; list_idx < ARRAY_SIZE(tbf_lists); list_idx += 1)
 	{
 
-		llist_for_each(pos, tbf_lists[list_idx]) {
-			tbf = pos->entry();
+		llist_for_each_entry(pos, tbf_lists[list_idx], list) {
+			tbf = (struct gprs_rlcmac_tbf *)pos->entry;
 			for (pdch_no = 0; pdch_no < 8; pdch_no += 1) {
 				struct gprs_rlcmac_pdch *pdch = tbf->pdch[pdch_no];
 				if (pdch == NULL)
@@ -86,14 +88,14 @@
 				if (tbf->direction == GPRS_RLCMAC_DL_TBF) {
 					OSMO_ASSERT(pdch->dl_tbf_by_tfi(
 							tbf->tfi()) == tbf);
-					OSMO_ASSERT(the_bts->dl_tbf_by_tfi(
+					OSMO_ASSERT(bts_dl_tbf_by_tfi(bts,
 							tbf->tfi(),
 							tbf->trx->trx_no,
 							pdch_no) == tbf);
 				} else {
 					OSMO_ASSERT(pdch->ul_tbf_by_tfi(
 							tbf->tfi()) == tbf);
-					OSMO_ASSERT(the_bts->ul_tbf_by_tfi(
+					OSMO_ASSERT(bts_ul_tbf_by_tfi(bts,
 							tbf->tfi(),
 							tbf->trx->trx_no,
 							pdch_no) == tbf);
@@ -112,14 +114,12 @@
 	int tfi;
 	int i;
 	uint8_t used_trx, tmp_trx;
-	BTS the_bts(the_pcu);
+	struct gprs_rlcmac_bts *bts = bts_alloc(the_pcu);
 	GprsMs *ms;
-	struct gprs_rlcmac_bts *bts;
 	struct gprs_rlcmac_tbf *tbfs[32*8+1] = { 0, };
 
 	printf("Testing alloc_a direction(%d)\n", dir);
 
-	bts = the_bts.bts_data();
 	the_pcu->alloc_algorithm = alloc_algorithm_a;
 
 	struct gprs_rlcmac_trx *trx = &bts->trx[0];
@@ -136,17 +136,17 @@
 	 * least this part is working okay.
 	 */
 	for (i = 0; i < (int)ARRAY_SIZE(tbfs); ++i) {
-		ms = bts->bts->ms_alloc(0, 0);
+		ms = bts_alloc_ms(bts, 0, 0);
 		tbfs[i] = tbf_alloc(bts, ms, dir, -1, 0);
 		if (tbfs[i] == NULL)
 			break;
 
 		used_trx = tbfs[i]->trx->trx_no;
-		tfi = the_bts.tfi_find_free(dir, &tmp_trx, used_trx);
+		tfi = bts_tfi_find_free(bts, dir, &tmp_trx, used_trx);
 		OSMO_ASSERT(tbfs[i]->tfi() != tfi);
 	}
 
-	check_tfi_usage(&the_bts);
+	check_tfi_usage(bts);
 
 	OSMO_ASSERT(i == count);
 
@@ -154,10 +154,11 @@
 		if (tbfs[i])
 			tbf_free(tbfs[i]);
 
-	ms = bts->bts->ms_alloc(0, 0);
+	ms = bts_alloc_ms(bts, 0, 0);
 	tbfs[0] = tbf_alloc(bts, ms, dir, -1, 0);
 	OSMO_ASSERT(tbfs[0]);
 	tbf_free(tbfs[0]);
+	talloc_free(bts);
 }
 
 static void test_alloc_a()
@@ -205,8 +206,7 @@
 static inline bool test_alloc_b_ul_dl(bool ts0, bool ts1, bool ts2, bool ts3, bool ts4, bool ts5, bool ts6, bool ts7,
 				      uint8_t ms_class, bool verbose)
 {
-	BTS the_bts(the_pcu);
-	struct gprs_rlcmac_bts *bts = the_bts.bts_data();
+	struct gprs_rlcmac_bts *bts = bts_alloc(the_pcu);
 	GprsMs *ms;
 	gprs_rlcmac_ul_tbf *ul_tbf;
 	gprs_rlcmac_dl_tbf *dl_tbf;
@@ -218,7 +218,7 @@
 
 	enable_ts_on_bts(bts, ts0, ts1, ts2, ts3, ts4, ts5, ts6, ts7);
 
-	ms = the_bts.ms_alloc(ms_class, 0);
+	ms = bts_alloc_ms(bts, ms_class, 0);
 	/* Avoid delaying free to avoid tons of to-be-freed ms objects queuing */
 	ms_set_timeout(ms, 0);
 	ul_tbf = tbf_alloc_ul_tbf(bts, ms, -1, true);
@@ -239,19 +239,18 @@
 
 	OSMO_ASSERT(dl_tbf->first_common_ts == ul_tbf->first_common_ts);
 
-	check_tfi_usage(&the_bts);
+	check_tfi_usage(bts);
 
 	tbf_free(dl_tbf);
 	tbf_free(ul_tbf);
-
+	talloc_free(bts);
 	return true;
 }
 
 static inline bool test_alloc_b_dl_ul(bool ts0, bool ts1, bool ts2, bool ts3, bool ts4, bool ts5, bool ts6, bool ts7,
 				      uint8_t ms_class, bool verbose)
 {
-	BTS the_bts(the_pcu);
-	struct gprs_rlcmac_bts *bts = the_bts.bts_data();
+	struct gprs_rlcmac_bts *bts = bts_alloc(the_pcu);
 	GprsMs *ms;
 	gprs_rlcmac_ul_tbf *ul_tbf;
 	gprs_rlcmac_dl_tbf *dl_tbf;
@@ -263,7 +262,7 @@
 
 	enable_ts_on_bts(bts, ts0, ts1, ts2, ts3, ts4, ts5, ts6, ts7);
 
-	ms = the_bts.ms_alloc(ms_class, 0);
+	ms = bts_alloc_ms(bts, ms_class, 0);
 	/* Avoid delaying free to avoid tons of to-be-freed ms objects queuing */
 	ms_set_timeout(ms, 0);
 	dl_tbf = tbf_alloc_dl_tbf(bts, ms, -1, true);
@@ -292,18 +291,17 @@
 	dump_assignment(dl_tbf, "DL", verbose);
 	OSMO_ASSERT(dl_tbf->first_common_ts == ul_tbf->first_common_ts);
 
-	check_tfi_usage(&the_bts);
+	check_tfi_usage(bts);
 
 	tbf_free(dl_tbf);
 	tbf_free(ul_tbf);
-
+	talloc_free(bts);
 	return true;
 }
 
 static inline bool test_alloc_b_jolly(uint8_t ms_class)
 {
-	BTS the_bts(the_pcu);
-	struct gprs_rlcmac_bts *bts = the_bts.bts_data();
+	struct gprs_rlcmac_bts *bts = bts_alloc(the_pcu);
 	GprsMs *ms;
 	int tfi;
 	uint8_t trx_no;
@@ -315,9 +313,9 @@
 
 	enable_ts_on_bts(bts, false, true, true, true, true, false, false, false);
 
-	tfi = the_bts.tfi_find_free(GPRS_RLCMAC_UL_TBF, &trx_no, -1);
+	tfi = bts_tfi_find_free(bts, GPRS_RLCMAC_UL_TBF, &trx_no, -1);
 	OSMO_ASSERT(tfi >= 0);
-	ms = the_bts.ms_alloc(ms_class, 0);
+	ms = bts_alloc_ms(bts, ms_class, 0);
 	/* Avoid delaying free to avoid tons of to-be-freed ms objects queuing */
 	ms_set_timeout(ms, 0);
 	ul_tbf = tbf_alloc_ul_tbf(bts, ms, -1, false);
@@ -338,11 +336,11 @@
 
 	OSMO_ASSERT(dl_tbf->first_common_ts == ul_tbf->first_common_ts);
 
-	check_tfi_usage(&the_bts);
+	check_tfi_usage(bts);
 
 	tbf_free(dl_tbf);
 	tbf_free(ul_tbf);
-
+	talloc_free(bts);
 	return true;
 }
 
@@ -459,16 +457,13 @@
 	}
 }
 
-static GprsMs *alloc_tbfs(BTS *the_bts, struct GprsMs *old_ms, enum test_mode mode)
+static GprsMs *alloc_tbfs(struct gprs_rlcmac_bts *bts, struct GprsMs *old_ms, enum test_mode mode)
 {
-	struct gprs_rlcmac_bts *bts;
 	struct GprsMs *ms, *new_ms;
 	uint8_t trx_no = -1;
 
 	OSMO_ASSERT(old_ms != NULL);
 
-	bts = the_bts->bts_data();
-
 	gprs_rlcmac_tbf *tbf = NULL;
 
 	if (ms_current_trx(old_ms))
@@ -517,12 +512,12 @@
 
 	case TEST_MODE_DL_AFTER_UL:
 	case TEST_MODE_UL_AND_DL:
-		new_ms = alloc_tbfs(the_bts, ms, TEST_MODE_DL_ONLY);
+		new_ms = alloc_tbfs(bts, ms, TEST_MODE_DL_ONLY);
 		break;
 
 	case TEST_MODE_UL_AFTER_DL:
 	case TEST_MODE_DL_AND_UL:
-		new_ms = alloc_tbfs(the_bts, ms, TEST_MODE_UL_ONLY);
+		new_ms = alloc_tbfs(bts, ms, TEST_MODE_UL_ONLY);
 		break;
 	}
 
@@ -546,7 +541,7 @@
 	return new_ms;
 }
 
-static unsigned alloc_many_tbfs(BTS *the_bts, unsigned min_class,
+static unsigned alloc_many_tbfs(struct gprs_rlcmac_bts *bts, unsigned min_class,
 	unsigned max_class, enum test_mode mode)
 {
 	unsigned counter;
@@ -566,11 +561,11 @@
 		enum gprs_rlcmac_tbf_direction dir;
 		uint32_t tlli = counter + 0xc0000000;
 
-		ms = the_bts->ms_by_tlli(tlli);
+		ms = bts_ms_by_tlli(bts, tlli, GSM_RESERVED_TMSI);
 		if (!ms)
-			ms = the_bts->ms_alloc(0, 0);
+			ms = bts_alloc_ms(bts, 0, 0);
 		ms_set_ms_class(ms, ms_class);
-		ms = alloc_tbfs(the_bts, ms, mode);
+		ms = alloc_tbfs(bts, ms, mode);
 		if (!ms)
 			break;
 
@@ -630,7 +625,7 @@
 
 		if (tfi >= 0) {
 			OSMO_ASSERT(ms_current_trx(ms));
-			tfi2 = the_bts->tfi_find_free(dir, &trx_no2,
+			tfi2 = bts_tfi_find_free(bts, dir, &trx_no2,
 				ms_current_trx(ms)->trx_no);
 			OSMO_ASSERT(tfi != tfi2);
 			OSMO_ASSERT(tfi2 < 0 ||
@@ -649,15 +644,13 @@
 	unsigned max_class, enum test_mode mode,
 	unsigned expect_num, const char *text)
 {
-	BTS the_bts(the_pcu);
-	struct gprs_rlcmac_bts *bts;
+	struct gprs_rlcmac_bts *bts = bts_alloc(the_pcu);
 	struct gprs_rlcmac_trx *trx;
 	unsigned counter;
 
 	printf("Going to test assignment with many TBF, algorithm %s class %u..%u (%s)\n",
 	       text, min_class, max_class, test_mode_descr(mode));
 
-	bts = the_bts.bts_data();
 	the_pcu->alloc_algorithm = algo;
 
 	trx = &bts->trx[0];
@@ -667,7 +660,7 @@
 	trx->pdch[6].enable();
 	trx->pdch[7].enable();
 
-	counter = alloc_many_tbfs(&the_bts, min_class, max_class, mode);
+	counter = alloc_many_tbfs(bts, min_class, max_class, mode);
 
 	printf("  Successfully allocated %u UL TBFs, algorithm %s class %u..%u (%s)\n",
 	       counter, text, min_class, max_class, test_mode_descr(mode));
@@ -677,14 +670,14 @@
 
 	OSMO_ASSERT(counter == expect_num);
 
-	check_tfi_usage(&the_bts);
+	check_tfi_usage(bts);
+	talloc_free(bts);
 }
 
 static void test_many_connections(algo_t algo, unsigned expect_num,
 	const char *text)
 {
-	BTS the_bts(the_pcu);
-	struct gprs_rlcmac_bts *bts;
+	struct gprs_rlcmac_bts *bts = bts_alloc(the_pcu);
 	struct gprs_rlcmac_trx *trx;
 	int counter1, counter2 = -1;
 	unsigned i;
@@ -697,7 +690,6 @@
 
 	printf("Going to test assignment with many connections, algorithm %s\n", text);
 
-	bts = the_bts.bts_data();
 	the_pcu->alloc_algorithm = algo;
 
 	trx = &bts->trx[0];
@@ -708,11 +700,11 @@
 	trx->pdch[7].enable();
 
 	for (i = 0; i < ARRAY_SIZE(mode_seq); i += 1) {
-		counter1 = alloc_many_tbfs(&the_bts, 1, mslot_class_max(), mode_seq[i]);
+		counter1 = alloc_many_tbfs(bts, 1, mslot_class_max(), mode_seq[i]);
 		fprintf(stderr, "  Allocated %d TBFs (previously %d)\n",
 			counter1, counter2);
 
-		check_tfi_usage(&the_bts);
+		check_tfi_usage(bts);
 
 		/* This will stop earlier due to USF shortage */
 		if (mode_seq[i] == TEST_MODE_UL_ONLY)
@@ -733,6 +725,7 @@
 		fprintf(stderr, "  Expected %d TBFs (got %d) for algorithm %s\n", expect_num, counter1, text);
 
 	OSMO_ASSERT(expect_num == (unsigned)counter1);
+	talloc_free(bts);
 }
 
 static inline void test_a_b_dyn(enum test_mode mode, uint8_t exp_A, uint8_t exp_B, uint8_t exp_dyn)
@@ -761,9 +754,8 @@
 
 static void test_2_consecutive_dl_tbfs()
 {
-	BTS the_bts(the_pcu);
+	struct gprs_rlcmac_bts *bts = bts_alloc(the_pcu);
 	GprsMs *ms;
-	struct gprs_rlcmac_bts *bts;
 	struct gprs_rlcmac_trx *trx;
 	uint8_t ms_class = 11;
 	uint8_t egprs_ms_class = 11;
@@ -772,7 +764,6 @@
 
 	printf("Testing DL TS allocation for Multi UEs\n");
 
-	bts = the_bts.bts_data();
 	the_pcu->alloc_algorithm = alloc_algorithm_b;
 
 	trx = &bts->trx[0];
@@ -781,7 +772,7 @@
 	trx->pdch[6].enable();
 	trx->pdch[7].enable();
 
-	ms = the_bts.ms_alloc(ms_class, egprs_ms_class);
+	ms = bts_alloc_ms(bts, ms_class, egprs_ms_class);
 	dl_tbf1 = tbf_alloc_dl_tbf(bts, ms, 0, false);
 	OSMO_ASSERT(dl_tbf1);
 
@@ -792,7 +783,7 @@
 	OSMO_ASSERT(numTs1 == 4);
 	printf("TBF1: numTs(%d)\n", numTs1);
 
-	ms = the_bts.ms_alloc(ms_class, egprs_ms_class);
+	ms = bts_alloc_ms(bts, ms_class, egprs_ms_class);
 	dl_tbf2 = tbf_alloc_dl_tbf(bts, ms, 0, false);
 	OSMO_ASSERT(dl_tbf2);
 
@@ -810,6 +801,7 @@
 
 	tbf_free(dl_tbf1);
 	tbf_free(dl_tbf2);
+	talloc_free(bts);
 }
 
 int main(int argc, char **argv)
diff --git a/tests/alloc/MslotTest.cpp b/tests/alloc/MslotTest.cpp
index e354641..b32a828 100644
--- a/tests/alloc/MslotTest.cpp
+++ b/tests/alloc/MslotTest.cpp
@@ -62,15 +62,12 @@
 
 static inline void test_multislot_total_ascending(bool seq)
 {
-	BTS the_bts(the_pcu);
-	struct gprs_rlcmac_bts *bts;
+	struct gprs_rlcmac_bts *bts = bts_alloc(the_pcu);
 	struct gprs_rlcmac_trx *trx;
 	int i;
 
 	printf("%s(): %s\n", __func__, seq ? "sequential" : "accumulative");
 
-	bts = the_bts.bts_data();
-
 	trx = &bts->trx[0];
 
 	for (i = 0; i < 8; i++) {
@@ -79,19 +76,17 @@
 
 		test_all_classes(trx, seq);
 	}
+	talloc_free(bts);
 }
 
 static inline void test_multislot_total_descending(bool seq)
 {
-	BTS the_bts(the_pcu);
-	struct gprs_rlcmac_bts *bts;
+	struct gprs_rlcmac_bts *bts = bts_alloc(the_pcu);
 	struct gprs_rlcmac_trx *trx;
 	int i;
 
 	printf("%s(): %s\n", __func__, seq ? "sequential" : "accumulative");
 
-	bts = the_bts.bts_data();
-
 	trx = &bts->trx[0];
 
 	for (i = 7; i >= 0; i--) {
@@ -100,18 +95,16 @@
 
 		test_all_classes(trx, seq);
 	}
+	talloc_free(bts);
 }
 
 static inline void test_multislot_middle(bool seq)
 {
-	BTS the_bts(the_pcu);
-	struct gprs_rlcmac_bts *bts;
+	struct gprs_rlcmac_bts *bts = bts_alloc(the_pcu);
 	struct gprs_rlcmac_trx *trx;
 
 	printf("%s(): %s\n", __func__, seq ? "sequential" : "accumulative");
 
-	bts = the_bts.bts_data();
-
 	trx = &bts->trx[0];
 
 	trx->pdch[2].enable();
@@ -119,24 +112,23 @@
 	trx->pdch[4].enable();
 
 	test_all_classes(trx, seq);
+	talloc_free(bts);
 }
 
 static inline void test_multislot_ends(bool seq)
 {
-	BTS the_bts(the_pcu);
-	struct gprs_rlcmac_bts *bts;
+	struct gprs_rlcmac_bts *bts = bts_alloc(the_pcu);
 	struct gprs_rlcmac_trx *trx;
 
 	printf("%s(): %s\n", __func__, seq ? "sequential" : "accumulative");
 
-	bts = the_bts.bts_data();
-
 	trx = &bts->trx[0];
 
 	trx->pdch[0].enable();
 	trx->pdch[7].enable();
 
 	test_all_classes(trx, seq);
+	talloc_free(bts);
 }
 
 static inline void test_window_wrapper()
diff --git a/tests/app_info/AppInfoTest.cpp b/tests/app_info/AppInfoTest.cpp
index 962ead4..e0b2853 100644
--- a/tests/app_info/AppInfoTest.cpp
+++ b/tests/app_info/AppInfoTest.cpp
@@ -20,6 +20,7 @@
 #include <assert.h>
 #include "gprs_rlcmac.h"
 #include "bts.h"
+#include "tbf_dl.h"
 
 extern "C" {
 #include <osmocom/vty/telnet_interface.h>
@@ -77,25 +78,23 @@
 
 void prepare_bts_with_two_dl_tbf_subscr()
 {
-	BTS *bts = BTS::main_bts();
-	struct gprs_rlcmac_bts *bts_data;
+	struct gprs_rlcmac_bts *bts = the_pcu->bts;
 	struct gprs_rlcmac_trx *trx;
 
 	fprintf(stderr, "--- %s ---\n",  __func__);
 
-	bts_data = bts->bts_data();
 	the_pcu->alloc_algorithm = alloc_algorithm_b;
 
-	trx = bts_data->trx;
+	trx = bts->trx;
 	trx->pdch[4].enable();
 	trx->pdch[5].enable();
 	trx->pdch[6].enable();
 	trx->pdch[7].enable();
 
-	ms1 = bts->ms_alloc(10, 11);
-	tbf1 = tbf_alloc_dl_tbf(bts_data, ms1, 0, false);
-	ms2 = bts->ms_alloc(12, 13);
-	tbf2 = tbf_alloc_dl_tbf(bts_data, ms2, 0, false);
+	ms1 = bts_alloc_ms(bts, 10, 11);
+	tbf1 = tbf_alloc_dl_tbf(bts, ms1, 0, false);
+	ms2 = bts_alloc_ms(bts, 12, 13);
+	tbf2 = tbf_alloc_dl_tbf(bts, ms2, 0, false);
 
 	fprintf(stderr, "\n");
 }
@@ -122,15 +121,15 @@
 
 void test_sched_app_info_missing_app_info_in_bts(const struct gsm_pcu_if_app_info_req *req)
 {
-	struct gprs_rlcmac_bts *bts_data = BTS::main_bts()->bts_data();
+	struct gprs_rlcmac_bts *bts = the_pcu->bts;
 	struct gsm_pcu_if pcu_prim = {PCU_IF_MSG_APP_INFO_REQ, };
 
 	fprintf(stderr, "--- %s ---\n",  __func__);
 	pcu_prim.u.app_info_req = *req;
 	pcu_rx(PCU_IF_MSG_APP_INFO_REQ, &pcu_prim);
 
-	msgb_free(bts_data->app_info);
-	bts_data->app_info = NULL;
+	msgb_free(bts->app_info);
+	bts->app_info = NULL;
 
 	assert(sched_app_info(tbf1) == NULL);
 
@@ -154,8 +153,8 @@
 
 	tbf_free(tbf1);
 	tbf_free(tbf2);
-	BTS::main_bts()->cleanup();
-	/* FIXME: talloc report disabled, because bts->ms_alloc() in prepare_bts_with_two_dl_tbf_subscr() causes leak */
+	TALLOC_FREE(the_pcu->bts);
+	/* FIXME: talloc report disabled, because bts_alloc_ms(bts, ) in prepare_bts_with_two_dl_tbf_subscr() causes leak */
 	/* talloc_report_full(tall_pcu_ctx, stderr); */
 	talloc_free(the_pcu);
 	talloc_free(tall_pcu_ctx);
diff --git a/tests/edge/EdgeTest.cpp b/tests/edge/EdgeTest.cpp
index c5ce730..d67646e 100644
--- a/tests/edge/EdgeTest.cpp
+++ b/tests/edge/EdgeTest.cpp
@@ -1151,19 +1151,17 @@
 	printf("=== end %s ===\n", __func__);
 }
 
-static void setup_bts(BTS *the_bts, uint8_t ts_no, uint8_t cs = 1)
+static void setup_bts(struct gprs_rlcmac_bts *bts, uint8_t ts_no, uint8_t cs = 1)
 {
-	gprs_rlcmac_bts *bts;
 	gprs_rlcmac_trx *trx;
 
-	bts = the_bts->bts_data();
 	the_pcu->alloc_algorithm = alloc_algorithm_a;
 	bts->initial_cs_dl = cs;
 	bts->initial_cs_ul = cs;
 	trx = &bts->trx[0];
 	trx->pdch[ts_no].enable();
 }
-static void uplink_header_type_2_parsing_test(BTS *the_bts,
+static void uplink_header_type_2_parsing_test(struct gprs_rlcmac_bts *bts,
 	uint8_t ts_no, uint32_t tlli, uint32_t *fn, uint16_t qta,
 	uint8_t ms_class)
 {
@@ -1254,7 +1252,7 @@
 
 static void uplink_header_type2_test(void)
 {
-	BTS the_bts(the_pcu);
+	struct gprs_rlcmac_bts *bts = bts_alloc(the_pcu);
 	int ts_no = 7;
 	uint32_t fn = 2654218;
 	uint16_t qta = 31;
@@ -1262,14 +1260,15 @@
 	uint8_t ms_class = 1;
 
 	printf("=== start %s ===\n", __func__);
-	setup_bts(&the_bts, ts_no, 10);
+	setup_bts(bts, ts_no, 10);
 
-	uplink_header_type_2_parsing_test(&the_bts, ts_no,
+	uplink_header_type_2_parsing_test(bts, ts_no,
 			tlli, &fn, qta, ms_class);
 	printf("=== end %s ===\n", __func__);
+	talloc_free(bts);
 }
 
-static void uplink_header_type_1_parsing_test(BTS *the_bts,
+static void uplink_header_type_1_parsing_test(struct gprs_rlcmac_bts *bts,
 	uint8_t ts_no, uint32_t tlli, uint32_t *fn, uint16_t qta,
 	uint8_t ms_class)
 {
@@ -1371,7 +1370,7 @@
 
 void uplink_header_type1_test(void)
 {
-	BTS the_bts(the_pcu);
+	struct gprs_rlcmac_bts  *bts = bts_alloc(the_pcu);
 	int ts_no = 7;
 	uint32_t fn = 2654218;
 	uint16_t qta = 31;
@@ -1379,8 +1378,8 @@
 	uint8_t ms_class = 1;
 
 	printf("=== start %s ===\n", __func__);
-	setup_bts(&the_bts, ts_no, 12);
-	uplink_header_type_1_parsing_test(&the_bts, ts_no, tlli, &fn,
+	setup_bts(bts, ts_no, 12);
+	uplink_header_type_1_parsing_test(bts, ts_no, tlli, &fn,
 			qta, ms_class);
 	printf("=== end %s ===\n", __func__);
 }
diff --git a/tests/emu/pcu_emu.cpp b/tests/emu/pcu_emu.cpp
index 2336e67..ab2804b 100644
--- a/tests/emu/pcu_emu.cpp
+++ b/tests/emu/pcu_emu.cpp
@@ -142,7 +142,7 @@
 	init_pcu(pcu);
 	init_main_bts();
 	bssgp_set_bssgp_callback(gprs_gp_send_cb, pcu->nsi);
-	create_and_connect_bssgp(bts_data(pcu->bts), INADDR_LOOPBACK, 23000);
+	create_and_connect_bssgp(pcu->bts, INADDR_LOOPBACK, 23000);
 
 	for (;;)
 		osmo_select_main(0);
diff --git a/tests/fn/FnTest.cpp b/tests/fn/FnTest.cpp
index 3185bd5..dd30b34 100644
--- a/tests/fn/FnTest.cpp
+++ b/tests/fn/FnTest.cpp
@@ -36,24 +36,24 @@
 int16_t spoof_mnc = 0, spoof_mcc = 0;
 bool spoof_mnc_3_digits = false;
 
-static uint32_t calc_fn(BTS * bts, uint32_t rfn)
+static uint32_t calc_fn(struct gprs_rlcmac_bts * bts, uint32_t rfn)
 {
 	uint32_t fn;
-	fn = bts->rfn_to_fn(rfn);
+	fn = bts_rfn_to_fn(bts, rfn);
 	printf("rfn=%i ==> fn=%i\n", rfn, fn);
 	return fn;
 }
 
-static void set_fn(BTS * bts, uint32_t fn)
+static void set_fn(struct gprs_rlcmac_bts * bts, uint32_t fn)
 {
 	printf("\n");
-	bts->set_current_frame_number(fn);
+	bts_set_current_frame_number(bts, fn);
 	printf("bts: fn=%i\n", fn);
 }
 
 static void run_test()
 {
-	BTS bts(the_pcu);
+	struct gprs_rlcmac_bts *bts = bts_alloc(the_pcu);
 	uint32_t fn;
 
 	printf("RFN_MODULUS=%i\n",RFN_MODULUS);
@@ -63,20 +63,20 @@
 	/* Test with a collection of real world examples,
 	 * all all of them are not critical and do not
 	 * assume the occurence of any race contions */
-	set_fn(&bts, 1320462);
-	fn = calc_fn(&bts, 5066);
+	set_fn(bts, 1320462);
+	fn = calc_fn(bts, 5066);
 	OSMO_ASSERT(fn == 1320458);
 
-	set_fn(&bts, 8246);
-	fn = calc_fn(&bts, 8244);
+	set_fn(bts, 8246);
+	fn = calc_fn(bts, 8244);
 	OSMO_ASSERT(fn == 8244);
 
-	set_fn(&bts, 10270);
-	fn = calc_fn(&bts, 10269);
+	set_fn(bts, 10270);
+	fn = calc_fn(bts, 10269);
 	OSMO_ASSERT(fn == 10269);
 
-	set_fn(&bts, 311276);
-	fn = calc_fn(&bts, 14250);
+	set_fn(bts, 311276);
+	fn = calc_fn(bts, 14250);
 	OSMO_ASSERT(fn == 311274);
 
 
@@ -84,20 +84,20 @@
 	 * just wrapped over a little bit above the
 	 * modulo 42432 raster, but the rach request
 	 * occurred before the wrapping */
-	set_fn(&bts, RFN_MODULUS + 30);
-	fn = calc_fn(&bts, RFN_MODULUS - 10);
+	set_fn(bts, RFN_MODULUS + 30);
+	fn = calc_fn(bts, RFN_MODULUS - 10);
 	OSMO_ASSERT(fn == 42422);
 
-	set_fn(&bts, RFN_MODULUS + 1);
-	fn = calc_fn(&bts, RFN_MODULUS - 1);
+	set_fn(bts, RFN_MODULUS + 1);
+	fn = calc_fn(bts, RFN_MODULUS - 1);
 	OSMO_ASSERT(fn == 42431);
 
-	set_fn(&bts, RFN_MODULUS * 123 + 16);
-	fn = calc_fn(&bts, RFN_MODULUS - 4);
+	set_fn(bts, RFN_MODULUS * 123 + 16);
+	fn = calc_fn(bts, RFN_MODULUS - 4);
 	OSMO_ASSERT(fn == 5219132);
 
-	set_fn(&bts, RFN_MODULUS * 123 + 451);
-	fn = calc_fn(&bts, RFN_MODULUS - 175);
+	set_fn(bts, RFN_MODULUS * 123 + 451);
+	fn = calc_fn(bts, RFN_MODULUS - 175);
 	OSMO_ASSERT(fn == 5218961);
 
 
@@ -105,41 +105,42 @@
 	 * the BTS just wrapped its internal frame number
 	 * but we still get rach requests with high relative
 	 * frame numbers. */
-	set_fn(&bts, 0);
-	fn = calc_fn(&bts, RFN_MODULUS - 13);
+	set_fn(bts, 0);
+	fn = calc_fn(bts, RFN_MODULUS - 13);
 	OSMO_ASSERT(fn == 2715635);
 
-	set_fn(&bts, 453);
-	fn = calc_fn(&bts, RFN_MODULUS - 102);
+	set_fn(bts, 453);
+	fn = calc_fn(bts, RFN_MODULUS - 102);
 	OSMO_ASSERT(fn == 2715546);
 
-	set_fn(&bts, 10);
-	fn = calc_fn(&bts, RFN_MODULUS - 10);
+	set_fn(bts, 10);
+	fn = calc_fn(bts, RFN_MODULUS - 10);
 	OSMO_ASSERT(fn == 2715638);
 
-	set_fn(&bts, 23);
-	fn = calc_fn(&bts, RFN_MODULUS - 42);
+	set_fn(bts, 23);
+	fn = calc_fn(bts, RFN_MODULUS - 42);
 	OSMO_ASSERT(fn == 2715606);
 
 
 	/* Also check with some corner case
 	 * values where Fn and RFn reach its
 	 * maximum/minimum valid range */
-	set_fn(&bts, GSM_MAX_FN);
-	fn = calc_fn(&bts, RFN_MODULUS-1);
+	set_fn(bts, GSM_MAX_FN);
+	fn = calc_fn(bts, RFN_MODULUS-1);
 	OSMO_ASSERT(fn == GSM_MAX_FN-1);
 
-	set_fn(&bts, 0);
-	fn = calc_fn(&bts, RFN_MODULUS-1);
+	set_fn(bts, 0);
+	fn = calc_fn(bts, RFN_MODULUS-1);
 	OSMO_ASSERT(fn == GSM_MAX_FN-1);
 
-	set_fn(&bts, GSM_MAX_FN);
-	fn = calc_fn(&bts, 0);
+	set_fn(bts, GSM_MAX_FN);
+	fn = calc_fn(bts, 0);
 	OSMO_ASSERT(fn == GSM_MAX_FN);
 
-	set_fn(&bts, 0);
-	fn = calc_fn(&bts, 0);
+	set_fn(bts, 0);
+	fn = calc_fn(bts, 0);
 	OSMO_ASSERT(fn == 0);
+	talloc_free(bts);
 }
 
 int main(int argc, char **argv)
diff --git a/tests/ms/MsTest.cpp b/tests/ms/MsTest.cpp
index d6c8f18..58579f8 100644
--- a/tests/ms/MsTest.cpp
+++ b/tests/ms/MsTest.cpp
@@ -51,18 +51,18 @@
 	uint32_t tlli = 0xffeeddbb;
 	gprs_rlcmac_dl_tbf *dl_tbf;
 	gprs_rlcmac_ul_tbf *ul_tbf;
-	BTS the_bts(the_pcu);
+	struct gprs_rlcmac_bts *bts = bts_alloc(the_pcu);
 	GprsMs *ms;
 
 	printf("=== start %s ===\n", __func__);
 
-	ms = ms_alloc(&the_bts, tlli);
+	ms = ms_alloc(bts, tlli);
 	OSMO_ASSERT(ms_is_idle(ms));
 
 	dl_tbf = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_dl_tbf);
-	new (dl_tbf) gprs_rlcmac_dl_tbf(&the_bts, ms);
+	new (dl_tbf) gprs_rlcmac_dl_tbf(bts, ms);
 	ul_tbf = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_ul_tbf);
-	new (ul_tbf) gprs_rlcmac_ul_tbf(&the_bts, ms);
+	new (ul_tbf) gprs_rlcmac_ul_tbf(bts, ms);
 
 	ms_attach_tbf(ms, ul_tbf);
 	OSMO_ASSERT(!ms_is_idle(ms));
@@ -88,7 +88,7 @@
 
 	talloc_free(dl_tbf);
 	talloc_free(ul_tbf);
-
+	talloc_free(bts);
 	printf("=== end %s ===\n", __func__);
 }
 
@@ -114,21 +114,21 @@
 	uint32_t tlli = 0xffeeddbb;
 	gprs_rlcmac_dl_tbf *dl_tbf;
 	gprs_rlcmac_ul_tbf *ul_tbf;
-	BTS the_bts(the_pcu);
+	struct gprs_rlcmac_bts *bts = bts_alloc(the_pcu);
 	GprsMs *ms;
 	last_cb = CB_UNKNOWN;
 
 	printf("=== start %s ===\n", __func__);
 
-	ms = ms_alloc(&the_bts, tlli);
+	ms = ms_alloc(bts, tlli);
 	ms_set_callback(ms, &ms_cb);
 
 	OSMO_ASSERT(ms_is_idle(ms));
 
 	dl_tbf = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_dl_tbf);
-	new (dl_tbf) gprs_rlcmac_dl_tbf(&the_bts, ms);
+	new (dl_tbf) gprs_rlcmac_dl_tbf(bts, ms);
 	ul_tbf = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_ul_tbf);
-	new (ul_tbf) gprs_rlcmac_ul_tbf(&the_bts, ms);
+	new (ul_tbf) gprs_rlcmac_ul_tbf(bts, ms);
 
 	OSMO_ASSERT(last_cb == CB_UNKNOWN);
 
@@ -163,7 +163,7 @@
 
 	talloc_free(dl_tbf);
 	talloc_free(ul_tbf);
-
+	talloc_free(bts);
 	printf("=== end %s ===\n", __func__);
 }
 
@@ -188,23 +188,23 @@
 	uint32_t tlli = 0xffeeddbb;
 	gprs_rlcmac_dl_tbf *dl_tbf[2];
 	gprs_rlcmac_ul_tbf *ul_tbf;
-	BTS the_bts(the_pcu);
+	struct gprs_rlcmac_bts *bts = bts_alloc(the_pcu);
 	GprsMs *ms;
 
 	printf("=== start %s ===\n", __func__);
 
-	ms = ms_alloc(&the_bts, tlli);
+	ms = ms_alloc(bts, tlli);
 	ms_set_callback(ms, &ms_replace_tbf_cb);
 
 	OSMO_ASSERT(ms_is_idle(ms));
 	was_idle = false;
 
 	dl_tbf[0] = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_dl_tbf);
-	new (dl_tbf[0]) gprs_rlcmac_dl_tbf(&the_bts, ms);
+	new (dl_tbf[0]) gprs_rlcmac_dl_tbf(bts, ms);
 	dl_tbf[1] = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_dl_tbf);
-	new (dl_tbf[1]) gprs_rlcmac_dl_tbf(&the_bts, ms);
+	new (dl_tbf[1]) gprs_rlcmac_dl_tbf(bts, ms);
 	ul_tbf = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_ul_tbf);
-	new (ul_tbf) gprs_rlcmac_ul_tbf(&the_bts, ms);
+	new (ul_tbf) gprs_rlcmac_ul_tbf(bts, ms);
 
 	ms_attach_tbf(ms, dl_tbf[0]);
 	OSMO_ASSERT(!ms_is_idle(ms));
@@ -253,7 +253,7 @@
 	talloc_free(dl_tbf[0]);
 	talloc_free(dl_tbf[1]);
 	talloc_free(ul_tbf);
-
+	talloc_free(bts);
 	printf("=== end %s ===\n", __func__);
 }
 
@@ -262,12 +262,12 @@
 	uint32_t start_tlli = 0xaa000000;
 	uint32_t new_ms_tlli = 0xff001111;
 	uint32_t other_sgsn_tlli = 0xff00eeee;
-	BTS the_bts(the_pcu);
+	struct gprs_rlcmac_bts *bts = bts_alloc(the_pcu);
 	GprsMs *ms;
 
 	printf("=== start %s ===\n", __func__);
 
-	ms = ms_alloc(&the_bts, start_tlli);
+	ms = ms_alloc(bts, start_tlli);
 
 	OSMO_ASSERT(ms_is_idle(ms));
 
@@ -347,7 +347,7 @@
 	OSMO_ASSERT(!ms_check_tlli(ms, start_tlli));
 
 	talloc_free(ms);
-
+	talloc_free(bts);
 	printf("=== end %s ===\n", __func__);
 }
 
@@ -374,9 +374,9 @@
 	const char *imsi2 = "001001987654322";
 
 	gprs_rlcmac_ul_tbf *ul_tbf;
-	BTS the_bts(the_pcu);
+	struct gprs_rlcmac_bts *bts = bts_alloc(the_pcu);
 	GprsMs *ms, *ms_tmp;
-	GprsMsStorage store(&the_bts);
+	GprsMsStorage store(bts);
 
 	printf("=== start %s ===\n", __func__);
 
@@ -420,7 +420,7 @@
 	ms = store.get_ms(tlli + 0);
 	OSMO_ASSERT(ms != NULL);
 	ul_tbf = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_ul_tbf);
-	new (ul_tbf) gprs_rlcmac_ul_tbf(&the_bts, ms);
+	new (ul_tbf) gprs_rlcmac_ul_tbf(bts, ms);
 	ms_attach_tbf(ms, ul_tbf);
 	ms_detach_tbf(ms, ul_tbf);
 	ms = store.get_ms(tlli + 0);
@@ -437,7 +437,7 @@
 	OSMO_ASSERT(ms == NULL);
 
 	talloc_free(ul_tbf);
-
+	talloc_free(bts);
 	printf("=== end %s ===\n", __func__);
 }
 
@@ -446,22 +446,22 @@
 	uint32_t tlli = 0xffeeddbb;
 	gprs_rlcmac_dl_tbf *dl_tbf;
 	gprs_rlcmac_ul_tbf *ul_tbf;
-	BTS the_bts(the_pcu);
+	struct gprs_rlcmac_bts *bts = bts_alloc(the_pcu);
 	GprsMs *ms;
 	last_cb = CB_UNKNOWN;
 
 	printf("=== start %s ===\n", __func__);
 
-	ms = ms_alloc(&the_bts, tlli);
+	ms = ms_alloc(bts, tlli);
 	ms_set_callback(ms, &ms_cb);
 	ms_set_timeout(ms, 1);
 
 	OSMO_ASSERT(ms_is_idle(ms));
 
 	dl_tbf = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_dl_tbf);
-	new (dl_tbf) gprs_rlcmac_dl_tbf(&the_bts, ms);
+	new (dl_tbf) gprs_rlcmac_dl_tbf(bts, ms);
 	ul_tbf = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_ul_tbf);
-	new (ul_tbf) gprs_rlcmac_ul_tbf(&the_bts, ms);
+	new (ul_tbf) gprs_rlcmac_ul_tbf(bts, ms);
 
 	OSMO_ASSERT(last_cb == CB_UNKNOWN);
 
@@ -493,14 +493,13 @@
 	talloc_free(ms);
 	talloc_free(dl_tbf);
 	talloc_free(ul_tbf);
-
+	talloc_free(bts);
 	printf("=== end %s ===\n", __func__);
 }
 
 static void test_ms_cs_selection()
 {
-	BTS the_bts(the_pcu);
-	gprs_rlcmac_bts *bts = the_bts.bts_data();
+	struct gprs_rlcmac_bts *bts = bts_alloc(the_pcu);
 	uint32_t tlli = 0xffeeddbb;
 
 	gprs_rlcmac_dl_tbf *dl_tbf;
@@ -513,12 +512,12 @@
 	the_pcu->vty.cs_downgrade_threshold = 0;
 	the_pcu->vty.cs_adj_lower_limit = 0;
 
-	ms = ms_alloc(&the_bts, tlli);
+	ms = ms_alloc(bts, tlli);
 
 	OSMO_ASSERT(ms_is_idle(ms));
 
 	dl_tbf = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_dl_tbf);
-	new (dl_tbf) gprs_rlcmac_dl_tbf(&the_bts, ms);
+	new (dl_tbf) gprs_rlcmac_dl_tbf(bts, ms);
 	ms_attach_tbf(ms, dl_tbf);
 
 	OSMO_ASSERT(!ms_is_idle(ms));
@@ -530,7 +529,7 @@
 	OSMO_ASSERT(mcs_chan_code(ms_current_cs_dl(ms)) == 2);
 
 	talloc_free(dl_tbf);
-
+	talloc_free(bts);
 	printf("=== end %s ===\n", __func__);
 }
 
@@ -545,8 +544,7 @@
 
 static void test_ms_mcs_mode()
 {
-	BTS the_bts(the_pcu);
-	gprs_rlcmac_bts *bts = the_bts.bts_data();
+	struct gprs_rlcmac_bts *bts = bts_alloc(the_pcu);
 	uint32_t tlli = 0xdeadbeef;
 
 	gprs_rlcmac_dl_tbf *dl_tbf;
@@ -554,18 +552,18 @@
 
 	printf("=== start %s ===\n", __func__);
 
-	ms1 = ms_alloc(&the_bts, tlli);
+	ms1 = ms_alloc(bts, tlli);
 	dump_ms(ms1, "1: no BTS defaults  ");
 
 	bts->initial_cs_dl = 4;
 	bts->initial_cs_ul = 1;
 	the_pcu->vty.cs_downgrade_threshold = 0;
 
-	ms2 = ms_alloc(&the_bts, tlli + 1);
+	ms2 = ms_alloc(bts, tlli + 1);
 	dump_ms(ms2, "2: with BTS defaults");
 
 	dl_tbf = talloc_zero(tall_pcu_ctx, struct gprs_rlcmac_dl_tbf);
-	new (dl_tbf) gprs_rlcmac_dl_tbf(&the_bts, ms2);
+	new (dl_tbf) gprs_rlcmac_dl_tbf(bts, ms2);
 	ms_attach_tbf(ms2, dl_tbf);
 
 	dump_ms(ms2, "2: after TBF attach ");
@@ -599,7 +597,7 @@
 	dump_ms(ms2, "2: after mode set   ");
 
 	talloc_free(dl_tbf);
-
+	talloc_free(bts);
 	printf("=== end %s ===\n", __func__);
 }
 
diff --git a/tests/tbf/TbfTest.cpp b/tests/tbf/TbfTest.cpp
index 1b85201..ef26862 100644
--- a/tests/tbf/TbfTest.cpp
+++ b/tests/tbf/TbfTest.cpp
@@ -22,8 +22,11 @@
 
 #include "bts.h"
 #include "tbf.h"
+#include "tbf_dl.h"
 #include "tbf_ul.h"
+#include "gprs_ms.h"
 #include "gprs_debug.h"
+#include "gprs_ms_storage.h"
 #include "pcu_utils.h"
 #include "gprs_bssgp_pcu.h"
 #include "pcu_l1_if.h"
@@ -54,7 +57,7 @@
 /* Measurements shared by all unit tests */
 static struct pcu_l1_meas meas;
 
-static int bts_handle_rach(BTS *bts, uint16_t ra, uint32_t Fn, int16_t qta)
+static int bts_handle_rach(struct gprs_rlcmac_bts *bts, uint16_t ra, uint32_t Fn, int16_t qta)
 {
 	struct rach_ind_params rip = {
 		.burst_type = GSM_L1_BURST_TYPE_ACCESS_0,
@@ -66,7 +69,7 @@
 		.qta = qta,
 	};
 
-	return bts->rcv_rach(&rip);
+	return bts_rcv_rach(bts, &rip);
 }
 
 static void check_tbf(gprs_rlcmac_tbf *tbf)
@@ -93,20 +96,20 @@
 {
 	the_pcu = gprs_pcu_alloc(tall_pcu_ctx);
 	the_pcu->bts = bts_alloc(the_pcu);
-	BTS *the_bts = the_pcu->bts;
+	struct gprs_rlcmac_bts *bts = the_pcu->bts;
 	GprsMs *ms, *ms_new;
 
 	fprintf(stderr, "=== start %s ===\n", __func__);
 
 	the_pcu->alloc_algorithm = alloc_algorithm_a;
-	the_bts->bts_data()->trx[0].pdch[2].enable();
-	the_bts->bts_data()->trx[0].pdch[3].enable();
+	bts->trx[0].pdch[2].enable();
+	bts->trx[0].pdch[3].enable();
 
 	/*
 	 * Make a uplink and downlink allocation
 	 */
-	ms = the_bts->ms_alloc(0, 0);
-	gprs_rlcmac_tbf *dl_tbf = tbf_alloc_dl_tbf(the_bts->bts_data(),
+	ms = bts_alloc_ms(bts, 0, 0);
+	gprs_rlcmac_tbf *dl_tbf = tbf_alloc_dl_tbf(bts,
 						ms, 0, false);
 	OSMO_ASSERT(dl_tbf != NULL);
 	dl_tbf->update_ms(0x2342, GPRS_RLCMAC_DL_TBF);
@@ -114,14 +117,14 @@
 	OSMO_ASSERT(ms_dl_tbf(ms) == dl_tbf);
 	OSMO_ASSERT(dl_tbf->ms() == ms);
 
-	gprs_rlcmac_tbf *ul_tbf = tbf_alloc_ul_tbf(the_bts->bts_data(),
+	gprs_rlcmac_tbf *ul_tbf = tbf_alloc_ul_tbf(bts,
 						   ms, 0, false);
 	OSMO_ASSERT(ul_tbf != NULL);
 	ul_tbf->update_ms(0x2342, GPRS_RLCMAC_UL_TBF);
 	OSMO_ASSERT(ms_ul_tbf(ms) == ul_tbf);
 	OSMO_ASSERT(ul_tbf->ms() == ms);
 
-	OSMO_ASSERT(the_bts->ms_by_tlli(0x2342) == ms);
+	OSMO_ASSERT(bts_ms_by_tlli(bts, 0x2342, GSM_RESERVED_TMSI) == ms);
 
 	/*
 	 * Now check.. that DL changes and that the timing advance
@@ -130,20 +133,20 @@
 	dl_tbf->update_ms(0x4232, GPRS_RLCMAC_DL_TBF);
 
 	/* It is still there, since the new TLLI has not been used for UL yet */
-	ms_new = the_bts->ms_by_tlli(0x2342);
+	ms_new = bts_ms_by_tlli(bts, 0x2342, GSM_RESERVED_TMSI);
 	OSMO_ASSERT(ms == ms_new);
 
-	ms_new = the_bts->ms_by_tlli(0x4232);
+	ms_new = bts_ms_by_tlli(bts, 0x4232, GSM_RESERVED_TMSI);
 	OSMO_ASSERT(ms == ms_new);
 	OSMO_ASSERT(ms_dl_tbf(ms) == dl_tbf);
 	OSMO_ASSERT(ms_ul_tbf(ms) == ul_tbf);
 
 	/* Now use the new TLLI for UL */
 	ul_tbf->update_ms(0x4232, GPRS_RLCMAC_UL_TBF);
-	ms_new = the_bts->ms_by_tlli(0x2342);
+	ms_new = bts_ms_by_tlli(bts, 0x2342, GSM_RESERVED_TMSI);
 	OSMO_ASSERT(ms_new == NULL);
 
-	ms_new = the_bts->ms_by_tlli(0x4232);
+	ms_new = bts_ms_by_tlli(bts, 0x4232, GSM_RESERVED_TMSI);
 	OSMO_ASSERT(ms_new != NULL);
 	OSMO_ASSERT(ms_ta(ms_new) == 4);
 
@@ -168,12 +171,10 @@
 	return 0;
 }
 
-static void setup_bts(BTS *the_bts, uint8_t ts_no, uint8_t cs = 1)
+static void setup_bts(struct gprs_rlcmac_bts *bts, uint8_t ts_no, uint8_t cs = 1)
 {
-	gprs_rlcmac_bts *bts;
 	gprs_rlcmac_trx *trx;
 
-	bts = the_bts->bts_data();
 	the_pcu->alloc_algorithm = alloc_algorithm_a;
 	bts->initial_cs_dl = cs;
 	bts->initial_cs_ul = cs;
@@ -182,22 +183,20 @@
 	trx = &bts->trx[0];
 
 	trx->pdch[ts_no].enable();
-	the_bts->set_current_frame_number(DUMMY_FN);
+	bts_set_current_frame_number(bts, DUMMY_FN);
 }
 
-static gprs_rlcmac_dl_tbf *create_dl_tbf(BTS *the_bts, uint8_t ms_class,
+static gprs_rlcmac_dl_tbf *create_dl_tbf(struct gprs_rlcmac_bts *bts, uint8_t ms_class,
 	uint8_t egprs_ms_class, uint8_t *trx_no_)
 {
-	gprs_rlcmac_bts *bts;
 	int tfi;
 	uint8_t trx_no;
 	GprsMs *ms;
 	gprs_rlcmac_dl_tbf *dl_tbf;
 
-	bts = the_bts->bts_data();
-	ms = the_bts->ms_alloc(ms_class, egprs_ms_class);
+	ms = bts_alloc_ms(bts, ms_class, egprs_ms_class);
 
-	tfi = the_bts->tfi_find_free(GPRS_RLCMAC_DL_TBF, &trx_no, -1);
+	tfi = bts_tfi_find_free(bts, GPRS_RLCMAC_DL_TBF, &trx_no, -1);
 	OSMO_ASSERT(tfi >= 0);
 	dl_tbf = tbf_alloc_dl_tbf(bts, ms, trx_no, true);
 	OSMO_ASSERT(dl_tbf);
@@ -243,7 +242,7 @@
 static void request_dl_rlc_block(struct gprs_rlcmac_tbf *tbf,
 	uint32_t *fn, uint8_t *block_nr = NULL)
 {
-	request_dl_rlc_block(tbf->bts->bts_data(), tbf->trx->trx_no,
+	request_dl_rlc_block(tbf->bts, tbf->trx->trx_no,
 		tbf->control_ts, fn, block_nr);
 }
 
@@ -256,7 +255,7 @@
 {
 	the_pcu = gprs_pcu_alloc(tall_pcu_ctx);
 	the_pcu->bts = bts_alloc(the_pcu);
-	BTS *the_bts = the_pcu->bts;
+	struct gprs_rlcmac_bts *bts = the_pcu->bts;
 	uint8_t ts_no = 4;
 	unsigned i;
 	uint8_t ms_class = 45;
@@ -273,8 +272,8 @@
 	gprs_rlcmac_dl_tbf *dl_tbf;
 	gprs_rlcmac_tbf *new_tbf;
 
-	setup_bts(the_bts, ts_no);
-	dl_tbf = create_dl_tbf(the_bts, ms_class, 0, &trx_no);
+	setup_bts(bts, ts_no);
+	dl_tbf = create_dl_tbf(bts, ms_class, 0, &trx_no);
 	dl_tbf->update_ms(tlli, GPRS_RLCMAC_DL_TBF);
 	ms = dl_tbf->ms();
 
@@ -342,7 +341,7 @@
 {
 	the_pcu = gprs_pcu_alloc(tall_pcu_ctx);
 	the_pcu->bts = bts_alloc(the_pcu);
-	BTS *the_bts = the_pcu->bts;
+	struct gprs_rlcmac_bts *bts = the_pcu->bts;
 	uint8_t ts_no = 4;
 	unsigned i;
 	uint8_t ms_class = 45;
@@ -357,10 +356,10 @@
 
 	fprintf(stderr, "=== start %s ===\n", __func__);
 
-	setup_bts(the_bts, ts_no);
+	setup_bts(bts, ts_no);
 	OSMO_ASSERT(osmo_tdef_set(the_pcu->T_defs, -2031, 200, OSMO_TDEF_MS) == 0);
 
-	dl_tbf = create_dl_tbf(the_bts, ms_class, 0, &trx_no);
+	dl_tbf = create_dl_tbf(bts, ms_class, 0, &trx_no);
 	dl_tbf->update_ms(tlli, GPRS_RLCMAC_DL_TBF);
 
 	for (i = 0; i < sizeof(llc_data); i++)
@@ -413,7 +412,7 @@
 {
 	the_pcu = gprs_pcu_alloc(tall_pcu_ctx);
 	the_pcu->bts = bts_alloc(the_pcu);
-	BTS *the_bts = the_pcu->bts;
+	struct gprs_rlcmac_bts *bts = the_pcu->bts;
 	uint8_t ts_no = 4;
 	uint8_t ms_class = 45;
 	uint8_t trx_no;
@@ -423,27 +422,27 @@
 
 	fprintf(stderr, "=== start %s ===\n", __func__);
 
-	setup_bts(the_bts, ts_no);
+	setup_bts(bts, ts_no);
 
-	dl_tbf[0] = create_dl_tbf(the_bts, ms_class, 0, &trx_no);
-	dl_tbf[1] = create_dl_tbf(the_bts, ms_class, 0, &trx_no);
+	dl_tbf[0] = create_dl_tbf(bts, ms_class, 0, &trx_no);
+	dl_tbf[1] = create_dl_tbf(bts, ms_class, 0, &trx_no);
 
 	dl_tbf[0]->update_ms(0xf1000001, GPRS_RLCMAC_DL_TBF);
 	dl_tbf[1]->update_ms(0xf1000002, GPRS_RLCMAC_DL_TBF);
 
 	ms_set_imsi(dl_tbf[0]->ms(), "001001000000001");
-	ms1 = the_bts->ms_store().get_ms(0, 0, "001001000000001");
+	ms1 = bts_ms_store(bts)->get_ms(0, 0, "001001000000001");
 	OSMO_ASSERT(ms1 != NULL);
-	ms2 = the_bts->ms_store().get_ms(0xf1000001);
+	ms2 = bts_ms_store(bts)->get_ms(0xf1000001);
 	OSMO_ASSERT(ms2 != NULL);
 	OSMO_ASSERT(strcmp(ms_imsi(ms2), "001001000000001") == 0);
 	OSMO_ASSERT(ms1 == ms2);
 
 	/* change the IMSI on TBF 0 */
 	ms_set_imsi(dl_tbf[0]->ms(), "001001000000002");
-	ms1 = the_bts->ms_store().get_ms(0, 0, "001001000000001");
+	ms1 = bts_ms_store(bts)->get_ms(0, 0, "001001000000001");
 	OSMO_ASSERT(ms1 == NULL);
-	ms1 = the_bts->ms_store().get_ms(0, 0, "001001000000002");
+	ms1 = bts_ms_store(bts)->get_ms(0, 0, "001001000000002");
 	OSMO_ASSERT(ms1 != NULL);
 	OSMO_ASSERT(strcmp(ms_imsi(ms2), "001001000000002") == 0);
 	OSMO_ASSERT(ms1 == ms2);
@@ -452,7 +451,7 @@
 	{
 		ms_ref(ms2);
 		ms_set_imsi(dl_tbf[1]->ms(), "001001000000002");
-		ms1 = the_bts->ms_store().get_ms(0, 0, "001001000000002");
+		ms1 = bts_ms_store(bts)->get_ms(0, 0, "001001000000002");
 		OSMO_ASSERT(ms1 != NULL);
 		OSMO_ASSERT(ms1 != ms2);
 		OSMO_ASSERT(strcmp(ms_imsi(ms1), "001001000000002") == 0);
@@ -460,11 +459,11 @@
 		ms_unref(ms2);
 	}
 
-	ms2 = the_bts->ms_store().get_ms(0xf1000001);
+	ms2 = bts_ms_store(bts)->get_ms(0xf1000001);
 	OSMO_ASSERT(ms2 == NULL);
 
 	tbf_free(dl_tbf[1]);
-	ms1 = the_bts->ms_store().get_ms(0, 0, "001001000000002");
+	ms1 = bts_ms_store(bts)->get_ms(0, 0, "001001000000002");
 	OSMO_ASSERT(ms1 == NULL);
 
 	TALLOC_FREE(the_pcu);
@@ -475,8 +474,7 @@
 {
 	the_pcu = gprs_pcu_alloc(tall_pcu_ctx);
 	the_pcu->bts = bts_alloc(the_pcu);
-	BTS *the_bts = the_pcu->bts;
-	gprs_rlcmac_bts *bts;
+	struct gprs_rlcmac_bts *bts = the_pcu->bts;
 	unsigned i;
 	uint8_t ts_no = 4;
 	uint8_t ms_class = 45;
@@ -486,14 +484,13 @@
 
 	fprintf(stderr, "=== start %s ===\n", __func__);
 
-	bts = the_bts->bts_data();
-	the_bts->pcu->nsi = gprs_ns2_instantiate(tall_pcu_ctx, gprs_ns_prim_cb, NULL);
-	if (!the_bts->pcu->nsi) {
+	bts->pcu->nsi = gprs_ns2_instantiate(tall_pcu_ctx, gprs_ns_prim_cb, NULL);
+	if (!bts->pcu->nsi) {
 		LOGP(DBSSGP, LOGL_ERROR, "Failed to create NS instance\n");
 		abort();
 	}
 
-	setup_bts(the_bts, ts_no);
+	setup_bts(bts, ts_no);
 	gprs_bssgp_init(bts, 1234, 1234, 1, 1, false, 0, 0, 0);
 
 	for (i = 0; i < 1024; i++) {
@@ -521,8 +518,7 @@
 {
 	the_pcu = gprs_pcu_alloc(tall_pcu_ctx);
 	the_pcu->bts = bts_alloc(the_pcu);
-	BTS *the_bts = the_pcu->bts;
-	gprs_rlcmac_bts *bts;
+	struct gprs_rlcmac_bts *bts = the_pcu->bts;
 	uint8_t ts_no = 4;
 	uint8_t ms_class = 45;
 	int rc = 0;
@@ -533,16 +529,15 @@
 
 	uint8_t buf[19];
 
-	bts = the_bts->bts_data();
-	the_bts->pcu->nsi = gprs_ns2_instantiate(tall_pcu_ctx, gprs_ns_prim_cb, NULL);
-	if (!the_bts->pcu->nsi) {
+	bts->pcu->nsi = gprs_ns2_instantiate(tall_pcu_ctx, gprs_ns_prim_cb, NULL);
+	if (!bts->pcu->nsi) {
 		LOGP(DBSSGP, LOGL_ERROR, "Failed to create NS instance\n");
 		abort();
 	}
 
 	fprintf(stderr, "=== start %s ===\n", __func__);
 
-	setup_bts(the_bts, ts_no);
+	setup_bts(bts, ts_no);
 	/* keep the MS object 10 seconds */
 	OSMO_ASSERT(osmo_tdef_set(the_pcu->T_defs, -2030, 10, OSMO_TDEF_S) == 0);
 
@@ -554,7 +549,7 @@
 		delay_csec, buf, sizeof(buf));
 	OSMO_ASSERT(rc >= 0);
 
-	ms = the_bts->ms_store().get_ms(0, 0, imsi);
+	ms = bts_ms_store(bts)->get_ms(0, 0, imsi);
 	OSMO_ASSERT(ms != NULL);
 	OSMO_ASSERT(ms_dl_tbf(ms) != NULL);
 	ms_dl_tbf(ms)->set_ta(0);
@@ -605,7 +600,7 @@
 	TALLOC_FREE(the_pcu);
 }
 
-static gprs_rlcmac_ul_tbf *establish_ul_tbf_single_phase(BTS *the_bts,
+static gprs_rlcmac_ul_tbf *establish_ul_tbf_single_phase(struct gprs_rlcmac_bts *bts,
 	uint8_t ts_no, uint32_t tlli, uint32_t *fn, uint16_t qta)
 {
 	GprsMs *ms;
@@ -614,11 +609,11 @@
 	uint8_t trx_no = 0;
 	struct gprs_rlcmac_pdch *pdch;
 
-	tfi = the_bts->tfi_find_free(GPRS_RLCMAC_UL_TBF, &trx_no, -1);
+	tfi = bts_tfi_find_free(bts, GPRS_RLCMAC_UL_TBF, &trx_no, -1);
 
-	bts_handle_rach(the_bts, 0x03, *fn, qta);
+	bts_handle_rach(bts, 0x03, *fn, qta);
 
-	ul_tbf = the_bts->ul_tbf_by_tfi(tfi, trx_no, ts_no);
+	ul_tbf = bts_ul_tbf_by_tfi(bts, tfi, trx_no, ts_no);
 	OSMO_ASSERT(ul_tbf != NULL);
 
 	OSMO_ASSERT(ul_tbf->ta() == qta / 4);
@@ -631,16 +626,16 @@
 		uint8_t(tlli >> 8), uint8_t(tlli), /* TLLI */
 	};
 
-	pdch = &the_bts->bts_data()->trx[trx_no].pdch[ts_no];
+	pdch = &bts->trx[trx_no].pdch[ts_no];
 	pdch->rcv_block(&data_msg[0], sizeof(data_msg), *fn, &meas);
 
-	ms = the_bts->ms_by_tlli(tlli);
+	ms = bts_ms_by_tlli(bts, tlli, GSM_RESERVED_TMSI);
 	OSMO_ASSERT(ms != NULL);
 
 	return ul_tbf;
 }
 
-static void send_ul_mac_block(BTS *the_bts, unsigned trx_no, unsigned ts_no,
+static void send_ul_mac_block(struct gprs_rlcmac_bts *bts, unsigned trx_no, unsigned ts_no,
 	RlcMacUplink_t *ulreq, unsigned fn)
 {
 	bitvec *rlc_block;
@@ -655,9 +650,9 @@
 	OSMO_ASSERT(size_t(num_bytes) < sizeof(buf));
 	bitvec_free(rlc_block);
 
-	the_bts->set_current_block_frame_number(fn, 0);
+	bts_set_current_block_frame_number(bts, fn, 0);
 
-	pdch = &the_bts->bts_data()->trx[trx_no].pdch[ts_no];
+	pdch = &bts->trx[trx_no].pdch[ts_no];
 	pdch->rcv_block(&buf[0], num_bytes, fn, &meas);
 }
 
@@ -678,7 +673,7 @@
 		&ulreq, tbf->poll_fn);
 }
 
-static gprs_rlcmac_ul_tbf *puan_urbb_len_issue(BTS *the_bts,
+static gprs_rlcmac_ul_tbf *puan_urbb_len_issue(struct gprs_rlcmac_bts *bts,
 	uint8_t ts_no, uint32_t tlli, uint32_t *fn, uint16_t qta,
 	uint8_t ms_class, uint8_t egprs_ms_class)
 {
@@ -689,12 +684,9 @@
 	int tfi = 0;
 	gprs_rlcmac_ul_tbf *ul_tbf;
 	struct gprs_rlcmac_pdch *pdch;
-	gprs_rlcmac_bts *bts;
 	RlcMacUplink_t ulreq = {0};
 	struct gprs_rlc_ul_header_egprs_3 *egprs3  = NULL;
 
-	bts = the_bts->bts_data();
-
 	/* needed to set last_rts_fn in the PDCH object */
 	request_dl_rlc_block(bts, trx_no, ts_no, fn);
 
@@ -702,10 +694,10 @@
 	 * simulate RACH, this sends an Immediate
 	 * Assignment Uplink on the AGCH
 	 */
-	bts_handle_rach(the_bts, 0x73, rach_fn, qta);
+	bts_handle_rach(bts, 0x73, rach_fn, qta);
 
 	/* get next free TFI */
-	tfi = the_bts->tfi_find_free(GPRS_RLCMAC_UL_TBF, &trx_no, -1);
+	tfi = bts_tfi_find_free(bts, GPRS_RLCMAC_UL_TBF, &trx_no, -1);
 
 	/* fake a resource request */
 	ulreq.u.MESSAGE_TYPE = MT_PACKET_RESOURCE_REQUEST;
@@ -733,10 +725,10 @@
 			Multislot_capability.EGPRS_multislot_class = ms_class;
 	}
 
-	send_ul_mac_block(the_bts, trx_no, ts_no, &ulreq, sba_fn);
+	send_ul_mac_block(bts, trx_no, ts_no, &ulreq, sba_fn);
 
 	/* check the TBF */
-	ul_tbf = the_bts->ul_tbf_by_tfi(tfi, trx_no, ts_no);
+	ul_tbf = bts_ul_tbf_by_tfi(bts, tfi, trx_no, ts_no);
 	OSMO_ASSERT(ul_tbf);
 	OSMO_ASSERT(ul_tbf->ta() == qta / 4);
 
@@ -755,10 +747,10 @@
 		1, /* BSN:7, E:1 */
 	};
 
-	pdch = &the_bts->bts_data()->trx[trx_no].pdch[ts_no];
+	pdch = &bts->trx[trx_no].pdch[ts_no];
 	pdch->rcv_block(&data_msg[0], 23, *fn, &meas);
 
-	ms = the_bts->ms_by_tlli(tlli);
+	ms = bts_ms_by_tlli(bts, tlli, GSM_RESERVED_TMSI);
 	OSMO_ASSERT(ms != NULL);
 	OSMO_ASSERT(ms_ta(ms) == qta/4);
 	OSMO_ASSERT(ms_ul_tbf(ms) == ul_tbf);
@@ -825,7 +817,7 @@
 	return ul_tbf;
 }
 
-static gprs_rlcmac_ul_tbf *establish_ul_tbf_two_phase_spb(BTS *the_bts,
+static gprs_rlcmac_ul_tbf *establish_ul_tbf_two_phase_spb(struct gprs_rlcmac_bts *bts,
 	uint8_t ts_no, uint32_t tlli, uint32_t *fn, uint16_t qta,
 	uint8_t ms_class, uint8_t egprs_ms_class)
 {
@@ -836,12 +828,9 @@
 	int tfi = 0, i = 0;
 	gprs_rlcmac_ul_tbf *ul_tbf;
 	struct gprs_rlcmac_pdch *pdch;
-	gprs_rlcmac_bts *bts;
 	RlcMacUplink_t ulreq = {0};
 	struct gprs_rlc_ul_header_egprs_3 *egprs3  = NULL;
 
-	bts = the_bts->bts_data();
-
 	/* needed to set last_rts_fn in the PDCH object */
 	request_dl_rlc_block(bts, trx_no, ts_no, fn);
 
@@ -849,10 +838,10 @@
 	 * simulate RACH, this sends an Immediate
 	 * Assignment Uplink on the AGCH
 	 */
-	bts_handle_rach(the_bts, 0x73, rach_fn, qta);
+	bts_handle_rach(bts, 0x73, rach_fn, qta);
 
 	/* get next free TFI */
-	tfi = the_bts->tfi_find_free(GPRS_RLCMAC_UL_TBF, &trx_no, -1);
+	tfi = bts_tfi_find_free(bts, GPRS_RLCMAC_UL_TBF, &trx_no, -1);
 
 	/* fake a resource request */
 	ulreq.u.MESSAGE_TYPE = MT_PACKET_RESOURCE_REQUEST;
@@ -880,10 +869,10 @@
 			Multislot_capability.EGPRS_multislot_class = ms_class;
 	}
 
-	send_ul_mac_block(the_bts, trx_no, ts_no, &ulreq, sba_fn);
+	send_ul_mac_block(bts, trx_no, ts_no, &ulreq, sba_fn);
 
 	/* check the TBF */
-	ul_tbf = the_bts->ul_tbf_by_tfi(tfi, trx_no, ts_no);
+	ul_tbf = bts_ul_tbf_by_tfi(bts, tfi, trx_no, ts_no);
 	OSMO_ASSERT(ul_tbf != NULL);
 	OSMO_ASSERT(ul_tbf->ta() == qta / 4);
 
@@ -903,10 +892,10 @@
 		uint8_t(1), /* BSN:7, E:1 */
 	};
 
-	pdch = &the_bts->bts_data()->trx[trx_no].pdch[ts_no];
+	pdch = &bts->trx[trx_no].pdch[ts_no];
 	pdch->rcv_block(&data_msg[0], 23, *fn, &meas);
 
-	ms = the_bts->ms_by_tlli(tlli);
+	ms = bts_ms_by_tlli(bts, tlli, GSM_RESERVED_TMSI);
 	OSMO_ASSERT(ms != NULL);
 	OSMO_ASSERT(ms_ta(ms) == qta/4);
 	OSMO_ASSERT(ms_ul_tbf(ms) == ul_tbf);
@@ -1263,7 +1252,7 @@
 	return ul_tbf;
 }
 
-static gprs_rlcmac_ul_tbf *establish_ul_tbf(BTS *the_bts,
+static gprs_rlcmac_ul_tbf *establish_ul_tbf(struct gprs_rlcmac_bts *bts,
 	uint8_t ts_no, uint32_t tlli, uint32_t *fn, uint16_t qta,
 	uint8_t ms_class, uint8_t egprs_ms_class)
 {
@@ -1272,11 +1261,8 @@
 	uint8_t trx_no = 0;
 	int tfi = 0;
 	gprs_rlcmac_ul_tbf *ul_tbf;
-	gprs_rlcmac_bts *bts;
 	RlcMacUplink_t ulreq = {0};
 
-	bts = the_bts->bts_data();
-
 	/* needed to set last_rts_fn in the PDCH object */
 	request_dl_rlc_block(bts, trx_no, ts_no, fn);
 
@@ -1284,10 +1270,10 @@
 	 * simulate RACH, this sends an Immediate
 	 * Assignment Uplink on the AGCH
 	 */
-	bts_handle_rach(the_bts, 0x73, rach_fn, qta);
+	bts_handle_rach(bts, 0x73, rach_fn, qta);
 
 	/* get next free TFI */
-	tfi = the_bts->tfi_find_free(GPRS_RLCMAC_UL_TBF, &trx_no, -1);
+	tfi = bts_tfi_find_free(bts, GPRS_RLCMAC_UL_TBF, &trx_no, -1);
 
 	/* fake a resource request */
 	ulreq.u.MESSAGE_TYPE = MT_PACKET_RESOURCE_REQUEST;
@@ -1314,10 +1300,10 @@
 			MS_RA_capability_value[0].u.Content.
 			Multislot_capability.EGPRS_multislot_class = ms_class;
 	}
-	send_ul_mac_block(the_bts, trx_no, ts_no, &ulreq, sba_fn);
+	send_ul_mac_block(bts, trx_no, ts_no, &ulreq, sba_fn);
 
 	/* check the TBF */
-	ul_tbf = the_bts->ul_tbf_by_tfi(tfi, trx_no, ts_no);
+	ul_tbf = bts_ul_tbf_by_tfi(bts, tfi, trx_no, ts_no);
 	/* send packet uplink assignment */
 	*fn = sba_fn;
 	request_dl_rlc_block(ul_tbf, fn);
@@ -1330,7 +1316,7 @@
 	return ul_tbf;
 }
 
-static gprs_rlcmac_ul_tbf *establish_ul_tbf_two_phase_puan_URBB_no_length(BTS *the_bts,
+static gprs_rlcmac_ul_tbf *establish_ul_tbf_two_phase_puan_URBB_no_length(struct gprs_rlcmac_bts *bts,
 	uint8_t ts_no, uint32_t tlli, uint32_t *fn, uint16_t qta,
 	uint8_t ms_class, uint8_t egprs_ms_class, gprs_rlcmac_ul_tbf *ul_tbf)
 {
@@ -1369,7 +1355,7 @@
 		data[5] = 0x0;
 		data[6] = 0x2b;
 		data[7] = 0x2b;
-		pdch = &the_bts->bts_data()->trx[trx_no].pdch[ts_no];
+		pdch = &bts->trx[trx_no].pdch[ts_no];
 		pdch->rcv_block(&data[0], sizeof(data), *fn, &meas);
 	}
 	ul_tbf->create_ul_ack(*fn, ts_no);
@@ -1394,7 +1380,7 @@
 	data[6] = 0x2b;
 	data[7] = 0x2b;
 
-	pdch = &the_bts->bts_data()->trx[trx_no].pdch[ts_no];
+	pdch = &bts->trx[trx_no].pdch[ts_no];
 	pdch->rcv_block(&data[0], sizeof(data), *fn, &meas);
 
 	request_dl_rlc_block(ul_tbf, fn);
@@ -1402,7 +1388,7 @@
 	check_tbf(ul_tbf);
 	OSMO_ASSERT(ul_tbf->ul_ack_state_is(GPRS_RLCMAC_UL_ACK_NONE));
 
-	ms = the_bts->ms_by_tlli(tlli);
+	ms = bts_ms_by_tlli(bts, tlli, GSM_RESERVED_TMSI);
 	OSMO_ASSERT(ms != NULL);
 	OSMO_ASSERT(ms_ta(ms) == qta/4);
 	OSMO_ASSERT(ms_ul_tbf(ms) == ul_tbf);
@@ -1410,7 +1396,7 @@
 	return ul_tbf;
 }
 
-static gprs_rlcmac_ul_tbf *establish_ul_tbf_two_phase_puan_URBB_with_length(BTS *the_bts,
+static gprs_rlcmac_ul_tbf *establish_ul_tbf_two_phase_puan_URBB_with_length(struct gprs_rlcmac_bts *bts,
 	uint8_t ts_no, uint32_t tlli, uint32_t *fn, uint16_t qta,
 	uint8_t ms_class, uint8_t egprs_ms_class, gprs_rlcmac_ul_tbf *ul_tbf)
 {
@@ -1451,7 +1437,7 @@
 		data[5] = 0x0;
 		data[6] = 0x2b;
 		data[7] = 0x2b;
-		pdch = &the_bts->bts_data()->trx[trx_no].pdch[ts_no];
+		pdch = &bts->trx[trx_no].pdch[ts_no];
 		pdch->rcv_block(&data[0], sizeof(data), *fn, &meas);
 	}
 	ul_tbf->create_ul_ack(*fn, ts_no);
@@ -1476,7 +1462,7 @@
 	data[6] = 0x2b;
 	data[7] = 0x2b;
 
-	pdch = &the_bts->bts_data()->trx[trx_no].pdch[ts_no];
+	pdch = &bts->trx[trx_no].pdch[ts_no];
 	pdch->rcv_block(&data[0], sizeof(data), *fn, &meas);
 	ul_tbf->create_ul_ack(*fn, ts_no);
 
@@ -1485,7 +1471,7 @@
 	check_tbf(ul_tbf);
 	OSMO_ASSERT(ul_tbf->ul_ack_state_is(GPRS_RLCMAC_UL_ACK_NONE));
 
-	ms = the_bts->ms_by_tlli(tlli);
+	ms = bts_ms_by_tlli(bts, tlli, GSM_RESERVED_TMSI);
 	OSMO_ASSERT(ms != NULL);
 	OSMO_ASSERT(ms_ta(ms) == qta/4);
 	OSMO_ASSERT(ms_ul_tbf(ms) == ul_tbf);
@@ -1493,7 +1479,7 @@
 	return ul_tbf;
 }
 
-static gprs_rlcmac_ul_tbf *establish_ul_tbf_two_phase_puan_CRBB(BTS *the_bts,
+static gprs_rlcmac_ul_tbf *establish_ul_tbf_two_phase_puan_CRBB(struct gprs_rlcmac_bts *bts,
 	uint8_t ts_no, uint32_t tlli, uint32_t *fn, uint16_t qta,
 	uint8_t ms_class, uint8_t egprs_ms_class)
 {
@@ -1504,7 +1490,7 @@
 	struct gprs_rlcmac_pdch *pdch;
 
 	/* check the TBF */
-	ul_tbf = the_bts->ul_tbf_by_tfi(tfi, trx_no, ts_no);
+	ul_tbf = bts_ul_tbf_by_tfi(bts, tfi, trx_no, ts_no);
 	OSMO_ASSERT(ul_tbf);
 	OSMO_ASSERT(ul_tbf->ta() == qta / 4);
 
@@ -1537,7 +1523,7 @@
 		data[5] = 0x0;
 		data[6] = 0x2b;
 		data[7] = 0x2b;
-		pdch = &the_bts->bts_data()->trx[trx_no].pdch[ts_no];
+		pdch = &bts->trx[trx_no].pdch[ts_no];
 		pdch->rcv_block(&data[0], sizeof(data), *fn, &meas);
 	}
 	ul_tbf->create_ul_ack(*fn, ts_no);
@@ -1562,7 +1548,7 @@
 	data[6] = 0x2b;
 	data[7] = 0x2b;
 
-	pdch = &the_bts->bts_data()->trx[trx_no].pdch[ts_no];
+	pdch = &bts->trx[trx_no].pdch[ts_no];
 	pdch->rcv_block(&data[0], sizeof(data), *fn, &meas);
 
 	request_dl_rlc_block(ul_tbf, fn);
@@ -1570,14 +1556,14 @@
 	check_tbf(ul_tbf);
 	OSMO_ASSERT(ul_tbf->ul_ack_state_is(GPRS_RLCMAC_UL_ACK_NONE));
 
-	ms = the_bts->ms_by_tlli(tlli);
+	ms = bts_ms_by_tlli(bts, tlli, GSM_RESERVED_TMSI);
 	OSMO_ASSERT(ms != NULL);
 	OSMO_ASSERT(ms_ta(ms) == qta/4);
 	OSMO_ASSERT(ms_ul_tbf(ms) == ul_tbf);
 
 	return ul_tbf;
 }
-static gprs_rlcmac_ul_tbf *establish_ul_tbf_two_phase(BTS *the_bts,
+static gprs_rlcmac_ul_tbf *establish_ul_tbf_two_phase(struct gprs_rlcmac_bts *bts,
 	uint8_t ts_no, uint32_t tlli, uint32_t *fn, uint16_t qta,
 	uint8_t ms_class, uint8_t egprs_ms_class)
 {
@@ -1588,19 +1574,16 @@
 	int tfi = 0;
 	gprs_rlcmac_ul_tbf *ul_tbf;
 	struct gprs_rlcmac_pdch *pdch;
-	gprs_rlcmac_bts *bts;
 	RlcMacUplink_t ulreq = {0};
 
-	bts = the_bts->bts_data();
-
 	/* needed to set last_rts_fn in the PDCH object */
 	request_dl_rlc_block(bts, trx_no, ts_no, fn);
 
 	/* simulate RACH, sends an Immediate Assignment Uplink on the AGCH */
-	bts_handle_rach(the_bts, 0x73, rach_fn, qta);
+	bts_handle_rach(bts, 0x73, rach_fn, qta);
 
 	/* get next free TFI */
-	tfi = the_bts->tfi_find_free(GPRS_RLCMAC_UL_TBF, &trx_no, -1);
+	tfi = bts_tfi_find_free(bts, GPRS_RLCMAC_UL_TBF, &trx_no, -1);
 
 	/* fake a resource request */
 	ulreq.u.MESSAGE_TYPE = MT_PACKET_RESOURCE_REQUEST;
@@ -1627,10 +1610,10 @@
 			EGPRS_multislot_class = ms_class;
 	}
 
-	send_ul_mac_block(the_bts, trx_no, ts_no, &ulreq, sba_fn);
+	send_ul_mac_block(bts, trx_no, ts_no, &ulreq, sba_fn);
 
 	/* check the TBF */
-	ul_tbf = the_bts->ul_tbf_by_tfi(tfi, trx_no, ts_no);
+	ul_tbf = bts_ul_tbf_by_tfi(bts, tfi, trx_no, ts_no);
 	OSMO_ASSERT(ul_tbf != NULL);
 	OSMO_ASSERT(ul_tbf->ta() == qta / 4);
 
@@ -1650,10 +1633,10 @@
 		uint8_t(1), /* BSN:7, E:1 */
 	};
 
-	pdch = &the_bts->bts_data()->trx[trx_no].pdch[ts_no];
+	pdch = &bts->trx[trx_no].pdch[ts_no];
 	pdch->rcv_block(&data_msg[0], sizeof(data_msg), *fn, &meas);
 
-	ms = the_bts->ms_by_tlli(tlli);
+	ms = bts_ms_by_tlli(bts, tlli, GSM_RESERVED_TMSI);
 	OSMO_ASSERT(ms != NULL);
 	OSMO_ASSERT(ms_ta(ms) == qta/4);
 	OSMO_ASSERT(ms_ul_tbf(ms) == ul_tbf);
@@ -1661,34 +1644,34 @@
 	return ul_tbf;
 }
 
-static void send_dl_data(BTS *the_bts, uint32_t tlli, const char *imsi,
+static void send_dl_data(struct gprs_rlcmac_bts *bts, uint32_t tlli, const char *imsi,
 	const uint8_t *data, unsigned data_size)
 {
 	GprsMs *ms, *ms2;
 
-	ms = the_bts->ms_store().get_ms(tlli, 0, imsi);
+	ms = bts_ms_store(bts)->get_ms(tlli, 0, imsi);
 
-	gprs_rlcmac_dl_tbf::handle(the_bts->bts_data(), tlli, 0, imsi, 0, 0,
+	gprs_rlcmac_dl_tbf::handle(bts, tlli, 0, imsi, 0, 0,
 		1000, data, data_size);
 
-	ms = the_bts->ms_by_imsi(imsi);
+	ms = bts_ms_by_imsi(bts, imsi);
 	OSMO_ASSERT(ms != NULL);
 	OSMO_ASSERT(ms_dl_tbf(ms) != NULL);
 
 	if (imsi[0] && strcmp(imsi, "000") != 0) {
-		ms2 = the_bts->ms_by_tlli(tlli);
+		ms2 = bts_ms_by_tlli(bts, tlli, GSM_RESERVED_TMSI);
 		OSMO_ASSERT(ms == ms2);
 	}
 }
 
-static void transmit_dl_data(BTS *the_bts, uint32_t tlli, uint32_t *fn,
+static void transmit_dl_data(struct gprs_rlcmac_bts *bts, uint32_t tlli, uint32_t *fn,
 	uint8_t slots = 0xff)
 {
 	gprs_rlcmac_dl_tbf *dl_tbf;
 	GprsMs *ms;
 	unsigned ts_no;
 
-	ms = the_bts->ms_by_tlli(tlli);
+	ms = bts_ms_by_tlli(bts, tlli, GSM_RESERVED_TMSI);
 	OSMO_ASSERT(ms);
 	dl_tbf = ms_dl_tbf(ms);
 	OSMO_ASSERT(dl_tbf);
@@ -1698,7 +1681,7 @@
 		for (ts_no = 0 ; ts_no < 8; ts_no += 1) {
 			if (!(slots & (1 << ts_no)))
 				continue;
-			gprs_rlcmac_rcv_rts_block(the_bts->bts_data(),
+			gprs_rlcmac_rcv_rts_block(bts,
 				dl_tbf->trx->trx_no, ts_no,
 				*fn, bn);
 		}
@@ -1717,7 +1700,7 @@
 {
 	the_pcu = gprs_pcu_alloc(tall_pcu_ctx);
 	the_pcu->bts = bts_alloc(the_pcu);
-	BTS *the_bts = the_pcu->bts;
+	struct gprs_rlcmac_bts *bts = the_pcu->bts;
 	int ts_no = 7;
 	uint32_t fn = DUMMY_FN; /* 17,25,9 */
 	uint32_t tlli = 0xf1223344;
@@ -1727,12 +1710,12 @@
 
 	fprintf(stderr, "=== start %s ===\n", __func__);
 
-	setup_bts(the_bts, ts_no);
+	setup_bts(bts, ts_no);
 
-	ul_tbf = establish_ul_tbf_single_phase(the_bts, ts_no, tlli, &fn, qta);
+	ul_tbf = establish_ul_tbf_single_phase(bts, ts_no, tlli, &fn, qta);
 
 	print_ta_tlli(ul_tbf, true);
-	send_dl_data(the_bts, tlli, imsi, (const uint8_t *)"TEST", 4);
+	send_dl_data(bts, tlli, imsi, (const uint8_t *)"TEST", 4);
 
 	fprintf(stderr, "=== end %s ===\n", __func__);
 	TALLOC_FREE(the_pcu);
@@ -1742,7 +1725,7 @@
 {
 	the_pcu = gprs_pcu_alloc(tall_pcu_ctx);
 	the_pcu->bts = bts_alloc(the_pcu);
-	BTS *the_bts = the_pcu->bts;
+	struct gprs_rlcmac_bts *bts = the_pcu->bts;
 	int ts_no = 7;
 	uint32_t fn = 2654218;
 	uint16_t qta = 31;
@@ -1757,36 +1740,36 @@
 
 	memset(test_data, 1, sizeof(test_data));
 
-	setup_bts(the_bts, ts_no, 4);
-	the_bts->bts_data()->initial_mcs_dl = 9;
+	setup_bts(bts, ts_no, 4);
+	bts->initial_mcs_dl = 9;
 	the_pcu->vty.ws_base = 128;
 	the_pcu->vty.ws_pdch = 64;
 
-	ul_tbf = establish_ul_tbf(the_bts, ts_no, tlli, &fn, qta, ms_class, egprs_ms_class);
+	ul_tbf = establish_ul_tbf(bts, ts_no, tlli, &fn, qta, ms_class, egprs_ms_class);
 	/* Function to generate URBB with no length */
-	ul_tbf = establish_ul_tbf_two_phase_puan_URBB_no_length(the_bts, ts_no, tlli, &fn,
+	ul_tbf = establish_ul_tbf_two_phase_puan_URBB_no_length(bts, ts_no, tlli, &fn,
 		qta, ms_class, egprs_ms_class, ul_tbf);
 
 	print_ta_tlli(ul_tbf, true);
-	send_dl_data(the_bts, tlli, imsi, test_data, sizeof(test_data));
+	send_dl_data(bts, tlli, imsi, test_data, sizeof(test_data));
 
 	static_cast<gprs_rlc_ul_window *>(ul_tbf->window())->reset_state();
 	/* Function to generate URBB with length */
-	ul_tbf = establish_ul_tbf_two_phase_puan_URBB_with_length(the_bts, ts_no, tlli, &fn,
+	ul_tbf = establish_ul_tbf_two_phase_puan_URBB_with_length(bts, ts_no, tlli, &fn,
 		qta, ms_class, egprs_ms_class, ul_tbf);
 
 	print_ta_tlli(ul_tbf, true);
-	send_dl_data(the_bts, tlli, imsi, test_data, sizeof(test_data));
+	send_dl_data(bts, tlli, imsi, test_data, sizeof(test_data));
 
 	static_cast<gprs_rlc_ul_window *>(ul_tbf->window())->reset_state();
 	/* Function to generate CRBB */
 	the_pcu->vty.ws_base = 128;
 	the_pcu->vty.ws_pdch = 64;
-	ul_tbf = establish_ul_tbf_two_phase_puan_CRBB(the_bts, ts_no, tlli, &fn,
+	ul_tbf = establish_ul_tbf_two_phase_puan_CRBB(bts, ts_no, tlli, &fn,
 		qta, ms_class, egprs_ms_class);
 
 	print_ta_tlli(ul_tbf, true);
-	send_dl_data(the_bts, tlli, imsi, test_data, sizeof(test_data));
+	send_dl_data(bts, tlli, imsi, test_data, sizeof(test_data));
 
 	TALLOC_FREE(the_pcu);
 	fprintf(stderr, "=== end %s ===\n", __func__);
@@ -1798,16 +1781,16 @@
 {
 	the_pcu = gprs_pcu_alloc(tall_pcu_ctx);
 	the_pcu->bts = bts_alloc(the_pcu);
-	BTS *the_bts = the_pcu->bts;
+	struct gprs_rlcmac_bts *bts = the_pcu->bts;
 	uint32_t fn = 2654218;
 	uint16_t qta = 31;
 	int ts_no = 7;
 
 	fprintf(stderr, "=== start %s ===\n", __func__);
 
-	setup_bts(the_bts, ts_no, 4);
+	setup_bts(bts, ts_no, 4);
 
-	the_bts->bts_data()->trx[0].pdch[ts_no].disable();
+	bts->trx[0].pdch[ts_no].disable();
 
 	uint32_t rach_fn = fn - 51;
 
@@ -1817,7 +1800,7 @@
 	 * simulate RACH, sends an Immediate Assignment
 	 * Uplink reject on the AGCH
 	 */
-	rc = bts_handle_rach(the_bts, 0x70, rach_fn, qta);
+	rc = bts_handle_rach(bts, 0x70, rach_fn, qta);
 
 	OSMO_ASSERT(rc == -EINVAL);
 
@@ -1832,14 +1815,14 @@
 {
 	the_pcu = gprs_pcu_alloc(tall_pcu_ctx);
 	the_pcu->bts = bts_alloc(the_pcu);
-	BTS *the_bts = the_pcu->bts;
+	struct gprs_rlcmac_bts *bts = the_pcu->bts;
 	uint32_t fn = 2654218;
 	uint16_t qta = 31;
 	int ts_no = 7;
 
 	fprintf(stderr, "=== start %s ===\n", __func__);
 
-	setup_bts(the_bts, ts_no, 4);
+	setup_bts(bts, ts_no, 4);
 
 	uint32_t rach_fn = fn - 51;
 
@@ -1849,14 +1832,14 @@
 	 * simulate RACH, sends an Immediate Assignment Uplink
 	 * reject on the AGCH
 	 */
-	rc = bts_handle_rach(the_bts, 0x78, rach_fn, qta);
-	rc = bts_handle_rach(the_bts, 0x79, rach_fn, qta);
-	rc = bts_handle_rach(the_bts, 0x7a, rach_fn, qta);
-	rc = bts_handle_rach(the_bts, 0x7b, rach_fn, qta);
-	rc = bts_handle_rach(the_bts, 0x7c, rach_fn, qta);
-	rc = bts_handle_rach(the_bts, 0x7d, rach_fn, qta);
-	rc = bts_handle_rach(the_bts, 0x7e, rach_fn, qta);
-	rc = bts_handle_rach(the_bts, 0x7f, rach_fn, qta);
+	rc = bts_handle_rach(bts, 0x78, rach_fn, qta);
+	rc = bts_handle_rach(bts, 0x79, rach_fn, qta);
+	rc = bts_handle_rach(bts, 0x7a, rach_fn, qta);
+	rc = bts_handle_rach(bts, 0x7b, rach_fn, qta);
+	rc = bts_handle_rach(bts, 0x7c, rach_fn, qta);
+	rc = bts_handle_rach(bts, 0x7d, rach_fn, qta);
+	rc = bts_handle_rach(bts, 0x7e, rach_fn, qta);
+	rc = bts_handle_rach(bts, 0x7f, rach_fn, qta);
 
 	OSMO_ASSERT(rc == -EBUSY);
 
@@ -1874,7 +1857,7 @@
 {
 	the_pcu = gprs_pcu_alloc(tall_pcu_ctx);
 	the_pcu->bts = bts_alloc(the_pcu);
-	BTS *the_bts = the_pcu->bts;
+	struct gprs_rlcmac_bts *bts = the_pcu->bts;
 	int ts_no = 7;
 	uint32_t fn = 2654218;
 	uint16_t qta = 31;
@@ -1885,13 +1868,13 @@
 
 	fprintf(stderr, "=== start %s ===\n", __func__);
 
-	setup_bts(the_bts, ts_no, 4);
+	setup_bts(bts, ts_no, 4);
 
-	ul_tbf = establish_ul_tbf_two_phase(the_bts, ts_no, tlli, &fn, qta,
+	ul_tbf = establish_ul_tbf_two_phase(bts, ts_no, tlli, &fn, qta,
 		ms_class, 0);
 
 	print_ta_tlli(ul_tbf, true);
-	send_dl_data(the_bts, tlli, imsi, (const uint8_t *)"TEST", 4);
+	send_dl_data(bts, tlli, imsi, (const uint8_t *)"TEST", 4);
 
 	TALLOC_FREE(the_pcu);
 	fprintf(stderr, "=== end %s ===\n", __func__);
@@ -1907,7 +1890,7 @@
 {
 	the_pcu = gprs_pcu_alloc(tall_pcu_ctx);
 	the_pcu->bts = bts_alloc(the_pcu);
-	BTS *the_bts = the_pcu->bts;
+	struct gprs_rlcmac_bts *bts = the_pcu->bts;
 	int ts_no = 7;
 	uint32_t fn = 2654218;
 	uint16_t qta = 31;
@@ -1920,15 +1903,15 @@
 
 	fprintf(stderr, "=== start %s ===\n", __func__);
 
-	setup_bts(the_bts, ts_no, 4);
+	setup_bts(bts, ts_no, 4);
 
-	ul_tbf = establish_ul_tbf_two_phase(the_bts, ts_no, tlli1, &fn, qta,
+	ul_tbf = establish_ul_tbf_two_phase(bts, ts_no, tlli1, &fn, qta,
 		ms_class, 0);
 
 	ms1 = ul_tbf->ms();
 	print_ta_tlli(ul_tbf, false);
 
-	send_dl_data(the_bts, tlli1, imsi, (const uint8_t *)"RAU_ACCEPT", 10);
+	send_dl_data(bts, tlli1, imsi, (const uint8_t *)"RAU_ACCEPT", 10);
 	print_ms(ms1, true);
 
 	/* Send Packet Downlink Assignment to MS */
@@ -1939,11 +1922,11 @@
 
 	/* Make sure the RAU Accept gets sent to the MS */
 	OSMO_ASSERT(llc_queue_size(ms_llc_queue(ms1)) == 1);
-	transmit_dl_data(the_bts, tlli1, &fn);
+	transmit_dl_data(bts, tlli1, &fn);
 	OSMO_ASSERT(llc_queue_size(ms_llc_queue(ms1)) == 0);
 
 	/* Now establish a new TBF for the RA UPDATE COMPLETE (new TLLI) */
-	ul_tbf = establish_ul_tbf_two_phase(the_bts, ts_no, tlli2, &fn, qta,
+	ul_tbf = establish_ul_tbf_two_phase(bts, ts_no, tlli2, &fn, qta,
 		ms_class, 0);
 
 	ms2 = ul_tbf->ms();
@@ -1954,16 +1937,16 @@
 
 	/* Send some downlink data along with the new TLLI and the IMSI so that
 	 * the PCU can see, that both MS objects belong to same MS */
-	send_dl_data(the_bts, tlli2, imsi, (const uint8_t *)"DATA", 4);
+	send_dl_data(bts, tlli2, imsi, (const uint8_t *)"DATA", 4);
 
-	ms = the_bts->ms_by_imsi(imsi);
+	ms = bts_ms_by_imsi(bts, imsi);
 	OSMO_ASSERT(ms == ms2);
 
 	print_ms(ms2, false);
 
-	ms = the_bts->ms_by_tlli(tlli1);
+	ms = bts_ms_by_tlli(bts, tlli1, GSM_RESERVED_TMSI);
 	OSMO_ASSERT(ms == NULL);
-	ms = the_bts->ms_by_tlli(tlli2);
+	ms = bts_ms_by_tlli(bts, tlli2, GSM_RESERVED_TMSI);
 	OSMO_ASSERT(ms == ms2);
 
 	TALLOC_FREE(the_pcu);
@@ -1974,7 +1957,7 @@
 {
 	the_pcu = gprs_pcu_alloc(tall_pcu_ctx);
 	the_pcu->bts = bts_alloc(the_pcu);
-	BTS *the_bts = the_pcu->bts;
+	struct gprs_rlcmac_bts *bts = the_pcu->bts;
 	int ts_no = 7;
 	uint32_t fn = 2654218;
 	uint16_t qta = 31;
@@ -1987,16 +1970,16 @@
 
 	fprintf(stderr, "=== start %s ===\n", __func__);
 
-	setup_bts(the_bts, ts_no, 1);
+	setup_bts(bts, ts_no, 1);
 
-	ul_tbf = establish_ul_tbf_two_phase(the_bts, ts_no, tlli1, &fn, qta,
+	ul_tbf = establish_ul_tbf_two_phase(bts, ts_no, tlli1, &fn, qta,
 		ms_class, 0);
 
 	ms1 = ul_tbf->ms();
 	print_ta_tlli(ul_tbf, false);
 
-	send_dl_data(the_bts, tlli1, imsi, (const uint8_t *)"DATA 1 *************", 20);
-	send_dl_data(the_bts, tlli1, imsi, (const uint8_t *)"DATA 2 *************", 20);
+	send_dl_data(bts, tlli1, imsi, (const uint8_t *)"DATA 1 *************", 20);
+	send_dl_data(bts, tlli1, imsi, (const uint8_t *)"DATA 2 *************", 20);
 	print_ms(ms1, true);
 
 	OSMO_ASSERT(llc_queue_size(ms_llc_queue(ms1)) == 2);
@@ -2005,11 +1988,11 @@
 
 	/* Get rid of old UL TBF */
 	tbf_free(ul_tbf);
-	ms = the_bts->ms_by_tlli(tlli1);
+	ms = bts_ms_by_tlli(bts, tlli1, GSM_RESERVED_TMSI);
 	OSMO_ASSERT(ms1 == ms);
 
 	/* Now establish a new UL TBF, this will consume one LLC packet */
-	ul_tbf = establish_ul_tbf_two_phase(the_bts, ts_no, tlli1, &fn, qta,
+	ul_tbf = establish_ul_tbf_two_phase(bts, ts_no, tlli1, &fn, qta,
 		ms_class, 0);
 
 	ms2 = ul_tbf->ms();
@@ -2018,7 +2001,7 @@
 	/* This should be the same MS object */
 	OSMO_ASSERT(ms2 == ms1);
 
-	ms = the_bts->ms_by_tlli(tlli1);
+	ms = bts_ms_by_tlli(bts, tlli1, GSM_RESERVED_TMSI);
 	OSMO_ASSERT(ms2 == ms);
 
 	/* A DL TBF should still exist */
@@ -2036,7 +2019,7 @@
 {
 	the_pcu = gprs_pcu_alloc(tall_pcu_ctx);
 	the_pcu->bts = bts_alloc(the_pcu);
-	BTS *the_bts = the_pcu->bts;
+	struct gprs_rlcmac_bts *bts = the_pcu->bts;
 	int ts_no = 7;
 	uint32_t fn = 2654218;
 	uint16_t qta = 31;
@@ -2049,16 +2032,16 @@
 
 	fprintf(stderr, "=== start %s ===\n", __func__);
 
-	setup_bts(the_bts, ts_no, 1);
+	setup_bts(bts, ts_no, 1);
 
-	ul_tbf = establish_ul_tbf_two_phase(the_bts, ts_no, tlli1, &fn, qta,
+	ul_tbf = establish_ul_tbf_two_phase(bts, ts_no, tlli1, &fn, qta,
 		ms_class, 0);
 
 	ms1 = ul_tbf->ms();
 	print_ta_tlli(ul_tbf, false);
 
-	send_dl_data(the_bts, tlli1, imsi, (const uint8_t *)"DATA 1 *************", 20);
-	send_dl_data(the_bts, tlli1, imsi, (const uint8_t *)"DATA 2 *************", 20);
+	send_dl_data(bts, tlli1, imsi, (const uint8_t *)"DATA 1 *************", 20);
+	send_dl_data(bts, tlli1, imsi, (const uint8_t *)"DATA 2 *************", 20);
 	print_ms(ms1, true);
 
 	OSMO_ASSERT(llc_queue_size(ms_llc_queue(ms1)) == 2);
@@ -2067,11 +2050,11 @@
 
 	/* Get rid of old UL TBF */
 	tbf_free(ul_tbf);
-	ms = the_bts->ms_by_tlli(tlli1);
+	ms = bts_ms_by_tlli(bts, tlli1, GSM_RESERVED_TMSI);
 	OSMO_ASSERT(ms1 == ms);
 
 	/* Now establish a new UL TBF */
-	ul_tbf = establish_ul_tbf_single_phase(the_bts, ts_no, tlli1, &fn, qta);
+	ul_tbf = establish_ul_tbf_single_phase(bts, ts_no, tlli1, &fn, qta);
 
 	ms2 = ul_tbf->ms();
 	print_ms(ms2, false);
@@ -2079,7 +2062,7 @@
 	/* There should be a different MS object */
 	OSMO_ASSERT(ms2 != ms1);
 
-	ms = the_bts->ms_by_tlli(tlli1);
+	ms = bts_ms_by_tlli(bts, tlli1, GSM_RESERVED_TMSI);
 	OSMO_ASSERT(ms2 == ms);
 	OSMO_ASSERT(ms1 != ms);
 
@@ -2097,7 +2080,7 @@
 {
 	the_pcu = gprs_pcu_alloc(tall_pcu_ctx);
 	the_pcu->bts = bts_alloc(the_pcu);
-	BTS *the_bts = the_pcu->bts;
+	struct gprs_rlcmac_bts *bts = the_pcu->bts;
 	int ts_no = 7;
 	uint32_t fn = 2654218;
 	uint16_t qta = 31;
@@ -2112,9 +2095,9 @@
 
 	fprintf(stderr, "=== start %s ===\n", __func__);
 
-	setup_bts(the_bts, ts_no, 1);
+	setup_bts(bts, ts_no, 1);
 
-	ul_tbf = establish_ul_tbf_two_phase(the_bts, ts_no, tlli1, &fn, qta,
+	ul_tbf = establish_ul_tbf_two_phase(bts, ts_no, tlli1, &fn, qta,
 		ms_class, 0);
 
 	ms1 = ul_tbf->ms();
@@ -2128,7 +2111,7 @@
 		rc = snprintf(buf, sizeof(buf), "LLC PACKET %02i", i);
 		OSMO_ASSERT(rc > 0);
 
-		send_dl_data(the_bts, tlli1, imsi, (const uint8_t *)buf, rc);
+		send_dl_data(bts, tlli1, imsi, (const uint8_t *)buf, rc);
 	}
 
 	print_ms(ms1, true);
@@ -2140,7 +2123,7 @@
 	send_control_ack(ul_tbf);
 
 	/* Transmit all data */
-	transmit_dl_data(the_bts, tlli1, &fn);
+	transmit_dl_data(bts, tlli1, &fn);
 	OSMO_ASSERT(llc_queue_size(ms_llc_queue(ms1)) == 0);
 	OSMO_ASSERT(ms_dl_tbf(ms1));
 	OSMO_ASSERT(ms_dl_tbf(ms1)->state_is(GPRS_RLCMAC_FINISHED));
@@ -2155,7 +2138,7 @@
 		rc = snprintf(buf, sizeof(buf), "LLC PACKET %02i (TBF 2)", i);
 		OSMO_ASSERT(rc > 0);
 
-		send_dl_data(the_bts, tlli1, imsi, (const uint8_t *)buf, rc);
+		send_dl_data(bts, tlli1, imsi, (const uint8_t *)buf, rc);
 	}
 
 	/* Fake Final DL Ack/Nack */
@@ -2166,13 +2149,13 @@
 	ack->DOWNLINK_TFI = dl_tbf1->tfi();
 	ack->Ack_Nack_Description.FINAL_ACK_INDICATION = 1;
 
-	send_ul_mac_block(the_bts, 0, dl_tbf1->poll_ts, &ulreq, dl_tbf1->poll_fn);
+	send_ul_mac_block(bts, 0, dl_tbf1->poll_ts, &ulreq, dl_tbf1->poll_fn);
 
 	OSMO_ASSERT(dl_tbf1->state_is(GPRS_RLCMAC_WAIT_RELEASE));
 
 	request_dl_rlc_block(dl_tbf1, &fn);
 
-	ms2 = the_bts->ms_by_tlli(tlli1);
+	ms2 = bts_ms_by_tlli(bts, tlli1, GSM_RESERVED_TMSI);
 	OSMO_ASSERT(ms2 == ms1);
 	OSMO_ASSERT(ms_dl_tbf(ms2));
 	OSMO_ASSERT(ms_dl_tbf(ms2)->state_is(GPRS_RLCMAC_ASSIGN));
@@ -2185,7 +2168,7 @@
 	OSMO_ASSERT(dl_tbf2->state_is(GPRS_RLCMAC_FLOW));
 
 	/* Transmit all data */
-	transmit_dl_data(the_bts, tlli1, &fn);
+	transmit_dl_data(bts, tlli1, &fn);
 	OSMO_ASSERT(llc_queue_size(ms_llc_queue(ms2)) == 0);
 	OSMO_ASSERT(ms_dl_tbf(ms2));
 	OSMO_ASSERT(ms_dl_tbf(ms2)->state_is(GPRS_RLCMAC_FINISHED));
@@ -2198,8 +2181,7 @@
 {
 	the_pcu = gprs_pcu_alloc(tall_pcu_ctx);
 	the_pcu->bts = bts_alloc(the_pcu);
-	BTS *the_bts = the_pcu->bts;
-	gprs_rlcmac_bts *bts;
+	struct gprs_rlcmac_bts *bts = the_pcu->bts;
 	uint8_t ts_no = 4;
 	uint8_t ms_class = 45;
 	int rc = 0;
@@ -2211,14 +2193,13 @@
 
 	fprintf(stderr, "=== start %s ===\n", __func__);
 
-	bts = the_bts->bts_data();
-	the_bts->pcu->nsi = gprs_ns2_instantiate(tall_pcu_ctx, gprs_ns_prim_cb, NULL);
-	if (!the_bts->pcu->nsi) {
+	bts->pcu->nsi = gprs_ns2_instantiate(tall_pcu_ctx, gprs_ns_prim_cb, NULL);
+	if (!bts->pcu->nsi) {
 		LOGP(DBSSGP, LOGL_ERROR, "Failed to create NS instance\n");
 		abort();
 	}
 
-	setup_bts(the_bts, ts_no);
+	setup_bts(bts, ts_no);
 
 	/* EGPRS-only */
 
@@ -2238,7 +2219,7 @@
 static inline void ws_check(gprs_rlcmac_dl_tbf *dl_tbf, const char *test, uint8_t exp_slots, uint16_t exp_ws,
 			    bool free, bool end)
 {
-	gprs_rlcmac_bts *bts = dl_tbf->bts->bts_data();
+	gprs_rlcmac_bts *bts = dl_tbf->bts;
 	if (!dl_tbf) {
 		fprintf(stderr, "%s(): FAILED (NULL TBF)\n", test);
 		return;
@@ -2268,8 +2249,7 @@
 {
 	the_pcu = gprs_pcu_alloc(tall_pcu_ctx);
 	the_pcu->bts = bts_alloc(the_pcu);
-	BTS *the_bts = the_pcu->bts;
-	gprs_rlcmac_bts *bts;
+	struct gprs_rlcmac_bts *bts = the_pcu->bts;
 	GprsMs *ms;
 	uint8_t ts_no = 4;
 	uint8_t ms_class = 12;
@@ -2277,14 +2257,13 @@
 
 	fprintf(stderr, "=== start %s ===\n", __func__);
 
-	bts = the_bts->bts_data();
-	the_bts->pcu->nsi = gprs_ns2_instantiate(tall_pcu_ctx, gprs_ns_prim_cb, NULL);
-	if (!the_bts->pcu->nsi) {
+	bts->pcu->nsi = gprs_ns2_instantiate(tall_pcu_ctx, gprs_ns_prim_cb, NULL);
+	if (!bts->pcu->nsi) {
 		LOGP(DBSSGP, LOGL_ERROR, "Failed to create NS instance\n");
 		abort();
 	}
 
-	setup_bts(the_bts, ts_no);
+	setup_bts(bts, ts_no);
 
 	the_pcu->vty.ws_base = 128;
 	the_pcu->vty.ws_pdch = 64;
@@ -2297,7 +2276,7 @@
 	gprs_bssgp_init(bts, 4234, 4234, 1, 1, false, 0, 0, 0);
 
 	/* Does no support EGPRS */
-	ms = the_bts->ms_alloc(ms_class, 0);
+	ms = bts_alloc_ms(bts, ms_class, 0);
 	dl_tbf = tbf_alloc_dl_tbf(bts, ms, 0, false);
 
 	ws_check(dl_tbf, __func__, 4, 64, true, false);
@@ -2305,7 +2284,7 @@
 	/* EGPRS-only */
 
 	/* Does support EGPRS */
-	ms = the_bts->ms_alloc(ms_class, ms_class);
+	ms = bts_alloc_ms(bts, ms_class, ms_class);
 	dl_tbf = tbf_alloc_dl_tbf(bts, ms, 0, false);
 
 	ws_check(dl_tbf, __func__, 4, 128 + 4 * 64, true, true);
@@ -2316,8 +2295,7 @@
 {
 	the_pcu = gprs_pcu_alloc(tall_pcu_ctx);
 	the_pcu->bts = bts_alloc(the_pcu);
-	BTS *the_bts = the_pcu->bts;
-	gprs_rlcmac_bts *bts;
+	struct gprs_rlcmac_bts *bts = the_pcu->bts;
 	GprsMs *ms;
 	uint8_t ts_no = 4;
 	uint8_t ms_class = 11;
@@ -2325,14 +2303,13 @@
 
 	fprintf(stderr, "=== start %s ===\n", __func__);
 
-	bts = the_bts->bts_data();
-	the_bts->pcu->nsi = gprs_ns2_instantiate(tall_pcu_ctx, gprs_ns_prim_cb, NULL);
-	if (!the_bts->pcu->nsi) {
+	bts->pcu->nsi = gprs_ns2_instantiate(tall_pcu_ctx, gprs_ns_prim_cb, NULL);
+	if (!bts->pcu->nsi) {
 		LOGP(DBSSGP, LOGL_ERROR, "Failed to create NS instance\n");
 		abort();
 	}
 
-	setup_bts(the_bts, ts_no);
+	setup_bts(bts, ts_no);
 
 	the_pcu->vty.ws_base = 128;
 	the_pcu->vty.ws_pdch = 64;
@@ -2347,7 +2324,7 @@
 	/* EGPRS-only */
 
 	/* Does support EGPRS */
-	ms = the_bts->ms_alloc(ms_class, ms_class);
+	ms = bts_alloc_ms(bts, ms_class, ms_class);
 	dl_tbf = tbf_alloc_dl_tbf(bts, ms, 0, true);
 
 	ws_check(dl_tbf, __func__, 1, 128 + 1 * 64, false, false);
@@ -2363,7 +2340,7 @@
 {
 	the_pcu = gprs_pcu_alloc(tall_pcu_ctx);
 	the_pcu->bts = bts_alloc(the_pcu);
-	BTS *the_bts = the_pcu->bts;
+	struct gprs_rlcmac_bts *bts = the_pcu->bts;
 	int ts_no = 7;
 	uint32_t fn = 2654218;
 	uint16_t qta = 31;
@@ -2378,20 +2355,20 @@
 
 	memset(test_data, 1, sizeof(test_data));
 
-	setup_bts(the_bts, ts_no, 4);
-	the_bts->bts_data()->initial_mcs_dl = 9;
+	setup_bts(bts, ts_no, 4);
+	bts->initial_mcs_dl = 9;
 
-	ul_tbf = puan_urbb_len_issue(the_bts, ts_no, tlli, &fn, qta,
+	ul_tbf = puan_urbb_len_issue(bts, ts_no, tlli, &fn, qta,
 		ms_class, egprs_ms_class);
 
 	print_ta_tlli(ul_tbf, true);
-	send_dl_data(the_bts, tlli, imsi, test_data, sizeof(test_data));
+	send_dl_data(bts, tlli, imsi, test_data, sizeof(test_data));
 
 	TALLOC_FREE(the_pcu);
 	fprintf(stderr, "=== end %s ===\n", __func__);
 }
 
-static gprs_rlcmac_ul_tbf *tbf_li_decoding(BTS *the_bts,
+static gprs_rlcmac_ul_tbf *tbf_li_decoding(struct gprs_rlcmac_bts *bts,
 	uint8_t ts_no, uint32_t tlli, uint32_t *fn, uint16_t qta,
 	uint8_t ms_class, uint8_t egprs_ms_class)
 {
@@ -2402,15 +2379,12 @@
 	int tfi = 0;
 	gprs_rlcmac_ul_tbf *ul_tbf;
 	struct gprs_rlcmac_pdch *pdch;
-	gprs_rlcmac_bts *bts;
 	RlcMacUplink_t ulreq = {0};
 	struct gprs_rlc_ul_header_egprs_3 *egprs3  = NULL;
 	Packet_Resource_Request_t *presreq = NULL;
 	MS_Radio_Access_capability_t *pmsradiocap = NULL;
 	Multislot_capability_t *pmultislotcap = NULL;
 
-	bts = the_bts->bts_data();
-
 	/* needed to set last_rts_fn in the PDCH object */
 	request_dl_rlc_block(bts, trx_no, ts_no, fn);
 
@@ -2418,10 +2392,10 @@
 	 * simulate RACH, this sends an Immediate
 	 * Assignment Uplink on the AGCH
 	 */
-	bts_handle_rach(the_bts, 0x73, rach_fn, qta);
+	bts_handle_rach(bts, 0x73, rach_fn, qta);
 
 	/* get next free TFI */
-	tfi = the_bts->tfi_find_free(GPRS_RLCMAC_UL_TBF, &trx_no, -1);
+	tfi = bts_tfi_find_free(bts, GPRS_RLCMAC_UL_TBF, &trx_no, -1);
 
 	/* fake a resource request */
 	ulreq.u.MESSAGE_TYPE = MT_PACKET_RESOURCE_REQUEST;
@@ -2444,10 +2418,10 @@
 		pmultislotcap->EGPRS_multislot_class = ms_class;
 	}
 
-	send_ul_mac_block(the_bts, trx_no, ts_no, &ulreq, sba_fn);
+	send_ul_mac_block(bts, trx_no, ts_no, &ulreq, sba_fn);
 
 	/* check the TBF */
-	ul_tbf = the_bts->ul_tbf_by_tfi(tfi, trx_no, ts_no);
+	ul_tbf = bts_ul_tbf_by_tfi(bts, tfi, trx_no, ts_no);
 	OSMO_ASSERT(ul_tbf);
 	OSMO_ASSERT(ul_tbf->ta() == qta / 4);
 
@@ -2462,9 +2436,9 @@
 
 	uint8_t data_msg[49] = {0};
 
-	pdch = &the_bts->bts_data()->trx[trx_no].pdch[ts_no];
+	pdch = &bts->trx[trx_no].pdch[ts_no];
 
-	ms = the_bts->ms_by_tlli(tlli);
+	ms = bts_ms_by_tlli(bts, tlli, GSM_RESERVED_TMSI);
 	OSMO_ASSERT(ms != NULL);
 	OSMO_ASSERT(ms_ta(ms) == qta/4);
 	OSMO_ASSERT(ms_ul_tbf(ms) == ul_tbf);
@@ -2506,7 +2480,7 @@
 {
 	the_pcu = gprs_pcu_alloc(tall_pcu_ctx);
 	the_pcu->bts = bts_alloc(the_pcu);
-	BTS *the_bts = the_pcu->bts;
+	struct gprs_rlcmac_bts *bts = the_pcu->bts;
 	int ts_no = 7;
 	uint32_t fn = 2654218;
 	uint16_t qta = 31;
@@ -2521,14 +2495,14 @@
 
 	memset(test_data, 1, sizeof(test_data));
 
-	setup_bts(the_bts, ts_no, 4);
-	the_bts->bts_data()->initial_mcs_dl = 9;
+	setup_bts(bts, ts_no, 4);
+	bts->initial_mcs_dl = 9;
 
-	ul_tbf = tbf_li_decoding(the_bts, ts_no, tlli, &fn, qta,
+	ul_tbf = tbf_li_decoding(bts, ts_no, tlli, &fn, qta,
 		ms_class, egprs_ms_class);
 
 	print_ta_tlli(ul_tbf, true);
-	send_dl_data(the_bts, tlli, imsi, test_data, sizeof(test_data));
+	send_dl_data(bts, tlli, imsi, test_data, sizeof(test_data));
 
 	TALLOC_FREE(the_pcu);
 	fprintf(stderr, "=== end %s ===\n", __func__);
@@ -2543,7 +2517,7 @@
 {
 	the_pcu = gprs_pcu_alloc(tall_pcu_ctx);
 	the_pcu->bts = bts_alloc(the_pcu);
-	BTS *the_bts = the_pcu->bts;
+	struct gprs_rlcmac_bts *bts = the_pcu->bts;
 	uint8_t ms_class = 11;
 	uint8_t egprs_ms_class = 11;
 	uint8_t trx_no;
@@ -2564,7 +2538,7 @@
 
 	fprintf(stderr, "=== start %s ===\n", __func__);
 
-	setup_bts(the_bts, ts_no);
+	setup_bts(bts, ts_no);
 	OSMO_ASSERT(osmo_tdef_set(the_pcu->T_defs, -2031, 200, OSMO_TDEF_MS) == 0);
 	/* ARQ II */
 	the_pcu->vty.dl_arq_type = EGPRS_ARQ2;
@@ -2579,7 +2553,7 @@
 				0xff, 0xff, 0xfb, 0x80, 0x00, 0x00,
 				0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
 
-	dl_tbf = create_dl_tbf(the_bts, ms_class, egprs_ms_class, &trx_no);
+	dl_tbf = create_dl_tbf(bts, ms_class, egprs_ms_class, &trx_no);
 	dl_tbf->update_ms(tlli, GPRS_RLCMAC_DL_TBF);
 	prlcdlwindow = static_cast<gprs_rlc_dl_window *>(dl_tbf->window());
 	prlcmvb = &prlcdlwindow->m_v_b;
@@ -2635,7 +2609,7 @@
 {
 	the_pcu = gprs_pcu_alloc(tall_pcu_ctx);
 	the_pcu->bts = bts_alloc(the_pcu);
-	BTS *the_bts = the_pcu->bts;
+	struct gprs_rlcmac_bts *bts = the_pcu->bts;
 	int ts_no = 7;
 	uint32_t fn = 2654218;
 	uint16_t qta = 31;
@@ -2650,14 +2624,14 @@
 
 	memset(test_data, 1, sizeof(test_data));
 
-	setup_bts(the_bts, ts_no, 4);
-	the_bts->bts_data()->initial_mcs_dl = 9;
+	setup_bts(bts, ts_no, 4);
+	bts->initial_mcs_dl = 9;
 
-	ul_tbf = establish_ul_tbf_two_phase_spb(the_bts, ts_no, tlli, &fn, qta,
+	ul_tbf = establish_ul_tbf_two_phase_spb(bts, ts_no, tlli, &fn, qta,
 		ms_class, egprs_ms_class);
 
 	print_ta_tlli(ul_tbf, true);
-	send_dl_data(the_bts, tlli, imsi, test_data, sizeof(test_data));
+	send_dl_data(bts, tlli, imsi, test_data, sizeof(test_data));
 
 	TALLOC_FREE(the_pcu);
 	fprintf(stderr, "=== end %s ===\n", __func__);
@@ -2667,7 +2641,7 @@
 {
 	the_pcu = gprs_pcu_alloc(tall_pcu_ctx);
 	the_pcu->bts = bts_alloc(the_pcu);
-	BTS *the_bts = the_pcu->bts;
+	struct gprs_rlcmac_bts *bts = the_pcu->bts;
 	int ts_no = 7;
 	uint32_t fn = 2654218;
 	uint16_t qta = 31;
@@ -2682,20 +2656,20 @@
 
 	memset(test_data, 1, sizeof(test_data));
 
-	setup_bts(the_bts, ts_no, 4);
-	the_bts->bts_data()->initial_mcs_dl = 9;
+	setup_bts(bts, ts_no, 4);
+	bts->initial_mcs_dl = 9;
 
-	ul_tbf = establish_ul_tbf_two_phase(the_bts, ts_no, tlli, &fn, qta,
+	ul_tbf = establish_ul_tbf_two_phase(bts, ts_no, tlli, &fn, qta,
 		ms_class, egprs_ms_class);
 
 	print_ta_tlli(ul_tbf, true);
-	send_dl_data(the_bts, tlli, imsi, test_data, sizeof(test_data));
+	send_dl_data(bts, tlli, imsi, test_data, sizeof(test_data));
 
 	TALLOC_FREE(the_pcu);
 	fprintf(stderr, "=== end %s ===\n", __func__);
 }
 
-static void establish_and_use_egprs_dl_tbf(BTS *the_bts, int mcs)
+static void establish_and_use_egprs_dl_tbf(struct gprs_rlcmac_bts *bts, int mcs)
 {
 	unsigned i;
 	uint8_t ms_class = 11;
@@ -2712,9 +2686,9 @@
 	fprintf(stderr, "Testing MCS-%d\n", mcs);
 
 	memset(test_data, 1, sizeof(test_data));
-	the_bts->bts_data()->initial_mcs_dl = mcs;
+	bts->initial_mcs_dl = mcs;
 
-	dl_tbf = create_dl_tbf(the_bts, ms_class, egprs_ms_class, &trx_no);
+	dl_tbf = create_dl_tbf(bts, ms_class, egprs_ms_class, &trx_no);
 	dl_tbf->update_ms(tlli, GPRS_RLCMAC_DL_TBF);
 
 	for (i = 0; i < sizeof(llc_data); i++)
@@ -2753,7 +2727,7 @@
 	tbf_free(dl_tbf);
 }
 
-static gprs_rlcmac_dl_tbf *tbf_init(BTS *the_bts,
+static gprs_rlcmac_dl_tbf *tbf_init(struct gprs_rlcmac_bts *bts,
 		int mcs)
 {
 	unsigned i;
@@ -2766,9 +2740,9 @@
 	gprs_rlcmac_dl_tbf *dl_tbf;
 
 	memset(test_data, 1, sizeof(test_data));
-	the_bts->bts_data()->initial_mcs_dl = mcs;
+	bts->initial_mcs_dl = mcs;
 
-	dl_tbf = create_dl_tbf(the_bts, ms_class, egprs_ms_class, &trx_no);
+	dl_tbf = create_dl_tbf(bts, ms_class, egprs_ms_class, &trx_no);
 	dl_tbf->update_ms(tlli, GPRS_RLCMAC_DL_TBF);
 
 	for (i = 0; i < sizeof(test_data); i++)
@@ -2830,7 +2804,7 @@
 			CHECK_NACKED(tbf, cs, 0);			\
 	} while(0)
 
-static void egprs_spb_to_normal_validation(BTS *the_bts,
+static void egprs_spb_to_normal_validation(struct gprs_rlcmac_bts *bts,
 		unsigned int mcs, unsigned int demanded_mcs)
 {
 	uint32_t fn = 0;
@@ -2842,7 +2816,7 @@
 
 	fprintf(stderr, "Testing retx for MCS %u to reseg_mcs %u\n", mcs, demanded_mcs);
 
-	dl_tbf = tbf_init(the_bts, mcs);
+	dl_tbf = tbf_init(bts, mcs);
 
 	/*
 	 * Table 10.4.8a.3.1 of 44.060.
@@ -2908,7 +2882,7 @@
 	tbf_cleanup(dl_tbf);
 }
 
-static void establish_and_use_egprs_dl_tbf_for_spb(BTS *the_bts,
+static void establish_and_use_egprs_dl_tbf_for_spb(struct gprs_rlcmac_bts *bts,
 		unsigned int mcs, unsigned int demanded_mcs)
 {
 	uint32_t fn = 0;
@@ -2918,7 +2892,7 @@
 
 	fprintf(stderr, "Testing retx for MCS %u to reseg_mcs %u\n", mcs, demanded_mcs);
 
-	dl_tbf = tbf_init(the_bts, mcs);
+	dl_tbf = tbf_init(bts, mcs);
 
 	/*
 	 * Table 10.4.8a.3.1 of 44.060.
@@ -2995,7 +2969,7 @@
 	tbf_cleanup(dl_tbf);
 }
 
-static void establish_and_use_egprs_dl_tbf_for_retx(BTS *the_bts,
+static void establish_and_use_egprs_dl_tbf_for_retx(struct gprs_rlcmac_bts *bts,
 		unsigned int mcs, unsigned int demanded_mcs)
 {
 	uint32_t fn = 0;
@@ -3004,7 +2978,7 @@
 
 	fprintf(stderr, "Testing retx for MCS %u - %u\n", mcs, demanded_mcs);
 
-	dl_tbf = tbf_init(the_bts, mcs);
+	dl_tbf = tbf_init(bts, mcs);
 
 	/* For MCS reduction cases like MCS9->MCS6, MCS7->MCS5
 	 * The MCS transition are referred from table Table 8.1.1.2
@@ -3091,26 +3065,26 @@
 {
 	the_pcu = gprs_pcu_alloc(tall_pcu_ctx);
 	the_pcu->bts = bts_alloc(the_pcu);
-	BTS *the_bts = the_pcu->bts;
+	struct gprs_rlcmac_bts *bts = the_pcu->bts;
 	uint8_t ts_no = 4;
 
 	fprintf(stderr, "=== start %s ===\n", __func__);
 
 	the_pcu->vty.cs_downgrade_threshold = 0;
-	setup_bts(the_bts, ts_no);
+	setup_bts(bts, ts_no);
 	OSMO_ASSERT(osmo_tdef_set(the_pcu->T_defs, -2031, 200, OSMO_TDEF_MS) == 0);
 	/* ARQ II */
 	the_pcu->vty.dl_arq_type = EGPRS_ARQ2;
 
 
 	/* First parameter is current MCS, second one is demanded_mcs */
-	establish_and_use_egprs_dl_tbf_for_retx(the_bts, 6, 6);
-	establish_and_use_egprs_dl_tbf_for_retx(the_bts, 1, 9);
-	establish_and_use_egprs_dl_tbf_for_retx(the_bts, 2, 8);
-	establish_and_use_egprs_dl_tbf_for_retx(the_bts, 5, 7);
-	establish_and_use_egprs_dl_tbf_for_retx(the_bts, 6, 9);
-	establish_and_use_egprs_dl_tbf_for_retx(the_bts, 7, 5);
-	establish_and_use_egprs_dl_tbf_for_retx(the_bts, 9, 6);
+	establish_and_use_egprs_dl_tbf_for_retx(bts, 6, 6);
+	establish_and_use_egprs_dl_tbf_for_retx(bts, 1, 9);
+	establish_and_use_egprs_dl_tbf_for_retx(bts, 2, 8);
+	establish_and_use_egprs_dl_tbf_for_retx(bts, 5, 7);
+	establish_and_use_egprs_dl_tbf_for_retx(bts, 6, 9);
+	establish_and_use_egprs_dl_tbf_for_retx(bts, 7, 5);
+	establish_and_use_egprs_dl_tbf_for_retx(bts, 9, 6);
 
 	TALLOC_FREE(the_pcu);
 	fprintf(stderr, "=== end %s ===\n", __func__);
@@ -3120,13 +3094,13 @@
 {
 	the_pcu = gprs_pcu_alloc(tall_pcu_ctx);
 	the_pcu->bts = bts_alloc(the_pcu);
-	BTS *the_bts = the_pcu->bts;
+	struct gprs_rlcmac_bts *bts = the_pcu->bts;
 	uint8_t ts_no = 4;
 
 	fprintf(stderr, "=== start %s ===\n", __func__);
 
 	the_pcu->vty.cs_downgrade_threshold = 0;
-	setup_bts(the_bts, ts_no);
+	setup_bts(bts, ts_no);
 	OSMO_ASSERT(osmo_tdef_set(the_pcu->T_defs, -2031, 200, OSMO_TDEF_MS) == 0);
 
 	/* ARQ I resegmentation support */
@@ -3137,11 +3111,11 @@
 	 * currently only MCS5->MCS2, MCS6->3, MCS4->MCS1 is tested in UT
 	 * rest scenarios has been integration tested
 	 */
-	establish_and_use_egprs_dl_tbf_for_spb(the_bts, 6, 3);
-	establish_and_use_egprs_dl_tbf_for_spb(the_bts, 5, 2);
-	establish_and_use_egprs_dl_tbf_for_spb(the_bts, 4, 1);
+	establish_and_use_egprs_dl_tbf_for_spb(bts, 6, 3);
+	establish_and_use_egprs_dl_tbf_for_spb(bts, 5, 2);
+	establish_and_use_egprs_dl_tbf_for_spb(bts, 4, 1);
 	/* check MCS6->(MCS3+MCS3)->MCS6 case */
-	egprs_spb_to_normal_validation(the_bts, 6, 3);
+	egprs_spb_to_normal_validation(bts, 6, 3);
 
 	TALLOC_FREE(the_pcu);
 	fprintf(stderr, "=== end %s ===\n", __func__);
@@ -3151,19 +3125,19 @@
 {
 	the_pcu = gprs_pcu_alloc(tall_pcu_ctx);
 	the_pcu->bts = bts_alloc(the_pcu);
-	BTS *the_bts = the_pcu->bts;
+	struct gprs_rlcmac_bts *bts = the_pcu->bts;
 	uint8_t ts_no = 4;
 	int i;
 
 	fprintf(stderr, "=== start %s ===\n", __func__);
 
-	setup_bts(the_bts, ts_no);
+	setup_bts(bts, ts_no);
 	OSMO_ASSERT(osmo_tdef_set(the_pcu->T_defs, -2031, 200, OSMO_TDEF_MS) == 0);
 	/* ARQ II */
 	the_pcu->vty.dl_arq_type = EGPRS_ARQ2;
 
 	for (i = 1; i <= 9; i++)
-		establish_and_use_egprs_dl_tbf(the_bts, i);
+		establish_and_use_egprs_dl_tbf(bts, i);
 
 	TALLOC_FREE(the_pcu);
 	fprintf(stderr, "=== end %s ===\n", __func__);
@@ -3175,7 +3149,7 @@
 {
 	the_pcu = gprs_pcu_alloc(tall_pcu_ctx);
 	the_pcu->bts = bts_alloc(the_pcu);
-	BTS *the_bts = the_pcu->bts;
+	struct gprs_rlcmac_bts *bts = the_pcu->bts;
 	uint32_t fn = 2654218;
 	int ts_no = 7;
 	uint8_t trx_no = 0;
@@ -3184,11 +3158,11 @@
 
 	fprintf(stderr, "=== start %s ===\n", __func__);
 
-	setup_bts(the_bts, ts_no, 4);
+	setup_bts(bts, ts_no, 4);
 
 	int rc = 0;
 
-	ul_tbf = handle_tbf_reject(the_bts->bts_data(), NULL, tlli,
+	ul_tbf = handle_tbf_reject(bts, NULL, tlli,
 				trx_no, ts_no);
 
 	OSMO_ASSERT(ul_tbf != 0);
@@ -3196,7 +3170,7 @@
 	/* trigger packet access reject */
 	uint8_t bn = fn2bn(fn);
 
-	rc = gprs_rlcmac_rcv_rts_block(the_bts->bts_data(),
+	rc = gprs_rlcmac_rcv_rts_block(bts,
 		trx_no, ts_no, fn, bn);
 
 	OSMO_ASSERT(rc == 0);
@@ -3211,7 +3185,7 @@
 {
 	the_pcu = gprs_pcu_alloc(tall_pcu_ctx);
 	the_pcu->bts = bts_alloc(the_pcu);
-	BTS *the_bts = the_pcu->bts;
+	struct gprs_rlcmac_bts *bts = the_pcu->bts;
 	uint32_t fn = 2654218;
 	uint16_t qta = 31;
 	int ts_no = 7;
@@ -3228,20 +3202,20 @@
 
 	fprintf(stderr, "=== start %s ===\n", __func__);
 
-	setup_bts(the_bts, ts_no, 4);
+	setup_bts(bts, ts_no, 4);
 
 	int rc = 0;
 
 	/*
 	 * Trigger rach till resources(USF) exhaust
 	 */
-	rc = bts_handle_rach(the_bts, 0x78, rach_fn, qta);
-	rc = bts_handle_rach(the_bts, 0x79, rach_fn, qta);
-	rc = bts_handle_rach(the_bts, 0x7a, rach_fn, qta);
-	rc = bts_handle_rach(the_bts, 0x7b, rach_fn, qta);
-	rc = bts_handle_rach(the_bts, 0x7c, rach_fn, qta);
-	rc = bts_handle_rach(the_bts, 0x7d, rach_fn, qta);
-	rc = bts_handle_rach(the_bts, 0x7e, rach_fn, qta);
+	rc = bts_handle_rach(bts, 0x78, rach_fn, qta);
+	rc = bts_handle_rach(bts, 0x79, rach_fn, qta);
+	rc = bts_handle_rach(bts, 0x7a, rach_fn, qta);
+	rc = bts_handle_rach(bts, 0x7b, rach_fn, qta);
+	rc = bts_handle_rach(bts, 0x7c, rach_fn, qta);
+	rc = bts_handle_rach(bts, 0x7d, rach_fn, qta);
+	rc = bts_handle_rach(bts, 0x7e, rach_fn, qta);
 
 	/* fake a resource request */
 	ulreq.u.MESSAGE_TYPE = MT_PACKET_RESOURCE_REQUEST;
@@ -3264,12 +3238,12 @@
 		pmultislotcap->EGPRS_multislot_class = egprs_ms_class;
 	}
 
-	send_ul_mac_block(the_bts, trx_no, ts_no, &ulreq, sba_fn);
+	send_ul_mac_block(bts, trx_no, ts_no, &ulreq, sba_fn);
 
 	/* trigger packet access reject */
 	uint8_t bn = fn2bn(fn);
 
-	rc = gprs_rlcmac_rcv_rts_block(the_bts->bts_data(),
+	rc = gprs_rlcmac_rcv_rts_block(bts,
 		trx_no, ts_no, fn, bn);
 
 	OSMO_ASSERT(rc == 0);
@@ -3282,7 +3256,7 @@
 {
 	the_pcu = gprs_pcu_alloc(tall_pcu_ctx);
 	the_pcu->bts = bts_alloc(the_pcu);
-	BTS *the_bts = the_pcu->bts;
+	struct gprs_rlcmac_bts *bts = the_pcu->bts;
 	uint32_t tlli = 0xffeeddcc;
 	static uint8_t exp[] = { 0x40, 0x84, 0x7f, 0xf7, 0x6e, 0xe6, 0x41, 0x4b,
 				 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
@@ -3290,8 +3264,8 @@
 	};
 
 	fprintf(stderr, "=== start %s ===\n", __func__);
-	setup_bts(the_bts, 4);
-	static gprs_rlcmac_dl_tbf *dl_tbf = tbf_init(the_bts, 1);
+	setup_bts(bts, 4);
+	static gprs_rlcmac_dl_tbf *dl_tbf = tbf_init(bts, 1);
 
 	dl_tbf->update_ms(tlli, GPRS_RLCMAC_DL_TBF);
 
diff --git a/tests/types/TypesTest.cpp b/tests/types/TypesTest.cpp
index 7e5d35a..2da6a64 100644
--- a/tests/types/TypesTest.cpp
+++ b/tests/types/TypesTest.cpp
@@ -23,6 +23,7 @@
 #include "bts.h"
 #include "tbf.h"
 #include "tbf_ul.h"
+#include "tbf_dl.h"
 #include "pcu_utils.h"
 #include "gprs_debug.h"
 #include "encoding.h"
@@ -352,7 +353,7 @@
 		uint16_t lost = 0, recv = 0;
 		char show_rbb[65];
 		uint8_t bits_data[8];
-		BTS dummy_bts(the_pcu);
+		struct gprs_rlcmac_bts *dummy_bts = bts_alloc(the_pcu);
 		gprs_rlc_dl_window dl_win;
 		bitvec bits;
 		int bsn_begin, bsn_end, num_blocks;
@@ -391,7 +392,7 @@
 		Decoding::extract_rbb(&bits, show_rbb);
 		printf("show_rbb: %s\n", show_rbb);
 
-		dl_win.update(&dummy_bts, &bits, 0, &lost, &recv);
+		dl_win.update(dummy_bts, &bits, 0, &lost, &recv);
 		OSMO_ASSERT(lost == 0);
 		OSMO_ASSERT(recv == 35);
 		OSMO_ASSERT(bsn_begin == 0);
@@ -423,7 +424,7 @@
 		printf("show_rbb: %s\n", show_rbb);
 
 		lost = recv = 0;
-		dl_win.update(&dummy_bts, &bits, 0, &lost, &recv);
+		dl_win.update(dummy_bts, &bits, 0, &lost, &recv);
 		OSMO_ASSERT(lost == 5);
 		OSMO_ASSERT(recv == 3);
 		OSMO_ASSERT(bitvec_get_bit_pos(&bits, 0) == 0);
@@ -431,6 +432,7 @@
 		OSMO_ASSERT(bsn_begin == 35);
 		OSMO_ASSERT(bsn_end == 43);
 		OSMO_ASSERT(num_blocks == 8);
+		talloc_free(dummy_bts);
 	}
 }
 
@@ -669,12 +671,12 @@
 
 	fprintf(stderr, "############## test_egprs_ul_ack_nack\n");
 
-	BTS the_bts(the_pcu);
+	struct gprs_rlcmac_bts *bts = bts_alloc(the_pcu);
 	the_pcu->alloc_algorithm = alloc_algorithm_a;
-	the_bts.bts_data()->trx[0].pdch[4].enable();
+	bts->trx[0].pdch[4].enable();
 
-	GprsMs *ms = the_bts.ms_alloc(1, 1);
-	struct gprs_rlcmac_ul_tbf *tbf = tbf_alloc_ul_tbf(the_bts.bts_data(), ms, 0, true);
+	GprsMs *ms = bts_alloc_ms(bts, 1, 1);
+	struct gprs_rlcmac_ul_tbf *tbf = tbf_alloc_ul_tbf(bts, ms, 0, true);
 	struct crbb_test crbb_test = {0};
 	bitvec *rbb = NULL;
 	unsigned int rbb_size;
@@ -722,6 +724,7 @@
 	extract_egprs_ul_ack_nack(tbf, dest, &ssn, &crbb_test, &rbb, false);
 	check_egprs_bitmap(tbf, ssn, &crbb_test, rbb, &rbb_size);
 	free_egprs_ul_ack_nack(&rbb, &crbb_test);
+	talloc_free(bts);
 }
 
 static void check_imm_ass(struct gprs_rlcmac_tbf *tbf, bool dl, enum ph_burst_type bt, const uint8_t *exp, uint8_t len,
@@ -759,13 +762,13 @@
 
 void test_immediate_assign_dl()
 {
-	BTS the_bts(the_pcu);
+	struct gprs_rlcmac_bts *bts = bts_alloc(the_pcu);
 	the_pcu->alloc_algorithm = alloc_algorithm_a;
-	the_bts.bts_data()->trx[0].pdch[2].enable();
-	the_bts.bts_data()->trx[0].pdch[3].enable();
-	GprsMs *ms = the_bts.ms_alloc(1, 0);
+	bts->trx[0].pdch[2].enable();
+	bts->trx[0].pdch[3].enable();
+	GprsMs *ms = bts_alloc_ms(bts, 1, 0);
 
-	struct gprs_rlcmac_tbf *tbf = tbf_alloc_dl_tbf(the_bts.bts_data(), ms, 0, false);
+	struct gprs_rlcmac_tbf *tbf = tbf_alloc_dl_tbf(bts, ms, 0, false);
 	static uint8_t res[] = { 0x06,
 				 0x3f, /* Immediate Assignment Message Type */
 				 0x30, /* §10.5.2.26 Page Mode and §10.5.2.25b Dedicated mode/TBF */
@@ -779,17 +782,18 @@
 				 0xdf, 0xff, 0xff, 0xff, 0xf8, 0x17, 0x47, 0x08, 0x0b, 0x5b, 0x2b, 0x2b, };
 
 	check_imm_ass(tbf, true, GSM_L1_BURST_TYPE_ACCESS_2, res, sizeof(res), "ia_rest_downlink");
+	talloc_free(bts);
 }
 
 void test_immediate_assign_ul0m()
 {
-	BTS the_bts(the_pcu);
+	struct gprs_rlcmac_bts *bts = bts_alloc(the_pcu);
 	the_pcu->alloc_algorithm = alloc_algorithm_a;
-	the_bts.bts_data()->trx[0].pdch[4].enable();
-	the_bts.bts_data()->trx[0].pdch[5].enable();
+	bts->trx[0].pdch[4].enable();
+	bts->trx[0].pdch[5].enable();
 
-	GprsMs *ms = the_bts.ms_alloc(1, 0);
-	struct gprs_rlcmac_tbf *tbf = tbf_alloc_ul_tbf(the_bts.bts_data(), ms, 0, false);
+	GprsMs *ms = bts_alloc_ms(bts, 1, 0);
+	struct gprs_rlcmac_tbf *tbf = tbf_alloc_ul_tbf(bts, ms, 0, false);
 	static uint8_t res[] = { 0x06,
 				 0x3f, /* Immediate Assignment Message Type */
 				 0x10, /* §10.5.2.26 Page Mode and §10.5.2.25b Dedicated mode/TBF */
@@ -803,6 +807,7 @@
 				 0xc8, 0x02, 0x1b, 0xa2, 0x0b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, };
 
 	check_imm_ass(tbf, false, GSM_L1_BURST_TYPE_ACCESS_0, res, sizeof(res), "ia_rest_uplink(MBA)");
+	talloc_free(bts);
 }
 
 void test_immediate_assign_ul0s()
@@ -824,13 +829,13 @@
 
 void test_immediate_assign_ul1s()
 {
-	BTS the_bts(the_pcu);
+	struct gprs_rlcmac_bts *bts = bts_alloc(the_pcu);
 	the_pcu->alloc_algorithm = alloc_algorithm_a;
-	the_bts.bts_data()->trx[0].pdch[1].enable();
-	the_bts.bts_data()->trx[0].pdch[2].enable();
+	bts->trx[0].pdch[1].enable();
+	bts->trx[0].pdch[2].enable();
 
-	GprsMs *ms = the_bts.ms_alloc(1, 1);
-	struct gprs_rlcmac_tbf *tbf = tbf_alloc_ul_tbf(the_bts.bts_data(), ms, 0, false);
+	GprsMs *ms = bts_alloc_ms(bts, 1, 1);
+	struct gprs_rlcmac_tbf *tbf = tbf_alloc_ul_tbf(bts, ms, 0, false);
 	static uint8_t res[] = { 0x06,
 				 0x3f, /* Immediate Assignment Message Type */
 				 0x10, /* §10.5.2.26 Page Mode and §10.5.2.25b Dedicated mode/TBF */
@@ -844,6 +849,7 @@
 				 0x46, 0xa0, 0x08, 0x00, 0x17, 0x44, 0x0b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, };
 
 	check_imm_ass(tbf, false, GSM_L1_BURST_TYPE_ACCESS_1, res, sizeof(res), "ia_rest_egprs_uplink(SBA)");
+	talloc_free(bts);
 }
 
 void test_immediate_assign_ul1m()

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

Gerrit-Project: osmo-pcu
Gerrit-Branch: master
Gerrit-Change-Id: I7d12c896c5ded659ca9d3bff4cf3a3fc857db9dd
Gerrit-Change-Number: 22196
Gerrit-PatchSet: 7
Gerrit-Owner: pespin <pespin at sysmocom.de>
Gerrit-Reviewer: Jenkins Builder
Gerrit-Reviewer: laforge <laforge at osmocom.org>
Gerrit-Reviewer: lynxis lazus <lynxis at fe80.eu>
Gerrit-Reviewer: osmith <osmith at sysmocom.de>
Gerrit-Reviewer: pespin <pespin at sysmocom.de>
Gerrit-MessageType: merged
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20210119/eb9dd18d/attachment.htm>


More information about the gerrit-log mailing list