rlc, nr: add pack/unpack of nack_range in status PDUs

This commit is contained in:
Robert Falkenberg 2022-02-25 05:42:13 +01:00
parent b55f8ea624
commit f7515e98cf
4 changed files with 106 additions and 14 deletions

View File

@ -162,17 +162,21 @@ public:
// NACK helper (for LTE and NR)
struct rlc_status_nack_t {
uint32_t nack_sn;
bool has_so;
uint16_t so_start;
uint16_t so_end;
uint32_t nack_sn; // Sequence Number (SN) of first missing SDU
bool has_so; // NACKs continuous sequence of bytes [so_start..so_end]
uint16_t so_start; // First missing byte in SDU with SN=nack_sn
uint16_t so_end; // Last missing byte in SDU with SN=nack_sn or SN=nack_sn+nack_range-1 if has_nack_range.
bool has_nack_range; // NACKs continuous sequence of SDUs
uint8_t nack_range; // Number of SDUs being NACKed (including SN=nack_sn)
rlc_status_nack_t()
{
has_so = false;
nack_sn = 0;
so_start = 0;
so_end = 0;
has_so = false;
nack_sn = 0;
so_start = 0;
so_end = 0;
has_nack_range = false;
nack_range = 0;
}
};

View File

@ -725,7 +725,12 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes)
// Process N_nacks
std::set<uint32_t> retx_sn_set; // Set of PDU SNs added for retransmission (no duplicates)
for (uint32_t nack_idx = 0; nack_idx < status.N_nack; nack_idx++) {
// TODO: Possibly loop NACK range
if (status.nacks[nack_idx].has_nack_range) {
RlcError("Handling NACK ranges is not yet implemented. Ignoring NACK across %d SDU(s) starting from SN=%d",
status.nacks[nack_idx].nack_range,
status.nacks[nack_idx].nack_sn);
continue;
}
if (st.tx_next_ack <= status.nacks[nack_idx].nack_sn && status.nacks[nack_idx].nack_sn <= st.tx_next) {
auto nack = status.nacks[nack_idx];
uint32_t nack_sn = nack.nack_sn;

View File

@ -191,10 +191,15 @@ uint32_t rlc_am_nr_read_status_pdu(const uint8_t* payload,
nack.nack_sn = (*ptr & 0xff) << 4;
ptr++;
e1 = *ptr & 0x08;
uint8_t e2 = *ptr & 0x04;
e1 = *ptr & 0x08; // 1 = further NACKs follow
uint8_t e2 = *ptr & 0x04; // 1 = set of {so_start, so_end} follows
uint8_t e3 = *ptr & 0x02; // 1 = NACK range follows (i.e. NACK across multiple SNs)
// uint8_t len2 = (*ptr & 0xF0) >> 4;
// sanity check for reserved bits
if ((*ptr & 0x01) != 0) {
fprintf(stderr, "Malformed PDU, reserved bits are set.\n");
return 0;
}
nack.nack_sn |= (*ptr & 0xF0) >> 4;
status->nacks[status->N_nack] = nack;
@ -210,6 +215,11 @@ uint32_t rlc_am_nr_read_status_pdu(const uint8_t* payload,
status->nacks[status->N_nack].so_end |= (*ptr);
ptr++;
}
if (e3 != 0) {
status->nacks[status->N_nack].has_nack_range = true;
status->nacks[status->N_nack].nack_range = (*ptr);
ptr++;
}
status->N_nack++;
if (uint32_t(ptr - payload) > nof_bytes) {
fprintf(stderr, "Malformed PDU, trying to read more bytes than it is available\n");
@ -267,8 +277,15 @@ int32_t rlc_am_nr_write_status_pdu(const rlc_am_nr_status_pdu_t& status_pdu,
if (status_pdu.nacks[i].has_so) {
// Set E2
*ptr |= 0x04;
}
ptr++;
if (status_pdu.nacks[i].has_nack_range) {
// Set E3
*ptr |= 0x02;
}
ptr++;
if (status_pdu.nacks[i].has_so) {
(*ptr) = status_pdu.nacks[i].so_start >> 8;
ptr++;
(*ptr) = status_pdu.nacks[i].so_start;
@ -276,8 +293,12 @@ int32_t rlc_am_nr_write_status_pdu(const rlc_am_nr_status_pdu_t& status_pdu,
(*ptr) = status_pdu.nacks[i].so_end >> 8;
ptr++;
(*ptr) = status_pdu.nacks[i].so_end;
ptr++;
}
if (status_pdu.nacks[i].has_nack_range) {
(*ptr) = status_pdu.nacks[i].nack_range;
ptr++;
}
ptr++;
}
}
} else {

View File

@ -376,6 +376,63 @@ int rlc_am_nr_control_pdu_test5()
return SRSRAN_SUCCESS;
}
// Status PDU for 12bit SN with ACK_SN=2065,
// NACK range0: 3 full SDUs, NACK_SN=273..275
// NACK range1: missing segment sequence across 4 SDUs
// starting at NACK_SN=276, SO_START=2,
// ending at NACK_SN=279, SO_END=5
// E1 and E3 bit set on first NACK, E2 and E3 bit set on the second.
int rlc_am_nr_control_pdu_test_nack_range()
{
test_delimit_logger delimiter("Control PDU test NACK range");
const int len = 13;
std::array<uint8_t, len> tv = {0x08, // D/C | CPT | ACK_SN_upper
0x11, // ACK_SN_lower
0x80, // E1 | R
0x11, // NACK_SN_upper
0x1a, // NACK_SN_lower | E1 | E2 | E3 | R
0x03, // NACK_range
0x11, // NACK_SN_upper
0x46, // NACK_SN_lower | E1 | E2 | E3 | R
0x00, // SO_START_upper
0x02, // SO_START_lower
0x00, // SO_END_upper
0x05, // SO_END_lower
0x04}; // NACK_range
srsran::byte_buffer_t pdu = make_pdu_and_log(tv);
TESTASSERT(rlc_am_is_control_pdu(pdu.msg) == true);
// unpack PDU
rlc_am_nr_status_pdu_t status_pdu = {};
TESTASSERT(rlc_am_nr_read_status_pdu(&pdu, srsran::rlc_am_nr_sn_size_t::size12bits, &status_pdu) == SRSRAN_SUCCESS);
TESTASSERT(status_pdu.ack_sn == 2065);
TESTASSERT(status_pdu.N_nack == 2);
TESTASSERT(status_pdu.nacks[0].nack_sn == 273);
TESTASSERT(status_pdu.nacks[0].has_so == false);
TESTASSERT(status_pdu.nacks[0].has_nack_range == true);
TESTASSERT(status_pdu.nacks[0].nack_range == 3);
TESTASSERT(status_pdu.nacks[1].nack_sn == 276);
TESTASSERT(status_pdu.nacks[1].has_so == true);
TESTASSERT(status_pdu.nacks[1].so_start == 2);
TESTASSERT(status_pdu.nacks[1].so_end == 5);
TESTASSERT(status_pdu.nacks[1].has_nack_range == true);
TESTASSERT(status_pdu.nacks[1].nack_range == 4);
// reset status PDU
pdu.clear();
// pack again
TESTASSERT(rlc_am_nr_write_status_pdu(status_pdu, srsran::rlc_am_nr_sn_size_t::size12bits, &pdu) == SRSRAN_SUCCESS);
TESTASSERT(pdu.N_bytes == tv.size());
write_pdu_to_pcap(4, pdu.msg, pdu.N_bytes);
TESTASSERT(memcmp(pdu.msg, tv.data(), pdu.N_bytes) == 0);
return SRSRAN_SUCCESS;
}
int main(int argc, char** argv)
{
static const struct option long_options[] = {{"pcap", no_argument, nullptr, 'p'}, {nullptr, 0, nullptr, 0}};
@ -457,5 +514,10 @@ int main(int argc, char** argv)
return SRSRAN_ERROR;
}
if (rlc_am_nr_control_pdu_test_nack_range()) {
fprintf(stderr, "rlc_am_nr_control_pdu_test_nack_range() failed.\n");
return SRSRAN_ERROR;
}
return SRSRAN_SUCCESS;
}