Merge branch 'next' into agpl_next

# Conflicts:
#	srsenb/hdr/stack/rrc/nr/rrc_config_nr.h
#	srsenb/hdr/stack/rrc/rrc_nr.h
#	srsenb/src/stack/mac/nr/sched_nr_signalling.cc
#	srsenb/test/rrc/rrc_nr_test.cc
master
Codebot 3 years ago committed by Your Name
commit 3a9014802d

@ -670,13 +670,14 @@ if(RF_FOUND)
endif(ENABLE_SRSUE)
if(ENABLE_SRSENB)
message(STATUS "Building with srsENB")
message(STATUS "Building with srsENB/srsGNB")
add_subdirectory(srsenb)
add_subdirectory(srsgnb)
else(ENABLE_SRSENB)
message(STATUS "srsENB build disabled")
message(STATUS "srsENB/srsGNB build disabled")
endif(ENABLE_SRSENB)
else(RF_FOUND)
message(STATUS "srsUE and srsENB builds disabled due to missing RF driver")
message(STATUS "srsUE and srsENB/srsGNB builds disabled due to missing RF driver")
endif(RF_FOUND)
if(ENABLE_SRSEPC)

@ -92,6 +92,10 @@ public:
using iterator = iterator_impl<T>;
using const_iterator = iterator_impl<const T>;
base_optional_span() = default;
base_optional_span(Vec&& v, size_t nof_elems_) : vec(std::move(v)), nof_elems(nof_elems_) {}
base_optional_span(const Vec& v, size_t nof_elems_) : vec(v), nof_elems(nof_elems_) {}
// Find first position that is empty
size_t find_first_empty(size_t start_guess = 0)
{
@ -143,6 +147,16 @@ public:
this->nof_elems += this->contains(idx) ? 0 : 1;
this->vec[idx] = std::forward<U>(u);
}
template <typename... Args>
void emplace(size_t idx, Args&&... args)
{
srsran_assert(idx < this->vec.size(), "Out-of-bounds access to array: %zd>=%zd", idx, this->vec.size());
if (not this->contains(idx)) {
this->nof_elems++;
}
this->vec[idx].emplace(std::forward<Args>(args)...);
}
};
template <typename Vec>
@ -157,8 +171,7 @@ public:
base_optional_vector() = default;
base_optional_vector(const base_optional_vector&) = default;
base_optional_vector(base_optional_vector&& other) noexcept : base_t::vec(std::move(other.vec)),
base_t::nof_elems(other.nof_elems)
base_optional_vector(base_optional_vector&& other) noexcept : base_t(std::move(other.vec), other.size())
{
other.nof_elems = 0;
}
@ -167,7 +180,7 @@ public:
{
this->vec = std::move(other.vec);
this->nof_elems = other.nof_elems;
this->nof_elems = 0;
other.nof_elems = 0;
return *this;
}
};
@ -205,6 +218,16 @@ public:
}
base_t::insert(idx, std::forward<U>(u));
}
/// May allocate and cause pointer invalidation
template <typename... Args>
void emplace(size_t idx, Args&&... args)
{
if (idx >= this->vec.size()) {
this->vec.resize(idx + 1);
}
base_t::emplace(idx, std::forward<Args>(args)...);
}
};
template <typename T>

@ -22,7 +22,6 @@
#ifndef SRSRAN_RRC_NR_UTILS_H
#define SRSRAN_RRC_NR_UTILS_H
#include "srsenb/hdr/stack/mac/sched_interface.h"
#include "srsran/common/phy_cfg_nr.h"
#include "srsran/interfaces/mac_interface_types.h"
#include "srsran/interfaces/pdcp_interface_types.h"
@ -152,9 +151,4 @@ pdcp_config_t make_drb_pdcp_config_t(const uint8_t bearer_id, bool is_ue, const
} // namespace srsran
namespace srsenb {
int set_sched_cell_cfg_sib1(srsenb::sched_interface::cell_cfg_t* sched_cfg, const asn1::rrc_nr::sib1_s& sib1);
}
#endif // SRSRAN_RRC_NR_UTILS_H

@ -128,7 +128,7 @@ public:
// avoid self assignment
if (&buf == this)
return *this;
msg = &buffer[SRSRAN_BUFFER_HEADER_OFFSET];
msg = &buffer[buf.msg - &(*buf.buffer)];
N_bytes = buf.N_bytes;
md = buf.md;
memcpy(msg, buf.msg, N_bytes);

@ -196,6 +196,12 @@ make_sctp_sdu_handler(srslog::basic_logger& logger, srsran::task_queue_handle& q
socket_manager_itf::recv_callback_t
make_sdu_handler(srslog::basic_logger& logger, srsran::task_queue_handle& queue, recvfrom_callback_t rx_callback);
inline socket_manager& get_rx_io_manager()
{
static socket_manager io;
return io;
}
} // namespace srsran
#endif // SRSRAN_RX_SOCKET_HANDLER_H

@ -146,6 +146,7 @@ inline void test_init(int argc, char** argv)
srsran_debug_handle_crash(argc, argv);
srslog::fetch_basic_logger("ALL").set_level(srslog::basic_levels::info);
srslog::fetch_basic_logger("TEST").set_level(srslog::basic_levels::info);
// Start the log backend.
srslog::init();
@ -162,6 +163,33 @@ inline void copy_msg_to_buffer(unique_byte_buffer_t& pdu, const_byte_span msg)
pdu->N_bytes = msg.size();
}
class test_delimit_logger
{
const size_t delimiter_length = 128;
public:
template <typename... Args>
explicit test_delimit_logger(const char* test_name_fmt, Args&&... args)
{
test_name = fmt::format(test_name_fmt, std::forward<Args>(args)...);
std::string name_str = fmt::format("[ Test \"{}\" ]", test_name);
double nof_repeats = (delimiter_length - name_str.size()) / 2.0;
fmt::print("{0:=>{1}}{2}{0:=>{3}}\n", "", (int)floor(nof_repeats), name_str, (int)ceil(nof_repeats));
}
test_delimit_logger(const test_delimit_logger&) = delete;
test_delimit_logger(test_delimit_logger&&) = delete;
test_delimit_logger& operator=(const test_delimit_logger&) = delete;
test_delimit_logger& operator=(test_delimit_logger&&) = delete;
~test_delimit_logger()
{
srslog::flush();
fmt::print("{:=>{}}\n", "", delimiter_length);
}
private:
std::string test_name;
};
} // namespace srsran
#define CONDERROR(cond, fmt, ...) srsran_assert(not(cond), fmt, ##__VA_ARGS__)

@ -141,8 +141,8 @@ class rrc_interface_mac_nr
{
public:
// Provides MIB packed message
virtual int read_pdu_bcch_bch(const uint32_t tti, srsran::unique_byte_buffer_t& buffer) = 0;
virtual int read_pdu_bcch_dlsch(uint32_t sib_index, srsran::unique_byte_buffer_t& buffer) = 0;
virtual int read_pdu_bcch_bch(const uint32_t tti, srsran::byte_buffer_t& buffer) = 0;
virtual int read_pdu_bcch_dlsch(uint32_t sib_index, srsran::byte_buffer_t& buffer) = 0;
/// User management
virtual int add_user(uint16_t rnti, const sched_nr_ue_cfg_t& uecfg) = 0;

@ -22,7 +22,7 @@
#ifndef SRSRAN_GNB_MAC_INTERFACES_H
#define SRSRAN_GNB_MAC_INTERFACES_H
#include "srsenb/hdr/stack/mac/nr/sched_nr_interface.h"
#include "srsgnb/hdr/stack/mac/sched_nr_interface.h"
namespace srsenb {

@ -21,7 +21,6 @@
#ifndef SRSRAN_GNB_RRC_NR_INTERFACES_H
#define SRSRAN_GNB_RRC_NR_INTERFACES_H
#include "srsenb/hdr/phy/phy_interfaces.h"
#include "srsran/asn1/ngap.h"
#include "srsran/common/byte_buffer.h"
@ -36,28 +35,12 @@ public:
virtual int ue_set_security_cfg_capabilities(uint16_t rnti, const asn1::ngap_nr::ue_security_cap_s& caps) = 0;
virtual int start_security_mode_procedure(uint16_t rnti) = 0;
virtual int
establish_rrc_bearer(uint16_t rnti, uint16_t pdu_session_id, srsran::const_byte_span nas_pdu, uint32_t lcid) = 0;
virtual int allocate_lcid(uint16_t rnti) = 0;
virtual int release_bearers(uint16_t rnti) = 0;
virtual void write_dl_info(uint16_t rnti, srsran::unique_byte_buffer_t sdu) = 0;
establish_rrc_bearer(uint16_t rnti, uint16_t pdu_session_id, srsran::const_byte_span nas_pdu, uint32_t lcid) = 0;
virtual int allocate_lcid(uint16_t rnti) = 0;
virtual int release_bearers(uint16_t rnti) = 0;
virtual void write_dl_info(uint16_t rnti, srsran::unique_byte_buffer_t sdu) = 0;
};
// Cell/Sector configuration for NR cells
struct rrc_cell_cfg_nr_t {
phy_cell_cfg_nr_t phy_cell; // already contains all PHY-related parameters (i.e. RF port, PCI, etc.)
uint32_t tac; // Tracking area code
uint32_t dl_arfcn; // DL freq already included in phy_cell
uint32_t ul_arfcn; // UL freq also in phy_cell
uint32_t dl_absolute_freq_point_a; // derived from DL ARFCN
uint32_t ul_absolute_freq_point_a; // derived from UL ARFCN
uint32_t ssb_absolute_freq_point; // derived from DL ARFCN
uint32_t band;
srsran_duplex_mode_t duplex_mode;
srsran_ssb_cfg_t ssb_cfg;
};
typedef std::vector<rrc_cell_cfg_nr_t> rrc_cell_list_nr_t;
} // namespace srsenb
#endif // SRSRAN_GNB_RRC_NR_INTERFACES_H

@ -0,0 +1,217 @@
/**
*
* \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_AM_DATA_STRUCTS_H
#define SRSRAN_RLC_AM_DATA_STRUCTS_H
#include "srsran/adt/circular_buffer.h"
#include "srsran/adt/circular_map.h"
#include "srsran/adt/intrusive_list.h"
#include <array>
#include <vector>
namespace srsran {
class rlc_amd_tx_pdu;
class pdcp_pdu_info;
/// Pool that manages the allocation of RLC AM PDU Segments to RLC PDUs and tracking of segments ACK state
struct rlc_am_pdu_segment_pool {
const static size_t MAX_POOL_SIZE = 16384;
using rlc_list_tag = default_intrusive_tag;
struct free_list_tag {};
/// RLC AM PDU Segment, containing the PDCP SN and RLC SN it has been assigned to, and its current ACK state
struct segment_resource : public intrusive_forward_list_element<rlc_list_tag>,
public intrusive_forward_list_element<free_list_tag>,
public intrusive_double_linked_list_element<> {
const static uint32_t invalid_rlc_sn = std::numeric_limits<uint32_t>::max();
const static uint32_t invalid_pdcp_sn = std::numeric_limits<uint32_t>::max() - 1; // -1 for Status Report
int id() const;
void release_pdcp_sn();
void release_rlc_sn();
uint32_t rlc_sn() const { return rlc_sn_; }
uint32_t pdcp_sn() const { return pdcp_sn_; }
bool empty() const { return rlc_sn_ == invalid_rlc_sn and pdcp_sn_ == invalid_pdcp_sn; }
private:
friend struct rlc_am_pdu_segment_pool;
uint32_t rlc_sn_ = invalid_rlc_sn;
uint32_t pdcp_sn_ = invalid_pdcp_sn;
rlc_am_pdu_segment_pool* parent_pool = nullptr;
};
rlc_am_pdu_segment_pool();
rlc_am_pdu_segment_pool(const rlc_am_pdu_segment_pool&) = delete;
rlc_am_pdu_segment_pool(rlc_am_pdu_segment_pool&&) = delete;
rlc_am_pdu_segment_pool& operator=(const rlc_am_pdu_segment_pool&) = delete;
rlc_am_pdu_segment_pool& operator=(rlc_am_pdu_segment_pool&&) = delete;
bool has_segments() const { return not free_list.empty(); }
bool make_segment(rlc_amd_tx_pdu& rlc_list, pdcp_pdu_info& pdcp_info);
private:
intrusive_forward_list<segment_resource, free_list_tag> free_list;
std::array<segment_resource, MAX_POOL_SIZE> segments;
};
/// RLC AM PDU Segment, containing the PDCP SN and RLC SN it has been assigned to, and its current ACK state
using rlc_am_pdu_segment = rlc_am_pdu_segment_pool::segment_resource;
/// Class that contains the parameters and state (e.g. unACKed segments) of a PDCP PDU
class pdcp_pdu_info
{
using list_type = intrusive_double_linked_list<rlc_am_pdu_segment>;
list_type list; // List of unACKed RLC PDUs that contain segments that belong to the PDCP PDU.
public:
const static uint32_t status_report_sn = std::numeric_limits<uint32_t>::max();
const static uint32_t invalid_pdcp_sn = std::numeric_limits<uint32_t>::max() - 1;
using iterator = typename list_type::iterator;
using const_iterator = typename list_type::const_iterator;
// Copy is forbidden to avoid multiple PDCP SN references to the same segment
pdcp_pdu_info() = default;
pdcp_pdu_info(pdcp_pdu_info&&) noexcept = default;
pdcp_pdu_info(const pdcp_pdu_info&) noexcept = delete;
pdcp_pdu_info& operator=(const pdcp_pdu_info&) noexcept = delete;
pdcp_pdu_info& operator=(pdcp_pdu_info&&) noexcept = default;
~pdcp_pdu_info() { clear(); }
uint32_t sn = invalid_pdcp_sn;
bool fully_txed = false; // Boolean indicating if the SDU is fully transmitted.
bool fully_acked() const { return fully_txed and list.empty(); }
bool valid() const { return sn != invalid_pdcp_sn; }
// Interface for list of unACKed RLC segments of the PDCP PDU
void add_segment(rlc_am_pdu_segment& segment) { list.push_front(&segment); }
void ack_segment(rlc_am_pdu_segment& segment);
void clear()
{
sn = invalid_pdcp_sn;
fully_txed = false;
while (not list.empty()) {
ack_segment(list.front());
}
}
const_iterator begin() const { return list.begin(); }
const_iterator end() const { return list.end(); }
};
template <class T, std::size_t WINDOW_SIZE>
struct rlc_ringbuffer_t {
T& add_pdu(size_t sn)
{
srsran_expect(not has_sn(sn), "The same SN=%zd should not be added twice", sn);
window.overwrite(sn, T(sn));
return window[sn];
}
void remove_pdu(size_t sn)
{
srsran_expect(has_sn(sn), "The removed SN=%zd is not in the window", sn);
window.erase(sn);
}
T& operator[](size_t sn) { return window[sn]; }
size_t size() const { return window.size(); }
bool empty() const { return window.empty(); }
void clear() { window.clear(); }
bool has_sn(uint32_t sn) const { return window.contains(sn); }
// Return the sum data bytes of all active PDUs (check PDU is non-null)
uint32_t get_buffered_bytes()
{
uint32_t buff_size = 0;
for (const auto& pdu : window) {
if (pdu.second.buf != nullptr) {
buff_size += pdu.second.buf->N_bytes;
}
}
return buff_size;
}
private:
srsran::static_circular_map<uint32_t, T, WINDOW_SIZE> window;
};
struct buffered_pdcp_pdu_list {
public:
explicit buffered_pdcp_pdu_list() : buffered_pdus(buffered_pdcp_pdu_list::buffer_size) { clear(); }
void clear()
{
count = 0;
for (pdcp_pdu_info& b : buffered_pdus) {
b.clear();
}
}
void add_pdcp_sdu(uint32_t sn)
{
srsran_expect(sn <= max_pdcp_sn or sn == status_report_sn, "Invalid PDCP SN=%d", sn);
srsran_assert(not has_pdcp_sn(sn), "Cannot re-add same PDCP SN twice");
pdcp_pdu_info& pdu = get_pdu_(sn);
if (pdu.valid()) {
pdu.clear();
count--;
}
pdu.sn = sn;
count++;
}
void clear_pdcp_sdu(uint32_t sn)
{
pdcp_pdu_info& pdu = get_pdu_(sn);
if (not pdu.valid()) {
return;
}
pdu.clear();
count--;
}
pdcp_pdu_info& operator[](uint32_t sn)
{
srsran_expect(has_pdcp_sn(sn), "Invalid access to non-existent PDCP SN=%d", sn);
return get_pdu_(sn);
}
bool has_pdcp_sn(uint32_t pdcp_sn) const
{
srsran_expect(pdcp_sn <= max_pdcp_sn or pdcp_sn == status_report_sn, "Invalid PDCP SN=%d", pdcp_sn);
return get_pdu_(pdcp_sn).sn == pdcp_sn;
}
uint32_t nof_sdus() const { return count; }
private:
const static size_t max_pdcp_sn = 262143u;
const static size_t buffer_size = 4096u;
const static uint32_t status_report_sn = pdcp_pdu_info::status_report_sn;
pdcp_pdu_info& get_pdu_(uint32_t sn)
{
return (sn == status_report_sn) ? status_report_pdu : buffered_pdus[static_cast<size_t>(sn % buffer_size)];
}
const pdcp_pdu_info& get_pdu_(uint32_t sn) const
{
return (sn == status_report_sn) ? status_report_pdu : buffered_pdus[static_cast<size_t>(sn % buffer_size)];
}
// size equal to buffer_size
std::vector<pdcp_pdu_info> buffered_pdus;
pdcp_pdu_info status_report_pdu;
uint32_t count = 0;
};
} // namespace srsran
#endif // SRSRAN_RLC_AM_DATA_STRUCTS_H

@ -25,13 +25,14 @@
#include "srsran/adt/accumulators.h"
#include "srsran/adt/circular_array.h"
#include "srsran/adt/circular_map.h"
#include "srsran/adt/intrusive_list.h"
#include "srsran/common/buffer_pool.h"
#include "srsran/common/common.h"
#include "srsran/common/task_scheduler.h"
#include "srsran/common/timeout.h"
#include "srsran/interfaces/pdcp_interface_types.h"
#include "srsran/rlc/rlc_am_base.h"
#include "srsran/rlc/rlc_am_data_structs.h"
#include "srsran/rlc/rlc_am_lte_packing.h"
#include "srsran/rlc/rlc_common.h"
#include "srsran/support/srsran_assert.h"
#include "srsran/upper/byte_buffer_queue.h"
@ -43,247 +44,6 @@ namespace srsran {
#undef RLC_AM_BUFFER_DEBUG
class rlc_amd_tx_pdu;
class pdcp_pdu_info;
/// Pool that manages the allocation of RLC AM PDU Segments to RLC PDUs and tracking of segments ACK state
struct rlc_am_pdu_segment_pool {
const static size_t MAX_POOL_SIZE = 16384;
using rlc_list_tag = default_intrusive_tag;
struct free_list_tag {};
/// RLC AM PDU Segment, containing the PDCP SN and RLC SN it has been assigned to, and its current ACK state
struct segment_resource : public intrusive_forward_list_element<rlc_list_tag>,
public intrusive_forward_list_element<free_list_tag>,
public intrusive_double_linked_list_element<> {
const static uint32_t invalid_rlc_sn = std::numeric_limits<uint32_t>::max();
const static uint32_t invalid_pdcp_sn = std::numeric_limits<uint32_t>::max() - 1; // -1 for Status Report
int id() const;
void release_pdcp_sn();
void release_rlc_sn();
uint32_t rlc_sn() const { return rlc_sn_; }
uint32_t pdcp_sn() const { return pdcp_sn_; }
bool empty() const { return rlc_sn_ == invalid_rlc_sn and pdcp_sn_ == invalid_pdcp_sn; }
private:
friend struct rlc_am_pdu_segment_pool;
uint32_t rlc_sn_ = invalid_rlc_sn;
uint32_t pdcp_sn_ = invalid_pdcp_sn;
rlc_am_pdu_segment_pool* parent_pool = nullptr;
};
rlc_am_pdu_segment_pool();
rlc_am_pdu_segment_pool(const rlc_am_pdu_segment_pool&) = delete;
rlc_am_pdu_segment_pool(rlc_am_pdu_segment_pool&&) = delete;
rlc_am_pdu_segment_pool& operator=(const rlc_am_pdu_segment_pool&) = delete;
rlc_am_pdu_segment_pool& operator=(rlc_am_pdu_segment_pool&&) = delete;
bool has_segments() const { return not free_list.empty(); }
bool make_segment(rlc_amd_tx_pdu& rlc_list, pdcp_pdu_info& pdcp_info);
private:
intrusive_forward_list<segment_resource, free_list_tag> free_list;
std::array<segment_resource, MAX_POOL_SIZE> segments;
};
/// RLC AM PDU Segment, containing the PDCP SN and RLC SN it has been assigned to, and its current ACK state
using rlc_am_pdu_segment = rlc_am_pdu_segment_pool::segment_resource;
struct rlc_amd_rx_pdu {
rlc_amd_pdu_header_t header;
unique_byte_buffer_t buf;
uint32_t rlc_sn;
rlc_amd_rx_pdu() = default;
explicit rlc_amd_rx_pdu(uint32_t rlc_sn_) : rlc_sn(rlc_sn_) {}
};
struct rlc_amd_rx_pdu_segments_t {
std::list<rlc_amd_rx_pdu> segments;
};
/// Class that contains the parameters and state (e.g. segments) of a RLC PDU
class rlc_amd_tx_pdu
{
using list_type = intrusive_forward_list<rlc_am_pdu_segment>;
const static uint32_t invalid_rlc_sn = std::numeric_limits<uint32_t>::max();
list_type list;
public:
using iterator = typename list_type::iterator;
using const_iterator = typename list_type::const_iterator;
const uint32_t rlc_sn = invalid_rlc_sn;
uint32_t retx_count = 0;
rlc_amd_pdu_header_t header;
unique_byte_buffer_t buf;
explicit rlc_amd_tx_pdu(uint32_t rlc_sn_) : rlc_sn(rlc_sn_) {}
rlc_amd_tx_pdu(const rlc_amd_tx_pdu&) = delete;
rlc_amd_tx_pdu(rlc_amd_tx_pdu&& other) noexcept = default;
rlc_amd_tx_pdu& operator=(const rlc_amd_tx_pdu& other) = delete;
rlc_amd_tx_pdu& operator=(rlc_amd_tx_pdu&& other) = delete;
~rlc_amd_tx_pdu();
// Segment List Interface
void add_segment(rlc_am_pdu_segment& segment) { list.push_front(&segment); }
const_iterator begin() const { return list.begin(); }
const_iterator end() const { return list.end(); }
iterator begin() { return list.begin(); }
iterator end() { return list.end(); }
};
struct rlc_amd_retx_t {
uint32_t sn;
bool is_segment;
uint32_t so_start;
uint32_t so_end;
};
struct rlc_sn_info_t {
uint32_t sn;
bool is_acked;
};
/// Class that contains the parameters and state (e.g. unACKed segments) of a PDCP PDU
class pdcp_pdu_info
{
using list_type = intrusive_double_linked_list<rlc_am_pdu_segment>;
list_type list; // List of unACKed RLC PDUs that contain segments that belong to the PDCP PDU.
public:
const static uint32_t status_report_sn = std::numeric_limits<uint32_t>::max();
const static uint32_t invalid_pdcp_sn = std::numeric_limits<uint32_t>::max() - 1;
using iterator = typename list_type::iterator;
using const_iterator = typename list_type::const_iterator;
// Copy is forbidden to avoid multiple PDCP SN references to the same segment
pdcp_pdu_info() = default;
pdcp_pdu_info(pdcp_pdu_info&&) noexcept = default;
pdcp_pdu_info(const pdcp_pdu_info&) noexcept = delete;
pdcp_pdu_info& operator=(const pdcp_pdu_info&) noexcept = delete;
pdcp_pdu_info& operator=(pdcp_pdu_info&&) noexcept = default;
~pdcp_pdu_info() { clear(); }
uint32_t sn = invalid_pdcp_sn;
bool fully_txed = false; // Boolean indicating if the SDU is fully transmitted.
bool fully_acked() const { return fully_txed and list.empty(); }
bool valid() const { return sn != invalid_pdcp_sn; }
// Interface for list of unACKed RLC segments of the PDCP PDU
void add_segment(rlc_am_pdu_segment& segment) { list.push_front(&segment); }
void ack_segment(rlc_am_pdu_segment& segment);
void clear()
{
sn = invalid_pdcp_sn;
fully_txed = false;
while (not list.empty()) {
ack_segment(list.front());
}
}
const_iterator begin() const { return list.begin(); }
const_iterator end() const { return list.end(); }
};
template <class T>
struct rlc_ringbuffer_t {
T& add_pdu(size_t sn)
{
srsran_expect(not has_sn(sn), "The same SN=%zd should not be added twice", sn);
window.overwrite(sn, T(sn));
return window[sn];
}
void remove_pdu(size_t sn)
{
srsran_expect(has_sn(sn), "The removed SN=%zd is not in the window", sn);
window.erase(sn);
}
T& operator[](size_t sn) { return window[sn]; }
size_t size() const { return window.size(); }
bool empty() const { return window.empty(); }
void clear() { window.clear(); }
bool has_sn(uint32_t sn) const { return window.contains(sn); }
// Return the sum data bytes of all active PDUs (check PDU is non-null)
uint32_t get_buffered_bytes()
{
uint32_t buff_size = 0;
for (const auto& pdu : window) {
if (pdu.second.buf != nullptr) {
buff_size += pdu.second.buf->N_bytes;
}
}
return buff_size;
}
private:
srsran::static_circular_map<uint32_t, T, RLC_AM_WINDOW_SIZE> window;
};
struct buffered_pdcp_pdu_list {
public:
explicit buffered_pdcp_pdu_list();
void clear();
void add_pdcp_sdu(uint32_t sn)
{
srsran_expect(sn <= max_pdcp_sn or sn == status_report_sn, "Invalid PDCP SN=%d", sn);
srsran_assert(not has_pdcp_sn(sn), "Cannot re-add same PDCP SN twice");
pdcp_pdu_info& pdu = get_pdu_(sn);
if (pdu.valid()) {
pdu.clear();
count--;
}
pdu.sn = sn;
count++;
}
void clear_pdcp_sdu(uint32_t sn)
{
pdcp_pdu_info& pdu = get_pdu_(sn);
if (not pdu.valid()) {
return;
}
pdu.clear();
count--;
}
pdcp_pdu_info& operator[](uint32_t sn)
{
srsran_expect(has_pdcp_sn(sn), "Invalid access to non-existent PDCP SN=%d", sn);
return get_pdu_(sn);
}
bool has_pdcp_sn(uint32_t pdcp_sn) const
{
srsran_expect(pdcp_sn <= max_pdcp_sn or pdcp_sn == status_report_sn, "Invalid PDCP SN=%d", pdcp_sn);
return get_pdu_(pdcp_sn).sn == pdcp_sn;
}
uint32_t nof_sdus() const { return count; }
private:
const static size_t max_pdcp_sn = 262143u;
const static size_t buffer_size = 4096u;
const static uint32_t status_report_sn = pdcp_pdu_info::status_report_sn;
pdcp_pdu_info& get_pdu_(uint32_t sn)
{
return (sn == status_report_sn) ? status_report_pdu : buffered_pdus[static_cast<size_t>(sn % buffer_size)];
}
const pdcp_pdu_info& get_pdu_(uint32_t sn) const
{
return (sn == status_report_sn) ? status_report_pdu : buffered_pdus[static_cast<size_t>(sn % buffer_size)];
}
// size equal to buffer_size
std::vector<pdcp_pdu_info> buffered_pdus;
pdcp_pdu_info status_report_pdu;
uint32_t count = 0;
};
class pdu_retx_queue
{
public:
@ -467,9 +227,9 @@ private:
bsr_callback_t bsr_callback;
// Tx windows
rlc_ringbuffer_t<rlc_amd_tx_pdu> tx_window;
pdu_retx_queue retx_queue;
pdcp_sn_vector_t notify_info_vec;
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;
// Mutexes
std::mutex mutex;
@ -541,8 +301,8 @@ private:
std::mutex mutex;
// Rx windows
rlc_ringbuffer_t<rlc_amd_rx_pdu> rx_window;
std::map<uint32_t, rlc_amd_rx_pdu_segments_t> rx_segments;
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
@ -576,31 +336,6 @@ private:
rlc_bearer_metrics_t metrics = {};
};
/****************************************************************************
* Header pack/unpack helper functions
* Ref: 3GPP TS 36.322 v10.0.0 Section 6.2.1
***************************************************************************/
void rlc_am_read_data_pdu_header(byte_buffer_t* pdu, rlc_amd_pdu_header_t* header);
void rlc_am_read_data_pdu_header(uint8_t** payload, uint32_t* nof_bytes, rlc_amd_pdu_header_t* header);
void rlc_am_write_data_pdu_header(rlc_amd_pdu_header_t* header, byte_buffer_t* pdu);
void rlc_am_write_data_pdu_header(rlc_amd_pdu_header_t* header, uint8_t** payload);
void rlc_am_read_status_pdu(byte_buffer_t* pdu, rlc_status_pdu_t* status);
void rlc_am_read_status_pdu(uint8_t* payload, uint32_t nof_bytes, rlc_status_pdu_t* status);
void rlc_am_write_status_pdu(rlc_status_pdu_t* status, byte_buffer_t* pdu);
int rlc_am_write_status_pdu(rlc_status_pdu_t* status, uint8_t* payload);
uint32_t rlc_am_packed_length(rlc_amd_pdu_header_t* header);
uint32_t rlc_am_packed_length(rlc_status_pdu_t* status);
uint32_t rlc_am_packed_length(rlc_amd_retx_t retx);
bool rlc_am_is_valid_status_pdu(const rlc_status_pdu_t& status, uint32_t rx_win_min = 0);
bool rlc_am_is_pdu_segment(uint8_t* payload);
std::string rlc_am_undelivered_sdu_info_to_string(const std::map<uint32_t, pdcp_pdu_info>& info_queue);
void log_rlc_amd_pdu_header_to_string(srslog::log_channel& log_ch, const rlc_amd_pdu_header_t& header);
bool rlc_am_start_aligned(const uint8_t fi);
bool rlc_am_end_aligned(const uint8_t fi);
bool rlc_am_is_unaligned(const uint8_t fi);
bool rlc_am_not_start_aligned(const uint8_t fi);
} // namespace srsran
#endif // SRSRAN_RLC_AM_LTE_H

