1424 lines
42 KiB
C++
1424 lines
42 KiB
C++
#include <stdint.h>
|
|
#include <endian.h>
|
|
|
|
#include "RLCMAC_Types.hh"
|
|
#include "RLCMAC_Templates.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>
|
|
* All rights reserved.
|
|
*
|
|
* Released under the terms of GNU General Public License, Version 2 or
|
|
* (at your option) any later version.
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later
|
|
*/
|
|
|
|
namespace RLCMAC__Types {
|
|
|
|
/////////////////////
|
|
// INTENRAL HELPERS
|
|
/////////////////////
|
|
|
|
/* 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));
|
|
|
|
/* 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));
|
|
|
|
/* 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 const char hex_chars[] = "0123456789abcdef";
|
|
void printbuffer(const char* ptr, TTCN_Buffer& buf) {
|
|
int len = buf.get_len();
|
|
const unsigned char* cbuf = buf.get_data();
|
|
|
|
fprintf(stderr, "printbuffer %s (len=%d): [", ptr, len);
|
|
|
|
for (int i = 0; i < len; i++) {
|
|
fprintf(stderr, " %c%c", hex_chars[cbuf[i] >> 4], hex_chars[cbuf[i] & 0xf]);
|
|
}
|
|
|
|
fprintf(stderr, " ]\n");
|
|
}
|
|
*/
|
|
|
|
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 60: /* fall through */
|
|
case 61:
|
|
return CodingScheme::MCS__5;
|
|
case 78: /* fall through */
|
|
case 79:
|
|
return CodingScheme::MCS__6;
|
|
case 118: /* fall through */
|
|
case 119:
|
|
return CodingScheme::MCS__7;
|
|
case 142: /* fall through */
|
|
case 143:
|
|
return CodingScheme::MCS__8;
|
|
case 154: /* fall through */
|
|
case 155:
|
|
return CodingScheme::MCS__9;
|
|
default:
|
|
fprintf(stderr, "ERROR: Unknown CodingSCheme for payload_len=%zu\n", payload_len);
|
|
return CodingScheme::CS__1;
|
|
}
|
|
}
|
|
|
|
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 result 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);
|
|
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);
|
|
|
|
/* 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);
|
|
}
|
|
|
|
/* bit-shift the entire 'src' of length 'length_bytes'
|
|
* and store the result to caller-allocated 'buffer' by 'offset_bits'. The shifting is
|
|
* done lsb-first. */
|
|
static void clone_unaligned_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_hdr_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;
|
|
}
|
|
|
|
/* Copy first header+data byte, it's not handled correctly by loop */
|
|
dst = buffer + hdr_bytes;
|
|
last_hdr_c = *dst;
|
|
last_c = *dst << (8 - extra_bits);
|
|
|
|
for (i = 0; i < length_bytes; i++) {
|
|
c = src[i];
|
|
*(dst++) = (last_c >> (8 - extra_bits)) | (c << extra_bits);
|
|
last_c = c;
|
|
}
|
|
/* overwrite the lower extra_bits */
|
|
*dst = (*dst & (0xff << extra_bits)) | (last_c >> (8 - extra_bits));
|
|
|
|
/* Copy back first header+data byte */
|
|
dst = buffer + hdr_bytes;
|
|
*(dst++) = last_hdr_c | (src[0] << (8 - extra_bits));
|
|
*dst |= (src[0] >> (extra_bits)) & (0xff >> (8 - extra_bits));
|
|
}
|
|
|
|
/* put an (aligned) EGPRS data block with given bit-offset and
|
|
* bit-length into parent buffer */
|
|
static void put_egprs_data_block(const TTCN_Buffer& aligned_data_block_buffer, unsigned int offset_bits,
|
|
unsigned int length_bits, TTCN_Buffer& dst_ttcn_buffer)
|
|
{
|
|
const unsigned int initial_spare_bits = 6;
|
|
unsigned char *unaligned_buf = NULL;
|
|
char tmpbuf[120];
|
|
int tmplen = dst_ttcn_buffer.get_len();
|
|
//size_t max_length_bytes = (initial_spare_bits + length_bits + 7) / 8;
|
|
size_t length_bytes = tmplen + aligned_data_block_buffer.get_len();
|
|
size_t accepted_len = length_bytes;
|
|
|
|
//fprintf(stderr, "RLMAC: trying to allocate %u bytes\n", length_bytes);
|
|
|
|
/* API .get_end() is the only one I could find to access writeable
|
|
memory in the buffer. It points to the end. Hence, we first copy
|
|
(readonly) data to tmpbuf and later clear() so that .get_end()
|
|
provides us with a pointer to the start of the buffer. */
|
|
memcpy(tmpbuf, dst_ttcn_buffer.get_data(), tmplen);
|
|
dst_ttcn_buffer.clear();
|
|
dst_ttcn_buffer.get_end(unaligned_buf, accepted_len);
|
|
if (accepted_len < tmplen) {
|
|
fprintf(stderr, "RLMAC: ERROR! asked for %zu bytes but got %zu\n", length_bytes, accepted_len);
|
|
}
|
|
memcpy(unaligned_buf, tmpbuf, tmplen);
|
|
|
|
/* Copy the data out of the tvb to an aligned buffer */
|
|
clone_unaligned_buffer_lsbf(
|
|
offset_bits - initial_spare_bits, length_bytes,
|
|
aligned_data_block_buffer.get_data(),
|
|
unaligned_buf);
|
|
|
|
dst_ttcn_buffer.increase_length(length_bytes);
|
|
}
|
|
|
|
/* Append padding bytes and spare bits at the end of ttcn_buffer, based on requested CS */
|
|
static void encode_trailing_padding_spb(TTCN_Buffer& ttcn_buffer, CodingScheme cs)
|
|
{
|
|
uint8_t buf[256]; /* enough to fit any RLCMAC buffer*/
|
|
uint32_t blk_len = RLCMAC__Templates::f__rlcmac__cs__mcs2block__len(cs);
|
|
uint32_t blk_len_no_spb = RLCMAC__Templates::f__rlcmac__cs__mcs2block__len__no__spare__bits(cs);
|
|
uint32_t data_len = ttcn_buffer.get_len();
|
|
|
|
if (data_len > blk_len_no_spb) {
|
|
fprintf(stderr, "Buffer too large for requested CS! %s (%s:%u)\n", __func__, __FILE__, __LINE__);
|
|
// TODO: throw exception?
|
|
}
|
|
|
|
for (int i = 0; i < blk_len_no_spb - data_len; i++)
|
|
buf[i] = 0x2b; /* Padding bits if needed */
|
|
for (int i = blk_len_no_spb - data_len; i < blk_len - data_len; i++)
|
|
buf[i] = 0x00; /* Spare bits if needed */
|
|
|
|
const OCTETSTRING& pad_octstr = OCTETSTRING(blk_len - data_len, buf);
|
|
ttcn_buffer.put_string(pad_octstr);
|
|
}
|
|
|
|
/////////////////////
|
|
// DECODE
|
|
/////////////////////
|
|
|
|
/* DECODE DOWNLINK */
|
|
|
|
RlcmacDlDataBlock dec__RlcmacDlDataBlock(const OCTETSTRING& stream)
|
|
{
|
|
RlcmacDlDataBlock ret_val;
|
|
TTCN_Buffer ttcn_buffer(stream);
|
|
int num_llc_blocks = 0;
|
|
|
|
ret_val.cs() = payload_len_2_coding_scheme(stream.lengthof());
|
|
|
|
/* 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.hdr() = OMIT_VALUE;
|
|
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;
|
|
}
|
|
|
|
static
|
|
EgprsDlMacDataHeader dec__EgprsDlMacDataHeader_type1(const OCTETSTRING& stream)
|
|
{
|
|
TTCN_Buffer ttcn_buffer(stream);
|
|
EgprsDlMacDataHeader ret_val;
|
|
const struct gprs_rlc_dl_header_egprs_1 *egprs1;
|
|
uint8_t tmp;
|
|
|
|
egprs1 = static_cast<const struct gprs_rlc_dl_header_egprs_1 *>
|
|
((const void *)ttcn_buffer.get_data());
|
|
|
|
ret_val.header__type() = EgprsHeaderType::RLCMAC__HDR__TYPE__1;
|
|
ret_val.tfi() = egprs1->tfi_lo << 1 | egprs1->tfi_hi << 0;
|
|
ret_val.rrbp() = egprs1->rrbp;
|
|
tmp = egprs1->es_p;
|
|
ret_val.esp() = BITSTRING(2, &tmp);
|
|
ret_val.usf() = egprs1->usf;
|
|
ret_val.bsn1() = egprs1->bsn1_lo << 10 | egprs1->bsn1_mid << 2 | egprs1->bsn1_hi;
|
|
ret_val.bsn2__offset() = egprs1->bsn2_lo << 7 | egprs1->bsn2_hi;
|
|
ret_val.pr() = egprs1->pr;
|
|
ret_val.cps() = egprs1->cps;
|
|
ret_val.spb() = OMIT_VALUE;
|
|
|
|
ttcn_buffer.increase_pos(sizeof(*egprs1));
|
|
return ret_val;
|
|
}
|
|
|
|
static
|
|
EgprsDlMacDataHeader dec__EgprsDlMacDataHeader_type2(const OCTETSTRING& stream)
|
|
{
|
|
TTCN_Buffer ttcn_buffer(stream);
|
|
EgprsDlMacDataHeader ret_val;
|
|
const struct gprs_rlc_dl_header_egprs_2 *egprs2;
|
|
uint8_t tmp;
|
|
|
|
egprs2 = static_cast<const struct gprs_rlc_dl_header_egprs_2 *>
|
|
((const void *)ttcn_buffer.get_data());
|
|
|
|
ret_val.header__type() = EgprsHeaderType::RLCMAC__HDR__TYPE__2;
|
|
ret_val.tfi() = egprs2->tfi_lo << 1 | egprs2->tfi_hi << 0;
|
|
ret_val.rrbp() = egprs2->rrbp;
|
|
tmp = egprs2->es_p;
|
|
ret_val.esp() = BITSTRING(2, &tmp);
|
|
ret_val.usf() = egprs2->usf;
|
|
ret_val.bsn1() = egprs2->bsn1_lo << 10 | egprs2->bsn1_mid << 2 | egprs2->bsn1_hi;
|
|
ret_val.bsn2__offset() = 0; /*TODO: mark optional and not set ? */
|
|
ret_val.pr() = egprs2->pr;
|
|
ret_val.cps() = egprs2->cps;
|
|
ret_val.spb() = OMIT_VALUE;
|
|
|
|
ttcn_buffer.increase_pos(sizeof(*egprs2));
|
|
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)
|
|
{
|
|
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;
|
|
|
|
ret_val.mcs() = payload_len_2_coding_scheme(stream.lengthof());
|
|
switch (ret_val.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(ret_val.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.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.hdr() = OMIT_VALUE;
|
|
lb.payload() = OCTETSTRING(length, ttcn_buffer.get_read_data());
|
|
aligned_buffer.increase_pos(length);
|
|
ret_val.blocks()[0] = lb;
|
|
} else {
|
|
/* 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 cs_mcs = payload_len_2_coding_scheme(stream_len);
|
|
unsigned char pt;
|
|
|
|
switch (cs_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);
|
|
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();
|
|
|
|
ret_val.cs() = payload_len_2_coding_scheme(stream.lengthof());
|
|
|
|
/* 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);
|
|
} else {
|
|
ret_val.tlli() = OMIT_VALUE;
|
|
}
|
|
/* parse optional PFI */
|
|
if (ret_val.mac__hdr().pfi__ind()) {
|
|
ret_val.pfi().decode(RlcmacUlDataBlock_pfi_descr_, ttcn_buffer, TTCN_EncDec::CT_RAW);
|
|
} else {
|
|
ret_val.pfi() = OMIT_VALUE;
|
|
}
|
|
|
|
/* 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.hdr() = OMIT_VALUE;
|
|
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)
|
|
{
|
|
TTCN_Buffer ttcn_buffer(stream);
|
|
EgprsUlMacDataHeader ret_val;
|
|
const struct gprs_rlc_ul_header_egprs_3 *egprs3;
|
|
uint8_t tmp;
|
|
|
|
egprs3 = static_cast<const struct gprs_rlc_ul_header_egprs_3 *>
|
|
((const void *)ttcn_buffer.get_data());
|
|
|
|
ret_val.header__type() = EgprsHeaderType::RLCMAC__HDR__TYPE__3;
|
|
ret_val.tfi() = egprs3->tfi_lo << 2 | egprs3->tfi_hi << 0;
|
|
ret_val.countdown() = egprs3->cv;
|
|
tmp = egprs3->si;
|
|
ret_val.foi__si() = BITSTRING(1, &tmp);
|
|
tmp = egprs3->r;
|
|
ret_val.r__ri() = BITSTRING(1, &tmp);
|
|
ret_val.bsn1() = egprs3->bsn1_lo << 5 | egprs3->bsn1_hi << 0;
|
|
ret_val.cps() = egprs3->cps_lo << 2 | egprs3->cps_hi << 0;
|
|
ret_val.pfi__ind() = egprs3->pi;
|
|
tmp = egprs3->rsb;
|
|
ret_val.rsb() = BITSTRING(1, &tmp);
|
|
tmp = egprs3->spb;
|
|
ret_val.spb() = BITSTRING(2, &tmp);
|
|
|
|
ttcn_buffer.increase_pos(sizeof(*egprs3));
|
|
return ret_val;
|
|
}
|
|
|
|
RlcmacUlEgprsDataBlock dec__RlcmacUlEgprsDataBlock(const OCTETSTRING& stream)
|
|
{
|
|
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;
|
|
|
|
ret_val.mcs() = payload_len_2_coding_scheme(stream.lengthof());
|
|
switch (ret_val.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(ret_val.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);
|
|
} else {
|
|
ret_val.tlli() = OMIT_VALUE;
|
|
}
|
|
/* parse optional PFI */
|
|
if (ret_val.mac__hdr().pfi__ind()) {
|
|
ret_val.pfi().decode(RlcmacUlEgprsDataBlock_pfi_descr_, aligned_buffer, TTCN_EncDec::CT_RAW);
|
|
} else {
|
|
ret_val.pfi() = OMIT_VALUE;
|
|
}
|
|
|
|
/* 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.hdr() = OMIT_VALUE;
|
|
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 cs_mcs = payload_len_2_coding_scheme(stream_len);
|
|
unsigned char pt;
|
|
|
|
switch (cs_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);
|
|
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());
|
|
}
|
|
}
|
|
|
|
encode_trailing_padding_spb(ttcn_buffer, in.cs());
|
|
|
|
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());
|
|
}
|
|
}
|
|
|
|
encode_trailing_padding_spb(ttcn_buffer, in.mcs());
|
|
|
|
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)
|
|
{
|
|
RlcmacUlDataBlock in = si;
|
|
OCTETSTRING ret_val;
|
|
TTCN_Buffer ttcn_buffer;
|
|
int i;
|
|
|
|
if (!in.blocks().is_bound()) {
|
|
/* we don't have any blocks: Add length value (zero) */
|
|
in.mac__hdr().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.mac__hdr().e() = true; /* E=0: extension octet follows */
|
|
} else {
|
|
/* Length value */
|
|
in.mac__hdr().e() = false;
|
|
}
|
|
|
|
/* Fix other presence indications */
|
|
in.mac__hdr().tlli__ind() = in.tlli().is_bound() && in.tlli() != OMIT_VALUE;
|
|
in.mac__hdr().pfi__ind() = in.pfi().is_bound() && in.pfi() != OMIT_VALUE;
|
|
|
|
/* use automatic/generated encoder for header */
|
|
in.mac__hdr().encode(UlMacDataHeader_descr_, ttcn_buffer, TTCN_EncDec::CT_RAW);
|
|
|
|
if (in.mac__hdr().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(LlcBlockHdr_descr_, ttcn_buffer,
|
|
TTCN_EncDec::CT_RAW);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (in.mac__hdr().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());
|
|
}
|
|
}
|
|
|
|
encode_trailing_padding_spb(ttcn_buffer, in.cs());
|
|
|
|
ttcn_buffer.get_string(ret_val);
|
|
return ret_val;
|
|
}
|
|
|
|
static
|
|
void enc__RlcmacUlEgprsDataHeader_type1(const EgprsUlMacDataHeader& si, TTCN_Buffer& ttcn_buffer)
|
|
{
|
|
struct gprs_rlc_ul_header_egprs_1 egprs1;
|
|
|
|
egprs1.r = bs2uint8(si.r__ri());
|
|
egprs1.si = bs2uint8(si.foi__si());
|
|
egprs1.cv = si.countdown();
|
|
egprs1.tfi_hi = si.tfi() >> 0;
|
|
egprs1.tfi_lo = si.tfi() >> 2;
|
|
egprs1.bsn1_hi = si.bsn1() >> 0;
|
|
egprs1.bsn1_lo = si.bsn1() >> 5;
|
|
egprs1.bsn2_hi = si.bsn2__offset() >> 0;
|
|
egprs1.bsn2_lo = si.bsn2__offset() >> 2;
|
|
egprs1.cps = si.cps();
|
|
egprs1.rsb = bs2uint8(si.rsb());
|
|
egprs1.pi = si.pfi__ind();
|
|
egprs1.spare_hi = 0;
|
|
egprs1.spare_lo = 0;
|
|
egprs1.dummy = 0;
|
|
|
|
ttcn_buffer.put_s(sizeof(egprs1), (const unsigned char *)&egprs1);
|
|
}
|
|
|
|
static
|
|
void enc__RlcmacUlEgprsDataHeader_type2(const EgprsUlMacDataHeader& si, TTCN_Buffer& ttcn_buffer)
|
|
{
|
|
struct gprs_rlc_ul_header_egprs_2 egprs2;
|
|
|
|
egprs2.r = bs2uint8(si.r__ri());
|
|
egprs2.si = bs2uint8(si.foi__si());
|
|
egprs2.cv = si.countdown();
|
|
egprs2.tfi_hi = si.tfi() >> 0;
|
|
egprs2.tfi_lo = si.tfi() >> 2;
|
|
egprs2.bsn1_hi = si.bsn1() >> 0;
|
|
egprs2.bsn1_lo = si.bsn1() >> 5;
|
|
egprs2.cps_hi = si.cps() >> 0;
|
|
egprs2.cps_lo = si.cps() >> 2;
|
|
egprs2.rsb = bs2uint8(si.rsb());
|
|
egprs2.pi = si.pfi__ind();
|
|
egprs2.spare_hi = 0;
|
|
egprs2.spare_lo = 0;
|
|
egprs2.dummy = 0;
|
|
|
|
ttcn_buffer.put_s(sizeof(egprs2), (const unsigned char *)&egprs2);
|
|
}
|
|
|
|
static
|
|
void enc__RlcmacUlEgprsDataHeader_type3(const EgprsUlMacDataHeader& si, TTCN_Buffer& ttcn_buffer)
|
|
{
|
|
struct gprs_rlc_ul_header_egprs_3 egprs3;
|
|
|
|
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() >> 2;
|
|
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.rsb());
|
|
egprs3.pi = si.pfi__ind();
|
|
egprs3.spare = 0;
|
|
egprs3.dummy = 0;
|
|
|
|
ttcn_buffer.put_s(sizeof(egprs3), (const unsigned char *)&egprs3);
|
|
}
|
|
|
|
OCTETSTRING enc__RlcmacUlEgprsDataBlock(const RlcmacUlEgprsDataBlock& si)
|
|
{
|
|
RlcmacUlEgprsDataBlock in = si;
|
|
OCTETSTRING ret_val;
|
|
TTCN_Buffer ttcn_buffer, aligned_buffer;
|
|
int i;
|
|
unsigned int data_block_bits, data_block_offsets[2];
|
|
unsigned int num_calls;
|
|
CodingScheme mcs;
|
|
|
|
mcs = RLCMAC__Templates::f__rlcmac__cps__htype__to__mcs(in.mac__hdr().cps(), in.mac__hdr().header__type());
|
|
//fprintf(stderr, "RLCMAC: infered MCS %s (%d)\n", mcs.enum_to_str(static_cast<CodingScheme::enum_type>(mcs.as_int())), mcs.as_int());
|
|
|
|
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 {
|
|
/* 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(in.mac__hdr(), ttcn_buffer);
|
|
break;
|
|
case EgprsHeaderType::RLCMAC__HDR__TYPE__2:
|
|
enc__RlcmacUlEgprsDataHeader_type2(in.mac__hdr(), ttcn_buffer);
|
|
break;
|
|
case EgprsHeaderType::RLCMAC__HDR__TYPE__3:
|
|
enc__RlcmacUlEgprsDataHeader_type3(in.mac__hdr(), ttcn_buffer);
|
|
default:
|
|
break; /* TODO: error */
|
|
}
|
|
|
|
/* Put first TI + E byte */
|
|
aligned_buffer.put_c((in.tlli__ind() & 0x01) << 1 | (in.e() & 0x01) << 0);
|
|
//printbuffer("After encoding first byte", aligned_buffer);
|
|
|
|
if (in.e() == false) {
|
|
/* Add LI octets, if any */
|
|
if (!in.blocks().is_bound()) {
|
|
aligned_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_, aligned_buffer,
|
|
TTCN_EncDec::CT_RAW);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
if (in.tlli__ind()) {
|
|
/* The TLLI is encoded in little endian for EGPRS (see
|
|
* TS 44.060, figure 10.3a.2.1, note 2) */
|
|
OCTETSTRING tlli = in.tlli();
|
|
aligned_buffer.put_c(tlli[3].get_octet());
|
|
aligned_buffer.put_c(tlli[2].get_octet());
|
|
aligned_buffer.put_c(tlli[1].get_octet());
|
|
aligned_buffer.put_c(tlli[0].get_octet());
|
|
}
|
|
|
|
if (in.mac__hdr().pfi__ind()) {
|
|
in.pfi().encode(RlcmacUlEgprsDataBlock_pfi_descr_, aligned_buffer, TTCN_EncDec::CT_RAW);
|
|
}
|
|
|
|
//printbuffer("Before encoding EgprsLlc payload", aligned_buffer);
|
|
if (in.blocks().is_bound()) {
|
|
for (i = 0; i < in.blocks().size_of(); i++) {
|
|
if (!in.blocks()[i].is_bound())
|
|
continue;
|
|
aligned_buffer.put_string(in.blocks()[i].payload());
|
|
}
|
|
}
|
|
//printbuffer("After encoding EgprsLlc payload", aligned_buffer);
|
|
|
|
setup_rlc_mac_priv(mcs, in.mac__hdr().header__type(), true,
|
|
&num_calls, &data_block_bits, data_block_offsets);
|
|
//printbuffer("before merging data block", ttcn_buffer);
|
|
put_egprs_data_block(aligned_buffer, data_block_offsets[0], data_block_bits, ttcn_buffer);
|
|
//printbuffer("after merging data block", ttcn_buffer);
|
|
|
|
encode_trailing_padding_spb(ttcn_buffer, in.mcs());
|
|
|
|
ttcn_buffer.get_string(ret_val);
|
|
return ret_val;
|
|
}
|
|
|
|
OCTETSTRING enc__RlcmacUlBlock(const RlcmacUlBlock& si)
|
|
{
|
|
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());
|
|
}
|
|
|
|
} // namespace
|