Change in osmo-ttcn3-hacks[master]: library/RLCMAC: Add partial support for EGPRS data block encoding/dec...

This is merely a historical archive of years 2008-2021, before the migration to mailman3.

A maintained and still updated list archive can be found at https://lists.osmocom.org/hyperkitty/list/gerrit-log@lists.osmocom.org/.

pespin gerrit-no-reply at lists.osmocom.org
Tue Apr 28 10:57:18 UTC 2020


pespin has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-ttcn3-hacks/+/17965 )


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

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
---
M library/RLCMAC_EncDec.cc
M library/RLCMAC_Types.ttcn
2 files changed, 1,082 insertions(+), 158 deletions(-)



  git pull ssh://gerrit.osmocom.org:29418/osmo-ttcn3-hacks refs/changes/65/17965/1

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
diff --git a/library/RLCMAC_Types.ttcn b/library/RLCMAC_Types.ttcn
index 97b89da..e5c2464 100644
--- a/library/RLCMAC_Types.ttcn
+++ b/library/RLCMAC_Types.ttcn
@@ -41,11 +41,18 @@
 		return 0;
 	}
 
+	type enumerated EgprsHeaderType {
+		RLCMAC_HDR_TYPE_1,
+		RLCMAC_HDR_TYPE_2,
+		RLCMAC_HDR_TYPE_3
+	};
+
 	type enumerated CodingScheme {
 		CS_1,
 		CS_2,
 		CS_3,
 		CS_4,
+		MCS_0,
 		MCS_1,
 		MCS_2,
 		MCS_3,
@@ -238,6 +245,52 @@
 	external function dec_RlcmacDlDataBlock(in octetstring stream) return RlcmacDlDataBlock;
 
 
+	/* a single RLC block / LLC-segment */
+	type record EgprsLlcBlockHdr {
+		uint7_t		length_ind,
+		/* 0 = another extension octet after LLC PDU, 1 = no more extension octets */
+		boolean		e
+	} with {
+		variant (e) "FIELDLENGTH(1)"
+		encode "RAW"
+	};
+
+	external function enc_EgprsLlcBlockHdr(in EgprsLlcBlockHdr si) return octetstring
+		with { extension "prototype(convert) encode(RAW)" };
+	external function dec_EgprsLlcBlockHdr(in octetstring stream) return EgprsLlcBlockHdr
+		with { extension "prototype(convert) decode(RAW)" };
+
+	type record EgprsLlcBlock {
+		/* Header is only present if LI field was present */
+		EgprsLlcBlockHdr	hdr optional,
+		octetstring 		payload
+	} with { variant "" };
+	type record of EgprsLlcBlock EgprsLlcBlocks;
+
+	/* TS 44.060 10.3a.1.1 EGPRS downlink RLC data block, manual c++ encoder/decoder */
+	type record EgprsDlMacDataHeader {
+		EgprsHeaderType	header_type, /* Set internally by decoder */
+		uint5_t		tfi,
+		MacRrbp		rrbp,
+		BIT2		esp,
+		uint3_t		usf,
+		uint14_t	bsn1,
+		uint8_t		bsn2_offset,
+		uint2_t		pr, /* power reduction */
+		uint2_t		spb,
+		uint4_t		cps
+	} with { variant "" };
+	/* Manual C++ Decoder: */
+	type record RlcmacDlEgprsDataBlock {
+		EgprsDlMacDataHeader	mac_hdr,
+		boolean			fbi,
+		boolean			e,
+		EgprsLlcBlocks		blocks
+	} with {
+		variant (fbi) "FIELDLENGTH(1)"
+		variant (e) "FIELDLENGTH(1)"
+	};
+
 	/* TS 44.060 10.2.2 */
 	type record UlMacDataHeader {
 		/* Octet 0 */
@@ -268,6 +321,35 @@
 		variant (m) "FIELDLENGTH(1)"
 	};
 
+	/* TS 44.060 10.3a.1.1 10.3a.4 EGPRS Uplink RLC/MAC header, manual c++ encoder/decoder */
+	type record EgprsUlMacDataHeader {
+		EgprsHeaderType	header_type, /* Set internally by decoder */
+		uint5_t		tfi,
+		uint4_t		countdown,
+		BIT1		foi_si,
+		BIT1		r_ri,
+		uint11_t	bsn1,
+		uint4_t		cps,
+		boolean		pfi_ind,
+		BIT1		rsb,
+		BIT2		spb
+	} with {
+		variant (pfi_ind) "FIELDLENGTH(1)"
+	};
+	/* Manual C++ Decoder:  10.3a.2.1 EGPRS Uplink RLC data block */
+	type record RlcmacUlEgprsDataBlock {
+		EgprsUlMacDataHeader	mac_hdr,
+		boolean			tlli_ind,
+		boolean			e,
+		/* Octet 3 ... M (optional): manual C++ Decoder */
+		GprsTlli		tlli optional,
+		RlcMacUlPfi		pfi optional,
+		EgprsLlcBlocks		blocks
+	} with {
+		variant (tlli_ind) "FIELDLENGTH(1)"
+		variant (e) "FIELDLENGTH(1)"
+	};
+
 	/* TS 44.060 10.2.2 */
 	type record RlcmacUlDataBlock {
 		/* MAC header */
@@ -286,11 +368,16 @@
 
 	type union RlcmacUlBlock {
 		RlcmacUlDataBlock	data,
+		RlcmacUlEgprsDataBlock	data_egprs,
 		RlcmacUlCtrlBlock	ctrl
 	} with {
 		variant "TAG(data, mac_hdr.payload_type = MAC_PT_RLC_DATA;
-			     ctrl, mac_hdr.payload_type = MAC_PT_RLCMAC_NO_OPT;
-			     ctrl, mac_hdr.payload_type = MAC_PT_RLCMAC_OPT)"
+			     ctrl, {mac_hdr.payload_type = MAC_PT_RLCMAC_NO_OPT,
+				    mac_hdr.payload_type = MAC_PT_RLCMAC_OPT};
+			     data_egprs, {mac_hdr.header_type = RLCMAC_HDR_TYPE_1,
+					  mac_hdr.header_type = RLCMAC_HDR_TYPE_2,
+					  mac_hdr.header_type = RLCMAC_HDR_TYPE_3}
+			     )"
 	};
 
 	/* as the sub-types (RlcmacDl*Block) are not using the RAW coder, we cannot
@@ -301,11 +388,16 @@
 
 	type union RlcmacDlBlock {
 		RlcmacDlDataBlock	data,
+		RlcmacDlEgprsDataBlock	data_egprs,
 		RlcmacDlCtrlBlock	ctrl
 	} with {
 		variant "TAG(data, mac_hdr.mac_hdr.payload_type = MAC_PT_RLC_DATA;
-			     ctrl, mac_hdr.payload_type = MAC_PT_RLCMAC_NO_OPT;
-			     ctrl, mac_hdr.payload_type = MAC_PT_RLCMAC_OPT)"
+			     ctrl, {mac_hdr.payload_type = MAC_PT_RLCMAC_NO_OPT,
+				    mac_hdr.payload_type = MAC_PT_RLCMAC_OPT};
+			     data_egprs, {mac_hdr.header_type = RLCMAC_HDR_TYPE_1,
+					  mac_hdr.header_type = RLCMAC_HDR_TYPE_2,
+					  mac_hdr.header_type = RLCMAC_HDR_TYPE_3}
+			     )"
 	};
 
 	/* as the sub-types (RlcmacDl*Block) are not using the RAW coder, we cannot
@@ -546,6 +638,15 @@
 		}
 	}
 
+	template RlcmacDlBlock tr_RLCMAC_DATA_EGPRS := {
+		data_egprs := {
+			mac_hdr := ?,
+			fbi := ?,
+			e := ?,
+			blocks := ?
+		}
+	}
+
 	/* Template for Uplink MAC Control Header */
 	template UlMacCtrlHeader t_RLCMAC_UlMacCtrlH(template MacPayloadType pt, template boolean retry := false) := {
 		payload_type := pt,
@@ -576,8 +677,10 @@
 		payload := data
 	}
 
-	/* PTCCH/D (Packet Timing Advance Control Channel) message.
-	 * TODO: add a spec. reference to the message format definition. */
+/************************
+ * PTCCH/D (Packet Timing Advance Control Channel) message.
+ * TODO: add a spec. reference to the message format definition.
+ *************************/
 	type record PTCCHTimingAdvanceIE {
 		BIT1			spare ('0'B),
 		uint7_t			ta_val

-- 
To view, visit https://gerrit.osmocom.org/c/osmo-ttcn3-hacks/+/17965
To unsubscribe, or for help writing mail filters, visit https://gerrit.osmocom.org/settings

Gerrit-Project: osmo-ttcn3-hacks
Gerrit-Branch: master
Gerrit-Change-Id: I2bc4f877a5e17c57ffa8cf05565dc8593b45aae8
Gerrit-Change-Number: 17965
Gerrit-PatchSet: 1
Gerrit-Owner: pespin <pespin at sysmocom.de>
Gerrit-MessageType: newchange
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osmocom.org/pipermail/gerrit-log/attachments/20200428/649c4592/attachment.htm>


More information about the gerrit-log mailing list