Merge branch 'sysmocom/tbf-split'
diff --git a/src/Makefile.am b/src/Makefile.am
index beee4c0..d1ed701 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -42,6 +42,8 @@
 	pcu_l1_if.cpp \
 	pcu_vty.c \
 	tbf.cpp \
+	tbf_ul.cpp \
+	tbf_dl.cpp \
 	bts.cpp \
 	poll_controller.cpp \
 	encoding.cpp \
diff --git a/src/bts.cpp b/src/bts.cpp
index 73344a3..8665962 100644
--- a/src/bts.cpp
+++ b/src/bts.cpp
@@ -467,7 +467,7 @@
 			return -EBUSY;
 		}
 		tbf->ta = qta >> 2;
-		tbf_new_state(tbf, GPRS_RLCMAC_FLOW);
+		tbf->set_state(GPRS_RLCMAC_FLOW);
 		tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_CCCH);
 		tbf_timer_start(tbf, 3169, m_bts.t3169, 0);
 		LOGP(DRLCMAC, LOGL_DEBUG, "%s [UPLINK] START\n",
@@ -515,7 +515,7 @@
 		dl_tbf->ta = old_tbf->ta;
 		dl_tbf->was_releasing = dl_tbf->state_is(GPRS_RLCMAC_WAIT_RELEASE);
 		/* change state */
-		tbf_new_state(dl_tbf, GPRS_RLCMAC_ASSIGN);
+		dl_tbf->set_state(GPRS_RLCMAC_ASSIGN);
 		dl_tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_PACCH);
 		/* start timer */
 		tbf_timer_start(dl_tbf, 0, Tassign_pacch);
@@ -527,7 +527,7 @@
 		}
 		dl_tbf->was_releasing = dl_tbf->state_is(GPRS_RLCMAC_WAIT_RELEASE);
 		/* change state */
-		tbf_new_state(dl_tbf, GPRS_RLCMAC_ASSIGN);
+		dl_tbf->set_state(GPRS_RLCMAC_ASSIGN);
 		dl_tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_CCCH);
 		dl_tbf->assign_imsi(imsi);
 		/* send immediate assignment */
@@ -774,7 +774,7 @@
 				"TBF is gone TLLI=0x%08x\n", tlli);
 			return;
 		}
-		tbf_new_state(tbf, GPRS_RLCMAC_FLOW);
+		tbf->set_state(GPRS_RLCMAC_FLOW);
 		/* stop pending assignment timer */
 		tbf->stop_timer();
 		if ((tbf->state_flags &
@@ -800,7 +800,7 @@
 				"TBF is gone TLLI=0x%08x\n", tlli);
 			return;
 		}
-		tbf_new_state(tbf, GPRS_RLCMAC_FLOW);
+		tbf->set_state(GPRS_RLCMAC_FLOW);
 		if ((tbf->state_flags &
 			(1 << GPRS_RLCMAC_FLAG_TO_UL_ASS))) {
 			tbf->state_flags &=
diff --git a/src/gprs_rlcmac.h b/src/gprs_rlcmac.h
index 148250a..6f8a7a4 100644
--- a/src/gprs_rlcmac.h
+++ b/src/gprs_rlcmac.h
@@ -62,10 +62,10 @@
 	uint8_t block_payload;
 };
 
-int gprs_rlcmac_received_lost(struct gprs_rlcmac_tbf *tbf, uint16_t received,
+int gprs_rlcmac_received_lost(struct gprs_rlcmac_dl_tbf *tbf, uint16_t received,
 	uint16_t lost);
 
-int gprs_rlcmac_lost_rep(struct gprs_rlcmac_tbf *tbf);
+int gprs_rlcmac_lost_rep(struct gprs_rlcmac_dl_tbf *tbf);
 
 int gprs_rlcmac_meas_rep(Packet_Measurement_Report_t *pmr);
 
@@ -73,7 +73,7 @@
 
 int gprs_rlcmac_rssi_rep(struct gprs_rlcmac_tbf *tbf);
 
-int gprs_rlcmac_dl_bw(struct gprs_rlcmac_tbf *tbf, uint16_t octets);
+int gprs_rlcmac_dl_bw(struct gprs_rlcmac_dl_tbf *tbf, uint16_t octets);
 
 /* TS 44.060 Section 10.4.7 Table 10.4.7.1: Payload Type field */
 enum gprs_rlcmac_block_type {
diff --git a/src/gprs_rlcmac_meas.cpp b/src/gprs_rlcmac_meas.cpp
index 30958fc..5a2e38e 100644
--- a/src/gprs_rlcmac_meas.cpp
+++ b/src/gprs_rlcmac_meas.cpp
@@ -112,10 +112,10 @@
  */
 
 /* Lost frames reported from RLCMAC layer */
-int gprs_rlcmac_received_lost(struct gprs_rlcmac_tbf *tbf, uint16_t received,
+int gprs_rlcmac_received_lost(struct gprs_rlcmac_dl_tbf *tbf, uint16_t received,
 	uint16_t lost)
 {
-	struct timeval now_tv, *loss_tv = &tbf->meas.dl_loss_tv;
+	struct timeval now_tv, *loss_tv = &tbf->m_bw.dl_loss_tv;
 	uint32_t elapsed;
 	uint16_t sum = received + lost;
 
@@ -126,8 +126,8 @@
 	LOGP(DRLCMACMEAS, LOGL_DEBUG, "DL Loss of TLLI 0x%08x: Received: %4d  "
 		"Lost: %4d  Sum: %4d\n", tbf->tlli(), received, lost, sum);
 
-	tbf->meas.dl_loss_received += received;
-	tbf->meas.dl_loss_lost += lost;
+	tbf->m_bw.dl_loss_received += received;
+	tbf->m_bw.dl_loss_lost += lost;
 
 	gettimeofday(&now_tv, NULL);
 	elapsed = ((now_tv.tv_sec - loss_tv->tv_sec) << 7)
@@ -139,16 +139,16 @@
 
 	/* reset lost values and timestamp */
 	memcpy(loss_tv, &now_tv, sizeof(struct timeval));
-	tbf->meas.dl_loss_received = 0;
-	tbf->meas.dl_loss_lost = 0;
+	tbf->m_bw.dl_loss_received = 0;
+	tbf->m_bw.dl_loss_lost = 0;
 
 	return 0;
 }
 
 /* Give Lost report */
-int gprs_rlcmac_lost_rep(struct gprs_rlcmac_tbf *tbf)
+int gprs_rlcmac_lost_rep(struct gprs_rlcmac_dl_tbf *tbf)
 {
-	uint16_t sum = tbf->meas.dl_loss_lost + tbf->meas.dl_loss_received;
+	uint16_t sum = tbf->m_bw.dl_loss_lost + tbf->m_bw.dl_loss_received;
 
 	/* No measurement values */
 	if (!sum)
@@ -156,7 +156,7 @@
 
 	LOGP(DRLCMACMEAS, LOGL_INFO, "DL packet loss of IMSI=%s / TLLI=0x%08x: "
 		"%d%%\n", tbf->imsi(), tbf->tlli(),
-		tbf->meas.dl_loss_lost * 100 / sum);
+		tbf->m_bw.dl_loss_lost * 100 / sum);
 
 	return 0;
 }
@@ -166,12 +166,12 @@
  * downlink bandwidth
  */
 
-int gprs_rlcmac_dl_bw(struct gprs_rlcmac_tbf *tbf, uint16_t octets)
+int gprs_rlcmac_dl_bw(struct gprs_rlcmac_dl_tbf *tbf, uint16_t octets)
 {
-	struct timeval now_tv, *bw_tv = &tbf->meas.dl_bw_tv;
+	struct timeval now_tv, *bw_tv = &tbf->m_bw.dl_bw_tv;
 	uint32_t elapsed;
 
-	tbf->meas.dl_bw_octets += octets;
+	tbf->m_bw.dl_bw_octets += octets;
 
 	gettimeofday(&now_tv, NULL);
 	elapsed = ((now_tv.tv_sec - bw_tv->tv_sec) << 7)
@@ -181,11 +181,11 @@
 
 	LOGP(DRLCMACMEAS, LOGL_INFO, "DL Bandwitdh of IMSI=%s / TLLI=0x%08x: "
 		"%d KBits/s\n", tbf->imsi(), tbf->tlli(),
-		tbf->meas.dl_bw_octets / elapsed);
+		tbf->m_bw.dl_bw_octets / elapsed);
 
 	/* reset bandwidth values timestamp */
 	memcpy(bw_tv, &now_tv, sizeof(struct timeval));
-	tbf->meas.dl_bw_octets = 0;
+	tbf->m_bw.dl_bw_octets = 0;
 
 	return 0;
 }
diff --git a/src/tbf.cpp b/src/tbf.cpp
index 22b8ce0..fd25e8d 100644
--- a/src/tbf.cpp
+++ b/src/tbf.cpp
@@ -36,185 +36,21 @@
 #include <errno.h>
 #include <string.h>
 
-/* After sending these frames, we poll for ack/nack. */
-#define POLL_ACK_AFTER_FRAMES 20
-/* After receiving these frames, we send ack/nack. */
-#define SEND_ACK_AFTER_FRAMES 20
-
-
-static const struct gprs_rlcmac_cs gprs_rlcmac_cs[] = {
-/*	frame length	data block	max payload */
-	{ 0,		0,		0  },
-	{ 23,		23,		20 }, /* CS-1 */
-	{ 34,		33,		30 }, /* CS-2 */
-	{ 40,		39,		36 }, /* CS-3 */
-	{ 54,		53,		50 }, /* CS-4 */
-};
-
-extern "C" {
-int bssgp_tx_llc_discarded(struct bssgp_bvc_ctx *bctx, uint32_t tlli,
-                           uint8_t num_frames, uint32_t num_octets);
-}
-
 extern void *tall_pcu_ctx;
 
 static void tbf_timer_cb(void *_tbf);
 
-inline gprs_rlcmac_bts *gprs_rlcmac_tbf::bts_data() const
+gprs_rlcmac_bts *gprs_rlcmac_tbf::bts_data() const
 {
 	return bts->bts_data();
 }
 
-static inline void tbf_update_ms_class(struct gprs_rlcmac_tbf *tbf,
-					const uint8_t ms_class)
-{
-	if (!tbf->ms_class && ms_class)
-		tbf->ms_class = ms_class;
-}
-
 void gprs_rlcmac_tbf::assign_imsi(const char *imsi)
 {
 	strncpy(m_imsi, imsi, sizeof(m_imsi));
 	m_imsi[sizeof(m_imsi) - 1] = '\0';
 }
 
-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_dl_tbf::append_data(const uint8_t ms_class,
-				const uint16_t pdu_delay_csec,
-				const uint8_t *data, const uint16_t len)
-{
-	LOGP(DRLCMAC, LOGL_INFO, "%s append\n", tbf_name(this));
-	if (state_is(GPRS_RLCMAC_WAIT_RELEASE)) {
-		LOGP(DRLCMAC, LOGL_DEBUG,
-			"%s in WAIT RELEASE state "
-			"(T3193), so reuse TBF\n", tbf_name(this));
-		tbf_update_ms_class(this, ms_class);
-		reuse_tbf(data, len);
-	} else {
-		/* the TBF exists, so we must write it in the queue
-		 * we prepend lifetime in front of PDU */
-		struct timeval *tv;
-		struct msgb *llc_msg = msgb_alloc(len + sizeof(*tv) * 2,
-			"llc_pdu_queue");
-		if (!llc_msg)
-			return -ENOMEM;
-		tv = (struct timeval *)msgb_put(llc_msg, sizeof(*tv));
-		gprs_llc::calc_pdu_lifetime(bts, pdu_delay_csec, tv);
-		tv = (struct timeval *)msgb_put(llc_msg, sizeof(*tv));
-		gettimeofday(tv, NULL);
-		memcpy(msgb_put(llc_msg, len), data, len);
-		m_llc.enqueue(llc_msg);
-		tbf_update_ms_class(this, ms_class);
-	}
-
-	return 0;
-}
-
-static int tbf_new_dl_assignment(struct gprs_rlcmac_bts *bts,
-				const char *imsi,
-				const uint32_t tlli, const uint8_t ms_class,
-				const uint8_t *data, const uint16_t len)
-{
-	uint8_t trx, ta, ss;
-	int8_t use_trx;
-	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"
-	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_ul_tbf = ul_tbf;
-	} else {
-		use_trx = -1;
-		/* we already have an uplink TBF, so we use that TA */
-		if (ul_tbf)
-			ta = ul_tbf->ta;
-		else {
-			/* recall TA */
-			rc = bts->bts->timing_advance()->recall(tlli);
-			if (rc < 0) {
-				LOGP(DRLCMAC, LOGL_NOTICE, "TA unknown"
-					", assuming 0\n");
-				ta = 0;
-			} else
-				ta = rc;
-		}
-		ss = 1; /* PCH assignment only allows one timeslot */
-		old_ul_tbf = NULL;
-	}
-
-	// Create new TBF (any TRX)
-#warning "Copy and paste with alloc_ul_tbf"
-	tfi = bts->bts->tfi_find_free(GPRS_RLCMAC_DL_TBF, &trx, use_trx);
-	if (tfi < 0) {
-		LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH resource\n");
-		/* FIXME: send reject */
-		return -EBUSY;
-	}
-	/* set number of downlink slots according to multislot class */
-	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;
-	}
-	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(dl_tbf));
-
-	/* new TBF, so put first frame */
-	dl_tbf->m_llc.put_frame(data, len);
-	dl_tbf->bts->llc_frame_sched();
-
-	/* Store IMSI for later look-up and PCH retransmission */
-	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. */
-	dl_tbf->bts->trigger_dl_ass(dl_tbf, old_ul_tbf, imsi);
-	return 0;
-}
-
-/**
- * TODO: split into unit test-able parts...
- */
-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_dl_tbf *dl_tbf;
-
-	/* check for existing TBF */
-	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)
-			dl_tbf->assign_imsi(imsi);
-		return rc;
-	} 
-
-	return tbf_new_dl_assignment(bts, imsi, tlli, ms_class, data, len);
-}
-
 gprs_rlcmac_ul_tbf *tbf_alloc_ul(struct gprs_rlcmac_bts *bts,
 	int8_t use_trx, uint8_t ms_class,
 	uint32_t tlli, uint8_t ta, struct gprs_rlcmac_tbf *dl_tbf)
