Added an RLC AM base class to avoid code duplication in the RLC AM NR entity.

This class is based on a template that receives as argument the
rlc_am_*_tx/rx entities, so that those are different for LTE and NR.

Moved code from rlc_am_lte/nr entities so that they use the new base class.
This commit is contained in:
Pedro Alvarez 2021-07-06 17:45:03 +01:00
parent e65bcd7147
commit b15f63f32f
5 changed files with 469 additions and 659 deletions

View File

@ -15,6 +15,8 @@
#include "srsran/common/buffer_pool.h"
#include "srsran/common/common.h"
#include "srsran/common/timers.h"
#include "srsran/interfaces/ue_rrc_interfaces.h"
#include "srsran/rlc/rlc_common.h"
#include "srsran/upper/byte_buffer_queue.h"
#include <map>
@ -22,15 +24,6 @@
#include <pthread.h>
#include <queue>
namespace srsran {
///< Add rlc_am_base here
bool rlc_am_is_control_pdu(uint8_t* payload);
bool rlc_am_is_control_pdu(byte_buffer_t* pdu);
} // namespace srsran
namespace srsue {
class pdcp_interface_rlc;
@ -38,4 +31,180 @@ class rrc_interface_rlc;
} // namespace srsue
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>
class rlc_am_base : public rlc_common
{
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)
{}
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
}
void stop() final
{
logger.debug("Stopped bearer %s", rb_name.c_str());
tx.stop();
rx.stop();
}
void empty_queue() final
{
// Drop all messages in TX SDU queue
tx.empty_queue();
}
rlc_mode_t get_mode() final { return rlc_mode_t::am; }
uint32_t get_bearer() final { return lcid; }
/****************************************************************************
* 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);
std::lock_guard<std::mutex> lock(metrics_mutex);
metrics.num_lost_sdus++;
}
bool sdu_queue_is_full() final { return tx.sdu_queue_is_full(); }
/****************************************************************************
* 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);
std::lock_guard<std::mutex> lock(metrics_mutex);
metrics.num_tx_pdus++;
metrics.num_tx_pdu_bytes += read_bytes;
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;
}
/****************************************************************************
* 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;
}
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); }
private:
// 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 = {};
std::string rb_name;
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 = {};
};
} // namespace srsran
#endif // SRSRAN_RLC_AM_BASE_H

View File

@ -80,251 +80,198 @@ private:
size_t rpos = 0;
};
class rlc_am_lte : public rlc_common
// 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
{
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_);
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();
void empty_queue();
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();
rlc_mode_t get_mode();
uint32_t get_bearer();
// PDCP interface
void write_sdu(unique_byte_buffer_t sdu);
void discard_sdu(uint32_t pdcp_sn);
bool sdu_queue_is_full();
// MAC interface
bool has_data();
uint32_t get_buffer_state();
void get_buffer_state(uint32_t& tx_queue, uint32_t& prio_tx_queue);
uint32_t read_pdu(uint8_t* payload, uint32_t nof_bytes);
void write_pdu(uint8_t* payload, uint32_t nof_bytes);
rlc_bearer_metrics_t get_metrics();
void reset_metrics();
// Timeout callback interface
void timer_expired(uint32_t timeout_id) final;
// Interface for Rx subclass
void handle_control_pdu(uint8_t* payload, uint32_t nof_bytes);
void set_bsr_callback(bsr_callback_t callback);
private:
// Transmitter sub-class
class rlc_am_lte_tx : public timer_callback
{
public:
rlc_am_lte_tx(rlc_am_lte* parent_);
~rlc_am_lte_tx();
void stop_nolock();
bool configure(const rlc_config_t& cfg_);
int build_status_pdu(uint8_t* payload, uint32_t nof_bytes);
int build_retx_pdu(uint8_t* payload, uint32_t nof_bytes);
int build_segment(uint8_t* payload, uint32_t nof_bytes, rlc_amd_retx_t retx);
int build_data_pdu(uint8_t* payload, uint32_t nof_bytes);
void update_notification_ack_info(uint32_t rlc_sn);
void empty_queue();
void reestablish();
void stop();
void empty_queue_nolock();
void debug_state();
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();
int required_buffer_size(const rlc_amd_retx_t& retx);
void retransmit_pdu(uint32_t sn);
bool has_data();
uint32_t get_buffer_state();
void get_buffer_state(uint32_t& new_tx, uint32_t& prio_tx);
// 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);
// Timeout callback interface
void timer_expired(uint32_t timeout_id);
rlc_am_lte* parent = nullptr;
byte_buffer_pool* pool = nullptr;
srslog::basic_logger& logger;
rlc_am_pdu_segment_pool segment_pool;
// Interface for Rx subclass
void handle_control_pdu(uint8_t* payload, uint32_t nof_bytes);
/****************************************************************************
* Configurable parameters
* Ref: 3GPP TS 36.322 v10.0.0 Section 7
***************************************************************************/
void set_bsr_callback(bsr_callback_t callback);
rlc_am_config_t cfg = {};
private:
void stop_nolock();
// TX SDU buffers
byte_buffer_queue tx_sdu_queue;
unique_byte_buffer_t tx_sdu;
int build_status_pdu(uint8_t* payload, uint32_t nof_bytes);
int build_retx_pdu(uint8_t* payload, uint32_t nof_bytes);
int build_segment(uint8_t* payload, uint32_t nof_bytes, rlc_amd_retx_t retx);
int build_data_pdu(uint8_t* payload, uint32_t nof_bytes);
void update_notification_ack_info(uint32_t rlc_sn);
bool tx_enabled = false;
void debug_state();
void empty_queue_nolock();
/****************************************************************************
* State variables and counters
* Ref: 3GPP TS 36.322 v10.0.0 Section 7
***************************************************************************/
int required_buffer_size(const rlc_amd_retx_t& retx);
void retransmit_pdu(uint32_t sn);
// Tx state variables
uint32_t vt_a = 0; // ACK state. SN of next PDU in sequence to be ACKed. Low edge of tx window.
uint32_t vt_ms = RLC_AM_WINDOW_SIZE; // Max send state. High edge of tx window. vt_a + window_size.
uint32_t vt_s = 0; // Send state. SN to be assigned for next PDU.
uint32_t poll_sn = 0; // Poll send state. SN of most recent PDU txed with poll bit set.
void get_buffer_state_nolock(uint32_t& new_tx, uint32_t& prio_tx);
// Tx counters
uint32_t pdu_without_poll = 0;
uint32_t byte_without_poll = 0;
// Helpers
bool poll_required();
bool do_status();
void check_sn_reached_max_retx(uint32_t sn);
rlc_status_pdu_t tx_status;
rlc_am_lte* parent = nullptr;
byte_buffer_pool* pool = nullptr;
srslog::basic_logger& logger;
rlc_am_pdu_segment_pool segment_pool;
/****************************************************************************
* Timers
* Ref: 3GPP TS 36.322 v10.0.0 Section 7
***************************************************************************/
/****************************************************************************
* Configurable parameters
* Ref: 3GPP TS 36.322 v10.0.0 Section 7
***************************************************************************/
srsran::timer_handler::unique_timer poll_retx_timer;
srsran::timer_handler::unique_timer status_prohibit_timer;
rlc_am_config_t cfg = {};
// SDU info for PDCP notifications
buffered_pdcp_pdu_list undelivered_sdu_info_queue;
// TX SDU buffers
byte_buffer_queue tx_sdu_queue;
unique_byte_buffer_t tx_sdu;
// Callback function for buffer status report
bsr_callback_t bsr_callback;
bool tx_enabled = false;
// Tx windows
rlc_ringbuffer_t<rlc_amd_tx_pdu, RLC_AM_WINDOW_SIZE> tx_window;
pdu_retx_queue retx_queue;
pdcp_sn_vector_t notify_info_vec;
/****************************************************************************
* State variables and counters
* Ref: 3GPP TS 36.322 v10.0.0 Section 7
***************************************************************************/
// Mutexes
std::mutex mutex;
// Tx state variables
uint32_t vt_a = 0; // ACK state. SN of next PDU in sequence to be ACKed. Low edge of tx window.
uint32_t vt_ms = RLC_AM_WINDOW_SIZE; // Max send state. High edge of tx window. vt_a + window_size.
uint32_t vt_s = 0; // Send state. SN to be assigned for next PDU.
uint32_t poll_sn = 0; // Poll send state. SN of most recent PDU txed with poll bit set.
// default to RLC SDU queue length
const uint32_t MAX_SDUS_PER_RLC_PDU = RLC_TX_QUEUE_LEN;
};
// Tx counters
uint32_t pdu_without_poll = 0;
uint32_t byte_without_poll = 0;
// Receiver sub-class
class rlc_am_lte_rx : public timer_callback
{
public:
rlc_am_lte_rx(rlc_am_lte* parent_);
~rlc_am_lte_rx();
rlc_status_pdu_t tx_status;
bool configure(rlc_config_t cfg_);
void reestablish();
void stop();
/****************************************************************************
* Timers
* Ref: 3GPP TS 36.322 v10.0.0 Section 7
***************************************************************************/
void write_pdu(uint8_t* payload, uint32_t nof_bytes);
srsran::timer_handler::unique_timer poll_retx_timer;
srsran::timer_handler::unique_timer status_prohibit_timer;
uint32_t get_rx_buffered_bytes(); // returns sum of PDUs in rx_window
uint32_t get_sdu_rx_latency_ms();
// SDU info for PDCP notifications
buffered_pdcp_pdu_list undelivered_sdu_info_queue;
// Timeout callback interface
void timer_expired(uint32_t timeout_id);
// Callback function for buffer status report
bsr_callback_t bsr_callback;
// 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);
bool get_do_status();
// Tx windows
rlc_ringbuffer_t<rlc_amd_tx_pdu, RLC_AM_WINDOW_SIZE> tx_window;
pdu_retx_queue retx_queue;
pdcp_sn_vector_t notify_info_vec;
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();
bool inside_rx_window(const int16_t sn);
void debug_state();
void print_rx_segments();
bool add_segment_and_check(rlc_amd_rx_pdu_segments_t* pdu, rlc_amd_rx_pdu* segment);
void reset_status();
// Mutexes
std::mutex mutex;
rlc_am_lte* parent = nullptr;
byte_buffer_pool* pool = nullptr;
srslog::basic_logger& logger;
// default to RLC SDU queue length
const uint32_t MAX_SDUS_PER_RLC_PDU = RLC_TX_QUEUE_LEN;
};
/****************************************************************************
* Configurable parameters
* Ref: 3GPP TS 36.322 v10.0.0 Section 7
***************************************************************************/
rlc_am_config_t cfg = {};
// Receiver sub-class
class rlc_am_lte_rx : public timer_callback
{
public:
rlc_am_lte_rx(rlc_am_lte* parent_);
~rlc_am_lte_rx();
// RX SDU buffers
unique_byte_buffer_t rx_sdu;
bool configure(rlc_am_config_t cfg_);
void reestablish();
void stop();
/****************************************************************************
* State variables and counters
* Ref: 3GPP TS 36.322 v10.0.0 Section 7
***************************************************************************/
void write_pdu(uint8_t* payload, uint32_t nof_bytes);
// Rx state variables
uint32_t vr_r = 0; // Receive state. SN following last in-sequence received PDU. Low edge of rx window
uint32_t vr_mr = RLC_AM_WINDOW_SIZE; // Max acceptable receive state. High edge of rx window. vr_r + window size.
uint32_t vr_x = 0; // t_reordering state. SN following PDU which triggered t_reordering.
uint32_t vr_ms = 0; // Max status tx state. Highest possible value of SN for ACK_SN in status PDU.
uint32_t vr_h = 0; // Highest rx state. SN following PDU with highest SN among rxed PDUs.
uint32_t get_rx_buffered_bytes(); // returns sum of PDUs in rx_window
uint32_t get_sdu_rx_latency_ms();
// Mutex to protect members
std::mutex mutex;
// Timeout callback interface
void timer_expired(uint32_t timeout_id);
// Rx windows
rlc_ringbuffer_t<rlc_amd_rx_pdu, RLC_AM_WINDOW_SIZE> rx_window;
std::map<uint32_t, rlc_amd_rx_pdu_segments_t> rx_segments;
// 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);
bool get_do_status();
bool poll_received = false;
std::atomic<bool> do_status = {false}; // light-weight access from Tx entity
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();
bool inside_rx_window(const int16_t sn);
void debug_state();
void print_rx_segments();
bool add_segment_and_check(rlc_amd_rx_pdu_segments_t* pdu, rlc_amd_rx_pdu* segment);
void reset_status();
/****************************************************************************
* Timers
* Ref: 3GPP TS 36.322 v10.0.0 Section 7
***************************************************************************/
rlc_am_lte* parent = nullptr;
byte_buffer_pool* pool = nullptr;
srslog::basic_logger& logger;
srsran::timer_handler::unique_timer reordering_timer;
/****************************************************************************
* Configurable parameters
* Ref: 3GPP TS 36.322 v10.0.0 Section 7
***************************************************************************/
rlc_am_config_t cfg = {};
// RX SDU buffers
unique_byte_buffer_t rx_sdu;
/****************************************************************************
* State variables and counters
* Ref: 3GPP TS 36.322 v10.0.0 Section 7
***************************************************************************/
// Rx state variables
uint32_t vr_r = 0; // Receive state. SN following last in-sequence received PDU. Low edge of rx window
uint32_t vr_mr = RLC_AM_WINDOW_SIZE; // Max acceptable receive state. High edge of rx window. vr_r + window size.
uint32_t vr_x = 0; // t_reordering state. SN following PDU which triggered t_reordering.
uint32_t vr_ms = 0; // Max status tx state. Highest possible value of SN for ACK_SN in status PDU.
uint32_t vr_h = 0; // Highest rx state. SN following PDU with highest SN among rxed PDUs.
// Mutex to protect members
std::mutex mutex;
// Rx windows
rlc_ringbuffer_t<rlc_amd_rx_pdu, RLC_AM_WINDOW_SIZE> rx_window;
std::map<uint32_t, rlc_amd_rx_pdu_segments_t> rx_segments;
bool poll_received = false;
std::atomic<bool> do_status = {false}; // light-weight access from Tx entity
/****************************************************************************
* Timers
* Ref: 3GPP TS 36.322 v10.0.0 Section 7
***************************************************************************/
srsran::timer_handler::unique_timer reordering_timer;
srsran::rolling_average<double> sdu_rx_latency_ms;
};
// 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 = {};
std::string rb_name;
static const int poll_periodicity = 8; // After how many data PDUs a status PDU shall be requested
// Rx and Tx objects
rlc_am_lte_tx tx;
rlc_am_lte_rx rx;
std::mutex metrics_mutex;
rlc_bearer_metrics_t metrics = {};
srsran::rolling_average<double> sdu_rx_latency_ms;
};
} // namespace srsran

