Merge branch 'jolly_new'
Merge is based on jolly_new branch with two modifications.
1. Modified PCU L1 interface.
pcu_l1_if.cpp - common functions for tx and rx messages on L1 interface.
sysmo_sock.cpp - SYSMO-PCU socket functions.
openbts_sock.cpp - OpenBTS-PCU socket functions.
pcuif_proto.h - L1 interface's primitives.
2. Modified encoding of RLC/MAC Control messages, now we use structures and encode_gsm_rlcmac_downlink() function for encode control blocks (without  hand-coding).
diff --git a/src/.gitignore b/src/.gitignore
new file mode 100644
index 0000000..9cf9852
--- /dev/null
+++ b/src/.gitignore
@@ -0,0 +1,6 @@
+.gitignore
+.libs/
+RLCMACTest
+libgprs.la
+pcu
+
diff --git a/src/Makefile.am b/src/Makefile.am
index b7b9885..ca586fd 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -18,12 +18,8 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
-# FIXME: This has to go!!
-OPENBSC_DIR = $(top_srcdir)/../openbsc/openbsc
-OPENGGSN_DIR = $(top_srcdir)/../openggsn
-
-AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) -I$(OPENBSC_DIR)/include
-AM_CXXFLAGS = -Wall -ldl -pthread -lgtp
+AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
+AM_CXXFLAGS = -Wall -ldl -pthread
 
 noinst_LTLIBRARIES = libgprs.la
 
@@ -33,15 +29,18 @@
 	gsm_rlcmac.cpp \
 	gprs_bssgp_pcu.cpp \
 	gprs_rlcmac.cpp \
+	gprs_rlcmac_data.cpp \
+	gprs_rlcmac_sched.cpp \
 	gsm_timer.cpp \
-	bitvector.cpp
+	bitvector.cpp \
+	pcu_l1_if.cpp
 
 if ENABLE_SYSMOBTS
 libgprs_la_SOURCES += \
-	sysmo_l1_if.cpp
+	sysmo_sock.cpp
 else
 libgprs_la_SOURCES += \
-	pcu_l1_if.cpp
+	openbts_sock.cpp
 endif
 
 noinst_PROGRAMS = \
@@ -54,6 +53,7 @@
 	gsm_rlcmac.h \
 	gprs_bssgp_pcu.h \
 	gprs_rlcmac.h \
+	pcuif_proto.h \
 	pcu_l1_if.h \
 	gsm_timer.h \
 	bitvector.h
diff --git a/src/gprs_bssgp_pcu.cpp b/src/gprs_bssgp_pcu.cpp
index 2dfaba3..f9a421a 100644
--- a/src/gprs_bssgp_pcu.cpp
+++ b/src/gprs_bssgp_pcu.cpp
@@ -23,19 +23,24 @@
 
 struct sgsn_instance *sgsn;
 void *tall_bsc_ctx;
-struct bssgp_bvc_ctx *bctx = btsctx_alloc(BVCI, NSEI);
+struct bssgp_bvc_ctx *bctx = NULL;
+struct gprs_nsvc *nsvc = NULL;
+extern uint16_t spoof_mcc, spoof_mnc;
 
 int gprs_bssgp_pcu_rx_dl_ud(struct msgb *msg, struct tlv_parsed *tp)
 {
 	struct bssgp_ud_hdr *budh;
-	int i = 0;
-	uint8_t trx, ts;
 
-	budh = (struct bssgp_ud_hdr *)msgb_bssgph(msg);
-	
+	int tfi;
+	uint32_t tlli;
+	int i, j;
+	uint8_t trx, ts;
+	uint8_t *data;
+	uint16_t len;
 	struct gprs_rlcmac_tbf *tbf;
 
-	tbf = tbf_alloc(GPRS_RLCMAC_DL_TBF, ntohl(budh->tlli));
+	budh = (struct bssgp_ud_hdr *)msgb_bssgph(msg);
+	tlli = ntohl(budh->tlli);
 
 	if (!tbf)
 	{
@@ -44,31 +49,88 @@
 	/* LLC_PDU is mandatory IE */
 	if (!TLVP_PRESENT(tp, BSSGP_IE_LLC_PDU))
 	{
-		LOGP(DBSSGP, LOGL_ERROR, "BSSGP TLLI=0x%08x Rx UL-UD missing mandatory IE\n", tbf->tlli);
+		LOGP(DBSSGP, LOGL_NOTICE, "BSSGP TLLI=0x%08x Rx UL-UD missing mandatory IE\n", tlli);
 		return bssgp_tx_status(BSSGP_CAUSE_MISSING_MAND_IE, NULL, msg);
 	}
 
-	tbf_dl_establish(tbf);
-
-	uint8_t *llc_pdu = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_LLC_PDU);
-	uint16_t llc_pdu_len = TLVP_LEN(tp, BSSGP_IE_LLC_PDU);
-	
-	tbf_dl_data_transfer(tbf, llc_pdu, llc_pdu_len);
-}
-
-int gprs_bssgp_pcu_rx_paging_ps(struct msgb *msg, struct tlv_parsed *tp)
-{
-	uint8_t *ptmsi = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_TMSI);
-	uint16_t ptmsi_len = TLVP_LEN(tp, BSSGP_IE_TMSI);
-
-	LOGP(DBSSGP, LOGL_NOTICE, " P-TMSI = ");
-	for (int i = 0; i < ptmsi_len; i++)
+	data = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_LLC_PDU);
+	len = TLVP_LEN(tp, BSSGP_IE_LLC_PDU);
+	if (len > sizeof(tbf->llc_frame))
 	{
-		LOGPC(DBSSGP, LOGL_NOTICE, "%02x", ptmsi[i]);
+		LOGP(DBSSGP, LOGL_NOTICE, "BSSGP TLLI=0x%08x Rx UL-UD IE_LLC_PDU too large\n", tlli);
+		return bssgp_tx_status(BSSGP_CAUSE_COND_IE_ERR, NULL, msg);
 	}
-	LOGPC(DBSSGP, LOGL_NOTICE, "\n");
 
-	gprs_rlcmac_paging_request(ptmsi, ptmsi_len);
+	/* read IMSI. if no IMSI exists, use first paging block (any paging),
+	 * because during attachment the IMSI might not be known, so the MS
+	 * will listen to all paging blocks. */
+	char imsi[16] = "000";
+	if (TLVP_PRESENT(tp, BSSGP_IE_IMSI))
+	{
+		uint8_t imsi_len = TLVP_LEN(tp, BSSGP_IE_IMSI);
+		uint8_t *bcd_imsi = (uint8_t *) TLVP_VAL(tp, BSSGP_IE_IMSI);
+		if ((bcd_imsi[0] & 0x08))
+			imsi_len = imsi_len * 2 - 1;
+		else
+			imsi_len = (imsi_len - 1) * 2;
+		for (i = 0, j = 0; j < imsi_len && j < 16; j++)
+		{
+			if (!(j & 1)) {
+				imsi[j] = (bcd_imsi[i] >> 4) + '0';
+				i++;
+			} else
+				imsi[j] = (bcd_imsi[i] & 0xf) + '0';
+		}
+		imsi[j] = '\0';
+	}
+	LOGP(DBSSGP, LOGL_INFO, "LLC [SGSN -> PCU] = TLLI: 0x%08x IMSI: %s len: %d\n", tlli, imsi, len);
+
+	/* check for existing TBF */
+	if ((tbf = tbf_by_tlli(tlli, GPRS_RLCMAC_DL_TBF))) {
+		LOGP(DRLCMAC, LOGL_INFO, "TBF: APPEND TFI: %u TLLI: 0x%08x\n", tbf->tfi, tbf->tlli);
+		if (tbf->state == GPRS_RLCMAC_WAIT_RELEASE) {
+			LOGP(DRLCMAC, LOGL_DEBUG, "TBF in WAIT RELEASE state "
+				"(T3193), so reuse TBF\n");
+			memcpy(tbf->llc_frame, data, len);
+			tbf->llc_length = len;
+			memset(&tbf->dir.dl, 0, sizeof(tbf->dir.dl)); /* reset
+								rlc states */
+			gprs_rlcmac_trigger_downlink_assignment(tbf, 1, NULL);
+		} else {
+			/* the TBF exists, so we must write it in the queue */
+			struct msgb *llc_msg = msgb_alloc(len, "llc_pdu_queue");
+			if (!llc_msg)
+				return -ENOMEM;
+			memcpy(msgb_put(llc_msg, len), data, len);
+			msgb_enqueue(&tbf->llc_queue, llc_msg);
+		}
+	} else {
+		// Create new TBF
+		tfi = tfi_alloc(&trx, &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;
+		tbf->tlli = tlli;
+		tbf->tlli_valid = 1;
+
+		LOGP(DRLCMAC, LOGL_DEBUG, "TBF: [DOWNLINK] START TFI: %u TLLI: 0x%08x \n", tbf->tfi, tbf->tlli);
+
+		/* new TBF, so put first frame */
+		memcpy(tbf->llc_frame, data, len);
+		tbf->llc_length = len;
+
+		/* 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. */
+		gprs_rlcmac_trigger_downlink_assignment(tbf, 0, imsi);
+	}
+
+	return 0;
 }
 
 /* Receive a BSSGP PDU from a BSS on a PTP BVCI */
@@ -90,26 +152,26 @@
 
 	switch (pdu_type) {
 	case BSSGP_PDUT_DL_UNITDATA:
-		LOGP(DBSSGP, LOGL_NOTICE, "RX: [SGSN->PCU] BSSGP_PDUT_DL_UNITDATA\n");
+		LOGP(DBSSGP, LOGL_DEBUG, "RX: [SGSN->PCU] BSSGP_PDUT_DL_UNITDATA\n");
 		gprs_bssgp_pcu_rx_dl_ud(msg, tp);
 		break;
 	case BSSGP_PDUT_PAGING_PS:
-		LOGP(DBSSGP, LOGL_NOTICE, "rx BSSGP_PDUT_PAGING_PS\n");
+		LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_PAGING_PS\n");
 		break;
 	case BSSGP_PDUT_PAGING_CS:
-		LOGP(DBSSGP, LOGL_NOTICE, "rx BSSGP_PDUT_PAGING_CS\n");
+		LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_PAGING_CS\n");
 		break;
 	case BSSGP_PDUT_RA_CAPA_UPDATE_ACK:
-		LOGP(DBSSGP, LOGL_NOTICE, "rx BSSGP_PDUT_RA_CAPA_UPDATE_ACK\n");
+		LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_RA_CAPA_UPDATE_ACK\n");
 		break;
 	case BSSGP_PDUT_FLOW_CONTROL_BVC_ACK:
-		LOGP(DBSSGP, LOGL_NOTICE, "rx BSSGP_PDUT_FLOW_CONTROL_BVC_ACK\n");
+		LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_FLOW_CONTROL_BVC_ACK\n");
 		break;
 	case BSSGP_PDUT_FLOW_CONTROL_MS_ACK:
-		LOGP(DBSSGP, LOGL_NOTICE, "rx BSSGP_PDUT_FLOW_CONTROL_MS_ACK\n");
+		LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_FLOW_CONTROL_MS_ACK\n");
 		break;
 	default:
-		DEBUGP(DBSSGP, "BSSGP BVCI=%u PDU type 0x%02x unknown\n", bctx->bvci, pdu_type);
+		LOGP(DBSSGP, LOGL_NOTICE, "BSSGP BVCI=%u PDU type 0x%02x unknown\n", bctx->bvci, pdu_type);
 		rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
 		break;
 	}
@@ -128,41 +190,40 @@
 		/* FIXME: send NM_STATUS.ind to NM */
 		break;
 		case BSSGP_PDUT_SUSPEND_ACK:
-			LOGP(DBSSGP, LOGL_NOTICE, "rx BSSGP_PDUT_SUSPEND_ACK\n");
+			LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_SUSPEND_ACK\n");
 			break;
 		case BSSGP_PDUT_SUSPEND_NACK:
-			LOGP(DBSSGP, LOGL_NOTICE, "rx BSSGP_PDUT_SUSPEND_NACK\n");
+			LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_SUSPEND_NACK\n");
 			break;
 		case BSSGP_PDUT_BVC_RESET_ACK:
-			LOGP(DBSSGP, LOGL_NOTICE, "rx BSSGP_PDUT_BVC_RESET_ACK\n");
+			LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_BVC_RESET_ACK\n");
 			break;
 		case BSSGP_PDUT_PAGING_PS:
-			LOGP(DBSSGP, LOGL_NOTICE, "RX: [SGSN->PCU] BSSGP_PDUT_PAGING_PS\n");
-			gprs_bssgp_pcu_rx_paging_ps(msg, tp);
+			LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_PAGING_PS\n");
 			break;
 		case BSSGP_PDUT_PAGING_CS:
-			LOGP(DBSSGP, LOGL_NOTICE, "rx BSSGP_PDUT_PAGING_CS\n");
+			LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_PAGING_CS\n");
 			break;
 		case BSSGP_PDUT_RESUME_ACK:
-			LOGP(DBSSGP, LOGL_NOTICE, "rx BSSGP_PDUT_RESUME_ACK\n");
+			LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_RESUME_ACK\n");
 			break;
 		case BSSGP_PDUT_RESUME_NACK:
-			LOGP(DBSSGP, LOGL_NOTICE, "rx BSSGP_PDUT_RESUME_NACK\n");
+			LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_RESUME_NACK\n");
 			break;
 		case BSSGP_PDUT_FLUSH_LL:
-			LOGP(DBSSGP, LOGL_NOTICE, "rx BSSGP_PDUT_FLUSH_LL\n");
+			LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_FLUSH_LL\n");
 			break;
 		case BSSGP_PDUT_BVC_BLOCK_ACK:
-			LOGP(DBSSGP, LOGL_NOTICE, "rx BSSGP_PDUT_SUSPEND_ACK\n");
+			LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_SUSPEND_ACK\n");
 			break;
 		case BSSGP_PDUT_BVC_UNBLOCK_ACK:
-			LOGP(DBSSGP, LOGL_NOTICE, "rx BSSGP_PDUT_BVC_UNBLOCK_ACK\n");
+			LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_BVC_UNBLOCK_ACK\n");
 			break;
 		case BSSGP_PDUT_SGSN_INVOKE_TRACE:
-			LOGP(DBSSGP, LOGL_NOTICE, "rx BSSGP_PDUT_SGSN_INVOKE_TRACE\n");
+			LOGP(DBSSGP, LOGL_DEBUG, "rx BSSGP_PDUT_SGSN_INVOKE_TRACE\n");
 			break;
 		default:
-			DEBUGP(DBSSGP, "BSSGP BVCI=%u Rx PDU type 0x%02x unknown\n", bctx->bvci, bgph->pdu_type);
+			LOGP(DBSSGP, LOGL_NOTICE, "BSSGP BVCI=%u Rx PDU type 0x%02x unknown\n", bctx->bvci, bgph->pdu_type);
 			rc = bssgp_tx_status(BSSGP_CAUSE_PROTO_ERR_UNSPEC, NULL, msg);
 			break;
 	}
@@ -178,6 +239,13 @@
 	uint16_t ns_bvci = msgb_bvci(msg);
 	int data_len;
 	int rc = 0;
+	struct bssgp_bvc_ctx *bctx;
+
+	if (pdu_type == BSSGP_PDUT_STATUS) {
+		LOGP(DBSSGP, LOGL_NOTICE, "NSEI=%u/BVCI=%u received STATUS\n",
+			msgb_nsei(msg), ns_bvci);
+		return 0;
+	}
 
 	/* Identifiers from DOWN: NSEI, BVCI (both in msg->cb) */
 
@@ -196,12 +264,6 @@
 	/* look-up or create the BTS context for this BVC */
 	bctx = btsctx_by_bvci_nsei(ns_bvci, msgb_nsei(msg));
 
