Refactored RLC AM NR/LTE Rx and Tx entities to use a rlc_am_base_rx/tx class.

This was done to make it easier to share entity specific code between LTE and NR.
This removes the previously used templates.
master
Pedro Alvarez 3 years ago
parent b15f63f32f
commit 022c51493b

@ -36,70 +36,35 @@ namespace srsran {
bool rlc_am_is_control_pdu(uint8_t* payload);
bool rlc_am_is_control_pdu(byte_buffer_t* pdu);
/*******************************
* rlc_am_base class
******************************/
template <typename T_rlc_tx, typename T_rlc_rx>
/*******************************************************
* RLC AM entity
* This entity is common between LTE and NR
* and only the TX/RX entities change between them
*******************************************************/
class rlc_am_base : public rlc_common
{
protected:
class rlc_am_base_tx;
class rlc_am_base_rx;
public:
rlc_am_base(srslog::basic_logger& logger,
uint32_t lcid_,
srsue::pdcp_interface_rlc* pdcp_,
srsue::rrc_interface_rlc* rrc_,
srsran::timer_handler* timers_) :
logger(logger), rrc(rrc_), pdcp(pdcp_), timers(timers_), lcid(lcid_), tx(this), rx(this)
srsran::timer_handler* timers_,
rlc_am_base_tx* tx_,
rlc_am_base_rx* rx_) :
logger(logger), rrc(rrc_), pdcp(pdcp_), timers(timers_), lcid(lcid_), tx_base(tx_), rx_base(rx_)
{}
bool configure(const rlc_config_t& cfg_) final
{
// determine bearer name and configure Rx/Tx objects
rb_name = rrc->get_rb_name(lcid);
// store config
cfg = cfg_;
if (not rx.configure(cfg)) {
logger.error("Error configuring bearer (RX)");
return false;
}
if (not tx.configure(cfg)) {
logger.error("Error configuring bearer (TX)");
return false;
}
logger.info("%s configured: t_poll_retx=%d, poll_pdu=%d, poll_byte=%d, max_retx_thresh=%d, "
"t_reordering=%d, t_status_prohibit=%d",
rb_name.c_str(),
cfg.am.t_poll_retx,
cfg.am.poll_pdu,
cfg.am.poll_byte,
cfg.am.max_retx_thresh,
cfg.am.t_reordering,
cfg.am.t_status_prohibit);
return true;
}
void reestablish() final
{
logger.debug("Reestablished bearer %s", rb_name.c_str());
tx.reestablish(); // calls stop and enables tx again
rx.reestablish(); // calls only stop
}
bool configure(const rlc_config_t& cfg_) final;
void stop() final
{
logger.debug("Stopped bearer %s", rb_name.c_str());
tx.stop();
rx.stop();
}
void reestablish() final;
void empty_queue() final
{
// Drop all messages in TX SDU queue
tx.empty_queue();
}
void stop() final;
void empty_queue() final { tx_base->empty_queue(); }
rlc_mode_t get_mode() final { return rlc_mode_t::am; }
@ -108,84 +73,37 @@ public:
/****************************************************************************
* PDCP interface
***************************************************************************/
void write_sdu(unique_byte_buffer_t sdu) final
{
if (tx.write_sdu(std::move(sdu)) == SRSRAN_SUCCESS) {
std::lock_guard<std::mutex> lock(metrics_mutex);
metrics.num_tx_sdus++;
}
}
void discard_sdu(uint32_t discard_sn) final
{
tx.discard_sdu(discard_sn);
void write_sdu(unique_byte_buffer_t sdu) final;
std::lock_guard<std::mutex> lock(metrics_mutex);
metrics.num_lost_sdus++;
}
void discard_sdu(uint32_t discard_sn) final;
bool sdu_queue_is_full() final { return tx.sdu_queue_is_full(); }
bool sdu_queue_is_full() final;
/****************************************************************************
* MAC interface
***************************************************************************/
bool has_data() final { return tx.has_data(); }
uint32_t get_buffer_state() final { return tx.get_buffer_state(); }
void get_buffer_state(uint32_t& tx_queue, uint32_t& prio_tx_queue) final
{
tx.get_buffer_state(tx_queue, prio_tx_queue);
}
uint32_t read_pdu(uint8_t* payload, uint32_t nof_bytes) final
{
uint32_t read_bytes = tx.read_pdu(payload, nof_bytes);
bool has_data() final;
uint32_t get_buffer_state() final;
void get_buffer_state(uint32_t& n_bytes_newtx, uint32_t& n_bytes_prio) final;
std::lock_guard<std::mutex> lock(metrics_mutex);
metrics.num_tx_pdus++;
metrics.num_tx_pdu_bytes += read_bytes;
uint32_t read_pdu(uint8_t* payload, uint32_t nof_bytes) final;
return read_bytes;
}
void write_pdu(uint8_t* payload, uint32_t nof_bytes) final
{
rx.write_pdu(payload, nof_bytes);
std::lock_guard<std::mutex> lock(metrics_mutex);
metrics.num_rx_pdus++;
metrics.num_rx_pdu_bytes += nof_bytes;
}
void write_pdu(uint8_t* payload, uint32_t nof_bytes) final;
/****************************************************************************
* Metrics
***************************************************************************/
rlc_bearer_metrics_t get_metrics()
{
// update values that aren't calculated on the fly
uint32_t latency = rx.get_sdu_rx_latency_ms();
uint32_t buffered_bytes = rx.get_rx_buffered_bytes();
std::lock_guard<std::mutex> lock(metrics_mutex);
metrics.rx_latency_ms = latency;
metrics.rx_buffered_bytes = buffered_bytes;
return metrics;
}
rlc_bearer_metrics_t get_metrics() final;
void reset_metrics() final;
void reset_metrics()
{
std::lock_guard<std::mutex> lock(metrics_mutex);
metrics = {};
}
// BSR callback
void set_bsr_callback(bsr_callback_t callback) final { tx.set_bsr_callback(callback); }
/****************************************************************************
* BSR Callback
***************************************************************************/
void set_bsr_callback(bsr_callback_t callback) final;
private:
protected:
// Common variables needed/provided by parent class
srsue::rrc_interface_rlc* rrc = nullptr;
srslog::basic_logger& logger;
srsue::pdcp_interface_rlc* pdcp = nullptr;
srsran::timer_handler* timers = nullptr;
uint32_t lcid = 0;
rlc_config_t cfg = {};
@ -193,16 +111,66 @@ private:
static const int poll_periodicity = 8; // After how many data PDUs a status PDU shall be requested
// Rx and Tx objects
friend class rlc_am_lte_tx;
friend class rlc_am_lte_rx;
friend class rlc_am_nr_tx;
friend class rlc_am_nr_rx;
T_rlc_tx tx;
T_rlc_rx rx;
std::mutex metrics_mutex;
rlc_bearer_metrics_t metrics = {};
srsue::rrc_interface_rlc* rrc = nullptr;
srsue::pdcp_interface_rlc* pdcp = nullptr;
/*******************************************************
* RLC AM TX entity
* This class is used for common code between the
* LTE and NR TX entitites
*******************************************************/
class rlc_am_base_tx
{
public:
virtual bool configure(const rlc_config_t& cfg_) = 0;
virtual void reestablish() = 0;
virtual void stop() = 0;
virtual void empty_queue() = 0;
virtual void discard_sdu(uint32_t pdcp_sn) = 0;
virtual bool sdu_queue_is_full() = 0;
virtual bool has_data() = 0;
virtual uint32_t get_buffer_state() = 0;
virtual void get_buffer_state(uint32_t& tx_queue, uint32_t& prio_tx_queue) = 0;
virtual uint32_t read_pdu(uint8_t* payload, uint32_t nof_bytes) = 0;
void set_bsr_callback(bsr_callback_t callback);
int write_sdu(unique_byte_buffer_t sdu);
byte_buffer_pool* pool = nullptr;
bool tx_enabled = false;
std::string rb_name;
bsr_callback_t bsr_callback;
// Tx SDU buffers
byte_buffer_queue tx_sdu_queue;
// Mutexes
std::mutex mutex;
};
/*******************************************************
* RLC AM RX entity
* This class is used for common code between the
* LTE and NR RX entitites
*******************************************************/
class rlc_am_base_rx
{
public:
virtual bool configure(const rlc_config_t& cfg_) = 0;
virtual void reestablish() = 0;
virtual void stop() = 0;
virtual void write_pdu(uint8_t* payload, uint32_t nof_bytes) = 0;
virtual uint32_t get_sdu_rx_latency_ms() = 0;
virtual uint32_t get_rx_buffered_bytes() = 0;
};
rlc_am_base_tx* tx_base = nullptr;
rlc_am_base_rx* rx_base = nullptr;
};
} // namespace srsran