@ -0,0 +1,135 @@
/**
*
* \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_AM_LTE_PACKING_H
#define SRSRAN_RLC_AM_LTE_PACKING_H
#include "srsran/common/string_helpers.h"
#include "srsran/rlc/rlc_am_base.h"
#include "srsran/rlc/rlc_am_data_structs.h" // required for rlc_am_pdu_segment
#include <list>
namespace srsran {
/// Class that contains the parameters and state (e.g. segments) of a RLC PDU
class rlc_amd_tx_pdu
{
using list_type = intrusive_forward_list<rlc_am_pdu_segment>;
const static uint32_t invalid_rlc_sn = std::numeric_limits<uint32_t>::max();
list_type list;
public:
using iterator = typename list_type::iterator;
using const_iterator = typename list_type::const_iterator;
const uint32_t rlc_sn = invalid_rlc_sn;
uint32_t retx_count = 0;
rlc_amd_pdu_header_t header;
unique_byte_buffer_t buf;
explicit rlc_amd_tx_pdu(uint32_t rlc_sn_) : rlc_sn(rlc_sn_) {}
rlc_amd_tx_pdu(const rlc_amd_tx_pdu&) = delete;
rlc_amd_tx_pdu(rlc_amd_tx_pdu&& other) noexcept = default;
rlc_amd_tx_pdu& operator=(const rlc_amd_tx_pdu& other) = delete;
rlc_amd_tx_pdu& operator=(rlc_amd_tx_pdu&& other) = delete;
~rlc_amd_tx_pdu();
// Segment List Interface
void add_segment(rlc_am_pdu_segment& segment) { list.push_front(&segment); }
const_iterator begin() const { return list.begin(); }
const_iterator end() const { return list.end(); }
iterator begin() { return list.begin(); }
iterator end() { return list.end(); }
};
struct rlc_amd_retx_t {
uint32_t sn;
bool is_segment;
uint32_t so_start;
uint32_t so_end;
};
struct rlc_sn_info_t {
uint32_t sn;
bool is_acked;
};
struct rlc_amd_rx_pdu {
rlc_amd_pdu_header_t header;
unique_byte_buffer_t buf;
uint32_t rlc_sn;
rlc_amd_rx_pdu() = default;
explicit rlc_amd_rx_pdu(uint32_t rlc_sn_) : rlc_sn(rlc_sn_) {}
};
struct rlc_amd_rx_pdu_segments_t {
std::list<rlc_amd_rx_pdu> segments;
};
/****************************************************************************
* Header pack/unpack helper functions
* Ref: 3GPP TS 36.322 v10.0.0 Section 6.2.1
***************************************************************************/
void rlc_am_read_data_pdu_header(byte_buffer_t* pdu, rlc_amd_pdu_header_t* header);
void rlc_am_read_data_pdu_header(uint8_t** payload, uint32_t* nof_bytes, rlc_amd_pdu_header_t* header);
void rlc_am_write_data_pdu_header(rlc_amd_pdu_header_t* header, byte_buffer_t* pdu);
void rlc_am_write_data_pdu_header(rlc_amd_pdu_header_t* header, uint8_t** payload);
void rlc_am_read_status_pdu(byte_buffer_t* pdu, rlc_status_pdu_t* status);
void rlc_am_read_status_pdu(uint8_t* payload, uint32_t nof_bytes, rlc_status_pdu_t* status);
void rlc_am_write_status_pdu(rlc_status_pdu_t* status, byte_buffer_t* pdu);
int rlc_am_write_status_pdu(rlc_status_pdu_t* status, uint8_t* payload);
uint32_t rlc_am_packed_length(rlc_amd_pdu_header_t* header);
uint32_t rlc_am_packed_length(rlc_status_pdu_t* status);
uint32_t rlc_am_packed_length(rlc_amd_retx_t retx);
bool rlc_am_is_pdu_segment(uint8_t* payload);
bool rlc_am_is_valid_status_pdu(const rlc_status_pdu_t& status, uint32_t rx_win_min = 0);
std::string rlc_am_undelivered_sdu_info_to_string(const std::map<uint32_t, pdcp_pdu_info>& info_queue);
void log_rlc_amd_pdu_header_to_string(srslog::log_channel& log_ch, const rlc_amd_pdu_header_t& header);
bool rlc_am_start_aligned(const uint8_t fi);
bool rlc_am_end_aligned(const uint8_t fi);
bool rlc_am_is_unaligned(const uint8_t fi);
bool rlc_am_not_start_aligned(const uint8_t fi);
/**
* Logs Status PDU into provided log channel, using fmt_str as format string
*/
template <typename... Args>
void log_rlc_am_status_pdu_to_string(srslog::log_channel& log_ch,
const char* fmt_str,
rlc_status_pdu_t* status,
Args&&... args)
{
if (not log_ch.enabled()) {
return;
}
fmt::memory_buffer buffer;
fmt::format_to(buffer, "ACK_SN = {}, N_nack = {}", status->ack_sn, status->N_nack);
if (status->N_nack > 0) {
fmt::format_to(buffer, ", NACK_SN = ");
for (uint32_t i = 0; i < status->N_nack; ++i) {
if (status->nacks[i].has_so) {
fmt::format_to(
buffer, "[{} {}:{}]", status->nacks[i].nack_sn, status->nacks[i].so_start, status->nacks[i].so_end);
} else {
fmt::format_to(buffer, "[{}]", status->nacks[i].nack_sn);
}
}
}
log_ch(fmt_str, std::forward<Args>(args)..., to_c_str(buffer));
}
} // namespace srsran
#endif // SRSRAN_RLC_AM_LTE_PACKING_H

@ -24,6 +24,7 @@
#include "srsran/common/buffer_pool.h"
#include "srsran/common/common.h"
#include "srsran/common/timers.h"
#include "srsran/rlc/rlc_am_base.h"
#include "srsran/upper/byte_buffer_queue.h"
#include <map>
@ -33,41 +34,113 @@
namespace srsran {
typedef struct {
rlc_am_nr_pdu_header_t header;
unique_byte_buffer_t buf;
} rlc_amd_pdu_nr_t;
class rlc_am_nr : public rlc_common
{
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;
///< add class here
rlc_mode_t get_mode() final;
uint32_t get_bearer() final;
/****************************************************************************
* Header pack/unpack helper functions for NR
* Ref: 3GPP TS 38.322 v15.3.0 Section 6.2.2.3
***************************************************************************/
uint32_t rlc_am_nr_read_data_pdu_header(const byte_buffer_t* pdu,
const rlc_am_nr_sn_size_t sn_size,
rlc_am_nr_pdu_header_t* header);
void reestablish() final;
void empty_queue() final;
void set_bsr_callback(bsr_callback_t callback) final;
uint32_t rlc_am_nr_read_data_pdu_header(const uint8_t* payload,
const uint32_t nof_bytes,
const rlc_am_nr_sn_size_t sn_size,
rlc_am_nr_pdu_header_t* header);
// 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;
uint32_t rlc_am_nr_write_data_pdu_header(const rlc_am_nr_pdu_header_t& header, byte_buffer_t* pdu);
// 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;
uint32_t rlc_am_nr_packed_length(const rlc_am_nr_pdu_header_t& header);
rlc_bearer_metrics_t get_metrics() final;
void reset_metrics() final;
uint32_t
rlc_am_nr_read_status_pdu(const byte_buffer_t* pdu, const rlc_am_nr_sn_size_t sn_size, rlc_am_nr_status_pdu_t* status);
private:
// Transmitter sub-class
class rlc_am_nr_tx
{
public:
explicit rlc_am_nr_tx(rlc_am_nr* parent_);
~rlc_am_nr_tx() = default;
uint32_t rlc_am_nr_read_status_pdu(const uint8_t* payload,
const uint32_t nof_bytes,
const rlc_am_nr_sn_size_t sn_size,
rlc_am_nr_status_pdu_t* status);
bool configure(const rlc_am_config_t& cfg_);
void stop();
int32_t rlc_am_nr_write_status_pdu(const rlc_am_nr_status_pdu_t& status_pdu,
const rlc_am_nr_sn_size_t sn_size,
byte_buffer_t* pdu);
int write_sdu(unique_byte_buffer_t sdu);
uint32_t read_pdu(uint8_t* payload, uint32_t nof_bytes);
void discard_sdu(uint32_t discard_sn);
bool sdu_queue_is_full();
bool has_data();
uint32_t get_buffer_state();
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 = {};
};
// 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 = {};
};
} // namespace srsran

@ -0,0 +1,75 @@
/**
*
* \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_AM_NR_PACKING_H
#define SRSRAN_RLC_AM_NR_PACKING_H
#include "srsran/rlc/rlc_am_base.h"
namespace srsran {
///< AM NR PDU header
typedef struct {
rlc_dc_field_t dc; ///< Data/Control (D/C) field
uint8_t p; ///< Polling bit
rlc_nr_si_field_t si; ///< Segmentation info
rlc_am_nr_sn_size_t sn_size; ///< Sequence number size (12 or 18 bits)
uint32_t sn; ///< Sequence number
uint16_t so; ///< Sequence offset
} rlc_am_nr_pdu_header_t;
struct rlc_amd_pdu_nr_t {
rlc_am_nr_pdu_header_t header;
unique_byte_buffer_t buf;
};
///< AM NR Status PDU header (perhaps merge with LTE version)
typedef struct {
rlc_am_nr_control_pdu_type_t cpt;
uint32_t ack_sn; ///< SN of the next not received RLC Data PDU
uint16_t N_nack; ///< number of NACKs
uint8_t nack_range; ///< number of consecutively lost RLC SDUs starting from and including NACK_SN
rlc_status_nack_t nacks[RLC_AM_WINDOW_SIZE];
} rlc_am_nr_status_pdu_t;
/****************************************************************************
* Header pack/unpack helper functions for NR
* Ref: 3GPP TS 38.322 v15.3.0 Section 6.2.2.3
***************************************************************************/
uint32_t rlc_am_nr_read_data_pdu_header(const byte_buffer_t* pdu,
const rlc_am_nr_sn_size_t sn_size,
rlc_am_nr_pdu_header_t* header);
uint32_t rlc_am_nr_read_data_pdu_header(const uint8_t* payload,
const uint32_t nof_bytes,
const rlc_am_nr_sn_size_t sn_size,
rlc_am_nr_pdu_header_t* header);
uint32_t rlc_am_nr_write_data_pdu_header(const rlc_am_nr_pdu_header_t& header, byte_buffer_t* pdu);
uint32_t rlc_am_nr_packed_length(const rlc_am_nr_pdu_header_t& header);
uint32_t
rlc_am_nr_read_status_pdu(const byte_buffer_t* pdu, const rlc_am_nr_sn_size_t sn_size, rlc_am_nr_status_pdu_t* status);
uint32_t rlc_am_nr_read_status_pdu(const uint8_t* payload,
const uint32_t nof_bytes,
const rlc_am_nr_sn_size_t sn_size,
rlc_am_nr_status_pdu_t* status);
int32_t rlc_am_nr_write_status_pdu(const rlc_am_nr_status_pdu_t& status_pdu,
const rlc_am_nr_sn_size_t sn_size,
byte_buffer_t* pdu);
} // namespace srsran
#endif // SRSRAN_RLC_AM_NR_PACKING_H

@ -35,6 +35,7 @@ namespace srsran {
* 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)
@ -184,27 +185,6 @@ struct rlc_status_pdu_t {
}
};
/** RLC AM NR structs */
///< AM NR PDU header
typedef struct {
rlc_dc_field_t dc; ///< Data/Control (D/C) field
uint8_t p; ///< Polling bit
rlc_nr_si_field_t si; ///< Segmentation info
rlc_am_nr_sn_size_t sn_size; ///< Sequence number size (12 or 18 bits)
uint32_t sn; ///< Sequence number
uint16_t so; ///< Sequence offset
} rlc_am_nr_pdu_header_t;
///< AM NR Status PDU header (perhaps merge with LTE version)
typedef struct {
rlc_am_nr_control_pdu_type_t cpt;
uint32_t ack_sn; ///< SN of the next not received RLC Data PDU
uint16_t N_nack; ///< number of NACKs
uint8_t nack_range; ///< number of consecutively lost RLC SDUs starting from and including NACK_SN
rlc_status_nack_t nacks[RLC_AM_WINDOW_SIZE];
} rlc_am_nr_status_pdu_t;
typedef std::function<void(uint32_t, uint32_t, uint32_t)> bsr_callback_t;
/****************************************************************************

@ -0,0 +1,43 @@
/**
*
* \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_RRC_NR_CFG_UTILS_H
#define SRSRAN_RRC_NR_CFG_UTILS_H
#include "srsran/asn1/rrc_nr.h"
#include "srsran/interfaces/gnb_rrc_nr_interfaces.h"
namespace srsran {
struct basic_cell_args_t {
uint32_t cell_id = 0x19B01;
uint32_t nof_prbs = 52;
uint32_t scs = 15;
bool is_standalone = true;
bool is_fdd = true;
std::string plmn = "90170";
uint32_t tac = 7;
};
void generate_default_pdcch_cfg_common(asn1::rrc_nr::pdcch_cfg_common_s& cfg, const basic_cell_args_t& args = {});
void generate_default_init_dl_bwp(asn1::rrc_nr::bwp_dl_common_s& cfg, const basic_cell_args_t& args = {});
void generate_default_dl_cfg_common(asn1::rrc_nr::dl_cfg_common_s& cfg, const basic_cell_args_t& args = {});
void generate_default_mib(const basic_cell_args_t& args, asn1::rrc_nr::mib_s& cfg);
void generate_default_serv_cell_cfg_common_sib(const basic_cell_args_t& args,
asn1::rrc_nr::serving_cell_cfg_common_sib_s& cfg);
void generate_default_sib1(const basic_cell_args_t& args, asn1::rrc_nr::sib1_s& cfg);
} // namespace srsran
#endif // SRSRAN_RRC_NR_CFG_UTILS_H

@ -23,6 +23,7 @@ add_subdirectory(common)
add_subdirectory(mac)
add_subdirectory(phy)
add_subdirectory(radio)
add_subdirectory(rrc)
add_subdirectory(rlc)
add_subdirectory(pdcp)
add_subdirectory(gtpu)

@ -1604,32 +1604,3 @@ bool fill_phy_pdcch_cfg_common(const asn1::rrc_nr::pdcch_cfg_common_s& pdcch_cfg
}
} // namespace srsran
namespace srsenb {
int set_sched_cell_cfg_sib1(srsenb::sched_interface::cell_cfg_t* sched_cfg, const asn1::rrc_nr::sib1_s& sib1)
{
bzero(sched_cfg, sizeof(srsenb::sched_interface::cell_cfg_t));
// set SIB1 and SIB2+ period
sched_cfg->sibs[0].period_rf = 16; // SIB1 is always 16 rf
for (uint32_t i = 0; i < sib1.si_sched_info.sched_info_list.size(); i++) {
sched_cfg->sibs[i + 1].period_rf = sib1.si_sched_info.sched_info_list[i].si_periodicity.to_number();
}
// si-WindowLength
sched_cfg->si_window_ms = sib1.si_sched_info.si_win_len.to_number();
// setup PRACH
if (not sib1.si_sched_info.si_request_cfg.rach_occasions_si_present) {
asn1::log_warning("rach_occasions_si option not present");
return SRSRAN_ERROR;
}
sched_cfg->prach_rar_window = sib1.si_sched_info.si_request_cfg.rach_occasions_si.rach_cfg_si.ra_resp_win.to_number();
sched_cfg->prach_freq_offset = sib1.si_sched_info.si_request_cfg.rach_occasions_si.rach_cfg_si.msg1_freq_start;
sched_cfg->maxharq_msg3tx = sib1.si_sched_info.si_request_cfg.rach_occasions_si.rach_cfg_si.preamb_trans_max;
return SRSRAN_SUCCESS;
}
} // namespace srsenb

@ -95,7 +95,7 @@ void ue_bearer_manager::add_eps_bearer(uint8_t eps_bearer_id, srsran::srsran_rat
logger.info(
"Bearers: Registered EPS bearer ID %d for lcid=%d over %s-PDCP", eps_bearer_id, lcid, to_string(rat).c_str());
} else {
logger.error("Bearers: EPS bearer ID %d already registered", eps_bearer_id);
logger.warning("Bearers: EPS bearer ID %d already registered", eps_bearer_id);
}
}

@ -31,44 +31,38 @@
using namespace backward;
void srsran_debug_handle_crash(int argc, char** argv)
{
backward::SignalHandling sh;
static backward::SignalHandling sh;
}
#else // HAVE_BACKWARD
#include "srsran/common/backtrace.h"
#include "srsran/version.h"
const static char crash_file_name[] = "./srsRAN.backtrace.crash";
static int bt_argc;
static char** bt_argv;
static int bt_argc;
static char** bt_argv;
static void crash_handler(int sig)
{
FILE* f = fopen(crash_file_name, "a");
if (!f) {
printf("srsRAN crashed... we could not save backtrace in '%s'...\n", crash_file_name);
} else {
time_t lnTime;
struct tm stTime;
char strdate[32];
FILE* f = stderr;
time_t lnTime;
struct tm stTime;
char strdate[32];
time(&lnTime);
gmtime_r(&lnTime, &stTime);
time(&lnTime);
gmtime_r(&lnTime, &stTime);
strftime(strdate, sizeof(strdate), "%d/%m/%Y %H:%M:%S", &stTime);
strftime(strdate, sizeof(strdate), "%d/%m/%Y %H:%M:%S", &stTime);
fprintf(f, "--- command='");
for (int i = 0; i < bt_argc; i++) {
fprintf(f, "%s%s", (i == 0) ? "" : " ", bt_argv[i]);
}
fprintf(f, "' version=%s signal=%d date='%s' ---\n", SRSRAN_VERSION_STRING, sig, strdate);
fprintf(f, "--- command='");
for (int i = 0; i < bt_argc; i++) {
fprintf(f, "%s%s", (i == 0) ? "" : " ", bt_argv[i]);
}
fprintf(f, "' version=%s signal=%d date='%s' ---\n", SRSRAN_VERSION_STRING, sig, strdate);
srsran_backtrace_print(f);
fprintf(f, "\n");
srsran_backtrace_print(f);
printf("srsRAN crashed... backtrace saved in '%s'...\n", crash_file_name);
fclose(f);
}
printf("--- exiting ---\n");
fprintf(f, "srsRAN crashed. Please send this backtrace to the developers ...\n");
fprintf(f, "--- exiting ---\n");
exit(1);
}

@ -370,9 +370,9 @@ int srsran_symbol_sz(uint32_t nof_prb)
return 256;
} else if (nof_prb <= 25) {
return 384;
} else if (nof_prb <= 50) {
} else if (nof_prb <= 52) {
return 768;
} else if (nof_prb <= 75) {
} else if (nof_prb <= 79) {
return 1024;
} else if (nof_prb <= 110) {
return 1536;

@ -149,7 +149,8 @@ int srsran_ra_dl_nr_time(const srsran_sch_hl_cfg_nr_t* cfg,
// Determine which PDSCH Time domain RA configuration to apply (TS 38.214 Table 5.1.2.1.1-1)
if (rnti_type == srsran_rnti_type_si && ss_type == srsran_search_space_type_common_0) {
// Row 1
ERROR("Row not implemented");
// Note: Only Default A is supported, which corresponds SS/PBCH block and coreset mux pattern 1
srsran_ra_dl_nr_time_default_A(m, cfg->typeA_pos, grant);
} else if (rnti_type == srsran_rnti_type_si && ss_type == srsran_search_space_type_common_0A) {
// Row 2
ERROR("Row not implemented");

@ -22,10 +22,12 @@ set(SOURCES rlc.cc
rlc_tm.cc
rlc_um_base.cc
rlc_um_lte.cc
rlc_um_nr.cc
rlc_am_base.cc
rlc_am_lte.cc
rlc_um_nr.cc
rlc_am_nr.cc
rlc_am_lte_packing.cc
rlc_am_nr_packing.cc
bearer_mem_pool.cc)
add_library(srsran_rlc STATIC ${SOURCES})

@ -392,7 +392,7 @@ int rlc::add_bearer(uint32_t lcid, const rlc_config_t& cnfg)
rwlock_write_guard lock(rwlock);
if (valid_lcid(lcid)) {
logger.error("LCID %d already exists", lcid);
logger.warning("LCID %d already exists", lcid);
return SRSRAN_ERROR;
}

@ -20,13 +20,12 @@
*/
#include "srsran/rlc/rlc_am_lte.h"
#include "srsran/common/string_helpers.h"
#include "srsran/interfaces/ue_pdcp_interfaces.h"
#include "srsran/interfaces/ue_rrc_interfaces.h"
#include "srsran/rlc/rlc_am_lte_packing.h"
#include "srsran/srslog/event_trace.h"
#include <iostream>
#define MOD 1024
#define RX_MOD_BASE(x) (((x)-vr_r) % 1024)
#define TX_MOD_BASE(x) (((x)-vt_a) % 1024)
#define LCID (parent->lcid)
@ -35,38 +34,6 @@
namespace srsran {
/*******************************
* Helper methods
******************************/
/**
* Logs Status PDU into provided log channel, using fmt_str as format string
*/
template <typename... Args>
void log_rlc_am_status_pdu_to_string(srslog::log_channel& log_ch,
const char* fmt_str,
rlc_status_pdu_t* status,
Args&&... args)
{
if (not log_ch.enabled()) {
return;
}
fmt::memory_buffer buffer;
fmt::format_to(buffer, "ACK_SN = {}, N_nack = {}", status->ack_sn, status->N_nack);
if (status->N_nack > 0) {
fmt::format_to(buffer, ", NACK_SN = ");
for (uint32_t i = 0; i < status->N_nack; ++i) {
if (status->nacks[i].has_so) {
fmt::format_to(
buffer, "[{} {}:{}]", status->nacks[i].nack_sn, status->nacks[i].so_start, status->nacks[i].so_end);
} else {
fmt::format_to(buffer, "[{}]", status->nacks[i].nack_sn);
}
}
}
log_ch(fmt_str, std::forward<Args>(args)..., to_c_str(buffer));
}
/*******************************
* RLC AM Segments
******************************/
@ -2269,338 +2236,4 @@ void rlc_am_lte::rlc_am_lte_rx::debug_state()
logger.debug("%s vr_r = %d, vr_mr = %d, vr_x = %d, vr_ms = %d, vr_h = %d", RB_NAME, vr_r, vr_mr, vr_x, vr_ms, vr_h);
}
buffered_pdcp_pdu_list::buffered_pdcp_pdu_list() : buffered_pdus(buffered_pdcp_pdu_list::buffer_size)
{
clear();
}
void buffered_pdcp_pdu_list::clear()
{
count = 0;
for (pdcp_pdu_info& b : buffered_pdus) {
b.clear();
}
}
/****************************************************************************
* Header pack/unpack helper functions
* Ref: 3GPP TS 36.322 v10.0.0 Section 6.2.1
***************************************************************************/
// Read header from pdu struct, don't strip header
void rlc_am_read_data_pdu_header(byte_buffer_t* pdu, rlc_amd_pdu_header_t* header)
{
uint8_t* ptr = pdu->msg;
uint32_t n = 0;
rlc_am_read_data_pdu_header(&ptr, &n, header);
}
// Read header from raw pointer, strip header
void rlc_am_read_data_pdu_header(uint8_t** payload, uint32_t* nof_bytes, rlc_amd_pdu_header_t* header)
{
uint8_t ext;
uint8_t* ptr = *payload;
header->dc = static_cast<rlc_dc_field_t>((*ptr >> 7) & 0x01);
if (RLC_DC_FIELD_DATA_PDU == header->dc) {
// Fixed part
header->rf = ((*ptr >> 6) & 0x01);
header->p = ((*ptr >> 5) & 0x01);
header->fi = static_cast<rlc_fi_field_t>((*ptr >> 3) & 0x03);
ext = ((*ptr >> 2) & 0x01);
header->sn = (*ptr & 0x03) << 8; // 2 bits SN
ptr++;
header->sn |= (*ptr & 0xFF); // 8 bits SN
ptr++;
if (header->rf) {
header->lsf = ((*ptr >> 7) & 0x01);
header->so = (*ptr & 0x7F) << 8; // 7 bits of SO
ptr++;
header->so |= (*ptr & 0xFF); // 8 bits of SO
ptr++;
}
// Extension part
header->N_li = 0;
while (ext) {
if (header->N_li % 2 == 0) {
ext = ((*ptr >> 7) & 0x01);
header->li[header->N_li] = (*ptr & 0x7F) << 4; // 7 bits of LI
ptr++;
header->li[header->N_li] |= (*ptr & 0xF0) >> 4; // 4 bits of LI
header->N_li++;
} else {
ext = (*ptr >> 3) & 0x01;
header->li[header->N_li] = (*ptr & 0x07) << 8; // 3 bits of LI
ptr++;
header->li[header->N_li] |= (*ptr & 0xFF); // 8 bits of LI
header->N_li++;
ptr++;
}
}
// Account for padding if N_li is odd
if (header->N_li % 2 == 1) {
ptr++;
}
*nof_bytes -= ptr - *payload;
*payload = ptr;
}
}
// Write header to pdu struct
void rlc_am_write_data_pdu_header(rlc_amd_pdu_header_t* header, byte_buffer_t* pdu)
{
uint8_t* ptr = pdu->msg;
rlc_am_write_data_pdu_header(header, &ptr);
pdu->N_bytes += ptr - pdu->msg;
}
// Write header to pointer & move pointer
void rlc_am_write_data_pdu_header(rlc_amd_pdu_header_t* header, uint8_t** payload)
{
uint32_t i;
uint8_t ext = (header->N_li > 0) ? 1 : 0;
uint8_t* ptr = *payload;
// Fixed part
*ptr = (header->dc & 0x01) << 7;
*ptr |= (header->rf & 0x01) << 6;
*ptr |= (header->p & 0x01) << 5;
*ptr |= (header->fi & 0x03) << 3;
*ptr |= (ext & 0x01) << 2;
*ptr |= (header->sn & 0x300) >> 8; // 2 bits SN
ptr++;
*ptr = (header->sn & 0xFF); // 8 bits SN
ptr++;
// Segment part
if (header->rf) {
*ptr = (header->lsf & 0x01) << 7;
*ptr |= (header->so & 0x7F00) >> 8; // 7 bits of SO
ptr++;
*ptr = (header->so & 0x00FF); // 8 bits of SO
ptr++;
}
// Extension part
i = 0;
while (i < header->N_li) {
ext = ((i + 1) == header->N_li) ? 0 : 1;
*ptr = (ext & 0x01) << 7; // 1 bit header
*ptr |= (header->li[i] & 0x7F0) >> 4; // 7 bits of LI
ptr++;
*ptr = (header->li[i] & 0x00F) << 4; // 4 bits of LI
i++;
if (i < header->N_li) {
ext = ((i + 1) == header->N_li) ? 0 : 1;
*ptr |= (ext & 0x01) << 3; // 1 bit header
*ptr |= (header->li[i] & 0x700) >> 8; // 3 bits of LI
ptr++;
*ptr = (header->li[i] & 0x0FF); // 8 bits of LI
ptr++;
i++;
}
}
// Pad if N_li is odd
if (header->N_li % 2 == 1) {
ptr++;
}
*payload = ptr;
}
void rlc_am_read_status_pdu(byte_buffer_t* pdu, rlc_status_pdu_t* status)
{
rlc_am_read_status_pdu(pdu->msg, pdu->N_bytes, status);
}
void rlc_am_read_status_pdu(uint8_t* payload, uint32_t nof_bytes, rlc_status_pdu_t* status)
{
uint32_t i;
uint8_t ext1, ext2;
bit_buffer_t tmp;
uint8_t* ptr = tmp.msg;
srsran_bit_unpack_vector(payload, tmp.msg, nof_bytes * 8);
tmp.N_bits = nof_bytes * 8;
rlc_dc_field_t dc = static_cast<rlc_dc_field_t>(srsran_bit_pack(&ptr, 1));
if (RLC_DC_FIELD_CONTROL_PDU == dc) {
uint8_t cpt = srsran_bit_pack(&ptr, 3); // 3-bit Control PDU Type (0 == status)
if (0 == cpt) {
status->ack_sn = srsran_bit_pack(&ptr, 10); // 10 bits ACK_SN
ext1 = srsran_bit_pack(&ptr, 1); // 1 bits E1
status->N_nack = 0;
while (ext1) {
status->nacks[status->N_nack].nack_sn = srsran_bit_pack(&ptr, 10);
ext1 = srsran_bit_pack(&ptr, 1); // 1 bits E1
ext2 = srsran_bit_pack(&ptr, 1); // 1 bits E2
if (ext2) {
status->nacks[status->N_nack].has_so = true;
status->nacks[status->N_nack].so_start = srsran_bit_pack(&ptr, 15);
status->nacks[status->N_nack].so_end = srsran_bit_pack(&ptr, 15);
}
status->N_nack++;
}
}
}
}
void rlc_am_write_status_pdu(rlc_status_pdu_t* status, byte_buffer_t* pdu)
{
pdu->N_bytes = rlc_am_write_status_pdu(status, pdu->msg);
}
int rlc_am_write_status_pdu(rlc_status_pdu_t* status, uint8_t* payload)
{
uint32_t i;
uint8_t ext1;
bit_buffer_t tmp;
uint8_t* ptr = tmp.msg;
srsran_bit_unpack(RLC_DC_FIELD_CONTROL_PDU, &ptr, 1); // D/C
srsran_bit_unpack(0, &ptr, 3); // CPT (0 == STATUS)
srsran_bit_unpack(status->ack_sn, &ptr, 10); // 10 bit ACK_SN
ext1 = (status->N_nack == 0) ? 0 : 1;
srsran_bit_unpack(ext1, &ptr, 1); // E1
for (i = 0; i < status->N_nack; i++) {
srsran_bit_unpack(status->nacks[i].nack_sn, &ptr, 10); // 10 bit NACK_SN
ext1 = ((status->N_nack - 1) == i) ? 0 : 1;
srsran_bit_unpack(ext1, &ptr, 1); // E1
if (status->nacks[i].has_so) {
srsran_bit_unpack(1, &ptr, 1); // E2
srsran_bit_unpack(status->nacks[i].so_start, &ptr, 15);
srsran_bit_unpack(status->nacks[i].so_end, &ptr, 15);
} else {
srsran_bit_unpack(0, &ptr, 1); // E2
}
}
// Pad
tmp.N_bits = ptr - tmp.msg;
uint8_t n_pad = 8 - (tmp.N_bits % 8);
srsran_bit_unpack(0, &ptr, n_pad);
tmp.N_bits = ptr - tmp.msg;
// Pack bits
srsran_bit_pack_vector(tmp.msg, payload, tmp.N_bits);
return tmp.N_bits / 8;
}
bool rlc_am_is_valid_status_pdu(const rlc_status_pdu_t& status, uint32_t rx_win_min)
{
// check if ACK_SN is inside Rx window
if ((MOD + status.ack_sn - rx_win_min) % MOD > RLC_AM_WINDOW_SIZE) {
return false;
}
for (uint32_t i = 0; i < status.N_nack; ++i) {
// NACK can't be larger than ACK
if ((MOD + status.ack_sn - status.nacks[i].nack_sn) % MOD > RLC_AM_WINDOW_SIZE) {
return false;
}
// Don't NACK the ACK SN
if (status.nacks[i].nack_sn == status.ack_sn) {
return false;
}
}
return true;
}
uint32_t rlc_am_packed_length(rlc_amd_pdu_header_t* header)
{
uint32_t len = 2; // Fixed part is 2 bytes
if (header->rf) {
len += 2; // Segment header is 2 bytes
}
len += header->N_li * 1.5 + 0.5; // Extension part - integer rounding up
return len;
}
uint32_t rlc_am_packed_length(rlc_status_pdu_t* status)
{
uint32_t len_bits = 15; // Fixed part is 15 bits
for (uint32_t i = 0; i < status->N_nack; i++) {
if (status->nacks[i].has_so) {
len_bits += 42; // 10 bits SN, 2 bits ext, 15 bits so_start, 15 bits so_end
} else {
len_bits += 12; // 10 bits SN, 2 bits ext
}
}
return (len_bits + 7) / 8; // Convert to bytes - integer rounding up
}
bool rlc_am_is_pdu_segment(uint8_t* payload)
{
return ((*(payload) >> 6) & 0x01) == 1;
}
void rlc_am_undelivered_sdu_info_to_string(fmt::memory_buffer& buffer, const std::vector<pdcp_pdu_info>& info_queue)
{
fmt::format_to(buffer, "\n");
for (const auto& pdcp_pdu : info_queue) {
fmt::format_to(buffer, "\tPDCP_SN = {}, undelivered RLC SNs = [", pdcp_pdu.sn);
for (const auto& nacked_segment : pdcp_pdu) {
fmt::format_to(buffer, "{} ", nacked_segment.rlc_sn());
}
fmt::format_to(buffer, "]\n");
}
}
void log_rlc_amd_pdu_header_to_string(srslog::log_channel& log_ch, const rlc_amd_pdu_header_t& header)
{
if (not log_ch.enabled()) {
return;
}
fmt::memory_buffer buffer;
fmt::format_to(buffer,
"[{}, RF={}, P={}, FI={}, SN={}, LSF={}, SO={}, N_li={}",
rlc_dc_field_text[header.dc],
(header.rf ? "1" : "0"),
(header.p ? "1" : "0"),
(header.fi ? "1" : "0"),
header.sn,
(header.lsf ? "1" : "0"),
header.so,
header.N_li);
if (header.N_li > 0) {
fmt::format_to(buffer, " ({}", header.li[0]);
for (uint32_t i = 1; i < header.N_li; ++i) {
fmt::format_to(buffer, ", {}", header.li[i]);
}
fmt::format_to(buffer, ")");
}
fmt::format_to(buffer, "]");
log_ch("%s", to_c_str(buffer));
}
bool rlc_am_start_aligned(const uint8_t fi)
{
return (fi == RLC_FI_FIELD_START_AND_END_ALIGNED || fi == RLC_FI_FIELD_NOT_END_ALIGNED);
}
bool rlc_am_end_aligned(const uint8_t fi)
{
return (fi == RLC_FI_FIELD_START_AND_END_ALIGNED || fi == RLC_FI_FIELD_NOT_START_ALIGNED);
}
bool rlc_am_is_unaligned(const uint8_t fi)
{
return (fi == RLC_FI_FIELD_NOT_START_OR_END_ALIGNED);
}
bool rlc_am_not_start_aligned(const uint8_t fi)
{
return (fi == RLC_FI_FIELD_NOT_START_ALIGNED || fi == RLC_FI_FIELD_NOT_START_OR_END_ALIGNED);
}
} // namespace srsran