-	/* Only a RESET PDU can create a new BVC context */
-	if (!bctx)
-	{
-		bctx = btsctx_alloc(ns_bvci, msgb_nsei(msg));
-	}
-
 	if (!bctx && pdu_type != BSSGP_PDUT_BVC_RESET_ACK)
 	{
 		LOGP(DBSSGP, LOGL_NOTICE, "NSEI=%u/BVCI=%u Rejecting PDU "
@@ -219,17 +281,17 @@
 
 	if (ns_bvci == BVCI_SIGNALLING)
 	{
-		LOGP(DBSSGP, LOGL_NOTICE, "rx BVCI_SIGNALLING gprs_bssgp_rx_sign\n");
+		LOGP(DBSSGP, LOGL_DEBUG, "rx BVCI_SIGNALLING gprs_bssgp_rx_sign\n");
 		rc = gprs_bssgp_pcu_rx_sign(msg, &tp, bctx);
 	}
 	else if (ns_bvci == BVCI_PTM)
 	{
-		LOGP(DBSSGP, LOGL_NOTICE, "rx BVCI_PTM bssgp_tx_status\n");
+		LOGP(DBSSGP, LOGL_DEBUG, "rx BVCI_PTM bssgp_tx_status\n");
 		rc = bssgp_tx_status(BSSGP_CAUSE_PDU_INCOMP_FEAT, NULL, msg);
 	}
 	else
 	{
-		LOGP(DBSSGP, LOGL_NOTICE, "rx BVCI_PTP gprs_bssgp_rx_ptp\n");
+		LOGP(DBSSGP, LOGL_DEBUG, "rx BVCI_PTP gprs_bssgp_rx_ptp\n");
 		rc = gprs_bssgp_pcu_rx_ptp(msg, &tp, bctx);
 	}
 	return rc;
@@ -239,3 +301,128 @@
 {
 	return 0;
 }
+
+static int sgsn_ns_cb(enum gprs_ns_evt event, struct gprs_nsvc *nsvc, struct msgb *msg, uint16_t bvci)
+{
+	int rc = 0;
+	switch (event) {
+	case GPRS_NS_EVT_UNIT_DATA:
+		/* hand the message into the BSSGP implementation */
+		rc = gprs_bssgp_pcu_rcvmsg(msg);
+		break;
+	default:
+		LOGP(DPCU, LOGL_NOTICE, "RLCMAC: Unknown event %u from NS\n", event);
+		if (msg)
+			talloc_free(msg);
+		rc = -EIO;
+		break;
+	}
+	return rc;
+}
+
+static int nsvc_unblocked = 0;
+
+static int nsvc_signal_cb(unsigned int subsys, unsigned int signal,
+	void *handler_data, void *signal_data)
+{
+	struct ns_signal_data *nssd;
+
+	if (subsys != SS_L_NS)
+		return -EINVAL;
+
+	nssd = (struct ns_signal_data *)signal_data;
+	if (nssd->nsvc != nsvc) {
+		LOGP(DPCU, LOGL_ERROR, "Signal received of unknown NSVC\n");
+		return -EINVAL;
+	}
+
+	switch (signal) {
+	case S_NS_UNBLOCK:
+		if (!nsvc_unblocked) {
+			nsvc_unblocked = 1;
+			LOGP(DPCU, LOGL_NOTICE, "NS-VC is unblocked.\n");
+			bssgp_tx_bvc_reset(bctx, bctx->bvci,
+				BSSGP_CAUSE_PROTO_ERR_UNSPEC);
+		}
+		break;
+	case S_NS_BLOCK:
+		if (nsvc_unblocked) {
+			nsvc_unblocked = 0;
+			LOGP(DPCU, LOGL_NOTICE, "NS-VC is blocked.\n");
+		}
+		break;
+	}
+
+	return 0;
+}
+
+/* create BSSGP/NS layer instances */
+int gprs_bssgp_create(uint32_t sgsn_ip, uint16_t sgsn_port, uint16_t nsei,
+	uint16_t nsvci, uint16_t bvci, uint16_t mcc, uint16_t mnc, uint16_t lac,
+	uint16_t rac, uint16_t cell_id)
+{
+	struct sockaddr_in dest;
+
+	if (bctx)
+		return 0; /* if already created, must return 0: no error */
+
+	bssgp_nsi = gprs_ns_instantiate(&sgsn_ns_cb, NULL);
+	if (!bssgp_nsi) {
+		LOGP(DBSSGP, LOGL_ERROR, "Failed to create NS instance\n");
+		return -EINVAL;
+	}
+	gprs_ns_nsip_listen(bssgp_nsi);
+
+	dest.sin_family = AF_INET;
+	dest.sin_port = htons(sgsn_port);
+	dest.sin_addr.s_addr = htonl(sgsn_ip);
+
+	nsvc = gprs_ns_nsip_connect(bssgp_nsi, &dest, nsei, nsvci);
+	if (!nsvc) {
+		LOGP(DBSSGP, LOGL_ERROR, "Failed to create NSVCt\n");
+		gprs_ns_destroy(bssgp_nsi);
+		bssgp_nsi = NULL;
+		return -EINVAL;
+	}
+
+	bctx = btsctx_alloc(bvci, nsei);
+	if (!bctx) {
+		LOGP(DBSSGP, LOGL_ERROR, "Failed to create BSSGP context\n");
+		nsvc = NULL;
+		gprs_ns_destroy(bssgp_nsi);
+		bssgp_nsi = NULL;
+		return -EINVAL;
+	}
+	bctx->ra_id.mcc = spoof_mcc ? : mcc;
+	bctx->ra_id.mnc = spoof_mnc ? : mnc;
+	bctx->ra_id.lac = lac;
+	bctx->ra_id.rac = rac;
+	bctx->cell_id = cell_id;
+
+	osmo_signal_register_handler(SS_L_NS, nsvc_signal_cb, NULL);
+
+//	bssgp_tx_bvc_reset(bctx, bctx->bvci, BSSGP_CAUSE_PROTO_ERR_UNSPEC);
+
+	return 0;
+}
+
+void gprs_bssgp_destroy(void)
+{
+	if (!bssgp_nsi)
+		return;
+
+	osmo_signal_unregister_handler(SS_L_NS, nsvc_signal_cb, NULL);
+
+	nsvc = NULL;
+
+	/* FIXME: move this to libgb: btsctx_free() */
+	llist_del(&bctx->list);
+	talloc_free(bctx);
+	bctx = NULL;
+
+	/* FIXME: blocking... */
+
+	gprs_ns_destroy(bssgp_nsi);
+	bssgp_nsi = NULL;
+}
+
diff --git a/src/gprs_bssgp_pcu.h b/src/gprs_bssgp_pcu.h
index bf1e312..7d5f376 100644
--- a/src/gprs_bssgp_pcu.h
+++ b/src/gprs_bssgp_pcu.h
@@ -39,23 +39,10 @@
 }
 #include <gprs_debug.h>
 
-#define BVCI 7
-#define NSEI 3
-
 #define QOS_PROFILE 0
 #define BSSGP_HDR_LEN 53
 #define NS_HDR_LEN 4
-#define MAX_LEN_PDU 60
 #define IE_LLC_PDU 14
-#define BLOCK_DATA_LEN 20
-#define BLOCK_LEN 23
-
-#define CELL_ID 0
-#define MNC 1
-#define MCC 001
-#define PCU_LAC 1
-#define PCU_RAC 0
-
 
 extern struct bssgp_bvc_ctx *bctx;
 
@@ -67,4 +54,10 @@
 
 int gprs_bssgp_pcu_rcvmsg(struct msgb *msg);
 
+int gprs_bssgp_create(uint32_t sgsn_ip, uint16_t sgsn_port, uint16_t nsei,
+        uint16_t nsvci, uint16_t bvci, uint16_t mcc, uint16_t mnc, uint16_t lac,
+	        uint16_t rac, uint16_t cell_id);
+
+void gprs_bssgp_destroy(void);
+
 #endif // GPRS_BSSGP_PCU_H
diff --git a/src/gprs_debug.cpp b/src/gprs_debug.cpp
index 4b79cd6..bc19b77 100644
--- a/src/gprs_debug.cpp
+++ b/src/gprs_debug.cpp
@@ -33,12 +33,15 @@
 /* default categories */
 
 static const struct log_info_cat default_categories[] = {
-	{"DCSN1", "\033[1;31m", "Concrete Syntax Notation One (CSN1)", LOGL_NOTICE, 1},
-	{"DL1IF", "\033[1;32m", "GPRS PCU L1 interface (L1IF)", LOGL_NOTICE, 1},
-	{"DRLCMAC", "\033[1;33m", "GPRS RLC/MAC layer (RLCMAC)", LOGL_NOTICE, 1},
-	{"DRLCMACDATA", "\033[1;36m", "GPRS RLC/MAC layer Data (RLCMAC)", LOGL_NOTICE, 1},
-	{"DBSSGP", "\033[1;34m", "GPRS BSS Gateway Protocol (BSSGP)", LOGL_NOTICE , 1},
-	{"DPCU", "\033[1;35m", "GPRS Packet Control Unit (PCU)", LOGL_NOTICE, 1}
+ 	{"DCSN1", "\033[1;31m", "Concrete Syntax Notation One (CSN1)", LOGL_INFO, 0},
+ 	{"DL1IF", "\033[1;32m", "GPRS PCU L1 interface (L1IF)", LOGL_INFO, 1},
+ 	{"DRLCMAC", "\033[0;33m", "GPRS RLC/MAC layer (RLCMAC)", LOGL_INFO, 1},
+ 	{"DRLCMACDATA", "\033[0;33m", "GPRS RLC/MAC layer Data (RLCMAC)", LOGL_INFO, 1},
+ 	{"DRLCMACDL", "\033[1;33m", "GPRS RLC/MAC layer Data (RLCMAC)", LOGL_INFO, 1},
+ 	{"DRLCMACUL", "\033[1;36m", "GPRS RLC/MAC layer Data (RLCMAC)", LOGL_INFO, 1},
+ 	{"DRLCMACSCHED", "\033[0;36m", "GPRS RLC/MAC layer Data (RLCMAC)", LOGL_INFO, 1},
+ 	{"DBSSGP", "\033[1;34m", "GPRS BSS Gateway Protocol (BSSGP)", LOGL_INFO , 1},
+	{"DPCU", "\033[1;35m", "GPRS Packet Control Unit (PCU)", LOGL_NOTICE, 1},
 };
 
 enum {
diff --git a/src/gprs_debug.h b/src/gprs_debug.h
index ee95e19..b5b4276 100644
--- a/src/gprs_debug.h
+++ b/src/gprs_debug.h
@@ -31,6 +31,9 @@
 	DL1IF,
 	DRLCMAC,
 	DRLCMACDATA,
+	DRLCMACDL,
+	DRLCMACUL,
+	DRLCMACSCHED,
 	DBSSGP,
 	DPCU,
 	aDebug_LastEntry
diff --git a/src/gprs_rlcmac.cpp b/src/gprs_rlcmac.cpp
index 711fb61..9d2601c 100644
--- a/src/gprs_rlcmac.cpp
+++ b/src/gprs_rlcmac.cpp
@@ -1,6 +1,7 @@
 /* gprs_rlcmac.cpp
  *
  * Copyright (C) 2012 Ivan Klyuchnikov
+ * Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -20,506 +21,223 @@
 #include <gprs_bssgp_pcu.h>
 #include <pcu_l1_if.h>
 #include <gprs_rlcmac.h>
-#include <gsmL1prim.h>
 
 LLIST_HEAD(gprs_rlcmac_tbfs);
 void *rlcmac_tall_ctx;
-LLIST_HEAD(block_queue);
 
-int tfi_alloc()
+/* FIXME: spread ressources on multiple TRX */
+int tfi_alloc(uint8_t *_trx, uint8_t *_ts)
 {
-	struct gprs_rlcmac_tbf *tbf;
-	uint32_t tfi_map = 0;
-	uint32_t tfi_ind = 0;
-	uint32_t mask = 1;
-	uint8_t i;
+	struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
+	struct gprs_rlcmac_pdch *pdch;
+	uint8_t trx, ts, tfi;
 
-	llist_for_each_entry(tbf, &gprs_rlcmac_tbfs, list) {
-		tfi_ind = 1 << tbf->tfi;
-		tfi_map = tfi_map|tfi_ind;
+	for (trx = 0; trx < 8; trx++) {
+		for (ts = 0; ts < 8; ts++) {
+			pdch = &bts->trx[trx].pdch[ts];
+			if (!pdch->enable)
+				continue;
+			break;
+		}
+		if (ts < 8)
+			break;
+	}
+	if (trx == 8) {
+		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);
+	for (tfi = 0; tfi < 32; tfi++) {
+		if (!pdch->tbf[tfi])
+			break;
 	}
 	
-	for (i = 0; i < 32; i++) {
-		if(((tfi_map >> i) & mask) == 0) {
-			return i;
+	if (tfi < 32) {
+		LOGP(DRLCMAC, LOGL_DEBUG, " Found TFI=%d.\n", tfi);
+		*_trx = trx;
+		*_ts = ts;
+		return tfi;
+	}
+	LOGP(DRLCMAC, LOGL_NOTICE, "No TFI available.\n");
+
+	return -1;
+}
+
+int find_free_usf(uint8_t trx, 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];
+		if (!tbf)
+			continue;
+		if (tbf->direction != GPRS_RLCMAC_UL_TBF)
+			continue;
+		usf_map |= (1 << tbf->dir.ul.usf);
+	}
+
+	/* 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);
+			return usf;
 		}
 	}
+	LOGP(DRLCMAC, LOGL_NOTICE, "No USF available.\n");
+
 	return -1;
 }
 
 /* lookup TBF Entity (by TFI) */
-static struct gprs_rlcmac_tbf *tbf_by_tfi(uint8_t tfi, gprs_rlcmac_tbf_direction dir)
+#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;
 
 	llist_for_each_entry(tbf, &gprs_rlcmac_tbfs, list) {
-		if ((tbf->tfi == tfi)&&(tbf->direction == dir))
+		if (tbf->state != GPRS_RLCMAC_RELEASING
+		 && tbf->tfi == tfi
+		 && tbf->direction == direction)
 			return tbf;
 	}
 	return NULL;
 }
 
-static struct gprs_rlcmac_tbf *tbf_by_tlli(uint32_t tlli, gprs_rlcmac_tbf_direction dir)
+/* search for active downlink or uplink tbf */
+struct gprs_rlcmac_tbf *tbf_by_tlli(uint32_t tlli, int direction)
 {
 	struct gprs_rlcmac_tbf *tbf;
 	llist_for_each_entry(tbf, &gprs_rlcmac_tbfs, list) {
-		if ((tbf->tlli == tlli)&&(tbf->direction == dir))
+		if (tbf->state != GPRS_RLCMAC_RELEASING
+		 && tbf->tlli == tlli
+		 && tbf->direction == direction)
 			return tbf;
 	}
 	return NULL;
 }
 
-static void tbf_free(struct gprs_rlcmac_tbf *tbf)
+#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;
+	llist_for_each_entry(tbf, &gprs_rlcmac_tbfs, list) {
+		if (tbf->state != GPRS_RLCMAC_RELEASING
+		 && tbf->poll_state == GPRS_RLCMAC_POLL_SCHED
+		 && tbf->poll_fn == fn)
+			return tbf;
+	}
+	return NULL;
+}
+
+struct gprs_rlcmac_tbf *tbf_alloc(uint8_t tfi, uint8_t trx, uint8_t ts)
+{
+	struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
+	struct gprs_rlcmac_pdch *pdch;
+	struct gprs_rlcmac_tbf *tbf;
+
+	LOGP(DRLCMAC, LOGL_DEBUG, "********** TBF starts here **********\n");
+	LOGP(DRLCMAC, LOGL_INFO, "Allocating TBF with TFI=%d.\n", tfi);
+
+	if (trx >= 8 || 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->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;
+	tbf->ws = 64;
+	tbf->sns = 128;
+	INIT_LLIST_HEAD(&tbf->llc_queue);
+	llist_add(&tbf->list, &gprs_rlcmac_tbfs);
+	pdch->tbf[tfi] = tbf;
+
+	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;
+
+	LOGP(DRLCMAC, LOGL_INFO, "Free TBF=%d with TLLI=0x%08x.\n", tbf->tfi,
+		tbf->tlli);
+	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;
 	llist_del(&tbf->list);
+	LOGP(DRLCMAC, LOGL_DEBUG, "********** TBF ends here **********\n");
 	talloc_free(tbf);
 }
 
-/* Lookup LLC PDU in TBF list of LLC PDUs by number. */
-static struct tbf_llc_pdu *tbf_llc_pdu_by_num(struct llist_head llc_pdus, uint8_t num)
-{
-	struct tbf_llc_pdu *llc_pdu;
+const char *tbf_state_name[] = {
+	"NULL",
+	"ASSIGN",
+	"FLOW",
+	"FINISHED",
+	"WAIT RELEASE",
+	"RELEASING",
+};
 
-	llist_for_each_entry(llc_pdu, &llc_pdus, list) {
-		if (llc_pdu->num == num)
-			return llc_pdu;
-	}
-	return NULL;
+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]);
+	tbf->state = state;
 }
 
-/* Add new LLC PDU to the TBF list of LLC PDUs. */
-int tbf_add_llc_pdu(struct gprs_rlcmac_tbf *tbf, uint8_t *data, uint16_t llc_pdu_len)
+void tbf_timer_start(struct gprs_rlcmac_tbf *tbf, unsigned int T,
+			unsigned int seconds, unsigned int microseconds)
 {
-	struct tbf_llc_pdu *llc_pdu;
-
-	llc_pdu = talloc_zero(rlcmac_tall_ctx, struct tbf_llc_pdu);
-	if (!llc_pdu)
-		return 0;
-
-	llc_pdu->num = tbf->llc_pdu_list_len;
-	llc_pdu->len = llc_pdu_len;
-	
-	LOGP(DBSSGP, LOGL_NOTICE, "LLC PDU = ");
-	for (unsigned i = 0; i < llc_pdu_len; i++)
-	{
-		llc_pdu->data[i] = data[i];
-		LOGPC(DBSSGP, LOGL_NOTICE, "%02x", llc_pdu->data[i]);
-	}
-	LOGPC(DBSSGP, LOGL_NOTICE, "\n");
-
-	llist_add(&llc_pdu->list, &tbf->llc_pdus);
-	tbf->llc_pdu_list_len++;
-	return 1;
-}
-
-struct gprs_rlcmac_tbf *tbf_alloc(gprs_rlcmac_tbf_direction dir, uint32_t tlli)
-{
-	struct gprs_rlcmac_tbf *exist_tbf;
-	struct gprs_rlcmac_tbf *tbf;
-	uint8_t tfi;
-	uint8_t trx, ts;
-
-	// Downlink TDF allocation
-	if (dir == GPRS_RLCMAC_DL_TBF)
-	{
-		// Try to find already exist DL TBF
-		exist_tbf = tbf_by_tlli(tlli, GPRS_RLCMAC_DL_TBF);
-		if (exist_tbf)
-		{
-			// if DL TBF is in establish or data transfer state,
-			// send additional LLC PDU during current DL TBF.
-			if (exist_tbf->stage != TBF_RELEASE)
-			{
-				if (exist_tbf->state != FINISH_DATA_TRANSFER)
-				{
-					return exist_tbf;
-				}
-			}
-		}
-		
-		//Try to find already exist UL TBF
-		exist_tbf = tbf_by_tlli(tlli, GPRS_RLCMAC_UL_TBF);
-		if (exist_tbf)
-		{
-			// if UL TBF is in data transfer state,
-			// establish new DL TBF during current UL TBF.
-			if (exist_tbf->stage == TBF_DATA_TRANSFER && !(exist_tbf->next_tbf))
-			{
-				tbf = talloc_zero(rlcmac_tall_ctx, struct gprs_rlcmac_tbf);
-				if (tbf)
-				{
-					// Create new TBF
-					tfi = tfi_alloc();
-					if (tfi < 0) {
-						return NULL;
-					}
-					
-					/* FIXME: select right TRX/TS */
-					if (select_pdch(&trx, &ts)) {
-						LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH ressource\n");
-						/* FIXME: send reject */
-						return NULL;
-					}
-				
-					tbf->tfi = tfi;
-					tbf->trx = trx;
-					tbf->ts = ts;
-					tbf->arfcn = pcu_l1if_bts.trx[trx].arfcn;
-					tbf->tsc = pcu_l1if_bts.trx[trx].ts[ts].tsc;
-					tbf->llc_pdus = LLIST_HEAD_INIT(tbf->llc_pdus);
-					tbf->llc_pdu_list_len = 0;
-					tbf->direction = GPRS_RLCMAC_DL_TBF;
-					tbf->stage = TBF_ESTABLISH;
-					tbf->state = WAIT_ESTABLISH;
-					tbf->tlli = tlli;
-					llist_add(&tbf->list, &gprs_rlcmac_tbfs);
-					exist_tbf->next_tbf = tbf;
-					return tbf;
-				}
-				else
-				{
-					return NULL;
-				}
-			}
-		}
-		
-		// No UL and DL TBFs for current TLLI are found.
-		if (!exist_tbf)
-		{
-			tbf = talloc_zero(rlcmac_tall_ctx, struct gprs_rlcmac_tbf);
-			if (tbf)
-			{
-				// Create new TBF
-				tfi = tfi_alloc();
-				if (tfi < 0) {
-					return NULL;
-				}
-				
-				/* FIXME: select right TRX/TS */
-				if (select_pdch(&trx, &ts)) {
-					LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH ressource\n");
-					/* FIXME: send reject */
-					return NULL;
-				}
-				
-				tbf->tfi = tfi;
-				tbf->trx = trx;
-				tbf->ts = ts;
-				tbf->arfcn = pcu_l1if_bts.trx[trx].arfcn;
-				tbf->tsc = pcu_l1if_bts.trx[trx].ts[ts].tsc;
-				tbf->llc_pdus = LLIST_HEAD_INIT(tbf->llc_pdus);
-				tbf->llc_pdu_list_len = 0;
-				tbf->direction = GPRS_RLCMAC_DL_TBF;
-				tbf->stage = TBF_ESTABLISH;
-				tbf->state = CCCH_ESTABLISH;
-				tbf->tlli = tlli;
-				llist_add(&tbf->list, &gprs_rlcmac_tbfs);
-				return tbf;
-			}
-			else
-			{
-				return NULL;
-			}
-		}
-	}
+	if (!osmo_timer_pending(&tbf->timer))
+		LOGP(DRLCMAC, LOGL_DEBUG, "Starting TBF=%d timer %u.\n",
+			tbf->tfi, T);
 	else
-	{
-		// Uplink TBF allocation
-		tbf = talloc_zero(rlcmac_tall_ctx, struct gprs_rlcmac_tbf);
-		if (tbf)
-		{
-			// Create new TBF
-			tfi = tfi_alloc();
-			if (tfi < 0) {
-				return NULL;
-			}
-			if (select_pdch(&trx, &ts)) {
-				LOGP(DRLCMAC, LOGL_NOTICE, "No PDCH ressource\n");
-				/* FIXME: send reject */
-				return NULL;
-			}
-			tbf->tfi = tfi;
-			tbf->trx = trx;
-			tbf->ts = ts;
-			tbf->arfcn = pcu_l1if_bts.trx[trx].arfcn;
-			tbf->tsc = pcu_l1if_bts.trx[trx].ts[ts].tsc;
-			tbf->llc_pdus = LLIST_HEAD_INIT(tbf->llc_pdus);
-			tbf->llc_pdu_list_len = 0;
-			tbf->direction = GPRS_RLCMAC_UL_TBF;
-			tbf->stage = TBF_ESTABLISH;
-			tbf->state = WAIT_ESTABLISH;
-			tbf->next_tbf = NULL;
-			llist_add(&tbf->list, &gprs_rlcmac_tbfs);
-			return tbf;
-		}
-		else
-		{
-			return NULL;
-		}
-	}
-}
+		LOGP(DRLCMAC, LOGL_DEBUG, "Restarting TBF=%d timer %u while "
+			"old timer %u pending \n", tbf->tfi, T, tbf->T);
 
-/* Management of uplink TBF establishment. */
-int tbf_ul_establish(struct gprs_rlcmac_tbf *tbf, uint8_t ra, uint32_t Fn, uint16_t qta)
-{
-	if (tbf->direction != GPRS_RLCMAC_UL_TBF)
-	{
-		return -1;
-	}
-	
-	if (tbf->stage == TBF_ESTABLISH)
-	{
-		switch (tbf->state) {
-		case WAIT_ESTABLISH:
-			{
-				if (qta < 0)
-					qta = 0;
-				if (qta > 252)
-					qta = 252;
-				tbf->ta = qta >> 2;
-				LOGP(DRLCMAC, LOGL_NOTICE, "TBF: [UPLINK] START TFI: %u\n", tbf->tfi);
-				LOGP(DRLCMAC, LOGL_NOTICE, "RX: [PCU <- BTS] TFI: %u RACH qbit-ta=%d ra=%d, Fn=%d (%d,%d,%d)\n",
-                                                  tbf->tfi, qta, ra, Fn, (Fn / (26 * 51)) % 32, Fn % 51, Fn % 26);
-				LOGP(DRLCMAC, LOGL_NOTICE, "TX: [PCU -> BTS] TFI: %u Packet Immidiate Assignment\n", tbf->tfi);
-				bitvec *immediate_assignment = bitvec_alloc(23);
-				bitvec_unhex(immediate_assignment, "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
-				int len = write_immediate_assignment(immediate_assignment, 0, ra, Fn, tbf->ta, tbf->arfcn, tbf->ts, tbf->tsc, tbf->tfi);
-				pcu_l1if_tx_agch(immediate_assignment, len);
-				bitvec_free(immediate_assignment);
-				tbf->state = FINISH_ESTABLISH;
-			}
-			break;
-		default:
-			LOGP(DRLCMAC, LOGL_NOTICE, "TBF: [UPLINK] TFI: %u Unexpected TBF state = %u for stage = %u \n", 
-																			tbf->tfi, tbf->state, tbf->stage);
-			break;
-		}
-	}
-	else
-	{
-		return -1;
-	}
-	return 1;
-}
-
-/* Management of downlink TBF establishment. */
-int tbf_dl_establish(struct gprs_rlcmac_tbf *tbf)
-{
-	if (tbf->direction != GPRS_RLCMAC_DL_TBF)
-	{
-		return -1;
-	}
-	
-	if (tbf->stage == TBF_ESTABLISH)
-	{
-		switch (tbf->state) {
-		case WAIT_ESTABLISH:
-			// Wait while UL TBF establishes DL TBF.
-			LOGP(DRLCMAC, LOGL_NOTICE, "TBF: [DOWNLINK] TFI: Wait DL TBF establishment by UL TBF\n", tbf->tfi);
-			break;
-		case CCCH_ESTABLISH:
-			// Downlink TBF Establishment on CCCH ( Immediate Assignment )
-			tbf_gsm_timer_start(tbf, 1, 0);
-			break;
-		case PACCH_ESTABLISH:
-			// Downlink TBF Establishment on PACCH ( Packet Immediate Assignment )
-			gprs_rlcmac_packet_downlink_assignment(tbf);
-			tbf->state = FINISH_ESTABLISH;
-			break;
-		default:
-			LOGP(DRLCMAC, LOGL_NOTICE, "TBF: [DOWNLINK] TFI: %u Unexpected TBF state = %u for stage = %u \n", 
-																			tbf->tfi, tbf->state, tbf->stage);
-			break;
-		}
-	}
-	return 1;
-}
-
-/* Management of uplink TBF data transfer. */
-int tbf_ul_data_transfer(struct gprs_rlcmac_tbf *tbf, RlcMacUplinkDataBlock_t * ul_data_block)
-{
-	if ((tbf->stage == TBF_RELEASE)||(tbf->direction != GPRS_RLCMAC_UL_TBF))
-	{
-		return -1;
-	}
-
-	if (tbf->stage == TBF_ESTABLISH)
-	{
-		tbf->stage = TBF_DATA_TRANSFER;
-		tbf->state = WAIT_DATA_TRANSFER;
-	}
-
-	if (ul_data_block->TI == 1)
-	{
-		tbf->tlli = ul_data_block->TLLI;
-		// TODO: Kill all other UL TBFs with this TLLI.
-	}
-
-	switch (tbf->state) {
-	case WAIT_DATA_TRANSFER:
-		if (ul_data_block->BSN == 0)
-		{
-			tbf->data_index = 0;
-			gprs_rlcmac_data_block_parse(tbf, ul_data_block);
-			gprs_rlcmac_tx_ul_ack(tbf->tfi, tbf->tlli, ul_data_block->CV ? 0: 1, ul_data_block->BSN);
-			if (ul_data_block->CV == 0)
-			{
-				// Recieved last Data Block in this sequence.
-				tbf->state = FINISH_DATA_TRANSFER;
-				gprs_rlcmac_tx_ul_ud(tbf);
-			}
-			else
-			{
-				tbf->bsn = ul_data_block->BSN;
-				tbf->state = DATA_TRANSFER;
-			}
-		}
-		break;
-	case DATA_TRANSFER:
-		if (tbf->bsn == (ul_data_block->BSN - 1))
-		{
-			gprs_rlcmac_data_block_parse(tbf, ul_data_block);
-			
-			if (ul_data_block->CV == 0)
-			{
-				gprs_rlcmac_tx_ul_ack(tbf->tfi, tbf->tlli, 1, ul_data_block->BSN);
-				// Recieved last Data Block in this sequence.
-				tbf->state = FINISH_DATA_TRANSFER;
-				gprs_rlcmac_tx_ul_ud(tbf);
-			}
-			else
-			{
-				tbf->bsn = ul_data_block->BSN;
-			}
-		}
-		break;
-	case FINISH_DATA_TRANSFER:
-		// Now we just ignore all Data Blocks and wait release of TBF.
-		break;
-	default:
-		LOGP(DRLCMAC, LOGL_NOTICE, "TBF: [UPLINK] TFI: %u Unexpected TBF state = %u for stage = %u \n", 
-																		tbf->tfi, tbf->state, tbf->stage);
-		break;
-	}
-
-	if ((tbf->state == FINISH_DATA_TRANSFER) && (tbf->next_tbf))
-	{
-		// Establish DL TBF, if it is required.
-		if ((tbf->next_tbf)->state == WAIT_ESTABLISH)
-		{
-			(tbf->next_tbf)->state = PACCH_ESTABLISH;
-			tbf_dl_establish(tbf->next_tbf);
-		}
-	}
-
-	return 1;
-}
-
-/* Management of downlink TBF data transfer. */
-int tbf_dl_data_transfer(struct gprs_rlcmac_tbf *tbf, uint8_t *llc_pdu, uint16_t llc_pdu_len)
-{
-	if ((tbf->stage == TBF_RELEASE) || (tbf->direction != GPRS_RLCMAC_DL_TBF))
-	{
-		return -1;
-	}
-	
-	if (llc_pdu_len > 0)
-	{
-		tbf_add_llc_pdu(tbf, llc_pdu, llc_pdu_len);
-	}
-
-	if (tbf->stage == TBF_ESTABLISH)
-	{
-		if (tbf->state == FINISH_ESTABLISH)
-		{
-			tbf->stage = TBF_DATA_TRANSFER;
-			tbf->state = DATA_TRANSFER;
-		}
-	}
-
-	if (tbf->stage == TBF_DATA_TRANSFER)
-	{
-		switch (tbf->state) {
-		case DATA_TRANSFER:
-			gprs_rlcmac_tx_llc_pdus(tbf);
-			tbf->state = FINISH_DATA_TRANSFER;
-			break;
-		default:
-			LOGP(DRLCMAC, LOGL_NOTICE, "TBF: [DOWNLINK] TFI: %u Unexpected TBF state = %u for stage = %u \n", 
-																			tbf->tfi, tbf->state, tbf->stage);
-			break;
-		}
-	}
-
-	return 1;
-}
-
-/* Management of uplink TBF release. */
-int tbf_ul_release(struct gprs_rlcmac_tbf *tbf)
-{
-	if (tbf->direction != GPRS_RLCMAC_UL_TBF)
-	{
-		return -1;
-	}
-
-	if (tbf->next_tbf)
-	{
-		// UL TBF data transfer is finished, start DL TBF data transfer.
-		tbf_dl_data_transfer(tbf->next_tbf);
-	}
-	tbf->stage = TBF_RELEASE;
-	tbf->state = RELEASE;
-	LOGP(DRLCMAC, LOGL_NOTICE, "TBF: [UPLINK] END TFI: %u TLLI: 0x%08x \n", tbf->tfi, tbf->tlli);
-	tbf_free(tbf);
-	return 1;
-}
-
-/* Management of downlink TBF release. */
-int tbf_dl_release(struct gprs_rlcmac_tbf *tbf)
-{
-	if (tbf->direction != GPRS_RLCMAC_DL_TBF)
-	{
-		return -1;
-	}
-
-	tbf->stage = TBF_RELEASE;
-	tbf->state = RELEASE;
-	LOGP(DRLCMAC, LOGL_NOTICE, "TBF: [DOWNLINK] END TFI: %u TLLI: 0x%08x \n", tbf->tfi, tbf->tlli);
-	tbf_free(tbf);
-	return 1;
-}
-
-static void tbf_timer_cb(void *_tbf)
-{
-	struct gprs_rlcmac_tbf *tbf = (struct gprs_rlcmac_tbf *)_tbf;
-
-	tbf->num_T_exp++;
-
-	switch (tbf->T) {
-	case 1111:
-		// TODO: We should add timers for TBF.
-		break;
-	default:
-		LOGP(DRLCMAC, LOGL_NOTICE, "Timer expired in unknown mode: %u \n", tbf->T);
-	}
-}
-
-static void tbf_timer_start(struct gprs_rlcmac_tbf *tbf, unsigned int T,
-				unsigned int seconds)
-{
-	if (osmo_timer_pending(&tbf->timer))
-		LOGP(DRLCMAC, LOGL_NOTICE, "Starting TBF timer %u while old timer %u pending \n", T, tbf->T);
 	tbf->T = T;
 	tbf->num_T_exp = 0;
 
-	/* FIXME: we should do this only once ? */
+	/* Tunning timers can be safely re-scheduled. */
 	tbf->timer.data = tbf;
 	tbf->timer.cb = &tbf_timer_cb;
 
-	osmo_timer_schedule(&tbf->timer, seconds, 0);
+	osmo_timer_schedule(&tbf->timer, seconds, microseconds);
 }
 
+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",
+			tbf->tfi, tbf->T);
+		osmo_timer_del(&tbf->timer);
+	}
+}
 
+#if 0
 static void tbf_gsm_timer_cb(void *_tbf)
 {
 	struct gprs_rlcmac_tbf *tbf = (struct gprs_rlcmac_tbf *)_tbf;
@@ -527,23 +245,12 @@
 	tbf->num_fT_exp++;
 
 	switch (tbf->fT) {
-	case 1:
-		if (tbf_by_tlli(tbf->tlli, GPRS_RLCMAC_UL_TBF))
-		{
-			// Wait release of UL TBF
-			tbf_gsm_timer_start(tbf, 1, 10);
-		}
-		else
-		{
-			gprs_rlcmac_downlink_assignment(tbf);
-			// FIXME: Remove magic delay!
-			int delay = 50;
-			tbf_gsm_timer_start(tbf, 2, delay);
-		}
-		break;
-	case 2:
-		tbf->state = FINISH_ESTABLISH;
-		tbf_dl_data_transfer(tbf);
+	case 0:
+hier alles berdenken
+		// This is timer for delay RLC/MAC data sending after Downlink Immediate Assignment on CCCH.
+		gprs_rlcmac_segment_llc_pdu(tbf);
+		LOGP(DRLCMAC, LOGL_NOTICE, "TBF: [DOWNLINK] END TFI: %u TLLI: 0x%08x \n", tbf->tfi, tbf->tlli);
+		tbf_free(tbf);
 		break;
 	default:
 		LOGP(DRLCMAC, LOGL_NOTICE, "Timer expired in unknown mode: %u \n", tbf->fT);
@@ -565,78 +272,56 @@
 	osmo_gsm_timer_schedule(&tbf->gsm_timer, frames);
 }
 
-static void gprs_rlcmac_enqueue_block(bitvec *block, int len)
+eine stop-funktion, auch im tbf_free aufrufen
+
+#endif
+
+#if 0
+void gprs_rlcmac_enqueue_block(bitvec *block, int len)
 {
 	struct msgb *msg = msgb_alloc(len, "rlcmac_dl");
 	bitvec_pack(block, msgb_put(msg, len));
 	msgb_enqueue(&block_queue, msg);
 }
+#endif
 
-void write_packet_downlink_assignment(RlcMacDownlink_t * block, uint8_t tfi, uint16_t arfcn, uint8_t tn, uint8_t ta, uint8_t tsc)
+/* received RLC/MAC block from L1 */
+int gprs_rlcmac_rcv_block(uint8_t *data, uint8_t len, uint32_t fn)
 {
-	// Packet downlink assignment TS 44.060 11.2.7
+	unsigned payload = data[0] >> 6;
+	bitvec *block;
+	int rc = 0;
 
-	int i;
-
-	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
-	block->SP           = 0x1; // RRBP field is valid
-	block->USF          = 0x1; // Uplink state flag
-
-	block->u.Packet_Downlink_Assignment.MESSAGE_TYPE = 0x2;  // Packet Downlink Assignment
-	block->u.Packet_Downlink_Assignment.PAGE_MODE    = 0x0;  // Normal Paging
-
-	block->u.Packet_Downlink_Assignment.Exist_PERSISTENCE_LEVEL      = 0x0;   // PERSISTENCE_LEVEL: off
-
-	block->u.Packet_Downlink_Assignment.ID.UnionType                 = 0x0;   // TFI = on
-	block->u.Packet_Downlink_Assignment.ID.u.Global_TFI.UnionType    = 0x0;   // UPLINK TFI = on
-	block->u.Packet_Downlink_Assignment.ID.u.Global_TFI.u.UPLINK_TFI = tfi-1; // TFI
-
-	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         = 0x0; // 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.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.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.UnionType = 0x0;   // ARFCN = on
-	block->u.Packet_Downlink_Assignment.Frequency_Parameters.u.ARFCN   = arfcn; // ARFCN
-
-	block->u.Packet_Downlink_Assignment.Exist_DOWNLINK_TFI_ASSIGNMENT  = 0x1;   // DOWNLINK TFI ASSIGNMENT = on
-	block->u.Packet_Downlink_Assignment.DOWNLINK_TFI_ASSIGNMENT        = 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++)
-	{
-		if (tn == i)
-		{
-			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
-		}
-		else
-		{
-			block->u.Packet_Downlink_Assignment.Power_Control_Parameters.Slot[i].Exist    = 0x0; // Slot[i] = off
-		}
+	switch (payload) {
+	case GPRS_RLCMAC_DATA_BLOCK:
+		rc = gprs_rlcmac_rcv_data_block_acknowledged(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);
+		bitvec_free(block);
+		break;
+	case GPRS_RLCMAC_CONTROL_BLOCK_OPT:
+		LOGP(DRLCMAC, LOGL_NOTICE, "GPRS_RLCMAC_CONTROL_BLOCK_OPT block payload is not supported.\n");
+	default:
+		LOGP(DRLCMAC, LOGL_NOTICE, "Unknown RLCMAC block payload.\n");
+		rc = -EINVAL;
 	}
 
-	block->u.Packet_Downlink_Assignment.Exist_TBF_Starting_Time   = 0x0; // TBF Starting TIME = off
-	block->u.Packet_Downlink_Assignment.Exist_Measurement_Mapping = 0x0; // Measurement_Mapping = off
-	block->u.Packet_Downlink_Assignment.Exist_AdditionsR99        = 0x0; // AdditionsR99 = off
+	return rc;
 }
 
 // GSM 04.08 9.1.18 Immediate assignment
-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, uint8_t tfi, uint32_t tlli)
+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,
+	uint8_t tfi, uint8_t usf, uint32_t tlli,
+	uint8_t polling, uint32_t poll_fn)
 {
 	unsigned wp = 0;
+	uint8_t plen;
 
 	bitvec_write_field(dest, wp,0x0,4);  // Skip Indicator
 	bitvec_write_field(dest, wp,0x6,4);  // Protocol Discriminator
@@ -671,6 +356,13 @@
 	// A zero-length LV.  Just write L=0.
 	bitvec_write_field(dest, wp,0,8);
 
+	if ((wp % 8)) {
+		LOGP(DRLCMACUL, LOGL_ERROR, "Length of IMM.ASS without rest "
+			"octets is not multiple of 8 bits, PLEASE FIX!\n");
+		exit (0);
+	}
+	plen = wp / 8;
+
 	if (downlink)
 	{
 		// GSM 04.08 10.5.2.16 IA Rest Octets
@@ -680,19 +372,27 @@
 		bitvec_write_field(dest, wp,0x1,1);   // switch TFI   : on
 		bitvec_write_field(dest, wp,tfi,5);   // TFI
 		bitvec_write_field(dest, wp,0x0,1);   // RLC acknowledged mode
-		bitvec_write_field(dest, wp,0x0,1);   // ALPHA = present
+		bitvec_write_field(dest, wp,0x0,1);   // ALPHA = not present
 		bitvec_write_field(dest, wp,0x0,5);   // GAMMA power control parameter
-		bitvec_write_field(dest, wp,0x0,1);   // Polling Bit
-		bitvec_write_field(dest, wp,0x1,1);   // TA_VALID ???
+		bitvec_write_field(dest, wp,polling,1);   // Polling Bit
+		bitvec_write_field(dest, wp,!polling,1);   // TA_VALID ???
 		bitvec_write_field(dest, wp,0x1,1);   // switch TIMING_ADVANCE_INDEX = on
 		bitvec_write_field(dest, wp,0x0,4);   // TIMING_ADVANCE_INDEX
-		bitvec_write_field(dest, wp,0x0,1);   // TBF Starting TIME present
+		if (polling) {
+			bitvec_write_field(dest, wp,0x1,1);   // TBF Starting TIME present
+			bitvec_write_field(dest, wp,(poll_fn / (26 * 51)) % 32,5); // T1'
+			bitvec_write_field(dest, wp,poll_fn % 51,6);               // T3
+			bitvec_write_field(dest, wp,poll_fn % 26,5);               // T2
+		} else {
+			bitvec_write_field(dest, wp,0x0,1);   // TBF Starting TIME present
+		}
 		bitvec_write_field(dest, wp,0x0,1);   // P0 not present
-		bitvec_write_field(dest, wp,0x1,1);   // P0 not present
-		bitvec_write_field(dest, wp,0xb,4);
+//		bitvec_write_field(dest, wp,0x1,1);   // P0 not present
+//		bitvec_write_field(dest, wp,0xb,4);
 	}
 	else
 	{
+		struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
 		// GMS 04.08 10.5.2.37b 10.5.2.16
 		bitvec_write_field(dest, wp, 3, 2);    // "HH"
 		bitvec_write_field(dest, wp, 0, 2);    // "0" Packet Uplink Assignment
@@ -700,11 +400,11 @@
 		bitvec_write_field(dest, wp, tfi, 5);  // TFI_ASSIGNMENT Temporary Flow Identity
 		bitvec_write_field(dest, wp, 0, 1);    // POLLING
 		bitvec_write_field(dest, wp, 0, 1);    // ALLOCATION_TYPE: dynamic
-		bitvec_write_field(dest, wp, 1, 3);    // USF
+		bitvec_write_field(dest, wp, usf, 3);    // USF
 		bitvec_write_field(dest, wp, 0, 1);    // USF_GRANULARITY
 		bitvec_write_field(dest, wp, 0 , 1);   // "0" power control: Not Present
-		bitvec_write_field(dest, wp, 0, 2);    // CHANNEL_CODING_COMMAND 
-		bitvec_write_field(dest, wp, 0, 1);    // TLLI_BLOCK_CHANNEL_CODING
+		bitvec_write_field(dest, wp, bts->initial_cs-1, 2);    // CHANNEL_CODING_COMMAND 
+		bitvec_write_field(dest, wp, 1, 1);    // TLLI_BLOCK_CHANNEL_CODING
 		bitvec_write_field(dest, wp, 1 , 1);   // "1" Alpha : Present
 		bitvec_write_field(dest, wp, 0, 4);    // Alpha
 		bitvec_write_field(dest, wp, 0, 5);    // Gamma
@@ -712,487 +412,219 @@
 		bitvec_write_field(dest, wp, 0, 1);    // TBF_STARTING_TIME_FLAG
 	}
 
-	if (wp%8)
-		return wp/8+1;
-	else
-		return wp/8;
+	return plen;
 }
 