@ -80,30 +80,44 @@ private:
size_t rpos = 0;
};
// Transmitter sub-class
class rlc_am_lte_tx;
class rlc_am_lte_rx;
using rlc_am_lte = rlc_am_base<rlc_am_lte_tx, rlc_am_lte_rx>;
class rlc_am_lte_tx : public timer_callback
/******************************
*
* RLC AM LTE entity
*
*****************************/
class rlc_am_lte : public rlc_am_base
{
public:
rlc_am_lte(srslog::basic_logger& logger,
uint32_t lcid_,
srsue::pdcp_interface_rlc* pdcp_,
srsue::rrc_interface_rlc* rrc_,
srsran::timer_handler* timers_);
/******************************
* RLC AM LTE TX entity
*****************************/
class rlc_am_lte_tx : public rlc_am_base_tx, timer_callback
{
public:
explicit rlc_am_lte_tx(rlc_am_lte* parent_);
~rlc_am_lte_tx() = default;
bool configure(const rlc_config_t& cfg_);
void empty_queue();
void reestablish();
void stop();
int write_sdu(unique_byte_buffer_t sdu);
uint32_t read_pdu(uint8_t* payload, uint32_t nof_bytes);
void discard_sdu(uint32_t discard_sn);
bool sdu_queue_is_full();
bool has_data();
uint32_t get_buffer_state();
void get_buffer_state(uint32_t& tx_queue, uint32_t& prio_tx_queue);
void get_buffer_state(uint32_t& n_bytes_newtx, uint32_t& n_bytes_prio);
void empty_queue_nolock();
void debug_state();
// Timeout callback interface
void timer_expired(uint32_t timeout_id) final;
@ -111,9 +125,7 @@ public:
// Interface for Rx subclass
void handle_control_pdu(uint8_t* payload, uint32_t nof_bytes);
void set_bsr_callback(bsr_callback_t callback);
private:
private:
void stop_nolock();
int build_status_pdu(uint8_t* payload, uint32_t nof_bytes);
@ -122,17 +134,14 @@ private:
int build_data_pdu(uint8_t* payload, uint32_t nof_bytes);
void update_notification_ack_info(uint32_t rlc_sn);
void empty_queue_nolock();
void debug_state();
int required_buffer_size(const rlc_amd_retx_t& retx);
void retransmit_pdu(uint32_t sn);
// Helpers
void get_buffer_state_nolock(uint32_t& new_tx, uint32_t& prio_tx);
bool poll_required();
bool do_status();
void check_sn_reached_max_retx(uint32_t sn);
void get_buffer_state_nolock(uint32_t& new_tx, uint32_t& prio_tx);
rlc_am_lte* parent = nullptr;
byte_buffer_pool* pool = nullptr;
@ -147,11 +156,8 @@ private:
rlc_am_config_t cfg = {};
// TX SDU buffers
byte_buffer_queue tx_sdu_queue;
unique_byte_buffer_t tx_sdu;
bool tx_enabled = false;
/****************************************************************************
* State variables and counters
* Ref: 3GPP TS 36.322 v10.0.0 Section 7
@ -180,9 +186,6 @@ private:
// SDU info for PDCP notifications
buffered_pdcp_pdu_list undelivered_sdu_info_queue;
// Callback function for buffer status report
bsr_callback_t bsr_callback;
// Tx windows
rlc_ringbuffer_t<rlc_amd_tx_pdu, RLC_AM_WINDOW_SIZE> tx_window;
pdu_retx_queue retx_queue;
@ -193,33 +196,35 @@ private:
// default to RLC SDU queue length
const uint32_t MAX_SDUS_PER_RLC_PDU = RLC_TX_QUEUE_LEN;
};
};
// Receiver sub-class
class rlc_am_lte_rx : public timer_callback
{
public:
/******************************
* RLC AM LTE RX entity
*****************************/
class rlc_am_lte_rx : public rlc_am_base_rx, public timer_callback
{
public:
rlc_am_lte_rx(rlc_am_lte* parent_);
~rlc_am_lte_rx();
bool configure(rlc_config_t cfg_);
void reestablish();
void stop();
bool configure(const rlc_config_t& cfg_) final;
void reestablish() final;
void stop() final;
void write_pdu(uint8_t* payload, uint32_t nof_bytes);
void write_pdu(uint8_t* payload, uint32_t nof_bytes) final;
uint32_t get_rx_buffered_bytes(); // returns sum of PDUs in rx_window
uint32_t get_sdu_rx_latency_ms();
uint32_t get_rx_buffered_bytes() final; // returns sum of PDUs in rx_window
uint32_t get_sdu_rx_latency_ms() final;
// Timeout callback interface
void timer_expired(uint32_t timeout_id);
void timer_expired(uint32_t timeout_id) final;
// Functions needed by Tx subclass to query rx state
int get_status_pdu_length();
int get_status_pdu(rlc_status_pdu_t* status, const uint32_t nof_bytes);
int get_status_pdu(rlc_status_pdu_t* status, uint32_t nof_bytes);
bool get_do_status();
private:
private:
void handle_data_pdu(uint8_t* payload, uint32_t nof_bytes, rlc_amd_pdu_header_t& header);
void handle_data_pdu_segment(uint8_t* payload, uint32_t nof_bytes, rlc_amd_pdu_header_t& header);
void reassemble_rx_sdus();
@ -272,6 +277,11 @@ private:
srsran::timer_handler::unique_timer reordering_timer;
srsran::rolling_average<double> sdu_rx_latency_ms;
};
private:
rlc_am_lte_tx* tx = nullptr;
rlc_am_lte_rx* rx = nullptr;
};
} // namespace srsran

