srsLTE/srsgnb/src/stack/mac/mac_nr.cc

699 lines
23 KiB
C++

/**
* Copyright 2013-2022 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 "srsgnb/hdr/stack/mac/mac_nr.h"
#include "srsgnb/hdr/stack/mac/sched_nr.h"
#include "srsran/common/buffer_pool.h"
#include "srsran/common/phy_cfg_nr_default.h"
#include "srsran/common/rwlock_guard.h"
#include "srsran/common/standard_streams.h"
#include "srsran/common/string_helpers.h"
#include "srsran/common/time_prof.h"
#include "srsran/mac/mac_rar_pdu_nr.h"
//#define WRITE_SIB_PCAP
namespace srsenb {
/**
* @brief Handles UL PDU processing
*
* This class implements the demuxing of UL PDUs received at the MAC layer.
* When the PHY decodes a valid PUSCH it passes the PDU to the MAC which
* in turn puts them in a thread-safe task queue to return to the calling
* thread as quick as possible.
*
* The demuxing of the PDUs for all users takes place on the Stack thread
* which calls RLC and RRC for SDUs, or the MAC/scheduler for control elements.
*
*/
class mac_nr_rx
{
public:
explicit mac_nr_rx(rlc_interface_mac* rlc_,
rrc_interface_mac_nr* rrc_,
srsran::task_queue_handle& stack_task_queue_,
sched_nr_interface* sched_,
mac_interface_pdu_demux_nr& mac_,
srslog::basic_logger& logger_) :
task_queue(stack_task_queue_), rlc(rlc_), rrc(rrc_), sched(sched_), mac(mac_), logger(logger_)
{}
void handle_pdu(uint16_t rnti, srsran::unique_byte_buffer_t pdu)
{
task_queue.push(std::bind(
[this, rnti](srsran::unique_byte_buffer_t& pdu) { handle_pdu_impl(rnti, std::move(pdu)); }, std::move(pdu)));
}
private:
int handle_pdu_impl(uint16_t rnti, srsran::unique_byte_buffer_t pdu)
{
pdu_ul.init_rx(true);
if (pdu_ul.unpack(pdu->msg, pdu->N_bytes) != SRSRAN_SUCCESS) {
return SRSRAN_ERROR;
}
if (logger.info.enabled()) {
fmt::memory_buffer str_buffer;
pdu_ul.to_string(str_buffer);
logger.info("Rx PDU: rnti=0x%x, %s", rnti, srsran::to_c_str(str_buffer));
}
// Process MAC CRNTI CE first, if it exists
uint32_t crnti_ce_pos = pdu_ul.get_num_subpdus();
for (uint32_t n = pdu_ul.get_num_subpdus(); n > 0; --n) {
srsran::mac_sch_subpdu_nr& subpdu = pdu_ul.get_subpdu(n - 1);
if (subpdu.get_lcid() == srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::CRNTI) {
if (process_ce_subpdu(rnti, subpdu) != SRSRAN_SUCCESS) {
return SRSRAN_ERROR;
}
crnti_ce_pos = n - 1;
}
}
// Process SDUs and remaining MAC CEs
for (uint32_t n = 0; n < pdu_ul.get_num_subpdus(); ++n) {
srsran::mac_sch_subpdu_nr& subpdu = pdu_ul.get_subpdu(n);
if (subpdu.is_sdu()) {
rrc->set_activity_user(rnti);
rlc->write_pdu(rnti, subpdu.get_lcid(), subpdu.get_sdu(), subpdu.get_sdu_length());
} else if (n != crnti_ce_pos) {
if (process_ce_subpdu(rnti, subpdu) != SRSRAN_SUCCESS) {
return SRSRAN_ERROR;
}
}
}
return SRSRAN_SUCCESS;
}
int process_ce_subpdu(uint16_t& rnti, const srsran::mac_sch_subpdu_nr& subpdu)
{
// Handle MAC CEs
switch (subpdu.get_lcid()) {
case srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::CCCH_SIZE_48:
case srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::CCCH_SIZE_64: {
srsran::mac_sch_subpdu_nr& ccch_subpdu = const_cast<srsran::mac_sch_subpdu_nr&>(subpdu);
rlc->write_pdu(rnti, 0, ccch_subpdu.get_sdu(), ccch_subpdu.get_sdu_length());
// store content for ConRes CE and schedule CE accordingly
mac.store_msg3(rnti,
srsran::make_byte_buffer(ccch_subpdu.get_sdu(), ccch_subpdu.get_sdu_length(), __FUNCTION__));
sched->dl_mac_ce(rnti, srsran::mac_sch_subpdu_nr::CON_RES_ID);
} break;
case srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::CRNTI: {
uint16_t ce_crnti = subpdu.get_c_rnti();
if (ce_crnti == SRSRAN_INVALID_RNTI) {
logger.error("Malformed C-RNTI CE detected. C-RNTI can't be 0x0.", subpdu.get_lcid());
return SRSRAN_ERROR;
}
uint16_t prev_rnti = rnti;
rnti = ce_crnti;
rrc->update_user(prev_rnti, rnti);
sched->ul_sr_info(rnti); // provide UL grant regardless of other BSR content for UE to complete RA
} break;
case srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::SHORT_BSR:
case srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::SHORT_TRUNC_BSR: {
srsran::mac_sch_subpdu_nr::lcg_bsr_t sbsr = subpdu.get_sbsr();
uint32_t buffer_size_bytes = buff_size_field_to_bytes(sbsr.buffer_size, srsran::SHORT_BSR);
// Assume all LCGs are 0 if reported SBSR is 0
if (buffer_size_bytes == 0) {
for (uint32_t j = 0; j <= SCHED_NR_MAX_LC_GROUP; j++) {
sched->ul_bsr(rnti, j, 0);
}
} else {
sched->ul_bsr(rnti, sbsr.lcg_id, buffer_size_bytes);
}
} break;
case srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::LONG_BSR:
case srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::LONG_TRUNC_BSR: {
srsran::mac_sch_subpdu_nr::lbsr_t lbsr = subpdu.get_lbsr();
for (auto& lb : lbsr.list) {
sched->ul_bsr(rnti, lb.lcg_id, buff_size_field_to_bytes(lb.buffer_size, srsran::LONG_BSR));
}
} break;
case srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::SE_PHR:
// SE_PHR not implemented
break;
case srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::PADDING:
break;
default:
logger.warning("Unhandled subPDU with LCID=%d", subpdu.get_lcid());
}
return SRSRAN_SUCCESS;
}
/** Converts the buffer size field of a BSR (5 or 8-bit Buffer Size field) into Bytes
* @param buff_size_field The buffer size field contained in the MAC PDU
* @param format The BSR format that determines the buffer size field length
* @return uint32_t The actual buffer size level in Bytes
*/
static uint32_t buff_size_field_to_bytes(uint32_t buff_size_index, const srsran::bsr_format_nr_t& format)
{
using namespace srsran;
// early exit
if (buff_size_index == 0) {
return 0;
}
const uint32_t max_offset = 1; // make the reported value bigger than the 2nd biggest
switch (format) {
case SHORT_BSR:
case SHORT_TRUNC_BSR:
if (buff_size_index >= buffer_size_levels_5bit_max_idx) {
return buffer_size_levels_5bit[buffer_size_levels_5bit_max_idx] + max_offset;
} else {
return buffer_size_levels_5bit[buff_size_index];
}
break;
case LONG_BSR:
case LONG_TRUNC_BSR:
if (buff_size_index > buffer_size_levels_8bit_max_idx) {
return buffer_size_levels_8bit[buffer_size_levels_8bit_max_idx] + max_offset;
} else {
return buffer_size_levels_8bit[buff_size_index];
}
break;
default:
break;
}
return 0;
}
rlc_interface_mac* rlc;
rrc_interface_mac_nr* rrc;
sched_nr_interface* sched;
mac_interface_pdu_demux_nr& mac;
srslog::basic_logger& logger;
srsran::task_queue_handle& task_queue;
srsran::mac_sch_pdu_nr pdu_ul;
};
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
mac_nr::mac_nr(srsran::task_sched_handle task_sched_) :
logger(srslog::fetch_basic_logger("MAC-NR")),
task_sched(task_sched_),
bcch_bch_payload(srsran::make_byte_buffer()),
rar_pdu_buffer(srsran::make_byte_buffer()),
sched(new sched_nr{})
{
stack_task_queue = task_sched.make_task_queue();
}
mac_nr::~mac_nr()
{
stop();
}
int mac_nr::init(const mac_nr_args_t& args_,
phy_interface_stack_nr* phy_,
stack_interface_mac* stack_,
rlc_interface_mac* rlc_,
rrc_interface_mac_nr* rrc_)
{
args = args_;
phy = phy_;
stack = stack_;
rlc = rlc_;
rrc = rrc_;
if (args.pcap.enable) {
pcap = std::unique_ptr<srsran::mac_pcap>(new srsran::mac_pcap());
pcap->open(args.pcap.filename);
}
logger.info("Started");
started = true;
return SRSRAN_SUCCESS;
}
void mac_nr::stop()
{
bool started_prev = started.exchange(false);
if (started_prev) {
sched->stop();
if (pcap != nullptr) {
pcap->close();
}
}
}
/// Called from metrics thread.
/// Note: This can contend for the same mutexes as the ones used by L1/L2 workers.
/// However, get_metrics is called infrequently enough to cause major halts in the L1/L2
void mac_nr::get_metrics(srsenb::mac_metrics_t& metrics)
{
// TODO: We should comment on the logic we follow to get the metrics. Some of them are retrieved from MAC, some
// others from the scheduler.
get_metrics_nolock(metrics);
sched->get_metrics(metrics);
}
void mac_nr::get_metrics_nolock(srsenb::mac_metrics_t& metrics)
{
srsran::rwlock_read_guard lock(rwmutex);
metrics.ues.reserve(ue_db.size());
for (auto& u : ue_db) {
metrics.ues.emplace_back();
u.second->metrics_read(&metrics.ues.back());
}
metrics.cc_info.resize(detected_rachs.size());
for (unsigned cc = 0, e = detected_rachs.size(); cc != e; ++cc) {
metrics.cc_info[cc].cc_rach_counter = detected_rachs[cc];
metrics.cc_info[cc].pci = (cc < cell_config.size()) ? cell_config[cc].pci : 0;
}
}
int mac_nr::cell_cfg(const std::vector<srsenb::sched_nr_cell_cfg_t>& nr_cells)
{
cell_config = nr_cells;
sched->config(args.sched_cfg, nr_cells);
detected_rachs.resize(nr_cells.size());
// read SIBs from RRC (SIB1 for now only)
for (uint32_t i = 0; i < nr_cells[0].sibs.size(); i++) {
sib_info_t sib = {};
sib.index = i;
sib.periodicity = 160; // TODO: read period_rf from config
sib.payload = srsran::make_byte_buffer();
if (sib.payload == nullptr) {
logger.error("Couldn't allocate PDU in %s().", __FUNCTION__);
return SRSRAN_ERROR;
}
if (rrc->read_pdu_bcch_dlsch(sib.index, *sib.payload) != SRSRAN_SUCCESS) {
logger.error("Couldn't read SIB %d from RRC", sib.index);
}
logger.info("Including SIB %d into SI scheduling", sib.index + 1);
bcch_dlsch_payload.push_back(std::move(sib));
}
rx.reset(new mac_nr_rx{rlc, rrc, stack_task_queue, sched.get(), *this, logger});
default_ue_phy_cfg = get_common_ue_phy_cfg(cell_config[0]);
return SRSRAN_SUCCESS;
}
int mac_nr::ue_cfg(uint16_t rnti, const sched_nr_interface::ue_cfg_t& ue_cfg)
{
sched->ue_cfg(rnti, ue_cfg);
return SRSRAN_SUCCESS;
}
uint16_t mac_nr::reserve_rnti(uint32_t enb_cc_idx, const sched_nr_ue_cfg_t& uecfg)
{
uint16_t rnti = alloc_ue(enb_cc_idx);
if (rnti == SRSRAN_INVALID_RNTI) {
return rnti;
}
sched->ue_cfg(rnti, uecfg);
return rnti;
}
void mac_nr::rach_detected(const rach_info_t& rach_info)
{
static srsran::mutexed_tprof<srsran::avg_time_stats> rach_tprof("rach_tprof", "MAC-NR", 1);
logger.set_context(rach_info.slot_index);
auto rach_tprof_meas = rach_tprof.start();
uint32_t enb_cc_idx = 0;
stack_task_queue.push([this, rach_info, enb_cc_idx, rach_tprof_meas]() mutable {
rach_tprof_meas.defer_stop();
uint16_t rnti = alloc_ue(enb_cc_idx);
// Log this event.
{
srsran::rwlock_write_guard lock(rwmutex);
++detected_rachs[enb_cc_idx];
}
// Trigger scheduler RACH
srsenb::sched_nr_interface::rar_info_t rar_info = {};
rar_info.cc = enb_cc_idx;
rar_info.preamble_idx = rach_info.preamble;
rar_info.temp_crnti = rnti;
rar_info.ta_cmd = rach_info.time_adv;
rar_info.prach_slot = slot_point{NUMEROLOGY_IDX, rach_info.slot_index};
sched->dl_rach_info(rar_info);
rrc->add_user(rnti, enb_cc_idx);
logger.info("RACH: slot=%d, cc=%d, preamble=%d, offset=%d, temp_crnti=0x%x",
rach_info.slot_index,
enb_cc_idx,
rach_info.preamble,
rach_info.time_adv,
rnti);
srsran::console("RACH: slot=%d, cc=%d, preamble=%d, offset=%d, temp_crnti=0x%x\n",
rach_info.slot_index,
enb_cc_idx,
rach_info.preamble,
rach_info.time_adv,
rnti);
});
}
uint16_t mac_nr::alloc_ue(uint32_t enb_cc_idx)
{
ue_nr* inserted_ue = nullptr;
uint16_t rnti = SRSRAN_INVALID_RNTI;
do {
// Assign new RNTI
rnti = FIRST_RNTI + (ue_counter.fetch_add(1, std::memory_order_relaxed) % 60000);
// Pre-check if rnti is valid
{
srsran::rwlock_read_guard read_lock(rwmutex);
if (not is_rnti_valid_nolock(rnti)) {
continue;
}
}
// Allocate and initialize UE object
std::unique_ptr<ue_nr> ue_ptr(new ue_nr(rnti, enb_cc_idx, sched.get(), rrc, rlc, phy, logger));
// Add UE to rnti map
srsran::rwlock_write_guard rw_lock(rwmutex);
if (not is_rnti_valid_nolock(rnti)) {
continue;
}
auto ret = ue_db.insert(rnti, std::move(ue_ptr));
if (ret.has_value()) {
inserted_ue = ret.value()->second.get();
} else {
logger.info("Failed to allocate rnti=0x%x. Attempting a different rnti.", rnti);
}
} while (inserted_ue == nullptr);
return rnti;
}
// Remove UE from the perspective of L2/L3
int mac_nr::remove_ue(uint16_t rnti)
{
srsran::rwlock_write_guard lock(rwmutex);
if (is_rnti_active_nolock(rnti)) {
sched->ue_rem(rnti);
ue_db.erase(rnti);
} else {
logger.error("User rnti=0x%x not found", rnti);
return SRSRAN_ERROR;
}
return SRSRAN_SUCCESS;
}
bool mac_nr::is_rnti_valid_nolock(uint16_t rnti)
{
if (not started) {
logger.info("RACH ignored as eNB is being shutdown");
return false;
}
if (ue_db.full()) {
logger.warning("Maximum number of connected UEs %zd connected to the eNB. Ignoring PRACH", SRSENB_MAX_UES);
return false;
}
if (not ue_db.has_space(rnti)) {
logger.info("Failed to allocate rnti=0x%x. Attempting a different rnti.", rnti);
return false;
}
return true;
}
bool mac_nr::is_rnti_active_nolock(uint16_t rnti)
{
if (not ue_db.contains(rnti)) {
logger.error("User rnti=0x%x not found", rnti);
return false;
}
return ue_db[rnti]->is_active();
}
int mac_nr::rlc_buffer_state(uint16_t rnti, uint32_t lc_id, uint32_t tx_queue, uint32_t retx_queue)
{
sched->dl_buffer_state(rnti, lc_id, tx_queue, retx_queue);
return SRSRAN_SUCCESS;
}
void mac_nr::ul_bsr(uint16_t rnti, uint32_t lcid, uint32_t bsr)
{
sched->ul_bsr(rnti, lcid, bsr);
}
int mac_nr::slot_indication(const srsran_slot_cfg_t& slot_cfg)
{
return 0;
}
void mac_nr::store_msg3(uint16_t rnti, srsran::unique_byte_buffer_t pdu)
{
srsran::rwlock_read_guard rw_lock(rwmutex);
if (is_rnti_active_nolock(rnti)) {
ue_db[rnti]->store_msg3(std::move(pdu));
} else {
logger.error("User rnti=0x%x not found. Can't store Msg3.", rnti);
}
}
mac_nr::dl_sched_t* mac_nr::get_dl_sched(const srsran_slot_cfg_t& slot_cfg)
{
slot_point pdsch_slot = srsran::slot_point{NUMEROLOGY_IDX, slot_cfg.idx};
logger.set_context((pdsch_slot - TX_ENB_DELAY).to_uint());
// Initiate new slot and sync UE internal states
sched->slot_indication(pdsch_slot);
// Run DL Scheduler for CC
sched_nr::dl_res_t* dl_res = sched->get_dl_sched(pdsch_slot, 0);
if (dl_res == nullptr) {
return nullptr;
}
// Generate MAC DL PDUs
uint32_t rar_count = 0, si_count = 0, data_count = 0;
srsran::rwlock_read_guard rw_lock(rwmutex);
for (pdsch_t& pdsch : dl_res->phy.pdsch) {
if (pdsch.sch.grant.rnti_type == srsran_rnti_type_c) {
uint16_t rnti = pdsch.sch.grant.rnti;
if (not is_rnti_active_nolock(rnti)) {
continue;
}
for (auto& tb_data : pdsch.data) {
if (tb_data != nullptr and tb_data->N_bytes == 0) {
// TODO: exclude retx from packing
const sched_nr_interface::dl_pdu_t& pdu = dl_res->data[data_count++];
ue_db[rnti]->generate_pdu(tb_data, pdsch.sch.grant.tb->tbs / 8, pdu.subpdus);
if (pcap != nullptr) {
uint32_t pid = 0; // TODO: get PID from PDCCH struct?
pcap->write_dl_crnti_nr(tb_data->msg, tb_data->N_bytes, rnti, pid, slot_cfg.idx);
}
ue_db[rnti]->metrics_dl_mcs(pdsch.sch.grant.tb->mcs);
}
}
} else if (pdsch.sch.grant.rnti_type == srsran_rnti_type_ra) {
sched_nr_interface::rar_t& rar = dl_res->rar[rar_count++];
// for RARs we could actually move the byte_buffer to the PHY, as there are no retx
pdsch.data[0] = assemble_rar(rar.grants);
} else if (pdsch.sch.grant.rnti_type == srsran_rnti_type_si) {
uint32_t sib_idx = dl_res->sib_idxs[si_count++];
pdsch.data[0] = bcch_dlsch_payload[sib_idx].payload.get();
#ifdef WRITE_SIB_PCAP
if (pcap != nullptr) {
pcap->write_dl_si_rnti_nr(bcch_dlsch_payload[sib_idx].payload->msg,
bcch_dlsch_payload[sib_idx].payload->N_bytes,
SI_RNTI,
0,
slot_cfg.idx);
}
#endif
}
}
for (auto& u : ue_db) {
u.second->metrics_cnt();
}
return &dl_res->phy;
}
mac_nr::ul_sched_t* mac_nr::get_ul_sched(const srsran_slot_cfg_t& slot_cfg)
{
slot_point pusch_slot = srsran::slot_point{NUMEROLOGY_IDX, slot_cfg.idx};
ul_sched_t* ul_sched = sched->get_ul_sched(pusch_slot, 0);
srsran::rwlock_read_guard rw_lock(rwmutex);
for (auto& pusch : ul_sched->pusch) {
if (ue_db.contains(pusch.sch.grant.rnti)) {
ue_db[pusch.sch.grant.rnti]->metrics_ul_mcs(pusch.sch.grant.tb->mcs);
}
}
return ul_sched;
}
int mac_nr::pucch_info(const srsran_slot_cfg_t& slot_cfg, const mac_interface_phy_nr::pucch_info_t& pucch_info)
{
if (not handle_uci_data(pucch_info.uci_data.cfg.pucch.rnti, pucch_info.uci_data.cfg, pucch_info.uci_data.value)) {
logger.error("Error handling UCI data from PUCCH reception");
return SRSRAN_ERROR;
}
// process PUCCH SNR
uint16_t rnti = pucch_info.uci_data.cfg.pucch.rnti;
srsran::rwlock_read_guard rw_lock(rwmutex);
if (ue_db.contains(rnti)) {
ue_db[rnti]->metrics_pucch_sinr(pucch_info.csi.snr_dB);
}
return SRSRAN_SUCCESS;
}
bool mac_nr::handle_uci_data(uint16_t rnti, const srsran_uci_cfg_nr_t& cfg_, const srsran_uci_value_nr_t& value)
{
// Process HARQ-ACK
for (uint32_t i = 0; i < cfg_.ack.count; i++) {
const srsran_harq_ack_bit_t* ack_bit = &cfg_.ack.bits[i];
bool is_ok = (value.ack[i] == 1) and value.valid;
sched->dl_ack_info(rnti, 0, ack_bit->pid, 0, is_ok);
srsran::rwlock_read_guard rw_lock(rwmutex);
if (ue_db.contains(rnti)) {
ue_db[rnti]->metrics_tx(is_ok, 0 /*TODO get size of packet from scheduler somehow*/);
}
}
// Process SR
if (value.valid and value.sr > 0) {
sched->ul_sr_info(cfg_.pucch.rnti);
}
// Process CQI
for (uint32_t i = 0; i < cfg_.nof_csi; i++) {
// Skip if invalid or not supported CSI report
if (not value.valid or cfg_.csi[i].cfg.quantity != SRSRAN_CSI_REPORT_QUANTITY_CRI_RI_PMI_CQI or
cfg_.csi[i].cfg.freq_cfg != SRSRAN_CSI_REPORT_FREQ_WIDEBAND or value.csi[i].wideband_cri_ri_pmi_cqi.cqi == 0) {
continue;
}
// 1. Pass CQI report to scheduler
sched->dl_cqi_info(rnti, 0, value.csi->wideband_cri_ri_pmi_cqi.cqi);
// 2. Save CQI report for metrics stats
srsran::rwlock_read_guard rw_lock(rwmutex);
if (ue_db.contains(rnti) && value.valid) {
ue_db[rnti]->metrics_dl_cqi(cfg_, value.csi->wideband_cri_ri_pmi_cqi.cqi);
}
}
return true;
}
int mac_nr::pusch_info(const srsran_slot_cfg_t& slot_cfg, mac_interface_phy_nr::pusch_info_t& pusch_info)
{
uint16_t rnti = pusch_info.rnti;
uint32_t nof_bytes = pusch_info.pdu->N_bytes;
// Handle UCI data
if (not handle_uci_data(rnti, pusch_info.uci_cfg, pusch_info.pusch_data.uci)) {
logger.error("Error handling UCI data from PUCCH reception");
return SRSRAN_ERROR;
}
sched->ul_crc_info(rnti, 0, pusch_info.pid, pusch_info.pusch_data.tb[0].crc);
// process only PDUs with CRC=OK
if (pusch_info.pusch_data.tb[0].crc) {
if (pcap) {
pcap->write_ul_crnti_nr(
pusch_info.pdu->msg, pusch_info.pdu->N_bytes, pusch_info.rnti, pusch_info.pid, slot_cfg.idx);
}
// Decode and send PDU to upper layers
rx->handle_pdu(rnti, std::move(pusch_info.pdu));
}
srsran::rwlock_read_guard rw_lock(rwmutex);
if (ue_db.contains(rnti)) {
ue_db[rnti]->metrics_rx(pusch_info.pusch_data.tb[0].crc, nof_bytes);
ue_db[rnti]->metrics_pusch_sinr(pusch_info.csi.snr_dB);
}
return SRSRAN_SUCCESS;
}
srsran::byte_buffer_t* mac_nr::assemble_rar(srsran::const_span<sched_nr_interface::msg3_grant_t> grants)
{
srsran::mac_rar_pdu_nr rar_pdu;
uint32_t pdsch_tbs = 10; // TODO: how big is the PDSCH?
rar_pdu.init_tx(rar_pdu_buffer.get(), pdsch_tbs);
for (auto& rar_grant : grants) {
srsran::mac_rar_subpdu_nr& rar_subpdu = rar_pdu.add_subpdu();
// set values directly coming from scheduler
rar_subpdu.set_ta(rar_grant.data.ta_cmd);
rar_subpdu.set_rapid(rar_grant.data.preamble_idx);
rar_subpdu.set_temp_crnti(rar_grant.data.temp_crnti);
// convert Msg3 grant to raw UL grant
srsran_dci_nr_t dci = {};
srsran_dci_msg_nr_t dci_msg = {};
if (srsran_dci_nr_ul_pack(&dci, &rar_grant.msg3_dci, &dci_msg) != SRSRAN_SUCCESS) {
logger.error("Couldn't pack Msg3 UL grant");
return nullptr;
}
if (logger.info.enabled()) {
std::array<char, 512> str;
srsran_dci_ul_nr_to_str(&dci, &rar_grant.msg3_dci, str.data(), str.size());
logger.info("Setting RAR Grant %s", str.data());
}
// copy only the required bits
std::array<uint8_t, SRSRAN_RAR_UL_GRANT_NBITS> packed_ul_grant = {};
std::copy(
std::begin(dci_msg.payload), std::begin(dci_msg.payload) + SRSRAN_RAR_UL_GRANT_NBITS, packed_ul_grant.begin());
rar_subpdu.set_ul_grant(packed_ul_grant);
}
if (rar_pdu.pack() != SRSRAN_SUCCESS) {
logger.error("Couldn't assemble RAR PDU");
return nullptr;
}
fmt::memory_buffer buff;
rar_pdu.to_string(buff);
logger.info("%s", srsran::to_c_str(buff));
return rar_pdu_buffer.get();
}
} // namespace srsenb