-int write_paging_request(bitvec * dest, uint8_t *ptmsi, uint16_t ptmsi_len)
+/* 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)
 {
+	// 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;
 
-	bitvec_write_field(dest, wp,0x0,4);  // Skip Indicator
-	bitvec_write_field(dest, wp,0x6,4);  // Protocol Discriminator
-	bitvec_write_field(dest, wp,0x21,8); // Paging Request Message Type
+	bitvec_write_field(dest, wp,0x1,2);  // Payload Type
+	bitvec_write_field(dest, wp,0x0,2);  // Uplink block with TDMA framenumber (N+13)
+	bitvec_write_field(dest, wp,poll,1);  // Suppl/Polling Bit
+	bitvec_write_field(dest, wp,0x0,3);  // Uplink state flag
+	bitvec_write_field(dest, wp,0xa,6);  // MESSAGE TYPE
 
-	bitvec_write_field(dest, wp,0x0,4);  // Page Mode
-	bitvec_write_field(dest, wp,0x0,4);  // Channel Needed
+	bitvec_write_field(dest, wp,0x0,2);  // Page Mode
 
-	// Mobile Identity
-	bitvec_write_field(dest, wp,ptmsi_len+1,8);  // Mobile Identity length
-	bitvec_write_field(dest, wp,0xf,4);          // unused
-	bitvec_write_field(dest, wp,0x4,4);          // PTMSI type
-	for (int i = 0; i < ptmsi_len; i++)
-	{
-		bitvec_write_field(dest, wp,ptmsi[i],8); // PTMSI
+	bitvec_write_field(dest, wp,0x0,1); // switch PERSIST_LEVEL: off
+	if (use_tlli) {
+		bitvec_write_field(dest, wp,0x2,2); // switch TLLI   : on
+		bitvec_write_field(dest, wp,tlli,32); // TLLI
+	} else {
+		bitvec_write_field(dest, wp,0x0,1); // switch TFI : on
+		bitvec_write_field(dest, wp,old_downlink,1); // 0=UPLINK TFI, 1=DL TFI
+		bitvec_write_field(dest, wp,old_tfi,5); // TFI
 	}
-	bitvec_write_field(dest, wp,0x0,1); // "L" NLN(PCH) = off
-	bitvec_write_field(dest, wp,0x0,1); // "L" Priority1 = off
-	bitvec_write_field(dest, wp,0x1,1); // "L" Priority2 = off
-	bitvec_write_field(dest, wp,0x0,1); // "L" Group Call information = off
-	bitvec_write_field(dest, wp,0x0,1); // "H" Packet Page Indication 1 = packet paging procedure
-	bitvec_write_field(dest, wp,0x1,1); // "H" Packet Page Indication 2 = packet paging procedure
-	bitvec_write_field(dest, wp,0x3,2); // spare padding
-	return wp/8;
+
+	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,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,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,0x0,2); // ARFCN = present
+	bitvec_write_field(dest, wp,arfcn,10); // ARFCN
+#else
+	bitvec_write_field(dest, wp,0x0,1); // Frequency Parameters = off
+#endif
+
+	bitvec_write_field(dest, wp,0x1,2); // Dynamic Allocation
+	
+	bitvec_write_field(dest, wp,0x0,1); // Extended Dynamic Allocation = off
+	bitvec_write_field(dest, wp,0x0,1); // P0 = off
+	
+	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,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) {
+			bitvec_write_field(dest, wp,0x1,1); // USF_TN(i): on
+			bitvec_write_field(dest, wp,usf,3); // USF_TN(i)
+		} else
+			bitvec_write_field(dest, wp,0x0,1); // USF_TN(i): off
+	}
+//	bitvec_write_field(dest, wp,0x0,1); // Measurement Mapping struct not present
 }
 
-void write_packet_uplink_ack(RlcMacDownlink_t * block, uint8_t tfi, uint32_t tlli, uint8_t fi, uint8_t bsn)
+
+/* 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)
 {
-	// Packet Uplink Ack/Nack  TS 44.060 11.2.28
+	// Packet downlink assignment TS 44.060 11.2.7
 
 	int i;
 
-	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
-	block->SP           = fi;  // RRBP field is valid, if it is final ack
-	block->USF          = 0x1; // Uplink state flag
+	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
+	block->SP           = poll; // RRBP field is valid
+	block->USF          = 0x0;  // Uplink state flag
 
-	block->u.Packet_Uplink_Ack_Nack.MESSAGE_TYPE = 0x9; // Packet Downlink Assignment
-	block->u.Packet_Uplink_Ack_Nack.PAGE_MODE    = 0x0; // Normal Paging
-	block->u.Packet_Uplink_Ack_Nack.UPLINK_TFI   = tfi; // Uplink TFI
+	block->u.Packet_Downlink_Assignment.MESSAGE_TYPE = 0x2;  // Packet Downlink Assignment
+	block->u.Packet_Downlink_Assignment.PAGE_MODE    = 0x0;  // Normal Paging
 
-	block->u.Packet_Uplink_Ack_Nack.UnionType    = 0x0; // PU_AckNack_GPRS = on
-	block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.CHANNEL_CODING_COMMAND                        = 0x0;      // CS1
-	block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Ack_Nack_Description.FINAL_ACK_INDICATION     = fi;       // FINAL ACK INDICATION
-	block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Ack_Nack_Description.STARTING_SEQUENCE_NUMBER = bsn + 1;  // STARTING SEQUENCE NUMBER
+	block->u.Packet_Downlink_Assignment.Exist_PERSISTENCE_LEVEL      = 0x0;          // PERSISTENCE_LEVEL: off
+
+	block->u.Packet_Downlink_Assignment.ID.UnionType                 = 0x0;          // TFI = on
+	block->u.Packet_Downlink_Assignment.ID.u.Global_TFI.UnionType    = old_downlink; // 0=UPLINK TFI, 1=DL TFI
+	block->u.Packet_Downlink_Assignment.ID.u.Global_TFI.u.UPLINK_TFI = old_tfi;      // TFI
+
+	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.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.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.UnionType = 0x0;   // ARFCN = on
+	block->u.Packet_Downlink_Assignment.Frequency_Parameters.u.ARFCN   = 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.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++)
 	{
-		block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Ack_Nack_Description.RECEIVED_BLOCK_BITMAP[i] = 0xff; // RECEIVED BLOCK BITMAP
+		if (tn == i)
+		{
+			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
+		}
+		else
+		{
+			block->u.Packet_Downlink_Assignment.Power_Control_Parameters.Slot[i].Exist    = 0x0; // Slot[i] = off
+		}
 	}
 
-	block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.UnionType            = 0x0; // Fixed Allocation Dummy = on
+	block->u.Packet_Downlink_Assignment.Exist_TBF_Starting_Time   = 0x0; // TBF Starting TIME = off
+	block->u.Packet_Downlink_Assignment.Exist_Measurement_Mapping = 0x0; // Measurement_Mapping = off
+	block->u.Packet_Downlink_Assignment.Exist_AdditionsR99        = 0x0; // AdditionsR99 = off
+}
+
+/* generate uplink ack */
+void write_packet_uplink_ack(RlcMacDownlink_t * block, struct gprs_rlcmac_tbf *tbf,
+	uint8_t final)
+{
+	// Packet Uplink Ack/Nack  TS 44.060 11.2.28
+
+	char show_v_n[65];
+
+	uint8_t rbb = 0;
+	uint16_t i, bbn;
+	uint16_t mod_sns_half = (tbf->sns >> 1) - 1;
+	char bit;
+
+	LOGP(DRLCMACUL, LOGL_DEBUG, "Sending Ack/Nack for TBF=%d "
+		"(final=%d)\n", tbf->tfi, final);
+
+	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
+	block->SP           = final; // RRBP field is valid, if it is final ack
+	block->USF          = 0x0;   // Uplink state flag
+
+	block->u.Packet_Uplink_Ack_Nack.MESSAGE_TYPE = 0x9;      // Packet Downlink Assignment
+	block->u.Packet_Uplink_Ack_Nack.PAGE_MODE    = 0x0;      // Normal Paging
+	block->u.Packet_Uplink_Ack_Nack.UPLINK_TFI   = tbf->tfi; // Uplink TFI
+
+	block->u.Packet_Uplink_Ack_Nack.UnionType    = 0x0;      // PU_AckNack_GPRS = on
+	block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.CHANNEL_CODING_COMMAND                        = 0x0;             // CS1
+	block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Ack_Nack_Description.FINAL_ACK_INDICATION     = final;           // FINAL ACK INDICATION
+	block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Ack_Nack_Description.STARTING_SEQUENCE_NUMBER = tbf->dir.ul.v_r; // STARTING_SEQUENCE_NUMBER
+	// RECEIVE_BLOCK_BITMAP
+	for (i = 0, bbn = (tbf->dir.ul.v_r - 64) & mod_sns_half; i < 64;
+	     i++, bbn = (bbn + 1) & mod_sns_half) {
+	     	bit = tbf->dir.ul.v_n[bbn];
+		if (bit == 0)
+			bit = ' ';
+		show_v_n[i] = bit;
+		if (bit == 'R')
+			rbb = (rbb << 1)|1;
+		else
+			rbb = (rbb << 1);
+		if((i%8) == 7)
+		{
+			block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Ack_Nack_Description.RECEIVED_BLOCK_BITMAP[i/8] = rbb;
+			rbb = 0;
+		}
+	}
+	show_v_n[64] = '\0';
+	LOGP(DRLCMACUL, LOGL_DEBUG, "- V(N): \"%s\" R=Received "
+		"N=Not-Received\n", show_v_n);
+
+	block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.UnionType              = 0x0; // Fixed Allocation Dummy = on
 	block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.u.FixedAllocationDummy = 0x0; // Fixed Allocation Dummy
-	block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Exist_AdditionsR99   = 0x0; // AdditionsR99 = off
+	block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Exist_AdditionsR99     = 0x0; // AdditionsR99 = off
 
 	block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Common_Uplink_Ack_Nack_Data.Exist_CONTENTION_RESOLUTION_TLLI = 0x1;
-	block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Common_Uplink_Ack_Nack_Data.CONTENTION_RESOLUTION_TLLI       = tlli;
+	block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Common_Uplink_Ack_Nack_Data.CONTENTION_RESOLUTION_TLLI       = tbf->tlli;
 	block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Common_Uplink_Ack_Nack_Data.Exist_Packet_Timing_Advance      = 0x0;
 	block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Common_Uplink_Ack_Nack_Data.Exist_Extension_Bits             = 0x0;
 	block->u.Packet_Uplink_Ack_Nack.u.PU_AckNack_GPRS_Struct.Common_Uplink_Ack_Nack_Data.Exist_Power_Control_Parameters   = 0x0;
 }
 
