rrc paging mechanism improvements

- moved paging record handling to separate class
- parallel access to pending pcch messages by phy workers based on TTI and without common lock
- asn1 pcch message packing now takes place in stack thread, to avoid real-time issues
master
Francisco 4 years ago committed by Francisco Paisana
parent 318f064ff4
commit 01eace2631

@ -26,8 +26,6 @@ public:
* Segmentation happens in this function. RLC PDU is stored in payload. */ * Segmentation happens in this function. RLC PDU is stored in payload. */
virtual int read_pdu(uint16_t rnti, uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) = 0; virtual int read_pdu(uint16_t rnti, uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) = 0;
virtual void read_pdu_pcch(uint8_t* payload, uint32_t buffer_size) = 0;
/* MAC calls RLC to push an RLC PDU. This function is called from an independent MAC thread. /* MAC calls RLC to push an RLC PDU. This function is called from an independent MAC thread.
* PDU gets placed into the buffer and higher layer thread gets notified. */ * PDU gets placed into the buffer and higher layer thread gets notified. */
virtual void write_pdu(uint16_t rnti, uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) = 0; virtual void write_pdu(uint16_t rnti, uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) = 0;

@ -90,7 +90,6 @@ public:
class rrc_interface_rlc class rrc_interface_rlc
{ {
public: public:
virtual void read_pdu_pcch(uint8_t* payload, uint32_t payload_size) = 0;
virtual void max_retx_attempted(uint16_t rnti) = 0; virtual void max_retx_attempted(uint16_t rnti) = 0;
virtual void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t sdu) = 0; virtual void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t sdu) = 0;
}; };
@ -105,7 +104,8 @@ public:
virtual void set_activity_user(uint16_t rnti) = 0; virtual void set_activity_user(uint16_t rnti) = 0;
virtual void set_radiolink_dl_state(uint16_t rnti, bool crc_res) = 0; virtual void set_radiolink_dl_state(uint16_t rnti, bool crc_res) = 0;
virtual void set_radiolink_ul_state(uint16_t rnti, bool crc_res) = 0; virtual void set_radiolink_ul_state(uint16_t rnti, bool crc_res) = 0;
virtual bool is_paging_opportunity(uint32_t tti, uint32_t* payload_len) = 0; virtual bool is_paging_opportunity(uint32_t tti_tx_dl, uint32_t* payload_len) = 0;
virtual void read_pdu_pcch(uint32_t tti_tx_dl, uint8_t* payload, uint32_t payload_size) = 0;
///< Provide packed SIB to MAC (buffer is managed by RRC) ///< Provide packed SIB to MAC (buffer is managed by RRC)
virtual uint8_t* read_pdu_bcch_dlsch(const uint8_t enb_cc_idx, const uint32_t sib_index) = 0; virtual uint8_t* read_pdu_bcch_dlsch(const uint8_t enb_cc_idx, const uint32_t sib_index) = 0;

@ -36,6 +36,8 @@ class rlc_interface_rrc;
class mac_interface_rrc; class mac_interface_rrc;
class phy_interface_rrc_lte; class phy_interface_rrc_lte;
class paging_manager;
static const char rrc_state_text[RRC_STATE_N_ITEMS][100] = {"IDLE", static const char rrc_state_text[RRC_STATE_N_ITEMS][100] = {"IDLE",
"WAIT FOR CON SETUP COMPLETE", "WAIT FOR CON SETUP COMPLETE",
"WAIT FOR SECURITY MODE COMPLETE", "WAIT FOR SECURITY MODE COMPLETE",
@ -75,7 +77,7 @@ public:
uint8_t* read_pdu_bcch_dlsch(const uint8_t cc_idx, const uint32_t sib_index) override; uint8_t* read_pdu_bcch_dlsch(const uint8_t cc_idx, const uint32_t sib_index) override;
// rrc_interface_rlc // rrc_interface_rlc
void read_pdu_pcch(uint8_t* payload, uint32_t buffer_size) override; void read_pdu_pcch(uint32_t tti_tx_dl, uint8_t* payload, uint32_t buffer_size) override;
void max_retx_attempted(uint16_t rnti) override; void max_retx_attempted(uint16_t rnti) override;
// rrc_interface_s1ap // rrc_interface_s1ap
@ -165,7 +167,7 @@ private:
// state // state
std::unique_ptr<freq_res_common_list> cell_res_list; std::unique_ptr<freq_res_common_list> cell_res_list;
std::map<uint16_t, unique_rnti_ptr<ue> > users; // NOTE: has to have fixed addr std::map<uint16_t, unique_rnti_ptr<ue> > users; // NOTE: has to have fixed addr
std::map<uint32_t, asn1::rrc::paging_record_s> pending_paging; std::unique_ptr<paging_manager> pending_paging;
void process_release_complete(uint16_t rnti); void process_release_complete(uint16_t rnti);
void rem_user(uint16_t rnti); void rem_user(uint16_t rnti);
@ -208,8 +210,6 @@ private:
asn1::rrc::sib_type7_s sib7; asn1::rrc::sib_type7_s sib7;
void rem_user_thread(uint16_t rnti); void rem_user_thread(uint16_t rnti);
std::mutex paging_mutex;
}; };
} // namespace srsenb } // namespace srsenb

