aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJacob Erlbeck <jerlbeck@sysmocom.de>2016-01-13 11:28:10 +0100
committerJacob Erlbeck <jerlbeck@sysmocom.de>2016-02-05 18:24:50 +0100
commita88d065606a29f5cd7d140fbacb49d78428ceb98 (patch)
tree06fc28cf06a9d861c9aa75028d00f73e77826618
parent5058bd6e9e8335ca5cd8ad586de3843447c859bf (diff)
edge: Support MCS data block encoding
Currently only GPRS data block encoding is supported. This commit adds rlc_data_to_dl_append_egprs which does the EGPRS specific extension and chunk handling. It extends Encoding::rlc_data_to_dl_append to use that function for MCS coding schemes. Sponsored-by: On-Waves ehf
-rw-r--r--src/encoding.cpp146
-rw-r--r--tests/edge/EdgeTest.cpp243
2 files changed, 389 insertions, 0 deletions
diff --git a/src/encoding.cpp b/src/encoding.cpp
index 4c0ecf98..9def2831 100644
--- a/src/encoding.cpp
+++ b/src/encoding.cpp
@@ -873,6 +873,148 @@ static Encoding::AppendResult rlc_data_to_dl_append_gprs(
return Encoding::AR_COMPLETED_BLOCK_FILLED;
}
+static Encoding::AppendResult rlc_data_to_dl_append_egprs(
+ 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_egprs *li;
+ struct rlc_li_field_egprs *prev_li;
+ uint8_t *delimiter, *data;
+
+ data = data_block + *offset;
+ delimiter = data_block + *num_chunks;
+ prev_li = (struct rlc_li_field_egprs *)
+ (*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);
+ /* fill only space */
+ llc->consume(data, space);
+ /* return data block as message */
+ *offset = rdbi->data_len;
+ (*num_chunks)++;
+ return Encoding::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);
+ /* fill space */
+ llc->consume(data, space);
+ *offset = rdbi->data_len;
+ (*num_chunks)++;
+ rdbi->cv = 0;
+ return Encoding::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): just copy "
+ "it, and we are done. The next block will have "
+ "to start with an empty chunk\n",
+ chunk, space);
+ /* fill space */
+ llc->consume(data, space);
+ *offset = rdbi->data_len;
+ (*num_chunks)++;
+ return Encoding::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 += 1;
+ (*offset) += 1;
+ space -= 1;
+ /* add LI to delimit frame */
+ li = (struct rlc_li_field_egprs *)delimiter;
+ li->e = 1; /* Extension bit, maybe set later */
+ li->li = chunk; /* length of chunk */
+ /* tell previous extension header about the new one */
+ if (prev_li)
+ prev_li->e = 0;
+ rdbi->e = 0; /* 0: extensions present */
+ delimiter++;
+ prev_li = li;
+ (*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) {
+ if (!is_final)
+ return Encoding::AR_COMPLETED_SPACE_LEFT;
+
+ /* we don't have more LLC frames */
+ /* We will have to add another chunk with filling octets */
+ LOGP(DRLCMACDL, LOGL_DEBUG,
+ "-- There is remaining space (%d): add filling byte chunk\n",
+ space);
+
+ if (delimiter != data)
+ memmove(delimiter + 1, delimiter, data - delimiter);
+
+ data += 1;
+ (*offset) += 1;
+ space -= 1;
+
+ /* set filling bytes extension */
+ li = (struct rlc_li_field_egprs *)delimiter;
+ li->e = 1;
+ li->li = 127;
+
+ /* tell previous extension header about the new one */
+ if (prev_li)
+ prev_li->e = 0;
+
+ delimiter++;
+ (*num_chunks)++;
+
+ rdbi->cv = 0;
+
+ LOGP(DRLCMACDL, LOGL_DEBUG, "-- Final block, so we "
+ "are done.\n");
+
+ *offset = rdbi->data_len;
+ return Encoding::AR_COMPLETED_BLOCK_FILLED;
+ }
+
+ if (is_final) {
+ /* we don't have more LLC frames */
+ LOGP(DRLCMACDL, LOGL_DEBUG, "-- Final block, so we "
+ "are done.\n");
+ rdbi->cv = 0;
+ return Encoding::AR_COMPLETED_BLOCK_FILLED;
+ }
+
+ /* we have no space left */
+ LOGP(DRLCMACDL, LOGL_DEBUG, "-- No space left, so we are "
+ "done.\n");
+ return Encoding::AR_COMPLETED_BLOCK_FILLED;
+}
+
Encoding::AppendResult Encoding::rlc_data_to_dl_append(
struct gprs_rlc_data_block_info *rdbi, GprsCodingScheme cs,
gprs_llc *llc, int *offset, int *num_chunks,
@@ -883,6 +1025,10 @@ Encoding::AppendResult Encoding::rlc_data_to_dl_append(
return rlc_data_to_dl_append_gprs(rdbi,
llc, offset, num_chunks, data_block, is_final);
+ if (cs.isEgprs())
+ return rlc_data_to_dl_append_egprs(rdbi,
+ llc, offset, num_chunks, data_block, is_final);
+
LOGP(DRLCMACDL, LOGL_ERROR, "%s data block encoding not implemented\n",
cs.name());
diff --git a/tests/edge/EdgeTest.cpp b/tests/edge/EdgeTest.cpp
index c405b4bc..704a7355 100644
--- a/tests/edge/EdgeTest.cpp
+++ b/tests/edge/EdgeTest.cpp
@@ -730,6 +730,249 @@ static void test_rlc_unit_encoder()
OSMO_ASSERT(data[0] == ((10 << 2) | (1 << 1) | (1 << 0)));
OSMO_ASSERT(data[1] == 0);
+ /* TS 44.060, B.8.1 */
+ cs = GprsCodingScheme::MCS4;
+
+ /* 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, 11);
+
+ ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
+ &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, cs,
+ &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, cs,
+ &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 << 1) | (0 << 0)));
+ OSMO_ASSERT(data[1] == ((26 << 1) | (1 << 0)));
+ OSMO_ASSERT(data[2] == 0);
+
+ /* TS 44.060, B.8.2 */
+
+ /* Note that the spec confuses the byte numbering here, since it
+ * includes the FBI/E header bits into the N2 octet count which
+ * is not consistent with Section 10.3a.1 & 10.3a.2. */
+
+ cs = GprsCodingScheme::MCS2;
+
+ /* 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, 15);
+
+ ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
+ &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 + 15);
+ OSMO_ASSERT(num_chunks == 1);
+
+ llc.reset();
+ llc.put_frame(llc_data, 12);
+
+ ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
+ &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 == 2);
+
+ OSMO_ASSERT(data[0] == ((15 << 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() == 0);
+
+ ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
+ &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 + 0);
+ OSMO_ASSERT(num_chunks == 1);
+
+ llc.reset();
+ llc.put_frame(llc_data, 7);
+
+ ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
+ &llc, &write_offset, &num_chunks, data, false);
+
+ OSMO_ASSERT(ar == Encoding::AR_COMPLETED_SPACE_LEFT);
+ OSMO_ASSERT(rdbi.e == 0);
+ OSMO_ASSERT(rdbi.cv != 0);
+ OSMO_ASSERT(write_offset == 2 + 0 + 7);
+ OSMO_ASSERT(num_chunks == 2);
+
+ llc.reset();
+ llc.put_frame(llc_data, 18);
+
+ ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
+ &llc, &write_offset, &num_chunks, data, false);
+
+ OSMO_ASSERT(ar == Encoding::AR_COMPLETED_BLOCK_FILLED);
+ 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] == ((0 << 1) | (0 << 0)));
+ OSMO_ASSERT(data[1] == ((7 << 1) | (0 << 0)));
+ OSMO_ASSERT(data[2] == ((18 << 1) | (1 << 0)));
+ OSMO_ASSERT(data[3] == 0);
+
+ /* Block 3 */
+ 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, 6);
+
+ ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
+ &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 + 6);
+ OSMO_ASSERT(num_chunks == 1);
+
+ llc.reset();
+ llc.put_frame(llc_data, 12);
+
+ ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
+ &llc, &write_offset, &num_chunks, data, true);
+
+ OSMO_ASSERT(ar == Encoding::AR_COMPLETED_BLOCK_FILLED);
+ 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] == ((6 << 1) | (0 << 0)));
+ OSMO_ASSERT(data[1] == ((12 << 1) | (0 << 0)));
+ OSMO_ASSERT(data[2] == ((127 << 1) | (1 << 0)));
+ OSMO_ASSERT(data[3] == 0);
+
+ /* TS 44.060, B.8.3 */
+
+ /* Note that the spec confuses the byte numbering here, too (see above) */
+
+ cs = GprsCodingScheme::MCS2;
+
+ /* 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, rdbi.data_len);
+
+ ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
+ &llc, &write_offset, &num_chunks, data, true);
+
+ OSMO_ASSERT(ar == Encoding::AR_COMPLETED_BLOCK_FILLED);
+ OSMO_ASSERT(rdbi.e == 1);
+ OSMO_ASSERT(rdbi.cv == 0);
+ OSMO_ASSERT(write_offset == (int)rdbi.data_len);
+ OSMO_ASSERT(num_chunks == 1);
+
+ OSMO_ASSERT(data[0] == 0);
+
+ /* Final block with an LLC of size data_len-1 */
+
+ cs = GprsCodingScheme::MCS2;
+
+ /* 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, rdbi.data_len - 1);
+
+ ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
+ &llc, &write_offset, &num_chunks, data, true);
+
+ OSMO_ASSERT(ar == Encoding::AR_COMPLETED_BLOCK_FILLED);
+ OSMO_ASSERT(rdbi.e == 0);
+ OSMO_ASSERT(rdbi.cv == 0);
+ OSMO_ASSERT(write_offset == (int)rdbi.data_len);
+ OSMO_ASSERT(num_chunks == 1);
+
+ OSMO_ASSERT(data[0] == (((rdbi.data_len-1) << 1) | (1 << 0)));
+ OSMO_ASSERT(data[1] == 0);
+
+ /* Final block with an LLC of size data_len-2 */
+
+ cs = GprsCodingScheme::MCS2;
+
+ /* 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, rdbi.data_len - 2);
+
+ ar = Encoding::rlc_data_to_dl_append(&rdbi, cs,
+ &llc, &write_offset, &num_chunks, data, true);
+
+ OSMO_ASSERT(ar == Encoding::AR_COMPLETED_BLOCK_FILLED);
+ OSMO_ASSERT(rdbi.e == 0);
+ OSMO_ASSERT(rdbi.cv == 0);
+ OSMO_ASSERT(write_offset == (int)rdbi.data_len);
+ OSMO_ASSERT(num_chunks == 2);
+
+ OSMO_ASSERT(data[0] == (((rdbi.data_len-2) << 1) | (0 << 0)));
+ OSMO_ASSERT(data[1] == ((127 << 1) | (1 << 0)));
+ OSMO_ASSERT(data[2] == 0);
+
printf("=== end %s ===\n", __func__);
}