-void gprs_rlcmac_tx_ul_ack(uint8_t tfi, uint32_t tlli, uint8_t fi, uint8_t bsn)
-{
-	bitvec *packet_uplink_ack_vec = bitvec_alloc(23);
-	bitvec_unhex(packet_uplink_ack_vec, "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
-	RlcMacDownlink_t * packet_uplink_ack = (RlcMacDownlink_t *)malloc(sizeof(RlcMacDownlink_t));
-	write_packet_uplink_ack(packet_uplink_ack, tfi, tlli, fi, bsn);
-	LOGP(DRLCMAC, LOGL_NOTICE, "TX: [PCU -> BTS] TFI: %u TLLI: 0x%08x Packet Uplink Ack\n", tfi, tlli);
-	LOGP(DRLCMAC, LOGL_NOTICE, "+++++++++++++++++++++++++ TX : Packet Uplink Ack +++++++++++++++++++++++++\n");
-	encode_gsm_rlcmac_downlink(packet_uplink_ack_vec, packet_uplink_ack);
-	LOGPC(DRLCMAC, LOGL_NOTICE, "\n");
-	LOGP(DRLCMAC, LOGL_NOTICE, "------------------------- TX : Packet Uplink Ack -------------------------\n");
-	free(packet_uplink_ack);
-	gprs_rlcmac_enqueue_block(packet_uplink_ack_vec, 23);
-	bitvec_free(packet_uplink_ack_vec);
-}
-
-void gprs_rlcmac_data_block_parse(gprs_rlcmac_tbf* tbf, RlcMacUplinkDataBlock_t * ul_data_block)
-{
-	// 1. Count the number of octets in header and number of LLC PDU in uplink data block.
-	unsigned data_block_hdr_len = 3; // uplink data block header length: 3 mandatory octets
-	unsigned llc_pdu_num = 0; // number of LLC PDU in data block
-
-	
-	if (ul_data_block->E_1 == 0) // Extension octet follows immediately
-	{
-		unsigned i = -1;
-		do
-		{
-			i++;
-			data_block_hdr_len += 1;
-			llc_pdu_num++;
-			
-			// Singular case, TS 44.060 10.4.14
-			if (ul_data_block->LENGTH_INDICATOR[i] == 0)
-			{
-				break;
-			}
-			
-			// New LLC PDU starts after the current LLC PDU and continues until
-			// the end of the RLC information field, no more extension octets.
-			if ((ul_data_block->M[i] == 1)&&(ul_data_block->E[i] == 1))
-			{
-				llc_pdu_num++;
-			}
-		} while(ul_data_block->E[i] == 0); // there is another extension octet, which delimits the new LLC PDU
-	}
-	else
-	{
-		llc_pdu_num++;
-	}
-	if(ul_data_block->TI == 1) // TLLI field is present
-	{
-		tbf->tlli = ul_data_block->TLLI;
-		data_block_hdr_len += 4; // TLLI length : 4 octets
-		if (ul_data_block->PI == 1) // PFI is present if TI field indicates presence of TLLI
-		{
-			data_block_hdr_len += 1; // PFI length : 1 octet
-		}
-	}
-	
-	// 2. Extract all LLC PDU from uplink data block and send them to SGSN.
-	unsigned llc_pdu_len = 0;
-	unsigned data_octet_num = 0;
-
-	for (unsigned num = 0; num < llc_pdu_num; num ++)
-	{
-		if (ul_data_block->E_1 == 0) // Extension octet follows immediately
-		{
-			// Singular case, TS 44.060 10.4.14
-			if (ul_data_block->LENGTH_INDICATOR[num] == 0)
-			{
-				llc_pdu_len = UL_RLC_DATA_BLOCK_LEN - data_block_hdr_len;
-			}
-			else
-			{
-				llc_pdu_len = ul_data_block->LENGTH_INDICATOR[num];
-			}
-		}
-		else
-		{
-			llc_pdu_len = UL_RLC_DATA_BLOCK_LEN - data_block_hdr_len;
-		}
-		
-		for (unsigned i = tbf->data_index; i < tbf->data_index + llc_pdu_len; i++)
-		{
-			tbf->rlc_data[i] = ul_data_block->RLC_DATA[data_octet_num];
-			data_octet_num++;
-		}
-		tbf->data_index += llc_pdu_len;
-		
-		if (ul_data_block->E_1 == 0) // Extension octet follows immediately
-		{
-			// New LLC PDU starts after the current LLC PDU 
-			if (ul_data_block->M[num] == 1)
-			{
-				gprs_rlcmac_tx_ul_ud(tbf);
-				tbf->data_index = 0;
-				// New LLC PDU continues until the end of the RLC information field, no more extension octets.
-				if ((ul_data_block->E[num] == 1))
-				{
-					llc_pdu_len = UL_RLC_DATA_BLOCK_LEN - data_block_hdr_len - data_octet_num;
-					for (unsigned i = tbf->data_index; i < tbf->data_index + llc_pdu_len; i++)
-					{
-						tbf->rlc_data[i] = ul_data_block->RLC_DATA[data_octet_num];
-						data_octet_num++;
-					}
-					tbf->data_index += llc_pdu_len;
-					num++;
-				}
-			}
-		}
-	}
-}
-
-/* Received Uplink RLC data block. */
-int gprs_rlcmac_rcv_data_block(bitvec *rlc_block)
-{
-	struct gprs_rlcmac_tbf *tbf;
-	int rc = 0;
-
-	LOGP(DRLCMAC, LOGL_NOTICE, "RX: [PCU <- BTS] Uplink Data Block\n");
-	RlcMacUplinkDataBlock_t * ul_data_block = (RlcMacUplinkDataBlock_t *)malloc(sizeof(RlcMacUplinkDataBlock_t));
-	LOGP(DRLCMAC, LOGL_NOTICE, "+++++++++++++++++++++++++ RX : Uplink Data Block +++++++++++++++++++++++++\n");
-	decode_gsm_rlcmac_uplink_data(rlc_block, ul_data_block);
-	LOGP(DRLCMAC, LOGL_NOTICE, "------------------------- RX : Uplink Data Block -------------------------\n");
-
-	tbf = tbf_by_tfi(ul_data_block->TFI, GPRS_RLCMAC_UL_TBF);
-	if (!tbf) {
-		return -1;
-	}
-	
-	rc = tbf_ul_data_transfer(tbf, ul_data_block);
-	free(ul_data_block);
-	return rc;
-}
-
-/* Received Uplink RLC control block. */
-int gprs_rlcmac_rcv_control_block(bitvec *rlc_block)
-{
-	uint8_t tfi = 0;
-	uint32_t tlli = 0;
-	struct gprs_rlcmac_tbf *tbf;
-	RlcMacUplink_t * ul_control_block = (RlcMacUplink_t *)malloc(sizeof(RlcMacUplink_t));
-	LOGP(DRLCMAC, LOGL_NOTICE, "+++++++++++++++++++++++++ RX : Uplink Control Block +++++++++++++++++++++++++\n");
-	decode_gsm_rlcmac_uplink(rlc_block, ul_control_block);
-	LOGPC(DRLCMAC, LOGL_NOTICE, "\n");
-	LOGP(DRLCMAC, LOGL_NOTICE, "------------------------- RX : Uplink Control Block -------------------------\n");
-	switch (ul_control_block->u.MESSAGE_TYPE) {
-	case MT_PACKET_CONTROL_ACK:
-		tlli = ul_control_block->u.Packet_Control_Acknowledgement.TLLI;
-		tbf = tbf_by_tlli(tlli, GPRS_RLCMAC_UL_TBF);
-		if (!tbf) {
-			return 0;
-		}
-		LOGP(DRLCMAC, LOGL_NOTICE, "RX: [PCU <- BTS] TFI: %u TLLI: 0x%08x Packet Control Ack\n", tbf->tfi, tbf->tlli);
-		tbf_ul_release(tbf);
-		break;
-	case MT_PACKET_DOWNLINK_ACK_NACK:
-		tfi = ul_control_block->u.Packet_Downlink_Ack_Nack.DOWNLINK_TFI;
-		tbf = tbf_by_tfi(tfi, GPRS_RLCMAC_DL_TBF);
-		if (!tbf) {
-			return 0;
-		}
-		LOGP(DRLCMAC, LOGL_NOTICE, "RX: [PCU <- BTS] TFI: %u TLLI: 0x%08x Packet Downlink Ack/Nack\n", tbf->tfi, tbf->tlli);
-		tbf_dl_release(tbf);
-		break;
-	}
-	free(ul_control_block);
-	return 1;
-}
-
-void gprs_rlcmac_rcv_block(bitvec *rlc_block)
-{
-	unsigned readIndex = 0;
-	unsigned payload = bitvec_read_field(rlc_block, readIndex, 2);
-
-	switch (payload) {
-	case GPRS_RLCMAC_DATA_BLOCK:
-		gprs_rlcmac_rcv_data_block(rlc_block);
-		break;
-	case GPRS_RLCMAC_CONTROL_BLOCK:
-		gprs_rlcmac_rcv_control_block(rlc_block);
-		break;
-	case GPRS_RLCMAC_CONTROL_BLOCK_OPT:
-		LOGP(DRLCMAC, LOGL_NOTICE, "GPRS_RLCMAC_CONTROL_BLOCK_OPT block payload is not supported.\n");
-	default:
-		LOGP(DRLCMAC, LOGL_NOTICE, "Unknown RLCMAC block payload.\n");
-	}
-}
-
-struct msgb *gen_dummy_msg(uint8_t usf)
-{
-	struct msgb *msg = msgb_alloc(23, "rlcmac_dl_idle");
-	// RLC/MAC filler with USF=1
-	bitvec *filler = bitvec_alloc(23);
-#warning HACK
-	if (usf == 1)
-		bitvec_unhex(filler, "41942b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
-	else
-		bitvec_unhex(filler, "42942b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
-	bitvec_pack(filler, msgb_put(msg, 23));
-	bitvec_free(filler);
-	return msg;
-}
-
-void gprs_rlcmac_rcv_rts_block(uint8_t trx, uint8_t ts, uint16_t arfcn,
-	uint32_t fn, uint8_t block_nr)
-{
-	struct msgb *msg;
-	
-	set_current_fn(fn);
-	msg = msgb_dequeue(&block_queue);
-	if (!msg)
-		msg = gen_dummy_msg(block_nr ? 2 : 1);
-	pcu_l1if_tx_pdtch(msg, trx, ts, arfcn, fn, block_nr);
-}
-
-int select_pdch(uint8_t *_trx, uint8_t *_ts)
-{
-	uint8_t trx, ts;
-
-	for (trx = 0; trx < 8; trx++) {
-		for (ts = 0; ts < 8; ts++) {
-			if (pcu_l1if_bts.trx[trx].ts[ts].enable) {
-				*_trx = trx;
-				*_ts = ts;
-				return 0;
-			}
-		}
-	}
-
-	return -EBUSY;
-}
-
-int gprs_rlcmac_rcv_rach(uint8_t ra, uint32_t Fn, int16_t qta)
-{
-	struct gprs_rlcmac_tbf *tbf;
-	uint8_t trx, ts;
-
-	static uint8_t prev_ra = 0;
-
-	if (prev_ra == ra)
-	{
-		return -1;
-	}
-
-	tbf = tbf_alloc(GPRS_RLCMAC_UL_TBF);
-
-	return tbf_ul_establish(tbf, ra, Fn, qta);
-}
-
-int gprs_rlcmac_tx_llc_pdus(struct gprs_rlcmac_tbf *tbf)
-{
-	int fbi = 0;
-	int bsn = 0;
-
-
-	if (tbf->llc_pdu_list_len == 0)
-	{
-		return -1;
-	}
-	
-	bitvec *data_block_vector = bitvec_alloc(BLOCK_LEN);
-	bitvec_unhex(data_block_vector, "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
-	RlcMacDownlinkDataBlock_t * data_block = (RlcMacDownlinkDataBlock_t *)malloc(sizeof(RlcMacDownlinkDataBlock_t));
-	
-	struct tbf_llc_pdu *llc_pdu;
-	
-	int data_block_ready = 0;
-	unsigned data_oct_num = 0;
-	int llc_pdu_index;
-	for (unsigned i = 0; i < tbf->llc_pdu_list_len; i++)
-	{
-		llc_pdu = tbf_llc_pdu_by_num(tbf->llc_pdus, i);
-		if (!llc_pdu)
-		{
-			return -1;
-		}
-
-		llc_pdu_index = 0;
-
-		do
-		{
-			data_block->PAYLOAD_TYPE = 0;
-			data_block->RRBP = 0;
-			data_block->SP = 1;
-			data_block->USF = 1;
-			data_block->PR = 0;
-			data_block->TFI = tbf->tfi;
-			data_block->BSN = bsn;
-
-			// Write LLC PDU to Data Block
-			int j;
-			for(j = llc_pdu_index; j < llc_pdu->len; j++)
-			{
-				data_block->RLC_DATA[data_oct_num] = llc_pdu->data[j];
-				data_oct_num++;
-				llc_pdu_index++;
-				// RLC data field is completely filled.
-				if (data_oct_num == BLOCK_LEN - 3)
-				{
-					fbi = 0;
-					data_block->E_1 = 1;
-					data_block_ready = 1;
-					break;
-				}
-			}
-			if(!data_block_ready)
-			{
-				data_block->E_1 = 0;
-				data_block->LENGTH_INDICATOR[0] = data_oct_num;
-				if ((i+1) == tbf->llc_pdu_list_len)
-				{
-					// Current LLC PDU is last in TBF.
-					data_block->M[0] = 0;
-					data_block->E[0] = 1;
-					fbi = 1;
-					for(unsigned k = data_oct_num; k < BLOCK_LEN - 4; k++)
-					{
-						data_block->RLC_DATA[k] = 0x2b;
-					}
-					data_block_ready = 1; 
-				}
-				else
-				{
-					// More LLC PDUs should be transmited in this TBF.
-					data_block->M[0] = 1;
-					data_block->E[0] = 1;
-					data_block_ready = 1;
-					break;
-				}
-			}
-
-			data_block->FBI = fbi;
-
-			if(data_block_ready)
-			{
-				LOGP(DRLCMAC, LOGL_NOTICE, "TX: [PCU -> BTS] Downlink Data Block\n");
-				LOGP(DRLCMAC, LOGL_NOTICE, "+++++++++++++++++++++++++ TX : Downlink Data Block +++++++++++++++++++++++++\n");
-				encode_gsm_rlcmac_downlink_data(data_block_vector, data_block);
-				LOGP(DRLCMAC, LOGL_NOTICE, "------------------------- TX : Downlink Data Block -------------------------\n");
-				gprs_rlcmac_enqueue_block(data_block_vector, BLOCK_LEN);
-				bitvec_unhex(data_block_vector, "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
-				bsn++;
-				data_block_ready = 0;
-				data_oct_num = 0;
-			}
-		}
-		while(llc_pdu->len != llc_pdu_index);
-	}
-
-	return 0;
-}
-
 /* Send Uplink unit-data to SGSN. */
-void gprs_rlcmac_tx_ul_ud(gprs_rlcmac_tbf *tbf)
+int gprs_rlcmac_tx_ul_ud(gprs_rlcmac_tbf *tbf)
 {
 	const uint8_t qos_profile = QOS_PROFILE;
 	struct msgb *llc_pdu;
-	unsigned msg_len = NS_HDR_LEN + BSSGP_HDR_LEN + tbf->data_index;
+	unsigned msg_len = NS_HDR_LEN + BSSGP_HDR_LEN + tbf->llc_index;
 
-	LOGP(DBSSGP, LOGL_NOTICE, "TX: [PCU -> SGSN ] TFI: %u TLLI: 0x%08x DataLen: %u", tbf->tfi, tbf->tlli, tbf->data_index);
-	//LOGP(DBSSGP, LOGL_NOTICE, " Data = ");
-	//for (unsigned i = 0; i < tbf->data_index; i++)
-	//	LOGPC(DBSSGP, LOGL_NOTICE, "%02x ", tbf->rlc_data[i]);
+	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));
+	if (!bctx) {
+		LOGP(DBSSGP, LOGL_ERROR, "No bctx\n");
+		return -EIO;
+	}
 	
-	bctx->cell_id = CELL_ID;
-	bctx->nsei = NSEI;
-	bctx->ra_id.mnc = MNC;
-	bctx->ra_id.mcc = MCC;
-	bctx->ra_id.lac = PCU_LAC;
-	bctx->ra_id.rac = PCU_RAC;
-	bctx->bvci = BVCI;
-
 	llc_pdu = msgb_alloc_headroom(msg_len, msg_len,"llc_pdu");
-	msgb_tvlv_push(llc_pdu, BSSGP_IE_LLC_PDU, sizeof(uint8_t)*tbf->data_index, tbf->rlc_data);
+	msgb_tvlv_push(llc_pdu, BSSGP_IE_LLC_PDU, sizeof(uint8_t)*tbf->llc_index, tbf->llc_frame);
 	bssgp_tx_ul_ud(bctx, tbf->tlli, &qos_profile, llc_pdu);
-}
 
-void gprs_rlcmac_downlink_assignment(gprs_rlcmac_tbf *tbf)
-{
-	LOGP(DRLCMAC, LOGL_NOTICE, "TX: [PCU -> BTS] TFI: %u TLLI: 0x%08x Immidiate Assignment (CCCH)\n", tbf->tfi, tbf->tlli);
-	bitvec *immediate_assignment = bitvec_alloc(23);
-	bitvec_unhex(immediate_assignment, "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
-	int len = write_immediate_assignment(immediate_assignment, 1, 125, get_current_fn(), tbf->ta, tbf->arfcn, tbf->ts, tbf->tsc, tbf->tfi, tbf->tlli);
-	pcu_l1if_tx_agch(immediate_assignment, len);
-	bitvec_free(immediate_assignment);
-}
-
-void gprs_rlcmac_packet_downlink_assignment(gprs_rlcmac_tbf *tbf)
-{
-	LOGP(DRLCMAC, LOGL_NOTICE, "TX: [PCU -> BTS] TFI: %u TLLI: 0x%08x Packet DL Assignment\n", tbf->tfi, tbf->tlli);
-	bitvec *packet_downlink_assignment_vec = bitvec_alloc(23);
-	bitvec_unhex(packet_downlink_assignment_vec, "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
-	RlcMacDownlink_t * packet_downlink_assignment = (RlcMacDownlink_t *)malloc(sizeof(RlcMacDownlink_t));
-	write_packet_downlink_assignment(packet_downlink_assignment, tbf->tfi, tbf->arfcn, tbf->ts, tbf->ta, tbf->tsc);
-	LOGP(DRLCMAC, LOGL_NOTICE, "+++++++++++++++++++++++++ TX : Packet Downlink Assignment +++++++++++++++++++++++++\n");
-	encode_gsm_rlcmac_downlink(packet_downlink_assignment_vec, packet_downlink_assignment);
-	LOGPC(DRLCMAC, LOGL_NOTICE, "\n");
-	LOGP(DRLCMAC, LOGL_NOTICE, "------------------------- TX : Packet Downlink Assignment -------------------------\n");
-	free(packet_downlink_assignment);
-	gprs_rlcmac_enqueue_block(packet_downlink_assignment_vec, 23);
-	bitvec_free(packet_downlink_assignment_vec);
-}
-
-void gprs_rlcmac_paging_request(uint8_t *ptmsi, uint16_t ptmsi_len)
-{
-	LOGP(DRLCMAC, LOGL_NOTICE, "TX: [PCU -> BTS] Paging Request (CCCH)\n");
-	bitvec *paging_request = bitvec_alloc(23);
-	bitvec_unhex(paging_request, "2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
-	int len = write_paging_request(paging_request, ptmsi, ptmsi_len);
-	pcu_l1if_tx_pch(paging_request, len);
-	bitvec_free(paging_request);
+	return 0;
 }
diff --git a/src/gprs_rlcmac.h b/src/gprs_rlcmac.h
index ed0850b..635a6b1 100644
--- a/src/gprs_rlcmac.h
+++ b/src/gprs_rlcmac.h
@@ -29,24 +29,93 @@
 #include <osmocom/core/timer.h>
 }
 
-#define LLC_MAX_LEN 1543
-#define UL_RLC_DATA_BLOCK_LEN 23
+/* This special feature will delay assignment of downlink TBF by one second,
+ * in case there is already a TBF.
+ * This is usefull to debug downlink establishment during packet idle mode.
+ */
+//#define DEBUG_DL_ASS_IDLE
 
-enum gprs_rlcmac_tbf_stage {
-	TBF_ESTABLISH,
-	TBF_DATA_TRANSFER,
-	TBF_RELEASE
+/*
+ * PDCH instanc
+ */
+
+struct gprs_rlcmac_tbf;
+
+struct gprs_rlcmac_pdch {
+	uint8_t enable; /* TS is enabled */
+	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 */
+	uint32_t last_rts_fn; /* store last frame number of RTS */
 };
 
+struct gprs_rlcmac_trx {
+	uint16_t arfcn;
+	struct gprs_rlcmac_pdch pdch[8];
+};
+
+struct gprs_rlcmac_bts {
+	uint8_t cs1;
+	uint8_t cs2;
+	uint8_t cs3;
+	uint8_t cs4;
+	uint8_t initial_cs;
+	uint8_t t3142;
+	uint8_t t3169;
+	uint8_t t3191;
+	uint16_t t3193_msec;
+	uint8_t t3195;
+	uint8_t n3101;
+	uint8_t n3103;
+	uint8_t n3105;
+	struct gprs_rlcmac_trx trx[8];
+};
+
+extern struct gprs_rlcmac_bts *gprs_rlcmac_bts;
+
+/*
+ * TBF instance
+ */
+
+#define LLC_MAX_LEN 1543
+#define RLC_MAX_SNS 128 /* GPRS, must be power of 2 */
+#define RLC_MAX_WS  64 /* max window size */
+#define RLC_MAX_LEN 54 /* CS-4 including spare bits */
+
+#define Tassign_agch 0,500000/* wait for assignment, before transmitting DL */
+#define Tassign_pacch 0,100000/* wait for assignment, before transmitting DL */
+
 enum gprs_rlcmac_tbf_state {
-	WAIT_ESTABLISH,
-	CCCH_ESTABLISH,
-	PACCH_ESTABLISH,
-	FINISH_ESTABLISH,
-	WAIT_DATA_TRANSFER,
-	DATA_TRANSFER,
-	FINISH_DATA_TRANSFER,
-	RELEASE
+	GPRS_RLCMAC_NULL = 0,	/* new created TBF */
+	GPRS_RLCMAC_ASSIGN,	/* wait for downlink assignment */
+	GPRS_RLCMAC_FLOW,	/* RLC/MAC flow, ressource needed */
+	GPRS_RLCMAC_FINISHED,	/* flow finished, wait for release */
+	GPRS_RLCMAC_WAIT_RELEASE,/* wait for release or restart of DL TBF */
+	GPRS_RLCMAC_RELEASING,	/* releasing, wait to free TBI/USF */
+};
+
+enum gprs_rlcmac_tbf_poll_state {
+	GPRS_RLCMAC_POLL_NONE = 0,
+	GPRS_RLCMAC_POLL_SCHED, /* a polling was scheduled */
+};
+
+enum gprs_rlcmac_tbf_dl_ass_state {
+	GPRS_RLCMAC_DL_ASS_NONE = 0,
+	GPRS_RLCMAC_DL_ASS_SEND_ASS, /* send downlink assignment on next RTS */
+	GPRS_RLCMAC_DL_ASS_WAIT_ACK, /* wait for PACKET CONTROL ACK */
+};
+
+enum gprs_rlcmac_tbf_ul_ass_state {
+	GPRS_RLCMAC_UL_ASS_NONE = 0,
+	GPRS_RLCMAC_UL_ASS_SEND_ASS, /* send uplink assignment on next RTS */
+	GPRS_RLCMAC_UL_ASS_WAIT_ACK, /* wait for PACKET CONTROL ACK */
+};
+
+enum gprs_rlcmac_tbf_ul_ack_state {
+	GPRS_RLCMAC_UL_ACK_NONE = 0,
+	GPRS_RLCMAC_UL_ACK_SEND_ACK, /* send acknowledge on next RTS */
+	GPRS_RLCMAC_UL_ACK_WAIT_ACK, /* wait for PACKET CONTROL ACK */
 };
 
 enum gprs_rlcmac_tbf_direction {
@@ -54,30 +123,57 @@
 	GPRS_RLCMAC_UL_TBF
 };
 
-struct tbf_llc_pdu {
-	struct llist_head list;
-	uint8_t num;
-	uint8_t data[LLC_MAX_LEN];
-	uint16_t len;
-};
-
 struct gprs_rlcmac_tbf {
 	struct llist_head list;
 	enum gprs_rlcmac_tbf_state state;
-	enum gprs_rlcmac_tbf_stage stage;
 	enum gprs_rlcmac_tbf_direction direction;
-	struct gprs_rlcmac_tbf *next_tbf;
 	uint8_t tfi;
 	uint32_t tlli;
-	
-	struct llist_head llc_pdus;
-	struct tbf_llc_pdu llc_pdu;
-	uint8_t llc_pdu_list_len;
-	uint8_t rlc_data[LLC_MAX_LEN];
-	uint16_t data_index;
-	uint8_t bsn;
+	uint8_t tlli_valid;
 	uint8_t trx, ts, tsc;
+	struct gprs_rlcmac_pdch *pdch;
 	uint16_t arfcn, 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 */
+	llist_head llc_queue; /* queued LLC DL data */
+
+	enum gprs_rlcmac_tbf_dl_ass_state dl_ass_state;
+	enum gprs_rlcmac_tbf_ul_ass_state ul_ass_state;
+	enum gprs_rlcmac_tbf_ul_ack_state ul_ack_state;
+
+	enum gprs_rlcmac_tbf_poll_state poll_state;
+	uint32_t poll_fn;
+
+	uint16_t ws;	/* window size */
+	uint16_t sns;	/* sequence number space */
+
+	/* Please note that all variables here will be reset when changing
+	 * from WAIT RELEASE back to FLOW state (re-use of TBF).
+	 * All states that need reset must be in this struct, so this is why
+	 * variables are in both (dl and ul) structs and not outside union.
+	 */
+	union {
+		struct {
+			uint16_t bsn;	/* block sequence number */
+			uint16_t v_s;	/* send state */
+			uint16_t v_a;	/* ack state */
+			char v_b[RLC_MAX_SNS/2]; /* acknowledge state array */
+			int32_t tx_counter; /* count all transmitted blocks */
+			uint8_t n3105;	/* N3105 counter */
+		} dl;
+		struct {
+			uint16_t bsn;	/* block sequence number */
+			uint16_t v_r;	/* receive state */
+			uint16_t v_q;	/* receive window state */
+			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 */
+		} ul;
+	} dir;
+	uint8_t rlc_block[RLC_MAX_SNS/2][RLC_MAX_LEN]; /* block history */
+	uint8_t rlc_block_len[RLC_MAX_SNS/2]; /* block len  of history */
 	
 	struct osmo_timer_list	timer;
 	unsigned int T; /* Txxxx number */
@@ -88,6 +184,30 @@
 	unsigned int num_fT_exp; /* number of consecutive fT expirations */
 };
 
+extern struct llist_head gprs_rlcmac_tbfs;
+
+int tfi_alloc(uint8_t *_trx, uint8_t *_ts);
+
+struct gprs_rlcmac_tbf *tbf_alloc(uint8_t tfi, uint8_t trx, uint8_t ts);
+
+struct gprs_rlcmac_tbf *tbf_by_tfi(uint8_t tfi, int direction);
+
+struct gprs_rlcmac_tbf *tbf_by_tlli(uint32_t tlli, int direction);
+
+struct gprs_rlcmac_tbf *tbf_by_poll_fn(uint32_t fn);
+
+int find_free_usf(uint8_t trx, uint8_t ts);
+
+void tbf_free(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);
+
+void tbf_timer_stop(struct gprs_rlcmac_tbf *tbf);
+
 /* TS 44.060 Section 10.4.7 Table 10.4.7.1: Payload Type field */
 enum gprs_rlcmac_block_type {
 	GPRS_RLCMAC_DATA_BLOCK = 0x0,
@@ -96,64 +216,56 @@
 	GPRS_RLCMAC_RESERVED = 0x3
 };
 
-extern struct llist_head gprs_rlcmac_tbfs;
+int gprs_rlcmac_rcv_block(uint8_t *data, uint8_t len, uint32_t fn);
 
-int select_pdch(uint8_t *_trx, uint8_t *_ts);
+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, 
+        uint8_t tfi, uint8_t usf, uint32_t tlli, uint8_t polling,
+	uint32_t poll_fn);
 
-int tfi_alloc();
+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);
 
-static struct gprs_rlcmac_tbf *tbf_by_tfi(uint8_t tfi, gprs_rlcmac_tbf_direction dir);
+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);
 
-static struct gprs_rlcmac_tbf *tbf_by_tlli(uint32_t tlli, gprs_rlcmac_tbf_direction dir);
+void write_packet_uplink_ack(RlcMacDownlink_t * block, struct gprs_rlcmac_tbf *tbf,
+        uint8_t final);
 
-static void tbf_free(struct gprs_rlcmac_tbf *tbf);
+int gprs_rlcmac_tx_ul_ud(gprs_rlcmac_tbf *tbf);
 
-static struct tbf_llc_pdu *tbf_llc_pdu_by_num(struct llist_head llc_pdus, uint8_t num);
+void tbf_timer_cb(void *_tbf);
 
-int tbf_add_llc_pdu(struct gprs_rlcmac_tbf *tbf, uint8_t *data, uint16_t llc_pdu_len);
-
-struct gprs_rlcmac_tbf *tbf_alloc(gprs_rlcmac_tbf_direction dir, uint32_t tlli = 0);
-
-int tbf_ul_establish(struct gprs_rlcmac_tbf *tbf, uint8_t ra, uint32_t Fn, uint16_t qta);
-
-int tbf_dl_establish(struct gprs_rlcmac_tbf *tbf);
-
-int tbf_ul_data_transfer(struct gprs_rlcmac_tbf *tbf, RlcMacUplinkDataBlock_t * ul_data_block);
-
-int tbf_dl_data_transfer(struct gprs_rlcmac_tbf *tbf, uint8_t *llc_pdu = NULL, uint16_t llc_pdu_len = 0);
-
-int tbf_ul_release(struct gprs_rlcmac_tbf *tbf);
-
-int tbf_dl_release(struct gprs_rlcmac_tbf *tbf);
-
-static void tbf_timer_start(struct gprs_rlcmac_tbf *tbf, unsigned int T, unsigned int seconds);
-
-static void tbf_gsm_timer_start(struct gprs_rlcmac_tbf *tbf, unsigned int fT, int frames);
-
-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, uint8_t tfi, uint32_t tlli = 0);
-
-void gprs_rlcmac_tx_ul_ack(uint8_t tfi, uint32_t tlli, uint8_t ti, uint8_t bsn);
-
-void gprs_rlcmac_data_block_parse(gprs_rlcmac_tbf* tbf, RlcMacUplinkDataBlock_t * ul_data_block);
-
-int gprs_rlcmac_rcv_data_block(bitvec *rlc_block);
-
-int gprs_rlcmac_rcv_control_block(bitvec *rlc_block);
-
-void gprs_rlcmac_rcv_block(bitvec *rlc_block);
-
-void gprs_rlcmac_rcv_rts_block(uint8_t trx, uint8_t ts, uint16_t arfcn, 
-        uint32_t fn, uint8_t block_nr);
+int gprs_rlcmac_poll_timeout(struct gprs_rlcmac_tbf *tbf);
 
 int gprs_rlcmac_rcv_rach(uint8_t ra, uint32_t Fn, int16_t qta);
 
-int gprs_rlcmac_tx_llc_pdus(struct gprs_rlcmac_tbf *tbf);
+int gprs_rlcmac_rcv_control_block(bitvec *rlc_block, uint32_t fn);
 
-void gprs_rlcmac_tx_ul_ud(gprs_rlcmac_tbf *tbf);
+struct msgb *gprs_rlcmac_send_packet_uplink_assignment(
+        struct gprs_rlcmac_tbf *tbf, uint32_t fn);
 
-void gprs_rlcmac_downlink_assignment(gprs_rlcmac_tbf *tbf);
+struct msgb *gprs_rlcmac_send_packet_downlink_assignment(
+        struct gprs_rlcmac_tbf *tbf, uint32_t fn);
 
-void gprs_rlcmac_packet_downlink_assignment(gprs_rlcmac_tbf *tbf);
+void gprs_rlcmac_trigger_downlink_assignment(gprs_rlcmac_tbf *tbf,
+	uint8_t old_downlink, char *imsi);
 
-void gprs_rlcmac_paging_request(uint8_t *ptmsi, uint16_t ptmsi_len);
+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);
+
+struct msgb *gprs_rlcmac_send_data_block_acknowledged(
+        struct gprs_rlcmac_tbf *tbf, uint32_t fn);
+
+struct msgb *gprs_rlcmac_send_uplink_ack(struct gprs_rlcmac_tbf *tbf,
+        uint32_t fn);
+
+int gprs_rlcmac_rcv_rts_block(uint8_t trx, uint8_t ts, uint16_t arfcn, 
+        uint32_t fn, uint8_t block_nr);
+
 #endif // GPRS_RLCMAC_H
diff --git a/src/gprs_rlcmac_data.cpp b/src/gprs_rlcmac_data.cpp
new file mode 100644
index 0000000..7beca38
--- /dev/null
+++ b/src/gprs_rlcmac_data.cpp
@@ -0,0 +1,1409 @@
+/* Data block transfer
+ *
+ * Copyright (C) 2012 Ivan Klyuchnikov
+ * Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
+ *
+ * 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 <gprs_bssgp_pcu.h>
+#include <gprs_rlcmac.h>
+#include <pcu_l1_if.h>
+
+/* After receiving these framess, we send ack/nack. */
+#define ACK_AFTER_FRAMES 20
+
+/* If acknowledgement to uplink/downlin assignmentshould be polled */
+#define POLLING_ASSIGNMENT 0
+
+extern "C" {
+/* TS 04.60  10.2.2 */
+struct rlc_ul_header {
+	uint8_t	r:1,
+		 si:1,
+		 cv:4,
+		 pt:2;
+	uint8_t	ti:1,
+		 tfi:5,
+		 pi:1,
+		 spare:1;
+	uint8_t	e:1,
+		 bsn:7;
+} __attribute__ ((packed));
+
+struct rlc_dl_header {
+	uint8_t	usf:3,
+		 s_p:1,
+		 rrbp:2,
+		 pt:2;
+	uint8_t	fbi:1,
+		 tfi:5,
+		 pr:2;
+	uint8_t	e:1,
+		 bsn:7;
+} __attribute__ ((packed));
+
+struct rlc_li_field {
+	uint8_t	e:1,
+		 m:1,
+		 li:6;
+} __attribute__ ((packed));
+}
+
+int gprs_rlcmac_poll_timeout(struct gprs_rlcmac_tbf *tbf)
+{
+	LOGP(DRLCMAC, LOGL_NOTICE, "Poll timeout for TBF=%d\n", tbf->tfi);
+
+	tbf->poll_state = GPRS_RLCMAC_POLL_NONE;
+
+	if (tbf->ul_ack_state == GPRS_RLCMAC_UL_ACK_WAIT_ACK) {
+		LOGP(DRLCMAC, LOGL_DEBUG, "- Timeout for polling PACKET "
+			"CONTROL ACK for PACKET UPLINK ACK\n");
+		tbf->ul_ack_state = GPRS_RLCMAC_UL_ACK_NONE;
+		if (tbf->state == GPRS_RLCMAC_FINISHED) {
+			struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
+
+			tbf->dir.ul.n3103++;
+			if (tbf->dir.ul.n3103 == bts->n3103) {
+				LOGP(DRLCMAC, LOGL_DEBUG, "- N3103 exceeded\n");
+				tbf_new_state(tbf, GPRS_RLCMAC_RELEASING);
+				tbf_timer_start(tbf, 3169, bts->t3169, 0);
+				return 0;
+			}
+			/* reschedule UL ack */
+			tbf->ul_ack_state = GPRS_RLCMAC_UL_ACK_SEND_ACK;
+		}
+	} else
+	if (tbf->ul_ass_state == GPRS_RLCMAC_UL_ASS_WAIT_ACK) {
+		LOGP(DRLCMAC, LOGL_DEBUG, "- Timeout for polling PACKET "
+			"CONTROL ACK for PACKET UPLINK ASSIGNMENT.\n");
+		tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_NONE;
+	} else
+	if (tbf->dl_ass_state == GPRS_RLCMAC_DL_ASS_WAIT_ACK) {
+		LOGP(DRLCMAC, LOGL_DEBUG, "- Timeout for polling PACKET "
+			"CONTROL ACK for PACKET DOWNLINK ASSIGNMENT.\n");
+		tbf->dl_ass_state = GPRS_RLCMAC_DL_ASS_NONE;
+		/* in case out downlink assigment failed: */
+		if (tbf->state == GPRS_RLCMAC_ASSIGN) {
+			LOGP(DRLCMAC, LOGL_DEBUG, "- Assignment failed\n");
+			tbf_free(tbf);
+		}
+	} else
+	if (tbf->direction == GPRS_RLCMAC_DL_TBF)
+	{
+		struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
+
+		LOGP(DRLCMAC, LOGL_DEBUG, "- Timeout for polling PACKET "
+			" DOWNLINK ACK.\n");
+		tbf->dir.dl.n3105++;
+		if (tbf->dir.dl.n3105 == bts->n3105) {
+			LOGP(DRLCMAC, LOGL_DEBUG, "- N3105 exceeded\n");
+			tbf_new_state(tbf, GPRS_RLCMAC_RELEASING);
+			tbf_timer_start(tbf, 3195, bts->t3195, 0);
+			return 0;
+		}
+	}
+
+	return 0;
+}
+
+/* Received Uplink RLC control block. */
+int gprs_rlcmac_rcv_control_block(bitvec *rlc_block, uint32_t fn)
+{
+	uint8_t tfi = 0;
+	uint32_t tlli = 0;
+	struct gprs_rlcmac_tbf *tbf;
+
+	RlcMacUplink_t * ul_control_block = (RlcMacUplink_t *)malloc(sizeof(RlcMacUplink_t));
+	LOGP(DRLCMAC, LOGL_DEBUG, "+++++++++++++++++++++++++ RX : Uplink Control Block +++++++++++++++++++++++++\n");
+	decode_gsm_rlcmac_uplink(rlc_block, ul_control_block);
+	LOGPC(DCSN1, LOGL_NOTICE, "\n");
+	LOGP(DRLCMAC, LOGL_DEBUG, "------------------------- RX : Uplink Control Block -------------------------\n");
+	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);
+		if (!tbf) {
+			LOGP(DRLCMAC, LOGL_NOTICE, "PACKET CONTROL ACK with "
+				"unknown FN=%u TLL=0x%08x\n", fn, tlli);
+			break;
+		}
+		tfi = tbf->tfi;
+		if (tlli != tbf->tlli) {
+			LOGP(DRLCMAC, LOGL_INFO, "Phone changed TLLI to "
+				"0x%08x\n", tlli);
+			tbf->tlli = tlli;
+		}
+		LOGP(DRLCMAC, LOGL_DEBUG, "RX: [PCU <- BTS] TFI: %u TLLI: 0x%08x Packet Control Ack\n", tbf->tfi, tbf->tlli);
+		tbf->poll_state = GPRS_RLCMAC_POLL_NONE;
+
+		/* check if this control ack belongs to packet uplink ack */
+		if (tbf->ul_ack_state == GPRS_RLCMAC_UL_ACK_WAIT_ACK) {
+			LOGP(DRLCMAC, LOGL_DEBUG, "TBF: [UPLINK] END TFI: %u TLLI: 0x%08x \n", tbf->tfi, tbf->tlli);
+			tbf->ul_ack_state = GPRS_RLCMAC_UL_ACK_NONE;
+			tbf_free(tbf);
+			break;
+		}
+		if (tbf->dl_ass_state == GPRS_RLCMAC_DL_ASS_WAIT_ACK) {
+			LOGP(DRLCMAC, LOGL_DEBUG, "TBF: [UPLINK] DOWNLINK ASSIGNED TFI: %u TLLI: 0x%08x \n", tbf->tfi, tbf->tlli);
+			tbf->dl_ass_state = GPRS_RLCMAC_DL_ASS_NONE;
+			break;
+		}
+		if (tbf->ul_ass_state == GPRS_RLCMAC_UL_ASS_WAIT_ACK) {
+			LOGP(DRLCMAC, LOGL_DEBUG, "TBF: [DOWNLINK] UPLINK ASSIGNED TFI: %u TLLI: 0x%08x \n", tbf->tfi, tbf->tlli);
+			tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_NONE;
+			break;
+		}
+		LOGP(DRLCMAC, LOGL_ERROR, "Error: received PACET CONTROL ACK "
+			"at no request\n");
+		break;
+	case MT_PACKET_DOWNLINK_ACK_NACK:
+		tfi = ul_control_block->u.Packet_Downlink_Ack_Nack.DOWNLINK_TFI;
+		tbf = tbf_by_poll_fn(fn);
+		if (!tbf) {
+			LOGP(DRLCMAC, LOGL_NOTICE, "PACKET DOWNLINK ACK with "
+				"unknown FN=%u TBF=%d\n", fn, tfi);
+			break;
+		}
+		/* reset N3105 */
+		tbf->dir.dl.n3105 = 0;
+		/* stop timer T3191 */
+		tbf_timer_stop(tbf);
+		tlli = tbf->tlli;
+		LOGP(DRLCMAC, LOGL_DEBUG, "RX: [PCU <- BTS] TFI: %u TLLI: 0x%08x Packet Downlink Ack/Nack\n", tbf->tfi, tbf->tlli);
+		tbf->poll_state = GPRS_RLCMAC_POLL_NONE;
+
+		gprs_rlcmac_downlink_ack(tbf,
+			ul_control_block->u.Packet_Downlink_Ack_Nack.Ack_Nack_Description.FINAL_ACK_INDICATION,
+			ul_control_block->u.Packet_Downlink_Ack_Nack.Ack_Nack_Description.STARTING_SEQUENCE_NUMBER,
+			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;
+			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);
+			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: 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 */
+			tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_SEND_ASS;
+		}
+		break;
+	case MT_PACKET_RESOURCE_REQUEST:
+		if (ul_control_block->u.Packet_Resource_Request.ID.UnionType) {
+			tlli = ul_control_block->u.Packet_Resource_Request.ID.u.TLLI;
+			tbf = tbf_by_tlli(tlli, GPRS_RLCMAC_UL_TBF);
+			if (!tbf) {
+				LOGP(DRLCMAC, LOGL_NOTICE, "PACKET RESSOURCE REQ unknown uplink TLLI=0x%08x\n", tlli);
+				break;
+			}
+			tfi = tbf->tfi;
+		} 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);
+				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);
+				if (!tbf) {
+					LOGP(DRLCMAC, LOGL_NOTICE, "PACKET RESSOURCE REQ unknown uplink TBF=%d\n", tlli);
+					break;
+				}
+			}
+			tlli = tbf->tlli;
+		}
+		LOGP(DRLCMAC, LOGL_DEBUG, "RX: [PCU <- BTS] TFI: %u TLLI: 0x%08x Packet ressource request\n", tbf->tfi, tbf->tlli);
+#warning FIXME
+puts("FIXME: UL request during UL request");	exit(0);
+
+
+		break;
+	default:
+		LOGP(DRLCMAC, LOGL_NOTICE, "RX: [PCU <- BTS] unknown control block received\n");
+	}
+	free(ul_control_block);
+	return 1;
+}
+
+#ifdef DEBUG_DL_ASS_IDLE
+	char debug_imsi[16];
+#endif
+
+void tbf_timer_cb(void *_tbf)
+{
+	struct gprs_rlcmac_tbf *tbf = (struct gprs_rlcmac_tbf *)_tbf;
+
+	LOGP(DRLCMAC, LOGL_DEBUG, "TBF=%d timer %u expired.\n", tbf->tfi,
+		tbf->T);
+
+	tbf->num_T_exp++;
+
+	switch (tbf->T) {
+#ifdef DEBUG_DL_ASS_IDLE
+	case 1234:
+		gprs_rlcmac_trigger_downlink_assignment(tbf, 0, debug_imsi);
+		break;
+#endif
+	case 0: /* assignment */
+		/* change state to FLOW, so scheduler will start transmission */
+		if (tbf->state == GPRS_RLCMAC_ASSIGN)
+			tbf_new_state(tbf, GPRS_RLCMAC_FLOW);
+		else
+			LOGP(DRLCMAC, LOGL_ERROR, "Error: TBF is not in assign "
+				"state\n");
+		break;
+	case 3169:
+	case 3191:
+	case 3195:
+		LOGP(DRLCMAC, LOGL_NOTICE, "TBF T%d timeout during "
+			"transsmission\n", tbf->T);
+		/* fall through */
+	case 3193:
+		LOGP(DRLCMAC, LOGL_DEBUG, "TBF will be freed due to timeout\n");
+		/* free TBF */
+		tbf_free(tbf);
+		break;
+	default:
+		LOGP(DRLCMAC, LOGL_ERROR, "Timer expired in unknown mode: %u\n",
+			tbf->T);
+	}
+}
+
+/*
+ * UL data block flow
+ */
+
+/* get TLLI from received UL data block */
+static int tlli_from_ul_data(uint8_t *data, uint8_t len, uint32_t *tlli)
+{
+	struct rlc_ul_header *rh = (struct rlc_ul_header *)data;
+	struct rlc_li_field *li;
+	uint8_t e;
+
+	if (!rh->ti)
+		return -EINVAL;
+	
+	data += 3;
+	len -= 3;
+	e = rh->e;
+	/* if E is not set (LI follows) */
+	while (!e) {
+		if (!len) {
+			LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA LI extended, "
+				"but no more data\n");
+			return -EINVAL;
+		}
+		/* get new E */
+		li = (struct rlc_li_field *)data;
+		if (li->e == 0) /* if LI==0, E is interpreted as '1' */
+			e = 1;
+		else
+			e = li->e;
+		data++;
+		len--;
+	}
+	if (len < 4) {
+		LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA TLLI out of frame "
+			"border\n");
+		return -EINVAL;
+	}
+	*tlli = ntohl(*((uint32_t *)data));
+
+	return 0;
+}
+
+/* Store received block data in LLC message(s) and forward to SGSN if complete.
+ */
+static int gprs_rlcmac_assemble_llc(struct gprs_rlcmac_tbf *tbf, uint8_t *data,
+	uint8_t len)
+{
+	struct rlc_ul_header *rh = (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, "Too many frames in "
+				"block\n");
+			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, "UL DATA TBF=%d "
+				"ignored, because M='0' and E='0'.\n",
+				tbf->tfi);
+			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, "UL DATA TLLI out of "
+				"frame border\n");
+			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, "UL DATA PFI out of "
+				"frame border\n");
+			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, "UL DATA TBF=%d ignored, "
+			"because LI delimits data that exceeds block size.\n",
+			tbf->tfi);
+		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];
+		}
+		LOGP(DRLCMACUL, LOGL_DEBUG, "-- Appending chunk (len=%d) to "
+			"frame at %d.\n", chunk, tbf->llc_index);
+		if (tbf->llc_index + chunk > LLC_MAX_LEN) {
+			LOGP(DRLCMACUL, LOGL_NOTICE, "LLC frame exceeds "
+				"maximum size.\n");
+			chunk = LLC_MAX_LEN - tbf->llc_index;
+		}
+		memcpy(tbf->llc_frame + tbf->llc_index, data + frame_offset[i],
+			chunk);
+		tbf->llc_index += chunk;
+		/* not last frame. */
+		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));
+			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
+		 * 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, "Complete UL frame for "
+				"TBF=%d that fits precisely in last block: "
+				"%s\n", tbf->tfi,
+				osmo_hexdump(tbf->llc_frame, tbf->llc_index));
+			gprs_rlcmac_tx_ul_ud(tbf);
+			tbf->llc_index = 0; /* reset frame space */
+		}
+	}
+
+	return 0;
+}
+
+struct msgb *gprs_rlcmac_send_uplink_ack(struct gprs_rlcmac_tbf *tbf,
+	uint32_t fn)
+{
+	int final = (tbf->state == GPRS_RLCMAC_FINISHED);
+	struct msgb *msg;
+
+	if (final && tbf->poll_state != GPRS_RLCMAC_POLL_NONE) {
+		LOGP(DRLCMACUL, LOGL_DEBUG, "Polling is already "
+			"sheduled for TBF=%d, so we must wait for final uplink "
+			"ack...\n", tbf->tfi);
+			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 *)malloc(sizeof(RlcMacDownlink_t));
+	write_packet_uplink_ack(mac_control_block, tbf, final);
+	encode_gsm_rlcmac_downlink(ack_vec, mac_control_block);
+	bitvec_pack(ack_vec, msgb_put(msg, 23));
+	bitvec_free(ack_vec);
+	free(mac_control_block);
+
+	if (final) {
+		tbf->poll_state = GPRS_RLCMAC_POLL_SCHED;
+		tbf->poll_fn = (fn + 13) % 2715648;
+		/* waiting for final acknowledge */
+		tbf->ul_ack_state = GPRS_RLCMAC_UL_ACK_WAIT_ACK;
+	} else
+		tbf->ul_ack_state = GPRS_RLCMAC_UL_ACK_NONE;
+
+	return msg;
+}
+
+/* receive UL data block
+ *
+ * The blocks are defragmented and forwarded as LLC frames, if complete.
+ */
+int gprs_rlcmac_rcv_data_block_acknowledged(uint8_t *data, uint8_t len)
+{
+	struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
+	struct gprs_rlcmac_tbf *tbf;
+	struct rlc_ul_header *rh = (struct rlc_ul_header *)data;
+	uint16_t mod_sns, mod_sns_half, offset_v_q, offset_v_r, index;
+	int rc;
+
+	switch (len) {
+		case 54:
+			/* omitting spare bits */
+			len = 53;
+			break;
+		case 40:
+			/* omitting spare bits */
+			len = 39;
+			break;
+		case 34:
+			/* omitting spare bits */
+			len = 33;
+			break;
+		case 23:
+			break;
+	default:
+		LOGP(DRLCMACUL, LOGL_ERROR, "Dropping data block with invalid"
+			"length: %d)\n", len);
+		return -EINVAL;
+	}
+
+	/* find TBF inst from given TFI */
+	tbf = tbf_by_tfi(rh->tfi, 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);
+
+	/* get TLLI */
+	if (!tbf->tlli_valid) {
+		/* no TLLI yet */
+		if (!rh->ti) {
+			LOGP(DRLCMACUL, LOGL_NOTICE, "UL DATA TBF=%d without "
+				"TLLI, but no TLLI received yet\n", rh->tfi);
+			return 0;
+		}
+		rc = tlli_from_ul_data(data, len, &tbf->tlli);
+		if (rc) {
+			LOGP(DRLCMACUL, LOGL_NOTICE, "Failed to decode TLLI "
+				"of UL DATA TBF=%d.\n", rh->tfi);
+			return 0;
+		}
+		tbf->tlli_valid = 1;
+		LOGP(DRLCMACUL, LOGL_INFO, "Decoded premier TLLI=0x%08x of "
+			"UL DATA TBF=%d.\n", tbf->tlli, rh->tfi);
+	/* already have TLLI, but we stille get another one */
+	} else if (rh->ti) {
+		uint32_t tlli;
+		rc = tlli_from_ul_data(data, len, &tlli);
+		if (rc) {
+			LOGP(DRLCMACUL, LOGL_NOTICE, "Failed to decode TLLI "
+				"of UL DATA TBF=%d.\n", rh->tfi);
+			return 0;
+		}
+		if (tlli != tbf->tlli) {
+			LOGP(DRLCMACUL, LOGL_NOTICE, "TLLI mismatch on UL "
+				"DATA TBF=%d. (Ignoring due to contention "
+				"resolution)\n", rh->tfi);
+			return 0;
+		}
+	}
+
+	mod_sns = tbf->sns - 1;
+	mod_sns_half = (tbf->sns >> 1) - 1;
+
+	/* restart T3169 */
+	tbf_timer_start(tbf, 3169, bts->t3169, 0);
+
+	/* Increment RX-counter */
+	tbf->dir.ul.rx_counter++;
+
+	/* current block relative to lowest unreceived block */
+	offset_v_q = (rh->bsn - tbf->dir.ul.v_q) & mod_sns;
+	/* If out of window (may happen if blocks below V(Q) are received
+	 * again. */
+	if (offset_v_q >= tbf->ws) {
+		LOGP(DRLCMACUL, LOGL_DEBUG, "- BSN %d out of window "
+			"%d..%d (it's normal)\n", rh->bsn, tbf->dir.ul.v_q,
+			(tbf->dir.ul.v_q + tbf->ws - 1) & mod_sns);
+		return 0;
+	}
+	/* Write block to buffer and set receive state array. */
+	index = rh->bsn & mod_sns_half; /* memory index of block */
+	memcpy(tbf->rlc_block[index], data, len); /* Copy block. */
+	tbf->rlc_block_len[index] = len;
+	tbf->dir.ul.v_n[index] = 'R'; /* Mark received block. */
+	LOGP(DRLCMACUL, LOGL_DEBUG, "- BSN %d storing in window (%d..%d)\n",
+		rh->bsn, tbf->dir.ul.v_q,
+		(tbf->dir.ul.v_q + tbf->ws - 1) & mod_sns);
+	/* Raise V(R) to highest received sequence number not received. */
+	offset_v_r = (rh->bsn + 1 - tbf->dir.ul.v_r) & mod_sns;
+	if (offset_v_r < (tbf->sns >> 1)) { /* Positive offset, so raise. */
+		while (offset_v_r--) {
+			if (offset_v_r) /* all except the received block */
+				tbf->dir.ul.v_n[tbf->dir.ul.v_r & mod_sns_half]
+					= 'N'; /* Mark block as not received */
+			tbf->dir.ul.v_r = (tbf->dir.ul.v_r + 1) & mod_sns;
+				/* Inc V(R). */
+		}
+		LOGP(DRLCMACUL, LOGL_DEBUG, "- Raising V(R) to %d\n",
+			tbf->dir.ul.v_r);
+	}
+
+	/* 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.*/
+	while (tbf->dir.ul.v_q != tbf->dir.ul.v_r && tbf->dir.ul.v_n[
+			(index = tbf->dir.ul.v_q & mod_sns_half)] == 'R') {
+		LOGP(DRLCMACUL, LOGL_DEBUG, "- Taking block %d out, raising "
+			"V(Q) to %d\n", tbf->dir.ul.v_q,
+			(tbf->dir.ul.v_q + 1) & mod_sns);
+		/* get LLC data from block */
+		gprs_rlcmac_assemble_llc(tbf, tbf->rlc_block[index],
+			tbf->rlc_block_len[index]);
+		/* raise V(Q), because block already received */
+		tbf->dir.ul.v_q = (tbf->dir.ul.v_q + 1) & mod_sns;
+	}
+
+	/* Check CV of last frame in buffer */
+	if (tbf->state == GPRS_RLCMAC_FLOW /* still in flow state */
+	 && tbf->dir.ul.v_q == tbf->dir.ul.v_r) { /* if complete */
+		struct rlc_ul_header *last_rh = (struct rlc_ul_header *)
+			tbf->rlc_block[(tbf->dir.ul.v_r - 1) & mod_sns_half];
+		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(tbf, GPRS_RLCMAC_FINISHED);
+			/* Reset N3103 counter. */
+			tbf->dir.ul.n3103 = 0;
+		}
+	}
+
+	/* If TLLI is included or if we received half of the window, we send
+	 * an ack/nack */
+	if (rh->si || rh->ti || tbf->state == GPRS_RLCMAC_FINISHED
+	 || (tbf->dir.ul.rx_counter % ACK_AFTER_FRAMES) == 0) {
+		if (rh->si) {
+			LOGP(DRLCMACUL, LOGL_DEBUG, "- Scheduling Ack/Nack, "
+				"because MS is stalled.\n");
+		}
+		if (rh->ti) {
+			LOGP(DRLCMACUL, LOGL_DEBUG, "- Scheduling Ack/Nack, "
+				"because TLLI is included.\n");
+		}
+		if (tbf->state == GPRS_RLCMAC_FINISHED) {
+			LOGP(DRLCMACUL, LOGL_DEBUG, "- Scheduling Ack/Nack, "
+				"because last block has CV==0.\n");
+		}
+		if ((tbf->dir.ul.rx_counter % ACK_AFTER_FRAMES) == 0) {
+			LOGP(DRLCMACUL, LOGL_DEBUG, "- Scheduling Ack/Nack, "
+				"because %d frames received.\n",
+				ACK_AFTER_FRAMES);
+		}
+		if (tbf->ul_ack_state == GPRS_RLCMAC_UL_ACK_NONE) {
+			/* trigger sending at next RTS */
+			tbf->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");
+		}
+	}
+
+	return 0;
+}
+
+struct msgb *gprs_rlcmac_send_packet_uplink_assignment(
+	struct gprs_rlcmac_tbf *tbf, uint32_t fn)
+{
+	struct msgb *msg;
+	struct gprs_rlcmac_tbf *new_tbf;
+
+	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;
+	}
+
+	/* on down TBF we get the uplink TBF to be assigned. */
+	if (tbf->direction == GPRS_RLCMAC_DL_TBF)
+		new_tbf = tbf_by_tlli(tbf->tlli, GPRS_RLCMAC_UL_TBF);
+	else
+		new_tbf = tbf;
+		
+	if (!new_tbf) {
+		LOGP(DRLCMACUL, LOGL_ERROR, "We have a schedule for uplink "
+			"assignment at downlink TBF=%d, but there is no uplink "
+			"TBF\n", tbf->tfi);
+		tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_NONE;
+		return NULL;
+	}
+
+	msg = msgb_alloc(23, "rlcmac_ul_ass");
+	if (!msg)
+		return NULL;
+	LOGP(DRLCMAC, LOGL_INFO, "TBF: START TFI: %u TLLI: 0x%08x Packet Uplink Assignment (PACCH)\n", new_tbf->tfi, new_tbf->tlli);
+	bitvec *ass_vec = bitvec_alloc(23);
+	if (!ass_vec) {
+		msgb_free(msg);
+		return NULL;
+	}
+	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);
+	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");
+	decode_gsm_rlcmac_downlink(ass_vec, mac_control_block);
+	LOGPC(DCSN1, LOGL_NOTICE, "\n");
+	LOGP(DRLCMAC, LOGL_DEBUG, "------------------------- TX : Packet Uplink Assignment -------------------------\n");
+	bitvec_free(ass_vec);
+
+#if POLLING_ASSIGNMENT == 1
+	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->ul_ass_state = GPRS_RLCMAC_UL_ASS_WAIT_ACK;
+#else
+	tbf->ul_ass_state = GPRS_RLCMAC_UL_ASS_NONE;
+#endif
+
+	return msg;
+}
+
+int gprs_rlcmac_rcv_rach(uint8_t ra, uint32_t Fn, int16_t qta)
+{
+	struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
+	struct gprs_rlcmac_tbf *tbf;
+	uint8_t trx, ts;
+	int tfi, usf; /* 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);
+	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");
+		/* 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);
+	LOGP(DRLCMAC, LOGL_DEBUG, "RX: [PCU <- BTS] TFI: %u RACH qbit-ta=%d ra=%d, Fn=%d (%d,%d,%d)\n", tbf->tfi, qta, ra, Fn, (Fn / (26 * 51)) % 32, Fn % 51, Fn % 26);
+	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);
+	pcu_l1if_tx_agch(immediate_assignment, plen);
+	bitvec_free(immediate_assignment);
+
+	return 0;
+}
+
+
+/*
+ * DL data block flow
+ */
+
+/* send DL data block
+ *
+ * The messages are fragmented and forwarded as data blocks.
+ */
+struct msgb *gprs_rlcmac_send_data_block_acknowledged(
+	struct gprs_rlcmac_tbf *tbf, uint32_t fn)
+{
+	struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
+	struct rlc_dl_header *rh;
+	struct rlc_li_field *li;
+	uint8_t block_length; /* total length of block, including spare bits */
+	uint8_t block_data; /* usable data of block, w/o spare bits, inc. MAC */
+	struct msgb *msg, *dl_msg;
+	uint8_t bsn;
+	uint16_t mod_sns = tbf->sns - 1;
+	uint16_t mod_sns_half = (tbf->sns >> 1) - 1;
+	uint16_t index;
+	uint8_t *delimiter, *data, *e_pointer;
+	uint8_t len;
+	uint16_t space, chunk;
+
+	LOGP(DRLCMACDL, LOGL_DEBUG, "DL DATA TBF=%d downlink (V(A)==%d .. "
+		"V(S)==%d)\n", tbf->tfi, tbf->dir.dl.v_a, tbf->dir.dl.v_s);
+
+do_resend:
+	/* check if there is a block with negative acknowledgement */
+	for (bsn = tbf->dir.dl.v_a; bsn != tbf->dir.dl.v_s; 
+	     bsn = (bsn + 1) & mod_sns) {
+		index = (bsn & mod_sns_half);
+		if (tbf->dir.dl.v_b[index] == 'N'
+		 || tbf->dir.dl.v_b[index] == 'X') {
+			LOGP(DRLCMACDL, LOGL_DEBUG, "- Resending BSN %d\n",
+				bsn);
+			/* re-send block with negative aknowlegement */
+			tbf->dir.dl.v_b[index] = 'U'; /* unacked */
+			goto tx_block;
+		}
+	}
+
+	/* if the window has stalled, or transfer is complete,
+	 * send an unacknowledged block */
+	if (tbf->state == GPRS_RLCMAC_FINISHED
+	 || ((tbf->dir.dl.v_s - tbf->dir.dl.v_a) & mod_sns) == tbf->ws) {
+	 	int resend = 0;
+
+		if (tbf->state == GPRS_RLCMAC_FINISHED)
+			LOGP(DRLCMACDL, LOGL_DEBUG, "- Restarting at BSN %d, "
+				"because all blocks have been transmitted.\n",
+					tbf->dir.dl.v_a);
+		else
+			LOGP(DRLCMACDL, LOGL_DEBUG, "- Restarting at BSN %d, "
+				"because all window is stalled.\n",
+					tbf->dir.dl.v_a);
+		/* 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 (tbf->dir.dl.v_s == tbf->dir.dl.v_a) {
+			LOGP(DRLCMACDL, LOGL_ERROR, "- MS acked all block "
+				"(including final block), but did not include "
+				"FINAL_ACK_INDICATION!\n");
+			/* we just send final block again */
+			index = ((tbf->dir.dl.v_s - 1) & mod_sns_half);
+			goto tx_block;
+		}
+		
+		/* cycle through all unacked blocks */
+		for (bsn = tbf->dir.dl.v_a; bsn != tbf->dir.dl.v_s;
+		     bsn = (bsn + 1) & mod_sns) {
+			index = (bsn & mod_sns_half);
+			if (tbf->dir.dl.v_b[index] == 'U') {
+				/* mark to be re-send */
+				tbf->dir.dl.v_b[index] = 'X';
+				resend++;
+			}
+		}
+		/* At this point there should be at leasst 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 */
+			index = ((tbf->dir.dl.v_s - 1) & mod_sns_half);
+			goto tx_block;
+		}
+		goto do_resend;
+	}
+
+	LOGP(DRLCMACDL, LOGL_DEBUG, "- Sending new block at BSN %d\n",
+		tbf->dir.dl.v_s);
+
+	/* now we still have untransmitted LLC data, so we fill mac block */
+	index = tbf->dir.dl.v_s & mod_sns_half;
+	data = tbf->rlc_block[index];
+	switch (bts->initial_cs) {
+	case 2: /* CS-2 */
+		block_length = 34;
+		block_data = 33;
+		break;
+	case 3: /* CS-3 */
+		block_length = 40;
+		block_data = 39;
+		break;
+	case 4: /* CS-4 */
+		block_length = 54;
+		block_data = 53;
+		break;
+	default: /* CS-1 */
+		block_length = 23;
+		block_data = 23;
+	}
+	memset(data, 0x2b, block_data); /* spare bits will be left 0 */
+	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 = tbf->tfi; /* TFI */
+	rh->fbi = 0; /* Final Block Indicator, set late, if true */
+	rh->bsn = tbf->dir.dl.v_s; /* Block Sequence Number */
+	rh->e = 0; /* Extension bit, maybe set later */
+	e_pointer = data + 2; /* points to E of current chunk */
+	data += 3;
+	delimiter = data; /* where next length header would be stored */
+	space = block_data - 3;
+	while (1) {
+		chunk = tbf->llc_length - tbf->llc_index;
+		/* 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 */
+			memcpy(data, tbf->llc_frame + tbf->llc_index, space);
+			/* incement index */
+			tbf->llc_index += space;
+			/* return data block as message */
+			break;
+		}
+		/* if FINAL chunk would fit precisely in space left */
+		if (chunk == space && llist_empty(&tbf->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 "
+				"TBF=%d that fits precisely in last block: "
+				"%s\n", tbf->tfi,
+				osmo_hexdump(tbf->llc_frame, tbf->llc_length));
+			/* block is filled, so there is no extension */
+			*e_pointer |= 0x01;
+			/* fill space */
+			memcpy(data, tbf->llc_frame + tbf->llc_index, space);
+			/* reset LLC frame */
+			tbf->llc_index = tbf->llc_length = 0;
+			/* final block */
+			rh->fbi = 1; /* we indicate final block */
+			tbf_new_state(tbf, 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)
+				memcpy(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 */
+			memcpy(data, tbf->llc_frame + tbf->llc_index, space);
+			/* incement index */
+			tbf->llc_index += 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)
+			memcpy(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 */
+		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));
+		/* reset LLC frame */
+		tbf->llc_index = tbf->llc_length = 0;
+		/* dequeue next LLC frame, if any */
+		msg = msgb_dequeue(&tbf->llc_queue);
+		if (msg) {
+			LOGP(DRLCMACDL, LOGL_INFO, "- Dequeue next LLC for "
+				"TBF=%d (len=%d)\n", tbf->tfi, msg->len);
+			memcpy(tbf->llc_frame, msg->data, msg->len);
+			tbf->llc_length = msg->len;
+			msgb_free(msg);
+		}
+		/* if we have more data and we have space left */
+		if (space > 0 && tbf->llc_length) {
+			li->m = 1; /* we indicate more frames to follow */
+			continue;
+		}
+		/* if we don't have more LLC frames */
+		if (!tbf->llc_length) {
+			LOGP(DRLCMACDL, LOGL_DEBUG, "-- Final block, so we "
+				"done.\n");
+			li->e = 1; /* we cannot extend */
+			rh->fbi = 1; /* we indicate final block */
+			tbf_new_state(tbf, 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(tbf->rlc_block[index], block_length));
+	tbf->rlc_block_len[index] = block_length;
+	/* raise send state and set ack state array */
+	tbf->dir.dl.v_b[index] = 'U'; /* unacked */
+	tbf->dir.dl.v_s = (tbf->dir.dl.v_s + 1) & mod_sns; /* inc send state */
+
+tx_block:
+	/* from this point on, new block is sent or old block is resent */
+
+	/* get data and header from current block */
+	data = tbf->rlc_block[index];
+	len = tbf->rlc_block_len[index];
+	rh = (struct rlc_dl_header *)data;
+
+	/* Increment TX-counter */
+	tbf->dir.dl.tx_counter++;
+
+	/* Clear Polling, if still set in history buffer */
+	rh->s_p = 0;
+		
+	/* poll after ACK_AFTER_FRAMES frames, or when final block is tx. */
+	if (rh->fbi == 1 || (tbf->dir.dl.tx_counter % ACK_AFTER_FRAMES) == 0) {
+		if (rh->fbi == 1) {
+			LOGP(DRLCMACDL, LOGL_DEBUG, "- Scheduling Ack/Nack "
+				"polling, because final block sent.\n");
+		}
+		if ((tbf->dir.dl.tx_counter % ACK_AFTER_FRAMES) == 0) {
+			LOGP(DRLCMACDL, LOGL_DEBUG, "- Scheduling Ack/Nack "
+				"polling, because %d blocks sent.\n",
+				ACK_AFTER_FRAMES);
+		}
+		if (tbf->poll_state != GPRS_RLCMAC_POLL_NONE)
+			LOGP(DRLCMACDL, LOGL_DEBUG, "Polling is already "
+				"sheduled for TBF=%d, so we must wait for "
+				"requesting downlink ack\n", tbf->tfi);
+		else  {
+			/* start timer whenever we send the final block */
+			if (rh->fbi == 1)
+				tbf_timer_start(tbf, 3191, bts->t3191, 0);
+
+			/* schedule polling */
+			tbf->poll_state = GPRS_RLCMAC_POLL_SCHED;
+			tbf->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;
+	memcpy(msgb_put(dl_msg, len), data, len);
+
+	return dl_msg;
+}
+
+int gprs_rlcmac_downlink_ack(struct gprs_rlcmac_tbf *tbf, uint8_t final,
+	uint8_t ssn, uint8_t *rbb)
+{
+	char show_rbb[65], show_v_b[RLC_MAX_SNS + 1];
+	uint16_t mod_sns = tbf->sns - 1;
+	uint16_t mod_sns_half = (tbf->sns >> 1) - 1;
+	int i; /* must be signed */
+	int16_t dist; /* must be signed */
+	uint8_t bit;
+	uint16_t bsn;
+	struct msgb *msg;
+
+	LOGP(DRLCMACDL, LOGL_DEBUG, "TBF=%d downlink acknowledge\n",
+		tbf->tfi);
+
+	if (!final) {
+		/* show received array in debug (bit 64..1) */
+		for (i = 63; i >= 0; i--) {
+			bit = (rbb[i >> 3]  >>  (7 - (i&7)))   & 1;
+			show_rbb[i] = (bit) ? '1' : 'o';
+		}
+		show_rbb[64] = '\0';
+		LOGP(DRLCMACDL, LOGL_DEBUG, "- ack:  (BSN=%d)\"%s\""
+			"(BSN=%d)  1=ACK o=NACK\n", ssn - 64, show_rbb,
+			ssn - 1);
+
+		/* apply received array to receive state (SSN-64..SSN-1) */
+		/* calculate distance of ssn from V(S) */
+		dist = (tbf->dir.dl.v_s - ssn) & mod_sns;
+		/* check if distance is less than distance V(A)..V(S) */
+		if (dist < ((tbf->dir.dl.v_s - tbf->dir.dl.v_a) & mod_sns)) {
+			/* SSN - 1 is in range V(A)..V(S)-1 */
+			for (i = 63, bsn = (ssn - 1) & mod_sns;
+			     i >= 0 && bsn != ((tbf->dir.dl.v_a - 1) & mod_sns);
+			     i--, bsn = (bsn - 1) & mod_sns) {
+				bit = (rbb[i >> 3]  >>  (7 - (i&7)))   & 1;
+				if (bit) {
+					LOGP(DRLCMACDL, LOGL_DEBUG, "- got "
+						"ack for BSN=%d\n", bsn);
+					tbf->dir.dl.v_b[bsn & mod_sns_half]
+						= 'A';
+				} else {
+					LOGP(DRLCMACDL, LOGL_DEBUG, "- got "
+						"NACK for BSN=%d\n", bsn);
+					tbf->dir.dl.v_b[bsn & mod_sns_half]
+						= 'N';
+				}
+			}
+		} else {
+			LOGP(DRLCMACDL, LOGL_DEBUG, "- ack range is out of "
+				"V(A)..V(S) range\n");
+		}
+
+		/* raise V(A), if possible */
+		for (i = 0, bsn = tbf->dir.dl.v_a; bsn != tbf->dir.dl.v_s;
+		     i++, bsn = (bsn + 1) & mod_sns) {
+			if (tbf->dir.dl.v_b[bsn & mod_sns_half] == 'A') {
+				tbf->dir.dl.v_b[bsn & mod_sns_half] = 'I';
+					/* mark invalid */
+				tbf->dir.dl.v_a = (tbf->dir.dl.v_a + 1)
+								& mod_sns;
+			} else
+				break;
+		}
+
+		/* show receive state array in debug (V(A)..V(S)-1) */
+		for (i = 0, bsn = tbf->dir.dl.v_a; bsn != tbf->dir.dl.v_s;
+		     i++, bsn = (bsn + 1) & mod_sns) {
+			show_v_b[i] = tbf->dir.dl.v_b[bsn & mod_sns_half];
+			if (show_v_b[i] == 0)
+				show_v_b[i] = ' ';
+		}
+		show_v_b[i] = '\0';
+		LOGP(DRLCMACDL, LOGL_DEBUG, "- V(B): (V(A)=%d)\"%s\""
+			"(V(S)-1=%d)  A=Acked N=Nacked U=Unacked "
+			"X=Resend-Unacked\n", tbf->dir.dl.v_a, show_v_b,
+			(tbf->dir.dl.v_s - 1) & mod_sns);
+
+		return 0;
+	}
+
+	LOGP(DRLCMACDL, LOGL_DEBUG, "- Final ACK received.\n");
+
+	/* check for LLC PDU in the LLC Queue */
+	msg = msgb_dequeue(&tbf->llc_queue);
+	if (!msg) {
+		struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
+
+		/* no message, start T3193, change state to RELEASE */
+		LOGP(DRLCMACDL, LOGL_DEBUG, "- No new message, so we "
+			"release.\n");
+		/* start T3193 */
+		tbf_timer_start(tbf, 3193, bts->t3193_msec / 1000,
+			bts->t3193_msec & 1000);
+		tbf_new_state(tbf, GPRS_RLCMAC_WAIT_RELEASE);
+
+		return 0;
+	}
+	memcpy(tbf->llc_frame, msg->data, msg->len);
+	tbf->llc_length = msg->len;
+	msgb_free(msg);
+
+	/* we have a message, so we trigger downlink assignment, and there
+	 * set the state to ASSIGN. also we set old_downlink, because we
+	 * re-use this tbf. */
+	LOGP(DRLCMAC, LOGL_DEBUG, "Trigger dowlink assignment on PACCH, "
+		"because another LLC PDU has arrived in between\n");
+	memset(&tbf->dir.dl, 0, sizeof(tbf->dir.dl)); /* reset RLC states */
+	gprs_rlcmac_trigger_downlink_assignment(tbf, 1, NULL);
+
+	return 0;
+}
+
+
+struct msgb *gprs_rlcmac_send_packet_downlink_assignment(
+	struct gprs_rlcmac_tbf *tbf, uint32_t fn)
+{
+	struct msgb *msg;
+	struct gprs_rlcmac_tbf *new_tbf;
+
+	if (tbf->poll_state != GPRS_RLCMAC_POLL_NONE) {
+		LOGP(DRLCMACDL, LOGL_DEBUG, "Polling is already "
+			"sheduled for TBF=%d, so we must wait for downlink "
+			"assignment...\n", tbf->tfi);
+			return NULL;
+	}
+
+	/* on uplink TBF we get the downlink TBF to be assigned. */
+	if (tbf->direction == GPRS_RLCMAC_UL_TBF)
+		new_tbf = tbf_by_tlli(tbf->tlli, GPRS_RLCMAC_DL_TBF);
+	else
+		new_tbf = tbf;
+	if (!new_tbf) {
+		LOGP(DRLCMACDL, LOGL_ERROR, "We have a schedule for downlink "
+			"assignment at uplink TBF=%d, but there is no downlink "
+			"TBF\n", tbf->tfi);
+		tbf->dl_ass_state = GPRS_RLCMAC_DL_ASS_NONE;
+		return NULL;
+	}
+
+	msg = msgb_alloc(23, "rlcmac_dl_ass");
+	if (!msg)
+		return NULL;
+	bitvec *ass_vec = bitvec_alloc(23);
+	if (!ass_vec) {
+		msgb_free(msg);
+		return NULL;
+	}
+	bitvec_unhex(ass_vec,
+		"2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b");
+	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,
+		POLLING_ASSIGNMENT);
+	LOGP(DRLCMAC, LOGL_DEBUG, "+++++++++++++++++++++++++ TX : Packet Downlink Assignment +++++++++++++++++++++++++\n");
+	encode_gsm_rlcmac_downlink(ass_vec, mac_control_block);
+	LOGPC(DCSN1, LOGL_NOTICE, "\n");
+	LOGP(DRLCMAC, LOGL_DEBUG, "------------------------- TX : Packet Downlink Assignment -------------------------\n");
+	bitvec_pack(ass_vec, msgb_put(msg, 23));
+	bitvec_free(ass_vec);
+	free(mac_control_block);
+
+#if POLLING_ASSIGNMENT == 1
+	tbf->poll_state = GPRS_RLCMAC_POLL_SCHED;
+	tbf->poll_fn = (fn + 13) % 2715648;
+	tbf->dl_ass_state = GPRS_RLCMAC_DL_ASS_WAIT_ACK;
+#else
+	tbf->dl_ass_state = GPRS_RLCMAC_DL_ASS_NONE;
+#endif
+
+	return msg;
+}
+
+static void gprs_rlcmac_downlink_assignment(gprs_rlcmac_tbf *tbf, uint8_t poll,
+	char *imsi)
+{
+	LOGP(DRLCMAC, LOGL_INFO, "TX: START TFI: %u TLLI: 0x%08x Immediate Assignment Downlink (PCH)\n", tbf->tfi, tbf->tlli);
+	bitvec *immediate_assignment = bitvec_alloc(22); /* without plen */
+	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);
+	pcu_l1if_tx_pch(immediate_assignment, plen, imsi);
+	bitvec_free(immediate_assignment);
+}
+
+/* depending on the current TBF, we assign on PACCH or AGCH */
+void gprs_rlcmac_trigger_downlink_assignment(gprs_rlcmac_tbf *tbf,
+	uint8_t old_downlink, char *imsi)
+{
+	gprs_rlcmac_tbf *old_tbf;
+
+#ifdef DEBUG_DL_ASS_IDLE
+	strncpy(debug_imsi, imsi);
+	LOGP(DRLCMAC, LOGL_ERROR, "**** DEBUGGING DOWNLINK ASSIGNMENT ****\n");
+#endif
+
+	/* stop pending timer */
+	tbf_timer_stop(tbf);
+
+	/* check for downlink tbf:  */
+	if (old_downlink)
+		old_tbf = tbf_by_tlli(tbf->tlli, GPRS_RLCMAC_DL_TBF);
+	else
+		old_tbf = tbf_by_tlli(tbf->tlli, GPRS_RLCMAC_UL_TBF);
+	if (old_tbf) {
+#ifdef DEBUG_DL_ASS_IDLE
+		LOGP(DRLCMAC, LOGL_ERROR, "We must wait for current TBF to be "
+			"released.\n");
+		/* wait one second until assignment */
+		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",
+			old_tbf->tfi, old_tbf->tlli);
+		old_tbf->dl_ass_state = GPRS_RLCMAC_DL_ASS_SEND_ASS;
+		/* use TA from old TBF */
+		tbf->ta = old_tbf->ta;
+		/* change state */
+		tbf_new_state(tbf, GPRS_RLCMAC_ASSIGN);
+		/* start timer */
+		tbf_timer_start(tbf, 0, Tassign_pacch);
+#endif
+	} else {
+		LOGP(DRLCMAC, LOGL_DEBUG, "Send dowlink assignment for TBF=%d on PCH, no TBF exist (IMSI=%s)\n", tbf->tfi, imsi);
+		if (!imsi || strlen(imsi) < 3) {
+			LOGP(DRLCMAC, LOGL_ERROR, "No valid IMSI!\n");
+			return;
+		}
+		/* send immediate assignment */
+		gprs_rlcmac_downlink_assignment(tbf, 0, imsi);
+		/* change state */
+		tbf_new_state(tbf, GPRS_RLCMAC_ASSIGN);
+		/* start timer */
+		tbf_timer_start(tbf, 0, Tassign_agch);
+	}
+								                }
+
diff --git a/src/gprs_rlcmac_sched.cpp b/src/gprs_rlcmac_sched.cpp
new file mode 100644
index 0000000..4a4b85c
--- /dev/null
+++ b/src/gprs_rlcmac_sched.cpp
@@ -0,0 +1,184 @@
+/* PDCH scheduler
+ *
+ * Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
+ *
+ * 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 <gprs_bssgp_pcu.h>
+#include <gprs_rlcmac.h>
+#include <pcu_l1_if.h>
+
+extern struct llist_head block_queue;
+
+static uint8_t rlcmac_dl_idle[23] = {
+	0x47, /* control without optional header octets, no polling, USF=111 */
+	0x94, /* dummy downlink control message, paging mode 00 */
+	0x2b, /* no persistance level, 7 bits spare pattern */
+	0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b,
+	0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b, 0x2b
+};
+
+int gprs_rlcmac_rcv_rts_block(uint8_t trx, uint8_t ts, uint16_t arfcn,
+        uint32_t fn, uint8_t block_nr)
+{
+	struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
+	struct gprs_rlcmac_pdch *pdch;
+	struct gprs_rlcmac_tbf *tbf;
+	uint8_t usf = 0x7;
+	struct msgb *msg = NULL;
+	uint32_t poll_fn;
+	uint8_t i, tfi;
+
+	if (trx >= 8 || ts >= 8)
+		return -EINVAL;
+	pdch = &bts->trx[trx].pdch[ts];
+
+	if (!pdch->enable) {
+		LOGP(DRLCMACSCHED, LOGL_ERROR, "Received RTS on disabled PDCH: "
+			"TRX=%d TS=%d\n", trx, ts);
+		return -EIO;
+	}
+
+	/* store last frame number of RTS */
+	pdch->last_rts_fn = fn;
+
+	/* check uplink ressource for polling */
+	poll_fn = fn + 4;
+	if ((block_nr % 3) == 2)
+		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;
+	}
+	/* 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);
+		/* 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];
+			/* 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
+			 * for packet control ack. */
+			if (tbf->state != GPRS_RLCMAC_FLOW)
+				continue;
+
+			/* use this USF */
+			usf = tbf->dir.ul.usf;
+			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);
+			/* next TBF to handle ressource is the next one */
+			pdch->next_ul_tfi = (tfi + 1) & 31;
+			break;
+		}
+	}
+
+	/* Prio 1: select control message */
+	for (tfi = 0; tfi < 32; tfi++) {
+		tbf = pdch->tbf[tfi];
+		/* no TBF for this tfi, go next */
+		if (!tbf)
+			continue;
+		/* schedule PACKET DOWNLINK ASSIGNMENT */
+		if (tbf->dl_ass_state == GPRS_RLCMAC_DL_ASS_SEND_ASS)
+			msg = gprs_rlcmac_send_packet_downlink_assignment(tbf,
+				fn);
+		else
+		/* schedule PACKET UPLINK ASSIGNMENT */
+		if (tbf->ul_ass_state == GPRS_RLCMAC_UL_ASS_SEND_ASS)
+			msg = gprs_rlcmac_send_packet_uplink_assignment(tbf,
+				fn);
+		else
+		/* schedule PACKET UPLINK ACK */
+		if (tbf->ul_ack_state == GPRS_RLCMAC_UL_ACK_SEND_ACK)
+			msg = gprs_rlcmac_send_uplink_ack(tbf, fn);
+		if (msg) {
+			LOGP(DRLCMACSCHED, LOGL_DEBUG, "Scheduling control "
+				"message at RTS for TBF=%d\n", tfi);
+			break;
+		}
+	}
+
+	/* Prio 2: select data message for downlink */
+	if (!msg) {
+		/* select downlink ressource */
+		for (i = 0, tfi = pdch->next_dl_tfi; i < 32;
+		     i++, tfi = (tfi + 1) & 31) {
+			tbf = pdch->tbf[tfi];
+			/* no TBF for this tfi, go next */
+			if (!tbf)
+				continue;
+			/* no DL TBF, go next */
+			if (tbf->direction != GPRS_RLCMAC_DL_TBF)
+				continue;
+			/* no DL ressources needed, go next */
+			if (tbf->state != GPRS_RLCMAC_FLOW
+			 && tbf->state != GPRS_RLCMAC_FINISHED)
+				continue;
+
+			LOGP(DRLCMACSCHED, LOGL_DEBUG, "Scheduling data "
+				"message at RTS for TBF=%d\n", tfi);
+			/* next TBF to handle ressource is the next one */
+			pdch->next_dl_tfi = (tfi + 1) & 31;
+			/* generate DL data block */
+			msg = gprs_rlcmac_send_data_block_acknowledged(tbf, fn);
+			break;
+		}
+	}
+
+	/* Prio 3: send dummy contol message */
+	if (!msg) {
+		msg = msgb_alloc(23, "rlcmac_dl_idle");
+		if (!msg)
+			return -ENOMEM;
+		memcpy(msgb_put(msg, 23), rlcmac_dl_idle, 23);
+	}
+	/* msg is now available */
+
+	/* set USF */
+	msg->data[0] = (msg->data[0] & 0xf8) | usf;
+
+//	printf("len=%d, date=%s\n", msg->len, osmo_hexdump(msg->data, msg->len));
+
+	/* send PDTCH/PACCH to L1 */
+	pcu_l1if_tx_pdtch(msg, trx, ts, arfcn, fn, block_nr);
+
+	return 0;
+}
diff --git a/src/gsmL1prim.h b/src/gsmL1prim.h
deleted file mode 100644
index b348a1a..0000000
--- a/src/gsmL1prim.h
+++ /dev/null
@@ -1,415 +0,0 @@
-/*
-* Copyright 2012 Thomas Cooper <tacooper@vt.edu>
-*
-* This software is distributed under the terms of the GNU Affero Public License.
-* See the COPYING file in the main directory for details.
-*
-* This use of this software may be subject to additional restrictions.
-* See the LEGAL file in the main directory for details.
-
-This program is free software: you can redistribute it and/or modify
-it under the terms of the GNU Affero General Public License as published by
-the Free Software Foundation, either version 3 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 Affero General Public License for more details.
-
-You should have received a copy of the GNU Affero General Public License
-along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-*/
-
-#ifndef GSML1PRIM_H
-#define GSML1PRIM_H
-
-#include <stdint.h>
-
-typedef enum GsmL1_SubCh_t {
-GsmL1_SubCh_NA,
-} GsmL1_SubCh_t;
-
-typedef enum GsmL1_Sapi_t {
-GsmL1_Sapi_Fcch,
-GsmL1_Sapi_Sch,
-GsmL1_Sapi_Sacch,
-GsmL1_Sapi_Sdcch,
-GsmL1_Sapi_Bcch,
-GsmL1_Sapi_Pch,
-GsmL1_Sapi_Agch,
-GsmL1_Sapi_Cbch,
-GsmL1_Sapi_Rach,
-GsmL1_Sapi_TchF,
-GsmL1_Sapi_FacchF,
-GsmL1_Sapi_TchH,
-GsmL1_Sapi_FacchH,
-GsmL1_Sapi_Nch,
-GsmL1_Sapi_Pdtch,
-GsmL1_Sapi_Pacch,
-GsmL1_Sapi_Pbcch,
-GsmL1_Sapi_Pagch,
-GsmL1_Sapi_Ppch,
-GsmL1_Sapi_Pnch,
-GsmL1_Sapi_Ptcch,
-GsmL1_Sapi_Prach,
-GsmL1_Sapi_Idle,
-GsmL1_Sapi_NUM,
-} GsmL1_Sapi_t;
-
-typedef enum GsmL1_Status_t {
-GsmL1_Status_Success,
-GsmL1_Status_Generic,
-GsmL1_Status_NoMemory,
-GsmL1_Status_Timeout,
-GsmL1_Status_InvalidParam,
-GsmL1_Status_Busy,
-GsmL1_Status_NoRessource,
-GsmL1_Status_Uninitialized,
-GsmL1_Status_NullInterface,
-GsmL1_Status_NullFctnPtr,
-GsmL1_Status_BadCrc,
-GsmL1_Status_BadUsf,
-GsmL1_Status_InvalidCPS,
-GsmL1_Status_UnexpectedBurst,
-GsmL1_Status_UnavailCodec,
-GsmL1_Status_CriticalError,
-GsmL1_Status_OverheatError,
-GsmL1_Status_DeviceError,
-GsmL1_Status_FacchError,
-GsmL1_Status_AlreadyDeactivated,
-GsmL1_Status_TxBurstFifoOvrn,
-GsmL1_Status_TxBurstFifoUndr,
-GsmL1_Status_NotSynchronized,
-GsmL1_Status_Unsupported,
-GSML1_STATUS_NUM,
-} GsmL1_Status_t;
-
-typedef enum GsmL1_PrimId_t {
-GsmL1_PrimId_MphInitReq,
-GsmL1_PrimId_MphCloseReq,
-GsmL1_PrimId_MphConnectReq,
-GsmL1_PrimId_MphDisconnectReq,
-GsmL1_PrimId_MphActivateReq,
-GsmL1_PrimId_MphDeactivateReq,
-GsmL1_PrimId_MphConfigReq,
-GsmL1_PrimId_MphMeasureReq,
-GsmL1_PrimId_MphInitCnf,
-GsmL1_PrimId_MphCloseCnf,
-GsmL1_PrimId_MphConnectCnf,
-GsmL1_PrimId_MphDisconnectCnf,
-GsmL1_PrimId_MphActivateCnf,
-GsmL1_PrimId_MphDeactivateCnf,
-GsmL1_PrimId_MphConfigCnf,
-GsmL1_PrimId_MphMeasureCnf,
-GsmL1_PrimId_MphTimeInd,
-GsmL1_PrimId_MphSyncInd,
-GsmL1_PrimId_PhEmptyFrameReq,
-GsmL1_PrimId_PhDataReq,
-GsmL1_PrimId_PhConnectInd,
-GsmL1_PrimId_PhReadyToSendInd,
-GsmL1_PrimId_PhDataInd,
-GsmL1_PrimId_PhRaInd,
-GsmL1_PrimId_NUM,
-} GsmL1_PrimId_t;
-
-typedef enum GsmL1_Dir_t {
-GsmL1_Dir_TxDownlink,
-GsmL1_Dir_RxUplink,
-} GsmL1_Dir_t;
-
-typedef enum GsmL1_DevType_t {
-GsmL1_DevType_TxdRxu,
-} GsmL1_DevType_t;
-
-typedef enum GsmL1_TchPlType_t {
-GsmL1_TchPlType_NA,
-GsmL1_TchPlType_Efr,
-GsmL1_TchPlType_Fr,
-GsmL1_TchPlType_Hr,
-GsmL1_TchPlType_Amr,
-GsmL1_TchPlType_Amr_SidBad,
-GsmL1_TchPlType_Amr_Onset,
-GsmL1_TchPlType_Amr_Ratscch,
-GsmL1_TchPlType_Amr_SidUpdateInH,
-GsmL1_TchPlType_Amr_SidFirstP1,
-GsmL1_TchPlType_Amr_SidFirstP2,
-GsmL1_TchPlType_Amr_SidFirstInH,
-GsmL1_TchPlType_Amr_RatscchMarker,
-GsmL1_TchPlType_Amr_RatscchData,
-} GsmL1_TchPlType_t;
-
-typedef enum GsmL1_ConfigParamId_t {
-GsmL1_ConfigParamId_SetNbTsc,
-GsmL1_ConfigParamId_SetTxPowerLevel,
-GsmL1_ConfigParamId_SetLogChParams,
-GsmL1_ConfigParamId_SetCipheringParams,
-} GsmL1_ConfigParamId_t;
-
-typedef struct GsmL1_DeviceParam_t {
-enum GsmL1_DevType_t devType;
-int freqBand;
-uint16_t u16Arfcn;
-uint16_t u16BcchArfcn;
-uint8_t u8NbTsc;
-uint8_t u8Ncc;
-float fRxPowerLevel;
-float fTxPowerLevel;
-} GsmL1_DeviceParam_t;
-
-typedef struct GsmL1_MsgUnitParam_t {
-uint8_t u8Buffer[256];
-uint8_t u8Size;
-} GsmL1_MsgUnitParam_t;
-
-typedef struct GsmL1_MeasParam_t {
-float fRssi;
-float fLinkQuality;
-float fBer;
-int16_t i16BurstTiming;
-} GsmL1_MeasParam_t;
-
-typedef struct GsmL1_LogChParam_t {
-union {
-struct {
-enum GsmL1_TchPlType_t tchPlType;
-        
-enum {
-GsmL1_AmrCmiPhase_NA,
-GsmL1_AmrCmiPhase_Odd,
-} amrCmiPhase;
-        
-enum {
-GsmL1_AmrCodecMode_Unset,
-} amrInitCodecMode;
-
-enum {
-GsmL1_AmrCodec_Unset,
-GsmL1_AmrCodec_4_75,
-GsmL1_AmrCodec_5_15,
-GsmL1_AmrCodec_5_9,
-GsmL1_AmrCodec_6_7,
-GsmL1_AmrCodec_7_4,
-GsmL1_AmrCodec_7_95,
-GsmL1_AmrCodec_10_2,
-GsmL1_AmrCodec_12_2,
-} amrActiveCodecSet[8];
-} tch;
-
-struct {
-uint8_t u8Bsic;
-uint8_t u8NbrOfAgch;
-} rach;
-
-struct {
-uint8_t u8MsPowerLevel;
-} sacch;
-
-struct {
-uint8_t u8NbrOfAgch;
-} agch;
-};
-} GsmL1_LogChParam_t;
-
-typedef enum GsmL1_LogChComb_t {
-GsmL1_LogChComb_0,
-GsmL1_LogChComb_I,
-GsmL1_LogChComb_II,
-GsmL1_LogChComb_IV,
-GsmL1_LogChComb_V,
-GsmL1_LogChComb_VII,
-GsmL1_LogChComb_XIII,
-} GsmL1_LogChComb_t;
-
-enum {
-GsmL1_FreqBand_850,
-GsmL1_FreqBand_900,
-GsmL1_FreqBand_1800,
-GsmL1_FreqBand_1900,
-};
-
-typedef struct GsmL1_MphInitReq_t {
-struct GsmL1_DeviceParam_t deviceParam;
-} GsmL1_MphInitReq_t;
-
-typedef struct GsmL1_MphCloseReq_t {
-uint32_t hLayer1;
-} GsmL1_MphCloseReq_t;
-
-typedef struct GsmL1_MphConnectReq_t {
-uint32_t hLayer1;
-uint8_t u8Tn;
-enum GsmL1_LogChComb_t logChComb;
-} GsmL1_MphConnectReq_t;
-
-typedef struct GsmL1_MphDisconnectReq_t {
-uint32_t hLayer1;
-} GsmL1_MphDisconnectReq_t;
-
-typedef struct GsmL1_MphActivateReq_t {
-uint32_t hLayer1;
-struct GsmL1_LogChParam_t logChPrm;
-uint8_t u8Tn;
-enum GsmL1_SubCh_t subCh;
-enum GsmL1_Dir_t dir;
-enum GsmL1_Sapi_t sapi;
-uint32_t hLayer2;
-float fBFILevel;
-} GsmL1_MphActivateReq_t;
-
-typedef struct GsmL1_MphDeactivateReq_t {
-uint32_t hLayer1;
-uint8_t u8Tn;
-enum GsmL1_SubCh_t subCh;
-enum GsmL1_Dir_t dir;
-enum GsmL1_Sapi_t sapi;
-} GsmL1_MphDeactivateReq_t;
-
-typedef struct GsmL1_ConfigParam_t {
-struct {
-enum GsmL1_Sapi_t sapi;
-uint8_t u8Tn;
-enum GsmL1_SubCh_t subCh;
-enum GsmL1_Dir_t dir;
-struct GsmL1_LogChParam_t logChParams;
-} setLogChParams;
-} GsmL1_ConfigParam_t;
-
-typedef struct GsmL1_MphConfigReq_t {
-uint32_t hLayer1;
-enum GsmL1_ConfigParamId_t cfgParamId;
-struct GsmL1_ConfigParam_t cfgParams;
-} GsmL1_MphConfigReq_t;
-
-typedef struct GsmL1_MphConfigCnf_t {
-enum GsmL1_Status_t status;
-enum GsmL1_ConfigParamId_t cfgParamId;
-struct GsmL1_ConfigParam_t cfgParams;
-} GsmL1_MphConfigCnf_t;
-
-typedef struct GsmL1_MphMeasureReq_t {
-uint32_t hLayer1;
-} GsmL1_MphMeasureReq_t;
-
-typedef struct GsmL1_MphInitCnf_t {
-uint32_t hLayer1;
-enum GsmL1_Status_t status;
-} GsmL1_MphInitCnf_t;
-
-typedef struct GsmL1_MphCloseCnf_t {
-enum GsmL1_Status_t status;
-} GsmL1_MphCloseCnf_t;
-
-typedef struct GsmL1_MphConnectCnf_t {
-enum GsmL1_Status_t status;
-} GsmL1_MphConnectCnf_t;
-
-typedef struct GsmL1_MphDisconnectCnf_t {
-enum GsmL1_Status_t status;
-} GsmL1_MphDisconnectCnf_t;
-
-typedef struct GsmL1_MphActivateCnf_t {
-enum GsmL1_Status_t status;
-uint8_t u8Tn;
-int sapi;
-} GsmL1_MphActivateCnf_t;
-
-typedef struct GsmL1_MphDeactivateCnf_t {
-enum GsmL1_Status_t status;
-uint8_t u8Tn;
-enum GsmL1_Sapi_t sapi;
-} GsmL1_MphDeactivateCnf_t;
-
-typedef struct GsmL1_MphMeasureCnf_t {
-enum GsmL1_Status_t status;
-} GsmL1_MphMeasureCnf_t;
-
-typedef struct GsmL1_MphTimeInd_t {
-uint32_t u32Fn;
-} GsmL1_MphTimeInd_t;
-
-typedef struct GsmL1_MphSyncInd_t {
-} GsmL1_MphSyncInd_t;
-
-typedef struct GsmL1_PhEmptyFrameReq_t {
-uint32_t hLayer1;
-uint8_t u8Tn;
-uint32_t u32Fn;
-enum GsmL1_Sapi_t sapi;
-enum GsmL1_SubCh_t subCh;
-uint8_t u8BlockNbr;
-} GsmL1_PhEmptyFrameReq_t;
-
-typedef struct GsmL1_PhDataReq_t {
-uint32_t hLayer1;
-uint8_t u8Tn;
-uint32_t u32Fn;
-enum GsmL1_Sapi_t sapi;
-enum GsmL1_SubCh_t subCh;
-uint8_t u8BlockNbr;
-struct GsmL1_MsgUnitParam_t msgUnitParam;
-} GsmL1_PhDataReq_t;
-
-typedef struct GsmL1_PhConnectInd_t {
-uint8_t u8Tn;
-uint8_t u8Tsc;
-uint16_t u16Arfcn;
-} GsmL1_PhConnectInd_t;
-
-typedef struct GsmL1_PhReadyToSendInd_t {
-uint32_t hLayer1;
-uint8_t u8Tn;
-uint32_t u32Fn;
-enum GsmL1_Sapi_t sapi;
-enum GsmL1_SubCh_t subCh;
-uint8_t u8BlockNbr;
-uint32_t hLayer2;
-} GsmL1_PhReadyToSendInd_t;
-
-typedef struct GsmL1_PhDataInd_t {
-struct GsmL1_MeasParam_t measParam;
-struct GsmL1_MsgUnitParam_t msgUnitParam;
-enum GsmL1_Sapi_t sapi;
-uint32_t hLayer2;
-} GsmL1_PhDataInd_t;
-
-typedef struct GsmL1_PhRaInd_t {
-struct GsmL1_MeasParam_t measParam;
-struct GsmL1_MsgUnitParam_t msgUnitParam;
-uint32_t u32Fn;
-uint32_t hLayer2;
-} GsmL1_PhRaInd_t;
-
-typedef struct GsmL1_Prim_t {
-union {
-struct GsmL1_MphInitReq_t mphInitReq;
-struct GsmL1_MphCloseReq_t mphCloseReq;
-struct GsmL1_MphConnectReq_t mphConnectReq;
-struct GsmL1_MphDisconnectReq_t mphDisconnectReq;
-struct GsmL1_MphActivateReq_t mphActivateReq;
-struct GsmL1_MphDeactivateReq_t mphDeactivateReq;
-struct GsmL1_MphConfigReq_t mphConfigReq;
-struct GsmL1_MphMeasureReq_t mphMeasureReq;
-struct GsmL1_MphInitCnf_t mphInitCnf;
-struct GsmL1_MphCloseCnf_t mphCloseCnf;
-struct GsmL1_MphConnectCnf_t mphConnectCnf;
-struct GsmL1_MphDisconnectCnf_t mphDisconnectCnf;
-struct GsmL1_MphActivateCnf_t mphActivateCnf;
-struct GsmL1_MphDeactivateCnf_t mphDeactivateCnf;
-struct GsmL1_MphConfigCnf_t mphConfigCnf;
-struct GsmL1_MphMeasureCnf_t mphMeasureCnf;
-struct GsmL1_MphTimeInd_t mphTimeInd;
-struct GsmL1_MphSyncInd_t mphSyncInd;
-struct GsmL1_PhEmptyFrameReq_t phEmptyFrameReq;
-struct GsmL1_PhDataReq_t phDataReq;
-struct GsmL1_PhConnectInd_t phConnectInd;
-struct GsmL1_PhReadyToSendInd_t phReadyToSendInd;
-struct GsmL1_PhDataInd_t phDataInd;
-struct GsmL1_PhRaInd_t phRaInd;
-} u;
-
-enum GsmL1_PrimId_t id;
-} GsmL1_Prim_t;
-
-#endif
diff --git a/src/openbts_sock.cpp b/src/openbts_sock.cpp
new file mode 100644
index 0000000..cbf7adc
--- /dev/null
+++ b/src/openbts_sock.cpp
@@ -0,0 +1,183 @@
+/* openbts_sock.cpp
+ *
+ * Copyright (C) 2012 Ivan Klyuchnikov
+ *
+ * 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 <errno.h>
+#include <string.h>
+#include <gprs_rlcmac.h>
+#include <gprs_bssgp_pcu.h>
+#include <pcu_l1_if.h>
+#include <gprs_debug.h>
+#include <bitvector.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+extern "C" {
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/write_queue.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/core/timer.h>
+#include <osmocom/gsm/gsm_utils.h>
+#include <pcuif_proto.h>
+}
+
+struct femtol1_hdl {
+	struct gsm_time gsm_time;
+	uint32_t hLayer1;			/* handle to the L1 instance in the DSP */
+	uint32_t dsp_trace_f;
+	uint16_t clk_cal;
+	struct llist_head wlc_list;
+
+	void *priv;			/* user reference */
+
+	struct osmo_timer_list alive_timer;
+	unsigned int alive_prim_cnt;
+
+	struct osmo_fd read_ofd;	/* osmo file descriptors */
+	struct osmo_wqueue write_q;
+
+	struct {
+		uint16_t arfcn;
+		uint8_t tn;
+		uint8_t tsc;
+		uint16_t ta;
+	} channel_info;
+
+};
+
+struct l1fwd_hdl {
+	struct sockaddr_storage remote_sa;
+	socklen_t remote_sa_len;
+
+	struct osmo_wqueue udp_wq;
+
+	struct femtol1_hdl *fl1h;
+};
+
+struct l1fwd_hdl *l1fh = talloc_zero(NULL, struct l1fwd_hdl);
+
+// TODO: We should move this parameters to config file.
+#define PCU_L1_IF_PORT 5944
+
+/* OpenBTS socket functions */
+
+int pcu_sock_send(struct msgb *msg)
+{
+	osmo_wqueue_enqueue(&l1fh->udp_wq, msg);
+	return 0;
+}
+
+/* data has arrived on the udp socket */
+static int udp_read_cb(struct osmo_fd *ofd)
+{
+	struct msgb *msg = msgb_alloc_headroom(2048, 128, "udp_rx");
+	struct l1fwd_hdl *l1fh = (l1fwd_hdl *)ofd->data;
+	struct femtol1_hdl *fl1h = l1fh->fl1h;
+	int rc;
+
+	if (!msg)
+		return -ENOMEM;
+
+	msg->l1h = msg->data;
+
+	l1fh->remote_sa_len = sizeof(l1fh->remote_sa);
+	rc = recvfrom(ofd->fd, msg->l1h, msgb_tailroom(msg), 0,
+			(struct sockaddr *) &l1fh->remote_sa, &l1fh->remote_sa_len);
+	if (rc < 0) {
+		perror("read from udp");
+		msgb_free(msg);
+		return rc;
+	} else if (rc == 0) {
+		perror("len=0 read from udp");
+		msgb_free(msg);
+		return rc;
+	}
+	msgb_put(msg, rc);
+
+	struct gsm_pcu_if *pcu_prim = (gsm_pcu_if *)(msg->l1h);
+	rc = pcu_rx(pcu_prim->msg_type, pcu_prim);
+	return rc;
+}
+
+/* callback when we can write to the UDP socket */
+static int udp_write_cb(struct osmo_fd *ofd, struct msgb *msg)
+{
+	int rc;
+	struct l1fwd_hdl *l1fh = (l1fwd_hdl *)ofd->data;
+
+	//LOGP(DPCU, LOGL_ERROR, "UDP: Writing %u bytes for MQ_L1_WRITE queue\n", msgb_length(msg));
+
+	rc = sendto(ofd->fd, msgb_data(msg), msgb_length(msg), 0,
+			(const struct sockaddr *)&l1fh->remote_sa, l1fh->remote_sa_len);
+	if (rc < 0) {
+		LOGP(DPCU, LOGL_ERROR, "error writing to L1 msg_queue: %s\n",
+			strerror(errno));
+		return rc;
+	} else if (rc < (int)msgb_length(msg)) {
+		LOGP(DPCU, LOGL_ERROR, "short write to L1 msg_queue: "
+			"%u < %u\n", rc, msgb_length(msg));
+		return -EIO;
+	}
+
+	return 0;
+}
+
+int pcu_l1if_open()
+{
+	struct femtol1_hdl *fl1h;
+	int rc;
+
+	/* allocate new femtol1_handle */
+	fl1h = talloc_zero(NULL, struct femtol1_hdl);
+	INIT_LLIST_HEAD(&fl1h->wlc_list);
+
+	l1fh->fl1h = fl1h;
+	fl1h->priv = l1fh;
+
+	struct osmo_wqueue * queue = &((l1fh->fl1h)->write_q);
+	osmo_wqueue_init(queue, 10);
+	queue->bfd.when |= BSC_FD_READ;
+	queue->bfd.data = l1fh;
+	queue->bfd.priv_nr = 0;
+
+	/* Open UDP */
+	struct osmo_wqueue *wq = &l1fh->udp_wq;
+
+	osmo_wqueue_init(wq, 10);
+	wq->write_cb = udp_write_cb;
+	wq->read_cb = udp_read_cb;
+	wq->bfd.when |= BSC_FD_READ;
+	wq->bfd.data = l1fh;
+	wq->bfd.priv_nr = 0;
+	rc = osmo_sock_init_ofd(&wq->bfd, AF_UNSPEC, SOCK_DGRAM,
+				IPPROTO_UDP, NULL, PCU_L1_IF_PORT,
+				OSMO_SOCK_F_BIND);
+	if (rc < 0) {
+		perror("sock_init");
+		exit(1);
+	}
+
+	return 0;
+}
+
+void pcu_l1if_close(void)
+{
+	gprs_bssgp_destroy();
+
+	/* FIXME: cleanup l1if */
+	talloc_free(l1fh->fl1h);
+}
diff --git a/src/pcu_l1_if.cpp b/src/pcu_l1_if.cpp
index 4186e29..fd8b3c3 100644
--- a/src/pcu_l1_if.cpp
+++ b/src/pcu_l1_if.cpp
@@ -1,6 +1,6 @@
 /* pcu_l1_if.cpp
  *
- * Copyright (C) 2012 Ivan Klyuchnikov
+ * Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -17,63 +17,25 @@
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  */
 
