library/RLCMAC: Add partial support for EGPRS data block encoding/decoding

* RlcmacUlBlock and RlcmacDlBlock gain a new union field "egprs_data",
  which is chosen when msg contains an egprs data block. Hence one can
  use same structure for both gprs/egprs data and simply check
  "ischosen(block.data_egprs)" to know whether it contains egprs or gprs
  data block.
* C++ code in RLCMAC_EncDec.cc takes care of encoding and decoding of
  each data header type and exposes a generic ttcn3 struct
  "UlMacDataHeader" and "DlMacDataHeader". Decoded header type can be
  found in mac_hdr.header_type. This can be used t5ogether with CPS to
  get the MCS of the message received. Similarly, the encoder will use the
  same field to know how to encode the ttcn3 structure.
* In RLCMAC_EncDec.cc order of functions has been ordered to split
  between encoding and decoding, and inside these split between Ul and
  Dl messages.
* Only encoding of UL HeaderType3 and decoding of Dl HeaderType3 is
  implemented so far in RLCMAC_EncDec.cc. However, all code is already
  arranged and functions prepared (with FIXME fprintf) to easily add the
  missing header types once needed.
* Actually only the decoding of DL HeaderType3 has been tested to work so far.
  Encoding may still be missing to octet-align the data block after the header.
  All these wil lbe fixed once a test using them exists.

Change-Id: I2bc4f877a5e17c57ffa8cf05565dc8593b45aae8
diff --git a/library/RLCMAC_EncDec.cc b/library/RLCMAC_EncDec.cc
index 4ae65b1..777815f 100644
--- a/library/RLCMAC_EncDec.cc
+++ b/library/RLCMAC_EncDec.cc
@@ -1,3 +1,6 @@
+#include <stdint.h>
+#include <endian.h>
+
 #include "RLCMAC_Types.hh"
 #include "GSM_Types.hh"
 /* Decoding of TS 44.060 GPRS RLC/MAC blocks, portions requiring manual functions
@@ -14,48 +17,341 @@
 
 namespace RLCMAC__Types {
 
-OCTETSTRING enc__RlcmacDlDataBlock(const RlcmacDlDataBlock& si)
-{
-	RlcmacDlDataBlock in = si;
-	OCTETSTRING ret_val;
-	TTCN_Buffer ttcn_buffer;
-	int i;
+/////////////////////
+// INTENRAL HELPERS
+/////////////////////
 
-	/* Fix 'e' bit of initial header based on following blocks */
-	if (!in.blocks().is_bound() ||
-	    (in.blocks().size_of() == 1 && !in.blocks()[0].hdr().is_bound()))
-		in.mac__hdr().hdr__ext().e() = true;
-	else
-		in.mac__hdr().hdr__ext().e() = false;
+/* TS 04.60  10.3a.4.1.1 */
+struct gprs_rlc_ul_header_egprs_1 {
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+	uint8_t r:1,
+		 si:1,
+		 cv:4,
+		 tfi_hi:2;
+	uint8_t tfi_lo:3,
+		 bsn1_hi:5;
+	uint8_t bsn1_lo:6,
+		 bsn2_hi:2;
+	uint8_t bsn2_lo:8;
+	uint8_t cps:5,
+		 rsb:1,
+		 pi:1,
+		 spare_hi:1;
+	uint8_t spare_lo:6,
+		 dummy:2;
+#else
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+	uint8_t tfi_hi:2, cv:4, si:1, r:1;
+	uint8_t bsn1_hi:5, tfi_lo:3;
+	uint8_t bsn2_hi:2, bsn1_lo:6;
+	uint8_t bsn2_lo:8;
+	uint8_t spare_hi:1, pi:1, rsb:1, cps:5;
+	uint8_t dummy:2, spare_lo:6;
+#endif
+} __attribute__ ((packed));
 
-	/* use automatic/generated decoder for header */
-	in.mac__hdr().encode(DlMacDataHeader_descr_, ttcn_buffer, TTCN_EncDec::CT_RAW);
+/* TS 04.60  10.3a.4.2.1 */
+struct gprs_rlc_ul_header_egprs_2 {
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+	uint8_t r:1,
+		 si:1,
+		 cv:4,
+		 tfi_hi:2;
+	uint8_t tfi_lo:3,
+		 bsn1_hi:5;
+	uint8_t bsn1_lo:6,
+		 cps_hi:2;
+	uint8_t cps_lo:1,
+		 rsb:1,
+		 pi:1,
+		 spare_hi:5;
+	uint8_t spare_lo:5,
+		 dummy:3;
+#else
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+	uint8_t tfi_hi:2, cv:4, si:1, r:1;
+	uint8_t bsn1_hi:5, tfi_lo:3;
+	uint8_t cps_hi:2, bsn1_lo:6;
+	uint8_t spare_hi:5, pi:1, rsb:1, cps_lo:1;
+	uint8_t dummy:3, spare_lo:5;
+#endif
+} __attribute__ ((packed));
 