@ -25,15 +25,24 @@
namespace srsran {
// Transmitter sub-class
class rlc_am_nr_tx;
class rlc_am_nr_rx;
using rlc_am_nr = rlc_am_base<rlc_am_nr_tx, rlc_am_nr_rx>;
// Transmitter sub-class
class rlc_am_nr_tx
/******************************
*
* RLC AM NR entity
*
*****************************/
class rlc_am_nr : public rlc_am_base
{
public:
rlc_am_nr(srslog::basic_logger& logger,
uint32_t lcid_,
srsue::pdcp_interface_rlc* pdcp_,
srsue::rrc_interface_rlc* rrc_,
srsran::timer_handler* timers_);
// Transmitter sub-class
class rlc_am_nr_tx : public rlc_am_base_tx
{
public:
explicit rlc_am_nr_tx(rlc_am_nr* parent_);
~rlc_am_nr_tx() = default;
@ -50,9 +59,8 @@ public:
bool has_data();
uint32_t get_buffer_state();
void get_buffer_state(uint32_t& tx_queue, uint32_t& prio_tx_queue);
void set_bsr_callback(const bsr_callback_t& callback);
private:
private:
rlc_am_nr* parent = nullptr;
byte_buffer_pool* pool = nullptr;
srslog::basic_logger& logger;
@ -62,12 +70,24 @@ private:
* Ref: 3GPP TS 38.322 v10.0.0 Section 7.4
***************************************************************************/
rlc_am_config_t cfg = {};
};
// Receiver sub-class
class rlc_am_nr_rx
{
public:
/****************************************************************************
* Tx state variables
* Ref: 3GPP TS 38.322 v10.0.0 Section 7.1
***************************************************************************/
struct rlc_nr_tx_state_t {
uint32_t tx_next_ack;
uint32_t tx_next;
uint32_t poll_sn;
uint32_t pdu_without_poll;
uint32_t byte_without_poll;
} st = {};
};
// Receiver sub-class
class rlc_am_nr_rx : public rlc_am_base_rx
{
public:
explicit rlc_am_nr_rx(rlc_am_nr* parent_);
~rlc_am_nr_rx() = default;
@ -79,7 +99,7 @@ public:
uint32_t get_sdu_rx_latency_ms();
uint32_t get_rx_buffered_bytes();
private:
private:
rlc_am_nr* parent = nullptr;
byte_buffer_pool* pool = nullptr;
srslog::basic_logger& logger;
@ -89,8 +109,12 @@ private:
* Ref: 3GPP TS 38.322 v10.0.0 Section 7.4
***************************************************************************/
rlc_am_config_t cfg = {};
};
private:
rlc_am_nr_tx* tx = nullptr;
rlc_am_nr_rx* rx = nullptr;
};
} // namespace srsran
#endif // SRSRAN_RLC_AM_NR_H