-#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
 #include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+extern "C" {
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/msgb.h>
+}
+
 #include <gprs_rlcmac.h>
 #include <pcu_l1_if.h>
 #include <gprs_debug.h>
-#include <bitvector.h>
-#include <gsmL1prim.h>
-#include <sys/socket.h>
-#include <linux/in.h>
-extern "C" {
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/write_queue.h>
-#include <osmocom/core/socket.h>
-#include <osmocom/core/timer.h>
-#include <osmocom/gsm/gsm_utils.h>
-}
-
-#define MAX_UDP_LENGTH 1500
-
-#define msgb_l1prim(msg)	((GsmL1_Prim_t *)(msg)->l1h)
-
-struct femtol1_hdl {
-	struct gsm_time gsm_time;
-	uint32_t hLayer1;			/* handle to the L1 instance in the DSP */
-	uint32_t dsp_trace_f;
-	uint16_t clk_cal;
-	struct llist_head wlc_list;
-
-	void *priv;			/* user reference */
-
-	struct osmo_timer_list alive_timer;
-	unsigned int alive_prim_cnt;
-
-	struct osmo_fd read_ofd;	/* osmo file descriptors */
-	struct osmo_wqueue write_q;
-
-	struct {
-		uint16_t arfcn;
-		uint8_t tn;
-		uint8_t tsc;
-		uint16_t ta;
-	} channel_info;
-
-};
-
-struct l1fwd_hdl {
-	struct sockaddr_storage remote_sa;
-	socklen_t remote_sa_len;
-
-	struct osmo_wqueue udp_wq;
-
-	struct femtol1_hdl *fl1h;
-};
-
-struct l1fwd_hdl *l1fh = talloc_zero(NULL, struct l1fwd_hdl);
-
-struct pcu_l1if_bts pcu_l1if_bts;
+#include <gprs_bssgp_pcu.h>
+#include <pcuif_proto.h>
 
 // Variable for storage current FN.
 int frame_number;