View File

@ -25,112 +25,70 @@
namespace srsran {
class rlc_am_nr : public rlc_common
// 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
{
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_);
bool configure(const rlc_config_t& cfg_) final;
void stop() final;
explicit rlc_am_nr_tx(rlc_am_nr* parent_);
~rlc_am_nr_tx() = default;
rlc_mode_t get_mode() final;
uint32_t get_bearer() final;
bool configure(const rlc_config_t& cfg_);
void stop();
void reestablish() final;
void empty_queue() final;
void set_bsr_callback(bsr_callback_t callback) final;
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();
void reestablish();
// PDCP interface
void write_sdu(unique_byte_buffer_t sdu) final;
void discard_sdu(uint32_t pdcp_sn) final;
bool sdu_queue_is_full() final;
// MAC interface
bool has_data() final;
uint32_t get_buffer_state() final;
void get_buffer_state(uint32_t& tx_queue, uint32_t& prio_tx_queue) final;
uint32_t read_pdu(uint8_t* payload, uint32_t nof_bytes) final;
void write_pdu(uint8_t* payload, uint32_t nof_bytes) final;
rlc_bearer_metrics_t get_metrics() final;
void reset_metrics() final;
void empty_queue();
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:
// Transmitter sub-class
class rlc_am_nr_tx
{
public:
explicit rlc_am_nr_tx(rlc_am_nr* parent_);
~rlc_am_nr_tx() = default;
rlc_am_nr* parent = nullptr;
byte_buffer_pool* pool = nullptr;
srslog::basic_logger& logger;
bool configure(const rlc_am_config_t& cfg_);
void stop();
/****************************************************************************
* Configurable parameters
* Ref: 3GPP TS 38.322 v10.0.0 Section 7.4
***************************************************************************/
rlc_am_config_t cfg = {};
};
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();
// Receiver sub-class
class rlc_am_nr_rx
{
public:
explicit rlc_am_nr_rx(rlc_am_nr* parent_);
~rlc_am_nr_rx() = default;
bool has_data();
uint32_t get_buffer_state();
bool configure(const rlc_config_t& cfg_);
void stop();
void reestablish();
rlc_am_nr* parent = nullptr;
srslog::basic_logger& logger;
void write_pdu(uint8_t* payload, uint32_t nof_bytes);
uint32_t get_sdu_rx_latency_ms();
uint32_t get_rx_buffered_bytes();
private:
byte_buffer_pool* pool = nullptr;
private:
rlc_am_nr* parent = nullptr;
byte_buffer_pool* pool = nullptr;
srslog::basic_logger& logger;
/****************************************************************************
* Configurable parameters
* 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:
explicit rlc_am_nr_rx(rlc_am_nr* parent_);
~rlc_am_nr_rx() = default;
bool configure(const rlc_am_config_t& cfg_);
void stop();
void write_pdu(uint8_t* payload, uint32_t nof_bytes);
rlc_am_nr* parent = nullptr;
srslog::basic_logger& logger;
private:
byte_buffer_pool* pool = nullptr;
/****************************************************************************
* Configurable parameters
* Ref: 3GPP TS 38.322 v10.0.0 Section 7.4
***************************************************************************/
rlc_am_config_t cfg = {};
};
// 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 = {};
std::string rb_name;
static const int poll_periodicity = 8; // After how many data PDUs a status PDU shall be requested
// Rx and Tx objects
rlc_am_nr_tx tx;
rlc_am_nr_rx rx;
rlc_bearer_metrics_t metrics = {};
/****************************************************************************
* Configurable parameters
* Ref: 3GPP TS 38.322 v10.0.0 Section 7.4
***************************************************************************/
rlc_am_config_t cfg = {};
};
} // namespace srsran