@ -0,0 +1,339 @@
/**
*
* \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.
*
*/
#include "srsran/rlc/rlc_am_lte_packing.h"
#include <sstream>
namespace srsran {
/****************************************************************************
* Header pack/unpack helper functions
* Ref: 3GPP TS 36.322 v10.0.0 Section 6.2.1
***************************************************************************/
// Read header from pdu struct, don't strip header
void rlc_am_read_data_pdu_header(byte_buffer_t* pdu, rlc_amd_pdu_header_t* header)
{
uint8_t* ptr = pdu->msg;
uint32_t n = 0;
rlc_am_read_data_pdu_header(&ptr, &n, header);
}
// Read header from raw pointer, strip header
void rlc_am_read_data_pdu_header(uint8_t** payload, uint32_t* nof_bytes, rlc_amd_pdu_header_t* header)
{
uint8_t ext;
uint8_t* ptr = *payload;
header->dc = static_cast<rlc_dc_field_t>((*ptr >> 7) & 0x01);
if (RLC_DC_FIELD_DATA_PDU == header->dc) {
// Fixed part
header->rf = ((*ptr >> 6) & 0x01);
header->p = ((*ptr >> 5) & 0x01);
header->fi = static_cast<rlc_fi_field_t>((*ptr >> 3) & 0x03);
ext = ((*ptr >> 2) & 0x01);
header->sn = (*ptr & 0x03) << 8; // 2 bits SN
ptr++;
header->sn |= (*ptr & 0xFF); // 8 bits SN
ptr++;
if (header->rf) {
header->lsf = ((*ptr >> 7) & 0x01);
header->so = (*ptr & 0x7F) << 8; // 7 bits of SO
ptr++;
header->so |= (*ptr & 0xFF); // 8 bits of SO
ptr++;
}
// Extension part
header->N_li = 0;
while (ext) {
if (header->N_li % 2 == 0) {
ext = ((*ptr >> 7) & 0x01);
header->li[header->N_li] = (*ptr & 0x7F) << 4; // 7 bits of LI
ptr++;
header->li[header->N_li] |= (*ptr & 0xF0) >> 4; // 4 bits of LI
header->N_li++;
} else {
ext = (*ptr >> 3) & 0x01;
header->li[header->N_li] = (*ptr & 0x07) << 8; // 3 bits of LI
ptr++;
header->li[header->N_li] |= (*ptr & 0xFF); // 8 bits of LI
header->N_li++;
ptr++;
}
}
// Account for padding if N_li is odd
if (header->N_li % 2 == 1) {
ptr++;
}
*nof_bytes -= ptr - *payload;
*payload = ptr;
}
}
// Write header to pdu struct
void rlc_am_write_data_pdu_header(rlc_amd_pdu_header_t* header, byte_buffer_t* pdu)
{
uint8_t* ptr = pdu->msg;
rlc_am_write_data_pdu_header(header, &ptr);
pdu->N_bytes += ptr - pdu->msg;
}
// Write header to pointer & move pointer
void rlc_am_write_data_pdu_header(rlc_amd_pdu_header_t* header, uint8_t** payload)
{
uint32_t i;
uint8_t ext = (header->N_li > 0) ? 1 : 0;
uint8_t* ptr = *payload;
// Fixed part
*ptr = (header->dc & 0x01) << 7;
*ptr |= (header->rf & 0x01) << 6;
*ptr |= (header->p & 0x01) << 5;
*ptr |= (header->fi & 0x03) << 3;
*ptr |= (ext & 0x01) << 2;
*ptr |= (header->sn & 0x300) >> 8; // 2 bits SN
ptr++;
*ptr = (header->sn & 0xFF); // 8 bits SN
ptr++;
// Segment part
if (header->rf) {
*ptr = (header->lsf & 0x01) << 7;
*ptr |= (header->so & 0x7F00) >> 8; // 7 bits of SO
ptr++;
*ptr = (header->so & 0x00FF); // 8 bits of SO
ptr++;
}
// Extension part
i = 0;
while (i < header->N_li) {
ext = ((i + 1) == header->N_li) ? 0 : 1;
*ptr = (ext & 0x01) << 7; // 1 bit header
*ptr |= (header->li[i] & 0x7F0) >> 4; // 7 bits of LI
ptr++;
*ptr = (header->li[i] & 0x00F) << 4; // 4 bits of LI
i++;
if (i < header->N_li) {
ext = ((i + 1) == header->N_li) ? 0 : 1;
*ptr |= (ext & 0x01) << 3; // 1 bit header
*ptr |= (header->li[i] & 0x700) >> 8; // 3 bits of LI
ptr++;
*ptr = (header->li[i] & 0x0FF); // 8 bits of LI
ptr++;
i++;
}
}
// Pad if N_li is odd
if (header->N_li % 2 == 1) {
ptr++;
}
*payload = ptr;
}
void rlc_am_read_status_pdu(byte_buffer_t* pdu, rlc_status_pdu_t* status)
{
rlc_am_read_status_pdu(pdu->msg, pdu->N_bytes, status);
}
void rlc_am_read_status_pdu(uint8_t* payload, uint32_t nof_bytes, rlc_status_pdu_t* status)
{
uint32_t i;
uint8_t ext1, ext2;
bit_buffer_t tmp;
uint8_t* ptr = tmp.msg;
srsran_bit_unpack_vector(payload, tmp.msg, nof_bytes * 8);
tmp.N_bits = nof_bytes * 8;
rlc_dc_field_t dc = static_cast<rlc_dc_field_t>(srsran_bit_pack(&ptr, 1));
if (RLC_DC_FIELD_CONTROL_PDU == dc) {
uint8_t cpt = srsran_bit_pack(&ptr, 3); // 3-bit Control PDU Type (0 == status)
if (0 == cpt) {
status->ack_sn = srsran_bit_pack(&ptr, 10); // 10 bits ACK_SN
ext1 = srsran_bit_pack(&ptr, 1); // 1 bits E1
status->N_nack = 0;
while (ext1) {
status->nacks[status->N_nack].nack_sn = srsran_bit_pack(&ptr, 10);
ext1 = srsran_bit_pack(&ptr, 1); // 1 bits E1
ext2 = srsran_bit_pack(&ptr, 1); // 1 bits E2
if (ext2) {
status->nacks[status->N_nack].has_so = true;
status->nacks[status->N_nack].so_start = srsran_bit_pack(&ptr, 15);
status->nacks[status->N_nack].so_end = srsran_bit_pack(&ptr, 15);
}
status->N_nack++;
}
}
}
}
void rlc_am_write_status_pdu(rlc_status_pdu_t* status, byte_buffer_t* pdu)
{
pdu->N_bytes = rlc_am_write_status_pdu(status, pdu->msg);
}
int rlc_am_write_status_pdu(rlc_status_pdu_t* status, uint8_t* payload)
{
uint32_t i;
uint8_t ext1;
bit_buffer_t tmp;
uint8_t* ptr = tmp.msg;
srsran_bit_unpack(RLC_DC_FIELD_CONTROL_PDU, &ptr, 1); // D/C
srsran_bit_unpack(0, &ptr, 3); // CPT (0 == STATUS)
srsran_bit_unpack(status->ack_sn, &ptr, 10); // 10 bit ACK_SN
ext1 = (status->N_nack == 0) ? 0 : 1;
srsran_bit_unpack(ext1, &ptr, 1); // E1
for (i = 0; i < status->N_nack; i++) {
srsran_bit_unpack(status->nacks[i].nack_sn, &ptr, 10); // 10 bit NACK_SN
ext1 = ((status->N_nack - 1) == i) ? 0 : 1;
srsran_bit_unpack(ext1, &ptr, 1); // E1
if (status->nacks[i].has_so) {
srsran_bit_unpack(1, &ptr, 1); // E2
srsran_bit_unpack(status->nacks[i].so_start, &ptr, 15);
srsran_bit_unpack(status->nacks[i].so_end, &ptr, 15);
} else {
srsran_bit_unpack(0, &ptr, 1); // E2
}
}
// Pad
tmp.N_bits = ptr - tmp.msg;
uint8_t n_pad = 8 - (tmp.N_bits % 8);
srsran_bit_unpack(0, &ptr, n_pad);
tmp.N_bits = ptr - tmp.msg;
// Pack bits
srsran_bit_pack_vector(tmp.msg, payload, tmp.N_bits);
return tmp.N_bits / 8;
}
uint32_t rlc_am_packed_length(rlc_amd_pdu_header_t* header)
{
uint32_t len = 2; // Fixed part is 2 bytes
if (header->rf) {
len += 2; // Segment header is 2 bytes
}
len += header->N_li * 1.5 + 0.5; // Extension part - integer rounding up
return len;
}
uint32_t rlc_am_packed_length(rlc_status_pdu_t* status)
{
uint32_t len_bits = 15; // Fixed part is 15 bits
for (uint32_t i = 0; i < status->N_nack; i++) {
if (status->nacks[i].has_so) {
len_bits += 42; // 10 bits SN, 2 bits ext, 15 bits so_start, 15 bits so_end
} else {
len_bits += 12; // 10 bits SN, 2 bits ext
}
}
return (len_bits + 7) / 8; // Convert to bytes - integer rounding up
}
bool rlc_am_is_pdu_segment(uint8_t* payload)
{
return ((*(payload) >> 6) & 0x01) == 1;
}
bool rlc_am_is_valid_status_pdu(const rlc_status_pdu_t& status, uint32_t rx_win_min)
{
// check if ACK_SN is inside Rx window
if ((MOD + status.ack_sn - rx_win_min) % MOD > RLC_AM_WINDOW_SIZE) {
return false;
}
for (uint32_t i = 0; i < status.N_nack; ++i) {
// NACK can't be larger than ACK
if ((MOD + status.ack_sn - status.nacks[i].nack_sn) % MOD > RLC_AM_WINDOW_SIZE) {
return false;
}
// Don't NACK the ACK SN
if (status.nacks[i].nack_sn == status.ack_sn) {
return false;
}
}
return true;
}
void rlc_am_undelivered_sdu_info_to_string(fmt::memory_buffer& buffer, const std::vector<pdcp_pdu_info>& info_queue)
{
fmt::format_to(buffer, "\n");
for (const auto& pdcp_pdu : info_queue) {
fmt::format_to(buffer, "\tPDCP_SN = {}, undelivered RLC SNs = [", pdcp_pdu.sn);
for (const auto& nacked_segment : pdcp_pdu) {
fmt::format_to(buffer, "{} ", nacked_segment.rlc_sn());
}
fmt::format_to(buffer, "]\n");
}
}
bool rlc_am_start_aligned(const uint8_t fi)
{
return (fi == RLC_FI_FIELD_START_AND_END_ALIGNED || fi == RLC_FI_FIELD_NOT_END_ALIGNED);
}
bool rlc_am_end_aligned(const uint8_t fi)
{
return (fi == RLC_FI_FIELD_START_AND_END_ALIGNED || fi == RLC_FI_FIELD_NOT_START_ALIGNED);
}
bool rlc_am_is_unaligned(const uint8_t fi)
{
return (fi == RLC_FI_FIELD_NOT_START_OR_END_ALIGNED);
}
bool rlc_am_not_start_aligned(const uint8_t fi)
{
return (fi == RLC_FI_FIELD_NOT_START_ALIGNED || fi == RLC_FI_FIELD_NOT_START_OR_END_ALIGNED);
}
void log_rlc_amd_pdu_header_to_string(srslog::log_channel& log_ch, const rlc_amd_pdu_header_t& header)
{
if (not log_ch.enabled()) {
return;
}
fmt::memory_buffer buffer;
fmt::format_to(buffer,
"[{}, RF={}, P={}, FI={}, SN={}, LSF={}, SO={}, N_li={}",
rlc_dc_field_text[header.dc],
(header.rf ? "1" : "0"),
(header.p ? "1" : "0"),
(header.fi ? "1" : "0"),
header.sn,
(header.lsf ? "1" : "0"),
header.so,
header.N_li);
if (header.N_li > 0) {
fmt::format_to(buffer, " ({}", header.li[0]);
for (uint32_t i = 1; i < header.N_li; ++i) {
fmt::format_to(buffer, ", {}", header.li[i]);
}
fmt::format_to(buffer, ")");
}
fmt::format_to(buffer, "]");
log_ch("%s", to_c_str(buffer));
}
} // namespace srsran

@ -20,240 +20,206 @@
*/
#include "srsran/rlc/rlc_am_nr.h"
#include <sstream>
#include "srsran/common/string_helpers.h"
#include "srsran/interfaces/ue_pdcp_interfaces.h"
#include "srsran/interfaces/ue_rrc_interfaces.h"
#include "srsran/srslog/event_trace.h"
#include <iostream>
namespace srsran {
/****************************************************************************
* Header pack/unpack helper functions
* Ref: 3GPP TS 38.322 v15.3.0 Section 6.2.2.4
***************************************************************************/
uint32_t rlc_am_nr_read_data_pdu_header(const byte_buffer_t* pdu,
const rlc_am_nr_sn_size_t sn_size,
rlc_am_nr_pdu_header_t* header)
/*******************************
* 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_)
{
return rlc_am_nr_read_data_pdu_header(pdu->msg, pdu->N_bytes, sn_size, header);
}
// determine bearer name and configure Rx/Tx objects
rb_name = rrc->get_rb_name(lcid);
uint32_t rlc_am_nr_read_data_pdu_header(const uint8_t* payload,
const uint32_t nof_bytes,
const rlc_am_nr_sn_size_t sn_size,
rlc_am_nr_pdu_header_t* header)
{
uint8_t* ptr = const_cast<uint8_t*>(payload);
header->sn_size = sn_size;
// Fixed part
header->dc = (rlc_dc_field_t)((*ptr >> 7) & 0x01); // 1 bit D/C field
header->p = (*ptr >> 6) & 0x01; // 1 bit P flag
header->si = (rlc_nr_si_field_t)((*ptr >> 4) & 0x03); // 2 bits SI
if (sn_size == rlc_am_nr_sn_size_t::size12bits) {
header->sn = (*ptr & 0x0F) << 8; // first 4 bits SN
ptr++;
header->sn |= (*ptr & 0xFF); // last 8 bits SN
ptr++;
} else if (sn_size == rlc_am_nr_sn_size_t::size18bits) {
// sanity check
if ((*ptr & 0x0c) != 0) {
fprintf(stderr, "Malformed PDU, reserved bits are set.\n");
return 0;
}
header->sn = (*ptr & 0x03) << 16; // first 4 bits SN
ptr++;
header->sn |= (*ptr & 0xFF) << 8; // bit 2-10 of SN
ptr++;
header->sn |= (*ptr & 0xFF); // last 8 bits SN
ptr++;
} else {
fprintf(stderr, "Unsupported SN length\n");
return 0;
// store config
cfg = cfg_;
if (not rx.configure(cfg.am)) {
logger.error("Error configuring bearer (RX)");
return false;
}
// 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++;
if (not tx.configure(cfg.am)) {
logger.error("Error configuring bearer (TX)");
return false;
}
// return consumed bytes
return (ptr - payload);
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;
}
uint32_t rlc_am_nr_packed_length(const rlc_am_nr_pdu_header_t& header)
void rlc_am_nr::stop() {}
rlc_mode_t rlc_am_nr::get_mode()
{
uint32_t len = 0;
if (header.si == rlc_nr_si_field_t::full_sdu || header.si == rlc_nr_si_field_t::first_segment) {
len = 2;
if (header.sn_size == rlc_am_nr_sn_size_t::size18bits) {
len++;
}
} else {
// PDU contains SO
len = 4;
if (header.sn_size == rlc_am_nr_sn_size_t::size18bits) {
len++;
}
}
return len;
return rlc_mode_t::am;
}
uint32_t rlc_am_nr_write_data_pdu_header(const rlc_am_nr_pdu_header_t& header, byte_buffer_t* pdu)
uint32_t rlc_am_nr::get_bearer()
{
// Make room for the header
uint32_t len = rlc_am_nr_packed_length(header);
pdu->msg -= len;
uint8_t* ptr = pdu->msg;
// fixed header part
*ptr = (header.dc & 0x01) << 7; ///< 1 bit D/C field
*ptr |= (header.p & 0x01) << 6; ///< 1 bit P flag
*ptr |= (header.si & 0x03) << 4; ///< 2 bits SI
if (header.sn_size == rlc_am_nr_sn_size_t::size12bits) {
// write first 4 bit of SN
*ptr |= (header.sn >> 8) & 0x0f; // 4 bit SN
ptr++;
*ptr = header.sn & 0xff; // remaining 8 bit of SN
ptr++;
} else {
// 18bit SN
*ptr |= (header.sn >> 16) & 0x3; // 2 bit SN
ptr++;
*ptr = header.sn >> 8; // bit 3 - 10 of SN
ptr++;
*ptr = (header.sn & 0xff); // remaining 8 bit of SN
ptr++;
}
return 0;
}
if (header.so) {
// write SO
*ptr = header.so >> 8; // first part of SO
ptr++;
*ptr = (header.so & 0xff); // second part of SO
ptr++;
}
void rlc_am_nr::reestablish() {}
void rlc_am_nr::empty_queue() {}
pdu->N_bytes += ptr - pdu->msg;
void rlc_am_nr::set_bsr_callback(bsr_callback_t callback) {}
return len;
rlc_bearer_metrics_t rlc_am_nr::get_metrics()
{
return {};
}
uint32_t
rlc_am_nr_read_status_pdu(const byte_buffer_t* pdu, const rlc_am_nr_sn_size_t sn_size, rlc_am_nr_status_pdu_t* status)
void rlc_am_nr::reset_metrics() {}
/****************************************************************************
* PDCP interface
***************************************************************************/
void rlc_am_nr::write_sdu(unique_byte_buffer_t sdu)
{
return rlc_am_nr_read_status_pdu(pdu->msg, pdu->N_bytes, sn_size, status);
if (tx.write_sdu(std::move(sdu)) == SRSRAN_SUCCESS) {
metrics.num_tx_sdus++;
}
}
uint32_t rlc_am_nr_read_status_pdu(const uint8_t* payload,
const uint32_t nof_bytes,
const rlc_am_nr_sn_size_t sn_size,
rlc_am_nr_status_pdu_t* status)
void rlc_am_nr::discard_sdu(uint32_t pdcp_sn)
{
uint8_t* ptr = const_cast<uint8_t*>(payload);
tx.discard_sdu(pdcp_sn);
metrics.num_lost_sdus++;
}
// fixed part
status->cpt = (rlc_am_nr_control_pdu_type_t)((*ptr >> 4) & 0x07); // 3 bits CPT
bool rlc_am_nr::sdu_queue_is_full()
{
return tx.sdu_queue_is_full();
}
// sanity check
if (status->cpt != rlc_am_nr_control_pdu_type_t::status_pdu) {
fprintf(stderr, "Malformed PDU, reserved bits are set.\n");
return 0;
}
/****************************************************************************
* 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;
}
if (sn_size == rlc_am_nr_sn_size_t::size12bits) {
status->ack_sn = (*ptr & 0x0F) << 8; // first 4 bits SN
ptr++;
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;
}
status->ack_sn |= (*ptr & 0xFF); // last 8 bits SN
ptr++;
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;
}
// read E1 flag
uint8_t e1 = *ptr & 0x80;
/****************************************************************************
* Tx subclass implementation
***************************************************************************/
rlc_am_nr::rlc_am_nr_tx::rlc_am_nr_tx(rlc_am_nr* parent_) :
parent(parent_), logger(parent_->logger), pool(byte_buffer_pool::get_instance())
{}
// sanity check for reserved bits
if ((*ptr & 0x7f) != 0) {
fprintf(stderr, "Malformed PDU, reserved bits are set.\n");
return 0;
bool rlc_am_nr::rlc_am_nr_tx::configure(const rlc_am_config_t& cfg_)
{
/*
if (cfg_.tx_queue_length > MAX_SDUS_PER_RLC_PDU) {
logger.error("Configuring Tx queue length of %d PDUs too big. Maximum value is %d.",
cfg_.tx_queue_length,
MAX_SDUS_PER_RLC_PDU);
return false;
}
*/
cfg = cfg_;
// all good, continue with next byte depending on E1
ptr++;
return true;
}
// reset number of acks
status->N_nack = 0;
int rlc_am_nr::rlc_am_nr_tx::write_sdu(unique_byte_buffer_t sdu)
{
return 0;
}
if (e1) {
// E1 flag set, read a NACK_SN
rlc_status_nack_t nack = {};
nack.nack_sn = (*ptr & 0xff) << 4;
ptr++;
// uint8_t len2 = (*ptr & 0xF0) >> 4;
nack.nack_sn |= (*ptr & 0xF0) >> 4;
status->nacks[status->N_nack] = nack;
void rlc_am_nr::rlc_am_nr_tx::discard_sdu(uint32_t sn)
{
return;
}
status->N_nack++;
}
}
bool rlc_am_nr::rlc_am_nr_tx::sdu_queue_is_full()
{
return false;
}
return SRSRAN_SUCCESS;
bool rlc_am_nr::rlc_am_nr_tx::has_data()
{
return true;
}
/**
* Write a RLC AM NR status PDU to a PDU buffer and eets the length of the generate PDU accordingly
* @param status_pdu The status PDU
* @param pdu A pointer to a unique bytebuffer
* @return SRSRAN_SUCCESS if PDU was written, SRSRAN_ERROR otherwise
*/
int32_t rlc_am_nr_write_status_pdu(const rlc_am_nr_status_pdu_t& status_pdu,
const rlc_am_nr_sn_size_t sn_size,
byte_buffer_t* pdu)
uint32_t rlc_am_nr::rlc_am_nr_tx::read_pdu(uint8_t* payload, uint32_t nof_bytes)
{
uint8_t* ptr = pdu->msg;
// fixed header part
*ptr = 0; ///< 1 bit D/C field and 3bit CPT are all zero
if (sn_size == rlc_am_nr_sn_size_t::size12bits) {
// write first 4 bit of ACK_SN
*ptr |= (status_pdu.ack_sn >> 8) & 0x0f; // 4 bit ACK_SN
ptr++;
*ptr = status_pdu.ack_sn & 0xff; // remaining 8 bit of SN
ptr++;
// write E1 flag in octet 3
*ptr = (status_pdu.N_nack > 0) ? 0x80 : 0x00;
ptr++;
if (status_pdu.N_nack > 0) {
// write first 8 bit of NACK_SN
*ptr = (status_pdu.nacks[0].nack_sn >> 4) & 0xff;
ptr++;
// write remaining 4 bits of NACK_SN
*ptr = status_pdu.nacks[0].nack_sn & 0xf0;
ptr++;
}
} else {
// 18bit SN
*ptr |= (status_pdu.ack_sn >> 14) & 0x0f; // 4 bit ACK_SN
ptr++;
*ptr = status_pdu.ack_sn >> 8; // bit 3 - 10 of SN
ptr++;
*ptr = (status_pdu.ack_sn & 0xff); // remaining 6 bit of SN
ptr++;
}
return 0;
}
uint32_t rlc_am_nr::rlc_am_nr_tx::get_buffer_state()
{
return 0;
}
/****************************************************************************
* Rx subclass implementation
***************************************************************************/
rlc_am_nr::rlc_am_nr_rx::rlc_am_nr_rx(rlc_am_nr* parent_) :
parent(parent_), pool(byte_buffer_pool::get_instance()), logger(parent_->logger)
{}
pdu->N_bytes = ptr - pdu->msg;
bool rlc_am_nr::rlc_am_nr_rx::configure(const rlc_am_config_t& cfg_)
{
cfg = cfg_;
return SRSRAN_SUCCESS;
return true;
}
void rlc_am_nr::rlc_am_nr_rx::stop() {}
void rlc_am_nr::rlc_am_nr_rx::write_pdu(uint8_t* payload, uint32_t nof_bytes) {}
} // namespace srsran