-	/* Add LI octets, if any */
-	if (in.blocks().is_bound() &&
-	    (in.blocks().size_of() != 1 || in.blocks()[0].hdr().is_bound())) {
-		/* first write LI octets */
-		for (i = 0; i < in.blocks().size_of(); i++) {
-			/* fix the 'E' bit in case it is not clear */
-			if (i < in.blocks().size_of()-1)
-				in.blocks()[i].hdr()().e() = false;
-			else
-				in.blocks()[i].hdr()().e() = true;
-			in.blocks()[i].hdr()().encode(LlcBlockHdr_descr_, ttcn_buffer, TTCN_EncDec::CT_RAW);
-		}
+/* TS 04.60  10.3a.4.3.1 */
+struct gprs_rlc_ul_header_egprs_3 {
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+	uint8_t r:1,
+		 si:1,
+		 cv:4,
+		 tfi_hi:2;
+	uint8_t tfi_lo:3,
+		 bsn1_hi:5;
+	uint8_t bsn1_lo:6,
+		 cps_hi:2;
+	uint8_t cps_lo:2,
+		 spb:2,
+		 rsb:1,
+		 pi:1,
+		 spare:1,
+		 dummy:1;
+#else
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+	uint8_t tfi_hi:2, cv:4, si:1, r:1;
+	uint8_t bsn1_hi:5, tfi_lo:3;
+	uint8_t cps_hi:2, bsn1_lo:6;
+	uint8_t dummy:1, spare:1, pi:1, rsb:1, spb:2, cps_lo:2;
+#endif
+} __attribute__ ((packed));
+
+struct gprs_rlc_dl_header_egprs_1 {
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+	uint8_t usf:3,
+		 es_p:2,
+		 rrbp:2,
+		 tfi_hi:1;
+	uint8_t tfi_lo:4,
+		 pr:2,
+		 bsn1_hi:2;
+	uint8_t bsn1_mid:8;
+	uint8_t bsn1_lo:1,
+		 bsn2_hi:7;
+	uint8_t bsn2_lo:3,
+		 cps:5;
+#else
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+	uint8_t tfi_hi:1, rrbp:2, es_p:2, usf:3;
+	uint8_t bsn1_hi:2, pr:2, tfi_lo:4;
+	uint8_t bsn1_mid:8;
+	uint8_t bsn2_hi:7, bsn1_lo:1;
+	uint8_t cps:5, bsn2_lo:3;
+#endif
+} __attribute__ ((packed));
+
+struct gprs_rlc_dl_header_egprs_2 {
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+	uint8_t usf:3,
+		 es_p:2,
+		 rrbp:2,
+		 tfi_hi:1;
+	uint8_t tfi_lo:4,
+		 pr:2,
+		 bsn1_hi:2;
+	uint8_t bsn1_mid:8;
+	uint8_t bsn1_lo:1,
+		 cps:3,
+		 dummy:4;
+#else
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+	uint8_t tfi_hi:1, rrbp:2, es_p:2, usf:3;
+	uint8_t bsn1_hi:2, pr:2, tfi_lo:4;
+	uint8_t bsn1_mid:8;
+	uint8_t dummy:4, cps:3, bsn1_lo:1;
+#endif
+} __attribute__ ((packed));
+
+struct gprs_rlc_dl_header_egprs_3 {
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+	uint8_t usf:3,
+		 es_p:2,
+		 rrbp:2,
+		 tfi_hi:1;
+	uint8_t tfi_lo:4,
+		 pr:2,
+		 bsn1_hi:2;
+	uint8_t bsn1_mid:8;
+	uint8_t bsn1_lo:1,
+		 cps:4,
+		 spb:2,
+		 dummy:1;
+#else
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianess.py) */
+	uint8_t tfi_hi:1, rrbp:2, es_p:2, usf:3;
+	uint8_t bsn1_hi:2, pr:2, tfi_lo:4;
+	uint8_t bsn1_mid:8;
+	uint8_t dummy:1, spb:2, cps:4, bsn1_lo:1;
+#endif
+} __attribute__ ((packed));
+
+static CodingScheme::enum_type payload_len_2_coding_scheme(size_t payload_len) {
+	switch (payload_len) {
+	case 23:
+		return CodingScheme::CS__1;
+	case 34:
+		return CodingScheme::CS__2;
+	case 40:
+		return CodingScheme::CS__3;
+	case 54:
+		return CodingScheme::CS__4;
+	case 27:
+		return CodingScheme::MCS__1;
+	case 33:
+		return CodingScheme::MCS__2;
+	case 42:
+		return CodingScheme::MCS__3;
+	case 49:
+		return CodingScheme::MCS__4;
+	case 61:
+		return CodingScheme::MCS__5;
+	case 79:
+		return CodingScheme::MCS__6;
+	case 119:
+		return CodingScheme::MCS__7;
+	case 142:
+		return CodingScheme::MCS__8;
+	case 155:
+		return CodingScheme::MCS__9;
+	default:
+		return CodingScheme::CS__1;
 	}
-	if (in.blocks().is_bound()) {
-		for (i = 0; i < in.blocks().size_of(); i++) {
-			if (!in.blocks()[i].is_bound())
-				continue;
-			ttcn_buffer.put_string(in.blocks()[i].payload());
-		}
-	}
-
-	ttcn_buffer.get_string(ret_val);
-	return ret_val;
 }
 