@ -0,0 +1,203 @@
/**
*
* \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_PAGING_H
#define SRSRAN_RRC_PAGING_H
#include "srsran/adt/span.h"
#include "srsran/asn1/rrc/paging.h"
#include "srsran/common/tti_point.h"
namespace srsenb {
/**
* Class that handles the buffering of paging records and encoding of PCCH messages. It's thread-safe
*/
class paging_manager
{
public:
paging_manager(uint32_t default_paging_cycle_, uint32_t nb_) :
T(default_paging_cycle_),
Nb(T * nb_),
N(std::min(T, Nb)),
Ns(std::max(nb_, 1u)),
pending_paging(T),
logger(srslog::fetch_basic_logger("RRC"))
{
for (auto& sfn_pcch_msgs : pending_paging) {
for (pcch_info& pcch : sfn_pcch_msgs) {
pcch.pcch_msg.msg.set_c1().paging().paging_record_list_present = true;
}
}
}
bool add_imsi_paging(uint32_t ueid, srsran::const_byte_span imsi);
bool add_tmsi_paging(uint32_t ueid, uint8_t mmec, srsran::const_byte_span m_tmsi);
size_t pending_pcch_bytes(tti_point tti_tx_dl);
/**
* Invoke "callable" for PCCH indexed by tti_tx_dl in a mutexed context.
* Callable signature is bool(const_byte_span pdu, const pcch_msg& msg, bool is_first_tx)
* - "is_first_tx" tells if the PDU hasn't been transmitted yet.
* - the return should be true if the PDU is being transmitted, and false otherwise
*/
template <typename Callable>
bool read_pdu_pcch(tti_point tti_tx_dl, const Callable& callable);
private:
struct pcch_info {
mutable std::mutex mutex;
tti_point tti_tx_dl;
asn1::rrc::pcch_msg_s pcch_msg;
srsran::unique_byte_buffer_t pdu;
bool is_tx() const { return tti_tx_dl.is_valid(); }
bool empty() const { return pdu == nullptr; }
void clear()
{
tti_tx_dl = tti_point();
pcch_msg.msg.c1().paging().paging_record_list.clear();
pdu.reset();
}
};
bool add_paging_record(uint32_t ueid, const asn1::rrc::paging_record_s& paging_record);
pcch_info& get_pcch_info(tti_point tti_tx_dl) { return pending_paging[tti_tx_dl.sfn() % T][tti_tx_dl.sf_idx()]; }
const pcch_info& get_pcch_info(tti_point tti_tx_dl) const
{
return pending_paging[tti_tx_dl.sfn() % T][tti_tx_dl.sf_idx()];
}
void clear_pcch_info(tti_point tti);
// args
uint32_t T;
uint32_t Nb;
uint32_t N;
uint32_t Ns;
srslog::basic_logger& logger;
std::vector<std::array<pcch_info, SRSRAN_NOF_SF_X_FRAME> > pending_paging;
};
void paging_manager::clear_pcch_info(tti_point tti)
{
pcch_info& pcch = get_pcch_info(tti);
std::lock_guard<std::mutex> lock(pcch.mutex);
pcch.clear();
}
bool paging_manager::add_imsi_paging(uint32_t ueid, srsran::const_byte_span imsi)
{
asn1::rrc::paging_record_s paging_elem;
paging_elem.ue_id.set_imsi().resize(imsi.size());
std::copy(imsi.begin(), imsi.end(), paging_elem.ue_id.imsi().begin());
paging_elem.cn_domain = asn1::rrc::paging_record_s::cn_domain_e_::ps;
return add_paging_record(ueid, paging_elem);
}
bool paging_manager::add_tmsi_paging(uint32_t ueid, uint8_t mmec, srsran::const_byte_span m_tmsi)
{
asn1::rrc::paging_record_s paging_elem;
paging_elem.ue_id.set_s_tmsi().mmec.from_number(mmec);
uint32_t m_tmsi_val = 0;
for (uint32_t i = 0; i < m_tmsi.size(); i++) {
m_tmsi_val |= m_tmsi[i] << (8u * (m_tmsi.size() - i - 1u));
}
paging_elem.ue_id.s_tmsi().m_tmsi.from_number(m_tmsi_val);
paging_elem.cn_domain = asn1::rrc::paging_record_s::cn_domain_e_::ps;
return add_paging_record(ueid, paging_elem);
}
/// \remark See TS 36.304, Section 7
bool paging_manager::add_paging_record(uint32_t ueid, const asn1::rrc::paging_record_s& paging_record)
{
constexpr static const int sf_pattern[4][4] = {{9, 4, -1, 0}, {-1, 9, -1, 4}, {-1, -1, -1, 5}, {-1, -1, -1, 9}};
ueid = ((uint32_t)ueid) % 1024;
uint32_t i_s = (ueid / N) % Ns;
int sf_idx = sf_pattern[i_s % 4][(Ns - 1) % 4];
if (sf_idx < 0) {
logger.error("SF pattern is N/A for Ns=%d, i_s=%d, imsi_decimal=%d", Ns, i_s, ueid);
return false;
}
size_t sfn_cycle_idx = (T / N) * (ueid % N);
pcch_info& pending_pcch = pending_paging[sfn_cycle_idx][sf_idx];
auto& record_list = pending_pcch.pcch_msg.msg.c1().paging().paging_record_list;
std::lock_guard<std::mutex> lock(pending_pcch.mutex);
if (record_list.size() >= ASN1_RRC_MAX_PAGE_REC) {
logger.warning("Failed to add new paging record for ueid=%d. Cause: no paging record space left.", ueid);
return false;
}
if (pending_pcch.pdu == nullptr) {
pending_pcch.pdu = srsran::make_byte_buffer();
if (pending_pcch.pdu == nullptr) {
logger.warning("Failed to add new paging record for ueid=%d. Cause: No buffers available", ueid);
return false;
}
}
record_list.push_back(paging_record);
asn1::bit_ref bref(pending_pcch.pdu->msg, pending_pcch.pdu->get_tailroom());
if (pending_pcch.pcch_msg.pack(bref) == asn1::SRSASN_ERROR_ENCODE_FAIL) {
logger.error("Failed to pack PCCH message");
pending_pcch.clear();
return false;
}
pending_pcch.pdu->N_bytes = (uint32_t)bref.distance_bytes();
return true;
}
size_t paging_manager::pending_pcch_bytes(tti_point tti_tx_dl)
{
// clear old PCCH that has been transmitted at this point
clear_pcch_info(tti_tx_dl - SRSRAN_NOF_SF_X_FRAME);
const pcch_info& pending_pcch = get_pcch_info(tti_tx_dl);
std::lock_guard<std::mutex> lock(pending_pcch.mutex);
if (pending_pcch.empty()) {
return 0;
}
return pending_pcch.pdu->size();
}
template <typename Callable>
bool paging_manager::read_pdu_pcch(tti_point tti_tx_dl, const Callable& callable)
{
pcch_info& pending_pcch = get_pcch_info(tti_tx_dl);
std::lock_guard<std::mutex> lock(pending_pcch.mutex);
if (pending_pcch.empty()) {
logger.warning("read_pdu_pdcch(...) called for tti=%d, but there is no pending pcch message", tti_tx_dl.to_uint());
return false;
}
// Call callable for existing PCCH pdu
if (callable(*pending_pcch.pdu, pending_pcch.pcch_msg, pending_pcch.tti_tx_dl.is_valid())) {
pending_pcch.tti_tx_dl = tti_tx_dl;
return true;
}
return false;
}
} // namespace srsenb
#endif // SRSRAN_RRC_PAGING_H

