You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

345 lines
12 KiB
C

/**
*
* \section COPYRIGHT
*
* Copyright 2013-2021 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#ifndef SRSRAN_RLC_COMMON_H
#define SRSRAN_RLC_COMMON_H
#include "srsran/adt/circular_buffer.h"
#include "srsran/adt/circular_map.h"
#include "srsran/adt/intrusive_list.h"
#include "srsran/interfaces/rlc_interface_types.h"
#include "srsran/rlc/bearer_mem_pool.h"
#include "srsran/rlc/rlc_metrics.h"
#include <cstdlib>
#include <list>
namespace srsran {
/****************************************************************************
* Structs and Defines
* Ref: 3GPP TS 36.322 v10.0.0
***************************************************************************/
#define MOD 1024
#define RLC_AM_WINDOW_SIZE 512
#define RLC_MAX_SDU_SIZE ((1 << 11) - 1) // Length of LI field is 11bits
#define RLC_AM_MIN_DATA_PDU_SIZE (3) // AMD PDU with 10 bit SN (length of LI field is 11 bits) (No LI)
#define RLC_AM_NR_TYP_NACKS 512 // Expected number of NACKs in status PDU before expanding space by alloc
#define RLC_AM_NR_MAX_NACKS 2048 // Maximum number of NACKs in status PDU
#define RlcDebug(fmt, ...) logger.debug("%s: " fmt, rb_name, ##__VA_ARGS__)
#define RlcInfo(fmt, ...) logger.info("%s: " fmt, rb_name, ##__VA_ARGS__)
#define RlcWarning(fmt, ...) logger.warning("%s: " fmt, rb_name, ##__VA_ARGS__)
#define RlcError(fmt, ...) logger.error("%s: " fmt, rb_name, ##__VA_ARGS__)
#define RlcHexDebug(msg, bytes, fmt, ...) logger.debug(msg, bytes, "%s: " fmt, rb_name, ##__VA_ARGS__)
#define RlcHexInfo(msg, bytes, fmt, ...) logger.info(msg, bytes, "%s: " fmt, rb_name, ##__VA_ARGS__)
#define RlcHexWarning(msg, bytes, fmt, ...) logger.warning(msg, bytes, "%s: " fmt, rb_name, ##__VA_ARGS__)
#define RlcHexError(msg, bytes, fmt, ...) logger.error(msg, bytes, "%s: " fmt, rb_name, ##__VA_ARGS__)
typedef enum {
RLC_FI_FIELD_START_AND_END_ALIGNED = 0,
RLC_FI_FIELD_NOT_END_ALIGNED,
RLC_FI_FIELD_NOT_START_ALIGNED,
RLC_FI_FIELD_NOT_START_OR_END_ALIGNED,
RLC_FI_FIELD_N_ITEMS,
} rlc_fi_field_t;
static const char rlc_fi_field_text[RLC_FI_FIELD_N_ITEMS][32] = {"Start and end aligned",
"Not end aligned",
"Not start aligned",
"Not start or end aligned"};
enum class rlc_nr_si_field_t : unsigned {
full_sdu = 0b00,
first_segment = 0b01,
last_segment = 0b10,
neither_first_nor_last_segment = 0b11,
nulltype
};
inline std::string to_string(const rlc_nr_si_field_t& si)
{
constexpr static const char* options[] = {"Data field contains full SDU",
"Data field contains first segment of SDU",
"Data field contains last segment of SDU",
"Data field contains neither first nor last segment of SDU"};
return enum_to_text(options, (uint32_t)rlc_nr_si_field_t::nulltype, (uint32_t)si);
}
inline std::string to_string_short(const rlc_nr_si_field_t& si)
{
constexpr static const char* options[] = {"full", "first", "last", "middle"};
return enum_to_text(options, (uint32_t)rlc_nr_si_field_t::nulltype, (uint32_t)si);
}
static inline uint8_t operator&(rlc_nr_si_field_t lhs, int rhs)
{
return static_cast<uint8_t>(static_cast<std::underlying_type<rlc_nr_si_field_t>::type>(lhs) &
static_cast<std::underlying_type<rlc_nr_si_field_t>::type>(rhs));
}
enum class rlc_am_nr_control_pdu_type_t : unsigned { status_pdu = 0b000, nulltype };
inline std::string to_string(const rlc_am_nr_control_pdu_type_t& type)
{
constexpr static const char* options[] = {"Control PDU"};
return enum_to_text(options, (uint32_t)rlc_am_nr_control_pdu_type_t::nulltype, (uint32_t)type);
}
typedef enum {
RLC_DC_FIELD_CONTROL_PDU = 0,
RLC_DC_FIELD_DATA_PDU,
RLC_DC_FIELD_N_ITEMS,
} rlc_dc_field_t;
static const char rlc_dc_field_text[RLC_DC_FIELD_N_ITEMS][20] = {"Control PDU", "Data PDU"};
// UMD PDU Header
typedef struct {
uint8_t fi; // Framing info
rlc_umd_sn_size_t sn_size; // Sequence number size (5 or 10 bits)
uint16_t sn; // Sequence number
uint32_t N_li; // Number of length indicators
uint16_t li[RLC_AM_WINDOW_SIZE]; // Array of length indicators
} rlc_umd_pdu_header_t;
typedef struct {
rlc_nr_si_field_t si; // Segmentation info
rlc_um_nr_sn_size_t sn_size; // Sequence number size (6 or 12 bits)
uint16_t sn; // Sequence number
uint16_t so; // Segment offset
} rlc_um_nr_pdu_header_t;
// AMD PDU Header
class rlc_amd_pdu_header_t
{
public:
rlc_amd_pdu_header_t() = default;
rlc_amd_pdu_header_t(const rlc_amd_pdu_header_t& h) { copy(h); }
rlc_amd_pdu_header_t(rlc_amd_pdu_header_t&& h) noexcept { copy(h); }
rlc_amd_pdu_header_t& operator=(const rlc_amd_pdu_header_t& h)
{
if (this == &h) {
return *this;
}
copy(h);
return *this;
}
rlc_amd_pdu_header_t& operator=(rlc_amd_pdu_header_t&& h) noexcept
{
copy(h);
return *this;
}
void copy(const rlc_amd_pdu_header_t& h)
{
dc = h.dc;
rf = h.rf;
p = h.p;
fi = h.fi;
sn = h.sn;
lsf = h.lsf;
so = h.so;
N_li = h.N_li;
for (uint32_t i = 0; i < h.N_li; i++) {
li[i] = h.li[i];
}
}
rlc_dc_field_t dc = RLC_DC_FIELD_CONTROL_PDU; // Data or control
uint8_t rf = 0; // Resegmentation flag
uint8_t p = 0; // Polling bit
uint8_t fi = RLC_FI_FIELD_START_AND_END_ALIGNED; // Framing info
uint16_t sn = 0; // Sequence number
uint8_t lsf = 0; // Last segment flag
uint16_t so = 0; // Segment offset
uint32_t N_li = 0; // Number of length indicators
uint16_t li[RLC_AM_WINDOW_SIZE] = {0}; // Array of length indicators
};
// 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
uint16_t so_end; // Last missing byte in SDU with SN=nack_sn or SN=nack_sn+nack_range-1 if has_nack_range.
bool has_nack_range; // NACKs continuous sequence of SDUs
uint8_t nack_range; // Number of SDUs being NACKed (including SN=nack_sn)
rlc_status_nack_t()
{
has_so = false;
nack_sn = 0;
so_start = 0;
so_end = so_end_of_sdu;
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
struct rlc_status_pdu_t {
uint16_t ack_sn; // SN of the next not received RLC Data PDU
uint32_t N_nack;
rlc_status_nack_t nacks[RLC_AM_WINDOW_SIZE];
rlc_status_pdu_t()
{
N_nack = 0;
ack_sn = 0;
}
};
typedef std::function<void(uint32_t, uint32_t, uint32_t)> bsr_callback_t;
/****************************************************************************
* RLC Common interface
* Common interface for all RLC entities
***************************************************************************/
class rlc_common
{
public:
explicit rlc_common(srslog::basic_logger& logger_) : logger(logger_) {}
virtual ~rlc_common() = default;
virtual bool configure(const rlc_config_t& cnfg) = 0;
virtual void stop() = 0;
virtual void reestablish() = 0;
virtual void empty_queue() = 0;
bool suspend()
{
if (suspended) {
return false;
}
suspended = true;
return true;
}
// Pops all PDUs from queue and calls write_pdu() method for the bearer type
bool resume()
{
if (!suspended) {
return false;
}
unique_byte_buffer_t rx_pdu;
// Do not block
while (rx_pdu_resume_queue.try_pop(rx_pdu)) {
write_pdu(rx_pdu->msg, rx_pdu->N_bytes);
}
unique_byte_buffer_t tx_sdu;
while (tx_sdu_resume_queue.try_pop(tx_sdu)) {
write_sdu(std::move(tx_sdu));
}
suspended = false;
return true;
}
void write_pdu_s(uint8_t* payload, uint32_t nof_bytes)
{
if (suspended) {
queue_rx_pdu(payload, nof_bytes);
} else {
write_pdu(payload, nof_bytes);
}
}
void write_sdu_s(unique_byte_buffer_t sdu)
{
if (suspended) {
queue_tx_sdu(std::move(sdu));
} else {
write_sdu(std::move(sdu));
}
}
virtual rlc_mode_t get_mode() = 0;
virtual uint32_t get_lcid() = 0;
virtual rlc_bearer_metrics_t get_metrics() = 0;
virtual void reset_metrics() = 0;
// PDCP interface
virtual void write_sdu(unique_byte_buffer_t sdu) = 0;
virtual void discard_sdu(uint32_t discard_sn) = 0;
virtual bool sdu_queue_is_full() = 0;
// MAC interface
virtual bool has_data() = 0;
bool is_suspended() { return suspended; };
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;
virtual void write_pdu(uint8_t* payload, uint32_t nof_bytes) = 0;
virtual void set_bsr_callback(bsr_callback_t callback) = 0;
void* operator new(size_t sz) { return allocate_rlc_bearer(sz); }
void operator delete(void* p) { return deallocate_rlc_bearer(p); }
protected:
std::string rb_name = {};
srslog::basic_logger& logger;
private:
bool suspended = false;
// Enqueues the Rx PDU in the resume queue
void queue_rx_pdu(uint8_t* payload, uint32_t nof_bytes)
{
unique_byte_buffer_t rx_pdu = srsran::make_byte_buffer();
if (rx_pdu == nullptr) {
srslog::fetch_basic_logger("RLC").warning("Couldn't allocate PDU in %s().", __FUNCTION__);
return;
}
if (rx_pdu->get_tailroom() < nof_bytes) {
srslog::fetch_basic_logger("RLC").warning("Not enough space to store PDU.");
return;
}
memcpy(rx_pdu->msg, payload, nof_bytes);
rx_pdu->N_bytes = nof_bytes;
// Do not block ever
if (!rx_pdu_resume_queue.try_push(std::move(rx_pdu))) {
srslog::fetch_basic_logger("RLC").warning("Dropping SDUs while bearer suspended.");
return;
}
}
// Enqueues the Tx SDU in the resume queue
void queue_tx_sdu(unique_byte_buffer_t sdu)
{
// Do not block ever
if (not tx_sdu_resume_queue.try_push(std::move(sdu))) {
srslog::fetch_basic_logger("RLC").warning("Dropping SDUs while bearer suspended.");
return;
}
}
static_blocking_queue<unique_byte_buffer_t, 256> rx_pdu_resume_queue;
static_blocking_queue<unique_byte_buffer_t, 256> tx_sdu_resume_queue;
};
} // namespace srsran
#endif // SRSRAN_RLC_COMMON_H