mirror of https://github.com/PentHertz/srsLTE.git
Merge branch 'next' into agpl_next
This commit is contained in:
commit
e79aa61a9a
|
@ -102,7 +102,8 @@ enum class pdcp_t_reordering_t {
|
|||
ms2250 = 2250,
|
||||
ms2500 = 2500,
|
||||
ms2750 = 2750,
|
||||
ms3000 = 3000
|
||||
ms3000 = 3000,
|
||||
infinity = -1
|
||||
};
|
||||
|
||||
// Taken from PDCP-Config (TS 38.331 version 15.2.1)
|
||||
|
@ -122,7 +123,7 @@ enum class pdcp_discard_timer_t {
|
|||
ms500 = 500,
|
||||
ms750 = 750,
|
||||
ms1500 = 1500,
|
||||
infinity = 0
|
||||
infinity = -1
|
||||
};
|
||||
|
||||
class pdcp_config_t
|
||||
|
|
|
@ -104,6 +104,7 @@ public:
|
|||
bool configure(const rlc_config_t& cfg_) final;
|
||||
uint32_t read_pdu(uint8_t* payload, uint32_t nof_bytes) final;
|
||||
void handle_control_pdu(uint8_t* payload, uint32_t nof_bytes) final;
|
||||
void handle_nack(const rlc_status_nack_t& nack, std::set<uint32_t>& retx_sn_set);
|
||||
|
||||
void reestablish() final;
|
||||
void stop() final;
|
||||
|
|
|
@ -99,6 +99,8 @@ class rlc_am_nr_status_pdu_t
|
|||
private:
|
||||
/// Stored SN size required to compute the packed size
|
||||
rlc_am_nr_sn_size_t sn_size = rlc_am_nr_sn_size_t::nulltype;
|
||||
/// Stored modulus to determine continuous sequences across SN overflows
|
||||
uint32_t mod_nr = cardinality(rlc_am_nr_sn_size_t::nulltype);
|
||||
/// Internal NACK container; keep in sync with packed_size_
|
||||
std::vector<rlc_status_nack_t> nacks_ = {};
|
||||
/// Stores the current packed size; sync on each change of nacks_
|
||||
|
@ -119,6 +121,7 @@ public:
|
|||
|
||||
rlc_am_nr_status_pdu_t(rlc_am_nr_sn_size_t sn_size);
|
||||
void reset();
|
||||
bool is_continuous_sequence(const rlc_status_nack_t& left, const rlc_status_nack_t& right) const;
|
||||
void push_nack(const rlc_status_nack_t& nack);
|
||||
const std::vector<rlc_status_nack_t>& get_nacks() const { return nacks_; }
|
||||
uint32_t get_packed_size() const { return packed_size; }
|
||||
|
|
|
@ -174,6 +174,8 @@ public:
|
|||
|
||||
// NACK helper (for LTE and NR)
|
||||
struct rlc_status_nack_t {
|
||||
const static uint16_t so_end_of_sdu = 0xFFFF;
|
||||
|
||||
uint32_t nack_sn; // Sequence Number (SN) of first missing SDU
|
||||
bool has_so; // NACKs continuous sequence of bytes [so_start..so_end]
|
||||
uint16_t so_start; // First missing byte in SDU with SN=nack_sn
|
||||
|
@ -190,6 +192,15 @@ struct rlc_status_nack_t {
|
|||
has_nack_range = false;
|
||||
nack_range = 0;
|
||||
}
|
||||
|
||||
bool equals(const rlc_status_nack_t& other) const
|
||||
{
|
||||
return nack_sn == other.nack_sn && has_so == other.has_so && so_start == other.so_start && so_end == other.so_end &&
|
||||
has_nack_range == other.has_nack_range && nack_range == other.nack_range;
|
||||
}
|
||||
|
||||
bool operator==(const rlc_status_nack_t& other) const { return equals(other); }
|
||||
bool operator!=(const rlc_status_nack_t& other) const { return not equals(other); }
|
||||
};
|
||||
|
||||
// STATUS PDU
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#ifndef FMT_CORE_H_
|
||||
#define FMT_CORE_H_
|
||||
|
||||
#include <array>
|
||||
#include <cstdio> // std::FILE
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
|
|
|
@ -134,6 +134,12 @@ int make_rlc_config_t(const rlc_cfg_c& asn1_type, uint8_t bearer_id, rlc_config_
|
|||
default:
|
||||
break;
|
||||
}
|
||||
rlc_cfg.am_nr.t_poll_retx = asn1_type.am().ul_am_rlc.t_poll_retx.to_number();
|
||||
rlc_cfg.am_nr.poll_pdu = asn1_type.am().ul_am_rlc.poll_pdu.to_number();
|
||||
rlc_cfg.am_nr.poll_byte = asn1_type.am().ul_am_rlc.poll_byte.to_number();
|
||||
rlc_cfg.am_nr.max_retx_thresh = asn1_type.am().ul_am_rlc.max_retx_thres.to_number();
|
||||
rlc_cfg.am_nr.t_reassembly = asn1_type.am().dl_am_rlc.t_reassembly.to_number();
|
||||
rlc_cfg.am_nr.t_status_prohibit = asn1_type.am().dl_am_rlc.t_status_prohibit.to_number();
|
||||
break;
|
||||
case rlc_cfg_c::types_opts::um_bi_dir:
|
||||
rlc_cfg = rlc_config_t::default_rlc_um_nr_config();
|
||||
|
@ -157,6 +163,7 @@ int make_rlc_config_t(const rlc_cfg_c& asn1_type, uint8_t bearer_id, rlc_config_
|
|||
default:
|
||||
break;
|
||||
}
|
||||
rlc_cfg.um_nr.t_reassembly_ms = asn1_type.um_bi_dir().dl_um_rlc.t_reassembly.to_number();
|
||||
break;
|
||||
case rlc_cfg_c::types_opts::um_uni_dir_dl:
|
||||
asn1::log_warning("NR RLC type %s is not supported", asn1_type.type().to_string());
|
||||
|
@ -245,14 +252,119 @@ srsran::pdcp_config_t make_drb_pdcp_config_t(const uint8_t bearer_id, bool is_ue
|
|||
}
|
||||
}
|
||||
|
||||
pdcp_t_reordering_t t_reordering = pdcp_t_reordering_t::ms500;
|
||||
pdcp_t_reordering_t t_reordering = pdcp_t_reordering_t::infinity;
|
||||
if (pdcp_cfg.t_reordering_present) {
|
||||
switch (pdcp_cfg.t_reordering.to_number()) {
|
||||
case 0:
|
||||
t_reordering = pdcp_t_reordering_t::ms0;
|
||||
break;
|
||||
default:
|
||||
case 1:
|
||||
t_reordering = pdcp_t_reordering_t::ms1;
|
||||
break;
|
||||
case 2:
|
||||
t_reordering = pdcp_t_reordering_t::ms2;
|
||||
break;
|
||||
case 4:
|
||||
t_reordering = pdcp_t_reordering_t::ms4;
|
||||
break;
|
||||
case 5:
|
||||
t_reordering = pdcp_t_reordering_t::ms5;
|
||||
break;
|
||||
case 8:
|
||||
t_reordering = pdcp_t_reordering_t::ms8;
|
||||
break;
|
||||
case 10:
|
||||
t_reordering = pdcp_t_reordering_t::ms10;
|
||||
break;
|
||||
case 15:
|
||||
t_reordering = pdcp_t_reordering_t::ms15;
|
||||
break;
|
||||
case 20:
|
||||
t_reordering = pdcp_t_reordering_t::ms20;
|
||||
break;
|
||||
case 30:
|
||||
t_reordering = pdcp_t_reordering_t::ms30;
|
||||
break;
|
||||
case 40:
|
||||
t_reordering = pdcp_t_reordering_t::ms40;
|
||||
break;
|
||||
case 50:
|
||||
t_reordering = pdcp_t_reordering_t::ms50;
|
||||
break;
|
||||
case 60:
|
||||
t_reordering = pdcp_t_reordering_t::ms60;
|
||||
break;
|
||||
case 80:
|
||||
t_reordering = pdcp_t_reordering_t::ms80;
|
||||
break;
|
||||
case 100:
|
||||
t_reordering = pdcp_t_reordering_t::ms100;
|
||||
break;
|
||||
case 120:
|
||||
t_reordering = pdcp_t_reordering_t::ms120;
|
||||
break;
|
||||
case 140:
|
||||
t_reordering = pdcp_t_reordering_t::ms140;
|
||||
break;
|
||||
case 160:
|
||||
t_reordering = pdcp_t_reordering_t::ms160;
|
||||
break;
|
||||
case 180:
|
||||
t_reordering = pdcp_t_reordering_t::ms180;
|
||||
break;
|
||||
case 200:
|
||||
t_reordering = pdcp_t_reordering_t::ms200;
|
||||
break;
|
||||
case 220:
|
||||
t_reordering = pdcp_t_reordering_t::ms220;
|
||||
break;
|
||||
case 240:
|
||||
t_reordering = pdcp_t_reordering_t::ms240;
|
||||
break;
|
||||
case 260:
|
||||
t_reordering = pdcp_t_reordering_t::ms260;
|
||||
break;
|
||||
case 280:
|
||||
t_reordering = pdcp_t_reordering_t::ms280;
|
||||
break;
|
||||
case 300:
|
||||
t_reordering = pdcp_t_reordering_t::ms300;
|
||||
break;
|
||||
case 500:
|
||||
t_reordering = pdcp_t_reordering_t::ms500;
|
||||
break;
|
||||
case 750:
|
||||
t_reordering = pdcp_t_reordering_t::ms750;
|
||||
break;
|
||||
case 1000:
|
||||
t_reordering = pdcp_t_reordering_t::ms1000;
|
||||
break;
|
||||
case 1250:
|
||||
t_reordering = pdcp_t_reordering_t::ms1250;
|
||||
break;
|
||||
case 1500:
|
||||
t_reordering = pdcp_t_reordering_t::ms1500;
|
||||
break;
|
||||
case 1750:
|
||||
t_reordering = pdcp_t_reordering_t::ms1750;
|
||||
break;
|
||||
case 2000:
|
||||
t_reordering = pdcp_t_reordering_t::ms2000;
|
||||
break;
|
||||
case 2250:
|
||||
t_reordering = pdcp_t_reordering_t::ms2250;
|
||||
break;
|
||||
case 2500:
|
||||
t_reordering = pdcp_t_reordering_t::ms2500;
|
||||
break;
|
||||
case 2750:
|
||||
t_reordering = pdcp_t_reordering_t::ms2750;
|
||||
break;
|
||||
case 3000:
|
||||
t_reordering = pdcp_t_reordering_t::ms3000;
|
||||
break;
|
||||
default:
|
||||
t_reordering = pdcp_t_reordering_t::ms50;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -120,7 +120,12 @@ int pdcp::add_bearer(uint32_t lcid, const pdcp_config_t& cfg)
|
|||
if (cfg.rat == srsran::srsran_rat_t::lte) {
|
||||
entity.reset(new pdcp_entity_lte{rlc, rrc, gw, task_sched, logger, lcid});
|
||||
} else if (cfg.rat == srsran::srsran_rat_t::nr) {
|
||||
#ifdef USE_PDCP_NR
|
||||
#pragma message "Compiling with PDCP NR entity"
|
||||
entity.reset(new pdcp_entity_nr{rlc, rrc, gw, task_sched, logger, lcid});
|
||||
#else
|
||||
entity.reset(new pdcp_entity_lte{rlc, rrc, gw, task_sched, logger, lcid});
|
||||
#endif
|
||||
}
|
||||
|
||||
if (not entity->configure(cfg)) {
|
||||
|
|
|
@ -73,6 +73,12 @@ bool pdcp_entity_nr::configure(const pdcp_config_t& cnfg_)
|
|||
reordering_timer.set(static_cast<uint32_t>(cfg.t_reordering), *reordering_fnc);
|
||||
}
|
||||
active = true;
|
||||
logger.info("%s PDCP-NR entity configured. SN_LEN=%d, Discard timer %d, Re-ordering timer %d, RAT=%s",
|
||||
rb_name,
|
||||
cfg.sn_len,
|
||||
cfg.discard_timer,
|
||||
cfg.t_reordering,
|
||||
to_string(cfg.rat));
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,6 @@
|
|||
namespace srsran {
|
||||
|
||||
const static uint32_t max_tx_queue_size = 256;
|
||||
const static uint32_t so_end_of_sdu = 0xFFFF;
|
||||
|
||||
/****************************************************************************
|
||||
* RLC AM NR entity
|
||||
|
@ -837,106 +836,30 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes)
|
|||
std::set<uint32_t> retx_sn_set; // Set of PDU SNs added for retransmission (no duplicates)
|
||||
for (uint32_t nack_idx = 0; nack_idx < status.nacks.size(); nack_idx++) {
|
||||
if (status.nacks[nack_idx].has_nack_range) {
|
||||
RlcWarning("Handling NACK ranges is not yet implemented. Ignoring NACK across %d SDU(s) starting from SN=%d",
|
||||
status.nacks[nack_idx].nack_range,
|
||||
status.nacks[nack_idx].nack_sn);
|
||||
continue;
|
||||
}
|
||||
if (tx_mod_base_nr(st.tx_next_ack) <= tx_mod_base_nr(status.nacks[nack_idx].nack_sn) &&
|
||||
tx_mod_base_nr(status.nacks[nack_idx].nack_sn) <= tx_mod_base_nr(st.tx_next)) {
|
||||
RlcDebug("Handling NACK for SN=%d", status.nacks[nack_idx].nack_sn);
|
||||
auto nack = status.nacks[nack_idx];
|
||||
uint32_t nack_sn = nack.nack_sn;
|
||||
if (tx_window->has_sn(nack_sn)) {
|
||||
auto& pdu = (*tx_window)[nack_sn];
|
||||
|
||||
if (nack.has_so) {
|
||||
// NACK'ing missing bytes in SDU segment.
|
||||
// Retransmit all SDU segments within those missing bytes.
|
||||
if (pdu.segment_list.empty()) {
|
||||
RlcError("Received NACK with SO, but there is no segment information. SN=%d", nack_sn);
|
||||
}
|
||||
bool segment_found = false;
|
||||
for (const rlc_amd_tx_pdu_nr::pdu_segment& segm : pdu.segment_list) {
|
||||
if (segm.so >= nack.so_start && segm.so <= nack.so_end) {
|
||||
if (not retx_queue->has_sn(nack_sn, segm.so)) {
|
||||
rlc_amd_retx_nr_t& retx = retx_queue->push();
|
||||
retx.sn = nack_sn;
|
||||
retx.is_segment = true;
|
||||
retx.so_start = segm.so;
|
||||
retx.current_so = segm.so;
|
||||
retx.segment_length = segm.payload_len;
|
||||
retx_sn_set.insert(nack_sn);
|
||||
RlcInfo("Scheduled RETX of SDU segment SN=%d, so_start=%d, segment_length=%d",
|
||||
retx.sn,
|
||||
retx.so_start,
|
||||
retx.segment_length);
|
||||
} else {
|
||||
RlcInfo("Skip already scheduled RETX of SDU segment SN=%d, so_start=%d, segment_length=%d",
|
||||
nack_sn,
|
||||
segm.so,
|
||||
segm.payload_len);
|
||||
}
|
||||
segment_found = true;
|
||||
for (uint32_t range_sn = status.nacks[nack_idx].nack_sn;
|
||||
range_sn < status.nacks[nack_idx].nack_sn + status.nacks[nack_idx].nack_range;
|
||||
range_sn++) {
|
||||
rlc_status_nack_t nack = {};
|
||||
nack.nack_sn = range_sn;
|
||||
if (status.nacks[nack_idx].has_so) {
|
||||
if (range_sn == status.nacks[nack_idx].nack_sn) {
|
||||
// First SN
|
||||
nack.has_so = true;
|
||||
nack.so_start = status.nacks[nack_idx].so_start;
|
||||
nack.so_end = rlc_status_nack_t::so_end_of_sdu;
|
||||
} else if (range_sn == (status.nacks[nack_idx].nack_sn + status.nacks[nack_idx].nack_range - 1)) {
|
||||
// Last SN
|
||||
nack.has_so = true;
|
||||
// This might be first+last item at the same time, so don't change so_start here
|
||||
nack.so_end = status.nacks[nack_idx].so_end;
|
||||
}
|
||||
}
|
||||
if (!segment_found) {
|
||||
RlcWarning("Could not find segment for NACK_SN=%d. SO_start=%d, SO_end=%d",
|
||||
status.nacks[nack_idx].nack_sn,
|
||||
nack.so_start,
|
||||
nack.so_end);
|
||||
for (const rlc_amd_tx_pdu_nr::pdu_segment& segm : pdu.segment_list) {
|
||||
RlcDebug(
|
||||
"Segments for SN=%d. SO=%d, SO_end=%d", status.nacks[nack_idx].nack_sn, segm.so, segm.payload_len);
|
||||
}
|
||||
handle_nack(nack, retx_sn_set);
|
||||
}
|
||||
} else {
|
||||
// NACK'ing full SDU.
|
||||
// add to retx queue if it's not already there
|
||||
if (not retx_queue->has_sn(nack_sn)) {
|
||||
// Have we segmented the SDU already?
|
||||
if ((*tx_window)[nack_sn].segment_list.empty()) {
|
||||
rlc_amd_retx_nr_t& retx = retx_queue->push();
|
||||
retx.sn = nack_sn;
|
||||
retx.is_segment = false;
|
||||
retx.so_start = 0;
|
||||
retx.current_so = 0;
|
||||
retx.segment_length = pdu.sdu_buf->N_bytes;
|
||||
retx_sn_set.insert(nack_sn);
|
||||
RlcInfo("Scheduled RETX of SDU SN=%d", retx.sn);
|
||||
} else {
|
||||
RlcInfo("Scheduled RETX of SDU SN=%d", nack_sn);
|
||||
retx_sn_set.insert(nack_sn);
|
||||
for (auto segm : (*tx_window)[nack_sn].segment_list) {
|
||||
rlc_amd_retx_nr_t& retx = retx_queue->push();
|
||||
retx.sn = nack_sn;
|
||||
retx.is_segment = true;
|
||||
retx.so_start = segm.so;
|
||||
retx.current_so = segm.so;
|
||||
retx.segment_length = segm.payload_len;
|
||||
RlcInfo("Scheduled RETX of SDU Segment. SN=%d, SO=%d, len=%d", retx.sn, segm.so, segm.payload_len);
|
||||
handle_nack(status.nacks[nack_idx], retx_sn_set);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
RlcInfo("RETX queue already has NACK_SN. SDU SN=%d, Tx_Next_Ack=%d, Tx_Next=%d",
|
||||
status.nacks[nack_idx].nack_sn,
|
||||
st.tx_next_ack,
|
||||
st.tx_next);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
RlcInfo("TX window does not contain NACK_SN. SDU SN=%d, Tx_Next_Ack=%d, Tx_Next=%d",
|
||||
status.nacks[nack_idx].nack_sn,
|
||||
st.tx_next_ack,
|
||||
st.tx_next);
|
||||
} // TX window containts NACK SN
|
||||
} else {
|
||||
RlcInfo("RETX not in expected range. SDU SN=%d, Tx_Next_Ack=%d, Tx_Next=%d",
|
||||
status.nacks[nack_idx].nack_sn,
|
||||
st.tx_next_ack,
|
||||
st.tx_next);
|
||||
} // NACK SN within expected range
|
||||
} // NACK loop
|
||||
|
||||
// Process retx_count and inform upper layers if needed
|
||||
for (uint32_t retx_sn : retx_sn_set) {
|
||||
|
@ -961,6 +884,98 @@ void rlc_am_nr_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes)
|
|||
notify_info_vec.clear();
|
||||
}
|
||||
|
||||
void rlc_am_nr_tx::handle_nack(const rlc_status_nack_t& nack, std::set<uint32_t>& retx_sn_set)
|
||||
{
|
||||
if (tx_mod_base_nr(st.tx_next_ack) <= tx_mod_base_nr(nack.nack_sn) &&
|
||||
tx_mod_base_nr(nack.nack_sn) <= tx_mod_base_nr(st.tx_next)) {
|
||||
RlcDebug("Handling NACK for SN=%d", nack.nack_sn);
|
||||
if (tx_window->has_sn(nack.nack_sn)) {
|
||||
auto& pdu = (*tx_window)[nack.nack_sn];
|
||||
|
||||
if (nack.has_so) {
|
||||
// NACK'ing missing bytes in SDU segment.
|
||||
// Retransmit all SDU segments within those missing bytes.
|
||||
if (pdu.segment_list.empty()) {
|
||||
RlcError("Received NACK with SO, but there is no segment information. SN=%d", nack.nack_sn);
|
||||
}
|
||||
bool segment_found = false;
|
||||
for (const rlc_amd_tx_pdu_nr::pdu_segment& segm : pdu.segment_list) {
|
||||
if (segm.so >= nack.so_start && segm.so <= nack.so_end) {
|
||||
if (not retx_queue->has_sn(nack.nack_sn, segm.so)) {
|
||||
rlc_amd_retx_nr_t& retx = retx_queue->push();
|
||||
retx.sn = nack.nack_sn;
|
||||
retx.is_segment = true;
|
||||
retx.so_start = segm.so;
|
||||
retx.current_so = segm.so;
|
||||
retx.segment_length = segm.payload_len;
|
||||
retx_sn_set.insert(nack.nack_sn);
|
||||
RlcInfo("Scheduled RETX of SDU segment SN=%d, so_start=%d, segment_length=%d",
|
||||
retx.sn,
|
||||
retx.so_start,
|
||||
retx.segment_length);
|
||||
} else {
|
||||
RlcInfo("Skip already scheduled RETX of SDU segment SN=%d, so_start=%d, segment_length=%d",
|
||||
nack.nack_sn,
|
||||
segm.so,
|
||||
segm.payload_len);
|
||||
}
|
||||
segment_found = true;
|
||||
}
|
||||
}
|
||||
if (!segment_found) {
|
||||
RlcWarning("Could not find segment for NACK_SN=%d. SO_start=%d, SO_end=%d",
|
||||
nack.nack_sn,
|
||||
nack.so_start,
|
||||
nack.so_end);
|
||||
for (const rlc_amd_tx_pdu_nr::pdu_segment& segm : pdu.segment_list) {
|
||||
RlcDebug("Segments for SN=%d. SO=%d, SO_end=%d", nack.nack_sn, segm.so, segm.payload_len);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// NACK'ing full SDU.
|
||||
// add to retx queue if it's not already there
|
||||
if (not retx_queue->has_sn(nack.nack_sn)) {
|
||||
// Have we segmented the SDU already?
|
||||
if ((*tx_window)[nack.nack_sn].segment_list.empty()) {
|
||||
rlc_amd_retx_nr_t& retx = retx_queue->push();
|
||||
retx.sn = nack.nack_sn;
|
||||
retx.is_segment = false;
|
||||
retx.so_start = 0;
|
||||
retx.current_so = 0;
|
||||
retx.segment_length = pdu.sdu_buf->N_bytes;
|
||||
retx_sn_set.insert(nack.nack_sn);
|
||||
RlcInfo("Scheduled RETX of SDU SN=%d", retx.sn);
|
||||
} else {
|
||||
RlcInfo("Scheduled RETX of SDU SN=%d", nack.nack_sn);
|
||||
retx_sn_set.insert(nack.nack_sn);
|
||||
for (auto segm : (*tx_window)[nack.nack_sn].segment_list) {
|
||||
rlc_amd_retx_nr_t& retx = retx_queue->push();
|
||||
retx.sn = nack.nack_sn;
|
||||
retx.is_segment = true;
|
||||
retx.so_start = segm.so;
|
||||
retx.current_so = segm.so;
|
||||
retx.segment_length = segm.payload_len;
|
||||
RlcInfo("Scheduled RETX of SDU Segment. SN=%d, SO=%d, len=%d", retx.sn, segm.so, segm.payload_len);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
RlcInfo("RETX queue already has NACK_SN. SDU SN=%d, Tx_Next_Ack=%d, Tx_Next=%d",
|
||||
nack.nack_sn,
|
||||
st.tx_next_ack,
|
||||
st.tx_next);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
RlcInfo("TX window does not contain NACK_SN. SDU SN=%d, Tx_Next_Ack=%d, Tx_Next=%d",
|
||||
nack.nack_sn,
|
||||
st.tx_next_ack,
|
||||
st.tx_next);
|
||||
} // TX window containts NACK SN
|
||||
} else {
|
||||
RlcInfo(
|
||||
"RETX not in expected range. SDU SN=%d, Tx_Next_Ack=%d, Tx_Next=%d", nack.nack_sn, st.tx_next_ack, st.tx_next);
|
||||
} // NACK SN within expected range
|
||||
}
|
||||
/**
|
||||
* Helper to check if a SN has reached the max reTx threshold
|
||||
*
|
||||
|
@ -1736,7 +1751,7 @@ uint32_t rlc_am_nr_rx::get_status_pdu(rlc_am_nr_status_pdu_t* status, uint32_t m
|
|||
nack.nack_sn = i;
|
||||
nack.has_so = true;
|
||||
nack.so_start = last_so;
|
||||
nack.so_end = so_end_of_sdu;
|
||||
nack.so_end = rlc_status_nack_t::so_end_of_sdu;
|
||||
status->push_nack(nack);
|
||||
RlcDebug(
|
||||
"Final segment missing. NACK_SN=%d. SO_start=%d, SO_end=%d", nack.nack_sn, nack.so_start, nack.so_end);
|
||||
|
|
|
@ -28,7 +28,8 @@ namespace srsran {
|
|||
* Container implementation for pack/unpack functions
|
||||
***************************************************************************/
|
||||
|
||||
rlc_am_nr_status_pdu_t::rlc_am_nr_status_pdu_t(rlc_am_nr_sn_size_t sn_size) : sn_size(sn_size)
|
||||
rlc_am_nr_status_pdu_t::rlc_am_nr_status_pdu_t(rlc_am_nr_sn_size_t sn_size) :
|
||||
sn_size(sn_size), mod_nr(cardinality(sn_size))
|
||||
{
|
||||
nacks_.reserve(RLC_AM_NR_TYP_NACKS);
|
||||
}
|
||||
|
@ -41,10 +42,85 @@ void rlc_am_nr_status_pdu_t::reset()
|
|||
packed_size_ = rlc_am_nr_status_pdu_sizeof_header_ack_sn;
|
||||
}
|
||||
|
||||
bool rlc_am_nr_status_pdu_t::is_continuous_sequence(const rlc_status_nack_t& left, const rlc_status_nack_t& right) const
|
||||
{
|
||||
// SN must be continuous
|
||||
if (right.nack_sn != ((left.has_nack_range ? left.nack_sn + left.nack_range : (left.nack_sn + 1)) % mod_nr)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Segments on left side (if present) must reach the end of sdu
|
||||
if (left.has_so && left.so_end != rlc_status_nack_t::so_end_of_sdu) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Segments on right side (if present) must start from the beginning
|
||||
if (right.has_so && right.so_start != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void rlc_am_nr_status_pdu_t::push_nack(const rlc_status_nack_t& nack)
|
||||
{
|
||||
if (nacks_.size() == 0) {
|
||||
nacks_.push_back(nack);
|
||||
packed_size_ += nack_size(nack);
|
||||
return;
|
||||
}
|
||||
|
||||
rlc_status_nack_t& prev = nacks_.back();
|
||||
if (is_continuous_sequence(prev, nack) == false) {
|
||||
nacks_.push_back(nack);
|
||||
packed_size_ += nack_size(nack);
|
||||
return;
|
||||
}
|
||||
|
||||
// expand previous NACK
|
||||
// subtract size of previous NACK (add updated size later)
|
||||
packed_size_ -= nack_size(prev);
|
||||
|
||||
// enable and update NACK range
|
||||
if (nack.has_nack_range == true) {
|
||||
if (prev.has_nack_range == true) {
|
||||
// [NACK range][NACK range]
|
||||
prev.nack_range += nack.nack_range;
|
||||
} else {
|
||||
// [NACK SDU][NACK range]
|
||||
prev.nack_range = nack.nack_range + 1;
|
||||
prev.has_nack_range = true;
|
||||
}
|
||||
} else {
|
||||
if (prev.has_nack_range == true) {
|
||||
// [NACK range][NACK SDU]
|
||||
prev.nack_range++;
|
||||
} else {
|
||||
// [NACK SDU][NACK SDU]
|
||||
prev.nack_range = 2;
|
||||
prev.has_nack_range = true;
|
||||
}
|
||||
}
|
||||
|
||||
// enable and update segment offsets (if required)
|
||||
if (nack.has_so == true) {
|
||||
if (prev.has_so == false) {
|
||||
// [NACK SDU][NACK segm]
|
||||
prev.has_so = true;
|
||||
prev.so_start = 0;
|
||||
}
|
||||
// [NACK SDU][NACK segm] or [NACK segm][NACK segm]
|
||||
prev.so_end = nack.so_end;
|
||||
} else {
|
||||
if (prev.has_so == true) {
|
||||
// [NACK segm][NACK SDU]
|
||||
prev.so_end = rlc_status_nack_t::so_end_of_sdu;
|
||||
}
|
||||
// [NACK segm][NACK SDU] or [NACK SDU][NACK SDU]
|
||||
}
|
||||
|
||||
// add updated size
|
||||
packed_size_ += nack_size(prev);
|
||||
}
|
||||
|
||||
bool rlc_am_nr_status_pdu_t::trim(uint32_t max_packed_size)
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -2471,6 +2471,193 @@ bool poll_retx_expiry(rlc_am_nr_sn_size_t sn_size)
|
|||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
int rx_nack_range_no_so_test(rlc_am_nr_sn_size_t sn_size)
|
||||
{
|
||||
rlc_am_tester tester;
|
||||
timer_handler timers(8);
|
||||
|
||||
auto& test_logger = srslog::fetch_basic_logger("TESTER ");
|
||||
rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers);
|
||||
rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers);
|
||||
std::string str = "Rx NACK range test (" + std::to_string(to_number(sn_size)) + " bit SN)";
|
||||
test_delimit_logger delimiter(str.c_str());
|
||||
|
||||
rlc_am_nr_tx* tx1 = dynamic_cast<rlc_am_nr_tx*>(rlc1.get_tx());
|
||||
rlc_am_nr_rx* rx1 = dynamic_cast<rlc_am_nr_rx*>(rlc1.get_rx());
|
||||
rlc_am_nr_tx* tx2 = dynamic_cast<rlc_am_nr_tx*>(rlc2.get_tx());
|
||||
rlc_am_nr_rx* rx2 = dynamic_cast<rlc_am_nr_rx*>(rlc2.get_rx());
|
||||
|
||||
auto rlc_cnfg = rlc_config_t::default_rlc_am_nr_config(to_number(sn_size));
|
||||
rlc_cnfg.am_nr.t_poll_retx = -1;
|
||||
if (not rlc1.configure(rlc_cnfg)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// after configuring entity
|
||||
TESTASSERT(0 == rlc1.get_buffer_state());
|
||||
|
||||
int n_sdu_bufs = 5;
|
||||
int n_pdu_bufs = 15;
|
||||
|
||||
// Push 5 SDUs into RLC1
|
||||
std::vector<unique_byte_buffer_t> sdu_bufs(n_sdu_bufs);
|
||||
constexpr uint32_t payload_size = 3; // Give the SDU the size of 3 bytes
|
||||
uint32_t header_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3;
|
||||
for (int i = 0; i < n_sdu_bufs; 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 = payload_size; // Give each buffer a size of 3 bytes
|
||||
sdu_bufs[i]->md.pdcp_sn = i; // PDCP SN for notifications
|
||||
rlc1.write_sdu(std::move(sdu_bufs[i]));
|
||||
}
|
||||
|
||||
uint32_t expected_buffer_state = (header_size + payload_size) * n_sdu_bufs;
|
||||
TESTASSERT_EQ(expected_buffer_state, rlc1.get_buffer_state());
|
||||
|
||||
constexpr uint32_t so_size = 2;
|
||||
constexpr uint32_t segment_size = 1;
|
||||
uint32_t pdu_size_first = header_size + segment_size;
|
||||
uint32_t pdu_size_continued = header_size + so_size + segment_size;
|
||||
|
||||
// Read 15 PDUs from RLC1
|
||||
std::vector<unique_byte_buffer_t> pdu_bufs(n_pdu_bufs);
|
||||
for (int i = 0; i < n_pdu_bufs; i++) {
|
||||
// First also test buffer state
|
||||
uint32_t remaining_total_bytes = (payload_size * n_sdu_bufs) - (i * segment_size);
|
||||
uint32_t remaining_full_sdus = remaining_total_bytes / payload_size;
|
||||
uint32_t remaining_seg_bytes = remaining_total_bytes % payload_size;
|
||||
|
||||
uint32_t buffer_state_full_sdus = (header_size + payload_size) * remaining_full_sdus;
|
||||
uint32_t buffer_state_seg_sdu = remaining_seg_bytes == 0 ? 0 : (header_size + so_size + remaining_seg_bytes);
|
||||
expected_buffer_state = buffer_state_full_sdus + buffer_state_seg_sdu;
|
||||
TESTASSERT_EQ(expected_buffer_state, rlc1.get_buffer_state());
|
||||
|
||||
pdu_bufs[i] = srsran::make_byte_buffer();
|
||||
if (i == 0 || i == 3 || i == 6 || i == 9 || i == 12) {
|
||||
// First segment, no SO
|
||||
uint32_t len = rlc1.read_pdu(pdu_bufs[i]->msg, pdu_size_first); // 2 bytes for header + 1 byte payload
|
||||
pdu_bufs[i]->N_bytes = len;
|
||||
TESTASSERT_EQ(pdu_size_first, len);
|
||||
} else {
|
||||
// Middle or last segment, SO present
|
||||
uint32_t len = rlc1.read_pdu(pdu_bufs[i]->msg, pdu_size_continued); // 4 bytes for header + 1 byte payload
|
||||
pdu_bufs[i]->N_bytes = len;
|
||||
TESTASSERT_EQ(pdu_size_continued, len);
|
||||
}
|
||||
}
|
||||
|
||||
// Deliver dummy status report with nack range betwen PDU 6 and 10.
|
||||
rlc_am_nr_status_pdu_t status(sn_size);
|
||||
|
||||
rlc_status_nack_t nack = {};
|
||||
nack.nack_sn = 1;
|
||||
nack.has_nack_range = true;
|
||||
nack.nack_range = 3;
|
||||
status.push_nack(nack);
|
||||
byte_buffer_t status_pdu;
|
||||
rlc_am_nr_write_status_pdu(status, sn_size, &status_pdu);
|
||||
|
||||
rlc1.write_pdu(status_pdu.msg, status_pdu.N_bytes);
|
||||
|
||||
TESTASSERT_EQ(3 * pdu_size_first + 6 * pdu_size_continued, rlc1.get_buffer_state());
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
int rx_nack_range_with_so_test(rlc_am_nr_sn_size_t sn_size)
|
||||
{
|
||||
rlc_am_tester tester;
|
||||
timer_handler timers(8);
|
||||
|
||||
auto& test_logger = srslog::fetch_basic_logger("TESTER ");
|
||||
rlc_am rlc1(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers);
|
||||
rlc_am rlc2(srsran_rat_t::nr, srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers);
|
||||
std::string str = "Rx NACK range test (" + std::to_string(to_number(sn_size)) + " bit SN)";
|
||||
test_delimit_logger delimiter(str.c_str());
|
||||
|
||||
rlc_am_nr_tx* tx1 = dynamic_cast<rlc_am_nr_tx*>(rlc1.get_tx());
|
||||
rlc_am_nr_rx* rx1 = dynamic_cast<rlc_am_nr_rx*>(rlc1.get_rx());
|
||||
rlc_am_nr_tx* tx2 = dynamic_cast<rlc_am_nr_tx*>(rlc2.get_tx());
|
||||
rlc_am_nr_rx* rx2 = dynamic_cast<rlc_am_nr_rx*>(rlc2.get_rx());
|
||||
|
||||
auto rlc_cnfg = rlc_config_t::default_rlc_am_nr_config(to_number(sn_size));
|
||||
rlc_cnfg.am_nr.t_poll_retx = -1;
|
||||
if (not rlc1.configure(rlc_cnfg)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// after configuring entity
|
||||
TESTASSERT(0 == rlc1.get_buffer_state());
|
||||
|
||||
int n_sdu_bufs = 5;
|
||||
int n_pdu_bufs = 15;
|
||||
|
||||
// Push 5 SDUs into RLC1
|
||||
std::vector<unique_byte_buffer_t> sdu_bufs(n_sdu_bufs);
|
||||
constexpr uint32_t payload_size = 3; // Give the SDU the size of 3 bytes
|
||||
uint32_t header_size = sn_size == rlc_am_nr_sn_size_t::size12bits ? 2 : 3;
|
||||
for (int i = 0; i < n_sdu_bufs; 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 = payload_size; // Give each buffer a size of 3 bytes
|
||||
sdu_bufs[i]->md.pdcp_sn = i; // PDCP SN for notifications
|
||||
rlc1.write_sdu(std::move(sdu_bufs[i]));
|
||||
}
|
||||
|
||||
uint32_t expected_buffer_state = (header_size + payload_size) * n_sdu_bufs;
|
||||
TESTASSERT_EQ(expected_buffer_state, rlc1.get_buffer_state());
|
||||
|
||||
constexpr uint32_t so_size = 2;
|
||||
constexpr uint32_t segment_size = 1;
|
||||
uint32_t pdu_size_first = header_size + segment_size;
|
||||
uint32_t pdu_size_continued = header_size + so_size + segment_size;
|
||||
|
||||
// Read 15 PDUs from RLC1
|
||||
std::vector<unique_byte_buffer_t> pdu_bufs(n_pdu_bufs);
|
||||
for (int i = 0; i < n_pdu_bufs; i++) {
|
||||
// First also test buffer state
|
||||
uint32_t remaining_total_bytes = (payload_size * n_sdu_bufs) - (i * segment_size);
|
||||
uint32_t remaining_full_sdus = remaining_total_bytes / payload_size;
|
||||
uint32_t remaining_seg_bytes = remaining_total_bytes % payload_size;
|
||||
|
||||
uint32_t buffer_state_full_sdus = (header_size + payload_size) * remaining_full_sdus;
|
||||
uint32_t buffer_state_seg_sdu = remaining_seg_bytes == 0 ? 0 : (header_size + so_size + remaining_seg_bytes);
|
||||
expected_buffer_state = buffer_state_full_sdus + buffer_state_seg_sdu;
|
||||
TESTASSERT_EQ(expected_buffer_state, rlc1.get_buffer_state());
|
||||
|
||||
pdu_bufs[i] = srsran::make_byte_buffer();
|
||||
if (i == 0 || i == 3 || i == 6 || i == 9 || i == 12) {
|
||||
// First segment, no SO
|
||||
uint32_t len = rlc1.read_pdu(pdu_bufs[i]->msg, pdu_size_first); // 2 bytes for header + 1 byte payload
|
||||
pdu_bufs[i]->N_bytes = len;
|
||||
TESTASSERT_EQ(pdu_size_first, len);
|
||||
} else {
|
||||
// Middle or last segment, SO present
|
||||
uint32_t len = rlc1.read_pdu(pdu_bufs[i]->msg, pdu_size_continued); // 4 bytes for header + 1 byte payload
|
||||
pdu_bufs[i]->N_bytes = len;
|
||||
TESTASSERT_EQ(pdu_size_continued, len);
|
||||
}
|
||||
}
|
||||
|
||||
// Deliver dummy status report with nack range betwen PDU 6 and 10.
|
||||
rlc_am_nr_status_pdu_t status(sn_size);
|
||||
|
||||
rlc_status_nack_t nack = {};
|
||||
nack.nack_sn = 1;
|
||||
nack.has_nack_range = true;
|
||||
nack.nack_range = 3;
|
||||
nack.has_so = true;
|
||||
nack.so_start = 2;
|
||||
nack.so_end = 0;
|
||||
status.push_nack(nack);
|
||||
byte_buffer_t status_pdu;
|
||||
rlc_am_nr_write_status_pdu(status, sn_size, &status_pdu);
|
||||
|
||||
rlc1.write_pdu(status_pdu.msg, status_pdu.N_bytes);
|
||||
|
||||
TESTASSERT_EQ(2 * pdu_size_first + 3 * pdu_size_continued, rlc1.get_buffer_state());
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
// Setup the log message spy to intercept error and warning log entries from RLC
|
||||
|
@ -2515,6 +2702,8 @@ int main()
|
|||
TESTASSERT(poll_byte(sn_size) == SRSRAN_SUCCESS);
|
||||
TESTASSERT(poll_retx(sn_size) == SRSRAN_SUCCESS);
|
||||
TESTASSERT(poll_retx_expiry(sn_size) == SRSRAN_SUCCESS);
|
||||
TESTASSERT(rx_nack_range_no_so_test(sn_size) == SRSRAN_SUCCESS);
|
||||
TESTASSERT(rx_nack_range_with_so_test(sn_size) == SRSRAN_SUCCESS);
|
||||
}
|
||||
return SRSRAN_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -95,6 +95,40 @@ qci_config = (
|
|||
);
|
||||
|
||||
// 5G Section
|
||||
// srb1_5g_config = {
|
||||
// rlc_config = {
|
||||
// ul_am = {
|
||||
// sn_field_len = 12;
|
||||
// t_poll_retx = 50;
|
||||
// poll_pdu = 4;
|
||||
// poll_byte = 3000;
|
||||
// max_retx_thres = 4;
|
||||
// };
|
||||
// dl_am = {
|
||||
// sn_field_len = 12;
|
||||
// t_reassembly = 50;
|
||||
// t_status_prohibit = 50;
|
||||
// };
|
||||
// };
|
||||
// }
|
||||
|
||||
// srb2_5g_config = {
|
||||
// rlc_config = {
|
||||
// ul_am = {
|
||||
// sn_field_len = 12;
|
||||
// t_poll_retx = 50;
|
||||
// poll_pdu = 4;
|
||||
// poll_byte = 3000;
|
||||
// max_retx_thres = 4;
|
||||
// };
|
||||
// dl_am = {
|
||||
// sn_field_len = 12;
|
||||
// t_reassembly = 50;
|
||||
// t_status_prohibit = 50;
|
||||
// };
|
||||
// };
|
||||
// }
|
||||
|
||||
five_qi_config = (
|
||||
{
|
||||
five_qi = 7;
|
||||
|
|
|
@ -644,6 +644,87 @@ int field_qci::parse(libconfig::Setting& root)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int field_5g_srb::parse(libconfig::Setting& root)
|
||||
{
|
||||
// Parse RLC AM section
|
||||
asn1::rrc_nr::rlc_cfg_c* rlc_cfg = &cfg.rlc_cfg;
|
||||
if (root.exists("ul_am") && root.exists("dl_am")) {
|
||||
rlc_cfg->set_am();
|
||||
cfg.present = true;
|
||||
}
|
||||
|
||||
// RLC-UM must not exist in this section
|
||||
if (root.exists("ul_um") || root.exists("dl_um")) {
|
||||
ERROR("Error SRBs must be AM.");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
// Parse RLC-AM section
|
||||
if (root.exists("ul_am")) {
|
||||
asn1::rrc_nr::ul_am_rlc_s& ul_am_rlc = rlc_cfg->am().ul_am_rlc;
|
||||
|
||||
// SN length
|
||||
field_asn1_enum_number<asn1::rrc_nr::sn_field_len_am_e> rlc_sn_size_ul("sn_field_len", &ul_am_rlc.sn_field_len);
|
||||
if (rlc_sn_size_ul.parse(root["ul_am"]) == SRSRAN_ERROR) {
|
||||
ul_am_rlc.sn_field_len_present = false;
|
||||
} else {
|
||||
ul_am_rlc.sn_field_len_present = true;
|
||||
}
|
||||
|
||||
field_asn1_enum_number<asn1::rrc_nr::t_poll_retx_e> t_poll_retx("t_poll_retx", &ul_am_rlc.t_poll_retx);
|
||||
if (t_poll_retx.parse(root["ul_am"])) {
|
||||
ERROR("Error can't find t_poll_retx in section ul_am");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
field_asn1_enum_number<asn1::rrc_nr::poll_pdu_e> poll_pdu("poll_pdu", &ul_am_rlc.poll_pdu);
|
||||
if (poll_pdu.parse(root["ul_am"])) {
|
||||
ERROR("Error can't find poll_pdu in section ul_am");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
field_asn1_enum_number<asn1::rrc_nr::poll_byte_e> poll_byte("poll_byte", &ul_am_rlc.poll_byte);
|
||||
if (poll_byte.parse(root["ul_am"])) {
|
||||
ERROR("Error can't find poll_byte in section ul_am");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
field_asn1_enum_number<asn1::rrc_nr::ul_am_rlc_s::max_retx_thres_e_> max_retx_thresh("max_retx_thres",
|
||||
&ul_am_rlc.max_retx_thres);
|
||||
if (max_retx_thresh.parse(root["ul_am"])) {
|
||||
ERROR("Error can't find max_retx_thresh in section ul_am");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
if (root.exists("dl_am")) {
|
||||
asn1::rrc_nr::dl_am_rlc_s& dl_am_rlc = rlc_cfg->am().dl_am_rlc;
|
||||
|
||||
// SN length
|
||||
field_asn1_enum_number<asn1::rrc_nr::sn_field_len_am_e> rlc_sn_size_ul("sn_field_len", &dl_am_rlc.sn_field_len);
|
||||
if (rlc_sn_size_ul.parse(root["dl_am"]) == SRSRAN_ERROR) {
|
||||
dl_am_rlc.sn_field_len_present = false;
|
||||
} else {
|
||||
dl_am_rlc.sn_field_len_present = true;
|
||||
}
|
||||
|
||||
field_asn1_enum_number<asn1::rrc_nr::t_reassembly_e> t_reassembly("t_reassembly", &dl_am_rlc.t_reassembly);
|
||||
if (t_reassembly.parse(root["dl_am"])) {
|
||||
ERROR("Error can't find t_reordering in section dl_am");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
|
||||
field_asn1_enum_number<asn1::rrc_nr::t_status_prohibit_e> t_status_prohibit("t_status_prohibit",
|
||||
&dl_am_rlc.t_status_prohibit);
|
||||
if (t_status_prohibit.parse(root["dl_am"])) {
|
||||
ERROR("Error can't find t_status_prohibit in section dl_am");
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int field_five_qi::parse(libconfig::Setting& root)
|
||||
{
|
||||
uint32_t nof_five_qi = (uint32_t)root.getLength();
|
||||
|
@ -2360,6 +2441,22 @@ int parse_rb(all_args_t* args_, rrc_cfg_t* rrc_cfg_, rrc_nr_cfg_t* rrc_nr_cfg_)
|
|||
parser::section qci("qci_config");
|
||||
qci.add_field(new field_qci(rrc_cfg_->qci_cfg));
|
||||
|
||||
parser::section srb1_5g("srb1_5g_config");
|
||||
bool srb1_5g_present = false;
|
||||
srb1_5g.set_optional(&srb1_5g_present);
|
||||
|
||||
parser::section srb1_5g_rlc_cfg("rlc_config");
|
||||
srb1_5g.add_subsection(&srb1_5g_rlc_cfg);
|
||||
srb1_5g_rlc_cfg.add_field(new field_5g_srb(rrc_nr_cfg_->srb1_cfg));
|
||||
|
||||
parser::section srb2_5g("srb2_5g_config");
|
||||
bool srb2_5g_present = false;
|
||||
srb2_5g.set_optional(&srb2_5g_present);
|
||||
|
||||
parser::section srb2_5g_rlc_cfg("rlc_config");
|
||||
srb2_5g.add_subsection(&srb2_5g_rlc_cfg);
|
||||
srb2_5g_rlc_cfg.add_field(new field_5g_srb(rrc_nr_cfg_->srb2_cfg));
|
||||
|
||||
parser::section five_qi("five_qi_config");
|
||||
five_qi.add_field(new field_five_qi(rrc_nr_cfg_->five_qi_cfg));
|
||||
|
||||
|
@ -2368,6 +2465,8 @@ int parse_rb(all_args_t* args_, rrc_cfg_t* rrc_cfg_, rrc_nr_cfg_t* rrc_nr_cfg_)
|
|||
p.add_section(&srb1);
|
||||
p.add_section(&srb2);
|
||||
p.add_section(&qci);
|
||||
p.add_section(&srb1_5g);
|
||||
p.add_section(&srb2_5g);
|
||||
p.add_section(&five_qi);
|
||||
|
||||
int ret = p.parse();
|
||||
|
@ -2377,6 +2476,8 @@ int parse_rb(all_args_t* args_, rrc_cfg_t* rrc_cfg_, rrc_nr_cfg_t* rrc_nr_cfg_)
|
|||
if (not srb2_present) {
|
||||
rrc_cfg_->srb2_cfg.rlc_cfg.set_default_value();
|
||||
}
|
||||
rrc_nr_cfg_->srb1_cfg.present = srb1_5g_present;
|
||||
rrc_nr_cfg_->srb2_cfg.present = srb1_5g_present;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
|
|
@ -202,6 +202,18 @@ private:
|
|||
std::map<uint32_t, rrc_cfg_qci_t>& cfg;
|
||||
};
|
||||
|
||||
class field_5g_srb final : public parser::field_itf
|
||||
{
|
||||
public:
|
||||
explicit field_5g_srb(srb_5g_cfg_t& cfg_) : cfg(cfg_) {}
|
||||
const char* get_name() override { return "field_5g_srb"; }
|
||||
|
||||
int parse(Setting& root) override;
|
||||
|
||||
private:
|
||||
srb_5g_cfg_t& cfg;
|
||||
};
|
||||
|
||||
class field_five_qi final : public parser::field_itf
|
||||
{
|
||||
public:
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
# and at http://www.gnu.org/licenses/.
|
||||
#
|
||||
|
||||
add_library(test_helpers test_helpers.cc)
|
||||
add_library(test_helpers STATIC test_helpers.cc)
|
||||
target_link_libraries(test_helpers srsenb_rrc srsenb_common rrc_asn1 rrc_nr_asn1 s1ap_asn1 srsran_common enb_cfg_parser ${LIBCONFIGPP_LIBRARIES})
|
||||
|
||||
add_executable(rrc_meascfg_test rrc_meascfg_test.cc)
|
||||
|
|
|
@ -54,6 +54,11 @@ struct rrc_cell_cfg_nr_t {
|
|||
|
||||
typedef std::vector<rrc_cell_cfg_nr_t> rrc_cell_list_nr_t;
|
||||
|
||||
struct srb_5g_cfg_t {
|
||||
bool present = false;
|
||||
asn1::rrc_nr::rlc_cfg_c rlc_cfg;
|
||||
};
|
||||
|
||||
struct rrc_nr_cfg_five_qi_t {
|
||||
bool configured = false;
|
||||
asn1::rrc_nr::pdcp_cfg_s pdcp_cfg;
|
||||
|
@ -68,6 +73,9 @@ struct rrc_nr_cfg_t {
|
|||
uint16_t mnc;
|
||||
bool is_standalone;
|
||||
|
||||
srb_5g_cfg_t srb1_cfg;
|
||||
srb_5g_cfg_t srb2_cfg;
|
||||
|
||||
std::map<uint32_t, rrc_nr_cfg_five_qi_t> five_qi_cfg;
|
||||
|
||||
std::array<srsran::CIPHERING_ALGORITHM_ID_NR_ENUM, srsran::CIPHERING_ALGORITHM_ID_NR_N_ITEMS> nea_preference_list;
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
|
||||
set_directory_properties(PROPERTIES LABELS "sched;nr")
|
||||
|
||||
add_library(sched_nr_test_suite sched_nr_common_test.cc sched_nr_ue_ded_test_suite.cc sched_nr_sim_ue.cc)
|
||||
add_library(sched_nr_test_suite STATIC sched_nr_common_test.cc sched_nr_ue_ded_test_suite.cc sched_nr_sim_ue.cc)
|
||||
target_link_libraries(sched_nr_test_suite srsgnb_mac srsran_common rrc_nr_asn1)
|
||||
|
||||
add_executable(sched_nr_parallel_test sched_nr_parallel_test.cc)
|
||||
|
|
|
@ -956,6 +956,21 @@ void fill_srb(const rrc_nr_cfg_t& cfg, srsran::nr_srb srb_id, asn1::rrc_nr::rlc_
|
|||
out.served_radio_bearer_present = true;
|
||||
out.served_radio_bearer.set_srb_id() = (uint8_t)srb_id;
|
||||
|
||||
if (srb_id == srsran::nr_srb::srb1) {
|
||||
if (cfg.srb1_cfg.present) {
|
||||
out.rlc_cfg_present = true;
|
||||
out.rlc_cfg = cfg.srb1_cfg.rlc_cfg;
|
||||
} else {
|
||||
out.rlc_cfg_present = false;
|
||||
}
|
||||
} else if (srb_id == srsran::nr_srb::srb2) {
|
||||
if (cfg.srb2_cfg.present) {
|
||||
out.rlc_cfg_present = true;
|
||||
out.rlc_cfg = cfg.srb2_cfg.rlc_cfg;
|
||||
} else {
|
||||
out.rlc_cfg_present = false;
|
||||
}
|
||||
} else {
|
||||
out.rlc_cfg_present = true;
|
||||
auto& ul_am = out.rlc_cfg.set_am().ul_am_rlc;
|
||||
ul_am.sn_field_len_present = true;
|
||||
|
@ -969,6 +984,7 @@ void fill_srb(const rrc_nr_cfg_t& cfg, srsran::nr_srb srb_id, asn1::rrc_nr::rlc_
|
|||
dl_am.sn_field_len.value = asn1::rrc_nr::sn_field_len_am_opts::size12;
|
||||
dl_am.t_reassembly.value = t_reassembly_opts::ms35;
|
||||
dl_am.t_status_prohibit.value = asn1::rrc_nr::t_status_prohibit_opts::ms0;
|
||||
}
|
||||
|
||||
// mac-LogicalChannelConfig -- Cond LCH-Setup
|
||||
out.mac_lc_ch_cfg_present = true;
|
||||
|
|
|
@ -1418,14 +1418,31 @@ int rrc_nr::ue::update_rlc_bearers(const asn1::rrc_nr::cell_group_cfg_s& cell_gr
|
|||
// Add/Mod RLC radio bearers
|
||||
for (const rlc_bearer_cfg_s& rb : cell_group_diff.rlc_bearer_to_add_mod_list) {
|
||||
srsran::rlc_config_t rlc_cfg;
|
||||
uint8_t rb_id = rb.served_radio_bearer.type().value == rlc_bearer_cfg_s::served_radio_bearer_c_::types_opts::drb_id
|
||||
? rb.served_radio_bearer.drb_id()
|
||||
: rb.served_radio_bearer.srb_id();
|
||||
uint8_t rb_id = 0;
|
||||
if (rb.served_radio_bearer.type().value == rlc_bearer_cfg_s::served_radio_bearer_c_::types_opts::srb_id) {
|
||||
rb_id = rb.served_radio_bearer.srb_id();
|
||||
if (not rb.rlc_cfg_present) {
|
||||
rlc_cfg = srsran::rlc_config_t::default_rlc_am_nr_config();
|
||||
} else {
|
||||
if (srsran::make_rlc_config_t(rb.rlc_cfg, rb_id, &rlc_cfg) != SRSRAN_SUCCESS) {
|
||||
logger.error("Failed to build RLC config");
|
||||
// TODO: HANDLE
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
rb_id = rb.served_radio_bearer.drb_id();
|
||||
if (not rb.rlc_cfg_present) {
|
||||
logger.error("No RLC config for DRB");
|
||||
// TODO: HANDLE
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
if (srsran::make_rlc_config_t(rb.rlc_cfg, rb_id, &rlc_cfg) != SRSRAN_SUCCESS) {
|
||||
logger.error("Failed to build RLC config");
|
||||
// TODO: HANDLE
|
||||
return SRSRAN_ERROR;
|
||||
}
|
||||
}
|
||||
parent->rlc->add_bearer(rnti, rb.lc_ch_id, rlc_cfg);
|
||||
}
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
# and at http://www.gnu.org/licenses/.
|
||||
#
|
||||
|
||||
add_library(rrc_nr_test_helpers rrc_nr_test_helpers.cc)
|
||||
add_library(rrc_nr_test_helpers STATIC rrc_nr_test_helpers.cc)
|
||||
|
||||
add_executable(rrc_nr_test rrc_nr_test.cc)
|
||||
target_link_libraries(rrc_nr_test srsgnb_rrc srsgnb_rrc_config_utils srsran_common rrc_nr_asn1 rrc_nr_test_helpers srsgnb_mac ${ATOMIC_LIBS})
|
||||
|
|
|
@ -1070,6 +1070,9 @@ bool rrc_nr::apply_rlc_add_mod(const rlc_bearer_cfg_s& rlc_bearer_cfg)
|
|||
logger.error("Failed to build RLC config");
|
||||
return false;
|
||||
}
|
||||
} else if (not is_drb) {
|
||||
logger.debug("Using default RLC configs for SRB%d", srb_id);
|
||||
rlc_cfg = rlc_config_t::default_rlc_am_nr_config();
|
||||
} else {
|
||||
logger.error("In RLC bearer cfg does not contain rlc cfg");
|
||||
return false;
|
||||
|
|
Loading…
Reference in New Issue