Merge remote-tracking branch 'origin/sysmocom/tbf-split'

the TBF handling in osmo-pcu mixes UL and DL TBFs together. They
are quite different, however, and should be treated differently.

This patch series continues the work of splitting up UL and DL TBFs
into their own classes and explicitly using a DL TBF or UL TBF
instead of the base class in those parts of the code that deal
with only one variant.

I didn't test every single commit, but each should compile and
the result passes make check as well as our iperf test against
a GSM modem.

[Text from Daniel's cover mail]
diff --git a/src/bts.cpp b/src/bts.cpp
index 77c3f3e..73344a3 100644
--- a/src/bts.cpp
+++ b/src/bts.cpp
@@ -236,14 +236,12 @@
 	struct llist_pods *lpods;
 	if (dir == GPRS_RLCMAC_UL_TBF) {
 		llist_pods_for_each_entry(tbf, &m_bts.ul_tbfs, list, lpods) {
-			OSMO_ASSERT(tbf->direction == dir);
 			if (tbf->state_is_not(GPRS_RLCMAC_RELEASING)
 			 && tbf->tlli() == tlli && tbf->is_tlli_valid())
 				return tbf;
 		}
 	} else {
 		llist_pods_for_each_entry(tbf, &m_bts.dl_tbfs, list, lpods) {
-			OSMO_ASSERT(tbf->direction == dir);
 			if (tbf->state_is_not(GPRS_RLCMAC_RELEASING)
 			 && tbf->tlli() == tlli)
 				return tbf;
@@ -264,7 +262,6 @@
 		 && tbf->poll_state == GPRS_RLCMAC_POLL_SCHED
 		 && tbf->poll_fn == fn && tbf->trx->trx_no == trx
 		 && tbf->control_ts == ts) {
-			OSMO_ASSERT(tbf->direction == GPRS_RLCMAC_DL_TBF);
 			return static_cast<gprs_rlcmac_dl_tbf *>(tbf);
 		}
 	}
@@ -282,7 +279,6 @@
 		 && tbf->poll_state == GPRS_RLCMAC_POLL_SCHED
 		 && tbf->poll_fn == fn && tbf->trx->trx_no == trx
 		 && tbf->control_ts == ts) {
-			OSMO_ASSERT(tbf->direction == GPRS_RLCMAC_UL_TBF);
 			return static_cast<gprs_rlcmac_ul_tbf *>(tbf);
 		}
 	}
@@ -318,7 +314,6 @@
 		return NULL;
 
 	if (tbf->state_is_not(GPRS_RLCMAC_RELEASING)) {
-		OSMO_ASSERT(tbf->direction == dir);
 		return tbf;
 	}
 
@@ -382,7 +377,7 @@
 
 int BTS::rcv_imm_ass_cnf(const uint8_t *data, uint32_t fn)
 {
-	struct gprs_rlcmac_tbf *tbf;
+	struct gprs_rlcmac_dl_tbf *dl_tbf;
 	uint8_t plen;
 	uint32_t tlli;
 
@@ -404,8 +399,8 @@
 	tlli |= (*data++) << 4;
 	tlli |= (*data++) >> 4;
 
-	tbf = dl_tbf_by_tlli(tlli);
-	if (!tbf) {
+	dl_tbf = dl_tbf_by_tlli(tlli);
+	if (!dl_tbf) {
 		LOGP(DRLCMAC, LOGL_ERROR, "Got IMM.ASS confirm, but TLLI=%08x "
 			"does not exit\n", tlli);
 		return -EINVAL;
@@ -413,8 +408,8 @@
 
 	LOGP(DRLCMAC, LOGL_DEBUG, "Got IMM.ASS confirm for TLLI=%08x\n", tlli);
 
-	if (tbf->dir.dl.wait_confirm)
-		tbf_timer_start(tbf, 0, Tassign_agch);
+	if (dl_tbf->m_wait_confirm)
+		tbf_timer_start(dl_tbf, 0, Tassign_agch);
 
 	return 0;
 }
@@ -495,7 +490,7 @@
 	else
 		plen = Encoding::write_immediate_assignment(&m_bts, immediate_assignment, 0, ra,
 			Fn, tbf->ta, tbf->trx->arfcn, tbf->first_ts, tbf->tsc(),
-			tbf->tfi(), tbf->dir.ul.usf[tbf->first_ts], 0, 0, 0, 0,
+			tbf->tfi(), tbf->m_usf[tbf->first_ts], 0, 0, 0, 0,
 			m_bts.alpha, m_bts.gamma, -1);
 	pcu_l1if_tx_agch(immediate_assignment, plen);
 	bitvec_free(immediate_assignment);
@@ -505,11 +500,11 @@
 
 /* depending on the current TBF, we assign on PACCH or AGCH */
 void BTS::trigger_dl_ass(
-	struct gprs_rlcmac_tbf *tbf,
+	struct gprs_rlcmac_dl_tbf *dl_tbf,
 	struct gprs_rlcmac_tbf *old_tbf, const char *imsi)
 {
 	/* stop pending timer */
-	tbf->stop_timer();
+	dl_tbf->stop_timer();
 
 	/* check for downlink tbf:  */
 	if (old_tbf) {
@@ -517,27 +512,27 @@
 			"PACCH, because %s exists\n", tbf_name(old_tbf));
 		old_tbf->dl_ass_state = GPRS_RLCMAC_DL_ASS_SEND_ASS;
 		/* use TA from old TBF */
-		tbf->ta = old_tbf->ta;
-		tbf->was_releasing = tbf->state_is(GPRS_RLCMAC_WAIT_RELEASE);
+		dl_tbf->ta = old_tbf->ta;
+		dl_tbf->was_releasing = dl_tbf->state_is(GPRS_RLCMAC_WAIT_RELEASE);
 		/* change state */
-		tbf_new_state(tbf, GPRS_RLCMAC_ASSIGN);
-		tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_PACCH);
+		tbf_new_state(dl_tbf, GPRS_RLCMAC_ASSIGN);
+		dl_tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_PACCH);
 		/* start timer */
-		tbf_timer_start(tbf, 0, Tassign_pacch);
+		tbf_timer_start(dl_tbf, 0, Tassign_pacch);
 	} else {
-		LOGP(DRLCMAC, LOGL_DEBUG, "Send dowlink assignment for %s on PCH, no TBF exist (IMSI=%s)\n", tbf_name(tbf), imsi);
+		LOGP(DRLCMAC, LOGL_DEBUG, "Send dowlink assignment for %s on PCH, no TBF exist (IMSI=%s)\n", tbf_name(dl_tbf), imsi);
 		if (!imsi || strlen(imsi) < 3) {
 			LOGP(DRLCMAC, LOGL_ERROR, "No valid IMSI!\n");
 			return;
 		}
-		tbf->was_releasing = tbf->state_is(GPRS_RLCMAC_WAIT_RELEASE);
+		dl_tbf->was_releasing = dl_tbf->state_is(GPRS_RLCMAC_WAIT_RELEASE);
 		/* change state */
-		tbf_new_state(tbf, GPRS_RLCMAC_ASSIGN);
-		tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_CCCH);
-		tbf->assign_imsi(imsi);
+		tbf_new_state(dl_tbf, GPRS_RLCMAC_ASSIGN);
+		dl_tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_CCCH);
+		dl_tbf->assign_imsi(imsi);
 		/* send immediate assignment */
-		tbf->bts->snd_dl_ass(tbf, 0, imsi);
-		tbf->dir.dl.wait_confirm = 1;
+		dl_tbf->bts->snd_dl_ass(dl_tbf, 0, imsi);
+		dl_tbf->m_wait_confirm = 1;
 	}
 }
 
