srsLTE/srsue/src/stack/rrc_nr/rrc_nr_procedures.cc

568 lines
19 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 "srsue/hdr/stack/rrc_nr/rrc_nr_procedures.h"
#include "srsran/common/standard_streams.h"
#define Error(fmt, ...) rrc_handle.logger.error("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__)
#define Warning(fmt, ...) rrc_handle.logger.warning("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__)
#define Info(fmt, ...) rrc_handle.logger.info("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__)
#define Debug(fmt, ...) rrc_handle.logger.debug("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__)
using namespace asn1::rrc_nr;
using namespace asn1;
using namespace srsran;
namespace srsue {
rrc_nr::connection_reconf_no_ho_proc::connection_reconf_no_ho_proc(rrc_nr& parent_) : rrc_handle(parent_), initiator(nr)
{}
proc_outcome_t rrc_nr::connection_reconf_no_ho_proc::init(const reconf_initiator_t initiator_,
const bool endc_release_and_add_r15,
const asn1::rrc_nr::rrc_recfg_s& rrc_nr_reconf)
{
Info("Starting...");
initiator = initiator_;
asn1::json_writer js;
rrc_nr_reconf.to_json(js);
Debug("RRC NR Reconfiguration: %s", js.to_string().c_str());
if (rrc_nr_reconf.crit_exts.rrc_recfg().secondary_cell_group.size() > 0) {
if (rrc_nr_reconf.crit_exts.type() != asn1::rrc_nr::rrc_recfg_s::crit_exts_c_::types::rrc_recfg) {
Error("Reconfiguration does not contain Secondary Cell Group Config.");
return proc_outcome_t::error;
}
cbit_ref bref0(rrc_nr_reconf.crit_exts.rrc_recfg().secondary_cell_group.data(),
rrc_nr_reconf.crit_exts.rrc_recfg().secondary_cell_group.size());
cell_group_cfg_s secondary_cell_group_cfg;
if (secondary_cell_group_cfg.unpack(bref0) != asn1::SRSASN_SUCCESS) {
Error("Could not unpack Secondary Cell Group Config.");
return proc_outcome_t::error;
}
asn1::json_writer js1;
secondary_cell_group_cfg.to_json(js1);
Debug("Secondary Cell Group: %s", js1.to_string().c_str());
Info("Applying Secondary Cell Group Cfg.");
if (!rrc_handle.apply_cell_group_cfg(secondary_cell_group_cfg)) {
return proc_outcome_t::error;
}
}
if (rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.non_crit_ext.non_crit_ext.sk_counter_present) {
Info("Applying SK Counter");
if (!rrc_handle.configure_sk_counter(
(uint16_t)rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.non_crit_ext.non_crit_ext.sk_counter)) {
return proc_outcome_t::error;
}
}
if (rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.master_cell_group.size() > 0) {
cbit_ref bref1(rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.master_cell_group.data(),
rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.master_cell_group.size());
cell_group_cfg_s master_cell_group_cfg;
if (master_cell_group_cfg.unpack(bref1) != asn1::SRSASN_SUCCESS) {
Error("Could not unpack Master Cell Group Config.");
return proc_outcome_t::error;
}
asn1::json_writer js2;
master_cell_group_cfg.to_json(js2);
Debug("Master Cell Group: %s", js2.to_string().c_str());
Info("Applying Master Cell Group Cfg.");
if (!rrc_handle.apply_cell_group_cfg(master_cell_group_cfg)) {
return proc_outcome_t::error;
}
}
if (rrc_nr_reconf.crit_exts.rrc_recfg().radio_bearer_cfg_present) {
Info("Applying Radio Bearer Cfg.");
if (!rrc_handle.apply_radio_bearer_cfg(rrc_nr_reconf.crit_exts.rrc_recfg().radio_bearer_cfg)) {
return proc_outcome_t::error;
}
}
// only send reconfig complete in SA mode
if (rrc_handle.rrc_eutra == nullptr) {
rrc_handle.send_rrc_reconfig_complete();
}
// Handle NAS messages
if (rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.ded_nas_msg_list.size() > 0) {
for (uint32_t i = 0; i < rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.ded_nas_msg_list.size(); ++i) {
srsran::unique_byte_buffer_t nas_pdu = srsran::make_byte_buffer();
if (nas_pdu != nullptr) {
memcpy(nas_pdu->msg,
rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.ded_nas_msg_list[i].data(),
rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.ded_nas_msg_list[i].size());
nas_pdu->N_bytes = rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.ded_nas_msg_list[i].size();
rrc_handle.nas->write_pdu(std::move(nas_pdu));
} else {
rrc_handle.logger.error("Couldn't allocate SDU in %s.", __FUNCTION__);
return proc_outcome_t::error;
}
}
}
return proc_outcome_t::success;
}
proc_outcome_t rrc_nr::connection_reconf_no_ho_proc::react(const bool& config_complete)
{
if (not config_complete) {
Error("NR reconfiguration failed");
return proc_outcome_t::error;
}
// TODO phy ctrl
// in case there are scell to configure, wait for second phy configuration
// if (not rrc_ptr->phy_ctrl->is_config_pending()) {
// return proc_outcome_t::yield;
// }
Info("Reconfig NR return successful");
return proc_outcome_t::success;
}
void rrc_nr::connection_reconf_no_ho_proc::then(const srsran::proc_state_t& result)
{
if (result.is_success()) {
Info("Finished %s successfully", name());
srsran::console("RRC NR reconfiguration successful.\n");
if (rrc_handle.rrc_eutra) {
rrc_handle.rrc_eutra->nr_rrc_con_reconfig_complete(true);
}
} else {
// 5.3.5.8.2 Inability to comply with RRCReconfiguration
switch (initiator) {
case reconf_initiator_t::mcg_srb1:
if (rrc_handle.rrc_eutra) {
rrc_handle.rrc_eutra->nr_notify_reconfiguration_failure();
}
break;
default:
Warning("Reconfiguration failure not implemented for initiator %d", initiator);
break;
}
srsran::console("RRC NR reconfiguration failed.\n");
Warning("Finished %s with failure", name());
}
return;
}
/**************************************
* RRC Setup Request Procedure
*************************************/
rrc_nr::setup_request_proc::setup_request_proc(rrc_nr& parent_) :
rrc_handle(parent_), logger(srslog::fetch_basic_logger("RRC-NR"))
{}
proc_outcome_t rrc_nr::setup_request_proc::init(srsran::nr_establishment_cause_t cause_,
srsran::unique_byte_buffer_t dedicated_info_nas_)
{
cause = cause_;
dedicated_info_nas = std::move(dedicated_info_nas_);
if (!rrc_handle.plmn_is_selected) {
Error("Trying to connect but PLMN not selected.");
return proc_outcome_t::error;
}
if (rrc_handle.state != RRC_NR_STATE_IDLE) {
logger.warning("Requested RRC connection establishment while not in IDLE");
return proc_outcome_t::error;
}
// TODO: add T302 handling
Info("Initiation of Setup request procedure");
cell_search_ret = rrc_cell_search_result_t::no_cell;
state = state_t::cell_selection;
if (rrc_handle.cell_selector.is_idle()) {
// No one is running cell selection
if (not rrc_handle.cell_selector.launch()) {
Error("Failed to initiate cell selection procedure...");
return proc_outcome_t::error;
}
rrc_handle.callback_list.add_proc(rrc_handle.cell_selector);
} else {
Info("Cell selection proc already on-going. Wait for its result");
}
return proc_outcome_t::yield;
}
proc_outcome_t rrc_nr::setup_request_proc::step()
{
if (state == state_t::cell_selection) {
// NOTE: cell selection will signal back with an event trigger
return proc_outcome_t::yield;
}
if (state == state_t::config_serving_cell) {
// TODO: start serving cell config and start T300
// start T300
rrc_handle.t300.run();
// Send setup request message to lower layers
rrc_handle.send_setup_request(cause);
// Save dedicatedInfoNAS SDU, if needed (TODO: this should be passed to procedure without temp storage)
if (dedicated_info_nas.get()) {
if (rrc_handle.dedicated_info_nas.get()) {
Warning("Received a new dedicatedInfoNAS SDU but there was one still in queue. Removing it.");
rrc_handle.dedicated_info_nas.reset();
}
Debug("Updating dedicatedInfoNAS in RRC");
rrc_handle.dedicated_info_nas = std::move(dedicated_info_nas);
} else {
Debug("dedicatedInfoNAS has already been provided to RRC.");
}
Info("Waiting for RRCSetup/Reject or expiry");
state = state_t::wait_t300;
return step();
} else if (state == state_t::wait_t300) {
// Wait until t300 stops due to RRCConnectionSetup/Reject or expiry
if (rrc_handle.t300.is_running()) {
return proc_outcome_t::yield;
}
if (rrc_handle.state == RRC_NR_STATE_CONNECTED) {
// Received ConnectionSetup
return proc_outcome_t::success;
}
}
return proc_outcome_t::error;
}
void rrc_nr::setup_request_proc::then(const srsran::proc_state_t& result)
{
if (result.is_error()) {
logger.warning("Could not finish setup request. Deallocating dedicatedInfoNAS PDU");
dedicated_info_nas.reset();
rrc_handle.dedicated_info_nas.reset();
} else {
Info("Finished connection request procedure successfully.");
}
// TODO: signal back to NAS
// rrc_handle.nas->connection_request_completed(result.is_success());
}
srsran::proc_outcome_t rrc_nr::setup_request_proc::react(const cell_selection_proc::cell_selection_complete_ev& e)
{
if (state != state_t::cell_selection) {
// ignore if we are not expecting an cell selection result
return proc_outcome_t::yield;
}
if (e.is_error()) {
return proc_outcome_t::error;
}
cell_search_ret = *e.value();
// .. and SI acquisition
// TODO @ismagom use appropiate PHY interface
if (true /*rrc_handle.phy->cell_is_camping()*/) {
// TODO: Set default configurations
// rrc_handle.set_phy_default();
// rrc_handle.set_mac_default();
// CCCH configuration applied already at start
// timeAlignmentCommon applied in configure_serving_cell
Info("Configuring serving cell...");
state = state_t::config_serving_cell;
// Skip SI acquisition
return step();
}
}
/******************************************
* Connection Setup Procedure
*****************************************/
// Simple procedure mainly do defer the transmission of the SetupComplete until all PHY reconfiguration are done
rrc_nr::connection_setup_proc::connection_setup_proc(srsue::rrc_nr& parent_) :
rrc_handle(parent_), logger(srslog::fetch_basic_logger("RRC-NR"))
{}
srsran::proc_outcome_t rrc_nr::connection_setup_proc::init(const asn1::rrc_nr::radio_bearer_cfg_s& radio_bearer_cfg_,
const asn1::rrc_nr::cell_group_cfg_s& cell_group_,
srsran::unique_byte_buffer_t dedicated_info_nas_)
{
Info("Starting...");
if (dedicated_info_nas_ == nullptr) {
logger.error("Connection Setup Failed, no dedicatedInfoNAS available");
return proc_outcome_t::error;
}
dedicated_info_nas = std::move(dedicated_info_nas_);
// Stop T300
rrc_handle.t300.stop();
// Apply the Cell Group configuration
if (!rrc_handle.update_cell_group_cfg(cell_group_)) {
return proc_outcome_t::error;
}
// Apply the Radio Bearer configuration
if (!rrc_handle.apply_radio_bearer_cfg(radio_bearer_cfg_)) {
return proc_outcome_t::error;
}
return proc_outcome_t::yield;
}
srsran::proc_outcome_t rrc_nr::connection_setup_proc::react(const bool& config_complete)
{
if (not config_complete) {
logger.error("Connection Setup Failed");
return proc_outcome_t::error;
}
rrc_handle.send_con_setup_complete(std::move(dedicated_info_nas));
return proc_outcome_t::success;
}
void rrc_nr::connection_setup_proc::then(const srsran::proc_state_t& result)
{
if (result.is_success()) {
logger.info("Finished %s successfully", name());
return;
}
}
/**************************************
* Combined Cell Search/Selection Procedure
*************************************/
rrc_nr::cell_selection_proc::cell_selection_proc(rrc_nr& parent_) : rrc_handle(parent_) {}
// Starts PHY's cell search in the current ARFCN
proc_outcome_t rrc_nr::cell_selection_proc::init()
{
Info("Starting...");
state = state_t::phy_cell_search;
// TODO: add full cell selection
// Start cell search
phy_interface_rrc_nr::cell_search_args_t cs_args = {};
cs_args.center_freq_hz = rrc_handle.phy_cfg.carrier.dl_center_frequency_hz;
cs_args.ssb_freq_hz = rrc_handle.phy_cfg.carrier.ssb_center_freq_hz;
cs_args.ssb_scs = rrc_handle.phy_cfg.ssb.scs;
cs_args.ssb_pattern = rrc_handle.phy_cfg.ssb.pattern;
cs_args.duplex_mode = rrc_handle.phy_cfg.duplex.mode;
if (not rrc_handle.phy->start_cell_search(cs_args)) {
Error("Failed to initiate Cell Search.");
return proc_outcome_t::error;
}
return proc_outcome_t::yield;
}
proc_outcome_t rrc_nr::cell_selection_proc::step()
{
switch (state) {
case state_t::phy_cell_search:
case state_t::phy_cell_select:
case state_t::sib_acquire:
// Waits for cell select/search to complete
return proc_outcome_t::yield;
}
return proc_outcome_t::yield;
}
// Handles result of PHY's cell search and triggers PHY cell select when new cell was found
proc_outcome_t
rrc_nr::cell_selection_proc::handle_cell_search_result(const rrc_interface_phy_nr::cell_search_result_t& result)
{
if (!result.cell_found) {
Info("Cell search did not find any cell.");
return proc_outcome_t::error;
}
// Convert Cell measurement in Text
std::array<char, 512> csi_info_str = {};
srsran_csi_meas_info_short(&result.measurements, csi_info_str.data(), (uint32_t)csi_info_str.size());
// Unpack MIB and convert to text
srsran_mib_nr_t mib = {};
std::array<char, 512> mib_info_str = {};
if (srsran_pbch_msg_nr_mib_unpack(&result.pbch_msg, &mib) == SRSASN_SUCCESS) {
// Convert to text
srsran_pbch_msg_nr_mib_info(&mib, mib_info_str.data(), (uint32_t)mib_info_str.size());
} else {
// It could be the PBCH does not carry MIB
strcpy(mib_info_str.data(), "No MIB found");
Error("No MIB found\n");
return proc_outcome_t::error;
}
// Check unsupported settings
if (mib.cell_barred) {
Error("Cell barred");
return proc_outcome_t::error;
}
if (mib.scs_common != srsran_subcarrier_spacing_15kHz) {
Error("Unsupported SCS %s", srsran_subcarrier_spacing_to_str(mib.scs_common));
return proc_outcome_t::error;
}
// Logs the PCI, cell measurements and decoded MIB
Info("Cell search found ARFCN=%d PCI=%d %s %s",
result.ssb_arfcn,
result.pci,
csi_info_str.data(),
mib_info_str.data());
// Apply MIB settings
srsran::phy_cfg_nr_t& phy_cfg = rrc_handle.phy_cfg;
phy_cfg.pdsch.typeA_pos = mib.dmrs_typeA_pos;
phy_cfg.pdsch.scs_cfg = mib.scs_common;
phy_cfg.carrier.pci = result.pci;
// Get pointA and SSB absolute frequencies
double pointA_abs_freq_Hz = phy_cfg.carrier.dl_center_frequency_hz -
phy_cfg.carrier.nof_prb * SRSRAN_NRE * SRSRAN_SUBC_SPACING_NR(phy_cfg.carrier.scs) / 2;
double ssb_abs_freq_Hz = phy_cfg.carrier.ssb_center_freq_hz;
// Calculate integer SSB to pointA frequency offset in Hz
uint32_t ssb_pointA_freq_offset_Hz =
(ssb_abs_freq_Hz > pointA_abs_freq_Hz) ? (uint32_t)(ssb_abs_freq_Hz - pointA_abs_freq_Hz) : 0;
// Create coreset0
if (srsran_coreset_zero(phy_cfg.carrier.pci,
ssb_pointA_freq_offset_Hz,
phy_cfg.ssb.scs,
phy_cfg.carrier.scs,
mib.coreset0_idx,
&phy_cfg.pdcch.coreset[0])) {
Error("Error generating coreset0");
return proc_outcome_t::error;
}
phy_cfg.pdcch.coreset_present[0] = true;
// Create SearchSpace0
make_phy_search_space0_cfg(&phy_cfg.pdcch.search_space[0]);
phy_cfg.pdcch.search_space_present[0] = true;
// Set dummy offset to pass PRACH config check, real value is provided in SIB1
phy_cfg.prach.freq_offset = 1;
// Update PHY configuration
rrc_handle.phy_cfg_state = PHY_CFG_STATE_SA_MIB_CFG;
if (not rrc_handle.phy->set_config(phy_cfg)) {
Error("Setting PHY configuration");
return proc_outcome_t::error;
}
phy_interface_rrc_nr::cell_select_args_t cs_args = {};
cs_args.carrier = rrc_handle.phy_cfg.carrier;
cs_args.ssb_cfg = rrc_handle.phy_cfg.get_ssb_cfg();
// Transition to cell selection ignoring the cell search result
state = state_t::phy_cell_select;
if (not rrc_handle.phy->start_cell_select(cs_args)) {
Error("Could not set start cell search.");
return proc_outcome_t::error;
}
return proc_outcome_t::yield;
}
proc_outcome_t rrc_nr::cell_selection_proc::react(const rrc_interface_phy_nr::cell_select_result_t& event)
{
if (state != state_t::phy_cell_select) {
Warning("Received unexpected cell select result");
return proc_outcome_t::yield;
}
if (event.status != rrc_interface_phy_nr::cell_select_result_t::SUCCESSFUL) {
Error("Couldn't select new serving cell");
phy_search_result.cell_found = false;
rrc_search_result = rrc_nr::rrc_cell_search_result_t::no_cell;
return proc_outcome_t::error;
}
rrc_search_result = rrc_nr::rrc_cell_search_result_t::same_cell;
// PHY is now camping on serving cell
Info("Cell selection completed. Starting SIB1 acquisition");
// Transition to cell selection ignoring the cell search result
state = state_t::sib_acquire;
rrc_handle.mac->bcch_search(true);
return proc_outcome_t::yield;
}
proc_outcome_t rrc_nr::cell_selection_proc::react(const bool sib1_found)
{
if (state != state_t::sib_acquire) {
Warning("Received unexpected cell select result");
return proc_outcome_t::yield;
}
Info("SIB1 acquired successfully");
rrc_handle.mac->bcch_search(false);
return proc_outcome_t::success;
}
proc_outcome_t rrc_nr::cell_selection_proc::react(const rrc_interface_phy_nr::cell_search_result_t& event)
{
if (state != state_t::phy_cell_search) {
Error("Received unexpected cell search result");
return proc_outcome_t::error;
}
phy_search_result = event;
if (phy_search_result.cell_found) {
return handle_cell_search_result(phy_search_result);
}
return proc_outcome_t::error;
}
void rrc_nr::cell_selection_proc::then(const cell_selection_complete_ev& proc_result) const
{
Info("Completed with %s.", proc_result.is_success() ? "success" : "failure");
// Inform Connection Request Procedure
rrc_handle.task_sched.defer_task([this, proc_result]() {
if (rrc_handle.setup_req_proc.is_busy()) {
rrc_handle.setup_req_proc.trigger(proc_result);
}
});
}
} // namespace srsue