+static unsigned int coding_scheme_2_data_block_len(CodingScheme::enum_type mcs) {
+	switch (mcs) {
+	case CodingScheme::MCS__0:
+		return 0;
+	case CodingScheme::MCS__1:
+		return 22;
+	case CodingScheme::MCS__2:
+		return 28;
+	case CodingScheme::MCS__3:
+		return 37;
+	case CodingScheme::MCS__4:
+		return 44;
+	case CodingScheme::MCS__5:
+		return 56;
+	case CodingScheme::MCS__6:
+		return 74;
+	case CodingScheme::MCS__7:
+		return 56;
+	case CodingScheme::MCS__8:
+		return 68;
+	case CodingScheme::MCS__9:
+		return 74;
+	default:
+		return 22; /* MCS1*/
+	}
+}
+
+static uint8_t bs2uint8(const BITSTRING& bs)
+{
+	int len = bs.lengthof();
+	int i;
+	uint8_t res = 0;
+	for (i = 0; i < len; i++) {
+		res = res << 1;
+		res |= (bs[i].get_bit() ? 1 : 0);
+	}
+	return res;
+}
+
+/* determine the number of rlc data blocks and their size / offsets */
+static void
+setup_rlc_mac_priv(CodingScheme::enum_type mcs, EgprsHeaderType::enum_type hdrtype, boolean is_uplink,
+	unsigned int *n_calls, unsigned int *data_block_bits, unsigned int *data_block_offsets)
+{
+	unsigned int nc, dbl = 0, dbo[2] = {0,0};
+
+	dbl = coding_scheme_2_data_block_len(mcs);
+
+	switch (hdrtype) {
+	case EgprsHeaderType::RLCMAC__HDR__TYPE__1:
+		nc = 3;
+		dbo[0] = is_uplink ? 5*8 + 6 : 5*8 + 0;
+		dbo[1] = dbo[0] + dbl * 8 + 2;
+		break;
+	case EgprsHeaderType::RLCMAC__HDR__TYPE__2:
+		nc = 2;
+		dbo[0] = is_uplink ? 4*8 + 5 : 3*8 + 4;
+		break;
+	case EgprsHeaderType::RLCMAC__HDR__TYPE__3:
+		nc = 2;
+		dbo[0] = 3*8 + 7;
+		break;
+	default:
+		nc = 1;
+		break;
+	}
+
+	*n_calls = nc;
+	*data_block_bits = dbl * 8 + 2;
+	data_block_offsets[0] = dbo[0];
+	data_block_offsets[1] = dbo[1];
+}
+
+/* bit-shift the entire 'src' of length 'length_bytes' by 'offset_bits'
+ * and store the reuslt to caller-allocated 'buffer'.  The shifting is
+ * done lsb-first. */
+static void clone_aligned_buffer_lsbf(unsigned int offset_bits, unsigned int length_bytes,
+	const uint8_t *src, uint8_t *buffer)
+{
+	unsigned int hdr_bytes;
+	unsigned int extra_bits;
+	unsigned int i;
+
+	uint8_t c, last_c;
+	uint8_t *dst;
+
+	hdr_bytes = offset_bits / 8;
+	extra_bits = offset_bits % 8;
+
+	fprintf(stderr, "RLMAC: clone: hdr_bytes=%u extra_bits=%u (length_bytes=%u)\n", hdr_bytes, extra_bits, length_bytes);
+
+	if (extra_bits == 0) {
+		/* It is aligned already */
+		memcpy(buffer, src + hdr_bytes, length_bytes);
+		return;
+	}
+
+	dst = buffer;
+	src = src + hdr_bytes;
+	last_c = *(src++);
+
+	for (i = 0; i < length_bytes; i++) {
+		c = src[i];
+		*(dst++) = (last_c >> extra_bits) | (c << (8 - extra_bits));
+		last_c = c;
+	}
+}
+
+/* obtain an (aligned) EGPRS data block with given bit-offset and
+ * bit-length from the parent buffer */
+static void get_egprs_data_block(const TTCN_Buffer& orig_ttcn_buffer, unsigned int offset_bits,
+	unsigned int length_bits, TTCN_Buffer& dst_ttcn_buffer)
+{
+	const unsigned int initial_spare_bits = 6;
+	unsigned char *aligned_buf = NULL;
+	size_t min_src_length_bytes = (offset_bits + length_bits + 7) / 8;
+	size_t length_bytes = (initial_spare_bits + length_bits + 7) / 8;
+	size_t accepted_len = length_bytes;
+
+	fprintf(stderr, "RLMAC: trying to allocate %u bytes (orig is %zu bytes long with read pos %zu)\n", length_bytes, orig_ttcn_buffer.get_len(), orig_ttcn_buffer.get_pos());
+	dst_ttcn_buffer.get_end(aligned_buf, accepted_len);
+	fprintf(stderr, "RLMAC: For dst ptr=%p with length=%zu\n", aligned_buf, accepted_len);
+	if (accepted_len < length_bytes) {
+		fprintf(stderr, "RLMAC: ERROR! asked for %zu bytes but got %zu\n", length_bytes, accepted_len);
+	}
+
+	/* Copy the data out of the tvb to an aligned buffer */
+	clone_aligned_buffer_lsbf(
+		offset_bits - initial_spare_bits, length_bytes,
+		orig_ttcn_buffer.get_data(),
+		aligned_buf);
+
+	fprintf(stderr, "RLMAC: clone_aligned_buffer_lsbf success\n");
+
+	/* clear spare bits and move block header bits to the right */
+	aligned_buf[0] = aligned_buf[0] >> initial_spare_bits;
+
+	dst_ttcn_buffer.increase_length(length_bytes);
+}
+
+
+/////////////////////
+// DECODE
+/////////////////////
+
+/* DECODE DOWNLINK */
+
 RlcmacDlDataBlock dec__RlcmacDlDataBlock(const OCTETSTRING& stream)
 {
 	RlcmacDlDataBlock ret_val;
@@ -105,6 +401,534 @@
 	return ret_val;
 }
 