@@ -88,244 +50,398 @@
 	frame_number = fn;
 }
 
-struct msgb *l1p_msgb_alloc(void)
-{
-	struct msgb *msg = msgb_alloc(sizeof(GsmL1_Prim_t), "l1_prim");
+/*
+ * PCU messages
+ */
 
-	if (msg)
-		msg->l1h = msgb_put(msg, sizeof(GsmL1_Prim_t));
+struct msgb *pcu_msgb_alloc(uint8_t msg_type, uint8_t bts_nr)
+{
+	struct msgb *msg;
+	struct gsm_pcu_if *pcu_prim;
+
+	msg = msgb_alloc(sizeof(struct gsm_pcu_if), "pcu_sock_tx");
+	if (!msg)
+		return NULL;
+	msgb_put(msg, sizeof(struct gsm_pcu_if));
+	pcu_prim = (struct gsm_pcu_if *) msg->data;
+	pcu_prim->msg_type = msg_type;
+	pcu_prim->bts_nr = bts_nr;
 
 	return msg;
 }
 
-// Send RLC/MAC block to OpenBTS.
-void pcu_l1if_tx_pdtch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn,
-        uint32_t fn, uint8_t block_nr)
+static int pcu_tx_act_req(uint8_t trx, uint8_t ts, uint8_t activate)
 {
-	struct msgb *nmsg = l1p_msgb_alloc();
-	GsmL1_Prim_t *prim = msgb_l1prim(nmsg);
-	
-	prim->id = GsmL1_PrimId_PhDataReq;
-	prim->u.phDataReq.sapi = GsmL1_Sapi_Pdtch;
-	memcpy(prim->u.phDataReq.msgUnitParam.u8Buffer, msg->data, msg->len);
-	prim->u.phDataReq.msgUnitParam.u8Size = msg->len;
-	osmo_wqueue_enqueue(&l1fh->udp_wq, nmsg);
+	struct msgb *msg;
+	struct gsm_pcu_if *pcu_prim;
+	struct gsm_pcu_if_act_req *act_req;
+
+	LOGP(DL1IF, LOGL_INFO, "Sending %s request: trx=%d ts=%d\n",
+		(activate) ? "activate" : "deactivate", trx, ts);
+
+	msg = pcu_msgb_alloc(PCU_IF_MSG_ACT_REQ, 0);
+	if (!msg)
+		return -ENOMEM;
+	pcu_prim = (struct gsm_pcu_if *) msg->data;
+	act_req = &pcu_prim->u.act_req;
+	act_req->activate = activate;
+	act_req->trx_nr = trx;
+	act_req->ts_nr = ts;
+
+	return pcu_sock_send(msg);
+}
+
+static int pcu_tx_data_req(uint8_t trx, uint8_t ts, uint8_t sapi,
+	uint16_t arfcn, uint32_t fn, uint8_t block_nr, uint8_t *data,
+	uint8_t len)
+{
+	struct msgb *msg;
+	struct gsm_pcu_if *pcu_prim;
+	struct gsm_pcu_if_data *data_req;
+
+	LOGP(DL1IF, LOGL_DEBUG, "Sending data request: trx=%d ts=%d sapi=%d "
+		"arfcn=%d fn=%d block=%d data=%s\n", trx, ts, sapi, arfcn, fn,
+		block_nr, osmo_hexdump(data, len));
+
+	msg = pcu_msgb_alloc(PCU_IF_MSG_DATA_REQ, 0);
+	if (!msg)
+		return -ENOMEM;
+	pcu_prim = (struct gsm_pcu_if *) msg->data;
+	data_req = &pcu_prim->u.data_req;
+
+	data_req->sapi = sapi;
+	data_req->fn = fn;
+	data_req->arfcn = arfcn;
+	data_req->trx_nr = trx;
+	data_req->ts_nr = ts;
+	data_req->block_nr = block_nr;
+	memcpy(data_req->data, data, len);
+	data_req->len = len;
+
+	return pcu_sock_send(msg);
+}
+
+void pcu_l1if_tx_pdtch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn,
+	uint32_t fn, uint8_t block_nr)
+{
+	pcu_tx_data_req(trx, ts, PCU_IF_SAPI_PDTCH, arfcn, fn, block_nr,
+		msg->data, msg->len);
 	msgb_free(msg);
 }
 
-void pcu_l1if_tx_agch(bitvec * block, int len)
+void pcu_l1if_tx_ptcch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn,
+	uint32_t fn, uint8_t block_nr)
 {
-	struct msgb *msg = l1p_msgb_alloc();
-	GsmL1_Prim_t *prim = msgb_l1prim(msg);
+	pcu_tx_data_req(trx, ts, PCU_IF_SAPI_PTCCH, arfcn, fn, block_nr,
+		msg->data, msg->len);
+	msgb_free(msg);
+}
+
+void pcu_l1if_tx_agch(bitvec * block, int plen)
+{
+	uint8_t data[23]; /* prefix PLEN */
 	
-	prim->id = GsmL1_PrimId_PhDataReq;
-	prim->u.phDataReq.sapi = GsmL1_Sapi_Agch;
-	bitvec_pack(block, prim->u.phDataReq.msgUnitParam.u8Buffer);
-	prim->u.phDataReq.msgUnitParam.u8Size = len;
-	osmo_wqueue_enqueue(&l1fh->udp_wq, msg);
+	/* FIXME: why does OpenBTS has no PLEN and no fill in message? */
+	bitvec_pack(block, data + 1);
+	data[0] = (plen << 2) | 0x01;
+	pcu_tx_data_req(0, 0, PCU_IF_SAPI_AGCH, 0, 0, 0, data, 23);
 }
 
-void pcu_l1if_tx_pch(bitvec * block, int len)
+void pcu_l1if_tx_pch(bitvec * block, int plen, char *imsi)
 {
-	struct msgb *msg = l1p_msgb_alloc();
-	GsmL1_Prim_t *prim = msgb_l1prim(msg);
-	prim->id = GsmL1_PrimId_PhDataReq;
-	prim->u.phDataReq.sapi = GsmL1_Sapi_Pch;
-	bitvec_pack(block, prim->u.phDataReq.msgUnitParam.u8Buffer);
-	prim->u.phDataReq.msgUnitParam.u8Size = len;
-	osmo_wqueue_enqueue(&l1fh->udp_wq, msg);
+	uint8_t data[23+3]; /* prefix PLEN */
+
+	/* paging group */
+	if (!imsi || strlen(imsi) < 3)
+		return;
+	imsi += strlen(imsi) - 3;
+	data[0] = imsi[0];
+	data[1] = imsi[1];
+	data[2] = imsi[2];
+
+	bitvec_pack(block, data + 3+1);
+	data[3] = (plen << 2) | 0x01;
+	pcu_tx_data_req(0, 0, PCU_IF_SAPI_PCH, 0, 0, 0, data, 23+3);
 }
 
-int pcu_l1if_rx_pdch(GsmL1_PhDataInd_t *data_ind)
+static void pcu_l1if_tx_bcch(uint8_t *data, int len)
 {
-	bitvec *block = bitvec_alloc(data_ind->msgUnitParam.u8Size);
-	bitvec_unpack(block, data_ind->msgUnitParam.u8Buffer);
-	gprs_rlcmac_rcv_block(block);
-	bitvec_free(block);
-
-	return 0;
+	pcu_tx_data_req(0, 0, PCU_IF_SAPI_BCCH, 0, 0, 0, data, len);
 }
 
-static int handle_ph_connect_ind(struct femtol1_hdl *fl1, GsmL1_PhConnectInd_t *connect_ind)
-{
-	pcu_l1if_bts.trx[0].arfcn = connect_ind->u16Arfcn;
-	pcu_l1if_bts.trx[0].ts[connect_ind->u8Tn].enable = 1;
-	pcu_l1if_bts.trx[0].ts[connect_ind->u8Tn].tsc = connect_ind->u8Tsc;
-	(l1fh->fl1h)->channel_info.arfcn = connect_ind->u16Arfcn;
-	(l1fh->fl1h)->channel_info.tn = connect_ind->u8Tn;
-	(l1fh->fl1h)->channel_info.tsc = connect_ind->u8Tsc;
-	LOGP(DL1IF, LOGL_NOTICE, "RX: [ PCU <- BTS ] PhConnectInd: ARFCN: %u TN: %u TSC: %u \n",
-	        connect_ind->u16Arfcn, (unsigned)connect_ind->u8Tn, (unsigned)connect_ind->u8Tsc);
-
-	return 0;
-}
-
-static int handle_ph_readytosend_ind(struct femtol1_hdl *fl1, GsmL1_PhReadyToSendInd_t *readytosend_ind)
-{
-	gprs_rlcmac_rcv_rts_block(0,0, (l1fh->fl1h)->channel_info.arfcn, readytosend_ind->u32Fn, 0);
-	return 1;
-}
-
-static int handle_ph_data_ind(struct femtol1_hdl *fl1, GsmL1_PhDataInd_t *data_ind)
+static int pcu_rx_data_ind(struct gsm_pcu_if_data *data_ind)
 {
 	int rc = 0;
+
+	LOGP(DL1IF, LOGL_DEBUG, "Data indication received: sapi=%d arfcn=%d "
+		"block=%d data=%s\n", data_ind->sapi,
+		data_ind->arfcn, data_ind->block_nr,
+		osmo_hexdump(data_ind->data, data_ind->len));
+
 	switch (data_ind->sapi) {
-	case GsmL1_Sapi_Rach:
-		break;
-	case GsmL1_Sapi_Pdtch:
-	case GsmL1_Sapi_Pacch:
-		pcu_l1if_rx_pdch(data_ind);
-		break;
-	case GsmL1_Sapi_Pbcch:
-	case GsmL1_Sapi_Pagch:
-	case GsmL1_Sapi_Ppch:
-	case GsmL1_Sapi_Pnch:
-	case GsmL1_Sapi_Ptcch:
-	case GsmL1_Sapi_Prach:
+	case PCU_IF_SAPI_PDTCH:
+		rc = gprs_rlcmac_rcv_block(data_ind->data, data_ind->len,
+			data_ind->fn);
 		break;
 	default:
-		LOGP(DL1IF, LOGL_NOTICE, "Rx PH-DATA.ind for unknown L1 SAPI %u \n", data_ind->sapi);
-		break;
+		LOGP(DL1IF, LOGL_ERROR, "Received PCU data indication with "
+			"unsupported sapi %d\n", data_ind->sapi);
+		rc = -EINVAL;
 	}
 
 	return rc;
 }
 