@ -65,7 +65,6 @@ public:
// rlc_interface_mac // rlc_interface_mac
int read_pdu(uint16_t rnti, uint32_t lcid, uint8_t* payload, uint32_t nof_bytes); int read_pdu(uint16_t rnti, uint32_t lcid, uint8_t* payload, uint32_t nof_bytes);
void write_pdu(uint16_t rnti, uint32_t lcid, uint8_t* payload, uint32_t nof_bytes); void write_pdu(uint16_t rnti, uint32_t lcid, uint8_t* payload, uint32_t nof_bytes);
void read_pdu_pcch(uint8_t* payload, uint32_t buffer_size);
private: private:
class user_interface : public srsue::pdcp_interface_rlc, public srsue::rrc_interface_rlc class user_interface : public srsue::pdcp_interface_rlc, public srsue::rrc_interface_rlc

@ -727,7 +727,7 @@ int mac::get_dl_sched(uint32_t tti_tx_dl, dl_sched_list_t& dl_sched_res_list)
} else { } else {
dl_sched_res->pdsch[n].softbuffer_tx[0] = &common_buffers[enb_cc_idx].pcch_softbuffer_tx; dl_sched_res->pdsch[n].softbuffer_tx[0] = &common_buffers[enb_cc_idx].pcch_softbuffer_tx;
dl_sched_res->pdsch[n].data[0] = common_buffers[enb_cc_idx].pcch_payload_buffer; dl_sched_res->pdsch[n].data[0] = common_buffers[enb_cc_idx].pcch_payload_buffer;
rlc_h->read_pdu_pcch(common_buffers[enb_cc_idx].pcch_payload_buffer, pcch_payload_buffer_len); rrc_h->read_pdu_pcch(tti_tx_dl, common_buffers[enb_cc_idx].pcch_payload_buffer, pcch_payload_buffer_len);
if (pcap) { if (pcap) {
pcap->write_dl_pch(dl_sched_res->pdsch[n].data[0], sched_result.bc[i].tbs, true, tti_tx_dl, enb_cc_idx); pcap->write_dl_pch(dl_sched_res->pdsch[n].data[0], sched_result.bc[i].tbs, true, tti_tx_dl, enb_cc_idx);

@ -13,6 +13,7 @@
#include "srsenb/hdr/stack/rrc/rrc.h" #include "srsenb/hdr/stack/rrc/rrc.h"
#include "srsenb/hdr/stack/rrc/rrc_cell_cfg.h" #include "srsenb/hdr/stack/rrc/rrc_cell_cfg.h"
#include "srsenb/hdr/stack/rrc/rrc_mobility.h" #include "srsenb/hdr/stack/rrc/rrc_mobility.h"
#include "srsenb/hdr/stack/rrc/rrc_paging.h"
#include "srsran/asn1/asn1_utils.h" #include "srsran/asn1/asn1_utils.h"
#include "srsran/asn1/rrc_utils.h" #include "srsran/asn1/rrc_utils.h"
#include "srsran/common/bcd_helpers.h" #include "srsran/common/bcd_helpers.h"
@ -81,6 +82,9 @@ int32_t rrc::init(const rrc_cfg_t& cfg_,
logger.info("Inactivity timeout: %d ms", cfg.inactivity_timeout_ms); logger.info("Inactivity timeout: %d ms", cfg.inactivity_timeout_ms);
logger.info("Max consecutive MAC KOs: %d", cfg.max_mac_dl_kos); logger.info("Max consecutive MAC KOs: %d", cfg.max_mac_dl_kos);
pending_paging.reset(new paging_manager(cfg.sibs[1].sib2().rr_cfg_common.pcch_cfg.default_paging_cycle.to_number(),
cfg.sibs[1].sib2().rr_cfg_common.pcch_cfg.nb.to_number()));
running = true; running = true;
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;
@ -448,132 +452,39 @@ int rrc::modify_erab(uint16_t rnti,
void rrc::add_paging_id(uint32_t ueid, const asn1::s1ap::ue_paging_id_c& ue_paging_id) void rrc::add_paging_id(uint32_t ueid, const asn1::s1ap::ue_paging_id_c& ue_paging_id)
{ {
std::lock_guard<std::mutex> lock(paging_mutex);
if (pending_paging.count(ueid) > 0) {
logger.warning("Received Paging for UEID=%d but not yet transmitted", ueid);
return;
}
paging_record_s paging_elem;
if (ue_paging_id.type().value == asn1::s1ap::ue_paging_id_c::types_opts::imsi) { if (ue_paging_id.type().value == asn1::s1ap::ue_paging_id_c::types_opts::imsi) {
paging_elem.ue_id.set_imsi(); pending_paging->add_imsi_paging(ueid, ue_paging_id.imsi());
paging_elem.ue_id.imsi().resize(ue_paging_id.imsi().size());
memcpy(paging_elem.ue_id.imsi().data(), ue_paging_id.imsi().data(), ue_paging_id.imsi().size());
srsran::console("Warning IMSI paging not tested\n");
} else { } else {
paging_elem.ue_id.set_s_tmsi(); pending_paging->add_tmsi_paging(ueid, ue_paging_id.s_tmsi().mmec[0], ue_paging_id.s_tmsi().m_tmsi);
paging_elem.ue_id.s_tmsi().mmec.from_number(ue_paging_id.s_tmsi().mmec[0]);
uint32_t m_tmsi = 0;
uint32_t nof_octets = ue_paging_id.s_tmsi().m_tmsi.size();
for (uint32_t i = 0; i < nof_octets; i++) {
m_tmsi |= ue_paging_id.s_tmsi().m_tmsi[i] << (8u * (nof_octets - i - 1u));
}
paging_elem.ue_id.s_tmsi().m_tmsi.from_number(m_tmsi);
} }
paging_elem.cn_domain = paging_record_s::cn_domain_e_::ps;
pending_paging.insert(std::make_pair(ueid, paging_elem));
} }
// Described in Section 7 of 36.304
bool rrc::is_paging_opportunity(uint32_t tti, uint32_t* payload_len) bool rrc::is_paging_opportunity(uint32_t tti, uint32_t* payload_len)
{ {
constexpr static int sf_pattern[4][4] = {{9, 4, -1, 0}, {-1, 9, -1, 4}, {-1, -1, -1, 5}, {-1, -1, -1, 9}}; *payload_len = pending_paging->pending_pcch_bytes(tti_point(tti));
return *payload_len > 0;
if (tti == paging_tti) {
*payload_len = byte_buf_paging.N_bytes;
logger.debug("Sending paging to extra carriers. Payload len=%d, TTI=%d", *payload_len, tti);
return true;
} else {
paging_tti = INVALID_TTI;
}
if (pending_paging.empty()) {
return false;
} }
asn1::rrc::pcch_msg_s pcch_msg; void rrc::read_pdu_pcch(uint32_t tti_tx_dl, uint8_t* payload, uint32_t buffer_size)
pcch_msg.msg.set_c1();
paging_s* paging_rec = &pcch_msg.msg.c1().paging();
// Default paging cycle, should get DRX from user
uint32_t T = cfg.sibs[1].sib2().rr_cfg_common.pcch_cfg.default_paging_cycle.to_number();
uint32_t Nb = T * cfg.sibs[1].sib2().rr_cfg_common.pcch_cfg.nb.to_number();
uint32_t N = T < Nb ? T : Nb;
uint32_t Ns = Nb / T > 1 ? Nb / T : 1;
uint32_t sfn = tti / 10;
std::vector<uint32_t> ue_to_remove;
{ {
std::lock_guard<std::mutex> lock(paging_mutex); auto read_func = [this, payload, buffer_size](srsran::const_byte_span pdu, const pcch_msg_s& msg, bool first_tx) {
// copy PCCH pdu to buffer
int n = 0; if (pdu.size() > buffer_size) {
for (auto& item : pending_paging) { logger.warning("byte buffer with size=%zd is too small to fit pcch msg with size=%zd", buffer_size, pdu.size());
if (n >= ASN1_RRC_MAX_PAGE_REC) {
break;
}
const asn1::rrc::paging_record_s& u = item.second;
uint32_t ueid = ((uint32_t)item.first) % 1024;
uint32_t i_s = (ueid / N) % Ns;
if ((sfn % T) != (T / N) * (ueid % N)) {
continue;
}
int sf_idx = sf_pattern[i_s % 4][(Ns - 1) % 4];
if (sf_idx < 0) {
logger.error("SF pattern is N/A for Ns=%d, i_s=%d, imsi_decimal=%d", Ns, i_s, ueid);
continue;
}
if ((uint32_t)sf_idx == (tti % 10)) {
paging_rec->paging_record_list_present = true;
paging_rec->paging_record_list.push_back(u);
ue_to_remove.push_back(ueid);
n++;
logger.info("Assembled paging for ue_id=%d, tti=%d", ueid, tti);
}
}
for (unsigned int i : ue_to_remove) {
pending_paging.erase(i);
}
}
if (paging_rec->paging_record_list.size() > 0) {
byte_buf_paging.clear();
asn1::bit_ref bref(byte_buf_paging.msg, byte_buf_paging.get_tailroom());
if (pcch_msg.pack(bref) == asn1::SRSASN_ERROR_ENCODE_FAIL) {
logger.error("Failed to pack PCCH");
return false; return false;
} }
byte_buf_paging.N_bytes = (uint32_t)bref.distance_bytes(); std::copy(pdu.begin(), pdu.end(), payload);
uint32_t N_bits = (uint32_t)bref.distance();
if (payload_len) { if (first_tx) {
*payload_len = byte_buf_paging.N_bytes; logger.info("Assembling PCCH payload with %d UE identities, payload_len=%d bytes",
msg.msg.c1().paging().paging_record_list.size(),
pdu.size());
log_rrc_message("PCCH-Message", Tx, pdu, msg, msg.msg.c1().type().to_string());
} }
logger.info("Assembling PCCH payload with %d UE identities, payload_len=%d bytes, nbits=%d",
paging_rec->paging_record_list.size(),
byte_buf_paging.N_bytes,
N_bits);
log_rrc_message("PCCH-Message", Tx, &byte_buf_paging, pcch_msg, pcch_msg.msg.c1().type().to_string());
paging_tti = tti; // Store paging tti for other carriers
return true; return true;
} };
return false; pending_paging->read_pdu_pcch(tti_point(tti_tx_dl), read_func);
}
void rrc::read_pdu_pcch(uint8_t* payload, uint32_t buffer_size)
{
std::lock_guard<std::mutex> lock(paging_mutex);
if (byte_buf_paging.N_bytes <= buffer_size) {
memcpy(payload, byte_buf_paging.msg, byte_buf_paging.N_bytes);
}
} }
/******************************************************************************* /*******************************************************************************

@ -179,11 +179,6 @@ void rlc::update_bsr(uint32_t rnti, uint32_t lcid, uint32_t tx_queue, uint32_t r
mac->rlc_buffer_state(rnti, lcid, tx_queue, retx_queue); mac->rlc_buffer_state(rnti, lcid, tx_queue, retx_queue);
} }
void rlc::read_pdu_pcch(uint8_t* payload, uint32_t buffer_size)
{
rrc->read_pdu_pcch(payload, buffer_size);
}
int rlc::read_pdu(uint16_t rnti, uint32_t lcid, uint8_t* payload, uint32_t nof_bytes) int rlc::read_pdu(uint16_t rnti, uint32_t lcid, uint8_t* payload, uint32_t nof_bytes)
{ {
int ret; int ret;

@ -39,6 +39,7 @@ public:
void set_radiolink_dl_state(uint16_t rnti, bool crc_res) {} void set_radiolink_dl_state(uint16_t rnti, bool crc_res) {}
bool is_paging_opportunity(uint32_t tti, uint32_t* payload_len) { return false; } bool is_paging_opportunity(uint32_t tti, uint32_t* payload_len) { return false; }
uint8_t* read_pdu_bcch_dlsch(const uint8_t enb_cc_idx, const uint32_t sib_index) { return nullptr; } uint8_t* read_pdu_bcch_dlsch(const uint8_t enb_cc_idx, const uint32_t sib_index) { return nullptr; }
void read_pdu_pcch(uint32_t tti_tx_dl, uint8_t* payload, uint32_t n_bytes) {}
}; };
/************************** /**************************

Loading…
Cancel
Save