@ -0,0 +1,250 @@
/**
*
* \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.
*
*/
#include "srsran/rlc/rlc_am_nr_packing.h"
#include <sstream>
namespace srsran {
/****************************************************************************
* Header pack/unpack helper functions
* Ref: 3GPP TS 38.322 v15.3.0 Section 6.2.2.4
***************************************************************************/
uint32_t rlc_am_nr_read_data_pdu_header(const byte_buffer_t* pdu,
const rlc_am_nr_sn_size_t sn_size,
rlc_am_nr_pdu_header_t* header)
{
return rlc_am_nr_read_data_pdu_header(pdu->msg, pdu->N_bytes, sn_size, header);
}
uint32_t rlc_am_nr_read_data_pdu_header(const uint8_t* payload,
const uint32_t nof_bytes,
const rlc_am_nr_sn_size_t sn_size,
rlc_am_nr_pdu_header_t* header)
{
uint8_t* ptr = const_cast<uint8_t*>(payload);
header->sn_size = sn_size;
// Fixed part
header->dc = (rlc_dc_field_t)((*ptr >> 7) & 0x01); // 1 bit D/C field
header->p = (*ptr >> 6) & 0x01; // 1 bit P flag
header->si = (rlc_nr_si_field_t)((*ptr >> 4) & 0x03); // 2 bits SI
if (sn_size == rlc_am_nr_sn_size_t::size12bits) {
header->sn = (*ptr & 0x0F) << 8; // first 4 bits SN
ptr++;
header->sn |= (*ptr & 0xFF); // last 8 bits SN
ptr++;
} else if (sn_size == rlc_am_nr_sn_size_t::size18bits) {
// sanity check
if ((*ptr & 0x0c) != 0) {
fprintf(stderr, "Malformed PDU, reserved bits are set.\n");
return 0;
}
header->sn = (*ptr & 0x03) << 16; // first 4 bits SN
ptr++;
header->sn |= (*ptr & 0xFF) << 8; // bit 2-10 of SN
ptr++;
header->sn |= (*ptr & 0xFF); // last 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_am_nr_packed_length(const rlc_am_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 = 2;
if (header.sn_size == rlc_am_nr_sn_size_t::size18bits) {
len++;
}
} else {
// PDU contains SO
len = 4;
if (header.sn_size == rlc_am_nr_sn_size_t::size18bits) {
len++;
}
}
return len;
}
uint32_t rlc_am_nr_write_data_pdu_header(const rlc_am_nr_pdu_header_t& header, byte_buffer_t* pdu)
{
// Make room for the header
uint32_t len = rlc_am_nr_packed_length(header);
pdu->msg -= len;
uint8_t* ptr = pdu->msg;
// fixed header part
*ptr = (header.dc & 0x01) << 7; ///< 1 bit D/C field
*ptr |= (header.p & 0x01) << 6; ///< 1 bit P flag
*ptr |= (header.si & 0x03) << 4; ///< 2 bits SI
if (header.sn_size == rlc_am_nr_sn_size_t::size12bits) {
// write first 4 bit of SN
*ptr |= (header.sn >> 8) & 0x0f; // 4 bit SN
ptr++;
*ptr = header.sn & 0xff; // remaining 8 bit of SN
ptr++;
} else {
// 18bit SN
*ptr |= (header.sn >> 16) & 0x3; // 2 bit SN
ptr++;
*ptr = header.sn >> 8; // bit 3 - 10 of SN
ptr++;
*ptr = (header.sn & 0xff); // remaining 8 bit of 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;
}
uint32_t
rlc_am_nr_read_status_pdu(const byte_buffer_t* pdu, const rlc_am_nr_sn_size_t sn_size, rlc_am_nr_status_pdu_t* status)
{
return rlc_am_nr_read_status_pdu(pdu->msg, pdu->N_bytes, sn_size, status);
}
uint32_t rlc_am_nr_read_status_pdu(const uint8_t* payload,
const uint32_t nof_bytes,
const rlc_am_nr_sn_size_t sn_size,
rlc_am_nr_status_pdu_t* status)
{
uint8_t* ptr = const_cast<uint8_t*>(payload);
// fixed part
status->cpt = (rlc_am_nr_control_pdu_type_t)((*ptr >> 4) & 0x07); // 3 bits CPT
// sanity check
if (status->cpt != rlc_am_nr_control_pdu_type_t::status_pdu) {
fprintf(stderr, "Malformed PDU, reserved bits are set.\n");
return 0;
}
if (sn_size == rlc_am_nr_sn_size_t::size12bits) {
status->ack_sn = (*ptr & 0x0F) << 8; // first 4 bits SN
ptr++;
status->ack_sn |= (*ptr & 0xFF); // last 8 bits SN
ptr++;
// read E1 flag
uint8_t e1 = *ptr & 0x80;
// sanity check for reserved bits
if ((*ptr & 0x7f) != 0) {
fprintf(stderr, "Malformed PDU, reserved bits are set.\n");
return 0;
}
// all good, continue with next byte depending on E1
ptr++;
// reset number of acks
status->N_nack = 0;
if (e1) {
// E1 flag set, read a NACK_SN
rlc_status_nack_t nack = {};
nack.nack_sn = (*ptr & 0xff) << 4;
ptr++;
// uint8_t len2 = (*ptr & 0xF0) >> 4;
nack.nack_sn |= (*ptr & 0xF0) >> 4;
status->nacks[status->N_nack] = nack;
status->N_nack++;
}
}
return SRSRAN_SUCCESS;
}
/**
* Write a RLC AM NR status PDU to a PDU buffer and eets the length of the generate PDU accordingly
* @param status_pdu The status PDU
* @param pdu A pointer to a unique bytebuffer
* @return SRSRAN_SUCCESS if PDU was written, SRSRAN_ERROR otherwise
*/
int32_t rlc_am_nr_write_status_pdu(const rlc_am_nr_status_pdu_t& status_pdu,
const rlc_am_nr_sn_size_t sn_size,
byte_buffer_t* pdu)
{
uint8_t* ptr = pdu->msg;
// fixed header part
*ptr = 0; ///< 1 bit D/C field and 3bit CPT are all zero
if (sn_size == rlc_am_nr_sn_size_t::size12bits) {
// write first 4 bit of ACK_SN
*ptr |= (status_pdu.ack_sn >> 8) & 0x0f; // 4 bit ACK_SN
ptr++;
*ptr = status_pdu.ack_sn & 0xff; // remaining 8 bit of SN
ptr++;
// write E1 flag in octet 3
*ptr = (status_pdu.N_nack > 0) ? 0x80 : 0x00;
ptr++;
if (status_pdu.N_nack > 0) {
// write first 8 bit of NACK_SN
*ptr = (status_pdu.nacks[0].nack_sn >> 4) & 0xff;
ptr++;
// write remaining 4 bits of NACK_SN
*ptr = status_pdu.nacks[0].nack_sn & 0xf0;
ptr++;
}
} else {
// 18bit SN
*ptr |= (status_pdu.ack_sn >> 14) & 0x0f; // 4 bit ACK_SN
ptr++;
*ptr = status_pdu.ack_sn >> 8; // bit 3 - 10 of SN
ptr++;
*ptr = (status_pdu.ack_sn & 0xff); // remaining 6 bit of SN
ptr++;
}
pdu->N_bytes = ptr - pdu->msg;
return SRSRAN_SUCCESS;
}
} // namespace srsran

@ -0,0 +1,10 @@
#
# 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.
#
set(SOURCES nr/rrc_nr_cfg_utils.cc)
add_library(srsran_rrc_nr STATIC ${SOURCES})

@ -0,0 +1,239 @@
/**
*
* \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.
*
*/
#include "srsran/rrc/nr/rrc_nr_cfg_utils.h"
#include "srsran/asn1/rrc_nr.h"
#include "srsran/asn1/rrc_nr_utils.h"
using namespace asn1::rrc_nr;
namespace srsran {
void generate_default_pdcch_cfg_common(const basic_cell_args_t& args, pdcch_cfg_common_s& cfg)
{
cfg.ctrl_res_set_zero_present = true;
cfg.ctrl_res_set_zero = 0;
cfg.common_ctrl_res_set_present = false;
cfg.search_space_zero_present = true;
cfg.search_space_zero = 0;
cfg.common_search_space_list_present = true;
cfg.common_search_space_list.resize(1);
search_space_s& ss = cfg.common_search_space_list[0];
ss.search_space_id = 1;
ss.ctrl_res_set_id_present = true;
ss.ctrl_res_set_id = 0;
ss.monitoring_slot_periodicity_and_offset_present = true;
ss.monitoring_slot_periodicity_and_offset.set_sl1();
ss.monitoring_symbols_within_slot_present = true;
ss.monitoring_symbols_within_slot.from_number(0x2000);
ss.nrof_candidates_present = true;
ss.nrof_candidates.aggregation_level1.value = search_space_s::nrof_candidates_s_::aggregation_level1_opts::n0;
ss.nrof_candidates.aggregation_level2.value = search_space_s::nrof_candidates_s_::aggregation_level2_opts::n0;
ss.nrof_candidates.aggregation_level4.value = search_space_s::nrof_candidates_s_::aggregation_level4_opts::n1;
ss.nrof_candidates.aggregation_level8.value = search_space_s::nrof_candidates_s_::aggregation_level8_opts::n0;
ss.nrof_candidates.aggregation_level16.value = search_space_s::nrof_candidates_s_::aggregation_level16_opts::n0;
ss.search_space_type_present = true;
auto& common = ss.search_space_type.set_common();
common.dci_format0_minus0_and_format1_minus0_present = true;
cfg.search_space_sib1_present = true;
cfg.search_space_sib1 = 0;
cfg.search_space_other_sys_info_present = true;
cfg.search_space_other_sys_info = 1;
cfg.paging_search_space_present = true;
cfg.paging_search_space = 1;
cfg.ra_search_space_present = true;
cfg.ra_search_space = 1;
}
void generate_default_pdsch_cfg_common(const basic_cell_args_t& args, pdsch_cfg_common_s& cfg)
{
cfg.pdsch_time_domain_alloc_list_present = true;
cfg.pdsch_time_domain_alloc_list.resize(1);
cfg.pdsch_time_domain_alloc_list[0].map_type.value = pdsch_time_domain_res_alloc_s::map_type_opts::type_a;
cfg.pdsch_time_domain_alloc_list[0].start_symbol_and_len = 40;
}
void generate_default_init_dl_bwp(const basic_cell_args_t& args, bwp_dl_common_s& cfg)
{
cfg.generic_params.location_and_bw = 14025;
asn1::number_to_enum(cfg.generic_params.subcarrier_spacing, args.scs);
cfg.pdcch_cfg_common_present = true;
generate_default_pdcch_cfg_common(args, cfg.pdcch_cfg_common.set_setup());
cfg.pdsch_cfg_common_present = true;
generate_default_pdsch_cfg_common(args, cfg.pdsch_cfg_common.set_setup());
}
void generate_default_dl_cfg_common(dl_cfg_common_s& cfg, const basic_cell_args_t& args)
{
cfg.init_dl_bwp_present = true;
generate_default_init_dl_bwp(args, cfg.init_dl_bwp);
}
void generate_default_dl_cfg_common_sib(const basic_cell_args_t& args, dl_cfg_common_sib_s& cfg)
{
cfg.freq_info_dl.freq_band_list.resize(1);
cfg.freq_info_dl.freq_band_list[0].freq_band_ind_nr_present = true;
cfg.freq_info_dl.freq_band_list[0].freq_band_ind_nr = 20;
cfg.freq_info_dl.offset_to_point_a = 24;
cfg.freq_info_dl.scs_specific_carrier_list.resize(1);
cfg.freq_info_dl.scs_specific_carrier_list[0].offset_to_carrier = 0;
asn1::number_to_enum(cfg.freq_info_dl.scs_specific_carrier_list[0].subcarrier_spacing, args.scs);
cfg.freq_info_dl.scs_specific_carrier_list[0].carrier_bw = args.nof_prbs;
generate_default_init_dl_bwp(args, cfg.init_dl_bwp);
// disable InitialBWP-Only fields
cfg.init_dl_bwp.pdcch_cfg_common.setup().ctrl_res_set_zero_present = false;
cfg.init_dl_bwp.pdcch_cfg_common.setup().search_space_zero_present = false;
cfg.bcch_cfg.mod_period_coeff.value = bcch_cfg_s::mod_period_coeff_opts::n4;
cfg.pcch_cfg.default_paging_cycle.value = paging_cycle_opts::rf128;
cfg.pcch_cfg.nand_paging_frame_offset.set_one_t();
cfg.pcch_cfg.ns.value = pcch_cfg_s::ns_opts::one;
}
void generate_default_rach_cfg_common(const basic_cell_args_t& args, rach_cfg_common_s& cfg)
{
cfg.rach_cfg_generic.prach_cfg_idx = 16;
cfg.rach_cfg_generic.msg1_fdm.value = rach_cfg_generic_s::msg1_fdm_opts::one;
cfg.rach_cfg_generic.msg1_freq_start = 0;
cfg.rach_cfg_generic.zero_correlation_zone_cfg = 15;
cfg.rach_cfg_generic.preamb_rx_target_pwr = -110;
cfg.rach_cfg_generic.preamb_trans_max.value = rach_cfg_generic_s::preamb_trans_max_opts::n7;
cfg.rach_cfg_generic.pwr_ramp_step.value = rach_cfg_generic_s::pwr_ramp_step_opts::db4;
cfg.rach_cfg_generic.ra_resp_win.value = rach_cfg_generic_s::ra_resp_win_opts::sl10;
cfg.ssb_per_rach_occasion_and_cb_preambs_per_ssb_present = true;
cfg.ssb_per_rach_occasion_and_cb_preambs_per_ssb.set_one().value =
rach_cfg_common_s::ssb_per_rach_occasion_and_cb_preambs_per_ssb_c_::one_opts::n8;
cfg.ra_contention_resolution_timer.value = rach_cfg_common_s::ra_contention_resolution_timer_opts::sf64;
cfg.prach_root_seq_idx.set_l839() = 1;
cfg.restricted_set_cfg.value = rach_cfg_common_s::restricted_set_cfg_opts::unrestricted_set;
}
void generate_default_ul_cfg_common_sib(const basic_cell_args_t& args, ul_cfg_common_sib_s& cfg)
{
cfg.freq_info_ul.scs_specific_carrier_list.resize(1);
cfg.freq_info_ul.scs_specific_carrier_list[0].offset_to_carrier = 0;
asn1::number_to_enum(cfg.freq_info_ul.scs_specific_carrier_list[0].subcarrier_spacing, args.scs);
cfg.freq_info_ul.scs_specific_carrier_list[0].carrier_bw = args.nof_prbs;
cfg.init_ul_bwp.generic_params.location_and_bw = 14025;
asn1::number_to_enum(cfg.init_ul_bwp.generic_params.subcarrier_spacing, args.scs);
cfg.init_ul_bwp.rach_cfg_common_present = true;
generate_default_rach_cfg_common(args, cfg.init_ul_bwp.rach_cfg_common.set_setup());
cfg.init_ul_bwp.pusch_cfg_common_present = true;
pusch_cfg_common_s& pusch = cfg.init_ul_bwp.pusch_cfg_common.set_setup();
pusch.pusch_time_domain_alloc_list_present = true;
pusch.pusch_time_domain_alloc_list.resize(1);
pusch.pusch_time_domain_alloc_list[0].k2_present = true;
pusch.pusch_time_domain_alloc_list[0].k2 = 4;
pusch.pusch_time_domain_alloc_list[0].map_type.value = pusch_time_domain_res_alloc_s::map_type_opts::type_a;
pusch.pusch_time_domain_alloc_list[0].start_symbol_and_len = 27;
pusch.p0_nominal_with_grant_present = true;
pusch.p0_nominal_with_grant = -76;
cfg.init_ul_bwp.pucch_cfg_common_present = true;
pucch_cfg_common_s& pucch = cfg.init_ul_bwp.pucch_cfg_common.set_setup();
pucch.pucch_res_common_present = true;
pucch.pucch_res_common = 11;
pucch.pucch_group_hop.value = pucch_cfg_common_s::pucch_group_hop_opts::neither;
pucch.p0_nominal_present = true;
pucch.p0_nominal = -90;
cfg.time_align_timer_common.value = time_align_timer_opts::infinity;
}
void generate_default_serv_cell_cfg_common_sib(const basic_cell_args_t& args, serving_cell_cfg_common_sib_s& cfg)
{
generate_default_dl_cfg_common_sib(args, cfg.dl_cfg_common);
cfg.ul_cfg_common_present = true;
generate_default_ul_cfg_common_sib(args, cfg.ul_cfg_common);
cfg.ssb_positions_in_burst.in_one_group.from_number(0x80);
cfg.ssb_periodicity_serving_cell.value = serving_cell_cfg_common_sib_s::ssb_periodicity_serving_cell_opts::ms20;
cfg.ss_pbch_block_pwr = -16;
}
void generate_default_mib(const basic_cell_args_t& args, mib_s& cfg)
{
asn1::number_to_enum(cfg.sub_carrier_spacing_common, args.scs);
cfg.ssb_subcarrier_offset = 0;
cfg.intra_freq_resel.value = mib_s::intra_freq_resel_opts::allowed;
cfg.cell_barred.value = mib_s::cell_barred_opts::not_barred;
cfg.pdcch_cfg_sib1.search_space_zero = 0;
cfg.pdcch_cfg_sib1.ctrl_res_set_zero = 0;
cfg.dmrs_type_a_position.value = mib_s::dmrs_type_a_position_opts::pos2;
cfg.sys_frame_num.from_number(0);
}
void generate_default_sib1(const basic_cell_args_t& args, sib1_s& cfg)
{
cfg.cell_sel_info_present = true;
cfg.cell_sel_info.q_rx_lev_min = -70;
cfg.cell_sel_info.q_qual_min_present = true;
cfg.cell_sel_info.q_qual_min = -20;
cfg.cell_access_related_info.plmn_id_list.resize(1);
cfg.cell_access_related_info.plmn_id_list[0].plmn_id_list.resize(1);
srsran::plmn_id_t plmn;
plmn.from_string(args.plmn);
srsran::to_asn1(&cfg.cell_access_related_info.plmn_id_list[0].plmn_id_list[0], plmn);
cfg.cell_access_related_info.plmn_id_list[0].tac_present = true;
cfg.cell_access_related_info.plmn_id_list[0].tac.from_number(args.tac);
cfg.cell_access_related_info.plmn_id_list[0].cell_id.from_number(args.cell_id);
cfg.cell_access_related_info.plmn_id_list[0].cell_reserved_for_oper.value =
plmn_id_info_s::cell_reserved_for_oper_opts::not_reserved;
cfg.conn_est_fail_ctrl_present = true;
cfg.conn_est_fail_ctrl.conn_est_fail_count.value = conn_est_fail_ctrl_s::conn_est_fail_count_opts::n1;
cfg.conn_est_fail_ctrl.conn_est_fail_offset_validity.value =
conn_est_fail_ctrl_s::conn_est_fail_offset_validity_opts::s30;
cfg.conn_est_fail_ctrl.conn_est_fail_offset_present = true;
cfg.conn_est_fail_ctrl.conn_est_fail_offset = 1;
// cfg.si_sched_info_present = true;
// cfg.si_sched_info.si_request_cfg.rach_occasions_si_present = true;
// cfg.si_sched_info.si_request_cfg.rach_occasions_si.rach_cfg_si.ra_resp_win.value =
// rach_cfg_generic_s::ra_resp_win_opts::sl8;
// cfg.si_sched_info.si_win_len.value = si_sched_info_s::si_win_len_opts::s20;
// cfg.si_sched_info.sched_info_list.resize(1);
// cfg.si_sched_info.sched_info_list[0].si_broadcast_status.value =
// sched_info_s::si_broadcast_status_opts::broadcasting; cfg.si_sched_info.sched_info_list[0].si_periodicity.value =
// sched_info_s::si_periodicity_opts::rf16; cfg.si_sched_info.sched_info_list[0].sib_map_info.resize(1);
// // scheduling of SI messages
// cfg.si_sched_info.sched_info_list[0].sib_map_info[0].type.value = sib_type_info_s::type_opts::sib_type2;
// cfg.si_sched_info.sched_info_list[0].sib_map_info[0].value_tag_present = true;
// cfg.si_sched_info.sched_info_list[0].sib_map_info[0].value_tag = 0;
cfg.serving_cell_cfg_common_present = true;
generate_default_serv_cell_cfg_common_sib(args, cfg.serving_cell_cfg_common);
cfg.ue_timers_and_consts_present = true;
cfg.ue_timers_and_consts.t300.value = ue_timers_and_consts_s::t300_opts::ms1000;
cfg.ue_timers_and_consts.t301.value = ue_timers_and_consts_s::t301_opts::ms1000;
cfg.ue_timers_and_consts.t310.value = ue_timers_and_consts_s::t310_opts::ms1000;
cfg.ue_timers_and_consts.n310.value = ue_timers_and_consts_s::n310_opts::n1;
cfg.ue_timers_and_consts.t311.value = ue_timers_and_consts_s::t311_opts::ms30000;
cfg.ue_timers_and_consts.n311.value = ue_timers_and_consts_s::n311_opts::n1;
cfg.ue_timers_and_consts.t319.value = ue_timers_and_consts_s::t319_opts::ms1000;
}
} // namespace srsran

@ -28,9 +28,13 @@ add_executable(rlc_am_control_test rlc_am_control_test.cc)
target_link_libraries(rlc_am_control_test srsran_rlc srsran_phy)
add_lte_test(rlc_am_control_test rlc_am_control_test)
add_executable(rlc_am_test rlc_am_test.cc)
target_link_libraries(rlc_am_test srsran_rlc srsran_phy srsran_common)
add_lte_test(rlc_am_test rlc_am_test)
add_executable(rlc_am_lte_test rlc_am_lte_test.cc)
target_link_libraries(rlc_am_lte_test srsran_rlc srsran_phy srsran_common)
add_lte_test(rlc_am_lte_test rlc_am_lte_test)
add_executable(rlc_am_nr_test rlc_am_nr_test.cc)
target_link_libraries(rlc_am_nr_test srsran_rlc srsran_phy srsran_common)
add_nr_test(rlc_am_nr_test rlc_am_nr_test)
add_executable(rlc_am_nr_pdu_test rlc_am_nr_pdu_test.cc)
target_link_libraries(rlc_am_nr_pdu_test srsran_rlc srsran_phy)

@ -19,6 +19,7 @@
*
*/
#include "rlc_test_common.h"
#include "srsran/common/buffer_pool.h"
#include "srsran/common/rlc_pcap.h"
#include "srsran/common/test_common.h"
@ -34,63 +35,6 @@
using namespace srsue;
using namespace srsran;
bool rx_is_tx(const rlc_bearer_metrics_t& rlc1_metrics, const rlc_bearer_metrics_t& rlc2_metrics)
{
if (rlc1_metrics.num_tx_pdu_bytes != rlc2_metrics.num_rx_pdu_bytes) {
return false;
}
if (rlc2_metrics.num_tx_pdu_bytes != rlc1_metrics.num_rx_pdu_bytes) {
return false;
}
return true;
}
class rlc_am_tester : public pdcp_interface_rlc, public rrc_interface_rlc
{
public:
rlc_am_tester(rlc_pcap* pcap_ = NULL) : pcap(pcap_) {}
// PDCP interface
void write_pdu(uint32_t lcid, unique_byte_buffer_t sdu)
{
assert(lcid == 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, srsran::unique_byte_buffer_t pdu) {}
void notify_delivery(uint32_t lcid, const srsran::pdcp_sn_vector_t& pdcp_sn_vec)
{
assert(lcid == 1);
for (uint32_t pdcp_sn : pdcp_sn_vec) {
if (notified_counts.find(pdcp_sn) == notified_counts.end()) {
notified_counts[pdcp_sn] = 0;
}
notified_counts[pdcp_sn] += 1;
}
}
void notify_failure(uint32_t lcid, const srsran::pdcp_sn_vector_t& pdcp_sn_vec)
{
assert(lcid == 1);
// TODO
}
// RRC interface
void max_retx_attempted() { max_retx_triggered = true; }
void protocol_failure() { protocol_failure_triggered = true; }
const char* get_rb_name(uint32_t lcid) { return ""; }
std::vector<unique_byte_buffer_t> sdus;
rlc_pcap* pcap = nullptr;
bool max_retx_triggered = false;
bool protocol_failure_triggered = false;
std::map<uint32_t, uint32_t> notified_counts; // Map of PDCP SNs to number of notifications
};
class ul_writer : public thread
{
public:

@ -21,7 +21,7 @@
#include "srsran/config.h"
#include "srsran/rlc/rlc.h"
#include "srsran/rlc/rlc_am_nr.h"
#include "srsran/rlc/rlc_am_nr_packing.h"
#include <array>
#include <iostream>

@ -0,0 +1,109 @@
/**
*
* \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.
*
*/
#include "rlc_test_common.h"
#include "srsran/common/buffer_pool.h"
#include "srsran/common/rlc_pcap.h"
#include "srsran/common/test_common.h"
#include "srsran/common/threads.h"
#include "srsran/interfaces/ue_pdcp_interfaces.h"
#include "srsran/interfaces/ue_rrc_interfaces.h"
#include "srsran/rlc/rlc_am_nr.h"
#define NBUFS 5
#define HAVE_PCAP 0
#define SDU_SIZE 500
using namespace srsue;
using namespace srsran;
int basic_test_tx(rlc_am_nr* rlc, byte_buffer_t pdu_bufs[NBUFS])
{
// Push 5 SDUs into RLC1
unique_byte_buffer_t sdu_bufs[NBUFS];
for (int i = 0; i < NBUFS; i++) {
sdu_bufs[i] = srsran::make_byte_buffer();
sdu_bufs[i]->msg[0] = i; // Write the index into the buffer
sdu_bufs[i]->N_bytes = 1; // Give each buffer a size of 1 byte
sdu_bufs[i]->md.pdcp_sn = i; // PDCP SN for notifications
rlc->write_sdu(std::move(sdu_bufs[i]));
}
TESTASSERT(13 == rlc->get_buffer_state()); // 2 Bytes for fixed header + 6 for LIs + 5 for payload
// Read 5 PDUs from RLC1 (1 byte each)
for (int i = 0; i < NBUFS; i++) {
uint32_t len = rlc->read_pdu(pdu_bufs[i].msg, 3); // 2 bytes for header + 1 byte payload
pdu_bufs[i].N_bytes = len;
TESTASSERT(3 == len);
}
TESTASSERT(0 == rlc->get_buffer_state());
return SRSRAN_SUCCESS;
}
int basic_test()
{
rlc_am_tester tester;
timer_handler timers(8);
byte_buffer_t pdu_bufs[NBUFS];
rlc_am_nr rlc1(srslog::fetch_basic_logger("RLC_AM_1"), 1, &tester, &tester, &timers);
rlc_am_nr rlc2(srslog::fetch_basic_logger("RLC_AM_2"), 1, &tester, &tester, &timers);
// before configuring entity
TESTASSERT(0 == rlc1.get_buffer_state());
if (not rlc1.configure(rlc_config_t::default_rlc_am_config())) {
return -1;
}
if (not rlc2.configure(rlc_config_t::default_rlc_am_config())) {
return -1;
}
// basic_test_tx(&rlc1, pdu_bufs);
return SRSRAN_SUCCESS;
}
int main(int argc, char** argv)
{
// Setup the log message spy to intercept error and warning log entries from RLC
if (!srslog::install_custom_sink(srsran::log_sink_message_spy::name(),
std::unique_ptr<srsran::log_sink_message_spy>(
new srsran::log_sink_message_spy(srslog::get_default_log_formatter())))) {
return SRSRAN_ERROR;
}
auto* spy = static_cast<srsran::log_sink_message_spy*>(srslog::find_sink(srsran::log_sink_message_spy::name()));
if (spy == nullptr) {
return SRSRAN_ERROR;
}
srslog::set_default_sink(*spy);
auto& logger_rlc1 = srslog::fetch_basic_logger("RLC_NR_AM_1", *spy, false);
auto& logger_rlc2 = srslog::fetch_basic_logger("RLC_NR_AM_2", *spy, false);
logger_rlc1.set_hex_dump_max_size(100);
logger_rlc2.set_hex_dump_max_size(100);
logger_rlc1.set_level(srslog::basic_levels::debug);
logger_rlc2.set_level(srslog::basic_levels::debug);
// start log backend
srslog::init();
if (basic_test()) {
printf("basic_test failed\n");
exit(-1);
};
return SRSRAN_SUCCESS;
}

@ -23,8 +23,10 @@
#define SRSRAN_RLC_TEST_COMMON_H
#include "srsran/common/byte_buffer.h"
#include "srsran/common/rlc_pcap.h"
#include "srsran/interfaces/ue_pdcp_interfaces.h"
#include "srsran/interfaces/ue_rrc_interfaces.h"
#include "srsran/rlc/rlc_metrics.h"
#include <vector>
namespace srsran {
@ -32,10 +34,10 @@ namespace srsran {
class rlc_um_tester : public srsue::pdcp_interface_rlc, public srsue::rrc_interface_rlc
{
public:
rlc_um_tester() {}
rlc_um_tester() = default;
// PDCP interface
void write_pdu(uint32_t lcid, unique_byte_buffer_t sdu)
void write_pdu(uint32_t lcid, unique_byte_buffer_t sdu) final
{
// check length
if (lcid != 3 && sdu->N_bytes != expected_sdu_len) {
@ -56,16 +58,16 @@ public:
// srsran_vec_fprint_byte(stdout, sdu->msg, sdu->N_bytes);
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, srsran::unique_byte_buffer_t sdu) { sdus.push_back(std::move(sdu)); }
void notify_delivery(uint32_t lcid, const srsran::pdcp_sn_vector_t& pdcp_sns) {}
void notify_failure(uint32_t lcid, const srsran::pdcp_sn_vector_t& pdcp_sns) {}
void write_pdu_bcch_bch(unique_byte_buffer_t sdu) final {}
void write_pdu_bcch_dlsch(unique_byte_buffer_t sdu) final {}
void write_pdu_pcch(unique_byte_buffer_t sdu) final {}
void write_pdu_mch(uint32_t lcid, srsran::unique_byte_buffer_t sdu) final { sdus.push_back(std::move(sdu)); }
void notify_delivery(uint32_t lcid, const srsran::pdcp_sn_vector_t& pdcp_sns) final {}
void notify_failure(uint32_t lcid, const srsran::pdcp_sn_vector_t& pdcp_sns) final {}
// RRC interface
void max_retx_attempted() {}
void protocol_failure() {}
void max_retx_attempted() final {}
void protocol_failure() final {}
const char* get_rb_name(uint32_t lcid) { return ""; }
void set_expected_sdu_len(uint32_t len) { expected_sdu_len = len; }
@ -76,6 +78,62 @@ public:
uint32_t expected_sdu_len = 0;
};
class rlc_am_tester : public srsue::pdcp_interface_rlc, public srsue::rrc_interface_rlc
{
public:
rlc_am_tester(rlc_pcap* pcap_ = NULL) : pcap(pcap_) {}
// PDCP interface
void write_pdu(uint32_t lcid, unique_byte_buffer_t sdu)
{
assert(lcid == 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, srsran::unique_byte_buffer_t pdu) {}
void notify_delivery(uint32_t lcid, const srsran::pdcp_sn_vector_t& pdcp_sn_vec)
{
assert(lcid == 1);
for (uint32_t pdcp_sn : pdcp_sn_vec) {
if (notified_counts.find(pdcp_sn) == notified_counts.end()) {
notified_counts[pdcp_sn] = 0;
}
notified_counts[pdcp_sn] += 1;
}
}
void notify_failure(uint32_t lcid, const srsran::pdcp_sn_vector_t& pdcp_sn_vec)
{
assert(lcid == 1);
// TODO
}
// RRC interface
void max_retx_attempted() { max_retx_triggered = true; }
void protocol_failure() { protocol_failure_triggered = true; }
const char* get_rb_name(uint32_t lcid) { return ""; }
std::vector<unique_byte_buffer_t> sdus;
rlc_pcap* pcap = nullptr;
bool max_retx_triggered = false;
bool protocol_failure_triggered = false;
std::map<uint32_t, uint32_t> notified_counts; // Map of PDCP SNs to number of notifications
};
bool rx_is_tx(const rlc_bearer_metrics_t& rlc1_metrics, const rlc_bearer_metrics_t& rlc2_metrics)
{
if (rlc1_metrics.num_tx_pdu_bytes != rlc2_metrics.num_rx_pdu_bytes) {
return false;
}
if (rlc2_metrics.num_tx_pdu_bytes != rlc1_metrics.num_rx_pdu_bytes) {
return false;
}
return true;
}
} // namespace srsran
#endif // SRSRAN_RLC_TEST_COMMON_H

@ -40,8 +40,8 @@
#include "srsenb/hdr/stack/enb_stack_base.h"
#include "srsenb/hdr/stack/rrc/rrc_config.h"
#include "srsenb/hdr/stack/gnb_stack_nr.h"
#include "srsenb/hdr/stack/mac/sched_interface.h"
#include "srsgnb/hdr/stack/gnb_stack_nr.h"
#include "srsran/common/bcd_helpers.h"
#include "srsran/common/buffer_pool.h"
#include "srsran/common/interfaces_common.h"

@ -158,8 +158,6 @@ private:
stack_args_t args = {};
rrc_cfg_t rrc_cfg = {};
srsran::socket_manager rx_sockets;
srslog::basic_logger& mac_logger;
srslog::basic_logger& rlc_logger;
srslog::basic_logger& pdcp_logger;

@ -1,59 +0,0 @@
/**
* Copyright 2013-2021 Software Radio Systems Limited
*
* This file is part of srsRAN.
*
* srsRAN 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.
*
* srsRAN 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 SRSRAN_RRC_CONFIG_NR_H
#define SRSRAN_RRC_CONFIG_NR_H
#include "../rrc_config_common.h"
#include "srsran/asn1/rrc_nr.h"
#include "srsran/interfaces/gnb_rrc_nr_interfaces.h"
#include "srsue/hdr/phy/phy_common.h"
namespace srsenb {
// TODO: Make this common to NR and LTE
struct rrc_nr_cfg_sr_t {
uint32_t period;
// asn1::rrc::sched_request_cfg_c::setup_s_::dsr_trans_max_e_ dsr_max;
uint32_t nof_prb;
uint32_t sf_mapping[80];
uint32_t nof_subframes;
};
struct rrc_nr_cfg_t {
asn1::rrc_nr::mib_s mib;
asn1::rrc_nr::sib1_s sib1;
asn1::rrc_nr::sys_info_ies_s::sib_type_and_info_item_c_ sibs[ASN1_RRC_NR_MAX_SIB];
uint32_t nof_sibs;
rrc_nr_cfg_sr_t sr_cfg;
rrc_cfg_cqi_t cqi_cfg;
rrc_cell_list_nr_t cell_list;
asn1::rrc_nr::rach_cfg_common_s rach_cfg_common;
uint16_t prach_root_seq_idx_type;
std::string log_name = "RRC-NR";
std::string log_level;
uint32_t log_hex_limit;
};
} // namespace srsenb
#endif // SRSRAN_RRC_CONFIG_NR_H

@ -1,276 +0,0 @@
/**
* Copyright 2013-2021 Software Radio Systems Limited
*
* This file is part of srsRAN.
*
* srsRAN 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.
*
* srsRAN 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 SRSENB_RRC_NR_H
#define SRSENB_RRC_NR_H
#include "rrc_config_common.h"
#include "rrc_metrics.h"
#include "srsenb/hdr/stack/enb_stack_base.h"
#include "srsenb/hdr/stack/rrc/nr/rrc_config_nr.h"
#include "srsran/asn1/rrc_nr.h"
#include "srsran/common/block_queue.h"
#include "srsran/common/buffer_pool.h"
#include "srsran/common/common.h"
#include "srsran/common/task_scheduler.h"
#include "srsran/common/threads.h"
#include "srsran/common/timeout.h"
#include "srsran/interfaces/enb_pdcp_interfaces.h"
#include "srsran/interfaces/enb_rlc_interfaces.h"
#include "srsran/interfaces/enb_rrc_interfaces.h"
#include "srsran/interfaces/enb_x2_interfaces.h"
#include "srsran/interfaces/gnb_interfaces.h"
#include "srsran/interfaces/gnb_mac_interfaces.h"
#include "srsran/interfaces/gnb_ngap_interfaces.h"
#include "srsran/interfaces/gnb_rrc_nr_interfaces.h"
#include <map>
#include <queue>
namespace srsenb {
enum class rrc_nr_state_t { RRC_IDLE, RRC_INACTIVE, RRC_CONNECTED };
class rrc_nr final : public rrc_interface_pdcp_nr,
public rrc_interface_mac_nr,
public rrc_interface_rlc_nr,
public rrc_interface_ngap_nr,
public rrc_nr_interface_rrc
{
public:
explicit rrc_nr(srsran::task_sched_handle task_sched_);
int32_t init(const rrc_nr_cfg_t& cfg,
phy_interface_stack_nr* phy,
mac_interface_rrc_nr* mac,
rlc_interface_rrc* rlc,
pdcp_interface_rrc* pdcp,
ngap_interface_rrc_nr* ngap_,
gtpu_interface_rrc_nr* gtpu,
rrc_eutra_interface_rrc_nr* rrc_eutra_);
void stop();
void get_metrics(srsenb::rrc_metrics_t& m);
rrc_nr_cfg_t update_default_cfg(const rrc_nr_cfg_t& rrc_cfg);
void config_phy();
void config_mac();
int32_t generate_sibs();
int read_pdu_bcch_bch(const uint32_t tti, srsran::unique_byte_buffer_t& buffer) final;
int read_pdu_bcch_dlsch(uint32_t sib_index, srsran::unique_byte_buffer_t& buffer) final;
/// User manegement
int add_user(uint16_t rnti, const sched_nr_ue_cfg_t& uecfg);
void rem_user(uint16_t rnti);
int update_user(uint16_t new_rnti, uint16_t old_rnti);
void set_activity_user(uint16_t rnti);
// RLC interface
// TODO
void read_pdu_pcch(uint8_t* payload, uint32_t payload_size) {}
void max_retx_attempted(uint16_t rnti) {}
void protocol_failure(uint16_t rnti) {}
const char* get_rb_name(uint32_t lcid) { return "invalid"; }
// PDCP interface
void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu) final;
void notify_pdcp_integrity_error(uint16_t rnti, uint32_t lcid) final;
// Interface for EUTRA RRC
void sgnb_addition_request(uint16_t rnti, const sgnb_addition_req_params_t& params);
void sgnb_reconfiguration_complete(uint16_t rnti, const asn1::dyn_octstring& reconfig_response) final;
void sgnb_release_request(uint16_t nr_rnti) final;
// Interfaces for NGAP
int ue_set_security_cfg_key(uint16_t rnti, const asn1::fixed_bitstring<256, false, true>& key);
int ue_set_bitrates(uint16_t rnti, const asn1::ngap_nr::ue_aggregate_maximum_bit_rate_s& rates);
int ue_set_security_cfg_capabilities(uint16_t rnti, const asn1::ngap_nr::ue_security_cap_s& caps);
int start_security_mode_procedure(uint16_t rnti);
int establish_rrc_bearer(uint16_t rnti, uint16_t pdu_session_id, srsran::const_byte_span nas_pdu, uint32_t lcid);
int release_bearers(uint16_t rnti);
void write_dl_info(uint16_t rnti, srsran::unique_byte_buffer_t sdu);
int set_aggregate_max_bitrate(uint16_t rnti, const asn1::ngap_nr::ue_aggregate_maximum_bit_rate_s& rates);
int allocate_lcid(uint16_t rnti);
// logging
typedef enum { Rx = 0, Tx } direction_t;
template <class T>
void log_rrc_message(const std::string& source,
const direction_t dir,
const asn1::dyn_octstring& oct,
const T& msg,
const std::string& msg_type);
template <class T>
void log_rrc_message(const std::string& source,
const direction_t dir,
const srsran::byte_buffer_t& pdu,
const T& msg,
const std::string& msg_type);
class ue
{
public:
enum activity_timeout_type_t {
MSG3_RX_TIMEOUT = 0, ///< Msg3 has its own timeout to quickly remove fake UEs from random PRACHs
UE_INACTIVITY_TIMEOUT, ///< (currently unused) UE inactivity timeout (usually bigger than reestablishment timeout)
MSG5_RX_TIMEOUT, ///< (currently unused) for receiving RRCConnectionSetupComplete/RRCReestablishmentComplete
nulltype
};
/// @param [in] start_msg3_timer: indicates whether the UE is created as part of a RACH process
ue(rrc_nr* parent_, uint16_t rnti_, const sched_nr_ue_cfg_t& uecfg, bool start_msg3_timer = true);
void send_connection_setup();
void send_dl_ccch(asn1::rrc_nr::dl_ccch_msg_s* dl_dcch_msg);
int handle_sgnb_addition_request(uint16_t eutra_rnti, const sgnb_addition_req_params_t& params);
void crnti_ce_received();
// getters
bool is_connected() { return state == rrc_nr_state_t::RRC_CONNECTED; }
bool is_idle() { return state == rrc_nr_state_t::RRC_IDLE; }
bool is_inactive() { return state == rrc_nr_state_t::RRC_INACTIVE; }
bool is_endc() { return endc; }
uint16_t get_eutra_rnti() { return eutra_rnti; }
void get_metrics(rrc_ue_metrics_t& ue_metrics) { ue_metrics = {}; /*TODO fill RRC metrics*/ };
// setters
int pack_rrc_reconfiguration();
void deactivate_bearers();
/// methods to handle activity timer
std::string to_string(const activity_timeout_type_t& type);
void set_activity_timeout(activity_timeout_type_t type);
void set_activity(bool enabled = true);
void activity_timer_expired(const activity_timeout_type_t type);
private:
rrc_nr* parent = nullptr;
uint16_t rnti = SRSRAN_INVALID_RNTI;
/// for basic DL/UL activity timeout
srsran::unique_timer activity_timer;
int pack_rrc_reconfiguration(asn1::dyn_octstring& packed_rrc_reconfig);
int pack_secondary_cell_group_cfg(asn1::dyn_octstring& packed_secondary_cell_config);
int pack_secondary_cell_group_rlc_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_secondary_cell_group_mac_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_secondary_cell_group_sp_cell_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_sp_cell_cfg_ded(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_sp_cell_cfg_ded_init_dl_bwp(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_sp_cell_cfg_ded_init_dl_bwp_pdsch_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_sp_cell_cfg_ded_init_dl_bwp_radio_link_monitoring(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_sp_cell_cfg_ded_ul_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_sp_cell_cfg_ded_ul_cfg_init_ul_bwp(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_sp_cell_cfg_ded_ul_cfg_init_ul_bwp_pucch_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_sp_cell_cfg_ded_ul_cfg_init_ul_bwp_pusch_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_sp_cell_cfg_ded_pdcch_serving_cell_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_recfg_with_sync(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_recfg_with_sync_sp_cell_cfg_common(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_recfg_with_sync_sp_cell_cfg_common_dl_cfg_common(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_recfg_with_sync_sp_cell_cfg_common_dl_cfg_common_phy_cell_group_cfg(
asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_recfg_with_sync_sp_cell_cfg_common_dl_cfg_init_dl_bwp(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_recfg_with_sync_sp_cell_cfg_common_dl_cfg_init_dl_bwp_pdsch_cfg_common(
asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_recfg_with_sync_sp_cell_cfg_common_ul_cfg_common(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_recfg_with_sync_sp_cell_cfg_common_ul_cfg_common_init_ul_bwp(
asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_recfg_with_sync_sp_cell_cfg_common_ul_cfg_common_init_ul_bwp_pusch_cfg_common(
asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_nr_radio_bearer_config(asn1::dyn_octstring& packed_nr_bearer_config);
int add_drb();
// logging helpers
template <class T, class M>
void log_rrc_message(const direction_t dir, const M& pdu, const T& msg, const std::string& msg_type);
// state
rrc_nr_state_t state = rrc_nr_state_t::RRC_IDLE;
uint8_t transaction_id = 0;
// RRC configs for UEs
asn1::rrc_nr::cell_group_cfg_s cell_group_cfg;
asn1::rrc_nr::radio_bearer_cfg_s radio_bearer_cfg;
// MAC controller
sched_nr_interface::ue_cfg_t uecfg{};
const uint32_t drb1_lcid = 4;
// NSA specific variables
bool endc = false;
uint16_t eutra_rnti = SRSRAN_INVALID_RNTI;
};
private:
static constexpr uint32_t UE_PSCELL_CC_IDX = 0; // first NR cell is always Primary Secondary Cell for UE
rrc_nr_cfg_t cfg = {};
// interfaces
phy_interface_stack_nr* phy = nullptr;
mac_interface_rrc_nr* mac = nullptr;
rlc_interface_rrc* rlc = nullptr;
pdcp_interface_rrc* pdcp = nullptr;
gtpu_interface_rrc_nr* gtpu = nullptr;
ngap_interface_rrc_nr* ngap = nullptr;
rrc_eutra_interface_rrc_nr* rrc_eutra = nullptr;
// args
srsran::task_sched_handle task_sched;
// derived
uint32_t slot_dur_ms = 0;
srslog::basic_logger& logger;
asn1::rrc_nr::sp_cell_cfg_s base_sp_cell_cfg;
// vars
std::map<uint16_t, std::unique_ptr<ue> > users;
bool running = false;
std::vector<srsran::unique_byte_buffer_t> sib_buffer;
srsran::unique_byte_buffer_t mib_buffer = nullptr;
uint32_t nof_si_messages = 0;
/// Private Methods
void handle_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu);
/// This gets called by rrc_nr::sgnb_addition_request and WILL NOT TRIGGER the RX MSG3 activity timer
int add_user(uint16_t rnti, const sched_nr_ue_cfg_t& uecfg, bool start_msg3_timer);
// Helper to create PDU from RRC message
template <class T>
srsran::unique_byte_buffer_t pack_into_pdu(const T& msg);
};
} // namespace srsenb
#endif // SRSENB_RRC_NR_H

@ -39,9 +39,9 @@
#ifndef SRSENB_X2_ADAPTER_H
#define SRSENB_X2_ADAPTER_H
#include "srsgnb/hdr/stack/gnb_stack_nr.h"
#include "srsran/interfaces/enb_x2_interfaces.h"
#include "stack/enb_stack_lte.h"
#include "stack/gnb_stack_nr.h"
namespace srsenb {

@ -41,7 +41,7 @@ add_executable(srsenb main.cc enb.cc metrics_stdout.cc metrics_csv.cc metrics_js
set(SRSENB_SOURCES srsenb_phy srsenb_stack srsenb_common srsenb_s1ap srsenb_upper srsenb_mac srsenb_rrc srslog system)
set(SRSRAN_SOURCES srsran_common srsran_mac srsran_phy srsran_gtpu srsran_rlc srsran_pdcp srsran_radio rrc_asn1 s1ap_asn1 enb_cfg_parser srslog support system)
set(SRSENB_SOURCES ${SRSENB_SOURCES} srsgnb_stack srsgnb_ngap srsgnb_upper srsgnb_mac srsgnb_rrc)
set(SRSENB_SOURCES ${SRSENB_SOURCES} srsgnb_stack srsgnb_ngap srsgnb_mac srsgnb_rrc)
set(SRSRAN_SOURCES ${SRSRAN_SOURCES} rrc_nr_asn1 ngap_nr_asn1)
target_link_libraries(srsenb ${SRSENB_SOURCES}

@ -21,9 +21,9 @@
#include "srsenb/hdr/enb.h"
#include "srsenb/hdr/stack/enb_stack_lte.h"
#include "srsenb/hdr/stack/gnb_stack_nr.h"
#include "srsenb/hdr/x2_adapter.h"
#include "srsenb/src/enb_cfg_parser.h"
#include "srsgnb/hdr/stack/gnb_stack_nr.h"
#include "srsran/build_info.h"
#include "srsran/common/enb_events.h"
#include "srsran/radio/radio_null.h"

@ -1185,15 +1185,26 @@ int parse_cfg_files(all_args_t* args_, rrc_cfg_t* rrc_cfg_, rrc_nr_cfg_t* rrc_nr
// update number of NR cells
rrc_cfg_->num_nr_cells = rrc_nr_cfg_->cell_list.size();
args_->rf.nof_carriers = rrc_cfg_->cell_list.size() + rrc_nr_cfg_->cell_list.size();
// update EUTRA RRC params for ENDC
if (rrc_nr_cfg_->cell_list.size() == 1) {
rrc_cfg_->endc_cfg.abs_frequency_ssb = rrc_nr_cfg_->cell_list.at(0).ssb_absolute_freq_point;
rrc_cfg_->endc_cfg.nr_band = rrc_nr_cfg_->cell_list.at(0).band;
rrc_cfg_->endc_cfg.ssb_period_offset.set_sf10_r15();
rrc_cfg_->endc_cfg.ssb_duration = asn1::rrc::mtc_ssb_nr_r15_s::ssb_dur_r15_opts::sf1;
rrc_cfg_->endc_cfg.ssb_ssc = asn1::rrc::rs_cfg_ssb_nr_r15_s::subcarrier_spacing_ssb_r15_opts::khz15;
rrc_cfg_->endc_cfg.act_from_b1_event = true; // ENDC will only be activated from B1 measurment
ASSERT_VALID_CFG(args_->rf.nof_carriers > 0, "There must be at least one NR or LTE cell");
if (rrc_nr_cfg_->cell_list.size() > 0) {
// NR cells available.
if (rrc_cfg_->cell_list.size() == 0) {
// SA mode. Update NGAP args
rrc_nr_cfg_->is_standalone = true;
args_->nr_stack.ngap.gnb_id = args_->enb.enb_id;
args_->nr_stack.ngap.cell_id = rrc_nr_cfg_->cell_list[0].phy_cell.cell_id;
args_->nr_stack.ngap.tac = rrc_nr_cfg_->cell_list[0].tac;
} else {
// NSA mode.
rrc_nr_cfg_->is_standalone = false;
// update EUTRA RRC params for ENDC
rrc_cfg_->endc_cfg.abs_frequency_ssb = rrc_nr_cfg_->cell_list.at(0).ssb_absolute_freq_point;
rrc_cfg_->endc_cfg.nr_band = rrc_nr_cfg_->cell_list.at(0).band;
rrc_cfg_->endc_cfg.ssb_period_offset.set_sf10_r15();
rrc_cfg_->endc_cfg.ssb_duration = asn1::rrc::mtc_ssb_nr_r15_s::ssb_dur_r15_opts::sf1;
rrc_cfg_->endc_cfg.ssb_ssc = asn1::rrc::rs_cfg_ssb_nr_r15_s::subcarrier_spacing_ssb_r15_opts::khz15;
rrc_cfg_->endc_cfg.act_from_b1_event = true; // ENDC will only be activated from B1 measurment
}
}
return SRSRAN_SUCCESS;
@ -1202,41 +1213,42 @@ int parse_cfg_files(all_args_t* args_, rrc_cfg_t* rrc_cfg_, rrc_nr_cfg_t* rrc_nr
int set_derived_args(all_args_t* args_, rrc_cfg_t* rrc_cfg_, phy_cfg_t* phy_cfg_, const srsran_cell_t& cell_cfg_)
{
// Sanity checks
ASSERT_VALID_CFG(not rrc_cfg_->cell_list.empty(), "No cell specified in rr.conf.");
ASSERT_VALID_CFG(args_->stack.mac.nof_prealloc_ues <= SRSENB_MAX_UES,
"mac.nof_prealloc_ues=%d must be within [0, %d]",
args_->stack.mac.nof_prealloc_ues,
SRSENB_MAX_UES);
// Check for a forced DL EARFCN or frequency (only valid for a single cell config (Xico's favorite feature))
if (rrc_cfg_->cell_list.size() == 1) {
auto& cfg = rrc_cfg_->cell_list.at(0);
if (args_->enb.dl_earfcn > 0 and args_->enb.dl_earfcn != cfg.dl_earfcn) {
cfg.dl_earfcn = args_->enb.dl_earfcn;
ERROR("Force DL EARFCN for cell PCI=%d to %d", cfg.pci, cfg.dl_earfcn);
}
if (args_->rf.dl_freq > 0) {
cfg.dl_freq_hz = args_->rf.dl_freq;
ERROR("Force DL freq for cell PCI=%d to %f MHz", cfg.pci, cfg.dl_freq_hz / 1e6f);
}
if (args_->rf.ul_freq > 0) {
cfg.ul_freq_hz = args_->rf.ul_freq;
ERROR("Force UL freq for cell PCI=%d to %f MHz", cfg.pci, cfg.ul_freq_hz / 1e6f);
}
} else {
// If more than one cell is defined, single EARFCN or DL freq will be ignored
if (args_->enb.dl_earfcn > 0 || args_->rf.dl_freq > 0) {
INFO("Multiple cells defined in rr.conf. Ignoring single EARFCN and/or frequency config.");
if (rrc_cfg_->cell_list.size() > 0) {
if (rrc_cfg_->cell_list.size() == 1) {
auto& cfg = rrc_cfg_->cell_list.at(0);
if (args_->enb.dl_earfcn > 0 and args_->enb.dl_earfcn != cfg.dl_earfcn) {
cfg.dl_earfcn = args_->enb.dl_earfcn;
ERROR("Force DL EARFCN for cell PCI=%d to %d", cfg.pci, cfg.dl_earfcn);
}
if (args_->rf.dl_freq > 0) {
cfg.dl_freq_hz = args_->rf.dl_freq;
ERROR("Force DL freq for cell PCI=%d to %f MHz", cfg.pci, cfg.dl_freq_hz / 1e6f);
}
if (args_->rf.ul_freq > 0) {
cfg.ul_freq_hz = args_->rf.ul_freq;
ERROR("Force UL freq for cell PCI=%d to %f MHz", cfg.pci, cfg.ul_freq_hz / 1e6f);
}
} else {
// If more than one cell is defined, single EARFCN or DL freq will be ignored
if (args_->enb.dl_earfcn > 0 || args_->rf.dl_freq > 0) {
INFO("Multiple cells defined in rr.conf. Ignoring single EARFCN and/or frequency config.");
}
}
}
// set config for RRC's base cell
rrc_cfg_->cell = cell_cfg_;
// set config for RRC's base cell
rrc_cfg_->cell = cell_cfg_;
// Set S1AP related params from cell list
args_->stack.s1ap.enb_id = args_->enb.enb_id;
args_->stack.s1ap.cell_id = rrc_cfg_->cell_list.at(0).cell_id;
args_->stack.s1ap.tac = rrc_cfg_->cell_list.at(0).tac;
// Set S1AP related params from cell list
args_->stack.s1ap.enb_id = args_->enb.enb_id;
args_->stack.s1ap.cell_id = rrc_cfg_->cell_list.at(0).cell_id;
args_->stack.s1ap.tac = rrc_cfg_->cell_list.at(0).tac;
}
// Create dedicated cell configuration from RRC configuration
for (auto it = rrc_cfg_->cell_list.begin(); it != rrc_cfg_->cell_list.end(); ++it) {

@ -94,7 +94,7 @@ void phy::parse_common_config(const phy_cfg_t& cfg)
prach_cfg.root_seq_idx = cfg.prach_cnfg.root_seq_idx;
prach_cfg.zero_corr_zone = cfg.prach_cnfg.prach_cfg_info.zero_correlation_zone_cfg;
prach_cfg.freq_offset = cfg.prach_cnfg.prach_cfg_info.prach_freq_offset;
prach_cfg.num_ra_preambles = cfg.phy_cell_cfg.at(0).num_ra_preambles;
prach_cfg.num_ra_preambles = cfg.phy_cell_cfg.empty() ? 0 : cfg.phy_cell_cfg.at(0).num_ra_preambles;
// DMRS
workers_common.dmrs_pusch_cfg.cyclic_shift = cfg.pusch_cnfg.ul_ref_sigs_pusch.cyclic_shift;
workers_common.dmrs_pusch_cfg.delta_ss = cfg.pusch_cnfg.ul_ref_sigs_pusch.group_assign_pusch;
@ -169,7 +169,7 @@ int phy::init_lte(const phy_args_t& args,
phy_log.set_hex_dump_max_size(args.log.phy_hex_limit);
radio = radio_;
nof_workers = args.nof_phy_threads;
nof_workers = cfg.phy_cell_cfg.empty() ? 0 : args.nof_phy_threads;
workers_common.params = args;

@ -185,7 +185,7 @@ void txrx::run_thread()
tti,
timestamp.get(0).full_secs,
timestamp.get(0).frac_secs,
lte_worker->get_id());
lte_worker ? lte_worker->get_id() : 0);
// Trigger prach worker execution
for (uint32_t cc = 0; cc < worker_com->get_nof_carriers_lte(); cc++) {

@ -21,13 +21,8 @@
add_subdirectory(mac)
add_subdirectory(rrc)
add_subdirectory(s1ap)
add_subdirectory(ngap)
add_subdirectory(upper)
set(SOURCES enb_stack_lte.cc)
add_library(srsenb_stack STATIC ${SOURCES})
target_link_libraries(srsenb_stack)
add_library(srsgnb_stack STATIC gnb_stack_nr.cc)
target_link_libraries(srsgnb_stack srsue_upper)

@ -98,8 +98,8 @@ enb_stack_lte::enb_stack_lte(srslog::sink& log_sink) :
pdcp(&task_sched, pdcp_logger),
mac(&task_sched, mac_logger),
rlc(rlc_logger),
gtpu(&task_sched, gtpu_logger, &rx_sockets),
s1ap(&task_sched, s1ap_logger, &rx_sockets),
gtpu(&task_sched, gtpu_logger, &get_rx_io_manager()),
s1ap(&task_sched, s1ap_logger, &get_rx_io_manager()),
rrc(&task_sched, bearers),
mac_pcap(),
pending_stack_metrics(64)
@ -238,7 +238,7 @@ void enb_stack_lte::stop()
void enb_stack_lte::stop_impl()
{
rx_sockets.stop();
get_rx_io_manager().stop();
s1ap.stop();
gtpu.stop();

@ -28,5 +28,3 @@ set(SOURCES mac.cc ue.cc sched.cc sched_carrier.cc sched_grid.cc sched_ue_ctrl/s
sched_helpers.cc)
add_library(srsenb_mac STATIC ${SOURCES} $<TARGET_OBJECTS:mac_schedulers>)
target_link_libraries(srsenb_mac srsenb_mac_common)
add_subdirectory(nr)

@ -1,114 +0,0 @@
/**
* Copyright 2013-2021 Software Radio Systems Limited
*
* This file is part of srsRAN.
*
* srsRAN 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.
*
* srsRAN 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 "srsenb/hdr/stack/mac/nr/sched_nr_signalling.h"
#define POS_IN_BURST_FIRST_BIT_IDX 0
#define POS_IN_BURST_SECOND_BIT_IDX 1
#define POS_IN_BURST_THIRD_BIT_IDX 2
#define POS_IN_BURST_FOURTH_BIT_IDX 3
#define DEFAULT_SSB_PERIODICITY 5
namespace srsenb {
namespace sched_nr_impl {
void sched_nzp_csi_rs(srsran::const_span<srsran_csi_rs_nzp_set_t> nzp_csi_rs_sets_cfg,
const srsran_slot_cfg_t& slot_cfg,
nzp_csi_rs_list& csi_rs_list)
{
for (const srsran_csi_rs_nzp_set_t& set : nzp_csi_rs_sets_cfg) {
// For each NZP-CSI-RS resource available in the set
for (uint32_t i = 0; i < set.count; ++i) {
// Select resource
const srsran_csi_rs_nzp_resource_t& nzp_csi_resource = set.data[i];
// Check if the resource is scheduled for this slot
if (srsran_csi_rs_send(&nzp_csi_resource.periodicity, &slot_cfg)) {
if (csi_rs_list.full()) {
srslog::fetch_basic_logger("MAC-NR").error("SCHED: Failed to allocate NZP-CSI RS");
return;
}
csi_rs_list.push_back(nzp_csi_resource);
}
}
}
}
void sched_ssb_basic(const slot_point& sl_point, uint32_t ssb_periodicity, ssb_list& ssb_list)
{
if (ssb_list.full()) {
srslog::fetch_basic_logger("MAC-NR").error("SCHED: Failed to allocate SSB");
return;
}
// If the periodicity is 0, it means that the parameter was not passed by the upper layers.
// In that case, we use default value of 5ms (see Clause 4.1, TS 38.213)
if (ssb_periodicity == 0) {
ssb_periodicity = DEFAULT_SSB_PERIODICITY;
}
uint32_t sl_cnt = sl_point.to_uint();
// Perform mod operation of slot index by ssb_periodicity;
// "ssb_periodicity * nof_slots_per_subframe" gives the number of slots in 1 ssb_periodicity time interval
uint32_t sl_point_mod = sl_cnt % (ssb_periodicity * (uint32_t)sl_point.nof_slots_per_subframe());
// code below is simplified, it assumes 15kHz subcarrier spacing and sub 3GHz carrier
if (sl_point_mod == 0) {
ssb_t ssb_msg = {};
srsran_mib_nr_t mib_msg = {};
mib_msg.sfn = sl_point.sfn();
mib_msg.hrf = (sl_point.slot_idx() % SRSRAN_NSLOTS_PER_FRAME_NR(srsran_subcarrier_spacing_15kHz) >=
SRSRAN_NSLOTS_PER_FRAME_NR(srsran_subcarrier_spacing_15kHz) / 2);
// This corresponds to "Position in Burst" = 1000
mib_msg.ssb_idx = 0;
// Setting the following 4 parameters is redundant, but it makes it explicit what values are passed to PHY
mib_msg.dmrs_typeA_pos = srsran_dmrs_sch_typeA_pos_2;
mib_msg.scs_common = srsran_subcarrier_spacing_15kHz;
mib_msg.coreset0_idx = 0;
mib_msg.ss0_idx = 0;
// Pack mib message to be sent to PHY
int packing_ret_code = srsran_pbch_msg_nr_mib_pack(&mib_msg, &ssb_msg.pbch_msg);
srsran_assert(packing_ret_code == SRSRAN_SUCCESS, "SSB packing returned en error");
ssb_list.push_back(ssb_msg);
}
}
void sched_dl_signalling(const bwp_params_t& bwp_params,
slot_point sl_pdcch,
ssb_list& ssb_list,
nzp_csi_rs_list& nzp_csi_rs)
{
srsran_slot_cfg_t cfg;
cfg.idx = sl_pdcch.to_uint();
// Schedule SSB
sched_ssb_basic(sl_pdcch, bwp_params.cell_cfg.ssb.periodicity_ms, ssb_list);
// Schedule NZP-CSI-RS
sched_nzp_csi_rs(bwp_params.cfg.pdsch.nzp_csi_rs_sets, cfg, nzp_csi_rs);
// Schedule SIBs
// TODO
}
} // namespace sched_nr_impl
} // namespace srsenb

@ -21,5 +21,3 @@
set(SOURCES rrc.cc rrc_ue.cc rrc_mobility.cc rrc_cell_cfg.cc rrc_bearer_cfg.cc mac_controller.cc ue_rr_cfg.cc ue_meas_cfg.cc rrc_endc.cc)
add_library(srsenb_rrc STATIC ${SOURCES})
set(SOURCES rrc_nr.cc nr/cell_asn1_config.cc)
add_library(srsgnb_rrc STATIC ${SOURCES})

@ -643,7 +643,7 @@ void rrc::sgnb_release_ack(uint16_t eutra_rnti)
void rrc::parse_ul_ccch(ue& ue, srsran::unique_byte_buffer_t pdu)
{
srsran_assert(pdu != nullptr, "parse_ul_ccch called for empty message");
srsran_assert(pdu != nullptr, "handle_ul_ccch called for empty message");
ul_ccch_msg_s ul_ccch_msg;
asn1::cbit_ref bref(pdu->msg, pdu->N_bytes);
@ -676,7 +676,7 @@ void rrc::parse_ul_ccch(ue& ue, srsran::unique_byte_buffer_t pdu)
///< User mutex must be hold by caller
void rrc::parse_ul_dcch(ue& ue, uint32_t lcid, srsran::unique_byte_buffer_t pdu)
{
srsran_assert(pdu != nullptr, "parse_ul_dcch called for empty message");
srsran_assert(pdu != nullptr, "handle_ul_dcch called for empty message");
ue.parse_ul_dcch(lcid, std::move(pdu));
}

@ -573,6 +573,10 @@ bool s1ap::handle_mme_rx_msg(srsran::unique_byte_buffer_t pdu,
logger.info("SCTP peer addres unreachable. Association: %d", sri.sinfo_assoc_id);
srsran::console("SCTP peer address unreachable. Association: %d\n", sri.sinfo_assoc_id);
restart_s1 = true;
} else if (notification->sn_header.sn_type == SCTP_REMOTE_ERROR) {
logger.info("SCTP remote error. Association: %d", sri.sinfo_assoc_id);
srsran::console("SCTP remote error. Association: %d\n", sri.sinfo_assoc_id);
restart_s1 = true;
} else if (notification->sn_header.sn_type == SCTP_ASSOC_CHANGE) {
logger.info("SCTP association changed. Association: %d", sri.sinfo_assoc_id);
srsran::console("SCTP association changed. Association: %d\n", sri.sinfo_assoc_id);

@ -21,6 +21,3 @@
set(SOURCES gtpu.cc pdcp.cc rlc.cc)
add_library(srsenb_upper STATIC ${SOURCES})
target_link_libraries(srsenb_upper srsran_asn1 srsran_gtpu)
set(SOURCES sdap.cc)
add_library(srsgnb_upper STATIC ${SOURCES})

@ -23,7 +23,6 @@ add_subdirectory(phy)
add_subdirectory(upper)
add_subdirectory(rrc)
add_subdirectory(s1ap)
add_subdirectory(ngap)
add_executable(enb_metrics_test enb_metrics_test.cc ../src/metrics_stdout.cc ../src/metrics_csv.cc)
target_link_libraries(enb_metrics_test srsran_phy srsran_common)

@ -86,5 +86,3 @@ add_test(sched_cqi_test sched_cqi_test)
add_executable(sched_phy_resource_test sched_phy_resource_test.cc)
target_link_libraries(sched_phy_resource_test srsran_common srsenb_mac srsran_mac sched_test_common)
add_test(sched_phy_resource_test sched_phy_resource_test)
add_subdirectory(nr)

@ -21,10 +21,6 @@
add_library(test_helpers test_helpers.cc)
target_link_libraries(test_helpers srsenb_rrc srsenb_common rrc_asn1 rrc_nr_asn1 s1ap_asn1 srsran_common enb_cfg_parser ${LIBCONFIGPP_LIBRARIES})
add_executable(rrc_nr_test rrc_nr_test.cc)
target_link_libraries(rrc_nr_test srsgnb_rrc test_helpers ${ATOMIC_LIBS})
add_test(rrc_nr_test rrc_nr_test -i ${CMAKE_CURRENT_SOURCE_DIR}/../..)
add_executable(rrc_meascfg_test rrc_meascfg_test.cc)
target_link_libraries(rrc_meascfg_test test_helpers ${ATOMIC_LIBS})

@ -1,132 +0,0 @@
/**
* Copyright 2013-2021 Software Radio Systems Limited
*
* This file is part of srsRAN.
*
* srsRAN 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.
*
* srsRAN 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 "srsenb/hdr/enb.h"
#include "srsenb/hdr/stack/rrc/rrc_nr.h"
#include "srsenb/test/common/dummy_classes_common.h"
#include "srsenb/test/common/dummy_classes_nr.h"
#include "srsenb/test/rrc/test_helpers.h"
#include "srsran/common/test_common.h"
#include "srsran/interfaces/gnb_rrc_nr_interfaces.h"
#include <iostream>
using namespace asn1::rrc_nr;
namespace srsenb {
int test_cell_cfg(const srsenb::sched_interface::cell_cfg_t& cellcfg)
{
// SIB1 must exist and have period 16rf
TESTASSERT(cellcfg.sibs[0].len > 0);
TESTASSERT(cellcfg.sibs[0].period_rf == 16);
TESTASSERT(cellcfg.si_window_ms > 0);
return SRSRAN_SUCCESS;
}
/*
* Test 1 - Test default SIB generation
* Description: Check whether the SIBs were set correctly
*/
int test_sib_generation()
{
srsran::task_scheduler task_sched;
mac_nr_dummy mac_obj;
rlc_dummy rlc_obj;
pdcp_dummy pdcp_obj;
rrc_nr rrc_obj(&task_sched);
// set cfg
rrc_nr_cfg_t default_cfg = {};
rrc_nr_cfg_t rrc_cfg = rrc_obj.update_default_cfg(default_cfg);
auto& sched_elem = rrc_cfg.sib1.si_sched_info.sched_info_list[0];
TESTASSERT(rrc_obj.init(rrc_cfg, nullptr, &mac_obj, &rlc_obj, &pdcp_obj, nullptr, nullptr, nullptr) ==
SRSRAN_SUCCESS);
TESTASSERT(test_cell_cfg(mac_obj.cellcfgobj) == SRSRAN_SUCCESS);
// TEMP tests
TESTASSERT(mac_obj.cellcfgobj.sibs[1].len > 0);
TESTASSERT(mac_obj.cellcfgobj.sibs[1].period_rf == sched_elem.si_periodicity.to_number());
for (int i = 2; i < 16; ++i) {
TESTASSERT(mac_obj.cellcfgobj.sibs[i].len == 0);
}
TESTASSERT(mac_obj.cellcfgobj.cell.nof_prb == 25);
return SRSRAN_SUCCESS;
}
int test_rrc_setup()
{
srsran::task_scheduler task_sched;
phy_nr_dummy phy_obj;
mac_nr_dummy mac_obj;
rlc_dummy rlc_obj;
pdcp_dummy pdcp_obj;
rrc_nr rrc_obj(&task_sched);
// set cfg
all_args_t args{};
phy_cfg_t phy_cfg{};
rrc_nr_cfg_t rrc_cfg_nr = rrc_obj.update_default_cfg(rrc_nr_cfg_t{});
rrc_cfg_nr.cell_list.emplace_back();
rrc_cfg_nr.cell_list[0].phy_cell.carrier.pci = 500;
rrc_cfg_nr.cell_list[0].dl_arfcn = 634240;
rrc_cfg_nr.cell_list[0].band = 78;
args.enb.n_prb = 50;
enb_conf_sections::set_derived_args_nr(&args, &rrc_cfg_nr, &phy_cfg);
TESTASSERT(rrc_obj.init(rrc_cfg_nr, &phy_obj, &mac_obj, &rlc_obj, &pdcp_obj, nullptr, nullptr, nullptr) ==
SRSRAN_SUCCESS);
for (uint32_t n = 0; n < 2; ++n) {
uint32_t timeout = 5500;
for (uint32_t i = 0; i < timeout and rlc_obj.last_sdu == nullptr; ++i) {
task_sched.tic();
}
// TODO: trigger proper RRC Setup procedure (not timer based)
// TESTASSERT(rlc_obj.last_sdu != nullptr);
}
return SRSRAN_SUCCESS;
}
} // namespace srsenb
int main(int argc, char** argv)
{
auto& logger = srslog::fetch_basic_logger("ASN1");
logger.set_level(srslog::basic_levels::info);
srslog::init();
if (argc < 3) {
argparse::usage(argv[0]);
return -1;
}
argparse::parse_args(argc, argv);
// FIXME: disabled temporarily until SIB generation is fixed
// TESTASSERT(srsenb::test_sib_generation() == SRSRAN_SUCCESS);
TESTASSERT(srsenb::test_rrc_setup() == SRSRAN_SUCCESS);
return SRSRAN_SUCCESS;
}

@ -23,6 +23,8 @@
#include "srsenb/hdr/enb.h"
#include "srsran/common/test_common.h"
using namespace asn1::rrc;
namespace argparse {
std::string repository_dir;

@ -28,7 +28,6 @@
#include "srsran/adt/span.h"
using namespace srsenb;
using namespace asn1::rrc;
namespace argparse {
@ -238,9 +237,9 @@ namespace srsenb {
meas_cell_cfg_t generate_cell1();
report_cfg_eutra_s generate_rep1();
asn1::rrc::report_cfg_eutra_s generate_rep1();
bool is_cell_cfg_equal(const meas_cell_cfg_t& cfg, const cells_to_add_mod_s& cell);
bool is_cell_cfg_equal(const meas_cell_cfg_t& cfg, const asn1::rrc::cells_to_add_mod_s& cell);
} // namespace srsenb

@ -0,0 +1,9 @@
#
# 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.
#
add_subdirectory(src)

@ -24,14 +24,37 @@
#include "srsran/interfaces/gnb_interfaces.h"
#include "srsran/interfaces/gnb_mac_interfaces.h"
#include "srsran/interfaces/gnb_ngap_interfaces.h"
namespace srsenb {
class ngap_dummy : public ngap_interface_rrc_nr
{
void initial_ue(uint16_t rnti,
uint32_t gnb_cc_idx,
asn1::ngap_nr::rrcestablishment_cause_e cause,
srsran::unique_byte_buffer_t pdu)
{}
void initial_ue(uint16_t rnti,
uint32_t gnb_cc_idx,
asn1::ngap_nr::rrcestablishment_cause_e cause,
srsran::unique_byte_buffer_t pdu,
uint32_t m_tmsi)
{}
void write_pdu(uint16_t rnti, srsran::unique_byte_buffer_t pdu) {}
bool user_exists(uint16_t rnti) { return true; }
void user_mod(uint16_t old_rnti, uint16_t new_rnti) {}
bool user_release(uint16_t rnti, asn1::ngap_nr::cause_radio_network_e cause_radio) { return true; }
bool is_amf_connected() { return true; }
void ue_notify_rrc_reconf_complete(uint16_t rnti, bool outcome) {}
};
class rrc_nr_dummy : public rrc_interface_mac_nr
{
public:
int read_pdu_bcch_bch(const uint32_t tti, srsran::unique_byte_buffer_t& buffer) { return SRSRAN_SUCCESS; }
int read_pdu_bcch_dlsch(uint32_t sib_index, srsran::unique_byte_buffer_t& buffer) { return SRSRAN_SUCCESS; }
int read_pdu_bcch_bch(const uint32_t tti, srsran::byte_buffer_t& buffer) { return SRSRAN_SUCCESS; }
int read_pdu_bcch_dlsch(uint32_t sib_index, srsran::byte_buffer_t& buffer) { return SRSRAN_SUCCESS; }
int add_user(uint16_t rnti, const sched_nr_ue_cfg_t& uecfg) { return SRSRAN_SUCCESS; }
int update_user(uint16_t new_rnti, uint16_t old_rnti) { return SRSRAN_SUCCESS; }
void set_activity_user(uint16_t rnti) {}
@ -48,14 +71,18 @@ public:
class mac_nr_dummy : public mac_interface_rrc_nr
{
public:
int cell_cfg(const std::vector<srsenb::sched_nr_interface::cell_cfg_t>& nr_cells) override { return SRSRAN_SUCCESS; }
int cell_cfg(const std::vector<srsenb::sched_nr_interface::cell_cfg_t>& nr_cells_) override
{
nr_cells = nr_cells_;
return SRSRAN_SUCCESS;
}
uint16_t reserve_rnti(uint32_t enb_cc_idx, const sched_nr_ue_cfg_t& uecfg) override { return 0x4601; }
int ue_cfg(uint16_t rnti, const sched_nr_interface::ue_cfg_t& ue_cfg) override { return SRSRAN_SUCCESS; }
int remove_ue(uint16_t rnti) override { return SRSRAN_SUCCESS; }
srsenb::sched_interface::cell_cfg_t cellcfgobj;
std::vector<srsenb::sched_nr_interface::cell_cfg_t> nr_cells;
};
class phy_nr_dummy : public phy_interface_stack_nr

@ -27,20 +27,24 @@
#ifndef SRSRAN_GNB_STACK_NR_H
#define SRSRAN_GNB_STACK_NR_H
#include "srsenb/hdr/stack/mac/nr/mac_nr.h"
#include "srsenb/hdr/stack/rrc/rrc_nr.h"
#include "upper/pdcp.h"
#include "upper/rlc.h"
#include "upper/sdap.h"
#include "srsenb/hdr/stack/upper/pdcp.h"
#include "srsenb/hdr/stack/upper/rlc.h"
#include "srsgnb/hdr/stack/mac/mac_nr.h"
#include "srsgnb/hdr/stack/rrc/rrc_nr.h"
#include "srsgnb/hdr/stack/sdap/sdap.h"
#include "enb_stack_base.h"
#include "srsenb/hdr/stack/enb_stack_base.h"
#include "srsran/interfaces/gnb_interfaces.h"
namespace srsenb {
class ngap;
class gtpu;
struct gnb_stack_args_t {
stack_log_args_t log;
mac_nr_args_t mac;
ngap_args_t ngap;
};
class gnb_stack_nr final : public srsenb::enb_stack_base,
@ -130,6 +134,8 @@ private:
srslog::basic_logger& mac_logger;
srslog::basic_logger& rlc_logger;
srslog::basic_logger& pdcp_logger;
srslog::basic_logger& ngap_logger;
srslog::basic_logger& gtpu_logger;
srslog::basic_logger& stack_logger;
// task scheduling
@ -142,11 +148,13 @@ private:
std::mutex metrics_mutex;
std::condition_variable metrics_cvar;
// derived
srsenb::mac_nr mac;
srsenb::rlc rlc;
srsenb::pdcp pdcp;
srsenb::rrc_nr rrc;
// layers
srsenb::mac_nr mac;
srsenb::rlc rlc;
srsenb::pdcp pdcp;
srsenb::rrc_nr rrc;
std::unique_ptr<srsenb::ngap> ngap;
std::unique_ptr<srsenb::gtpu> gtpu;
// std::unique_ptr<sdap> m_sdap;
// state

@ -27,7 +27,7 @@
#include "srsenb/hdr/common/rnti_pool.h"
#include "srsenb/hdr/stack/enb_stack_base.h"
#include "srsenb/hdr/stack/mac/nr/ue_nr.h"
#include "srsgnb/hdr/stack/mac/ue_nr.h"
#include "srsran/common/task_scheduler.h"
#include "srsran/interfaces/enb_metrics_interface.h"
#include "srsran/interfaces/enb_rlc_interfaces.h"
@ -44,6 +44,7 @@ struct mac_nr_args_t {
};
class sched_nr;
class mac_nr_rx;
class mac_nr final : public mac_interface_phy_nr, public mac_interface_rrc_nr, public mac_interface_rlc_nr
{
@ -91,17 +92,15 @@ private:
bool is_rnti_active_nolock(uint16_t rnti);
// handle UCI data from either PUCCH or PUSCH
bool handle_uci_data(const uint16_t rnti, const srsran_uci_cfg_nr_t& cfg, const srsran_uci_value_nr_t& value);
// PDU processing
int handle_pdu(srsran::unique_byte_buffer_t pdu);
bool handle_uci_data(uint16_t rnti, const srsran_uci_cfg_nr_t& cfg, const srsran_uci_value_nr_t& value);
// Metrics processing
void get_metrics_nolock(srsenb::mac_metrics_t& metrics);
// Encoding
srsran::byte_buffer_t* assemble_rar(srsran::const_span<sched_nr_interface::msg3_grant_t> grants);
srsran::unique_byte_buffer_t rar_pdu_buffer = nullptr;
srsran::byte_buffer_t* assemble_rar(srsran::const_span<sched_nr_interface::msg3_grant_t> grants);
srsran::unique_byte_buffer_t rar_pdu_buffer;
// Interaction with other components
phy_interface_stack_nr* phy = nullptr;
@ -110,8 +109,8 @@ private:
rrc_interface_mac_nr* rrc = nullptr;
// args
srsran::task_sched_handle task_sched;
srsran::task_multiqueue::queue_handle stack_task_queue;
srsran::task_sched_handle task_sched;
srsran::task_queue_handle stack_task_queue;
std::unique_ptr<srsran::mac_pcap> pcap = nullptr;
mac_nr_args_t args = {};
@ -141,6 +140,9 @@ private:
// Number of rach preambles detected for a CC
std::vector<uint32_t> detected_rachs;
// Decoding of UL PDUs
std::unique_ptr<mac_nr_rx> rx;
};
} // namespace srsenb

@ -24,36 +24,13 @@
#include "sched_nr_cfg.h"
#include "sched_nr_grant_allocator.h"
#include "sched_nr_signalling.h"
#include "sched_nr_time_rr.h"
#include "srsran/adt/pool/cached_alloc.h"
namespace srsenb {
namespace sched_nr_impl {
/// SIB scheduler
class si_sched
{
public:
explicit si_sched(const bwp_params_t& bwp_cfg_);
void run_slot(bwp_slot_allocator& slot_alloc);
private:
const bwp_params_t* bwp_cfg = nullptr;
srslog::basic_logger& logger;
struct sched_si_t {
uint32_t n = 0;
uint32_t len = 0;
uint32_t win_len = 0;
uint32_t period = 0;
uint32_t n_tx = 0;
alloc_result result = alloc_result::invalid_coderate;
slot_point win_start;
};
srsran::bounded_vector<sched_si_t, 10> pending_sis;
};
using dl_sched_rar_info_t = sched_nr_interface::rar_info_t;
/// RAR/Msg3 scheduler
@ -96,6 +73,7 @@ public:
const bwp_params_t* cfg;
// channel-specific schedulers
si_sched si;
ra_sched ra;
std::unique_ptr<sched_nr_base> data_sched;

@ -25,6 +25,7 @@
#include "sched_nr_interface.h"
#include "sched_nr_rb.h"
#include "srsenb/hdr/common/common_enb.h"
#include "srsran/adt/optional_array.h"
namespace srsenb {
@ -100,6 +101,8 @@ struct bwp_params_t {
bwp_cce_pos_list rar_cce_list;
srsran::optional_vector<bwp_cce_pos_list> common_cce_list;
bwp_params_t(const cell_cfg_t& cell, const sched_args_t& sched_cfg_, uint32_t cc, uint32_t bwp_id);
};

@ -22,12 +22,12 @@
#ifndef SRSRAN_SCHED_NR_GRANT_ALLOCATOR_H
#define SRSRAN_SCHED_NR_GRANT_ALLOCATOR_H
#include "../sched_common.h"
#include "lib/include/srsran/adt/circular_array.h"
#include "sched_nr_helpers.h"
#include "sched_nr_interface.h"
#include "sched_nr_pdcch.h"
#include "sched_nr_ue.h"
#include "srsenb/hdr/stack/mac/sched_common.h"
namespace srsenb {
namespace sched_nr_impl {
@ -99,7 +99,11 @@ class bwp_slot_allocator
public:
explicit bwp_slot_allocator(bwp_res_grid& bwp_grid_, slot_point pdcch_slot_, slot_ue_map_t& ues_);
alloc_result alloc_si(uint32_t aggr_idx, uint32_t si_idx, uint32_t si_ntx, const prb_interval& prbs);
alloc_result alloc_si(uint32_t aggr_idx,
uint32_t si_idx,
uint32_t si_ntx,
const prb_interval& prbs,
tx_harq_softbuffer& softbuffer);
alloc_result alloc_rar_and_msg3(uint16_t ra_rnti,
uint32_t aggr_idx,
prb_interval interv,
@ -107,11 +111,14 @@ public:
alloc_result alloc_pdsch(slot_ue& ue, const prb_grant& dl_grant);
alloc_result alloc_pusch(slot_ue& ue, const prb_grant& dl_mask);
slot_point get_pdcch_tti() const { return pdcch_slot; }
slot_point get_tti_rx() const { return pdcch_slot - TX_ENB_DELAY; }
const bwp_res_grid& res_grid() const { return bwp_grid; }
slot_point get_pdcch_tti() const { return pdcch_slot; }
slot_point get_tti_rx() const { return pdcch_slot - TX_ENB_DELAY; }
const bwp_res_grid& res_grid() const { return bwp_grid; }
const bwp_slot_grid& tx_slot_grid() const { return bwp_grid[pdcch_slot]; }
bwp_slot_grid& tx_slot_grid() { return bwp_grid[pdcch_slot]; }
const bwp_params_t& cfg;
srslog::basic_logger& logger;
const bwp_params_t& cfg;
private:
alloc_result
@ -119,8 +126,7 @@ private:
alloc_result verify_pusch_space(bwp_slot_grid& pusch_grid, bwp_slot_grid* pdcch_grid = nullptr) const;
alloc_result verify_ue_cfg(const ue_carrier_params_t& ue_cfg, harq_proc* harq) const;
srslog::basic_logger& logger;
bwp_res_grid& bwp_grid;
bwp_res_grid& bwp_grid;
slot_point pdcch_slot;
slot_ue_map_t& slot_ues;

@ -23,7 +23,7 @@
#define SRSRAN_SCHED_NR_HARQ_H
#include "sched_nr_cfg.h"
#include "srsenb/hdr/stack/mac/nr/harq_softbuffer.h"
#include "srsgnb/hdr/stack/mac/harq_softbuffer.h"
#include "srsran/common/slot_point.h"
#include <array>

@ -34,6 +34,12 @@ struct bwp_res_grid;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
bool fill_dci_sib(prb_interval interv,
uint32_t sib_idx,
uint32_t si_ntx,
const bwp_params_t& bwp_cfg,
srsran_dci_dl_nr_t& dci);
bool fill_dci_rar(prb_interval interv, uint16_t ra_rnti, const bwp_params_t& bwp_cfg, srsran_dci_dl_nr_t& dci);
bool fill_dci_msg3(const slot_ue& ue, const bwp_params_t& bwp_cfg, srsran_dci_ul_nr_t& dci);

@ -59,6 +59,7 @@ class sched_nr_interface
{
public:
static const size_t MAX_GRANTS = mac_interface_phy_nr::MAX_GRANTS;
static const size_t MAX_SIBS = 2;
///// Configuration /////
@ -72,11 +73,18 @@ public:
uint32_t numerology_idx = 0;
};
struct cell_cfg_sib_t {
uint32_t len;
uint32_t period_rf;
uint32_t si_window_slots;
};
struct cell_cfg_t {
srsran_carrier_nr_t carrier = {};
srsran_duplex_config_nr_t duplex = {};
srsran::phy_cfg_nr_t::ssb_cfg_t ssb = {};
srsran::bounded_vector<bwp_cfg_t, SCHED_NR_MAX_BWP_PER_CELL> bwps{1}; // idx0 for BWP-common
srsran::bounded_vector<cell_cfg_sib_t, MAX_SIBS> sibs;
};
struct sched_args_t {
@ -91,7 +99,7 @@ public:
using ue_cc_cfg_t = sched_nr_ue_cc_cfg_t;
using ue_cfg_t = sched_nr_ue_cfg_t;
////// RA procedure //////
////// RA signalling //////
struct rar_info_t {
uint32_t preamble_idx; // is this the RAPID?
@ -119,6 +127,8 @@ public:
struct dl_res_t {
dl_sched_t phy;
sched_rar_list_t rar;
srsran::bounded_vector<uint32_t, MAX_GRANTS> sib_idxs;
};
virtual ~sched_nr_interface() = default;

@ -22,7 +22,7 @@
#ifndef SRSRAN_SCHED_NR_PDCCH_H
#define SRSRAN_SCHED_NR_PDCCH_H
#include "srsenb/hdr/stack/mac/nr/sched_nr_cfg.h"
#include "srsgnb/hdr/stack/mac/sched_nr_cfg.h"
#include "srsran/adt/bounded_bitset.h"
#include "srsran/adt/bounded_vector.h"
#include "srsran/phy/common/phy_common_nr.h"
@ -72,7 +72,9 @@ private:
uint32_t coreset_id;
uint32_t slot_idx;
uint32_t nof_freq_res = 0;
const bwp_cce_pos_list& rar_cce_list;
const bwp_cce_pos_list& rar_cce_list;
const srsran::optional_vector<bwp_cce_pos_list>& common_cce_list;
// List of PDCCH grants
struct alloc_record {

@ -22,7 +22,7 @@
#ifndef SRSRAN_SCHED_NR_RB_H
#define SRSRAN_SCHED_NR_RB_H
#include "srsenb/hdr/stack/mac/nr/sched_nr_interface.h"
#include "srsgnb/hdr/stack/mac/sched_nr_interface.h"
#include "srsran/adt/bounded_bitset.h"
#include "srsran/phy/common/phy_common_nr.h"

@ -22,12 +22,16 @@
#ifndef SRSRAN_SCHED_NR_SIGNALLING_H
#define SRSRAN_SCHED_NR_SIGNALLING_H
#include "harq_softbuffer.h"
#include "sched_nr_cfg.h"
#include "sched_nr_interface.h"
#include "srsenb/hdr/stack/mac/sched_common.h"
namespace srsenb {
namespace sched_nr_impl {
class bwp_slot_allocator;
/// Schedule NZP-CSI-RS resources for given slot
void sched_nzp_csi_rs(srsran::const_span<srsran_csi_rs_nzp_set_t> nzp_csi_rs_sets,
const srsran_slot_cfg_t& slot_cfg,
@ -50,11 +54,39 @@ void sched_nzp_csi_rs(srsran::const_span<srsran_csi_rs_nzp_set_t> nzp_csi_rs_set
*/
void sched_ssb_basic(const slot_point& sl_point, uint32_t ssb_periodicity, ssb_list& ssb_list);
/// Fill DCI fields with SIB info
bool fill_dci_sib(prb_interval interv, uint32_t sib_idx, const bwp_params_t& bwp_cfg, srsran_dci_dl_nr_t& dci);
/// For a given BWP and slot, schedule SSB, NZP CSI RS and SIBs
void sched_dl_signalling(const bwp_params_t& bwp_params,
slot_point sl_pdcch,
ssb_list& ssb_list,
nzp_csi_rs_list& nzp_csi_rs);
void sched_dl_signalling(bwp_slot_allocator& bwp_alloc);
/// scheduler for SIBs
class si_sched
{
public:
explicit si_sched(const bwp_params_t& bwp_cfg_);
void run_slot(bwp_slot_allocator& slot_alloc);
private:
const bwp_params_t* bwp_cfg = nullptr;
srslog::basic_logger& logger;
struct si_msg_ctxt_t {
// args
uint32_t n = 0; /// 0 for SIB1, n/index in schedulingInfoList in si-SchedulingInfo in SIB1
uint32_t len_bytes = 0; /// length in bytes of SIB1 / SI message
uint32_t win_len_slots = 0; /// window length in slots
uint32_t period_frames = 0; /// periodicity of SIB1/SI window in frames
// state
uint32_t n_tx = 0; /// nof transmissions of the same SIB1 / SI message
alloc_result result = alloc_result::invalid_coderate; /// last attempt to schedule SI
slot_point win_start; /// start of SI window, invalid if outside
srsran::unique_pool_ptr<tx_harq_softbuffer> si_softbuffer;
};
srsran::bounded_vector<si_msg_ctxt_t, 10> pending_sis; /// configured SIB1 and SI messages
};
} // namespace sched_nr_impl
} // namespace srsenb

@ -23,7 +23,7 @@
#define SRSENB_UE_NR_H
#include "srsenb/hdr/stack/mac/common/mac_metrics.h"
#include "srsenb/hdr/stack/mac/nr/sched_nr_interface.h"
#include "srsgnb/hdr/stack/mac/sched_nr_interface.h"
#include "srsran/common/block_queue.h"
#include "srsran/common/interfaces_common.h"
#include "srsran/interfaces/enb_rlc_interfaces.h"
@ -59,7 +59,6 @@ public:
bool is_active() const { return active_state.load(std::memory_order_relaxed); }
int generate_pdu(srsran::byte_buffer_t* pdu, uint32_t grant_size);
int process_pdu(srsran::unique_byte_buffer_t pdu);
std::mutex metrics_mutex = {};
void metrics_read(mac_ue_metrics_t* metrics_);
@ -78,10 +77,6 @@ public:
uint32_t read_pdu(uint32_t lcid, uint8_t* payload, uint32_t requested_bytes) final;
private:
// helper methods
uint32_t buff_size_field_to_bytes(uint32_t buff_size_index, const srsran::bsr_format_nr_t& format);
int process_ce_subpdu(const srsran::mac_sch_subpdu_nr& subpdu);
rlc_interface_mac* rlc = nullptr;
rrc_interface_mac_nr* rrc = nullptr;
phy_interface_stack_nr* phy = nullptr;

@ -35,16 +35,16 @@
#include "srsran/common/standard_streams.h"
#include "srsran/common/task_scheduler.h"
#include "srsran/common/threads.h"
#include "srsran/interfaces/enb_gtpu_interfaces.h"
#include "srsran/interfaces/gnb_ngap_interfaces.h"
#include "srsran/interfaces/gnb_rrc_nr_interfaces.h"
#include "srsran/interfaces/enb_gtpu_interfaces.h"
#include "srsran/srslog/srslog.h"
#include <iostream>
#include <unordered_map>
namespace srsenb {
class ngap : public ngap_interface_rrc_nr
class ngap final : public ngap_interface_rrc_nr
{
public:
class ue;
@ -79,7 +79,7 @@ public:
// Stack interface
bool
handle_amf_rx_msg(srsran::unique_byte_buffer_t pdu, const sockaddr_in& from, const sctp_sndrcvinfo& sri, int flags);
handle_amf_rx_msg(srsran::unique_byte_buffer_t pdu, const sockaddr_in& from, const sctp_sndrcvinfo& sri, int flags);
void get_metrics(ngap_metrics_t& m);
void get_args(ngap_args_t& args_);

@ -25,7 +25,7 @@
#include "ngap_interfaces.h"
#include "ngap_ue_utils.h"
#include "srsenb/hdr/stack/ngap/ngap_ue_bearer_manager.h"
#include "srsgnb/hdr/stack/ngap/ngap_ue_bearer_manager.h"
#include "srsran/asn1/asn1_utils.h"
#include "srsran/asn1/ngap.h"
#include "srsran/common/buffer_pool.h"

@ -29,6 +29,9 @@ namespace srsenb {
int fill_sp_cell_cfg_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, asn1::rrc_nr::sp_cell_cfg_s& sp_cell);
}
int fill_mib_from_enb_cfg(const rrc_nr_cfg_t& cfg, asn1::rrc_nr::mib_s& mib);
int fill_sib1_from_enb_cfg(const rrc_nr_cfg_t& cfg, asn1::rrc_nr::sib1_s& sib1);
} // namespace srsenb
#endif // SRSRAN_CELL_ASN1_CONFIG_H

@ -0,0 +1,64 @@
/**
*
* \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_RRC_CONFIG_NR_H
#define SRSRAN_RRC_CONFIG_NR_H
#include "srsenb/hdr/phy/phy_interfaces.h"
#include "srsenb/hdr/stack/rrc/rrc_config_common.h"
#include "srsran/asn1/rrc_nr.h"
#include "srsran/interfaces/gnb_rrc_nr_interfaces.h"
#include "srsue/hdr/phy/phy_common.h"
namespace srsenb {
// TODO: Make this common to NR and LTE
struct rrc_nr_cfg_sr_t {
uint32_t period;
// asn1::rrc::sched_request_cfg_c::setup_s_::dsr_trans_max_e_ dsr_max;
uint32_t nof_prb;
uint32_t sf_mapping[80];
uint32_t nof_subframes;
};
// Cell/Sector configuration for NR cells
struct rrc_cell_cfg_nr_t {
phy_cell_cfg_nr_t phy_cell; // already contains all PHY-related parameters (i.e. RF port, PCI, etc.)
uint32_t tac; // Tracking area code
uint32_t dl_arfcn; // DL freq already included in phy_cell
uint32_t ul_arfcn; // UL freq also in phy_cell
uint32_t dl_absolute_freq_point_a; // derived from DL ARFCN
uint32_t ul_absolute_freq_point_a; // derived from UL ARFCN
uint32_t ssb_absolute_freq_point; // derived from DL ARFCN
uint32_t band;
srsran_duplex_mode_t duplex_mode;
srsran_ssb_cfg_t ssb_cfg;
};
typedef std::vector<rrc_cell_cfg_nr_t> rrc_cell_list_nr_t;
struct rrc_nr_cfg_t {
rrc_nr_cfg_sr_t sr_cfg;
rrc_cfg_cqi_t cqi_cfg;
rrc_cell_list_nr_t cell_list;
asn1::rrc_nr::rach_cfg_common_s rach_cfg_common;
uint16_t prach_root_seq_idx_type;
bool is_standalone;
std::string log_name = "RRC-NR";
std::string log_level;
uint32_t log_hex_limit;
};
} // namespace srsenb
#endif // SRSRAN_RRC_CONFIG_NR_H

@ -0,0 +1,174 @@
/**
*
* \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 SRSENB_RRC_NR_H
#define SRSENB_RRC_NR_H
#include "srsenb/hdr/stack/enb_stack_base.h"
#include "srsenb/hdr/stack/rrc/rrc_config_common.h"
#include "srsenb/hdr/stack/rrc/rrc_metrics.h"
#include "srsgnb/hdr/stack/rrc/rrc_config_nr.h"
#include "srsran/asn1/rrc_nr.h"
#include "srsran/common/block_queue.h"
#include "srsran/common/buffer_pool.h"
#include "srsran/common/common.h"
#include "srsran/common/task_scheduler.h"
#include "srsran/common/threads.h"
#include "srsran/common/timeout.h"
#include "srsran/interfaces/enb_pdcp_interfaces.h"
#include "srsran/interfaces/enb_rlc_interfaces.h"
#include "srsran/interfaces/enb_rrc_interfaces.h"
#include "srsran/interfaces/enb_x2_interfaces.h"
#include "srsran/interfaces/gnb_interfaces.h"
#include "srsran/interfaces/gnb_mac_interfaces.h"
#include "srsran/interfaces/gnb_ngap_interfaces.h"
#include "srsran/interfaces/gnb_rrc_nr_interfaces.h"
#include <map>
#include <queue>
namespace srsenb {
enum class rrc_nr_state_t { RRC_IDLE, RRC_INACTIVE, RRC_CONNECTED };
class rrc_nr final : public rrc_interface_pdcp_nr,
public rrc_interface_mac_nr,
public rrc_interface_rlc_nr,
public rrc_interface_ngap_nr,
public rrc_nr_interface_rrc
{
public:
explicit rrc_nr(srsran::task_sched_handle task_sched_);
~rrc_nr();
int32_t init(const rrc_nr_cfg_t& cfg,
phy_interface_stack_nr* phy,
mac_interface_rrc_nr* mac,
rlc_interface_rrc* rlc,
pdcp_interface_rrc* pdcp,
ngap_interface_rrc_nr* ngap_,
gtpu_interface_rrc_nr* gtpu,
rrc_eutra_interface_rrc_nr* rrc_eutra_);
void stop();
void get_metrics(srsenb::rrc_metrics_t& m);
void config_phy();
void config_mac();
int32_t generate_sibs();
int read_pdu_bcch_bch(const uint32_t tti, srsran::byte_buffer_t& buffer) final;
int read_pdu_bcch_dlsch(uint32_t sib_index, srsran::byte_buffer_t& buffer) final;
/// User manegement
int add_user(uint16_t rnti, const sched_nr_ue_cfg_t& uecfg) final;
void rem_user(uint16_t rnti);
int update_user(uint16_t new_rnti, uint16_t old_rnti) final;
void set_activity_user(uint16_t rnti) final;
// RLC interface
// TODO
void read_pdu_pcch(uint8_t* payload, uint32_t payload_size) final {}
void max_retx_attempted(uint16_t rnti) final {}
void protocol_failure(uint16_t rnti) final {}
const char* get_rb_name(uint32_t lcid) final { return "invalid"; }
// PDCP interface
void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu) final;
void notify_pdcp_integrity_error(uint16_t rnti, uint32_t lcid) final;
// Interface for EUTRA RRC
void sgnb_addition_request(uint16_t rnti, const sgnb_addition_req_params_t& params) final;
void sgnb_reconfiguration_complete(uint16_t rnti, const asn1::dyn_octstring& reconfig_response) final;
void sgnb_release_request(uint16_t nr_rnti) final;
// Interfaces for NGAP
int ue_set_security_cfg_key(uint16_t rnti, const asn1::fixed_bitstring<256, false, true>& key) final;
int ue_set_bitrates(uint16_t rnti, const asn1::ngap_nr::ue_aggregate_maximum_bit_rate_s& rates) final;
int ue_set_security_cfg_capabilities(uint16_t rnti, const asn1::ngap_nr::ue_security_cap_s& caps) final;
int start_security_mode_procedure(uint16_t rnti) final;
int establish_rrc_bearer(uint16_t rnti,
uint16_t pdu_session_id,
srsran::const_byte_span nas_pdu,
uint32_t lcid) final;
int release_bearers(uint16_t rnti) final;
void write_dl_info(uint16_t rnti, srsran::unique_byte_buffer_t sdu) final;
int set_aggregate_max_bitrate(uint16_t rnti, const asn1::ngap_nr::ue_aggregate_maximum_bit_rate_s& rates) final;
int allocate_lcid(uint16_t rnti) final;
// logging
typedef enum { Rx = 0, Tx } direction_t;
template <class T>
void log_rrc_message(const char* source,
const direction_t dir,
srsran::const_byte_span pdu,
const T& msg,
const char* msg_type);
class ue;
private:
static constexpr uint32_t UE_PSCELL_CC_IDX = 0; // first NR cell is always Primary Secondary Cell for UE
rrc_nr_cfg_t cfg = {};
// interfaces
phy_interface_stack_nr* phy = nullptr;
mac_interface_rrc_nr* mac = nullptr;
rlc_interface_rrc* rlc = nullptr;
pdcp_interface_rrc* pdcp = nullptr;
gtpu_interface_rrc_nr* gtpu = nullptr;
ngap_interface_rrc_nr* ngap = nullptr;
rrc_eutra_interface_rrc_nr* rrc_eutra = nullptr;
// args
srsran::task_sched_handle task_sched;
// derived
uint32_t slot_dur_ms = 0;
srslog::basic_logger& logger;
asn1::rrc_nr::sp_cell_cfg_s base_sp_cell_cfg;
// vars
struct cell_ctxt_t {
asn1::rrc_nr::mib_s mib;
asn1::rrc_nr::sib1_s sib1;
asn1::rrc_nr::sys_info_ies_s::sib_type_and_info_l_ sibs;
srsran::unique_byte_buffer_t mib_buffer = nullptr;
std::vector<srsran::unique_byte_buffer_t> sib_buffer;
};
std::unique_ptr<cell_ctxt_t> cell_ctxt;
rnti_map_t<std::unique_ptr<ue> > users;
bool running = false;
/// Private Methods
void handle_pdu(uint16_t rnti, uint32_t lcid, srsran::const_byte_span pdu);
void handle_ul_ccch(uint16_t rnti, srsran::const_byte_span pdu);
void handle_ul_dcch(uint16_t rnti, uint32_t lcid, srsran::const_byte_span pdu);
// TS 38.331, 5.3.3 - RRC connection establishment
void handle_rrc_setup_request(uint16_t rnti, const asn1::rrc_nr::rrc_setup_request_s& msg);
/// This gets called by rrc_nr::sgnb_addition_request and WILL NOT TRIGGER the RX MSG3 activity timer
int add_user(uint16_t rnti, const sched_nr_ue_cfg_t& uecfg, bool start_msg3_timer);
// Helper to create PDU from RRC message
template <class T>
srsran::unique_byte_buffer_t pack_into_pdu(const T& msg);
void log_rx_pdu_fail(uint16_t rnti,
uint32_t lcid,
srsran::const_byte_span pdu,
const char* cause_str,
bool log_hex = true);
};
} // namespace srsenb
#endif // SRSENB_RRC_NR_H

@ -0,0 +1,141 @@
/**
*
* \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_RRC_NR_UE_H
#define SRSRAN_RRC_NR_UE_H
#include "rrc_nr.h"
namespace srsenb {
class rrc_nr::ue
{
public:
enum activity_timeout_type_t {
MSG3_RX_TIMEOUT = 0, ///< Msg3 has its own timeout to quickly remove fake UEs from random PRACHs
UE_INACTIVITY_TIMEOUT, ///< (currently unused) UE inactivity timeout (usually bigger than reestablishment timeout)
MSG5_RX_TIMEOUT, ///< (currently unused) for receiving RRCConnectionSetupComplete/RRCReestablishmentComplete
nulltype
};
/// @param [in] start_msg3_timer: indicates whether the UE is created as part of a RACH process
ue(rrc_nr* parent_, uint16_t rnti_, const sched_nr_ue_cfg_t& uecfg, bool start_msg3_timer = true);
~ue();
void send_dl_ccch(const asn1::rrc_nr::dl_ccch_msg_s& dl_dcch_msg);
int handle_sgnb_addition_request(uint16_t eutra_rnti, const sgnb_addition_req_params_t& params);
void crnti_ce_received();
// getters
bool is_connected() { return state == rrc_nr_state_t::RRC_CONNECTED; }
bool is_idle() { return state == rrc_nr_state_t::RRC_IDLE; }
bool is_inactive() { return state == rrc_nr_state_t::RRC_INACTIVE; }
bool is_endc() { return endc; }
uint16_t get_eutra_rnti() { return eutra_rnti; }
void get_metrics(rrc_ue_metrics_t& ue_metrics) { ue_metrics = {}; /*TODO fill RRC metrics*/ };
// setters
void deactivate_bearers();
/// methods to handle activity timer
std::string to_string(const activity_timeout_type_t& type);
void set_activity_timeout(activity_timeout_type_t type);
void set_activity(bool enabled = true);
void activity_timer_expired(const activity_timeout_type_t type);
/* TS 38.331 - 5.3.3 RRC connection establishment */
void handle_rrc_setup_request(const asn1::rrc_nr::rrc_setup_request_s& msg);
void handle_rrc_setup_complete(const asn1::rrc_nr::rrc_setup_complete_s& msg);
private:
rrc_nr* parent = nullptr;
uint16_t rnti = SRSRAN_INVALID_RNTI;
/* TS 38.331 - 5.3.3 RRC connection establishment */
void send_rrc_setup();
void send_rrc_reject(uint8_t reject_wait_time_secs);
int pack_rrc_reconfiguration(asn1::dyn_octstring& packed_rrc_reconfig);
int pack_secondary_cell_group_cfg(asn1::dyn_octstring& packed_secondary_cell_config);
int pack_secondary_cell_group_rlc_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_secondary_cell_group_mac_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_secondary_cell_group_sp_cell_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_sp_cell_cfg_ded(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_sp_cell_cfg_ded_init_dl_bwp(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_sp_cell_cfg_ded_init_dl_bwp_pdsch_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_sp_cell_cfg_ded_init_dl_bwp_radio_link_monitoring(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_sp_cell_cfg_ded_ul_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_sp_cell_cfg_ded_ul_cfg_init_ul_bwp(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_sp_cell_cfg_ded_ul_cfg_init_ul_bwp_pucch_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_sp_cell_cfg_ded_ul_cfg_init_ul_bwp_pusch_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_sp_cell_cfg_ded_pdcch_serving_cell_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_recfg_with_sync(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_recfg_with_sync_sp_cell_cfg_common(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_recfg_with_sync_sp_cell_cfg_common_dl_cfg_common(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_recfg_with_sync_sp_cell_cfg_common_dl_cfg_common_phy_cell_group_cfg(
asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_recfg_with_sync_sp_cell_cfg_common_dl_cfg_init_dl_bwp(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_recfg_with_sync_sp_cell_cfg_common_dl_cfg_init_dl_bwp_pdsch_cfg_common(
asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_recfg_with_sync_sp_cell_cfg_common_ul_cfg_common(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_recfg_with_sync_sp_cell_cfg_common_ul_cfg_common_init_ul_bwp(
asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_recfg_with_sync_sp_cell_cfg_common_ul_cfg_common_init_ul_bwp_pusch_cfg_common(
asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack);
int pack_nr_radio_bearer_config(asn1::dyn_octstring& packed_nr_bearer_config);
int add_drb();
// logging helpers
template <class M>
void log_rrc_message(srsran::nr_srb srb,
const direction_t dir,
srsran::const_byte_span pdu,
const M& msg,
const char* msg_type);
template <class M>
void log_rrc_container(const direction_t dir, srsran::const_byte_span pdu, const M& msg, const char* msg_type);
// state
rrc_nr_state_t state = rrc_nr_state_t::RRC_IDLE;
uint8_t transaction_id = 0;
srsran::unique_timer activity_timer; /// for basic DL/UL activity timeout
// RRC configs for UEs
asn1::rrc_nr::cell_group_cfg_s cell_group_cfg;
asn1::rrc_nr::radio_bearer_cfg_s radio_bearer_cfg;
// MAC controller
sched_nr_interface::ue_cfg_t uecfg{};
const uint32_t drb1_lcid = 4;
// NSA specific variables
bool endc = false;
uint16_t eutra_rnti = SRSRAN_INVALID_RNTI;
};
} // namespace srsenb
#endif // SRSRAN_RRC_NR_UE_H

@ -0,0 +1,9 @@
#
# 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.
#
add_subdirectory(stack)

@ -0,0 +1,18 @@
#
# 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.
#
include_directories(${PROJECT_SOURCE_DIR})
add_subdirectory(mac)
add_subdirectory(ngap)
add_subdirectory(rrc)
add_subdirectory(sdap)
set(SOURCES gnb_stack_nr.cc)
add_library(srsgnb_stack STATIC ${SOURCES})

@ -19,7 +19,10 @@
*
*/
#include "srsenb/hdr/stack/gnb_stack_nr.h"
#include "srsgnb/hdr/stack/gnb_stack_nr.h"
#include "srsenb/hdr/stack/upper/gtpu.h"
#include "srsgnb/hdr/stack/ngap/ngap.h"
#include "srsran/common/network_utils.h"
#include "srsran/common/standard_streams.h"
#include "srsran/srsran.h"
#include <srsran/interfaces/enb_metrics_interface.h>
@ -34,6 +37,8 @@ gnb_stack_nr::gnb_stack_nr(srslog::sink& log_sink) :
pdcp_logger(srslog::fetch_basic_logger("PDCP-NR", log_sink, false)),
rrc_logger(srslog::fetch_basic_logger("RRC-NR", log_sink, false)),
stack_logger(srslog::fetch_basic_logger("STCK-NR", log_sink, false)),
gtpu_logger(srslog::fetch_basic_logger("GTPU", log_sink, false)),
ngap_logger(srslog::fetch_basic_logger("NGAP", log_sink, false)),
mac(&task_sched),
rrc(&task_sched),
pdcp(&task_sched, pdcp_logger),
@ -70,12 +75,22 @@ int gnb_stack_nr::init(const gnb_stack_args_t& args_,
pdcp_logger.set_level(srslog::str_to_basic_level(args.log.pdcp_level));
rrc_logger.set_level(srslog::str_to_basic_level(args.log.rrc_level));
stack_logger.set_level(srslog::str_to_basic_level(args.log.stack_level));
ngap_logger.set_level(srslog::str_to_basic_level(args.log.s1ap_level));
gtpu_logger.set_level(srslog::str_to_basic_level(args.log.gtpu_level));
mac_logger.set_hex_dump_max_size(args.log.mac_hex_limit);
rlc_logger.set_hex_dump_max_size(args.log.rlc_hex_limit);
pdcp_logger.set_hex_dump_max_size(args.log.pdcp_hex_limit);
rrc_logger.set_hex_dump_max_size(args.log.rrc_hex_limit);
stack_logger.set_hex_dump_max_size(args.log.stack_hex_limit);
ngap_logger.set_hex_dump_max_size(args.log.s1ap_hex_limit);
gtpu_logger.set_hex_dump_max_size(args.log.gtpu_hex_limit);
if (x2_ == nullptr) {
// SA mode
ngap.reset(new srsenb::ngap(&task_sched, ngap_logger, &srsran::get_rx_io_manager()));
gtpu.reset(new srsenb::gtpu(&task_sched, gtpu_logger, &srsran::get_rx_io_manager()));
}
// Init all layers
if (mac.init(args.mac, phy, nullptr, &rlc, &rrc) != SRSRAN_SUCCESS) {
@ -86,15 +101,21 @@ int gnb_stack_nr::init(const gnb_stack_args_t& args_,
rlc.init(&pdcp, &rrc, &mac, task_sched.get_timer_handler());
pdcp.init(&rlc, &rrc, x2_);
if (rrc.init(rrc_cfg_, phy, &mac, &rlc, &pdcp, nullptr, nullptr, x2_) != SRSRAN_SUCCESS) {
if (rrc.init(rrc_cfg_, phy, &mac, &rlc, &pdcp, ngap.get(), nullptr, x2_) != SRSRAN_SUCCESS) {
stack_logger.error("Couldn't initialize RRC");
return SRSRAN_ERROR;
}
// TODO: add SDAP, NGAP
// m_gtpu->init(args.s1ap.gtp_bind_addr, args.s1ap.mme_addr,
// args.expert.m1u_multiaddr, args.expert.m1u_if_addr, nullptr, &gtpu_log,
// args.expert.enable_mbsfn);
if (ngap != nullptr) {
ngap->init(args.ngap, &rrc, nullptr);
gtpu_args_t gtpu_args;
gtpu_args.embms_enable = false;
gtpu_args.mme_addr = args.ngap.amf_addr;
gtpu_args.gtp_bind_addr = args.ngap.gtp_bind_addr;
gtpu->init(gtpu_args, &pdcp);
}
// TODO: add SDAP
running = true;
@ -113,6 +134,8 @@ void gnb_stack_nr::stop()
void gnb_stack_nr::stop_impl()
{
srsran::get_rx_io_manager().stop();
rrc.stop();
pdcp.stop();
mac.stop();

@ -36,3 +36,6 @@ set(SOURCES mac_nr.cc
add_library(srsgnb_mac STATIC ${SOURCES})
target_link_libraries(srsgnb_mac srsenb_mac_common)
include_directories(${PROJECT_SOURCE_DIR})
add_subdirectory(test)

@ -19,7 +19,7 @@
*
*/
#include "srsenb/hdr/stack/mac/nr/harq_softbuffer.h"
#include "srsgnb/hdr/stack/mac/harq_softbuffer.h"
#include "srsran/adt/pool/obj_pool.h"
namespace srsenb {

@ -19,8 +19,8 @@
*
*/
#include "srsenb/hdr/stack/mac/nr/mac_nr.h"
#include "srsenb/hdr/stack/mac/nr/sched_nr.h"
#include "srsgnb/hdr/stack/mac/mac_nr.h"
#include "srsgnb/hdr/stack/mac/sched_nr.h"
#include "srsran/common/buffer_pool.h"
#include "srsran/common/phy_cfg_nr_default.h"
#include "srsran/common/rwlock_guard.h"
@ -31,6 +31,155 @@
namespace srsenb {
class mac_nr_rx
{
public:
explicit mac_nr_rx(rlc_interface_mac* rlc_,
rrc_interface_mac_nr* rrc_,
srsran::task_queue_handle& stack_task_queue_,
sched_nr_interface* sched_,
srslog::basic_logger& logger_) :
task_queue(stack_task_queue_), rlc(rlc_), rrc(rrc_), sched(sched_), logger(logger_)
{}
void handle_pdu(uint16_t rnti, srsran::unique_byte_buffer_t pdu)
{
task_queue.push(std::bind(
[this, rnti](srsran::unique_byte_buffer_t& pdu) { handle_pdu_impl(rnti, std::move(pdu)); }, std::move(pdu)));
}
private:
int handle_pdu_impl(uint16_t rnti, srsran::unique_byte_buffer_t pdu)
{
pdu_ul.init_rx(true);
if (pdu_ul.unpack(pdu->msg, pdu->N_bytes) != SRSRAN_SUCCESS) {
return SRSRAN_ERROR;
}
if (logger.info.enabled()) {
fmt::memory_buffer str_buffer;
pdu_ul.to_string(str_buffer);
logger.info("Rx PDU: rnti=0x%x, %s", rnti, srsran::to_c_str(str_buffer));
}
// Process MAC CRNTI CE first, if it exists
uint32_t crnti_ce_pos = pdu_ul.get_num_subpdus();
for (uint32_t n = pdu_ul.get_num_subpdus(); n > 0; --n) {
srsran::mac_sch_subpdu_nr& subpdu = pdu_ul.get_subpdu(n - 1);
if (subpdu.get_lcid() == srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::CRNTI) {
if (process_ce_subpdu(rnti, subpdu) != SRSRAN_SUCCESS) {
return SRSRAN_ERROR;
}
crnti_ce_pos = n - 1;
}
}
// Process SDUs and remaining MAC CEs
for (uint32_t n = 0; n < pdu_ul.get_num_subpdus(); ++n) {
srsran::mac_sch_subpdu_nr& subpdu = pdu_ul.get_subpdu(n);
if (subpdu.is_sdu()) {
rrc->set_activity_user(rnti);
rlc->write_pdu(rnti, subpdu.get_lcid(), subpdu.get_sdu(), subpdu.get_sdu_length());
} else if (n != crnti_ce_pos) {
if (process_ce_subpdu(rnti, subpdu) != SRSRAN_SUCCESS) {
return SRSRAN_ERROR;
}
}
}
return SRSRAN_SUCCESS;
}
int process_ce_subpdu(uint16_t& rnti, const srsran::mac_sch_subpdu_nr& subpdu)
{
// Handle MAC CEs
switch (subpdu.get_lcid()) {
case srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::CRNTI: {
uint16_t ce_crnti = subpdu.get_c_rnti();
uint16_t prev_rnti = rnti;
rnti = ce_crnti;
rrc->update_user(prev_rnti, rnti);
sched->ul_sr_info(rnti); // provide UL grant regardless of other BSR content for UE to complete RA
} break;
case srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::SHORT_BSR:
case srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::SHORT_TRUNC_BSR: {
srsran::mac_sch_subpdu_nr::lcg_bsr_t sbsr = subpdu.get_sbsr();
uint32_t buffer_size_bytes = buff_size_field_to_bytes(sbsr.buffer_size, srsran::SHORT_BSR);
// Assume all LCGs are 0 if reported SBSR is 0
if (buffer_size_bytes == 0) {
for (uint32_t j = 0; j <= SCHED_NR_MAX_LC_GROUP; j++) {
sched->ul_bsr(rnti, j, 0);
}
} else {
sched->ul_bsr(rnti, sbsr.lcg_id, buffer_size_bytes);
}
} break;
case srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::LONG_BSR:
case srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::LONG_TRUNC_BSR: {
srsran::mac_sch_subpdu_nr::lbsr_t lbsr = subpdu.get_lbsr();
for (auto& lb : lbsr.list) {
sched->ul_bsr(rnti, lb.lcg_id, buff_size_field_to_bytes(lb.buffer_size, srsran::LONG_BSR));
}
} break;
case srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::PADDING:
break;
default:
logger.warning("Unhandled subPDU with LCID=%d", subpdu.get_lcid());
}
return SRSRAN_SUCCESS;
}
/** Converts the buffer size field of a BSR (5 or 8-bit Buffer Size field) into Bytes
* @param buff_size_field The buffer size field contained in the MAC PDU
* @param format The BSR format that determines the buffer size field length
* @return uint32_t The actual buffer size level in Bytes
*/
static uint32_t buff_size_field_to_bytes(uint32_t buff_size_index, const srsran::bsr_format_nr_t& format)
{
using namespace srsran;
// early exit
if (buff_size_index == 0) {
return 0;
}
const uint32_t max_offset = 1; // make the reported value bigger than the 2nd biggest
switch (format) {
case SHORT_BSR:
case SHORT_TRUNC_BSR:
if (buff_size_index >= buffer_size_levels_5bit_max_idx) {
return buffer_size_levels_5bit[buffer_size_levels_5bit_max_idx] + max_offset;
} else {
return buffer_size_levels_5bit[buff_size_index];
}
break;
case LONG_BSR:
case LONG_TRUNC_BSR:
if (buff_size_index > buffer_size_levels_8bit_max_idx) {
return buffer_size_levels_8bit[buffer_size_levels_8bit_max_idx] + max_offset;
} else {
return buffer_size_levels_8bit[buff_size_index];
}
break;
default:
break;
}
return 0;
}
rlc_interface_mac* rlc;
rrc_interface_mac_nr* rrc;
sched_nr_interface* sched;
srslog::basic_logger& logger;
srsran::task_queue_handle& task_queue;
srsran::mac_sch_pdu_nr pdu_ul;
};
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
mac_nr::mac_nr(srsran::task_sched_handle task_sched_) :
logger(srslog::fetch_basic_logger("MAC-NR")),
task_sched(task_sched_),
@ -115,26 +264,25 @@ int mac_nr::cell_cfg(const std::vector<srsenb::sched_nr_interface::cell_cfg_t>&
detected_rachs.resize(nr_cells.size());
// read SIBs from RRC (SIB1 for now only)
for (int i = 0; i < 1 /* srsenb::sched_interface::MAX_SIBS */; i++) {
// TODO: add flag for SIBs into cell config
if (true) {
sib_info_t sib = {};
sib.index = i;
sib.periodicity = 4; // TODO: read period_rf from config
sib.payload = srsran::make_byte_buffer();
if (sib.payload == nullptr) {
logger.error("Couldn't allocate PDU in %s().", __FUNCTION__);
return SRSRAN_ERROR;
}
if (rrc->read_pdu_bcch_dlsch(sib.index, sib.payload) != SRSRAN_SUCCESS) {
logger.error("Couldn't read SIB %d from RRC", sib.index);
}
logger.info("Including SIB %d into SI scheduling", sib.index);
bcch_dlsch_payload.push_back(std::move(sib));
for (uint32_t i = 0; i < nr_cells[0].sibs.size(); i++) {
sib_info_t sib = {};
sib.index = i;
sib.periodicity = 160; // TODO: read period_rf from config
sib.payload = srsran::make_byte_buffer();
if (sib.payload == nullptr) {
logger.error("Couldn't allocate PDU in %s().", __FUNCTION__);
return SRSRAN_ERROR;
}
if (rrc->read_pdu_bcch_dlsch(sib.index, *sib.payload) != SRSRAN_SUCCESS) {
logger.error("Couldn't read SIB %d from RRC", sib.index);
}
logger.info("Including SIB %d into SI scheduling", sib.index + 1);
bcch_dlsch_payload.push_back(std::move(sib));
}
rx.reset(new mac_nr_rx{rlc, rrc, stack_task_queue, sched.get(), logger});
return SRSRAN_SUCCESS;
}
@ -318,7 +466,7 @@ mac_nr::dl_sched_t* mac_nr::get_dl_sched(const srsran_slot_cfg_t& slot_cfg)
}
// Generate MAC DL PDUs
uint32_t rar_count = 0;
uint32_t rar_count = 0, si_count = 0;
srsran::rwlock_read_guard rw_lock(rwmutex);
for (pdsch_t& pdsch : dl_res->phy.pdsch) {
if (pdsch.sch.grant.rnti_type == srsran_rnti_type_c) {
@ -342,6 +490,9 @@ mac_nr::dl_sched_t* mac_nr::get_dl_sched(const srsran_slot_cfg_t& slot_cfg)
sched_nr_interface::rar_t& rar = dl_res->rar[rar_count++];
// for RARs we could actually move the byte_buffer to the PHY, as there are no retx
pdsch.data[0] = assemble_rar(rar.grants);
} else if (pdsch.sch.grant.rnti_type == srsran_rnti_type_si) {
uint32_t sib_idx = dl_res->sib_idxs[si_count++];
pdsch.data[0] = bcch_dlsch_payload[sib_idx].payload.get();
}
}
for (auto& u : ue_db) {
@ -382,7 +533,7 @@ int mac_nr::pucch_info(const srsran_slot_cfg_t& slot_cfg, const mac_interface_ph
return SRSRAN_SUCCESS;
}
bool mac_nr::handle_uci_data(const uint16_t rnti, const srsran_uci_cfg_nr_t& cfg_, const srsran_uci_value_nr_t& value)
bool mac_nr::handle_uci_data(uint16_t rnti, const srsran_uci_cfg_nr_t& cfg_, const srsran_uci_value_nr_t& value)
{
// Process HARQ-ACK
for (uint32_t i = 0; i < cfg_.ack.count; i++) {
@ -431,15 +582,8 @@ int mac_nr::pusch_info(const srsran_slot_cfg_t& slot_cfg, mac_interface_phy_nr::
pusch_info.pdu->msg, pusch_info.pdu->N_bytes, pusch_info.rnti, pusch_info.pid, slot_cfg.idx);
}
auto process_pdu_task = [this, rnti](srsran::unique_byte_buffer_t& pdu) {
srsran::rwlock_read_guard lock(rwmutex);
if (is_rnti_active_nolock(rnti)) {
ue_db[rnti]->process_pdu(std::move(pdu));
} else {
logger.debug("Discarding PDU rnti=0x%x", rnti);
}
};
stack_task_queue.try_push(std::bind(process_pdu_task, std::move(pusch_info.pdu)));
// Decode and send PDU to upper layers
rx->handle_pdu(rnti, std::move(pusch_info.pdu));
}
srsran::rwlock_read_guard rw_lock(rwmutex);
if (ue_db.contains(rnti)) {

@ -19,11 +19,11 @@
*
*/
#include "srsenb/hdr/stack/mac/nr/sched_nr.h"
#include "srsgnb/hdr/stack/mac/sched_nr.h"
#include "srsenb/hdr/stack/mac/common/mac_metrics.h"
#include "srsenb/hdr/stack/mac/nr/harq_softbuffer.h"
#include "srsenb/hdr/stack/mac/nr/sched_nr_bwp.h"
#include "srsenb/hdr/stack/mac/nr/sched_nr_worker.h"
#include "srsgnb/hdr/stack/mac/harq_softbuffer.h"
#include "srsgnb/hdr/stack/mac/sched_nr_bwp.h"
#include "srsgnb/hdr/stack/mac/sched_nr_worker.h"
#include "srsran/common/phy_cfg_nr_default.h"
#include "srsran/common/string_helpers.h"
#include "srsran/common/thread_pool.h"

@ -19,73 +19,13 @@
*
*/
#include "srsenb/hdr/stack/mac/nr/sched_nr_bwp.h"
#include "srsgnb/hdr/stack/mac/sched_nr_bwp.h"
#include "srsran/common/standard_streams.h"
#include "srsran/common/string_helpers.h"
namespace srsenb {
namespace sched_nr_impl {
si_sched::si_sched(const bwp_params_t& bwp_cfg_) :
bwp_cfg(&bwp_cfg_), logger(srslog::fetch_basic_logger(bwp_cfg_.sched_cfg.logger_name))
{}
void si_sched::run_slot(bwp_slot_allocator& slot_alloc)
{
const uint32_t si_aggr_level = 2;
slot_point pdcch_slot = slot_alloc.get_pdcch_tti();
const prb_bitmap& prbs = slot_alloc.res_grid()[pdcch_slot].dl_prbs.prbs();
// Update SI windows
uint32_t N = bwp_cfg->slots.size();
for (sched_si_t& si : pending_sis) {
uint32_t x = (si.n - 1) * si.win_len;
if (not si.win_start.valid() and (pdcch_slot.sfn() % si.period == x / N) and
pdcch_slot.slot_idx() == x % bwp_cfg->slots.size()) {
// If start o SI message window
si.win_start = pdcch_slot;
} else if (si.win_start.valid() and si.win_start + si.win_len >= pdcch_slot) {
// If end of SI message window
logger.warning(
"SCHED: Could not allocate SI message idx=%d, len=%d. Cause: %s", si.n, si.len, to_string(si.result));
si.win_start.clear();
}
}
// Schedule pending SIs
if (bwp_cfg->slots[pdcch_slot.slot_idx()].is_dl) {
for (sched_si_t& si : pending_sis) {
if (not si.win_start.valid()) {
continue;
}
// TODO: NOTE 2: The UE is not required to monitor PDCCH monitoring occasion(s) corresponding to each transmitted
// SSB in SI-window.
// Attempt grants with increasing number of PRBs (if the number of PRBs is too low, the coderate is invalid)
si.result = alloc_result::invalid_coderate;
uint32_t prb_start_idx = 0;
for (uint32_t nprbs = 4; nprbs < bwp_cfg->cfg.rb_width and si.result == alloc_result::invalid_coderate; ++nprbs) {
prb_interval grant = find_empty_interval_of_length(prbs, nprbs, prb_start_idx);
prb_start_idx = grant.start();
if (grant.length() != nprbs) {
si.result = alloc_result::no_sch_space;
break;
}
si.result = slot_alloc.alloc_si(si_aggr_level, si.n, si.n_tx, grant);
if (si.result == alloc_result::success) {
// SIB scheduled successfully
si.win_start.clear();
si.n_tx++;
}
}
}
}
}
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
ra_sched::ra_sched(const bwp_params_t& bwp_cfg_) :
bwp_cfg(&bwp_cfg_), logger(srslog::fetch_basic_logger(bwp_cfg_.sched_cfg.logger_name))
{}
@ -230,7 +170,7 @@ int ra_sched::dl_rach_info(const dl_sched_rar_info_t& rar_info)
}
bwp_manager::bwp_manager(const bwp_params_t& bwp_cfg) :
cfg(&bwp_cfg), ra(bwp_cfg), grid(bwp_cfg), data_sched(new sched_nr_time_rr())
cfg(&bwp_cfg), ra(bwp_cfg), si(bwp_cfg), grid(bwp_cfg), data_sched(new sched_nr_time_rr())
{}
} // namespace sched_nr_impl

@ -19,8 +19,8 @@
*
*/
#include "srsenb/hdr/stack/mac/nr/sched_nr_cfg.h"
#include "srsenb/hdr/stack/mac/nr/sched_nr_helpers.h"
#include "srsgnb/hdr/stack/mac/sched_nr_cfg.h"
#include "srsgnb/hdr/stack/mac/sched_nr_helpers.h"
#include "srsran/adt/optional_array.h"
extern "C" {
#include "srsran/phy/phch/ra_ul_nr.h"
@ -98,6 +98,24 @@ bwp_params_t::bwp_params_t(const cell_cfg_t& cell, const sched_args_t& sched_cfg
rar_cce_list[sl][agg_idx].resize(n);
}
}
for (uint32_t ss_id = 0; ss_id < SRSRAN_UE_DL_NR_MAX_NOF_SEARCH_SPACE; ++ss_id) {
if (cell_cfg.bwps[0].pdcch.search_space_present[ss_id]) {
auto& ss = cell_cfg.bwps[0].pdcch.search_space[ss_id];
auto& coreset = cell_cfg.bwps[0].pdcch.coreset[ss.coreset_id];
common_cce_list.emplace(ss_id);
bwp_cce_pos_list& ss_cce_list = common_cce_list[ss_id];
for (uint32_t sl = 0; sl < SRSRAN_NOF_SF_X_FRAME; ++sl) {
for (uint32_t agg_idx = 0; agg_idx < MAX_NOF_AGGR_LEVELS; ++agg_idx) {
ss_cce_list[sl][agg_idx].resize(SRSRAN_SEARCH_SPACE_MAX_NOF_CANDIDATES_NR);
int n = srsran_pdcch_nr_locations_coreset(
&coreset, &ss, SRSRAN_SIRNTI, agg_idx, sl, ss_cce_list[sl][agg_idx].data());
srsran_assert(n >= 0, "Failed to configure DCI locations of search space id=%d", ss_id);
ss_cce_list[sl][agg_idx].resize(n);
}
}
}
}
}
cell_params_t::cell_params_t(uint32_t cc_, const cell_cfg_t& cell, const sched_args_t& sched_cfg_) :

@ -19,9 +19,9 @@
*
*/
#include "srsenb/hdr/stack/mac/nr/sched_nr_grant_allocator.h"
#include "srsenb/hdr/stack/mac/nr/sched_nr_bwp.h"
#include "srsenb/hdr/stack/mac/nr/sched_nr_helpers.h"
#include "srsgnb/hdr/stack/mac/sched_nr_grant_allocator.h"
#include "srsgnb/hdr/stack/mac/sched_nr_bwp.h"
#include "srsgnb/hdr/stack/mac/sched_nr_helpers.h"
namespace srsenb {
namespace sched_nr_impl {
@ -56,6 +56,7 @@ void bwp_slot_grid::reset()
dl.phy.pdcch_ul.clear();
dl.phy.pdsch.clear();
dl.rar.clear();
dl.sib_idxs.clear();
ul.pusch.clear();
ul.pucch.clear();
pending_acks.clear();
@ -74,23 +75,55 @@ bwp_slot_allocator::bwp_slot_allocator(bwp_res_grid& bwp_grid_, slot_point pdcch
logger(bwp_grid_.cfg->logger), cfg(*bwp_grid_.cfg), bwp_grid(bwp_grid_), pdcch_slot(pdcch_slot_), slot_ues(ues_)
{}
alloc_result bwp_slot_allocator::alloc_si(uint32_t aggr_idx, uint32_t si_idx, uint32_t si_ntx, const prb_interval& prbs)
alloc_result bwp_slot_allocator::alloc_si(uint32_t aggr_idx,
uint32_t si_idx,
uint32_t si_ntx,
const prb_interval& prbs,
tx_harq_softbuffer& softbuffer)
{
bwp_slot_grid& bwp_pdcch_slot = bwp_grid[pdcch_slot];
if (not bwp_pdcch_slot.is_dl()) {
logger.warning("SCHED: Trying to allocate PDSCH in TDD non-DL slot index=%d", bwp_pdcch_slot.slot_idx);
return alloc_result::no_sch_space;
}
pdcch_dl_list_t& pdsch_grants = bwp_pdcch_slot.dl.phy.pdcch_dl;
if (pdsch_grants.full()) {
logger.warning("SCHED: Maximum number of DL allocations reached");
return alloc_result::no_grant_space;
alloc_result ret = verify_pdsch_space(bwp_pdcch_slot, bwp_pdcch_slot);
if (ret != alloc_result::success) {
return ret;
}
if (bwp_pdcch_slot.dl_prbs.collides(prbs)) {
return alloc_result::sch_collision;
}
// TODO: Allocate PDCCH and PDSCH
const uint32_t coreset_id = 0;
const uint32_t ss_id = 0;
if (not bwp_pdcch_slot.coresets[coreset_id]->alloc_dci(pdcch_grant_type_t::sib, aggr_idx, ss_id)) {
logger.warning("SCHED: Cannot allocate SIB1 due to lack of PDCCH space.");
return alloc_result::no_cch_space;
}
// RAR allocation successful.
bwp_pdcch_slot.dl_prbs |= prbs;
// Generate DCI for RAR with given RA-RNTI
pdcch_dl_t& pdcch = bwp_pdcch_slot.dl.phy.pdcch_dl.back();
if (not fill_dci_sib(prbs, si_idx, si_ntx, *bwp_grid.cfg, pdcch.dci)) {
// Cancel on-going PDCCH allocation
bwp_pdcch_slot.coresets[coreset_id]->rem_last_dci();
return alloc_result::invalid_coderate;
}
// Generate PDSCH
bwp_pdcch_slot.dl.phy.pdsch.emplace_back();
pdsch_t& pdsch = bwp_pdcch_slot.dl.phy.pdsch.back();
srsran_slot_cfg_t slot_cfg;
slot_cfg.idx = pdcch_slot.to_uint();
int code = srsran_ra_dl_dci_to_grant_nr(
&cfg.cell_cfg.carrier, &slot_cfg, &cfg.cfg.pdsch, &pdcch.dci, &pdsch.sch, &pdsch.sch.grant);
if (code != SRSRAN_SUCCESS) {
logger.warning("Error generating SIB PDSCH grant.");
bwp_pdcch_slot.coresets[coreset_id]->rem_last_dci();
bwp_pdcch_slot.dl.phy.pdsch.pop_back();
return alloc_result::other_cause;
}
pdsch.sch.grant.tb[0].softbuffer.tx = softbuffer.get();
// Store SI msg index
bwp_pdcch_slot.dl.sib_idxs.push_back(si_idx);
return alloc_result::success;
}
@ -113,6 +146,11 @@ alloc_result bwp_slot_allocator::alloc_rar_and_msg3(uint16_t
if (ret != alloc_result::success) {
return ret;
}
if (not bwp_pdcch_slot.dl.phy.ssb.empty()) {
// TODO: support concurrent PDSCH and SSB
logger.debug("SCHED: skipping RAR allocation. Cause: concurrent PDSCH and SSB not yet supported");
return alloc_result::no_sch_space;
}
if (pending_rachs.size() > bwp_pdcch_slot.dl.rar.capacity() - bwp_pdcch_slot.dl.rar.size()) {
logger.error("SCHED: Trying to allocate too many Msg3 grants in a single slot (%zd)", pending_rachs.size());
return alloc_result::invalid_grant_params;
@ -219,6 +257,11 @@ alloc_result bwp_slot_allocator::alloc_pdsch(slot_ue& ue, const prb_grant& dl_gr
if (result != alloc_result::success) {
return result;
}
if (not bwp_pdsch_slot.dl.phy.ssb.empty()) {
// TODO: support concurrent PDSCH and SSB
logger.debug("SCHED: skipping PDSCH allocation. Cause: concurrent PDSCH and SSB not yet supported");
return alloc_result::no_sch_space;
}
if (bwp_pdsch_slot.dl_prbs.collides(dl_grant)) {
return alloc_result::sch_collision;
}
@ -395,11 +438,6 @@ alloc_result bwp_slot_allocator::verify_pdsch_space(bwp_slot_grid& pdsch_grid,
return alloc_result::no_grant_space;
}
}
if (not pdsch_grid.dl.phy.ssb.empty()) {
// TODO: support concurrent PDSCH and SSB
logger.debug("SCHED: skipping PDSCH allocation. Cause: concurrent PDSCH and SSB not yet supported");
return alloc_result::no_sch_space;
}
return alloc_result::success;
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save