Add hand-written encoder/decoder for RLC/MAC UL/DL data blocks
Their format is simply too complex to be used with the automatic RAW
encoder/decoder. Let's implement it by hand, using the automatic
coder whenever possible.
diff --git a/library/RLCMAC_EncDec.cc b/library/RLCMAC_EncDec.cc
new file mode 100644
index 0000000..b2f0525
--- /dev/null
+++ b/library/RLCMAC_EncDec.cc
@@ -0,0 +1,265 @@
+#include "RLCMAC_Types.hh"
+#include "GSM_Types.hh"
+/* Decoding of TS 44.060 GPRS RLC/MAC blocks, portions requiring manual functions
+ * beyond what TITAN RAW coder can handle internally.
+ *
+ * (C) 2017 by Harald Welte <laforge@gnumonks.org>
+ */
+
+namespace RLCMAC__Types {
+
+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;
+}
+
+RlcmacDlDataBlock dec__RlcmacDlDataBlock(const OCTETSTRING& stream)
+{
+ RlcmacDlDataBlock ret_val;
+ TTCN_Buffer ttcn_buffer(stream);
+ int num_llc_blocks = 0;
+
+ /* use automatic/generated decoder for header */
+ ret_val.mac__hdr().decode(DlMacDataHeader_descr_, ttcn_buffer, TTCN_EncDec::CT_RAW);
+
+ /* optional extension octets, containing LI+M+E of Llc blocks */
+ if (ret_val.mac__hdr().hdr__ext().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;
+
+ /* if E == '1'B, we can proceed further */
+ if (lb.hdr().e() == true)
+ break;
+ }
+ }
+
+ /* RLC blocks at end */
+ if (ret_val.mac__hdr().hdr__ext().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);
+ }
+ }
+ }
+
+ return ret_val;
+}
+
+
+OCTETSTRING enc__RlcmacUlDataBlock(const RlcmacUlDataBlock& si)
+{
+ RlcmacUlDataBlock 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().e() = true;
+ else
+ in.mac__hdr().e() = false;
+
+ /* Fix other presence indications */
+ in.mac__hdr().tlli__ind() = in.tlli().is_bound();
+ in.mac__hdr().pfi__ind() = in.pfi().is_bound();
+
+ /* use automatic/generated decoder for header */
+ in.mac__hdr().encode(UlMacDataHeader_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.mac__hdr().tlli__ind()) {
+ /* FIXME */
+ //in.tlli().encode(GSM__Types::GprsTlli_descr_, ttcn_buffer, TTCN_EncDec::CT_RAW);
+ INTEGER t = in.tlli();
+ unsigned int tmp = t.get_long_long_val();
+ ttcn_buffer.put_c(tmp >> 24);
+ ttcn_buffer.put_c(tmp >> 16);
+ ttcn_buffer.put_c(tmp >> 8);
+ ttcn_buffer.put_c(tmp);
+ }
+
+ 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;
+}
+
+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()) {
+ /* FIXME: Why is this not working ?!? */
+ //ret_val.tlli().decode(GSM__Types::GprsTlli_descr_, ttcn_buffer, TTCN_EncDec::CT_RAW);
+ const unsigned char *cur = ttcn_buffer.get_read_data();
+ unsigned int tmp = cur[0] << 24 | cur[1] << 16 | cur[2] << 8 | cur[3];
+ INTEGER t;
+ t.set_long_long_val(tmp);
+ ret_val.tlli() = t;
+ 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;
+}
+
+
+} // namespace
diff --git a/library/RLCMAC_Types.ttcn b/library/RLCMAC_Types.ttcn
index f73721c..6cd6e49 100644
--- a/library/RLCMAC_Types.ttcn
+++ b/library/RLCMAC_Types.ttcn
@@ -104,66 +104,74 @@
with { extension "prototype(convert) decode(RAW)" };
/* a single RLC block / LLC-segment */
-
- type record RlcBlockHdr {
+ type record LlcBlockHdr {
uint6_t length_ind,
/* 1 = new LLC PDU starts */
BIT1 more,
/* 0 = another extension octet after LLC PDU, 1 = no more extension octets */
- BIT1 e
- } with { variant "" };
-
- type record RlcBlock {
- uint6_t length_ind,
- BIT1 more,
- BIT1 e,
- octetstring rlc optional
+ boolean e
} with {
- variant (rlc) "PRESENCE (more = '1'B)"
- variant (length_ind) "LENGTHTO(length_ind, more, e, rlc)"
+ variant (e) "FIELDLENGTH(1)"
};
-
- type record of RlcBlock RlcBlocks;
+ type record LlcBlock {
+ /* Header is only present if LI field was present */
+ LlcBlockHdr hdr,
+ octetstring payload
+ } with { variant "" };
+ type record of LlcBlock LlcBlocks;
/* TS 44.060 10.2.1 Downlink RLC data block */
- type record RlcmacDlDataBlock {
+ type record DlMacHdrDataExt {
/* Octet 1 */
- DlMacHeader mac_hdr,
- /* Octet 2 */
PowerReduction pr,
BIT1 spare,
uint4_t tfi, /* 3 or 4? */
boolean fbi,
- /* Octet 3 */
+ /* Octet 2 */
uint7_t bsn,
- BIT1 e ('1'B),
- RlcBlocks rlc_blocks
+ boolean e
+ } with {
+ variant (e) "FIELDLENGTH(1)"
+ };
+ type record DlMacDataHeader {
+ DlMacHeader mac_hdr,
+ DlMacHdrDataExt hdr_ext
} with { variant "" };
+ type record RlcmacDlDataBlock {
+ DlMacDataHeader mac_hdr,
+ /* Octet 3..M / N: manual C++ Decoder */
+ LlcBlocks blocks
+ } with {
+ variant ""
+ };
- external function enc_RlcmacDlDataBlock(in RlcmacDlDataBlock si) return octetstring
- with { extension "prototype(convert) encode(RAW)" };
- external function dec_RlcmacDlDataBlock(in octetstring stream) return RlcmacDlDataBlock
- with { extension "prototype(convert) decode(RAW)" };
+ external function enc_RlcmacDlDataBlock(in RlcmacDlDataBlock si) return octetstring;
+ external function dec_RlcmacDlDataBlock(in octetstring stream) return RlcmacDlDataBlock;
/* TS 44.060 10.2.2 */
type record UlMacDataHeader {
+ /* Octet 0 */
MacPayloadType pt,
uint4_t countdown,
boolean stall_ind,
- boolean retry
+ boolean retry,
+ /* Octet 1 */
+ BIT1 spare,
+ boolean pfi_ind,
+ uint5_t tfi,
+ boolean tlli_ind,
+ /* Octet 2 */
+ uint7_t bsn,
+ boolean e
} with {
- variant (stall_ind) "FIELDLENGTH(1)"
- variant (retry) "FIELDLENGTH(1)"
+ variant (stall_ind) "FIELDLENGTH(1)"
+ variant (retry) "FIELDLENGTH(1)"
+ variant (pfi_ind) "FIELDLENGTH(1)"
+ variant (tlli_ind) "FIELDLENGTH(1)"
+ variant (e) "FIELDLENGTH(1)"
};
- type record RlcMacUlTlli {
- RlcBlockHdr hdr,
- uint32_t tlli
- } with {
- variant ""
- }
-
type record RlcMacUlPfi {
uint7_t pfi,
boolean m
@@ -175,26 +183,16 @@
type record RlcmacUlDataBlock {
/* MAC header */
UlMacDataHeader mac_hdr,
- /* Octet 1 */
- BIT1 spare,
- boolean pfi_ind,
- uint5_t tfi,
- boolean tlli_ind,
- /* Octet 2 */
- uint7_t bsn,
- BIT1 e ('1'B),
- /* Octet 3 (optional) */
- RlcMacUlTlli tlli,
- RlcMacUlPfi pfi,
- RlcBlocks blocks
+ /* Octet 3 ... M (optional): manual C++ Decoder */
+ GprsTlli tlli optional,
+ RlcMacUlPfi pfi optional,
+ LlcBlocks blocks
} with {
- variant (tlli) "PRESENCE(tlli_ind = true)"
- variant (pfi) "PRESENCE(pfi_ind = true)"
+ variant (tlli) "PRESENCE(mac_hdr.tlli_ind = true)"
+ variant (pfi) "PRESENCE(mac_hdr.pfi_ind = true)"
};
- external function enc_RlcmacUlDataBlock(in RlcmacUlDataBlock si) return octetstring
- with { extension "prototype(convert) encode(RAW)" };
- external function dec_RlcmacUlDataBlock(in octetstring stream) return RlcmacUlDataBlock
- with { extension "prototype(convert) decode(RAW)" };
+ external function enc_RlcmacUlDataBlock(in RlcmacUlDataBlock si) return octetstring;
+ external function dec_RlcmacUlDataBlock(in octetstring stream) return RlcmacUlDataBlock;
} with { encode "RAW"; variant "FIELDORDER(msb)" }