From 14bb0947b49e7f936f3ff0708e5891a873362d3b Mon Sep 17 00:00:00 2001 From: Jacob Erlbeck Date: Tue, 12 Jan 2016 11:58:13 +0100 Subject: edge: Add Encoding::rlc_data_to_dl_append This function appends a single chunk to an RLC downlink data block. The implementation is basically taken from the gprs_rlcmac_dl_tbf::create_new_bsn method without the TBF related functionality and any side effects. Note that it still only supports GRPS. Sponsored-by: On-Waves ehf --- src/encoding.cpp | 121 +++++++++++++++++++++++ src/encoding.h | 14 +++ tests/edge/EdgeTest.cpp | 251 ++++++++++++++++++++++++++++++++++++++++++++++++ tests/edge/EdgeTest.ok | 2 + 4 files changed, 388 insertions(+) diff --git a/src/encoding.cpp b/src/encoding.cpp index 0756ffcb..eb424cb7 100644 --- a/src/encoding.cpp +++ b/src/encoding.cpp @@ -751,3 +751,124 @@ unsigned int Encoding::rlc_copy_from_aligned_buffer( return rdbi->data_len; } + +Encoding::AppendResult Encoding::rlc_data_to_dl_append( + struct gprs_rlc_data_block_info *rdbi, + gprs_llc *llc, int *offset, int *num_chunks, + uint8_t *data_block, + bool is_final) +{ + int chunk; + int space; + struct rlc_li_field *li; + uint8_t *delimiter, *data, *e_pointer; + + data = data_block + *offset; + delimiter = data_block + *num_chunks; + e_pointer = (*num_chunks ? delimiter - 1 : NULL); + + chunk = llc->chunk_size(); + space = rdbi->data_len - *offset; + + /* if chunk will exceed block limit */ + if (chunk > space) { + LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d " + "larger than space (%d) left in block: copy " + "only remaining space, and we are done\n", + chunk, space); + /* block is filled, so there is no extension */ + if (e_pointer) + *e_pointer |= 0x01; + /* fill only space */ + llc->consume(data, space); + /* return data block as message */ + *offset = rdbi->data_len; + (*num_chunks)++; + return AR_NEED_MORE_BLOCKS; + } + /* if FINAL chunk would fit precisely in space left */ + if (chunk == space && is_final) + { + LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d " + "would exactly fit into space (%d): because " + "this is a final block, we don't add length " + "header, and we are done\n", chunk, space); + /* block is filled, so there is no extension */ + if (e_pointer) + *e_pointer |= 0x01; + /* fill space */ + llc->consume(data, space); + *offset = rdbi->data_len; + (*num_chunks)++; + rdbi->cv = 0; + return AR_COMPLETED_BLOCK_FILLED; + } + /* if chunk would fit exactly in space left */ + if (chunk == space) { + LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d " + "would exactly fit into space (%d): add length " + "header with LI=0, to make frame extend to " + "next block, and we are done\n", chunk, space); + /* make space for delimiter */ + if (delimiter != data) + memmove(delimiter + 1, delimiter, + data - delimiter); + data++; + (*offset)++; + space--; + /* add LI with 0 length */ + li = (struct rlc_li_field *)delimiter; + li->e = 1; /* not more extension */ + li->m = 0; /* shall be set to 0, in case of li = 0 */ + li->li = 0; /* chunk fills the complete space */ + rdbi->e = 0; /* 0: extensions present */ + // no need to set e_pointer nor increase delimiter + /* fill only space, which is 1 octet less than chunk */ + llc->consume(data, space); + /* return data block as message */ + *offset = rdbi->data_len; + (*num_chunks)++; + return AR_NEED_MORE_BLOCKS; + } + + LOGP(DRLCMACDL, LOGL_DEBUG, "-- Chunk with length %d is less " + "than remaining space (%d): add length header to " + "to delimit LLC frame\n", chunk, space); + /* the LLC frame chunk ends in this block */ + /* make space for delimiter */ + if (delimiter != data) + memmove(delimiter + 1, delimiter, data - delimiter); + data++; + (*offset)++; + space--; + /* add LI to delimit frame */ + li = (struct rlc_li_field *)delimiter; + li->e = 0; /* Extension bit, maybe set later */ + li->m = 0; /* will be set later, if there is more LLC data */ + li->li = chunk; /* length of chunk */ + rdbi->e = 0; /* 0: extensions present */ + (*num_chunks)++; + /* copy (rest of) LLC frame to space and reset later */ + llc->consume(data, chunk); + data += chunk; + space -= chunk; + (*offset) += chunk; + /* if we have more data and we have space left */ + if (space > 0 && !is_final) { + li->m = 1; /* we indicate more frames to follow */ + return AR_COMPLETED_SPACE_LEFT; + } + /* if we don't have more LLC frames */ + if (is_final) { + LOGP(DRLCMACDL, LOGL_DEBUG, "-- Final block, so we " + "done.\n"); + li->e = 1; /* we cannot extend */ + rdbi->cv = 0; + return AR_COMPLETED_BLOCK_FILLED; + } + /* we have no space left */ + LOGP(DRLCMACDL, LOGL_DEBUG, "-- No space left, so we are " + "done.\n"); + li->e = 1; /* we cannot extend */ + return AR_COMPLETED_BLOCK_FILLED; +} diff --git a/src/encoding.h b/src/encoding.h index 6764ce41..9b4b09ee 100644 --- a/src/encoding.h +++ b/src/encoding.h @@ -26,6 +26,8 @@ struct gprs_rlcmac_bts; struct gprs_rlcmac_tbf; struct bitvec; +struct gprs_llc; +struct gprs_rlc_data_block_info; /** * I help with encoding data into CSN1 messages. @@ -76,4 +78,16 @@ public: const struct gprs_rlc_data_info *rlc, unsigned int data_block_idx, uint8_t *dst, const uint8_t *buffer); + + enum AppendResult { + AR_NEED_MORE_BLOCKS, + AR_COMPLETED_SPACE_LEFT, + AR_COMPLETED_BLOCK_FILLED, + }; + + static AppendResult rlc_data_to_dl_append( + struct gprs_rlc_data_block_info *rdbi, + gprs_llc *llc, int *offset, int *num_chunks, + uint8_t *data, + bool is_final); }; diff --git a/tests/edge/EdgeTest.cpp b/tests/edge/EdgeTest.cpp index 11459457..70e99d53 100644 --- a/tests/edge/EdgeTest.cpp +++ b/tests/edge/EdgeTest.cpp @@ -25,6 +25,7 @@ #include "decoding.h" #include "encoding.h" #include "rlc.h" +#include "llc.h" extern "C" { #include "pcu_vty.h" @@ -483,6 +484,255 @@ static void test_rlc_unit_decoder() printf("=== end %s ===\n", __func__); } +static void test_rlc_unit_encoder() +{ + struct gprs_rlc_data_block_info rdbi = {0}; + GprsCodingScheme cs; + uint8_t data[74]; + uint8_t llc_data[1500] = {0,}; + int num_chunks = 0; + int write_offset; + struct gprs_llc llc; + Encoding::AppendResult ar; + + printf("=== start %s ===\n", __func__); + + llc.init(); + + /* TS 44.060, B.1 */ + cs = GprsCodingScheme::CS4; + gprs_rlc_data_block_info_init(&rdbi, cs); + num_chunks = 0; + write_offset = 0; + memset(data, 0, sizeof(data)); + + llc.reset(); + llc.put_frame(llc_data, 11); + + ar = Encoding::rlc_data_to_dl_append(&rdbi, + &llc, &write_offset, &num_chunks, data, false); + + OSMO_ASSERT(ar == Encoding::AR_COMPLETED_SPACE_LEFT); + OSMO_ASSERT(rdbi.e == 0); + OSMO_ASSERT(write_offset == 1 + 11); + OSMO_ASSERT(num_chunks == 1); + + llc.reset(); + llc.put_frame(llc_data, 26); + + ar = Encoding::rlc_data_to_dl_append(&rdbi, + &llc, &write_offset, &num_chunks, data, false); + + OSMO_ASSERT(ar == Encoding::AR_COMPLETED_SPACE_LEFT); + OSMO_ASSERT(rdbi.e == 0); + OSMO_ASSERT(write_offset == 2 + 11 + 26); + OSMO_ASSERT(num_chunks == 2); + + llc.reset(); + llc.put_frame(llc_data, 99); + + ar = Encoding::rlc_data_to_dl_append(&rdbi, + &llc, &write_offset, &num_chunks, data, false); + + OSMO_ASSERT(ar == Encoding::AR_NEED_MORE_BLOCKS); + OSMO_ASSERT(rdbi.e == 0); + OSMO_ASSERT(rdbi.cv != 0); + OSMO_ASSERT(write_offset == (int)rdbi.data_len); + OSMO_ASSERT(num_chunks == 3); + + OSMO_ASSERT(data[0] == ((11 << 2) | (1 << 1) | (0 << 0))); + OSMO_ASSERT(data[1] == ((26 << 2) | (1 << 1) | (1 << 0))); + OSMO_ASSERT(data[2] == 0); + + /* TS 44.060, B.2 */ + cs = GprsCodingScheme::CS1; + + /* Block 1 */ + gprs_rlc_data_block_info_init(&rdbi, cs); + num_chunks = 0; + write_offset = 0; + memset(data, 0, sizeof(data)); + + llc.reset(); + llc.put_frame(llc_data, 20); + + ar = Encoding::rlc_data_to_dl_append(&rdbi, + &llc, &write_offset, &num_chunks, data, false); + + OSMO_ASSERT(ar == Encoding::AR_NEED_MORE_BLOCKS); + OSMO_ASSERT(rdbi.e == 0); + OSMO_ASSERT(write_offset == 1 + 19); + OSMO_ASSERT(num_chunks == 1); + + OSMO_ASSERT(data[0] == ((0 << 2) | (0 << 1) | (1 << 0))); + OSMO_ASSERT(data[1] == 0); + + /* Block 2 */ + gprs_rlc_data_block_info_init(&rdbi, cs); + num_chunks = 0; + write_offset = 0; + memset(data, 0, sizeof(data)); + + OSMO_ASSERT(llc.chunk_size() == 1); + + ar = Encoding::rlc_data_to_dl_append(&rdbi, + &llc, &write_offset, &num_chunks, data, false); + + OSMO_ASSERT(ar == Encoding::AR_COMPLETED_SPACE_LEFT); + OSMO_ASSERT(rdbi.e == 0); + OSMO_ASSERT(write_offset == 1 + 1); + OSMO_ASSERT(num_chunks == 1); + + llc.reset(); + llc.put_frame(llc_data, 99); + + ar = Encoding::rlc_data_to_dl_append(&rdbi, + &llc, &write_offset, &num_chunks, data, false); + + OSMO_ASSERT(ar == Encoding::AR_NEED_MORE_BLOCKS); + OSMO_ASSERT(rdbi.e == 0); + OSMO_ASSERT(write_offset == 1 + 1 + 18); + OSMO_ASSERT(num_chunks == 2); + + OSMO_ASSERT(data[0] == ((1 << 2) | (1 << 1) | (1 << 0))); + OSMO_ASSERT(data[1] == 0); + + /* TS 44.060, B.3 */ + cs = GprsCodingScheme::CS1; + + /* Block 1 */ + gprs_rlc_data_block_info_init(&rdbi, cs); + num_chunks = 0; + write_offset = 0; + memset(data, 0, sizeof(data)); + + llc.reset(); + llc.put_frame(llc_data, 7); + + ar = Encoding::rlc_data_to_dl_append(&rdbi, + &llc, &write_offset, &num_chunks, data, false); + + OSMO_ASSERT(ar == Encoding::AR_COMPLETED_SPACE_LEFT); + OSMO_ASSERT(rdbi.e == 0); + OSMO_ASSERT(write_offset == 1 + 7); + OSMO_ASSERT(num_chunks == 1); + + llc.reset(); + llc.put_frame(llc_data, 11); + + ar = Encoding::rlc_data_to_dl_append(&rdbi, + &llc, &write_offset, &num_chunks, data, false); + + OSMO_ASSERT(ar == Encoding::AR_COMPLETED_BLOCK_FILLED); + OSMO_ASSERT(rdbi.e == 0); + OSMO_ASSERT(write_offset == 2 + 7 + 11); + OSMO_ASSERT(num_chunks == 2); + + OSMO_ASSERT(data[0] == ((7 << 2) | (1 << 1) | (0 << 0))); + OSMO_ASSERT(data[1] == ((11 << 2) | (0 << 1) | (1 << 0))); + OSMO_ASSERT(data[2] == 0); + + /* TS 44.060, B.4 */ + cs = GprsCodingScheme::CS1; + + /* Block 1 */ + gprs_rlc_data_block_info_init(&rdbi, cs); + num_chunks = 0; + write_offset = 0; + memset(data, 0, sizeof(data)); + + llc.reset(); + llc.put_frame(llc_data, 99); + + ar = Encoding::rlc_data_to_dl_append(&rdbi, + &llc, &write_offset, &num_chunks, data, false); + + OSMO_ASSERT(ar == Encoding::AR_NEED_MORE_BLOCKS); + OSMO_ASSERT(rdbi.e == 1); + OSMO_ASSERT(write_offset == 20); + OSMO_ASSERT(num_chunks == 1); + OSMO_ASSERT(rdbi.cv != 0); + + OSMO_ASSERT(data[0] == 0); + + /* TS 44.060, B.5 */ + cs = GprsCodingScheme::CS1; + + /* Block 1 */ + gprs_rlc_data_block_info_init(&rdbi, cs); + num_chunks = 0; + write_offset = 0; + memset(data, 0, sizeof(data)); + + llc.reset(); + llc.put_frame(llc_data, 20); + + ar = Encoding::rlc_data_to_dl_append(&rdbi, + &llc, &write_offset, &num_chunks, data, true); + + OSMO_ASSERT(ar == Encoding::AR_COMPLETED_BLOCK_FILLED); + OSMO_ASSERT(rdbi.e == 1); + OSMO_ASSERT(write_offset == 20); + OSMO_ASSERT(num_chunks == 1); + OSMO_ASSERT(rdbi.cv == 0); + + OSMO_ASSERT(data[0] == 0); + + /* TS 44.060, B.7 */ + cs = GprsCodingScheme::CS1; + + /* Block 1 */ + gprs_rlc_data_block_info_init(&rdbi, cs); + num_chunks = 0; + write_offset = 0; + memset(data, 0, sizeof(data)); + + llc.reset(); + llc.put_frame(llc_data, 30); + + ar = Encoding::rlc_data_to_dl_append(&rdbi, + &llc, &write_offset, &num_chunks, data, false); + + OSMO_ASSERT(ar == Encoding::AR_NEED_MORE_BLOCKS); + OSMO_ASSERT(rdbi.e == 1); + OSMO_ASSERT(write_offset == 20); + OSMO_ASSERT(num_chunks == 1); + + OSMO_ASSERT(data[0] == 0); + + /* Block 2 */ + gprs_rlc_data_block_info_init(&rdbi, cs); + num_chunks = 0; + write_offset = 0; + memset(data, 0, sizeof(data)); + + OSMO_ASSERT(llc.chunk_size() == 10); + + ar = Encoding::rlc_data_to_dl_append(&rdbi, + &llc, &write_offset, &num_chunks, data, false); + + OSMO_ASSERT(ar == Encoding::AR_COMPLETED_SPACE_LEFT); + OSMO_ASSERT(rdbi.e == 0); + OSMO_ASSERT(write_offset == 1 + 10); + OSMO_ASSERT(num_chunks == 1); + + llc.reset(); + llc.put_frame(llc_data, 99); + + ar = Encoding::rlc_data_to_dl_append(&rdbi, + &llc, &write_offset, &num_chunks, data, false); + + OSMO_ASSERT(ar == Encoding::AR_NEED_MORE_BLOCKS); + OSMO_ASSERT(rdbi.e == 0); + OSMO_ASSERT(write_offset == 1 + 10 + 9); + OSMO_ASSERT(num_chunks == 2); + + OSMO_ASSERT(data[0] == ((10 << 2) | (1 << 1) | (1 << 0))); + OSMO_ASSERT(data[1] == 0); + + printf("=== end %s ===\n", __func__); +} + static void test_rlc_unaligned_copy() { uint8_t bits[256]; @@ -614,6 +864,7 @@ int main(int argc, char **argv) test_rlc_info_init(); test_rlc_unit_decoder(); test_rlc_unaligned_copy(); + test_rlc_unit_encoder(); if (getenv("TALLOC_REPORT_FULL")) talloc_report_full(tall_pcu_ctx, stderr); diff --git a/tests/edge/EdgeTest.ok b/tests/edge/EdgeTest.ok index 95ec2b3a..9554df35 100644 --- a/tests/edge/EdgeTest.ok +++ b/tests/edge/EdgeTest.ok @@ -4,3 +4,5 @@ === end test_rlc_info_init === === start test_rlc_unit_decoder === === end test_rlc_unit_decoder === +=== start test_rlc_unit_encoder === +=== end test_rlc_unit_encoder === -- cgit v1.2.3