@@ -242,7 +78,7 @@
 	tbf->m_tlli_valid = 1; /* no contention resolution */
 	tbf->m_contention_resolution_done = 1;
 	tbf->ta = ta; /* use current TA */
-	tbf_new_state(tbf, GPRS_RLCMAC_ASSIGN);
+	tbf->set_state(GPRS_RLCMAC_ASSIGN);
 	tbf->state_flags |= (1 << GPRS_RLCMAC_FLAG_PACCH);
 	tbf_timer_start(tbf, 3169, bts->t3169, 0);
 
@@ -268,7 +104,10 @@
 {
 	/* Give final measurement report */
 	gprs_rlcmac_rssi_rep(tbf);
-	gprs_rlcmac_lost_rep(tbf);
+	if (tbf->direction == GPRS_RLCMAC_DL_TBF) {
+		gprs_rlcmac_dl_tbf *dl_tbf = static_cast<gprs_rlcmac_dl_tbf *>(tbf);
+		gprs_rlcmac_lost_rep(dl_tbf);
+	}
 
 	LOGP(DRLCMAC, LOGL_INFO, "%s free\n", tbf_name(tbf));
 	if (tbf->ul_ass_state != GPRS_RLCMAC_UL_ASS_NONE)
@@ -335,7 +174,7 @@
 	return 0;
 }
 
-static const char *tbf_state_name[] = {
+const char *gprs_rlcmac_tbf::tbf_state_name[] = {
 	"NULL",
 	"ASSIGN",
 	"FLOW",
@@ -344,15 +183,6 @@
 	"RELEASING",
 };
 
-void tbf_new_state(struct gprs_rlcmac_tbf *tbf,
-	enum gprs_rlcmac_tbf_state state)
-{
-	LOGP(DRLCMAC, LOGL_DEBUG, "%s changes state from %s to %s\n",
-		tbf_name(tbf),
-		tbf_state_name[tbf->state], tbf_state_name[state]);
-	tbf->set_state(state);
-}
-
 void tbf_timer_start(struct gprs_rlcmac_tbf *tbf, unsigned int T,
 			unsigned int seconds, unsigned int microseconds)
 {
@@ -409,7 +239,7 @@
 			if (ul_tbf->m_n3103 == ul_tbf->bts->bts_data()->n3103) {
 				LOGP(DRLCMAC, LOGL_NOTICE,
 					"- N3103 exceeded\n");
-				tbf_new_state(ul_tbf, GPRS_RLCMAC_RELEASING);
+				ul_tbf->set_state(GPRS_RLCMAC_RELEASING);
 				tbf_timer_start(ul_tbf, 3169, ul_tbf->bts->bts_data()->t3169, 0);
 				return;
 			}
@@ -428,7 +258,7 @@
 		n3105++;
 		if (n3105 == bts_data()->n3105) {
 			LOGP(DRLCMAC, LOGL_NOTICE, "- N3105 exceeded\n");
-			tbf_new_state(this, GPRS_RLCMAC_RELEASING);
+			set_state(GPRS_RLCMAC_RELEASING);
 			tbf_timer_start(this, 3195, bts_data()->t3195, 0);
 			return;
 		}
@@ -446,7 +276,7 @@
 		n3105++;
 		if (n3105 == bts->bts_data()->n3105) {
 			LOGP(DRLCMAC, LOGL_NOTICE, "- N3105 exceeded\n");
-			tbf_new_state(this, GPRS_RLCMAC_RELEASING);
+			set_state(GPRS_RLCMAC_RELEASING);
 			tbf_timer_start(this, 3195, bts_data()->t3195, 0);
 			return;
 		}
@@ -464,7 +294,7 @@
 		dl_tbf->n3105++;
 		if (dl_tbf->n3105 == dl_tbf->bts->bts_data()->n3105) {
 			LOGP(DRLCMAC, LOGL_NOTICE, "- N3105 exceeded\n");
-			tbf_new_state(dl_tbf, GPRS_RLCMAC_RELEASING);
+			dl_tbf->set_state(GPRS_RLCMAC_RELEASING);
 			tbf_timer_start(dl_tbf, 3195, dl_tbf->bts_data()->t3195, 0);
 			return;
 		}
@@ -519,9 +349,7 @@
 	}
 
 	/* set timestamp */
-	gettimeofday(&tbf->meas.dl_bw_tv, NULL);
 	gettimeofday(&tbf->meas.rssi_tv, NULL);
-	gettimeofday(&tbf->meas.dl_loss_tv, NULL);
 
 	tbf->m_llc.init();
 	return 0;
@@ -591,6 +419,9 @@
 	llist_add(&tbf->list.list, &bts->dl_tbfs);
 	tbf->bts->tbf_dl_created();
 
+	gettimeofday(&tbf->m_bw.dl_bw_tv, NULL);
+	gettimeofday(&tbf->m_bw.dl_loss_tv, NULL);
+
 	return tbf;
 }
 
@@ -628,7 +459,7 @@
 				if (!dl_tbf->upgrade_to_multislot) {
 					/* change state to FLOW, so scheduler
 					 * will start transmission */
-					tbf_new_state(dl_tbf, GPRS_RLCMAC_FLOW);
+					dl_tbf->set_state(GPRS_RLCMAC_FLOW);
 					break;
 				}
 
@@ -687,519 +518,6 @@
 	return 0;
 }
 