View File

@ -89,172 +89,11 @@ rlc_amd_tx_pdu::~rlc_amd_tx_pdu()
}
}
/*******************************
* rlc_am_lte class
******************************/
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_) :
logger(logger), rrc(rrc_), pdcp(pdcp_), timers(timers_), lcid(lcid_), tx(this), rx(this)
{}
// Applies new configuration. Must be just reestablished or initiated
bool rlc_am_lte::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.configure(cfg.am)) {
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 rlc_am_lte::set_bsr_callback(bsr_callback_t callback)
{
tx.set_bsr_callback(callback);
}
void rlc_am_lte::empty_queue()
{
// Drop all messages in TX SDU queue
tx.empty_queue();
}
void rlc_am_lte::reestablish()
{
logger.debug("Reestablished bearer %s", rb_name.c_str());
tx.reestablish(); // calls stop and enables tx again
rx.reestablish(); // calls only stop
}
void rlc_am_lte::stop()
{
logger.debug("Stopped bearer %s", rb_name.c_str());
tx.stop();
rx.stop();
}
rlc_mode_t rlc_am_lte::get_mode()
{
return rlc_mode_t::am;
}
uint32_t rlc_am_lte::get_bearer()
{
return lcid;
}
rlc_bearer_metrics_t rlc_am_lte::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;
}
void rlc_am_lte::reset_metrics()
{
std::lock_guard<std::mutex> lock(metrics_mutex);
metrics = {};
}
/****************************************************************************
* PDCP interface
***************************************************************************/
void rlc_am_lte::write_sdu(unique_byte_buffer_t sdu)
{
if (tx.write_sdu(std::move(sdu)) == SRSRAN_SUCCESS) {
std::lock_guard<std::mutex> lock(metrics_mutex);
metrics.num_tx_sdus++;
}
}
void rlc_am_lte::discard_sdu(uint32_t discard_sn)
{
tx.discard_sdu(discard_sn);
std::lock_guard<std::mutex> lock(metrics_mutex);
metrics.num_lost_sdus++;
}
bool rlc_am_lte::sdu_queue_is_full()
{
return tx.sdu_queue_is_full();
}
/****************************************************************************
* MAC interface
***************************************************************************/
bool rlc_am_lte::has_data()
{
return tx.has_data();
}
uint32_t rlc_am_lte::get_buffer_state()
{
return tx.get_buffer_state();
}
void rlc_am_lte::get_buffer_state(uint32_t& tx_queue, uint32_t& prio_tx_queue)
{
tx.get_buffer_state(tx_queue, prio_tx_queue);
}
uint32_t rlc_am_lte::read_pdu(uint8_t* payload, uint32_t nof_bytes)
{
uint32_t read_bytes = tx.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_lte::write_pdu(uint8_t* payload, uint32_t nof_bytes)
{
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;
}
/****************************************************************************
* Tx subclass implementation
***************************************************************************/
rlc_am_lte::rlc_am_lte_tx::rlc_am_lte_tx(rlc_am_lte* parent_) :
rlc_am_lte_tx::rlc_am_lte_tx(rlc_am_lte* parent_) :
parent(parent_),
logger(parent_->logger),
pool(byte_buffer_pool::get_instance()),
@ -262,14 +101,12 @@ rlc_am_lte::rlc_am_lte_tx::rlc_am_lte_tx(rlc_am_lte* parent_) :
status_prohibit_timer(parent_->timers->get_unique_timer())
{}
rlc_am_lte::rlc_am_lte_tx::~rlc_am_lte_tx() {}
void rlc_am_lte::rlc_am_lte_tx::set_bsr_callback(bsr_callback_t callback)
void rlc_am_lte_tx::set_bsr_callback(bsr_callback_t callback)
{
bsr_callback = callback;
}
bool rlc_am_lte::rlc_am_lte_tx::configure(const rlc_config_t& cfg_)
bool 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) {
@ -307,13 +144,13 @@ bool rlc_am_lte::rlc_am_lte_tx::configure(const rlc_config_t& cfg_)
return true;
}
void rlc_am_lte::rlc_am_lte_tx::stop()
void rlc_am_lte_tx::stop()
{
std::lock_guard<std::mutex> lock(mutex);
stop_nolock();
}
void rlc_am_lte::rlc_am_lte_tx::stop_nolock()
void rlc_am_lte_tx::stop_nolock()
{
empty_queue_nolock();
@ -345,13 +182,13 @@ void rlc_am_lte::rlc_am_lte_tx::stop_nolock()
undelivered_sdu_info_queue.clear();
}
void rlc_am_lte::rlc_am_lte_tx::empty_queue()
void rlc_am_lte_tx::empty_queue()
{
std::lock_guard<std::mutex> lock(mutex);
empty_queue_nolock();
}
void rlc_am_lte::rlc_am_lte_tx::empty_queue_nolock()
void rlc_am_lte_tx::empty_queue_nolock()
{
// deallocate all SDUs in transmit queue
while (tx_sdu_queue.size() > 0) {
@ -365,20 +202,20 @@ void rlc_am_lte::rlc_am_lte_tx::empty_queue_nolock()
tx_sdu.reset();
}
void rlc_am_lte::rlc_am_lte_tx::reestablish()
void rlc_am_lte_tx::reestablish()
{
std::lock_guard<std::mutex> lock(mutex);
stop_nolock();
tx_enabled = true;
}
bool rlc_am_lte::rlc_am_lte_tx::do_status()
bool rlc_am_lte_tx::do_status()
{
return parent->rx.get_do_status();
}
// Function is supposed to return as fast as possible
bool rlc_am_lte::rlc_am_lte_tx::has_data()
bool 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
@ -395,7 +232,7 @@ bool rlc_am_lte::rlc_am_lte_tx::has_data()
*
* @param sn The SN of the PDU to check
*/
void rlc_am_lte::rlc_am_lte_tx::check_sn_reached_max_retx(uint32_t sn)
void 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);
@ -411,27 +248,27 @@ void rlc_am_lte::rlc_am_lte_tx::check_sn_reached_max_retx(uint32_t sn)
}
}
uint32_t rlc_am_lte::rlc_am_lte_tx::get_buffer_state()
uint32_t 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::rlc_am_lte_tx::get_buffer_state(uint32_t& n_bytes_newtx, uint32_t& n_bytes_prio)
void 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::rlc_am_lte_tx::get_buffer_state_nolock(uint32_t& n_bytes_newtx, uint32_t& n_bytes_prio)
void 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;
uint32_t n_sdus = 0;
logger.debug("%s Buffer state - do_status=%s, status_prohibit_running=%s (%d/%d)",
RB_NAME,
parent->rb_name,
do_status() ? "yes" : "no",
status_prohibit_timer.is_running() ? "yes" : "no",
status_prohibit_timer.time_elapsed(),
@ -490,7 +327,7 @@ void rlc_am_lte::rlc_am_lte_tx::get_buffer_state_nolock(uint32_t& n_bytes_newtx,
}
}
int rlc_am_lte::rlc_am_lte_tx::write_sdu(unique_byte_buffer_t sdu)
int rlc_am_lte_tx::write_sdu(unique_byte_buffer_t sdu)
{
std::lock_guard<std::mutex> lock(mutex);
@ -526,7 +363,7 @@ int rlc_am_lte::rlc_am_lte_tx::write_sdu(unique_byte_buffer_t sdu)
return SRSRAN_SUCCESS;
}
void rlc_am_lte::rlc_am_lte_tx::discard_sdu(uint32_t discard_sn)
void rlc_am_lte_tx::discard_sdu(uint32_t discard_sn)
{
if (!tx_enabled) {
return;
@ -544,12 +381,12 @@ void rlc_am_lte::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::rlc_am_lte_tx::sdu_queue_is_full()
bool rlc_am_lte_tx::sdu_queue_is_full()
{
return tx_sdu_queue.is_full();
}
uint32_t rlc_am_lte::rlc_am_lte_tx::read_pdu(uint8_t* payload, uint32_t nof_bytes)
uint32_t rlc_am_lte_tx::read_pdu(uint8_t* payload, uint32_t nof_bytes)
{
std::lock_guard<std::mutex> lock(mutex);
@ -587,7 +424,7 @@ uint32_t rlc_am_lte::rlc_am_lte_tx::read_pdu(uint8_t* payload, uint32_t nof_byte
return build_data_pdu(payload, nof_bytes);
}
void rlc_am_lte::rlc_am_lte_tx::timer_expired(uint32_t timeout_id)
void 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) {
@ -608,7 +445,7 @@ void rlc_am_lte::rlc_am_lte_tx::timer_expired(uint32_t timeout_id)
}
}
void rlc_am_lte::rlc_am_lte_tx::retransmit_pdu(uint32_t sn)
void rlc_am_lte_tx::retransmit_pdu(uint32_t sn)
{
if (tx_window.empty()) {
logger.warning("%s No PDU to retransmit", RB_NAME);
@ -647,7 +484,7 @@ void rlc_am_lte::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::rlc_am_lte_tx::poll_required()
bool rlc_am_lte_tx::poll_required()
{
if (cfg.poll_pdu > 0 && pdu_without_poll > static_cast<uint32_t>(cfg.poll_pdu)) {
return true;
@ -675,14 +512,14 @@ bool rlc_am_lte::rlc_am_lte_tx::poll_required()
* However, it seems more appropiate to request more often if polling
* is disabled otherwise, e.g. every N PDUs.
*/
if (cfg.poll_pdu == 0 && cfg.poll_byte == 0 && vt_s % poll_periodicity == 0) {
if (cfg.poll_pdu == 0 && cfg.poll_byte == 0 && vt_s % rlc_am_lte::poll_periodicity == 0) {
return true;
}
return false;
}
int rlc_am_lte::rlc_am_lte_tx::build_status_pdu(uint8_t* payload, uint32_t nof_bytes)
int 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);
if (pdu_len == SRSRAN_ERROR) {
@ -704,7 +541,7 @@ int rlc_am_lte::rlc_am_lte_tx::build_status_pdu(uint8_t* payload, uint32_t nof_b
return pdu_len;
}
int rlc_am_lte::rlc_am_lte_tx::build_retx_pdu(uint8_t* payload, uint32_t nof_bytes)
int 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()) {
@ -785,7 +622,7 @@ int rlc_am_lte::rlc_am_lte_tx::build_retx_pdu(uint8_t* payload, uint32_t nof_byt
return (ptr - payload) + tx_window[retx.sn].buf->N_bytes;
}
int rlc_am_lte::rlc_am_lte_tx::build_segment(uint8_t* payload, uint32_t nof_bytes, rlc_amd_retx_t retx)
int 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);
@ -960,7 +797,7 @@ int rlc_am_lte::rlc_am_lte_tx::build_segment(uint8_t* payload, uint32_t nof_byte
return pdu_len;
}
int rlc_am_lte::rlc_am_lte_tx::build_data_pdu(uint8_t* payload, uint32_t nof_bytes)
int 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");
@ -1176,7 +1013,7 @@ int rlc_am_lte::rlc_am_lte_tx::build_data_pdu(uint8_t* payload, uint32_t nof_byt
return total_len;
}
void rlc_am_lte::rlc_am_lte_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes)
void rlc_am_lte_tx::handle_control_pdu(uint8_t* payload, uint32_t nof_bytes)
{
if (not tx_enabled) {
return;
@ -1324,7 +1161,7 @@ void rlc_am_lte::rlc_am_lte_tx::handle_control_pdu(uint8_t* payload, uint32_t no
* @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::rlc_am_lte_tx::update_notification_ack_info(uint32_t rlc_sn)
void 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,
@ -1361,12 +1198,12 @@ void rlc_am_lte::rlc_am_lte_tx::update_notification_ack_info(uint32_t rlc_sn)
}
}
void rlc_am_lte::rlc_am_lte_tx::debug_state()
void 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::rlc_am_lte_tx::required_buffer_size(const rlc_amd_retx_t& retx)
int rlc_am_lte_tx::required_buffer_size(const rlc_amd_retx_t& retx)
{
if (!retx.is_segment) {
if (tx_window.has_sn(retx.sn)) {
@ -1443,19 +1280,19 @@ int rlc_am_lte::rlc_am_lte_tx::required_buffer_size(const rlc_amd_retx_t& retx)
* Rx subclass implementation
***************************************************************************/
rlc_am_lte::rlc_am_lte_rx::rlc_am_lte_rx(rlc_am_lte* parent_) :
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::rlc_am_lte_rx::~rlc_am_lte_rx() {}
rlc_am_lte_rx::~rlc_am_lte_rx() {}
bool rlc_am_lte::rlc_am_lte_rx::configure(rlc_am_config_t cfg_)
bool rlc_am_lte_rx::configure(rlc_config_t cfg_)
{
// TODO: add config checks
cfg = cfg_;
cfg = cfg_.am;
// check timers
if (not reordering_timer.is_valid()) {
@ -1471,12 +1308,12 @@ bool rlc_am_lte::rlc_am_lte_rx::configure(rlc_am_config_t cfg_)
return true;
}
void rlc_am_lte::rlc_am_lte_rx::reestablish()
void rlc_am_lte_rx::reestablish()
{
stop();
}
void rlc_am_lte::rlc_am_lte_rx::stop()
void rlc_am_lte_rx::stop()
{
std::lock_guard<std::mutex> lock(mutex);
@ -1508,7 +1345,7 @@ void rlc_am_lte::rlc_am_lte_rx::stop()
* @param nof_bytes Payload length
* @param header Reference to PDU header (unpacked by caller)
*/
void rlc_am_lte::rlc_am_lte_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_bytes, rlc_amd_pdu_header_t& header)
void 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;
@ -1625,9 +1462,7 @@ void rlc_am_lte::rlc_am_lte_rx::handle_data_pdu(uint8_t* payload, uint32_t nof_b
debug_state();
}
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)
void 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;
@ -1715,7 +1550,7 @@ void rlc_am_lte::rlc_am_lte_rx::handle_data_pdu_segment(uint8_t* pa
debug_state();
}
void rlc_am_lte::rlc_am_lte_rx::reassemble_rx_sdus()
void rlc_am_lte_rx::reassemble_rx_sdus()
{
uint32_t len = 0;
if (rx_sdu == NULL) {
@ -1866,18 +1701,18 @@ void rlc_am_lte::rlc_am_lte_rx::reassemble_rx_sdus()
}
}
void rlc_am_lte::rlc_am_lte_rx::reset_status()
void rlc_am_lte_rx::reset_status()
{
do_status = false;
poll_received = false;
}
bool rlc_am_lte::rlc_am_lte_rx::get_do_status()
bool rlc_am_lte_rx::get_do_status()
{
return do_status.load(std::memory_order_relaxed);
}
void rlc_am_lte::rlc_am_lte_rx::write_pdu(uint8_t* payload, const uint32_t nof_bytes)
void rlc_am_lte_rx::write_pdu(uint8_t* payload, const uint32_t nof_bytes)
{
if (nof_bytes < 1) {
return;
@ -1902,13 +1737,13 @@ void rlc_am_lte::rlc_am_lte_rx::write_pdu(uint8_t* payload, const uint32_t nof_b
}
}
uint32_t rlc_am_lte::rlc_am_lte_rx::get_rx_buffered_bytes()
uint32_t 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::rlc_am_lte_rx::get_sdu_rx_latency_ms()
uint32_t rlc_am_lte_rx::get_sdu_rx_latency_ms()
{
std::lock_guard<std::mutex> lock(mutex);
return sdu_rx_latency_ms.value();
@ -1919,7 +1754,7 @@ uint32_t rlc_am_lte::rlc_am_lte_rx::get_sdu_rx_latency_ms()
*
* @param timeout_id
*/
void rlc_am_lte::rlc_am_lte_rx::timer_expired(uint32_t timeout_id)
void 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) {
@ -1946,7 +1781,7 @@ void rlc_am_lte::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::rlc_am_lte_rx::get_status_pdu(rlc_status_pdu_t* status, const uint32_t max_pdu_size)
int 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()) {
@ -1999,7 +1834,7 @@ int rlc_am_lte::rlc_am_lte_rx::get_status_pdu(rlc_status_pdu_t* status, const ui
}
// Called from Tx object to obtain length of the full status PDU
int rlc_am_lte::rlc_am_lte_rx::get_status_pdu_length()
int rlc_am_lte_rx::get_status_pdu_length()
{
std::unique_lock<std::mutex> lock(mutex, std::try_to_lock);
if (not lock.owns_lock()) {
@ -2017,7 +1852,7 @@ int rlc_am_lte::rlc_am_lte_rx::get_status_pdu_length()
return rlc_am_packed_length(&status);
}
void rlc_am_lte::rlc_am_lte_rx::print_rx_segments()
void rlc_am_lte_rx::print_rx_segments()
{
std::map<uint32_t, rlc_amd_rx_pdu_segments_t>::iterator it;
std::stringstream ss;
@ -2033,7 +1868,7 @@ void rlc_am_lte::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::rlc_am_lte_rx::add_segment_and_check(rlc_amd_rx_pdu_segments_t* pdu, rlc_amd_rx_pdu* segment)
bool 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();
@ -2213,7 +2048,7 @@ bool rlc_am_lte::rlc_am_lte_rx::add_segment_and_check(rlc_amd_rx_pdu_segments_t*
return true;
}
bool rlc_am_lte::rlc_am_lte_rx::inside_rx_window(const int16_t sn)
bool 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;
@ -2222,7 +2057,7 @@ bool rlc_am_lte::rlc_am_lte_rx::inside_rx_window(const int16_t sn)
}
}
void rlc_am_lte::rlc_am_lte_rx::debug_state()
void 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);
}

View File

@ -20,137 +20,14 @@
namespace srsran {
/*******************************
* RLC AM NR class
******************************/
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_) :
logger(logger), rrc(rrc_), pdcp(pdcp_), timers(timers_), lcid(lcid_), tx(this), rx(this)
{}
// Applies new configuration. Must be just reestablished or initiated
bool rlc_am_nr::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.configure(cfg.am)) {
logger.error("Error configuring bearer (RX)");
return false;
}
if (not tx.configure(cfg.am)) {
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_nr::stop() {}
rlc_mode_t rlc_am_nr::get_mode()
{
return rlc_mode_t::am;
}
uint32_t rlc_am_nr::get_bearer()
{
return 0;
}
void rlc_am_nr::reestablish() {}
void rlc_am_nr::empty_queue() {}
void rlc_am_nr::set_bsr_callback(bsr_callback_t callback) {}
rlc_bearer_metrics_t rlc_am_nr::get_metrics()
{
return {};
}
void rlc_am_nr::reset_metrics() {}
/****************************************************************************
* PDCP interface
* RLC AM NR
* Tx subclass implementation
***************************************************************************/
void rlc_am_nr::write_sdu(unique_byte_buffer_t sdu)
{
if (tx.write_sdu(std::move(sdu)) == SRSRAN_SUCCESS) {
metrics.num_tx_sdus++;
}
}
void rlc_am_nr::discard_sdu(uint32_t pdcp_sn)
{
tx.discard_sdu(pdcp_sn);
metrics.num_lost_sdus++;
}
bool rlc_am_nr::sdu_queue_is_full()
{
return tx.sdu_queue_is_full();
}
/****************************************************************************
* MAC interface
***************************************************************************/
bool rlc_am_nr::has_data()
{
return tx.has_data();
}
uint32_t rlc_am_nr::get_buffer_state()
{
return tx.get_buffer_state();
}
void rlc_am_nr::get_buffer_state(uint32_t& tx_queue, uint32_t& prio_tx_queue)
{
// TODO
tx_queue = tx.get_buffer_state();
prio_tx_queue = 0;
}
uint32_t rlc_am_nr::read_pdu(uint8_t* payload, uint32_t nof_bytes)
{
uint32_t read_bytes = tx.read_pdu(payload, nof_bytes);
metrics.num_tx_pdus++;
metrics.num_tx_pdu_bytes += read_bytes;
return read_bytes;
}
void rlc_am_nr::write_pdu(uint8_t* payload, uint32_t nof_bytes)
{
rx.write_pdu(payload, nof_bytes);
metrics.num_rx_pdus++;
metrics.num_rx_pdu_bytes += nof_bytes;
}
/****************************************************************************
* Tx subclass implementation
***************************************************************************/
rlc_am_nr::rlc_am_nr_tx::rlc_am_nr_tx(rlc_am_nr* parent_) :
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::rlc_am_nr_tx::configure(const rlc_am_config_t& cfg_)
bool rlc_am_nr_tx::configure(const rlc_config_t& cfg_)
{
/*
if (cfg_.tx_queue_length > MAX_SDUS_PER_RLC_PDU) {
@ -160,57 +37,81 @@ bool rlc_am_nr::rlc_am_nr_tx::configure(const rlc_am_config_t& cfg_)
return false;
}
*/
cfg = cfg_;
cfg = cfg_.am;
return true;
}
int rlc_am_nr::rlc_am_nr_tx::write_sdu(unique_byte_buffer_t sdu)
bool rlc_am_nr_tx::has_data()
{
return true;
}
uint32_t rlc_am_nr_tx::read_pdu(uint8_t* payload, uint32_t nof_bytes)
{
return 0;
}
void rlc_am_nr::rlc_am_nr_tx::discard_sdu(uint32_t sn)
void rlc_am_nr_tx::reestablish()
{
return;
stop();
}
bool rlc_am_nr::rlc_am_nr_tx::sdu_queue_is_full()
uint32_t 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) {}
int rlc_am_nr_tx::write_sdu(unique_byte_buffer_t sdu)
{
return 0;
}
void rlc_am_nr_tx::discard_sdu(uint32_t discard_sn) {}
bool rlc_am_nr_tx::sdu_queue_is_full()
{
return false;
}
bool rlc_am_nr::rlc_am_nr_tx::has_data()
{
return true;
}
void rlc_am_nr_tx::empty_queue() {}
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::set_bsr_callback(const bsr_callback_t& callback) {}
uint32_t rlc_am_nr::rlc_am_nr_tx::get_buffer_state()
{
return 0;
}
void rlc_am_nr_tx::stop() {}
/****************************************************************************
* Rx subclass implementation
***************************************************************************/
rlc_am_nr::rlc_am_nr_rx::rlc_am_nr_rx(rlc_am_nr* parent_) :
rlc_am_nr_rx::rlc_am_nr_rx(rlc_am_nr* parent_) :
parent(parent_), pool(byte_buffer_pool::get_instance()), logger(parent_->logger)
{}
bool rlc_am_nr::rlc_am_nr_rx::configure(const rlc_am_config_t& cfg_)
bool rlc_am_nr_rx::configure(const rlc_config_t& cfg_)
{
cfg = cfg_;
cfg = cfg_.am;
return true;
}
void rlc_am_nr::rlc_am_nr_rx::stop() {}
void rlc_am_nr_rx::stop() {}
void rlc_am_nr::rlc_am_nr_rx::write_pdu(uint8_t* payload, uint32_t nof_bytes) {}
void rlc_am_nr_rx::write_pdu(uint8_t* payload, uint32_t nof_bytes) {}
void rlc_am_nr_rx::reestablish()
{
stop();
}
uint32_t rlc_am_nr_rx::get_sdu_rx_latency_ms()
{
return 0;
}
uint32_t rlc_am_nr_rx::get_rx_buffered_bytes()
{
return 0;
}
} // namespace srsran