-static int handle_ph_ra_ind(struct femtol1_hdl *fl1, GsmL1_PhRaInd_t *ra_ind)
+static int pcu_rx_rts_req(struct gsm_pcu_if_rts_req *rts_req)
 {
 	int rc = 0;
-	(l1fh->fl1h)->channel_info.ta = ra_ind->measParam.i16BurstTiming;
-	rc = gprs_rlcmac_rcv_rach(ra_ind->msgUnitParam.u8Buffer[0], ra_ind->u32Fn, ra_ind->measParam.i16BurstTiming);
-	return rc;
-}
 
-/* handle any random indication from the L1 */
-int pcu_l1if_handle_l1prim(struct femtol1_hdl *fl1, struct msgb *msg)
-{
-	GsmL1_Prim_t *l1p = msgb_l1prim(msg);
-	int rc = 0;
+	LOGP(DL1IF, LOGL_DEBUG, "RTS request received: trx=%d ts=%d sapi=%d "
+		"arfcn=%d fn=%d block=%d\n", rts_req->trx_nr, rts_req->ts_nr,
+		rts_req->sapi, rts_req->arfcn, rts_req->fn, rts_req->block_nr);
 
-	switch (l1p->id) {
-	case GsmL1_PrimId_PhConnectInd:
-		rc = handle_ph_connect_ind(fl1, &l1p->u.phConnectInd);
+	switch (rts_req->sapi) {
+	case PCU_IF_SAPI_PDTCH:
+		gprs_rlcmac_rcv_rts_block(rts_req->trx_nr, rts_req->ts_nr,
+			rts_req->arfcn, rts_req->fn, rts_req->block_nr);
 		break;
-	case GsmL1_PrimId_PhReadyToSendInd:
-		rc = handle_ph_readytosend_ind(fl1, &l1p->u.phReadyToSendInd);
-		break;
-	case GsmL1_PrimId_PhDataInd:
-		rc = handle_ph_data_ind(fl1, &l1p->u.phDataInd);
-		break;
-	case GsmL1_PrimId_PhRaInd:
-		rc = handle_ph_ra_ind(fl1, &l1p->u.phRaInd);
+	case PCU_IF_SAPI_PTCCH:
+		/* FIXME */
+		{
+			struct msgb *msg = msgb_alloc(23, "l1_prim");
+			memset(msgb_put(msg, 23), 0x2b, 23);
+			pcu_l1if_tx_ptcch(msg, rts_req->trx_nr, rts_req->ts_nr,
+				rts_req->arfcn, rts_req->fn, rts_req->block_nr);
+		}
 		break;
 	default:
+		LOGP(DL1IF, LOGL_ERROR, "Received PCU RTS request with "
+			"unsupported sapi %d\n", rts_req->sapi);
+		rc = -EINVAL;
+	}
+
+	return rc;
+}
+
+static int pcu_rx_rach_ind(struct gsm_pcu_if_rach_ind *rach_ind)
+{
+	int rc = 0;
+
+	LOGP(DL1IF, LOGL_INFO, "RACH request received: sapi=%d "
+		"qta=%d, ra=%d, fn=%d\n", rach_ind->sapi, rach_ind->qta,
+		rach_ind->ra, rach_ind->fn);
+
+	switch (rach_ind->sapi) {
+	case PCU_IF_SAPI_RACH:
+		rc = gprs_rlcmac_rcv_rach(rach_ind->ra, rach_ind->fn,
+			rach_ind->qta);
 		break;
+	default:
+		LOGP(DL1IF, LOGL_ERROR, "Received PCU rach request with "
+			"unsupported sapi %d\n", rach_ind->sapi);
+		rc = -EINVAL;
 	}
 
-	/* Special return value '1' means: do not free */
-	if (rc != 1)
-		msgb_free(msg);
-
 	return rc;
 }
 
-/* OpenBTS socket functions */
-
-// TODO: We should move this parameters to config file.
-#define PCU_L1_IF_PORT 5944
-
-/* data has arrived on the udp socket */
-static int udp_read_cb(struct osmo_fd *ofd)
+static int pcu_rx_info_ind(struct gsm_pcu_if_info_ind *info_ind)
 {
-	struct msgb *msg = msgb_alloc_headroom(2048, 128, "udp_rx");
-	struct l1fwd_hdl *l1fh = (l1fwd_hdl *)ofd->data;
-	struct femtol1_hdl *fl1h = l1fh->fl1h;
-	int rc;
+	struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
+	int rc = 0;
+	int trx, ts, tfi;
+	struct gprs_rlcmac_tbf *tbf;
+	int i;
 
-	if (!msg)
-		return -ENOMEM;
+	LOGP(DL1IF, LOGL_DEBUG, "Info indication received:\n");
 
-	msg->l1h = msg->data;
-
-	l1fh->remote_sa_len = sizeof(l1fh->remote_sa);
-	rc = recvfrom(ofd->fd, msg->l1h, msgb_tailroom(msg), 0,
-			(struct sockaddr *) &l1fh->remote_sa, &l1fh->remote_sa_len);
-	if (rc < 0) {
-		perror("read from udp");
-		msgb_free(msg);
-		return rc;
-	} else if (rc == 0) {
-		perror("len=0 read from udp");
-		msgb_free(msg);
-		return rc;
+	if (!(info_ind->flags & PCU_IF_FLAG_ACTIVE)) {
+		LOGP(DL1IF, LOGL_NOTICE, "BTS not available\n");
+bssgp_failed:
+		/* free all TBF */
+		for (trx = 0; trx < 8; trx++) {
+			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];
+					if (tbf)
+						tbf_free(tbf);
+				}
+			}
+		}
+		gprs_bssgp_destroy();
+		return 0;
 	}
-	msgb_put(msg, rc);
+	LOGP(DL1IF, LOGL_INFO, "BTS available\n");
+	LOGP(DL1IF, LOGL_DEBUG, " mcc=%d\n", info_ind->mcc);
+	LOGP(DL1IF, LOGL_DEBUG, " mnc=%d\n", info_ind->mnc);
+	LOGP(DL1IF, LOGL_DEBUG, " lac=%d\n", info_ind->lac);
+	LOGP(DL1IF, LOGL_DEBUG, " rac=%d\n", info_ind->rac);
+	LOGP(DL1IF, LOGL_DEBUG, " cell_id=%d\n", info_ind->cell_id);
+	LOGP(DL1IF, LOGL_DEBUG, " nsei=%d\n", info_ind->nsei);
+	LOGP(DL1IF, LOGL_DEBUG, " nse_timer=%d %d %d %d %d %d %d\n",
+		info_ind->nse_timer[0], info_ind->nse_timer[1],
+		info_ind->nse_timer[2], info_ind->nse_timer[3],
+		info_ind->nse_timer[4], info_ind->nse_timer[5],
+		info_ind->nse_timer[6]);
+	LOGP(DL1IF, LOGL_DEBUG, " cell_timer=%d %d %d %d %d %d %d %d %d %d "
+		"%d\n",
+		info_ind->cell_timer[0], info_ind->cell_timer[1],
+		info_ind->cell_timer[2], info_ind->cell_timer[3],
+		info_ind->cell_timer[4], info_ind->cell_timer[5],
+		info_ind->cell_timer[6], info_ind->cell_timer[7],
+		info_ind->cell_timer[8], info_ind->cell_timer[9],
+		info_ind->cell_timer[10]);
+	LOGP(DL1IF, LOGL_DEBUG, " repeat_time=%d\n", info_ind->repeat_time);
+	LOGP(DL1IF, LOGL_DEBUG, " repeat_count=%d\n", info_ind->repeat_count);
+	LOGP(DL1IF, LOGL_DEBUG, " bvci=%d\n", info_ind->bvci);
+	LOGP(DL1IF, LOGL_DEBUG, " t3142=%d\n", info_ind->t3142);
+	LOGP(DL1IF, LOGL_DEBUG, " t3169=%d\n", info_ind->t3169);
+	LOGP(DL1IF, LOGL_DEBUG, " t3191=%d\n", info_ind->t3191);
+	LOGP(DL1IF, LOGL_DEBUG, " t3193=%d (ms)\n", info_ind->t3193_10ms * 10);
+	LOGP(DL1IF, LOGL_DEBUG, " t3195=%d\n", info_ind->t3195);
+	LOGP(DL1IF, LOGL_DEBUG, " n3101=%d\n", info_ind->n3101);
+	LOGP(DL1IF, LOGL_DEBUG, " n3103=%d\n", info_ind->n3103);
+	LOGP(DL1IF, LOGL_DEBUG, " n3105=%d\n", info_ind->n3105);
+	LOGP(DL1IF, LOGL_DEBUG, " cv_countdown=%d\n", info_ind->cv_countdown);
+	LOGP(DL1IF, LOGL_DEBUG, " dl_tbf_ext=%d\n", info_ind->dl_tbf_ext);
+	LOGP(DL1IF, LOGL_DEBUG, " ul_tbf_ext=%d\n", info_ind->ul_tbf_ext);
+	for (i = 0; i < 4; i++) {
+		if ((info_ind->flags & (PCU_IF_FLAG_CS1 << i)))
+			LOGP(DL1IF, LOGL_DEBUG, " Use CS%d\n", i+1);
+	}
+	for (i = 0; i < 9; i++) {
+		if ((info_ind->flags & (PCU_IF_FLAG_MCS1 << i)))
+			LOGP(DL1IF, LOGL_DEBUG, " Use MCS%d\n", i+1);
+	}
+	LOGP(DL1IF, LOGL_DEBUG, " initial_cs=%d\n", info_ind->initial_cs);
+	LOGP(DL1IF, LOGL_DEBUG, " initial_mcs=%d\n", info_ind->initial_mcs);
+	LOGP(DL1IF, LOGL_DEBUG, " nsvci=%d\n", info_ind->nsvci[0]);
+	LOGP(DL1IF, LOGL_DEBUG, " local_port=%d\n", info_ind->local_port[0]);
+	LOGP(DL1IF, LOGL_DEBUG, " remote_port=%d\n", info_ind->remote_port[0]);
+	LOGP(DL1IF, LOGL_DEBUG, " remote_ip=%d\n", info_ind->remote_ip[0]);
 
-	rc = pcu_l1if_handle_l1prim(fl1h, msg);
+	rc = gprs_bssgp_create(info_ind->remote_ip[0], info_ind->remote_port[0],
+		info_ind->nsei, info_ind->nsvci[0], info_ind->bvci,
+		info_ind->mcc, info_ind->mnc, info_ind->lac, info_ind->rac,
+		info_ind->cell_id);
+	if (rc < 0) {
+		LOGP(DL1IF, LOGL_NOTICE, "SGSN not available\n");
+		goto bssgp_failed;
+	}
+
+	bts->cs1 = !!(info_ind->flags & PCU_IF_FLAG_CS1);
+	bts->cs2 = !!(info_ind->flags & PCU_IF_FLAG_CS2);
+	bts->cs3 = !!(info_ind->flags & PCU_IF_FLAG_CS3);
+	bts->cs4 = !!(info_ind->flags & PCU_IF_FLAG_CS4);
+	if (!bts->cs1 && !bts->cs2 && !bts->cs3 && !bts->cs4)
+		bts->cs1 = 1;
+	if (info_ind->t3142) { /* if timer values are set */
+		bts->t3142 = info_ind->t3142;
+		bts->t3169 = info_ind->t3169;
+		bts->t3191 = info_ind->t3191;
+		bts->t3193_msec = info_ind->t3193_10ms * 10;
+		bts->t3195 = info_ind->t3195;
+		bts->n3101 = info_ind->n3101;
+		bts->n3103 = info_ind->n3103;
+		bts->n3105 = info_ind->n3105;
+	}
+	if (info_ind->initial_cs < 1 || info_ind->initial_cs > 4)
+		bts->initial_cs = 1;
+	else
+		bts->initial_cs = info_ind->initial_cs;
+
+	for (trx = 0; trx < 8; trx++) {
+		bts->trx[trx].arfcn = info_ind->trx[trx].arfcn;
+		for (ts = 0; ts < 8; ts++) {
+			if ((info_ind->trx[trx].pdch_mask & (1 << ts))) {
+				/* FIXME: activate dynamically at RLCMAC */
+				if (!bts->trx[trx].pdch[ts].enable)
+					pcu_tx_act_req(trx, ts, 1);
+				bts->trx[trx].pdch[ts].enable = 1;
+				bts->trx[trx].pdch[ts].tsc =
+					info_ind->trx[trx].tsc[ts];
+				LOGP(DL1IF, LOGL_INFO, "PDCH: trx=%d ts=%d\n",
+					trx, ts);
+			} else {
+				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  */
+				for (tfi = 0; tfi < 32; tfi++) {
+					tbf = bts->trx[trx].pdch[ts].tbf[tfi];
+					if (tbf)
+						tbf_free(tbf);
+				}
+			}
+		}
+	}
+
 	return rc;
 }
 
-/* callback when we can write to the UDP socket */
-static int udp_write_cb(struct osmo_fd *ofd, struct msgb *msg)
+static int pcu_rx_time_ind(struct gsm_pcu_if_time_ind *time_ind)
 {
-	int rc;
-	struct l1fwd_hdl *l1fh = (l1fwd_hdl *)ofd->data;
+	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;
 
-	//DEBUGP(DGPRS, "UDP: Writing %u bytes for MQ_L1_WRITE queue\n", msgb_l1len(msg));
+	/* omit frame numbers not starting at a MAC block */
+	if (fn13 != 0 && fn13 != 4 && fn13 != 8)
+		return 0;
 
-	rc = sendto(ofd->fd, msg->l1h, msgb_l1len(msg), 0,
-			(const struct sockaddr *)&l1fh->remote_sa, l1fh->remote_sa_len);
-	if (rc < 0) {
-		LOGP(DPCU, LOGL_ERROR, "error writing to L1 msg_queue: %s\n",
-			strerror(errno));
-		return rc;
-	} else if (rc < (int)msgb_l1len(msg)) {
-		LOGP(DPCU, LOGL_ERROR, "short write to L1 msg_queue: "
-			"%u < %u\n", rc, msgb_l1len(msg));
-		return -EIO;
+	LOGP(DL1IF, LOGL_DEBUG, "Time indication received: %d\n",
+		time_ind->fn % 52);
+
+	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);
+			}
+		}
 	}
 
 	return 0;
 }
 
-int pcu_l1if_open()
+int pcu_rx(uint8_t msg_type, struct gsm_pcu_if *pcu_prim)
 {
-	//struct l1fwd_hdl *l1fh;
-	struct femtol1_hdl *fl1h;
-	int rc;
+	int rc = 0;
 
-	memset(&pcu_l1if_bts, 0, sizeof(pcu_l1if_bts));
-
-	/* allocate new femtol1_handle */
-	fl1h = talloc_zero(NULL, struct femtol1_hdl);
-	INIT_LLIST_HEAD(&fl1h->wlc_list);
-
-	l1fh->fl1h = fl1h;
-	fl1h->priv = l1fh;
-
-	struct osmo_wqueue * queue = &((l1fh->fl1h)->write_q);
-	osmo_wqueue_init(queue, 10);
-	queue->bfd.when |= BSC_FD_READ;
-	queue->bfd.data = l1fh;
-	queue->bfd.priv_nr = 0;
-
-	/* Open UDP */
-	struct osmo_wqueue *wq = &l1fh->udp_wq;
-
-	osmo_wqueue_init(wq, 10);
-	wq->write_cb = udp_write_cb;
-	wq->read_cb = udp_read_cb;
-	wq->bfd.when |= BSC_FD_READ;
-	wq->bfd.data = l1fh;
-	wq->bfd.priv_nr = 0;
-	rc = osmo_sock_init_ofd(&wq->bfd, AF_UNSPEC, SOCK_DGRAM,
-				IPPROTO_UDP, NULL, PCU_L1_IF_PORT,
-				OSMO_SOCK_F_BIND);
-	if (rc < 0) {
-		perror("sock_init");
-		exit(1);
+	switch (msg_type) {
+	case PCU_IF_MSG_DATA_IND:
+		rc = pcu_rx_data_ind(&pcu_prim->u.data_ind);
+		break;
+	case PCU_IF_MSG_RTS_REQ:
+		rc = pcu_rx_rts_req(&pcu_prim->u.rts_req);
+		break;
+	case PCU_IF_MSG_RACH_IND:
+		rc = pcu_rx_rach_ind(&pcu_prim->u.rach_ind);
+		break;
+	case PCU_IF_MSG_INFO_IND:
+		rc = pcu_rx_info_ind(&pcu_prim->u.info_ind);
+		break;
+	case PCU_IF_MSG_TIME_IND:
+		rc = pcu_rx_time_ind(&pcu_prim->u.time_ind);
+		break;
+	default:
+		LOGP(DL1IF, LOGL_ERROR, "Received unknwon PCU msg type %d\n",
+			msg_type);
+		rc = -EINVAL;
 	}
 
-	return 0;
+	return rc;
 }
-
diff --git a/src/pcu_l1_if.h b/src/pcu_l1_if.h
index 82fa884..f3ac597 100644
--- a/src/pcu_l1_if.h
+++ b/src/pcu_l1_if.h
@@ -29,33 +29,19 @@
 #include <osmocom/gsm/gsm_utils.h>
 }
 
-struct pcu_l1if_ts {
-	uint8_t enable;
-	uint8_t tsc;
-};
-
-struct pcu_l1if_trx {
-	uint16_t arfcn;
-	struct pcu_l1if_ts ts[8];
-};
-
-struct pcu_l1if_bts {
-	struct pcu_l1if_trx trx[8];
-};
-
-extern struct pcu_l1if_bts pcu_l1if_bts;
-
 int get_current_fn();
-void set_current_fn(int fn);
 
 void pcu_l1if_tx_pdtch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn, 
         uint32_t fn, uint8_t block_nr);
 void pcu_l1if_tx_ptcch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn, 
         uint32_t fn, uint8_t block_nr);
 void pcu_l1if_tx_agch(bitvec * block, int len);
-void pcu_l1if_tx_pch(bitvec * block, int len);
+
+void pcu_l1if_tx_pch(bitvec * block, int plen, char *imsi);
 
 int pcu_l1if_open(void);
 void pcu_l1if_close(void);
 
+int pcu_rx(uint8_t msg_type, struct gsm_pcu_if *pcu_prim);
+int pcu_sock_send(struct msgb *msg);
 #endif // PCU_L1_IF_H
diff --git a/src/pcu_main.cpp b/src/pcu_main.cpp
index fa1ffb1..6c30c1e 100644
--- a/src/pcu_main.cpp
+++ b/src/pcu_main.cpp
@@ -20,66 +20,96 @@
 #include <gprs_bssgp_pcu.h>
 #include <arpa/inet.h>
 #include <pcu_l1_if.h>
+#include <gprs_rlcmac.h>
 #include <gsm_timer.h>
 #include <gprs_debug.h>
+#include <unistd.h>
+#include <getopt.h>
 
-// TODO: We should move this parameters to config file.
-#define SGSN_IP "127.0.0.1"
-#define SGSN_PORT 23000
-#define NSVCI 4
+struct gprs_rlcmac_bts *gprs_rlcmac_bts;
+extern struct gprs_nsvc *nsvc;
+uint16_t spoof_mcc = 0, spoof_mnc = 0;
 