-struct msgb *gprs_rlcmac_tbf::llc_dequeue(bssgp_bvc_ctx *bctx)
-{
-	struct msgb *msg;
-	struct timeval *tv, tv_now;
-	uint32_t octets = 0, frames = 0;
-
-	gettimeofday(&tv_now, NULL);
-
-	while ((msg = m_llc.dequeue())) {
-		tv = (struct timeval *)msg->data;
-		msgb_pull(msg, sizeof(*tv));
-		msgb_pull(msg, sizeof(*tv));
-
-		if (gprs_llc::is_frame_expired(&tv_now, tv)) {
-			LOGP(DRLCMACDL, LOGL_NOTICE, "%s Discarding LLC PDU "
-				"because lifetime limit reached. Queue size %zu\n",
-				tbf_name(this), m_llc.m_queue_size);
-			bts->llc_timedout_frame();
-			frames++;
-			octets += msg->len;
-			msgb_free(msg);
-			continue;
-		}
-		break;
-	}
-
-	if (frames) {
-		if (frames > 0xff)
-			frames = 0xff;
-		if (octets > 0xffffff)
-			octets = 0xffffff;
-		bssgp_tx_llc_discarded(bctx, m_tlli, frames, octets);
-	}
-
-	return msg;
-}
-
-/*
- * Store received block data in LLC message(s) and forward to SGSN
- * if complete.
- */
-int gprs_rlcmac_tbf::assemble_forward_llc(const gprs_rlc_data *_data)
-{
-	const uint8_t *data = _data->block;
-	uint8_t len = _data->len;
-	const struct rlc_ul_header *rh = (const struct rlc_ul_header *) data;
-	uint8_t e, m;
-	struct rlc_li_field *li;
-	uint8_t frame_offset[16], offset = 0, chunk;
-	int i, frames = 0;
-
-	LOGP(DRLCMACUL, LOGL_DEBUG, "- Assembling frames: (len=%d)\n", len);
-
-	data += 3;
-	len -= 3;
-	e = rh->e; /* if extended */
-	m = 1; /* more frames, that means: the first frame */
-
-	/* Parse frame offsets from length indicator(s), if any. */
-	while (1) {
-		if (frames == (int)sizeof(frame_offset)) {
-			LOGP(DRLCMACUL, LOGL_ERROR, "%s too many frames in "
-				"block\n", tbf_name(this));
-			return -EINVAL;
-		}
-		frame_offset[frames++] = offset;
-		LOGP(DRLCMACUL, LOGL_DEBUG, "-- Frame %d starts at offset "
-			"%d\n", frames, offset);
-		if (!len)
-			break;
-		/* M == 0 and E == 0 is not allowed in this version. */
-		if (!m && !e) {
-			LOGP(DRLCMACUL, LOGL_NOTICE, "%s UL DATA "
-				"ignored, because M='0' and E='0'.\n",
-				tbf_name(this));
-			return 0;
-		}
-		/* no more frames in this segment */
-		if (e) {
-			break;
-		}
-		/* There is a new frame and an LI that delimits it. */
-		if (m) {
-			li = (struct rlc_li_field *)data;
-			LOGP(DRLCMACUL, LOGL_DEBUG, "-- Delimiter len=%d\n",
-				li->li);
-			/* Special case: LI == 0
-			 * If the last segment would fit precisely into the
-			 * rest of the RLC MAC block, there would be no way
-			 * to delimit that this segment ends and is not
-			 * continued in the next block.
-			 * The special LI (0) is used to force the segment to
-			 * extend into the next block, so it is delimited there.
-			 * This LI must be skipped. Also it is the last LI.
-			 */
-			if (li->li == 0) {
-				data++;
-				len--;
-				m = 1; /* M is ignored, we know there is more */
-				break; /* handle E as '1', so we break! */
-			}
-			e = li->e;
-			m = li->m;
-			offset += li->li;
-			data++;
-			len--;
-			continue;
-		}
-	}
-	if (!m) {
-		LOGP(DRLCMACUL, LOGL_DEBUG, "- Last frame carries spare "
-			"data\n");
-	}
-
-	LOGP(DRLCMACUL, LOGL_DEBUG, "- Data length after length fields: %d\n",
-		len);
-	/* TLLI */
-	if (rh->ti) {
-		if (len < 4) {
-			LOGP(DRLCMACUL, LOGL_NOTICE, "%s UL DATA TLLI out of "
-				"frame border\n", tbf_name(this));
-			return -EINVAL;
-		}
-		data += 4;
-		len -= 4;
-		LOGP(DRLCMACUL, LOGL_DEBUG, "- Length after skipping TLLI: "
-			"%d\n", len);
-	}
-
-	/* PFI */
-	if (rh->pi) {
-		LOGP(DRLCMACUL, LOGL_ERROR, "ERROR: PFI not supported, "
-			"please disable in SYSTEM INFORMATION\n");
-		if (len < 1) {
-			LOGP(DRLCMACUL, LOGL_NOTICE, "%s UL DATA PFI out of "
-				"frame border\n", tbf_name(this));
-			return -EINVAL;
-		}
-		data++;
-		len--;
-		LOGP(DRLCMACUL, LOGL_DEBUG, "- Length after skipping PFI: "
-			"%d\n", len);
-	}
-
-	/* Now we have:
-	 * - a list of frames offsets: frame_offset[]
-	 * - number of frames: i
-	 * - m == 0: Last frame carries spare data (end of TBF).
-	 */
-
-	/* Check if last offset would exceed frame. */
-	if (offset > len) {
-		LOGP(DRLCMACUL, LOGL_NOTICE, "%s UL DATA ignored, "
-			"because LI delimits data that exceeds block size.\n",
-			tbf_name(this));
-		return -EINVAL;
-	}
-
-	/* create LLC frames */
-	for (i = 0; i < frames; i++) {
-		/* last frame ? */
-		if (i == frames - 1) {
-			/* no more data in last frame */
-			if (!m)
-				break;
-			/* data until end of frame */
-			chunk = len - frame_offset[i];
-		} else {
-			/* data until next frame */
-			chunk = frame_offset[i + 1] - frame_offset[i];
-		}
-		if (!m_llc.fits_in_current_frame(chunk)) {
-			LOGP(DRLCMACUL, LOGL_NOTICE, "%s LLC frame exceeds "
-				"maximum size %u.\n", tbf_name(this),
-				m_llc.remaining_space());
-			chunk = m_llc.remaining_space();
-		}
-		m_llc.append_frame(data + frame_offset[i], chunk);
-		m_llc.consume(chunk);
-		/* not last frame. */
-		if (i != frames - 1) {
-			/* send frame to SGSN */
-			LOGP(DRLCMACUL, LOGL_INFO, "%s complete UL frame len=%d\n",
-				tbf_name(this) , m_llc.frame_length());
-			snd_ul_ud();
-			m_llc.reset();
-		/* also check if CV==0, because the frame may fill up the
-		 * block precisely, then it is also complete. normally the
-		 * frame would be extended into the next block with a 0-length
-		 * delimiter added to this block. */
-		} else if (rh->cv == 0) {
-			/* send frame to SGSN */
-			LOGP(DRLCMACUL, LOGL_INFO, "%s complete UL frame "
-				"that fits precisely in last block: "
-				"len=%d\n", tbf_name(this), m_llc.frame_length());
-			snd_ul_ud();
-			m_llc.reset();
-		}
-	}
-
-	return 0;
-}
-
-/*
- * Create DL data block
- * The messages are fragmented and forwarded as data blocks.
- */
-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),
-		m_window.v_a(), m_window.v_s());
-
-do_resend:
-	/* check if there is a block with negative acknowledgement */
-	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 */
-		m_window.m_v_b.mark_unacked(resend_bsn);
-		bts->rlc_resent();
-		return create_dl_acked_block(fn, ts, resend_bsn, false);
-	}
-
-	/* if the window has stalled, or transfer is complete,
-	 * send an unacknowledged block */
-	if (state_is(GPRS_RLCMAC_FINISHED) || dl_window_stalled()) {
-		if (state_is(GPRS_RLCMAC_FINISHED)) {
-			LOGP(DRLCMACDL, LOGL_DEBUG, "- Restarting at BSN %d, "
-				"because all blocks have been transmitted.\n",
-					m_window.v_a());
-			bts->rlc_restarted();
-		} else {
-			LOGP(DRLCMACDL, LOGL_NOTICE, "- Restarting at BSN %d, "
-				"because all window is stalled.\n",
-					m_window.v_a());
-			bts->rlc_stalled();
-		}
-		/* If V(S) == V(A) and finished state, we would have received
-		 * acknowledgement of all transmitted block. In this case we
-		 * would have transmitted the final block, and received ack
-		 * 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 (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 = 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 = 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. */
-		if (resend == 0) {
-			LOGP(DRLCMACDL, LOGL_ERROR, "Software error: "
-				"There are no unacknowledged blocks, but V(A) "
-				" != V(S). PLEASE FIX!\n");
-			/* we just send final block again */
-			int16_t index = m_window.v_s_mod(-1);
-			return create_dl_acked_block(fn, ts, index, false);
-		}
-		goto do_resend;
-	}
-
-	return create_new_bsn(fn, 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;
-	struct msgb *msg;
-	uint8_t *delimiter, *data, *e_pointer;
-	uint16_t space, chunk;
-	gprs_rlc_data *rlc_data;
-	bool first_fin_ack = false;
-	const uint16_t bsn = m_window.v_s();
-
-	LOGP(DRLCMACDL, LOGL_DEBUG, "- Sending new block at BSN %d\n",
-		m_window.v_s());
-
-#warning "Selection of the CS doesn't belong here"
-	if (cs == 0) {
-		cs = bts_data()->initial_cs_dl;
-		if (cs < 1 || cs > 4)
-			cs = 1;
-	}
-	/* total length of block, including spare bits */
-	const uint8_t block_length = gprs_rlcmac_cs[cs].block_length;
-	/* length of usable data of block, w/o spare bits, inc. MAC */
-	const uint8_t block_data_len = gprs_rlcmac_cs[cs].block_data;
-
-	/* now we still have untransmitted LLC data, so we fill mac block */
-	rlc_data = m_rlc.block(bsn);
-	data = rlc_data->prepare(block_data_len);
-
-	rh = (struct rlc_dl_header *)data;
-	rh->pt = 0; /* Data Block */
-	rh->rrbp = rh->s_p = 0; /* Polling, set later, if required */
-	rh->usf = 7; /* will be set at scheduler */
-	rh->pr = 0; /* FIXME: power reduction */
-	rh->tfi = m_tfi; /* TFI */
-	rh->fbi = 0; /* Final Block Indicator, set late, if true */
-	rh->bsn = bsn; /* Block Sequence Number */
-	rh->e = 0; /* Extension bit, maybe set later */
-	e_pointer = data + 2; /* points to E of current chunk */
-	data += sizeof(*rh);
-	delimiter = data; /* where next length header would be stored */
-	space = block_data_len - sizeof(*rh);
-	while (1) {
-		chunk = m_llc.chunk_size();
-		/* if chunk will exceed block limit */
-		if (chunk > space) {
-			LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d "
-				"larger than space (%d) left in block: copy "
-				"only remaining space, and we are done\n",
-				chunk, space);
-			/* block is filled, so there is no extension */
-			*e_pointer |= 0x01;
-			/* fill only space */
-			m_llc.consume(data, space);
-			/* return data block as message */
-			break;
-		}
-		/* if FINAL chunk would fit precisely in space left */
-		if (chunk == space && llist_empty(&m_llc.queue)) {
-			LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d "
-				"would exactly fit into space (%d): because "
-				"this is a final block, we don't add length "
-				"header, and we are done\n", chunk, space);
-			LOGP(DRLCMACDL, LOGL_INFO, "Complete DL frame for "
-				"%s that fits precisely in last block: "
-				"len=%d\n", tbf_name(this), m_llc.frame_length());
-			gprs_rlcmac_dl_bw(this, m_llc.frame_length());
-			/* block is filled, so there is no extension */
-			*e_pointer |= 0x01;
-			/* fill space */
-			m_llc.consume(data, space);
-			m_llc.reset();
-			/* final block */
-			rh->fbi = 1; /* we indicate final block */
-			tbf_new_state(this, GPRS_RLCMAC_FINISHED);
-			/* return data block as message */
-			break;
-		}
-		/* if chunk would fit exactly in space left */
-		if (chunk == space) {
-			LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d "
-				"would exactly fit into space (%d): add length "
-				"header with LI=0, to make frame extend to "
-				"next block, and we are done\n", chunk, space);
-			/* make space for delimiter */
-			if (delimiter != data)
-				memmove(delimiter + 1, delimiter,
-					data - delimiter);
-			data++;
-			space--;
-			/* add LI with 0 length */
-			li = (struct rlc_li_field *)delimiter;
-			li->e = 1; /* not more extension */
-			li->m = 0; /* shall be set to 0, in case of li = 0 */
-			li->li = 0; /* chunk fills the complete space */
-			// no need to set e_pointer nor increase delimiter
-			/* fill only space, which is 1 octet less than chunk */
-			m_llc.consume(data, space);
-			/* return data block as message */
-			break;
-		}
-		LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d is less "
-			"than remaining space (%d): add length header to "
-			"to delimit LLC frame\n", chunk, space);
-		/* the LLC frame chunk ends in this block */
-		/* make space for delimiter */
-		if (delimiter != data)
-			memmove(delimiter + 1, delimiter, data - delimiter);
-		data++;
-		space--;
-		/* add LI to delimit frame */
-		li = (struct rlc_li_field *)delimiter;
-		li->e = 0; /* Extension bit, maybe set later */
-		li->m = 0; /* will be set later, if there is more LLC data */
-		li->li = chunk; /* length of chunk */
-		e_pointer = delimiter; /* points to E of current delimiter */
-		delimiter++;
-		/* copy (rest of) LLC frame to space and reset later */
-		m_llc.consume(data, chunk);
-		data += chunk;
-		space -= chunk;
-		LOGP(DRLCMACDL, LOGL_INFO, "Complete DL frame for %s"
-			"len=%d\n", tbf_name(this), m_llc.frame_length());
-		gprs_rlcmac_dl_bw(this, m_llc.frame_length());
-		m_llc.reset();
-		/* dequeue next LLC frame, if any */
-		msg = llc_dequeue(gprs_bssgp_pcu_current_bctx());
-		if (msg) {
-			LOGP(DRLCMACDL, LOGL_INFO, "- Dequeue next LLC for "
-				"%s (len=%d)\n", tbf_name(this), msg->len);
-			m_llc.put_frame(msg->data, msg->len);
-			bts->llc_frame_sched();
-			msgb_free(msg);
-		}
-		/* if we have more data and we have space left */
-		if (space > 0 && m_llc.frame_length()) {
-			li->m = 1; /* we indicate more frames to follow */
-			continue;
-		}
-		/* if we don't have more LLC frames */
-		if (!m_llc.frame_length()) {
-			LOGP(DRLCMACDL, LOGL_DEBUG, "-- Final block, so we "
-				"done.\n");
-			li->e = 1; /* we cannot extend */
-			rh->fbi = 1; /* we indicate final block */
-			first_fin_ack = true;
-				/* + 1 indicates: first final ack */
-			tbf_new_state(this, GPRS_RLCMAC_FINISHED);
-			break;
-		}
-		/* we have no space left */
-		LOGP(DRLCMACDL, LOGL_DEBUG, "-- No space left, so we are "
-			"done.\n");
-		li->e = 1; /* we cannot extend */
-		break;
-	}
-	LOGP(DRLCMACDL, LOGL_DEBUG, "data block: %s\n",
-		osmo_hexdump(rlc_data->block, block_length));
-#warning "move this up?"
-	rlc_data->len = block_length;
-	/* raise send state and set ack state array */
-	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_dl_tbf::create_dl_acked_block(
-				const uint32_t fn, const uint8_t ts,
-				const int index, const bool first_fin_ack)
-{
-	uint8_t *data;
-	struct rlc_dl_header *rh;
-	struct msgb *dl_msg;
-	uint8_t len;
-
-	/* get data and header from current block */
-	data = m_rlc.block(index)->block;
-	len = m_rlc.block(index)->len;
-	rh = (struct rlc_dl_header *)data;
-
-	/* Clear Polling, if still set in history buffer */
-	rh->s_p = 0;
-		
-	/* poll after POLL_ACK_AFTER_FRAMES frames, or when final block is tx.
-	 */
-	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");
-		} else {
-			LOGP(DRLCMACDL, LOGL_DEBUG, "- Scheduling Ack/Nack "
-				"polling, because %d blocks sent.\n",
-				POLL_ACK_AFTER_FRAMES);
-		}
-		/* scheduling not possible, because: */
-		if (poll_state != GPRS_RLCMAC_POLL_NONE)
-			LOGP(DRLCMACDL, LOGL_DEBUG, "Polling is already "
-				"sheduled for %s, so we must wait for "
-				"requesting downlink ack\n", tbf_name(this));
-		else if (control_ts != ts)
-			LOGP(DRLCMACDL, LOGL_DEBUG, "Polling cannot be "
-				"sheduled in this TS %d, waiting for "
-				"TS %d\n", ts, control_ts);
-#warning "What happens to the first_fin_ack in case something is already scheduled?"
-		else if (bts->sba()->find(trx->trx_no, ts, (fn + 13) % 2715648))
-			LOGP(DRLCMACDL, LOGL_DEBUG, "Polling cannot be "
-				"sheduled, because single block alllocation "
-				"already exists\n");
-		else  {
-			LOGP(DRLCMACDL, LOGL_DEBUG, "Polling sheduled in this "
-				"TS %d\n", ts);
-			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);
-
-			/* schedule polling */
-			poll_state = GPRS_RLCMAC_POLL_SCHED;
-			poll_fn = (fn + 13) % 2715648;
-
-			/* set polling in header */
-			rh->rrbp = 0; /* N+13 */
-			rh->s_p = 1; /* Polling */
-		}
-	}
-
-	/* return data block as message */
-	dl_msg = msgb_alloc(len, "rlcmac_dl_data");
-	if (!dl_msg)
-		return NULL;
-
-	/* Increment TX-counter */
-	m_tx_counter++;
-
-	memcpy(msgb_put(dl_msg, len), data, len);
-	bts->rlc_sent();
-
-	return dl_msg;
-}
-
 struct msgb *gprs_rlcmac_tbf::create_dl_ass(uint32_t fn)
 {
 	struct msgb *msg;
@@ -1280,7 +598,7 @@
 		dl_ass_state = GPRS_RLCMAC_DL_ASS_WAIT_ACK;
 	} else {
 		dl_ass_state = GPRS_RLCMAC_DL_ASS_NONE;
-		tbf_new_state(new_dl_tbf, GPRS_RLCMAC_FLOW);
+		new_dl_tbf->set_state(GPRS_RLCMAC_FLOW);
 		tbf_assign_control_ts(new_dl_tbf);
 		/* stop pending assignment timer */
 		new_dl_tbf->stop_timer();
@@ -1353,155 +671,6 @@
 	return msg;
 }
 
