mirror of https://github.com/pvnis/srsRAN_4G.git
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.ccmaster
commit
3a9014802d
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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;
|
||||||
|
}
|
@ -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
|
|
@ -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
|
|
@ -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;
|
|
||||||
}
|
|
@ -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)
|
@ -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})
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue