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.
This commit is contained in:
parent
88849c61b2
commit
43e060a50d
|
@ -30,5 +30,5 @@ gen_links $DIR $FILES
|
|||
|
||||
|
||||
DIR=../library
|
||||
FILES="General_Types.ttcn GSM_Types.ttcn GSM_RR_Types.ttcn Osmocom_Types.ttcn RLCMAC_Types.ttcn L1CTL_Types.ttcn L1CTL_PortType.ttcn"
|
||||
FILES="General_Types.ttcn GSM_Types.ttcn GSM_RR_Types.ttcn Osmocom_Types.ttcn RLCMAC_Types.ttcn RLCMAC_EncDec.cc L1CTL_Types.ttcn L1CTL_PortType.ttcn"
|
||||
gen_links $DIR $FILES
|
||||
|
|
|
@ -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
|
|
@ -104,66 +104,74 @@ module RLCMAC_Types {
|
|||
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 @@ module RLCMAC_Types {
|
|||
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)" }
|
||||
|
|
Loading…
Reference in New Issue