-struct msgb *gprs_rlcmac_ul_tbf::create_ul_ack(uint32_t fn)
-{
-	int final = (state_is(GPRS_RLCMAC_FINISHED));
-	struct msgb *msg;
-
-	if (final) {
-		if (poll_state != GPRS_RLCMAC_POLL_NONE) {
-			LOGP(DRLCMACUL, LOGL_DEBUG, "Polling is already "
-				"sheduled for %s, so we must wait for "
-				"final uplink ack...\n", tbf_name(this));
-			return NULL;
-		}
-		if (bts->sba()->find(trx->trx_no, control_ts, (fn + 13) % 2715648)) {
-			LOGP(DRLCMACUL, LOGL_DEBUG, "Polling is already "
-				"scheduled for single block allocation...\n");
-			return NULL;
-		}
-	}
-
-	msg = msgb_alloc(23, "rlcmac_ul_ack");
-	if (!msg)
-		return NULL;
-	bitvec *ack_vec = bitvec_alloc(23);
-	if (!ack_vec) {
-		msgb_free(msg);
-		return NULL;
-	}
-	bitvec_unhex(ack_vec,
-		"2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
-	RlcMacDownlink_t * mac_control_block = (RlcMacDownlink_t *)talloc_zero(tall_pcu_ctx, RlcMacDownlink_t);
-	Encoding::write_packet_uplink_ack(bts_data(), mac_control_block, this, final);
-	encode_gsm_rlcmac_downlink(ack_vec, mac_control_block);
-	bitvec_pack(ack_vec, msgb_put(msg, 23));
-	bitvec_free(ack_vec);
-	talloc_free(mac_control_block);
-
-	/* now we must set this flag, so we are allowed to assign downlink
-	 * TBF on PACCH. it is only allowed when TLLI is acknowledged. */
-	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;
-		m_final_ack_sent = 1;
-	} else
-		ul_ack_state = GPRS_RLCMAC_UL_ACK_NONE;
-
-	return msg;
-}
-
-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 = m_window.mod_sns();
-
-	Decoding::extract_rbb(rbb, show_rbb);
-	/* show received array in debug (bit 64..1) */
-	LOGP(DRLCMACDL, LOGL_DEBUG, "- ack:  (BSN=%d)\"%s\""
-		"(BSN=%d)  R=ACK I=NACK\n", (ssn - 64) & mod_sns,
-		show_rbb, (ssn - 1) & mod_sns);
-
-	/* apply received array to receive state (SSN-64..SSN-1) */
-	/* calculate distance of ssn from V(S) */
-	dist = (m_window.v_s() - ssn) & mod_sns;
-	/* check if distance is less than distance V(A)..V(S) */
-	if (dist >= m_window.distance()) {
-		/* this might happpen, if the downlink assignment
-		 * was not received by ms and the ack refers
-		 * to previous TBF
-		 * FIXME: we should implement polling for
-		 * control ack!*/
-		LOGP(DRLCMACDL, LOGL_NOTICE, "- ack range is out of "
-			"V(A)..V(S) range %s Free TBF!\n", tbf_name(this));
-		return 1; /* indicate to free TBF */
-	}
-
-	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 */
-	m_window.raise(m_window.move_window());
-
-	/* show receive state array in debug (V(A)..V(S)-1) */
-	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",
-		m_window.v_a(), show_v_b,
-		m_window.v_s_mod(-1));
-
-	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");
-	}
-	return 0;
-}
-
-
-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 = m_window.count_unacked();
-
-	/* report all outstanding packets as received */
-	gprs_rlcmac_received_lost(this, received, 0);
-
-	tbf_new_state(this, GPRS_RLCMAC_WAIT_RELEASE);
-
-	/* check for LLC PDU in the LLC Queue */
-	msg = llc_dequeue(gprs_bssgp_pcu_current_bctx());
-	if (!msg) {
-		/* no message, start T3193, change state to RELEASE */
-		LOGP(DRLCMACDL, LOGL_DEBUG, "- No new message, so we release.\n");
-		/* start T3193 */
-		tbf_timer_start(this, 3193,
-			bts_data()->t3193_msec / 1000,
-			(bts_data()->t3193_msec % 1000) * 1000);
-
-		return 0;
-	}
-
-	/* we have more data so we will re-use this tbf */
-	reuse_tbf(msg->data, msg->len);
-	msgb_free(msg);
-	return 0;
-}
-
-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));
-
-	if (!final_ack)
-		return update_window(ssn, rbb);
-	return maybe_start_new_window();
-}
-
 void gprs_rlcmac_tbf::free_all(struct gprs_rlcmac_trx *trx)
 {
 	for (uint8_t tfi = 0; tfi < 32; tfi++) {
@@ -1620,161 +789,6 @@
 	return 1;
 }
 
-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 = 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->m_window.v_q(),
-		this->m_window.v_r());
-
-	/* process RSSI */
-	gprs_rlcmac_rssi(this, rssi);
-
-	/* get TLLI */
-	if (!this->is_tlli_valid()) {
-		if (!extract_tlli(data, len))
-			return 0;
-	/* already have TLLI, but we stille get another one */
-	} else if (rh->ti) {
-		uint32_t tlli;
-		rc = Decoding::tlli_from_ul_data(data, len, &tlli);
-		if (rc) {
-			LOGP(DRLCMACUL, LOGL_NOTICE, "Failed to decode TLLI "
-				"of UL DATA TFI=%d.\n", rh->tfi);
-			return 0;
-		}
-		if (tlli != this->tlli()) {
-			LOGP(DRLCMACUL, LOGL_NOTICE, "TLLI mismatch on UL "
-				"DATA TFI=%d. (Ignoring due to contention "
-				"resolution)\n", rh->tfi);
-			return 0;
-		}
-	}
-
-	/* restart T3169 */
-	tbf_timer_start(this, 3169, bts_data()->t3169, 0);
-
-	/* Increment RX-counter */
-	this->m_rx_counter++;
-
-	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,
-			m_window.v_q(),
-			(m_window.v_q() + ws - 1) & mod_sns);
-		maybe_schedule_uplink_acknack(rh);
-		return 0;
-	}
-
-	/* 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, 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 = m_window.v_q();
-
-	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) {
-		uint16_t index = (v_q_beg + i) & mod_sns;
-		assemble_forward_llc(m_rlc.block(index));
-	}
-
-	/* Check CV of last frame in buffer */
-	if (this->state_is(GPRS_RLCMAC_FLOW) /* still in flow state */
-	 && 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((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);
-		if (last_rh->cv == 0) {
-			LOGP(DRLCMACUL, LOGL_DEBUG, "- Finished with UL "
-				"TBF\n");
-			tbf_new_state(this, GPRS_RLCMAC_FINISHED);
-			/* Reset N3103 counter. */
-			this->m_n3103 = 0;
-		}
-	}
-
-	/* If TLLI is included or if we received half of the window, we send
-	 * an ack/nack */
-	maybe_schedule_uplink_acknack(rh);
-
-	return 0;
-}
-
-void gprs_rlcmac_ul_tbf::maybe_schedule_uplink_acknack(const rlc_ul_header *rh)
-{
-	if (rh->si || rh->ti || state_is(GPRS_RLCMAC_FINISHED)
-	 || (m_rx_counter % SEND_ACK_AFTER_FRAMES) == 0) {
-		if (rh->si) {
-			LOGP(DRLCMACUL, LOGL_NOTICE, "- Scheduling Ack/Nack, "
-				"because MS is stalled.\n");
-		}
-		if (rh->ti) {
-			LOGP(DRLCMACUL, LOGL_DEBUG, "- Scheduling Ack/Nack, "
-				"because TLLI is included.\n");
-		}
-		if (state_is(GPRS_RLCMAC_FINISHED)) {
-			LOGP(DRLCMACUL, LOGL_DEBUG, "- Scheduling Ack/Nack, "
-				"because last block has CV==0.\n");
-		}
-		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);
-		}
-		if (ul_ack_state == GPRS_RLCMAC_UL_ACK_NONE) {
-			/* trigger sending at next RTS */
-			ul_ack_state = GPRS_RLCMAC_UL_ACK_SEND_ACK;
-		} else {
-			/* already triggered */
-			LOGP(DRLCMACUL, LOGL_DEBUG, "-  Sending Ack/Nack is "
-				"already triggered, don't schedule!\n");
-		}
-	}
-}
-
-/* Send Uplink unit-data to SGSN. */
-int gprs_rlcmac_tbf::snd_ul_ud()
-{
-	uint8_t qos_profile[3];
-	struct msgb *llc_pdu;
-	unsigned msg_len = NS_HDR_LEN + BSSGP_HDR_LEN + m_llc.frame_length();
-	struct bssgp_bvc_ctx *bctx = gprs_bssgp_pcu_current_bctx();
-
-	LOGP(DBSSGP, LOGL_INFO, "LLC [PCU -> SGSN] %s len=%d\n", tbf_name(this), m_llc.frame_length());
-	if (!bctx) {
-		LOGP(DBSSGP, LOGL_ERROR, "No bctx\n");
-		m_llc.reset_frame_space();
-		return -EIO;
-	}
-	
-	llc_pdu = msgb_alloc_headroom(msg_len, msg_len,"llc_pdu");
-	uint8_t *buf = msgb_push(llc_pdu, TL16V_GROSS_LEN(sizeof(uint8_t)*m_llc.frame_length()));
-	tl16v_put(buf, BSSGP_IE_LLC_PDU, sizeof(uint8_t)*m_llc.frame_length(), m_llc.frame);
-	qos_profile[0] = QOS_PROFILE >> 16;
-	qos_profile[1] = QOS_PROFILE >> 8;
-	qos_profile[2] = QOS_PROFILE;
-	bssgp_tx_ul_ud(bctx, tlli(), qos_profile, llc_pdu);
-
-	m_llc.reset_frame_space();
-	return 0;
-}
-
 const char *tbf_name(gprs_rlcmac_tbf *tbf)
 {
 	static char buf[40];
@@ -1785,35 +799,6 @@
 	return buf;
 }
 
