|
|
@ -21,11 +21,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
#define RLC_AM_NR_WINDOW_SIZE 2048
|
|
|
|
#define RLC_AM_NR_WINDOW_SIZE 2048
|
|
|
|
|
|
|
|
|
|
|
|
#define Debug(fmt, ...) logger->debug("%s: " fmt, rb_name, ##__VA_ARGS__)
|
|
|
|
|
|
|
|
#define Info(fmt, ...) logger->info("%s: " fmt, rb_name, ##__VA_ARGS__)
|
|
|
|
|
|
|
|
#define Warning(fmt, ...) logger->warning("%s: " fmt, rb_name, ##__VA_ARGS__)
|
|
|
|
|
|
|
|
#define Error(fmt, ...) logger->error("%s: " fmt, rb_name, ##__VA_ARGS__)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
namespace srsran {
|
|
|
|
namespace srsran {
|
|
|
|
|
|
|
|
|
|
|
|
const static uint32_t max_tx_queue_size = 256;
|
|
|
|
const static uint32_t max_tx_queue_size = 256;
|
|
|
@ -36,7 +31,7 @@ const static uint32_t max_tx_queue_size = 256;
|
|
|
|
/***************************************************************************
|
|
|
|
/***************************************************************************
|
|
|
|
* Tx subclass implementation
|
|
|
|
* Tx subclass implementation
|
|
|
|
***************************************************************************/
|
|
|
|
***************************************************************************/
|
|
|
|
rlc_am_nr_tx::rlc_am_nr_tx(rlc_am* parent_) : parent(parent_), rlc_am_base_tx(&parent_->logger) {}
|
|
|
|
rlc_am_nr_tx::rlc_am_nr_tx(rlc_am* parent_) : parent(parent_), rlc_am_base_tx(parent_->logger) {}
|
|
|
|
|
|
|
|
|
|
|
|
bool rlc_am_nr_tx::configure(const rlc_config_t& cfg_)
|
|
|
|
bool rlc_am_nr_tx::configure(const rlc_config_t& cfg_)
|
|
|
|
{
|
|
|
|
{
|
|
|
@ -44,14 +39,14 @@ bool rlc_am_nr_tx::configure(const rlc_config_t& cfg_)
|
|
|
|
rb_name = parent->rb_name;
|
|
|
|
rb_name = parent->rb_name;
|
|
|
|
|
|
|
|
|
|
|
|
if (cfg.tx_sn_field_length != rlc_am_nr_sn_size_t::size12bits) {
|
|
|
|
if (cfg.tx_sn_field_length != rlc_am_nr_sn_size_t::size12bits) {
|
|
|
|
Warning("RLC AM NR only supports 12 bit SN length.");
|
|
|
|
RlcWarning("RLC AM NR only supports 12 bit SN length.");
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (cfg_.tx_queue_length > max_tx_queue_size) {
|
|
|
|
if (cfg_.tx_queue_length > max_tx_queue_size) {
|
|
|
|
Error("configuring tx queue length of %d PDUs too big. Maximum value is %d.",
|
|
|
|
RlcError("configuring tx queue length of %d PDUs too big. Maximum value is %d.",
|
|
|
|
cfg_.tx_queue_length,
|
|
|
|
cfg_.tx_queue_length,
|
|
|
|
max_tx_queue_size);
|
|
|
|
max_tx_queue_size);
|
|
|
|
return false;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -62,7 +57,7 @@ bool rlc_am_nr_tx::configure(const rlc_config_t& cfg_)
|
|
|
|
|
|
|
|
|
|
|
|
tx_enabled = true;
|
|
|
|
tx_enabled = true;
|
|
|
|
|
|
|
|
|
|
|
|
Debug("RLC AM NR tx entity configured.");
|
|
|
|
RlcDebug("RLC AM NR tx entity configured.");
|
|
|
|
return true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -77,30 +72,30 @@ uint32_t rlc_am_nr_tx::read_pdu(uint8_t* payload, uint32_t nof_bytes)
|
|
|
|
std::lock_guard<std::mutex> lock(mutex);
|
|
|
|
std::lock_guard<std::mutex> lock(mutex);
|
|
|
|
|
|
|
|
|
|
|
|
if (not tx_enabled) {
|
|
|
|
if (not tx_enabled) {
|
|
|
|
Debug("RLC entity not active. Not generating PDU.");
|
|
|
|
RlcDebug("RLC entity not active. Not generating PDU.");
|
|
|
|
return 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Debug("MAC opportunity - bytes=%d, tx_window size=%zu PDUs", nof_bytes, tx_window.size());
|
|
|
|
RlcDebug("MAC opportunity - bytes=%d, tx_window size=%zu PDUs", nof_bytes, tx_window.size());
|
|
|
|
|
|
|
|
|
|
|
|
// Tx STATUS if requested
|
|
|
|
// Tx STATUS if requested
|
|
|
|
if (do_status()) {
|
|
|
|
if (do_status()) {
|
|
|
|
unique_byte_buffer_t tx_pdu = srsran::make_byte_buffer();
|
|
|
|
unique_byte_buffer_t tx_pdu = srsran::make_byte_buffer();
|
|
|
|
if (tx_pdu == nullptr) {
|
|
|
|
if (tx_pdu == nullptr) {
|
|
|
|
Error("couldn't allocate PDU in %s().", __FUNCTION__);
|
|
|
|
RlcError("couldn't allocate PDU in %s().", __FUNCTION__);
|
|
|
|
return 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
build_status_pdu(tx_pdu.get(), nof_bytes);
|
|
|
|
build_status_pdu(tx_pdu.get(), nof_bytes);
|
|
|
|
memcpy(payload, tx_pdu->msg, tx_pdu->N_bytes);
|
|
|
|
memcpy(payload, tx_pdu->msg, tx_pdu->N_bytes);
|
|
|
|
Debug("status PDU built - %d bytes", tx_pdu->N_bytes);
|
|
|
|
RlcDebug("status PDU built - %d bytes", tx_pdu->N_bytes);
|
|
|
|
return tx_pdu->N_bytes;
|
|
|
|
return tx_pdu->N_bytes;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Retransmit if required
|
|
|
|
// Retransmit if required
|
|
|
|
if (not retx_queue.empty()) {
|
|
|
|
if (not retx_queue.empty()) {
|
|
|
|
Info("re-transmission required. Retransmission queue size: %d", retx_queue.size());
|
|
|
|
RlcInfo("re-transmission required. Retransmission queue size: %d", retx_queue.size());
|
|
|
|
unique_byte_buffer_t tx_pdu = srsran::make_byte_buffer();
|
|
|
|
unique_byte_buffer_t tx_pdu = srsran::make_byte_buffer();
|
|
|
|
if (tx_pdu == nullptr) {
|
|
|
|
if (tx_pdu == nullptr) {
|
|
|
|
Error("couldn't allocate PDU in %s().", __FUNCTION__);
|
|
|
|
RlcError("couldn't allocate PDU in %s().", __FUNCTION__);
|
|
|
|
return 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
int retx_err = build_retx_pdu(tx_pdu, nof_bytes);
|
|
|
|
int retx_err = build_retx_pdu(tx_pdu, nof_bytes);
|
|
|
@ -114,8 +109,8 @@ uint32_t rlc_am_nr_tx::read_pdu(uint8_t* payload, uint32_t nof_bytes)
|
|
|
|
if (sdu_under_segmentation.rlc_sn != INVALID_RLC_SN) {
|
|
|
|
if (sdu_under_segmentation.rlc_sn != INVALID_RLC_SN) {
|
|
|
|
if (not tx_window.has_sn(sdu_under_segmentation.rlc_sn)) {
|
|
|
|
if (not tx_window.has_sn(sdu_under_segmentation.rlc_sn)) {
|
|
|
|
sdu_under_segmentation.rlc_sn = INVALID_RLC_SN;
|
|
|
|
sdu_under_segmentation.rlc_sn = INVALID_RLC_SN;
|
|
|
|
Error("SDU currently being segmented does not exist in tx_window. Aborting segmentation SN=%d",
|
|
|
|
RlcError("SDU currently being segmented does not exist in tx_window. Aborting segmentation SN=%d",
|
|
|
|
sdu_under_segmentation.rlc_sn);
|
|
|
|
sdu_under_segmentation.rlc_sn);
|
|
|
|
return 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return build_continuation_sdu_segment(tx_window[sdu_under_segmentation.rlc_sn], payload, nof_bytes);
|
|
|
|
return build_continuation_sdu_segment(tx_window[sdu_under_segmentation.rlc_sn], payload, nof_bytes);
|
|
|
@ -123,21 +118,21 @@ uint32_t rlc_am_nr_tx::read_pdu(uint8_t* payload, uint32_t nof_bytes)
|
|
|
|
|
|
|
|
|
|
|
|
// Check whether there is something to TX
|
|
|
|
// Check whether there is something to TX
|
|
|
|
if (tx_sdu_queue.is_empty()) {
|
|
|
|
if (tx_sdu_queue.is_empty()) {
|
|
|
|
Info("no data available to be sent");
|
|
|
|
RlcInfo("no data available to be sent");
|
|
|
|
return 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Read new SDU from TX queue
|
|
|
|
// Read new SDU from TX queue
|
|
|
|
unique_byte_buffer_t tx_sdu;
|
|
|
|
unique_byte_buffer_t tx_sdu;
|
|
|
|
Debug("reading from RLC SDU queue. Queue size %d", tx_sdu_queue.size());
|
|
|
|
RlcDebug("reading from RLC SDU queue. Queue size %d", tx_sdu_queue.size());
|
|
|
|
do {
|
|
|
|
do {
|
|
|
|
tx_sdu = tx_sdu_queue.read();
|
|
|
|
tx_sdu = tx_sdu_queue.read();
|
|
|
|
} while (tx_sdu == nullptr && tx_sdu_queue.size() != 0);
|
|
|
|
} while (tx_sdu == nullptr && tx_sdu_queue.size() != 0);
|
|
|
|
|
|
|
|
|
|
|
|
if (tx_sdu != nullptr) {
|
|
|
|
if (tx_sdu != nullptr) {
|
|
|
|
Debug("read RLC SDU - %d bytes", tx_sdu->N_bytes);
|
|
|
|
RlcDebug("read RLC SDU - %d bytes", tx_sdu->N_bytes);
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
Debug("no SDUs left in the tx queue.");
|
|
|
|
RlcDebug("no SDUs left in the tx queue.");
|
|
|
|
return 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -146,13 +141,13 @@ uint32_t rlc_am_nr_tx::read_pdu(uint8_t* payload, uint32_t nof_bytes)
|
|
|
|
rlc_amd_tx_pdu_nr& tx_pdu = tx_window.add_pdu(st.tx_next);
|
|
|
|
rlc_amd_tx_pdu_nr& tx_pdu = tx_window.add_pdu(st.tx_next);
|
|
|
|
tx_pdu.buf = srsran::make_byte_buffer();
|
|
|
|
tx_pdu.buf = srsran::make_byte_buffer();
|
|
|
|
if (tx_pdu.buf == nullptr) {
|
|
|
|
if (tx_pdu.buf == nullptr) {
|
|
|
|
Error("couldn't allocate PDU in %s().", __FUNCTION__);
|
|
|
|
RlcError("couldn't allocate PDU in %s().", __FUNCTION__);
|
|
|
|
return 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Segment new SDU if necessary
|
|
|
|
// Segment new SDU if necessary
|
|
|
|
if (tx_sdu->N_bytes + min_hdr_size > nof_bytes) {
|
|
|
|
if (tx_sdu->N_bytes + min_hdr_size > nof_bytes) {
|
|
|
|
Info("trying to build PDU segment from SDU.");
|
|
|
|
RlcInfo("trying to build PDU segment from SDU.");
|
|
|
|
return build_new_sdu_segment(std::move(tx_sdu), tx_pdu, payload, nof_bytes);
|
|
|
|
return build_new_sdu_segment(std::move(tx_sdu), tx_pdu, payload, nof_bytes);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -167,19 +162,19 @@ uint32_t rlc_am_nr_tx::read_pdu(uint8_t* payload, uint32_t nof_bytes)
|
|
|
|
hdr.sn_size = rlc_am_nr_sn_size_t::size12bits;
|
|
|
|
hdr.sn_size = rlc_am_nr_sn_size_t::size12bits;
|
|
|
|
hdr.sn = st.tx_next;
|
|
|
|
hdr.sn = st.tx_next;
|
|
|
|
tx_pdu.header = hdr;
|
|
|
|
tx_pdu.header = hdr;
|
|
|
|
log_rlc_am_nr_pdu_header_to_string(logger->info, hdr);
|
|
|
|
log_rlc_am_nr_pdu_header_to_string(logger.info, hdr);
|
|
|
|
|
|
|
|
|
|
|
|
// Write header
|
|
|
|
// Write header
|
|
|
|
uint32_t len = rlc_am_nr_write_data_pdu_header(hdr, tx_sdu.get());
|
|
|
|
uint32_t len = rlc_am_nr_write_data_pdu_header(hdr, tx_sdu.get());
|
|
|
|
if (len > nof_bytes) {
|
|
|
|
if (len > nof_bytes) {
|
|
|
|
Error("error writing AMD PDU header");
|
|
|
|
RlcError("error writing AMD PDU header");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Update TX Next
|
|
|
|
// Update TX Next
|
|
|
|
st.tx_next = (st.tx_next + 1) % MOD;
|
|
|
|
st.tx_next = (st.tx_next + 1) % MOD;
|
|
|
|
|
|
|
|
|
|
|
|
memcpy(payload, tx_sdu->msg, tx_sdu->N_bytes);
|
|
|
|
memcpy(payload, tx_sdu->msg, tx_sdu->N_bytes);
|
|
|
|
Debug("wrote RLC PDU - %d bytes", tx_sdu->N_bytes);
|
|
|
|
RlcDebug("wrote RLC PDU - %d bytes", tx_sdu->N_bytes);
|
|
|
|
|
|
|
|
|
|
|
|
return tx_sdu->N_bytes;
|
|
|
|
return tx_sdu->N_bytes;
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -189,21 +184,21 @@ int rlc_am_nr_tx::build_new_sdu_segment(unique_byte_buffer_t tx_sdu,
|
|
|
|
uint8_t* payload,
|
|
|
|
uint8_t* payload,
|
|
|
|
uint32_t nof_bytes)
|
|
|
|
uint32_t nof_bytes)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
Info("creating new SDU segment. Tx SDU (%d B), nof_bytes=%d B ", tx_sdu->N_bytes, nof_bytes);
|
|
|
|
RlcInfo("creating new SDU segment. Tx SDU (%d B), nof_bytes=%d B ", tx_sdu->N_bytes, nof_bytes);
|
|
|
|
|
|
|
|
|
|
|
|
// Sanity check: can this SDU be sent this in a single PDU?
|
|
|
|
// Sanity check: can this SDU be sent this in a single PDU?
|
|
|
|
if ((tx_sdu->N_bytes + min_hdr_size) < nof_bytes) {
|
|
|
|
if ((tx_sdu->N_bytes + min_hdr_size) < nof_bytes) {
|
|
|
|
Error("calling build_new_sdu_segment(), but there are enough bytes to tx in a single PDU. Tx SDU (%d B), "
|
|
|
|
RlcError("calling build_new_sdu_segment(), but there are enough bytes to tx in a single PDU. Tx SDU (%d B), "
|
|
|
|
"nof_bytes=%d B ",
|
|
|
|
"nof_bytes=%d B ",
|
|
|
|
tx_sdu->N_bytes,
|
|
|
|
tx_sdu->N_bytes,
|
|
|
|
nof_bytes);
|
|
|
|
nof_bytes);
|
|
|
|
return 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Sanity check: can this SDU be sent considering header overhead?
|
|
|
|
// Sanity check: can this SDU be sent considering header overhead?
|
|
|
|
if (nof_bytes <= min_hdr_size) { // Small header as SO is not present
|
|
|
|
if (nof_bytes <= min_hdr_size) { // Small header as SO is not present
|
|
|
|
Error("cannot build new sdu_segment, there are not enough bytes allocated to tx header plus data. nof_bytes=%d",
|
|
|
|
RlcError("cannot build new sdu_segment, there are not enough bytes allocated to tx header plus data. nof_bytes=%d",
|
|
|
|
nof_bytes);
|
|
|
|
nof_bytes);
|
|
|
|
return 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -216,12 +211,12 @@ int rlc_am_nr_tx::build_new_sdu_segment(unique_byte_buffer_t tx_sdu,
|
|
|
|
hdr.sn = st.tx_next;
|
|
|
|
hdr.sn = st.tx_next;
|
|
|
|
hdr.so = 0;
|
|
|
|
hdr.so = 0;
|
|
|
|
tx_pdu.header = hdr;
|
|
|
|
tx_pdu.header = hdr;
|
|
|
|
log_rlc_am_nr_pdu_header_to_string(logger->info, hdr);
|
|
|
|
log_rlc_am_nr_pdu_header_to_string(logger.info, hdr);
|
|
|
|
|
|
|
|
|
|
|
|
// Write header
|
|
|
|
// Write header
|
|
|
|
uint32_t hdr_len = rlc_am_nr_write_data_pdu_header(hdr, payload);
|
|
|
|
uint32_t hdr_len = rlc_am_nr_write_data_pdu_header(hdr, payload);
|
|
|
|
if (hdr_len >= nof_bytes || hdr_len != min_hdr_size) {
|
|
|
|
if (hdr_len >= nof_bytes || hdr_len != min_hdr_size) {
|
|
|
|
Error("error writing AMD PDU header");
|
|
|
|
RlcError("error writing AMD PDU header");
|
|
|
|
return 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -243,18 +238,18 @@ int rlc_am_nr_tx::build_new_sdu_segment(unique_byte_buffer_t tx_sdu,
|
|
|
|
|
|
|
|
|
|
|
|
int rlc_am_nr_tx::build_continuation_sdu_segment(rlc_amd_tx_pdu_nr& tx_pdu, uint8_t* payload, uint32_t nof_bytes)
|
|
|
|
int rlc_am_nr_tx::build_continuation_sdu_segment(rlc_amd_tx_pdu_nr& tx_pdu, uint8_t* payload, uint32_t nof_bytes)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
Info("continuing SDU segment. SN=%d, Tx SDU (%d B), nof_bytes=%d B ",
|
|
|
|
RlcInfo("continuing SDU segment. SN=%d, Tx SDU (%d B), nof_bytes=%d B ",
|
|
|
|
sdu_under_segmentation.rlc_sn,
|
|
|
|
|
|
|
|
sdu_under_segmentation.buf->N_bytes,
|
|
|
|
|
|
|
|
nof_bytes);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Sanity check: is there an initial SDU segment?
|
|
|
|
|
|
|
|
if (tx_pdu.segment_list.empty()) {
|
|
|
|
|
|
|
|
Error("build_continuation_sdu_segment was called, but there was no initial segment. SN=%d, Tx SDU (%d B), "
|
|
|
|
|
|
|
|
"nof_bytes=%d B ",
|
|
|
|
|
|
|
|
sdu_under_segmentation.rlc_sn,
|
|
|
|
sdu_under_segmentation.rlc_sn,
|
|
|
|
sdu_under_segmentation.buf->N_bytes,
|
|
|
|
sdu_under_segmentation.buf->N_bytes,
|
|
|
|
nof_bytes);
|
|
|
|
nof_bytes);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Sanity check: is there an initial SDU segment?
|
|
|
|
|
|
|
|
if (tx_pdu.segment_list.empty()) {
|
|
|
|
|
|
|
|
RlcError("build_continuation_sdu_segment was called, but there was no initial segment. SN=%d, Tx SDU (%d B), "
|
|
|
|
|
|
|
|
"nof_bytes=%d B ",
|
|
|
|
|
|
|
|
sdu_under_segmentation.rlc_sn,
|
|
|
|
|
|
|
|
sdu_under_segmentation.buf->N_bytes,
|
|
|
|
|
|
|
|
nof_bytes);
|
|
|
|
sdu_under_segmentation.rlc_sn = INVALID_RLC_SN;
|
|
|
|
sdu_under_segmentation.rlc_sn = INVALID_RLC_SN;
|
|
|
|
sdu_under_segmentation.buf = nullptr;
|
|
|
|
sdu_under_segmentation.buf = nullptr;
|
|
|
|
return 0;
|
|
|
|
return 0;
|
|
|
@ -262,19 +257,19 @@ int rlc_am_nr_tx::build_continuation_sdu_segment(rlc_amd_tx_pdu_nr& tx_pdu, uint
|
|
|
|
|
|
|
|
|
|
|
|
// Sanity check: can this SDU be sent considering header overhead?
|
|
|
|
// Sanity check: can this SDU be sent considering header overhead?
|
|
|
|
if ((max_hdr_size + 1) < nof_bytes) { // Larger header size, as SO is present
|
|
|
|
if ((max_hdr_size + 1) < nof_bytes) { // Larger header size, as SO is present
|
|
|
|
Error("cannot build new sdu_segment, there are not enough bytes allocated to tx header plus data. nof_bytes=%d",
|
|
|
|
RlcError("cannot build new sdu_segment, there are not enough bytes allocated to tx header plus data. nof_bytes=%d",
|
|
|
|
nof_bytes);
|
|
|
|
nof_bytes);
|
|
|
|
return 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Can the rest of the SDU be sent on a single segment PDU?
|
|
|
|
// Can the rest of the SDU be sent on a single segment PDU?
|
|
|
|
const rlc_amd_tx_pdu_nr::pdu_segment& seg = tx_pdu.segment_list.back();
|
|
|
|
const rlc_amd_tx_pdu_nr::pdu_segment& seg = tx_pdu.segment_list.back();
|
|
|
|
uint32_t last_byte = seg.so + seg.payload_len;
|
|
|
|
uint32_t last_byte = seg.so + seg.payload_len;
|
|
|
|
Debug("continuing SDU segment. SN=%d, last byte transmitted %d", tx_pdu.rlc_sn, last_byte);
|
|
|
|
RlcDebug("continuing SDU segment. SN=%d, last byte transmitted %d", tx_pdu.rlc_sn, last_byte);
|
|
|
|
|
|
|
|
|
|
|
|
// Sanity check: last byte must be smaller than SDU
|
|
|
|
// Sanity check: last byte must be smaller than SDU
|
|
|
|
if (sdu_under_segmentation.buf->N_bytes < last_byte) {
|
|
|
|
if (sdu_under_segmentation.buf->N_bytes < last_byte) {
|
|
|
|
Error("last byte transmitted larger than SDU len. SDU len=%d B, last_byte=%d B", tx_pdu.buf->N_bytes, last_byte);
|
|
|
|
RlcError("last byte transmitted larger than SDU len. SDU len=%d B, last_byte=%d B", tx_pdu.buf->N_bytes, last_byte);
|
|
|
|
return 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -283,18 +278,18 @@ int rlc_am_nr_tx::build_continuation_sdu_segment(rlc_amd_tx_pdu_nr& tx_pdu, uint
|
|
|
|
rlc_nr_si_field_t si = {};
|
|
|
|
rlc_nr_si_field_t si = {};
|
|
|
|
|
|
|
|
|
|
|
|
if (segment_payload_full_len > nof_bytes) {
|
|
|
|
if (segment_payload_full_len > nof_bytes) {
|
|
|
|
Info("grant is not large enough for full SDU. "
|
|
|
|
RlcInfo("grant is not large enough for full SDU. "
|
|
|
|
"SDU bytes left %d, nof_bytes %d, ",
|
|
|
|
"SDU bytes left %d, nof_bytes %d, ",
|
|
|
|
segment_payload_full_len,
|
|
|
|
segment_payload_full_len,
|
|
|
|
nof_bytes);
|
|
|
|
nof_bytes);
|
|
|
|
si = rlc_nr_si_field_t::neither_first_nor_last_segment;
|
|
|
|
si = rlc_nr_si_field_t::neither_first_nor_last_segment;
|
|
|
|
segment_payload_len = nof_bytes - max_hdr_size;
|
|
|
|
segment_payload_len = nof_bytes - max_hdr_size;
|
|
|
|
segment_payload_full_len = nof_bytes;
|
|
|
|
segment_payload_full_len = nof_bytes;
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
Info("grant is large enough for full SDU."
|
|
|
|
RlcInfo("grant is large enough for full SDU."
|
|
|
|
"SDU bytes left %d, nof_bytes %d, ",
|
|
|
|
"SDU bytes left %d, nof_bytes %d, ",
|
|
|
|
segment_payload_full_len,
|
|
|
|
segment_payload_full_len,
|
|
|
|
nof_bytes);
|
|
|
|
nof_bytes);
|
|
|
|
si = rlc_nr_si_field_t::last_segment;
|
|
|
|
si = rlc_nr_si_field_t::last_segment;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -307,12 +302,12 @@ int rlc_am_nr_tx::build_continuation_sdu_segment(rlc_amd_tx_pdu_nr& tx_pdu, uint
|
|
|
|
hdr.sn = st.tx_next;
|
|
|
|
hdr.sn = st.tx_next;
|
|
|
|
hdr.so = last_byte;
|
|
|
|
hdr.so = last_byte;
|
|
|
|
tx_pdu.header = hdr;
|
|
|
|
tx_pdu.header = hdr;
|
|
|
|
log_rlc_am_nr_pdu_header_to_string(logger->info, hdr);
|
|
|
|
log_rlc_am_nr_pdu_header_to_string(logger.info, hdr);
|
|
|
|
|
|
|
|
|
|
|
|
// Write header
|
|
|
|
// Write header
|
|
|
|
uint32_t hdr_len = rlc_am_nr_write_data_pdu_header(hdr, payload);
|
|
|
|
uint32_t hdr_len = rlc_am_nr_write_data_pdu_header(hdr, payload);
|
|
|
|
if (hdr_len >= nof_bytes || hdr_len != max_hdr_size) {
|
|
|
|
if (hdr_len >= nof_bytes || hdr_len != max_hdr_size) {
|
|
|
|
Error("error writing AMD PDU header");
|
|
|
|
RlcError("error writing AMD PDU header");
|
|
|
|
return 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -327,11 +322,11 @@ int rlc_am_nr_tx::build_continuation_sdu_segment(rlc_amd_tx_pdu_nr& tx_pdu, uint
|
|
|
|
tx_pdu.segment_list.push_back(segment_info);
|
|
|
|
tx_pdu.segment_list.push_back(segment_info);
|
|
|
|
|
|
|
|
|
|
|
|
if (si == rlc_nr_si_field_t::neither_first_nor_last_segment) {
|
|
|
|
if (si == rlc_nr_si_field_t::neither_first_nor_last_segment) {
|
|
|
|
Info("grant is not large enough for full SDU."
|
|
|
|
RlcInfo("grant is not large enough for full SDU."
|
|
|
|
"Storing SDU segment info");
|
|
|
|
"Storing SDU segment info");
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
Info("grant is large enough for full SDU."
|
|
|
|
RlcInfo("grant is large enough for full SDU."
|
|
|
|
"Removing current SDU info");
|
|
|
|
"Removing current SDU info");
|
|
|
|
sdu_under_segmentation.rlc_sn = INVALID_RLC_SN;
|
|
|
|
sdu_under_segmentation.rlc_sn = INVALID_RLC_SN;
|
|
|
|
sdu_under_segmentation.buf = nullptr;
|
|
|
|
sdu_under_segmentation.buf = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -343,7 +338,7 @@ int rlc_am_nr_tx::build_retx_pdu(unique_byte_buffer_t& tx_pdu, uint32_t nof_byte
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// Check there is at least 1 element before calling front()
|
|
|
|
// Check there is at least 1 element before calling front()
|
|
|
|
if (retx_queue.empty()) {
|
|
|
|
if (retx_queue.empty()) {
|
|
|
|
Error("in build_retx_pdu(): retx_queue is empty");
|
|
|
|
RlcError("in build_retx_pdu(): retx_queue is empty");
|
|
|
|
return SRSRAN_ERROR;
|
|
|
|
return SRSRAN_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -351,12 +346,12 @@ int rlc_am_nr_tx::build_retx_pdu(unique_byte_buffer_t& tx_pdu, uint32_t nof_byte
|
|
|
|
|
|
|
|
|
|
|
|
// Sanity check - drop any retx SNs not present in tx_window
|
|
|
|
// Sanity check - drop any retx SNs not present in tx_window
|
|
|
|
while (not tx_window.has_sn(retx.sn)) {
|
|
|
|
while (not tx_window.has_sn(retx.sn)) {
|
|
|
|
Warning("SN=%d not in tx window. Ignoring retx.", retx.sn);
|
|
|
|
RlcWarning("SN=%d not in tx window. Ignoring retx.", retx.sn);
|
|
|
|
retx_queue.pop();
|
|
|
|
retx_queue.pop();
|
|
|
|
if (!retx_queue.empty()) {
|
|
|
|
if (!retx_queue.empty()) {
|
|
|
|
retx = retx_queue.front();
|
|
|
|
retx = retx_queue.front();
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
Warning("empty retx queue, cannot provide retx PDU");
|
|
|
|
RlcWarning("empty retx queue, cannot provide retx PDU");
|
|
|
|
return SRSRAN_ERROR;
|
|
|
|
return SRSRAN_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -367,7 +362,7 @@ int rlc_am_nr_tx::build_retx_pdu(unique_byte_buffer_t& tx_pdu, uint32_t nof_byte
|
|
|
|
|
|
|
|
|
|
|
|
// Check if we exceed allocated number of bytes
|
|
|
|
// Check if we exceed allocated number of bytes
|
|
|
|
if (hdr_len + tx_window[retx.sn].buf->N_bytes > nof_bytes) {
|
|
|
|
if (hdr_len + tx_window[retx.sn].buf->N_bytes > nof_bytes) {
|
|
|
|
Warning("segmentation not supported yet. Cannot provide retx PDU");
|
|
|
|
RlcWarning("segmentation not supported yet. Cannot provide retx PDU");
|
|
|
|
return SRSRAN_ERROR;
|
|
|
|
return SRSRAN_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// TODO Consider re-segmentation
|
|
|
|
// TODO Consider re-segmentation
|
|
|
@ -377,16 +372,15 @@ int rlc_am_nr_tx::build_retx_pdu(unique_byte_buffer_t& tx_pdu, uint32_t nof_byte
|
|
|
|
|
|
|
|
|
|
|
|
retx_queue.pop();
|
|
|
|
retx_queue.pop();
|
|
|
|
|
|
|
|
|
|
|
|
logger->info(tx_window[retx.sn].buf->msg,
|
|
|
|
RlcHexInfo(tx_window[retx.sn].buf->msg,
|
|
|
|
tx_window[retx.sn].buf->N_bytes,
|
|
|
|
tx_window[retx.sn].buf->N_bytes,
|
|
|
|
"%s Original SDU SN=%d (%d B) (attempt %d/%d)",
|
|
|
|
"Original SDU SN=%d (%d B) (attempt %d/%d)",
|
|
|
|
parent->rb_name,
|
|
|
|
retx.sn,
|
|
|
|
retx.sn,
|
|
|
|
tx_window[retx.sn].buf->N_bytes,
|
|
|
|
tx_window[retx.sn].buf->N_bytes,
|
|
|
|
tx_window[retx.sn].retx_count + 1,
|
|
|
|
tx_window[retx.sn].retx_count + 1,
|
|
|
|
cfg.max_retx_thresh);
|
|
|
|
cfg.max_retx_thresh);
|
|
|
|
RlcHexInfo(tx_pdu->msg, tx_pdu->N_bytes, "retx PDU SN=%d (%d B)", retx.sn, tx_pdu->N_bytes);
|
|
|
|
logger->info(tx_pdu->msg, tx_pdu->N_bytes, "%s retx PDU SN=%d (%d B)", parent->rb_name, retx.sn, tx_pdu->N_bytes);
|
|
|
|
log_rlc_am_nr_pdu_header_to_string(logger.debug, new_header);
|
|
|
|
log_rlc_am_nr_pdu_header_to_string(logger->debug, new_header);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// debug_state();
|
|
|
|
// debug_state();
|
|
|
|
return SRSRAN_SUCCESS;
|
|
|
|
return SRSRAN_SUCCESS;
|
|
|
@ -394,18 +388,18 @@ int rlc_am_nr_tx::build_retx_pdu(unique_byte_buffer_t& tx_pdu, uint32_t nof_byte
|
|
|
|
|
|
|
|
|
|
|
|
uint32_t rlc_am_nr_tx::build_status_pdu(byte_buffer_t* payload, uint32_t nof_bytes)
|
|
|
|
uint32_t rlc_am_nr_tx::build_status_pdu(byte_buffer_t* payload, uint32_t nof_bytes)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
Info("generating status PDU. Bytes available:%d", nof_bytes);
|
|
|
|
RlcInfo("generating status PDU. Bytes available:%d", nof_bytes);
|
|
|
|
rlc_am_nr_status_pdu_t tx_status;
|
|
|
|
rlc_am_nr_status_pdu_t tx_status;
|
|
|
|
int pdu_len = rx->get_status_pdu(&tx_status, nof_bytes);
|
|
|
|
int pdu_len = rx->get_status_pdu(&tx_status, nof_bytes);
|
|
|
|
if (pdu_len == SRSRAN_ERROR) {
|
|
|
|
if (pdu_len == SRSRAN_ERROR) {
|
|
|
|
Debug("deferred status PDU. Cause: Failed to acquire rx lock");
|
|
|
|
RlcDebug("deferred status PDU. Cause: Failed to acquire rx lock");
|
|
|
|
pdu_len = 0;
|
|
|
|
pdu_len = 0;
|
|
|
|
} else if (pdu_len > 0 && nof_bytes >= static_cast<uint32_t>(pdu_len)) {
|
|
|
|
} else if (pdu_len > 0 && nof_bytes >= static_cast<uint32_t>(pdu_len)) {
|
|
|
|
Debug("generated status PDU. Bytes:%d", pdu_len);
|
|
|
|
RlcDebug("generated status PDU. Bytes:%d", pdu_len);
|
|
|
|
log_rlc_am_nr_status_pdu_to_string(logger->info, "%s tx status PDU - %s", &tx_status, rb_name);
|
|
|
|
log_rlc_am_nr_status_pdu_to_string(logger.info, "%s tx status PDU - %s", &tx_status, rb_name);
|
|
|
|
pdu_len = rlc_am_nr_write_status_pdu(tx_status, rlc_am_nr_sn_size_t::size12bits, payload);
|
|
|
|
pdu_len = rlc_am_nr_write_status_pdu(tx_status, rlc_am_nr_sn_size_t::size12bits, payload);
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
Info("cannot tx status PDU - %d bytes available, %d bytes required", nof_bytes, pdu_len);
|
|
|
|
RlcInfo("cannot tx status PDU - %d bytes available, %d bytes required", nof_bytes, pdu_len);
|
|
|
|
pdu_len = 0;
|
|
|
|
pdu_len = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -419,9 +413,9 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
rlc_am_nr_status_pdu_t status = {};
|
|
|
|
rlc_am_nr_status_pdu_t status = {};
|
|
|
|
logger->debug(payload, nof_bytes, "%s Rx control PDU", parent->rb_name);
|
|
|
|
RlcHexDebug(payload, nof_bytes, "%s Rx control PDU", parent->rb_name);
|
|
|
|
rlc_am_nr_read_status_pdu(payload, nof_bytes, rlc_am_nr_sn_size_t::size12bits, &status);
|
|
|
|
rlc_am_nr_read_status_pdu(payload, nof_bytes, rlc_am_nr_sn_size_t::size12bits, &status);
|
|
|
|
log_rlc_am_nr_status_pdu_to_string(logger->info, "%s Rx Status PDU: %s", &status, parent->rb_name);
|
|
|
|
log_rlc_am_nr_status_pdu_to_string(logger.info, "%s Rx Status PDU: %s", &status, parent->rb_name);
|
|
|
|
// Local variables for handling Status PDU will be updated with lock
|
|
|
|
// Local variables for handling Status PDU will be updated with lock
|
|
|
|
/*
|
|
|
|
/*
|
|
|
|
* - if the SN of the corresponding RLC SDU falls within the range
|
|
|
|
* - if the SN of the corresponding RLC SDU falls within the range
|
|
|
@ -434,7 +428,7 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes)
|
|
|
|
? status.ack_sn
|
|
|
|
? status.ack_sn
|
|
|
|
: status.nacks[0].nack_sn - 1; // Stop processing ACKs at the first NACK, if it exists.
|
|
|
|
: status.nacks[0].nack_sn - 1; // Stop processing ACKs at the first NACK, if it exists.
|
|
|
|
if (stop_sn > st.tx_next) {
|
|
|
|
if (stop_sn > st.tx_next) {
|
|
|
|
Error("Received ACK or NACK larger than TX_NEXT. Ignoring status report");
|
|
|
|
RlcError("Received ACK or NACK larger than TX_NEXT. Ignoring status report");
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (uint32_t sn = st.tx_next_ack; sn < stop_sn; sn++) {
|
|
|
|
for (uint32_t sn = st.tx_next_ack; sn < stop_sn; sn++) {
|
|
|
@ -443,7 +437,7 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes)
|
|
|
|
st.tx_next_ack = sn + 1;
|
|
|
|
st.tx_next_ack = sn + 1;
|
|
|
|
// TODO notify PDCP
|
|
|
|
// TODO notify PDCP
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
Error("Missing ACKed SN from TX window");
|
|
|
|
RlcError("Missing ACKed SN from TX window");
|
|
|
|
break;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -495,31 +489,31 @@ uint32_t rlc_am_nr_tx::get_buffer_state()
|
|
|
|
void rlc_am_nr_tx::get_buffer_state(uint32_t& n_bytes_new, uint32_t& n_bytes_prio)
|
|
|
|
void rlc_am_nr_tx::get_buffer_state(uint32_t& n_bytes_new, uint32_t& n_bytes_prio)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
std::lock_guard<std::mutex> lock(mutex);
|
|
|
|
std::lock_guard<std::mutex> lock(mutex);
|
|
|
|
Debug("buffer state - do_status=%s", do_status() ? "yes" : "no");
|
|
|
|
RlcDebug("buffer state - do_status=%s", do_status() ? "yes" : "no");
|
|
|
|
|
|
|
|
|
|
|
|
// Bytes needed for status report
|
|
|
|
// Bytes needed for status report
|
|
|
|
if (do_status()) {
|
|
|
|
if (do_status()) {
|
|
|
|
n_bytes_prio += rx->get_status_pdu_length();
|
|
|
|
n_bytes_prio += rx->get_status_pdu_length();
|
|
|
|
Debug("buffer state - total status report: %d bytes", n_bytes_prio);
|
|
|
|
RlcDebug("buffer state - total status report: %d bytes", n_bytes_prio);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Bytes needed for retx
|
|
|
|
// Bytes needed for retx
|
|
|
|
if (not retx_queue.empty()) {
|
|
|
|
if (not retx_queue.empty()) {
|
|
|
|
rlc_amd_retx_t& retx = retx_queue.front();
|
|
|
|
rlc_amd_retx_t& retx = retx_queue.front();
|
|
|
|
Debug("buffer state - retx - SN=%d, Segment: %s, %d:%d",
|
|
|
|
RlcDebug("buffer state - retx - SN=%d, Segment: %s, %d:%d",
|
|
|
|
retx.sn,
|
|
|
|
retx.sn,
|
|
|
|
retx.is_segment ? "true" : "false",
|
|
|
|
retx.is_segment ? "true" : "false",
|
|
|
|
retx.so_start,
|
|
|
|
retx.so_start,
|
|
|
|
retx.so_end);
|
|
|
|
retx.so_end);
|
|
|
|
if (tx_window.has_sn(retx.sn)) {
|
|
|
|
if (tx_window.has_sn(retx.sn)) {
|
|
|
|
int req_bytes = retx.so_end - retx.so_start;
|
|
|
|
int req_bytes = retx.so_end - retx.so_start;
|
|
|
|
int hdr_req_bytes = retx.is_segment ? max_hdr_size : min_hdr_size; // Segmentation not supported yet
|
|
|
|
int hdr_req_bytes = retx.is_segment ? max_hdr_size : min_hdr_size; // Segmentation not supported yet
|
|
|
|
if (req_bytes <= 0) {
|
|
|
|
if (req_bytes <= 0) {
|
|
|
|
Error("in get_buffer_state(): Removing retx with SN=%d from queue", retx.sn);
|
|
|
|
RlcError("in get_buffer_state(): Removing retx with SN=%d from queue", retx.sn);
|
|
|
|
retx_queue.pop();
|
|
|
|
retx_queue.pop();
|
|
|
|
} else {
|
|
|
|
} else {
|
|
|
|
n_bytes_prio += (req_bytes + hdr_req_bytes);
|
|
|
|
n_bytes_prio += (req_bytes + hdr_req_bytes);
|
|
|
|
Debug("buffer state - retx: %d bytes", n_bytes_prio);
|
|
|
|
RlcDebug("buffer state - retx: %d bytes", n_bytes_prio);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -530,10 +524,10 @@ void rlc_am_nr_tx::get_buffer_state(uint32_t& n_bytes_new, uint32_t& n_bytes_pri
|
|
|
|
|
|
|
|
|
|
|
|
// Room needed for fixed header of data PDUs
|
|
|
|
// Room needed for fixed header of data PDUs
|
|
|
|
n_bytes_new += min_hdr_size * n_sdus;
|
|
|
|
n_bytes_new += min_hdr_size * n_sdus;
|
|
|
|
Debug("total buffer state - %d SDUs (%d B)", n_sdus, n_bytes_new + n_bytes_prio);
|
|
|
|
RlcDebug("total buffer state - %d SDUs (%d B)", n_sdus, n_bytes_new + n_bytes_prio);
|
|
|
|
|
|
|
|
|
|
|
|
if (bsr_callback) {
|
|
|
|
if (bsr_callback) {
|
|
|
|
Debug("calling BSR callback - %d new_tx, %d priority bytes", n_bytes_new, n_bytes_prio);
|
|
|
|
RlcDebug("calling BSR callback - %d new_tx, %d priority bytes", n_bytes_new, n_bytes_prio);
|
|
|
|
bsr_callback(parent->lcid, n_bytes_new, n_bytes_prio);
|
|
|
|
bsr_callback(parent->lcid, n_bytes_new, n_bytes_prio);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -594,7 +588,7 @@ rlc_am_nr_rx::rlc_am_nr_rx(rlc_am* parent_) :
|
|
|
|
pool(byte_buffer_pool::get_instance()),
|
|
|
|
pool(byte_buffer_pool::get_instance()),
|
|
|
|
status_prohibit_timer(parent->timers->get_unique_timer()),
|
|
|
|
status_prohibit_timer(parent->timers->get_unique_timer()),
|
|
|
|
reassembly_timer(parent->timers->get_unique_timer()),
|
|
|
|
reassembly_timer(parent->timers->get_unique_timer()),
|
|
|
|
rlc_am_base_rx(parent_, &parent_->logger)
|
|
|
|
rlc_am_base_rx(parent_, parent_->logger)
|
|
|
|
{}
|
|
|
|
{}
|
|
|
|
|
|
|
|
|
|
|
|
bool rlc_am_nr_rx::configure(const rlc_config_t& cfg_)
|
|
|
|
bool rlc_am_nr_rx::configure(const rlc_config_t& cfg_)
|
|
|
@ -610,12 +604,12 @@ bool rlc_am_nr_rx::configure(const rlc_config_t& cfg_)
|
|
|
|
// Configure t_reassembly timer
|
|
|
|
// Configure t_reassembly timer
|
|
|
|
if (cfg.t_reassembly > 0) {
|
|
|
|
if (cfg.t_reassembly > 0) {
|
|
|
|
reassembly_timer.set(static_cast<uint32_t>(cfg.t_reassembly), [this](uint32_t timerid) { timer_expired(timerid); });
|
|
|
|
reassembly_timer.set(static_cast<uint32_t>(cfg.t_reassembly), [this](uint32_t timerid) { timer_expired(timerid); });
|
|
|
|
Info("configured reassembly timer. t-Reassembly=%d ms", cfg.t_reassembly);
|
|
|
|
RlcInfo("configured reassembly timer. t-Reassembly=%d ms", cfg.t_reassembly);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
mod_nr = (cfg.rx_sn_field_length == rlc_am_nr_sn_size_t::size12bits) ? 4096 : 262144;
|
|
|
|
mod_nr = (cfg.rx_sn_field_length == rlc_am_nr_sn_size_t::size12bits) ? 4096 : 262144;
|
|
|
|
|
|
|
|
|
|
|
|
Debug("RLC AM NR configured rx entity.");
|
|
|
|
RlcDebug("RLC AM NR configured rx entity.");
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -633,18 +627,18 @@ void rlc_am_nr_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes)
|
|
|
|
rlc_am_nr_pdu_header_t header = {};
|
|
|
|
rlc_am_nr_pdu_header_t header = {};
|
|
|
|
uint32_t hdr_len = rlc_am_nr_read_data_pdu_header(payload, nof_bytes, rlc_am_nr_sn_size_t::size12bits, &header);
|
|
|
|
uint32_t hdr_len = rlc_am_nr_read_data_pdu_header(payload, nof_bytes, rlc_am_nr_sn_size_t::size12bits, &header);
|
|
|
|
|
|
|
|
|
|
|
|
logger->info(payload, nof_bytes, "%s Rx data PDU SN=%d (%d B)", parent->rb_name, header.sn, nof_bytes);
|
|
|
|
RlcHexInfo(payload, nof_bytes, "Rx data PDU SN=%d (%d B)", header.sn, nof_bytes);
|
|
|
|
log_rlc_am_nr_pdu_header_to_string(logger->debug, header);
|
|
|
|
log_rlc_am_nr_pdu_header_to_string(logger.debug, header);
|
|
|
|
|
|
|
|
|
|
|
|
// Check whether SDU is within Rx Window
|
|
|
|
// Check whether SDU is within Rx Window
|
|
|
|
if (!inside_rx_window(header.sn)) {
|
|
|
|
if (!inside_rx_window(header.sn)) {
|
|
|
|
Info("SN=%d outside rx window [%d:%d] - discarding", header.sn, st.rx_next, st.rx_next + RLC_AM_NR_WINDOW_SIZE);
|
|
|
|
RlcInfo("SN=%d outside rx window [%d:%d] - discarding", header.sn, st.rx_next, st.rx_next + RLC_AM_NR_WINDOW_SIZE);
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Section 5.2.3.2.2, discard duplicate PDUs
|
|
|
|
// Section 5.2.3.2.2, discard duplicate PDUs
|
|
|
|
if (rx_window.has_sn(header.sn) && rx_window[header.sn].fully_received) {
|
|
|
|
if (rx_window.has_sn(header.sn) && rx_window[header.sn].fully_received) {
|
|
|
|
Info("discarding duplicate SN=%d", header.sn);
|
|
|
|
RlcInfo("discarding duplicate SN=%d", header.sn);
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -663,7 +657,7 @@ void rlc_am_nr_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes)
|
|
|
|
|
|
|
|
|
|
|
|
// Check poll bit
|
|
|
|
// Check poll bit
|
|
|
|
if (header.p) {
|
|
|
|
if (header.p) {
|
|
|
|
Info("status packet requested through polling bit");
|
|
|
|
RlcInfo("status packet requested through polling bit");
|
|
|
|
do_status = true;
|
|
|
|
do_status = true;
|
|
|
|
status_prohibit_timer.stop();
|
|
|
|
status_prohibit_timer.stop();
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -767,7 +761,7 @@ int rlc_am_nr_rx::handle_full_data_sdu(const rlc_am_nr_pdu_header_t& header, con
|
|
|
|
rlc_amd_rx_sdu_nr_t& rx_sdu = rx_window.add_pdu(header.sn);
|
|
|
|
rlc_amd_rx_sdu_nr_t& rx_sdu = rx_window.add_pdu(header.sn);
|
|
|
|
rx_sdu.buf = srsran::make_byte_buffer();
|
|
|
|
rx_sdu.buf = srsran::make_byte_buffer();
|
|
|
|
if (rx_sdu.buf == nullptr) {
|
|
|
|
if (rx_sdu.buf == nullptr) {
|
|
|
|
Error("fatal error. Couldn't allocate PDU in %s.", __FUNCTION__);
|
|
|
|
RlcError("fatal error. Couldn't allocate PDU in %s.", __FUNCTION__);
|
|
|
|
rx_window.remove_pdu(header.sn);
|
|
|
|
rx_window.remove_pdu(header.sn);
|
|
|
|
return SRSRAN_ERROR;
|
|
|
|
return SRSRAN_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -775,7 +769,7 @@ int rlc_am_nr_rx::handle_full_data_sdu(const rlc_am_nr_pdu_header_t& header, con
|
|
|
|
|
|
|
|
|
|
|
|
// check available space for payload
|
|
|
|
// check available space for payload
|
|
|
|
if (nof_bytes > rx_sdu.buf->get_tailroom()) {
|
|
|
|
if (nof_bytes > rx_sdu.buf->get_tailroom()) {
|
|
|
|
Error("discarding SN=%d of size %d B (available space %d B)", header.sn, nof_bytes, rx_sdu.buf->get_tailroom());
|
|
|
|
RlcError("discarding SN=%d of size %d B (available space %d B)", header.sn, nof_bytes, rx_sdu.buf->get_tailroom());
|
|
|
|
rx_window.remove_pdu(header.sn);
|
|
|
|
rx_window.remove_pdu(header.sn);
|
|
|
|
return SRSRAN_ERROR;
|
|
|
|
return SRSRAN_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -791,7 +785,7 @@ int rlc_am_nr_rx::handle_segment_data_sdu(const rlc_am_nr_pdu_header_t& header,
|
|
|
|
uint32_t nof_bytes)
|
|
|
|
uint32_t nof_bytes)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (header.si == rlc_nr_si_field_t::full_sdu) {
|
|
|
|
if (header.si == rlc_nr_si_field_t::full_sdu) {
|
|
|
|
Error("called %s but the SI implies a full SDU. SN=%d", __FUNCTION__, header.sn);
|
|
|
|
RlcError("called %s but the SI implies a full SDU. SN=%d", __FUNCTION__, header.sn);
|
|
|
|
return SRSRAN_ERROR;
|
|
|
|
return SRSRAN_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -799,11 +793,11 @@ int rlc_am_nr_rx::handle_segment_data_sdu(const rlc_am_nr_pdu_header_t& header,
|
|
|
|
|
|
|
|
|
|
|
|
// Log SDU segment reception
|
|
|
|
// Log SDU segment reception
|
|
|
|
if (header.si == rlc_nr_si_field_t::first_segment) { // Check whether it's a full SDU
|
|
|
|
if (header.si == rlc_nr_si_field_t::first_segment) { // Check whether it's a full SDU
|
|
|
|
Debug("Initial segment PDU. SN=%d.", header.sn);
|
|
|
|
RlcDebug("Initial segment PDU. SN=%d.", header.sn);
|
|
|
|
} else if (header.si == rlc_nr_si_field_t::neither_first_nor_last_segment) {
|
|
|
|
} else if (header.si == rlc_nr_si_field_t::neither_first_nor_last_segment) {
|
|
|
|
Debug("Middle segment PDU. SN=%d.", header.sn);
|
|
|
|
RlcDebug("Middle segment PDU. SN=%d.", header.sn);
|
|
|
|
} else if (header.si == rlc_nr_si_field_t::last_segment) {
|
|
|
|
} else if (header.si == rlc_nr_si_field_t::last_segment) {
|
|
|
|
Debug("Final segment PDU. SN=%d.", header.sn);
|
|
|
|
RlcDebug("Final segment PDU. SN=%d.", header.sn);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Add a new SDU to the RX window if necessary
|
|
|
|
// Add a new SDU to the RX window if necessary
|
|
|
@ -814,7 +808,7 @@ int rlc_am_nr_rx::handle_segment_data_sdu(const rlc_am_nr_pdu_header_t& header,
|
|
|
|
pdu_segment.header = header;
|
|
|
|
pdu_segment.header = header;
|
|
|
|
pdu_segment.buf = srsran::make_byte_buffer();
|
|
|
|
pdu_segment.buf = srsran::make_byte_buffer();
|
|
|
|
if (pdu_segment.buf == nullptr) {
|
|
|
|
if (pdu_segment.buf == nullptr) {
|
|
|
|
Error("fatal error. Couldn't allocate PDU in %s.", __FUNCTION__);
|
|
|
|
RlcError("fatal error. Couldn't allocate PDU in %s.", __FUNCTION__);
|
|
|
|
return SRSRAN_ERROR;
|
|
|
|
return SRSRAN_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
memcpy(pdu_segment.buf->msg, payload + hdr_len, nof_bytes - hdr_len); // Don't copy header
|
|
|
|
memcpy(pdu_segment.buf->msg, payload + hdr_len, nof_bytes - hdr_len); // Don't copy header
|
|
|
@ -826,10 +820,10 @@ int rlc_am_nr_rx::handle_segment_data_sdu(const rlc_am_nr_pdu_header_t& header,
|
|
|
|
// Check weather all segments have been received
|
|
|
|
// Check weather all segments have been received
|
|
|
|
rx_sdu.fully_received = have_all_segments_been_received(rx_sdu.segments);
|
|
|
|
rx_sdu.fully_received = have_all_segments_been_received(rx_sdu.segments);
|
|
|
|
if (rx_sdu.fully_received) {
|
|
|
|
if (rx_sdu.fully_received) {
|
|
|
|
Info("Fully received segmented SDU. SN=%d.", header.sn);
|
|
|
|
RlcInfo("Fully received segmented SDU. SN=%d.", header.sn);
|
|
|
|
rx_sdu.buf = srsran::make_byte_buffer();
|
|
|
|
rx_sdu.buf = srsran::make_byte_buffer();
|
|
|
|
if (rx_sdu.buf == nullptr) {
|
|
|
|
if (rx_sdu.buf == nullptr) {
|
|
|
|
Error("fatal error. Couldn't allocate PDU in %s.", __FUNCTION__);
|
|
|
|
RlcError("fatal error. Couldn't allocate PDU in %s.", __FUNCTION__);
|
|
|
|
rx_window.remove_pdu(header.sn);
|
|
|
|
rx_window.remove_pdu(header.sn);
|
|
|
|
return SRSRAN_ERROR;
|
|
|
|
return SRSRAN_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -898,13 +892,13 @@ void rlc_am_nr_rx::timer_expired(uint32_t timeout_id)
|
|
|
|
|
|
|
|
|
|
|
|
// Status Prohibit
|
|
|
|
// Status Prohibit
|
|
|
|
if (status_prohibit_timer.is_valid() && status_prohibit_timer.id() == timeout_id) {
|
|
|
|
if (status_prohibit_timer.is_valid() && status_prohibit_timer.id() == timeout_id) {
|
|
|
|
Debug("Status prohibit timer expired after %dms", status_prohibit_timer.duration());
|
|
|
|
RlcDebug("Status prohibit timer expired after %dms", status_prohibit_timer.duration());
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Reassembly
|
|
|
|
// Reassembly
|
|
|
|
if (reassembly_timer.is_valid() && reassembly_timer.id() == timeout_id) {
|
|
|
|
if (reassembly_timer.is_valid() && reassembly_timer.id() == timeout_id) {
|
|
|
|
Debug("Reassembly timer expired after %dms", reassembly_timer.duration());
|
|
|
|
RlcDebug("Reassembly timer expired after %dms", reassembly_timer.duration());
|
|
|
|
/*
|
|
|
|
/*
|
|
|
|
* 5.2.3.2.4 Actions when t-Reassembly expires:
|
|
|
|
* 5.2.3.2.4 Actions when t-Reassembly expires:
|
|
|
|
* - update RX_Highest_Status to the SN of the first RLC SDU with SN >= RX_Next_Status_Trigger for which not
|
|
|
|
* - update RX_Highest_Status to the SN of the first RLC SDU with SN >= RX_Next_Status_Trigger for which not
|
|
|
@ -1007,10 +1001,10 @@ bool rlc_am_nr_rx::have_all_segments_been_received(const std::list<rlc_amd_rx_pd
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
void rlc_am_nr_rx::debug_state()
|
|
|
|
void rlc_am_nr_rx::debug_state()
|
|
|
|
{
|
|
|
|
{
|
|
|
|
Debug("RX entity state: Rx_Next %d, Rx_Next_Status_Trigger %d, Rx_Highest_Status %d, Rx_Next_Highest",
|
|
|
|
RlcDebug("RX entity state: Rx_Next %d, Rx_Next_Status_Trigger %d, Rx_Highest_Status %d, Rx_Next_Highest",
|
|
|
|
st.rx_next,
|
|
|
|
st.rx_next,
|
|
|
|
st.rx_next_status_trigger,
|
|
|
|
st.rx_next_status_trigger,
|
|
|
|
st.rx_highest_status,
|
|
|
|
st.rx_highest_status,
|
|
|
|
st.rx_next_highest);
|
|
|
|
st.rx_next_highest);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} // namespace srsran
|
|
|
|
} // namespace srsran
|
|
|
|