From a88d065606a29f5cd7d140fbacb49d78428ceb98 Mon Sep 17 00:00:00 2001 From: Jacob Erlbeck Date: Wed, 13 Jan 2016 11:28:10 +0100 Subject: 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 --- src/encoding.cpp | 146 +++++++++++++++++++++++++++++ tests/edge/EdgeTest.cpp | 243 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 389 insertions(+) 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__); } -- cgit v1.2.3