-
-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 */
-	m_tx_counter = 0;
-	m_wait_confirm = 0;
-	m_window.reset();
-
-	/* keep to flags */
-	state_flags &= GPRS_RLCMAC_FLAG_TO_MASK;
-	state_flags &= ~(1 << GPRS_RLCMAC_FLAG_CCCH);
-
-	update();
-
-	LOGP(DRLCMAC, LOGL_DEBUG, "%s Trigger dowlink assignment on PACCH, "
-		"because another LLC PDU has arrived in between\n",
-		tbf_name(this));
-	bts->trigger_dl_ass(this, this, NULL);
-}
-
-bool gprs_rlcmac_dl_tbf::dl_window_stalled() const
-{
-	return m_window.window_stalled();
-}
-
 void gprs_rlcmac_tbf::rotate_in_list()
 {
 	llist_del(&list.list);
diff --git a/src/tbf.h b/src/tbf.h
index 23f8a7b..7172163 100644
--- a/src/tbf.h
+++ b/src/tbf.h
@@ -23,6 +23,7 @@
 #include "gprs_rlcmac.h"
 #include "llc.h"
 #include "rlc.h"
+#include <gprs_debug.h>
 
 #include <stdint.h>
 
@@ -115,15 +116,8 @@
 	bool state_is_not(enum gprs_rlcmac_tbf_state rhs) const;
 	void set_state(enum gprs_rlcmac_tbf_state new_state);
 
-	/* TODO: add the gettimeofday as parameter */
-	struct msgb *llc_dequeue(bssgp_bvc_ctx *bctx);
-
-	/* TODO: extract LLC class? */
-	int assemble_forward_llc(const gprs_rlc_data *data);
-
 	struct msgb *create_dl_ass(uint32_t fn);
 	struct msgb *create_ul_ass(uint32_t fn);
-	int snd_ul_ud();
 
 	uint8_t tsc() const;
 
@@ -186,17 +180,9 @@
 	unsigned int num_fT_exp; /* number of consecutive fT expirations */
 
 	struct {
-		struct timeval dl_bw_tv; /* timestamp for dl bw calculation */
-		uint32_t dl_bw_octets; /* number of octets since bw_tv */
-
 		struct timeval rssi_tv; /* timestamp for rssi calculation */
 		int32_t rssi_sum; /* sum of rssi values */
 		int rssi_num; /* number of rssi values added since rssi_tv */
-
-		struct timeval dl_loss_tv; /* timestamp for loss calculation */
-		uint16_t dl_loss_lost; /* sum of lost packets */
-		uint16_t dl_loss_received; /* sum of received packets */
-
 	} meas;
 
 	uint8_t cs; /* current coding scheme */
@@ -232,6 +218,7 @@
 
 	int extract_tlli(const uint8_t *data, const size_t len);
 
+	static const char *tbf_state_name[6];
 };
 
 
@@ -253,9 +240,6 @@
 
 int tbf_assign_control_ts(struct gprs_rlcmac_tbf *tbf);
 
-void tbf_new_state(struct gprs_rlcmac_tbf *tbf,
-        enum gprs_rlcmac_tbf_state state);
-
 void tbf_timer_start(struct gprs_rlcmac_tbf *tbf, unsigned int T,
                         unsigned int seconds, unsigned int microseconds);
 
@@ -269,8 +253,13 @@
 	return state != rhs;
 }
 
+const char *tbf_name(gprs_rlcmac_tbf *tbf);
+
 inline void gprs_rlcmac_tbf::set_state(enum gprs_rlcmac_tbf_state new_state)
 {
+	LOGP(DRLCMAC, LOGL_DEBUG, "%s changes state from %s to %s\n",
+		tbf_name(this),
+		tbf_state_name[state], tbf_state_name[new_state]);
 	state = new_state;
 }
 
@@ -294,8 +283,6 @@
 	return m_imsi;
 }
 
-const char *tbf_name(gprs_rlcmac_tbf *tbf);
-
 inline time_t gprs_rlcmac_tbf::created_ts() const
 {
 	return m_created_ts;
@@ -314,6 +301,9 @@
 	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);
 
+	/* TODO: add the gettimeofday as parameter */
+	struct msgb *llc_dequeue(bssgp_bvc_ctx *bctx);
+
 	/* 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
@@ -323,6 +313,15 @@
 	int32_t m_tx_counter; /* count all transmitted blocks */
 	uint8_t m_wait_confirm; /* wait for CCCH IMM.ASS cnf */
 
+	struct {
+		struct timeval dl_bw_tv; /* timestamp for dl bw calculation */
+		uint32_t dl_bw_octets; /* number of octets since bw_tv */
+
+		struct timeval dl_loss_tv; /* timestamp for loss calculation */
+		uint16_t dl_loss_lost; /* sum of lost packets */
+		uint16_t dl_loss_received; /* sum of received packets */
+	} m_bw;
+
 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,
@@ -339,6 +338,10 @@
 	/* blocks were acked */
 	int rcv_data_block_acknowledged(const uint8_t *data, size_t len, int8_t rssi);
 