@ -25,4 +25,191 @@ bool rlc_am_is_control_pdu(byte_buffer_t* pdu)
return rlc_am_is_control_pdu(pdu->msg);
}
/*******************************************************
* RLC AM entity
* This entity is common between LTE and NR
* and only the TX/RX entities change between them
*******************************************************/
bool rlc_am_base::configure(const rlc_config_t& cfg_)
{
// determine bearer name and configure Rx/Tx objects
rb_name = rrc->get_rb_name(lcid);
// store config
cfg = cfg_;
if (not rx_base->configure(cfg)) {
logger.error("Error configuring bearer (RX)");
return false;
}
if (not tx_base->configure(cfg)) {
logger.error("Error configuring bearer (TX)");
return false;
}
logger.info("%s configured: t_poll_retx=%d, poll_pdu=%d, poll_byte=%d, max_retx_thresh=%d, "
"t_reordering=%d, t_status_prohibit=%d",
rb_name.c_str(),
cfg.am.t_poll_retx,
cfg.am.poll_pdu,
cfg.am.poll_byte,
cfg.am.max_retx_thresh,
cfg.am.t_reordering,
cfg.am.t_status_prohibit);
return true;
}
void rlc_am_base::stop()
{
logger.debug("Stopped bearer %s", rb_name.c_str());
tx_base->stop();
rx_base->stop();
}
void rlc_am_base::reestablish()
{
logger.debug("Reestablished bearer %s", rb_name.c_str());
tx_base->reestablish(); // calls stop and enables tx again
rx_base->reestablish(); // calls only stop
}
/****************************************************************************
* PDCP interface
***************************************************************************/
void rlc_am_base::write_sdu(unique_byte_buffer_t sdu)
{
if (tx_base->write_sdu(std::move(sdu)) == SRSRAN_SUCCESS) {
std::lock_guard<std::mutex> lock(metrics_mutex);
metrics.num_tx_sdus++;
}
}
void rlc_am_base::discard_sdu(uint32_t discard_sn)
{
tx_base->discard_sdu(discard_sn);
std::lock_guard<std::mutex> lock(metrics_mutex);
metrics.num_lost_sdus++;
}
bool rlc_am_base::sdu_queue_is_full()
{
return tx_base->sdu_queue_is_full();
}
/****************************************************************************
* MAC interface
***************************************************************************/
bool rlc_am_base::has_data()
{
return tx_base->has_data();
}
uint32_t rlc_am_base::get_buffer_state()
{
return tx_base->get_buffer_state();
}
void rlc_am_base::get_buffer_state(uint32_t& n_bytes_newtx, uint32_t& n_bytes_prio)
{
tx_base->get_buffer_state(n_bytes_newtx, n_bytes_prio);
return;
}
uint32_t rlc_am_base::read_pdu(uint8_t* payload, uint32_t nof_bytes)
{
uint32_t read_bytes = tx_base->read_pdu(payload, nof_bytes);
std::lock_guard<std::mutex> lock(metrics_mutex);
metrics.num_tx_pdus++;
metrics.num_tx_pdu_bytes += read_bytes;
return read_bytes;
}
void rlc_am_base::write_pdu(uint8_t* payload, uint32_t nof_bytes)
{
rx_base->write_pdu(payload, nof_bytes);
std::lock_guard<std::mutex> lock(metrics_mutex);
metrics.num_rx_pdus++;
metrics.num_rx_pdu_bytes += nof_bytes;
}
/****************************************************************************
* Metrics
***************************************************************************/
rlc_bearer_metrics_t rlc_am_base::get_metrics()
{
// update values that aren't calculated on the fly
uint32_t latency = rx_base->get_sdu_rx_latency_ms();
uint32_t buffered_bytes = rx_base->get_rx_buffered_bytes();
std::lock_guard<std::mutex> lock(metrics_mutex);
metrics.rx_latency_ms = latency;
metrics.rx_buffered_bytes = buffered_bytes;
return metrics;
}
void rlc_am_base::reset_metrics()
{
std::lock_guard<std::mutex> lock(metrics_mutex);
metrics = {};
}
/****************************************************************************
* BSR callback
***************************************************************************/
void rlc_am_base::set_bsr_callback(bsr_callback_t callback)
{
tx_base->set_bsr_callback(callback);
}
/*******************************************************
* RLC AM TX entity
* This class is used for common code between the
* LTE and NR TX entitites
*******************************************************/
int rlc_am_base::rlc_am_base_tx::write_sdu(unique_byte_buffer_t sdu)
{
std::lock_guard<std::mutex> lock(mutex);
if (!tx_enabled) {
return SRSRAN_ERROR;
}
if (sdu.get() == nullptr) {
srslog::fetch_basic_logger("RLC").warning("NULL SDU pointer in write_sdu()");
return SRSRAN_ERROR;
}
// Get SDU info
uint32_t sdu_pdcp_sn = sdu->md.pdcp_sn;
// Store SDU
uint8_t* msg_ptr = sdu->msg;
uint32_t nof_bytes = sdu->N_bytes;
srsran::error_type<unique_byte_buffer_t> ret = tx_sdu_queue.try_write(std::move(sdu));
if (ret) {
srslog::fetch_basic_logger("RLC").info(
msg_ptr, nof_bytes, "%s Tx SDU (%d B, tx_sdu_queue_len=%d)", rb_name, nof_bytes, tx_sdu_queue.size());
} else {
// in case of fail, the try_write returns back the sdu
srslog::fetch_basic_logger("RLC").warning(ret.error()->msg,
ret.error()->N_bytes,
"[Dropped SDU] %s Tx SDU (%d B, tx_sdu_queue_len=%d)",
rb_name,
ret.error()->N_bytes,
tx_sdu_queue.size());
return SRSRAN_ERROR;
}
return SRSRAN_SUCCESS;
}
void rlc_am_base::rlc_am_base_tx::set_bsr_callback(bsr_callback_t callback)
{
bsr_callback = callback;
}
} // namespace srsran