+static
+EgprsDlMacDataHeader dec__EgprsDlMacDataHeader_type1(const OCTETSTRING& stream)
+{
+	EgprsDlMacDataHeader ret_val;
+
+	fprintf(stderr, "FIXME: Not implemented! %s (%s:%u)\n", __func__, __FILE__, __LINE__);
+
+	return ret_val;
+}
+
+static
+EgprsDlMacDataHeader dec__EgprsDlMacDataHeader_type2(const OCTETSTRING& stream)
+{
+	EgprsDlMacDataHeader ret_val;
+
+	fprintf(stderr, "FIXME: Not implemented! %s (%s:%u)\n", __func__, __FILE__, __LINE__);
+
+	return ret_val;
+}
+
+static
+EgprsDlMacDataHeader dec__EgprsDlMacDataHeader_type3(const OCTETSTRING& stream)
+{
+	TTCN_Buffer ttcn_buffer(stream);
+	EgprsDlMacDataHeader ret_val;
+	const struct gprs_rlc_dl_header_egprs_3 *egprs3;
+	uint8_t tmp;
+
+	egprs3 = static_cast<const struct gprs_rlc_dl_header_egprs_3 *>
+		((const void *)ttcn_buffer.get_data());
+
+	ret_val.header__type() = EgprsHeaderType::RLCMAC__HDR__TYPE__3;
+	ret_val.tfi() = egprs3->tfi_lo << 1 | egprs3->tfi_hi << 0;
+	ret_val.rrbp() = egprs3->rrbp;
+	tmp = egprs3->es_p;
+	ret_val.esp() = BITSTRING(2, &tmp);
+	ret_val.usf() = egprs3->usf;
+	ret_val.bsn1() = egprs3->bsn1_lo << 10 | egprs3->bsn1_mid << 2 | egprs3->bsn1_hi;
+	ret_val.bsn2__offset() = 0; /*TODO: mark optional and not set ? */
+	ret_val.pr() = egprs3->pr;
+	ret_val.spb() = egprs3->spb;
+	ret_val.cps() = egprs3->cps;
+
+	ttcn_buffer.increase_pos(sizeof(*egprs3));
+	return ret_val;
+}
+
+static
+RlcmacDlEgprsDataBlock dec__RlcmacDlEgprsDataBlock(const OCTETSTRING& stream, CodingScheme::enum_type mcs)
+{
+	RlcmacDlEgprsDataBlock ret_val;
+	TTCN_Buffer ttcn_buffer(stream);
+	TTCN_Buffer aligned_buffer;
+	int num_llc_blocks = 0;
+	unsigned int data_block_bits, data_block_offsets[2];
+	unsigned int num_calls;
+	const uint8_t *ti_e;
+
+	switch (mcs) {
+	case CodingScheme::MCS__0:
+	case CodingScheme::MCS__1:
+	case CodingScheme::MCS__2:
+	case CodingScheme::MCS__3:
+	case CodingScheme::MCS__4:
+		ret_val.mac__hdr() = dec__EgprsDlMacDataHeader_type3(stream);
+		break;
+	case CodingScheme::MCS__5:
+	case CodingScheme::MCS__6:
+		ret_val.mac__hdr() = dec__EgprsDlMacDataHeader_type2(stream);
+		break;
+	case CodingScheme::MCS__7:
+	case CodingScheme::MCS__8:
+	case CodingScheme::MCS__9:
+		ret_val.mac__hdr() = dec__EgprsDlMacDataHeader_type1(stream);
+		break;
+	}
+	setup_rlc_mac_priv(mcs, ret_val.mac__hdr().header__type(), false,
+			   &num_calls, &data_block_bits, data_block_offsets);
+	get_egprs_data_block(ttcn_buffer, data_block_offsets[0], data_block_bits, aligned_buffer);
+
+	ti_e = aligned_buffer.get_read_data();
+	ret_val.fbi() = *ti_e & 0x02 ? true : false;
+	ret_val.e() = *ti_e & 0x01 ? true : false;
+	aligned_buffer.increase_pos(1);
+
+	/* optional extension octets, containing LI+E of Llc blocks */
+	if (ret_val.e() == false) {
+		/* extension octet follows, i.e. optional Llc length octets */
+		while (1) {
+			/* decode one more extension octet with LlcBlocHdr inside */
+			EgprsLlcBlock lb;
+			lb.hdr()().decode(EgprsLlcBlockHdr_descr_, aligned_buffer, TTCN_EncDec::CT_RAW);
+			ret_val.blocks()[num_llc_blocks++] = lb;
+
+			/* if E == '1'B, we can proceed further */
+			if (lb.hdr()().e() == true)
+				break;
+		}
+	}
+
+	/* RLC blocks at end */
+	if (ret_val.blocks().is_bound()) {
+		for (int i = 0; i < ret_val.blocks().size_of(); i++) {
+			unsigned int length = ret_val.blocks()[i].hdr()().length__ind();
+			if (length > aligned_buffer.get_read_len())
+				length = aligned_buffer.get_read_len();
+			ret_val.blocks()[i].payload() = OCTETSTRING(length, aligned_buffer.get_read_data());
+			aligned_buffer.increase_pos(length);
+		}
+	}
+
+	return ret_val;
+}
+
+RlcmacDlBlock dec__RlcmacDlBlock(const OCTETSTRING& stream)
+{
+	RlcmacDlBlock ret_val;
+	size_t stream_len = stream.lengthof();
+	CodingScheme::enum_type mcs = payload_len_2_coding_scheme(stream_len);
+	unsigned char pt;
+
+	switch (mcs) {
+	case CodingScheme::CS__1:
+	case CodingScheme::CS__2:
+	case CodingScheme::CS__3:
+	case CodingScheme::CS__4:
+		pt = stream[0].get_octet() >> 6;
+		if (pt == MacPayloadType::MAC__PT__RLC__DATA)
+			ret_val.data() = dec__RlcmacDlDataBlock(stream);
+		else
+			ret_val.ctrl() = dec__RlcmacDlCtrlBlock(stream);
+		break;
+	case CodingScheme::MCS__0:
+	case CodingScheme::MCS__1:
+	case CodingScheme::MCS__2:
+	case CodingScheme::MCS__3:
+	case CodingScheme::MCS__4:
+	case CodingScheme::MCS__5:
+	case CodingScheme::MCS__6:
+	case CodingScheme::MCS__7:
+	case CodingScheme::MCS__8:
+	case CodingScheme::MCS__9:
+		ret_val.data__egprs() = dec__RlcmacDlEgprsDataBlock(stream, mcs);
+		break;
+	}
+	return ret_val;
+}
+
+/* DECODE UPLINK */
+
+RlcmacUlDataBlock dec__RlcmacUlDataBlock(const OCTETSTRING& stream)
+{
+	RlcmacUlDataBlock ret_val;
+	TTCN_Buffer ttcn_buffer(stream);
+	int num_llc_blocks = 0;
+
+	TTCN_Logger::begin_event(TTCN_Logger::DEBUG_ENCDEC);
+	TTCN_Logger::log_event_str("==================================\n"
+				"dec_RlcmacUlDataBlock(): Stream before decoding: ");
+	stream.log();
+	TTCN_Logger::end_event();
+
+	/* use automatic/generated decoder for header */
+	ret_val.mac__hdr().decode(UlMacDataHeader_descr_, ttcn_buffer, TTCN_EncDec::CT_RAW);
+
+	TTCN_Logger::begin_event(TTCN_Logger::DEBUG_ENCDEC);
+	TTCN_Logger::log_event_str("dec_RlcmacUlDataBlock(): Stream after decoding hdr: ");
+	ttcn_buffer.log();
+	TTCN_Logger::end_event();
+	TTCN_Logger::begin_event(TTCN_Logger::DEBUG_ENCDEC);
+	TTCN_Logger::log_event_str("dec_RlcmacUlDataBlock(): ret_val after decoding hdr: ");
+	ret_val.log();
+	TTCN_Logger::end_event();
+
+	/* Manually decoder remainder of ttcn_buffer, containing optional header octets,
+	 * optional tlli, optional pfi and LLC Blocks */
+
+	/* optional extension octets, containing LI+M+E of Llc blocks */
+	if (ret_val.mac__hdr().e() == false) {
+		/* extension octet follows, i.e. optional Llc length octets */
+		while (1) {
+			/* decode one more extension octet with LlcBlocHdr inside */
+			LlcBlock lb;
+			lb.hdr()().decode(LlcBlockHdr_descr_, ttcn_buffer, TTCN_EncDec::CT_RAW);
+			ret_val.blocks()[num_llc_blocks++] = lb;
+
+			TTCN_Logger::begin_event(TTCN_Logger::DEBUG_ENCDEC);
+			TTCN_Logger::log_event_str("dec_RlcmacUlDataBlock(): Stream after decoding ExtOct: ");
+			ttcn_buffer.log();
+			TTCN_Logger::end_event();
+			TTCN_Logger::begin_event(TTCN_Logger::DEBUG_ENCDEC);
+			TTCN_Logger::log_event_str("dec_RlcmacUlDataBlock(): ret_val after decoding ExtOct: ");
+			ret_val.log();
+			TTCN_Logger::end_event();
+
+			/* if E == '1'B, we can proceed further */
+			if (lb.hdr()().e() == true)
+				break;
+		}
+	}
+
+	/* parse optional TLLI */
+	if (ret_val.mac__hdr().tlli__ind()) {
+		ret_val.tlli() = OCTETSTRING(4, ttcn_buffer.get_read_data());
+		ttcn_buffer.increase_pos(4);
+	}
+	/* parse optional PFI */
+	if (ret_val.mac__hdr().pfi__ind()) {
+		ret_val.pfi().decode(RlcmacUlDataBlock_pfi_descr_, ttcn_buffer, TTCN_EncDec::CT_RAW);
+	}
+
+	/* RLC blocks at end */
+	if (ret_val.mac__hdr().e() == true) {
+		LlcBlock lb;
+		unsigned int length = ttcn_buffer.get_read_len();
+		/* LI not present: The Upper Layer PDU that starts with the current RLC data block either
+		 * fills the current RLC data block precisely or continues in the following in-sequence RLC
+		 * data block */
+		lb.payload() = OCTETSTRING(length, ttcn_buffer.get_read_data());
+		ttcn_buffer.increase_pos(length);
+		ret_val.blocks()[0] = lb;
+	} else {
+		if (ret_val.blocks().is_bound()) {
+			for (int i = 0; i < ret_val.blocks().size_of(); i++) {
+				unsigned int length = ret_val.blocks()[i].hdr()().length__ind();
+				if (length > ttcn_buffer.get_read_len())
+					length = ttcn_buffer.get_read_len();
+				ret_val.blocks()[i].payload() = OCTETSTRING(length, ttcn_buffer.get_read_data());
+				ttcn_buffer.increase_pos(length);
+			}
+		}
+	}
+
+	TTCN_Logger::begin_event(TTCN_Logger::DEBUG_ENCDEC);
+	TTCN_Logger::log_event_str("dec_RlcmacUlDataBlock(): Stream before return: ");
+	ttcn_buffer.log();
+	TTCN_Logger::end_event();
+	TTCN_Logger::begin_event(TTCN_Logger::DEBUG_ENCDEC);
+	TTCN_Logger::log_event_str("dec_RlcmacUlDataBlock(): ret_val before return: ");
+	ret_val.log();
+	TTCN_Logger::end_event();
+
+	return ret_val;
+}
+
+static
+EgprsUlMacDataHeader dec__EgprsUlMacDataHeader_type1(const OCTETSTRING& stream)
+{
+	EgprsUlMacDataHeader ret_val;
+
+	fprintf(stderr, "FIXME: Not implemented! %s (%s:%u)\n", __func__, __FILE__, __LINE__);
+
+	return ret_val;
+}
+
+static
+EgprsUlMacDataHeader dec__EgprsUlMacDataHeader_type2(const OCTETSTRING& stream)
+{
+	EgprsUlMacDataHeader ret_val;
+
+	fprintf(stderr, "FIXME: Not implemented! %s (%s:%u)\n", __func__, __FILE__, __LINE__);
+
+	return ret_val;
+}
+
+static
+EgprsUlMacDataHeader dec__EgprsUlMacDataHeader_type3(const OCTETSTRING& stream)
+{
+	EgprsUlMacDataHeader ret_val;
+
+	fprintf(stderr, "FIXME: Not implemented! %s (%s:%u)\n", __func__, __FILE__, __LINE__);
+
+	return ret_val;
+}
+
+RlcmacUlEgprsDataBlock dec__RlcmacUlEgprsDataBlock(const OCTETSTRING& stream, CodingScheme::enum_type mcs)
+{
+	RlcmacUlEgprsDataBlock ret_val;
+	TTCN_Buffer ttcn_buffer(stream);
+	TTCN_Buffer aligned_buffer;
+	int num_llc_blocks = 0;
+	unsigned int data_block_bits, data_block_offsets[2];
+	unsigned int num_calls;
+	const uint8_t *ti_e;
+
+	switch (mcs) {
+	case CodingScheme::MCS__1:
+	case CodingScheme::MCS__2:
+	case CodingScheme::MCS__3:
+	case CodingScheme::MCS__4:
+		ret_val.mac__hdr() = dec__EgprsUlMacDataHeader_type3(stream);
+		break;
+	case CodingScheme::MCS__5:
+	case CodingScheme::MCS__6:
+		ret_val.mac__hdr() = dec__EgprsUlMacDataHeader_type2(stream);
+		break;
+	case CodingScheme::MCS__7:
+	case CodingScheme::MCS__8:
+	case CodingScheme::MCS__9:
+		ret_val.mac__hdr() = dec__EgprsUlMacDataHeader_type1(stream);
+		break;
+	}
+	setup_rlc_mac_priv(mcs, ret_val.mac__hdr().header__type(), true,
+			   &num_calls, &data_block_bits, data_block_offsets);
+	get_egprs_data_block(ttcn_buffer, data_block_offsets[0], data_block_bits, aligned_buffer);
+
+	ti_e = aligned_buffer.get_read_data();
+	ret_val.tlli__ind() = *ti_e & 0x02 ? true : false;
+	ret_val.e() = *ti_e & 0x01 ? true : false;
+	aligned_buffer.increase_pos(1);
+
+	/* Manually decoder remainder of aligned_buffer, containing optional header octets,
+	 * optional tlli, optional pfi and LLC Blocks */
+
+	/* optional extension octets, containing LI+M+E of Llc blocks */
+	if (ret_val.e() == false) {
+		/* extension octet follows, i.e. optional Llc length octets */
+		while (1) {
+			/* decode one more extension octet with LlcBlocHdr inside */
+			EgprsLlcBlock lb;
+			lb.hdr()().decode(EgprsLlcBlockHdr_descr_, aligned_buffer, TTCN_EncDec::CT_RAW);
+			ret_val.blocks()[num_llc_blocks++] = lb;
+
+			/* if E == '1'B, we can proceed further */
+			if (lb.hdr()().e() == true)
+				break;
+		}
+	}
+
+	/* parse optional TLLI */
+	if (ret_val.tlli__ind()) {
+		ret_val.tlli() = OCTETSTRING(4, aligned_buffer.get_read_data());
+		aligned_buffer.increase_pos(4);
+	}
+	/* parse optional PFI */
+	if (ret_val.mac__hdr().pfi__ind()) {
+		ret_val.pfi().decode(RlcmacUlDataBlock_pfi_descr_, aligned_buffer, TTCN_EncDec::CT_RAW);
+	}
+
+	/* RLC blocks at end */
+	if (ret_val.e() == true) {
+		EgprsLlcBlock lb;
+		unsigned int length = aligned_buffer.get_read_len();
+		/* LI not present: The Upper Layer PDU that starts with the current RLC data block either
+		 * fills the current RLC data block precisely or continues in the following in-sequence RLC
+		 * data block */
+		lb.payload() = OCTETSTRING(length, aligned_buffer.get_read_data());
+		aligned_buffer.increase_pos(length);
+		ret_val.blocks()[0] = lb;
+	} else {
+		if (ret_val.blocks().is_bound()) {
+			for (int i = 0; i < ret_val.blocks().size_of(); i++) {
+				unsigned int length = ret_val.blocks()[i].hdr()().length__ind();
+				if (length > aligned_buffer.get_read_len())
+					length = aligned_buffer.get_read_len();
+				ret_val.blocks()[i].payload() = OCTETSTRING(length, aligned_buffer.get_read_data());
+				aligned_buffer.increase_pos(length);
+			}
+		}
+	}
+
+	return ret_val;
+}
+
+RlcmacUlBlock dec__RlcmacUlBlock(const OCTETSTRING& stream)
+{
+	RlcmacUlBlock ret_val;
+	size_t stream_len = stream.lengthof();
+	CodingScheme::enum_type mcs = payload_len_2_coding_scheme(stream_len);
+	unsigned char pt;
+
+	switch (mcs) {
+	case CodingScheme::CS__1:
+	case CodingScheme::CS__2:
+	case CodingScheme::CS__3:
+	case CodingScheme::CS__4:
+		pt = stream[0].get_octet() >> 6;
+		if (pt == MacPayloadType::MAC__PT__RLC__DATA)
+			ret_val.data() = dec__RlcmacUlDataBlock(stream);
+		else
+			ret_val.ctrl() = dec__RlcmacUlCtrlBlock(stream);
+		break;
+	case CodingScheme::MCS__1:
+	case CodingScheme::MCS__2:
+	case CodingScheme::MCS__3:
+	case CodingScheme::MCS__4:
+	case CodingScheme::MCS__5:
+	case CodingScheme::MCS__6:
+	case CodingScheme::MCS__7:
+	case CodingScheme::MCS__8:
+	case CodingScheme::MCS__9:
+		ret_val.data__egprs() = dec__RlcmacUlEgprsDataBlock(stream, mcs);
+		break;
+	}
+
+	return ret_val;
+}
+
+
+/////////////////////
+// ENCODE
+/////////////////////
+
+/* ENCODE DOWNLINK */
+
+OCTETSTRING enc__RlcmacDlDataBlock(const RlcmacDlDataBlock& si)
+{
+	RlcmacDlDataBlock in = si;
+	OCTETSTRING ret_val;
+	TTCN_Buffer ttcn_buffer;
+	int i;
+
+	/* Fix 'e' bit of initial header based on following blocks */
+	if (!in.blocks().is_bound() ||
+	    (in.blocks().size_of() == 1 && !in.blocks()[0].hdr().is_bound()))
+		in.mac__hdr().hdr__ext().e() = true;
+	else
+		in.mac__hdr().hdr__ext().e() = false;
+
+	/* use automatic/generated decoder for header */
+	in.mac__hdr().encode(DlMacDataHeader_descr_, ttcn_buffer, TTCN_EncDec::CT_RAW);
+
+	/* Add LI octets, if any */
+	if (in.blocks().is_bound() &&
+	    (in.blocks().size_of() != 1 || in.blocks()[0].hdr().is_bound())) {
+		/* first write LI octets */
+		for (i = 0; i < in.blocks().size_of(); i++) {
+			/* fix the 'E' bit in case it is not clear */
+			if (i < in.blocks().size_of()-1)
+				in.blocks()[i].hdr()().e() = false;
+			else
+				in.blocks()[i].hdr()().e() = true;
+			in.blocks()[i].hdr()().encode(LlcBlockHdr_descr_, ttcn_buffer, TTCN_EncDec::CT_RAW);
+		}
+	}
+	if (in.blocks().is_bound()) {
+		for (i = 0; i < in.blocks().size_of(); i++) {
+			if (!in.blocks()[i].is_bound())
+				continue;
+			ttcn_buffer.put_string(in.blocks()[i].payload());
+		}
+	}
+
+	ttcn_buffer.get_string(ret_val);
+	return ret_val;
+}
+
+static
+void enc__RlcmacDlEgprsDataHeader_type1(const EgprsDlMacDataHeader& si, TTCN_Buffer& ttcn_buffer)
+{
+	fprintf(stderr, "FIXME: Not implemented! %s (%s:%u)\n", __func__, __FILE__, __LINE__);
+}
+
+static
+void enc__RlcmacDlEgprsDataHeader_type2(const EgprsDlMacDataHeader& si, TTCN_Buffer& ttcn_buffer)
+{
+	fprintf(stderr, "FIXME: Not implemented! %s (%s:%u)\n", __func__, __FILE__, __LINE__);
+}
+
+static
+void enc__RlcmacDlEgprsDataHeader_type3(const EgprsDlMacDataHeader& si, TTCN_Buffer& ttcn_buffer)
+{
+	fprintf(stderr, "FIXME: Not implemented! %s (%s:%u)\n", __func__, __FILE__, __LINE__);
+}
+
+OCTETSTRING enc__RlcmacDlEgprsDataBlock(const RlcmacDlEgprsDataBlock& si)
+{
+	RlcmacDlEgprsDataBlock in = si;
+	OCTETSTRING ret_val;
+	TTCN_Buffer ttcn_buffer;
+	int i;
+
+	/* Fix 'e' bit of initial header based on following blocks */
+	if (!in.blocks().is_bound() ||
+	    (in.blocks().size_of() == 1 && !in.blocks()[0].hdr().is_bound()))
+		in.e() = true;
+	else
+		in.e() = false;
+
+	switch (in.mac__hdr().header__type()) {
+	case EgprsHeaderType::RLCMAC__HDR__TYPE__1:
+		enc__RlcmacDlEgprsDataHeader_type1(si.mac__hdr(), ttcn_buffer);
+		break;
+	case EgprsHeaderType::RLCMAC__HDR__TYPE__2:
+		enc__RlcmacDlEgprsDataHeader_type2(si.mac__hdr(), ttcn_buffer);
+		break;
+	case EgprsHeaderType::RLCMAC__HDR__TYPE__3:
+		enc__RlcmacDlEgprsDataHeader_type3(si.mac__hdr(), ttcn_buffer);
+	default:
+		break; /* TODO: error */
+	}
+
+	/* Add LI octets, if any */
+	if (in.blocks().is_bound() &&
+	    (in.blocks().size_of() != 1 || in.blocks()[0].hdr().is_bound())) {
+		/* first write LI octets */
+		for (i = 0; i < in.blocks().size_of(); i++) {
+			/* fix the 'E' bit in case it is not clear */
+			if (i < in.blocks().size_of()-1)
+				in.blocks()[i].hdr()().e() = false;
+			else
+				in.blocks()[i].hdr()().e() = true;
+			in.blocks()[i].hdr()().encode(EgprsLlcBlockHdr_descr_, ttcn_buffer, TTCN_EncDec::CT_RAW);
+		}
+	}
+	if (in.blocks().is_bound()) {
+		for (i = 0; i < in.blocks().size_of(); i++) {
+			if (!in.blocks()[i].is_bound())
+				continue;
+			ttcn_buffer.put_string(in.blocks()[i].payload());
+		}
+	}
+
+	ttcn_buffer.get_string(ret_val);
+	return ret_val;
+}
+
+OCTETSTRING enc__RlcmacDlBlock(const RlcmacDlBlock& si)
+{
+	if (si.ischosen(RlcmacDlBlock::ALT_data__egprs))
+		return enc__RlcmacDlEgprsDataBlock(si.data__egprs());
+	else if (si.ischosen(RlcmacDlBlock::ALT_data))
+		return enc__RlcmacDlDataBlock(si.data());
+	else
+		return enc__RlcmacDlCtrlBlock(si.ctrl());
+}
+
+/* ENCODE UPLINK */
 
 OCTETSTRING enc__RlcmacUlDataBlock(const RlcmacUlDataBlock& si)
 {
@@ -187,143 +1011,140 @@
 	return ret_val;
 }
 
