|
|
|
/**
|
|
|
|
*
|
|
|
|
* \section COPYRIGHT
|
|
|
|
*
|
|
|
|
* Copyright 2013-2021 Software Radio Systems Limited
|
|
|
|
*
|
|
|
|
* By using this file, you agree to the terms and conditions set
|
|
|
|
* forth in the LICENSE file which can be found at the top level of
|
|
|
|
* the distribution.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "srsran/common/buffer_pool.h"
|
|
|
|
#include "srsran/common/rlc_pcap.h"
|
|
|
|
#include "srsran/common/test_common.h"
|
|
|
|
#include "srsran/common/threads.h"
|
|
|
|
#include "srsran/interfaces/ue_pdcp_interfaces.h"
|
|
|
|
#include "srsran/interfaces/ue_rrc_interfaces.h"
|
|
|
|
#include "srsran/upper/rlc_am_lte.h"
|
|
|
|
|
|
|
|
#define NBUFS 5
|
|
|
|
#define HAVE_PCAP 0
|
|
|
|
#define SDU_SIZE 500
|
|
|
|
|
|
|
|
using namespace srsue;
|
|
|
|
using namespace srsran;
|
|
|
|
|
|
|
|
bool rx_is_tx(const rlc_bearer_metrics_t& rlc1_metrics, const rlc_bearer_metrics_t& rlc2_metrics)
|
|
|
|
{
|
|
|
|
if (rlc1_metrics.num_tx_pdu_bytes != rlc2_metrics.num_rx_pdu_bytes) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rlc2_metrics.num_tx_pdu_bytes != rlc1_metrics.num_rx_pdu_bytes) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
class rlc_am_tester : public pdcp_interface_rlc, public rrc_interface_rlc
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
rlc_am_tester(rlc_pcap* pcap_ = NULL) : pcap(pcap_) {}
|
|
|
|
|
|
|
|
// PDCP interface
|
|
|
|
void write_pdu(uint32_t lcid, unique_byte_buffer_t sdu)
|
|
|
|
{
|
|
|
|
assert(lcid == 1);
|
|
|
|
sdus.push_back(std::move(sdu));
|
|
|
|
}
|
|
|
|
void write_pdu_bcch_bch(unique_byte_buffer_t sdu) {}
|
|
|
|
void write_pdu_bcch_dlsch(unique_byte_buffer_t sdu) {}
|
|
|
|
void write_pdu_pcch(unique_byte_buffer_t sdu) {}
|
|
|
|
void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t pdu) {}
|
|
|
|
void notify_delivery(uint32_t lcid, const srsran::pdcp_sn_vector_t& pdcp_sn_vec)
|
|
|
|
{
|
|
|
|
assert(lcid == 1);
|
|
|
|
for (uint32_t pdcp_sn : pdcp_sn_vec) {
|
|
|
|
if (notified_counts.find(pdcp_sn) == notified_counts.end()) {
|
|
|
|
notified_counts[pdcp_sn] = 0;
|
|
|
|
}
|
|
|
|
notified_counts[pdcp_sn] += 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
void notify_failure(uint32_t lcid, const srsran::pdcp_sn_vector_t& pdcp_sn_vec)
|
|
|
|
{
|
|
|
|
assert(lcid == 1);
|
|
|
|
// TODO
|
|
|
|
}
|
|
|
|
|
|
|
|
// RRC interface
|
|
|
|
void max_retx_attempted() { max_retx_triggered = true; }
|
|
|
|
void protocol_failure() { protocol_failure_triggered = true; }
|
|
|
|
|
|
|
|
const char* get_rb_name(uint32_t lcid) { return ""; }
|
|
|
|
|
|
|
|
std::vector<unique_byte_buffer_t> sdus;
|
|
|
|
rlc_pcap* pcap = nullptr;
|
|
|
|
bool max_retx_triggered = false;
|
|
|
|
bool protocol_failure_triggered = false;
|
|
|
|
|
|
|
|
std::map<uint32_t, uint32_t> notified_counts; // Map of PDCP SNs to number of notifications
|
|
|
|
};
|
|
|
|
|
|
|
|
class ul_writer : public thread
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
ul_writer(rlc_am_lte* rlc_) : rlc(rlc_), thread("UL_WRITER") {}
|
|
|
|
~ul_writer() { stop(); }
|
|
|
|
void stop()
|
|
|
|
{
|
|
|
|
running = false;
|
|
|
|
int cnt = 0;
|
|
|
|
while (running && cnt < 100) {
|
|
|
|
usleep(10000);
|
|
|
|
cnt++;
|
|
|
|
}
|
|
|
|
wait_thread_finish();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
void run_thread()
|
|
|
|
{
|
|
|
|
int sn = 0;
|
|
|
|
running = true;
|
|
|
|
while (running) {
|
|
|
|
unique_byte_buffer_t pdu = srsran::make_byte_buffer();
|
|
|
|
if (!pdu) {
|
|
|
|
printf("Error: Could not allocate PDU in rlc_tester::run_thread\n\n\n");
|
|
|
|
// backoff for a bit
|
|
|
|
usleep(1000);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
for (uint32_t i = 0; i < SDU_SIZE; i++) {
|
|
|
|
pdu->msg[i] = sn;
|
|
|
|
}
|
|
|
|
sn++;
|
|
|
|
pdu->N_bytes = SDU_SIZE;
|
|
|
|
rlc->write_sdu(std::move(pdu));
|
|
|
|
}
|
|
|
|
running = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
rlc_am_lte* rlc = nullptr;
|
|
|
|
std::atomic<bool> running = {false};
|
|
|
|
};
|
|
|
|
|
|
|
|
int basic_test_tx(rlc_am_lte* rlc, byte_buffer_t pdu_bufs[NBUFS])
|
|
|
|
{
|
|
|
|
// Push 5 SDUs into RLC1
|
|
|
|
unique_byte_buffer_t sdu_bufs[NBUFS];
|
|
|
|
for (int i = 0; i < NBUFS; i++) {
|
|
|
|
sdu_bufs[i] = srsran::make_byte_buffer();
|
|
|
|
sdu_bufs[i]->msg[0] = i; // Write the index into the buffer
|
|
|
|
sdu_bufs[i]->N_bytes = 1; // Give each buffer a size of 1 byte
|
|
|
|
sdu_bufs[i]->md.pdcp_sn = i; // PDCP SN for notifications
|
|
|
|
rlc->write_sdu(std::move(sdu_bufs[i]));
|
|
|
|
}
|
|
|
|
|
|
|
|
TESTASSERT(13 == rlc->get_buffer_state()); // 2 Bytes for fixed header + 6 for LIs + 5 for payload
|
|
|
|
|
|
|
|
// Read 5 PDUs from RLC1 (1 byte each)
|
|
|
|
for (int i = 0; i < NBUFS; i++) {
|
|
|
|
uint32_t len = rlc->read_pdu(pdu_bufs[i].msg, 3); // 2 bytes for header + 1 byte payload
|
|
|
|
pdu_bufs[i].N_bytes = len;
|
|
|
|
TESTASSERT(3 == len);
|
|
|
|
}
|
|
|
|
|
|
|
|
TESTASSERT(0 == rlc->get_buffer_state());
|
|
|
|
return SRSRAN_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
int basic_test()
|
|
|
|
{
|
|
|
|
rlc_am_tester tester;
|
|
|
|
timer_handler timers(8);
|
|
|
|
byte_buffer_t pdu_bufs[NBUFS];
|
|
|
|
|
|
|
|
rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers);
|
|
|
|
rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers);
|
|
|
|
|
|
|
|
// before configuring entity
|
|
|
|
TESTASSERT(0 == rlc1.get_buffer_state());
|
|
|
|
|
|
|
|
if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (not rlc2.configure(rlc_config_t::default_rlc_am_config())) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
basic_test_tx(&rlc1, pdu_bufs);
|
|
|
|
|
|
|
|
// Write 5 PDUs into RLC2
|
|
|
|
for (int i = 0; i < NBUFS; i++) {
|
|
|
|
rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes);
|
|
|
|
}
|
|
|
|
|
|
|
|
TESTASSERT(2 == rlc2.get_buffer_state());
|
|
|
|
|
|
|
|
// Read status PDU from RLC2
|
|
|
|
byte_buffer_t status_buf;
|
|
|
|
int len = rlc2.read_pdu(status_buf.msg, 2);
|
|
|
|
status_buf.N_bytes = len;
|
|
|
|
|
|
|
|
TESTASSERT(0 == rlc2.get_buffer_state());
|
|
|
|
|
|
|
|
// Assert status is correct
|
|
|
|
rlc_status_pdu_t status_check = {};
|
|
|
|
rlc_am_read_status_pdu(status_buf.msg, status_buf.N_bytes, &status_check);
|
|
|
|
TESTASSERT(status_check.ack_sn == 5); // 5 is the last SN that was not received.
|
|
|
|
TESTASSERT(rlc_am_is_valid_status_pdu(status_check));
|
|
|
|
|
|
|
|
// Write status PDU to RLC1
|
|
|
|
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
|
|
|
|
|
|
|
|
// Check PDCP notifications
|
|
|
|
TESTASSERT(tester.notified_counts.size() == 5);
|
|
|
|
for (uint16_t i = 0; i < tester.sdus.size(); i++) {
|
|
|
|
TESTASSERT(tester.sdus[i]->N_bytes == 1);
|
|
|
|
TESTASSERT(*(tester.sdus[i]->msg) == i);
|
|
|
|
TESTASSERT(tester.notified_counts[i] == 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check statistics
|
|
|
|
TESTASSERT(rx_is_tx(rlc1.get_metrics(), rlc2.get_metrics()));
|
|
|
|
|
|
|
|
return SRSRAN_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
int concat_test()
|
|
|
|
{
|
|
|
|
rlc_am_tester tester;
|
|
|
|
srsran::timer_handler timers(8);
|
|
|
|
|
|
|
|
rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers);
|
|
|
|
rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers);
|
|
|
|
|
|
|
|
if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (not rlc2.configure(rlc_config_t::default_rlc_am_config())) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Push 5 SDUs into RLC1
|
|
|
|
unique_byte_buffer_t sdu_bufs[NBUFS];
|
|
|
|
for (int i = 0; i < NBUFS; i++) {
|
|
|
|
sdu_bufs[i] = srsran::make_byte_buffer();
|
|
|
|
sdu_bufs[i]->msg[0] = i; // Write the index into the buffer
|
|
|
|
sdu_bufs[i]->N_bytes = 1; // Give each buffer a size of 1 byte
|
|
|
|
sdu_bufs[i]->md.pdcp_sn = i; // PDCP SN for notifications
|
|
|
|
rlc1.write_sdu(std::move(sdu_bufs[i]));
|
|
|
|
}
|
|
|
|
|
|
|
|
TESTASSERT(13 == rlc1.get_buffer_state()); // 2 Bytes for fixed header + 6 for LIs + 5 for payload
|
|
|
|
|
|
|
|
// Read 1 PDUs from RLC1 containing all 5 SDUs
|
|
|
|
byte_buffer_t pdu_buf;
|
|
|
|
int len = rlc1.read_pdu(pdu_buf.msg, 13); // 8 bytes for header + payload
|
|
|
|
pdu_buf.N_bytes = len;
|
|
|
|
|
|
|
|
TESTASSERT(0 == rlc1.get_buffer_state());
|
|
|
|
|
|
|
|
// Write PDU into RLC2
|
|
|
|
rlc2.write_pdu(pdu_buf.msg, pdu_buf.N_bytes);
|
|
|
|
|
|
|
|
// Check status report
|
|
|
|
TESTASSERT(2 == rlc2.get_buffer_state());
|
|
|
|
byte_buffer_t status_buf;
|
|
|
|
len = rlc2.read_pdu(status_buf.msg, 2);
|
|
|
|
status_buf.N_bytes = len;
|
|
|
|
|
|
|
|
TESTASSERT(0 == rlc2.get_buffer_state());
|
|
|
|
|
|
|
|
// Assert status is correct
|
|
|
|
rlc_status_pdu_t status_check = {};
|
|
|
|
rlc_am_read_status_pdu(status_buf.msg, status_buf.N_bytes, &status_check);
|
|
|
|
TESTASSERT(status_check.ack_sn == 1); // 1 is the last SN that was not received.
|
|
|
|
|
|
|
|
// Write status PDU to RLC1
|
|
|
|
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
|
|
|
|
|
|
|
|
TESTASSERT(tester.sdus.size() == 5);
|
|
|
|
for (uint32_t i = 0; i < tester.sdus.size(); i++) {
|
|
|
|
TESTASSERT(tester.sdus[i]->N_bytes == 1);
|
|
|
|
TESTASSERT(*(tester.sdus[i]->msg) == i);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check PDCP notifications
|
|
|
|
TESTASSERT(tester.notified_counts.size() == 5);
|
|
|
|
for (uint32_t i = 0; i < tester.sdus.size(); i++) {
|
|
|
|
TESTASSERT(tester.sdus[i]->N_bytes == 1);
|
|
|
|
TESTASSERT(*(tester.sdus[i]->msg) == i);
|
|
|
|
TESTASSERT(tester.notified_counts[i] == 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check statistics
|
|
|
|
TESTASSERT(rx_is_tx(rlc1.get_metrics(), rlc2.get_metrics()));
|
|
|
|
|
|
|
|
return SRSRAN_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
int segment_test(bool in_seq_rx)
|
|
|
|
{
|
|
|
|
rlc_am_tester tester;
|
|
|
|
srsran::timer_handler timers(8);
|
|
|
|
int len = 0;
|
|
|
|
|
|
|
|
rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers);
|
|
|
|
rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers);
|
|
|
|
|
|
|
|
if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (not rlc2.configure(rlc_config_t::default_rlc_am_config())) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Push 5 SDUs into RLC1
|
|
|
|
unique_byte_buffer_t sdu_bufs[NBUFS];
|
|
|
|
for (int i = 0; i < NBUFS; i++) {
|
|
|
|
sdu_bufs[i] = srsran::make_byte_buffer();
|
|
|
|
for (int j = 0; j < 10; j++)
|
|
|
|
sdu_bufs[i]->msg[j] = j;
|
|
|
|
sdu_bufs[i]->N_bytes = 10; // Give each buffer a size of 10 bytes
|
|
|
|
sdu_bufs[i]->md.pdcp_sn = i; // PDCP SN for notifications
|
|
|
|
rlc1.write_sdu(std::move(sdu_bufs[i]));
|
|
|
|
}
|
|
|
|
|
|
|
|
TESTASSERT(58 == rlc1.get_buffer_state()); // 2 bytes for header + 6 bytes for LI + 50 bytes for payload
|
|
|
|
|
|
|
|
// Read PDUs from RLC1 (force segmentation)
|
|
|
|
byte_buffer_t pdu_bufs[20];
|
|
|
|
int n_pdus = 0;
|
|
|
|
while (rlc1.get_buffer_state() > 0) {
|
|
|
|
len = rlc1.read_pdu(pdu_bufs[n_pdus].msg, 10); // 2 header + payload
|
|
|
|
pdu_bufs[n_pdus++].N_bytes = len;
|
|
|
|
}
|
|
|
|
|
|
|
|
TESTASSERT(0 == rlc1.get_buffer_state());
|
|
|
|
|
|
|
|
// Write PDUs into RLC2
|
|
|
|
if (in_seq_rx) {
|
|
|
|
// deliver PDUs in order
|
|
|
|
for (int i = 0; i < n_pdus; ++i) {
|
|
|
|
rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// deliver PDUs in reverse order
|
|
|
|
for (int i = n_pdus - 1; i >= 0; --i) {
|
|
|
|
rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Receiver will only generate status PDU if they arrive in order
|
|
|
|
// If SN=7 arrives first, but the Rx expects SN=0, status reporting will be delayed, see TS 36.322 v10 Section 5.2.3
|
|
|
|
if (in_seq_rx) {
|
|
|
|
TESTASSERT(2 == rlc2.get_buffer_state());
|
|
|
|
|
|
|
|
// Read status PDU from RLC2
|
|
|
|
byte_buffer_t status_buf;
|
|
|
|
len = rlc2.read_pdu(status_buf.msg, 10); // 10 bytes is enough to hold the status
|
|
|
|
status_buf.N_bytes = len;
|
|
|
|
|
|
|
|
// Assert status is correct
|
|
|
|
rlc_status_pdu_t status_check = {};
|
|
|
|
rlc_am_read_status_pdu(status_buf.msg, status_buf.N_bytes, &status_check);
|
|
|
|
TESTASSERT(status_check.ack_sn == n_pdus); // n_pdus (8) is the last SN that was not received.
|
|
|
|
|
|
|
|
// Write status PDU to RLC1
|
|
|
|
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
|
|
|
|
|
|
|
|
// Check all notification of ack'ed PDUs
|
|
|
|
TESTASSERT(tester.notified_counts.size() == 5);
|
|
|
|
for (int i = 0; i < NBUFS; i++) {
|
|
|
|
auto not_it = tester.notified_counts.find(i);
|
|
|
|
TESTASSERT(not_it != tester.notified_counts.end() && tester.notified_counts[i] == 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TESTASSERT(0 == rlc2.get_buffer_state());
|
|
|
|
|
|
|
|
TESTASSERT(tester.sdus.size() == 5);
|
|
|
|
for (uint32_t i = 0; i < tester.sdus.size(); i++) {
|
|
|
|
TESTASSERT(tester.sdus[i]->N_bytes == 10);
|
|
|
|
for (int j = 0; j < 10; j++) {
|
|
|
|
TESTASSERT(tester.sdus[i]->msg[j] == j);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check statistics
|
|
|
|
TESTASSERT(rx_is_tx(rlc1.get_metrics(), rlc2.get_metrics()));
|
|
|
|
|
|
|
|
return SRSRAN_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
int retx_test()
|
|
|
|
{
|
|
|
|
rlc_am_tester tester;
|
|
|
|
timer_handler timers(8);
|
|
|
|
int len = 0;
|
|
|
|
|
|
|
|
rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers);
|
|
|
|
rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers);
|
|
|
|
|
|
|
|
if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (not rlc2.configure(rlc_config_t::default_rlc_am_config())) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Push 5 SDUs into RLC1
|
|
|
|
unique_byte_buffer_t sdu_bufs[NBUFS];
|
|
|
|
for (int i = 0; i < NBUFS; i++) {
|
|
|
|
sdu_bufs[i] = srsran::make_byte_buffer();
|
|
|
|
sdu_bufs[i]->msg[0] = i; // Write the index into the buffer
|
|
|
|
sdu_bufs[i]->N_bytes = 1; // Give each buffer a size of 1 byte
|
|
|
|
sdu_bufs[i]->md.pdcp_sn = i; // PDCP SN for notifications
|
|
|
|
rlc1.write_sdu(std::move(sdu_bufs[i]));
|
|
|
|
}
|
|
|
|
|
|
|
|
TESTASSERT(13 == rlc1.get_buffer_state());
|
|
|
|
|
|
|
|
// Read 5 PDUs from RLC1 (1 byte each)
|
|
|
|
byte_buffer_t pdu_bufs[NBUFS];
|
|
|
|
for (int i = 0; i < NBUFS; i++) {
|
|
|
|
len = rlc1.read_pdu(pdu_bufs[i].msg, 3); // 2 byte header + 1 byte payload
|
|
|
|
pdu_bufs[i].N_bytes = len;
|
|
|
|
}
|
|
|
|
|
|
|
|
TESTASSERT(0 == rlc1.get_buffer_state());
|
|
|
|
|
|
|
|
// Write PDUs into RLC2 (skip SN 1)
|
|
|
|
for (int i = 0; i < NBUFS; i++) {
|
|
|
|
if (i != 1) {
|
|
|
|
rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// check buffered bytes at receiver, 3 PDUs with one 1 B each (SN=0 has been delivered already)
|
|
|
|
rlc_bearer_metrics_t metrics = rlc2.get_metrics();
|
|
|
|
TESTASSERT(metrics.rx_buffered_bytes == 3);
|
|
|
|
|
|
|
|
// Step timers until reordering timeout expires
|
|
|
|
for (int cnt = 0; cnt < 5; cnt++) {
|
|
|
|
timers.step_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t buffer_state = rlc2.get_buffer_state();
|
|
|
|
TESTASSERT(4 == buffer_state);
|
|
|
|
|
|
|
|
// Read status PDU from RLC2
|
|
|
|
byte_buffer_t status_buf;
|
|
|
|
len = rlc2.read_pdu(status_buf.msg, buffer_state); // provide exactly the reported buffer state
|
|
|
|
status_buf.N_bytes = len;
|
|
|
|
|
|
|
|
// Assert all bytes for status PDU were read
|
|
|
|
buffer_state = rlc2.get_buffer_state();
|
|
|
|
TESTASSERT(0 == buffer_state);
|
|
|
|
|
|
|
|
// Assert status is correct
|
|
|
|
rlc_status_pdu_t status_check = {};
|
|
|
|
rlc_am_read_status_pdu(status_buf.msg, status_buf.N_bytes, &status_check);
|
|
|
|
TESTASSERT(status_check.N_nack == 1); // 1 packet was lost.
|
|
|
|
TESTASSERT(status_check.nacks[0].nack_sn == 1); // SN 1 was lost.
|
|
|
|
TESTASSERT(status_check.ack_sn == 5); // Delivered up to SN 4.
|
|
|
|
|
|
|
|
// Write status PDU to RLC1
|
|
|
|
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
|
|
|
|
|
|
|
|
TESTASSERT(3 == rlc1.get_buffer_state()); // 2 byte header + 1 byte payload
|
|
|
|
|
|
|
|
// Check notifications of ack'ed PDUs
|
|
|
|
TESTASSERT(tester.notified_counts.size() == 4);
|
|
|
|
for (int i = 0; i < NBUFS; i++) {
|
|
|
|
auto not_it = tester.notified_counts.find(i);
|
|
|
|
if (i != 1) {
|
|
|
|
TESTASSERT(not_it != tester.notified_counts.end() && tester.notified_counts[i] == 1);
|
|
|
|
} else {
|
|
|
|
TESTASSERT(not_it == tester.notified_counts.end());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read the retx PDU from RLC1
|
|
|
|
byte_buffer_t retx;
|
|
|
|
len = rlc1.read_pdu(retx.msg, 3); // 2 byte header + 1 byte payload
|
|
|
|
retx.N_bytes = len;
|
|
|
|
|
|
|
|
// Write the retx PDU to RLC2
|
|
|
|
rlc2.write_pdu(retx.msg, retx.N_bytes);
|
|
|
|
|
|
|
|
TESTASSERT(tester.sdus.size() == 5);
|
|
|
|
for (uint32_t i = 0; i < tester.sdus.size(); i++) {
|
|
|
|
if (tester.sdus[i]->N_bytes != 1)
|
|
|
|
return -1;
|
|
|
|
if (*(tester.sdus[i]->msg) != i)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step timers until poll Retx timeout expires
|
|
|
|
for (int cnt = 0; cnt < 5; cnt++) {
|
|
|
|
timers.step_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get status report of RETX PDU
|
|
|
|
buffer_state = rlc2.get_buffer_state();
|
|
|
|
TESTASSERT(2 == buffer_state);
|
|
|
|
len = rlc2.read_pdu(status_buf.msg, buffer_state); // provide exactly the reported buffer state
|
|
|
|
status_buf.N_bytes = len;
|
|
|
|
|
|
|
|
// Assert status is correct
|
|
|
|
status_check = {};
|
|
|
|
rlc_am_read_status_pdu(status_buf.msg, status_buf.N_bytes, &status_check);
|
|
|
|
TESTASSERT(status_check.N_nack == 0); // No packet was lost.
|
|
|
|
TESTASSERT(status_check.ack_sn == 5); // Delivered up to SN 4.
|
|
|
|
|
|
|
|
// Write status PDU to RLC1
|
|
|
|
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
|
|
|
|
|
|
|
|
// Check all notification of ack'ed PDUs
|
|
|
|
TESTASSERT(tester.notified_counts.size() == 5);
|
|
|
|
for (int i = 0; i < NBUFS; i++) {
|
|
|
|
auto not_it = tester.notified_counts.find(i);
|
|
|
|
TESTASSERT(not_it != tester.notified_counts.end() && tester.notified_counts[i] == 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Test correct upper layer signaling when maxRetx (default 4) have been reached
|
|
|
|
int max_retx_test()
|
|
|
|
{
|
|
|
|
rlc_am_tester tester;
|
|
|
|
timer_handler timers(8);
|
|
|
|
int len = 0;
|
|
|
|
|
|
|
|
rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers);
|
|
|
|
|
|
|
|
const rlc_config_t rlc_cfg = rlc_config_t::default_rlc_am_config();
|
|
|
|
if (not rlc1.configure(rlc_cfg)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Push 2 SDUs into RLC1
|
|
|
|
const uint32_t n_sdus = 2;
|
|
|
|
unique_byte_buffer_t sdu_bufs[n_sdus];
|
|
|
|
for (uint32_t i = 0; i < n_sdus; i++) {
|
|
|
|
sdu_bufs[i] = srsran::make_byte_buffer();
|
|
|
|
sdu_bufs[i]->msg[0] = i; // Write the index into the buffer
|
|
|
|
sdu_bufs[i]->N_bytes = 1; // Give each buffer a size of 1 byte
|
|
|
|
sdu_bufs[i]->md.pdcp_sn = i; // PDCP SN for notifications
|
|
|
|
rlc1.write_sdu(std::move(sdu_bufs[i]));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read 2 PDUs from RLC1 (1 byte each)
|
|
|
|
const uint32_t n_pdus = 2;
|
|
|
|
byte_buffer_t pdu_bufs[n_pdus];
|
|
|
|
for (uint32_t i = 0; i < n_pdus; i++) {
|
|
|
|
len = rlc1.read_pdu(pdu_bufs[i].msg, 3); // 2 byte header + 1 byte payload
|
|
|
|
pdu_bufs[i].N_bytes = len;
|
|
|
|
}
|
|
|
|
|
|
|
|
TESTASSERT(0 == rlc1.get_buffer_state());
|
|
|
|
|
|
|
|
// Fake status PDU that ack SN=1
|
|
|
|
rlc_status_pdu_t fake_status = {};
|
|
|
|
fake_status.ack_sn = 2; // delivered up to SN=1
|
|
|
|
fake_status.N_nack = 1; // one SN was lost
|
|
|
|
fake_status.nacks[0].nack_sn = 0; // it was SN=0 that was lost
|
|
|
|
|
|
|
|
// pack into PDU
|
|
|
|
byte_buffer_t status_pdu;
|
|
|
|
rlc_am_write_status_pdu(&fake_status, &status_pdu);
|
|
|
|
|
|
|
|
// We've Tx'ed once already, loop until the max is reached
|
|
|
|
for (uint32_t retx_count = 0; retx_count < rlc_cfg.am.max_retx_thresh; ++retx_count) {
|
|
|
|
// we've not yet reached max attempts
|
|
|
|
TESTASSERT(tester.max_retx_triggered == false);
|
|
|
|
|
|
|
|
// Write status PDU to RLC1
|
|
|
|
rlc1.write_pdu(status_pdu.msg, status_pdu.N_bytes);
|
|
|
|
|
|
|
|
byte_buffer_t pdu_buf;
|
|
|
|
len = rlc1.read_pdu(pdu_buf.msg, 3);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now maxRetx should have been triggered
|
|
|
|
TESTASSERT(tester.max_retx_triggered == true);
|
|
|
|
|
|
|
|
return SRSRAN_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Purpose: test correct retx of lost segment and pollRetx timer expiration
|
|
|
|
int segment_retx_test()
|
|
|
|
{
|
|
|
|
rlc_am_tester tester;
|
|
|
|
timer_handler timers(8);
|
|
|
|
int len = 0;
|
|
|
|
|
|
|
|
rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers);
|
|
|
|
rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers);
|
|
|
|
|
|
|
|
if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (not rlc2.configure(rlc_config_t::default_rlc_am_config())) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Push SDU(s) into RLC1
|
|
|
|
const uint32_t nof_sdus = 1; // just one SDU to make sure the transmitter sets polling bit
|
|
|
|
unique_byte_buffer_t sdu_bufs[nof_sdus];
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < nof_sdus; i++) {
|
|
|
|
sdu_bufs[i] = srsran::make_byte_buffer();
|
|
|
|
sdu_bufs[i]->N_bytes = 10; // Give each buffer a size of 10 bytes
|
|
|
|
std::fill(sdu_bufs[i]->msg, sdu_bufs[i]->msg + sdu_bufs[i]->N_bytes, 0);
|
|
|
|
sdu_bufs[i]->msg[0] = i; // Write the index into the buffer
|
|
|
|
rlc1.write_sdu(std::move(sdu_bufs[i]));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read 2 PDUs from RLC1
|
|
|
|
const uint32_t nof_pdus = 2;
|
|
|
|
byte_buffer_t pdu_bufs[nof_pdus];
|
|
|
|
for (uint32_t i = 0; i < nof_pdus; i++) {
|
|
|
|
len = rlc1.read_pdu(pdu_bufs[i].msg, 7); // 2 byte header
|
|
|
|
pdu_bufs[i].N_bytes = len;
|
|
|
|
}
|
|
|
|
|
|
|
|
TESTASSERT(rlc1.get_buffer_state() == 0);
|
|
|
|
|
|
|
|
// Step timers until poll Retx timeout expires
|
|
|
|
for (int cnt = 0; cnt < 5; cnt++) {
|
|
|
|
timers.step_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t buffer_state = rlc1.get_buffer_state();
|
|
|
|
TESTASSERT(buffer_state == 7);
|
|
|
|
|
|
|
|
// Read retx PDU from RLC1
|
|
|
|
byte_buffer_t retx_pdu;
|
|
|
|
len = rlc1.read_pdu(retx_pdu.msg, buffer_state); // provide exactly the reported buffer state
|
|
|
|
retx_pdu.N_bytes = len;
|
|
|
|
|
|
|
|
// Write retx segment to RLC2
|
|
|
|
rlc2.write_pdu(retx_pdu.msg, retx_pdu.N_bytes);
|
|
|
|
|
|
|
|
buffer_state = rlc2.get_buffer_state(); // Status PDU
|
|
|
|
TESTASSERT(buffer_state == 2);
|
|
|
|
|
|
|
|
// Read status PDU from RLC2
|
|
|
|
byte_buffer_t status_buf;
|
|
|
|
len = rlc2.read_pdu(status_buf.msg, 10); // 10 bytes is enough to hold the status
|
|
|
|
status_buf.N_bytes = len;
|
|
|
|
|
|
|
|
// Assert status is correct
|
|
|
|
rlc_status_pdu_t status_check = {};
|
|
|
|
rlc_am_read_status_pdu(status_buf.msg, status_buf.N_bytes, &status_check);
|
|
|
|
TESTASSERT(status_check.N_nack == 0); // No packet was lost.
|
|
|
|
TESTASSERT(status_check.ack_sn == 1); // Delivered up to SN 0.
|
|
|
|
|
|
|
|
// Write status PDU to RLC1
|
|
|
|
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
|
|
|
|
|
|
|
|
// Make sure no notifications yet
|
|
|
|
TESTASSERT(tester.notified_counts.size() == 0);
|
|
|
|
|
|
|
|
// Step timers again until poll Retx timeout expires
|
|
|
|
for (int cnt = 0; cnt < 5; cnt++) {
|
|
|
|
TESTASSERT(rlc1.get_buffer_state() == 0); // No status transmissions until pollRetx expires
|
|
|
|
timers.step_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
// read buffer state from RLC1 again to see if it has rescheduled SN=1 for retx
|
|
|
|
buffer_state = rlc1.get_buffer_state(); // Status PDU
|
|
|
|
TESTASSERT(buffer_state == 7);
|
|
|
|
|
|
|
|
// Read 2nd retx PDU from RLC1
|
|
|
|
byte_buffer_t retx_pdu2;
|
|
|
|
len = rlc1.read_pdu(retx_pdu2.msg, buffer_state); // provide exactly the reported buffer state
|
|
|
|
retx_pdu2.N_bytes = len;
|
|
|
|
|
|
|
|
// Write retx segment to RLC2
|
|
|
|
rlc2.write_pdu(retx_pdu2.msg, retx_pdu2.N_bytes);
|
|
|
|
|
|
|
|
// read Status PDU from RLC2 again
|
|
|
|
len = rlc2.read_pdu(status_buf.msg, 10); // 10 bytes is enough to hold the status
|
|
|
|
status_buf.N_bytes = len;
|
|
|
|
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
|
|
|
|
|
|
|
|
// Assert status is correct
|
|
|
|
status_check = {};
|
|
|
|
rlc_am_read_status_pdu(status_buf.msg, status_buf.N_bytes, &status_check);
|
|
|
|
TESTASSERT(status_check.N_nack == 0); // No packet was lost.
|
|
|
|
TESTASSERT(status_check.ack_sn == 2); // Delivered up to SN 0.
|
|
|
|
|
|
|
|
// Make sure SDU was notified
|
|
|
|
TESTASSERT(tester.notified_counts.size() == 1);
|
|
|
|
TESTASSERT(tester.notified_counts.find(0) != tester.notified_counts.end() && tester.notified_counts[0] == 1);
|
|
|
|
|
|
|
|
TESTASSERT(tester.sdus.size() == nof_sdus);
|
|
|
|
for (uint32_t i = 0; i < tester.sdus.size(); i++) {
|
|
|
|
if (tester.sdus[i]->N_bytes != 10) {
|
|
|
|
return SRSRAN_ERROR;
|
|
|
|
}
|
|
|
|
if (*(tester.sdus[i]->msg) != i) {
|
|
|
|
return SRSRAN_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return SRSRAN_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
int resegment_test_1()
|
|
|
|
{
|
|
|
|
// SDUs: | 10 | 10 | 10 | 10 | 10 |
|
|
|
|
// PDUs: | 10 | 10 | 10 | 10 | 10 |
|
|
|
|
// Retx PDU segments: | 5 | 5|
|
|
|
|
|
|
|
|
rlc_am_tester tester;
|
|
|
|
timer_handler timers(8);
|
|
|
|
int len = 0;
|
|
|
|
|
|
|
|
rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers);
|
|
|
|
rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers);
|
|
|
|
|
|
|
|
if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (not rlc2.configure(rlc_config_t::default_rlc_am_config())) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Push 5 SDUs into RLC1
|
|
|
|
unique_byte_buffer_t sdu_bufs[NBUFS];
|
|
|
|
for (int i = 0; i < NBUFS; i++) {
|
|
|
|
sdu_bufs[i] = srsran::make_byte_buffer();
|
|
|
|
for (int j = 0; j < 10; j++)
|
|
|
|
sdu_bufs[i]->msg[j] = j;
|
|
|
|
sdu_bufs[i]->N_bytes = 10; // Give each buffer a size of 10 bytes
|
|
|
|
sdu_bufs[i]->md.pdcp_sn = i;
|
|
|
|
rlc1.write_sdu(std::move(sdu_bufs[i]));
|
|
|
|
}
|
|
|
|
|
|
|
|
TESTASSERT(58 == rlc1.get_buffer_state()); // 2 bytes for fixed header, 6 bytes for LIs, 50 bytes for data
|
|
|
|
|
|
|
|
// Read 5 PDUs from RLC1 (10 bytes each)
|
|
|
|
byte_buffer_t pdu_bufs[NBUFS];
|
|
|
|
for (int i = 0; i < NBUFS; i++) {
|
|
|
|
len = rlc1.read_pdu(pdu_bufs[i].msg, 12); // 12 bytes for header + payload
|
|
|
|
pdu_bufs[i].N_bytes = len;
|
|
|
|
}
|
|
|
|
|
|
|
|
TESTASSERT(0 == rlc1.get_buffer_state());
|
|
|
|
|
|
|
|
// Write PDUs into RLC2 (skip SN 1)
|
|
|
|
for (int i = 0; i < NBUFS; i++) {
|
|
|
|
if (i != 1)
|
|
|
|
rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step timers until reordering timeout expires
|
|
|
|
for (int cnt = 0; cnt < 5; cnt++) {
|
|
|
|
timers.step_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
TESTASSERT(4 == rlc2.get_buffer_state());
|
|
|
|
|
|
|
|
// Read status PDU from RLC2
|
|
|
|
byte_buffer_t status_buf;
|
|
|
|
len = rlc2.read_pdu(status_buf.msg, 10); // 10 bytes is enough to hold the status
|
|
|
|
status_buf.N_bytes = len;
|
|
|
|
|
|
|
|
// Assert status is correct
|
|
|
|
rlc_status_pdu_t status_check = {};
|
|
|
|
rlc_am_read_status_pdu(status_buf.msg, status_buf.N_bytes, &status_check);
|
|
|
|
TESTASSERT(status_check.N_nack == 1); // 1 packet was lost.
|
|
|
|
TESTASSERT(status_check.nacks[0].nack_sn == 1); // SN 1 was lost.
|
|
|
|
TESTASSERT(status_check.ack_sn == 5); // Delivered up to SN 5.
|
|
|
|
|
|
|
|
// Write status PDU to RLC1
|
|
|
|
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
|
|
|
|
|
|
|
|
TESTASSERT(12 == rlc1.get_buffer_state()); // 2 byte header + 10 data
|
|
|
|
|
|
|
|
// Check notifications
|
|
|
|
srslog::fetch_basic_logger("RLC_AM_1").debug("%ld", tester.notified_counts.size());
|
|
|
|
TESTASSERT(tester.notified_counts.size() == 4);
|
|
|
|
for (int i = 0; i < 5; i++) {
|
|
|
|
auto it = tester.notified_counts.find(i);
|
|
|
|
if (i != 1) {
|
|
|
|
TESTASSERT(it != tester.notified_counts.end() && tester.notified_counts[i] == 1);
|
|
|
|
} else {
|
|
|
|
TESTASSERT(it == tester.notified_counts.end());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read the retx PDU from RLC1 and force resegmentation
|
|
|
|
byte_buffer_t retx1;
|
|
|
|
len = rlc1.read_pdu(retx1.msg, 9); // 4 byte header + 5 data
|
|
|
|
retx1.N_bytes = len;
|
|
|
|
|
|
|
|
// Write the retx PDU to RLC2
|
|
|
|
rlc2.write_pdu(retx1.msg, retx1.N_bytes);
|
|
|
|
|
|
|
|
TESTASSERT(9 == rlc1.get_buffer_state());
|
|
|
|
|
|
|
|
// Step timers to get status report
|
|
|
|
for (int cnt = 0; cnt < 5; cnt++) {
|
|
|
|
timers.step_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read status PDU from RLC2
|
|
|
|
status_buf = {};
|
|
|
|
len = rlc2.read_pdu(status_buf.msg, 10); // 10 bytes is enough to hold the status
|
|
|
|
status_buf.N_bytes = len;
|
|
|
|
|
|
|
|
// Assert status is correct
|
|
|
|
status_check = {};
|
|
|
|
rlc_am_read_status_pdu(status_buf.msg, status_buf.N_bytes, &status_check);
|
|
|
|
TESTASSERT(status_check.N_nack == 1); // 1 packet was lost.
|
|
|
|
TESTASSERT(status_check.nacks[0].nack_sn == 1); // SN 1 was lost.
|
|
|
|
TESTASSERT(status_check.ack_sn == 5); // Delivered up to SN 5.
|
|
|
|
|
|
|
|
// Read the remaining segment
|
|
|
|
byte_buffer_t retx2;
|
|
|
|
len = rlc1.read_pdu(retx2.msg, 9); // 4 byte header + 5 data
|
|
|
|
retx2.N_bytes = len;
|
|
|
|
|
|
|
|
// Write the retx PDU to RLC2
|
|
|
|
rlc2.write_pdu(retx2.msg, retx2.N_bytes);
|
|
|
|
|
|
|
|
TESTASSERT(tester.sdus.size() == 5);
|
|
|
|
for (uint32_t i = 0; i < tester.sdus.size(); i++) {
|
|
|
|
if (tester.sdus[i]->N_bytes != 10)
|
|
|
|
return -1;
|
|
|
|
for (int j = 0; j < 10; j++)
|
|
|
|
if (tester.sdus[i]->msg[j] != j)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step timers to get status report
|
|
|
|
for (int cnt = 0; cnt < 5; cnt++) {
|
|
|
|
timers.step_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read status PDU from RLC2
|
|
|
|
status_buf = {};
|
|
|
|
len = rlc2.read_pdu(status_buf.msg, 10); // 10 bytes is enough to hold the status
|
|
|
|
status_buf.N_bytes = len;
|
|
|
|
|
|
|
|
// Assert status is correct
|
|
|
|
status_check = {};
|
|
|
|
rlc_am_read_status_pdu(status_buf.msg, status_buf.N_bytes, &status_check);
|
|
|
|
TESTASSERT(status_check.N_nack == 0); // all packets delivered.
|
|
|
|
TESTASSERT(status_check.ack_sn == 5); // Delivered up to SN 5.
|
|
|
|
|
|
|
|
// Write status PDU to RLC1
|
|
|
|
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
|
|
|
|
|
|
|
|
// Check notifications
|
|
|
|
TESTASSERT(tester.notified_counts.size() == 5);
|
|
|
|
for (int i = 0; i < 5; i++) {
|
|
|
|
auto it = tester.notified_counts.find(i);
|
|
|
|
TESTASSERT(it != tester.notified_counts.end() && tester.notified_counts[i] == 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int resegment_test_2()
|
|
|
|
{
|
|
|
|
// SDUs: | 10 | 10 | 10 | 10 | 10 |
|
|
|
|
// PDUs: | 5 | 10 | 20 | 10 | 5 |
|
|
|
|
// Retx PDU segments: | 10 | 10 |
|
|
|
|
|
|
|
|
rlc_am_tester tester;
|
|
|
|
timer_handler timers(8);
|
|
|
|
|
|
|
|
rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers);
|
|
|
|
rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers);
|
|
|
|
|
|
|
|
if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (not rlc2.configure(rlc_config_t::default_rlc_am_config())) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Push 5 SDUs into RLC1
|
|
|
|
unique_byte_buffer_t sdu_bufs[NBUFS];
|
|
|
|
for (int i = 0; i < NBUFS; i++) {
|
|
|
|
sdu_bufs[i] = srsran::make_byte_buffer();
|
|
|
|
for (int j = 0; j < 10; j++)
|
|
|
|
sdu_bufs[i]->msg[j] = j;
|
|
|
|
sdu_bufs[i]->N_bytes = 10; // Give each buffer a size of 10 bytes
|
|
|
|
sdu_bufs[i]->md.pdcp_sn = i; // Give each buffer a size of 10 bytes
|
|
|
|
rlc1.write_sdu(std::move(sdu_bufs[i]));
|
|
|
|
}
|
|
|
|
|
|
|
|
TESTASSERT(58 == rlc1.get_buffer_state());
|
|
|
|
|
|
|
|
// Read 5 PDUs from RLC1 (5 bytes, 10 bytes, 20 bytes, 10 bytes, 5 bytes)
|
|
|
|
byte_buffer_t pdu_bufs[NBUFS];
|
|
|
|
pdu_bufs[0].N_bytes = rlc1.read_pdu(pdu_bufs[0].msg, 7); // 2 byte header + 5 byte payload
|
|
|
|
pdu_bufs[1].N_bytes = rlc1.read_pdu(pdu_bufs[1].msg, 14); // 4 byte header + 10 byte payload
|
|
|
|
pdu_bufs[2].N_bytes = rlc1.read_pdu(pdu_bufs[2].msg, 25); // 5 byte header + 20 byte payload
|
|
|
|
pdu_bufs[3].N_bytes = rlc1.read_pdu(pdu_bufs[3].msg, 14); // 4 byte header + 10 byte payload
|
|
|
|
pdu_bufs[4].N_bytes = rlc1.read_pdu(pdu_bufs[4].msg, 7); // 2 byte header + 5 byte payload
|
|
|
|
|
|
|
|
TESTASSERT(0 == rlc1.get_buffer_state());
|
|
|
|
|
|
|
|
// Write PDUs into RLC2 (skip SN 2)
|
|
|
|
for (int i = 0; i < NBUFS; i++) {
|
|
|
|
if (i != 2)
|
|
|
|
rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step timers until reordering timeout expires
|
|
|
|
for (int cnt = 0; cnt < 5; cnt++) {
|
|
|
|
timers.step_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
TESTASSERT(4 == rlc2.get_buffer_state());
|
|
|
|
|
|
|
|
// Read status PDU from RLC2
|
|
|
|
byte_buffer_t status_buf;
|
|
|
|
status_buf.N_bytes = rlc2.read_pdu(status_buf.msg, 10); // 10 bytes is enough to hold the status
|
|
|
|
|
|
|
|
// Assert status is correct
|
|
|
|
rlc_status_pdu_t status_check = {};
|
|
|
|
rlc_am_read_status_pdu(status_buf.msg, status_buf.N_bytes, &status_check);
|
|
|
|
TESTASSERT(status_check.N_nack == 1); // One packet was lost.
|
|
|
|
TESTASSERT(status_check.ack_sn == 5); // Delivered up to SN 5.
|
|
|
|
|
|
|
|
// Write status PDU to RLC1
|
|
|
|
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
|
|
|
|
|
|
|
|
TESTASSERT(25 == rlc1.get_buffer_state()); // 4 byte header + 20 data
|
|
|
|
|
|
|
|
// Check notifications
|
|
|
|
TESTASSERT(tester.notified_counts.size() == 2);
|
|
|
|
for (int i = 0; i < 5; i++) {
|
|
|
|
auto it = tester.notified_counts.find(i);
|
|
|
|
if (i == 0 || i == 4) {
|
|
|
|
TESTASSERT(it != tester.notified_counts.end() && tester.notified_counts[i] == 1);
|
|
|
|
} else {
|
|
|
|
TESTASSERT(it == tester.notified_counts.end());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read the retx PDU from RLC1 and force resegmentation
|
|
|
|
byte_buffer_t retx1;
|
|
|
|
retx1.N_bytes = rlc1.read_pdu(retx1.msg, 16); // 6 byte header + 10 data
|
|
|
|
|
|
|
|
// Write the retx PDU to RLC2
|
|
|
|
rlc2.write_pdu(retx1.msg, retx1.N_bytes);
|
|
|
|
|
|
|
|
TESTASSERT(16 == rlc1.get_buffer_state());
|
|
|
|
|
|
|
|
// Read the remaining segment
|
|
|
|
byte_buffer_t retx2;
|
|
|
|
retx2.N_bytes = rlc1.read_pdu(retx2.msg, 18); // 6 byte header + 12 data
|
|
|
|
|
|
|
|
// Write the retx PDU to RLC2
|
|
|
|
rlc2.write_pdu(retx2.msg, retx2.N_bytes);
|
|
|
|
|
|
|
|
TESTASSERT(tester.sdus.size() == 5);
|
|
|
|
for (uint32_t i = 0; i < tester.sdus.size(); i++) {
|
|
|
|
if (tester.sdus[i]->N_bytes != 10)
|
|
|
|
return -1;
|
|
|
|
for (int j = 0; j < 10; j++)
|
|
|
|
if (tester.sdus[i]->msg[j] != j)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step timers until reordering timeout expires
|
|
|
|
for (int cnt = 0; cnt < 5; cnt++) {
|
|
|
|
timers.step_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read status PDU from RLC2
|
|
|
|
status_buf.N_bytes = rlc2.read_pdu(status_buf.msg, 10); // 10 bytes is enough to hold the status
|
|
|
|
|
|
|
|
// Assert status is correct
|
|
|
|
status_check = {};
|
|
|
|
rlc_am_read_status_pdu(status_buf.msg, status_buf.N_bytes, &status_check);
|
|
|
|
TESTASSERT(status_check.N_nack == 0); // all packets delivered.
|
|
|
|
TESTASSERT(status_check.ack_sn == 5); // Delivered up to SN 5.
|
|
|
|
|
|
|
|
// Write status PDU to RLC1
|
|
|
|
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
|
|
|
|
TESTASSERT(tester.notified_counts.size() == 5);
|
|
|
|
|
|
|
|
for (int i = 0; i < 5; i++) {
|
|
|
|
auto it = tester.notified_counts.find(i);
|
|
|
|
TESTASSERT(it != tester.notified_counts.end() && tester.notified_counts[i] == 1);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int resegment_test_3()
|
|
|
|
{
|
|
|
|
// SDUs: | 10 | 10 | 10 | 10 | 10 |
|
|
|
|
// PDUs: | 5 | 5| 20 | 10 | 10 |
|
|
|
|
// Retx PDU segments: | 10 | 10 |
|
|
|
|
|
|
|
|
rlc_am_tester tester;
|
|
|
|
srsran::timer_handler timers(8);
|
|
|
|
|
|
|
|
rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers);
|
|
|
|
rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers);
|
|
|
|
|
|
|
|
if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (not rlc2.configure(rlc_config_t::default_rlc_am_config())) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Push 5 SDUs into RLC1
|
|
|
|
unique_byte_buffer_t sdu_bufs[NBUFS];
|
|
|
|
for (int i = 0; i < NBUFS; i++) {
|
|
|
|
sdu_bufs[i] = srsran::make_byte_buffer();
|
|
|
|
for (int j = 0; j < 10; j++)
|
|
|
|
sdu_bufs[i]->msg[j] = j;
|
|
|
|
sdu_bufs[i]->N_bytes = 10; // Give each buffer a size of 10 bytes
|
|
|
|
sdu_bufs[i]->md.pdcp_sn = i;
|
|
|
|
rlc1.write_sdu(std::move(sdu_bufs[i]));
|
|
|
|
}
|
|
|
|
|
|
|
|
TESTASSERT(58 == rlc1.get_buffer_state());
|
|
|
|
|
|
|
|
// Read 5 PDUs from RLC1 (5 bytes, 5 bytes, 20 bytes, 10 bytes, 10 bytes)
|
|
|
|
byte_buffer_t pdu_bufs[NBUFS];
|
|
|
|
pdu_bufs[0].N_bytes = rlc1.read_pdu(pdu_bufs[0].msg, 7); // 2 byte header + 5 byte payload
|
|
|
|
pdu_bufs[1].N_bytes = rlc1.read_pdu(pdu_bufs[1].msg, 7); // 2 byte header + 5 byte payload
|
|
|
|
pdu_bufs[2].N_bytes = rlc1.read_pdu(pdu_bufs[2].msg, 24); // 4 byte header + 20 byte payload
|
|
|
|
pdu_bufs[3].N_bytes = rlc1.read_pdu(pdu_bufs[3].msg, 12); // 2 byte header + 10 byte payload
|
|
|
|
pdu_bufs[4].N_bytes = rlc1.read_pdu(pdu_bufs[4].msg, 12); // 2 byte header + 10 byte payload
|
|
|
|
|
|
|
|
TESTASSERT(0 == rlc1.get_buffer_state());
|
|
|
|
|
|
|
|
// Write PDUs into RLC2 (skip SN 2)
|
|
|
|
for (int i = 0; i < NBUFS; i++) {
|
|
|
|
if (i != 2)
|
|
|
|
rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step timers until reordering timeout expires
|
|
|
|
for (int cnt = 0; cnt < 5; cnt++) {
|
|
|
|
timers.step_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
TESTASSERT(4 == rlc2.get_buffer_state());
|
|
|
|
|
|
|
|
// Read status PDU from RLC2
|
|
|
|
byte_buffer_t status_buf;
|
|
|
|
status_buf.N_bytes = rlc2.read_pdu(status_buf.msg, 10); // 10 bytes is enough to hold the status
|
|
|
|
|
|
|
|
// Assert status is correct
|
|
|
|
rlc_status_pdu_t status_check = {};
|
|
|
|
rlc_am_read_status_pdu(status_buf.msg, status_buf.N_bytes, &status_check);
|
|
|
|
TESTASSERT(status_check.N_nack == 1); // One packet was lost.
|
|
|
|
TESTASSERT(status_check.nacks[0].nack_sn == 2); // SN 2 was lost.
|
|
|
|
TESTASSERT(status_check.ack_sn == 5); // Delivered up to SN 5.
|
|
|
|
|
|
|
|
// Write status PDU to RLC1
|
|
|
|
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
|
|
|
|
|
|
|
|
// Check notifications
|
|
|
|
TESTASSERT(tester.notified_counts.size() == 3);
|
|
|
|
for (int i = 0; i < 5; i++) {
|
|
|
|
auto it = tester.notified_counts.find(i);
|
|
|
|
if (i == 0 || i == 3 || i == 4) {
|
|
|
|
TESTASSERT(it != tester.notified_counts.end() && tester.notified_counts[i] == 1);
|
|
|
|
} else {
|
|
|
|
TESTASSERT(it == tester.notified_counts.end());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read the retx PDU from RLC1 and force resegmentation
|
|
|
|
byte_buffer_t retx1;
|
|
|
|
retx1.N_bytes = rlc1.read_pdu(retx1.msg, 16); // 6 byte header + 10 data
|
|
|
|
|
|
|
|
// Write the retx PDU to RLC2
|
|
|
|
rlc2.write_pdu(retx1.msg, retx1.N_bytes);
|
|
|
|
|
|
|
|
// Read the remaining segment
|
|
|
|
byte_buffer_t retx2;
|
|
|
|
retx2.N_bytes = rlc1.read_pdu(retx2.msg, 16); // 6 byte header + 10 data
|
|
|
|
|
|
|
|
// Write the retx PDU to RLC2
|
|
|
|
rlc2.write_pdu(retx2.msg, retx2.N_bytes);
|
|
|
|
|
|
|
|
TESTASSERT(tester.sdus.size() == 5);
|
|
|
|
for (uint32_t i = 0; i < tester.sdus.size(); i++) {
|
|
|
|
if (tester.sdus[i]->N_bytes != 10)
|
|
|
|
return -1;
|
|
|
|
for (int j = 0; j < 10; j++)
|
|
|
|
if (tester.sdus[i]->msg[j] != j)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get status from RLC 2
|
|
|
|
for (int cnt = 0; cnt < 5; cnt++) {
|
|
|
|
timers.step_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read status PDU from RLC2
|
|
|
|
status_buf.N_bytes = rlc2.read_pdu(status_buf.msg, 10); // 10 bytes is enough to hold the status
|
|
|
|
|
|
|
|
// Assert status is correct
|
|
|
|
status_check = {};
|
|
|
|
rlc_am_read_status_pdu(status_buf.msg, status_buf.N_bytes, &status_check);
|
|
|
|
TESTASSERT(status_check.N_nack == 0); // all packets delivered.
|
|
|
|
TESTASSERT(status_check.ack_sn == 5); // Delivered up to SN 5.
|
|
|
|
|
|
|
|
// Write status PDU to RLC1
|
|
|
|
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
|
|
|
|
|
|
|
|
// Check final notifications
|
|
|
|
TESTASSERT(tester.notified_counts.size() == 5);
|
|
|
|
for (int i = 0; i < 5; i++) {
|
|
|
|
auto it = tester.notified_counts.find(i);
|
|
|
|
TESTASSERT(it != tester.notified_counts.end() && tester.notified_counts[i] == 1);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int resegment_test_4()
|
|
|
|
{
|
|
|
|
// SDUs: | 10 | 10 | 10 | 10 | 10 |
|
|
|
|
// PDUs: | 5 | 5| 30 | 5 | 5|
|
|
|
|
// Retx PDU segments: | 15 | 15 |
|
|
|
|
|
|
|
|
rlc_am_tester tester;
|
|
|
|
srsran::timer_handler timers(8);
|
|
|
|
|
|
|
|
rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers);
|
|
|
|
rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers);
|
|
|
|
|
|
|
|
if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (not rlc2.configure(rlc_config_t::default_rlc_am_config())) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Push 5 SDUs into RLC1
|
|
|
|
unique_byte_buffer_t sdu_bufs[NBUFS];
|
|
|
|
for (int i = 0; i < NBUFS; i++) {
|
|
|
|
sdu_bufs[i] = srsran::make_byte_buffer();
|
|
|
|
for (int j = 0; j < 10; j++)
|
|
|
|
sdu_bufs[i]->msg[j] = j;
|
|
|
|
sdu_bufs[i]->N_bytes = 10; // Give each buffer a size of 10 bytes
|
|
|
|
sdu_bufs[i]->md.pdcp_sn = i;
|
|
|
|
rlc1.write_sdu(std::move(sdu_bufs[i]));
|
|
|
|
}
|
|
|
|
|
|
|
|
TESTASSERT(58 == rlc1.get_buffer_state());
|
|
|
|
|
|
|
|
// Read 5 PDUs from RLC1 (5 bytes, 5 bytes, 30 bytes, 5 bytes, 5 bytes)
|
|
|
|
byte_buffer_t pdu_bufs[NBUFS];
|
|
|
|
pdu_bufs[0].N_bytes = rlc1.read_pdu(pdu_bufs[0].msg, 7); // 2 byte header + 5 byte payload
|
|
|
|
pdu_bufs[1].N_bytes = rlc1.read_pdu(pdu_bufs[1].msg, 7); // 2 byte header + 5 byte payload
|
|
|
|
pdu_bufs[2].N_bytes = rlc1.read_pdu(pdu_bufs[2].msg, 35); // 5 byte header + 30 byte payload
|
|
|
|
pdu_bufs[3].N_bytes = rlc1.read_pdu(pdu_bufs[3].msg, 7); // 2 byte header + 5 byte payload
|
|
|
|
pdu_bufs[4].N_bytes = rlc1.read_pdu(pdu_bufs[4].msg, 7); // 2 byte header + 5 byte payload
|
|
|
|
|
|
|
|
TESTASSERT(0 == rlc1.get_buffer_state());
|
|
|
|
|
|
|
|
// Write PDUs into RLC2 (skip SN 2)
|
|
|
|
for (int i = 0; i < NBUFS; i++) {
|
|
|
|
if (i != 2)
|
|
|
|
rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step timers until reordering timeout expires
|
|
|
|
int cnt = 5;
|
|
|
|
while (cnt--) {
|
|
|
|
timers.step_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
TESTASSERT(4 == rlc2.get_buffer_state());
|
|
|
|
|
|
|
|
// Read status PDU from RLC2
|
|
|
|
byte_buffer_t status_buf;
|
|
|
|
status_buf.N_bytes = rlc2.read_pdu(status_buf.msg, 10); // 10 bytes is enough to hold the status
|
|
|
|
|
|
|
|
// Assert status is correct
|
|
|
|
rlc_status_pdu_t status_check = {};
|
|
|
|
rlc_am_read_status_pdu(status_buf.msg, status_buf.N_bytes, &status_check);
|
|
|
|
TESTASSERT(status_check.N_nack == 1); // one packet lost.
|
|
|
|
TESTASSERT(status_check.nacks[0].nack_sn == 2); // SN 2 was lost.
|
|
|
|
TESTASSERT(status_check.ack_sn == 5); // Delivered up to SN 5.
|
|
|
|
|
|
|
|
// Write status PDU to RLC1
|
|
|
|
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
|
|
|
|
|
|
|
|
TESTASSERT(tester.notified_counts.size() == 2);
|
|
|
|
for (int i = 0; i < 5; i++) {
|
|
|
|
auto it = tester.notified_counts.find(i);
|
|
|
|
if (i == 0 || i == 4) {
|
|
|
|
TESTASSERT(it != tester.notified_counts.end() && tester.notified_counts[i] == 1);
|
|
|
|
} else {
|
|
|
|
TESTASSERT(it == tester.notified_counts.end());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read the retx PDU from RLC1 and force resegmentation
|
|
|
|
byte_buffer_t retx1;
|
|
|
|
retx1.N_bytes = rlc1.read_pdu(retx1.msg, 21); // 6 byte header + 15 data
|
|
|
|
|
|
|
|
// Write the retx PDU to RLC2
|
|
|
|
rlc2.write_pdu(retx1.msg, retx1.N_bytes);
|
|
|
|
|
|
|
|
TESTASSERT(21 == rlc1.get_buffer_state());
|
|
|
|
|
|
|
|
// Read the remaining segment
|
|
|
|
byte_buffer_t retx2;
|
|
|
|
retx2.N_bytes = rlc1.read_pdu(retx2.msg, 21);
|
|
|
|
|
|
|
|
// Write the retx PDU to RLC2
|
|
|
|
rlc2.write_pdu(retx2.msg, retx2.N_bytes);
|
|
|
|
|
|
|
|
TESTASSERT(tester.sdus.size() == 5);
|
|
|
|
for (uint32_t i = 0; i < tester.sdus.size(); i++) {
|
|
|
|
if (tester.sdus[i]->N_bytes != 10)
|
|
|
|
return -1;
|
|
|
|
for (int j = 0; j < 10; j++)
|
|
|
|
if (tester.sdus[i]->msg[j] != j)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get status from RLC 2
|
|
|
|
for (int i = 0; i < 5; i++) {
|
|
|
|
timers.step_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read status PDU from RLC2
|
|
|
|
status_buf.N_bytes = rlc2.read_pdu(status_buf.msg, 10); // 10 bytes is enough to hold the status
|
|
|
|
|
|
|
|
// Assert status is correct
|
|
|
|
status_check = {};
|
|
|
|
rlc_am_read_status_pdu(status_buf.msg, status_buf.N_bytes, &status_check);
|
|
|
|
TESTASSERT(status_check.N_nack == 0); // all packets delivered.
|
|
|
|
TESTASSERT(status_check.ack_sn == 5); // Delivered up to SN 5.
|
|
|
|
|
|
|
|
// Write status PDU to RLC1
|
|
|
|
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
|
|
|
|
|
|
|
|
// Check final notifications
|
|
|
|
TESTASSERT(tester.notified_counts.size() == 5);
|
|
|
|
for (int i = 0; i < 5; i++) {
|
|
|
|
auto it = tester.notified_counts.find(i);
|
|
|
|
TESTASSERT(it != tester.notified_counts.end() && tester.notified_counts[i] == 1);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int resegment_test_5()
|
|
|
|
{
|
|
|
|
// SDUs: | 10 | 10 | 10 | 10 | 10 |
|
|
|
|
// PDUs: |2|3| 40 |3|2|
|
|
|
|
// Retx PDU segments: | 20 | 20 |
|
|
|
|
|
|
|
|
rlc_am_tester tester;
|
|
|
|
srsran::timer_handler timers(8);
|
|
|
|
|
|
|
|
rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers);
|
|
|
|
rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers);
|
|
|
|
|
|
|
|
if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (not rlc2.configure(rlc_config_t::default_rlc_am_config())) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Push 5 SDUs into RLC1
|
|
|
|
unique_byte_buffer_t sdu_bufs[NBUFS];
|
|
|
|
for (int i = 0; i < NBUFS; i++) {
|
|
|
|
sdu_bufs[i] = srsran::make_byte_buffer();
|
|
|
|
for (int j = 0; j < 10; j++) {
|
|
|
|
sdu_bufs[i]->msg[j] = i;
|
|
|
|
}
|
|
|
|
sdu_bufs[i]->N_bytes = 10; // Give each buffer a size of 10 bytes
|
|
|
|
sdu_bufs[i]->md.pdcp_sn = i;
|
|
|
|
rlc1.write_sdu(std::move(sdu_bufs[i]));
|
|
|
|
}
|
|
|
|
|
|
|
|
TESTASSERT(58 == rlc1.get_buffer_state());
|
|
|
|
|
|
|
|
// Read 5 PDUs from RLC1 (2 bytes, 3 bytes, 40 bytes, 3 bytes, 2 bytes)
|
|
|
|
byte_buffer_t pdu_bufs[NBUFS];
|
|
|
|
pdu_bufs[0].N_bytes = rlc1.read_pdu(pdu_bufs[0].msg, 4); // 2 byte header + 2 byte payload
|
|
|
|
pdu_bufs[1].N_bytes = rlc1.read_pdu(pdu_bufs[1].msg, 5); // 2 byte header + 3 byte payload
|
|
|
|
pdu_bufs[2].N_bytes = rlc1.read_pdu(pdu_bufs[2].msg, 48); // 8 byte header + 40 byte payload
|
|
|
|
pdu_bufs[3].N_bytes = rlc1.read_pdu(pdu_bufs[3].msg, 5); // 2 byte header + 3 byte payload
|
|
|
|
pdu_bufs[4].N_bytes = rlc1.read_pdu(pdu_bufs[4].msg, 4); // 2 byte header + 2 byte payload
|
|
|
|
|
|
|
|
TESTASSERT(0 == rlc1.get_buffer_state());
|
|
|
|
|
|
|
|
// Write PDUs into RLC2 (skip SN 2)
|
|
|
|
for (int i = 0; i < NBUFS; i++) {
|
|
|
|
if (i != 2)
|
|
|
|
rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step timers until reordering timeout expires
|
|
|
|
int cnt = 5;
|
|
|
|
while (cnt--) {
|
|
|
|
timers.step_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
TESTASSERT(4 == rlc2.get_buffer_state());
|
|
|
|
|
|
|
|
// Read status PDU from RLC2
|
|
|
|
byte_buffer_t status_buf;
|
|
|
|
status_buf.N_bytes = rlc2.read_pdu(status_buf.msg, 10); // 10 bytes is enough to hold the status
|
|
|
|
|
|
|
|
// Assert status is correct
|
|
|
|
rlc_status_pdu_t status_check = {};
|
|
|
|
rlc_am_read_status_pdu(status_buf.msg, status_buf.N_bytes, &status_check);
|
|
|
|
TESTASSERT(status_check.N_nack == 1); // one packet was lost.
|
|
|
|
TESTASSERT(status_check.nacks[0].nack_sn == 2); // SN 2 was lost.
|
|
|
|
TESTASSERT(status_check.ack_sn == 5); // Delivered up to SN 5.
|
|
|
|
|
|
|
|
// Write status PDU to RLC1
|
|
|
|
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
|
|
|
|
|
|
|
|
// Check notifications
|
|
|
|
TESTASSERT(tester.notified_counts.size() == 0);
|
|
|
|
|
|
|
|
// Read the retx PDU from RLC1 and force resegmentation
|
|
|
|
byte_buffer_t retx1;
|
|
|
|
retx1.N_bytes = rlc1.read_pdu(retx1.msg, 27); // 7 byte header + 20 data
|
|
|
|
|
|
|
|
// Write the retx PDU to RLC2
|
|
|
|
rlc2.write_pdu(retx1.msg, retx1.N_bytes);
|
|
|
|
|
|
|
|
TESTASSERT(32 == rlc1.get_buffer_state());
|
|
|
|
|
|
|
|
// Read the remaining segment
|
|
|
|
byte_buffer_t retx2;
|
|
|
|
retx2.N_bytes = rlc1.read_pdu(retx2.msg, 40);
|
|
|
|
|
|
|
|
// Write the retx PDU to RLC2
|
|
|
|
rlc2.write_pdu(retx2.msg, retx2.N_bytes);
|
|
|
|
|
|
|
|
TESTASSERT(tester.sdus.size() == 5);
|
|
|
|
for (uint32_t i = 0; i < tester.sdus.size(); i++) {
|
|
|
|
TESTASSERT(tester.sdus[i]->N_bytes == 10);
|
|
|
|
for (int j = 0; j < 10; j++) {
|
|
|
|
TESTASSERT(tester.sdus[i]->msg[j] == i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get status from RLC 2
|
|
|
|
for (int i = 0; i < 5; i++) {
|
|
|
|
timers.step_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read status PDU from RLC2
|
|
|
|
status_buf.N_bytes = rlc2.read_pdu(status_buf.msg, 10); // 10 bytes is enough to hold the status
|
|
|
|
|
|
|
|
// Assert status is correct
|
|
|
|
status_check = {};
|
|
|
|
rlc_am_read_status_pdu(status_buf.msg, status_buf.N_bytes, &status_check);
|
|
|
|
TESTASSERT(status_check.N_nack == 0); // all packets delivered.
|
|
|
|
TESTASSERT(status_check.ack_sn == 5); // Delivered up to SN 5.
|
|
|
|
|
|
|
|
// Write status PDU to RLC1
|
|
|
|
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
|
|
|
|
|
|
|
|
// Check final notifications
|
|
|
|
TESTASSERT(tester.notified_counts.size() == 5);
|
|
|
|
for (int i = 0; i < 5; i++) {
|
|
|
|
auto it = tester.notified_counts.find(i);
|
|
|
|
TESTASSERT(it != tester.notified_counts.end() && tester.notified_counts[i] == 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int resegment_test_6()
|
|
|
|
{
|
|
|
|
// SDUs: |10|10|10| 54 | 54 | 54 | 54 | 54 | 54 |
|
|
|
|
// PDUs: |10|10|10| 270 | 54 |
|
|
|
|
// Retx PDU segments: | 120 | 150 |
|
|
|
|
|
|
|
|
rlc_am_tester tester;
|
|
|
|
srsran::timer_handler timers(8);
|
|
|
|
int len = 0;
|
|
|
|
|
|
|
|
rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers);
|
|
|
|
rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers);
|
|
|
|
|
|
|
|
if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (not rlc2.configure(rlc_config_t::default_rlc_am_config())) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Push SDUs into RLC1
|
|
|
|
unique_byte_buffer_t sdu_bufs[9];
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
|
|
sdu_bufs[i] = srsran::make_byte_buffer();
|
|
|
|
for (int j = 0; j < 10; j++)
|
|
|
|
sdu_bufs[i]->msg[j] = j;
|
|
|
|
sdu_bufs[i]->N_bytes = 10; // Give each buffer a size of 10 bytes
|
|
|
|
sdu_bufs[i]->md.pdcp_sn = i;
|
|
|
|
rlc1.write_sdu(std::move(sdu_bufs[i]));
|
|
|
|
}
|
|
|
|
for (int i = 3; i < 9; i++) {
|
|
|
|
sdu_bufs[i] = srsran::make_byte_buffer();
|
|
|
|
for (int j = 0; j < 54; j++)
|
|
|
|
sdu_bufs[i]->msg[j] = j;
|
|
|
|
sdu_bufs[i]->N_bytes = 54;
|
|
|
|
sdu_bufs[i]->md.pdcp_sn = i;
|
|
|
|
rlc1.write_sdu(std::move(sdu_bufs[i]));
|
|
|
|
}
|
|
|
|
|
|
|
|
TESTASSERT(368 == rlc1.get_buffer_state());
|
|
|
|
|
|
|
|
// Read PDUs from RLC1 (10, 10, 10, 270, 54)
|
|
|
|
byte_buffer_t pdu_bufs[5];
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
|
|
len = rlc1.read_pdu(pdu_bufs[i].msg, 12);
|
|
|
|
pdu_bufs[i].N_bytes = len;
|
|
|
|
}
|
|
|
|
len = rlc1.read_pdu(pdu_bufs[3].msg, 278);
|
|
|
|
pdu_bufs[3].N_bytes = len;
|
|
|
|
len = rlc1.read_pdu(pdu_bufs[4].msg, 56);
|
|
|
|
pdu_bufs[4].N_bytes = len;
|
|
|
|
|
|
|
|
TESTASSERT(0 == rlc1.get_buffer_state());
|
|
|
|
|
|
|
|
// Write PDUs into RLC2 (skip SN 3)
|
|
|
|
for (int i = 0; i < 5; i++) {
|
|
|
|
if (i != 3)
|
|
|
|
rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step timers until reordering timeout expires
|
|
|
|
int cnt = 5;
|
|
|
|
while (cnt--) {
|
|
|
|
timers.step_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
TESTASSERT(4 == rlc2.get_buffer_state());
|
|
|
|
|
|
|
|
// Read status PDU from RLC2
|
|
|
|
byte_buffer_t status_buf;
|
|
|
|
len = rlc2.read_pdu(status_buf.msg, 10); // 10 bytes is enough to hold the status
|
|
|
|
status_buf.N_bytes = len;
|
|
|
|
|
|
|
|
// Assert status is correct
|
|
|
|
rlc_status_pdu_t status_check = {};
|
|
|
|
rlc_am_read_status_pdu(status_buf.msg, status_buf.N_bytes, &status_check);
|
|
|
|
TESTASSERT(status_check.N_nack == 1); // One packet was lost.
|
|
|
|
TESTASSERT(status_check.nacks[0].nack_sn == 3); // SN 3 was lost.
|
|
|
|
TESTASSERT(status_check.ack_sn == 5);
|
|
|
|
|
|
|
|
// Write status PDU to RLC1
|
|
|
|
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
|
|
|
|
|
|
|
|
TESTASSERT(278 == rlc1.get_buffer_state());
|
|
|
|
|
|
|
|
// Check notifications
|
|
|
|
TESTASSERT(tester.notified_counts.size() == 4);
|
|
|
|
for (int i = 0; i < 5; i++) {
|
|
|
|
auto it = tester.notified_counts.find(i);
|
|
|
|
if (i == 0 || i == 1 || i == 2 || i == 8) {
|
|
|
|
TESTASSERT(it != tester.notified_counts.end() && tester.notified_counts[i] == 1);
|
|
|
|
} else {
|
|
|
|
TESTASSERT(it == tester.notified_counts.end());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read the retx PDU from RLC1 and force resegmentation
|
|
|
|
byte_buffer_t retx1;
|
|
|
|
len = rlc1.read_pdu(retx1.msg, 129);
|
|
|
|
retx1.N_bytes = len;
|
|
|
|
|
|
|
|
// Write the retx PDU to RLC2
|
|
|
|
rlc2.write_pdu(retx1.msg, retx1.N_bytes);
|
|
|
|
|
|
|
|
TESTASSERT(169 == rlc1.get_buffer_state());
|
|
|
|
|
|
|
|
// Read the remaining segment
|
|
|
|
byte_buffer_t retx2;
|
|
|
|
len = rlc1.read_pdu(retx2.msg, 169);
|
|
|
|
retx2.N_bytes = len;
|
|
|
|
|
|
|
|
// Write the retx PDU to RLC2
|
|
|
|
rlc2.write_pdu(retx2.msg, retx2.N_bytes);
|
|
|
|
|
|
|
|
TESTASSERT(tester.sdus.size() == 9);
|
|
|
|
for (int i = 0; i < 3; i++) {
|
|
|
|
TESTASSERT(tester.sdus[i]->N_bytes == 10);
|
|
|
|
for (int j = 0; j < 10; j++)
|
|
|
|
TESTASSERT(tester.sdus[i]->msg[j] == j);
|
|
|
|
}
|
|
|
|
for (uint32_t i = 3; i < 9; i++) {
|
|
|
|
if (i >= tester.sdus.size()) {
|
|
|
|
return SRSRAN_ERROR;
|
|
|
|
}
|
|
|
|
TESTASSERT(tester.sdus[i]->N_bytes == 54);
|
|
|
|
for (int j = 0; j < 54; j++) {
|
|
|
|
TESTASSERT(tester.sdus[i]->msg[j] == j);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get status from RLC 2
|
|
|
|
for (int i = 0; i < 5; i++) {
|
|
|
|
timers.step_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read status PDU from RLC2
|
|
|
|
status_buf.N_bytes = rlc2.read_pdu(status_buf.msg, 10); // 10 bytes is enough to hold the status
|
|
|
|
|
|
|
|
// Assert status is correct
|
|
|
|
status_check = {};
|
|
|
|
rlc_am_read_status_pdu(status_buf.msg, status_buf.N_bytes, &status_check);
|
|
|
|
TESTASSERT(status_check.N_nack == 0); // all packets delivered.
|
|
|
|
TESTASSERT(status_check.ack_sn == 5); // Delivered up to SN 5.
|
|
|
|
|
|
|
|
// Write status PDU to RLC1
|
|
|
|
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
|
|
|
|
|
|
|
|
// Check final notifications
|
|
|
|
TESTASSERT(tester.notified_counts.size() == 9);
|
|
|
|
for (int i = 0; i < 9; i++) {
|
|
|
|
auto it = tester.notified_counts.find(i);
|
|
|
|
TESTASSERT(it != tester.notified_counts.end() && tester.notified_counts[i] == 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Retransmission of PDU segments of the same size
|
|
|
|
int resegment_test_7()
|
|
|
|
{
|
|
|
|
// SDUs: | 30 | 30 |
|
|
|
|
// PDUs: | 13 | 13 | 11 | 13 | 10 |
|
|
|
|
// Rxed PDUs | 13 | 13 | | 13 | 10 |
|
|
|
|
// Retx PDU segments: | 4 | 7 |
|
|
|
|
// Retx PDU segments: |3|3]3|2|
|
|
|
|
const uint32_t N_SDU_BUFS = 2;
|
|
|
|
const uint32_t N_PDU_BUFS = 5;
|
|
|
|
const uint32_t sdu_size = 30;
|
|
|
|
|
|
|
|
#if HAVE_PCAP
|
|
|
|
rlc_pcap pcap;
|
|
|
|
pcap.open("rlc_am_test7.pcap", rlc_config_t::default_rlc_am_config());
|
|
|
|
rlc_am_tester tester(&pcap);
|
|
|
|
#else
|
|
|
|
rlc_am_tester tester(NULL);
|
|
|
|
#endif
|
|
|
|
srsran::timer_handler timers(8);
|
|
|
|
|
|
|
|
rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers);
|
|
|
|
rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers);
|
|
|
|
|
|
|
|
if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (not rlc2.configure(rlc_config_t::default_rlc_am_config())) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Push 2 SDUs into RLC1
|
|
|
|
unique_byte_buffer_t sdu_bufs[N_SDU_BUFS];
|
|
|
|
for (uint32_t i = 0; i < N_SDU_BUFS; i++) {
|
|
|
|
sdu_bufs[i] = srsran::make_byte_buffer();
|
|
|
|
for (uint32_t j = 0; j < sdu_size; j++) {
|
|
|
|
sdu_bufs[i]->msg[j] = i;
|
|
|
|
}
|
|
|
|
sdu_bufs[i]->N_bytes = sdu_size; // Give each buffer a size of 15 bytes
|
|
|
|
sdu_bufs[i]->md.pdcp_sn = i;
|
|
|
|
rlc1.write_sdu(std::move(sdu_bufs[i]));
|
|
|
|
}
|
|
|
|
|
|
|
|
TESTASSERT(64 == rlc1.get_buffer_state());
|
|
|
|
|
|
|
|
// Read PDUs from RLC1 (15 bytes each)
|
|
|
|
byte_buffer_t pdu_bufs[N_PDU_BUFS];
|
|
|
|
for (uint32_t i = 0; i < N_PDU_BUFS; i++) {
|
|
|
|
pdu_bufs[i].N_bytes = rlc1.read_pdu(pdu_bufs[i].msg, 15); // 2 bytes for header + 12 B payload
|
|
|
|
TESTASSERT(pdu_bufs[i].N_bytes);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step timers until poll_retx timeout expires
|
|
|
|
int cnt = 5;
|
|
|
|
while (cnt--) {
|
|
|
|
timers.step_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
// RLC should try to retx a random PDU because it needs to request a status from the receiver
|
|
|
|
TESTASSERT(0 != rlc1.get_buffer_state());
|
|
|
|
|
|
|
|
// Skip PDU with SN 2
|
|
|
|
for (uint32_t i = 0; i < N_PDU_BUFS; i++) {
|
|
|
|
if (i != 2) {
|
|
|
|
rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes);
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.write_dl_ccch(pdu_bufs[i].msg, pdu_bufs[i].N_bytes);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step timers until reordering timeout expires
|
|
|
|
cnt = 5;
|
|
|
|
while (cnt--) {
|
|
|
|
timers.step_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
// RLC should try to retransmit a random PDU because it needs to re-request a status PDU from the receiver
|
|
|
|
TESTASSERT(0 != rlc1.get_buffer_state());
|
|
|
|
|
|
|
|
// first round of retx, forcing resegmentation
|
|
|
|
byte_buffer_t retx[4];
|
|
|
|
for (uint32_t i = 0; i < 4; i++) {
|
|
|
|
TESTASSERT(0 != rlc1.get_buffer_state());
|
|
|
|
retx[i].N_bytes = rlc1.read_pdu(retx[i].msg, 7);
|
|
|
|
TESTASSERT(retx[i].N_bytes);
|
|
|
|
|
|
|
|
// Write the last two segments to RLC2
|
|
|
|
if (i > 1) {
|
|
|
|
rlc2.write_pdu(retx[i].msg, retx[i].N_bytes);
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.write_dl_ccch(retx[i].msg, retx[i].N_bytes);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read status PDU from RLC2
|
|
|
|
TESTASSERT(rlc2.get_buffer_state());
|
|
|
|
byte_buffer_t status_buf;
|
|
|
|
status_buf.N_bytes = rlc2.read_pdu(status_buf.msg, 10); // 10 bytes is enough to hold the status
|
|
|
|
|
|
|
|
// Assert status is correct
|
|
|
|
rlc_status_pdu_t status_check = {};
|
|
|
|
rlc_am_read_status_pdu(status_buf.msg, status_buf.N_bytes, &status_check);
|
|
|
|
TESTASSERT(status_check.N_nack == 1); // one packet dropped.
|
|
|
|
TESTASSERT(status_check.nacks[0].nack_sn == 2); // SN 2 dropped.
|
|
|
|
TESTASSERT(status_check.ack_sn == 5); // Delivered up to SN 5.
|
|
|
|
|
|
|
|
// Write status PDU to RLC1
|
|
|
|
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.write_ul_ccch(status_buf.msg, status_buf.N_bytes);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
TESTASSERT(15 == rlc1.get_buffer_state());
|
|
|
|
|
|
|
|
// Check notifications
|
|
|
|
TESTASSERT(tester.notified_counts.size() == 0);
|
|
|
|
|
|
|
|
// second round of retx, forcing resegmentation
|
|
|
|
byte_buffer_t retx2[4];
|
|
|
|
for (uint32_t i = 0; i < 4; i++) {
|
|
|
|
TESTASSERT(rlc1.get_buffer_state() != 0);
|
|
|
|
retx2[i].N_bytes = rlc1.read_pdu(retx2[i].msg, 9);
|
|
|
|
TESTASSERT(retx2[i].N_bytes != 0);
|
|
|
|
|
|
|
|
rlc2.write_pdu(retx2[i].msg, retx2[i].N_bytes);
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.write_dl_ccch(retx[i].msg, retx[i].N_bytes);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
// check buffer states
|
|
|
|
TESTASSERT(0 == rlc1.get_buffer_state());
|
|
|
|
|
|
|
|
// Step timers until poll_retx timeout expires
|
|
|
|
cnt = 5;
|
|
|
|
while (cnt--) {
|
|
|
|
timers.step_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read status PDU from RLC2
|
|
|
|
TESTASSERT(rlc2.get_buffer_state());
|
|
|
|
status_buf.N_bytes = rlc2.read_pdu(status_buf.msg, 10); // 10 bytes is enough to hold the status
|
|
|
|
|
|
|
|
// Assert status is correct
|
|
|
|
status_check = {};
|
|
|
|
rlc_am_read_status_pdu(status_buf.msg, status_buf.N_bytes, &status_check);
|
|
|
|
TESTASSERT(status_check.N_nack == 0); // all packets delivered.
|
|
|
|
TESTASSERT(status_check.ack_sn == 5); // Delivered up to SN 5.
|
|
|
|
|
|
|
|
// Write status PDU to RLC1
|
|
|
|
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.write_ul_ccch(status_buf.msg, status_buf.N_bytes);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// check status again
|
|
|
|
TESTASSERT(0 == rlc1.get_buffer_state());
|
|
|
|
TESTASSERT(0 == rlc2.get_buffer_state());
|
|
|
|
|
|
|
|
// Check number of SDUs and their content
|
|
|
|
TESTASSERT(tester.sdus.size() == N_SDU_BUFS);
|
|
|
|
for (uint32_t i = 0; i < tester.sdus.size(); i++) {
|
|
|
|
TESTASSERT(tester.sdus[i]->N_bytes == sdu_size);
|
|
|
|
for (uint32_t j = 0; j < N_SDU_BUFS; j++) {
|
|
|
|
TESTASSERT(tester.sdus[i]->msg[j] == i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check final notifications
|
|
|
|
TESTASSERT(tester.notified_counts.size() == 2);
|
|
|
|
for (int i = 0; i < 2; i++) {
|
|
|
|
auto it = tester.notified_counts.find(i);
|
|
|
|
TESTASSERT(it != tester.notified_counts.end() && tester.notified_counts[i] == 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.close();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Retransmission of PDU segments with different size
|
|
|
|
int resegment_test_8()
|
|
|
|
{
|
|
|
|
// SDUs: | 30 | 30 |
|
|
|
|
// PDUs: | 15 | 15 | 15 | 15 | 15 |
|
|
|
|
// Rxed PDUs | 15 | | 15 | 15 |
|
|
|
|
// Retx PDU segments: | 7 | 7 | 7 | 7 |
|
|
|
|
// Retx PDU segments: | 6 | 6 ] 6 | 6 | 6 | 6 | 6 | 6 |
|
|
|
|
const uint32_t N_SDU_BUFS = 2;
|
|
|
|
const uint32_t N_PDU_BUFS = 5;
|
|
|
|
const uint32_t sdu_size = 30;
|
|
|
|
|
|
|
|
#if HAVE_PCAP
|
|
|
|
rlc_pcap pcap;
|
|
|
|
pcap.open("rlc_am_test8.pcap", rlc_config_t::default_rlc_am_config());
|
|
|
|
rlc_am_tester tester(&pcap);
|
|
|
|
#else
|
|
|
|
rlc_am_tester tester(NULL);
|
|
|
|
#endif
|
|
|
|
srsran::timer_handler timers(8);
|
|
|
|
|
|
|
|
rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers);
|
|
|
|
rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers);
|
|
|
|
|
|
|
|
if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (not rlc2.configure(rlc_config_t::default_rlc_am_config())) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Push 2 SDUs into RLC1
|
|
|
|
unique_byte_buffer_t sdu_bufs[N_SDU_BUFS];
|
|
|
|
for (uint32_t i = 0; i < N_SDU_BUFS; i++) {
|
|
|
|
sdu_bufs[i] = srsran::make_byte_buffer();
|
|
|
|
for (uint32_t j = 0; j < sdu_size; j++) {
|
|
|
|
sdu_bufs[i]->msg[j] = i;
|
|
|
|
}
|
|
|
|
sdu_bufs[i]->N_bytes = sdu_size; // Give each buffer a size of 30 bytes
|
|
|
|
sdu_bufs[i]->md.pdcp_sn = i;
|
|
|
|
rlc1.write_sdu(std::move(sdu_bufs[i]));
|
|
|
|
}
|
|
|
|
|
|
|
|
TESTASSERT(64 == rlc1.get_buffer_state());
|
|
|
|
|
|
|
|
// Read PDUs from RLC1 (15 bytes each)
|
|
|
|
byte_buffer_t pdu_bufs[N_PDU_BUFS];
|
|
|
|
for (uint32_t i = 0; i < N_PDU_BUFS; i++) {
|
|
|
|
pdu_bufs[i].N_bytes = rlc1.read_pdu(pdu_bufs[i].msg, 15); // 12 bytes for header + payload
|
|
|
|
TESTASSERT(pdu_bufs[i].N_bytes);
|
|
|
|
}
|
|
|
|
|
|
|
|
TESTASSERT(0 == rlc1.get_buffer_state());
|
|
|
|
|
|
|
|
// Skip PDU one and two
|
|
|
|
for (uint32_t i = 0; i < N_PDU_BUFS; i++) {
|
|
|
|
if (i < 1 || i > 2) {
|
|
|
|
rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes);
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.write_dl_ccch(pdu_bufs[i].msg, pdu_bufs[i].N_bytes);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step timers until reordering timeout expires
|
|
|
|
int cnt = 5;
|
|
|
|
while (cnt--) {
|
|
|
|
timers.step_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
// what PDU to retransmit is random but it must not be zero
|
|
|
|
TESTASSERT(0 != rlc1.get_buffer_state());
|
|
|
|
|
|
|
|
// first round of retx, forcing resegmentation
|
|
|
|
byte_buffer_t retx[4];
|
|
|
|
for (uint32_t i = 0; i < 3; i++) {
|
|
|
|
TESTASSERT(rlc1.get_buffer_state());
|
|
|
|
retx[i].N_bytes = rlc1.read_pdu(retx[i].msg, 8);
|
|
|
|
TESTASSERT(retx[i].N_bytes);
|
|
|
|
|
|
|
|
// Write the last two segments to RLC2
|
|
|
|
if (i > 1) {
|
|
|
|
rlc2.write_pdu(retx[i].msg, retx[i].N_bytes);
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.write_dl_ccch(retx[i].msg, retx[i].N_bytes);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step timers until reordering timeout expires
|
|
|
|
cnt = 7;
|
|
|
|
while (cnt--) {
|
|
|
|
timers.step_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read status PDU from RLC2
|
|
|
|
TESTASSERT(rlc2.get_buffer_state());
|
|
|
|
byte_buffer_t status_buf;
|
|
|
|
status_buf.N_bytes = rlc2.read_pdu(status_buf.msg, 10); // 10 bytes is enough to hold the status
|
|
|
|
|
|
|
|
// Write status PDU to RLC1
|
|
|
|
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.write_ul_ccch(status_buf.msg, status_buf.N_bytes);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
TESTASSERT(15 == rlc1.get_buffer_state());
|
|
|
|
|
|
|
|
// second round of retx, reduce grant size to force different segment sizes
|
|
|
|
byte_buffer_t retx2[20];
|
|
|
|
for (uint32_t i = 0; i < 7; i++) {
|
|
|
|
TESTASSERT(rlc1.get_buffer_state() != 0);
|
|
|
|
retx2[i].N_bytes = rlc1.read_pdu(retx2[i].msg, 9);
|
|
|
|
TESTASSERT(retx2[i].N_bytes != 0);
|
|
|
|
rlc2.write_pdu(retx2[i].msg, retx2[i].N_bytes);
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.write_dl_ccch(retx[i].msg, retx[i].N_bytes);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
// get BSR from RLC2
|
|
|
|
status_buf.N_bytes = rlc2.read_pdu(status_buf.msg, 10); // 10 bytes is enough to hold the status
|
|
|
|
|
|
|
|
// Write status PDU to RLC1
|
|
|
|
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.write_ul_ccch(status_buf.msg, status_buf.N_bytes);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// check buffer states
|
|
|
|
if (rlc1.get_buffer_state() != 0) {
|
|
|
|
return -1;
|
|
|
|
};
|
|
|
|
if (rlc2.get_buffer_state() != 0) {
|
|
|
|
return -1;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Check number of SDUs and their content
|
|
|
|
TESTASSERT(tester.sdus.size() == N_SDU_BUFS);
|
|
|
|
for (uint32_t i = 0; i < tester.sdus.size(); i++) {
|
|
|
|
if (tester.sdus[i]->N_bytes != sdu_size)
|
|
|
|
return -1;
|
|
|
|
for (uint32_t j = 0; j < N_SDU_BUFS; j++) {
|
|
|
|
if (tester.sdus[i]->msg[j] != i)
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.close();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Resegmentation with 1 B segments
|
|
|
|
int resegment_test_9()
|
|
|
|
{
|
|
|
|
// SDUs: | 10 | 10 | 10 |
|
|
|
|
// PDUs: | 9 | x |
|
|
|
|
// Retx PDU segments: |2| 9 |
|
|
|
|
|
|
|
|
const rlc_config_t config = rlc_config_t::default_rlc_am_config();
|
|
|
|
#if HAVE_PCAP
|
|
|
|
rlc_pcap pcap;
|
|
|
|
pcap.open("rlc_resegment_test_9.pcap", config);
|
|
|
|
rlc_am_tester tester(&pcap);
|
|
|
|
#else
|
|
|
|
rlc_am_tester tester(NULL);
|
|
|
|
#endif
|
|
|
|
srsran::timer_handler timers(8);
|
|
|
|
|
|
|
|
rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers);
|
|
|
|
rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers);
|
|
|
|
|
|
|
|
if (not rlc1.configure(config)) {
|
|
|
|
return SRSRAN_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (not rlc2.configure(config)) {
|
|
|
|
return SRSRAN_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Push 3 SDUs into RLC1
|
|
|
|
const uint32_t n_bufs = 3;
|
|
|
|
unique_byte_buffer_t sdu_bufs[n_bufs];
|
|
|
|
for (uint32_t i = 0; i < n_bufs; i++) {
|
|
|
|
sdu_bufs[i] = srsran::make_byte_buffer();
|
|
|
|
for (uint32_t j = 0; j < 10; j++) {
|
|
|
|
sdu_bufs[i]->msg[j] = i;
|
|
|
|
}
|
|
|
|
sdu_bufs[i]->N_bytes = 10; // Give each buffer a size of 10 bytes
|
|
|
|
sdu_bufs[i]->md.pdcp_sn = i;
|
|
|
|
rlc1.write_sdu(std::move(sdu_bufs[i]));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read 5 PDUs from RLC1 (2 bytes, 3 bytes, 40 bytes, 3 bytes, 2 bytes)
|
|
|
|
byte_buffer_t pdu_bufs[n_bufs];
|
|
|
|
pdu_bufs[0].N_bytes = rlc1.read_pdu(pdu_bufs[0].msg, 11); // 2 byte header + 9 byte payload
|
|
|
|
pdu_bufs[1].N_bytes = rlc1.read_pdu(pdu_bufs[1].msg, 15); // 4 byte header + 11 byte payload
|
|
|
|
pdu_bufs[2].N_bytes = rlc1.read_pdu(pdu_bufs[2].msg, 12); // 2 byte header + 10 byte payload
|
|
|
|
|
|
|
|
TESTASSERT(0 == rlc1.get_buffer_state());
|
|
|
|
|
|
|
|
// Write PDUs into RLC2 (skip SN 0)
|
|
|
|
for (uint32_t i = 0; i < n_bufs; i++) {
|
|
|
|
if (i != 1) {
|
|
|
|
rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes);
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.write_dl_ccch(pdu_bufs[i].msg, pdu_bufs[i].N_bytes);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step timers until reordering timeout expires
|
|
|
|
int cnt = 5;
|
|
|
|
while (cnt--) {
|
|
|
|
timers.step_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read status PDU from RLC2
|
|
|
|
byte_buffer_t status_buf;
|
|
|
|
status_buf.N_bytes = rlc2.read_pdu(status_buf.msg, 10); // 10 bytes is enough to hold the status
|
|
|
|
|
|
|
|
// Write status PDU to RLC1
|
|
|
|
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.write_ul_ccch(status_buf.msg, status_buf.N_bytes);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Read the retx PDU from RLC1 and force resegmentation
|
|
|
|
byte_buffer_t retx1;
|
|
|
|
byte_buffer_t retx2;
|
|
|
|
retx1.N_bytes = rlc1.read_pdu(retx1.msg, 8); // 6 byte header + 2 data
|
|
|
|
|
|
|
|
// Write the retx PDU to RLC2
|
|
|
|
rlc2.write_pdu(retx1.msg, retx1.N_bytes);
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.write_dl_ccch(retx1.msg, retx1.N_bytes);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Read 2nd with a big enough grant to fit remaining content
|
|
|
|
retx2.N_bytes = rlc1.read_pdu(retx2.msg, 40);
|
|
|
|
|
|
|
|
// Write the retx PDU to RLC2
|
|
|
|
rlc2.write_pdu(retx2.msg, retx2.N_bytes);
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.write_dl_ccch(retx2.msg, retx2.N_bytes);
|
|
|
|
#endif
|
|
|
|
// goto exit;
|
|
|
|
|
|
|
|
TESTASSERT(tester.sdus.size() == n_bufs);
|
|
|
|
for (uint32_t i = 0; i < tester.sdus.size(); i++) {
|
|
|
|
TESTASSERT(tester.sdus[i]->N_bytes == 10);
|
|
|
|
for (int j = 0; j < 10; j++) {
|
|
|
|
TESTASSERT(tester.sdus[i]->msg[j] == i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get status from RLC 2
|
|
|
|
for (int i = 0; i < 5; i++) {
|
|
|
|
timers.step_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read status PDU from RLC2
|
|
|
|
status_buf.N_bytes = rlc2.read_pdu(status_buf.msg, 10); // 10 bytes is enough to hold the status
|
|
|
|
|
|
|
|
// Write status PDU to RLC1
|
|
|
|
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
|
|
|
|
|
|
|
|
// Check final notifications
|
|
|
|
TESTASSERT(tester.notified_counts.size() == n_bufs);
|
|
|
|
for (uint32_t i = 0; i < n_bufs; i++) {
|
|
|
|
auto it = tester.notified_counts.find(i);
|
|
|
|
TESTASSERT(it != tester.notified_counts.end() && tester.notified_counts[i] == 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// exit:
|
|
|
|
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.close();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Retransmission of segment Resegmentation with 1 B segments
|
|
|
|
int resegment_test_10()
|
|
|
|
{
|
|
|
|
/// 21:35:17.369012 [RLC_1] [I] DRB1 Tx PDU SN=520 (20 B)
|
|
|
|
/// 0000: 9e 08 80 40 0a 34 34 34 34 35 35 35 35 35 35 35
|
|
|
|
/// 0010: 35 35 35 36
|
|
|
|
/// 21:35:17.369016 [RLC_1] [D] [Data PDU, RF=0, P=0, FI=1, SN=520, LSF=0, SO=0, N_li=2 (4, 10, )]
|
|
|
|
|
|
|
|
/// 21:35:17.369703 [RLC_1] [I] DRB1 Retx PDU segment SN=520 [so=0] (10 B) (attempt 2/16)
|
|
|
|
/// 0000: fe 08 00 00 00 40 34 34 34 34
|
|
|
|
/// 21:35:17.369712 [RLC_2] [I] DRB1 Rx data PDU segment of SN=520 (4 B), SO=0, N_li=1
|
|
|
|
/// 0000: 34 34 34 34
|
|
|
|
/// 21:35:17.369718 [RLC_2] [D] [Data PDU, RF=1, P=1, FI=1, SN=520, LSF=0, SO=0, N_li=1 (4, )]
|
|
|
|
|
|
|
|
// SDUs: | 10 | 10 | 10 | 10 |
|
|
|
|
// PDUs: | 6 | 25(x) | 9 |
|
|
|
|
// Retx PDU segments: |4| 50 |
|
|
|
|
|
|
|
|
const rlc_config_t config = rlc_config_t::default_rlc_am_config();
|
|
|
|
#if HAVE_PCAP
|
|
|
|
rlc_pcap pcap;
|
|
|
|
pcap.open("rlc_resegment_test_10.pcap", config);
|
|
|
|
rlc_am_tester tester(&pcap);
|
|
|
|
#else
|
|
|
|
rlc_am_tester tester(NULL);
|
|
|
|
#endif
|
|
|
|
srsran::timer_handler timers(8);
|
|
|
|
|
|
|
|
rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers);
|
|
|
|
rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers);
|
|
|
|
|
|
|
|
if (not rlc1.configure(config)) {
|
|
|
|
return SRSRAN_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (not rlc2.configure(config)) {
|
|
|
|
return SRSRAN_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Push 3 SDUs into RLC1
|
|
|
|
const uint32_t n_sdus = 4;
|
|
|
|
unique_byte_buffer_t sdu_bufs[n_sdus];
|
|
|
|
for (uint32_t i = 0; i < n_sdus; i++) {
|
|
|
|
sdu_bufs[i] = srsran::make_byte_buffer();
|
|
|
|
for (uint32_t j = 0; j < 10; j++) {
|
|
|
|
sdu_bufs[i]->msg[j] = i;
|
|
|
|
}
|
|
|
|
sdu_bufs[i]->N_bytes = 10; // Give each buffer a size of 10 bytes
|
|
|
|
sdu_bufs[i]->md.pdcp_sn = i;
|
|
|
|
rlc1.write_sdu(std::move(sdu_bufs[i]));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read 5 PDUs from RLC1 (2 bytes, 3 bytes, 40 bytes, 3 bytes, 2 bytes)
|
|
|
|
const uint32_t n_pdus = 3;
|
|
|
|
byte_buffer_t pdu_bufs[n_pdus];
|
|
|
|
pdu_bufs[0].N_bytes = rlc1.read_pdu(pdu_bufs[0].msg, 8); // 2 byte header + 6 byte payload
|
|
|
|
pdu_bufs[1].N_bytes = rlc1.read_pdu(pdu_bufs[1].msg, 32); // 4 byte header + 25 byte payload
|
|
|
|
pdu_bufs[2].N_bytes = rlc1.read_pdu(pdu_bufs[2].msg, 11); // 2 byte header + 9 byte payload
|
|
|
|
|
|
|
|
TESTASSERT(0 == rlc1.get_buffer_state());
|
|
|
|
|
|
|
|
// Write PDUs into RLC2 (skip SN 0)
|
|
|
|
for (uint32_t i = 0; i < n_pdus; i++) {
|
|
|
|
if (i != 1) {
|
|
|
|
rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes);
|
|
|
|
}
|
|
|
|
#if HAVE_PCAP
|
|
|
|
// write to PCAP even if its lost in the TC
|
|
|
|
pcap.write_dl_ccch(pdu_bufs[i].msg, pdu_bufs[i].N_bytes);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step timers until reordering timeout expires
|
|
|
|
int cnt = 5;
|
|
|
|
while (cnt--) {
|
|
|
|
timers.step_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read status PDU from RLC2
|
|
|
|
byte_buffer_t status_buf;
|
|
|
|
status_buf.N_bytes = rlc2.read_pdu(status_buf.msg, 10); // 10 bytes is enough to hold the status
|
|
|
|
|
|
|
|
// Write status PDU to RLC1
|
|
|
|
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.write_ul_ccch(status_buf.msg, status_buf.N_bytes);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Read the retx PDU from RLC1 and force resegmentation
|
|
|
|
byte_buffer_t retx1;
|
|
|
|
byte_buffer_t retx2;
|
|
|
|
retx1.N_bytes = rlc1.read_pdu(retx1.msg, 13); // 6 byte header + 4 data ( +2 B MAC)
|
|
|
|
|
|
|
|
// Write the retx PDU to RLC2
|
|
|
|
rlc2.write_pdu(retx1.msg, retx1.N_bytes);
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.write_dl_ccch(retx1.msg, retx1.N_bytes);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Read 2nd with a big enough grant to fit remaining content
|
|
|
|
retx2.N_bytes = rlc1.read_pdu(retx2.msg, 32);
|
|
|
|
rlc2.write_pdu(retx2.msg, retx2.N_bytes);
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.write_dl_ccch(retx2.msg, retx2.N_bytes);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
TESTASSERT(tester.sdus.size() == n_sdus);
|
|
|
|
for (uint32_t i = 0; i < tester.sdus.size(); i++) {
|
|
|
|
TESTASSERT(tester.sdus[i]->N_bytes == 10);
|
|
|
|
for (int j = 0; j < 10; j++) {
|
|
|
|
TESTASSERT(tester.sdus[i]->msg[j] == i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get status from RLC 2
|
|
|
|
for (int i = 0; i < 5; i++) {
|
|
|
|
timers.step_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read status PDU from RLC2
|
|
|
|
status_buf.N_bytes = rlc2.read_pdu(status_buf.msg, 10); // 10 bytes is enough to hold the status
|
|
|
|
|
|
|
|
// Write status PDU to RLC1
|
|
|
|
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
|
|
|
|
|
|
|
|
// Check final notifications
|
|
|
|
TESTASSERT(tester.notified_counts.size() == n_sdus);
|
|
|
|
for (uint32_t i = 0; i < n_sdus; i++) {
|
|
|
|
auto it = tester.notified_counts.find(i);
|
|
|
|
TESTASSERT(it != tester.notified_counts.end() && tester.notified_counts[i] == 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return SRSRAN_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Custom resegmentation test of a orignal PDU with N_li=2
|
|
|
|
// Because the provided MAC grant is relativly small, the retx segment
|
|
|
|
// can only accomodate 2 B of the original PDU.
|
|
|
|
// The test verifies the correct PDU packing, specifically the LI value
|
|
|
|
int resegment_test_11()
|
|
|
|
{
|
|
|
|
/// Original PDU:
|
|
|
|
/// 11:29:16.065008 [RLC_1] [I] DRB1 Tx PDU SN=419 (21 B)
|
|
|
|
/// 0000: bd a3 80 50 0a aa aa aa aa aa ab ab ab ab ab ab
|
|
|
|
/// 0010: ab ab ab ab ac
|
|
|
|
/// 11:29:16.065013 [RLC_1] [D] [Data PDU, RF=0, P=1, FI=1, SN=419, LSF=0, SO=0, N_li=2 (5, 10, )]
|
|
|
|
|
|
|
|
/// Log messages with the restoration bug:
|
|
|
|
/// 11:29:16.065688 [RLC_1] [D] MAC opportunity - 10 bytes
|
|
|
|
/// 11:29:16.065695 [RLC_1] [D] DRB1 build_retx_pdu - resegmentation required
|
|
|
|
/// 11:29:16.065702 [RLC_1] [D] retx.so_start=2, retx.so_end=6
|
|
|
|
/// 11:29:16.065703 [RLC_1] [D] new_header head_len=4
|
|
|
|
/// 11:29:16.065706 [RLC_1] [D] old_header.li[0], head_len=6, pdu_space=4
|
|
|
|
/// 11:29:16.065710 [RLC_1] [D] new_header head_len=6
|
|
|
|
/// 11:29:16.065713 [RLC_1] [D] old_header.li[1], head_len=8, pdu_space=2
|
|
|
|
/// 11:29:16.065716 [RLC_1] [D] DRB1 vt_a = 419, vt_ms = 931, vt_s = 426, poll_sn = 424
|
|
|
|
/// 11:29:16.065718 [RLC_1] [I] DRB1 Retx PDU segment SN=419 [so=2] (8 B) (attempt 2/16)
|
|
|
|
/// 0000: dd a3 00 02 00 30 aa aa
|
|
|
|
/// 11:29:16.065723 [RLC_2] [I] DRB1 Rx data PDU segment of SN=419 (2 B), SO=2, N_li=1
|
|
|
|
/// 0000: aa aa
|
|
|
|
/// 11:29:16.065730 [RLC_2] [D] [Data PDU, RF=1, P=0, FI=1, SN=419, LSF=0, SO=2, N_li=1 (3, )]
|
|
|
|
/// NOTE: this segment is malformed, it has 2 B data and a larger LI field of 3 B
|
|
|
|
|
|
|
|
// SDUs: | 10 | 10 | 10 | 10 |
|
|
|
|
// PDUs: | 15 | 16(x) | 9 |
|
|
|
|
// Retx PDU segments: |4| 50 |
|
|
|
|
|
|
|
|
const rlc_config_t config = rlc_config_t::default_rlc_am_config();
|
|
|
|
#if HAVE_PCAP
|
|
|
|
rlc_pcap pcap;
|
|
|
|
pcap.open("rlc_resegment_test_11.pcap", config);
|
|
|
|
rlc_am_tester tester(&pcap);
|
|
|
|
#else
|
|
|
|
rlc_am_tester tester(NULL);
|
|
|
|
#endif
|
|
|
|
srsran::timer_handler timers(8);
|
|
|
|
|
|
|
|
rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers);
|
|
|
|
rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers);
|
|
|
|
|
|
|
|
if (not rlc1.configure(config)) {
|
|
|
|
return SRSRAN_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (not rlc2.configure(config)) {
|
|
|
|
return SRSRAN_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Push 4 SDUs into RLC1
|
|
|
|
const uint32_t n_sdus = 4;
|
|
|
|
unique_byte_buffer_t sdu_bufs[n_sdus];
|
|
|
|
for (uint32_t i = 0; i < n_sdus; i++) {
|
|
|
|
sdu_bufs[i] = srsran::make_byte_buffer();
|
|
|
|
for (uint32_t j = 0; j < 10; j++) {
|
|
|
|
sdu_bufs[i]->msg[j] = i;
|
|
|
|
}
|
|
|
|
sdu_bufs[i]->N_bytes = 10; // Give each buffer a size of 10 bytes
|
|
|
|
sdu_bufs[i]->md.pdcp_sn = i;
|
|
|
|
rlc1.write_sdu(std::move(sdu_bufs[i]));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read 3 PDUs from RLC1 (MAC opportunities are taken from logs)
|
|
|
|
const uint32_t n_pdus = 3;
|
|
|
|
byte_buffer_t pdu_bufs[n_pdus];
|
|
|
|
pdu_bufs[0].N_bytes = rlc1.read_pdu(pdu_bufs[0].msg, 19);
|
|
|
|
pdu_bufs[1].N_bytes = rlc1.read_pdu(pdu_bufs[1].msg, 21);
|
|
|
|
pdu_bufs[2].N_bytes = rlc1.read_pdu(pdu_bufs[2].msg, 12);
|
|
|
|
|
|
|
|
TESTASSERT(0 == rlc1.get_buffer_state());
|
|
|
|
|
|
|
|
// Write PDUs into RLC2 (skip SN 1)
|
|
|
|
for (uint32_t i = 0; i < n_pdus; i++) {
|
|
|
|
if (i != 1) {
|
|
|
|
rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes);
|
|
|
|
}
|
|
|
|
#if HAVE_PCAP
|
|
|
|
// write to PCAP even if its lost in the TC
|
|
|
|
pcap.write_dl_ccch(pdu_bufs[i].msg, pdu_bufs[i].N_bytes);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step timers until reordering timeout expires
|
|
|
|
int cnt = 5;
|
|
|
|
while (cnt--) {
|
|
|
|
timers.step_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read status PDU from RLC2
|
|
|
|
byte_buffer_t status_buf;
|
|
|
|
status_buf.N_bytes = rlc2.read_pdu(status_buf.msg, 10); // 10 bytes is enough to hold the status
|
|
|
|
|
|
|
|
// Write status PDU to RLC1
|
|
|
|
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.write_ul_ccch(status_buf.msg, status_buf.N_bytes);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Read the retx PDU from RLC1 and force resegmentation
|
|
|
|
byte_buffer_t retx1;
|
|
|
|
retx1.N_bytes = rlc1.read_pdu(retx1.msg, 8);
|
|
|
|
rlc2.write_pdu(retx1.msg, retx1.N_bytes);
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.write_dl_ccch(retx1.msg, retx1.N_bytes);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Read 2nd with a small grant to trigger the original segmentation bug
|
|
|
|
byte_buffer_t retx2;
|
|
|
|
retx2.N_bytes = rlc1.read_pdu(retx2.msg, 10);
|
|
|
|
|
|
|
|
// Write the retx PDU to RLC2
|
|
|
|
rlc2.write_pdu(retx2.msg, retx2.N_bytes);
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.write_dl_ccch(retx2.msg, retx2.N_bytes);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Read 3nd with a big enough grant to fit remaining content
|
|
|
|
byte_buffer_t retx3;
|
|
|
|
retx3.N_bytes = rlc1.read_pdu(retx3.msg, 20);
|
|
|
|
rlc2.write_pdu(retx3.msg, retx3.N_bytes);
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.write_dl_ccch(retx3.msg, retx3.N_bytes);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
TESTASSERT(tester.sdus.size() == n_sdus);
|
|
|
|
for (uint32_t i = 0; i < tester.sdus.size(); i++) {
|
|
|
|
TESTASSERT(tester.sdus[i]->N_bytes == 10);
|
|
|
|
for (int j = 0; j < 10; j++) {
|
|
|
|
TESTASSERT(tester.sdus[i]->msg[j] == i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get status from RLC 2
|
|
|
|
for (int i = 0; i < 5; i++) {
|
|
|
|
timers.step_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read status PDU from RLC2
|
|
|
|
status_buf.N_bytes = rlc2.read_pdu(status_buf.msg, 10); // 10 bytes is enough to hold the status
|
|
|
|
|
|
|
|
// Write status PDU to RLC1
|
|
|
|
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
|
|
|
|
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.close();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return SRSRAN_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Custom resegmentation test of a orignal PDU with N_li=2
|
|
|
|
// The test triggered a bug in the packing and was creating a too large
|
|
|
|
// PDU
|
|
|
|
int resegment_test_12()
|
|
|
|
{
|
|
|
|
/// Original PDU:
|
|
|
|
/// 17:19:51.296653 [RLC_1] [I] DRB1 Tx PDU SN=728 (21 B)
|
|
|
|
/// 0000: be d8 80 10 0a d1 d2 d2 d2 d2 d2 d2 d2 d2 d2 d2
|
|
|
|
/// 0010: d3 d3 d3 d3 d3
|
|
|
|
/// 17:19:51.296659 [RLC_1] [D] [Data PDU, RF=0, P=1, FI=1, SN=728, LSF=0, SO=0, N_li=2 (1, 10, )]
|
|
|
|
|
|
|
|
/// Log messages with the segmentation bug:
|
|
|
|
/// 17:19:51.297485 [RLC_1] [D] MAC opportunity - 18 bytes
|
|
|
|
/// 17:19:51.297487 [RLC_1] [D] tx_window size - 2 PDUs
|
|
|
|
/// 17:19:51.297489 [RLC_1] [D] DRB1 build_retx_pdu - resegmentation required
|
|
|
|
/// 17:19:51.297498 [RLC_1] [I] DRB1 pdu_without_poll: 4
|
|
|
|
/// 17:19:51.297499 [RLC_1] [I] DRB1 byte_without_poll: 67
|
|
|
|
/// 17:19:51.297501 [RLC_1] [D] retx.so_start=0, retx.so_end=12
|
|
|
|
/// 17:19:51.297502 [RLC_1] [D] new_header head_len=4
|
|
|
|
/// 17:19:51.297504 [RLC_1] [D] old_header.li[0], head_len=4, pdu_space=14
|
|
|
|
/// 17:19:51.297505 [RLC_1] [D] new_header head_len=6
|
|
|
|
/// 17:19:51.297506 [RLC_1] [D] old_header.li[1], head_len=6, pdu_space=12
|
|
|
|
/// 17:19:51.297509 [RLC_1] [D] DRB1 vt_a = 724, vt_ms = 212, vt_s = 736, poll_sn = 733
|
|
|
|
/// 17:19:51.297513 [RLC_1] [E] DRB1 Retx PDU segment length error. Available: 18, Used: 19
|
|
|
|
/// 17:19:51.297522 [RLC_1] [D] DRB1 Retx PDU segment length error. Header len: 7, Payload len: 12, N_li: 2
|
|
|
|
/// 17:19:51.297527 [RLC_1] [I] DRB1 Retx PDU segment SN=728 [so=0] (19 B) (attempt 2/16)
|
|
|
|
/// 0000: de d8 00 00 80 10 0a d1 d2 d2 d2 d2 d2 d2 d2 d2
|
|
|
|
/// 0010: d2 d2 d3
|
|
|
|
/// 17:19:51.297531 [RLC_2] [I] DRB1 Rx data PDU segment of SN=728 (12 B), SO=0, N_li=2
|
|
|
|
/// 0000: d1 d2 d2 d2 d2 d2 d2 d2 d2 d2 d2 d3
|
|
|
|
/// 17:19:51.297538 [RLC_2] [D] [Data PDU, RF=1, P=0, FI=1, SN=728, LSF=0, SO=0, N_li=2 (1, 10, )]
|
|
|
|
|
|
|
|
// SDUs: | 10 | 10 | 10 | 10 |
|
|
|
|
// PDUs: | 9 | 16(x) | 9 |
|
|
|
|
// Retx PDU segments: |4| 50 |
|
|
|
|
|
|
|
|
const rlc_config_t config = rlc_config_t::default_rlc_am_config();
|
|
|
|
#if HAVE_PCAP
|
|
|
|
rlc_pcap pcap;
|
|
|
|
pcap.open("rlc_resegment_test_12.pcap", config);
|
|
|
|
rlc_am_tester tester(&pcap);
|
|
|
|
#else
|
|
|
|
rlc_am_tester tester(NULL);
|
|
|
|
#endif
|
|
|
|
srsran::timer_handler timers(8);
|
|
|
|
|
|
|
|
rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers);
|
|
|
|
rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers);
|
|
|
|
|
|
|
|
if (not rlc1.configure(config)) {
|
|
|
|
return SRSRAN_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (not rlc2.configure(config)) {
|
|
|
|
return SRSRAN_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Push 4 SDUs into RLC1
|
|
|
|
const uint32_t n_sdus = 4;
|
|
|
|
unique_byte_buffer_t sdu_bufs[n_sdus];
|
|
|
|
for (uint32_t i = 0; i < n_sdus; i++) {
|
|
|
|
sdu_bufs[i] = srsran::make_byte_buffer();
|
|
|
|
for (uint32_t j = 0; j < 10; j++) {
|
|
|
|
sdu_bufs[i]->msg[j] = i;
|
|
|
|
}
|
|
|
|
sdu_bufs[i]->N_bytes = 10; // Give each buffer a size of 10 bytes
|
|
|
|
sdu_bufs[i]->md.pdcp_sn = i;
|
|
|
|
rlc1.write_sdu(std::move(sdu_bufs[i]));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read 3 PDUs from RLC1 (MAC opportunities are taken from logs)
|
|
|
|
const uint32_t n_pdus = 3;
|
|
|
|
byte_buffer_t pdu_bufs[n_pdus];
|
|
|
|
pdu_bufs[0].N_bytes = rlc1.read_pdu(pdu_bufs[0].msg, 11);
|
|
|
|
pdu_bufs[1].N_bytes = rlc1.read_pdu(pdu_bufs[1].msg, 21);
|
|
|
|
pdu_bufs[2].N_bytes = rlc1.read_pdu(pdu_bufs[2].msg, 19);
|
|
|
|
|
|
|
|
TESTASSERT(0 == rlc1.get_buffer_state());
|
|
|
|
|
|
|
|
// Write PDUs into RLC2 (skip SN 1)
|
|
|
|
for (uint32_t i = 0; i < n_pdus; i++) {
|
|
|
|
if (i != 1) {
|
|
|
|
rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes);
|
|
|
|
}
|
|
|
|
#if HAVE_PCAP
|
|
|
|
// write to PCAP even if its lost in the TC
|
|
|
|
pcap.write_dl_ccch(pdu_bufs[i].msg, pdu_bufs[i].N_bytes);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step timers until reordering timeout expires
|
|
|
|
int cnt = 5;
|
|
|
|
while (cnt--) {
|
|
|
|
timers.step_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read status PDU from RLC2
|
|
|
|
byte_buffer_t status_buf;
|
|
|
|
status_buf.N_bytes = rlc2.read_pdu(status_buf.msg, 10); // 10 bytes is enough to hold the status
|
|
|
|
|
|
|
|
// Write status PDU to RLC1
|
|
|
|
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.write_ul_ccch(status_buf.msg, status_buf.N_bytes);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Read the retx PDU from RLC1 and force resegmentation
|
|
|
|
byte_buffer_t retx1;
|
|
|
|
retx1.N_bytes = rlc1.read_pdu(retx1.msg, 18);
|
|
|
|
rlc2.write_pdu(retx1.msg, retx1.N_bytes);
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.write_dl_ccch(retx1.msg, retx1.N_bytes);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Read 2nd to trigger the original segmentation bug
|
|
|
|
byte_buffer_t retx2;
|
|
|
|
retx2.N_bytes = rlc1.read_pdu(retx2.msg, 18);
|
|
|
|
rlc2.write_pdu(retx2.msg, retx2.N_bytes);
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.write_dl_ccch(retx2.msg, retx2.N_bytes);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Read 3nd with a big enough grant to fit remaining content
|
|
|
|
byte_buffer_t retx3;
|
|
|
|
retx3.N_bytes = rlc1.read_pdu(retx3.msg, 20);
|
|
|
|
rlc2.write_pdu(retx3.msg, retx3.N_bytes);
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.write_dl_ccch(retx3.msg, retx3.N_bytes);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
TESTASSERT(tester.sdus.size() == n_sdus);
|
|
|
|
for (uint32_t i = 0; i < tester.sdus.size(); i++) {
|
|
|
|
TESTASSERT(tester.sdus[i]->N_bytes == 10);
|
|
|
|
for (int j = 0; j < 10; j++) {
|
|
|
|
TESTASSERT(tester.sdus[i]->msg[j] == i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get status from RLC 2
|
|
|
|
for (int i = 0; i < 5; i++) {
|
|
|
|
timers.step_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read status PDU from RLC2
|
|
|
|
status_buf.N_bytes = rlc2.read_pdu(status_buf.msg, 10); // 10 bytes is enough to hold the status
|
|
|
|
|
|
|
|
// Write status PDU to RLC1
|
|
|
|
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
|
|
|
|
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.close();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return SRSRAN_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Series of header reconstruction tests that all used canned TV generated with the rlc_stress_test
|
|
|
|
// In this particular case, check correct reconstruction of headers after 2 segment retx
|
|
|
|
int header_reconstruction_test(srsran::log_sink_message_spy& spy)
|
|
|
|
{
|
|
|
|
/// Original SN=277 with 3 segments, including full SDU with 24
|
|
|
|
/// 13:35:16.337011 [RLC_1] [I] DRB1 Tx PDU SN=277 (20 B)
|
|
|
|
/// 0000: 9d 15 80 20 0a 23 23 24 24 24 24 24 24 24 24 24
|
|
|
|
/// 0010: 24 25 25 25
|
|
|
|
/// 13:35:16.337016 [RLC_1] [D] [Data PDU, RF=0, P=0, FI=1, SN=277, LSF=0, SO=0, N_li=2 (2, 10)]
|
|
|
|
|
|
|
|
// 2nd retransmission with SO=9
|
|
|
|
std::array<uint8_t, 12> tv2 = {0xdd, 0x15, 0x80, 0x09, 0x00, 0x30, 0x24, 0x24, 0x24, 0x25, 0x25, 0x25};
|
|
|
|
|
|
|
|
// 3rd retransmission with S0=0
|
|
|
|
std::array<uint8_t, 17> tv3 = {
|
|
|
|
0xdd, 0x15, 0x00, 0x00, 0x00, 0x20, 0x23, 0x23, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24, 0x24};
|
|
|
|
|
|
|
|
byte_buffer_t pdu_tv2;
|
|
|
|
memcpy(pdu_tv2.msg, tv2.data(), tv2.size());
|
|
|
|
pdu_tv2.N_bytes = tv2.size();
|
|
|
|
|
|
|
|
byte_buffer_t pdu_tv3;
|
|
|
|
memcpy(pdu_tv3.msg, tv3.data(), tv3.size());
|
|
|
|
pdu_tv3.N_bytes = tv3.size();
|
|
|
|
|
|
|
|
#if HAVE_PCAP
|
|
|
|
rlc_pcap pcap;
|
|
|
|
pcap.open("rlc_am_header_reconstruction_test.pcap", rlc_config_t::default_rlc_am_config());
|
|
|
|
rlc_am_tester tester(&pcap);
|
|
|
|
#else
|
|
|
|
rlc_am_tester tester(NULL);
|
|
|
|
#endif
|
|
|
|
srsran::timer_handler timers(8);
|
|
|
|
|
|
|
|
rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers);
|
|
|
|
|
|
|
|
if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.write_dl_ccch(pdu_tv2.msg, pdu_tv2.N_bytes);
|
|
|
|
pcap.write_dl_ccch(pdu_tv3.msg, pdu_tv3.N_bytes);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
rlc1.write_pdu(pdu_tv2.msg, pdu_tv2.N_bytes);
|
|
|
|
rlc1.write_pdu(pdu_tv3.msg, pdu_tv3.N_bytes);
|
|
|
|
|
|
|
|
// Check RLC re-assembled message header
|
|
|
|
TESTASSERT(spy.has_message("[Data PDU, RF=0, P=0, FI=1, SN=277, LSF=0, SO=0, N_li=2 (2, 10)]"));
|
|
|
|
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.close();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check correct reconstruction of headers after 3 segment retx
|
|
|
|
int header_reconstruction_test2(srsran::log_sink_message_spy& spy)
|
|
|
|
{
|
|
|
|
/// Original SN=199 with 3 segments, including full SDU with d4
|
|
|
|
/// 15:19:19.148272 [RLC_1] [I] DRB1 Tx PDU SN=199 (19 B)
|
|
|
|
/// 0000: 9c c7 80 30 0a d3 d3 d3 d4 d4 d4 d4 d4 d4 d4 d4
|
|
|
|
/// 0010: d4 d4 d5
|
|
|
|
/// 15:19:19.148278 [RLC_1] [D] [Data PDU, RF=0, P=0, FI=1, SN=199, LSF=0, SO=0, N_li=2 (3, 10, )]
|
|
|
|
|
|
|
|
// 2nd retransmission with SO=0
|
|
|
|
std::array<uint8_t, 6> tv1 = {0xd8, 0xc7, 0x00, 0x00, 0xd3, 0xd3};
|
|
|
|
|
|
|
|
// 3rd retransmission with S0=2
|
|
|
|
std::array<uint8_t, 16> tv2 = {
|
|
|
|
0xdc, 0xc7, 0x00, 0x02, 0x00, 0x10, 0xd3, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4};
|
|
|
|
|
|
|
|
std::array<uint8_t, 8> tv3 = {0xdc, 0xc7, 0x80, 0x0c, 0x00, 0x10, 0xd4, 0xd5};
|
|
|
|
|
|
|
|
byte_buffer_t pdu_tv1;
|
|
|
|
memcpy(pdu_tv1.msg, tv1.data(), tv1.size());
|
|
|
|
pdu_tv1.N_bytes = tv1.size();
|
|
|
|
|
|
|
|
byte_buffer_t pdu_tv2;
|
|
|
|
memcpy(pdu_tv2.msg, tv2.data(), tv2.size());
|
|
|
|
pdu_tv2.N_bytes = tv2.size();
|
|
|
|
|
|
|
|
byte_buffer_t pdu_tv3;
|
|
|
|
memcpy(pdu_tv3.msg, tv3.data(), tv3.size());
|
|
|
|
pdu_tv3.N_bytes = tv3.size();
|
|
|
|
|
|
|
|
#if HAVE_PCAP
|
|
|
|
rlc_pcap pcap;
|
|
|
|
pcap.open("rlc_am_header_reconstruction_test2.pcap", rlc_config_t::default_rlc_am_config());
|
|
|
|
rlc_am_tester tester(&pcap);
|
|
|
|
#else
|
|
|
|
rlc_am_tester tester(NULL);
|
|
|
|
#endif
|
|
|
|
srsran::timer_handler timers(8);
|
|
|
|
|
|
|
|
rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers);
|
|
|
|
if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.write_dl_ccch(pdu_tv1.msg, pdu_tv1.N_bytes);
|
|
|
|
pcap.write_dl_ccch(pdu_tv2.msg, pdu_tv2.N_bytes);
|
|
|
|
pcap.write_dl_ccch(pdu_tv3.msg, pdu_tv3.N_bytes);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
rlc1.write_pdu(pdu_tv1.msg, pdu_tv1.N_bytes);
|
|
|
|
rlc1.write_pdu(pdu_tv2.msg, pdu_tv2.N_bytes);
|
|
|
|
rlc1.write_pdu(pdu_tv3.msg, pdu_tv3.N_bytes);
|
|
|
|
|
|
|
|
// Check RLC re-assembled message header
|
|
|
|
TESTASSERT(spy.has_message("[Data PDU, RF=0, P=0, FI=1, SN=199, LSF=0, SO=0, N_li=2 (3, 10)]"));
|
|
|
|
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.close();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return SRSRAN_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
// TC with 3 segment retx
|
|
|
|
int header_reconstruction_test3(srsran::log_sink_message_spy& spy)
|
|
|
|
{
|
|
|
|
// Original PDU
|
|
|
|
// 11:13:25.994566 [RLC_1] [I] DRB1 Tx PDU SN=206 (18 B)
|
|
|
|
// 0000: 8c ce 00 a0 db db db db db db db db db db dc dc
|
|
|
|
// 0010: dc dc
|
|
|
|
// 11:13:25.994571 [RLC_1] [D] [Data PDU, RF=0, P=0, FI=1, SN=206, LSF=0, SO=0, N_li=1 (10)]
|
|
|
|
|
|
|
|
// 11:13:25.995744 [RLC_1] [I] DRB1 Retx PDU segment SN=206 [so=8] (12 B) (attempt 2/16)
|
|
|
|
// 0000: dc ce 80 08 00 20 db db dc dc dc dc
|
|
|
|
// 11:13:25.995752 [RLC_2] [I] DRB1 Rx data PDU segment of SN=206 (6 B), SO=8, N_li=1
|
|
|
|
// 0000: db db dc dc dc dc
|
|
|
|
std::array<uint8_t, 12> tv0 = {0xdc, 0xce, 0x80, 0x08, 0x00, 0x20, 0xdb, 0xdb, 0xdc, 0xdc, 0xdc, 0xdc};
|
|
|
|
|
|
|
|
// 11:13:25.996267 [RLC_1] [I] DRB1 Retx PDU segment SN=206 [so=0] (14 B) (attempt 3/16)
|
|
|
|
// 0000: c0 ce 00 00 db db db db db db db db db db
|
|
|
|
// 11:13:25.996272 [RLC_2] [I] DRB1 Rx data PDU segment of SN=206 (10 B), SO=0, N_li=0
|
|
|
|
// 0000: db db db db db db db db db db
|
|
|
|
std::array<uint8_t, 14> tv1 = {0xc0, 0xce, 0x00, 0x00, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb, 0xdb};
|
|
|
|
|
|
|
|
byte_buffer_t pdu_tv0;
|
|
|
|
memcpy(pdu_tv0.msg, tv0.data(), tv0.size());
|
|
|
|
pdu_tv0.N_bytes = tv0.size();
|
|
|
|
|
|
|
|
byte_buffer_t pdu_tv1;
|
|
|
|
memcpy(pdu_tv1.msg, tv1.data(), tv1.size());
|
|
|
|
pdu_tv1.N_bytes = tv1.size();
|
|
|
|
|
|
|
|
#if HAVE_PCAP
|
|
|
|
rlc_pcap pcap;
|
|
|
|
pcap.open("rlc_am_header_reconstruction_test3.pcap", rlc_config_t::default_rlc_am_config());
|
|
|
|
rlc_am_tester tester(&pcap);
|
|
|
|
#else
|
|
|
|
rlc_am_tester tester(NULL);
|
|
|
|
#endif
|
|
|
|
srsran::timer_handler timers(8);
|
|
|
|
|
|
|
|
// configure RLC
|
|
|
|
rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers);
|
|
|
|
if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.write_dl_ccch(pdu_tv0.msg, pdu_tv0.N_bytes);
|
|
|
|
pcap.write_dl_ccch(pdu_tv1.msg, pdu_tv1.N_bytes);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
rlc1.write_pdu(pdu_tv0.msg, pdu_tv0.N_bytes);
|
|
|
|
rlc1.write_pdu(pdu_tv1.msg, pdu_tv1.N_bytes);
|
|
|
|
|
|
|
|
// Check RLC re-assembled message header
|
|
|
|
TESTASSERT(spy.has_message("[Data PDU, RF=0, P=0, FI=1, SN=206, LSF=0, SO=0, N_li=1 (10)]"));
|
|
|
|
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.close();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return SRSRAN_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
int header_reconstruction_test4(srsran::log_sink_message_spy& spy)
|
|
|
|
{
|
|
|
|
// Original PDU
|
|
|
|
// 15:32:20.667043 [RLC_1] [I] DRB1 Tx PDU SN=172 (22 B)
|
|
|
|
// 0000: 9c ac 80 10 0a af b0 b0 b0 b0 b0 b0 b0 b0 b0 b0
|
|
|
|
// 0010: b1 b1 b1 b1 b1 b1
|
|
|
|
// 15:32:20.667048 [RLC_1] [D] [Data PDU, RF=0, P=0, FI=1, SN=172, LSF=0, SO=0, N_li=2 (1, 10)]
|
|
|
|
|
|
|
|
// 15:32:20.668094 [RLC_1] [I] DRB1 Retx PDU segment SN=172 [so=0] (14 B) (attempt 2/16)
|
|
|
|
// 0000: dc ac 00 00 00 10 af b0 b0 b0 b0 b0 b0 b0
|
|
|
|
// 15:32:20.668100 [RLC_2] [I] DRB1 Rx data PDU segment of SN=172 (8 B), SO=0, N_li=1
|
|
|
|
// 0000: af b0 b0 b0 b0 b0 b0 b0
|
|
|
|
// 15:32:20.668105 [RLC_2] [D] [Data PDU, RF=1, P=0, FI=1, SN=172, LSF=0, SO=0, N_li=1 (1)]
|
|
|
|
std::array<uint8_t, 14> tv1 = {0xdc, 0xac, 0x00, 0x00, 0x00, 0x10, 0xaf, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0};
|
|
|
|
|
|
|
|
// 15:32:20.668497 [RLC_1] [I] DRB1 Retx PDU segment SN=172 [so=0] (12 B) (attempt 3/16)
|
|
|
|
// 0000: fc ac 00 00 00 10 af b0 b0 b0 b0 b0
|
|
|
|
// 15:32:20.668502 [RLC_2] [I] DRB1 Rx data PDU segment of SN=172 (6 B), SO=0, N_li=1
|
|
|
|
// 0000: af b0 b0 b0 b0 b0
|
|
|
|
// 15:32:20.668507 [RLC_2] [D] [Data PDU, RF=1, P=1, FI=1, SN=172, LSF=0, SO=0, N_li=1 (1)]
|
|
|
|
std::array<uint8_t, 12> tv2 = {0xfc, 0xac, 0x00, 0x00, 0x00, 0x10, 0xaf, 0xb0, 0xb0, 0xb0, 0xb0, 0xb0};
|
|
|
|
|
|
|
|
// 15:32:20.668575 [RLC_1] [I] DRB1 Retx PDU segment SN=172 [so=6] (7 B) (attempt 3/16)
|
|
|
|
// 0000: d8 ac 00 06 b0 b0 b0
|
|
|
|
// 15:32:20.668581 [RLC_1] [I] DRB1 Tx SDU (10 B, tx_sdu_queue_len=33)
|
|
|
|
// 0000: d8 d8 d8 d8 d8 d8 d8 d8 d8 d8
|
|
|
|
// 15:32:20.668582 [RLC_2] [I] DRB1 Rx data PDU segment of SN=172 (3 B), SO=6, N_li=0
|
|
|
|
// 0000: b0 b0 b0
|
|
|
|
std::array<uint8_t, 7> tv3 = {0xd8, 0xac, 0x00, 0x06, 0xb0, 0xb0, 0xb0};
|
|
|
|
|
|
|
|
// 15:32:20.668665 [RLC_1] [I] DRB1 Retx PDU segment SN=172 [so=9] (14 B) (attempt 3/16)
|
|
|
|
// 0000: dc ac 80 09 00 20 b0 b0 b1 b1 b1 b1 b1 b1
|
|
|
|
// 15:32:20.668671 [RLC_2] [I] DRB1 Rx data PDU segment of SN=172 (8 B), SO=9, N_li=1
|
|
|
|
// 0000: b0 b0 b1 b1 b1 b1 b1 b1
|
|
|
|
// 15:32:20.668675 [RLC_2] [D] [Data PDU, RF=1, P=0, FI=1, SN=172, LSF=1, SO=9, N_li=1 (2)]
|
|
|
|
std::array<uint8_t, 14> tv4 = {0xdc, 0xac, 0x80, 0x09, 0x00, 0x20, 0xb0, 0xb0, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1, 0xb1};
|
|
|
|
|
|
|
|
byte_buffer_t pdu_tv1;
|
|
|
|
memcpy(pdu_tv1.msg, tv1.data(), tv1.size());
|
|
|
|
pdu_tv1.N_bytes = tv1.size();
|
|
|
|
|
|
|
|
byte_buffer_t pdu_tv2;
|
|
|
|
memcpy(pdu_tv2.msg, tv2.data(), tv2.size());
|
|
|
|
pdu_tv2.N_bytes = tv2.size();
|
|
|
|
|
|
|
|
byte_buffer_t pdu_tv3;
|
|
|
|
memcpy(pdu_tv3.msg, tv3.data(), tv3.size());
|
|
|
|
pdu_tv3.N_bytes = tv3.size();
|
|
|
|
|
|
|
|
byte_buffer_t pdu_tv4;
|
|
|
|
memcpy(pdu_tv4.msg, tv4.data(), tv4.size());
|
|
|
|
pdu_tv4.N_bytes = tv4.size();
|
|
|
|
|
|
|
|
#if HAVE_PCAP
|
|
|
|
rlc_pcap pcap;
|
|
|
|
pcap.open("rlc_am_header_reconstruction_test4.pcap", rlc_config_t::default_rlc_am_config());
|
|
|
|
rlc_am_tester tester(&pcap);
|
|
|
|
#else
|
|
|
|
rlc_am_tester tester(NULL);
|
|
|
|
#endif
|
|
|
|
srsran::timer_handler timers(8);
|
|
|
|
|
|
|
|
// configure RLC
|
|
|
|
rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers);
|
|
|
|
if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.write_dl_ccch(pdu_tv1.msg, pdu_tv1.N_bytes);
|
|
|
|
pcap.write_dl_ccch(pdu_tv2.msg, pdu_tv2.N_bytes);
|
|
|
|
pcap.write_dl_ccch(pdu_tv3.msg, pdu_tv3.N_bytes);
|
|
|
|
pcap.write_dl_ccch(pdu_tv4.msg, pdu_tv4.N_bytes);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
rlc1.write_pdu(pdu_tv1.msg, pdu_tv1.N_bytes);
|
|
|
|
rlc1.write_pdu(pdu_tv2.msg, pdu_tv2.N_bytes);
|
|
|
|
rlc1.write_pdu(pdu_tv3.msg, pdu_tv3.N_bytes);
|
|
|
|
rlc1.write_pdu(pdu_tv4.msg, pdu_tv4.N_bytes);
|
|
|
|
|
|
|
|
// Check RLC re-assembled message header
|
|
|
|
TESTASSERT(spy.has_message("[Data PDU, RF=0, P=0, FI=1, SN=172, LSF=0, SO=0, N_li=2 (1, 10)]"));
|
|
|
|
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.close();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return SRSRAN_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
int header_reconstruction_test5(srsran::log_sink_message_spy& spy)
|
|
|
|
{
|
|
|
|
// Original PDU:
|
|
|
|
// 18:46:22.372858 [RLC_1] [I] DRB1 Tx PDU SN=222 (22 B)
|
|
|
|
// 0000: bc de 80 30 0a ee ee ee ef ef ef ef ef ef ef ef
|
|
|
|
// 0010: ef ef f0 f0 f0 f0
|
|
|
|
// 18:46:22.372863 [RLC_1] [D] [Data PDU, RF=0, P=1, FI=1, SN=222, LSF=0, SO=0, N_li=2 (3, 10)]
|
|
|
|
|
|
|
|
// 18:46:22.373623 [RLC_1] [I] DRB1 Retx PDU segment SN=222 [so=0] (7 B) (attempt 2/16)
|
|
|
|
// 0000: d0 de 00 00 ee ee ee
|
|
|
|
// 18:46:22.373629 [RLC_2] [I] DRB1 Rx data PDU segment of SN=222 (3 B), SO=0, N_li=0
|
|
|
|
// 0000: ee ee ee
|
|
|
|
std::array<uint8_t, 7> tv0 = {0xd0, 0xde, 0x00, 0x00, 0xee, 0xee, 0xee};
|
|
|
|
|
|
|
|
// 18:46:22.373707 [RLC_1] [I] DRB1 Retx PDU segment SN=222 [so=3] (19 B) (attempt 2/16)
|
|
|
|
// 0000: cc de 00 03 00 a0 ef ef ef ef ef ef ef ef ef ef
|
|
|
|
// 0010: f0 f0 f0
|
|
|
|
// 18:46:22.373714 [RLC_2] [I] DRB1 Rx data PDU segment of SN=222 (13 B), SO=3, N_li=1
|
|
|
|
// 0000: ef ef ef ef ef ef ef ef ef ef f0 f0 f0
|
|
|
|
std::array<uint8_t, 19> tv1 = {
|
|
|
|
0xcc, 0xde, 0x00, 0x03, 0x00, 0xa0, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xef, 0xf0, 0xf0, 0xf0};
|
|
|
|
|
|
|
|
// 18:46:22.373793 [RLC_1] [I] DRB1 Retx PDU segment SN=222 [so=16] (5 B) (attempt 2/16)
|
|
|
|
// 0000: d8 de 80 10 f0
|
|
|
|
// 18:46:22.373798 [RLC_2] [I] DRB1 Rx data PDU segment of SN=222 (1 B), SO=16, N_li=0
|
|
|
|
// 0000: f0
|
|
|
|
std::array<uint8_t, 5> tv2 = {0xd8, 0xde, 0x80, 0x10, 0xf0};
|
|
|
|
|
|
|
|
byte_buffer_t pdu_tv0;
|
|
|
|
memcpy(pdu_tv0.msg, tv0.data(), tv0.size());
|
|
|
|
pdu_tv0.N_bytes = tv0.size();
|
|
|
|
|
|
|
|
byte_buffer_t pdu_tv1;
|
|
|
|
memcpy(pdu_tv1.msg, tv1.data(), tv1.size());
|
|
|
|
pdu_tv1.N_bytes = tv1.size();
|
|
|
|
|
|
|
|
byte_buffer_t pdu_tv2;
|
|
|
|
memcpy(pdu_tv2.msg, tv2.data(), tv2.size());
|
|
|
|
pdu_tv2.N_bytes = tv2.size();
|
|
|
|
|
|
|
|
#if HAVE_PCAP
|
|
|
|
rlc_pcap pcap;
|
|
|
|
pcap.open("rlc_am_header_reconstruction_test5.pcap", rlc_config_t::default_rlc_am_config());
|
|
|
|
rlc_am_tester tester(&pcap);
|
|
|
|
#else
|
|
|
|
rlc_am_tester tester(NULL);
|
|
|
|
#endif
|
|
|
|
srsran::timer_handler timers(8);
|
|
|
|
|
|
|
|
// configure RLC
|
|
|
|
rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers);
|
|
|
|
if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.write_dl_ccch(pdu_tv0.msg, pdu_tv0.N_bytes);
|
|
|
|
pcap.write_dl_ccch(pdu_tv1.msg, pdu_tv1.N_bytes);
|
|
|
|
pcap.write_dl_ccch(pdu_tv2.msg, pdu_tv2.N_bytes);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// don't write original PDU
|
|
|
|
rlc1.write_pdu(pdu_tv0.msg, pdu_tv0.N_bytes);
|
|
|
|
rlc1.write_pdu(pdu_tv1.msg, pdu_tv1.N_bytes);
|
|
|
|
rlc1.write_pdu(pdu_tv2.msg, pdu_tv2.N_bytes);
|
|
|
|
|
|
|
|
// Check RLC re-assembled message header
|
|
|
|
TESTASSERT(spy.has_message("[Data PDU, RF=0, P=0, FI=1, SN=222, LSF=0, SO=0, N_li=2 (3, 10)]"));
|
|
|
|
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.close();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return SRSRAN_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
int header_reconstruction_test6(srsran::log_sink_message_spy& spy)
|
|
|
|
{
|
|
|
|
// Original PDU:
|
|
|
|
// 21:50:12.709646 [RLC_1] [I] DRB1 Tx PDU SN=509 (20 B)
|
|
|
|
// 0000: 9d fd 80 40 0a b1 b1 b1 b1 b2 b2 b2 b2 b2 b2 b2
|
|
|
|
// 0010: b2 b2 b2 b3
|
|
|
|
// 21:50:12.709653 [RLC_1] [D] [Data PDU, RF=0, P=0, FI=1, SN=509, LSF=0, SO=0, N_li=2 (4, 10)]]
|
|
|
|
|
|
|
|
// 21:50:12.711022 [RLC_1] [I] DRB1 Retx PDU segment SN=509 [so=0] (5 B) (attempt 3/16)
|
|
|
|
// 0000: d9 fd 00 00 b1
|
|
|
|
// 21:50:12.711029 [RLC_2] [I] DRB1 Rx data PDU segment of SN=509 (1 B), SO=0, N_li=0
|
|
|
|
// 0000: b1
|
|
|
|
// 21:50:12.711034 [RLC_2] [D] [Data PDU, RF=1, P=0, FI=1, SN=509, LSF=0, SO=0, N_li=0]
|
|
|
|
std::array<uint8_t, 5> tv0 = {0xd9, 0xfd, 0x00, 0x00, 0xb1};
|
|
|
|
|
|
|
|
// 21:50:12.711104 [RLC_1] [I] DRB1 Retx PDU segment SN=509 [so=1] (7 B) (attempt 3/16)
|
|
|
|
// 0000: d1 fd 00 01 b1 b1 b1
|
|
|
|
// 21:50:12.711110 [RLC_2] [I] DRB1 Rx data PDU segment of SN=509 (3 B), SO=1, N_li=0
|
|
|
|
// 0000: b1 b1 b1
|
|
|
|
// 21:50:12.711115 [RLC_2] [D] [Data PDU, RF=1, P=0, FI=1, SN=509, LSF=0, SO=1, N_li=0]
|
|
|
|
std::array<uint8_t, 7> tv1 = {0xd1, 0xfd, 0x00, 0x01, 0xb1, 0xb1, 0xb1};
|
|
|
|
|
|
|
|
// 21:50:12.711201 [RLC_1] [I] DRB1 Retx PDU segment SN=509 [so=4] (17 B) (attempt 3/16)
|
|
|
|
// 0000: ed fd 80 04 00 a0 b2 b2 b2 b2 b2 b2 b2 b2 b2 b2
|
|
|
|
// 0010: b3
|
|
|
|
// 21:50:12.711210 [RLC_2] [I] DRB1 Rx data PDU segment of SN=509 (11 B), SO=4, N_li=1
|
|
|
|
// 0000: b2 b2 b2 b2 b2 b2 b2 b2 b2 b2 b3
|
|
|
|
// 21:50:12.711216 [RLC_2] [D] [Data PDU, RF=1, P=1, FI=1, SN=509, LSF=1, SO=4, N_li=1 (10)]
|
|
|
|
std::array<uint8_t, 17> tv2 = {
|
|
|
|
0xed, 0xfd, 0x80, 0x04, 0x00, 0xa0, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb2, 0xb3};
|
|
|
|
|
|
|
|
byte_buffer_t pdu_tv0;
|
|
|
|
memcpy(pdu_tv0.msg, tv0.data(), tv0.size());
|
|
|
|
pdu_tv0.N_bytes = tv0.size();
|
|
|
|
|
|
|
|
byte_buffer_t pdu_tv1;
|
|
|
|
memcpy(pdu_tv1.msg, tv1.data(), tv1.size());
|
|
|
|
pdu_tv1.N_bytes = tv1.size();
|
|
|
|
|
|
|
|
byte_buffer_t pdu_tv2;
|
|
|
|
memcpy(pdu_tv2.msg, tv2.data(), tv2.size());
|
|
|
|
pdu_tv2.N_bytes = tv2.size();
|
|
|
|
|
|
|
|
#if HAVE_PCAP
|
|
|
|
rlc_pcap pcap;
|
|
|
|
pcap.open("rlc_am_header_reconstruction_test6.pcap", rlc_config_t::default_rlc_am_config());
|
|
|
|
rlc_am_tester tester(&pcap);
|
|
|
|
#else
|
|
|
|
rlc_am_tester tester(NULL);
|
|
|
|
#endif
|
|
|
|
srsran::timer_handler timers(8);
|
|
|
|
|
|
|
|
// configure RLC
|
|
|
|
rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers);
|
|
|
|
if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.write_dl_ccch(pdu_tv0.msg, pdu_tv0.N_bytes);
|
|
|
|
pcap.write_dl_ccch(pdu_tv1.msg, pdu_tv1.N_bytes);
|
|
|
|
pcap.write_dl_ccch(pdu_tv2.msg, pdu_tv2.N_bytes);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// don't write original PDU
|
|
|
|
rlc1.write_pdu(pdu_tv0.msg, pdu_tv0.N_bytes);
|
|
|
|
rlc1.write_pdu(pdu_tv1.msg, pdu_tv1.N_bytes);
|
|
|
|
rlc1.write_pdu(pdu_tv2.msg, pdu_tv2.N_bytes);
|
|
|
|
|
|
|
|
// Check RLC re-assembled message header
|
|
|
|
TESTASSERT(spy.has_message("[Data PDU, RF=0, P=1, FI=1, SN=509, LSF=0, SO=0, N_li=2 (4, 10)]"));
|
|
|
|
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.close();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return SRSRAN_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
int header_reconstruction_test7(srsran::log_sink_message_spy& spy)
|
|
|
|
{
|
|
|
|
// Original PDU:
|
|
|
|
// 22:14:54.646530 [RLC_1] [I] DRB1 Tx PDU SN=282 (19 B)
|
|
|
|
// 0000: 9d 1a 80 10 0a 28 29 29 29 29 29 29 29 29 29 29
|
|
|
|
// 0010: 2a 2a 2a
|
|
|
|
// 22:14:54.646535 [RLC_1] [D] [Data PDU, RF=0, P=0, FI=1, SN=282, LSF=0, SO=0, N_li=2 (1, 10)]
|
|
|
|
|
|
|
|
// 22:14:54.648484 [RLC_1] [I] DRB1 Retx PDU segment SN=282 [so=2] (6 B) (attempt 2/16)
|
|
|
|
// 0000: f9 1a 00 02 29 29
|
|
|
|
// 22:14:54.648490 [RLC_2] [I] DRB1 Rx data PDU segment of SN=282 (2 B), SO=2, N_li=0
|
|
|
|
// 0000: 29 29
|
|
|
|
// 22:14:54.648495 [RLC_2] [D] [Data PDU, RF=1, P=1, FI=1, SN=282, LSF=0, SO=2, N_li=0]
|
|
|
|
std::array<uint8_t, 6> tv0 = {0xf9, 0x1a, 0x00, 0x02, 0x29, 0x29};
|
|
|
|
|
|
|
|
// 22:14:54.648576 [RLC_1] [I] DRB1 Retx PDU segment SN=282 [so=4] (11 B) (attempt 2/16)
|
|
|
|
// 0000: d1 1a 00 04 29 29 29 29 29 29 29
|
|
|
|
// 22:14:54.648583 [RLC_2] [I] DRB1 Rx data PDU segment of SN=282 (7 B), SO=4, N_li=0
|
|
|
|
// 0000: 29 29 29 29 29 29 29
|
|
|
|
// 22:14:54.648588 [RLC_2] [D] [Data PDU, RF=1, P=0, FI=1, SN=282, LSF=0, SO=4, N_li=0]
|
|
|
|
std::array<uint8_t, 11> tv1 = {0xd1, 0x1a, 0x00, 0x04, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29, 0x29};
|
|
|
|
|
|
|
|
// 22:14:54.648701 [RLC_1] [I] DRB1 Retx PDU segment SN=282 [so=11] (7 B) (attempt 2/16)
|
|
|
|
// 0000: d9 1a 80 0b 2a 2a 2a
|
|
|
|
// 22:14:54.648707 [RLC_2] [I] DRB1 Rx data PDU segment of SN=282 (3 B), SO=11, N_li=0
|
|
|
|
// 0000: 2a 2a 2a
|
|
|
|
// 22:14:54.648713 [RLC_2] [D] [Data PDU, RF=1, P=0, FI=1, SN=282, LSF=1, SO=11, N_li=0]
|
|
|
|
std::array<uint8_t, 7> tv2 = {0xd9, 0x1a, 0x80, 0x0b, 0x2a, 0x2a, 0x2a};
|
|
|
|
|
|
|
|
// 22:14:54.648860 [RLC_1] [I] DRB1 Retx PDU segment SN=282 [so=0] (5 B) (attempt 3/16)
|
|
|
|
// 0000: d1 1a 00 00 28
|
|
|
|
// 22:14:54.648866 [RLC_2] [I] DRB1 Rx data PDU segment of SN=282 (1 B), SO=0, N_li=0
|
|
|
|
// 0000: 28
|
|
|
|
// 22:14:54.648871 [RLC_2] [D] [Data PDU, RF=1, P=0, FI=1, SN=282, LSF=0, SO=0, N_li=0]
|
|
|
|
std::array<uint8_t, 5> tv3 = {0xd1, 0x1a, 0x00, 0x00, 0x28};
|
|
|
|
|
|
|
|
// 22:14:54.648948 [RLC_1] [I] DRB1 Retx PDU segment SN=282 [so=1] (8 B) (attempt 3/16)
|
|
|
|
// 0000: c9 1a 00 01 29 29 29 29
|
|
|
|
// 22:14:54.648957 [RLC_2] [I] DRB1 Rx data PDU segment of SN=282 (4 B), SO=1, N_li=0
|
|
|
|
// 0000: 29 29 29 29
|
|
|
|
// 22:14:54.648962 [RLC_2] [D] [Data PDU, RF=1, P=0, FI=1, SN=282, LSF=0, SO=1, N_li=0]
|
|
|
|
std::array<uint8_t, 8> tv4 = {0xc9, 0x1a, 0x00, 0x01, 0x29, 0x29, 0x29, 0x29};
|
|
|
|
|
|
|
|
byte_buffer_t pdu_tv0;
|
|
|
|
memcpy(pdu_tv0.msg, tv0.data(), tv0.size());
|
|
|
|
pdu_tv0.N_bytes = tv0.size();
|
|
|
|
|
|
|
|
byte_buffer_t pdu_tv1;
|
|
|
|
memcpy(pdu_tv1.msg, tv1.data(), tv1.size());
|
|
|
|
pdu_tv1.N_bytes = tv1.size();
|
|
|
|
|
|
|
|
byte_buffer_t pdu_tv2;
|
|
|
|
memcpy(pdu_tv2.msg, tv2.data(), tv2.size());
|
|
|
|
pdu_tv2.N_bytes = tv2.size();
|
|
|
|
|
|
|
|
byte_buffer_t pdu_tv3;
|
|
|
|
memcpy(pdu_tv3.msg, tv3.data(), tv3.size());
|
|
|
|
pdu_tv3.N_bytes = tv3.size();
|
|
|
|
|
|
|
|
byte_buffer_t pdu_tv4;
|
|
|
|
memcpy(pdu_tv4.msg, tv4.data(), tv4.size());
|
|
|
|
pdu_tv4.N_bytes = tv4.size();
|
|
|
|
|
|
|
|
#if HAVE_PCAP
|
|
|
|
rlc_pcap pcap;
|
|
|
|
pcap.open("rlc_am_header_reconstruction_test7.pcap", rlc_config_t::default_rlc_am_config());
|
|
|
|
rlc_am_tester tester(&pcap);
|
|
|
|
#else
|
|
|
|
rlc_am_tester tester(NULL);
|
|
|
|
#endif
|
|
|
|
srsran::timer_handler timers(8);
|
|
|
|
|
|
|
|
// configure RLC
|
|
|
|
rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers);
|
|
|
|
if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.write_dl_ccch(pdu_tv0.msg, pdu_tv0.N_bytes);
|
|
|
|
pcap.write_dl_ccch(pdu_tv1.msg, pdu_tv1.N_bytes);
|
|
|
|
pcap.write_dl_ccch(pdu_tv2.msg, pdu_tv2.N_bytes);
|
|
|
|
pcap.write_dl_ccch(pdu_tv3.msg, pdu_tv3.N_bytes);
|
|
|
|
pcap.write_dl_ccch(pdu_tv4.msg, pdu_tv4.N_bytes);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// don't write original PDU
|
|
|
|
rlc1.write_pdu(pdu_tv0.msg, pdu_tv0.N_bytes);
|
|
|
|
rlc1.write_pdu(pdu_tv1.msg, pdu_tv1.N_bytes);
|
|
|
|
rlc1.write_pdu(pdu_tv2.msg, pdu_tv2.N_bytes);
|
|
|
|
rlc1.write_pdu(pdu_tv3.msg, pdu_tv3.N_bytes);
|
|
|
|
rlc1.write_pdu(pdu_tv4.msg, pdu_tv4.N_bytes);
|
|
|
|
|
|
|
|
// Check RLC re-assembled message header
|
|
|
|
TESTASSERT(spy.has_message("[Data PDU, RF=0, P=0, FI=1, SN=282, LSF=0, SO=0, N_li=2 (1, 10)]"));
|
|
|
|
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.close();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return SRSRAN_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
int header_reconstruction_test8(srsran::log_sink_message_spy& spy)
|
|
|
|
{
|
|
|
|
// Original PDU:
|
|
|
|
// 21:23:34.407718 [RLC_1] [I] DRB1 Tx PDU SN=423 (40 B)
|
|
|
|
// 0000: b5 a7 80 38 0a 00 a0 77 77 77 78 78 78 78 78 78
|
|
|
|
// 0010: 78 78 78 78 79 79 79 79 79 79 79 79 79 79 7a 7a
|
|
|
|
// 0020: 7a 7a 7a 7a 7a 7a 7a 7a
|
|
|
|
// 21:23:34.407724 [RLC_1] [D] [Data PDU, RF=0, P=1, FI=1, SN=423, LSF=0, SO=0, N_li=3 (3, 10, 10)]
|
|
|
|
|
|
|
|
// 21:23:34.408815 [RLC_1] [I] DRB1 Retx PDU segment SN=423 [so=0] (18 B) (attempt 2/8)
|
|
|
|
// 0000: fd a7 00 00 00 30 77 77 77 78 78 78 78 78 78 78
|
|
|
|
// 0010: 78 78
|
|
|
|
// 21:23:34.408822 [RLC_2] [I] DRB1 Rx data PDU segment of SN=423 (12 B), SO=0, N_li=1
|
|
|
|
// 0000: 77 77 77 78 78 78 78 78 78 78 78 78
|
|
|
|
// 21:23:34.408828 [RLC_2] [D] [Data PDU, RF=1, P=1, FI=1, SN=423, LSF=0, SO=0, N_li=1 (3)]
|
|
|
|
std::array<uint8_t, 18> tv0 = {
|
|
|
|
0xfd, 0xa7, 0x00, 0x00, 0x00, 0x30, 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78};
|
|
|
|
|
|
|
|
// 21:23:34.408913 [RLC_1] [I] DRB1 Retx PDU segment SN=423 [so=12] (17 B) (attempt 2/8)
|
|
|
|
// 0000: f5 a7 00 0c 00 10 78 79 79 79 79 79 79 79 79 79
|
|
|
|
// 0010: 79
|
|
|
|
// 21:23:34.408919 [RLC_2] [I] DRB1 Rx data PDU segment of SN=423 (11 B), SO=12, N_li=1
|
|
|
|
// 0000: 78 79 79 79 79 79 79 79 79 79 79
|
|
|
|
// 21:23:34.408925 [RLC_2] [D] [Data PDU, RF=1, P=1, FI=1, SN=423, LSF=0, SO=12, N_li=1 (1)]
|
|
|
|
std::array<uint8_t, 17> tv1 = {
|
|
|
|
0xf5, 0xa7, 0x00, 0x0c, 0x00, 0x10, 0x78, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79};
|
|
|
|
|
|
|
|
// 21:23:34.409421 [RLC_1] [I] DRB1 Retx PDU segment SN=423 [so=0] (19 B) (attempt 3/8)
|
|
|
|
// 0000: f5 a7 00 00 00 30 77 77 77 78 78 78 78 78 78 78
|
|
|
|
// 0010: 78 78 78
|
|
|
|
// 21:23:34.409433 [RLC_2] [I] DRB1 Rx data PDU segment of SN=423 (13 B), SO=0, N_li=1
|
|
|
|
// 0000: 77 77 77 78 78 78 78 78 78 78 78 78 78
|
|
|
|
// 21:23:34.409440 [RLC_2] [D] [Data PDU, RF=1, P=1, FI=1, SN=423, LSF=0, SO=0, N_li=1 (3)]
|
|
|
|
std::array<uint8_t, 19> tv2 = {
|
|
|
|
0xf5, 0xa7, 0x00, 0x00, 0x00, 0x30, 0x77, 0x77, 0x77, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78};
|
|
|
|
|
|
|
|
// 21:23:34.409524 [RLC_1] [I] DRB1 Retx PDU segment SN=423 [so=13] (26 B) (attempt 3/8)
|
|
|
|
// 0000: e5 a7 80 0d 00 a0 79 79 79 79 79 79 79 79 79 79
|
|
|
|
// 0010: 7a 7a 7a 7a 7a 7a 7a 7a 7a 7a
|
|
|
|
// 21:23:34.409531 [RLC_2] [I] DRB1 Rx data PDU segment of SN=423 (20 B), SO=13, N_li=1
|
|
|
|
// 0000: 79 79 79 79 79 79 79 79 79 79 7a 7a 7a 7a 7a 7a
|
|
|
|
// 0010: 7a 7a 7a 7a
|
|
|
|
// 21:23:34.409537 [RLC_2] [D] [Data PDU, RF=1, P=1, FI=0, SN=423, LSF=1, SO=13, N_li=1 (10)]
|
|
|
|
std::array<uint8_t, 26> tv3 = {0xe5, 0xa7, 0x80, 0x0d, 0x00, 0xa0, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79, 0x79,
|
|
|
|
0x79, 0x79, 0x79, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a, 0x7a};
|
|
|
|
|
|
|
|
byte_buffer_t pdu_tv0;
|
|
|
|
memcpy(pdu_tv0.msg, tv0.data(), tv0.size());
|
|
|
|
pdu_tv0.N_bytes = tv0.size();
|
|
|
|
|
|
|
|
byte_buffer_t pdu_tv1;
|
|
|
|
memcpy(pdu_tv1.msg, tv1.data(), tv1.size());
|
|
|
|
pdu_tv1.N_bytes = tv1.size();
|
|
|
|
|
|
|
|
byte_buffer_t pdu_tv2;
|
|
|
|
memcpy(pdu_tv2.msg, tv2.data(), tv2.size());
|
|
|
|
pdu_tv2.N_bytes = tv2.size();
|
|
|
|
|
|
|
|
byte_buffer_t pdu_tv3;
|
|
|
|
memcpy(pdu_tv3.msg, tv3.data(), tv3.size());
|
|
|
|
pdu_tv3.N_bytes = tv3.size();
|
|
|
|
|
|
|
|
#if HAVE_PCAP
|
|
|
|
rlc_pcap pcap;
|
|
|
|
pcap.open("rlc_am_header_reconstruction_test8.pcap", rlc_config_t::default_rlc_am_config());
|
|
|
|
rlc_am_tester tester(&pcap);
|
|
|
|
#else
|
|
|
|
rlc_am_tester tester(NULL);
|
|
|
|
#endif
|
|
|
|
srsran::timer_handler timers(8);
|
|
|
|
|
|
|
|
// configure RLC
|
|
|
|
rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers);
|
|
|
|
if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.write_dl_ccch(pdu_tv0.msg, pdu_tv0.N_bytes);
|
|
|
|
pcap.write_dl_ccch(pdu_tv1.msg, pdu_tv1.N_bytes);
|
|
|
|
pcap.write_dl_ccch(pdu_tv2.msg, pdu_tv2.N_bytes);
|
|
|
|
pcap.write_dl_ccch(pdu_tv3.msg, pdu_tv3.N_bytes);
|
|
|
|
pcap.close();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// don't write original PDU
|
|
|
|
rlc1.write_pdu(pdu_tv0.msg, pdu_tv0.N_bytes);
|
|
|
|
rlc1.write_pdu(pdu_tv1.msg, pdu_tv1.N_bytes);
|
|
|
|
rlc1.write_pdu(pdu_tv2.msg, pdu_tv2.N_bytes);
|
|
|
|
rlc1.write_pdu(pdu_tv3.msg, pdu_tv3.N_bytes);
|
|
|
|
|
|
|
|
// Check RLC re-assembled message header
|
|
|
|
TESTASSERT(spy.has_message("[Data PDU, RF=0, P=1, FI=1, SN=423, LSF=0, SO=0, N_li=3 (3, 10, 10)]"));
|
|
|
|
|
|
|
|
return SRSRAN_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool reset_test()
|
|
|
|
{
|
|
|
|
rlc_am_tester tester;
|
|
|
|
srsran::timer_handler timers(8);
|
|
|
|
int len = 0;
|
|
|
|
|
|
|
|
rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers);
|
|
|
|
|
|
|
|
if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Push 1 SDU of size 10 into RLC1
|
|
|
|
unique_byte_buffer_t sdu_buf = srsran::make_byte_buffer();
|
|
|
|
sdu_buf->N_bytes = 100;
|
|
|
|
std::fill(sdu_buf->msg, sdu_buf->msg + sdu_buf->N_bytes, 0);
|
|
|
|
sdu_buf->msg[0] = 1; // Write the index into the buffer
|
|
|
|
rlc1.write_sdu(std::move(sdu_buf));
|
|
|
|
|
|
|
|
// read 1 PDU from RLC1 and force segmentation
|
|
|
|
byte_buffer_t pdu_bufs;
|
|
|
|
len = rlc1.read_pdu(pdu_bufs.msg, 4);
|
|
|
|
pdu_bufs.N_bytes = len;
|
|
|
|
|
|
|
|
// reset RLC1
|
|
|
|
rlc1.stop();
|
|
|
|
|
|
|
|
// read another PDU segment from RLC1
|
|
|
|
len = rlc1.read_pdu(pdu_bufs.msg, 4);
|
|
|
|
pdu_bufs.N_bytes = len;
|
|
|
|
|
|
|
|
// now empty RLC buffer
|
|
|
|
len = rlc1.read_pdu(pdu_bufs.msg, 100);
|
|
|
|
pdu_bufs.N_bytes = len;
|
|
|
|
|
|
|
|
if (0 != rlc1.get_buffer_state()) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool resume_test()
|
|
|
|
{
|
|
|
|
rlc_am_tester tester;
|
|
|
|
srsran::timer_handler timers(8);
|
|
|
|
int len = 0;
|
|
|
|
|
|
|
|
rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers);
|
|
|
|
|
|
|
|
if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Push 1 SDU of size 10 into RLC1
|
|
|
|
unique_byte_buffer_t sdu_buf = srsran::make_byte_buffer();
|
|
|
|
sdu_buf->N_bytes = 100;
|
|
|
|
std::fill(sdu_buf->msg, sdu_buf->msg + sdu_buf->N_bytes, 0);
|
|
|
|
sdu_buf->msg[0] = 1; // Write the index into the buffer
|
|
|
|
rlc1.write_sdu(std::move(sdu_buf));
|
|
|
|
|
|
|
|
// read 1 PDU from RLC1 and force segmentation
|
|
|
|
byte_buffer_t pdu_bufs;
|
|
|
|
len = rlc1.read_pdu(pdu_bufs.msg, 4);
|
|
|
|
pdu_bufs.N_bytes = len;
|
|
|
|
|
|
|
|
// reestablish RLC1
|
|
|
|
rlc1.reestablish();
|
|
|
|
|
|
|
|
// resume RLC1
|
|
|
|
rlc1.resume();
|
|
|
|
|
|
|
|
// Buffer should be zero
|
|
|
|
if (0 != rlc1.get_buffer_state()) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Do basic test
|
|
|
|
byte_buffer_t pdu_bufs_tx[NBUFS];
|
|
|
|
basic_test_tx(&rlc1, pdu_bufs_tx);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool stop_test()
|
|
|
|
{
|
|
|
|
rlc_am_tester tester;
|
|
|
|
srsran::timer_handler timers(8);
|
|
|
|
|
|
|
|
rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers);
|
|
|
|
|
|
|
|
if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// start thread reading
|
|
|
|
ul_writer writer(&rlc1);
|
|
|
|
writer.start(-2);
|
|
|
|
|
|
|
|
// let writer thread block on tx_queue
|
|
|
|
usleep(1e6);
|
|
|
|
|
|
|
|
// stop RLC1
|
|
|
|
rlc1.stop();
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This test checks if status PDUs are generated even though the grant size may not
|
|
|
|
// be enough to fit all SNs that would need to be NACKed
|
|
|
|
bool status_pdu_test()
|
|
|
|
{
|
|
|
|
rlc_am_tester tester;
|
|
|
|
srsran::timer_handler timers(8);
|
|
|
|
int len = 0;
|
|
|
|
|
|
|
|
rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers);
|
|
|
|
rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers);
|
|
|
|
|
|
|
|
if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (not rlc2.configure(rlc_config_t::default_rlc_am_config())) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Push 5 SDUs into RLC1
|
|
|
|
const uint32_t n_sdus = 10;
|
|
|
|
unique_byte_buffer_t sdu_bufs[n_sdus];
|
|
|
|
for (uint32_t i = 0; i < n_sdus; i++) {
|
|
|
|
sdu_bufs[i] = srsran::make_byte_buffer();
|
|
|
|
sdu_bufs[i]->N_bytes = 1; // Give each buffer a size of 1 byte
|
|
|
|
sdu_bufs[i]->msg[0] = i; // Write the index into the buffer
|
|
|
|
rlc1.write_sdu(std::move(sdu_bufs[i]));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read 5 PDUs from RLC1 (1 byte each)
|
|
|
|
const uint32_t n_pdus = n_sdus;
|
|
|
|
byte_buffer_t pdu_bufs[n_pdus];
|
|
|
|
for (uint32_t i = 0; i < n_pdus; i++) {
|
|
|
|
len = rlc1.read_pdu(pdu_bufs[i].msg, 3); // 2 byte header + 1 byte payload
|
|
|
|
pdu_bufs[i].N_bytes = len;
|
|
|
|
}
|
|
|
|
|
|
|
|
TESTASSERT(0 == rlc1.get_buffer_state());
|
|
|
|
|
|
|
|
// Only pass 1st and last PDUs to RLC2
|
|
|
|
for (uint32_t i = 0; i < n_pdus; ++i) {
|
|
|
|
if (i == 0 || i == n_pdus - 1) {
|
|
|
|
rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Step timers until reordering timeout expires
|
|
|
|
int cnt = 5;
|
|
|
|
while (cnt--) {
|
|
|
|
timers.step_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t buffer_state = rlc2.get_buffer_state();
|
|
|
|
|
|
|
|
// Read status PDU from RLC2
|
|
|
|
byte_buffer_t status_buf;
|
|
|
|
len = rlc2.read_pdu(status_buf.msg, 5); // provide only small grant
|
|
|
|
status_buf.N_bytes = len;
|
|
|
|
|
|
|
|
// check status PDU doesn't contain ACK_SN in NACK list
|
|
|
|
rlc_status_pdu_t status_pdu = {};
|
|
|
|
rlc_am_read_status_pdu(status_buf.msg, status_buf.N_bytes, &status_pdu);
|
|
|
|
TESTASSERT(rlc_am_is_valid_status_pdu(status_pdu));
|
|
|
|
|
|
|
|
// Write status PDU to RLC1
|
|
|
|
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
|
|
|
|
|
|
|
|
// Read the retx PDU from RLC1
|
|
|
|
byte_buffer_t retx;
|
|
|
|
len = rlc1.read_pdu(retx.msg, 10);
|
|
|
|
retx.N_bytes = len;
|
|
|
|
|
|
|
|
// Write the retx PDU to RLC2
|
|
|
|
rlc2.write_pdu(retx.msg, retx.N_bytes);
|
|
|
|
|
|
|
|
// Step timers until reordering timeout expires
|
|
|
|
cnt = 5;
|
|
|
|
while (cnt--) {
|
|
|
|
timers.step_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
// get buffer state and status PDU again
|
|
|
|
status_buf.clear();
|
|
|
|
len = rlc2.read_pdu(status_buf.msg, 20); // big enough grant to fit full status PDU
|
|
|
|
status_buf.N_bytes = len;
|
|
|
|
TESTASSERT(status_buf.N_bytes != 0);
|
|
|
|
|
|
|
|
// Write status PDU to RLC1
|
|
|
|
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
|
|
|
|
|
|
|
|
// retransmission of remaining PDUs
|
|
|
|
for (int i = 0; i < 10; i++) {
|
|
|
|
retx.clear();
|
|
|
|
len = rlc1.read_pdu(retx.msg, 3);
|
|
|
|
retx.N_bytes = len;
|
|
|
|
|
|
|
|
// Write the retx PDU to RLC2
|
|
|
|
rlc2.write_pdu(retx.msg, retx.N_bytes);
|
|
|
|
}
|
|
|
|
|
|
|
|
TESTASSERT(tester.sdus.size() == n_sdus);
|
|
|
|
for (uint32_t i = 0; i < tester.sdus.size(); i++) {
|
|
|
|
TESTASSERT(tester.sdus[i]->N_bytes == 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return SRSRAN_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This test checks the correct handling of a sending RLC entity when an incorrect status PDU is injected.
|
|
|
|
// In this test, the receiver requests the retransmission of a SN that he has acknowledeged before.
|
|
|
|
// The incidence is reported to the upper layers.
|
|
|
|
bool incorrect_status_pdu_test()
|
|
|
|
{
|
|
|
|
rlc_am_tester tester;
|
|
|
|
srsran::timer_handler timers(8);
|
|
|
|
int len = 0;
|
|
|
|
|
|
|
|
rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers);
|
|
|
|
rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers);
|
|
|
|
|
|
|
|
if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Push 5 SDUs into RLC1
|
|
|
|
const uint32_t n_sdus = 10;
|
|
|
|
unique_byte_buffer_t sdu_bufs[n_sdus];
|
|
|
|
for (uint32_t i = 0; i < n_sdus; i++) {
|
|
|
|
sdu_bufs[i] = srsran::make_byte_buffer();
|
|
|
|
sdu_bufs[i]->N_bytes = 1; // Give each buffer a size of 1 byte
|
|
|
|
sdu_bufs[i]->msg[0] = i; // Write the index into the buffer
|
|
|
|
rlc1.write_sdu(std::move(sdu_bufs[i]));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read 5 PDUs from RLC1 (1 byte each)
|
|
|
|
const uint32_t n_pdus = n_sdus;
|
|
|
|
byte_buffer_t pdu_bufs[n_pdus];
|
|
|
|
for (uint32_t i = 0; i < n_pdus; i++) {
|
|
|
|
len = rlc1.read_pdu(pdu_bufs[i].msg, 3); // 2 byte header + 1 byte payload
|
|
|
|
pdu_bufs[i].N_bytes = len;
|
|
|
|
}
|
|
|
|
|
|
|
|
TESTASSERT(0 == rlc1.get_buffer_state());
|
|
|
|
|
|
|
|
// Construct a status PDU that ACKs SN 1
|
|
|
|
rlc_status_pdu_t status_pdu = {};
|
|
|
|
status_pdu.ack_sn = 4;
|
|
|
|
status_pdu.N_nack = 3;
|
|
|
|
status_pdu.nacks[0].nack_sn = 0;
|
|
|
|
status_pdu.nacks[1].nack_sn = 2;
|
|
|
|
TESTASSERT(rlc_am_is_valid_status_pdu(status_pdu));
|
|
|
|
|
|
|
|
// pack PDU and write to RLC
|
|
|
|
byte_buffer_t status_buf;
|
|
|
|
rlc_am_write_status_pdu(&status_pdu, &status_buf);
|
|
|
|
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
|
|
|
|
|
|
|
|
// This will remove SN=1 from the Tx window
|
|
|
|
|
|
|
|
TESTASSERT(tester.protocol_failure_triggered == false);
|
|
|
|
|
|
|
|
// construct a valid but conflicting status PDU that request SN=1 for retx
|
|
|
|
status_pdu.N_nack = 1;
|
|
|
|
status_pdu.nacks[0].nack_sn = 1;
|
|
|
|
TESTASSERT(rlc_am_is_valid_status_pdu(status_pdu));
|
|
|
|
|
|
|
|
// pack and write to RLC again
|
|
|
|
rlc_am_write_status_pdu(&status_pdu, &status_buf);
|
|
|
|
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
|
|
|
|
|
|
|
|
TESTASSERT(tester.protocol_failure_triggered == true);
|
|
|
|
return SRSRAN_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// The test checks the correct detection of an out-of-order status PDUs
|
|
|
|
/// In contrast to the without explicitly NACK-ing specific SNs
|
|
|
|
bool incorrect_status_pdu_test2()
|
|
|
|
{
|
|
|
|
rlc_am_tester tester;
|
|
|
|
srsran::timer_handler timers(8);
|
|
|
|
int len = 0;
|
|
|
|
|
|
|
|
rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers);
|
|
|
|
rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers);
|
|
|
|
|
|
|
|
if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
if (not rlc2.configure(rlc_config_t::default_rlc_am_config())) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Push 10 SDUs into RLC1
|
|
|
|
const uint32_t n_sdus = 10;
|
|
|
|
unique_byte_buffer_t sdu_bufs[n_sdus];
|
|
|
|
for (uint32_t i = 0; i < n_sdus; i++) {
|
|
|
|
sdu_bufs[i] = srsran::make_byte_buffer();
|
|
|
|
sdu_bufs[i]->N_bytes = 1; // Give each buffer a size of 1 byte
|
|
|
|
sdu_bufs[i]->msg[0] = i; // Write the index into the buffer
|
|
|
|
rlc1.write_sdu(std::move(sdu_bufs[i]));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Read 10 PDUs from RLC1 (1 byte each) and push half of them to RLC2
|
|
|
|
const uint32_t n_pdus = n_sdus;
|
|
|
|
byte_buffer_t pdu_bufs[n_pdus];
|
|
|
|
for (uint32_t i = 0; i < n_pdus; i++) {
|
|
|
|
len = rlc1.read_pdu(pdu_bufs[i].msg, 3); // 2 byte header + 1 byte payload
|
|
|
|
pdu_bufs[i].N_bytes = len;
|
|
|
|
if (i < 5) {
|
|
|
|
rlc2.write_pdu(pdu_bufs[i].msg, pdu_bufs[i].N_bytes);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TESTASSERT(0 == rlc1.get_buffer_state());
|
|
|
|
|
|
|
|
// Construct a status PDU that ACKs all SNs
|
|
|
|
rlc_status_pdu_t status_pdu = {};
|
|
|
|
status_pdu.ack_sn = 5;
|
|
|
|
status_pdu.N_nack = 0;
|
|
|
|
TESTASSERT(rlc_am_is_valid_status_pdu(status_pdu));
|
|
|
|
|
|
|
|
// pack PDU and write to RLC
|
|
|
|
byte_buffer_t status_buf;
|
|
|
|
rlc_am_write_status_pdu(&status_pdu, &status_buf);
|
|
|
|
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
|
|
|
|
|
|
|
|
TESTASSERT(tester.protocol_failure_triggered == false);
|
|
|
|
|
|
|
|
// construct a valid but conflicting status PDU that acks a lower SN and requests SN=1 for retx
|
|
|
|
status_pdu.ack_sn = 3;
|
|
|
|
status_pdu.N_nack = 1;
|
|
|
|
status_pdu.nacks[0].nack_sn = 1;
|
|
|
|
TESTASSERT(rlc_am_is_valid_status_pdu(status_pdu));
|
|
|
|
|
|
|
|
// pack and write to RLC again
|
|
|
|
rlc_am_write_status_pdu(&status_pdu, &status_buf);
|
|
|
|
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
|
|
|
|
|
|
|
|
// the PDU should be dropped
|
|
|
|
|
|
|
|
// resend first Status PDU again
|
|
|
|
status_pdu.ack_sn = 5;
|
|
|
|
status_pdu.N_nack = 0;
|
|
|
|
TESTASSERT(rlc_am_is_valid_status_pdu(status_pdu));
|
|
|
|
|
|
|
|
// pack and write to RLC again
|
|
|
|
rlc_am_write_status_pdu(&status_pdu, &status_buf);
|
|
|
|
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
|
|
|
|
|
|
|
|
// Step timers until reordering timeout expires
|
|
|
|
int cnt = 5;
|
|
|
|
while (cnt--) {
|
|
|
|
timers.step_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
// retransmit all outstanding PDUs
|
|
|
|
for (int i = 0; i < 5; i++) {
|
|
|
|
byte_buffer_t retx;
|
|
|
|
retx.N_bytes = rlc1.read_pdu(retx.msg, 3);
|
|
|
|
rlc2.write_pdu(retx.msg, retx.N_bytes);
|
|
|
|
|
|
|
|
// Step timers until reordering timeout expires
|
|
|
|
int cnt = 5;
|
|
|
|
while (cnt--) {
|
|
|
|
timers.step_all();
|
|
|
|
}
|
|
|
|
|
|
|
|
// read status
|
|
|
|
byte_buffer_t status_buf;
|
|
|
|
status_buf.N_bytes = rlc2.read_pdu(status_buf.msg, 10);
|
|
|
|
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
|
|
|
|
}
|
|
|
|
|
|
|
|
TESTASSERT(tester.sdus.size() == n_sdus);
|
|
|
|
for (uint32_t i = 0; i < tester.sdus.size(); i++) {
|
|
|
|
TESTASSERT(tester.sdus[i]->N_bytes == 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return SRSRAN_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This test checks the correct functioning of RLC reestablishment
|
|
|
|
// after maxRetx attempt.
|
|
|
|
bool reestablish_test()
|
|
|
|
{
|
|
|
|
const rlc_config_t config = rlc_config_t::default_rlc_am_config();
|
|
|
|
#if HAVE_PCAP
|
|
|
|
rlc_pcap pcap;
|
|
|
|
pcap.open("rlc_am_reestablish_test.pcap", config);
|
|
|
|
rlc_am_tester tester(&pcap);
|
|
|
|
#else
|
|
|
|
rlc_am_tester tester(NULL);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
srsran::timer_handler timers(8);
|
|
|
|
|
|
|
|
rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers);
|
|
|
|
rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers);
|
|
|
|
|
|
|
|
srslog::fetch_basic_logger("RLC_AM_1").set_hex_dump_max_size(100);
|
|
|
|
srslog::fetch_basic_logger("RLC_AM_2").set_hex_dump_max_size(100);
|
|
|
|
srslog::fetch_basic_logger("RLC").set_hex_dump_max_size(100);
|
|
|
|
|
|
|
|
if (not rlc1.configure(config)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (not rlc2.configure(config)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool reetablished_once = false;
|
|
|
|
|
|
|
|
// Generate 40 SDUs/PDUs
|
|
|
|
const uint32_t total_num_tx_pdus = config.am.max_retx_thresh * 10;
|
|
|
|
uint32_t num_tx_pdus = 0;
|
|
|
|
|
|
|
|
// Create a few SDUs and write to RLC1 to make sure buffers aren't empty after tx one PDU
|
|
|
|
for (uint32_t i = num_tx_pdus; i < 5; ++i) {
|
|
|
|
// Write SDU
|
|
|
|
unique_byte_buffer_t sdu = srsran::make_byte_buffer();
|
|
|
|
TESTASSERT(sdu != nullptr);
|
|
|
|
sdu->N_bytes = 5; // Give each buffer a size of 1 byte
|
|
|
|
for (uint32_t k = 0; k < sdu->N_bytes; ++k) {
|
|
|
|
sdu->msg[k] = i; // Write the index into the buffer
|
|
|
|
}
|
|
|
|
sdu->md.pdcp_sn = i;
|
|
|
|
rlc1.write_sdu(std::move(sdu));
|
|
|
|
}
|
|
|
|
|
|
|
|
for (uint32_t i = num_tx_pdus; i < total_num_tx_pdus; i++) {
|
|
|
|
// Write SDU
|
|
|
|
unique_byte_buffer_t sdu = srsran::make_byte_buffer();
|
|
|
|
TESTASSERT(sdu != nullptr);
|
|
|
|
sdu->N_bytes = 5; // Give each buffer a size of 1 byte
|
|
|
|
for (uint32_t k = 0; k < sdu->N_bytes; ++k) {
|
|
|
|
sdu->msg[k] = i; // Write the index into the buffer
|
|
|
|
}
|
|
|
|
sdu->md.pdcp_sn = i;
|
|
|
|
rlc1.write_sdu(std::move(sdu));
|
|
|
|
|
|
|
|
// Read PDU
|
|
|
|
unique_byte_buffer_t pdu = srsran::make_byte_buffer();
|
|
|
|
pdu->N_bytes = rlc1.read_pdu(pdu->msg, 7); // 2 byte header + 5 byte payload;
|
|
|
|
|
|
|
|
// Find SN=0 PDU
|
|
|
|
bool is_data_pdu_sn0 = false;
|
|
|
|
if (not rlc_am_is_control_pdu(pdu->msg)) {
|
|
|
|
// After reestablishment after maxretx, also SN=0 is delivered
|
|
|
|
if (not reetablished_once) {
|
|
|
|
rlc_amd_pdu_header_t header = {};
|
|
|
|
rlc_am_read_data_pdu_header(pdu.get(), &header);
|
|
|
|
if (header.sn == 0) {
|
|
|
|
is_data_pdu_sn0 = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Deliver all PDUs but SN=0 to RLC2
|
|
|
|
if (not is_data_pdu_sn0) {
|
|
|
|
rlc2.write_pdu(pdu->msg, pdu->N_bytes);
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.write_dl_ccch(pdu->msg, pdu->N_bytes);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check if RLC2 has something to send
|
|
|
|
if (rlc2.get_buffer_state() > 0) {
|
|
|
|
byte_buffer_t status_buf;
|
|
|
|
status_buf.N_bytes = rlc2.read_pdu(status_buf.msg, 5); // provide only small grant
|
|
|
|
TESTASSERT(status_buf.N_bytes != 0);
|
|
|
|
|
|
|
|
// Write status PDU to RLC1
|
|
|
|
rlc1.write_pdu(status_buf.msg, status_buf.N_bytes);
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.write_ul_ccch(status_buf.msg, status_buf.N_bytes);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
// each interation is one TTI
|
|
|
|
timers.step_all();
|
|
|
|
|
|
|
|
// Reestablish if max retx have been reached
|
|
|
|
if (tester.max_retx_triggered and !reetablished_once) {
|
|
|
|
rlc1.reestablish();
|
|
|
|
rlc2.reestablish();
|
|
|
|
// make sure we only reesablish once
|
|
|
|
reetablished_once = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
TESTASSERT(tester.sdus.size() == 17);
|
|
|
|
|
|
|
|
srslog::fetch_basic_logger("TEST").info("Received %zd SDUs", tester.sdus.size());
|
|
|
|
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.close();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return SRSRAN_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
// This test checks the correct functioning of RLC discard functionality
|
|
|
|
bool discard_test()
|
|
|
|
{
|
|
|
|
const rlc_config_t config = rlc_config_t::default_rlc_am_config();
|
|
|
|
#if HAVE_PCAP
|
|
|
|
rlc_pcap pcap;
|
|
|
|
pcap.open("rlc_am_reestablish_test.pcap", config);
|
|
|
|
rlc_am_tester tester(&pcap);
|
|
|
|
#else
|
|
|
|
rlc_am_tester tester(NULL);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
srsran::timer_handler timers(8);
|
|
|
|
|
|
|
|
rlc_am_lte rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers);
|
|
|
|
rlc_am_lte rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers);
|
|
|
|
|
|
|
|
srslog::fetch_basic_logger("RLC_AM_1").set_hex_dump_max_size(100);
|
|
|
|
srslog::fetch_basic_logger("RLC_AM_2").set_hex_dump_max_size(100);
|
|
|
|
srslog::fetch_basic_logger("RLC").set_hex_dump_max_size(100);
|
|
|
|
|
|
|
|
if (not rlc1.configure(config)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (not rlc2.configure(config)) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check has_data() after a SDU discard
|
|
|
|
{
|
|
|
|
uint32_t num_tx_pdus = 1;
|
|
|
|
for (uint32_t i = 0; i < num_tx_pdus; ++i) {
|
|
|
|
// Write SDU
|
|
|
|
unique_byte_buffer_t sdu = srsran::make_byte_buffer();
|
|
|
|
TESTASSERT(sdu != nullptr);
|
|
|
|
sdu->N_bytes = 5;
|
|
|
|
for (uint32_t k = 0; k < sdu->N_bytes; ++k) {
|
|
|
|
sdu->msg[k] = i; // Write the index into the buffer
|
|
|
|
}
|
|
|
|
sdu->md.pdcp_sn = i;
|
|
|
|
rlc1.write_sdu(std::move(sdu));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
rlc1.discard_sdu(0); // Try to discard PDCP_SN=1
|
|
|
|
TESTASSERT(rlc1.has_data() == false);
|
|
|
|
|
|
|
|
// Discard an SDU in the midle of the queue and read PDUs after
|
|
|
|
{
|
|
|
|
uint32_t num_tx_pdus = 10;
|
|
|
|
for (uint32_t i = 0; i < num_tx_pdus; ++i) {
|
|
|
|
// Write SDU
|
|
|
|
unique_byte_buffer_t sdu = srsran::make_byte_buffer();
|
|
|
|
TESTASSERT(sdu != nullptr);
|
|
|
|
sdu->N_bytes = 1;
|
|
|
|
for (uint32_t k = 0; k < sdu->N_bytes; ++k) {
|
|
|
|
sdu->msg[k] = i; // Write the index into the buffer
|
|
|
|
}
|
|
|
|
sdu->md.pdcp_sn = i;
|
|
|
|
rlc1.write_sdu(std::move(sdu));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
rlc1.discard_sdu(3); // Try to discard PDCP_SN=1
|
|
|
|
TESTASSERT(rlc1.has_data() == true);
|
|
|
|
TESTASSERT(rlc1.get_buffer_state() == 23); // 2 bytes fixed header, 12 , 9 bytes of data,
|
|
|
|
|
|
|
|
unique_byte_buffer_t pdu = srsran::make_byte_buffer();
|
|
|
|
uint32_t len = rlc1.read_pdu(pdu->msg, 50); // enough for all PDUs
|
|
|
|
pdu->N_bytes = len;
|
|
|
|
TESTASSERT(23 == len);
|
|
|
|
|
|
|
|
srslog::fetch_basic_logger("TEST").info("Received %zd SDUs", tester.sdus.size());
|
|
|
|
|
|
|
|
#if HAVE_PCAP
|
|
|
|
pcap.close();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return SRSRAN_SUCCESS;
|
|
|
|
}
|
|
|
|
int main(int argc, char** argv)
|
|
|
|
{
|
|
|
|
// Setup the log message spy to intercept error and warning log entries from RLC
|
|
|
|
if (!srslog::install_custom_sink(srsran::log_sink_message_spy::name(),
|
|
|
|
std::unique_ptr<srsran::log_sink_message_spy>(
|
|
|
|
new srsran::log_sink_message_spy(srslog::get_default_log_formatter())))) {
|
|
|
|
return SRSRAN_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto* spy = static_cast<srsran::log_sink_message_spy*>(srslog::find_sink(srsran::log_sink_message_spy::name()));
|
|
|
|
if (!spy) {
|
|
|
|
return SRSRAN_ERROR;
|
|
|
|
}
|
|
|
|
srslog::set_default_sink(*spy);
|
|
|
|
|
|
|
|
auto& logger_rrc1 = srslog::fetch_basic_logger("RLC_AM_1", *spy, false);
|
|
|
|
auto& logger_rrc2 = srslog::fetch_basic_logger("RLC_AM_2", *spy, false);
|
|
|
|
logger_rrc1.set_hex_dump_max_size(100);
|
|
|
|
logger_rrc2.set_hex_dump_max_size(100);
|
|
|
|
logger_rrc1.set_level(srslog::basic_levels::debug);
|
|
|
|
logger_rrc2.set_level(srslog::basic_levels::debug);
|
|
|
|
|
|
|
|
// start log backend
|
|
|
|
srslog::init();
|
|
|
|
|
|
|
|
if (basic_test()) {
|
|
|
|
printf("basic_test failed\n");
|
|
|
|
exit(-1);
|
|
|
|
};
|
|
|
|
|
|
|
|
if (concat_test()) {
|
|
|
|
printf("concat_test failed\n");
|
|
|
|
exit(-1);
|
|
|
|
};
|
|
|
|
|
|
|
|
if (segment_test(true)) {
|
|
|
|
printf("segment_test with in-order PDU reception failed\n");
|
|
|
|
exit(-1);
|
|
|
|
};
|
|
|
|
|
|
|
|
if (segment_test(false)) {
|
|
|
|
printf("segment_test with out-of-order PDU reception failed\n");
|
|
|
|
exit(-1);
|
|
|
|
};
|
|
|
|
|
|
|
|
if (retx_test()) {
|
|
|
|
printf("retx_test failed\n");
|
|
|
|
exit(-1);
|
|
|
|
};
|
|
|
|
|
|
|
|
if (max_retx_test()) {
|
|
|
|
printf("max_retx_test failed\n");
|
|
|
|
exit(-1);
|
|
|
|
};
|
|
|
|
|
|
|
|
if (reestablish_test()) {
|
|
|
|
printf("reestablish_test failed\n");
|
|
|
|
exit(-1);
|
|
|
|
};
|
|
|
|
|
|
|
|
if (segment_retx_test()) {
|
|
|
|
printf("segment_retx_test failed\n");
|
|
|
|
exit(-1);
|
|
|
|
};
|
|
|
|
|
|
|
|
if (resegment_test_1()) {
|
|
|
|
printf("resegment_test_1 failed\n");
|
|
|
|
exit(-1);
|
|
|
|
};
|
|
|
|
|
|
|
|
if (resegment_test_2()) {
|
|
|
|
printf("resegment_test_2 failed\n");
|
|
|
|
exit(-1);
|
|
|
|
};
|
|
|
|
|
|
|
|
if (resegment_test_3()) {
|
|
|
|
printf("resegment_test_3 failed\n");
|
|
|
|
exit(-1);
|
|
|
|
};
|
|
|
|
|
|
|
|
if (resegment_test_4()) {
|
|
|
|
printf("resegment_test_4 failed\n");
|
|
|
|
exit(-1);
|
|
|
|
};
|
|
|
|
|
|
|
|
if (resegment_test_5()) {
|
|
|
|
printf("resegment_test_5 failed\n");
|
|
|
|
exit(-1);
|
|
|
|
};
|
|
|
|
|
|
|
|
if (resegment_test_6()) {
|
|
|
|
printf("resegment_test_6 failed\n");
|
|
|
|
exit(-1);
|
|
|
|
};
|
|
|
|
|
|
|
|
logger_rrc1.set_hex_dump_max_size(100);
|
|
|
|
logger_rrc2.set_hex_dump_max_size(100);
|
|
|
|
if (resegment_test_7()) {
|
|
|
|
printf("resegment_test_7 failed\n");
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (resegment_test_8()) {
|
|
|
|
printf("resegment_test_8 failed\n");
|
|
|
|
exit(-1);
|
|
|
|
};
|
|
|
|
logger_rrc1.set_hex_dump_max_size(-1);
|
|
|
|
logger_rrc2.set_hex_dump_max_size(-1);
|
|
|
|
|
|
|
|
if (resegment_test_9()) {
|
|
|
|
printf("resegment_test_9 failed\n");
|
|
|
|
exit(-1);
|
|
|
|
};
|
|
|
|
|
|
|
|
if (resegment_test_10()) {
|
|
|
|
printf("resegment_test_10 failed\n");
|
|
|
|
exit(-1);
|
|
|
|
};
|
|
|
|
|
|
|
|
if (resegment_test_11()) {
|
|
|
|
printf("resegment_test_11 failed\n");
|
|
|
|
exit(-1);
|
|
|
|
};
|
|
|
|
|
|
|
|
if (resegment_test_12()) {
|
|
|
|
printf("resegment_test_12 failed\n");
|
|
|
|
exit(-1);
|
|
|
|
};
|
|
|
|
|
|
|
|
// Set of unique header reconstruction tests using the logspy
|
|
|
|
if (header_reconstruction_test(*spy)) {
|
|
|
|
printf("header_reconstruction_test failed\n");
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (header_reconstruction_test2(*spy)) {
|
|
|
|
printf("header_reconstruction_test2 failed\n");
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (header_reconstruction_test3(*spy)) {
|
|
|
|
printf("header_reconstruction_test3 failed\n");
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (header_reconstruction_test4(*spy)) {
|
|
|
|
printf("header_reconstruction_test4 failed\n");
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (header_reconstruction_test5(*spy)) {
|
|
|
|
printf("header_reconstruction_test5 failed\n");
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (header_reconstruction_test6(*spy)) {
|
|
|
|
printf("header_reconstruction_test6 failed\n");
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (header_reconstruction_test7(*spy)) {
|
|
|
|
printf("header_reconstruction_test7 failed\n");
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (header_reconstruction_test8(*spy)) {
|
|
|
|
printf("header_reconstruction_test8 failed\n");
|
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (reset_test()) {
|
|
|
|
printf("reset_test failed\n");
|
|
|
|
exit(-1);
|
|
|
|
};
|
|
|
|
|
|
|
|
if (stop_test()) {
|
|
|
|
printf("stop_test failed\n");
|
|
|
|
exit(-1);
|
|
|
|
};
|
|
|
|
|
|
|
|
if (resume_test()) {
|
|
|
|
printf("resume_test failed\n");
|
|
|
|
exit(-1);
|
|
|
|
};
|
|
|
|
|
|
|
|
if (status_pdu_test()) {
|
|
|
|
printf("status_pdu_test failed\n");
|
|
|
|
exit(-1);
|
|
|
|
};
|
|
|
|
|
|
|
|
if (incorrect_status_pdu_test()) {
|
|
|
|
printf("incorrect_status_pdu_test failed\n");
|
|
|
|
exit(-1);
|
|
|
|
};
|
|
|
|
|
|
|
|
if (incorrect_status_pdu_test2()) {
|
|
|
|
printf("incorrect_status_pdu_test2 failed\n");
|
|
|
|
exit(-1);
|
|
|
|
};
|
|
|
|
|
|
|
|
if (discard_test()) {
|
|
|
|
printf("discard_test failed\n");
|
|
|
|
exit(-1);
|
|
|
|
};
|
|
|
|
|
|
|
|
return SRSRAN_SUCCESS;
|
|
|
|
}
|