@ -89,11 +89,25 @@ rlc_amd_tx_pdu::~rlc_amd_tx_pdu()
}
}
/****************************************************************************
* RLC AM LTE entity
***************************************************************************/
rlc_am_lte::rlc_am_lte(srslog::basic_logger& logger,
uint32_t lcid_,
srsue::pdcp_interface_rlc* pdcp_,
srsue::rrc_interface_rlc* rrc_,
srsran::timer_handler* timers_) :
rlc_am_base(logger, lcid_, pdcp_, rrc_, timers_, nullptr, nullptr)
{
tx = new rlc_am_lte::rlc_am_lte_tx(this);
rx = new rlc_am_lte::rlc_am_lte_rx(this);
tx_base = tx;
rx_base = rx;
}
/****************************************************************************
* Tx subclass implementation
***************************************************************************/
rlc_am_lte_tx::rlc_am_lte_tx(rlc_am_lte* parent_) :
rlc_am_lte::rlc_am_lte_tx::rlc_am_lte_tx(rlc_am_lte* parent_) :
parent(parent_),
logger(parent_->logger),
pool(byte_buffer_pool::get_instance()),
@ -101,12 +115,7 @@ rlc_am_lte_tx::rlc_am_lte_tx(rlc_am_lte* parent_) :
status_prohibit_timer(parent_->timers->get_unique_timer())
{}
void rlc_am_lte_tx::set_bsr_callback(bsr_callback_t callback)
{
bsr_callback = callback;
}
bool rlc_am_lte_tx::configure(const rlc_config_t& cfg_)
bool rlc_am_lte::rlc_am_lte_tx::configure(const rlc_config_t& cfg_)
{
std::lock_guard<std::mutex> lock(mutex);
if (cfg_.tx_queue_length > MAX_SDUS_PER_RLC_PDU) {
@ -144,13 +153,13 @@ bool rlc_am_lte_tx::configure(const rlc_config_t& cfg_)
return true;
}
void rlc_am_lte_tx::stop()
void rlc_am_lte::rlc_am_lte_tx::stop()
{
std::lock_guard<std::mutex> lock(mutex);
stop_nolock();
}
void rlc_am_lte_tx::stop_nolock()
void rlc_am_lte::rlc_am_lte_tx::stop_nolock()
{
empty_queue_nolock();
@ -182,13 +191,13 @@ void rlc_am_lte_tx::stop_nolock()
undelivered_sdu_info_queue.clear();
}
void rlc_am_lte_tx::empty_queue()
void rlc_am_lte::rlc_am_lte_tx::empty_queue()
{
std::lock_guard<std::mutex> lock(mutex);
empty_queue_nolock();
}
void rlc_am_lte_tx::empty_queue_nolock()
void rlc_am_lte::rlc_am_lte_tx::empty_queue_nolock()
{
// deallocate all SDUs in transmit queue
while (tx_sdu_queue.size() > 0) {
@ -202,20 +211,20 @@ void rlc_am_lte_tx::empty_queue_nolock()
tx_sdu.reset();
}
void rlc_am_lte_tx::reestablish()
void rlc_am_lte::rlc_am_lte_tx::reestablish()
{
std::lock_guard<std::mutex> lock(mutex);
stop_nolock();
tx_enabled = true;
}
bool rlc_am_lte_tx::do_status()
bool rlc_am_lte::rlc_am_lte_tx::do_status()
{
return parent->rx.get_do_status();
return parent->rx->get_do_status();
}
// Function is supposed to return as fast as possible
bool rlc_am_lte_tx::has_data()
bool rlc_am_lte::rlc_am_lte_tx::has_data()
{
return (((do_status() && not status_prohibit_timer.is_running())) || // if we have a status PDU to transmit
(not retx_queue.empty()) || // if we have a retransmission
@ -232,7 +241,7 @@ bool rlc_am_lte_tx::has_data()
*
* @param sn The SN of the PDU to check
*/
void rlc_am_lte_tx::check_sn_reached_max_retx(uint32_t sn)
void rlc_am_lte::rlc_am_lte_tx::check_sn_reached_max_retx(uint32_t sn)
{
if (tx_window[sn].retx_count == cfg.max_retx_thresh) {
logger.warning("%s Signaling max number of reTx=%d for SN=%d", RB_NAME, tx_window[sn].retx_count, sn);
@ -248,20 +257,20 @@ void rlc_am_lte_tx::check_sn_reached_max_retx(uint32_t sn)
}
}
uint32_t rlc_am_lte_tx::get_buffer_state()
uint32_t rlc_am_lte::rlc_am_lte_tx::get_buffer_state()
{
uint32_t new_tx_queue = 0, prio_tx_queue = 0;
get_buffer_state(new_tx_queue, prio_tx_queue);
return new_tx_queue + prio_tx_queue;
}
void rlc_am_lte_tx::get_buffer_state(uint32_t& n_bytes_newtx, uint32_t& n_bytes_prio)
void rlc_am_lte::rlc_am_lte_tx::get_buffer_state(uint32_t& n_bytes_newtx, uint32_t& n_bytes_prio)
{
std::lock_guard<std::mutex> lock(mutex);
get_buffer_state_nolock(n_bytes_newtx, n_bytes_prio);
}
void rlc_am_lte_tx::get_buffer_state_nolock(uint32_t& n_bytes_newtx, uint32_t& n_bytes_prio)
void rlc_am_lte::rlc_am_lte_tx::get_buffer_state_nolock(uint32_t& n_bytes_newtx, uint32_t& n_bytes_prio)
{
n_bytes_newtx = 0;
n_bytes_prio = 0;
@ -276,7 +285,7 @@ void rlc_am_lte_tx::get_buffer_state_nolock(uint32_t& n_bytes_newtx, uint32_t& n
// Bytes needed for status report
if (do_status() && not status_prohibit_timer.is_running()) {
n_bytes_prio += parent->rx.get_status_pdu_length();
n_bytes_prio += parent->rx->get_status_pdu_length();
logger.debug("%s Buffer state - total status report: %d bytes", RB_NAME, n_bytes_prio);
}
@ -323,47 +332,12 @@ void rlc_am_lte_tx::get_buffer_state_nolock(uint32_t& n_bytes_newtx, uint32_t& n
}
if (bsr_callback) {
logger.debug("%s Calling BSR callback - %d new_tx, %d prio bytes", RB_NAME, n_bytes_newtx, n_bytes_prio);
bsr_callback(parent->lcid, n_bytes_newtx, n_bytes_prio);
}
}
int rlc_am_lte_tx::write_sdu(unique_byte_buffer_t sdu)
{
std::lock_guard<std::mutex> lock(mutex);
if (!tx_enabled) {
return SRSRAN_ERROR;
}
if (sdu.get() == nullptr) {
logger.warning("NULL SDU pointer in write_sdu()");
return SRSRAN_ERROR;
}
// Get SDU info
uint32_t sdu_pdcp_sn = sdu->md.pdcp_sn;
// Store SDU
uint8_t* msg_ptr = sdu->msg;
uint32_t nof_bytes = sdu->N_bytes;
srsran::error_type<unique_byte_buffer_t> ret = tx_sdu_queue.try_write(std::move(sdu));
if (ret) {
logger.info(msg_ptr, nof_bytes, "%s Tx SDU (%d B, tx_sdu_queue_len=%d)", RB_NAME, nof_bytes, tx_sdu_queue.size());
} else {
// in case of fail, the try_write returns back the sdu
logger.warning(ret.error()->msg,
ret.error()->N_bytes,
"[Dropped SDU] %s Tx SDU (%d B, tx_sdu_queue_len=%d)",
RB_NAME,
ret.error()->N_bytes,
tx_sdu_queue.size());
return SRSRAN_ERROR;
}
return SRSRAN_SUCCESS;
}
void rlc_am_lte_tx::discard_sdu(uint32_t discard_sn)
void rlc_am_lte::rlc_am_lte_tx::discard_sdu(uint32_t discard_sn)
{
if (!tx_enabled) {
return;
@ -381,12 +355,12 @@ void rlc_am_lte_tx::discard_sdu(uint32_t discard_sn)
logger.info("%s PDU with PDCP_SN=%d", discarded ? "Discarding" : "Couldn't discard", discard_sn);
}
bool rlc_am_lte_tx::sdu_queue_is_full()
bool rlc_am_lte::rlc_am_lte_tx::sdu_queue_is_full()
{
return tx_sdu_queue.is_full();
}
uint32_t rlc_am_lte_tx::read_pdu(uint8_t* payload, uint32_t nof_bytes)
uint32_t rlc_am_lte::rlc_am_lte_tx::read_pdu(uint8_t* payload, uint32_t nof_bytes)
{
std::lock_guard<std::mutex> lock(mutex);
@ -424,7 +398,7 @@ uint32_t rlc_am_lte_tx::read_pdu(uint8_t* payload, uint32_t nof_bytes)
return build_data_pdu(payload, nof_bytes);
}
void rlc_am_lte_tx::timer_expired(uint32_t timeout_id)
void rlc_am_lte::rlc_am_lte_tx::timer_expired(uint32_t timeout_id)
{
std::unique_lock<std::mutex> lock(mutex);
if (poll_retx_timer.is_valid() && poll_retx_timer.id() == timeout_id) {
@ -445,7 +419,7 @@ void rlc_am_lte_tx::timer_expired(uint32_t timeout_id)
}
}
void rlc_am_lte_tx::retransmit_pdu(uint32_t sn)
void rlc_am_lte::rlc_am_lte_tx::retransmit_pdu(uint32_t sn)
{
if (tx_window.empty()) {
logger.warning("%s No PDU to retransmit", RB_NAME);
@ -484,7 +458,7 @@ void rlc_am_lte_tx::retransmit_pdu(uint32_t sn)
*
* @return True if a status PDU needs to be requested, false otherwise.
*/
bool rlc_am_lte_tx::poll_required()
bool rlc_am_lte::rlc_am_lte_tx::poll_required()
{
if (cfg.poll_pdu > 0 && pdu_without_poll > static_cast<uint32_t>(cfg.poll_pdu)) {
return true;
@ -519,9 +493,10 @@ bool rlc_am_lte_tx::poll_required()
return false;
}
int rlc_am_lte_tx::build_status_pdu(uint8_t* payload, uint32_t nof_bytes)
int rlc_am_lte::rlc_am_lte_tx::build_status_pdu(uint8_t* payload, uint32_t nof_bytes)
{
int pdu_len = parent->rx.get_status_pdu(&tx_status, nof_bytes);
logger->debug("%s Generating status PDU. Nof bytes %d", RB_NAME, nof_bytes);
int pdu_len = parent->rx->get_status_pdu(&tx_status, nof_bytes);
if (pdu_len == SRSRAN_ERROR) {
logger.debug("%s Deferred Status PDU. Cause: Failed to acquire Rx lock", RB_NAME);
pdu_len = 0;
@ -541,7 +516,7 @@ int rlc_am_lte_tx::build_status_pdu(uint8_t* payload, uint32_t nof_bytes)
return pdu_len;
}
int rlc_am_lte_tx::build_retx_pdu(uint8_t* payload, uint32_t nof_bytes)
int rlc_am_lte::rlc_am_lte_tx::build_retx_pdu(uint8_t* payload, uint32_t nof_bytes)
{
// Check there is at least 1 element before calling front()
if (retx_queue.empty()) {
@ -622,7 +597,7 @@ int rlc_am_lte_tx::build_retx_pdu(uint8_t* payload, uint32_t nof_bytes)
return (ptr - payload) + tx_window[retx.sn].buf->N_bytes;
}
int rlc_am_lte_tx::build_segment(uint8_t* payload, uint32_t nof_bytes, rlc_amd_retx_t retx)
int rlc_am_lte::rlc_am_lte_tx::build_segment(uint8_t* payload, uint32_t nof_bytes, rlc_amd_retx_t retx)
{
if (tx_window[retx.sn].buf == NULL) {
logger.error("In build_segment: retx.sn=%d has null buffer", retx.sn);
@ -797,7 +772,7 @@ int rlc_am_lte_tx::build_segment(uint8_t* payload, uint32_t nof_bytes, rlc_amd_r
return pdu_len;
}
int rlc_am_lte_tx::build_data_pdu(uint8_t* payload, uint32_t nof_bytes)
int rlc_am_lte::rlc_am_lte_tx::build_data_pdu(uint8_t* payload, uint32_t nof_bytes)
{
if (tx_sdu == NULL && tx_sdu_queue.is_empty()) {
logger.info("No data available to be sent");
@ -1013,7 +988,7 @@ int rlc_am_lte_tx::build_data_pdu(uint8_t* payload, uint32_t nof_bytes)
return total_len;
}
void rlc_am_lte_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes)
void rlc_am_lte::rlc_am_lte_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes)
{
if (not tx_enabled) {
return;
@ -1161,7 +1136,7 @@ void rlc_am_lte_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes)
* @tx_pdu: RLC PDU that was ack'ed.
* @notify_info_vec: Vector which will keep track of the PDCP PDU SNs that have been fully ack'ed.
*/
void rlc_am_lte_tx::update_notification_ack_info(uint32_t rlc_sn)
void rlc_am_lte::rlc_am_lte_tx::update_notification_ack_info(uint32_t rlc_sn)
{
logger.debug("Updating ACK info: RLC SN=%d, number of notified SDU=%ld, number of undelivered SDUs=%ld",
rlc_sn,
@ -1198,12 +1173,12 @@ void rlc_am_lte_tx::update_notification_ack_info(uint32_t rlc_sn)
}
}
void rlc_am_lte_tx::debug_state()
void rlc_am_lte::rlc_am_lte_tx::debug_state()
{
logger.debug("%s vt_a = %d, vt_ms = %d, vt_s = %d, poll_sn = %d", RB_NAME, vt_a, vt_ms, vt_s, poll_sn);
}
int rlc_am_lte_tx::required_buffer_size(const rlc_amd_retx_t& retx)
int rlc_am_lte::rlc_am_lte_tx::required_buffer_size(const rlc_amd_retx_t& retx)
{
if (!retx.is_segment) {
if (tx_window.has_sn(retx.sn)) {
@ -1280,16 +1255,16 @@ int rlc_am_lte_tx::required_buffer_size(const rlc_amd_retx_t& retx)
* Rx subclass implementation
***************************************************************************/
rlc_am_lte_rx::rlc_am_lte_rx(rlc_am_lte* parent_) :
rlc_am_lte::rlc_am_lte_rx::rlc_am_lte_rx(rlc_am_lte* parent_) :
parent(parent_),
pool(byte_buffer_pool::get_instance()),
logger(parent_->logger),
reordering_timer(parent_->timers->get_unique_timer())
{}
rlc_am_lte_rx::~rlc_am_lte_rx() {}
rlc_am_lte::rlc_am_lte_rx::~rlc_am_lte_rx() {}
bool rlc_am_lte_rx::configure(rlc_config_t cfg_)
bool rlc_am_lte::rlc_am_lte_rx::configure(const rlc_config_t& cfg_)
{
// TODO: add config checks
cfg = cfg_.am;
@ -1308,12 +1283,12 @@ bool rlc_am_lte_rx::configure(rlc_config_t cfg_)
return true;
}
void rlc_am_lte_rx::reestablish()
void rlc_am_lte::rlc_am_lte_rx::reestablish()
{
stop();
}
void rlc_am_lte_rx::stop()
void rlc_am_lte::rlc_am_lte_rx::stop()
{
std::lock_guard<std::mutex> lock(mutex);
@ -1345,7 +1320,7 @@ void rlc_am_lte_rx::stop()
* @param nof_bytes Payload length
* @param header Reference to PDU header (unpacked by caller)
*/
void rlc_am_lte_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes, rlc_amd_pdu_header_t& header)
void rlc_am_lte::rlc_am_lte_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes, rlc_amd_pdu_header_t& header)
{
std::map<uint32_t, rlc_amd_rx_pdu>::iterator it;
@ -1462,7 +1437,9 @@ void rlc_am_lte_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes, rlc_am
debug_state();
}
void rlc_am_lte_rx::handle_data_pdu_segment(uint8_t* payload, uint32_t nof_bytes, rlc_amd_pdu_header_t& header)
void rlc_am_lte::rlc_am_lte_rx::handle_data_pdu_segment(uint8_t* payload,
uint32_t nof_bytes,
rlc_amd_pdu_header_t& header)
{
std::map<uint32_t, rlc_amd_rx_pdu_segments_t>::iterator it;
@ -1550,7 +1527,7 @@ void rlc_am_lte_rx::handle_data_pdu_segment(uint8_t* payload, uint32_t nof_bytes
debug_state();
}
void rlc_am_lte_rx::reassemble_rx_sdus()
void rlc_am_lte::rlc_am_lte_rx::reassemble_rx_sdus()
{
uint32_t len = 0;
if (rx_sdu == NULL) {
@ -1701,25 +1678,25 @@ void rlc_am_lte_rx::reassemble_rx_sdus()
}
}
void rlc_am_lte_rx::reset_status()
void rlc_am_lte::rlc_am_lte_rx::reset_status()
{
do_status = false;
poll_received = false;
}
bool rlc_am_lte_rx::get_do_status()
bool rlc_am_lte::rlc_am_lte_rx::get_do_status()
{
return do_status.load(std::memory_order_relaxed);
}
void rlc_am_lte_rx::write_pdu(uint8_t* payload, const uint32_t nof_bytes)
void rlc_am_lte::rlc_am_lte_rx::write_pdu(uint8_t* payload, const uint32_t nof_bytes)
{
if (nof_bytes < 1) {
return;
}
if (rlc_am_is_control_pdu(payload)) {
parent->tx.handle_control_pdu(payload, nof_bytes);
parent->tx->handle_control_pdu(payload, nof_bytes);
} else {
std::lock_guard<std::mutex> lock(mutex);
rlc_amd_pdu_header_t header = {};
@ -1737,13 +1714,13 @@ void rlc_am_lte_rx::write_pdu(uint8_t* payload, const uint32_t nof_bytes)
}
}
uint32_t rlc_am_lte_rx::get_rx_buffered_bytes()
uint32_t rlc_am_lte::rlc_am_lte_rx::get_rx_buffered_bytes()
{
std::lock_guard<std::mutex> lock(mutex);
return rx_window.get_buffered_bytes();
}
uint32_t rlc_am_lte_rx::get_sdu_rx_latency_ms()
uint32_t rlc_am_lte::rlc_am_lte_rx::get_sdu_rx_latency_ms()
{
std::lock_guard<std::mutex> lock(mutex);
return sdu_rx_latency_ms.value();
@ -1754,7 +1731,7 @@ uint32_t rlc_am_lte_rx::get_sdu_rx_latency_ms()
*
* @param timeout_id
*/
void rlc_am_lte_rx::timer_expired(uint32_t timeout_id)
void rlc_am_lte::rlc_am_lte_rx::timer_expired(uint32_t timeout_id)
{
std::lock_guard<std::mutex> lock(mutex);
if (reordering_timer.is_valid() and reordering_timer.id() == timeout_id) {
@ -1781,7 +1758,7 @@ void rlc_am_lte_rx::timer_expired(uint32_t timeout_id)
// Called from Tx object to pack status PDU that doesn't exceed a given size
// If lock-acquisition fails, return -1. Otherwise it returns the length of the generated PDU.
int rlc_am_lte_rx::get_status_pdu(rlc_status_pdu_t* status, const uint32_t max_pdu_size)
int rlc_am_lte::rlc_am_lte_rx::get_status_pdu(rlc_status_pdu_t* status, const uint32_t max_pdu_size)
{
std::unique_lock<std::mutex> lock(mutex, std::try_to_lock);
if (not lock.owns_lock()) {
@ -1834,7 +1811,7 @@ int rlc_am_lte_rx::get_status_pdu(rlc_status_pdu_t* status, const uint32_t max_p
}
// Called from Tx object to obtain length of the full status PDU
int rlc_am_lte_rx::get_status_pdu_length()
int rlc_am_lte::rlc_am_lte_rx::get_status_pdu_length()
{
std::unique_lock<std::mutex> lock(mutex, std::try_to_lock);
if (not lock.owns_lock()) {
@ -1852,7 +1829,7 @@ int rlc_am_lte_rx::get_status_pdu_length()
return rlc_am_packed_length(&status);
}
void rlc_am_lte_rx::print_rx_segments()
void rlc_am_lte::rlc_am_lte_rx::print_rx_segments()
{
std::map<uint32_t, rlc_amd_rx_pdu_segments_t>::iterator it;
std::stringstream ss;
@ -1868,7 +1845,7 @@ void rlc_am_lte_rx::print_rx_segments()
}
// NOTE: Preference would be to capture by value, and then move; but header is stack allocated
bool rlc_am_lte_rx::add_segment_and_check(rlc_amd_rx_pdu_segments_t* pdu, rlc_amd_rx_pdu* segment)
bool rlc_am_lte::rlc_am_lte_rx::add_segment_and_check(rlc_amd_rx_pdu_segments_t* pdu, rlc_amd_rx_pdu* segment)
{
// Find segment insertion point in the list of segments
auto it1 = pdu->segments.begin();
@ -2048,7 +2025,7 @@ bool rlc_am_lte_rx::add_segment_and_check(rlc_amd_rx_pdu_segments_t* pdu, rlc_am
return true;
}
bool rlc_am_lte_rx::inside_rx_window(const int16_t sn)
bool rlc_am_lte::rlc_am_lte_rx::inside_rx_window(const int16_t sn)
{
if (RX_MOD_BASE(sn) >= RX_MOD_BASE(static_cast<int16_t>(vr_r)) && RX_MOD_BASE(sn) < RX_MOD_BASE(vr_mr)) {
return true;
@ -2057,7 +2034,7 @@ bool rlc_am_lte_rx::inside_rx_window(const int16_t sn)
}
}
void rlc_am_lte_rx::debug_state()
void rlc_am_lte::rlc_am_lte_rx::debug_state()
{
logger.debug("%s vr_r = %d, vr_mr = %d, vr_x = %d, vr_ms = %d, vr_h = %d", RB_NAME, vr_r, vr_mr, vr_x, vr_ms, vr_h);
}

@ -19,15 +19,29 @@
namespace srsran {
/****************************************************************************
* RLC AM NR entity
***************************************************************************/
rlc_am_nr::rlc_am_nr(srslog::basic_logger& logger,
uint32_t lcid_,
srsue::pdcp_interface_rlc* pdcp_,
srsue::rrc_interface_rlc* rrc_,
srsran::timer_handler* timers_) :
rlc_am_base(logger, lcid_, pdcp_, rrc_, timers_, new rlc_am_nr::rlc_am_nr_tx(this), new rlc_am_nr::rlc_am_nr_rx(this))
{
tx = dynamic_cast<rlc_am_nr::rlc_am_nr_tx*>(tx_base);
rx = dynamic_cast<rlc_am_nr::rlc_am_nr_rx*>(rx_base);
}
/*******************************
* RLC AM NR
* Tx subclass implementation
***************************************************************************/
rlc_am_nr_tx::rlc_am_nr_tx(rlc_am_nr* parent_) :
rlc_am_nr::rlc_am_nr_tx::rlc_am_nr_tx(rlc_am_nr* parent_) :
parent(parent_), logger(parent_->logger), pool(byte_buffer_pool::get_instance())
{}
bool rlc_am_nr_tx::configure(const rlc_config_t& cfg_)
bool rlc_am_nr::rlc_am_nr_tx::configure(const rlc_config_t& cfg_)
{
/*
if (cfg_.tx_queue_length > MAX_SDUS_PER_RLC_PDU) {
@ -42,75 +56,73 @@ bool rlc_am_nr_tx::configure(const rlc_config_t& cfg_)
return true;
}
bool rlc_am_nr_tx::has_data()
bool rlc_am_nr::rlc_am_nr_tx::has_data()
{
return true;
}
uint32_t rlc_am_nr_tx::read_pdu(uint8_t* payload, uint32_t nof_bytes)
uint32_t rlc_am_nr::rlc_am_nr_tx::read_pdu(uint8_t* payload, uint32_t nof_bytes)
{
return 0;
}
void rlc_am_nr_tx::reestablish()
void rlc_am_nr::rlc_am_nr_tx::reestablish()
{
stop();
}
uint32_t rlc_am_nr_tx::get_buffer_state()
uint32_t rlc_am_nr::rlc_am_nr_tx::get_buffer_state()
{
return 0;
}
void rlc_am_nr_tx::get_buffer_state(uint32_t& tx_queue, uint32_t& prio_tx_queue) {}
void rlc_am_nr::rlc_am_nr_tx::get_buffer_state(uint32_t& tx_queue, uint32_t& prio_tx_queue) {}
int rlc_am_nr_tx::write_sdu(unique_byte_buffer_t sdu)
int rlc_am_nr::rlc_am_nr_tx::write_sdu(unique_byte_buffer_t sdu)
{
return 0;
}
void rlc_am_nr_tx::discard_sdu(uint32_t discard_sn) {}
void rlc_am_nr::rlc_am_nr_tx::discard_sdu(uint32_t discard_sn) {}
bool rlc_am_nr_tx::sdu_queue_is_full()
bool rlc_am_nr::rlc_am_nr_tx::sdu_queue_is_full()
{
return false;
}
void rlc_am_nr_tx::empty_queue() {}
void rlc_am_nr_tx::set_bsr_callback(const bsr_callback_t& callback) {}
void rlc_am_nr::rlc_am_nr_tx::empty_queue() {}
void rlc_am_nr_tx::stop() {}
void rlc_am_nr::rlc_am_nr_tx::stop() {}
/****************************************************************************
* Rx subclass implementation
***************************************************************************/
rlc_am_nr_rx::rlc_am_nr_rx(rlc_am_nr* parent_) :
parent(parent_), pool(byte_buffer_pool::get_instance()), logger(parent_->logger)
rlc_am_nr::rlc_am_nr_rx::rlc_am_nr_rx(rlc_am_nr* parent_) :
parent(parent_), logger(parent_->logger), pool(byte_buffer_pool::get_instance())
{}
bool rlc_am_nr_rx::configure(const rlc_config_t& cfg_)
bool rlc_am_nr::rlc_am_nr_rx::configure(const rlc_config_t& cfg_)
{
cfg = cfg_.am;
return true;
}
void rlc_am_nr_rx::stop() {}
void rlc_am_nr::rlc_am_nr_rx::stop() {}
void rlc_am_nr_rx::write_pdu(uint8_t* payload, uint32_t nof_bytes) {}
void rlc_am_nr::rlc_am_nr_rx::write_pdu(uint8_t* payload, uint32_t nof_bytes) {}
void rlc_am_nr_rx::reestablish()
void rlc_am_nr::rlc_am_nr_rx::reestablish()
{
stop();
}
uint32_t rlc_am_nr_rx::get_sdu_rx_latency_ms()
uint32_t rlc_am_nr::rlc_am_nr_rx::get_sdu_rx_latency_ms()
{
return 0;
}
uint32_t rlc_am_nr_rx::get_rx_buffered_bytes()
uint32_t rlc_am_nr::rlc_am_nr_rx::get_rx_buffered_bytes()
{
return 0;
}

Loading…
Cancel
Save