aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJacob Erlbeck <jerlbeck@sysmocom.de>2016-01-12 11:58:13 +0100
committerJacob Erlbeck <jerlbeck@sysmocom.de>2016-02-05 13:26:34 +0100
commit14bb0947b49e7f936f3ff0708e5891a873362d3b (patch)
tree892774559efb340e991501cb7eca790db2ef5a2d
parent3a3b6a7e861d813894ae9fb35c973a60628ce58a (diff)
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
-rw-r--r--src/encoding.cpp121
-rw-r--r--src/encoding.h14
-rw-r--r--tests/edge/EdgeTest.cpp251
-rw-r--r--tests/edge/EdgeTest.ok2
4 files changed, 388 insertions, 0 deletions
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 ===