@@ -823,7 +818,7 @@
 void gprs_rlcmac_pdch::rcv_control_dl_ack_nack(Packet_Downlink_Ack_Nack_t *ack_nack, uint32_t fn)
 {
 	int8_t tfi = 0; /* must be signed */
-	struct gprs_rlcmac_tbf *tbf;
+	struct gprs_rlcmac_dl_tbf *tbf;
 	int rc;
 
 	tfi = ack_nack->DOWNLINK_TFI;
@@ -1049,7 +1044,6 @@
 	struct llist_pods *lpods;
 
 	llist_pods_for_each_entry(tbf, tbf_list, list, lpods) {
-		OSMO_ASSERT(tbf->direction == dir);
 		if (tbf->tfi() != tfi)
 			continue;
 		if (!tbf->pdch[ts_no])
diff --git a/src/bts.h b/src/bts.h
index 8f99942..e845995 100644
--- a/src/bts.h
+++ b/src/bts.h
@@ -205,7 +205,7 @@
 	int rcv_imm_ass_cnf(const uint8_t *data, uint32_t fn);
 	int rcv_rach(uint8_t ra, uint32_t Fn, int16_t qta);
 
-	void trigger_dl_ass(gprs_rlcmac_tbf *tbf, gprs_rlcmac_tbf *old_tbf, const char *imsi);
+	void trigger_dl_ass(gprs_rlcmac_dl_tbf *tbf, gprs_rlcmac_tbf *old_tbf, const char *imsi);
 	void snd_dl_ass(gprs_rlcmac_tbf *tbf, uint8_t poll, const char *imsi);
 
 	/*
diff --git a/src/encoding.cpp b/src/encoding.cpp
index 13848aa..ffd6108 100644
--- a/src/encoding.cpp
+++ b/src/encoding.cpp
@@ -166,7 +166,7 @@
 	struct gprs_rlcmac_bts *bts,
 	bitvec * dest, uint8_t old_tfi,
 	uint8_t old_downlink, uint32_t tlli, uint8_t use_tlli,
-	struct gprs_rlcmac_tbf *tbf, uint8_t poll, uint8_t alpha,
+	struct gprs_rlcmac_ul_tbf *tbf, uint8_t poll, uint8_t alpha,
 	uint8_t gamma, int8_t ta_idx)
 {
 	// TODO We should use our implementation of encode RLC/MAC Control messages.
@@ -232,7 +232,7 @@
 	for (ts = 0; ts < 8; ts++) {
 		if (tbf->pdch[ts]) {
 			bitvec_write_field(dest, wp,0x1,1); // USF_TN(i): on
-			bitvec_write_field(dest, wp,tbf->dir.ul.usf[ts],3); // USF_TN(i)
+			bitvec_write_field(dest, wp,tbf->m_usf[ts],3); // USF_TN(i)
 			if (alpha || gamma)
 				bitvec_write_field(dest, wp,gamma,5);   // GAMMA power control parameter
 		} else
@@ -375,14 +375,14 @@
 
 /* generate uplink ack */
 void Encoding::write_packet_uplink_ack(struct gprs_rlcmac_bts *bts,
-	RlcMacDownlink_t * block, struct gprs_rlcmac_tbf *tbf,
+	RlcMacDownlink_t * block, struct gprs_rlcmac_ul_tbf *tbf,
 	uint8_t final)
 {
 	// Packet Uplink Ack/Nack  TS 44.060 11.2.28
 
 	char rbb[65];
 
-	tbf->dir.ul.window.update_rbb(rbb);
+	tbf->m_window.update_rbb(rbb);
 
 	LOGP(DRLCMACUL, LOGL_DEBUG, "Encoding Ack/Nack for %s "
 		"(final=%d)\n", tbf_name(tbf), final);
@@ -399,7 +399,7 @@
 	block->u.Packet_Uplink_Ack_Nack.UnionType    = 0x0;      // PU_AckNack_GPRS = on
 	block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.CHANNEL_CODING_COMMAND                        = bts->initial_cs_ul - 1;             // CS1
 	block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Ack_Nack_Description.FINAL_ACK_INDICATION     = final;           // FINAL ACK INDICATION
-	block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Ack_Nack_Description.STARTING_SEQUENCE_NUMBER = tbf->dir.ul.window.ssn(); // STARTING_SEQUENCE_NUMBER
+	block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Ack_Nack_Description.STARTING_SEQUENCE_NUMBER = tbf->m_window.ssn(); // STARTING_SEQUENCE_NUMBER
 
 	encode_rbb(rbb, block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Ack_Nack_Description.RECEIVED_BLOCK_BITMAP);
 
diff --git a/src/encoding.h b/src/encoding.h
index e62c2c8..0004c42 100644
--- a/src/encoding.h
+++ b/src/encoding.h
@@ -47,7 +47,7 @@
 			struct gprs_rlcmac_bts *bts,
 			bitvec * dest, uint8_t old_tfi,
 			uint8_t old_downlink, uint32_t tlli, uint8_t use_tlli, 
-			struct gprs_rlcmac_tbf *tbf, uint8_t poll, uint8_t alpha,
+			struct gprs_rlcmac_ul_tbf *tbf, uint8_t poll, uint8_t alpha,
 			uint8_t gamma, int8_t ta_idx);
 
 	static void write_packet_downlink_assignment(RlcMacDownlink_t * block, uint8_t old_tfi,
@@ -56,7 +56,7 @@
 
 	static void encode_rbb(const char *show_rbb, uint8_t *rbb);
 
-	static void write_packet_uplink_ack(struct gprs_rlcmac_bts *bts, RlcMacDownlink_t * block, struct gprs_rlcmac_tbf *tbf,
+	static void write_packet_uplink_ack(struct gprs_rlcmac_bts *bts, RlcMacDownlink_t * block, struct gprs_rlcmac_ul_tbf *tbf,
 		        uint8_t final);
 
 	static int write_paging_request(bitvec * dest, uint8_t *ptmsi, uint16_t ptmsi_len);
diff --git a/src/gprs_bssgp_pcu.cpp b/src/gprs_bssgp_pcu.cpp
index 6c5d72c..0782dbf 100644
--- a/src/gprs_bssgp_pcu.cpp
+++ b/src/gprs_bssgp_pcu.cpp
@@ -152,7 +152,7 @@
 
 	LOGP(DBSSGP, LOGL_INFO, "LLC [SGSN -> PCU] = TLLI: 0x%08x IMSI: %s len: %d\n", tlli, imsi, len);
 
-	return gprs_rlcmac_tbf::handle(the_pcu.bts, tlli, imsi, ms_class, delay_csec, data, len);
+	return gprs_rlcmac_dl_tbf::handle(the_pcu.bts, tlli, imsi, ms_class, delay_csec, data, len);
 }
 
 int gprs_bssgp_pcu_rx_paging_ps(struct msgb *msg, struct tlv_parsed *tp)
diff --git a/src/gprs_rlcmac_sched.cpp b/src/gprs_rlcmac_sched.cpp
index dab38d8..40a183b 100644
--- a/src/gprs_rlcmac_sched.cpp
+++ b/src/gprs_rlcmac_sched.cpp
@@ -28,9 +28,10 @@
 		    struct gprs_rlcmac_tbf **poll_tbf,
 		    struct gprs_rlcmac_tbf **ul_ass_tbf,
 		    struct gprs_rlcmac_tbf **dl_ass_tbf,
-		    struct gprs_rlcmac_tbf **ul_ack_tbf)
+		    struct gprs_rlcmac_ul_tbf **ul_ack_tbf)
 {
-	struct gprs_rlcmac_tbf *tbf;
+	struct gprs_rlcmac_ul_tbf *ul_tbf;
+	struct gprs_rlcmac_dl_tbf *dl_tbf;
 	struct llist_pods *lpods;
 	uint32_t poll_fn;
 
@@ -39,34 +40,34 @@
 	if ((block_nr % 3) == 2)
 		poll_fn ++;
 	poll_fn = poll_fn % 2715648;
-	llist_pods_for_each_entry(tbf, &bts->ul_tbfs, list, lpods) {
+	llist_pods_for_each_entry(ul_tbf, &bts->ul_tbfs, list, lpods) {
 		/* this trx, this ts */
-		if (tbf->trx->trx_no != trx || tbf->control_ts != ts)
+		if (ul_tbf->trx->trx_no != trx || ul_tbf->control_ts != ts)
 			continue;
 		/* polling for next uplink block */
-		if (tbf->poll_state == GPRS_RLCMAC_POLL_SCHED
-		 && tbf->poll_fn == poll_fn)
-			*poll_tbf = tbf;
-		if (tbf->ul_ack_state == GPRS_RLCMAC_UL_ACK_SEND_ACK)
-			*ul_ack_tbf = tbf;
-		if (tbf->dl_ass_state == GPRS_RLCMAC_DL_ASS_SEND_ASS)
-			*dl_ass_tbf = tbf;
-		if (tbf->ul_ass_state == GPRS_RLCMAC_UL_ASS_SEND_ASS)
-			*ul_ass_tbf = tbf;
+		if (ul_tbf->poll_state == GPRS_RLCMAC_POLL_SCHED
+		 && ul_tbf->poll_fn == poll_fn)
+			*poll_tbf = ul_tbf;
+		if (ul_tbf->ul_ack_state == GPRS_RLCMAC_UL_ACK_SEND_ACK)
+			*ul_ack_tbf = ul_tbf;
+		if (ul_tbf->dl_ass_state == GPRS_RLCMAC_DL_ASS_SEND_ASS)
+			*dl_ass_tbf = ul_tbf;
+		if (ul_tbf->ul_ass_state == GPRS_RLCMAC_UL_ASS_SEND_ASS)
+			*ul_ass_tbf = ul_tbf;
 #warning "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_pods_for_each_entry(tbf, &bts->dl_tbfs, list, lpods) {
+	llist_pods_for_each_entry(dl_tbf, &bts->dl_tbfs, list, lpods) {
 		/* this trx, this ts */
-		if (tbf->trx->trx_no != trx || tbf->control_ts != ts)
+		if (dl_tbf->trx->trx_no != trx || dl_tbf->control_ts != ts)
 			continue;
 		/* polling for next uplink block */
-		if (tbf->poll_state == GPRS_RLCMAC_POLL_SCHED
-		 && tbf->poll_fn == poll_fn)
-			*poll_tbf = tbf;
-		if (tbf->dl_ass_state == GPRS_RLCMAC_DL_ASS_SEND_ASS)
-			*dl_ass_tbf = tbf;
-		if (tbf->ul_ass_state == GPRS_RLCMAC_UL_ASS_SEND_ASS)
-			*ul_ass_tbf = tbf;
+		if (dl_tbf->poll_state == GPRS_RLCMAC_POLL_SCHED
+		 && dl_tbf->poll_fn == poll_fn)
+			*poll_tbf = dl_tbf;
+		if (dl_tbf->dl_ass_state == GPRS_RLCMAC_DL_ASS_SEND_ASS)
+			*dl_ass_tbf = dl_tbf;
+		if (dl_tbf->ul_ass_state == GPRS_RLCMAC_UL_ASS_SEND_ASS)
+			*ul_ass_tbf = dl_tbf;
 	}
 
 	return poll_fn;
@@ -75,7 +76,7 @@
 static uint8_t sched_select_uplink(uint8_t trx, uint8_t ts, uint32_t fn,
 	uint8_t block_nr, struct gprs_rlcmac_pdch *pdch)
 {
-	struct gprs_rlcmac_tbf *tbf;
+	struct gprs_rlcmac_ul_tbf *tbf;
 	uint8_t usf = 0x07;
 	uint8_t i, tfi;
 
@@ -94,7 +95,7 @@
 			continue;
 
 		/* use this USF */
-		usf = tbf->dir.ul.usf[ts];
+		usf = tbf->m_usf[ts];
 		LOGP(DRLCMACSCHED, LOGL_DEBUG, "Received RTS for PDCH: TRX=%d "
 			"TS=%d FN=%d block_nr=%d scheduling USF=%d for "
 			"required uplink resource of UL TFI=%d\n", trx, ts, fn,
@@ -112,7 +113,7 @@
 		    uint8_t block_nr, struct gprs_rlcmac_pdch *pdch,
 		    struct gprs_rlcmac_tbf *ul_ass_tbf,
 		    struct gprs_rlcmac_tbf *dl_ass_tbf,
-		    struct gprs_rlcmac_tbf *ul_ack_tbf)
+		    struct gprs_rlcmac_ul_tbf *ul_ack_tbf)
 {
 	struct msgb *msg = NULL;
 	struct gprs_rlcmac_tbf *tbf = NULL;
@@ -124,11 +125,13 @@
 			continue;
 
 		if (tbf == ul_ass_tbf)
-			msg = tbf->create_ul_ass(fn);
+			msg = ul_ass_tbf->create_ul_ass(fn);
 		else if (tbf == dl_ass_tbf)
-			msg = tbf->create_dl_ass(fn);
+			msg = dl_ass_tbf->create_dl_ass(fn);
+		else if (tbf == ul_ack_tbf)
+			msg = ul_ack_tbf->create_ul_ack(fn);
 		else
-			msg = tbf->create_ul_ack(fn);
+			abort();
 
 		if (!msg) {
 			tbf = NULL;
@@ -164,7 +167,7 @@
 		    uint8_t block_nr, struct gprs_rlcmac_pdch *pdch)
 {
 	struct msgb *msg = NULL;
-	struct gprs_rlcmac_tbf *tbf = NULL;
+	struct gprs_rlcmac_dl_tbf *tbf = NULL;
 	uint8_t i, tfi;
 
 	/* select downlink resource */
@@ -183,7 +186,7 @@
 			continue;
 
 		/* waiting for CCCH IMM.ASS confirm */
-		if (tbf->dir.dl.wait_confirm)
+		if (tbf->m_wait_confirm)
 			continue;
 
 		LOGP(DRLCMACSCHED, LOGL_DEBUG, "Scheduling data message at "
@@ -224,7 +227,8 @@
 {
 	struct gprs_rlcmac_pdch *pdch;
 	struct gprs_rlcmac_tbf *poll_tbf = NULL, *dl_ass_tbf = NULL,
-		*ul_ass_tbf = NULL, *ul_ack_tbf = NULL;
+		*ul_ass_tbf = NULL;
+	struct gprs_rlcmac_ul_tbf *ul_ack_tbf = NULL;
 	uint8_t usf = 0x7;
 	struct msgb *msg = NULL;
 	uint32_t poll_fn, sba_fn;
diff --git a/src/gprs_rlcmac_ts_alloc.cpp b/src/gprs_rlcmac_ts_alloc.cpp
index b070c2e..27b01d7 100644
--- a/src/gprs_rlcmac_ts_alloc.cpp
+++ b/src/gprs_rlcmac_ts_alloc.cpp
@@ -77,7 +77,7 @@
 
 static inline int8_t find_free_usf(struct gprs_rlcmac_pdch *pdch)
 {
-	struct gprs_rlcmac_tbf *tbf;
+	struct gprs_rlcmac_ul_tbf *tbf;
 	uint8_t usf_map = 0;
 	uint8_t tfi, usf;
 
@@ -86,7 +86,7 @@
 		tbf = pdch->ul_tbf_by_tfi(tfi);
 		if (!tbf)
 			continue;
-		usf_map |= (1 << tbf->dir.ul.usf[pdch->ts_no]);
+		usf_map |= (1 << tbf->m_usf[pdch->ts_no]);
 	}
 
 	/* look for USF, don't use USF=7 */
@@ -118,16 +118,16 @@
 
 static void assign_uplink_tbf_usf(
 				struct gprs_rlcmac_pdch *pdch,
-				struct gprs_rlcmac_tbf *tbf, int8_t usf)
+				struct gprs_rlcmac_ul_tbf *tbf, int8_t usf)
 {
 	tbf->trx->ul_tbf[tbf->tfi()] = tbf;
 	tbf->pdch[pdch->ts_no] = pdch;
-	tbf->dir.ul.usf[pdch->ts_no] = usf;
+	tbf->m_usf[pdch->ts_no] = usf;
 }
 
 static void assign_dlink_tbf(
 				struct gprs_rlcmac_pdch *pdch,
-				struct gprs_rlcmac_tbf *tbf)
+				struct gprs_rlcmac_dl_tbf *tbf)
 {
 	tbf->trx->dl_tbf[tbf->tfi()] = tbf;
 	tbf->pdch[pdch->ts_no] = pdch;
@@ -155,6 +155,7 @@
 	pdch = &tbf->trx->pdch[ts];
 	if (tbf->direction == GPRS_RLCMAC_UL_TBF) {
 		int8_t usf; /* must be signed */
+		struct gprs_rlcmac_ul_tbf *ul_tbf = static_cast<gprs_rlcmac_ul_tbf *>(tbf);
 
 		/* if USF available */
 		usf = find_free_usf(pdch);
@@ -165,10 +166,11 @@
 		}
 		LOGP(DRLCMAC, LOGL_DEBUG, "- Assign uplink "
 			"TS=%d USF=%d\n", ts, usf);
-		assign_uplink_tbf_usf(pdch, tbf, usf);
+		assign_uplink_tbf_usf(pdch, ul_tbf, usf);
 	} else {
+		struct gprs_rlcmac_dl_tbf *dl_tbf = static_cast<gprs_rlcmac_dl_tbf *>(tbf);
 		LOGP(DRLCMAC, LOGL_DEBUG, "- Assign downlink TS=%d\n", ts);
-		assign_dlink_tbf(pdch, tbf);
+		assign_dlink_tbf(pdch, dl_tbf);
 	}
 	/* the only one TS is the common TS */
 	tbf->first_ts = tbf->first_common_ts = ts;
@@ -627,6 +629,7 @@
 	}
 
 	if (tbf->direction == GPRS_RLCMAC_DL_TBF) {
+		struct gprs_rlcmac_dl_tbf *dl_tbf = static_cast<gprs_rlcmac_dl_tbf *>(tbf);
 		/* assign downlink */
 		if (rx_window == 0) {
 			LOGP(DRLCMAC, LOGL_NOTICE, "No downlink slots "
@@ -642,23 +645,24 @@
 					continue;
 				LOGP(DRLCMAC, LOGL_DEBUG, "- Assigning DL TS "
 					"%d\n", ts);
-				assign_dlink_tbf(&tbf->trx->pdch[ts], tbf);
+				assign_dlink_tbf(&tbf->trx->pdch[ts], dl_tbf);
 				slotcount++;
 				if (slotcount == 1)
-					tbf->first_ts = ts;
+					dl_tbf->first_ts = ts;
 				if (single)
 					break;
 			}
 		}
 	} else {
+		struct gprs_rlcmac_ul_tbf *ul_tbf = static_cast<gprs_rlcmac_ul_tbf *>(tbf);
 		for (ts = 0; ts < 8; ts++) {
 			if ((tx_window & (1 << ts))) {
 				LOGP(DRLCMAC, LOGL_DEBUG, "- Assigning UL TS "
 					"%d\n", ts);
-				assign_uplink_tbf_usf(&tbf->trx->pdch[ts], tbf, usf[ts]);
+				assign_uplink_tbf_usf(&tbf->trx->pdch[ts], ul_tbf, usf[ts]);
 				slotcount++;
 				if (slotcount == 1)
-					tbf->first_ts = ts;
+					ul_tbf->first_ts = ts;
 				if (single)
 					break;
 			}
diff --git a/src/poll_controller.cpp b/src/poll_controller.cpp
index dea75ed..7e1aa78 100644
--- a/src/poll_controller.cpp
+++ b/src/poll_controller.cpp
@@ -31,26 +31,27 @@
 void PollController::expireTimedout(int frame_number)
 {
 	struct gprs_rlcmac_bts *bts = m_bts.bts_data();
-	struct gprs_rlcmac_tbf *tbf;
+	struct gprs_rlcmac_dl_tbf *dl_tbf;
+	struct gprs_rlcmac_ul_tbf *ul_tbf;
 	struct gprs_rlcmac_sba *sba, *sba2;
 	struct llist_pods *lpods;
 	uint32_t elapsed;
 
 	/* check for poll timeout */
-	llist_pods_for_each_entry(tbf, &bts->ul_tbfs, list, lpods) {
-		if (tbf->poll_state == GPRS_RLCMAC_POLL_SCHED) {
-			elapsed = (frame_number + 2715648 - tbf->poll_fn)
+	llist_pods_for_each_entry(ul_tbf, &bts->ul_tbfs, list, lpods) {
+		if (ul_tbf->poll_state == GPRS_RLCMAC_POLL_SCHED) {
+			elapsed = (frame_number + 2715648 - ul_tbf->poll_fn)
 								% 2715648;
 			if (elapsed >= 20 && elapsed < 2715400)
-				tbf->poll_timeout();
+				ul_tbf->poll_timeout();
 		}
 	}
-	llist_pods_for_each_entry(tbf, &bts->dl_tbfs, list, lpods) {
-		if (tbf->poll_state == GPRS_RLCMAC_POLL_SCHED) {
-			elapsed = (frame_number + 2715648 - tbf->poll_fn)
+	llist_pods_for_each_entry(dl_tbf, &bts->dl_tbfs, list, lpods) {
+		if (dl_tbf->poll_state == GPRS_RLCMAC_POLL_SCHED) {
+			elapsed = (frame_number + 2715648 - dl_tbf->poll_fn)
 								% 2715648;
 			if (elapsed >= 20 && elapsed < 2715400)
-				tbf->poll_timeout();
+				dl_tbf->poll_timeout();
 		}
 	}
 	llist_for_each_entry_safe(sba, sba2, &m_bts.sba()->m_sbas, list) {
diff --git a/src/rlc.cpp b/src/rlc.cpp
index 36320dc..42b602c 100644
--- a/src/rlc.cpp
+++ b/src/rlc.cpp
@@ -46,6 +46,13 @@
 		mark_invalid(i);
 }
 
+void gprs_rlc_dl_window::reset()
+{
+	m_v_s = 0;
+	m_v_a = 0;
+	m_v_b.reset();
+}
+
 int gprs_rlc_dl_window::resend_needed()
 {
 	for (uint16_t bsn = v_a(); bsn != v_s(); bsn = (bsn + 1) & mod_sns()) {
diff --git a/src/rlc.h b/src/rlc.h
index 7bea8a1..55cae3f 100644
--- a/src/rlc.h
+++ b/src/rlc.h
@@ -109,6 +109,7 @@
  * most simple form of inheritance is not a POD anymore.
  */
 struct gprs_rlc_dl_window {
+	void reset();
 	const uint16_t mod_sns() const;
 	const uint16_t sns() const;
 	const uint16_t ws() const;
diff --git a/src/tbf.cpp b/src/tbf.cpp
index 2257018..22b8ce0 100644
--- a/src/tbf.cpp
+++ b/src/tbf.cpp
@@ -78,14 +78,14 @@
 	m_imsi[sizeof(m_imsi) - 1] = '\0';
 }
 
-static struct gprs_rlcmac_tbf *tbf_lookup_dl(BTS *bts,
+static struct gprs_rlcmac_dl_tbf *tbf_lookup_dl(BTS *bts,
 					const uint32_t tlli, const char *imsi)
 {
 	/* TODO: look up by IMSI first, then tlli, then old_tlli */
 	return bts->dl_tbf_by_tlli(tlli);
 }
 
-int gprs_rlcmac_tbf::append_data(const uint8_t ms_class,
+int gprs_rlcmac_dl_tbf::append_data(const uint8_t ms_class,
 				const uint16_t pdu_delay_csec,
 				const uint8_t *data, const uint16_t len)
 {
@@ -123,25 +123,26 @@
 {
 	uint8_t trx, ta, ss;
 	int8_t use_trx;
-	struct gprs_rlcmac_tbf *old_tbf, *tbf;
+	struct gprs_rlcmac_ul_tbf *ul_tbf, *old_ul_tbf;
+	struct gprs_rlcmac_dl_tbf *dl_tbf;
 	int8_t tfi; /* must be signed */
 	int rc;
 
 	/* check for uplink data, so we copy our informations */
 #warning "Do the same look up for IMSI, TLLI and OLD_TLLI"
 #warning "Refactor the below lines... into a new method"
-	tbf = bts->bts->ul_tbf_by_tlli(tlli);
-	if (tbf && tbf->dir.ul.contention_resolution_done
-	 && !tbf->dir.ul.final_ack_sent) {
-		use_trx = tbf->trx->trx_no;
-		ta = tbf->ta;
+	ul_tbf = bts->bts->ul_tbf_by_tlli(tlli);
+	if (ul_tbf && ul_tbf->m_contention_resolution_done
+	 && !ul_tbf->m_final_ack_sent) {
+		use_trx = ul_tbf->trx->trx_no;
+		ta = ul_tbf->ta;
 		ss = 0;
-		old_tbf = tbf;
+		old_ul_tbf = ul_tbf;
 	} else {
 		use_trx = -1;
 		/* we already have an uplink TBF, so we use that TA */
-		if (tbf)
-			ta = tbf->ta;
+		if (ul_tbf)
+			ta = ul_tbf->ta;
 		else {
 			/* recall TA */
 			rc = bts->bts->timing_advance()->recall(tlli);
@@ -153,7 +154,7 @@
 				ta = rc;
 		}
 		ss = 1; /* PCH assignment only allows one timeslot */
-		old_tbf = NULL;
+		old_ul_tbf = NULL;
 	}
 
 	// Create new TBF (any TRX)
@@ -165,49 +166,49 @@
 		return -EBUSY;
 	}
 	/* set number of downlink slots according to multislot class */
-	tbf = tbf_alloc_dl_tbf(bts, tbf, tfi, trx, ms_class, ss);
-	if (!tbf) {
+	dl_tbf = tbf_alloc_dl_tbf(bts, ul_tbf, tfi, trx, ms_class, ss);
+	if (!dl_tbf) {
 		LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH resource\n");
 		/* FIXME: send reject */
 		return -EBUSY;
 	}
-	tbf->m_tlli = tlli;
-	tbf->m_tlli_valid = 1;
-	tbf->ta = ta;
+	dl_tbf->m_tlli = tlli;
+	dl_tbf->m_tlli_valid = 1;
+	dl_tbf->ta = ta;
 
-	LOGP(DRLCMAC, LOGL_DEBUG, "%s [DOWNLINK] START\n", tbf_name(tbf));
+	LOGP(DRLCMAC, LOGL_DEBUG, "%s [DOWNLINK] START\n", tbf_name(dl_tbf));
 
 	/* new TBF, so put first frame */
-	tbf->m_llc.put_frame(data, len);
-	tbf->bts->llc_frame_sched();
+	dl_tbf->m_llc.put_frame(data, len);
+	dl_tbf->bts->llc_frame_sched();
 
 	/* Store IMSI for later look-up and PCH retransmission */
-	tbf->assign_imsi(imsi);
+	dl_tbf->assign_imsi(imsi);
 
 	/* trigger downlink assignment and set state to ASSIGN.
 	 * we don't use old_downlink, so the possible uplink is used
 	 * to trigger downlink assignment. if there is no uplink,
 	 * AGCH is used. */
-	tbf->bts->trigger_dl_ass(tbf, old_tbf, imsi);
+	dl_tbf->bts->trigger_dl_ass(dl_tbf, old_ul_tbf, imsi);
 	return 0;
 }
 
 /**
  * TODO: split into unit test-able parts...
  */
-int gprs_rlcmac_tbf::handle(struct gprs_rlcmac_bts *bts,
+int gprs_rlcmac_dl_tbf::handle(struct gprs_rlcmac_bts *bts,
 		const uint32_t tlli, const char *imsi,
 		const uint8_t ms_class, const uint16_t delay_csec,
 		const uint8_t *data, const uint16_t len)
 {
-	struct gprs_rlcmac_tbf *tbf;
+	struct gprs_rlcmac_dl_tbf *dl_tbf;
 
 	/* check for existing TBF */
-	tbf = tbf_lookup_dl(bts->bts, tlli, imsi);
-	if (tbf) {
-		int rc = tbf->append_data(ms_class, delay_csec, data, len);
+	dl_tbf = tbf_lookup_dl(bts->bts, tlli, imsi);
+	if (dl_tbf) {
+		int rc = dl_tbf->append_data(ms_class, delay_csec, data, len);
 		if (rc >= 0)
-			tbf->assign_imsi(imsi);
+			dl_tbf->assign_imsi(imsi);
 		return rc;
 	} 
 
@@ -239,7 +240,7 @@
 	}
 	tbf->m_tlli = tlli;
 	tbf->m_tlli_valid = 1; /* no contention resolution */
-	tbf->dir.ul.contention_resolution_done = 1;
+	tbf->m_contention_resolution_done = 1;
 	tbf->ta = ta; /* use current TA */
 	tbf_new_state(tbf, GPRS_RLCMAC_ASSIGN);
 	tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_PACCH);
@@ -403,16 +404,17 @@
 		}
 		ul_ack_state = GPRS_RLCMAC_UL_ACK_NONE;
 		if (state_is(GPRS_RLCMAC_FINISHED)) {
-			dir.ul.n3103++;
-			if (dir.ul.n3103 == bts->bts_data()->n3103) {
+			gprs_rlcmac_ul_tbf *ul_tbf = static_cast<gprs_rlcmac_ul_tbf *>(this);
+			ul_tbf->m_n3103++;
+			if (ul_tbf->m_n3103 == ul_tbf->bts->bts_data()->n3103) {
 				LOGP(DRLCMAC, LOGL_NOTICE,
 					"- N3103 exceeded\n");
-				tbf_new_state(this, GPRS_RLCMAC_RELEASING);
-				tbf_timer_start(this, 3169, bts->bts_data()->t3169, 0);
+				tbf_new_state(ul_tbf, GPRS_RLCMAC_RELEASING);
+				tbf_timer_start(ul_tbf, 3169, ul_tbf->bts->bts_data()->t3169, 0);
 				return;
 			}
 			/* reschedule UL ack */
-			ul_ack_state = GPRS_RLCMAC_UL_ACK_SEND_ACK;
+			ul_tbf->ul_ack_state = GPRS_RLCMAC_UL_ACK_SEND_ACK;
 		}
 	} else if (ul_ass_state == GPRS_RLCMAC_UL_ASS_WAIT_ACK) {
 		if (!(state_flags & (1 << GPRS_RLCMAC_FLAG_TO_UL_ASS))) {
@@ -451,29 +453,31 @@
 		/* reschedule DL assignment */
 		dl_ass_state = GPRS_RLCMAC_DL_ASS_SEND_ASS;
 	} else if (direction == GPRS_RLCMAC_DL_TBF) {
-		if (!(state_flags & (1 << GPRS_RLCMAC_FLAG_TO_DL_ACK))) {
+		gprs_rlcmac_dl_tbf *dl_tbf = static_cast<gprs_rlcmac_dl_tbf *>(this);
+
+		if (!(dl_tbf->state_flags & (1 << GPRS_RLCMAC_FLAG_TO_DL_ACK))) {
 			LOGP(DRLCMAC, LOGL_NOTICE, "- Timeout for polling "
 				"PACKET DOWNLINK ACK.\n");
-			rlcmac_diag();
-			state_flags |= (1 << GPRS_RLCMAC_FLAG_TO_DL_ACK);
+			dl_tbf->rlcmac_diag();
+			dl_tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_TO_DL_ACK);
 		}
-		n3105++;
-		if (n3105 == bts->bts_data()->n3105) {
+		dl_tbf->n3105++;
+		if (dl_tbf->n3105 == dl_tbf->bts->bts_data()->n3105) {
 			LOGP(DRLCMAC, LOGL_NOTICE, "- N3105 exceeded\n");
-			tbf_new_state(this, GPRS_RLCMAC_RELEASING);
-			tbf_timer_start(this, 3195, bts_data()->t3195, 0);
+			tbf_new_state(dl_tbf, GPRS_RLCMAC_RELEASING);
+			tbf_timer_start(dl_tbf, 3195, dl_tbf->bts_data()->t3195, 0);
 			return;
 		}
 		/* resend IMM.ASS on CCCH on timeout */
-		if ((state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH))
-		 && !(state_flags & (1 << GPRS_RLCMAC_FLAG_DL_ACK))) {
+		if ((dl_tbf->state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH))
+		 && !(dl_tbf->state_flags & (1 << GPRS_RLCMAC_FLAG_DL_ACK))) {
 			LOGP(DRLCMAC, LOGL_DEBUG, "Re-send dowlink assignment "
 				"for %s on PCH (IMSI=%s)\n",
-				tbf_name(this),
+				tbf_name(dl_tbf),
 				m_imsi);
 			/* send immediate assignment */
-			bts->snd_dl_ass(this, 0, m_imsi);
-			dir.dl.wait_confirm = 1;
+			dl_tbf->bts->snd_dl_ass(dl_tbf, 0, m_imsi);
+			dl_tbf->m_wait_confirm = 1;
 		}
 	} else
 		LOGP(DRLCMAC, LOGL_ERROR, "- Poll Timeout, but no event!\n");
@@ -616,14 +620,15 @@
 					"in assign state\n", tbf_name(this));
 		}
 		if ((state_flags & (1 << GPRS_RLCMAC_FLAG_CCCH))) {
-			dir.dl.wait_confirm = 0;
-			if (state_is(GPRS_RLCMAC_ASSIGN)) {
-				tbf_assign_control_ts(this);
+			gprs_rlcmac_dl_tbf *dl_tbf = static_cast<gprs_rlcmac_dl_tbf *>(this);
+			dl_tbf->m_wait_confirm = 0;
+			if (dl_tbf->state_is(GPRS_RLCMAC_ASSIGN)) {
+				tbf_assign_control_ts(dl_tbf);
 
-				if (!upgrade_to_multislot) {
+				if (!dl_tbf->upgrade_to_multislot) {
 					/* change state to FLOW, so scheduler
 					 * will start transmission */
-					tbf_new_state(this, GPRS_RLCMAC_FLOW);
+					tbf_new_state(dl_tbf, GPRS_RLCMAC_FLOW);
 					break;
 				}
 
@@ -633,15 +638,15 @@
 				 * PDCH. */
 
 				/* keep to flags */
-				state_flags &= GPRS_RLCMAC_FLAG_TO_MASK;
-				state_flags &= ~(1 << GPRS_RLCMAC_FLAG_CCCH);
+				dl_tbf->state_flags &= GPRS_RLCMAC_FLAG_TO_MASK;
+				dl_tbf->state_flags &= ~(1 << GPRS_RLCMAC_FLAG_CCCH);
 
-				update();
+				dl_tbf->update();
 
-				bts->trigger_dl_ass(this, this, NULL);
+				dl_tbf->bts->trigger_dl_ass(dl_tbf, dl_tbf, NULL);
 			} else
 				LOGP(DRLCMAC, LOGL_NOTICE, "%s Continue flow after "
-					"IMM.ASS confirm\n", tbf_name(this));
+					"IMM.ASS confirm\n", tbf_name(dl_tbf));
 		}
 		break;
 	case 3169:
@@ -889,19 +894,19 @@
  * Create DL data block
  * The messages are fragmented and forwarded as data blocks.
  */
-struct msgb *gprs_rlcmac_tbf::create_dl_acked_block(uint32_t fn, uint8_t ts)
+struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(uint32_t fn, uint8_t ts)
 {
 	LOGP(DRLCMACDL, LOGL_DEBUG, "%s downlink (V(A)==%d .. "
 		"V(S)==%d)\n", tbf_name(this),
-		dir.dl.window.v_a(), dir.dl.window.v_s());
+		m_window.v_a(), m_window.v_s());
 
 do_resend:
 	/* check if there is a block with negative acknowledgement */
-	int resend_bsn = dir.dl.window.resend_needed();
+	int resend_bsn = m_window.resend_needed();
 	if (resend_bsn >= 0) {
 		LOGP(DRLCMACDL, LOGL_DEBUG, "- Resending BSN %d\n", resend_bsn);
 		/* re-send block with negative aknowlegement */
-		dir.dl.window.m_v_b.mark_unacked(resend_bsn);
+		m_window.m_v_b.mark_unacked(resend_bsn);
 		bts->rlc_resent();
 		return create_dl_acked_block(fn, ts, resend_bsn, false);
 	}
@@ -912,12 +917,12 @@
 		if (state_is(GPRS_RLCMAC_FINISHED)) {
 			LOGP(DRLCMACDL, LOGL_DEBUG, "- Restarting at BSN %d, "
 				"because all blocks have been transmitted.\n",
-					dir.dl.window.v_a());
+					m_window.v_a());
 			bts->rlc_restarted();
 		} else {
 			LOGP(DRLCMACDL, LOGL_NOTICE, "- Restarting at BSN %d, "
 				"because all window is stalled.\n",
-					dir.dl.window.v_a());
+					m_window.v_a());
 			bts->rlc_stalled();
 		}
 		/* If V(S) == V(A) and finished state, we would have received
@@ -926,17 +931,17 @@
 		 * from MS. But in this case we did not receive the final ack
 		 * indication from MS. This should never happen if MS works
 		 * correctly. */
-		if (dir.dl.window.window_empty()) {
+		if (m_window.window_empty()) {
 			LOGP(DRLCMACDL, LOGL_DEBUG, "- MS acked all blocks, "
 				"so we re-transmit final block!\n");
 			/* we just send final block again */
-			int16_t index = dir.dl.window.v_s_mod(-1);
+			int16_t index = m_window.v_s_mod(-1);
 			bts->rlc_resent();
 			return create_dl_acked_block(fn, ts, index, false);
 		}
 		
 		/* cycle through all unacked blocks */
-		int resend = dir.dl.window.mark_for_resend();
+		int resend = m_window.mark_for_resend();
 
 		/* At this point there should be at least one unacked block
 		 * to be resent. If not, this is an software error. */
@@ -945,7 +950,7 @@
 				"There are no unacknowledged blocks, but V(A) "
 				" != V(S). PLEASE FIX!\n");
 			/* we just send final block again */
-			int16_t index = dir.dl.window.v_s_mod(-1);
+			int16_t index = m_window.v_s_mod(-1);
 			return create_dl_acked_block(fn, ts, index, false);
 		}
 		goto do_resend;
@@ -954,7 +959,7 @@
 	return create_new_bsn(fn, ts);
 }
 
-struct msgb *gprs_rlcmac_tbf::create_new_bsn(const uint32_t fn, const uint8_t ts)
+struct msgb *gprs_rlcmac_dl_tbf::create_new_bsn(const uint32_t fn, const uint8_t ts)
 {
 	struct rlc_dl_header *rh;
 	struct rlc_li_field *li;
@@ -963,10 +968,10 @@
 	uint16_t space, chunk;
 	gprs_rlc_data *rlc_data;
 	bool first_fin_ack = false;
-	const uint16_t bsn = dir.dl.window.v_s();
+	const uint16_t bsn = m_window.v_s();
 
 	LOGP(DRLCMACDL, LOGL_DEBUG, "- Sending new block at BSN %d\n",
-		dir.dl.window.v_s());
+		m_window.v_s());
 
 #warning "Selection of the CS doesn't belong here"
 	if (cs == 0) {
@@ -1115,13 +1120,13 @@
 #warning "move this up?"
 	rlc_data->len = block_length;
 	/* raise send state and set ack state array */
-	dir.dl.window.m_v_b.mark_unacked(bsn);
-	dir.dl.window.increment_send();
+	m_window.m_v_b.mark_unacked(bsn);
+	m_window.increment_send();
 
 	return create_dl_acked_block(fn, ts, bsn, first_fin_ack);
 }
 
-struct msgb *gprs_rlcmac_tbf::create_dl_acked_block(
+struct msgb *gprs_rlcmac_dl_tbf::create_dl_acked_block(
 				const uint32_t fn, const uint8_t ts,
 				const int index, const bool first_fin_ack)
 {
@@ -1140,7 +1145,7 @@
 		
 	/* poll after POLL_ACK_AFTER_FRAMES frames, or when final block is tx.
 	 */
-	if (dir.dl.tx_counter >= POLL_ACK_AFTER_FRAMES || first_fin_ack) {
+	if (m_tx_counter >= POLL_ACK_AFTER_FRAMES || first_fin_ack) {
 		if (first_fin_ack) {
 			LOGP(DRLCMACDL, LOGL_DEBUG, "- Scheduling Ack/Nack "
 				"polling, because first final block sent.\n");
@@ -1166,7 +1171,7 @@
 		else  {
 			LOGP(DRLCMACDL, LOGL_DEBUG, "Polling sheduled in this "
 				"TS %d\n", ts);
-			dir.dl.tx_counter = 0;
+			m_tx_counter = 0;
 			/* start timer whenever we send the final block */
 			if (rh->fbi == 1)
 				tbf_timer_start(this, 3191, bts_data()->t3191, 0);
@@ -1187,7 +1192,7 @@
 		return NULL;
 
 	/* Increment TX-counter */
-	dir.dl.tx_counter++;
+	m_tx_counter++;
 
 	memcpy(msgb_put(dl_msg, len), data, len);
 	bts->rlc_sent();
@@ -1198,7 +1203,7 @@
 struct msgb *gprs_rlcmac_tbf::create_dl_ass(uint32_t fn)
 {
 	struct msgb *msg;
-	struct gprs_rlcmac_tbf *new_tbf;
+	struct gprs_rlcmac_dl_tbf *new_dl_tbf;
 	int poll_ass_dl = 1;
 
 	if (direction == GPRS_RLCMAC_DL_TBF && control_ts != first_common_ts) {
@@ -1224,19 +1229,21 @@
 
 	/* on uplink TBF we get the downlink TBF to be assigned. */
 	if (direction == GPRS_RLCMAC_UL_TBF) {
+		gprs_rlcmac_ul_tbf *ul_tbf = static_cast<gprs_rlcmac_ul_tbf *>(this);
+
 		/* be sure to check first, if contention resolution is done,
 		 * otherwise we cannot send the assignment yet */
-		if (!dir.ul.contention_resolution_done) {
+		if (!ul_tbf->m_contention_resolution_done) {
 			LOGP(DRLCMAC, LOGL_DEBUG, "Cannot assign DL TBF now, "
 				"because contention resolution is not "
 				"finished.\n");
 			return NULL;
 		}
 		#warning "THIS should probably go over the IMSI too"
-		new_tbf = bts->dl_tbf_by_tlli(m_tlli);
+		new_dl_tbf = ul_tbf->bts->dl_tbf_by_tlli(m_tlli);
 	} else
-		new_tbf = this;
-	if (!new_tbf) {
+		new_dl_tbf = static_cast<gprs_rlcmac_dl_tbf *>(this);
+	if (!new_dl_tbf) {
 		LOGP(DRLCMACDL, LOGL_ERROR, "We have a schedule for downlink "
 			"assignment at uplink %s, but there is no downlink "
 			"TBF\n", tbf_name(this));
@@ -1254,10 +1261,10 @@
 	}
 	bitvec_unhex(ass_vec,
 		"2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
-	LOGP(DRLCMAC, LOGL_INFO, "%s  start Packet Downlink Assignment (PACCH)\n", tbf_name(new_tbf));
+	LOGP(DRLCMAC, LOGL_INFO, "%s  start Packet Downlink Assignment (PACCH)\n", tbf_name(new_dl_tbf));
 	RlcMacDownlink_t * mac_control_block = (RlcMacDownlink_t *)talloc_zero(tall_pcu_ctx, RlcMacDownlink_t);
 	Encoding::write_packet_downlink_assignment(mac_control_block, m_tfi,
-		(direction == GPRS_RLCMAC_DL_TBF), new_tbf,
+		(direction == GPRS_RLCMAC_DL_TBF), new_dl_tbf,
 		poll_ass_dl, bts_data()->alpha, bts_data()->gamma, -1, 0);
 	LOGP(DRLCMAC, LOGL_DEBUG, "+++++++++++++++++++++++++ TX : Packet Downlink Assignment +++++++++++++++++++++++++\n");
 	encode_gsm_rlcmac_downlink(ass_vec, mac_control_block);
@@ -1273,10 +1280,10 @@
 		dl_ass_state = GPRS_RLCMAC_DL_ASS_WAIT_ACK;
 	} else {
 		dl_ass_state = GPRS_RLCMAC_DL_ASS_NONE;
-		tbf_new_state(new_tbf, GPRS_RLCMAC_FLOW);
-		tbf_assign_control_ts(new_tbf);
+		tbf_new_state(new_dl_tbf, GPRS_RLCMAC_FLOW);
+		tbf_assign_control_ts(new_dl_tbf);
 		/* stop pending assignment timer */
-		new_tbf->stop_timer();
+		new_dl_tbf->stop_timer();
 
 	}
 
@@ -1286,7 +1293,7 @@
 struct msgb *gprs_rlcmac_tbf::create_ul_ass(uint32_t fn)
 {
 	struct msgb *msg;
-	struct gprs_rlcmac_tbf *new_tbf;
+	struct gprs_rlcmac_ul_tbf *new_tbf;
 
 	if (poll_state != GPRS_RLCMAC_POLL_NONE) {
 		LOGP(DRLCMACUL, LOGL_DEBUG, "Polling is already "
@@ -1305,7 +1312,7 @@
 	if (direction == GPRS_RLCMAC_DL_TBF)
 		new_tbf = bts->ul_tbf_by_tlli(m_tlli);
 	else
-		new_tbf = this;
+		new_tbf = static_cast<gprs_rlcmac_ul_tbf *>(this);
 
 	if (!new_tbf) {
 		LOGP(DRLCMACUL, LOGL_ERROR, "We have a schedule for uplink "
@@ -1346,7 +1353,7 @@
 	return msg;
 }
 
-struct msgb *gprs_rlcmac_tbf::create_ul_ack(uint32_t fn)
+struct msgb *gprs_rlcmac_ul_tbf::create_ul_ack(uint32_t fn)
 {
 	int final = (state_is(GPRS_RLCMAC_FINISHED));
 	struct msgb *msg;
@@ -1384,27 +1391,27 @@
 
 	/* now we must set this flag, so we are allowed to assign downlink
 	 * TBF on PACCH. it is only allowed when TLLI is acknowledged. */
-	dir.ul.contention_resolution_done = 1;
+	m_contention_resolution_done = 1;
 
 	if (final) {
 		poll_state = GPRS_RLCMAC_POLL_SCHED;
 		poll_fn = (fn + 13) % 2715648;
 		/* waiting for final acknowledge */
 		ul_ack_state = GPRS_RLCMAC_UL_ACK_WAIT_ACK;
-		dir.ul.final_ack_sent = 1;
+		m_final_ack_sent = 1;
 	} else
 		ul_ack_state = GPRS_RLCMAC_UL_ACK_NONE;
 
 	return msg;
 }
 
-int gprs_rlcmac_tbf::update_window(const uint8_t ssn, const uint8_t *rbb)
+int gprs_rlcmac_dl_tbf::update_window(const uint8_t ssn, const uint8_t *rbb)
 {
 	int16_t dist; /* must be signed */
 	uint16_t lost = 0, received = 0;
 	char show_rbb[65];
 	char show_v_b[RLC_MAX_SNS + 1];
-	const uint16_t mod_sns = dir.dl.window.mod_sns();
+	const uint16_t mod_sns = m_window.mod_sns();
 
 	Decoding::extract_rbb(rbb, show_rbb);
 	/* show received array in debug (bit 64..1) */
@@ -1414,9 +1421,9 @@
 
 	/* apply received array to receive state (SSN-64..SSN-1) */
 	/* calculate distance of ssn from V(S) */
-	dist = (dir.dl.window.v_s() - ssn) & mod_sns;
+	dist = (m_window.v_s() - ssn) & mod_sns;
 	/* check if distance is less than distance V(A)..V(S) */
-	if (dist >= dir.dl.window.distance()) {
+	if (dist >= m_window.distance()) {
 		/* this might happpen, if the downlink assignment
 		 * was not received by ms and the ack refers
 		 * to previous TBF
@@ -1427,24 +1434,24 @@
 		return 1; /* indicate to free TBF */
 	}
 
-	dir.dl.window.update(bts, show_rbb, ssn,
+	m_window.update(bts, show_rbb, ssn,
 			&lost, &received);
 
 	/* report lost and received packets */
 	gprs_rlcmac_received_lost(this, received, lost);
 
 	/* raise V(A), if possible */
-	dir.dl.window.raise(dir.dl.window.move_window());
+	m_window.raise(m_window.move_window());
 
 	/* show receive state array in debug (V(A)..V(S)-1) */
-	dir.dl.window.show_state(show_v_b);
+	m_window.show_state(show_v_b);
 	LOGP(DRLCMACDL, LOGL_DEBUG, "- V(B): (V(A)=%d)\"%s\""
 		"(V(S)-1=%d)  A=Acked N=Nacked U=Unacked "
 		"X=Resend-Unacked I=Invalid\n",
-		dir.dl.window.v_a(), show_v_b,
-		dir.dl.window.v_s_mod(-1));
+		m_window.v_a(), show_v_b,
+		m_window.v_s_mod(-1));
 
-	if (state_is(GPRS_RLCMAC_FINISHED) && dir.dl.window.window_empty()) {
+	if (state_is(GPRS_RLCMAC_FINISHED) && m_window.window_empty()) {
 		LOGP(DRLCMACDL, LOGL_NOTICE, "Received acknowledge of "
 			"all blocks, but without final ack "
 			"inidcation (don't worry)\n");
@@ -1453,14 +1460,14 @@
 }
 
 
-int gprs_rlcmac_tbf::maybe_start_new_window()
+int gprs_rlcmac_dl_tbf::maybe_start_new_window()
 {
 	struct msgb *msg;
 	uint16_t received;
 
 	LOGP(DRLCMACDL, LOGL_DEBUG, "- Final ACK received.\n");
 	/* range V(A)..V(S)-1 */
-	received = dir.dl.window.count_unacked();
+	received = m_window.count_unacked();
 
 	/* report all outstanding packets as received */
 	gprs_rlcmac_received_lost(this, received, 0);
@@ -1486,7 +1493,7 @@
 	return 0;
 }
 
-int gprs_rlcmac_tbf::rcvd_dl_ack(uint8_t final_ack, uint8_t ssn, uint8_t *rbb)
+int gprs_rlcmac_dl_tbf::rcvd_dl_ack(uint8_t final_ack, uint8_t ssn, uint8_t *rbb)
 {
 	LOGP(DRLCMACDL, LOGL_DEBUG, "%s downlink acknowledge\n", tbf_name(this));
 
@@ -1613,19 +1620,19 @@
 	return 1;
 }
 
-int gprs_rlcmac_tbf::rcv_data_block_acknowledged(const uint8_t *data, size_t len, int8_t rssi)
+int gprs_rlcmac_ul_tbf::rcv_data_block_acknowledged(const uint8_t *data, size_t len, int8_t rssi)
 {
 	struct rlc_ul_header *rh = (struct rlc_ul_header *)data;
 	int rc;
 
-	const uint16_t mod_sns = dir.ul.window.mod_sns();
-	const uint16_t ws = dir.ul.window.ws();
+	const uint16_t mod_sns = m_window.mod_sns();
+	const uint16_t ws = m_window.ws();
 
 	this->state_flags |= (1 << GPRS_RLCMAC_FLAG_UL_DATA);
 
 	LOGP(DRLCMACUL, LOGL_DEBUG, "UL DATA TFI=%d received (V(Q)=%d .. "
-		"V(R)=%d)\n", rh->tfi, this->dir.ul.window.v_q(),
-		this->dir.ul.window.v_r());
+		"V(R)=%d)\n", rh->tfi, this->m_window.v_q(),
+		this->m_window.v_r());
 
 	/* process RSSI */
 	gprs_rlcmac_rssi(this, rssi);
@@ -1655,13 +1662,13 @@
 	tbf_timer_start(this, 3169, bts_data()->t3169, 0);
 
 	/* Increment RX-counter */
-	this->dir.ul.rx_counter++;
+	this->m_rx_counter++;
 
-	if (!dir.ul.window.is_in_window(rh->bsn)) {
+	if (!m_window.is_in_window(rh->bsn)) {
 		LOGP(DRLCMACUL, LOGL_DEBUG, "- BSN %d out of window "
 			"%d..%d (it's normal)\n", rh->bsn,
-			dir.ul.window.v_q(),
-			(dir.ul.window.v_q() + ws - 1) & mod_sns);
+			m_window.v_q(),
+			(m_window.v_q() + ws - 1) & mod_sns);
 		maybe_schedule_uplink_acknack(rh);
 		return 0;
 	}
@@ -1669,15 +1676,15 @@
 	/* Write block to buffer and set receive state array. */
 	m_rlc.block(rh->bsn)->put_data(data, len);
 	LOGP(DRLCMACUL, LOGL_DEBUG, "- BSN %d storing in window (%d..%d)\n",
-		rh->bsn, dir.ul.window.v_q(),
-		(dir.ul.window.v_q() + ws - 1) & mod_sns);
+		rh->bsn, m_window.v_q(),
+		(m_window.v_q() + ws - 1) & mod_sns);
 
 	/* Raise V(Q) if possible, and retrieve LLC frames from blocks.
 	 * This is looped until there is a gap (non received block) or
 	 * the window is empty.*/
-	const uint16_t v_q_beg = dir.ul.window.v_q();
+	const uint16_t v_q_beg = m_window.v_q();
 
-	const uint16_t count = dir.ul.window.receive_bsn(rh->bsn);
+	const uint16_t count = m_window.receive_bsn(rh->bsn);
 
 	/* Retrieve LLC frames from blocks that are ready */
 	for (uint16_t i = 0; i < count; ++i) {
@@ -1687,9 +1694,9 @@
 
 	/* Check CV of last frame in buffer */
 	if (this->state_is(GPRS_RLCMAC_FLOW) /* still in flow state */
-	 && this->dir.ul.window.v_q() == this->dir.ul.window.v_r()) { /* if complete */
+	 && this->m_window.v_q() == this->m_window.v_r()) { /* if complete */
 		struct rlc_ul_header *last_rh = (struct rlc_ul_header *)
-			m_rlc.block((dir.ul.window.v_r() - 1) & mod_sns)->block;
+			m_rlc.block((m_window.v_r() - 1) & mod_sns)->block;
 		LOGP(DRLCMACUL, LOGL_DEBUG, "- No gaps in received block, "
 			"last block: BSN=%d CV=%d\n", last_rh->bsn,
 			last_rh->cv);
@@ -1698,7 +1705,7 @@
 				"TBF\n");
 			tbf_new_state(this, GPRS_RLCMAC_FINISHED);
 			/* Reset N3103 counter. */
-			this->dir.ul.n3103 = 0;
+			this->m_n3103 = 0;
 		}
 	}
 
@@ -1709,10 +1716,10 @@
 	return 0;
 }
 
-void gprs_rlcmac_tbf::maybe_schedule_uplink_acknack(const rlc_ul_header *rh)
+void gprs_rlcmac_ul_tbf::maybe_schedule_uplink_acknack(const rlc_ul_header *rh)
 {
 	if (rh->si || rh->ti || state_is(GPRS_RLCMAC_FINISHED)
-	 || (dir.ul.rx_counter % SEND_ACK_AFTER_FRAMES) == 0) {
+	 || (m_rx_counter % SEND_ACK_AFTER_FRAMES) == 0) {
 		if (rh->si) {
 			LOGP(DRLCMACUL, LOGL_NOTICE, "- Scheduling Ack/Nack, "
 				"because MS is stalled.\n");
@@ -1725,7 +1732,7 @@
 			LOGP(DRLCMACUL, LOGL_DEBUG, "- Scheduling Ack/Nack, "
 				"because last block has CV==0.\n");
 		}
-		if ((dir.ul.rx_counter % SEND_ACK_AFTER_FRAMES) == 0) {
+		if ((m_rx_counter % SEND_ACK_AFTER_FRAMES) == 0) {
 			LOGP(DRLCMACUL, LOGL_DEBUG, "- Scheduling Ack/Nack, "
 				"because %d frames received.\n",
 				SEND_ACK_AFTER_FRAMES);
@@ -1779,15 +1786,16 @@
 }
 
 
-void gprs_rlcmac_tbf::reuse_tbf(const uint8_t *data, const uint16_t len)
+void gprs_rlcmac_dl_tbf::reuse_tbf(const uint8_t *data, const uint16_t len)
 {
 	bts->tbf_reused();
 	m_llc.put_frame(data, len);
 	bts->llc_frame_sched();
 
 	/* reset rlc states */
-	memset(&dir.dl, 0, sizeof(dir.dl));
-	dir.dl.window.m_v_b.reset();
+	m_tx_counter = 0;
+	m_wait_confirm = 0;
+	m_window.reset();
 
 	/* keep to flags */
 	state_flags &= GPRS_RLCMAC_FLAG_TO_MASK;
@@ -1801,9 +1809,9 @@
 	bts->trigger_dl_ass(this, this, NULL);
 }
 
-bool gprs_rlcmac_tbf::dl_window_stalled() const
+bool gprs_rlcmac_dl_tbf::dl_window_stalled() const
 {
-	return dir.dl.window.window_stalled();
+	return m_window.window_stalled();
 }
 
 void gprs_rlcmac_tbf::rotate_in_list()
diff --git a/src/tbf.h b/src/tbf.h
index 95355bd..23f8a7b 100644
--- a/src/tbf.h
+++ b/src/tbf.h
@@ -121,21 +121,10 @@
 	/* TODO: extract LLC class? */
 	int assemble_forward_llc(const gprs_rlc_data *data);
 
-	struct msgb *create_dl_acked_block(uint32_t fn, uint8_t ts);
 	struct msgb *create_dl_ass(uint32_t fn);
 	struct msgb *create_ul_ass(uint32_t fn);
-	struct msgb *create_ul_ack(uint32_t fn);
-	int rcvd_dl_ack(uint8_t final, uint8_t ssn, uint8_t *rbb);
 	int snd_ul_ud();
 
-	/* blocks were acked */
-	int rcv_data_block_acknowledged(const uint8_t *data, size_t len, int8_t rssi);
-
-	/* dispatch Unitdata.DL messages */
-	static int handle(struct gprs_rlcmac_bts *bts,
-		const uint32_t tlli, const char *imsi, const uint8_t ms_class,
-		const uint16_t delay_csec, const uint8_t *data, const uint16_t len);
-
 	uint8_t tsc() const;
 
 	int rlcmac_diag();
@@ -158,8 +147,6 @@
 	const char *imsi() const;
 	void assign_imsi(const char *imsi);
 
-	uint16_t sns() const;
-
 	time_t created_ts() const;
 
 	/* attempt to make things a bit more fair */
@@ -186,27 +173,6 @@
 	enum gprs_rlcmac_tbf_poll_state poll_state;
 	uint32_t poll_fn; /* frame number to poll */
 
-	/* Please note that all variables here will be reset when changing
-	 * from WAIT RELEASE back to FLOW state (re-use of TBF).
-	 * All states that need reset must be in this struct, so this is why
-	 * variables are in both (dl and ul) structs and not outside union.
-	 */
-	union {
-		struct {
-			gprs_rlc_dl_window window;
-			int32_t tx_counter; /* count all transmitted blocks */
-			uint8_t wait_confirm; /* wait for CCCH IMM.ASS cnf */
-		} dl;
-		struct {
-			gprs_rlc_ul_window window;
-			int32_t rx_counter; /* count all received blocks */
-			uint8_t n3103;	/* N3103 counter */
-			uint8_t usf[8];	/* list USFs per PDCH (timeslot) */
-			uint8_t contention_resolution_done; /* set after done */
-			uint8_t final_ack_sent; /* set if we sent final ack */
-		} ul;
-	} dir;
-
 	gprs_rlc m_rlc;
 	
 	uint8_t n3105;	/* N3105 counter */
@@ -262,22 +228,10 @@
 	char m_imsi[16];
 
 protected:
-	int update_window(const uint8_t ssn, const uint8_t *rbb);
-	int maybe_start_new_window();
-	void reuse_tbf(const uint8_t *data, const uint16_t len);
 	gprs_rlcmac_bts *bts_data() const;
-	bool dl_window_stalled() const;
 
 	int extract_tlli(const uint8_t *data, const size_t len);
-	void maybe_schedule_uplink_acknack(const rlc_ul_header *rh);
 
-	int append_data(const uint8_t ms_class,
-			const uint16_t pdu_delay_csec,
-			const uint8_t *data, const uint16_t len);
-
-	struct msgb *create_dl_acked_block(const uint32_t fn, const uint8_t ts,
-					const int index, const bool fin_first_ack);
-	struct msgb *create_new_bsn(const uint32_t fn, const uint8_t ts);
 };
 
 
@@ -340,12 +294,6 @@
 	return m_imsi;
 }
 
-inline uint16_t gprs_rlcmac_tbf::sns() const
-{
-	/* assume dl/ul do the same thing */
-	return dir.dl.window.sns();
-}
-
 const char *tbf_name(gprs_rlcmac_tbf *tbf);
 
 inline time_t gprs_rlcmac_tbf::created_ts() const
@@ -354,9 +302,57 @@
 }
 
 struct gprs_rlcmac_dl_tbf : public gprs_rlcmac_tbf {
+	/* dispatch Unitdata.DL messages */
+	static int handle(struct gprs_rlcmac_bts *bts,
+		const uint32_t tlli, const char *imsi, const uint8_t ms_class,
+		const uint16_t delay_csec, const uint8_t *data, const uint16_t len);
+
+	int append_data(const uint8_t ms_class,
+			const uint16_t pdu_delay_csec,
+			const uint8_t *data, const uint16_t len);
+
+	int rcvd_dl_ack(uint8_t final, uint8_t ssn, uint8_t *rbb);
+	struct msgb *create_dl_acked_block(uint32_t fn, uint8_t ts);
+
+	/* Please note that all variables here will be reset when changing
+	 * from WAIT RELEASE back to FLOW state (re-use of TBF).
+	 * All states that need reset must be in this struct, so this is why
+	 * variables are in both (dl and ul) structs and not outside union.
+	 */
+	gprs_rlc_dl_window m_window;
+	int32_t m_tx_counter; /* count all transmitted blocks */
+	uint8_t m_wait_confirm; /* wait for CCCH IMM.ASS cnf */
+
+protected:
+	struct msgb *create_new_bsn(const uint32_t fn, const uint8_t ts);
+	struct msgb *create_dl_acked_block(const uint32_t fn, const uint8_t ts,
+					const int index, const bool fin_first_ack);
+	int update_window(const uint8_t ssn, const uint8_t *rbb);
+	int maybe_start_new_window();
+	bool dl_window_stalled() const;
+	void reuse_tbf(const uint8_t *data, const uint16_t len);
 };
 
 struct gprs_rlcmac_ul_tbf : public gprs_rlcmac_tbf {
+	struct msgb *create_ul_ack(uint32_t fn);
+
+	/* blocks were acked */
+	int rcv_data_block_acknowledged(const uint8_t *data, size_t len, int8_t rssi);
+
+	/* Please note that all variables here will be reset when changing
+	 * from WAIT RELEASE back to FLOW state (re-use of TBF).
+	 * All states that need reset must be in this struct, so this is why
+	 * variables are in both (dl and ul) structs and not outside union.
+	 */
+	gprs_rlc_ul_window m_window;
+	int32_t m_rx_counter; /* count all received blocks */
+	uint8_t m_n3103;	/* N3103 counter */
+	uint8_t m_usf[8];	/* list USFs per PDCH (timeslot) */
+	uint8_t m_contention_resolution_done; /* set after done */
+	uint8_t m_final_ack_sent; /* set if we sent final ack */
+
+protected:
+	void maybe_schedule_uplink_acknack(const rlc_ul_header *rh);
 };
 
 #endif
diff --git a/tests/alloc/AllocTest.cpp b/tests/alloc/AllocTest.cpp
index 830ca90..d711ad8 100644
--- a/tests/alloc/AllocTest.cpp
+++ b/tests/alloc/AllocTest.cpp
@@ -174,7 +174,8 @@
 		int tfi;
 		uint8_t ts_no, trx_no;
 
-		gprs_rlcmac_tbf *ul_tbf, *dl_tbf;
+		gprs_rlcmac_ul_tbf *ul_tbf;
+		gprs_rlcmac_dl_tbf *dl_tbf;
 
 		printf("Testing DL then UL assignment followed by update\n");
 
@@ -199,7 +200,7 @@
 		ul_tbf = tbf_alloc_ul_tbf(bts, dl_tbf, tfi, trx_no, ms_class, 0);
 		ul_tbf->m_tlli = 0x23;
 		ul_tbf->m_tlli_valid = true;
-		ul_tbf->dir.ul.contention_resolution_done = 1;
+		ul_tbf->m_contention_resolution_done = 1;
 		OSMO_ASSERT(ul_tbf);
 		dump_assignment(ul_tbf, "UL");
 
@@ -327,7 +328,8 @@
 		int tfi;
 		uint8_t ts_no, trx_no;
 
-		gprs_rlcmac_tbf *ul_tbf, *dl_tbf;
+		gprs_rlcmac_ul_tbf *ul_tbf;
+		gprs_rlcmac_dl_tbf *dl_tbf;
 
 		bts = the_bts.bts_data();
 		bts->alloc_algorithm = alloc_algorithm_b;
@@ -355,7 +357,7 @@
 		OSMO_ASSERT(ul_tbf);
 		ul_tbf->m_tlli = 0x23;
 		ul_tbf->m_tlli_valid = true;
-		ul_tbf->dir.ul.contention_resolution_done = 1;
+		ul_tbf->m_contention_resolution_done = 1;
 
 		OSMO_ASSERT(dl_tbf->first_common_ts == ul_tbf->first_common_ts);