-int sgsn_ns_cb(enum gprs_ns_evt event, struct gprs_nsvc *nsvc, struct msgb *msg, uint16_t bvci)
+static void print_help()
 {
-	int rc = 0;
-	switch (event) {
-	case GPRS_NS_EVT_UNIT_DATA:
-		/* hand the message into the BSSGP implementation */
-		rc = gprs_bssgp_pcu_rcvmsg(msg);
-		break;
-	default:
-		LOGP(DPCU, LOGL_ERROR, "RLCMAC: Unknown event %u from NS\n", event);
-		if (msg)
-			talloc_free(msg);
-		rc = -EIO;
-		break;
+	printf( "Some useful options:\n"
+		"  -h	--help		this text\n"
+		"  -m	--mcc MCC	use given MCC instead of value "
+			"provided by BTS\n"
+		"  -n	--mnc MNC	use given MNC instead of value "
+			"provided by BTS\n"
+		);
+}
+
+/* FIXME: finally get some option parsing code into libosmocore */
+static void handle_options(int argc, char **argv)
+{
+	while (1) {
+		int option_idx = 0, c;
+		static const struct option long_options[] = {
+			{ "help", 0, 0, 'h' },
+			{ "mcc", 1, 0, 'm' },
+			{ "mnc", 1, 0, 'n' },
+			{ 0, 0, 0, 0 }
+		};
+
+		c = getopt_long(argc, argv, "hm:n:",
+				long_options, &option_idx);
+		if (c == -1)
+			break;
+
+		switch (c) {
+		case 'h':
+			print_help();
+			exit(0);
+			break;
+		case 'm':
+			spoof_mcc = atoi(optarg);
+			break;
+		case 'n':
+			spoof_mnc = atoi(optarg);
+			break;
+		default:
+			fprintf(stderr, "Unknown option '%c'\n", c);
+			exit(0);
+			break;
+		}
 	}
-	return rc;
 }
 
 int main(int argc, char *argv[])
 {
-	uint16_t nsvci = NSVCI;
-	struct gprs_ns_inst *sgsn_nsi;
-	struct gprs_nsvc *nsvc;
+	struct gprs_rlcmac_bts *bts;
+	int rc;
+
+	bts = gprs_rlcmac_bts = talloc_zero(NULL, struct gprs_rlcmac_bts);
+	if (!gprs_rlcmac_bts)
+		return -ENOMEM;
+	gprs_rlcmac_bts->initial_cs = 1;
+	bts->initial_cs = 1;
+	bts->cs1 = 1;
+	bts->t3142 = 20;
+	bts->t3169 = 5;
+	bts->t3191 = 5;
+	bts->t3193_msec = 100;
+	bts->t3195 = 5;
+	bts->n3101 = 10;
+	bts->n3103 = 4;
+	bts->n3105 = 8;
+
 	osmo_init_logging(&gprs_log_info);
-	pcu_l1if_open();
 
-	sgsn_nsi = gprs_ns_instantiate(&sgsn_ns_cb, NULL);
-	bssgp_nsi = sgsn_nsi;
-
-	if (!bssgp_nsi)
-	{
-		LOGP(DPCU, LOGL_ERROR, "Unable to instantiate NS\n");
-		exit(1);
+	handle_options(argc, argv);
+	if ((!!spoof_mcc) + (!!spoof_mnc) == 1) {
+		fprintf(stderr, "--mcc and --mnc must be specified "
+			"together.\n");
+		exit(0);
 	}
-	bctx = btsctx_alloc(BVCI, NSEI);
-	bctx->cell_id = CELL_ID;
-	bctx->nsei = NSEI;
-	bctx->ra_id.mnc = MNC;
-	bctx->ra_id.mcc = MCC;
-	bctx->ra_id.lac = PCU_LAC;
-	bctx->ra_id.rac = PCU_RAC;
-	bctx->bvci = BVCI;
-	uint8_t cause = 39;
-	gprs_ns_nsip_listen(sgsn_nsi);
+	rc = pcu_l1if_open();
 
-	struct sockaddr_in dest;
-	dest.sin_family = AF_INET;
-	dest.sin_port = htons(SGSN_PORT);
-	inet_aton(SGSN_IP, &dest.sin_addr);
+	if (rc < 0)
+		return rc;
 
-	nsvc = gprs_ns_nsip_connect(sgsn_nsi, &dest, NSEI, nsvci);
-	unsigned i = 0;
 	while (1) 
 	{
 		osmo_gsm_timers_check();
@@ -87,11 +117,10 @@
 		osmo_gsm_timers_update();
 
 		osmo_select_main(0);
-		if (i == 7)
-		{
-			bssgp_tx_bvc_reset(bctx, BVCI, cause);
-		}
-		i++;
 	}
-}
 
+	pcu_l1if_close();
+	talloc_free(gprs_rlcmac_bts);
+
+	return 0;
+}
diff --git a/src/pcuif_proto.h b/src/pcuif_proto.h
new file mode 100644
index 0000000..3609f79
--- /dev/null
+++ b/src/pcuif_proto.h
@@ -0,0 +1,137 @@
+#ifndef _PCUIF_PROTO_H
+#define _PCUIF_PROTO_H
+
+/* msg_type */
+#define PCU_IF_MSG_DATA_REQ	0x00	/* send data to given channel */
+#define PCU_IF_MSG_DATA_IND	0x02	/* receive data from given channel */	
+#define PCU_IF_MSG_RTS_REQ	0x10	/* ready to send data to given chan. */
+#define PCU_IF_MSG_RACH_IND	0x22	/* receive rach */
+#define PCU_IF_MSG_INFO_IND	0x32	/* retrieve BTS info */
+#define PCU_IF_MSG_ACT_REQ	0x40	/* activate/deactivate PDCH */
+#define PCU_IF_MSG_TIME_IND	0x52	/* gsm time indication */
+
+/* sapi */
+#define PCU_IF_SAPI_RACH	0x01	/* channel request on CCCH */
+#define PCU_IF_SAPI_AGCH	0x02	/* assignment on CCCH */
+#define PCU_IF_SAPI_PCH		0x03	/* paging request on CCCH */
+#define PCU_IF_SAPI_BCCH	0x04	/* SI on BCCH */
+#define PCU_IF_SAPI_PDTCH	0x05	/* packet data/control/ccch block */
+#define PCU_IF_SAPI_PRACH	0x06	/* packet random access channel */
+#define PCU_IF_SAPI_PTCCH	0x07	/* packet TA control channel */
+
+/* flags */
+#define PCU_IF_FLAG_ACTIVE	(1 << 0)/* BTS is active */
+#define PCU_IF_FLAG_SYSMO	(1 << 1)/* access PDCH of sysmoBTS directly */
+#define PCU_IF_FLAG_CS1		(1 << 16)
+#define PCU_IF_FLAG_CS2		(1 << 17)
+#define PCU_IF_FLAG_CS3		(1 << 18)
+#define PCU_IF_FLAG_CS4		(1 << 19)
+#define PCU_IF_FLAG_MCS1	(1 << 20)
+#define PCU_IF_FLAG_MCS2	(1 << 21)
+#define PCU_IF_FLAG_MCS3	(1 << 22)
+#define PCU_IF_FLAG_MCS4	(1 << 23)
+#define PCU_IF_FLAG_MCS5	(1 << 24)
+#define PCU_IF_FLAG_MCS6	(1 << 25)
+#define PCU_IF_FLAG_MCS7	(1 << 26)
+#define PCU_IF_FLAG_MCS8	(1 << 27)
+#define PCU_IF_FLAG_MCS9	(1 << 28)
+
+struct gsm_pcu_if_data {
+	uint8_t		sapi;
+	uint8_t		len;
+	uint8_t		data[162];
+	uint32_t	fn;
+	uint16_t	arfcn;
+	uint8_t		trx_nr;
+	uint8_t		ts_nr;
+	uint8_t		block_nr;
+} __attribute__ ((packed));
+
+struct gsm_pcu_if_rts_req {
+	uint8_t		sapi;
+	uint8_t		spare[3];
+	uint32_t	fn;
+	uint16_t	arfcn;
+	uint8_t		trx_nr;
+	uint8_t		ts_nr;
+	uint8_t		block_nr;
+} __attribute__ ((packed));
+
+struct gsm_pcu_if_rach_ind {
+	uint8_t		sapi;
+	uint8_t		ra;
+	int16_t		qta;
+	uint32_t	fn;
+	uint16_t	arfcn;
+} __attribute__ ((packed));
+
+struct gsm_pcu_if_info_trx {
+	uint16_t	arfcn;
+	uint8_t		pdch_mask;		/* PDCH channels per TS */
+	uint8_t		spare;
+	uint8_t		tsc[8];			/* TSC per channel */
+} __attribute__ ((packed));
+
+struct gsm_pcu_if_info_ind {
+	uint32_t	flags;
+	struct gsm_pcu_if_info_trx trx[8];	/* TRX infos per BTS */
+	/* RAI */
+	uint16_t	mcc, mnc, lac, rac;
+	/* NSE */
+	uint16_t	nsei;
+	uint8_t		nse_timer[7];
+	uint8_t		cell_timer[11];
+	/* cell  */
+	uint16_t	cell_id;
+	uint16_t	repeat_time;
+	uint8_t		repeat_count;
+	uint16_t	bvci;
+	uint8_t		t3142;
+	uint8_t		t3169;
+	uint8_t		t3191;
+	uint8_t		t3193_10ms;
+	uint8_t		t3195;
+	uint8_t		n3101;
+	uint8_t		n3103;
+	uint8_t		n3105;
+	uint8_t		cv_countdown;
+	uint16_t	dl_tbf_ext;
+	uint16_t	ul_tbf_ext;
+	uint8_t		initial_cs;
+	uint8_t		initial_mcs;
+	/* NSVC */
+	uint16_t	nsvci[2];
+	uint16_t	local_port[2];
+	uint16_t	remote_port[2];
+	uint32_t	remote_ip[2];
+} __attribute__ ((packed));
+
+struct gsm_pcu_if_act_req {
+	uint8_t		activate;
+	uint8_t		trx_nr;
+	uint8_t		ts_nr;
+	uint8_t		spare;
+} __attribute__ ((packed));
+
+struct gsm_pcu_if_time_ind {
+	uint32_t	fn;
+} __attribute__ ((packed));
+
+struct gsm_pcu_if {
+	/* context based information */
+	uint8_t		msg_type;	/* message type */
+	uint8_t		bts_nr;		/* bts number */
+	uint8_t		spare[2];
+
+	union {
+		struct gsm_pcu_if_data		data_req;
+		struct gsm_pcu_if_data		data_ind;
+		struct gsm_pcu_if_rts_req	rts_req;
+		struct gsm_pcu_if_rach_ind	rach_ind;
+		struct gsm_pcu_if_info_ind	info_ind;
+		struct gsm_pcu_if_act_req	act_req;
+		struct gsm_pcu_if_time_ind	time_ind;
+	} u;
+} __attribute__ ((packed));
+
+#endif /* _PCUIF_PROTO_H */
diff --git a/src/sysmo_l1_if.cpp b/src/sysmo_l1_if.cpp
deleted file mode 100644
index 09a3149..0000000
--- a/src/sysmo_l1_if.cpp
+++ /dev/null
@@ -1,562 +0,0 @@
-/* sysmo_l1_if.cpp
- *
- * Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
- *
- * 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 <stdio.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <assert.h>
-#include <sys/socket.h>
-#include <sys/un.h>
-extern "C" {
-#include <osmocom/core/talloc.h>
-#include <osmocom/core/select.h>
-#include <osmocom/core/msgb.h>
-}
-
-#include <gprs_rlcmac.h>
-#include <pcu_l1_if.h>
-#include <gprs_debug.h>
-#include "../../osmo-bts/include/osmo-bts/pcuif_proto.h"
-
-static int pcu_sock_send(struct msgb *msg);
-static void pcu_sock_timeout(void *_priv);
-
-struct pcu_l1if_bts pcu_l1if_bts;
-
-// Variable for storage current FN.
-int frame_number;
-
-int get_current_fn()
-{
-	return frame_number;
-}
-
-void set_current_fn(int fn)
-{
-	frame_number = fn;
-}
-
-/*
- * PCU messages
- */
-
-struct msgb *pcu_msgb_alloc(uint8_t msg_type, uint8_t bts_nr)
-{
-	struct msgb *msg;
-	struct gsm_pcu_if *pcu_prim;
-
-	msg = msgb_alloc(sizeof(struct gsm_pcu_if), "pcu_sock_tx");
-	if (!msg)
-		return NULL;
-	msgb_put(msg, sizeof(struct gsm_pcu_if));
-	pcu_prim = (struct gsm_pcu_if *) msg->data;
-	pcu_prim->msg_type = msg_type;
-	pcu_prim->bts_nr = bts_nr;
-
-	return msg;
-}
-
-static int pcu_tx_act_req(uint8_t trx, uint8_t ts, uint8_t activate)
-{
-	struct msgb *msg;
-	struct gsm_pcu_if *pcu_prim;
-	struct gsm_pcu_if_act_req *act_req;
-
-	LOGP(DL1IF, LOGL_INFO, "Sending %s request: trx=%d ts=%d\n",
-		(activate) ? "activate" : "deactivate", trx, ts);
-
-	msg = pcu_msgb_alloc(PCU_IF_MSG_ACT_REQ, 0);
-	if (!msg)
-		return -ENOMEM;
-	pcu_prim = (struct gsm_pcu_if *) msg->data;
-	act_req = &pcu_prim->u.act_req;
-	act_req->activate = activate;
-	act_req->trx_nr = trx;
-	act_req->ts_nr = ts;
-
-	return pcu_sock_send(msg);
-}
-
-static int pcu_tx_data_req(uint8_t trx, uint8_t ts, uint8_t sapi,
-	uint16_t arfcn, uint32_t fn, uint8_t block_nr, uint8_t *data,
-	uint8_t len)
-{
-	struct msgb *msg;
-	struct gsm_pcu_if *pcu_prim;
-	struct gsm_pcu_if_data *data_req;
-
-	LOGP(DL1IF, LOGL_DEBUG, "Sending data request: trx=%d ts=%d sapi=%d "
-		"arfcn=%d fn=%d block=%d data=%s\n", trx, ts, sapi, arfcn, fn,
-		block_nr, osmo_hexdump(data, len));
-
-	msg = pcu_msgb_alloc(PCU_IF_MSG_DATA_REQ, 0);
-	if (!msg)
-		return -ENOMEM;
-	pcu_prim = (struct gsm_pcu_if *) msg->data;
-	data_req = &pcu_prim->u.data_req;
-
-	data_req->sapi = sapi;
-	data_req->fn = fn;
-	data_req->arfcn = arfcn;
-	data_req->trx_nr = trx;
-	data_req->ts_nr = ts;
-	data_req->block_nr = block_nr;
-	memcpy(data_req->data, data, len);
-	data_req->len = len;
-
-	return pcu_sock_send(msg);
-}
-
-void pcu_l1if_tx_pdtch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn,
-	uint32_t fn, uint8_t block_nr)
-{
-	pcu_tx_data_req(trx, ts, PCU_IF_SAPI_PDTCH, arfcn, fn, block_nr,
-		msg->data, msg->len);
-	msgb_free(msg);
-}
-
-void pcu_l1if_tx_ptcch(msgb *msg, uint8_t trx, uint8_t ts, uint16_t arfcn,
-	uint32_t fn, uint8_t block_nr)
-{
-	pcu_tx_data_req(trx, ts, PCU_IF_SAPI_PTCCH, arfcn, fn, block_nr,
-		msg->data, msg->len);
-	msgb_free(msg);
-}
-
-void pcu_l1if_tx_agch(bitvec * block, int len)
-{
-	uint8_t data[24]; /* prefix PLEN */
-	
-	/* FIXME: why does OpenBTS has no PLEN and no fill in message? */
-	bitvec_pack(block, data + 1);
-	data[0] = (len << 2) | 0x01;
-	pcu_tx_data_req(0, 0, PCU_IF_SAPI_AGCH, 0, 0, 0, data, 23);
-}
-
-static void pcu_l1if_tx_bcch(uint8_t *data, int len)
-{
-	pcu_tx_data_req(0, 0, PCU_IF_SAPI_BCCH, 0, 0, 0, data, len);
-}
-
-static int pcu_rx_data_ind(struct gsm_pcu_if_data *data_ind)
-{
-	int rc = 0;
-	bitvec *block;
-
-	LOGP(DL1IF, LOGL_DEBUG, "Data indication received: sapi=%d arfcn=%d "
-		"block=%d data=%s\n", data_ind->sapi,
-		data_ind->arfcn, data_ind->block_nr,
-		osmo_hexdump(data_ind->data, data_ind->len));
-
-	switch (data_ind->sapi) {
-	case PCU_IF_SAPI_PDTCH:
-		block = bitvec_alloc(data_ind->len);
-		if (!block) {
-			rc = -ENOMEM;
-			break;
-		}
-		bitvec_unpack(block, data_ind->data);
-		gprs_rlcmac_rcv_block(block);
-		bitvec_free(block);
-		break;
-	default:
-		LOGP(DL1IF, LOGL_ERROR, "Received PCU data indication with "
-			"unsupported sapi %d\n", data_ind->sapi);
-		rc = -EINVAL;
-	}
-
-	return rc;
-}
-
-static int pcu_rx_rts_req(struct gsm_pcu_if_rts_req *rts_req)
-{
-	int rc = 0;
-
-	LOGP(DL1IF, LOGL_DEBUG, "RTS request received: trx=%d ts=%d sapi=%d "
-		"arfcn=%d fn=%d block=%d\n", rts_req->trx_nr, rts_req->ts_nr,
-		rts_req->sapi, rts_req->arfcn, rts_req->fn, rts_req->block_nr);
-
-	switch (rts_req->sapi) {
-	case PCU_IF_SAPI_PDTCH:
-		gprs_rlcmac_rcv_rts_block(rts_req->trx_nr, rts_req->ts_nr,
-			rts_req->arfcn, rts_req->fn, rts_req->block_nr);
-		break;
-	case PCU_IF_SAPI_PTCCH:
-		/* FIXME */
-		{
-			struct msgb *msg = msgb_alloc(23, "l1_prim");
-			memset(msgb_put(msg, 23), 0x2b, 23);
-			pcu_l1if_tx_ptcch(msg, rts_req->trx_nr, rts_req->ts_nr,
-				rts_req->arfcn, rts_req->fn, rts_req->block_nr);
-		}
-		break;
-	default:
-		LOGP(DL1IF, LOGL_ERROR, "Received PCU RTS request with "
-			"unsupported sapi %d\n", rts_req->sapi);
-		rc = -EINVAL;
-	}
-
-	return rc;
-}
-
-static int pcu_rx_rach_ind(struct gsm_pcu_if_rach_ind *rach_ind)
-{
-	int rc = 0;
-
-	LOGP(DL1IF, LOGL_INFO, "RACH request received: sapi=%d "
-		"qta=%d, ra=%d, fn=%d\n", rach_ind->sapi, rach_ind->qta,
-		rach_ind->ra, rach_ind->fn);
-
-	switch (rach_ind->sapi) {
-	case PCU_IF_SAPI_RACH:
-		rc = gprs_rlcmac_rcv_rach(rach_ind->ra, rach_ind->fn,
-			rach_ind->qta);
-		break;
-	default:
-		LOGP(DL1IF, LOGL_ERROR, "Received PCU rach request with "
-			"unsupported sapi %d\n", rach_ind->sapi);
-		rc = -EINVAL;
-	}
-
-	return rc;
-}
-
-static int pcu_rx_info_ind(struct gsm_pcu_if_info_ind *info_ind)
-{
-	int rc = 0;
-	int trx, ts;
-//	uint8_t si13[23];
-
-	LOGP(DL1IF, LOGL_INFO, "Info indication received:\n");
-
-	if (!(info_ind->flags & PCU_IF_FLAG_ACTIVE)) {
-		LOGP(DL1IF, LOGL_NOTICE, "BTS not available\n");
-		return 0;
-	}
-	LOGP(DL1IF, LOGL_INFO, "BTS available\n");
-
-	for (trx = 0; trx < 8; trx++) {
-		pcu_l1if_bts.trx[trx].arfcn = info_ind->trx[trx].arfcn;
-		for (ts = 0; ts < 8; ts++) {
-			if ((info_ind->trx[trx].pdch_mask & (1 << ts))) {
-				/* FIXME: activate dynamically at RLCMAC */
-				if (!pcu_l1if_bts.trx[trx].ts[ts].enable)
-					pcu_tx_act_req(trx, ts, 1);
-				pcu_l1if_bts.trx[trx].ts[ts].enable = 1;
-				pcu_l1if_bts.trx[trx].ts[ts].tsc =
-					info_ind->trx[trx].tsc[ts];
-				LOGP(DL1IF, LOGL_INFO, "PDCH: trx=%d ts=%d\n",
-					trx, ts);
-			} else {
-				if (pcu_l1if_bts.trx[trx].ts[ts].enable)
-					pcu_tx_act_req(trx, ts, 0);
-				pcu_l1if_bts.trx[trx].ts[ts].enable = 0;
-			}
-		}
-	}
-
-#warning FIXME: RAC
-//	rc = generate_si13(si13, 0 /* rac */);
-//	printf("rc=%d\n", rc);
-//	pcu_l1if_tx_bcch(si13, 23);
-
-	return rc;
-}
-
-static int pcu_rx(uint8_t msg_type, struct gsm_pcu_if *pcu_prim)
-{
-	int rc = 0;
-
-	switch (msg_type) {
-	case PCU_IF_MSG_DATA_IND:
-		rc = pcu_rx_data_ind(&pcu_prim->u.data_ind);
-		break;
-	case PCU_IF_MSG_RTS_REQ:
-		rc = pcu_rx_rts_req(&pcu_prim->u.rts_req);
-		break;
-	case PCU_IF_MSG_RACH_IND:
-		rc = pcu_rx_rach_ind(&pcu_prim->u.rach_ind);
-		break;
-	case PCU_IF_MSG_INFO_IND:
-		rc = pcu_rx_info_ind(&pcu_prim->u.info_ind);
-		break;
-	default:
-		LOGP(DL1IF, LOGL_ERROR, "Received unknwon PCU msg type %d\n",
-			msg_type);
-		rc = -EINVAL;
-	}
-
-	return rc;
-}
-
-/*
- * SYSMO-PCU socket functions
- */
-
-struct pcu_sock_state {
-	struct osmo_fd conn_bfd;	/* fd for connection to lcr */
-	struct osmo_timer_list timer;	/* socket connect retry timer */
-	struct llist_head upqueue;	/* queue for sending messages */
-} *pcu_sock_state = NULL;
-
-static int pcu_sock_send(struct msgb *msg)
-{
-	struct pcu_sock_state *state = pcu_sock_state;
-	struct osmo_fd *conn_bfd;
-
-	if (!state) {
-		LOGP(DL1IF, LOGL_NOTICE, "PCU socket not created, dropping "
-			"message\n");
-		return -EINVAL;
-	}
-	conn_bfd = &state->conn_bfd;
-	if (conn_bfd->fd <= 0) {
-		LOGP(DL1IF, LOGL_NOTICE, "PCU socket not connected, dropping "
-			"message\n");
-		return -EIO;
-	}
-	msgb_enqueue(&state->upqueue, msg);
-	conn_bfd->when |= BSC_FD_WRITE;
-
-	return 0;
-}
-
-static void pcu_sock_close(struct pcu_sock_state *state)
-{
-	struct osmo_fd *bfd = &state->conn_bfd;
-
-	LOGP(DL1IF, LOGL_NOTICE, "PCU socket has LOST connection\n");
-
-	close(bfd->fd);
-	bfd->fd = -1;
-	osmo_fd_unregister(bfd);
-
-	/* flush the queue */
-	while (!llist_empty(&state->upqueue)) {
-		struct msgb *msg = msgb_dequeue(&state->upqueue);
-		msgb_free(msg);
-	}
-
-	/* disable all slots */
-	memset(&pcu_l1if_bts, 0, sizeof(pcu_l1if_bts));
-
-	state->timer.cb = pcu_sock_timeout;
-	osmo_timer_schedule(&state->timer, 5, 0);
-}
-
-static int pcu_sock_read(struct osmo_fd *bfd)
-{
-	struct pcu_sock_state *state = (struct pcu_sock_state *)bfd->data;
-	struct gsm_pcu_if *pcu_prim;
-	struct msgb *msg;
-	int rc;
-
-	msg = msgb_alloc(sizeof(*pcu_prim), "pcu_sock_rx");
-	if (!msg)
-		return -ENOMEM;
-
-	pcu_prim = (struct gsm_pcu_if *) msg->tail;
-
-	rc = recv(bfd->fd, msg->tail, msgb_tailroom(msg), 0);
-	if (rc == 0)
-		goto close;
-
-	if (rc < 0) {
-		if (errno == EAGAIN)
-			return 0;
-		goto close;
-	}
-
-	rc = pcu_rx(pcu_prim->msg_type, pcu_prim);
-
-	/* as we always synchronously process the message in pcu_rx() and
-	 * its callbacks, we can free the message here. */
-	msgb_free(msg);
-
-	return rc;
-
-close:
-	msgb_free(msg);
-	pcu_sock_close(state);
-	return -1;
-}
-
-static int pcu_sock_write(struct osmo_fd *bfd)
-{
-	struct pcu_sock_state *state = (struct pcu_sock_state *)bfd->data;
-	int rc;
-
-	while (!llist_empty(&state->upqueue)) {
-		struct msgb *msg, *msg2;
-		struct gsm_pcu_if *pcu_prim;
-
-		/* peek at the beginning of the queue */
-		msg = llist_entry(state->upqueue.next, struct msgb, list);
-		pcu_prim = (struct gsm_pcu_if *)msg->data;
-
-		bfd->when &= ~BSC_FD_WRITE;
-
-		/* bug hunter 8-): maybe someone forgot msgb_put(...) ? */
-		if (!msgb_length(msg)) {
-			LOGP(DL1IF, LOGL_ERROR, "message type (%d) with ZERO "
-				"bytes!\n", pcu_prim->msg_type);
-			goto dontsend;
-		}
-
-		/* try to send it over the socket */
-		rc = write(bfd->fd, msgb_data(msg), msgb_length(msg));
-		if (rc == 0)
-			goto close;
-		if (rc < 0) {
-			if (errno == EAGAIN) {
-				bfd->when |= BSC_FD_WRITE;
-				break;
-			}
-			goto close;
-		}
-
-dontsend:
-		/* _after_ we send it, we can deueue */
-		msg2 = msgb_dequeue(&state->upqueue);
-		assert(msg == msg2);
-		msgb_free(msg);
-	}
-	return 0;
-
-close:
-	pcu_sock_close(state);
-
-	return -1;
-}
-
-static int pcu_sock_cb(struct osmo_fd *bfd, unsigned int flags)
-{
-	int rc = 0;
-
-	if (flags & BSC_FD_READ)
-		rc = pcu_sock_read(bfd);
-	if (rc < 0)
-		return rc;
-
-	if (flags & BSC_FD_WRITE)
-		rc = pcu_sock_write(bfd);
-
-	return rc;
-}
-
-int pcu_l1if_open(void)
-{
-	struct pcu_sock_state *state;
-	struct osmo_fd *bfd;
-	struct sockaddr_un local;
-	unsigned int namelen;
-	int rc;
-
-	memset(&pcu_l1if_bts, 0, sizeof(pcu_l1if_bts));
-
-	state = pcu_sock_state;
-	if (!state) {
-		state = talloc_zero(NULL, struct pcu_sock_state);
-		if (!state)
-			return -ENOMEM;
-		INIT_LLIST_HEAD(&state->upqueue);
-	}
-
-	bfd = &state->conn_bfd;
-
-	bfd->fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
-	if (bfd->fd < 0) {
-		LOGP(DL1IF, LOGL_ERROR, "Failed to create PCU-SYSMO socket.\n");
-		talloc_free(state);
-		return -1;
-	}
-
-	local.sun_family = AF_UNIX;
-	strncpy(local.sun_path, "/tmp/pcu_bts", sizeof(local.sun_path));
-	local.sun_path[sizeof(local.sun_path) - 1] = '\0';
-
-	/* we use the same magic that X11 uses in Xtranssock.c for
-	 * calculating the proper length of the sockaddr */
-#if defined(BSD44SOCKETS) || defined(__UNIXWARE__)
-	local.sun_len = strlen(local.sun_path);
-#endif
-#if defined(BSD44SOCKETS) || defined(SUN_LEN)
-	namelen = SUN_LEN(&local);
-#else
-	namelen = strlen(local.sun_path) +
-		  offsetof(struct sockaddr_un, sun_path);
-#endif
-	rc = connect(bfd->fd, (struct sockaddr *) &local, namelen);
-	if (rc != 0) {
-		LOGP(DL1IF, LOGL_ERROR, "Failed to Connect the PCU-SYSMO "
-			"socket, delaying... '%s'\n", local.sun_path);
-		close(bfd->fd);
-		bfd->fd = -1;
-		state->timer.cb = pcu_sock_timeout;
-		osmo_timer_schedule(&state->timer, 5, 0);
-		return -1;
-	}
-
-	bfd->when = BSC_FD_READ;
-	bfd->cb = pcu_sock_cb;
-	bfd->data = state;
-
-	rc = osmo_fd_register(bfd);
-	if (rc < 0) {
-		LOGP(DL1IF, LOGL_ERROR, "Could not register PCU fd: %d\n", rc);
-		close(bfd->fd);
-		talloc_free(state);
-		return rc;
-	}
-
-	LOGP(DL1IF, LOGL_NOTICE, "PCU-SYSMO socket has been connected\n");
-
-	pcu_sock_state = state;
-
-	return 0;
-}
-
-void pcu_l1if_close(void)
-{
-	struct pcu_sock_state *state = pcu_sock_state;
-	struct osmo_fd *bfd;
-
-	if (!state)
-		return;
-
-	if (osmo_timer_pending(&state->timer))
-		osmo_timer_del(&state->timer);
-
-	bfd = &state->conn_bfd;
-	if (bfd->fd > 0)
-		pcu_sock_close(state);
-	talloc_free(state);
-	pcu_sock_state = NULL;
-}
-
-static void pcu_sock_timeout(void *_priv)
-{
-	pcu_l1if_open();
-}
-
-
-
-
diff --git a/src/sysmo_sock.cpp b/src/sysmo_sock.cpp
new file mode 100644
index 0000000..8d83ca2
--- /dev/null
+++ b/src/sysmo_sock.cpp
@@ -0,0 +1,303 @@
+/* sysmo_sock.cpp
+ *
+ * Copyright (C) 2012 Andreas Eversberg <jolly@eversberg.eu>
+ *
+ * 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 <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+extern "C" {
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/msgb.h>
+}
+
+#include <gprs_rlcmac.h>
+#include <pcu_l1_if.h>
+#include <gprs_debug.h>
+#include <gprs_bssgp_pcu.h>
+#include <pcuif_proto.h>
+
+
+/*
+ * SYSMO-PCU socket functions
+ */
+
+struct pcu_sock_state {
+	struct osmo_fd conn_bfd;	/* fd for connection to lcr */
+	struct osmo_timer_list timer;	/* socket connect retry timer */
+	struct llist_head upqueue;	/* queue for sending messages */
+} *pcu_sock_state = NULL;
+
+static void pcu_sock_timeout(void *_priv);
+
+int pcu_sock_send(struct msgb *msg)
+{
+	struct pcu_sock_state *state = pcu_sock_state;
+	struct osmo_fd *conn_bfd;
+
+	if (!state) {
+		LOGP(DL1IF, LOGL_NOTICE, "PCU socket not created, dropping "
+			"message\n");
+		return -EINVAL;
+	}
+	conn_bfd = &state->conn_bfd;
+	if (conn_bfd->fd <= 0) {
+		LOGP(DL1IF, LOGL_NOTICE, "PCU socket not connected, dropping "
+			"message\n");
+		return -EIO;
+	}
+	msgb_enqueue(&state->upqueue, msg);
+	conn_bfd->when |= BSC_FD_WRITE;
+
+	return 0;
+}
+
+static void pcu_sock_close(struct pcu_sock_state *state)
+{
+	struct osmo_fd *bfd = &state->conn_bfd;
+	struct gprs_rlcmac_bts *bts = gprs_rlcmac_bts;
+	struct gprs_rlcmac_tbf *tbf;
+	uint8_t trx, ts, tfi;
+
+	LOGP(DL1IF, LOGL_NOTICE, "PCU socket has LOST connection\n");
+
+	close(bfd->fd);
+	bfd->fd = -1;
+	osmo_fd_unregister(bfd);
+
+	/* flush the queue */
+	while (!llist_empty(&state->upqueue)) {
+		struct msgb *msg = msgb_dequeue(&state->upqueue);
+		msgb_free(msg);
+	}
+
+	/* disable all slots, kick all TBFs */
+	for (trx = 0; trx < 8; trx++) {
+		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];
+				if (tbf)
+					tbf_free(tbf);
+			}
+		}
+	}
+
+	gprs_bssgp_destroy();
+
+	state->timer.cb = pcu_sock_timeout;
+	osmo_timer_schedule(&state->timer, 5, 0);
+}
+
+static int pcu_sock_read(struct osmo_fd *bfd)
+{
+	struct pcu_sock_state *state = (struct pcu_sock_state *)bfd->data;
+	struct gsm_pcu_if *pcu_prim;
+	struct msgb *msg;
+	int rc;
+
+	msg = msgb_alloc(sizeof(*pcu_prim), "pcu_sock_rx");
+	if (!msg)
+		return -ENOMEM;
+
+	pcu_prim = (struct gsm_pcu_if *) msg->tail;
+
+	rc = recv(bfd->fd, msg->tail, msgb_tailroom(msg), 0);
+	if (rc == 0)
+		goto close;
+
+	if (rc < 0) {
+		if (errno == EAGAIN)
+			return 0;
+		goto close;
+	}
+
+	rc = pcu_rx(pcu_prim->msg_type, pcu_prim);
+
+	/* as we always synchronously process the message in pcu_rx() and
+	 * its callbacks, we can free the message here. */
+	msgb_free(msg);
+
+	return rc;
+
+close:
+	msgb_free(msg);
+	pcu_sock_close(state);
+	return -1;
+}
+
+static int pcu_sock_write(struct osmo_fd *bfd)
+{
+	struct pcu_sock_state *state = (struct pcu_sock_state *)bfd->data;
+	int rc;
+
+	while (!llist_empty(&state->upqueue)) {
+		struct msgb *msg, *msg2;
+		struct gsm_pcu_if *pcu_prim;
+
+		/* peek at the beginning of the queue */
+		msg = llist_entry(state->upqueue.next, struct msgb, list);
+		pcu_prim = (struct gsm_pcu_if *)msg->data;
+
+		bfd->when &= ~BSC_FD_WRITE;
+
+		/* bug hunter 8-): maybe someone forgot msgb_put(...) ? */
+		if (!msgb_length(msg)) {
+			LOGP(DL1IF, LOGL_ERROR, "message type (%d) with ZERO "
+				"bytes!\n", pcu_prim->msg_type);
+			goto dontsend;
+		}
+
+		/* try to send it over the socket */
+		rc = write(bfd->fd, msgb_data(msg), msgb_length(msg));
+		if (rc == 0)
+			goto close;
+		if (rc < 0) {
+			if (errno == EAGAIN) {
+				bfd->when |= BSC_FD_WRITE;
+				break;
+			}
+			goto close;
+		}
+
+dontsend:
+		/* _after_ we send it, we can deueue */
+		msg2 = msgb_dequeue(&state->upqueue);
+		assert(msg == msg2);
+		msgb_free(msg);
+	}
+	return 0;
+
+close:
+	pcu_sock_close(state);
+
+	return -1;
+}
+
+static int pcu_sock_cb(struct osmo_fd *bfd, unsigned int flags)
+{
+	int rc = 0;
+
+	if (flags & BSC_FD_READ)
+		rc = pcu_sock_read(bfd);
+	if (rc < 0)
+		return rc;
+
+	if (flags & BSC_FD_WRITE)
+		rc = pcu_sock_write(bfd);
+
+	return rc;
+}
+
+int pcu_l1if_open(void)
+{
+	struct pcu_sock_state *state;
+	struct osmo_fd *bfd;
+	struct sockaddr_un local;
+	unsigned int namelen;
+	int rc;
+
+	state = pcu_sock_state;
+	if (!state) {
+		state = talloc_zero(NULL, struct pcu_sock_state);
+		if (!state)
+			return -ENOMEM;
+		INIT_LLIST_HEAD(&state->upqueue);
+	}
+
+	bfd = &state->conn_bfd;
+
+	bfd->fd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+	if (bfd->fd < 0) {
+		LOGP(DL1IF, LOGL_ERROR, "Failed to create PCU-SYSMO socket.\n");
+		talloc_free(state);
+		return -1;
+	}
+
+	local.sun_family = AF_UNIX;
+	strncpy(local.sun_path, "/tmp/pcu_bts", sizeof(local.sun_path));
+	local.sun_path[sizeof(local.sun_path) - 1] = '\0';
+
+	/* we use the same magic that X11 uses in Xtranssock.c for
+	 * calculating the proper length of the sockaddr */
+#if defined(BSD44SOCKETS) || defined(__UNIXWARE__)
+	local.sun_len = strlen(local.sun_path);
+#endif
+#if defined(BSD44SOCKETS) || defined(SUN_LEN)
+	namelen = SUN_LEN(&local);
+#else
+	namelen = strlen(local.sun_path) +
+		  offsetof(struct sockaddr_un, sun_path);
+#endif
+	rc = connect(bfd->fd, (struct sockaddr *) &local, namelen);
+	if (rc != 0) {
+		LOGP(DL1IF, LOGL_ERROR, "Failed to Connect the PCU-SYSMO "
+			"socket, delaying... '%s'\n", local.sun_path);
+		close(bfd->fd);
+		bfd->fd = -1;
+		state->timer.cb = pcu_sock_timeout;
+		osmo_timer_schedule(&state->timer, 5, 0);
+		return 0;
+	}
+
+	bfd->when = BSC_FD_READ;
+	bfd->cb = pcu_sock_cb;
+	bfd->data = state;
+
+	rc = osmo_fd_register(bfd);
+	if (rc < 0) {
+		LOGP(DL1IF, LOGL_ERROR, "Could not register PCU fd: %d\n", rc);
+		close(bfd->fd);
+		talloc_free(state);
+		return rc;
+	}
+
+	LOGP(DL1IF, LOGL_NOTICE, "PCU-SYSMO socket has been connected\n");
+
+	pcu_sock_state = state;
+
+	return 0;
+}
+
+void pcu_l1if_close(void)
+{
+	struct pcu_sock_state *state = pcu_sock_state;
+	struct osmo_fd *bfd;
+
+	if (!state)
+		return;
+
+	if (osmo_timer_pending(&state->timer))
+		osmo_timer_del(&state->timer);
+
+	bfd = &state->conn_bfd;
+	if (bfd->fd > 0)
+		pcu_sock_close(state);
+	talloc_free(state);
+	pcu_sock_state = NULL;
+}
+
+static void pcu_sock_timeout(void *_priv)
+{
+	pcu_l1if_open();
+}
diff --git a/src/tbf.txt b/src/tbf.txt
new file mode 100644
index 0000000..3c06b39
--- /dev/null
+++ b/src/tbf.txt
@@ -0,0 +1,111 @@
+This document describes the handling of TBFs
+--------------------------------------------
+
+Notes:
+  TBF
+    Instance for one temporary block flow.
+
+  LLC Frame
+    Current frame to be transmitted or received. If tbf->llc_length is not 0,
+    the frame exists, if 0, the frame does not exist. If tbf->llc_index is not
+    0, parts of the frame have been transmitted or received so far.
+
+  LLC Queue
+    Queue of next frames to be transmitted.
+
+States:
+  GPRS_RLCMAC_ASSIGN
+    After a downlink TBF is created, it resides in this state until the
+    block flow can start. This is required to give the mobile time to listen
+    to connect to downlink PDCH.
+
+   GPRS_RLCMAC_FLOW,
+    During packet flow, this state indicates downlink and uplink TBF block
+    flow.
+
+   GPRS_RLCMAC_FINISHED,
+    Uplink TBF:
+    After final block is received AND all other blocks are completely
+    received, the state is entered. The PACKET CONTROL ACK is still not
+    received. (Counter N3103 is counted on each poll request.)
+    Downlink TBF:
+    All blocks including the final block has been transmitted. Not all
+    downlink blocks are acknowledged yet. (Counter N3015 is counted on each
+    poll request.)
+
+   GPRS_RLCMAC_WAIT_RELEASE,
+    The all blocks on downlink TBF have been acked by mobile. The penalty
+    timer T3192 is running on mobile.
+
+   GPRS_RLCMAC_RELEASING,
+    Wait for TFI/USF to be re-used. This state is entered when a counter
+    reaches it's maximum and T3169 is running.
+
+
+When downlink LLC PDU is received:
+
+  If downlink TBF exists for given TLLI, but not in RELEASING state:
+    If downlink TBF is in FLOW or FINISHED state:
+      Enqueue PDU to LLC Queue of TBF.
+      Done.
+    If downlink TBF is in WAIT RELEASE state:
+      Attach PDU to LLC Frame of TBF.
+      Put TBF back into FLOW state.
+      Done.
+  If dowlink TBF does not exists for given TLLI, or in RELEASING state:
+    Create new downlink TBF.
+    Attach PDU to LLC Frame of TBF.
+    If uplink TBF exists for given TLLI, but not in RELEASING state:
+      Assign packet channel PACCH.
+      Done.
+    If uplink TBF does not exists for given TLLI, or in RELEASING state:
+      Assign packet channel AGCH.
+      Done.
+
+When channel request for uplink TBF is received:
+
+  Create new uplink TBF.
+  If channel request was received on RACH
+    Assign packet channel AGCH.
+    Done
+  If channel request was received on PACCH
+    Assign packet channel PACCH.
+    Done
+
+Handling of LLC Frame of downlink TBF and LLC Queue of downlink TBF:
+
+  If a downlink TBF is created, the LLC PDU is attached to LLC Frame of TBF
+  (downlink flow is assigned to MS).
+
+  If a downlink TBF exists, the LLC PDU is enqueued to LLC Queue of TBF.
+
+  During transfer of downlink blocks, the LLC Queue is dequeued whenever all
+  segments of LLC Frame were used for generation of downlink blocks. (If a
+  block is partly filled with LLC Frame segment, its remaining space is filled
+  with the next LLC Frame from the Queue.)
+
+  If the transfer is finished, no more LLC Frames are dequeued from the LLC
+  Queue, in case a new frame has arrived during lifetime of TBF.
+
+  If the all downlink blocks have been acknowledged, the LLC Queue is
+  dequeued and an existing frame is attached to LLC Frame. If so, the new
+  state is FLOW (downlink flow is assigned to MS), otherwise WAIT RELEASE.
+
+  If a new LLC PDU is attached to LLC Frame during WAIT RELEASE state, the
+  state is changed to FLOW (downlink flow is assigned to MS).
+
+Handling of LLC Frame on uplink TBF:
+
+  Received uplink blocks are appended to LLC Frame of TBF. If the PDU is
+  complete, it is forwarded to the upper layer.
+
+Polling:
+  In order to poll uplink control block from MS, a special poll state and
+  frame number is stored at TBF. The scheduler reads that value and will not
+  assign uplink ressource for other TBFs at that frame number.
+
+  When there is no uplink transmission received on the block, a timeout is
+  indicated by layer 1 interface. There are two ways of checking timeout:
+   - The received frame is bad (BFI).
+   - The GSM indicates that the block should have been already received.
+