-RlcmacUlDataBlock dec__RlcmacUlDataBlock(const OCTETSTRING& stream)
+static
+void enc__RlcmacUlEgprsDataHeader_type1(const EgprsUlMacDataHeader& si, TTCN_Buffer& ttcn_buffer)
 {
-	RlcmacUlDataBlock ret_val;
-	TTCN_Buffer ttcn_buffer(stream);
-	int num_llc_blocks = 0;
+	fprintf(stderr, "FIXME: Not implemented! %s (%s:%u)\n", __func__, __FILE__, __LINE__);
+}
 
-	TTCN_Logger::begin_event(TTCN_Logger::DEBUG_ENCDEC);
-	TTCN_Logger::log_event_str("==================================\n"
-				"dec_RlcmacUlDataBlock(): Stream before decoding: ");
-	stream.log();
-	TTCN_Logger::end_event();
+static
+void enc__RlcmacUlEgprsDataHeader_type2(const EgprsUlMacDataHeader& si, TTCN_Buffer& ttcn_buffer)
+{
+	fprintf(stderr, "FIXME: Not implemented! %s (%s:%u)\n", __func__, __FILE__, __LINE__);
+}
 
-	/* use automatic/generated decoder for header */
-	ret_val.mac__hdr().decode(UlMacDataHeader_descr_, ttcn_buffer, TTCN_EncDec::CT_RAW);
+static
+void enc__RlcmacUlEgprsDataHeader_type3(const EgprsUlMacDataHeader& si, TTCN_Buffer& ttcn_buffer)
+{
+	struct gprs_rlc_ul_header_egprs_3 egprs3;
 
-	TTCN_Logger::begin_event(TTCN_Logger::DEBUG_ENCDEC);
-	TTCN_Logger::log_event_str("dec_RlcmacUlDataBlock(): Stream after decoding hdr: ");
-	ttcn_buffer.log();
-	TTCN_Logger::end_event();
-	TTCN_Logger::begin_event(TTCN_Logger::DEBUG_ENCDEC);
-	TTCN_Logger::log_event_str("dec_RlcmacUlDataBlock(): ret_val after decoding hdr: ");
-	ret_val.log();
-	TTCN_Logger::end_event();
+	egprs3.r = bs2uint8(si.r__ri());
+	egprs3.si = bs2uint8(si.foi__si());
+	egprs3.cv = si.countdown();
+	egprs3.tfi_hi = si.tfi() >> 0;
+	egprs3.tfi_lo = si.tfi() >> 1;
+	egprs3.bsn1_hi = si.bsn1() >> 0;
+	egprs3.bsn1_lo = si.bsn1() >> 5;
+	egprs3.cps_hi = si.cps() >> 0;
+	egprs3.cps_lo = si.cps() >> 2;
+	egprs3.spb = bs2uint8(si.spb());
+	egprs3.rsb = bs2uint8(si.spb());
+	egprs3.pi = si.pfi__ind();
+	egprs3.spare = 0;
+	egprs3.dummy = 0;
 
-	/* Manually decoder remainder of ttcn_buffer, containing optional header octets, 
-	 * optional tlli, optional pfi and LLC Blocks */
+	ttcn_buffer.put_s(sizeof(egprs3), (const unsigned char *)&egprs3);
+}
 
