Changed data structures for TBF and PDCH instances, to allow multislot

The new data structure is required to define slot/TFI assigment for MS
with multislot capability.

Now there are two lists for TBFs: uplink and downlink. It is possible to
have different TBFs with same TFI in the same direction, as long as they
are assigned on different timeslots.

See tbf.txt for description.

Note: This does not implement any multislot support. It defines the new
data structure. Currently only the first slot is assigned.
diff --git a/src/gprs_bssgp_pcu.cpp b/src/gprs_bssgp_pcu.cpp
index d1ef046..c4d70af 100644
--- a/src/gprs_bssgp_pcu.cpp
+++ b/src/gprs_bssgp_pcu.cpp
@@ -31,10 +31,9 @@
 {
 	struct bssgp_ud_hdr *budh;
 
-	int tfi;
+	int8_t tfi; /* must be signed */
 	uint32_t tlli;
 	int i, j;
-	uint8_t trx, ts;
 	uint8_t *data;
 	uint16_t len;
 	struct gprs_rlcmac_tbf *tbf;
@@ -101,19 +100,36 @@
 			msgb_enqueue(&tbf->llc_queue, llc_msg);
 		}
 	} else {
-		// Create new TBF
-		tfi = tfi_alloc(&trx, &ts);
+		uint8_t trx, ts, use_trx, first_ts;
+
+		/* check for uplink data, so we copy our informations */
+		if ((tbf = tbf_by_tlli(tlli, GPRS_RLCMAC_UL_TBF))) {
+			use_trx = tbf->trx;
+			first_ts = tbf->first_ts;
+		} else {
+			use_trx = -1;
+			first_ts = -1;
+		}
+
+		// Create new TBF (any TRX)
+		tfi = tfi_alloc(GPRS_RLCMAC_DL_TBF, &trx, &ts, use_trx, first_ts);
 		if (tfi < 0) {
 			LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH ressource\n");
 			/* FIXME: send reject */
 			return -EBUSY;
 		}
-		tbf = tbf_alloc(tfi, trx, ts);
-		tbf->direction = GPRS_RLCMAC_DL_TBF;
+		/* FIXME: set number of downlink slots according to multislot
+		 * class */
+		tbf = tbf_alloc(GPRS_RLCMAC_DL_TBF, tfi, trx, ts, 1);
+		if (!tbf) {
+			LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH ressource\n");
+			/* FIXME: send reject */
+			return -EBUSY;
+		}
 		tbf->tlli = tlli;
 		tbf->tlli_valid = 1;
 
-		LOGP(DRLCMAC, LOGL_DEBUG, "TBF: [DOWNLINK] START TFI: %u TLLI: 0x%08x \n", tbf->tfi, tbf->tlli);
+		LOGP(DRLCMAC, LOGL_DEBUG, "TBF: [DOWNLINK] START TFI: %d TLLI: 0x%08x \n", tbf->tfi, tbf->tlli);
 
 		/* new TBF, so put first frame */
 		memcpy(tbf->llc_frame, data, len);
diff --git a/src/gprs_rlcmac.cpp b/src/gprs_rlcmac.cpp
index 9d2601c..ab04aea 100644
--- a/src/gprs_rlcmac.cpp
+++ b/src/gprs_rlcmac.cpp
@@ -22,18 +22,33 @@
 #include <pcu_l1_if.h>
 #include <gprs_rlcmac.h>
 
-LLIST_HEAD(gprs_rlcmac_tbfs);
+LLIST_HEAD(gprs_rlcmac_ul_tbfs);
+LLIST_HEAD(gprs_rlcmac_dl_tbfs);
 void *rlcmac_tall_ctx;
 
-/* FIXME: spread ressources on multiple TRX */
-int tfi_alloc(uint8_t *_trx, uint8_t *_ts)
+/* FIXME: spread ressources over multiple TRX. Also add option to use same
+ * TRX in case of existing TBF for TLLI in the other direction. */
+/* search for free TFI and return TFI, TRX and first TS */
+int tfi_alloc(enum gprs_rlcmac_tbf_direction dir, uint8_t *_trx, uint8_t *_ts,
+	uint8_t use_trx, uint8_t first_ts)
 {
 	struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
 	struct gprs_rlcmac_pdch *pdch;
-	uint8_t trx, ts, tfi;
+	struct gprs_rlcmac_tbf **tbfp;
+	uint8_t trx_from, trx_to, trx, ts, tfi;
 
-	for (trx = 0; trx < 8; trx++) {
-		for (ts = 0; ts < 8; ts++) {
+	if (use_trx >= 0 && use_trx < 8)
+		trx_from = trx_to = use_trx;
+	else {
+		trx_from = 0;
+		trx_to = 7;
+	}
+	if (first_ts < 0 || first_ts >= 8)
+		first_ts = 0;
+
+	/* on TRX find first enabled TS */
+	for (trx = trx_from; trx <= trx_to; trx++) {
+		for (ts = first_ts; ts < 8; ts++) {
 			pdch = &bts->trx[trx].pdch[ts];
 			if (!pdch->enable)
 				continue;
@@ -42,16 +57,20 @@
 		if (ts < 8)
 			break;
 	}
-	if (trx == 8) {
+	if (trx > trx_to) {
 		LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH available.\n");
 		return -EINVAL;
 	}
 
 
 	LOGP(DRLCMAC, LOGL_DEBUG, "Searching for first unallocated TFI: "
-		"TRX=%d TS=%d\n", trx, ts);
+		"TRX=%d first TS=%d\n", trx, ts);
+	if (dir == GPRS_RLCMAC_UL_TBF)
+		tbfp = pdch->ul_tbf;
+	else
+		tbfp = pdch->dl_tbf;
 	for (tfi = 0; tfi < 32; tfi++) {
-		if (!pdch->tbf[tfi])
+		if (!tbfp[tfi])
 			break;
 	}
 	
@@ -66,126 +85,221 @@
 	return -1;
 }
 
-int find_free_usf(uint8_t trx, uint8_t ts)
+static inline int8_t find_free_usf(struct gprs_rlcmac_pdch *pdch, uint8_t ts)
 {
-	struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
-	struct gprs_rlcmac_pdch *pdch;
 	struct gprs_rlcmac_tbf *tbf;
 	uint8_t usf_map = 0;
 	uint8_t tfi, usf;
 
-	if (trx >= 8 || ts >= 8)
-		return -EINVAL;
-	pdch = &bts->trx[trx].pdch[ts];
-
 	/* make map of used USF */
 	for (tfi = 0; tfi < 32; tfi++) {
-		tbf = pdch->tbf[tfi];
+		tbf = pdch->ul_tbf[tfi];
 		if (!tbf)
 			continue;
-		if (tbf->direction != GPRS_RLCMAC_UL_TBF)
-			continue;
-		usf_map |= (1 << tbf->dir.ul.usf);
+		usf_map |= (1 << tbf->dir.ul.usf[ts]);
 	}
 
 	/* look for USF, don't use USF=7 */
 	for (usf = 0; usf < 7; usf++) {
-		if (!(usf_map & (1 << usf))) {
-			LOGP(DRLCMAC, LOGL_DEBUG, " Found USF=%d.\n", usf);
+		if (!(usf_map & (1 << usf)))
 			return usf;
-		}
 	}
-	LOGP(DRLCMAC, LOGL_NOTICE, "No USF available.\n");
 
 	return -1;
 }
 
 /* lookup TBF Entity (by TFI) */
-#warning FIXME: use pdch instance by trx and ts, because tfi is local
-struct gprs_rlcmac_tbf *tbf_by_tfi(uint8_t tfi, int direction)
+struct gprs_rlcmac_tbf *tbf_by_tfi(uint8_t tfi, uint8_t trx, uint8_t ts,
+	enum gprs_rlcmac_tbf_direction dir)
 {
 	struct gprs_rlcmac_tbf *tbf;
+	struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
 
-	llist_for_each_entry(tbf, &gprs_rlcmac_tbfs, list) {
-		if (tbf->state != GPRS_RLCMAC_RELEASING
-		 && tbf->tfi == tfi
-		 && tbf->direction == direction)
+	if (tfi >= 32 || trx >= 8 || ts >= 8)
+		return NULL;
+
+	if (dir == GPRS_RLCMAC_UL_TBF)
+		tbf = bts->trx[trx].pdch[ts].ul_tbf[tfi];
+	else
+		tbf = bts->trx[trx].pdch[ts].dl_tbf[tfi];
+	if (!tbf)
+		return NULL;
+
+	if (tbf->state != GPRS_RLCMAC_RELEASING)
 			return tbf;
-	}
+
 	return NULL;
 }
 
 /* search for active downlink or uplink tbf */
-struct gprs_rlcmac_tbf *tbf_by_tlli(uint32_t tlli, int direction)
+struct gprs_rlcmac_tbf *tbf_by_tlli(uint32_t tlli,
+	enum gprs_rlcmac_tbf_direction dir)
 {
 	struct gprs_rlcmac_tbf *tbf;
-	llist_for_each_entry(tbf, &gprs_rlcmac_tbfs, list) {
-		if (tbf->state != GPRS_RLCMAC_RELEASING
-		 && tbf->tlli == tlli
-		 && tbf->direction == direction)
-			return tbf;
+	if (dir == GPRS_RLCMAC_UL_TBF) {
+		llist_for_each_entry(tbf, &gprs_rlcmac_ul_tbfs, list) {
+			if (tbf->state != GPRS_RLCMAC_RELEASING
+			 && tbf->tlli == tlli)
+				return tbf;
+		}
+	} else {
+		llist_for_each_entry(tbf, &gprs_rlcmac_dl_tbfs, list) {
+			if (tbf->state != GPRS_RLCMAC_RELEASING
+			 && tbf->tlli == tlli)
+				return tbf;
+		}
 	}
 	return NULL;
 }
 
-#warning FIXME: use pdch instance by trx and ts, because polling is local
-struct gprs_rlcmac_tbf *tbf_by_poll_fn(uint32_t fn)
+struct gprs_rlcmac_tbf *tbf_by_poll_fn(uint32_t fn, uint8_t trx, uint8_t ts)
 {
+	struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
 	struct gprs_rlcmac_tbf *tbf;
-	llist_for_each_entry(tbf, &gprs_rlcmac_tbfs, list) {
-		if (tbf->state != GPRS_RLCMAC_RELEASING
+	uint8_t tfi;
+
+	/* only one TBF can poll on specific TS/FN, because scheduler can only
+	 * schedule one downlink control block (with polling) at a FN per TS */
+	for (tfi = 0; tfi < 32; tfi++) {
+		tbf = bts->trx[trx].pdch[ts].ul_tbf[tfi];
+		if (tbf && tbf->state != GPRS_RLCMAC_RELEASING
 		 && tbf->poll_state == GPRS_RLCMAC_POLL_SCHED
-		 && tbf->poll_fn == fn)
+		 && tbf->poll_fn == fn && tbf->poll_ts == ts)
+			return tbf;
+		tbf = bts->trx[trx].pdch[ts].dl_tbf[tfi];
+		if (tbf && tbf->state != GPRS_RLCMAC_RELEASING
+		 && tbf->poll_state == GPRS_RLCMAC_POLL_SCHED
+		 && tbf->poll_fn == fn && tbf->poll_ts == ts)
 			return tbf;
 	}
 	return NULL;
 }
 
-struct gprs_rlcmac_tbf *tbf_alloc(uint8_t tfi, uint8_t trx, uint8_t ts)
+struct gprs_rlcmac_tbf *tbf_alloc(enum gprs_rlcmac_tbf_direction dir,
+	uint8_t tfi, uint8_t trx, uint8_t first_ts, uint8_t num_ts)
 {
 	struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
 	struct gprs_rlcmac_pdch *pdch;
 	struct gprs_rlcmac_tbf *tbf;
+	uint8_t ts_count, ts;
+	int8_t usf, tsc = -1; /* both must be signed */
 
 	LOGP(DRLCMAC, LOGL_DEBUG, "********** TBF starts here **********\n");
-	LOGP(DRLCMAC, LOGL_INFO, "Allocating TBF with TFI=%d.\n", tfi);
+	LOGP(DRLCMAC, LOGL_INFO, "Allocating %s TBF with TFI=%d on TRX=%d.\n",
+		(dir == GPRS_RLCMAC_UL_TBF) ? "UL" : "DL", tfi, trx);
 
-	if (trx >= 8 || ts >= 8 || tfi >= 32)
+	if (trx >= 8 || first_ts >= 8 || tfi >= 32)
 		return NULL;
-	pdch = &bts->trx[trx].pdch[ts];
 
 	tbf = talloc_zero(rlcmac_tall_ctx, struct gprs_rlcmac_tbf);
 	if (!tbf)
 		return NULL;
 
+	tbf->direction = dir;
 	tbf->tfi = tfi;
 	tbf->trx = trx;
-	tbf->ts = ts;
 	tbf->arfcn = bts->trx[trx].arfcn;
-	tbf->tsc = bts->trx[trx].pdch[ts].tsc;
-	tbf->pdch = pdch;
+	/* assign free TS to TBF, where TFI is free
+	 * for uplink: assign free USF to each uplink TS
+	 * Note that the first TS must be free, because it was selected by
+	 * tfi_alloc(). */
+	for (ts_count = 0, ts = first_ts; ts < 8; ts++) {
+		pdch = &bts->trx[trx].pdch[ts];
+		if (!pdch->enable)
+			continue;
+		if (tsc < 0)
+			tbf->tsc = tsc = pdch->tsc;
+		else if (tsc != pdch->tsc) {
+			LOGP(DRLCMAC, LOGL_ERROR, "Skipping TS=%d of TRX=%d, "
+				"because it has different TSC than lower TS "
+				"of TRX. In order to allow multislot, all "
+				"slots must be configured with the same TSC!\n",
+				ts, trx);
+			continue;
+		}
+		if (dir == GPRS_RLCMAC_UL_TBF) {
+			/* if TFI is free on TS */
+			if (!pdch->ul_tbf[tfi]) {
+				/* if USF available */
+				usf = find_free_usf(pdch, ts);
+				if (usf >= 0) {
+					LOGP(DRLCMAC, LOGL_DEBUG, " Assign "
+						"uplink TS=%d USF=%d\n",
+						ts, usf);
+					pdch->ul_tbf[tfi] = tbf;
+					tbf->pdch[ts] = pdch;
+					ts_count++;
+				} else
+					LOGP(DRLCMAC, LOGL_DEBUG, " Skipping "
+						"TS=%d, no USF available\n",
+						ts);
+			}
+		} else {
+			/* if TFI is free on TS */
+			if (!pdch->dl_tbf[tfi]) {
+				LOGP(DRLCMAC, LOGL_DEBUG, " Assign downlink "
+					"TS=%d\n", ts);
+				pdch->dl_tbf[tfi] = tbf;
+				tbf->pdch[ts] = pdch;
+				ts_count++;
+			}
+		}
+		if (ts_count == num_ts)
+			break;
+	}
+	if (!ts_count) { /* implies that direction is uplink */
+		LOGP(DRLCMAC, LOGL_NOTICE, "No USF available\n");
+		talloc_free(tbf);
+		return NULL;
+	}
+
+	tbf->first_ts = first_ts;
 	tbf->ws = 64;
 	tbf->sns = 128;
 	INIT_LLIST_HEAD(&tbf->llc_queue);
-	llist_add(&tbf->list, &gprs_rlcmac_tbfs);
-	pdch->tbf[tfi] = tbf;
+	if (dir == GPRS_RLCMAC_UL_TBF)
+		llist_add(&tbf->list, &gprs_rlcmac_ul_tbfs);
+	else
+		llist_add(&tbf->list, &gprs_rlcmac_dl_tbfs);
 
 	return tbf;
 }
 
 void tbf_free(struct gprs_rlcmac_tbf *tbf)
 {
-	struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
 	struct gprs_rlcmac_pdch *pdch;
 	struct msgb *msg;
+	int ts;
 
-	LOGP(DRLCMAC, LOGL_INFO, "Free TBF=%d with TLLI=0x%08x.\n", tbf->tfi,
+	LOGP(DRLCMAC, LOGL_INFO, "Free %s TBF=%d with TLLI=0x%08x.\n",
+		(tbf->direction == GPRS_RLCMAC_UL_TBF) ? "UL" : "DL", tbf->tfi,
 		tbf->tlli);
+	if (tbf->ul_ass_state != GPRS_RLCMAC_UL_ASS_NONE)
+		LOGP(DRLCMAC, LOGL_ERROR, "Software error: Pending uplink "
+			"assignment. This may not happen, because the "
+			"assignment message never gets transmitted. Please "
+			"be shure not to free in this state. PLEASE FIX!\n");
+	if (tbf->dl_ass_state != GPRS_RLCMAC_DL_ASS_NONE)
+		LOGP(DRLCMAC, LOGL_ERROR, "Software error: Pending downlink "
+			"assignment. This may not happen, because the "
+			"assignment message never gets transmitted. Please "
+			"be shure not to free in this state. PLEASE FIX!\n");
 	tbf_timer_stop(tbf);
 	while ((msg = msgb_dequeue(&tbf->llc_queue)))
 		msgb_free(msg);
-	pdch = &bts->trx[tbf->trx].pdch[tbf->ts];
-	pdch->tbf[tbf->tfi] = NULL;
+	if (tbf->direction == GPRS_RLCMAC_UL_TBF) {
+		for (ts = 0; ts < 8; ts++) {
+			pdch = tbf->pdch[ts];
+			if (pdch)
+				pdch->ul_tbf[tbf->tfi] = NULL;
+		}
+	} else {
+		for (ts = 0; ts < 8; ts++) {
+			pdch = tbf->pdch[ts];
+			if (pdch)
+				pdch->dl_tbf[tbf->tfi] = NULL;
+		}
+	}
 	llist_del(&tbf->list);
 	LOGP(DRLCMAC, LOGL_DEBUG, "********** TBF ends here **********\n");
 	talloc_free(tbf);
@@ -203,8 +317,9 @@
 void tbf_new_state(struct gprs_rlcmac_tbf *tbf,
 	enum gprs_rlcmac_tbf_state state)
 {
-	LOGP(DRLCMAC, LOGL_DEBUG, "TBF=%d changes state from %s to %s\n",
-		tbf->tfi, tbf_state_name[tbf->state], tbf_state_name[state]);
+	LOGP(DRLCMAC, LOGL_DEBUG, "%s TBF=%d changes state from %s to %s\n",
+		(tbf->direction == GPRS_RLCMAC_UL_TBF) ? "UL" : "DL", tbf->tfi,
+		tbf_state_name[tbf->state], tbf_state_name[state]);
 	tbf->state = state;
 }
 
@@ -212,11 +327,14 @@
 			unsigned int seconds, unsigned int microseconds)
 {
 	if (!osmo_timer_pending(&tbf->timer))
-		LOGP(DRLCMAC, LOGL_DEBUG, "Starting TBF=%d timer %u.\n",
+		LOGP(DRLCMAC, LOGL_DEBUG, "Starting %s TBF=%d timer %u.\n",
+			(tbf->direction == GPRS_RLCMAC_UL_TBF) ? "UL" : "DL",
 			tbf->tfi, T);
 	else
-		LOGP(DRLCMAC, LOGL_DEBUG, "Restarting TBF=%d timer %u while "
-			"old timer %u pending \n", tbf->tfi, T, tbf->T);
+		LOGP(DRLCMAC, LOGL_DEBUG, "Restarting %s TBF=%d timer %u "
+			"while old timer %u pending \n",
+			(tbf->direction == GPRS_RLCMAC_UL_TBF) ? "UL" : "DL",
+			tbf->tfi, T, tbf->T);
 
 	tbf->T = T;
 	tbf->num_T_exp = 0;
@@ -231,7 +349,8 @@
 void tbf_timer_stop(struct gprs_rlcmac_tbf *tbf)
 {
 	if (osmo_timer_pending(&tbf->timer)) {
-		LOGP(DRLCMAC, LOGL_DEBUG, "Stopping TBF=%d timer %u.\n",
+		LOGP(DRLCMAC, LOGL_DEBUG, "Stopping %s TBF=%d timer %u.\n",
+			(tbf->direction == GPRS_RLCMAC_UL_TBF) ? "UL" : "DL",
 			tbf->tfi, tbf->T);
 		osmo_timer_del(&tbf->timer);
 	}
@@ -286,7 +405,8 @@
 #endif
 
 /* received RLC/MAC block from L1 */
-int gprs_rlcmac_rcv_block(uint8_t *data, uint8_t len, uint32_t fn)
+int gprs_rlcmac_rcv_block(uint8_t trx, uint8_t ts, uint8_t *data, uint8_t len,
+	uint32_t fn)
 {
 	unsigned payload = data[0] >> 6;
 	bitvec *block;
@@ -294,14 +414,15 @@
 
 	switch (payload) {
 	case GPRS_RLCMAC_DATA_BLOCK:
-		rc = gprs_rlcmac_rcv_data_block_acknowledged(data, len);
+		rc = gprs_rlcmac_rcv_data_block_acknowledged(trx, ts, data,
+			len);
 		break;
 	case GPRS_RLCMAC_CONTROL_BLOCK:
 		block = bitvec_alloc(len);
 		if (!block)
 			return -ENOMEM;
 		bitvec_unpack(block, data);
-		rc = gprs_rlcmac_rcv_control_block(block, fn);
+		rc = gprs_rlcmac_rcv_control_block(block, trx, ts, fn);
 		bitvec_free(block);
 		break;
 	case GPRS_RLCMAC_CONTROL_BLOCK_OPT:
@@ -417,14 +538,13 @@
 
 /* generate uplink assignment */
 void write_packet_uplink_assignment(bitvec * dest, uint8_t old_tfi,
-	uint8_t old_downlink, uint32_t tlli, uint8_t use_tlli, uint8_t new_tfi,
-	uint8_t usf, uint16_t arfcn, uint8_t tn, uint8_t ta, uint8_t tsc,
-	uint8_t poll)
+	uint8_t old_downlink, uint32_t tlli, uint8_t use_tlli,
+	struct gprs_rlcmac_tbf *tbf, uint8_t poll)
 {
 	// TODO We should use our implementation of encode RLC/MAC Control messages.
 	struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
 	unsigned wp = 0;
-	int i;
+	uint8_t ts;
 
 	bitvec_write_field(dest, wp,0x1,2);  // Payload Type
 	bitvec_write_field(dest, wp,0x0,2);  // Uplink block with TDMA framenumber (N+13)
@@ -445,18 +565,18 @@
 	}
 
 	bitvec_write_field(dest, wp,0x0,1); // Message escape
-	bitvec_write_field(dest, wp, bts->initial_cs-1, 2); // CHANNEL_CODING_COMMAND 
+	bitvec_write_field(dest, wp,bts->initial_cs-1, 2); // CHANNEL_CODING_COMMAND 
 	bitvec_write_field(dest, wp,0x1,1); // TLLI_BLOCK_CHANNEL_CODING 
 
 	bitvec_write_field(dest, wp,0x1,1); // switch TIMING_ADVANCE_VALUE = on
-	bitvec_write_field(dest, wp,ta,6); // TIMING_ADVANCE_VALUE
+	bitvec_write_field(dest, wp,tbf->ta,6); // TIMING_ADVANCE_VALUE
 	bitvec_write_field(dest, wp,0x0,1); // switch TIMING_ADVANCE_INDEX = off
 
 #if 1
 	bitvec_write_field(dest, wp,0x1,1); // Frequency Parameters information elements = present
-	bitvec_write_field(dest, wp,tsc,3); // Training Sequence Code (TSC)
+	bitvec_write_field(dest, wp,tbf->tsc,3); // Training Sequence Code (TSC)
 	bitvec_write_field(dest, wp,0x0,2); // ARFCN = present
-	bitvec_write_field(dest, wp,arfcn,10); // ARFCN
+	bitvec_write_field(dest, wp,tbf->arfcn,10); // ARFCN
 #else
 	bitvec_write_field(dest, wp,0x0,1); // Frequency Parameters = off
 #endif
@@ -468,16 +588,16 @@
 	
 	bitvec_write_field(dest, wp,0x0,1); // USF_GRANULARITY
 	bitvec_write_field(dest, wp,0x1,1); // switch TFI   : on
-	bitvec_write_field(dest, wp,new_tfi,5);// TFI
+	bitvec_write_field(dest, wp,tbf->tfi,5);// TFI
 
 	bitvec_write_field(dest, wp,0x0,1); //
 	bitvec_write_field(dest, wp,0x0,1); // TBF Starting Time = off
 	bitvec_write_field(dest, wp,0x0,1); // Timeslot Allocation
 	
-	for (i = 0; i < 8; i++) {
-		if (tn == i) {
+	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,usf,3); // USF_TN(i)
+			bitvec_write_field(dest, wp,tbf->dir.ul.usf[ts],3); // USF_TN(i)
 		} else
 			bitvec_write_field(dest, wp,0x0,1); // USF_TN(i): off
 	}
@@ -487,12 +607,11 @@
 
 /* generate downlink assignment */
 void write_packet_downlink_assignment(RlcMacDownlink_t * block, uint8_t old_tfi,
-	uint8_t old_downlink, uint8_t new_tfi, uint16_t arfcn,
-	uint8_t tn, uint8_t ta, uint8_t tsc, uint8_t poll)
+	uint8_t old_downlink, struct gprs_rlcmac_tbf *tbf, uint8_t poll)
 {
 	// Packet downlink assignment TS 44.060 11.2.7
 
-	int i;
+	uint8_t tn;
 
 	block->PAYLOAD_TYPE = 0x1;  // RLC/MAC control block that does not include the optional octets of the RLC/MAC control header
 	block->RRBP         = 0x0;  // N+13
@@ -511,35 +630,39 @@
 	block->u.Packet_Downlink_Assignment.MAC_MODE            = 0x0;          // Dynamic Allocation
 	block->u.Packet_Downlink_Assignment.RLC_MODE            = 0x0;          // RLC acknowledged mode
 	block->u.Packet_Downlink_Assignment.CONTROL_ACK         = old_downlink; // NW establishes no new DL TBF for the MS with running timer T3192
-	block->u.Packet_Downlink_Assignment.TIMESLOT_ALLOCATION = 0x80 >> tn;   // timeslot(s)
+	block->u.Packet_Downlink_Assignment.TIMESLOT_ALLOCATION = 0;   // timeslot(s)
+	for (tn = 0; tn < 8; tn++) {
+		if (tbf->pdch[tn])
+			block->u.Packet_Downlink_Assignment.TIMESLOT_ALLOCATION |= 0x80 >> tn;   // timeslot(s)
+	}
 
 	block->u.Packet_Downlink_Assignment.Packet_Timing_Advance.Exist_TIMING_ADVANCE_VALUE = 0x1; // TIMING_ADVANCE_VALUE = on
-	block->u.Packet_Downlink_Assignment.Packet_Timing_Advance.TIMING_ADVANCE_VALUE       = ta;  // TIMING_ADVANCE_VALUE
+	block->u.Packet_Downlink_Assignment.Packet_Timing_Advance.TIMING_ADVANCE_VALUE       = tbf->ta;  // TIMING_ADVANCE_VALUE
 	block->u.Packet_Downlink_Assignment.Packet_Timing_Advance.Exist_IndexAndtimeSlot     = 0x0; // TIMING_ADVANCE_INDEX = off
 
 	block->u.Packet_Downlink_Assignment.Exist_P0_and_BTS_PWR_CTRL_MODE = 0x0;   // POWER CONTROL = off
 
 	block->u.Packet_Downlink_Assignment.Exist_Frequency_Parameters     = 0x1;   // Frequency Parameters = on
-	block->u.Packet_Downlink_Assignment.Frequency_Parameters.TSC       = tsc;   // Training Sequence Code (TSC)
+	block->u.Packet_Downlink_Assignment.Frequency_Parameters.TSC       = tbf->tsc;   // Training Sequence Code (TSC)
 	block->u.Packet_Downlink_Assignment.Frequency_Parameters.UnionType = 0x0;   // ARFCN = on
-	block->u.Packet_Downlink_Assignment.Frequency_Parameters.u.ARFCN   = arfcn; // ARFCN
+	block->u.Packet_Downlink_Assignment.Frequency_Parameters.u.ARFCN   = tbf->arfcn; // ARFCN
 
 	block->u.Packet_Downlink_Assignment.Exist_DOWNLINK_TFI_ASSIGNMENT  = 0x1;     // DOWNLINK TFI ASSIGNMENT = on
-	block->u.Packet_Downlink_Assignment.DOWNLINK_TFI_ASSIGNMENT        = new_tfi; // TFI
+	block->u.Packet_Downlink_Assignment.DOWNLINK_TFI_ASSIGNMENT        = tbf->tfi; // TFI
 
 	block->u.Packet_Downlink_Assignment.Exist_Power_Control_Parameters = 0x1;   // Power Control Parameters = on
 	block->u.Packet_Downlink_Assignment.Power_Control_Parameters.ALPHA = 0x0;   // ALPHA
 
-	for (i = 0; i < 8; i++)
+	for (tn = 0; tn < 8; tn++)
 	{
-		if (tn == i)
+		if (tbf->pdch[tn])
 		{
-			block->u.Packet_Downlink_Assignment.Power_Control_Parameters.Slot[i].Exist    = 0x1; // Slot[i] = on
-			block->u.Packet_Downlink_Assignment.Power_Control_Parameters.Slot[i].GAMMA_TN = 0x0; // GAMMA_TN
+			block->u.Packet_Downlink_Assignment.Power_Control_Parameters.Slot[tn].Exist    = 0x1; // Slot[i] = on
+			block->u.Packet_Downlink_Assignment.Power_Control_Parameters.Slot[tn].GAMMA_TN = 0x0; // GAMMA_TN
 		}
 		else
 		{
-			block->u.Packet_Downlink_Assignment.Power_Control_Parameters.Slot[i].Exist    = 0x0; // Slot[i] = off
+			block->u.Packet_Downlink_Assignment.Power_Control_Parameters.Slot[tn].Exist    = 0x0; // Slot[i] = off
 		}
 	}
 
@@ -616,7 +739,7 @@
 	struct msgb *llc_pdu;
 	unsigned msg_len = NS_HDR_LEN + BSSGP_HDR_LEN + tbf->llc_index;
 
-	LOGP(DBSSGP, LOGL_INFO, "LLC [PCU -> SGSN] TFI: %u TLLI: 0x%08x %s\n", tbf->tfi, tbf->tlli, osmo_hexdump(tbf->llc_frame, tbf->llc_index));
+	LOGP(DBSSGP, LOGL_INFO, "LLC [PCU -> SGSN] TFI: %u TLLI: 0x%08x len=%d\n", tbf->tfi, tbf->tlli, tbf->llc_index);
 	if (!bctx) {
 		LOGP(DBSSGP, LOGL_ERROR, "No bctx\n");
 		return -EIO;
diff --git a/src/gprs_rlcmac.h b/src/gprs_rlcmac.h
index 635a6b1..db0e9ab 100644
--- a/src/gprs_rlcmac.h
+++ b/src/gprs_rlcmac.h
@@ -46,7 +46,8 @@
 	uint8_t tsc; /* TSC of this slot */
 	uint8_t next_ul_tfi; /* next uplink TBF/TFI to schedule (0..31) */
 	uint8_t next_dl_tfi; /* next downlink TBF/TFI to schedule (0..31) */
-	struct gprs_rlcmac_tbf *tbf[32]; /* array of TBF pointers, by TFI */
+	struct gprs_rlcmac_tbf *ul_tbf[32]; /* array of UL TBF, by UL TFI */
+	struct gprs_rlcmac_tbf *dl_tbf[32]; /* array of DL TBF, by DL TFI */
 	uint32_t last_rts_fn; /* store last frame number of RTS */
 };
 
@@ -130,9 +131,12 @@
 	uint8_t tfi;
 	uint32_t tlli;
 	uint8_t tlli_valid;
-	uint8_t trx, ts, tsc;
-	struct gprs_rlcmac_pdch *pdch;
-	uint16_t arfcn, ta;
+	uint8_t trx;
+	uint16_t arfcn;
+	uint8_t tsc;
+	uint8_t first_ts;
+	struct gprs_rlcmac_pdch *pdch[8]; /* list of PDCHs allocated to TBF */
+	uint16_t ta;
 	uint8_t llc_frame[LLC_MAX_LEN]; /* current DL or UL frame */
 	uint16_t llc_index; /* current write/read position of frame */
 	uint16_t llc_length; /* len of current DL LLC_frame, 0 == no frame */
@@ -143,7 +147,8 @@
 	enum gprs_rlcmac_tbf_ul_ack_state ul_ack_state;
 
 	enum gprs_rlcmac_tbf_poll_state poll_state;
-	uint32_t poll_fn;
+	uint32_t poll_fn; /* frame number to poll */
+	uint8_t poll_ts; /* timeslot to poll */
 
 	uint16_t ws;	/* window size */
 	uint16_t sns;	/* sequence number space */
@@ -169,7 +174,7 @@
 			char v_n[RLC_MAX_SNS/2]; /* receive state array */
 			int32_t rx_counter; /* count all received blocks */
 			uint8_t n3103;	/* N3103 counter */
-			uint8_t usf;	/* USF */
+			uint8_t usf[8];	/* list USFs per PDCH (timeslot) */
 		} ul;
 	} dir;
 	uint8_t rlc_block[RLC_MAX_SNS/2][RLC_MAX_LEN]; /* block history */
@@ -184,19 +189,22 @@
 	unsigned int num_fT_exp; /* number of consecutive fT expirations */
 };
 
-extern struct llist_head gprs_rlcmac_tbfs;
+extern struct llist_head gprs_rlcmac_ul_tbfs; /* list of uplink TBFs */
+extern struct llist_head gprs_rlcmac_dl_tbfs; /* list of downlink TBFs */
 
-int tfi_alloc(uint8_t *_trx, uint8_t *_ts);
+int tfi_alloc(enum gprs_rlcmac_tbf_direction dir, uint8_t *_trx, uint8_t *_ts,
+	uint8_t use_trx, uint8_t first_ts);
 
-struct gprs_rlcmac_tbf *tbf_alloc(uint8_t tfi, uint8_t trx, uint8_t ts);
+struct gprs_rlcmac_tbf *tbf_alloc(enum gprs_rlcmac_tbf_direction dir,
+	uint8_t tfi, uint8_t trx, uint8_t first_ts, uint8_t num_ts);
 
-struct gprs_rlcmac_tbf *tbf_by_tfi(uint8_t tfi, int direction);
+struct gprs_rlcmac_tbf *tbf_by_tfi(uint8_t tfi, uint8_t trx, uint8_t ts,
+        enum gprs_rlcmac_tbf_direction dir);
 
-struct gprs_rlcmac_tbf *tbf_by_tlli(uint32_t tlli, int direction);
+struct gprs_rlcmac_tbf *tbf_by_tlli(uint32_t tlli,
+	enum gprs_rlcmac_tbf_direction dir);
 
-struct gprs_rlcmac_tbf *tbf_by_poll_fn(uint32_t fn);
-
-int find_free_usf(uint8_t trx, uint8_t ts);
+struct gprs_rlcmac_tbf *tbf_by_poll_fn(uint32_t fn, uint8_t trx, uint8_t ts);
 
 void tbf_free(struct gprs_rlcmac_tbf *tbf);
 
@@ -216,7 +224,8 @@
 	GPRS_RLCMAC_RESERVED = 0x3
 };
 
-int gprs_rlcmac_rcv_block(uint8_t *data, uint8_t len, uint32_t fn);
+int gprs_rlcmac_rcv_block(uint8_t trx, uint8_t ts, uint8_t *data, uint8_t len,
+	uint32_t fn);
 
 int write_immediate_assignment(bitvec * dest, uint8_t downlink, uint8_t ra, 
         uint32_t fn, uint8_t ta, uint16_t arfcn, uint8_t ts, uint8_t tsc, 
@@ -224,13 +233,13 @@
 	uint32_t poll_fn);
 
 void write_packet_uplink_assignment(bitvec * dest, uint8_t old_tfi,
-        uint8_t old_downlink, uint32_t tlli, uint8_t use_tlli, uint8_t new_tfi,
-        uint8_t usf, uint16_t arfcn, uint8_t tn, uint8_t ta, uint8_t tsc,
-        uint8_t poll);
+	uint8_t old_downlink, uint32_t tlli, uint8_t use_tlli, 
+	struct gprs_rlcmac_tbf *tbf, uint8_t poll);
 
 void write_packet_downlink_assignment(RlcMacDownlink_t * block, uint8_t old_tfi,
-        uint8_t old_downlink, uint8_t new_tfi, uint16_t arfcn,
-        uint8_t tn, uint8_t ta, uint8_t tsc, uint8_t poll);
+	uint8_t old_downlink, struct gprs_rlcmac_tbf *tbf, uint8_t poll);
+
+
 
 void write_packet_uplink_ack(RlcMacDownlink_t * block, struct gprs_rlcmac_tbf *tbf,
         uint8_t final);
@@ -243,7 +252,8 @@
 
 int gprs_rlcmac_rcv_rach(uint8_t ra, uint32_t Fn, int16_t qta);
 
-int gprs_rlcmac_rcv_control_block(bitvec *rlc_block, uint32_t fn);
+int gprs_rlcmac_rcv_control_block(bitvec *rlc_block, uint8_t trx, uint8_t ts,
+	uint32_t fn);
 
 struct msgb *gprs_rlcmac_send_packet_uplink_assignment(
         struct gprs_rlcmac_tbf *tbf, uint32_t fn);
@@ -257,7 +267,8 @@
 int gprs_rlcmac_downlink_ack(struct gprs_rlcmac_tbf *tbf, uint8_t final,
         uint8_t ssn, uint8_t *rbb);
 
-int gprs_rlcmac_rcv_data_block_acknowledged(uint8_t *data, uint8_t len);
+int gprs_rlcmac_rcv_data_block_acknowledged(uint8_t trx, uint8_t ts,
+	uint8_t *data, uint8_t len);
 
 struct msgb *gprs_rlcmac_send_data_block_acknowledged(
         struct gprs_rlcmac_tbf *tbf, uint32_t fn);
diff --git a/src/gprs_rlcmac_data.cpp b/src/gprs_rlcmac_data.cpp
index 7beca38..0e8aca3 100644
--- a/src/gprs_rlcmac_data.cpp
+++ b/src/gprs_rlcmac_data.cpp
@@ -64,7 +64,8 @@
 
 int gprs_rlcmac_poll_timeout(struct gprs_rlcmac_tbf *tbf)
 {
-	LOGP(DRLCMAC, LOGL_NOTICE, "Poll timeout for TBF=%d\n", tbf->tfi);
+	LOGP(DRLCMAC, LOGL_NOTICE, "Poll timeout for %s TBF=%d\n",
+		(tbf->direction == GPRS_RLCMAC_UL_TBF) ? "UL" : "DL", tbf->tfi);
 
 	tbf->poll_state = GPRS_RLCMAC_POLL_NONE;
 
@@ -120,9 +121,10 @@
 }
 
 /* Received Uplink RLC control block. */
-int gprs_rlcmac_rcv_control_block(bitvec *rlc_block, uint32_t fn)
+int gprs_rlcmac_rcv_control_block(bitvec *rlc_block, uint8_t trx, uint8_t ts,
+	uint32_t fn)
 {
-	uint8_t tfi = 0;
+	int8_t tfi = 0; /* must be signed */
 	uint32_t tlli = 0;
 	struct gprs_rlcmac_tbf *tbf;
 
@@ -134,7 +136,7 @@
 	switch (ul_control_block->u.MESSAGE_TYPE) {
 	case MT_PACKET_CONTROL_ACK:
 		tlli = ul_control_block->u.Packet_Control_Acknowledgement.TLLI;
-		tbf = tbf_by_poll_fn(fn);
+		tbf = tbf_by_poll_fn(fn, trx, ts);
 		if (!tbf) {
 			LOGP(DRLCMAC, LOGL_NOTICE, "PACKET CONTROL ACK with "
 				"unknown FN=%u TLL=0x%08x\n", fn, tlli);
@@ -171,7 +173,7 @@
 		break;
 	case MT_PACKET_DOWNLINK_ACK_NACK:
 		tfi = ul_control_block->u.Packet_Downlink_Ack_Nack.DOWNLINK_TFI;
-		tbf = tbf_by_poll_fn(fn);
+		tbf = tbf_by_poll_fn(fn, trx, ts);
 		if (!tbf) {
 			LOGP(DRLCMAC, LOGL_NOTICE, "PACKET DOWNLINK ACK with "
 				"unknown FN=%u TBF=%d\n", fn, tfi);
@@ -191,32 +193,31 @@
 			ul_control_block->u.Packet_Downlink_Ack_Nack.Ack_Nack_Description.RECEIVED_BLOCK_BITMAP);
 		/* check for channel request */
 		if (ul_control_block->u.Packet_Downlink_Ack_Nack.Exist_Channel_Request_Description) {
-			uint8_t trx, ts, usf;
+			uint8_t trx, ts;
 			struct gprs_rlcmac_tbf *ul_tbf;
 			struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
 
 			LOGP(DRLCMAC, LOGL_DEBUG, "MS requests UL TBF in ack "
 				"message, so we provide one:\n");
 uplink_request:
-			/* create new tbf */
-			tfi = tfi_alloc(&trx, &ts);
+			/* create new TBF, use sme TRX as DL TBF */
+			tfi = tfi_alloc(GPRS_RLCMAC_UL_TBF, &trx, &ts, tbf->trx, tbf->first_ts);
 			if (tfi < 0) {
 				LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH ressource\n");
 				/* FIXME: send reject */
 				break;
 			}
-			usf = find_free_usf(trx, ts);
-			if (usf < 0) {
-				LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH ressource for USF\n");
+			/* FIXME: set number of downlink slots according to
+			 * multislot class */
+			ul_tbf = tbf_alloc(GPRS_RLCMAC_UL_TBF, tfi, trx, ts, 1);
+			if (!ul_tbf) {
+				LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH ressource\n");
 				/* FIXME: send reject */
 				break;
 			}
-			ul_tbf = tbf_alloc(tfi, trx, ts);
 			ul_tbf->tlli = tbf->tlli;
 			ul_tbf->tlli_valid = 1; /* no content resolution */
 			ul_tbf->ta = tbf->ta; /* use current TA */
-			ul_tbf->direction = GPRS_RLCMAC_UL_TBF;
-			ul_tbf->dir.ul.usf = usf;
 			tbf_new_state(ul_tbf, GPRS_RLCMAC_FLOW);
 			tbf_timer_start(ul_tbf, 3169, bts->t3169, 0);
 			/* schedule uplink assignment */
@@ -235,14 +236,14 @@
 		} else {
 			if (ul_control_block->u.Packet_Resource_Request.ID.u.Global_TFI.UnionType) {
 				tfi = ul_control_block->u.Packet_Resource_Request.ID.u.Global_TFI.u.DOWNLINK_TFI;
-				tbf = tbf_by_tfi(tfi, GPRS_RLCMAC_DL_TBF);
+				tbf = tbf_by_tfi(tfi, trx, ts, GPRS_RLCMAC_DL_TBF);
 				if (!tbf) {
 					LOGP(DRLCMAC, LOGL_NOTICE, "PACKET RESSOURCE REQ unknown downlink TBF=%d\n", tlli);
 					break;
 				}
 			} else {
 				tfi = ul_control_block->u.Packet_Resource_Request.ID.u.Global_TFI.u.UPLINK_TFI;
-				tbf = tbf_by_tfi(tfi, GPRS_RLCMAC_UL_TBF);
+				tbf = tbf_by_tfi(tfi, trx, ts, GPRS_RLCMAC_UL_TBF);
 				if (!tbf) {
 					LOGP(DRLCMAC, LOGL_NOTICE, "PACKET RESSOURCE REQ unknown uplink TBF=%d\n", tlli);
 					break;
@@ -250,7 +251,7 @@
 			}
 			tlli = tbf->tlli;
 		}
-		LOGP(DRLCMAC, LOGL_DEBUG, "RX: [PCU <- BTS] TFI: %u TLLI: 0x%08x Packet ressource request\n", tbf->tfi, tbf->tlli);
+		LOGP(DRLCMAC, LOGL_INFO, "RX: [PCU <- BTS] %s TFI: %u TLLI: 0x%08x Packet ressource request\n", (tbf->direction == GPRS_RLCMAC_UL_TBF) ? "UL" : "DL", tbf->tfi, tbf->tlli);
 #warning FIXME
 puts("FIXME: UL request during UL request");	exit(0);
 
@@ -271,7 +272,8 @@
 {
 	struct gprs_rlcmac_tbf *tbf = (struct gprs_rlcmac_tbf *)_tbf;
 
-	LOGP(DRLCMAC, LOGL_DEBUG, "TBF=%d timer %u expired.\n", tbf->tfi,
+	LOGP(DRLCMAC, LOGL_DEBUG, "%s TBF=%d timer %u expired.\n",
+		(tbf->direction == GPRS_RLCMAC_UL_TBF) ? "UL" : "DL", tbf->tfi,
 		tbf->T);
 
 	tbf->num_T_exp++;
@@ -495,8 +497,7 @@
 		if (i != frames - 1) {
 			/* send frame to SGSN */
 			LOGP(DRLCMACUL, LOGL_INFO, "Complete UL frame for "
-				"TBF=%d: %s\n", tbf->tfi,
-				osmo_hexdump(tbf->llc_frame, tbf->llc_index));
+				"TBF=%d: len=%d\n", tbf->tfi, tbf->llc_index);
 			gprs_rlcmac_tx_ul_ud(tbf);
 			tbf->llc_index = 0; /* reset frame space */
 		/* also check if CV==0, because the frame may fill up the
@@ -507,8 +508,7 @@
 			/* send frame to SGSN */
 			LOGP(DRLCMACUL, LOGL_INFO, "Complete UL frame for "
 				"TBF=%d that fits precisely in last block: "
-				"%s\n", tbf->tfi,
-				osmo_hexdump(tbf->llc_frame, tbf->llc_index));
+				"len=%d\n", tbf->tfi, tbf->llc_index);
 			gprs_rlcmac_tx_ul_ud(tbf);
 			tbf->llc_index = 0; /* reset frame space */
 		}
@@ -550,6 +550,7 @@
 	if (final) {
 		tbf->poll_state = GPRS_RLCMAC_POLL_SCHED;
 		tbf->poll_fn = (fn + 13) % 2715648;
+		tbf->poll_ts = tbf->first_ts;
 		/* waiting for final acknowledge */
 		tbf->ul_ack_state = GPRS_RLCMAC_UL_ACK_WAIT_ACK;
 	} else
@@ -562,7 +563,8 @@
  *
  * The blocks are defragmented and forwarded as LLC frames, if complete.
  */
-int gprs_rlcmac_rcv_data_block_acknowledged(uint8_t *data, uint8_t len)
+int gprs_rlcmac_rcv_data_block_acknowledged(uint8_t trx, uint8_t ts,
+	uint8_t *data, uint8_t len)
 {
 	struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
 	struct gprs_rlcmac_tbf *tbf;
@@ -592,19 +594,13 @@
 	}
 
 	/* find TBF inst from given TFI */
-	tbf = tbf_by_tfi(rh->tfi, GPRS_RLCMAC_UL_TBF);
+	tbf = tbf_by_tfi(rh->tfi, trx, ts, GPRS_RLCMAC_UL_TBF);
 	if (!tbf) {
 		LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA unknown TBF=%d\n",
 			rh->tfi);
 		return 0;
 	}
 
-	if (tbf->direction != GPRS_RLCMAC_UL_TBF) {
-		LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA TBF=%d not Uplink "
-			"tbf\n", rh->tfi);
-		return 0;
-	}
-
 	LOGP(DRLCMACUL, LOGL_DEBUG, "UL DATA TBF=%d received (V(Q)=%d .. "
 		"V(R)=%d)\n", rh->tfi, tbf->dir.ul.v_q, tbf->dir.ul.v_r);
 
@@ -755,12 +751,14 @@
 	struct msgb *msg;
 	struct gprs_rlcmac_tbf *new_tbf;
 
+#if POLLING_ASSIGNMENT == 1
 	if (tbf->poll_state != GPRS_RLCMAC_POLL_NONE) {
 		LOGP(DRLCMACUL, LOGL_DEBUG, "Polling is already "
 			"sheduled for TBF=%d, so we must wait for uplink "
 			"assignment...\n", tbf->tfi);
 			return NULL;
 	}
+#endif
 
 	/* on down TBF we get the uplink TBF to be assigned. */
 	if (tbf->direction == GPRS_RLCMAC_DL_TBF)
@@ -788,9 +786,8 @@
 	bitvec_unhex(ass_vec,
 		"2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
 	write_packet_uplink_assignment(ass_vec, tbf->tfi,
-		(tbf->direction == GPRS_RLCMAC_DL_TBF), 0, 0, new_tbf->tfi,
-		new_tbf->dir.ul.usf, new_tbf->arfcn, new_tbf->ts, new_tbf->ta,
-		new_tbf->tsc, POLLING_ASSIGNMENT);
+		(tbf->direction == GPRS_RLCMAC_DL_TBF), 0, 0, new_tbf,
+		POLLING_ASSIGNMENT);
 	bitvec_pack(ass_vec, msgb_put(msg, 23));
 	RlcMacDownlink_t * mac_control_block = (RlcMacDownlink_t *)malloc(sizeof(RlcMacDownlink_t));
 	LOGP(DRLCMAC, LOGL_DEBUG, "+++++++++++++++++++++++++ TX : Packet Uplink Assignment +++++++++++++++++++++++++\n");
@@ -803,6 +800,7 @@
 	FIXME process does not work, also the acknowledgement is not checked.
 	tbf->poll_state = GPRS_RLCMAC_POLL_SCHED;
 	tbf->poll_fn = (fn + 13) % 2715648;
+	tbf->poll_ts = tbf->first_ts;
 	tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_WAIT_ACK;
 #else
 	tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_NONE;
@@ -816,31 +814,29 @@
 	struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
 	struct gprs_rlcmac_tbf *tbf;
 	uint8_t trx, ts;
-	int tfi, usf; /* must be signed */
+	int8_t tfi; /* must be signed */
 
 	LOGP(DRLCMAC, LOGL_DEBUG, "MS requests UL TBF on RACH, so we provide "
 		"one:\n");
 	// Create new TBF
-	tfi = tfi_alloc(&trx, &ts);
+	tfi = tfi_alloc(GPRS_RLCMAC_UL_TBF, &trx, &ts, -1, -1);
 	if (tfi < 0) {
 		LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH ressource\n");
 		/* FIXME: send reject */
 		return -EBUSY;
 	}
-	usf = find_free_usf(trx, ts);
-	if (usf < 0) {
-		LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH ressource for USF\n");
+	/* select only one TS, since we don't know the multislot class yet */
+	tbf = tbf_alloc(GPRS_RLCMAC_UL_TBF, tfi, trx, ts, 1);
+	if (!tbf) {
+		LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH ressource\n");
 		/* FIXME: send reject */
 		return -EBUSY;
 	}
-	tbf = tbf_alloc(tfi, trx, ts);
 	if (qta < 0)
 		qta = 0;
 	if (qta > 252)
 		qta = 252;
 	tbf->ta = qta >> 2;
-	tbf->direction = GPRS_RLCMAC_UL_TBF;
-	tbf->dir.ul.usf = usf;
 	tbf_new_state(tbf, GPRS_RLCMAC_FLOW);
 	tbf_timer_start(tbf, 3169, bts->t3169, 0);
 	LOGP(DRLCMAC, LOGL_DEBUG, "TBF: [UPLINK] START TFI: %u\n", tbf->tfi);
@@ -848,7 +844,7 @@
 	LOGP(DRLCMAC, LOGL_INFO, "TX: START TFI: %u Immediate Assignment Uplink (AGCH)\n", tbf->tfi);
 	bitvec *immediate_assignment = bitvec_alloc(22) /* without plen */;
 	bitvec_unhex(immediate_assignment, "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
-	int plen = write_immediate_assignment(immediate_assignment, 0, ra, Fn, tbf->ta, tbf->arfcn, tbf->ts, tbf->tsc, tbf->tfi, usf, 0, 0, 0);
+	int plen = write_immediate_assignment(immediate_assignment, 0, ra, Fn, tbf->ta, tbf->arfcn, tbf->first_ts, tbf->tsc, tbf->tfi, tbf->dir.ul.usf[tbf->first_ts], 0, 0, 0);
 	pcu_l1if_tx_agch(immediate_assignment, plen);
 	bitvec_free(immediate_assignment);
 
@@ -1013,8 +1009,7 @@
 				"header, and we are done\n", chunk, space);
 			LOGP(DRLCMACDL, LOGL_INFO, "Complete DL frame for "
 				"TBF=%d that fits precisely in last block: "
-				"%s\n", tbf->tfi,
-				osmo_hexdump(tbf->llc_frame, tbf->llc_length));
+				"len=%d\n", tbf->tfi, tbf->llc_length);
 			/* block is filled, so there is no extension */
 			*e_pointer |= 0x01;
 			/* fill space */
@@ -1072,9 +1067,8 @@
 		memcpy(data, tbf->llc_frame + tbf->llc_index, chunk);
 		data += chunk;
 		space -= chunk;
-		LOGP(DRLCMACDL, LOGL_INFO, "Complete DL frame for TBF=%d: %s\n",
-			tbf->tfi,
-			osmo_hexdump(tbf->llc_frame, tbf->llc_length));
+		LOGP(DRLCMACDL, LOGL_INFO, "Complete DL frame for TBF=%d: "
+			"len=%d\n", tbf->tfi, tbf->llc_length);
 		/* reset LLC frame */
 		tbf->llc_index = tbf->llc_length = 0;
 		/* dequeue next LLC frame, if any */
@@ -1150,6 +1144,7 @@
 			/* schedule polling */
 			tbf->poll_state = GPRS_RLCMAC_POLL_SCHED;
 			tbf->poll_fn = (fn + 13) % 2715648;
+			tbf->poll_ts = tbf->first_ts;
 
 			/* set polling in header */
 			rh->rrbp = 0; /* N+13 */
@@ -1286,12 +1281,14 @@
 	struct msgb *msg;
 	struct gprs_rlcmac_tbf *new_tbf;
 
+#if POLLING_ASSIGNMENT == 1
 	if (tbf->poll_state != GPRS_RLCMAC_POLL_NONE) {
-		LOGP(DRLCMACDL, LOGL_DEBUG, "Polling is already "
+		LOGP(DRLCMAC, LOGL_DEBUG, "Polling is already "
 			"sheduled for TBF=%d, so we must wait for downlink "
 			"assignment...\n", tbf->tfi);
 			return NULL;
 	}
+#endif
 
 	/* on uplink TBF we get the downlink TBF to be assigned. */
 	if (tbf->direction == GPRS_RLCMAC_UL_TBF)
@@ -1319,8 +1316,7 @@
 	LOGP(DRLCMAC, LOGL_INFO, "TBF: START TFI: %u TLLI: 0x%08x Packet Downlink Assignment (PACCH)\n", new_tbf->tfi, new_tbf->tlli);
 	RlcMacDownlink_t * mac_control_block = (RlcMacDownlink_t *)malloc(sizeof(RlcMacDownlink_t));
 	write_packet_downlink_assignment(mac_control_block, tbf->tfi,
-		(tbf->direction == GPRS_RLCMAC_DL_TBF), new_tbf->tfi,
-		new_tbf->arfcn, new_tbf->ts, new_tbf->ta, new_tbf->tsc,
+		(tbf->direction == GPRS_RLCMAC_DL_TBF), new_tbf,
 		POLLING_ASSIGNMENT);
 	LOGP(DRLCMAC, LOGL_DEBUG, "+++++++++++++++++++++++++ TX : Packet Downlink Assignment +++++++++++++++++++++++++\n");
 	encode_gsm_rlcmac_downlink(ass_vec, mac_control_block);
@@ -1333,6 +1329,7 @@
 #if POLLING_ASSIGNMENT == 1
 	tbf->poll_state = GPRS_RLCMAC_POLL_SCHED;
 	tbf->poll_fn = (fn + 13) % 2715648;
+	tbf->poll_ts = tbf->first_ts;
 	tbf->dl_ass_state = GPRS_RLCMAC_DL_ASS_WAIT_ACK;
 #else
 	tbf->dl_ass_state = GPRS_RLCMAC_DL_ASS_NONE;
@@ -1349,7 +1346,7 @@
 	bitvec_unhex(immediate_assignment, "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
 	/* use request reference that has maximum distance to current time,
 	 * so the assignment will not conflict with possible RACH requests. */
-	int plen = write_immediate_assignment(immediate_assignment, 1, 125, (tbf->pdch->last_rts_fn + 21216) % 2715648, tbf->ta, tbf->arfcn, tbf->ts, tbf->tsc, tbf->tfi, 0, tbf->tlli, poll, tbf->poll_fn);
+	int plen = write_immediate_assignment(immediate_assignment, 1, 125, (tbf->pdch[tbf->first_ts]->last_rts_fn + 21216) % 2715648, tbf->ta, tbf->arfcn, tbf->first_ts, tbf->tsc, tbf->tfi, 0, tbf->tlli, poll, tbf->poll_fn);
 	pcu_l1if_tx_pch(immediate_assignment, plen, imsi);
 	bitvec_free(immediate_assignment);
 }
@@ -1381,8 +1378,8 @@
 		tbf_timer_start(tbf, 1234, 1,0);
 #else
 		LOGP(DRLCMAC, LOGL_DEBUG, "Send dowlink assignment on "
-			"PACCH, because %slink TBF=%d exists for TLLI=0x%08x\n",
-			(tbf->direction == GPRS_RLCMAC_DL_TBF) ? "down" : "up",
+			"PACCH, because %s TBF=%d exists for TLLI=0x%08x\n",
+			(old_tbf->direction == GPRS_RLCMAC_UL_TBF) ? "UL" : "DL",
 			old_tbf->tfi, old_tbf->tlli);
 		old_tbf->dl_ass_state = GPRS_RLCMAC_DL_ASS_SEND_ASS;
 		/* use TA from old TBF */
diff --git a/src/gprs_rlcmac_sched.cpp b/src/gprs_rlcmac_sched.cpp
index 4a4b85c..7d9a156 100644
--- a/src/gprs_rlcmac_sched.cpp
+++ b/src/gprs_rlcmac_sched.cpp
@@ -61,36 +61,43 @@
 		poll_fn ++;
 	poll_fn = poll_fn % 2715648;
 	for (tfi = 0; tfi < 32; tfi++) {
-		tbf = pdch->tbf[tfi];
-		/* no TBF for this tfi, go next */
-		if (!tbf)
-			continue;
-		/* no polling */
-		if (tbf->poll_state != GPRS_RLCMAC_POLL_SCHED)
-			continue;
-		/* polling for next uplink block */
-		if (tbf->poll_fn == poll_fn)
-			break;
+		tbf = pdch->ul_tbf[tfi];
+		if (tbf) {
+			/* no polling */
+			if (tbf->poll_state != GPRS_RLCMAC_POLL_SCHED)
+				continue;
+			/* polling for next uplink block */
+			if (tbf->poll_fn == poll_fn)
+				break;
+		}
+		tbf = pdch->dl_tbf[tfi];
+		if (tbf) {
+			/* no polling */
+			if (tbf->poll_state != GPRS_RLCMAC_POLL_SCHED)
+				continue;
+			/* polling for next uplink block */
+			if (tbf->poll_fn == poll_fn)
+				break;
+		}
 	}
 	/* found uplink where a block is polled */
 	if (tfi < 32) {
 		LOGP(DRLCMACSCHED, LOGL_DEBUG, "Received RTS for PDCH: TRX=%d "
 			"TS=%d FN=%d block_nr=%d scheduling free USF for "
-			"polling at FN=%d of TFI=%d\n", trx, ts, fn, block_nr,
-			poll_fn, tfi);
+			"polling at FN=%d of %s TFI=%d\n", trx, ts, fn,
+			block_nr, poll_fn,
+			(tbf->direction == GPRS_RLCMAC_UL_TBF) ? "UL" : "DL",
+			tfi);
 		/* use free USF */
 	/* else, we search for uplink ressource */
 	} else {
 		/* select uplink ressource */
 		for (i = 0, tfi = pdch->next_ul_tfi; i < 32;
 		     i++, tfi = (tfi + 1) & 31) {
-			tbf = pdch->tbf[tfi];
+			tbf = pdch->ul_tbf[tfi];
 			/* no TBF for this tfi, go next */
 			if (!tbf)
 				continue;
-			/* no UL TBF, go next */
-			if (tbf->direction != GPRS_RLCMAC_UL_TBF)
-				continue;
 			/* no UL ressources needed, go next */
 			/* we don't need to give ressources in FINISHED state,
 			 * because we have received all blocks and only poll
@@ -99,11 +106,11 @@
 				continue;
 
 			/* use this USF */
-			usf = tbf->dir.ul.usf;
+			usf = tbf->dir.ul.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 ressource of "
-				"TBF=%d\n", trx, ts, fn, block_nr, usf, tfi);
+				"UL TBF=%d\n", trx, ts, fn, block_nr, usf, tfi);
 			/* next TBF to handle ressource is the next one */
 			pdch->next_ul_tfi = (tfi + 1) & 31;
 			break;
@@ -111,8 +118,11 @@
 	}
 
 	/* Prio 1: select control message */
-	for (tfi = 0; tfi < 32; tfi++) {
-		tbf = pdch->tbf[tfi];
+	for (i = 0; i < 64; i++) {
+		if (i < 32)
+			tbf = pdch->ul_tbf[i];
+		else
+			tbf = pdch->dl_tbf[i & 31];
 		/* no TBF for this tfi, go next */
 		if (!tbf)
 			continue;
@@ -131,7 +141,9 @@
 			msg = gprs_rlcmac_send_uplink_ack(tbf, fn);
 		if (msg) {
 			LOGP(DRLCMACSCHED, LOGL_DEBUG, "Scheduling control "
-				"message at RTS for TBF=%d\n", tfi);
+				"message at RTS for %s TBF=%d\n",
+				(tbf->direction == GPRS_RLCMAC_UL_TBF)
+						? "UL" : "DL", tbf->tfi);
 			break;
 		}
 	}
@@ -141,7 +153,7 @@
 		/* select downlink ressource */
 		for (i = 0, tfi = pdch->next_dl_tfi; i < 32;
 		     i++, tfi = (tfi + 1) & 31) {
-			tbf = pdch->tbf[tfi];
+			tbf = pdch->dl_tbf[tfi];
 			/* no TBF for this tfi, go next */
 			if (!tbf)
 				continue;
@@ -154,7 +166,7 @@
 				continue;
 
 			LOGP(DRLCMACSCHED, LOGL_DEBUG, "Scheduling data "
-				"message at RTS for TBF=%d\n", tfi);
+				"message at RTS for DL TBF=%d\n", tfi);
 			/* next TBF to handle ressource is the next one */
 			pdch->next_dl_tfi = (tfi + 1) & 31;
 			/* generate DL data block */
diff --git a/src/pcu_l1_if.cpp b/src/pcu_l1_if.cpp
index d0f2e32..a58a122 100644
--- a/src/pcu_l1_if.cpp
+++ b/src/pcu_l1_if.cpp
@@ -182,8 +182,8 @@
 
 	switch (data_ind->sapi) {
 	case PCU_IF_SAPI_PDTCH:
-		rc = gprs_rlcmac_rcv_block(data_ind->data, data_ind->len,
-			data_ind->fn);
+		rc = gprs_rlcmac_rcv_block(data_ind->trx_nr, data_ind->ts_nr,
+			data_ind->data, data_ind->len, data_ind->fn);
 		break;
 	default:
 		LOGP(DL1IF, LOGL_ERROR, "Received PCU data indication with "
@@ -265,7 +265,10 @@
 			bts->trx[trx].arfcn = info_ind->trx[trx].arfcn;
 			for (ts = 0; ts < 8; ts++) {
 				for (tfi = 0; tfi < 32; tfi++) {
-					tbf = bts->trx[trx].pdch[ts].tbf[tfi];
+				tbf = bts->trx[trx].pdch[ts].ul_tbf[tfi];
+					if (tbf)
+						tbf_free(tbf);
+				tbf = bts->trx[trx].pdch[ts].dl_tbf[tfi];
 					if (tbf)
 						tbf_free(tbf);
 				}
@@ -369,9 +372,12 @@
 				if (bts->trx[trx].pdch[ts].enable)
 					pcu_tx_act_req(trx, ts, 0);
 				bts->trx[trx].pdch[ts].enable = 0;
-				/* kick all tbf  FIXME: multislot  */
+				/* kick all TBF on slot */
 				for (tfi = 0; tfi < 32; tfi++) {
-					tbf = bts->trx[trx].pdch[ts].tbf[tfi];
+				tbf = bts->trx[trx].pdch[ts].ul_tbf[tfi];
+					if (tbf)
+						tbf_free(tbf);
+				tbf = bts->trx[trx].pdch[ts].dl_tbf[tfi];
 					if (tbf)
 						tbf_free(tbf);
 				}
@@ -384,8 +390,6 @@
 
 static int pcu_rx_time_ind(struct gsm_pcu_if_time_ind *time_ind)
 {
-	struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
-	int trx, ts, tfi;
 	struct gprs_rlcmac_tbf *tbf;
 	uint32_t elapsed;
 	uint8_t fn13 = time_ind->fn % 13;
@@ -400,19 +404,18 @@
 	set_current_fn(time_ind->fn);
 
 	/* check for poll timeout */
-	for (trx = 0; trx < 8; trx++) {
-		for (ts = 0; ts < 8; ts++) {
-			for (tfi = 0; tfi < 32; tfi++) {
-				tbf = bts->trx[trx].pdch[ts].tbf[tfi];
-				if (!tbf)
-					continue;
-				if (tbf->poll_state != GPRS_RLCMAC_POLL_SCHED)
-					continue;
-				elapsed = (frame_number - tbf->poll_fn)
-							% 2715648;
-				if (elapsed >= 20 && elapsed < 200)
-					gprs_rlcmac_poll_timeout(tbf);
-			}
+	llist_for_each_entry(tbf, &gprs_rlcmac_ul_tbfs, list) {
+		if (tbf->poll_state == GPRS_RLCMAC_POLL_SCHED) {
+			elapsed = (frame_number - tbf->poll_fn) % 2715648;
+			if (elapsed >= 20 && elapsed < 200)
+				gprs_rlcmac_poll_timeout(tbf);
+		}
+	}
+	llist_for_each_entry(tbf, &gprs_rlcmac_dl_tbfs, list) {
+		if (tbf->poll_state == GPRS_RLCMAC_POLL_SCHED) {
+			elapsed = (frame_number - tbf->poll_fn) % 2715648;
+			if (elapsed >= 20 && elapsed < 200)
+				gprs_rlcmac_poll_timeout(tbf);
 		}
 	}
 
diff --git a/src/sysmo_sock.cpp b/src/sysmo_sock.cpp
index 390f3f6..6b390ed 100644
--- a/src/sysmo_sock.cpp
+++ b/src/sysmo_sock.cpp
@@ -97,7 +97,10 @@
 		for (ts = 0; ts < 8; ts++) {
 			bts->trx[trx].pdch[ts].enable = 0;
 			for (tfi = 0; tfi < 32; tfi++) {
-				tbf = bts->trx[trx].pdch[ts].tbf[tfi];
+				tbf = bts->trx[trx].pdch[ts].ul_tbf[tfi];
+				if (tbf)
+					tbf_free(tbf);
+				tbf = bts->trx[trx].pdch[ts].dl_tbf[tfi];
 				if (tbf)
 					tbf_free(tbf);
 			}
diff --git a/src/tbf.txt b/src/tbf.txt
index 3c06b39..57254ea 100644
--- a/src/tbf.txt
+++ b/src/tbf.txt
@@ -109,3 +109,33 @@
    - The received frame is bad (BFI).
    - The GSM indicates that the block should have been already received.
 
+Data structures of TBFs and PDCHs:
+
+  There is a global structure for BTS.
+
+  The BTS structure has 8 TRX structures.
+
+  Each TRX structure has 8 PDCH structures, one for each timeslot.
+
+  There are two linked lists of TBF instances:
+   - uplink TBFs
+   - downlink TBFs
+
+  Each TBF instance also has:
+   - a direction
+   - a TFI of range 0..31
+   - an array of 8 PDCH structures, one for each assigned timeslot
+   - in case of uplink TBF: an array of 8 USFs, one for each assigned timeslot
+
+  Each PDCH structure also has:
+   - an array of 32 uplink TBFs that are assigned to this PDCH
+   - an array of 32 downlink TBFs that are assigned to this PDCH
+
+  On creation of a new TBF, it links to all assigned PDCHs.
+  Each PDCH links to that TBF. The TBF is added to the list of TBFs.
+  In case of uplink TBF: The allocated USFs are stored for each timeslot.
+
+  On release of a TBF, the link to this PDCH is removed from all assigned
+  PDCHs. The TBF is removed from the list of TBFs. The TBF is destroyed.
+
+