+	/* TODO: extract LLC class? */
+	int assemble_forward_llc(const gprs_rlc_data *data);
+	int snd_ul_ud();
+
 	/* 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
diff --git a/src/tbf_dl.cpp b/src/tbf_dl.cpp
new file mode 100644
index 0000000..8abea20
--- /dev/null
+++ b/src/tbf_dl.cpp
@@ -0,0 +1,671 @@
+/* Copied from tbf.cpp
+ *
+ * Copyright (C) 2012 Ivan Klyuchnikov
+ * Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
+ * Copyright (C) 2013 by Holger Hans Peter Freyther
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <bts.h>
+#include <tbf.h>
+#include <rlc.h>
+#include <gprs_rlcmac.h>
+#include <gprs_debug.h>
+#include <gprs_bssgp_pcu.h>
+#include <decoding.h>
+
+extern "C" {
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/talloc.h>
+}
+
+#include <errno.h>
+#include <string.h>
+
+/* After sending these frames, we poll for ack/nack. */
+#define POLL_ACK_AFTER_FRAMES 20
+
+
+static const struct gprs_rlcmac_cs gprs_rlcmac_cs[] = {
+/*	frame length	data block	max payload */
+	{ 0,		0,		0  },
+	{ 23,		23,		20 }, /* CS-1 */
+	{ 34,		33,		30 }, /* CS-2 */
+	{ 40,		39,		36 }, /* CS-3 */
+	{ 54,		53,		50 }, /* CS-4 */
+};
+
+extern "C" {
+int bssgp_tx_llc_discarded(struct bssgp_bvc_ctx *bctx, uint32_t tlli,
+                           uint8_t num_frames, uint32_t num_octets);
+}
+
+static inline void tbf_update_ms_class(struct gprs_rlcmac_tbf *tbf,
+					const uint8_t ms_class)
+{
+	if (!tbf->ms_class && ms_class)
+		tbf->ms_class = 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)
+{
+	LOGP(DRLCMAC, LOGL_INFO, "%s append\n", tbf_name(this));
+	if (state_is(GPRS_RLCMAC_WAIT_RELEASE)) {
+		LOGP(DRLCMAC, LOGL_DEBUG,
+			"%s in WAIT RELEASE state "
+			"(T3193), so reuse TBF\n", tbf_name(this));
+		tbf_update_ms_class(this, ms_class);
+		reuse_tbf(data, len);
+	} else {
+		/* the TBF exists, so we must write it in the queue
+		 * we prepend lifetime in front of PDU */
+		struct timeval *tv;
+		struct msgb *llc_msg = msgb_alloc(len + sizeof(*tv) * 2,
+			"llc_pdu_queue");
+		if (!llc_msg)
+			return -ENOMEM;
+		tv = (struct timeval *)msgb_put(llc_msg, sizeof(*tv));
+		gprs_llc::calc_pdu_lifetime(bts, pdu_delay_csec, tv);
+		tv = (struct timeval *)msgb_put(llc_msg, sizeof(*tv));
+		gettimeofday(tv, NULL);
+		memcpy(msgb_put(llc_msg, len), data, len);
+		m_llc.enqueue(llc_msg);
+		tbf_update_ms_class(this, ms_class);
+	}
+
+	return 0;
+}
+
+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);
+}
+
+static int tbf_new_dl_assignment(struct gprs_rlcmac_bts *bts,
+				const char *imsi,
+				const uint32_t tlli, const uint8_t ms_class,
+				const uint8_t *data, const uint16_t len)
+{
+	uint8_t trx, ta, ss;
+	int8_t use_trx;
+	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"
+	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_ul_tbf = ul_tbf;
+	} else {
+		use_trx = -1;
+		/* we already have an uplink TBF, so we use that TA */
+		if (ul_tbf)
+			ta = ul_tbf->ta;
+		else {
+			/* recall TA */
+			rc = bts->bts->timing_advance()->recall(tlli);
+			if (rc < 0) {
+				LOGP(DRLCMAC, LOGL_NOTICE, "TA unknown"
+					", assuming 0\n");
+				ta = 0;
+			} else
+				ta = rc;
+		}
+		ss = 1; /* PCH assignment only allows one timeslot */
+		old_ul_tbf = NULL;
+	}
+
+	// Create new TBF (any TRX)
+#warning "Copy and paste with alloc_ul_tbf"
+	tfi = bts->bts->tfi_find_free(GPRS_RLCMAC_DL_TBF, &trx, use_trx);
+	if (tfi < 0) {
+		LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH resource\n");
+		/* FIXME: send reject */
+		return -EBUSY;
+	}
+	/* set number of downlink slots according to multislot class */
+	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;
+	}
+	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(dl_tbf));
+
+	/* new TBF, so put first frame */
+	dl_tbf->m_llc.put_frame(data, len);
+	dl_tbf->bts->llc_frame_sched();
+
+	/* Store IMSI for later look-up and PCH retransmission */
+	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. */
+	dl_tbf->bts->trigger_dl_ass(dl_tbf, old_ul_tbf, imsi);
+	return 0;
+}
+
+/**
+ * TODO: split into unit test-able parts...
+ */
+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_dl_tbf *dl_tbf;
+
+	/* check for existing TBF */
+	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)
+			dl_tbf->assign_imsi(imsi);
+		return rc;
+	} 
+
+	return tbf_new_dl_assignment(bts, imsi, tlli, ms_class, data, len);
+}
+
+struct msgb *gprs_rlcmac_dl_tbf::llc_dequeue(bssgp_bvc_ctx *bctx)
+{
+	struct msgb *msg;
+	struct timeval *tv, tv_now;
+	uint32_t octets = 0, frames = 0;
+
+	gettimeofday(&tv_now, NULL);
+
+	while ((msg = m_llc.dequeue())) {
+		tv = (struct timeval *)msg->data;
+		msgb_pull(msg, sizeof(*tv));
+		msgb_pull(msg, sizeof(*tv));
+
+		if (gprs_llc::is_frame_expired(&tv_now, tv)) {
+			LOGP(DRLCMACDL, LOGL_NOTICE, "%s Discarding LLC PDU "
+				"because lifetime limit reached. Queue size %zu\n",
+				tbf_name(this), m_llc.m_queue_size);
+			bts->llc_timedout_frame();
+			frames++;
+			octets += msg->len;
+			msgb_free(msg);
+			continue;
+		}
+		break;
+	}
+
+	if (frames) {
+		if (frames > 0xff)
+			frames = 0xff;
+		if (octets > 0xffffff)
+			octets = 0xffffff;
+		bssgp_tx_llc_discarded(bctx, m_tlli, frames, octets);
+	}
+
+	return msg;
+}
+
+/*
+ * Create DL data block
+ * The messages are fragmented and forwarded as data blocks.
+ */
+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),
+		m_window.v_a(), m_window.v_s());
+
+do_resend:
+	/* check if there is a block with negative acknowledgement */
+	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 */
+		m_window.m_v_b.mark_unacked(resend_bsn);
+		bts->rlc_resent();
+		return create_dl_acked_block(fn, ts, resend_bsn, false);
+	}
+
+	/* if the window has stalled, or transfer is complete,
+	 * send an unacknowledged block */
+	if (state_is(GPRS_RLCMAC_FINISHED) || dl_window_stalled()) {
+		if (state_is(GPRS_RLCMAC_FINISHED)) {
+			LOGP(DRLCMACDL, LOGL_DEBUG, "- Restarting at BSN %d, "
+				"because all blocks have been transmitted.\n",
+					m_window.v_a());
+			bts->rlc_restarted();
+		} else {
+			LOGP(DRLCMACDL, LOGL_NOTICE, "- Restarting at BSN %d, "
+				"because all window is stalled.\n",
+					m_window.v_a());
+			bts->rlc_stalled();
+		}
+		/* If V(S) == V(A) and finished state, we would have received
+		 * acknowledgement of all transmitted block. In this case we
+		 * would have transmitted the final block, and received ack
+		 * 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 (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 = 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 = 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. */
+		if (resend == 0) {
+			LOGP(DRLCMACDL, LOGL_ERROR, "Software error: "
+				"There are no unacknowledged blocks, but V(A) "
+				" != V(S). PLEASE FIX!\n");
+			/* we just send final block again */
+			int16_t index = m_window.v_s_mod(-1);
+			return create_dl_acked_block(fn, ts, index, false);
+		}
+		goto do_resend;
+	}
+
+	return create_new_bsn(fn, 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;
+	struct msgb *msg;
+	uint8_t *delimiter, *data, *e_pointer;
+	uint16_t space, chunk;
+	gprs_rlc_data *rlc_data;
+	bool first_fin_ack = false;
+	const uint16_t bsn = m_window.v_s();
+
+	LOGP(DRLCMACDL, LOGL_DEBUG, "- Sending new block at BSN %d\n",
+		m_window.v_s());
+
+#warning "Selection of the CS doesn't belong here"
+	if (cs == 0) {
+		cs = bts_data()->initial_cs_dl;
+		if (cs < 1 || cs > 4)
+			cs = 1;
+	}
+	/* total length of block, including spare bits */
+	const uint8_t block_length = gprs_rlcmac_cs[cs].block_length;
+	/* length of usable data of block, w/o spare bits, inc. MAC */
+	const uint8_t block_data_len = gprs_rlcmac_cs[cs].block_data;
+
+	/* now we still have untransmitted LLC data, so we fill mac block */
+	rlc_data = m_rlc.block(bsn);
+	data = rlc_data->prepare(block_data_len);
+
+	rh = (struct rlc_dl_header *)data;
+	rh->pt = 0; /* Data Block */
+	rh->rrbp = rh->s_p = 0; /* Polling, set later, if required */
+	rh->usf = 7; /* will be set at scheduler */
+	rh->pr = 0; /* FIXME: power reduction */
+	rh->tfi = m_tfi; /* TFI */
+	rh->fbi = 0; /* Final Block Indicator, set late, if true */
+	rh->bsn = bsn; /* Block Sequence Number */
+	rh->e = 0; /* Extension bit, maybe set later */
+	e_pointer = data + 2; /* points to E of current chunk */
+	data += sizeof(*rh);
+	delimiter = data; /* where next length header would be stored */
+	space = block_data_len - sizeof(*rh);
+	while (1) {
+		chunk = m_llc.chunk_size();
+		/* if chunk will exceed block limit */
+		if (chunk > space) {
+			LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d "
+				"larger than space (%d) left in block: copy "
+				"only remaining space, and we are done\n",
+				chunk, space);
+			/* block is filled, so there is no extension */
+			*e_pointer |= 0x01;
+			/* fill only space */
+			m_llc.consume(data, space);
+			/* return data block as message */
+			break;
+		}
+		/* if FINAL chunk would fit precisely in space left */
+		if (chunk == space && llist_empty(&m_llc.queue)) {
+			LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d "
+				"would exactly fit into space (%d): because "
+				"this is a final block, we don't add length "
+				"header, and we are done\n", chunk, space);
+			LOGP(DRLCMACDL, LOGL_INFO, "Complete DL frame for "
+				"%s that fits precisely in last block: "
+				"len=%d\n", tbf_name(this), m_llc.frame_length());
+			gprs_rlcmac_dl_bw(this, m_llc.frame_length());
+			/* block is filled, so there is no extension */
+			*e_pointer |= 0x01;
+			/* fill space */
+			m_llc.consume(data, space);
+			m_llc.reset();
+			/* final block */
+			rh->fbi = 1; /* we indicate final block */
+			set_state(GPRS_RLCMAC_FINISHED);
+			/* return data block as message */
+			break;
+		}
+		/* if chunk would fit exactly in space left */
+		if (chunk == space) {
+			LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d "
+				"would exactly fit into space (%d): add length "
+				"header with LI=0, to make frame extend to "
+				"next block, and we are done\n", chunk, space);
+			/* make space for delimiter */
+			if (delimiter != data)
+				memmove(delimiter + 1, delimiter,
+					data - delimiter);
+			data++;
+			space--;
+			/* add LI with 0 length */
+			li = (struct rlc_li_field *)delimiter;
+			li->e = 1; /* not more extension */
+			li->m = 0; /* shall be set to 0, in case of li = 0 */
+			li->li = 0; /* chunk fills the complete space */
+			// no need to set e_pointer nor increase delimiter
+			/* fill only space, which is 1 octet less than chunk */
+			m_llc.consume(data, space);
+			/* return data block as message */
+			break;
+		}
+		LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d is less "
+			"than remaining space (%d): add length header to "
+			"to delimit LLC frame\n", chunk, space);
+		/* the LLC frame chunk ends in this block */
+		/* make space for delimiter */
+		if (delimiter != data)
+			memmove(delimiter + 1, delimiter, data - delimiter);
+		data++;
+		space--;
+		/* add LI to delimit frame */
+		li = (struct rlc_li_field *)delimiter;
+		li->e = 0; /* Extension bit, maybe set later */
+		li->m = 0; /* will be set later, if there is more LLC data */
+		li->li = chunk; /* length of chunk */
+		e_pointer = delimiter; /* points to E of current delimiter */
+		delimiter++;
+		/* copy (rest of) LLC frame to space and reset later */
+		m_llc.consume(data, chunk);
+		data += chunk;
+		space -= chunk;
+		LOGP(DRLCMACDL, LOGL_INFO, "Complete DL frame for %s"
+			"len=%d\n", tbf_name(this), m_llc.frame_length());
+		gprs_rlcmac_dl_bw(this, m_llc.frame_length());
+		m_llc.reset();
+		/* dequeue next LLC frame, if any */
+		msg = llc_dequeue(gprs_bssgp_pcu_current_bctx());
+		if (msg) {
+			LOGP(DRLCMACDL, LOGL_INFO, "- Dequeue next LLC for "
+				"%s (len=%d)\n", tbf_name(this), msg->len);
+			m_llc.put_frame(msg->data, msg->len);
+			bts->llc_frame_sched();
+			msgb_free(msg);
+		}
+		/* if we have more data and we have space left */
+		if (space > 0 && m_llc.frame_length()) {
+			li->m = 1; /* we indicate more frames to follow */
+			continue;
+		}
+		/* if we don't have more LLC frames */
+		if (!m_llc.frame_length()) {
+			LOGP(DRLCMACDL, LOGL_DEBUG, "-- Final block, so we "
+				"done.\n");
+			li->e = 1; /* we cannot extend */
+			rh->fbi = 1; /* we indicate final block */
+			first_fin_ack = true;
+				/* + 1 indicates: first final ack */
+			set_state(GPRS_RLCMAC_FINISHED);
+			break;
+		}
+		/* we have no space left */
+		LOGP(DRLCMACDL, LOGL_DEBUG, "-- No space left, so we are "
+			"done.\n");
+		li->e = 1; /* we cannot extend */
+		break;
+	}
+	LOGP(DRLCMACDL, LOGL_DEBUG, "data block: %s\n",
+		osmo_hexdump(rlc_data->block, block_length));
+#warning "move this up?"
+	rlc_data->len = block_length;
+	/* raise send state and set ack state array */
+	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_dl_tbf::create_dl_acked_block(
+				const uint32_t fn, const uint8_t ts,
+				const int index, const bool first_fin_ack)
+{
+	uint8_t *data;
+	struct rlc_dl_header *rh;
+	struct msgb *dl_msg;
+	uint8_t len;
+
+	/* get data and header from current block */
+	data = m_rlc.block(index)->block;
+	len = m_rlc.block(index)->len;
+	rh = (struct rlc_dl_header *)data;
+
+	/* Clear Polling, if still set in history buffer */
+	rh->s_p = 0;
+		
+	/* poll after POLL_ACK_AFTER_FRAMES frames, or when final block is tx.
+	 */
+	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");
+		} else {
+			LOGP(DRLCMACDL, LOGL_DEBUG, "- Scheduling Ack/Nack "
+				"polling, because %d blocks sent.\n",
+				POLL_ACK_AFTER_FRAMES);
+		}
+		/* scheduling not possible, because: */
+		if (poll_state != GPRS_RLCMAC_POLL_NONE)
+			LOGP(DRLCMACDL, LOGL_DEBUG, "Polling is already "
+				"sheduled for %s, so we must wait for "
+				"requesting downlink ack\n", tbf_name(this));
+		else if (control_ts != ts)
+			LOGP(DRLCMACDL, LOGL_DEBUG, "Polling cannot be "
+				"sheduled in this TS %d, waiting for "
+				"TS %d\n", ts, control_ts);
+#warning "What happens to the first_fin_ack in case something is already scheduled?"
+		else if (bts->sba()->find(trx->trx_no, ts, (fn + 13) % 2715648))
+			LOGP(DRLCMACDL, LOGL_DEBUG, "Polling cannot be "
+				"sheduled, because single block alllocation "
+				"already exists\n");
+		else  {
+			LOGP(DRLCMACDL, LOGL_DEBUG, "Polling sheduled in this "
+				"TS %d\n", ts);
+			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);
+
+			/* schedule polling */
+			poll_state = GPRS_RLCMAC_POLL_SCHED;
+			poll_fn = (fn + 13) % 2715648;
+
+			/* set polling in header */
+			rh->rrbp = 0; /* N+13 */
+			rh->s_p = 1; /* Polling */
+		}
+	}
+
+	/* return data block as message */
+	dl_msg = msgb_alloc(len, "rlcmac_dl_data");
+	if (!dl_msg)
+		return NULL;
+
+	/* Increment TX-counter */
+	m_tx_counter++;
+
+	memcpy(msgb_put(dl_msg, len), data, len);
+	bts->rlc_sent();
+
+	return dl_msg;
+}
+
+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 = m_window.mod_sns();
+
+	Decoding::extract_rbb(rbb, show_rbb);
+	/* show received array in debug (bit 64..1) */
+	LOGP(DRLCMACDL, LOGL_DEBUG, "- ack:  (BSN=%d)\"%s\""
+		"(BSN=%d)  R=ACK I=NACK\n", (ssn - 64) & mod_sns,
+		show_rbb, (ssn - 1) & mod_sns);
+
+	/* apply received array to receive state (SSN-64..SSN-1) */
+	/* calculate distance of ssn from V(S) */
+	dist = (m_window.v_s() - ssn) & mod_sns;
+	/* check if distance is less than distance V(A)..V(S) */
+	if (dist >= m_window.distance()) {
+		/* this might happpen, if the downlink assignment
+		 * was not received by ms and the ack refers
+		 * to previous TBF
+		 * FIXME: we should implement polling for
+		 * control ack!*/
+		LOGP(DRLCMACDL, LOGL_NOTICE, "- ack range is out of "
+			"V(A)..V(S) range %s Free TBF!\n", tbf_name(this));
+		return 1; /* indicate to free TBF */
+	}
+
+	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 */
+	m_window.raise(m_window.move_window());
+
+	/* show receive state array in debug (V(A)..V(S)-1) */
+	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",
+		m_window.v_a(), show_v_b,
+		m_window.v_s_mod(-1));
+
+	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");
+	}
+	return 0;
+}
+
+
+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 = m_window.count_unacked();
+
+	/* report all outstanding packets as received */
+	gprs_rlcmac_received_lost(this, received, 0);
+
+	set_state(GPRS_RLCMAC_WAIT_RELEASE);
+
+	/* check for LLC PDU in the LLC Queue */
+	msg = llc_dequeue(gprs_bssgp_pcu_current_bctx());
+	if (!msg) {
+		/* no message, start T3193, change state to RELEASE */
+		LOGP(DRLCMACDL, LOGL_DEBUG, "- No new message, so we release.\n");
+		/* start T3193 */
+		tbf_timer_start(this, 3193,
+			bts_data()->t3193_msec / 1000,
+			(bts_data()->t3193_msec % 1000) * 1000);
+
+		return 0;
+	}
+
+	/* we have more data so we will re-use this tbf */
+	reuse_tbf(msg->data, msg->len);
+	msgb_free(msg);
+	return 0;
+}
+
+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));
+
+	if (!final_ack)
+		return update_window(ssn, rbb);
+	return maybe_start_new_window();
+}
+
+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 */
+	m_tx_counter = 0;
+	m_wait_confirm = 0;
+	m_window.reset();
+
+	/* keep to flags */
+	state_flags &= GPRS_RLCMAC_FLAG_TO_MASK;
+	state_flags &= ~(1 << GPRS_RLCMAC_FLAG_CCCH);
+
+	update();
+
+	LOGP(DRLCMAC, LOGL_DEBUG, "%s Trigger dowlink assignment on PACCH, "
+		"because another LLC PDU has arrived in between\n",
+		tbf_name(this));
+	bts->trigger_dl_ass(this, this, NULL);
+}
+
+bool gprs_rlcmac_dl_tbf::dl_window_stalled() const
+{
+	return m_window.window_stalled();
+}
+
diff --git a/src/tbf_ul.cpp b/src/tbf_ul.cpp
new file mode 100644
index 0000000..3ab71f0
--- /dev/null
+++ b/src/tbf_ul.cpp
@@ -0,0 +1,418 @@
+/* Copied from tbf.cpp
+ *
+ * Copyright (C) 2012 Ivan Klyuchnikov
+ * Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
+ * Copyright (C) 2013 by Holger Hans Peter Freyther
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <bts.h>
+#include <tbf.h>
+#include <rlc.h>
+#include <encoding.h>
+#include <gprs_rlcmac.h>
+#include <gprs_debug.h>
+#include <gprs_bssgp_pcu.h>
+#include <decoding.h>
+
+extern "C" {
+#include <osmocom/core/msgb.h>
+#include <osmocom/core/talloc.h>
+}
+
+#include <errno.h>
+#include <string.h>
+
+/* After receiving these frames, we send ack/nack. */
+#define SEND_ACK_AFTER_FRAMES 20
+
+extern void *tall_pcu_ctx;
+
+
+/*
+ * Store received block data in LLC message(s) and forward to SGSN
+ * if complete.
+ */
+int gprs_rlcmac_ul_tbf::assemble_forward_llc(const gprs_rlc_data *_data)
+{
+	const uint8_t *data = _data->block;
+	uint8_t len = _data->len;
+	const struct rlc_ul_header *rh = (const struct rlc_ul_header *) data;
+	uint8_t e, m;
+	struct rlc_li_field *li;
+	uint8_t frame_offset[16], offset = 0, chunk;
+	int i, frames = 0;
+
+	LOGP(DRLCMACUL, LOGL_DEBUG, "- Assembling frames: (len=%d)\n", len);
+
+	data += 3;
+	len -= 3;
+	e = rh->e; /* if extended */
+	m = 1; /* more frames, that means: the first frame */
+
+	/* Parse frame offsets from length indicator(s), if any. */
+	while (1) {
+		if (frames == (int)sizeof(frame_offset)) {
+			LOGP(DRLCMACUL, LOGL_ERROR, "%s too many frames in "
+				"block\n", tbf_name(this));
+			return -EINVAL;
+		}
+		frame_offset[frames++] = offset;
+		LOGP(DRLCMACUL, LOGL_DEBUG, "-- Frame %d starts at offset "
+			"%d\n", frames, offset);
+		if (!len)
+			break;
+		/* M == 0 and E == 0 is not allowed in this version. */
+		if (!m && !e) {
+			LOGP(DRLCMACUL, LOGL_NOTICE, "%s UL DATA "
+				"ignored, because M='0' and E='0'.\n",
+				tbf_name(this));
+			return 0;
+		}
+		/* no more frames in this segment */
+		if (e) {
+			break;
+		}
+		/* There is a new frame and an LI that delimits it. */
+		if (m) {
+			li = (struct rlc_li_field *)data;
+			LOGP(DRLCMACUL, LOGL_DEBUG, "-- Delimiter len=%d\n",
+				li->li);
+			/* Special case: LI == 0
+			 * If the last segment would fit precisely into the
+			 * rest of the RLC MAC block, there would be no way
+			 * to delimit that this segment ends and is not
+			 * continued in the next block.
+			 * The special LI (0) is used to force the segment to
+			 * extend into the next block, so it is delimited there.
+			 * This LI must be skipped. Also it is the last LI.
+			 */
+			if (li->li == 0) {
+				data++;
+				len--;
+				m = 1; /* M is ignored, we know there is more */
+				break; /* handle E as '1', so we break! */
+			}
+			e = li->e;
+			m = li->m;
+			offset += li->li;
+			data++;
+			len--;
+			continue;
+		}
+	}
+	if (!m) {
+		LOGP(DRLCMACUL, LOGL_DEBUG, "- Last frame carries spare "
+			"data\n");
+	}
+
+	LOGP(DRLCMACUL, LOGL_DEBUG, "- Data length after length fields: %d\n",
+		len);
+	/* TLLI */
+	if (rh->ti) {
+		if (len < 4) {
+			LOGP(DRLCMACUL, LOGL_NOTICE, "%s UL DATA TLLI out of "
+				"frame border\n", tbf_name(this));
+			return -EINVAL;
+		}
+		data += 4;
+		len -= 4;
+		LOGP(DRLCMACUL, LOGL_DEBUG, "- Length after skipping TLLI: "
+			"%d\n", len);
+	}
+
+	/* PFI */
+	if (rh->pi) {
+		LOGP(DRLCMACUL, LOGL_ERROR, "ERROR: PFI not supported, "
+			"please disable in SYSTEM INFORMATION\n");
+		if (len < 1) {
+			LOGP(DRLCMACUL, LOGL_NOTICE, "%s UL DATA PFI out of "
+				"frame border\n", tbf_name(this));
+			return -EINVAL;
+		}
+		data++;
+		len--;
+		LOGP(DRLCMACUL, LOGL_DEBUG, "- Length after skipping PFI: "
+			"%d\n", len);
+	}
+
+	/* Now we have:
+	 * - a list of frames offsets: frame_offset[]
+	 * - number of frames: i
+	 * - m == 0: Last frame carries spare data (end of TBF).
+	 */
+
+	/* Check if last offset would exceed frame. */
+	if (offset > len) {
+		LOGP(DRLCMACUL, LOGL_NOTICE, "%s UL DATA ignored, "
+			"because LI delimits data that exceeds block size.\n",
+			tbf_name(this));
+		return -EINVAL;
+	}
+
+	/* create LLC frames */
+	for (i = 0; i < frames; i++) {
+		/* last frame ? */
+		if (i == frames - 1) {
+			/* no more data in last frame */
+			if (!m)
+				break;
+			/* data until end of frame */
+			chunk = len - frame_offset[i];
+		} else {
+			/* data until next frame */
+			chunk = frame_offset[i + 1] - frame_offset[i];
+		}
+		if (!m_llc.fits_in_current_frame(chunk)) {
+			LOGP(DRLCMACUL, LOGL_NOTICE, "%s LLC frame exceeds "
+				"maximum size %u.\n", tbf_name(this),
+				m_llc.remaining_space());
+			chunk = m_llc.remaining_space();
+		}
+		m_llc.append_frame(data + frame_offset[i], chunk);
+		m_llc.consume(chunk);
+		/* not last frame. */
+		if (i != frames - 1) {
+			/* send frame to SGSN */
+			LOGP(DRLCMACUL, LOGL_INFO, "%s complete UL frame len=%d\n",
+				tbf_name(this) , m_llc.frame_length());
+			snd_ul_ud();
+			m_llc.reset();
+		/* also check if CV==0, because the frame may fill up the
+		 * block precisely, then it is also complete. normally the
+		 * frame would be extended into the next block with a 0-length
+		 * delimiter added to this block. */
+		} else if (rh->cv == 0) {
+			/* send frame to SGSN */
+			LOGP(DRLCMACUL, LOGL_INFO, "%s complete UL frame "
+				"that fits precisely in last block: "
+				"len=%d\n", tbf_name(this), m_llc.frame_length());
+			snd_ul_ud();
+			m_llc.reset();
+		}
+	}
+
+	return 0;
+}
+
+
+struct msgb *gprs_rlcmac_ul_tbf::create_ul_ack(uint32_t fn)
+{
+	int final = (state_is(GPRS_RLCMAC_FINISHED));
+	struct msgb *msg;
+
+	if (final) {
+		if (poll_state != GPRS_RLCMAC_POLL_NONE) {
+			LOGP(DRLCMACUL, LOGL_DEBUG, "Polling is already "
+				"sheduled for %s, so we must wait for "
+				"final uplink ack...\n", tbf_name(this));
+			return NULL;
+		}
+		if (bts->sba()->find(trx->trx_no, control_ts, (fn + 13) % 2715648)) {
+			LOGP(DRLCMACUL, LOGL_DEBUG, "Polling is already "
+				"scheduled for single block allocation...\n");
+			return NULL;
+		}
+	}
+
+	msg = msgb_alloc(23, "rlcmac_ul_ack");
+	if (!msg)
+		return NULL;
+	bitvec *ack_vec = bitvec_alloc(23);
+	if (!ack_vec) {
+		msgb_free(msg);
+		return NULL;
+	}
+	bitvec_unhex(ack_vec,
+		"2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
+	RlcMacDownlink_t * mac_control_block = (RlcMacDownlink_t *)talloc_zero(tall_pcu_ctx, RlcMacDownlink_t);
+	Encoding::write_packet_uplink_ack(bts_data(), mac_control_block, this, final);
+	encode_gsm_rlcmac_downlink(ack_vec, mac_control_block);
+	bitvec_pack(ack_vec, msgb_put(msg, 23));
+	bitvec_free(ack_vec);
+	talloc_free(mac_control_block);
+
+	/* now we must set this flag, so we are allowed to assign downlink
+	 * TBF on PACCH. it is only allowed when TLLI is acknowledged. */
+	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;
+		m_final_ack_sent = 1;
+	} else
+		ul_ack_state = GPRS_RLCMAC_UL_ACK_NONE;
+
+	return msg;
+}
+
+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 = 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->m_window.v_q(),
+		this->m_window.v_r());
+
+	/* process RSSI */
+	gprs_rlcmac_rssi(this, rssi);
+
+	/* get TLLI */
+	if (!this->is_tlli_valid()) {
+		if (!extract_tlli(data, len))
+			return 0;
+	/* already have TLLI, but we stille get another one */
+	} else if (rh->ti) {
+		uint32_t tlli;
+		rc = Decoding::tlli_from_ul_data(data, len, &tlli);
+		if (rc) {
+			LOGP(DRLCMACUL, LOGL_NOTICE, "Failed to decode TLLI "
+				"of UL DATA TFI=%d.\n", rh->tfi);
+			return 0;
+		}
+		if (tlli != this->tlli()) {
+			LOGP(DRLCMACUL, LOGL_NOTICE, "TLLI mismatch on UL "
+				"DATA TFI=%d. (Ignoring due to contention "
+				"resolution)\n", rh->tfi);
+			return 0;
+		}
+	}
+
+	/* restart T3169 */
+	tbf_timer_start(this, 3169, bts_data()->t3169, 0);
+
+	/* Increment RX-counter */
+	this->m_rx_counter++;
+
+	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,
+			m_window.v_q(),
+			(m_window.v_q() + ws - 1) & mod_sns);
+		maybe_schedule_uplink_acknack(rh);
+		return 0;
+	}
+
+	/* 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, 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 = m_window.v_q();
+
+	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) {
+		uint16_t index = (v_q_beg + i) & mod_sns;
+		assemble_forward_llc(m_rlc.block(index));
+	}
+
+	/* Check CV of last frame in buffer */
+	if (this->state_is(GPRS_RLCMAC_FLOW) /* still in flow state */
+	 && 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((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);
+		if (last_rh->cv == 0) {
+			LOGP(DRLCMACUL, LOGL_DEBUG, "- Finished with UL "
+				"TBF\n");
+			set_state(GPRS_RLCMAC_FINISHED);
+			/* Reset N3103 counter. */
+			this->m_n3103 = 0;
+		}
+	}
+
+	/* If TLLI is included or if we received half of the window, we send
+	 * an ack/nack */
+	maybe_schedule_uplink_acknack(rh);
+
+	return 0;
+}
+
+void gprs_rlcmac_ul_tbf::maybe_schedule_uplink_acknack(const rlc_ul_header *rh)
+{
+	if (rh->si || rh->ti || state_is(GPRS_RLCMAC_FINISHED)
+	 || (m_rx_counter % SEND_ACK_AFTER_FRAMES) == 0) {
+		if (rh->si) {
+			LOGP(DRLCMACUL, LOGL_NOTICE, "- Scheduling Ack/Nack, "
+				"because MS is stalled.\n");
+		}
+		if (rh->ti) {
+			LOGP(DRLCMACUL, LOGL_DEBUG, "- Scheduling Ack/Nack, "
+				"because TLLI is included.\n");
+		}
+		if (state_is(GPRS_RLCMAC_FINISHED)) {
+			LOGP(DRLCMACUL, LOGL_DEBUG, "- Scheduling Ack/Nack, "
+				"because last block has CV==0.\n");
+		}
+		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);
+		}
+		if (ul_ack_state == GPRS_RLCMAC_UL_ACK_NONE) {
+			/* trigger sending at next RTS */
+			ul_ack_state = GPRS_RLCMAC_UL_ACK_SEND_ACK;
+		} else {
+			/* already triggered */
+			LOGP(DRLCMACUL, LOGL_DEBUG, "-  Sending Ack/Nack is "
+				"already triggered, don't schedule!\n");
+		}
+	}
+}
+
+/* Send Uplink unit-data to SGSN. */
+int gprs_rlcmac_ul_tbf::snd_ul_ud()
+{
+	uint8_t qos_profile[3];
+	struct msgb *llc_pdu;
+	unsigned msg_len = NS_HDR_LEN + BSSGP_HDR_LEN + m_llc.frame_length();
+	struct bssgp_bvc_ctx *bctx = gprs_bssgp_pcu_current_bctx();
+
+	LOGP(DBSSGP, LOGL_INFO, "LLC [PCU -> SGSN] %s len=%d\n", tbf_name(this), m_llc.frame_length());
+	if (!bctx) {
+		LOGP(DBSSGP, LOGL_ERROR, "No bctx\n");
+		m_llc.reset_frame_space();
+		return -EIO;
+	}
+	
+	llc_pdu = msgb_alloc_headroom(msg_len, msg_len,"llc_pdu");
+	uint8_t *buf = msgb_push(llc_pdu, TL16V_GROSS_LEN(sizeof(uint8_t)*m_llc.frame_length()));
+	tl16v_put(buf, BSSGP_IE_LLC_PDU, sizeof(uint8_t)*m_llc.frame_length(), m_llc.frame);
+	qos_profile[0] = QOS_PROFILE >> 16;
+	qos_profile[1] = QOS_PROFILE >> 8;
+	qos_profile[2] = QOS_PROFILE;
+	bssgp_tx_ul_ud(bctx, tlli(), qos_profile, llc_pdu);
+
+	m_llc.reset_frame_space();
+	return 0;
+}
+