-	/* optional extension octets, containing LI+M+E of Llc blocks */
-	if (ret_val.mac__hdr().e() == false) {
-		/* extension octet follows, i.e. optional Llc length octets */
-		while (1) {
-			/* decode one more extension octet with LlcBlocHdr inside */
-			LlcBlock lb;
-			lb.hdr()().decode(LlcBlockHdr_descr_, ttcn_buffer, TTCN_EncDec::CT_RAW);
-			ret_val.blocks()[num_llc_blocks++] = lb;
+OCTETSTRING enc__RlcmacUlEgprsDataBlock(const RlcmacUlEgprsDataBlock& si)
+{
+	RlcmacUlEgprsDataBlock in = si;
+	OCTETSTRING ret_val;
+	TTCN_Buffer ttcn_buffer;
+	int i;
 
-			TTCN_Logger::begin_event(TTCN_Logger::DEBUG_ENCDEC);
-			TTCN_Logger::log_event_str("dec_RlcmacUlDataBlock(): Stream after decoding ExtOct: ");
-			ttcn_buffer.log();
-			TTCN_Logger::end_event();
-			TTCN_Logger::begin_event(TTCN_Logger::DEBUG_ENCDEC);
-			TTCN_Logger::log_event_str("dec_RlcmacUlDataBlock(): ret_val after decoding ExtOct: ");
-			ret_val.log();
-			TTCN_Logger::end_event();
-
-			/* if E == '1'B, we can proceed further */
-			if (lb.hdr()().e() == true)
-				break;
-		}
-	}
-
-	/* parse optional TLLI */
-	if (ret_val.mac__hdr().tlli__ind()) {
-		ret_val.tlli() = OCTETSTRING(4, ttcn_buffer.get_read_data());
-		ttcn_buffer.increase_pos(4);
-	}
-	/* parse optional PFI */
-	if (ret_val.mac__hdr().pfi__ind()) {
-		ret_val.pfi().decode(RlcmacUlDataBlock_pfi_descr_, ttcn_buffer, TTCN_EncDec::CT_RAW);
-	}
-
-	/* RLC blocks at end */
-	if (ret_val.mac__hdr().e() == true) {
-		LlcBlock lb;
-		unsigned int length = ttcn_buffer.get_read_len();
-		/* LI not present: The Upper Layer PDU that starts with the current RLC data block either
-		 * fills the current RLC data block precisely or continues in the following in-sequence RLC
-		 * data block */
-		lb.payload() = OCTETSTRING(length, ttcn_buffer.get_read_data());
-		ttcn_buffer.increase_pos(length);
-		ret_val.blocks()[0] = lb;
+	if (!in.blocks().is_bound()) {
+		/* we don't have nay blocks: Add length value (zero) */
+		in.e() = false; /* E=0: extension octet follows */
+	} else if (in.blocks().size_of() == 1 && in.blocks()[0].hdr() == OMIT_VALUE) {
+		/* If there's only a single block, and that block has no HDR value defined, */
+		in.e() = true; /* E=0: extension octet follows */
 	} else {
-		if (ret_val.blocks().is_bound()) {
-			for (int i = 0; i < ret_val.blocks().size_of(); i++) {
-				unsigned int length = ret_val.blocks()[i].hdr()().length__ind();
-				if (length > ttcn_buffer.get_read_len())
-					length = ttcn_buffer.get_read_len();
-				ret_val.blocks()[i].payload() = OCTETSTRING(length, ttcn_buffer.get_read_data());
-				ttcn_buffer.increase_pos(length);
+		/* Length value */
+		in.e() = false;
+	}
+
+	/* Fix other presence indications */
+	in.tlli__ind() = in.tlli().is_bound() && in.tlli() != OMIT_VALUE;
+	in.mac__hdr().pfi__ind() = in.pfi().is_bound() && in.pfi() != OMIT_VALUE;
+
+	switch (in.mac__hdr().header__type()) {
+	case EgprsHeaderType::RLCMAC__HDR__TYPE__1:
+		enc__RlcmacUlEgprsDataHeader_type1(si.mac__hdr(), ttcn_buffer);
+		break;
+	case EgprsHeaderType::RLCMAC__HDR__TYPE__2:
+		enc__RlcmacUlEgprsDataHeader_type2(si.mac__hdr(), ttcn_buffer);
+		break;
+	case EgprsHeaderType::RLCMAC__HDR__TYPE__3:
+		enc__RlcmacUlEgprsDataHeader_type3(si.mac__hdr(), ttcn_buffer);
+	default:
+		break; /* TODO: error */
+	}
+
+	if (in.e() == false) {
+		/* Add LI octets, if any */
+		if (!in.blocks().is_bound()) {
+			ttcn_buffer.put_c(0x01); /* M=0, E=1 LEN=0 */
+		} else {
+			for (i = 0; i < in.blocks().size_of(); i++) {
+#if 0
+				/* check for penultimate block */
+				if (i == in.blocks().size_of()-2) {
+					/* if last block has no header, no more LI */
+					if (in.blocks()[i+1].hdr() == OMIT_VALUE) {
+						in.blocks()[i].hdr()().more() = true;
+					} else {
+						/* header present, we have to encode LI */
+						in.blocks()[i].hdr()().more() = false;
+						in.blocks()[i].hdr()().length__ind() =
+								in.blocks()[i+1].payload().lengthof();
+					}
+				} else if (i < in.blocks().size_of()-2) {
+					/* one of the first blocks, before the penultimate or last */
+					in.blocks()[i].hdr()().e() = false; /* LI present */
+					/* re-compute length */
+					in.blocks()[i].hdr()().length__ind() =
+								in.blocks()[i+1].payload().lengthof();
+				}
+				/* Encode LI octet if E=0 */
+				}
+#endif
+				if (in.blocks()[i].hdr() != OMIT_VALUE) {
+					in.blocks()[i].hdr()().encode(EgprsLlcBlockHdr_descr_, ttcn_buffer,
+									TTCN_EncDec::CT_RAW);
+				}
 			}
 		}
 	}
 
-	TTCN_Logger::begin_event(TTCN_Logger::DEBUG_ENCDEC);
-	TTCN_Logger::log_event_str("dec_RlcmacUlDataBlock(): Stream before return: ");
-	ttcn_buffer.log();
-	TTCN_Logger::end_event();
-	TTCN_Logger::begin_event(TTCN_Logger::DEBUG_ENCDEC);
-	TTCN_Logger::log_event_str("dec_RlcmacUlDataBlock(): ret_val before return: ");
-	ret_val.log();
-	TTCN_Logger::end_event();
+	if (in.tlli__ind()) {
+		ttcn_buffer.put_string(in.tlli());
+	}
 
+	if (in.mac__hdr().pfi__ind()) {
+		in.pfi().encode(RlcmacUlDataBlock_pfi_descr_, ttcn_buffer, TTCN_EncDec::CT_RAW);
+	}
+
+	if (in.blocks().is_bound()) {
+		for (i = 0; i < in.blocks().size_of(); i++) {
+			if (!in.blocks()[i].is_bound())
+				continue;
+			ttcn_buffer.put_string(in.blocks()[i].payload());
+		}
+	}
+
+	ttcn_buffer.get_string(ret_val);
 	return ret_val;
 }
 
 OCTETSTRING enc__RlcmacUlBlock(const RlcmacUlBlock& si)
 {
-	if (si.ischosen(RlcmacUlBlock::ALT_data))
+	if (si.ischosen(RlcmacUlBlock::ALT_data__egprs))
+		return enc__RlcmacUlEgprsDataBlock(si.data__egprs());
+	else if (si.ischosen(RlcmacUlBlock::ALT_data))
 		return enc__RlcmacUlDataBlock(si.data());
 	else
 		return enc__RlcmacUlCtrlBlock(si.ctrl());
 }
 
-OCTETSTRING enc__RlcmacDlBlock(const RlcmacDlBlock& si)
-{
-	if (si.ischosen(RlcmacDlBlock::ALT_data))
-		return enc__RlcmacDlDataBlock(si.data());
-	else
-		return enc__RlcmacDlCtrlBlock(si.ctrl());
-}
-
-
-RlcmacUlBlock dec__RlcmacUlBlock(const OCTETSTRING& stream)
-{
-	RlcmacUlBlock ret_val;
-	unsigned char pt = stream[0].get_octet() >> 6;
-
-	if (pt == MacPayloadType::MAC__PT__RLC__DATA)
-		ret_val.data() = dec__RlcmacUlDataBlock(stream);
-	else
-		ret_val.ctrl() = dec__RlcmacUlCtrlBlock(stream);
-
-	return ret_val;
-}
-
-RlcmacDlBlock dec__RlcmacDlBlock(const OCTETSTRING& stream)
-{
-	RlcmacDlBlock ret_val;
-	unsigned char pt = stream[0].get_octet() >> 6;
-
-	if (pt == MacPayloadType::MAC__PT__RLC__DATA)
-		ret_val.data() = dec__RlcmacDlDataBlock(stream);
-	else
-		ret_val.ctrl() = dec__RlcmacDlCtrlBlock(stream);
-
-	return ret_val;
-}
-
-
 } // namespace