mirror of https://github.com/PentHertz/srsLTE.git
add RLC UM NR transmitter
This commit is contained in:
parent
11109460e6
commit
c99d5077c8
|
@ -182,6 +182,18 @@ inline uint16_t to_number(const rlc_umd_sn_size_t& sn_size)
|
|||
return enum_to_number(options, (uint32_t)rlc_mode_t::nulltype, (uint32_t)sn_size);
|
||||
}
|
||||
|
||||
enum class rlc_um_nr_sn_size_t { size6bits, size12bits, nulltype };
|
||||
inline std::string to_string(const rlc_um_nr_sn_size_t& sn_size)
|
||||
{
|
||||
constexpr static const char* options[] = {"6 bits", "12 bits"};
|
||||
return enum_to_text(options, (uint32_t)rlc_mode_t::nulltype, (uint32_t)sn_size);
|
||||
}
|
||||
inline uint16_t to_number(const rlc_um_nr_sn_size_t& sn_size)
|
||||
{
|
||||
constexpr static uint16_t options[] = {6, 12};
|
||||
return enum_to_number(options, (uint32_t)rlc_mode_t::nulltype, (uint32_t)sn_size);
|
||||
}
|
||||
|
||||
struct rlc_am_config_t {
|
||||
/****************************************************************************
|
||||
* Configurable parameters
|
||||
|
@ -215,22 +227,49 @@ struct rlc_um_config_t {
|
|||
bool is_mrb; // Whether this is a multicast bearer
|
||||
};
|
||||
|
||||
struct rlc_um_nr_config_t {
|
||||
/****************************************************************************
|
||||
* Configurable parameters
|
||||
* Ref: 3GPP TS 38.322 v15.3.0 Section 7
|
||||
***************************************************************************/
|
||||
|
||||
rlc_um_nr_sn_size_t sn_field_length; // Number of bits used for sequence number
|
||||
uint32_t UM_Window_Size;
|
||||
uint32_t mod; // Rx/Tx counter modulus
|
||||
};
|
||||
|
||||
#define RLC_TX_QUEUE_LEN (128)
|
||||
|
||||
enum class rlc_type_t { lte, nr, nulltype };
|
||||
inline std::string to_string(const rlc_type_t& type)
|
||||
{
|
||||
constexpr static const char* options[] = {"LTE", "NR"};
|
||||
return enum_to_text(options, (uint32_t)rlc_type_t::nulltype, (uint32_t)type);
|
||||
}
|
||||
|
||||
class rlc_config_t
|
||||
{
|
||||
public:
|
||||
rlc_type_t type;
|
||||
rlc_mode_t rlc_mode;
|
||||
rlc_am_config_t am;
|
||||
rlc_um_config_t um;
|
||||
rlc_um_nr_config_t um_nr;
|
||||
uint32_t tx_queue_length;
|
||||
|
||||
rlc_config_t() : rlc_mode(rlc_mode_t::tm), am(), um(), tx_queue_length(RLC_TX_QUEUE_LEN){};
|
||||
rlc_config_t() :
|
||||
type(rlc_type_t::lte),
|
||||
rlc_mode(rlc_mode_t::tm),
|
||||
am(),
|
||||
um(),
|
||||
um_nr(),
|
||||
tx_queue_length(RLC_TX_QUEUE_LEN){};
|
||||
|
||||
// Factory for MCH
|
||||
static rlc_config_t mch_config()
|
||||
{
|
||||
rlc_config_t cfg;
|
||||
rlc_config_t cfg = {};
|
||||
cfg.type = rlc_type_t::lte;
|
||||
cfg.rlc_mode = rlc_mode_t::um;
|
||||
cfg.um.t_reordering = 45;
|
||||
cfg.um.rx_sn_field_length = rlc_umd_sn_size_t::size5bits;
|
||||
|
@ -248,7 +287,8 @@ public:
|
|||
return {};
|
||||
}
|
||||
// SRB1 and SRB2 are AM
|
||||
rlc_config_t rlc_cfg;
|
||||
rlc_config_t rlc_cfg = {};
|
||||
rlc_cfg.type = rlc_type_t::lte;
|
||||
rlc_cfg.rlc_mode = rlc_mode_t::am;
|
||||
rlc_cfg.am.t_poll_retx = 45;
|
||||
rlc_cfg.am.poll_pdu = -1;
|
||||
|
@ -260,7 +300,8 @@ public:
|
|||
}
|
||||
static rlc_config_t default_rlc_um_config(uint32_t sn_size = 10)
|
||||
{
|
||||
rlc_config_t cnfg;
|
||||
rlc_config_t cnfg = {};
|
||||
cnfg.type = rlc_type_t::lte;
|
||||
cnfg.rlc_mode = rlc_mode_t::um;
|
||||
cnfg.um.t_reordering = 5;
|
||||
if (sn_size == 10) {
|
||||
|
@ -282,7 +323,8 @@ public:
|
|||
}
|
||||
static rlc_config_t default_rlc_am_config()
|
||||
{
|
||||
rlc_config_t rlc_cnfg;
|
||||
rlc_config_t rlc_cnfg = {};
|
||||
rlc_cnfg.type = rlc_type_t::lte;
|
||||
rlc_cnfg.rlc_mode = rlc_mode_t::am;
|
||||
rlc_cnfg.am.t_reordering = 5;
|
||||
rlc_cnfg.am.t_status_prohibit = 5;
|
||||
|
@ -292,6 +334,24 @@ public:
|
|||
rlc_cnfg.am.t_poll_retx = 5;
|
||||
return rlc_cnfg;
|
||||
}
|
||||
static rlc_config_t default_rlc_um_nr_config(uint32_t sn_size = 6)
|
||||
{
|
||||
rlc_config_t cnfg = {};
|
||||
cnfg.type = rlc_type_t::nr;
|
||||
cnfg.rlc_mode = rlc_mode_t::um;
|
||||
if (sn_size == 6) {
|
||||
cnfg.um_nr.sn_field_length = rlc_um_nr_sn_size_t::size6bits;
|
||||
cnfg.um_nr.UM_Window_Size = 32;
|
||||
cnfg.um_nr.mod = 64;
|
||||
} else if (sn_size == 12) {
|
||||
cnfg.um_nr.sn_field_length = rlc_um_nr_sn_size_t::size12bits;
|
||||
cnfg.um_nr.UM_Window_Size = 2048;
|
||||
cnfg.um_nr.mod = 64;
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
return cnfg;
|
||||
}
|
||||
};
|
||||
|
||||
/***************************
|
||||
|
|
|
@ -48,6 +48,28 @@ static const char rlc_fi_field_text[RLC_FI_FIELD_N_ITEMS][32] = {"Start and end
|
|||
"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);
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
typedef enum{
|
||||
RLC_DC_FIELD_CONTROL_PDU = 0,
|
||||
RLC_DC_FIELD_DATA_PDU,
|
||||
|
@ -65,6 +87,13 @@ typedef struct{
|
|||
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; // Sequence offset
|
||||
} rlc_um_nr_pdu_header_t;
|
||||
|
||||
// AMD PDU Header
|
||||
struct rlc_amd_pdu_header_t{
|
||||
rlc_dc_field_t dc; // Data or control
|
||||
|
|
|
@ -23,13 +23,14 @@
|
|||
#define SRSLTE_RLC_UM_H
|
||||
|
||||
#include "srslte/common/buffer_pool.h"
|
||||
#include "srslte/common/log.h"
|
||||
#include "srslte/common/common.h"
|
||||
#include "srslte/common/log.h"
|
||||
#include "srslte/interfaces/ue_interfaces.h"
|
||||
#include "srslte/upper/rlc_tx_queue.h"
|
||||
#include "srslte/upper/rlc_common.h"
|
||||
#include <pthread.h>
|
||||
#include "srslte/upper/rlc_tx_queue.h"
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <pthread.h>
|
||||
#include <queue>
|
||||
|
||||
namespace srslte {
|
||||
|
@ -39,6 +40,11 @@ struct rlc_umd_pdu_t{
|
|||
unique_byte_buffer_t buf;
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
rlc_um_nr_pdu_header_t header;
|
||||
unique_byte_buffer_t buf;
|
||||
} rlc_umd_pdu_nr_t;
|
||||
|
||||
class rlc_um
|
||||
:public rlc_common
|
||||
{
|
||||
|
@ -73,14 +79,13 @@ public:
|
|||
void reset_metrics();
|
||||
|
||||
private:
|
||||
|
||||
// Transmitter sub-class
|
||||
class rlc_um_tx
|
||||
// Transmitter sub-class base
|
||||
class rlc_um_tx_base
|
||||
{
|
||||
public:
|
||||
rlc_um_tx(srslte::log* log_);
|
||||
~rlc_um_tx();
|
||||
bool configure(rlc_config_t cfg, std::string rb_name);
|
||||
rlc_um_tx_base(srslte::log* log_);
|
||||
virtual ~rlc_um_tx_base();
|
||||
virtual bool configure(rlc_config_t cfg, std::string rb_name) = 0;
|
||||
int build_data_pdu(uint8_t *payload, uint32_t nof_bytes);
|
||||
void stop();
|
||||
void reestablish();
|
||||
|
@ -92,7 +97,7 @@ private:
|
|||
bool has_data();
|
||||
uint32_t get_buffer_state();
|
||||
|
||||
private:
|
||||
protected:
|
||||
byte_buffer_pool* pool = nullptr;
|
||||
srslte::log* log = nullptr;
|
||||
std::string rb_name;
|
||||
|
@ -101,28 +106,58 @@ private:
|
|||
* Configurable parameters
|
||||
* Ref: 3GPP TS 36.322 v10.0.0 Section 7
|
||||
***************************************************************************/
|
||||
rlc_um_config_t cfg = {};
|
||||
rlc_config_t cfg = {};
|
||||
|
||||
// TX SDU buffers
|
||||
rlc_tx_queue tx_sdu_queue;
|
||||
unique_byte_buffer_t tx_sdu;
|
||||
|
||||
// Mutexes
|
||||
std::mutex mutex;
|
||||
|
||||
bool tx_enabled = false;
|
||||
|
||||
uint32_t num_tx_bytes = 0;
|
||||
|
||||
virtual int build_data_pdu(unique_byte_buffer_t pdu, uint8_t* payload, uint32_t nof_bytes) = 0;
|
||||
|
||||
// helper functions
|
||||
virtual void debug_state() = 0;
|
||||
const char* get_rb_name();
|
||||
};
|
||||
|
||||
// Transmitter sub-class for LTE
|
||||
class rlc_um_tx : public rlc_um_tx_base
|
||||
{
|
||||
public:
|
||||
rlc_um_tx(srslte::log* log_);
|
||||
|
||||
bool configure(rlc_config_t cfg, std::string rb_name);
|
||||
int build_data_pdu(unique_byte_buffer_t pdu, uint8_t* payload, uint32_t nof_bytes);
|
||||
|
||||
private:
|
||||
/****************************************************************************
|
||||
* State variables and counters
|
||||
* Ref: 3GPP TS 36.322 v10.0.0 Section 7
|
||||
***************************************************************************/
|
||||
uint32_t vt_us = 0; // Send state. SN to be assigned for next PDU.
|
||||
|
||||
// Mutexes
|
||||
pthread_mutex_t mutex;
|
||||
|
||||
bool tx_enabled = false;
|
||||
|
||||
uint32_t num_tx_bytes = 0;
|
||||
|
||||
// helper functions
|
||||
void debug_state();
|
||||
const char* get_rb_name();
|
||||
};
|
||||
|
||||
// Transmitter sub-class for NR
|
||||
class rlc_um_tx_nr : public rlc_um_tx_base
|
||||
{
|
||||
public:
|
||||
rlc_um_tx_nr(srslte::log* log_);
|
||||
|
||||
bool configure(rlc_config_t cfg, std::string rb_name);
|
||||
int build_data_pdu(unique_byte_buffer_t pdu, uint8_t* payload, uint32_t nof_bytes);
|
||||
|
||||
private:
|
||||
uint32_t TX_Next = 0; // send state as defined in TS 38.322 v15.3 Section 7
|
||||
|
||||
void debug_state();
|
||||
};
|
||||
|
||||
// Receiver sub-class
|
||||
|
@ -159,7 +194,7 @@ private:
|
|||
* Configurable parameters
|
||||
* Ref: 3GPP TS 36.322 v10.0.0 Section 7
|
||||
***************************************************************************/
|
||||
rlc_um_config_t cfg = {};
|
||||
rlc_config_t cfg = {};
|
||||
|
||||
// Rx window
|
||||
std::map<uint32_t, rlc_umd_pdu_t> rx_window;
|
||||
|
@ -182,7 +217,7 @@ private:
|
|||
uint32_t lcid = 0;
|
||||
|
||||
// Mutexes
|
||||
pthread_mutex_t mutex;
|
||||
std::mutex mutex;
|
||||
|
||||
bool rx_enabled = false;
|
||||
|
||||
|
@ -209,7 +244,7 @@ private:
|
|||
std::string get_rb_name(srsue::rrc_interface_rlc *rrc, uint32_t lcid, bool is_mrb);
|
||||
|
||||
// Rx and Tx objects
|
||||
rlc_um_tx tx;
|
||||
std::unique_ptr<rlc_um_tx_base> tx;
|
||||
rlc_um_rx rx;
|
||||
};
|
||||
|
||||
|
@ -225,6 +260,23 @@ uint32_t rlc_um_packed_length(rlc_umd_pdu_header_t *header);
|
|||
bool rlc_um_start_aligned(uint8_t fi);
|
||||
bool rlc_um_end_aligned(uint8_t fi);
|
||||
|
||||
/****************************************************************************
|
||||
* Header pack/unpack helper functions for NR
|
||||
* Ref: 3GPP TS 38.322 v15.3.0 Section 6.2.2.3
|
||||
***************************************************************************/
|
||||
uint32_t rlc_um_nr_read_data_pdu_header(const byte_buffer_t* pdu,
|
||||
const rlc_um_nr_sn_size_t sn_size,
|
||||
rlc_um_nr_pdu_header_t* header);
|
||||
|
||||
uint32_t rlc_um_nr_read_data_pdu_header(const uint8_t* payload,
|
||||
const uint32_t nof_bytes,
|
||||
const rlc_um_nr_sn_size_t sn_size,
|
||||
rlc_um_nr_pdu_header_t* header);
|
||||
|
||||
uint32_t rlc_um_nr_write_data_pdu_header(const rlc_um_nr_pdu_header_t& header, byte_buffer_t* pdu);
|
||||
|
||||
uint32_t rlc_um_nr_packed_length(const rlc_um_nr_pdu_header_t& header);
|
||||
|
||||
} // namespace srslte
|
||||
|
||||
#endif // SRSLTE_RLC_UM_H
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
#include "srslte/upper/rlc_um.h"
|
||||
#include <sstream>
|
||||
|
||||
#define RX_MOD_BASE(x) (((x)-vr_uh-cfg.rx_window_size)%cfg.rx_mod)
|
||||
#define RX_MOD_BASE(x) (((x)-vr_uh - cfg.um.rx_window_size) % cfg.um.rx_mod)
|
||||
|
||||
using namespace asn1::rrc;
|
||||
|
||||
|
@ -37,7 +37,6 @@ rlc_um::rlc_um(srslte::log* log_,
|
|||
pool(byte_buffer_pool::get_instance()),
|
||||
rrc(rrc_),
|
||||
log(log_),
|
||||
tx(log_),
|
||||
rx(log_, lcid_, pdcp_, rrc_, timers_)
|
||||
{
|
||||
}
|
||||
|
@ -60,31 +59,51 @@ bool rlc_um::configure(rlc_config_t cnfg_)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (not tx.configure(cfg, rb_name)) {
|
||||
return false;
|
||||
}
|
||||
if (cfg.type == rlc_type_t::lte) {
|
||||
tx.reset(new rlc_um_tx(log));
|
||||
if (not tx->configure(cfg, rb_name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
log->info("%s configured in %s: t_reordering=%d ms, rx_sn_field_length=%u bits, tx_sn_field_length=%u bits\n",
|
||||
rb_name.c_str(),
|
||||
srslte::to_string(cnfg_.rlc_mode).c_str(),
|
||||
cfg.um.t_reordering,
|
||||
srslte::to_number(cfg.um.rx_sn_field_length),
|
||||
srslte::to_number(cfg.um.tx_sn_field_length));
|
||||
log->info("%s configured in %s: t_reordering=%d ms, rx_sn_field_length=%u bits, tx_sn_field_length=%u bits\n",
|
||||
rb_name.c_str(),
|
||||
srslte::to_string(cnfg_.rlc_mode).c_str(),
|
||||
cfg.um.t_reordering,
|
||||
srslte::to_number(cfg.um.rx_sn_field_length),
|
||||
srslte::to_number(cfg.um.tx_sn_field_length));
|
||||
} else {
|
||||
tx.reset(new rlc_um_tx_nr(log));
|
||||
if (not tx->configure(cfg, rb_name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
log->info("%s configured in %s: sn_field_length=%u bits\n",
|
||||
rb_name.c_str(),
|
||||
srslte::to_string(cnfg_.rlc_mode).c_str(),
|
||||
srslte::to_number(cfg.um_nr.sn_field_length));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool rlc_um::rlc_um_rx::configure(rlc_config_t cnfg_, std::string rb_name_)
|
||||
{
|
||||
cfg = cnfg_.um;
|
||||
cfg = cnfg_;
|
||||
|
||||
if (cfg.rx_mod == 0) {
|
||||
log->error("Error configuring %s RLC UM: rx_mod==0\n", get_rb_name());
|
||||
return false;
|
||||
}
|
||||
if (cfg.type == rlc_type_t::lte) {
|
||||
if (cfg.um.rx_mod == 0) {
|
||||
log->error("Error configuring %s RLC UM: rx_mod==0\n", get_rb_name());
|
||||
return false;
|
||||
}
|
||||
|
||||
// set reordering timer
|
||||
if (reordering_timer != NULL) {
|
||||
reordering_timer->set(this, cfg.t_reordering);
|
||||
// set reordering timer
|
||||
if (reordering_timer != NULL) {
|
||||
reordering_timer->set(this, cfg.um.t_reordering);
|
||||
}
|
||||
} else {
|
||||
if (cfg.um_nr.mod == 0) {
|
||||
log->error("Error configuring %s RLC UM: rx_mod==0\n", get_rb_name());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
rb_name = rb_name_;
|
||||
|
@ -94,13 +113,12 @@ bool rlc_um::rlc_um_rx::configure(rlc_config_t cnfg_, std::string rb_name_)
|
|||
return true;
|
||||
}
|
||||
|
||||
|
||||
void rlc_um::empty_queue() {
|
||||
void rlc_um::empty_queue()
|
||||
{
|
||||
// Drop all messages in TX SDU queue
|
||||
tx.empty_queue();
|
||||
tx->empty_queue();
|
||||
}
|
||||
|
||||
|
||||
bool rlc_um::is_mrb()
|
||||
{
|
||||
return cfg.um.is_mrb;
|
||||
|
@ -109,14 +127,16 @@ bool rlc_um::is_mrb()
|
|||
|
||||
void rlc_um::reestablish()
|
||||
{
|
||||
tx.reestablish(); // calls stop and enables tx again
|
||||
tx->reestablish(); // calls stop and enables tx again
|
||||
rx.reestablish(); // nothing else needed
|
||||
}
|
||||
|
||||
|
||||
void rlc_um::stop()
|
||||
{
|
||||
tx.stop();
|
||||
if (tx) {
|
||||
tx->stop();
|
||||
}
|
||||
rx.stop();
|
||||
}
|
||||
|
||||
|
@ -137,9 +157,9 @@ uint32_t rlc_um::get_bearer()
|
|||
void rlc_um::write_sdu(unique_byte_buffer_t sdu, bool blocking)
|
||||
{
|
||||
if (blocking) {
|
||||
tx.write_sdu(std::move(sdu));
|
||||
tx->write_sdu(std::move(sdu));
|
||||
} else {
|
||||
tx.try_write_sdu(std::move(sdu));
|
||||
tx->try_write_sdu(std::move(sdu));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -149,17 +169,17 @@ void rlc_um::write_sdu(unique_byte_buffer_t sdu, bool blocking)
|
|||
|
||||
bool rlc_um::has_data()
|
||||
{
|
||||
return tx.has_data();
|
||||
return tx->has_data();
|
||||
}
|
||||
|
||||
uint32_t rlc_um::get_buffer_state()
|
||||
{
|
||||
return tx.get_buffer_state();
|
||||
return tx->get_buffer_state();
|
||||
}
|
||||
|
||||
int rlc_um::read_pdu(uint8_t *payload, uint32_t nof_bytes)
|
||||
{
|
||||
return tx.build_data_pdu(payload, nof_bytes);
|
||||
return tx->build_data_pdu(payload, nof_bytes);
|
||||
}
|
||||
|
||||
void rlc_um::write_pdu(uint8_t *payload, uint32_t nof_bytes)
|
||||
|
@ -169,7 +189,7 @@ void rlc_um::write_pdu(uint8_t *payload, uint32_t nof_bytes)
|
|||
|
||||
uint32_t rlc_um::get_num_tx_bytes()
|
||||
{
|
||||
return tx.get_num_tx_bytes();
|
||||
return tx->get_num_tx_bytes();
|
||||
}
|
||||
|
||||
uint32_t rlc_um::get_num_rx_bytes()
|
||||
|
@ -179,7 +199,7 @@ uint32_t rlc_um::get_num_rx_bytes()
|
|||
|
||||
void rlc_um::reset_metrics()
|
||||
{
|
||||
tx.reset_metrics();
|
||||
tx->reset_metrics();
|
||||
rx.reset_metrics();
|
||||
}
|
||||
|
||||
|
@ -199,57 +219,33 @@ std::string rlc_um::get_rb_name(srsue::rrc_interface_rlc *rrc, uint32_t lcid, bo
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/****************************************************************************
|
||||
* Tx subclass implementation
|
||||
* Tx subclass implementation (base)
|
||||
***************************************************************************/
|
||||
|
||||
rlc_um::rlc_um_tx::rlc_um_tx(srslte::log* log_) : pool(byte_buffer_pool::get_instance()), log(log_)
|
||||
rlc_um::rlc_um_tx_base::rlc_um_tx_base(srslte::log* log_) : pool(byte_buffer_pool::get_instance()), log(log_)
|
||||
{
|
||||
pthread_mutex_init(&mutex, NULL);
|
||||
}
|
||||
|
||||
|
||||
rlc_um::rlc_um_tx::~rlc_um_tx()
|
||||
rlc_um::rlc_um_tx_base::~rlc_um_tx_base()
|
||||
{
|
||||
pthread_mutex_destroy(&mutex);
|
||||
}
|
||||
|
||||
bool rlc_um::rlc_um_tx::configure(rlc_config_t cnfg_, std::string rb_name_)
|
||||
{
|
||||
cfg = cnfg_.um;
|
||||
|
||||
if (cfg.tx_mod == 0) {
|
||||
log->error("Error configuring %s RLC UM: tx_mod==0\n", get_rb_name());
|
||||
return false;
|
||||
}
|
||||
|
||||
tx_sdu_queue.resize(cnfg_.tx_queue_length);
|
||||
|
||||
rb_name = rb_name_;
|
||||
tx_enabled = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void rlc_um::rlc_um_tx::stop()
|
||||
void rlc_um::rlc_um_tx_base::stop()
|
||||
{
|
||||
tx_enabled = false;
|
||||
empty_queue();
|
||||
}
|
||||
|
||||
|
||||
void rlc_um::rlc_um_tx::reestablish()
|
||||
void rlc_um::rlc_um_tx_base::reestablish()
|
||||
{
|
||||
stop();
|
||||
tx_enabled = true;
|
||||
}
|
||||
|
||||
|
||||
void rlc_um::rlc_um_tx::empty_queue()
|
||||
void rlc_um::rlc_um_tx_base::empty_queue()
|
||||
{
|
||||
pthread_mutex_lock(&mutex);
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
|
||||
// deallocate all SDUs in transmit queue
|
||||
while(tx_sdu_queue.size() > 0) {
|
||||
|
@ -258,32 +254,25 @@ void rlc_um::rlc_um_tx::empty_queue()
|
|||
|
||||
// deallocate SDU that is currently processed
|
||||
tx_sdu.reset();
|
||||
|
||||
pthread_mutex_unlock(&mutex);
|
||||
}
|
||||
|
||||
|
||||
uint32_t rlc_um::rlc_um_tx::get_num_tx_bytes()
|
||||
uint32_t rlc_um::rlc_um_tx_base::get_num_tx_bytes()
|
||||
{
|
||||
return num_tx_bytes;
|
||||
}
|
||||
|
||||
|
||||
void rlc_um::rlc_um_tx::reset_metrics()
|
||||
void rlc_um::rlc_um_tx_base::reset_metrics()
|
||||
{
|
||||
pthread_mutex_lock(&mutex);
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
num_tx_bytes = 0;
|
||||
pthread_mutex_unlock(&mutex);
|
||||
}
|
||||
|
||||
|
||||
bool rlc_um::rlc_um_tx::has_data()
|
||||
bool rlc_um::rlc_um_tx_base::has_data()
|
||||
{
|
||||
return (tx_sdu != NULL || !tx_sdu_queue.is_empty());
|
||||
}
|
||||
|
||||
|
||||
uint32_t rlc_um::rlc_um_tx::get_buffer_state()
|
||||
uint32_t rlc_um::rlc_um_tx_base::get_buffer_state()
|
||||
{
|
||||
// Bytes needed for tx SDUs
|
||||
uint32_t n_sdus = tx_sdu_queue.size();
|
||||
|
@ -300,12 +289,12 @@ uint32_t rlc_um::rlc_um_tx::get_buffer_state()
|
|||
|
||||
// Room needed for fixed header?
|
||||
if(n_bytes > 0)
|
||||
n_bytes += (cfg.is_mrb)?2:3;
|
||||
n_bytes += (cfg.um.is_mrb) ? 2 : 3;
|
||||
|
||||
return n_bytes;
|
||||
}
|
||||
|
||||
void rlc_um::rlc_um_tx::write_sdu(unique_byte_buffer_t sdu)
|
||||
void rlc_um::rlc_um_tx_base::write_sdu(unique_byte_buffer_t sdu)
|
||||
{
|
||||
if (!tx_enabled) {
|
||||
return;
|
||||
|
@ -319,7 +308,7 @@ void rlc_um::rlc_um_tx::write_sdu(unique_byte_buffer_t sdu)
|
|||
}
|
||||
}
|
||||
|
||||
void rlc_um::rlc_um_tx::try_write_sdu(unique_byte_buffer_t sdu)
|
||||
void rlc_um::rlc_um_tx_base::try_write_sdu(unique_byte_buffer_t sdu)
|
||||
{
|
||||
if (!tx_enabled) {
|
||||
sdu.reset();
|
||||
|
@ -346,35 +335,67 @@ void rlc_um::rlc_um_tx::try_write_sdu(unique_byte_buffer_t sdu)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
int rlc_um::rlc_um_tx::build_data_pdu(uint8_t *payload, uint32_t nof_bytes)
|
||||
int rlc_um::rlc_um_tx_base::build_data_pdu(uint8_t* payload, uint32_t nof_bytes)
|
||||
{
|
||||
pthread_mutex_lock(&mutex);
|
||||
log->debug("MAC opportunity - %d bytes\n", nof_bytes);
|
||||
unique_byte_buffer_t pdu;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
log->debug("MAC opportunity - %d bytes\n", nof_bytes);
|
||||
|
||||
if (not tx_enabled) {
|
||||
pthread_mutex_unlock(&mutex);
|
||||
return 0;
|
||||
if (not tx_enabled) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!tx_sdu && tx_sdu_queue.size() == 0) {
|
||||
log->info("No data available to be sent\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
pdu = allocate_unique_buffer(*pool);
|
||||
if (!pdu || pdu->N_bytes != 0) {
|
||||
log->error("Failed to allocate PDU buffer\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return build_data_pdu(std::move(pdu), payload, nof_bytes);
|
||||
}
|
||||
|
||||
const char* rlc_um::rlc_um_tx_base::get_rb_name()
|
||||
{
|
||||
return rb_name.c_str();
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Tx Subclass implementation for LTE
|
||||
***************************************************************************/
|
||||
|
||||
rlc_um::rlc_um_tx::rlc_um_tx(srslte::log* log_) : rlc_um_tx_base(log_) {}
|
||||
|
||||
bool rlc_um::rlc_um_tx::configure(rlc_config_t cnfg_, std::string rb_name_)
|
||||
{
|
||||
cfg = cnfg_;
|
||||
|
||||
if (cfg.um.tx_mod == 0) {
|
||||
log->error("Error configuring %s RLC UM: tx_mod==0\n", get_rb_name());
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!tx_sdu && tx_sdu_queue.size() == 0) {
|
||||
log->info("No data available to be sent\n");
|
||||
pthread_mutex_unlock(&mutex);
|
||||
return 0;
|
||||
}
|
||||
tx_sdu_queue.resize(cnfg_.tx_queue_length);
|
||||
|
||||
unique_byte_buffer_t pdu = allocate_unique_buffer(*pool);
|
||||
if(!pdu || pdu->N_bytes != 0) {
|
||||
log->error("Failed to allocate PDU buffer\n");
|
||||
pthread_mutex_unlock(&mutex);
|
||||
return 0;
|
||||
}
|
||||
rb_name = rb_name_;
|
||||
tx_enabled = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int rlc_um::rlc_um_tx::build_data_pdu(unique_byte_buffer_t pdu, uint8_t* payload, uint32_t nof_bytes)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
rlc_umd_pdu_header_t header;
|
||||
header.fi = RLC_FI_FIELD_START_AND_END_ALIGNED;
|
||||
header.sn = vt_us;
|
||||
header.N_li = 0;
|
||||
header.sn_size = cfg.tx_sn_field_length;
|
||||
header.sn_size = cfg.um.tx_sn_field_length;
|
||||
|
||||
uint32_t to_move = 0;
|
||||
uint32_t last_li = 0;
|
||||
|
@ -387,7 +408,6 @@ int rlc_um::rlc_um_tx::build_data_pdu(uint8_t *payload, uint32_t nof_bytes)
|
|||
{
|
||||
log->warning("%s Cannot build a PDU - %d bytes available, %d bytes required for header\n",
|
||||
get_rb_name(), nof_bytes, head_len);
|
||||
pthread_mutex_unlock(&mutex);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -446,7 +466,7 @@ int rlc_um::rlc_um_tx::build_data_pdu(uint8_t *payload, uint32_t nof_bytes)
|
|||
|
||||
// Set SN
|
||||
header.sn = vt_us;
|
||||
vt_us = (vt_us + 1)%cfg.tx_mod;
|
||||
vt_us = (vt_us + 1) % cfg.um.tx_mod;
|
||||
|
||||
// Add header and TX
|
||||
rlc_um_write_data_pdu_header(&header, pdu.get());
|
||||
|
@ -459,22 +479,126 @@ int rlc_um::rlc_um_tx::build_data_pdu(uint8_t *payload, uint32_t nof_bytes)
|
|||
|
||||
num_tx_bytes += ret;
|
||||
|
||||
pthread_mutex_unlock(&mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
const char* rlc_um::rlc_um_tx::get_rb_name()
|
||||
{
|
||||
return rb_name.c_str();
|
||||
}
|
||||
|
||||
|
||||
void rlc_um::rlc_um_tx::debug_state()
|
||||
{
|
||||
log->debug("%s vt_us = %d\n", get_rb_name(), vt_us);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Tx Subclass implementation for NR
|
||||
***************************************************************************/
|
||||
|
||||
rlc_um::rlc_um_tx_nr::rlc_um_tx_nr(srslte::log* log_) : rlc_um_tx_base(log_) {}
|
||||
|
||||
bool rlc_um::rlc_um_tx_nr::configure(rlc_config_t cnfg_, std::string rb_name_)
|
||||
{
|
||||
cfg = cnfg_;
|
||||
|
||||
if (cfg.um_nr.mod == 0) {
|
||||
log->error("Error configuring %s RLC UM: tx_mod==0\n", get_rb_name());
|
||||
return false;
|
||||
}
|
||||
|
||||
tx_sdu_queue.resize(cnfg_.tx_queue_length);
|
||||
|
||||
rb_name = rb_name_;
|
||||
tx_enabled = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int rlc_um::rlc_um_tx_nr::build_data_pdu(unique_byte_buffer_t pdu, uint8_t* payload, uint32_t nof_bytes)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
rlc_um_nr_pdu_header_t header = {};
|
||||
header.si = rlc_nr_si_field_t::full_sdu;
|
||||
header.sn = TX_Next;
|
||||
header.sn_size = cfg.um_nr.sn_field_length;
|
||||
|
||||
uint32_t to_move = 0;
|
||||
uint8_t* pdu_ptr = pdu->msg;
|
||||
|
||||
int head_len = rlc_um_nr_packed_length(header);
|
||||
int pdu_space = SRSLTE_MIN(nof_bytes, pdu->get_tailroom());
|
||||
|
||||
if (pdu_space <= head_len + 1) {
|
||||
log->warning("%s Cannot build a PDU - %d bytes available, %d bytes required for header\n",
|
||||
get_rb_name(),
|
||||
nof_bytes,
|
||||
head_len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Check for SDU segment
|
||||
if (tx_sdu) {
|
||||
uint32_t space = pdu_space - head_len;
|
||||
to_move = space >= tx_sdu->N_bytes ? tx_sdu->N_bytes : space;
|
||||
log->debug(
|
||||
"%s adding remainder of SDU segment - %d bytes of %d remaining\n", get_rb_name(), to_move, tx_sdu->N_bytes);
|
||||
memcpy(pdu_ptr, tx_sdu->msg, to_move);
|
||||
pdu_ptr += to_move;
|
||||
pdu->N_bytes += to_move;
|
||||
tx_sdu->N_bytes -= to_move;
|
||||
tx_sdu->msg += to_move;
|
||||
if (tx_sdu->N_bytes == 0) {
|
||||
log->debug("%s Complete SDU scheduled for tx. Stack latency: %ld us\n", get_rb_name(), tx_sdu->get_latency_us());
|
||||
tx_sdu.reset();
|
||||
header.si = rlc_nr_si_field_t::last_segment;
|
||||
} else {
|
||||
header.si = rlc_nr_si_field_t::neither_first_nor_last_segment;
|
||||
}
|
||||
pdu_space -= SRSLTE_MIN(to_move, pdu->get_tailroom());
|
||||
} else {
|
||||
// Pull SDU from queue
|
||||
log->debug("pdu_space=%d, head_len=%d\n", pdu_space, head_len);
|
||||
|
||||
head_len = rlc_um_nr_packed_length(header);
|
||||
tx_sdu = tx_sdu_queue.read();
|
||||
uint32_t space = pdu_space - head_len;
|
||||
to_move = space >= tx_sdu->N_bytes ? tx_sdu->N_bytes : space;
|
||||
log->debug("%s adding new SDU - %d bytes of %d remaining\n", get_rb_name(), to_move, tx_sdu->N_bytes);
|
||||
memcpy(pdu_ptr, tx_sdu->msg, to_move);
|
||||
pdu_ptr += to_move;
|
||||
pdu->N_bytes += to_move;
|
||||
tx_sdu->N_bytes -= to_move;
|
||||
tx_sdu->msg += to_move;
|
||||
if (tx_sdu->N_bytes == 0) {
|
||||
log->debug("%s Complete SDU scheduled for tx. Stack latency: %ld us\n", get_rb_name(), tx_sdu->get_latency_us());
|
||||
tx_sdu.reset();
|
||||
header.si = rlc_nr_si_field_t::full_sdu;
|
||||
} else {
|
||||
header.si = rlc_nr_si_field_t::first_segment;
|
||||
}
|
||||
pdu_space -= to_move;
|
||||
}
|
||||
|
||||
// Update SN if needed
|
||||
if (header.si == rlc_nr_si_field_t::last_segment) {
|
||||
TX_Next = (TX_Next + 1) % cfg.um_nr.mod;
|
||||
}
|
||||
|
||||
// Add header and TX
|
||||
rlc_um_nr_write_data_pdu_header(header, pdu.get());
|
||||
memcpy(payload, pdu->msg, pdu->N_bytes);
|
||||
uint32_t ret = pdu->N_bytes;
|
||||
|
||||
log->info_hex(payload, ret, "%s Tx PDU SN=%d (%d B)\n", get_rb_name(), header.sn, pdu->N_bytes);
|
||||
|
||||
debug_state();
|
||||
|
||||
num_tx_bytes += ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void rlc_um::rlc_um_tx_nr::debug_state()
|
||||
{
|
||||
log->debug("%s TX_Next = %d\n", get_rb_name(), TX_Next);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Rx subclass implementation
|
||||
***************************************************************************/
|
||||
|
@ -493,16 +617,12 @@ rlc_um::rlc_um_rx::rlc_um_rx(srslte::log* log_,
|
|||
{
|
||||
reordering_timer_id = timers->get_unique_id();
|
||||
reordering_timer = timers->get(reordering_timer_id);
|
||||
|
||||
pthread_mutex_init(&mutex, NULL);
|
||||
}
|
||||
|
||||
rlc_um::rlc_um_rx::~rlc_um_rx()
|
||||
{
|
||||
reordering_timer->stop();
|
||||
timers->release_id(reordering_timer_id);
|
||||
|
||||
pthread_mutex_destroy(&mutex);
|
||||
}
|
||||
|
||||
void rlc_um::rlc_um_rx::reestablish()
|
||||
|
@ -515,22 +635,17 @@ void rlc_um::rlc_um_rx::reestablish()
|
|||
}
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&mutex);
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
reset();
|
||||
rx_enabled = true;
|
||||
pthread_mutex_unlock(&mutex);
|
||||
}
|
||||
|
||||
|
||||
void rlc_um::rlc_um_rx::stop()
|
||||
{
|
||||
pthread_mutex_lock(&mutex);
|
||||
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
reset();
|
||||
|
||||
reordering_timer->stop();
|
||||
|
||||
pthread_mutex_unlock(&mutex);
|
||||
}
|
||||
|
||||
void rlc_um::rlc_um_rx::reset()
|
||||
|
@ -550,7 +665,7 @@ void rlc_um::rlc_um_rx::reset()
|
|||
|
||||
void rlc_um::rlc_um_rx::handle_data_pdu(uint8_t *payload, uint32_t nof_bytes)
|
||||
{
|
||||
pthread_mutex_lock(&mutex);
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
|
||||
rlc_umd_pdu_t pdu;
|
||||
int header_len = 0;
|
||||
|
@ -558,34 +673,33 @@ void rlc_um::rlc_um_rx::handle_data_pdu(uint8_t *payload, uint32_t nof_bytes)
|
|||
rlc_umd_pdu_header_t header;
|
||||
|
||||
if (!rx_enabled) {
|
||||
goto unlock_and_exit;
|
||||
return;
|
||||
}
|
||||
|
||||
num_rx_bytes += nof_bytes;
|
||||
|
||||
rlc_um_read_data_pdu_header(payload, nof_bytes, cfg.rx_sn_field_length, &header);
|
||||
rlc_um_read_data_pdu_header(payload, nof_bytes, cfg.um.rx_sn_field_length, &header);
|
||||
|
||||
log->info_hex(payload, nof_bytes, "RX %s Rx data PDU SN: %d (%d B)", get_rb_name(), header.sn, nof_bytes);
|
||||
|
||||
if(RX_MOD_BASE(header.sn) >= RX_MOD_BASE(vr_uh-cfg.rx_window_size) &&
|
||||
RX_MOD_BASE(header.sn) < RX_MOD_BASE(vr_ur))
|
||||
{
|
||||
if (RX_MOD_BASE(header.sn) >= RX_MOD_BASE(vr_uh - cfg.um.rx_window_size) &&
|
||||
RX_MOD_BASE(header.sn) < RX_MOD_BASE(vr_ur)) {
|
||||
log->info("%s SN: %d outside rx window [%d:%d] - discarding\n",
|
||||
get_rb_name(), header.sn, vr_ur, vr_uh);
|
||||
goto unlock_and_exit;
|
||||
return;
|
||||
}
|
||||
it = rx_window.find(header.sn);
|
||||
if(rx_window.end() != it)
|
||||
{
|
||||
log->info("%s Discarding duplicate SN: %d\n", get_rb_name(), header.sn);
|
||||
goto unlock_and_exit;
|
||||
return;
|
||||
}
|
||||
|
||||
// Write to rx window
|
||||
pdu.buf = allocate_unique_buffer(*pool);
|
||||
if (!pdu.buf) {
|
||||
log->error("Discarting packet: no space in buffer pool\n");
|
||||
goto unlock_and_exit;
|
||||
return;
|
||||
}
|
||||
memcpy(pdu.buf->msg, payload, nof_bytes);
|
||||
pdu.buf->N_bytes = nof_bytes;
|
||||
|
@ -598,7 +712,7 @@ void rlc_um::rlc_um_rx::handle_data_pdu(uint8_t *payload, uint32_t nof_bytes)
|
|||
|
||||
// Update vr_uh
|
||||
if(!inside_reordering_window(header.sn)) {
|
||||
vr_uh = (header.sn + 1)%cfg.rx_mod;
|
||||
vr_uh = (header.sn + 1) % cfg.um.rx_mod;
|
||||
}
|
||||
|
||||
// Reassemble and deliver SDUs, while updating vr_ur
|
||||
|
@ -623,9 +737,6 @@ void rlc_um::rlc_um_rx::handle_data_pdu(uint8_t *payload, uint32_t nof_bytes)
|
|||
}
|
||||
|
||||
debug_state();
|
||||
|
||||
unlock_and_exit:
|
||||
pthread_mutex_unlock(&mutex);
|
||||
}
|
||||
|
||||
|
||||
|
@ -669,13 +780,14 @@ void rlc_um::rlc_um_rx::reassemble_rx_sdus()
|
|||
rx_sdu->N_bytes += len;
|
||||
rx_window[vr_ur].buf->msg += len;
|
||||
rx_window[vr_ur].buf->N_bytes -= len;
|
||||
if((pdu_lost && !rlc_um_start_aligned(rx_window[vr_ur].header.fi)) || (vr_ur != ((vr_ur_in_rx_sdu+1)%cfg.rx_mod))) {
|
||||
if ((pdu_lost && !rlc_um_start_aligned(rx_window[vr_ur].header.fi)) ||
|
||||
(vr_ur != ((vr_ur_in_rx_sdu + 1) % cfg.um.rx_mod))) {
|
||||
log->warning("Dropping remainder of lost PDU (lower edge middle segments, vr_ur=%d, vr_ur_in_rx_sdu=%d)\n", vr_ur, vr_ur_in_rx_sdu);
|
||||
rx_sdu->clear();
|
||||
} else {
|
||||
log->info_hex(rx_sdu->msg, rx_sdu->N_bytes, "%s Rx SDU vr_ur=%d, i=%d (lower edge middle segments)", get_rb_name(), vr_ur, i);
|
||||
rx_sdu->set_timestamp();
|
||||
if(cfg.is_mrb){
|
||||
if (cfg.um.is_mrb) {
|
||||
pdcp->write_pdu_mch(lcid, std::move(rx_sdu));
|
||||
} else {
|
||||
pdcp->write_pdu(lcid, std::move(rx_sdu));
|
||||
|
@ -705,7 +817,7 @@ void rlc_um::rlc_um_rx::reassemble_rx_sdus()
|
|||
} else {
|
||||
log->info_hex(rx_sdu->msg, rx_sdu->N_bytes, "%s Rx SDU vr_ur=%d (lower edge last segments)", get_rb_name(), vr_ur);
|
||||
rx_sdu->set_timestamp();
|
||||
if(cfg.is_mrb){
|
||||
if (cfg.um.is_mrb) {
|
||||
pdcp->write_pdu_mch(lcid, std::move(rx_sdu));
|
||||
} else {
|
||||
pdcp->write_pdu(lcid, std::move(rx_sdu));
|
||||
|
@ -724,7 +836,7 @@ void rlc_um::rlc_um_rx::reassemble_rx_sdus()
|
|||
rx_window.erase(vr_ur);
|
||||
}
|
||||
|
||||
vr_ur = (vr_ur + 1)%cfg.rx_mod;
|
||||
vr_ur = (vr_ur + 1) % cfg.um.rx_mod;
|
||||
}
|
||||
|
||||
// Now update vr_ur until we reach an SN we haven't yet received
|
||||
|
@ -772,8 +884,17 @@ void rlc_um::rlc_um_rx::reassemble_rx_sdus()
|
|||
log->info("Updating vr_ur_in_rx_sdu. old=%d, new=%d\n", vr_ur_in_rx_sdu, vr_ur);
|
||||
vr_ur_in_rx_sdu = vr_ur;
|
||||
} else {
|
||||
log->info_hex(rx_window[vr_ur].buf->msg, len, "Concatenating %d bytes in to current length %d. rx_window remaining bytes=%d, vr_ur_in_rx_sdu=%d, vr_ur=%d, rx_mod=%d, last_mod=%d\n",
|
||||
len, rx_sdu->N_bytes, rx_window[vr_ur].buf->N_bytes, vr_ur_in_rx_sdu, vr_ur, cfg.rx_mod, (vr_ur_in_rx_sdu+1)%cfg.rx_mod);
|
||||
log->info_hex(rx_window[vr_ur].buf->msg,
|
||||
len,
|
||||
"Concatenating %d bytes in to current length %d. rx_window remaining bytes=%d, "
|
||||
"vr_ur_in_rx_sdu=%d, vr_ur=%d, rx_mod=%d, last_mod=%d\n",
|
||||
len,
|
||||
rx_sdu->N_bytes,
|
||||
rx_window[vr_ur].buf->N_bytes,
|
||||
vr_ur_in_rx_sdu,
|
||||
vr_ur,
|
||||
cfg.um.rx_mod,
|
||||
(vr_ur_in_rx_sdu + 1) % cfg.um.rx_mod);
|
||||
}
|
||||
|
||||
memcpy(&rx_sdu->msg[rx_sdu->N_bytes], rx_window[vr_ur].buf->msg, len);
|
||||
|
@ -785,7 +906,7 @@ void rlc_um::rlc_um_rx::reassemble_rx_sdus()
|
|||
if (pdu_belongs_to_rx_sdu()) {
|
||||
log->info_hex(rx_sdu->msg, rx_sdu->N_bytes, "%s Rx SDU vr_ur=%d, i=%d, (update vr_ur middle segments)", get_rb_name(), vr_ur, i);
|
||||
rx_sdu->set_timestamp();
|
||||
if(cfg.is_mrb){
|
||||
if (cfg.um.is_mrb) {
|
||||
pdcp->write_pdu_mch(lcid, std::move(rx_sdu));
|
||||
} else {
|
||||
pdcp->write_pdu(lcid, std::move(rx_sdu));
|
||||
|
@ -831,7 +952,7 @@ void rlc_um::rlc_um_rx::reassemble_rx_sdus()
|
|||
} else {
|
||||
log->info_hex(rx_sdu->msg, rx_sdu->N_bytes, "%s Rx SDU vr_ur=%d (update vr_ur last segments)", get_rb_name(), vr_ur);
|
||||
rx_sdu->set_timestamp();
|
||||
if(cfg.is_mrb){
|
||||
if (cfg.um.is_mrb) {
|
||||
pdcp->write_pdu_mch(lcid, std::move(rx_sdu));
|
||||
} else {
|
||||
pdcp->write_pdu(lcid, std::move(rx_sdu));
|
||||
|
@ -849,7 +970,7 @@ clean_up_rx_window:
|
|||
// Clean up rx_window
|
||||
rx_window.erase(vr_ur);
|
||||
|
||||
vr_ur = (vr_ur + 1)%cfg.rx_mod;
|
||||
vr_ur = (vr_ur + 1) % cfg.um.rx_mod;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -858,7 +979,7 @@ clean_up_rx_window:
|
|||
bool rlc_um::rlc_um_rx::pdu_belongs_to_rx_sdu()
|
||||
{
|
||||
// return true if the currently received SDU
|
||||
if (((vr_ur_in_rx_sdu + 1)%cfg.rx_mod == vr_ur) || (vr_ur == vr_ur_in_rx_sdu)) {
|
||||
if (((vr_ur_in_rx_sdu + 1) % cfg.um.rx_mod == vr_ur) || (vr_ur == vr_ur_in_rx_sdu)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -869,10 +990,10 @@ bool rlc_um::rlc_um_rx::pdu_belongs_to_rx_sdu()
|
|||
// 36.322 Section 5.1.2.2.1
|
||||
bool rlc_um::rlc_um_rx::inside_reordering_window(uint16_t sn)
|
||||
{
|
||||
if (cfg.rx_window_size == 0 || rx_window.empty()) {
|
||||
if (cfg.um.rx_window_size == 0 || rx_window.empty()) {
|
||||
return true;
|
||||
}
|
||||
if (RX_MOD_BASE(vr_uh-cfg.rx_window_size) <= RX_MOD_BASE(sn) && RX_MOD_BASE(sn) < RX_MOD_BASE(vr_uh)) {
|
||||
if (RX_MOD_BASE(vr_uh - cfg.um.rx_window_size) <= RX_MOD_BASE(sn) && RX_MOD_BASE(sn) < RX_MOD_BASE(vr_uh)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
|
@ -888,9 +1009,8 @@ uint32_t rlc_um::rlc_um_rx::get_num_rx_bytes()
|
|||
|
||||
void rlc_um::rlc_um_rx::reset_metrics()
|
||||
{
|
||||
pthread_mutex_lock(&mutex);
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
num_rx_bytes = 0;
|
||||
pthread_mutex_unlock(&mutex);
|
||||
}
|
||||
|
||||
|
||||
|
@ -900,7 +1020,7 @@ void rlc_um::rlc_um_rx::reset_metrics()
|
|||
|
||||
void rlc_um::rlc_um_rx::timer_expired(uint32_t timeout_id)
|
||||
{
|
||||
pthread_mutex_lock(&mutex);
|
||||
std::lock_guard<std::mutex> lock(mutex);
|
||||
if (reordering_timer != NULL && reordering_timer_id == timeout_id) {
|
||||
// 36.322 v10 Section 5.1.2.2.4
|
||||
log->info("%s reordering timeout expiry - updating vr_ur and reassembling\n",
|
||||
|
@ -914,7 +1034,7 @@ void rlc_um::rlc_um_rx::timer_expired(uint32_t timeout_id)
|
|||
}
|
||||
|
||||
while(RX_MOD_BASE(vr_ur) < RX_MOD_BASE(vr_ux)) {
|
||||
vr_ur = (vr_ur + 1)%cfg.rx_mod;
|
||||
vr_ur = (vr_ur + 1) % cfg.um.rx_mod;
|
||||
log->debug("Entering Reassemble from timeout id=%d\n", timeout_id);
|
||||
reassemble_rx_sdus();
|
||||
log->debug("Finished reassemble from timeout id=%d\n", timeout_id);
|
||||
|
@ -928,7 +1048,6 @@ void rlc_um::rlc_um_rx::timer_expired(uint32_t timeout_id)
|
|||
|
||||
debug_state();
|
||||
}
|
||||
pthread_mutex_unlock(&mutex);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
|
@ -1075,4 +1194,128 @@ bool rlc_um_end_aligned(uint8_t fi)
|
|||
return (fi == RLC_FI_FIELD_START_AND_END_ALIGNED || fi == RLC_FI_FIELD_NOT_START_ALIGNED);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Header pack/unpack helper functions
|
||||
* Ref: 3GPP TS 38.322 v15.3.0 Section 6.2.2.3
|
||||
***************************************************************************/
|
||||
|
||||
uint32_t rlc_um_nr_read_data_pdu_header(const byte_buffer_t* pdu,
|
||||
const rlc_um_nr_sn_size_t sn_size,
|
||||
rlc_um_nr_pdu_header_t* header)
|
||||
{
|
||||
return rlc_um_nr_read_data_pdu_header(pdu->msg, pdu->N_bytes, sn_size, header);
|
||||
}
|
||||
|
||||
uint32_t rlc_um_nr_read_data_pdu_header(const uint8_t* payload,
|
||||
const uint32_t nof_bytes,
|
||||
const rlc_um_nr_sn_size_t sn_size,
|
||||
rlc_um_nr_pdu_header_t* header)
|
||||
{
|
||||
uint8_t* ptr = const_cast<uint8_t*>(payload);
|
||||
|
||||
header->sn_size = sn_size;
|
||||
|
||||
// Fixed part
|
||||
if (sn_size == rlc_um_nr_sn_size_t::size6bits) {
|
||||
header->si = (rlc_nr_si_field_t)((*ptr >> 6) & 0x03); // 2 bits SI
|
||||
header->sn = *ptr & 0x3F; // 6 bits SN
|
||||
// sanity check
|
||||
if (header->si == rlc_nr_si_field_t::full_sdu and not header->sn == 0) {
|
||||
fprintf(stderr, "Malformed PDU, reserved bits are set.\n");
|
||||
return 0;
|
||||
}
|
||||
ptr++;
|
||||
} else if (sn_size == rlc_um_nr_sn_size_t::size12bits) {
|
||||
header->si = (rlc_nr_si_field_t)((*ptr >> 6) & 0x03); // 2 bits SI
|
||||
header->sn = (*ptr & 0x0F) << 4; // 4 bits SN
|
||||
|
||||
// sanity check
|
||||
if (header->si == rlc_nr_si_field_t::first_segment) {
|
||||
// make sure two reserved bits are not set
|
||||
if (((*ptr >> 4) & 0x03) != 0) {
|
||||
fprintf(stderr, "Malformed PDU, reserved bits are set.\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// continue unpacking remaining SN
|
||||
ptr++;
|
||||
header->sn |= (*ptr & 0xFF); // 8 bits SN
|
||||
ptr++;
|
||||
} else {
|
||||
fprintf(stderr, "Unsupported SN length\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Read optional part
|
||||
if (header->si == rlc_nr_si_field_t::last_segment ||
|
||||
header->si == rlc_nr_si_field_t::neither_first_nor_last_segment) {
|
||||
// read SO
|
||||
header->so = (*ptr & 0xFF) << 8;
|
||||
ptr++;
|
||||
header->so |= (*ptr & 0xFF);
|
||||
ptr++;
|
||||
}
|
||||
|
||||
// return consumed bytes
|
||||
return (ptr - payload);
|
||||
}
|
||||
|
||||
uint32_t rlc_um_nr_packed_length(const rlc_um_nr_pdu_header_t& header)
|
||||
{
|
||||
uint32_t len = 0;
|
||||
if (header.si == rlc_nr_si_field_t::full_sdu || header.si == rlc_nr_si_field_t::first_segment) {
|
||||
len = 1;
|
||||
if (header.sn_size == rlc_um_nr_sn_size_t::size12bits) {
|
||||
len++;
|
||||
}
|
||||
} else {
|
||||
if (header.sn_size == rlc_um_nr_sn_size_t::size6bits) {
|
||||
len = 3;
|
||||
} else {
|
||||
len = 4;
|
||||
}
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
uint32_t rlc_um_nr_write_data_pdu_header(const rlc_um_nr_pdu_header_t& header, byte_buffer_t* pdu)
|
||||
{
|
||||
// Make room for the header
|
||||
uint32_t len = rlc_um_nr_packed_length(header);
|
||||
pdu->msg -= len;
|
||||
uint8_t* ptr = pdu->msg;
|
||||
|
||||
// write SI field
|
||||
*ptr = (header.si & 0x03) << 6; // 2 bits SI
|
||||
|
||||
if (header.si == rlc_nr_si_field_t::full_sdu) {
|
||||
// that's all ..
|
||||
ptr++;
|
||||
} else {
|
||||
if (header.sn_size == rlc_um_nr_sn_size_t::size6bits) {
|
||||
// write SN
|
||||
*ptr |= (header.sn & 0x3f); // 6 bit SN
|
||||
ptr++;
|
||||
} else {
|
||||
// 12bit SN
|
||||
*ptr |= (header.sn & 0xf); // 4 bit SN
|
||||
ptr++;
|
||||
*ptr = (header.sn & 0xFF); // remaining 8 bit SN
|
||||
ptr++;
|
||||
}
|
||||
if (header.so) {
|
||||
// write SO
|
||||
*ptr = (header.so) >> 8; // first part of SO
|
||||
ptr++;
|
||||
*ptr = (header.so & 0xFF); // second part of SO
|
||||
ptr++;
|
||||
}
|
||||
}
|
||||
|
||||
pdu->N_bytes += ptr - pdu->msg;
|
||||
|
||||
return len;
|
||||
}
|
||||
|
||||
} // namespace srslte
|
||||
|
|
|
@ -51,6 +51,14 @@ add_executable(rlc_common_test rlc_common_test.cc)
|
|||
target_link_libraries(rlc_common_test srslte_upper srslte_phy)
|
||||
add_test(rlc_common_test rlc_common_test)
|
||||
|
||||
add_executable(rlc_um_nr_pdu_test rlc_um_nr_pdu_test.cc)
|
||||
target_link_libraries(rlc_um_nr_pdu_test srslte_upper srslte_phy)
|
||||
add_test(rlc_um_nr_pdu_test rlc_um_nr_pdu_test)
|
||||
|
||||
add_executable(rlc_um_nr_test rlc_um_nr_test.cc)
|
||||
target_link_libraries(rlc_um_nr_test srslte_upper srslte_phy)
|
||||
add_test(rlc_um_nr_test rlc_um_nr_test)
|
||||
|
||||
add_executable(pdcp_nr_test pdcp_nr_test.cc)
|
||||
target_link_libraries(pdcp_nr_test srslte_upper srslte_common)
|
||||
add_test(pdcp_nr_test pdcp_nr_test)
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright 2013-2019 Software Radio Systems Limited
|
||||
*
|
||||
* This file is part of srsLTE.
|
||||
*
|
||||
* srsLTE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* srsLTE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* A copy of the GNU Affero General Public License can be found in
|
||||
* the LICENSE file in the top-level directory of this distribution
|
||||
* and at http://www.gnu.org/licenses/.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef SRSLTE_RLC_TEST_COMMON_H
|
||||
#define SRSLTE_RLC_TEST_COMMON_H
|
||||
|
||||
#include "srslte/interfaces/ue_interfaces.h"
|
||||
#include <vector>
|
||||
|
||||
namespace srslte {
|
||||
|
||||
class rlc_um_tester : public srsue::pdcp_interface_rlc, public srsue::rrc_interface_rlc
|
||||
{
|
||||
public:
|
||||
rlc_um_tester() {}
|
||||
|
||||
// PDCP interface
|
||||
void write_pdu(uint32_t lcid, unique_byte_buffer_t sdu)
|
||||
{
|
||||
if (lcid != 3 && sdu->N_bytes != expected_sdu_len) {
|
||||
printf("Received PDU with size %d, expected %d. Exiting.\n", sdu->N_bytes, expected_sdu_len);
|
||||
exit(-1);
|
||||
}
|
||||
sdus.push_back(std::move(sdu));
|
||||
}
|
||||
void write_pdu_bcch_bch(unique_byte_buffer_t sdu) {}
|
||||
void write_pdu_bcch_dlsch(unique_byte_buffer_t sdu) {}
|
||||
void write_pdu_pcch(unique_byte_buffer_t sdu) {}
|
||||
void write_pdu_mch(uint32_t lcid, srslte::unique_byte_buffer_t sdu) { sdus.push_back(std::move(sdu)); }
|
||||
|
||||
// RRC interface
|
||||
void max_retx_attempted() {}
|
||||
std::string get_rb_name(uint32_t lcid) { return std::string(""); }
|
||||
void set_expected_sdu_len(uint32_t len) { expected_sdu_len = len; }
|
||||
|
||||
uint32_t get_num_sdus() { return sdus.size(); }
|
||||
|
||||
// TODO: this should be private
|
||||
std::vector<unique_byte_buffer_t> sdus;
|
||||
uint32_t expected_sdu_len = 0;
|
||||
};
|
||||
|
||||
} // namespace srslte
|
||||
|
||||
#endif // SRSLTE_RLC_TEST_COMMON_H
|
|
@ -0,0 +1,240 @@
|
|||
/*
|
||||
* Copyright 2013-2019 Software Radio Systems Limited
|
||||
*
|
||||
* This file is part of srsLTE.
|
||||
*
|
||||
* srsLTE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* srsLTE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* A copy of the GNU Affero General Public License can be found in
|
||||
* the LICENSE file in the top-level directory of this distribution
|
||||
* and at http://www.gnu.org/licenses/.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "srslte/config.h"
|
||||
#include "srslte/upper/rlc.h"
|
||||
#include "srslte/upper/rlc_um.h"
|
||||
|
||||
#include <array>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#define TESTASSERT(cond) \
|
||||
{ \
|
||||
if (!(cond)) { \
|
||||
std::cout << "[" << __FUNCTION__ << "][Line " << __LINE__ << "]: FAIL at " << (#cond) << std::endl; \
|
||||
return -1; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define PCAP 0
|
||||
#define PCAP_CRNTI (0x1001)
|
||||
#define PCAP_TTI (666)
|
||||
|
||||
using namespace srslte;
|
||||
|
||||
#if PCAP
|
||||
#include "srslte/common/mac_nr_pcap.h"
|
||||
#include "srslte/common/mac_nr_pdu.h"
|
||||
static std::unique_ptr<srslte::mac_nr_pcap> pcap_handle = nullptr;
|
||||
#endif
|
||||
|
||||
int write_pdu_to_pcap(const uint32_t lcid, const uint8_t* payload, const uint32_t len)
|
||||
{
|
||||
#if PCAP
|
||||
if (pcap_handle) {
|
||||
byte_buffer_t tx_buffer;
|
||||
srslte::mac_nr_pdu tx_pdu;
|
||||
tx_pdu.init_tx(&tx_buffer, len + 10);
|
||||
tx_pdu.add_sdu(lcid, payload, len);
|
||||
tx_pdu.pack();
|
||||
pcap_handle->write_dl_crnti(tx_buffer.msg, tx_buffer.N_bytes, PCAP_CRNTI, true, PCAP_TTI);
|
||||
return SRSLTE_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
return SRSLTE_ERROR;
|
||||
}
|
||||
|
||||
template <std::size_t N>
|
||||
srslte::byte_buffer_t make_pdu_and_log(const std::array<uint8_t, N>& tv)
|
||||
{
|
||||
srslte::byte_buffer_t pdu;
|
||||
memcpy(pdu.msg, tv.data(), tv.size());
|
||||
pdu.N_bytes = tv.size();
|
||||
write_pdu_to_pcap(4, tv.data(), tv.size());
|
||||
return pdu;
|
||||
}
|
||||
|
||||
void corrupt_pdu_header(srslte::byte_buffer_t& pdu, const uint32_t header_len, const uint32_t payload_len)
|
||||
{
|
||||
// clear header only
|
||||
for (uint32_t i = 0; i < header_len; i++) {
|
||||
pdu.msg[i] = 0xaa;
|
||||
}
|
||||
pdu.msg += header_len;
|
||||
pdu.N_bytes = payload_len;
|
||||
}
|
||||
|
||||
// RLC UM PDU with complete SDU
|
||||
int rlc_um_nr_pdu_test1()
|
||||
{
|
||||
const int header_len = 1, payload_len = 4;
|
||||
std::array<uint8_t, header_len + payload_len> tv = {0x00, 0x11, 0x22, 0x33, 0x44};
|
||||
srslte::byte_buffer_t pdu = make_pdu_and_log(tv);
|
||||
|
||||
// unpack PDU
|
||||
rlc_um_nr_pdu_header_t header = {};
|
||||
TESTASSERT(rlc_um_nr_read_data_pdu_header(&pdu, srslte::rlc_um_nr_sn_size_t::size6bits, &header) != 0);
|
||||
TESTASSERT(header.si == rlc_nr_si_field_t::full_sdu);
|
||||
|
||||
// clear header
|
||||
corrupt_pdu_header(pdu, header_len, payload_len);
|
||||
|
||||
// pack again
|
||||
TESTASSERT(rlc_um_nr_write_data_pdu_header(header, &pdu) == header_len);
|
||||
TESTASSERT(pdu.N_bytes == tv.size());
|
||||
TESTASSERT(memcmp(pdu.msg, tv.data(), pdu.N_bytes) == 0);
|
||||
|
||||
return SRSLTE_SUCCESS;
|
||||
}
|
||||
|
||||
// RLC UM PDU with 6 Bit SN carrying the last segment of an SDU
|
||||
int rlc_um_nr_pdu_test2()
|
||||
{
|
||||
// SN = 1
|
||||
const int header_len = 3, payload_len = 4;
|
||||
const std::array<uint8_t, header_len + payload_len> tv = {0x81, 0x01, 0x02, 0x11, 0x22, 0x33, 0x44};
|
||||
srslte::byte_buffer_t pdu = make_pdu_and_log(tv);
|
||||
|
||||
// unpack PDU
|
||||
rlc_um_nr_pdu_header_t header = {};
|
||||
TESTASSERT(rlc_um_nr_read_data_pdu_header(&pdu, srslte::rlc_um_nr_sn_size_t::size6bits, &header) != 0);
|
||||
|
||||
TESTASSERT(header.si == rlc_nr_si_field_t::last_segment);
|
||||
TESTASSERT(header.sn == 1);
|
||||
TESTASSERT(header.so == 258);
|
||||
TESTASSERT(header.sn_size == srslte::rlc_um_nr_sn_size_t::size6bits);
|
||||
|
||||
// clear header
|
||||
corrupt_pdu_header(pdu, header_len, payload_len);
|
||||
|
||||
// pack again
|
||||
TESTASSERT(rlc_um_nr_write_data_pdu_header(header, &pdu) == header_len);
|
||||
TESTASSERT(pdu.N_bytes == tv.size());
|
||||
TESTASSERT(memcmp(pdu.msg, tv.data(), pdu.N_bytes) == 0);
|
||||
|
||||
return SRSLTE_SUCCESS;
|
||||
}
|
||||
|
||||
// RLC UM PDU with 6 Bit SN carrying a middle segment of an SDU
|
||||
int rlc_um_nr_pdu_test3()
|
||||
{
|
||||
// SN = 3
|
||||
const int header_len = 3, payload_len = 4;
|
||||
const std::array<uint8_t, header_len + payload_len> tv = {0xc3, 0x01, 0x02, 0x11, 0x22, 0x33, 0x44};
|
||||
srslte::byte_buffer_t pdu = make_pdu_and_log(tv);
|
||||
|
||||
// unpack PDU
|
||||
rlc_um_nr_pdu_header_t header = {};
|
||||
TESTASSERT(rlc_um_nr_read_data_pdu_header(&pdu, srslte::rlc_um_nr_sn_size_t::size6bits, &header) != 0);
|
||||
|
||||
TESTASSERT(header.si == rlc_nr_si_field_t::neither_first_nor_last_segment);
|
||||
TESTASSERT(header.so == 258);
|
||||
TESTASSERT(header.sn == 3);
|
||||
TESTASSERT(header.sn_size == srslte::rlc_um_nr_sn_size_t::size6bits);
|
||||
|
||||
// clear header
|
||||
corrupt_pdu_header(pdu, header_len, payload_len);
|
||||
|
||||
// pack again
|
||||
TESTASSERT(rlc_um_nr_write_data_pdu_header(header, &pdu) == header_len);
|
||||
TESTASSERT(pdu.N_bytes == tv.size());
|
||||
TESTASSERT(memcmp(pdu.msg, tv.data(), pdu.N_bytes) == 0);
|
||||
|
||||
return SRSLTE_SUCCESS;
|
||||
}
|
||||
|
||||
// RLC UM PDU with 6 Bit SN carrying a first segment of an SDU
|
||||
int rlc_um_nr_pdu_test4()
|
||||
{
|
||||
// SN = 31
|
||||
const int header_len = 1, payload_len = 4;
|
||||
std::array<uint8_t, header_len + payload_len> tv = {0x5f, 0x11, 0x22, 0x33, 0x44};
|
||||
srslte::byte_buffer_t pdu = make_pdu_and_log(tv);
|
||||
|
||||
// unpack PDU
|
||||
rlc_um_nr_pdu_header_t header = {};
|
||||
TESTASSERT(rlc_um_nr_read_data_pdu_header(&pdu, srslte::rlc_um_nr_sn_size_t::size6bits, &header) != 0);
|
||||
|
||||
TESTASSERT(header.si == rlc_nr_si_field_t::first_segment);
|
||||
TESTASSERT(header.so == 0);
|
||||
TESTASSERT(header.sn == 31);
|
||||
TESTASSERT(header.sn_size == srslte::rlc_um_nr_sn_size_t::size6bits);
|
||||
|
||||
// clear header
|
||||
corrupt_pdu_header(pdu, header_len, payload_len);
|
||||
|
||||
// pack again
|
||||
TESTASSERT(rlc_um_nr_write_data_pdu_header(header, &pdu) == header_len);
|
||||
TESTASSERT(pdu.N_bytes == tv.size());
|
||||
TESTASSERT(memcmp(pdu.msg, tv.data(), pdu.N_bytes) == 0);
|
||||
|
||||
return SRSLTE_SUCCESS;
|
||||
}
|
||||
|
||||
// This should fail unpacking because the PDU has reserved bits set
|
||||
int rlc_um_nr_pdu_unpack_test5()
|
||||
{
|
||||
std::array<uint8_t, 7> tv = {0x33, 0x01, 0x02, 0x11, 0x22, 0x33, 0x44};
|
||||
srslte::byte_buffer_t pdu = make_pdu_and_log(tv);
|
||||
|
||||
// unpack PDU
|
||||
rlc_um_nr_pdu_header_t header = {};
|
||||
TESTASSERT(rlc_um_nr_read_data_pdu_header(&pdu, srslte::rlc_um_nr_sn_size_t::size6bits, &header) == 0);
|
||||
|
||||
return SRSLTE_SUCCESS;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
#if PCAP
|
||||
pcap_handle = std::unique_ptr<srslte::mac_nr_pcap>(new srslte::mac_nr_pcap());
|
||||
pcap_handle->open("rlc_um_nr_pdu_test.pcap");
|
||||
#endif
|
||||
|
||||
if (rlc_um_nr_pdu_test1()) {
|
||||
fprintf(stderr, "rlc_um_nr_pdu_test1() failed.\n");
|
||||
return SRSLTE_ERROR;
|
||||
}
|
||||
|
||||
if (rlc_um_nr_pdu_test2()) {
|
||||
fprintf(stderr, "rlc_um_nr_pdu_test2() failed.\n");
|
||||
return SRSLTE_ERROR;
|
||||
}
|
||||
|
||||
if (rlc_um_nr_pdu_test3()) {
|
||||
fprintf(stderr, "rlc_um_nr_pdu_test3() failed.\n");
|
||||
return SRSLTE_ERROR;
|
||||
}
|
||||
|
||||
if (rlc_um_nr_pdu_test4()) {
|
||||
fprintf(stderr, "rlc_um_nr_pdu_test4() failed.\n");
|
||||
return SRSLTE_ERROR;
|
||||
}
|
||||
|
||||
if (rlc_um_nr_pdu_unpack_test5()) {
|
||||
fprintf(stderr, "rlc_um_nr_pdu_unpack_test5() failed.\n");
|
||||
return SRSLTE_ERROR;
|
||||
}
|
||||
|
||||
return SRSLTE_SUCCESS;
|
||||
}
|
|
@ -0,0 +1,210 @@
|
|||
/*
|
||||
* Copyright 2013-2019 Software Radio Systems Limited
|
||||
*
|
||||
* This file is part of srsLTE.
|
||||
*
|
||||
* srsLTE is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* srsLTE is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* A copy of the GNU Affero General Public License can be found in
|
||||
* the LICENSE file in the top-level directory of this distribution
|
||||
* and at http://www.gnu.org/licenses/.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "rlc_test_common.h"
|
||||
#include "srslte/common/log_filter.h"
|
||||
#include "srslte/config.h"
|
||||
#include "srslte/upper/rlc.h"
|
||||
#include "srslte/upper/rlc_um.h"
|
||||
|
||||
#include <array>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#define TESTASSERT(cond) \
|
||||
{ \
|
||||
if (!(cond)) { \
|
||||
std::cout << "[" << __FUNCTION__ << "][Line " << __LINE__ << "]: FAIL at " << (#cond) << std::endl; \
|
||||
return -1; \
|
||||
} \
|
||||
}
|
||||
|
||||
#define PCAP 0
|
||||
#define PCAP_CRNTI (0x1001)
|
||||
#define PCAP_TTI (666)
|
||||
|
||||
using namespace srslte;
|
||||
|
||||
#if PCAP
|
||||
#include "srslte/common/mac_nr_pcap.h"
|
||||
#include "srslte/common/mac_nr_pdu.h"
|
||||
static std::unique_ptr<srslte::mac_nr_pcap> pcap_handle = nullptr;
|
||||
#endif
|
||||
|
||||
int write_pdu_to_pcap(const uint32_t lcid, const uint8_t* payload, const uint32_t len)
|
||||
{
|
||||
#if PCAP
|
||||
if (pcap_handle) {
|
||||
byte_buffer_t tx_buffer;
|
||||
srslte::nr_mac_pdu tx_pdu;
|
||||
tx_pdu.init_tx(&tx_buffer, len + 10);
|
||||
tx_pdu.add_sdu(lcid, payload, len);
|
||||
tx_pdu.pack();
|
||||
pcap_handle->write_dl_crnti(tx_buffer.msg, tx_buffer.N_bytes, PCAP_CRNTI, true, PCAP_TTI);
|
||||
return SRSLTE_SUCCESS;
|
||||
}
|
||||
#endif
|
||||
return SRSLTE_ERROR;
|
||||
}
|
||||
|
||||
template <std::size_t N>
|
||||
srslte::byte_buffer_t make_pdu_and_log(const std::array<uint8_t, N>& tv)
|
||||
{
|
||||
srslte::byte_buffer_t pdu;
|
||||
memcpy(pdu.msg, tv.data(), tv.size());
|
||||
pdu.N_bytes = tv.size();
|
||||
write_pdu_to_pcap(4, tv.data(), tv.size());
|
||||
return pdu;
|
||||
}
|
||||
|
||||
// Basic test to write UM PDU with 6 bit SN
|
||||
int rlc_um_nr_test1()
|
||||
{
|
||||
srslte::log_filter log1("RLC_UM_1");
|
||||
srslte::log_filter log2("RLC_UM_2");
|
||||
log1.set_level(srslte::LOG_LEVEL_DEBUG);
|
||||
log2.set_level(srslte::LOG_LEVEL_DEBUG);
|
||||
log1.set_hex_limit(-1);
|
||||
log2.set_hex_limit(-1);
|
||||
rlc_um_tester tester;
|
||||
srslte::timers timers(16);
|
||||
const uint32_t num_sdus = 5;
|
||||
int len = 0;
|
||||
|
||||
rlc_um rlc1(&log1, 3, &tester, &tester, &timers);
|
||||
rlc_um rlc2(&log2, 3, &tester, &tester, &timers);
|
||||
|
||||
rlc_config_t cnfg = rlc_config_t::default_rlc_um_nr_config(6);
|
||||
|
||||
TESTASSERT(rlc1.configure(cnfg) == true);
|
||||
TESTASSERT(rlc2.configure(cnfg) == true);
|
||||
|
||||
tester.set_expected_sdu_len(1);
|
||||
|
||||
// Push 5 SDUs into RLC1
|
||||
byte_buffer_pool* pool = byte_buffer_pool::get_instance();
|
||||
unique_byte_buffer_t sdu_bufs[num_sdus];
|
||||
for (uint32_t i = 0; i < num_sdus; i++) {
|
||||
sdu_bufs[i] = srslte::allocate_unique_buffer(*pool, true);
|
||||
*sdu_bufs[i]->msg = i; // Write the index into the buffer
|
||||
sdu_bufs[i]->N_bytes = 1; // Give each buffer a size of 1 byte
|
||||
rlc1.write_sdu(std::move(sdu_bufs[i]));
|
||||
}
|
||||
|
||||
TESTASSERT(14 == rlc1.get_buffer_state());
|
||||
|
||||
// Read 5 PDUs from RLC1 (1 byte each)
|
||||
unique_byte_buffer_t pdu_bufs[num_sdus];
|
||||
for (uint32_t i = 0; i < num_sdus; i++) {
|
||||
pdu_bufs[i] = srslte::allocate_unique_buffer(*pool, true);
|
||||
len = rlc1.read_pdu(pdu_bufs[i]->msg, 4); // 3 bytes for header + payload
|
||||
pdu_bufs[i]->N_bytes = len;
|
||||
|
||||
// write PCAP
|
||||
write_pdu_to_pcap(4, pdu_bufs[i]->msg, pdu_bufs[i]->N_bytes);
|
||||
}
|
||||
|
||||
TESTASSERT(0 == rlc1.get_buffer_state());
|
||||
|
||||
// TODO: add receive test
|
||||
|
||||
return SRSLTE_SUCCESS;
|
||||
}
|
||||
|
||||
// Basic test for SDU segmentation
|
||||
int rlc_um_nr_test2()
|
||||
{
|
||||
srslte::log_filter log1("RLC_UM_1");
|
||||
srslte::log_filter log2("RLC_UM_2");
|
||||
log1.set_level(srslte::LOG_LEVEL_DEBUG);
|
||||
log2.set_level(srslte::LOG_LEVEL_DEBUG);
|
||||
log1.set_hex_limit(-1);
|
||||
log2.set_hex_limit(-1);
|
||||
rlc_um_tester tester;
|
||||
srslte::timers timers(16);
|
||||
const uint32_t num_sdus = 1;
|
||||
const uint32_t sdu_size = 100;
|
||||
int len = 0;
|
||||
|
||||
rlc_um rlc1(&log1, 3, &tester, &tester, &timers);
|
||||
rlc_um rlc2(&log2, 3, &tester, &tester, &timers);
|
||||
|
||||
rlc_config_t cnfg = rlc_config_t::default_rlc_um_nr_config(6);
|
||||
|
||||
TESTASSERT(rlc1.configure(cnfg) == true);
|
||||
TESTASSERT(rlc2.configure(cnfg) == true);
|
||||
|
||||
tester.set_expected_sdu_len(1);
|
||||
|
||||
// Push SDUs into RLC1
|
||||
byte_buffer_pool* pool = byte_buffer_pool::get_instance();
|
||||
unique_byte_buffer_t sdu_bufs[num_sdus];
|
||||
for (uint32_t i = 0; i < num_sdus; i++) {
|
||||
sdu_bufs[i] = srslte::allocate_unique_buffer(*pool, true);
|
||||
// Write the index into the buffer
|
||||
for (uint32_t k = 0; k < sdu_size; ++k) {
|
||||
sdu_bufs[i]->msg[k] = i;
|
||||
}
|
||||
sdu_bufs[i]->N_bytes = sdu_size;
|
||||
rlc1.write_sdu(std::move(sdu_bufs[i]));
|
||||
}
|
||||
|
||||
// FIXME: check buffer state calculation
|
||||
TESTASSERT(103 == rlc1.get_buffer_state());
|
||||
|
||||
// Read PDUs from RLC1 with grant of 25 Bytes each
|
||||
const uint32_t max_num_pdus = 10;
|
||||
unique_byte_buffer_t pdu_bufs[max_num_pdus];
|
||||
for (uint32_t i = 0; i < max_num_pdus; i++) {
|
||||
pdu_bufs[i] = srslte::allocate_unique_buffer(*pool, true);
|
||||
len = rlc1.read_pdu(pdu_bufs[i]->msg, 25); // 3 bytes for header + payload
|
||||
pdu_bufs[i]->N_bytes = len;
|
||||
|
||||
// write PCAP
|
||||
write_pdu_to_pcap(4, pdu_bufs[i]->msg, pdu_bufs[i]->N_bytes);
|
||||
}
|
||||
|
||||
TESTASSERT(0 == rlc1.get_buffer_state());
|
||||
|
||||
// TODO: add receive test
|
||||
|
||||
return SRSLTE_SUCCESS;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
#if PCAP
|
||||
pcap_handle = std::unique_ptr<srslte::mac_nr_pcap>(new srslte::mac_nr_pcap());
|
||||
pcap_handle->open("rlc_um_nr_test.pcap");
|
||||
#endif
|
||||
|
||||
if (rlc_um_nr_test1()) {
|
||||
fprintf(stderr, "rlc_um_nr_test1() failed.\n");
|
||||
return SRSLTE_ERROR;
|
||||
}
|
||||
|
||||
if (rlc_um_nr_test2()) {
|
||||
fprintf(stderr, "rlc_um_nr_test2() failed.\n");
|
||||
return SRSLTE_ERROR;
|
||||
}
|
||||
return SRSLTE_SUCCESS;
|
||||
}
|
|
@ -19,9 +19,10 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include <iostream>
|
||||
#include "rlc_test_common.h"
|
||||
#include "srslte/common/log_filter.h"
|
||||
#include "srslte/upper/rlc_um.h"
|
||||
#include <iostream>
|
||||
|
||||
#define TESTASSERT(cond) \
|
||||
{ \
|
||||
|
@ -38,43 +39,6 @@ using namespace srslte;
|
|||
using namespace srsue;
|
||||
using namespace asn1::rrc;
|
||||
|
||||
class rlc_um_tester : public pdcp_interface_rlc, public rrc_interface_rlc
|
||||
{
|
||||
public:
|
||||
rlc_um_tester(){
|
||||
bzero(sdus, sizeof(sdus));
|
||||
n_sdus = 0;
|
||||
expected_sdu_len = 0;
|
||||
}
|
||||
|
||||
// PDCP interface
|
||||
void write_pdu(uint32_t lcid, unique_byte_buffer_t sdu)
|
||||
{
|
||||
if (lcid != 3 && sdu->N_bytes != expected_sdu_len) {
|
||||
printf("Received PDU with size %d, expected %d. Exiting.\n", sdu->N_bytes, expected_sdu_len);
|
||||
exit(-1);
|
||||
}
|
||||
sdus[n_sdus++] = std::move(sdu);
|
||||
}
|
||||
void write_pdu_bcch_bch(unique_byte_buffer_t sdu) {}
|
||||
void write_pdu_bcch_dlsch(unique_byte_buffer_t sdu) {}
|
||||
void write_pdu_pcch(unique_byte_buffer_t sdu) {}
|
||||
void write_pdu_mch(uint32_t lcid, srslte::unique_byte_buffer_t sdu)
|
||||
{
|
||||
sdus[n_sdus++] = std::move(sdu);
|
||||
}
|
||||
|
||||
// RRC interface
|
||||
void max_retx_attempted(){}
|
||||
std::string get_rb_name(uint32_t lcid) { return std::string(""); }
|
||||
void set_expected_sdu_len(uint32_t len) { expected_sdu_len = len; }
|
||||
|
||||
unique_byte_buffer_t sdus[MAX_NBUFS];
|
||||
int n_sdus;
|
||||
uint32_t expected_sdu_len;
|
||||
};
|
||||
|
||||
|
||||
int basic_test()
|
||||
{
|
||||
srslte::log_filter log1("RLC_UM_1");
|
||||
|
@ -135,10 +99,9 @@ int basic_test()
|
|||
|
||||
TESTASSERT(0 == rlc2.get_buffer_state());
|
||||
|
||||
TESTASSERT(NBUFS == tester.n_sdus);
|
||||
for(int i=0; i<tester.n_sdus; i++)
|
||||
{
|
||||
TESTASSERT(tester.sdus[i]->N_bytes == 1);
|
||||
TESTASSERT(NBUFS == tester.get_num_sdus());
|
||||
for (uint32_t i = 0; i < tester.sdus.size(); i++) {
|
||||
TESTASSERT(tester.sdus.at(i)->N_bytes == 1);
|
||||
TESTASSERT(*(tester.sdus[i]->msg) == i);
|
||||
}
|
||||
|
||||
|
@ -201,7 +164,7 @@ int loss_test()
|
|||
while (!timers.get(1)->is_expired())
|
||||
timers.get(1)->step();
|
||||
|
||||
TESTASSERT(NBUFS - 1 == tester.n_sdus);
|
||||
TESTASSERT(NBUFS - 1 == tester.sdus.size());
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -257,9 +220,8 @@ int basic_mbsfn_test()
|
|||
|
||||
TESTASSERT(0 == rlc2.get_buffer_state());
|
||||
|
||||
TESTASSERT(NBUFS == tester.n_sdus);
|
||||
for(int i=0; i<tester.n_sdus; i++)
|
||||
{
|
||||
TESTASSERT(NBUFS == tester.sdus.size());
|
||||
for (uint32_t i = 0; i < tester.sdus.size(); i++) {
|
||||
TESTASSERT(tester.sdus[i]->N_bytes == 1);
|
||||
TESTASSERT(*(tester.sdus[i]->msg) == i);
|
||||
}
|
||||
|
@ -372,8 +334,8 @@ int reassmble_test()
|
|||
}
|
||||
|
||||
// We should have received one SDU less than we tx'ed
|
||||
TESTASSERT(tester.n_sdus == n_sdus - 1);
|
||||
for (int i = 0; i < tester.n_sdus; ++i) {
|
||||
TESTASSERT(tester.sdus.size() == n_sdus - 1);
|
||||
for (uint32_t i = 0; i < tester.sdus.size(); ++i) {
|
||||
TESTASSERT(tester.sdus[i]->N_bytes == sdu_len);
|
||||
}
|
||||
|
||||
|
@ -476,8 +438,8 @@ int reassmble_test2()
|
|||
}
|
||||
|
||||
// We should have received one SDU less than we tx'ed
|
||||
TESTASSERT(tester.n_sdus == n_sdus - 1);
|
||||
for (int i = 0; i < tester.n_sdus; ++i) {
|
||||
TESTASSERT(tester.sdus.size() == n_sdus - 1);
|
||||
for (uint32_t i = 0; i < tester.sdus.size(); ++i) {
|
||||
TESTASSERT(tester.sdus[i]->N_bytes == sdu_len);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue