2537 lines
88 KiB
C++
2537 lines
88 KiB
C++
/*
|
|
* Copyright 2013-2020 Software Radio Systems Limited
|
|
*
|
|
* This file is part of srsLTE.
|
|
*
|
|
* srsLTE 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.
|
|
*
|
|
* srsLTE 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/rrc.h"
|
|
#include "srslte/asn1/rrc_asn1.h"
|
|
#include "srslte/common/bcd_helpers.h"
|
|
#include "srslte/common/security.h"
|
|
#include "srsue/hdr/stack/rrc/phy_controller.h"
|
|
#include "srsue/hdr/stack/rrc/rrc_meas.h"
|
|
#include "srsue/hdr/stack/rrc/rrc_procedures.h"
|
|
#include <cstdlib>
|
|
#include <ctime>
|
|
#include <inttypes.h> // for printing uint64_t
|
|
#include <iostream>
|
|
#include <math.h>
|
|
#include <numeric>
|
|
#include <sstream>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
bool simulate_rlf = false;
|
|
|
|
using namespace srslte;
|
|
using namespace asn1::rrc;
|
|
|
|
namespace srsue {
|
|
|
|
const static uint32_t NOF_REQUIRED_SIBS = 4;
|
|
const static uint32_t required_sibs[NOF_REQUIRED_SIBS] = {0, 1, 2, 12}; // SIB1, SIB2, SIB3 and SIB13 (eMBMS)
|
|
|
|
/*******************************************************************************
|
|
Base functions
|
|
*******************************************************************************/
|
|
|
|
rrc::rrc(stack_interface_rrc* stack_, srslte::task_sched_handle task_sched_) :
|
|
stack(stack_),
|
|
task_sched(task_sched_),
|
|
state(RRC_STATE_IDLE),
|
|
last_state(RRC_STATE_CONNECTED),
|
|
drb_up(false),
|
|
rrc_log("RRC"),
|
|
measurements(new rrc_meas()),
|
|
cell_searcher(this),
|
|
si_acquirer(this),
|
|
serv_cell_cfg(this),
|
|
cell_selector(this),
|
|
idle_setter(this),
|
|
pcch_processor(this),
|
|
conn_req_proc(this),
|
|
plmn_searcher(this),
|
|
cell_reselector(this),
|
|
connection_reest(this),
|
|
ho_handler(this)
|
|
{}
|
|
|
|
rrc::~rrc() = default;
|
|
|
|
template <class T>
|
|
void rrc::log_rrc_message(const std::string source,
|
|
const direction_t dir,
|
|
const byte_buffer_t* pdu,
|
|
const T& msg,
|
|
const std::string& msg_type)
|
|
{
|
|
if (rrc_log->get_level() == srslte::LOG_LEVEL_INFO) {
|
|
rrc_log->info("%s - %s %s (%d B)\n", source.c_str(), (dir == Rx) ? "Rx" : "Tx", msg_type.c_str(), pdu->N_bytes);
|
|
} else if (rrc_log->get_level() >= srslte::LOG_LEVEL_DEBUG) {
|
|
asn1::json_writer json_writer;
|
|
msg.to_json(json_writer);
|
|
rrc_log->debug_hex(pdu->msg,
|
|
pdu->N_bytes,
|
|
"%s - %s %s (%d B)\n",
|
|
source.c_str(),
|
|
(dir == Rx) ? "Rx" : "Tx",
|
|
msg_type.c_str(),
|
|
pdu->N_bytes);
|
|
rrc_log->debug_long("Content:\n%s\n", json_writer.to_string().c_str());
|
|
}
|
|
}
|
|
|
|
void rrc::init(phy_interface_rrc_lte* phy_,
|
|
mac_interface_rrc* mac_,
|
|
rlc_interface_rrc* rlc_,
|
|
pdcp_interface_rrc* pdcp_,
|
|
nas_interface_rrc* nas_,
|
|
usim_interface_rrc* usim_,
|
|
gw_interface_rrc* gw_,
|
|
const rrc_args_t& args_)
|
|
{
|
|
pool = byte_buffer_pool::get_instance();
|
|
phy = phy_;
|
|
mac = mac_;
|
|
rlc = rlc_;
|
|
pdcp = pdcp_;
|
|
nas = nas_;
|
|
usim = usim_;
|
|
gw = gw_;
|
|
|
|
args = args_;
|
|
|
|
auto on_every_cell_selection = [this](uint32_t earfcn, uint32_t pci, bool csel_result) {
|
|
if (not csel_result) {
|
|
cell_t* c = meas_cells.find_cell(earfcn, pci);
|
|
if (c != nullptr) {
|
|
c->set_rsrp(-INFINITY);
|
|
}
|
|
}
|
|
};
|
|
phy_ctrl.reset(new phy_controller{phy, task_sched, on_every_cell_selection});
|
|
|
|
state = RRC_STATE_IDLE;
|
|
plmn_is_selected = false;
|
|
|
|
security_is_activated = false;
|
|
|
|
t300 = task_sched.get_unique_timer();
|
|
t301 = task_sched.get_unique_timer();
|
|
t302 = task_sched.get_unique_timer();
|
|
t310 = task_sched.get_unique_timer();
|
|
t311 = task_sched.get_unique_timer();
|
|
t304 = task_sched.get_unique_timer();
|
|
|
|
ue_identity_configured = false;
|
|
|
|
transaction_id = 0;
|
|
|
|
cell_clean_cnt = 0;
|
|
|
|
// Set default values for RRC. MAC and PHY are set to default themselves
|
|
set_rrc_default();
|
|
|
|
measurements->init(this);
|
|
|
|
struct timeval tv;
|
|
gettimeofday(&tv, NULL);
|
|
rrc_log->info("using srand seed of %ld\n", tv.tv_usec);
|
|
|
|
// set seed (used in CHAP auth and attach)
|
|
srand(tv.tv_usec);
|
|
|
|
// initiate unique procedures
|
|
ue_required_sibs.assign(&required_sibs[0], &required_sibs[NOF_REQUIRED_SIBS]);
|
|
|
|
running = true;
|
|
initiated = true;
|
|
}
|
|
|
|
void rrc::stop()
|
|
{
|
|
running = false;
|
|
stop_timers();
|
|
cmd_msg_t msg;
|
|
msg.command = cmd_msg_t::STOP;
|
|
cmd_q.push(std::move(msg));
|
|
}
|
|
|
|
void rrc::get_metrics(rrc_metrics_t& m)
|
|
{
|
|
m.state = state;
|
|
// Save strongest cells metrics
|
|
for (auto& c : meas_cells) {
|
|
rrc_interface_phy_lte::phy_meas_t meas = {};
|
|
meas.cfo_hz = c->get_cfo_hz();
|
|
meas.earfcn = c->get_earfcn();
|
|
meas.rsrq = c->get_rsrq();
|
|
meas.rsrp = c->get_rsrp();
|
|
meas.pci = c->get_pci();
|
|
m.neighbour_cells.push_back(meas);
|
|
}
|
|
}
|
|
|
|
bool rrc::is_connected()
|
|
{
|
|
return (RRC_STATE_CONNECTED == state);
|
|
}
|
|
|
|
bool rrc::have_drb()
|
|
{
|
|
return drb_up;
|
|
}
|
|
|
|
/*
|
|
*
|
|
* RRC State Machine
|
|
*
|
|
*/
|
|
void rrc::run_tti()
|
|
{
|
|
if (!initiated) {
|
|
return;
|
|
}
|
|
|
|
if (simulate_rlf) {
|
|
radio_link_failure_process();
|
|
simulate_rlf = false;
|
|
}
|
|
|
|
// Process pending PHY measurements in IDLE/CONNECTED
|
|
process_cell_meas();
|
|
|
|
// Process on-going callbacks, and clear finished callbacks
|
|
callback_list.run();
|
|
|
|
// Log state changes
|
|
if (state != last_state) {
|
|
rrc_log->debug("State %s\n", rrc_state_text[state]);
|
|
last_state = state;
|
|
}
|
|
|
|
// Run state machine
|
|
switch (state) {
|
|
case RRC_STATE_IDLE:
|
|
break;
|
|
case RRC_STATE_CONNECTED:
|
|
measurements->run_tti();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Handle Received Messages
|
|
if (running) {
|
|
cmd_msg_t msg;
|
|
if (cmd_q.try_pop(&msg)) {
|
|
switch (msg.command) {
|
|
case cmd_msg_t::PCCH:
|
|
process_pcch(std::move(msg.pdu));
|
|
break;
|
|
case cmd_msg_t::RLF:
|
|
radio_link_failure_process();
|
|
break;
|
|
case cmd_msg_t::RA_COMPLETE:
|
|
if (ho_handler.is_busy()) {
|
|
ho_handler.trigger(ho_proc::ra_completed_ev{msg.lcid > 0});
|
|
}
|
|
break;
|
|
case cmd_msg_t::STOP:
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Clean old neighbours
|
|
cell_clean_cnt++;
|
|
if (cell_clean_cnt == 1000) {
|
|
meas_cells.clean_neighbours();
|
|
cell_clean_cnt = 0;
|
|
}
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
*
|
|
*
|
|
* NAS interface: PLMN search and RRC connection establishment
|
|
*
|
|
*
|
|
*
|
|
*******************************************************************************/
|
|
|
|
uint16_t rrc::get_mcc()
|
|
{
|
|
return meas_cells.serving_cell().get_mcc();
|
|
}
|
|
|
|
uint16_t rrc::get_mnc()
|
|
{
|
|
return meas_cells.serving_cell().get_mnc();
|
|
}
|
|
|
|
/* NAS interface to search for available PLMNs.
|
|
* It goes through all known frequencies, synchronizes and receives SIB1 for each to extract PLMN.
|
|
* The function is blocking and waits until all frequencies have been
|
|
* searched and PLMNs are obtained.
|
|
*
|
|
* This function is thread-safe with connection_request()
|
|
*/
|
|
bool rrc::plmn_search()
|
|
{
|
|
if (not plmn_searcher.launch()) {
|
|
rrc_log->error("Unable to initiate PLMN search\n");
|
|
return false;
|
|
}
|
|
callback_list.add_proc(plmn_searcher);
|
|
return true;
|
|
}
|
|
|
|
/* This is the NAS interface. When NAS requests to select a PLMN we have to
|
|
* connect to either register or because there is pending higher layer traffic.
|
|
*/
|
|
void rrc::plmn_select(srslte::plmn_id_t plmn_id)
|
|
{
|
|
plmn_is_selected = true;
|
|
selected_plmn_id = plmn_id;
|
|
|
|
rrc_log->info("PLMN Selected %s\n", plmn_id.to_string().c_str());
|
|
}
|
|
|
|
/* 5.3.3.2 Initiation of RRC Connection Establishment procedure
|
|
*
|
|
* Higher layers request establishment of RRC connection while UE is in RRC_IDLE
|
|
*
|
|
* This procedure selects a suitable cell for transmission of RRCConnectionRequest and configures
|
|
* it. Sends connectionRequest message and returns if message transmitted successfully.
|
|
* It does not wait until completition of Connection Establishment procedure
|
|
*/
|
|
bool rrc::connection_request(srslte::establishment_cause_t cause, srslte::unique_byte_buffer_t dedicated_info_nas_)
|
|
{
|
|
if (not conn_req_proc.launch(cause, std::move(dedicated_info_nas_))) {
|
|
rrc_log->error("Failed to initiate connection request procedure\n");
|
|
return false;
|
|
}
|
|
callback_list.add_proc(conn_req_proc);
|
|
return true;
|
|
}
|
|
|
|
void rrc::set_ue_identity(srslte::s_tmsi_t s_tmsi)
|
|
{
|
|
ue_identity_configured = true;
|
|
ue_identity = s_tmsi;
|
|
rrc_log->info(
|
|
"Set ue-Identity to 0x%" PRIu64 ":0x%" PRIu64 "\n", (uint64_t)ue_identity.mmec, (uint64_t)ue_identity.m_tmsi);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
*
|
|
*
|
|
* PHY interface: neighbour and serving cell measurements and out-of-sync/in-sync
|
|
*
|
|
*
|
|
*
|
|
*******************************************************************************/
|
|
|
|
void rrc::cell_search_complete(rrc_interface_phy_lte::cell_search_ret_t cs_ret, phy_cell_t found_cell)
|
|
{
|
|
phy_ctrl->cell_search_completed(cs_ret, found_cell);
|
|
}
|
|
|
|
void rrc::cell_select_complete(bool cs_ret)
|
|
{
|
|
phy_ctrl->cell_selection_completed(cs_ret);
|
|
}
|
|
|
|
void rrc::set_config_complete(bool status) {}
|
|
|
|
void rrc::set_scell_complete(bool status) {}
|
|
|
|
/* This function is called from a PHY worker thus must return very quickly.
|
|
* Queue the values of the measurements and process them from the RRC thread
|
|
*/
|
|
void rrc::new_cell_meas(const std::vector<phy_meas_t>& meas)
|
|
{
|
|
cell_meas_q.push(meas);
|
|
}
|
|
|
|
/* Processes all pending PHY measurements in queue.
|
|
*/
|
|
void rrc::process_cell_meas()
|
|
{
|
|
std::vector<phy_meas_t> m;
|
|
while (cell_meas_q.try_pop(&m)) {
|
|
if (cell_meas_q.size() > 0) {
|
|
rrc_log->debug("MEAS: Processing measurement. %zd measurements in queue\n", cell_meas_q.size());
|
|
}
|
|
process_new_cell_meas(m);
|
|
}
|
|
}
|
|
|
|
void rrc::process_new_cell_meas(const std::vector<phy_meas_t>& meas)
|
|
{
|
|
const std::function<void(cell_t&, const phy_meas_t&)> filter = [this](cell_t& c, const phy_meas_t& m) {
|
|
c.set_rsrp(measurements->rsrp_filter(m.rsrp, c.get_rsrp()));
|
|
c.set_rsrq(measurements->rsrq_filter(m.rsrq, c.get_rsrq()));
|
|
c.set_cfo(m.cfo_hz);
|
|
};
|
|
|
|
rrc_log->debug("MEAS: Processing measurement of %zd cells\n", meas.size());
|
|
|
|
bool neighbour_added = meas_cells.process_new_cell_meas(meas, filter);
|
|
|
|
// Instruct measurements subclass to update phy with new cells to measure based on strongest neighbours
|
|
if (state == RRC_STATE_CONNECTED && neighbour_added) {
|
|
measurements->update_phy();
|
|
}
|
|
}
|
|
|
|
// Detection of physical layer problems in RRC_CONNECTED (5.3.11.1)
|
|
void rrc::out_of_sync()
|
|
{
|
|
// CAUTION: We do not lock in this function since they are called from real-time threads
|
|
if (meas_cells.serving_cell().is_valid() && rrc_log) {
|
|
phy_ctrl->out_sync();
|
|
|
|
// upon receiving N310 consecutive "out-of-sync" indications for the PCell from lower layers while neither T300,
|
|
// T301, T304 nor T311 is running:
|
|
if (state == RRC_STATE_CONNECTED) {
|
|
// upon receiving N310 consecutive "out-of-sync" indications from lower layers while neither T300, T301, T304
|
|
// nor T311 is running
|
|
bool t311_running = t311.is_running() || connection_reest.is_busy();
|
|
if (!t300.is_running() and !t301.is_running() and !t304.is_running() and !t310.is_running() and !t311_running) {
|
|
rrc_log->info("Received out-of-sync while in state %s. n310=%d, t311=%s, t310=%s\n",
|
|
rrc_state_text[state],
|
|
n310_cnt,
|
|
t311.is_running() ? "running" : "stop",
|
|
t310.is_running() ? "running" : "stop");
|
|
n310_cnt++;
|
|
if (n310_cnt == N310) {
|
|
rrc_log->info(
|
|
"Detected %d out-of-sync from PHY. Trying to resync. Starting T310 timer %d ms\n", N310, t310.duration());
|
|
t310.run();
|
|
n310_cnt = 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Recovery of physical layer problems (5.3.11.2)
|
|
void rrc::in_sync()
|
|
{
|
|
// CAUTION: We do not lock in this function since they are called from real-time threads
|
|
phy_ctrl->in_sync();
|
|
if (t310.is_running()) {
|
|
n311_cnt++;
|
|
if (n311_cnt == N311) {
|
|
t310.stop();
|
|
n311_cnt = 0;
|
|
rrc_log->info("Detected %d in-sync from PHY. Stopping T310 timer\n", N311);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
*
|
|
*
|
|
* Cell selection, reselection and neighbour cell database management
|
|
*
|
|
*
|
|
*
|
|
*******************************************************************************/
|
|
|
|
// Cell selection criteria Section 5.2.3.2 of 36.304
|
|
bool rrc::cell_selection_criteria(float rsrp, float rsrq)
|
|
{
|
|
return std::isnormal(rsrp) && (get_srxlev(rsrp) > 0 || !meas_cells.serving_cell().has_sib3());
|
|
}
|
|
|
|
float rrc::get_srxlev(float Qrxlevmeas)
|
|
{
|
|
// TODO: Do max power limitation
|
|
float Pcompensation = 0;
|
|
return Qrxlevmeas - (cell_resel_cfg.Qrxlevmin + cell_resel_cfg.Qrxlevminoffset) - Pcompensation;
|
|
}
|
|
|
|
float rrc::get_squal(float Qqualmeas)
|
|
{
|
|
return Qqualmeas - (cell_resel_cfg.Qqualmin + cell_resel_cfg.Qqualminoffset);
|
|
}
|
|
|
|
// Cell reselection in IDLE Section 5.2.4 of 36.304
|
|
void rrc::cell_reselection(float rsrp, float rsrq)
|
|
{
|
|
// Intra-frequency cell-reselection criteria
|
|
|
|
if (get_srxlev(rsrp) > cell_resel_cfg.s_intrasearchP && rsrp > -95.0) {
|
|
// UE may not perform intra-frequency measurements->
|
|
phy->meas_stop();
|
|
} else {
|
|
// UE must start intra-frequency measurements
|
|
auto pci = meas_cells.get_neighbour_pcis(meas_cells.serving_cell().get_earfcn());
|
|
phy->set_cells_to_meas(meas_cells.serving_cell().get_earfcn(), pci);
|
|
}
|
|
|
|
// TODO: Inter-frequency cell reselection
|
|
}
|
|
|
|
// Set new serving cell
|
|
void rrc::set_serving_cell(phy_cell_t phy_cell, bool discard_serving)
|
|
{
|
|
meas_cells.set_serving_cell(phy_cell, discard_serving);
|
|
}
|
|
|
|
int rrc::start_cell_select()
|
|
{
|
|
if (not cell_selector.launch()) {
|
|
rrc_log->error("Failed to initiate a Cell Selection procedure...\n");
|
|
return SRSLTE_ERROR;
|
|
}
|
|
callback_list.add_proc(cell_selector);
|
|
return SRSLTE_SUCCESS;
|
|
}
|
|
|
|
bool rrc::has_neighbour_cell(uint32_t earfcn, uint32_t pci) const
|
|
{
|
|
return meas_cells.has_neighbour_cell(earfcn, pci);
|
|
}
|
|
|
|
bool rrc::is_serving_cell(uint32_t earfcn, uint32_t pci) const
|
|
{
|
|
return meas_cells.serving_cell().phy_cell.earfcn == earfcn and meas_cells.serving_cell().phy_cell.pci == pci;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
*
|
|
*
|
|
* eMBMS Related Functions
|
|
*
|
|
*
|
|
*
|
|
*******************************************************************************/
|
|
std::string rrc::print_mbms()
|
|
{
|
|
mcch_msg_type_c msg = meas_cells.serving_cell().mcch.msg;
|
|
std::stringstream ss;
|
|
for (uint32_t i = 0; i < msg.c1().mbsfn_area_cfg_r9().pmch_info_list_r9.size(); i++) {
|
|
ss << "PMCH: " << i << std::endl;
|
|
pmch_info_r9_s* pmch = &msg.c1().mbsfn_area_cfg_r9().pmch_info_list_r9[i];
|
|
for (uint32_t j = 0; j < pmch->mbms_session_info_list_r9.size(); j++) {
|
|
mbms_session_info_r9_s* sess = &pmch->mbms_session_info_list_r9[j];
|
|
ss << " Service ID: " << sess->tmgi_r9.service_id_r9.to_string();
|
|
if (sess->session_id_r9_present) {
|
|
ss << ", Session ID: " << sess->session_id_r9.to_string();
|
|
}
|
|
if (sess->tmgi_r9.plmn_id_r9.type() == tmgi_r9_s::plmn_id_r9_c_::types::explicit_value_r9) {
|
|
ss << ", MCC: " << mcc_bytes_to_string(&sess->tmgi_r9.plmn_id_r9.explicit_value_r9().mcc[0]);
|
|
ss << ", MNC: " << mnc_bytes_to_string(sess->tmgi_r9.plmn_id_r9.explicit_value_r9().mnc);
|
|
} else {
|
|
ss << ", PLMN index: " << sess->tmgi_r9.plmn_id_r9.plmn_idx_r9();
|
|
}
|
|
ss << ", LCID: " << sess->lc_ch_id_r9 << std::endl;
|
|
}
|
|
}
|
|
return ss.str();
|
|
}
|
|
|
|
bool rrc::mbms_service_start(uint32_t serv, uint32_t port)
|
|
{
|
|
bool ret = false;
|
|
if (!meas_cells.serving_cell().has_mcch) {
|
|
rrc_log->error("MCCH not available at MBMS Service Start\n");
|
|
return ret;
|
|
}
|
|
|
|
rrc_log->info("%s\n", print_mbms().c_str());
|
|
|
|
mcch_msg_type_c msg = meas_cells.serving_cell().mcch.msg;
|
|
for (uint32_t i = 0; i < msg.c1().mbsfn_area_cfg_r9().pmch_info_list_r9.size(); i++) {
|
|
pmch_info_r9_s* pmch = &msg.c1().mbsfn_area_cfg_r9().pmch_info_list_r9[i];
|
|
for (uint32_t j = 0; j < pmch->mbms_session_info_list_r9.size(); j++) {
|
|
mbms_session_info_r9_s* sess = &pmch->mbms_session_info_list_r9[j];
|
|
if (serv == sess->tmgi_r9.service_id_r9.to_number()) {
|
|
rrc_log->console("MBMS service started. Service id=%d, port=%d, lcid=%d\n", serv, port, sess->lc_ch_id_r9);
|
|
ret = true;
|
|
add_mrb(sess->lc_ch_id_r9, port);
|
|
}
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
*
|
|
*
|
|
* Other functions
|
|
*
|
|
*
|
|
*
|
|
*******************************************************************************/
|
|
|
|
/*
|
|
* 5.3.11.3 Detection of RLF
|
|
* The RLF procedure starts:
|
|
* - upon T310 expiry;
|
|
* - upon random access problem indication from MAC while neither T300, T301, T304 nor T311 is running; or
|
|
* - upon indication from RLC that the maximum number of retransmissions has been reached:
|
|
*/
|
|
void rrc::radio_link_failure_push_cmd()
|
|
{
|
|
cmd_msg_t msg;
|
|
msg.command = cmd_msg_t::RLF;
|
|
cmd_q.push(std::move(msg));
|
|
}
|
|
|
|
/*
|
|
* Perform the actions upon detection of radio link failure (5.3.11.3)
|
|
* This function must be executed from the main RRC task to avoid stack loops
|
|
*/
|
|
void rrc::radio_link_failure_process()
|
|
{
|
|
// TODO: Generate and store failure report
|
|
rrc_log->console("Warning: Detected Radio-Link Failure\n");
|
|
|
|
if (state == RRC_STATE_CONNECTED) {
|
|
if (security_is_activated) {
|
|
rrc_log->warning("Detected Radio-Link Failure while SA activated. Starting ConnectionReestablishment...\n");
|
|
start_con_restablishment(reest_cause_e::other_fail);
|
|
} else {
|
|
rrc_log->warning("Detected Radio-Link Failure while SA not activated. Going to IDLE...\n");
|
|
start_go_idle();
|
|
}
|
|
} else {
|
|
rrc_log->warning("Detected Radio-Link Failure while RRC_IDLE. Ignoring it.\n");
|
|
}
|
|
}
|
|
|
|
/* Reception of PUCCH/SRS release procedure (Section 5.3.13) */
|
|
void rrc::release_pucch_srs()
|
|
{
|
|
// Apply default configuration for PUCCH (CQI and SR) and SRS (release)
|
|
if (initiated) {
|
|
set_phy_default_pucch_srs();
|
|
}
|
|
}
|
|
|
|
void rrc::ra_problem()
|
|
{
|
|
if (not t300.is_running() and not t301.is_running() and not t304.is_running() and not t311.is_running()) {
|
|
rrc_log->warning("MAC indicated RA problem. Starting RLF\n");
|
|
radio_link_failure_push_cmd();
|
|
} else {
|
|
rrc_log->warning("MAC indicated RA problem but either T300, T301, T304 or T311 is running. Ignoring it.\n");
|
|
}
|
|
}
|
|
|
|
void rrc::max_retx_attempted()
|
|
{
|
|
// TODO: Handle the radio link failure
|
|
rrc_log->warning("Max RLC reTx attempted. Starting RLF\n");
|
|
radio_link_failure_push_cmd();
|
|
}
|
|
|
|
void rrc::timer_expired(uint32_t timeout_id)
|
|
{
|
|
if (timeout_id == t310.id()) {
|
|
rrc_log->info("Timer T310 expired: Radio Link Failure\n");
|
|
radio_link_failure_push_cmd();
|
|
} else if (timeout_id == t311.id()) {
|
|
rrc_log->console("Timer T311 expired: Going to RRC IDLE\n");
|
|
if (connection_reest.is_idle()) {
|
|
rrc_log->info("Timer T311 expired: Going to RRC IDLE\n");
|
|
start_go_idle();
|
|
} else {
|
|
// Do nothing, this is handled by the procedure
|
|
connection_reest.trigger(connection_reest_proc::t311_expiry{});
|
|
}
|
|
} else if (timeout_id == t301.id()) {
|
|
if (state == RRC_STATE_IDLE) {
|
|
rrc_log->info("Timer T301 expired: Already in IDLE.\n");
|
|
} else {
|
|
rrc_log->info("Timer T301 expired: Going to RRC IDLE\n");
|
|
connection_reest.trigger(connection_reest_proc::t301_expiry{});
|
|
}
|
|
} else if (timeout_id == t302.id()) {
|
|
rrc_log->info("Timer T302 expired. Informing NAS about barrier alleviation\n");
|
|
nas->set_barring(srslte::barring_t::none);
|
|
} else if (timeout_id == t300.id()) {
|
|
// Do nothing, handled in connection_request()
|
|
} else if (timeout_id == t304.id()) {
|
|
rrc_log->console("Timer t304 expired: Handover failed\n");
|
|
rrc_log->info("Timer t304 expired: Handover failed\n");
|
|
ho_failed();
|
|
} else {
|
|
rrc_log->error("Timeout from unknown timer id %d\n", timeout_id);
|
|
}
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
*
|
|
*
|
|
* Connection Control: Establishment, Reconfiguration, Reestablishment and Release
|
|
*
|
|
*
|
|
*
|
|
*******************************************************************************/
|
|
|
|
void rrc::send_con_request(srslte::establishment_cause_t cause)
|
|
{
|
|
rrc_log->debug("Preparing RRC Connection Request\n");
|
|
|
|
// Prepare ConnectionRequest packet
|
|
ul_ccch_msg_s ul_ccch_msg;
|
|
rrc_conn_request_r8_ies_s* rrc_conn_req =
|
|
&ul_ccch_msg.msg.set_c1().set_rrc_conn_request().crit_exts.set_rrc_conn_request_r8();
|
|
|
|
if (ue_identity_configured) {
|
|
rrc_conn_req->ue_id.set_s_tmsi();
|
|
srslte::to_asn1(&rrc_conn_req->ue_id.s_tmsi(), ue_identity);
|
|
} else {
|
|
rrc_conn_req->ue_id.set_random_value();
|
|
// TODO use proper RNG
|
|
uint64_t random_id = 0;
|
|
for (uint i = 0; i < 5; i++) { // fill random ID bytewise, 40 bits = 5 bytes
|
|
random_id |= ((uint64_t)rand() & 0xFF) << i * 8;
|
|
}
|
|
rrc_conn_req->ue_id.random_value().from_number(random_id);
|
|
}
|
|
rrc_conn_req->establishment_cause = (establishment_cause_opts::options)cause;
|
|
|
|
send_ul_ccch_msg(ul_ccch_msg);
|
|
}
|
|
|
|
/* RRC connection re-establishment procedure (5.3.7.4) */
|
|
void rrc::send_con_restablish_request(reest_cause_e cause, uint16_t crnti, uint16_t pci, uint32_t cellid)
|
|
{
|
|
// Clean reestablishment type
|
|
reestablishment_successful = false;
|
|
|
|
if (cause.value != reest_cause_opts::ho_fail) {
|
|
if (cause.value != reest_cause_opts::other_fail) {
|
|
pci = meas_cells.serving_cell().get_pci();
|
|
}
|
|
cellid = meas_cells.serving_cell().get_cell_id();
|
|
}
|
|
|
|
// Compute shortMAC-I
|
|
uint8_t varShortMAC_packed[16] = {};
|
|
asn1::bit_ref bref(varShortMAC_packed, sizeof(varShortMAC_packed));
|
|
|
|
// ASN.1 encode VarShortMAC-Input
|
|
var_short_mac_input_s varmac;
|
|
varmac.cell_id.from_number(cellid);
|
|
varmac.pci = pci;
|
|
varmac.c_rnti.from_number(crnti);
|
|
varmac.pack(bref);
|
|
uint32_t N_bits = (uint32_t)bref.distance(varShortMAC_packed);
|
|
uint32_t N_bytes = ((N_bits - 1) / 8 + 1);
|
|
|
|
rrc_log->info(
|
|
"Encoded varShortMAC: cellId=0x%x, PCI=%d, rnti=0x%x (%d bytes, %d bits)\n", cellid, pci, crnti, N_bytes, N_bits);
|
|
|
|
// Compute MAC-I
|
|
uint8_t mac_key[4] = {};
|
|
switch (sec_cfg.integ_algo) {
|
|
case INTEGRITY_ALGORITHM_ID_128_EIA1:
|
|
security_128_eia1(&sec_cfg.k_rrc_int[16],
|
|
0xffffffff, // 32-bit all to ones
|
|
0x1f, // 5-bit all to ones
|
|
1, // 1-bit to one
|
|
varShortMAC_packed,
|
|
N_bytes,
|
|
mac_key);
|
|
break;
|
|
case INTEGRITY_ALGORITHM_ID_128_EIA2:
|
|
security_128_eia2(&sec_cfg.k_rrc_int[16],
|
|
0xffffffff, // 32-bit all to ones
|
|
0x1f, // 5-bit all to ones
|
|
1, // 1-bit to one
|
|
varShortMAC_packed,
|
|
N_bytes,
|
|
mac_key);
|
|
break;
|
|
case INTEGRITY_ALGORITHM_ID_128_EIA3:
|
|
security_128_eia3(&sec_cfg.k_rrc_int[16],
|
|
0xffffffff, // 32-bit all to ones
|
|
0x1f, // 5-bit all to ones
|
|
1, // 1-bit to one
|
|
varShortMAC_packed,
|
|
N_bytes,
|
|
mac_key);
|
|
break;
|
|
default:
|
|
rrc_log->info("Unsupported integrity algorithm during reestablishment\n");
|
|
}
|
|
|
|
// Prepare ConnectionRestalishmentRequest packet
|
|
asn1::rrc::ul_ccch_msg_s ul_ccch_msg;
|
|
rrc_conn_reest_request_r8_ies_s* rrc_conn_reest_req =
|
|
&ul_ccch_msg.msg.set_c1().set_rrc_conn_reest_request().crit_exts.set_rrc_conn_reest_request_r8();
|
|
|
|
rrc_conn_reest_req->ue_id.c_rnti.from_number(crnti);
|
|
rrc_conn_reest_req->ue_id.pci = pci;
|
|
rrc_conn_reest_req->ue_id.short_mac_i.from_number(mac_key[2] << 8 | mac_key[3]);
|
|
rrc_conn_reest_req->reest_cause = cause;
|
|
|
|
rrc_log->console("RRC Connection Reestablishment to PCI=%d, EARFCN=%d (Cause: \"%s\")\n",
|
|
meas_cells.serving_cell().phy_cell.pci,
|
|
meas_cells.serving_cell().phy_cell.earfcn,
|
|
cause.to_string().c_str());
|
|
rrc_log->info("RRC Connection Reestablishment to PCI=%d, EARFCN=%d (Cause: \"%s\")\n",
|
|
meas_cells.serving_cell().phy_cell.pci,
|
|
meas_cells.serving_cell().phy_cell.earfcn,
|
|
cause.to_string().c_str());
|
|
send_ul_ccch_msg(ul_ccch_msg);
|
|
}
|
|
|
|
void rrc::send_con_restablish_complete()
|
|
{
|
|
|
|
rrc_log->debug("Preparing RRC Connection Reestablishment Complete\n");
|
|
rrc_log->console("RRC Connected\n");
|
|
|
|
// Prepare ConnectionSetupComplete packet
|
|
ul_dcch_msg_s ul_dcch_msg;
|
|
ul_dcch_msg.msg.set_c1().set_rrc_conn_reest_complete().crit_exts.set_rrc_conn_reest_complete_r8();
|
|
ul_dcch_msg.msg.c1().rrc_conn_reest_complete().rrc_transaction_id = transaction_id;
|
|
|
|
send_ul_dcch_msg(RB_ID_SRB1, ul_dcch_msg);
|
|
|
|
reestablishment_successful = true;
|
|
}
|
|
|
|
void rrc::send_con_setup_complete(srslte::unique_byte_buffer_t nas_msg)
|
|
{
|
|
rrc_log->debug("Preparing RRC Connection Setup Complete\n");
|
|
|
|
// Prepare ConnectionSetupComplete packet
|
|
asn1::rrc::ul_dcch_msg_s ul_dcch_msg;
|
|
rrc_conn_setup_complete_r8_ies_s* rrc_conn_setup_complete =
|
|
&ul_dcch_msg.msg.set_c1().set_rrc_conn_setup_complete().crit_exts.set_c1().set_rrc_conn_setup_complete_r8();
|
|
|
|
ul_dcch_msg.msg.c1().rrc_conn_setup_complete().rrc_transaction_id = transaction_id;
|
|
|
|
rrc_conn_setup_complete->sel_plmn_id = 1;
|
|
rrc_conn_setup_complete->ded_info_nas.resize(nas_msg->N_bytes);
|
|
memcpy(rrc_conn_setup_complete->ded_info_nas.data(), nas_msg->msg, nas_msg->N_bytes); // TODO Check!
|
|
|
|
send_ul_dcch_msg(RB_ID_SRB1, ul_dcch_msg);
|
|
}
|
|
|
|
void rrc::send_ul_info_transfer(unique_byte_buffer_t nas_msg)
|
|
{
|
|
uint32_t lcid = rlc->has_bearer(RB_ID_SRB2) ? RB_ID_SRB2 : RB_ID_SRB1;
|
|
|
|
// Prepare UL INFO packet
|
|
asn1::rrc::ul_dcch_msg_s ul_dcch_msg;
|
|
ul_info_transfer_r8_ies_s* rrc_ul_info_transfer =
|
|
&ul_dcch_msg.msg.set_c1().set_ul_info_transfer().crit_exts.set_c1().set_ul_info_transfer_r8();
|
|
|
|
rrc_ul_info_transfer->ded_info_type.set_ded_info_nas();
|
|
rrc_ul_info_transfer->ded_info_type.ded_info_nas().resize(nas_msg->N_bytes);
|
|
memcpy(rrc_ul_info_transfer->ded_info_type.ded_info_nas().data(), nas_msg->msg, nas_msg->N_bytes); // TODO Check!
|
|
|
|
send_ul_dcch_msg(lcid, ul_dcch_msg);
|
|
}
|
|
|
|
void rrc::send_security_mode_complete()
|
|
{
|
|
rrc_log->debug("Preparing Security Mode Complete\n");
|
|
|
|
// Prepare Security Mode Command Complete
|
|
ul_dcch_msg_s ul_dcch_msg;
|
|
ul_dcch_msg.msg.set_c1().set_security_mode_complete().crit_exts.set_security_mode_complete_r8();
|
|
ul_dcch_msg.msg.c1().security_mode_complete().rrc_transaction_id = transaction_id;
|
|
|
|
send_ul_dcch_msg(RB_ID_SRB1, ul_dcch_msg);
|
|
}
|
|
|
|
void rrc::send_rrc_con_reconfig_complete()
|
|
{
|
|
rrc_log->debug("Preparing RRC Connection Reconfig Complete\n");
|
|
|
|
ul_dcch_msg_s ul_dcch_msg;
|
|
ul_dcch_msg.msg.set_c1().set_rrc_conn_recfg_complete().crit_exts.set_rrc_conn_recfg_complete_r8();
|
|
ul_dcch_msg.msg.c1().rrc_conn_recfg_complete().rrc_transaction_id = transaction_id;
|
|
|
|
send_ul_dcch_msg(RB_ID_SRB1, ul_dcch_msg);
|
|
}
|
|
|
|
void rrc::ra_completed()
|
|
{
|
|
cmd_msg_t msg;
|
|
msg.command = cmd_msg_t::RA_COMPLETE;
|
|
msg.lcid = 1;
|
|
cmd_q.push(std::move(msg));
|
|
}
|
|
|
|
bool rrc::con_reconfig_ho(const rrc_conn_recfg_s& reconfig)
|
|
{
|
|
if (not ho_handler.launch(reconfig)) {
|
|
rrc_log->error("Unable to launch Handover Preparation procedure\n");
|
|
return false;
|
|
}
|
|
callback_list.add_proc(ho_handler);
|
|
return true;
|
|
}
|
|
|
|
void rrc::start_go_idle()
|
|
{
|
|
if (not idle_setter.launch()) {
|
|
rrc_log->info("Failed to set RRC to IDLE\n");
|
|
return;
|
|
}
|
|
callback_list.add_proc(idle_setter);
|
|
}
|
|
|
|
// Handle RRC Reconfiguration without MobilityInformation Section 5.3.5.3
|
|
bool rrc::con_reconfig(const rrc_conn_recfg_s& reconfig)
|
|
{
|
|
const rrc_conn_recfg_r8_ies_s* reconfig_r8 = &reconfig.crit_exts.c1().rrc_conn_recfg_r8();
|
|
|
|
// If first message after reestablishment, resume SRB2 and all DRB
|
|
if (reestablishment_successful) {
|
|
for (int i = 2; i < SRSLTE_N_RADIO_BEARERS; i++) {
|
|
if (rlc->has_bearer(i)) {
|
|
rlc->resume_bearer(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
// If this is the first con_reconfig after a reestablishment
|
|
if (reestablishment_successful) {
|
|
// Reestablish PDCP and RLC for SRB2 and all DRB
|
|
// TODO: Which is the maximum LCID?
|
|
reestablishment_successful = false;
|
|
for (int i = 2; i < SRSLTE_N_RADIO_BEARERS; i++) {
|
|
if (rlc->has_bearer(i)) {
|
|
pdcp->reestablish(i);
|
|
rlc->reestablish(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Apply RR config as in 5.3.10
|
|
if (reconfig_r8->rr_cfg_ded_present) {
|
|
if (!apply_rr_config_dedicated(&reconfig_r8->rr_cfg_ded)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Apply Scell RR configurations (call is non-blocking). Make a copy since can be changed inside apply_scell_config()
|
|
// Note that apply_scell_config() calls set_scell() and set_config() which run in the background.
|
|
rrc_conn_recfg_r8_ies_s reconfig_r8_ = *reconfig_r8;
|
|
apply_scell_config(&reconfig_r8_, true);
|
|
|
|
if (!measurements->parse_meas_config(
|
|
reconfig_r8, reestablishment_successful, connection_reest.get()->get_source_earfcn())) {
|
|
return false;
|
|
}
|
|
|
|
// FIXME-@frankist: From here to the end need to be processed when set_config_complete() is called
|
|
send_rrc_con_reconfig_complete();
|
|
|
|
unique_byte_buffer_t nas_sdu;
|
|
for (uint32_t i = 0; i < reconfig_r8->ded_info_nas_list.size(); i++) {
|
|
nas_sdu = srslte::allocate_unique_buffer(*pool);
|
|
if (nas_sdu.get()) {
|
|
memcpy(nas_sdu->msg, reconfig_r8->ded_info_nas_list[i].data(), reconfig_r8->ded_info_nas_list[i].size());
|
|
nas_sdu->N_bytes = reconfig_r8->ded_info_nas_list[i].size();
|
|
nas->write_pdu(RB_ID_SRB1, std::move(nas_sdu));
|
|
} else {
|
|
rrc_log->error("Fatal Error: Couldn't allocate PDU in %s.\n", __FUNCTION__);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// HO failure from T304 expiry 5.3.5.6
|
|
void rrc::ho_failed()
|
|
{
|
|
ho_handler.trigger(ho_proc::t304_expiry{});
|
|
start_con_restablishment(reest_cause_e::ho_fail);
|
|
}
|
|
|
|
// Reconfiguration failure or Section 5.3.5.5
|
|
void rrc::con_reconfig_failed()
|
|
{
|
|
// Set previous PHY/MAC configuration
|
|
phy->set_config(previous_phy_cfg);
|
|
mac->set_config(previous_mac_cfg);
|
|
|
|
// And restore current configs
|
|
current_mac_cfg = previous_mac_cfg;
|
|
current_phy_cfg = previous_phy_cfg;
|
|
|
|
if (security_is_activated) {
|
|
// Start the Reestablishment Procedure
|
|
start_con_restablishment(reest_cause_e::recfg_fail);
|
|
} else {
|
|
start_go_idle();
|
|
}
|
|
}
|
|
|
|
void rrc::handle_rrc_con_reconfig(uint32_t lcid, const rrc_conn_recfg_s& reconfig)
|
|
{
|
|
previous_phy_cfg = current_phy_cfg;
|
|
previous_mac_cfg = current_mac_cfg;
|
|
|
|
const rrc_conn_recfg_r8_ies_s& reconfig_r8 = reconfig.crit_exts.c1().rrc_conn_recfg_r8();
|
|
if (reconfig_r8.mob_ctrl_info_present) {
|
|
con_reconfig_ho(reconfig);
|
|
} else {
|
|
if (!con_reconfig(reconfig)) {
|
|
con_reconfig_failed();
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Actions upon reception of RRCConnectionRelease 5.3.8.3 */
|
|
void rrc::rrc_connection_release(const std::string& cause)
|
|
{
|
|
// Save idleModeMobilityControlInfo, etc.
|
|
rrc_log->console("Received RRC Connection Release (releaseCause: %s)\n", cause.c_str());
|
|
start_go_idle();
|
|
}
|
|
|
|
/* Actions upon leaving RRC_CONNECTED 5.3.12 */
|
|
void rrc::leave_connected()
|
|
{
|
|
rrc_log->console("RRC IDLE\n");
|
|
rrc_log->info("Leaving RRC_CONNECTED state\n");
|
|
state = RRC_STATE_IDLE;
|
|
drb_up = false;
|
|
security_is_activated = false;
|
|
measurements->reset();
|
|
nas->left_rrc_connected();
|
|
pdcp->reset();
|
|
rlc->reset();
|
|
mac->reset();
|
|
set_phy_default();
|
|
set_mac_default();
|
|
stop_timers();
|
|
rrc_log->info("Going RRC_IDLE\n");
|
|
if (phy->cell_is_camping()) {
|
|
// Receive paging
|
|
mac->pcch_start_rx();
|
|
}
|
|
}
|
|
|
|
void rrc::stop_timers()
|
|
{
|
|
t300.stop();
|
|
t301.stop();
|
|
t310.stop();
|
|
t311.stop();
|
|
t304.stop();
|
|
}
|
|
|
|
/* Implementation of procedure in 3GPP 36.331 Section 5.3.7.2: Initiation
|
|
*
|
|
* This procedure shall be only initiated when:
|
|
* - upon detecting radio link failure, in accordance with 5.3.11; or
|
|
* - upon handover failure, in accordance with 5.3.5.6; or
|
|
* - upon mobility from E-UTRA failure, in accordance with 5.4.3.5; or
|
|
* - upon integrity check failure indication from lower layers; or
|
|
* - upon an RRC connection reconfiguration failure, in accordance with 5.3.5.5;
|
|
*
|
|
* The parameter cause shall indicate the cause of the reestablishment according to the sections mentioned adobe.
|
|
*/
|
|
void rrc::start_con_restablishment(reest_cause_e cause)
|
|
{
|
|
if (not connection_reest.launch(cause)) {
|
|
rrc_log->info("Failed to launch connection re-establishment procedure\n");
|
|
}
|
|
|
|
callback_list.add_proc(connection_reest);
|
|
}
|
|
|
|
/**
|
|
* Check whether data on SRB1 or SRB2 still needs to be sent.
|
|
* If the bearer is suspended it will not be considered.
|
|
*
|
|
* @return True if no further data needs to be sent on SRBs, False otherwise
|
|
*/
|
|
bool rrc::srbs_flushed()
|
|
{
|
|
// Check SRB1
|
|
if (rlc->has_data(RB_ID_SRB1) && not rlc->is_suspended(RB_ID_SRB1)) {
|
|
return false;
|
|
}
|
|
|
|
// Check SRB2
|
|
if (rlc->has_data(RB_ID_SRB2) && not rlc->is_suspended(RB_ID_SRB2)) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
* Interface from RRC measurements class
|
|
*
|
|
*******************************************************************************/
|
|
void rrc::send_srb1_msg(const ul_dcch_msg_s& msg)
|
|
{
|
|
send_ul_dcch_msg(RB_ID_SRB1, msg);
|
|
}
|
|
|
|
std::set<uint32_t> rrc::get_cells(const uint32_t earfcn)
|
|
{
|
|
return meas_cells.get_neighbour_pcis(earfcn);
|
|
}
|
|
|
|
float rrc::get_cell_rsrp(const uint32_t earfcn, const uint32_t pci)
|
|
{
|
|
cell_t* c = meas_cells.get_neighbour_cell_handle(earfcn, pci);
|
|
return (c != nullptr) ? c->get_rsrp() : NAN;
|
|
}
|
|
|
|
float rrc::get_cell_rsrq(const uint32_t earfcn, const uint32_t pci)
|
|
{
|
|
cell_t* c = meas_cells.get_neighbour_cell_handle(earfcn, pci);
|
|
return (c != nullptr) ? c->get_rsrq() : NAN;
|
|
}
|
|
|
|
cell_t* rrc::get_serving_cell()
|
|
{
|
|
return &meas_cells.serving_cell();
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
*
|
|
*
|
|
* Reception of Broadcast messages (MIB and SIBs)
|
|
*
|
|
*
|
|
*
|
|
*******************************************************************************/
|
|
void rrc::write_pdu_bcch_bch(unique_byte_buffer_t pdu)
|
|
{
|
|
bcch_bch_msg_s bch_msg;
|
|
asn1::cbit_ref bch_bref(pdu->msg, pdu->N_bytes);
|
|
asn1::SRSASN_CODE err = bch_msg.unpack(bch_bref);
|
|
|
|
if (err != asn1::SRSASN_SUCCESS) {
|
|
rrc_log->error("Could not unpack BCCH-BCH message.\n");
|
|
return;
|
|
}
|
|
|
|
log_rrc_message("BCCH-BCH", Rx, pdu.get(), bch_msg, "MIB");
|
|
|
|
// Nothing else to do ..
|
|
}
|
|
|
|
void rrc::write_pdu_bcch_dlsch(unique_byte_buffer_t pdu)
|
|
{
|
|
parse_pdu_bcch_dlsch(std::move(pdu));
|
|
}
|
|
|
|
void rrc::parse_pdu_bcch_dlsch(unique_byte_buffer_t pdu)
|
|
{
|
|
// Stop BCCH search after successful reception of 1 BCCH block
|
|
mac->bcch_stop_rx();
|
|
|
|
bcch_dl_sch_msg_s dlsch_msg;
|
|
asn1::cbit_ref dlsch_bref(pdu->msg, pdu->N_bytes);
|
|
asn1::SRSASN_CODE err = dlsch_msg.unpack(dlsch_bref);
|
|
|
|
if (err != asn1::SRSASN_SUCCESS or dlsch_msg.msg.type().value != bcch_dl_sch_msg_type_c::types_opts::c1) {
|
|
rrc_log->error_hex(pdu->msg, pdu->N_bytes, "Could not unpack BCCH DL-SCH message (%d B).\n", pdu->N_bytes);
|
|
return;
|
|
}
|
|
|
|
log_rrc_message("BCCH-DLSCH", Rx, pdu.get(), dlsch_msg, dlsch_msg.msg.c1().type().to_string());
|
|
|
|
if (dlsch_msg.msg.c1().type() == bcch_dl_sch_msg_type_c::c1_c_::types::sib_type1) {
|
|
rrc_log->info("Processing SIB1 (1/1)\n");
|
|
meas_cells.serving_cell().set_sib1(dlsch_msg.msg.c1().sib_type1());
|
|
si_acquirer.trigger(si_acquire_proc::sib_received_ev{});
|
|
handle_sib1();
|
|
} else {
|
|
sys_info_r8_ies_s::sib_type_and_info_l_& sib_list =
|
|
dlsch_msg.msg.c1().sys_info().crit_exts.sys_info_r8().sib_type_and_info;
|
|
for (uint32_t i = 0; i < sib_list.size(); ++i) {
|
|
rrc_log->info("Processing SIB%d (%d/%d)\n", sib_list[i].type().to_number(), i, sib_list.size());
|
|
switch (sib_list[i].type().value) {
|
|
case sib_info_item_c::types::sib2:
|
|
if (not meas_cells.serving_cell().has_sib2()) {
|
|
meas_cells.serving_cell().set_sib2(sib_list[i].sib2());
|
|
si_acquirer.trigger(si_acquire_proc::sib_received_ev{});
|
|
}
|
|
handle_sib2();
|
|
break;
|
|
case sib_info_item_c::types::sib3:
|
|
if (not meas_cells.serving_cell().has_sib3()) {
|
|
meas_cells.serving_cell().set_sib3(sib_list[i].sib3());
|
|
si_acquirer.trigger(si_acquire_proc::sib_received_ev{});
|
|
}
|
|
handle_sib3();
|
|
break;
|
|
case sib_info_item_c::types::sib13_v920:
|
|
if (not meas_cells.serving_cell().has_sib13()) {
|
|
meas_cells.serving_cell().set_sib13(sib_list[i].sib13_v920());
|
|
si_acquirer.trigger(si_acquire_proc::sib_received_ev{});
|
|
}
|
|
handle_sib13();
|
|
break;
|
|
default:
|
|
rrc_log->warning("SIB%d is not supported\n", sib_list[i].type().to_number());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void rrc::handle_sib1()
|
|
{
|
|
const sib_type1_s* sib1 = meas_cells.serving_cell().sib1ptr();
|
|
rrc_log->info("SIB1 received, CellID=%d, si_window=%d, sib2_period=%d\n",
|
|
meas_cells.serving_cell().get_cell_id() & 0xfff,
|
|
sib1->si_win_len.to_number(),
|
|
sib1->sched_info_list[0].si_periodicity.to_number());
|
|
|
|
// Print SIB scheduling info
|
|
for (uint32_t i = 0; i < sib1->sched_info_list.size(); ++i) {
|
|
sched_info_s::si_periodicity_e_ p = sib1->sched_info_list[i].si_periodicity;
|
|
for (uint32_t j = 0; j < sib1->sched_info_list[i].sib_map_info.size(); ++j) {
|
|
sib_type_e t = sib1->sched_info_list[i].sib_map_info[j];
|
|
rrc_log->debug("SIB scheduling info, sib_type=%d, si_periodicity=%d\n", t.to_number(), p.to_number());
|
|
}
|
|
}
|
|
|
|
// Set TDD Config
|
|
if (sib1->tdd_cfg_present) {
|
|
srslte_tdd_config_t tdd_config;
|
|
tdd_config.sf_config = sib1->tdd_cfg.sf_assign.to_number();
|
|
tdd_config.ss_config = sib1->tdd_cfg.special_sf_patterns.to_number();
|
|
phy->set_config_tdd(tdd_config);
|
|
}
|
|
}
|
|
|
|
void rrc::handle_sib2()
|
|
{
|
|
rrc_log->info("SIB2 received\n");
|
|
|
|
const sib_type2_s* sib2 = meas_cells.serving_cell().sib2ptr();
|
|
|
|
// Apply RACH and timeAlginmentTimer configuration
|
|
set_mac_cfg_t_rach_cfg_common(¤t_mac_cfg, sib2->rr_cfg_common.rach_cfg_common);
|
|
set_mac_cfg_t_time_alignment(¤t_mac_cfg, sib2->time_align_timer_common);
|
|
mac->set_config(current_mac_cfg);
|
|
|
|
// Set MBSFN configs
|
|
if (sib2->mbsfn_sf_cfg_list_present) {
|
|
srslte::mbsfn_sf_cfg_t list[ASN1_RRC_MAX_MBSFN_ALLOCS];
|
|
for (uint32_t i = 0; i < sib2->mbsfn_sf_cfg_list.size(); ++i) {
|
|
list[i] = srslte::make_mbsfn_sf_cfg(sib2->mbsfn_sf_cfg_list[i]);
|
|
}
|
|
phy->set_config_mbsfn_sib2(&list[0], sib2->mbsfn_sf_cfg_list.size());
|
|
}
|
|
|
|
// Apply PHY RR Config Common
|
|
set_phy_cfg_t_common_pdsch(¤t_phy_cfg, sib2->rr_cfg_common.pdsch_cfg_common);
|
|
set_phy_cfg_t_common_pusch(¤t_phy_cfg, sib2->rr_cfg_common.pusch_cfg_common);
|
|
set_phy_cfg_t_common_pucch(¤t_phy_cfg, sib2->rr_cfg_common.pucch_cfg_common);
|
|
set_phy_cfg_t_common_pwr_ctrl(¤t_phy_cfg, sib2->rr_cfg_common.ul_pwr_ctrl_common);
|
|
set_phy_cfg_t_common_prach(
|
|
¤t_phy_cfg, &sib2->rr_cfg_common.prach_cfg.prach_cfg_info, sib2->rr_cfg_common.prach_cfg.root_seq_idx);
|
|
set_phy_cfg_t_common_srs(¤t_phy_cfg, sib2->rr_cfg_common.srs_ul_cfg_common);
|
|
|
|
// According to 3GPP 36.331 v12 UE-EUTRA-Capability field descriptions
|
|
// Allow 64QAM for:
|
|
// ue-Category 5 and 8 when enable64QAM (without suffix)
|
|
// ue-CategoryUL 5 and 13 when enable64QAM (with suffix)
|
|
// enable64QAM-v1270 shall be ignored if enable64QAM (without suffix) is false
|
|
if (args.ue_category == 5 || (args.release >= 10 && args.ue_category == 8)) {
|
|
set_phy_cfg_t_enable_64qam(¤t_phy_cfg, sib2->rr_cfg_common.pusch_cfg_common.pusch_cfg_basic.enable64_qam);
|
|
} else if (args.release >= 12 && sib2->rr_cfg_common.pusch_cfg_common.pusch_cfg_basic.enable64_qam) {
|
|
if (args.ue_category_ul == 5 || args.ue_category_ul == 13) {
|
|
// ASN1 Generator simplifies enable64QAM-v1270 because it is an enumeration that is always true
|
|
set_phy_cfg_t_enable_64qam(¤t_phy_cfg, sib2->rr_cfg_common.pusch_cfg_common_v1270.is_present());
|
|
} else {
|
|
set_phy_cfg_t_enable_64qam(¤t_phy_cfg, false);
|
|
}
|
|
} else {
|
|
set_phy_cfg_t_enable_64qam(¤t_phy_cfg, false);
|
|
}
|
|
|
|
phy->set_config(current_phy_cfg);
|
|
|
|
log_rr_config_common();
|
|
|
|
auto timer_expire_func = [this](uint32_t tid) { timer_expired(tid); };
|
|
t300.set(sib2->ue_timers_and_consts.t300.to_number(), timer_expire_func);
|
|
t301.set(sib2->ue_timers_and_consts.t301.to_number(), timer_expire_func);
|
|
t310.set(sib2->ue_timers_and_consts.t310.to_number(), timer_expire_func);
|
|
t311.set(sib2->ue_timers_and_consts.t311.to_number(), timer_expire_func);
|
|
N310 = sib2->ue_timers_and_consts.n310.to_number();
|
|
N311 = sib2->ue_timers_and_consts.n311.to_number();
|
|
|
|
rrc_log->info("Set Constants and Timers: N310=%d, N311=%d, t300=%d, t301=%d, t310=%d, t311=%d\n",
|
|
N310,
|
|
N311,
|
|
t300.duration(),
|
|
t301.duration(),
|
|
t310.duration(),
|
|
t311.duration());
|
|
}
|
|
|
|
void rrc::handle_sib3()
|
|
{
|
|
rrc_log->info("SIB3 received\n");
|
|
|
|
const sib_type3_s* sib3 = meas_cells.serving_cell().sib3ptr();
|
|
|
|
// cellReselectionInfoCommon
|
|
cell_resel_cfg.q_hyst = sib3->cell_resel_info_common.q_hyst.to_number();
|
|
|
|
// cellReselectionServingFreqInfo
|
|
cell_resel_cfg.threshservinglow = sib3->thresh_serving_low_q_r9; // TODO: Check first if present
|
|
|
|
// intraFreqCellReselectionInfo
|
|
cell_resel_cfg.Qrxlevmin = sib3->intra_freq_cell_resel_info.q_rx_lev_min * 2; // multiply by two
|
|
if (sib3->intra_freq_cell_resel_info.s_intra_search_present) {
|
|
cell_resel_cfg.s_intrasearchP = sib3->intra_freq_cell_resel_info.s_intra_search;
|
|
} else {
|
|
cell_resel_cfg.s_intrasearchP = INFINITY;
|
|
}
|
|
}
|
|
|
|
void rrc::handle_sib13()
|
|
{
|
|
rrc_log->info("SIB13 received\n");
|
|
|
|
const sib_type13_r9_s* sib13 = meas_cells.serving_cell().sib13ptr();
|
|
|
|
phy->set_config_mbsfn_sib13(srslte::make_sib13(*sib13));
|
|
add_mrb(0, 0); // Add MRB0
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
*
|
|
*
|
|
* Reception of Paging messages
|
|
*
|
|
*
|
|
*
|
|
*******************************************************************************/
|
|
void rrc::write_pdu_pcch(unique_byte_buffer_t pdu)
|
|
{
|
|
cmd_msg_t msg;
|
|
msg.pdu = std::move(pdu);
|
|
msg.command = cmd_msg_t::PCCH;
|
|
cmd_q.push(std::move(msg));
|
|
}
|
|
|
|
void rrc::paging_completed(bool outcome)
|
|
{
|
|
pcch_processor.trigger(process_pcch_proc::paging_complete{outcome});
|
|
}
|
|
|
|
void rrc::process_pcch(unique_byte_buffer_t pdu)
|
|
{
|
|
if (pdu->N_bytes <= 0 or pdu->N_bytes >= SRSLTE_MAX_BUFFER_SIZE_BITS) {
|
|
rrc_log->error_hex(pdu->msg, pdu->N_bytes, "Dropping PCCH message with %d B\n", pdu->N_bytes);
|
|
return;
|
|
}
|
|
|
|
pcch_msg_s pcch_msg;
|
|
asn1::cbit_ref bref(pdu->msg, pdu->N_bytes);
|
|
if (pcch_msg.unpack(bref) != asn1::SRSASN_SUCCESS or pcch_msg.msg.type().value != pcch_msg_type_c::types_opts::c1) {
|
|
rrc_log->error_hex(pdu->msg, pdu->N_bytes, "Failed to unpack PCCH message (%d B)\n", pdu->N_bytes);
|
|
return;
|
|
}
|
|
|
|
log_rrc_message("PCCH", Rx, pdu.get(), pcch_msg, pcch_msg.msg.c1().type().to_string());
|
|
|
|
if (not ue_identity_configured) {
|
|
rrc_log->warning("Received paging message but no ue-Identity is configured\n");
|
|
return;
|
|
}
|
|
|
|
paging_s* paging = &pcch_msg.msg.c1().paging();
|
|
if (paging->paging_record_list.size() > ASN1_RRC_MAX_PAGE_REC) {
|
|
paging->paging_record_list.resize(ASN1_RRC_MAX_PAGE_REC);
|
|
}
|
|
|
|
if (not pcch_processor.launch(*paging)) {
|
|
rrc_log->error("Failed to launch process PCCH procedure\n");
|
|
return;
|
|
}
|
|
|
|
// we do not care about the outcome
|
|
callback_list.add_proc(pcch_processor);
|
|
}
|
|
|
|
void rrc::write_pdu_mch(uint32_t lcid, srslte::unique_byte_buffer_t pdu)
|
|
{
|
|
if (pdu->N_bytes <= 0 or pdu->N_bytes >= SRSLTE_MAX_BUFFER_SIZE_BITS) {
|
|
return;
|
|
}
|
|
// TODO: handle MCCH notifications and update MCCH
|
|
if (0 != lcid or meas_cells.serving_cell().has_mcch) {
|
|
return;
|
|
}
|
|
parse_pdu_mch(lcid, std::move(pdu));
|
|
}
|
|
|
|
void rrc::parse_pdu_mch(uint32_t lcid, srslte::unique_byte_buffer_t pdu)
|
|
{
|
|
asn1::cbit_ref bref(pdu->msg, pdu->N_bytes);
|
|
if (meas_cells.serving_cell().mcch.unpack(bref) != asn1::SRSASN_SUCCESS or
|
|
meas_cells.serving_cell().mcch.msg.type().value != mcch_msg_type_c::types_opts::c1) {
|
|
rrc_log->error("Failed to unpack MCCH message\n");
|
|
return;
|
|
}
|
|
meas_cells.serving_cell().has_mcch = true;
|
|
phy->set_config_mbsfn_mcch(srslte::make_mcch_msg(meas_cells.serving_cell().mcch));
|
|
log_rrc_message(
|
|
"MCH", Rx, pdu.get(), meas_cells.serving_cell().mcch, meas_cells.serving_cell().mcch.msg.c1().type().to_string());
|
|
if (args.mbms_service_id >= 0) {
|
|
rrc_log->info("Attempting to auto-start MBMS service %d\n", args.mbms_service_id);
|
|
mbms_service_start(args.mbms_service_id, args.mbms_service_port);
|
|
}
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
*
|
|
* Packet processing
|
|
*
|
|
*
|
|
*******************************************************************************/
|
|
|
|
void rrc::send_ul_ccch_msg(const ul_ccch_msg_s& msg)
|
|
{
|
|
// Reset and reuse sdu buffer if provided
|
|
unique_byte_buffer_t pdcp_buf = srslte::allocate_unique_buffer(*pool, true);
|
|
if (not pdcp_buf.get()) {
|
|
rrc_log->error("Fatal Error: Couldn't allocate PDU in byte_align_and_pack().\n");
|
|
return;
|
|
}
|
|
|
|
asn1::bit_ref bref(pdcp_buf->msg, pdcp_buf->get_tailroom());
|
|
msg.pack(bref);
|
|
bref.align_bytes_zero();
|
|
pdcp_buf->N_bytes = (uint32_t)bref.distance_bytes(pdcp_buf->msg);
|
|
pdcp_buf->set_timestamp();
|
|
|
|
// Set UE contention resolution ID in MAC
|
|
uint64_t uecri = 0;
|
|
uint8_t* ue_cri_ptr = (uint8_t*)&uecri;
|
|
uint32_t nbytes = 6;
|
|
for (uint32_t i = 0; i < nbytes; i++) {
|
|
ue_cri_ptr[nbytes - i - 1] = pdcp_buf->msg[i];
|
|
}
|
|
|
|
rrc_log->debug("Setting UE contention resolution ID: %" PRIu64 "\n", uecri);
|
|
mac->set_contention_id(uecri);
|
|
|
|
uint32_t lcid = RB_ID_SRB0;
|
|
log_rrc_message(get_rb_name(lcid).c_str(), Tx, pdcp_buf.get(), msg, msg.msg.c1().type().to_string());
|
|
|
|
rlc->write_sdu(lcid, std::move(pdcp_buf));
|
|
}
|
|
|
|
void rrc::send_ul_dcch_msg(uint32_t lcid, const ul_dcch_msg_s& msg)
|
|
{
|
|
// Reset and reuse sdu buffer if provided
|
|
unique_byte_buffer_t pdcp_buf = srslte::allocate_unique_buffer(*pool, true);
|
|
if (not pdcp_buf.get()) {
|
|
rrc_log->error("Fatal Error: Couldn't allocate PDU in byte_align_and_pack().\n");
|
|
return;
|
|
}
|
|
|
|
asn1::bit_ref bref(pdcp_buf->msg, pdcp_buf->get_tailroom());
|
|
msg.pack(bref);
|
|
bref.align_bytes_zero();
|
|
pdcp_buf->N_bytes = (uint32_t)bref.distance_bytes(pdcp_buf->msg);
|
|
pdcp_buf->set_timestamp();
|
|
|
|
log_rrc_message(get_rb_name(lcid).c_str(), Tx, pdcp_buf.get(), msg, msg.msg.c1().type().to_string());
|
|
|
|
pdcp->write_sdu(lcid, std::move(pdcp_buf));
|
|
}
|
|
|
|
void rrc::write_sdu(srslte::unique_byte_buffer_t sdu)
|
|
{
|
|
if (state == RRC_STATE_IDLE) {
|
|
rrc_log->warning("Received ULInformationTransfer SDU when in IDLE\n");
|
|
return;
|
|
}
|
|
send_ul_info_transfer(std::move(sdu));
|
|
}
|
|
|
|
void rrc::write_pdu(uint32_t lcid, unique_byte_buffer_t pdu)
|
|
{
|
|
process_pdu(lcid, std::move(pdu));
|
|
}
|
|
|
|
void rrc::process_pdu(uint32_t lcid, srslte::unique_byte_buffer_t pdu)
|
|
{
|
|
rrc_log->debug("RX PDU, LCID: %d\n", lcid);
|
|
switch (lcid) {
|
|
case RB_ID_SRB0:
|
|
parse_dl_ccch(std::move(pdu));
|
|
break;
|
|
case RB_ID_SRB1:
|
|
case RB_ID_SRB2:
|
|
parse_dl_dcch(lcid, std::move(pdu));
|
|
break;
|
|
default:
|
|
rrc_log->error("RX PDU with invalid bearer id: %d\n", lcid);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void rrc::parse_dl_ccch(unique_byte_buffer_t pdu)
|
|
{
|
|
asn1::cbit_ref bref(pdu->msg, pdu->N_bytes);
|
|
asn1::rrc::dl_ccch_msg_s dl_ccch_msg;
|
|
if (dl_ccch_msg.unpack(bref) != asn1::SRSASN_SUCCESS or
|
|
dl_ccch_msg.msg.type().value != dl_ccch_msg_type_c::types_opts::c1) {
|
|
rrc_log->error_hex(pdu->msg, pdu->N_bytes, "Failed to unpack DL-CCCH message (%d B)\n", pdu->N_bytes);
|
|
return;
|
|
}
|
|
log_rrc_message(get_rb_name(RB_ID_SRB0).c_str(), Rx, pdu.get(), dl_ccch_msg, dl_ccch_msg.msg.c1().type().to_string());
|
|
|
|
dl_ccch_msg_type_c::c1_c_* c1 = &dl_ccch_msg.msg.c1();
|
|
switch (dl_ccch_msg.msg.c1().type().value) {
|
|
case dl_ccch_msg_type_c::c1_c_::types::rrc_conn_reject: {
|
|
// 5.3.3.8
|
|
rrc_conn_reject_r8_ies_s* reject_r8 = &c1->rrc_conn_reject().crit_exts.c1().rrc_conn_reject_r8();
|
|
rrc_log->info("Received ConnectionReject. Wait time: %d\n", reject_r8->wait_time);
|
|
rrc_log->console("Received ConnectionReject. Wait time: %d\n", reject_r8->wait_time);
|
|
|
|
t300.stop();
|
|
|
|
if (reject_r8->wait_time) {
|
|
nas->set_barring(srslte::barring_t::all);
|
|
t302.set(reject_r8->wait_time * 1000, [this](uint32_t tid) { timer_expired(tid); });
|
|
t302.run();
|
|
} else {
|
|
// Perform the actions upon expiry of T302 if wait time is zero
|
|
nas->set_barring(srslte::barring_t::none);
|
|
start_go_idle();
|
|
}
|
|
} break;
|
|
case dl_ccch_msg_type_c::c1_c_::types::rrc_conn_setup: {
|
|
transaction_id = c1->rrc_conn_setup().rrc_transaction_id;
|
|
rrc_conn_setup_s conn_setup_copy = c1->rrc_conn_setup();
|
|
task_sched.defer_task([this, conn_setup_copy]() { handle_con_setup(conn_setup_copy); });
|
|
break;
|
|
}
|
|
case dl_ccch_msg_type_c::c1_c_::types::rrc_conn_reest: {
|
|
rrc_log->console("Reestablishment OK\n");
|
|
transaction_id = c1->rrc_conn_reest().rrc_transaction_id;
|
|
rrc_conn_reest_s conn_reest_copy = c1->rrc_conn_reest();
|
|
task_sched.defer_task([this, conn_reest_copy]() { handle_con_reest(conn_reest_copy); });
|
|
break;
|
|
}
|
|
/* Reception of RRCConnectionReestablishmentReject 5.3.7.8 */
|
|
case dl_ccch_msg_type_c::c1_c_::types::rrc_conn_reest_reject:
|
|
connection_reest.trigger(c1->rrc_conn_reest_reject());
|
|
break;
|
|
default:
|
|
rrc_log->error("The provided DL-CCCH message type is not recognized\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
void rrc::parse_dl_dcch(uint32_t lcid, unique_byte_buffer_t pdu)
|
|
{
|
|
asn1::cbit_ref bref(pdu->msg, pdu->N_bytes);
|
|
asn1::rrc::dl_dcch_msg_s dl_dcch_msg;
|
|
if (dl_dcch_msg.unpack(bref) != asn1::SRSASN_SUCCESS or
|
|
dl_dcch_msg.msg.type().value != dl_dcch_msg_type_c::types_opts::c1) {
|
|
rrc_log->error_hex(pdu->msg, pdu->N_bytes, "Failed to unpack DL-DCCH message (%d B)\n", pdu->N_bytes);
|
|
return;
|
|
}
|
|
log_rrc_message(get_rb_name(lcid).c_str(), Rx, pdu.get(), dl_dcch_msg, dl_dcch_msg.msg.c1().type().to_string());
|
|
|
|
dl_dcch_msg_type_c::c1_c_* c1 = &dl_dcch_msg.msg.c1();
|
|
switch (dl_dcch_msg.msg.c1().type().value) {
|
|
case dl_dcch_msg_type_c::c1_c_::types::dl_info_transfer:
|
|
pdu = srslte::allocate_unique_buffer(*pool, true);
|
|
if (!pdu.get()) {
|
|
rrc_log->error("Fatal error: out of buffers in pool\n");
|
|
return;
|
|
}
|
|
pdu->N_bytes = c1->dl_info_transfer().crit_exts.c1().dl_info_transfer_r8().ded_info_type.ded_info_nas().size();
|
|
memcpy(pdu->msg,
|
|
c1->dl_info_transfer().crit_exts.c1().dl_info_transfer_r8().ded_info_type.ded_info_nas().data(),
|
|
pdu->N_bytes);
|
|
nas->write_pdu(lcid, std::move(pdu));
|
|
break;
|
|
case dl_dcch_msg_type_c::c1_c_::types::security_mode_cmd:
|
|
transaction_id = c1->security_mode_cmd().rrc_transaction_id;
|
|
|
|
sec_cfg.cipher_algo = (CIPHERING_ALGORITHM_ID_ENUM)c1->security_mode_cmd()
|
|
.crit_exts.c1()
|
|
.security_mode_cmd_r8()
|
|
.security_cfg_smc.security_algorithm_cfg.ciphering_algorithm.value;
|
|
sec_cfg.integ_algo = (INTEGRITY_ALGORITHM_ID_ENUM)c1->security_mode_cmd()
|
|
.crit_exts.c1()
|
|
.security_mode_cmd_r8()
|
|
.security_cfg_smc.security_algorithm_cfg.integrity_prot_algorithm.value;
|
|
|
|
rrc_log->info("Received Security Mode Command eea: %s, eia: %s\n",
|
|
ciphering_algorithm_id_text[sec_cfg.cipher_algo],
|
|
integrity_algorithm_id_text[sec_cfg.integ_algo]);
|
|
|
|
// Generate AS security keys
|
|
generate_as_keys();
|
|
security_is_activated = true;
|
|
|
|
// Configure PDCP for security
|
|
pdcp->config_security(lcid, sec_cfg);
|
|
pdcp->enable_integrity(lcid, DIRECTION_TXRX);
|
|
send_security_mode_complete();
|
|
pdcp->enable_encryption(lcid, DIRECTION_TXRX);
|
|
break;
|
|
case dl_dcch_msg_type_c::c1_c_::types::rrc_conn_recfg: {
|
|
transaction_id = c1->rrc_conn_recfg().rrc_transaction_id;
|
|
rrc_conn_recfg_s recfg = c1->rrc_conn_recfg();
|
|
task_sched.defer_task([this, lcid, recfg]() { handle_rrc_con_reconfig(lcid, recfg); });
|
|
break;
|
|
}
|
|
case dl_dcch_msg_type_c::c1_c_::types::ue_cap_enquiry:
|
|
transaction_id = c1->ue_cap_enquiry().rrc_transaction_id;
|
|
handle_ue_capability_enquiry(c1->ue_cap_enquiry());
|
|
break;
|
|
case dl_dcch_msg_type_c::c1_c_::types::rrc_conn_release:
|
|
rrc_connection_release(c1->rrc_conn_release().crit_exts.c1().rrc_conn_release_r8().release_cause.to_string());
|
|
break;
|
|
default:
|
|
rrc_log->error("The provided DL-CCCH message type is not recognized or supported\n");
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Security helper used by Security Mode Command and Mobility handling routines
|
|
void rrc::generate_as_keys(void)
|
|
{
|
|
uint8_t k_asme[32] = {};
|
|
nas->get_k_asme(k_asme, 32);
|
|
rrc_log->debug_hex(k_asme, 32, "UE K_asme");
|
|
rrc_log->debug("Generating K_enb with UL NAS COUNT: %d\n", nas->get_k_enb_count());
|
|
usim->generate_as_keys(k_asme, nas->get_k_enb_count(), &sec_cfg);
|
|
rrc_log->info_hex(sec_cfg.k_rrc_enc.data(), 32, "RRC encryption key - k_rrc_enc");
|
|
rrc_log->info_hex(sec_cfg.k_rrc_int.data(), 32, "RRC integrity key - k_rrc_int");
|
|
rrc_log->info_hex(sec_cfg.k_up_enc.data(), 32, "UP encryption key - k_up_enc");
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
*
|
|
*
|
|
* Capabilities Message
|
|
*
|
|
*
|
|
*
|
|
*******************************************************************************/
|
|
void rrc::enable_capabilities()
|
|
{
|
|
bool enable_ul_64 = args.ue_category >= 5 &&
|
|
meas_cells.serving_cell().sib2ptr()->rr_cfg_common.pusch_cfg_common.pusch_cfg_basic.enable64_qam;
|
|
rrc_log->info("%s 64QAM PUSCH\n", enable_ul_64 ? "Enabling" : "Disabling");
|
|
}
|
|
|
|
void rrc::handle_ue_capability_enquiry(const ue_cap_enquiry_s& enquiry)
|
|
{
|
|
rrc_log->debug("Preparing UE Capability Info\n");
|
|
|
|
ul_dcch_msg_s ul_dcch_msg;
|
|
ue_cap_info_r8_ies_s* info = &ul_dcch_msg.msg.set_c1().set_ue_cap_info().crit_exts.set_c1().set_ue_cap_info_r8();
|
|
ul_dcch_msg.msg.c1().ue_cap_info().rrc_transaction_id = transaction_id;
|
|
|
|
// resize container to fit all requested RATs
|
|
info->ue_cap_rat_container_list.resize(enquiry.crit_exts.c1().ue_cap_enquiry_r8().ue_cap_request.size());
|
|
uint32_t rat_idx = 0;
|
|
|
|
for (uint32_t i = 0; i < enquiry.crit_exts.c1().ue_cap_enquiry_r8().ue_cap_request.size(); i++) {
|
|
if (enquiry.crit_exts.c1().ue_cap_enquiry_r8().ue_cap_request[i] == rat_type_e::eutra) {
|
|
// adding EUTRA caps
|
|
info->ue_cap_rat_container_list[0].rat_type = rat_type_e::eutra;
|
|
|
|
// Check UE config arguments bounds
|
|
if (args.release < SRSLTE_RELEASE_MIN || args.release > SRSLTE_RELEASE_MAX) {
|
|
uint32_t new_release = SRSLTE_MIN(SRSLTE_RELEASE_MAX, SRSLTE_MAX(SRSLTE_RELEASE_MIN, args.release));
|
|
rrc_log->error("Release is %d. It is out of bounds (%d ... %d), setting it to %d\n",
|
|
args.release,
|
|
SRSLTE_RELEASE_MIN,
|
|
SRSLTE_RELEASE_MAX,
|
|
new_release);
|
|
args.release = new_release;
|
|
}
|
|
|
|
args.ue_category = (uint32_t)strtol(args.ue_category_str.c_str(), nullptr, 10);
|
|
if (args.ue_category < SRSLTE_UE_CATEGORY_MIN || args.ue_category > SRSLTE_UE_CATEGORY_MAX) {
|
|
uint32_t new_category =
|
|
SRSLTE_MIN(SRSLTE_UE_CATEGORY_MAX, SRSLTE_MAX(SRSLTE_UE_CATEGORY_MIN, args.ue_category));
|
|
rrc_log->error("UE Category is %d. It is out of bounds (%d ... %d), setting it to %d\n",
|
|
args.ue_category,
|
|
SRSLTE_UE_CATEGORY_MIN,
|
|
SRSLTE_UE_CATEGORY_MAX,
|
|
new_category);
|
|
args.ue_category = new_category;
|
|
}
|
|
|
|
ue_eutra_cap_s cap;
|
|
cap.access_stratum_release = (access_stratum_release_e::options)(args.release - SRSLTE_RELEASE_MIN);
|
|
cap.ue_category = (uint8_t)((args.ue_category < 1 || args.ue_category > 5) ? 4 : args.ue_category);
|
|
cap.pdcp_params.max_num_rohc_context_sessions_present = false;
|
|
cap.pdcp_params.supported_rohc_profiles.profile0x0001_r15 = false;
|
|
cap.pdcp_params.supported_rohc_profiles.profile0x0002_r15 = false;
|
|
cap.pdcp_params.supported_rohc_profiles.profile0x0003_r15 = false;
|
|
cap.pdcp_params.supported_rohc_profiles.profile0x0004_r15 = false;
|
|
cap.pdcp_params.supported_rohc_profiles.profile0x0006_r15 = false;
|
|
cap.pdcp_params.supported_rohc_profiles.profile0x0101_r15 = false;
|
|
cap.pdcp_params.supported_rohc_profiles.profile0x0102_r15 = false;
|
|
cap.pdcp_params.supported_rohc_profiles.profile0x0103_r15 = false;
|
|
cap.pdcp_params.supported_rohc_profiles.profile0x0104_r15 = false;
|
|
|
|
cap.phy_layer_params.ue_specific_ref_sigs_supported = false;
|
|
cap.phy_layer_params.ue_tx_ant_sel_supported = false;
|
|
|
|
cap.rf_params.supported_band_list_eutra.resize(args.nof_supported_bands);
|
|
cap.meas_params.band_list_eutra.resize(args.nof_supported_bands);
|
|
for (uint32_t k = 0; k < args.nof_supported_bands; k++) {
|
|
cap.rf_params.supported_band_list_eutra[k].band_eutra = args.supported_bands[k];
|
|
cap.rf_params.supported_band_list_eutra[k].half_duplex = false;
|
|
cap.meas_params.band_list_eutra[k].inter_freq_band_list.resize(1);
|
|
cap.meas_params.band_list_eutra[k].inter_freq_band_list[0].inter_freq_need_for_gaps = true;
|
|
}
|
|
|
|
cap.feature_group_inds_present = true;
|
|
cap.feature_group_inds.from_number(args.feature_group);
|
|
|
|
if (args.release > 8) {
|
|
ue_eutra_cap_v920_ies_s cap_v920;
|
|
|
|
cap_v920.phy_layer_params_v920.enhanced_dual_layer_fdd_r9_present = false;
|
|
cap_v920.phy_layer_params_v920.enhanced_dual_layer_tdd_r9_present = false;
|
|
cap_v920.inter_rat_params_geran_v920.dtm_r9_present = false;
|
|
cap_v920.inter_rat_params_geran_v920.e_redirection_geran_r9_present = false;
|
|
cap_v920.csg_proximity_ind_params_r9.inter_freq_proximity_ind_r9_present = false;
|
|
cap_v920.csg_proximity_ind_params_r9.intra_freq_proximity_ind_r9_present = false;
|
|
cap_v920.csg_proximity_ind_params_r9.utran_proximity_ind_r9_present = false;
|
|
cap_v920.neigh_cell_si_acquisition_params_r9.inter_freq_si_acquisition_for_ho_r9_present = false;
|
|
cap_v920.neigh_cell_si_acquisition_params_r9.intra_freq_si_acquisition_for_ho_r9_present = false;
|
|
cap_v920.neigh_cell_si_acquisition_params_r9.utran_si_acquisition_for_ho_r9_present = false;
|
|
cap_v920.son_params_r9.rach_report_r9_present = false;
|
|
|
|
cap.non_crit_ext_present = true;
|
|
cap.non_crit_ext = cap_v920;
|
|
}
|
|
|
|
if (args.release > 9) {
|
|
|
|
phy_layer_params_v1020_s phy_layer_params_v1020;
|
|
phy_layer_params_v1020.two_ant_ports_for_pucch_r10_present = false;
|
|
phy_layer_params_v1020.tm9_with_minus8_tx_fdd_r10_present = false;
|
|
phy_layer_params_v1020.pmi_disabling_r10_present = false;
|
|
phy_layer_params_v1020.cross_carrier_sched_r10_present = args.support_ca;
|
|
phy_layer_params_v1020.simul_pucch_pusch_r10_present = false;
|
|
phy_layer_params_v1020.multi_cluster_pusch_within_cc_r10_present = false;
|
|
phy_layer_params_v1020.non_contiguous_ul_ra_within_cc_list_r10_present = false;
|
|
|
|
band_combination_params_r10_l combination_params;
|
|
if (args.support_ca) {
|
|
for (uint32_t k = 0; k < args.nof_supported_bands; k++) {
|
|
ca_mimo_params_dl_r10_s ca_mimo_params_dl;
|
|
ca_mimo_params_dl.ca_bw_class_dl_r10 = ca_bw_class_r10_e::f;
|
|
ca_mimo_params_dl.supported_mimo_cap_dl_r10_present = false;
|
|
|
|
ca_mimo_params_ul_r10_s ca_mimo_params_ul;
|
|
ca_mimo_params_ul.ca_bw_class_ul_r10 = ca_bw_class_r10_e::f;
|
|
ca_mimo_params_ul.supported_mimo_cap_ul_r10_present = false;
|
|
|
|
band_params_r10_s band_params;
|
|
band_params.band_eutra_r10 = args.supported_bands[i];
|
|
band_params.band_params_dl_r10_present = true;
|
|
band_params.band_params_dl_r10.push_back(ca_mimo_params_dl);
|
|
band_params.band_params_ul_r10_present = true;
|
|
band_params.band_params_ul_r10.push_back(ca_mimo_params_ul);
|
|
|
|
combination_params.push_back(band_params);
|
|
}
|
|
}
|
|
|
|
rf_params_v1020_s rf_params;
|
|
rf_params.supported_band_combination_r10.push_back(combination_params);
|
|
|
|
ue_eutra_cap_v1020_ies_s cap_v1020;
|
|
if (args.ue_category >= 6 && args.ue_category <= 8) {
|
|
cap_v1020.ue_category_v1020_present = true;
|
|
cap_v1020.ue_category_v1020 = (uint8_t)args.ue_category;
|
|
} else {
|
|
// Do not populate UE category for this release if the category is out of range
|
|
}
|
|
cap_v1020.phy_layer_params_v1020_present = true;
|
|
cap_v1020.phy_layer_params_v1020 = phy_layer_params_v1020;
|
|
cap_v1020.rf_params_v1020_present = args.support_ca;
|
|
cap_v1020.rf_params_v1020 = rf_params;
|
|
|
|
ue_eutra_cap_v940_ies_s cap_v940;
|
|
cap_v940.non_crit_ext_present = true;
|
|
cap_v940.non_crit_ext = cap_v1020;
|
|
|
|
cap.non_crit_ext.non_crit_ext_present = true;
|
|
cap.non_crit_ext.non_crit_ext = cap_v940;
|
|
}
|
|
|
|
if (args.release > 10) {
|
|
ue_eutra_cap_v11a0_ies_s cap_v11a0;
|
|
if (args.ue_category >= 11 && args.ue_category <= 12) {
|
|
cap_v11a0.ue_category_v11a0 = (uint8_t)args.ue_category;
|
|
cap_v11a0.ue_category_v11a0_present = true;
|
|
} else {
|
|
// Do not populate UE category for this release if the category is out of range
|
|
}
|
|
|
|
ue_eutra_cap_v1180_ies_s cap_v1180;
|
|
cap_v1180.non_crit_ext_present = true;
|
|
cap_v1180.non_crit_ext = cap_v11a0;
|
|
|
|
ue_eutra_cap_v1170_ies_s cap_v1170;
|
|
cap_v1170.non_crit_ext_present = true;
|
|
cap_v1170.non_crit_ext = cap_v1180;
|
|
if (args.ue_category >= 9 && args.ue_category <= 10) {
|
|
cap_v1170.ue_category_v1170 = (uint8_t)args.ue_category;
|
|
cap_v1170.ue_category_v1170_present = true;
|
|
} else {
|
|
// Do not populate UE category for this release if the category is out of range
|
|
}
|
|
|
|
ue_eutra_cap_v1130_ies_s cap_v1130;
|
|
cap_v1130.non_crit_ext_present = true;
|
|
cap_v1130.non_crit_ext = cap_v1170;
|
|
|
|
ue_eutra_cap_v1090_ies_s cap_v1090;
|
|
cap_v1090.non_crit_ext_present = true;
|
|
cap_v1090.non_crit_ext = cap_v1130;
|
|
|
|
ue_eutra_cap_v1060_ies_s cap_v1060;
|
|
cap_v1060.non_crit_ext_present = true;
|
|
cap_v1060.non_crit_ext = cap_v1090;
|
|
|
|
cap.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext_present = true;
|
|
cap.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext = cap_v1060;
|
|
}
|
|
|
|
if (args.release > 11) {
|
|
supported_band_list_eutra_v1250_l supported_band_list_eutra_v1250;
|
|
for (uint32_t k = 0; k < args.nof_supported_bands; k++) {
|
|
supported_band_eutra_v1250_s supported_band_eutra_v1250;
|
|
// According to 3GPP 36.306 v12 Table 4.1A-1, 256QAM is supported for ue_category_dl 11-16
|
|
supported_band_eutra_v1250.dl_minus256_qam_r12_present = (args.ue_category_dl >= 11);
|
|
|
|
// According to 3GPP 36.331 v12 UE-EUTRA-Capability field descriptions
|
|
// This field is only present when the field ue-CategoryUL is considered to 5 or 13.
|
|
supported_band_eutra_v1250.ul_minus64_qam_r12_present = true;
|
|
|
|
supported_band_list_eutra_v1250.push_back(supported_band_eutra_v1250);
|
|
}
|
|
|
|
rf_params_v1250_s rf_params_v1250;
|
|
rf_params_v1250.supported_band_list_eutra_v1250_present = true;
|
|
rf_params_v1250.supported_band_list_eutra_v1250 = supported_band_list_eutra_v1250;
|
|
|
|
ue_eutra_cap_v1250_ies_s cap_v1250;
|
|
|
|
// Optional UE Category UL/DL
|
|
// Warning: Make sure the UE Category UL/DL matches with 3GPP 36.306 Table 4.1A-6
|
|
if (args.ue_category_dl >= 0) {
|
|
cap_v1250.ue_category_dl_r12_present = true;
|
|
cap_v1250.ue_category_dl_r12 = (uint8_t)args.ue_category_dl;
|
|
} else {
|
|
// Do not populate UE category for this release if the category is not available
|
|
}
|
|
if (args.ue_category_ul >= 0) {
|
|
cap_v1250.ue_category_ul_r12_present = true;
|
|
cap_v1250.ue_category_ul_r12 = (uint8_t)args.ue_category_ul;
|
|
} else {
|
|
// Do not populate UE category for this release if the category is not available
|
|
}
|
|
cap_v1250.rf_params_v1250_present = true;
|
|
cap_v1250.rf_params_v1250 = rf_params_v1250;
|
|
|
|
cap.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext
|
|
.non_crit_ext.non_crit_ext_present = true;
|
|
cap.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext
|
|
.non_crit_ext.non_crit_ext = cap_v1250;
|
|
}
|
|
|
|
// Pack caps and copy to cap info
|
|
uint8_t buf[64] = {};
|
|
asn1::bit_ref bref(buf, sizeof(buf));
|
|
cap.pack(bref);
|
|
bref.align_bytes_zero();
|
|
auto cap_len = (uint32_t)bref.distance_bytes(buf);
|
|
info->ue_cap_rat_container_list[rat_idx].ue_cap_rat_container.resize(cap_len);
|
|
memcpy(info->ue_cap_rat_container_list[rat_idx].ue_cap_rat_container.data(), buf, cap_len);
|
|
rat_idx++;
|
|
}
|
|
}
|
|
|
|
// resize container back to the actually filled items
|
|
info->ue_cap_rat_container_list.resize(rat_idx);
|
|
|
|
send_ul_dcch_msg(RB_ID_SRB1, ul_dcch_msg);
|
|
}
|
|
|
|
/*******************************************************************************
|
|
*
|
|
*
|
|
*
|
|
* PHY and MAC Radio Resource configuration
|
|
*
|
|
*
|
|
*
|
|
*******************************************************************************/
|
|
|
|
void rrc::log_rr_config_common()
|
|
{
|
|
rrc_log->info("Set RACH ConfigCommon: NofPreambles=%d, ResponseWindow=%d, ContentionResolutionTimer=%d ms\n",
|
|
current_mac_cfg.rach_cfg.nof_preambles,
|
|
current_mac_cfg.rach_cfg.responseWindowSize,
|
|
current_mac_cfg.rach_cfg.contentionResolutionTimer);
|
|
|
|
rrc_log->info("Set PUSCH ConfigCommon: P0_pusch=%f, DMRS cs=%d, delta_ss=%d, N_sb=%d\n",
|
|
current_phy_cfg.ul_cfg.power_ctrl.p0_ue_pusch,
|
|
current_phy_cfg.ul_cfg.dmrs.cyclic_shift,
|
|
current_phy_cfg.ul_cfg.dmrs.delta_ss,
|
|
current_phy_cfg.ul_cfg.hopping.n_sb);
|
|
|
|
rrc_log->info("Set PUCCH ConfigCommon: DeltaShift=%d, CyclicShift=%d, N1=%d, NRB=%d\n",
|
|
current_phy_cfg.ul_cfg.pucch.delta_pucch_shift,
|
|
current_phy_cfg.ul_cfg.pucch.N_cs,
|
|
current_phy_cfg.ul_cfg.pucch.n1_pucch_an_cs[0][0],
|
|
current_phy_cfg.ul_cfg.pucch.n_rb_2);
|
|
|
|
rrc_log->info("Set PRACH ConfigCommon: SeqIdx=%d, HS=%s, FreqOffset=%d, ZC=%d, ConfigIndex=%d\n",
|
|
current_phy_cfg.prach_cfg.root_seq_idx,
|
|
current_phy_cfg.prach_cfg.hs_flag ? "yes" : "no",
|
|
current_phy_cfg.prach_cfg.freq_offset,
|
|
current_phy_cfg.prach_cfg.zero_corr_zone,
|
|
current_phy_cfg.prach_cfg.config_idx);
|
|
|
|
if (current_phy_cfg.ul_cfg.srs.configured) {
|
|
rrc_log->info("Set SRS ConfigCommon: BW-Configuration=%d, SF-Configuration=%d, Simult-ACKNACK=%s\n",
|
|
current_phy_cfg.ul_cfg.srs.bw_cfg,
|
|
current_phy_cfg.ul_cfg.srs.subframe_config,
|
|
current_phy_cfg.ul_cfg.srs.simul_ack ? "yes" : "no");
|
|
}
|
|
}
|
|
|
|
void rrc::apply_rr_config_common(rr_cfg_common_s* config, bool send_lower_layers)
|
|
{
|
|
rrc_log->info("Applying MAC/PHY config common\n");
|
|
|
|
if (config->rach_cfg_common_present) {
|
|
set_mac_cfg_t_rach_cfg_common(¤t_mac_cfg, config->rach_cfg_common);
|
|
}
|
|
|
|
if (config->prach_cfg.prach_cfg_info_present) {
|
|
set_phy_cfg_t_common_prach(¤t_phy_cfg, &config->prach_cfg.prach_cfg_info, config->prach_cfg.root_seq_idx);
|
|
} else {
|
|
set_phy_cfg_t_common_prach(¤t_phy_cfg, NULL, config->prach_cfg.root_seq_idx);
|
|
}
|
|
|
|
if (config->pdsch_cfg_common_present) {
|
|
set_phy_cfg_t_common_pdsch(¤t_phy_cfg, config->pdsch_cfg_common);
|
|
}
|
|
|
|
set_phy_cfg_t_common_pusch(¤t_phy_cfg, config->pusch_cfg_common);
|
|
|
|
if (config->phich_cfg_present) {
|
|
// TODO
|
|
}
|
|
|
|
if (config->pucch_cfg_common_present) {
|
|
set_phy_cfg_t_common_pucch(¤t_phy_cfg, config->pucch_cfg_common);
|
|
}
|
|
|
|
if (config->srs_ul_cfg_common_present) {
|
|
set_phy_cfg_t_common_srs(¤t_phy_cfg, config->srs_ul_cfg_common);
|
|
}
|
|
|
|
if (config->ul_pwr_ctrl_common_present) {
|
|
set_phy_cfg_t_common_pwr_ctrl(¤t_phy_cfg, config->ul_pwr_ctrl_common);
|
|
}
|
|
|
|
log_rr_config_common();
|
|
|
|
if (send_lower_layers) {
|
|
mac->set_config(current_mac_cfg);
|
|
phy->set_config(current_phy_cfg);
|
|
}
|
|
}
|
|
|
|
void rrc::log_phy_config_dedicated()
|
|
{
|
|
if (!rrc_log) {
|
|
return;
|
|
}
|
|
|
|
if (current_phy_cfg.dl_cfg.cqi_report.periodic_configured) {
|
|
rrc_log->info("Set cqi-PUCCH-ResourceIndex=%d, cqi-pmi-ConfigIndex=%d, cqi-FormatIndicatorPeriodic=%d\n",
|
|
current_phy_cfg.ul_cfg.pucch.n_pucch_2,
|
|
current_phy_cfg.dl_cfg.cqi_report.pmi_idx,
|
|
current_phy_cfg.dl_cfg.cqi_report.periodic_mode);
|
|
}
|
|
if (current_phy_cfg.dl_cfg.cqi_report.aperiodic_configured) {
|
|
rrc_log->info("Set cqi-ReportModeAperiodic=%d\n", current_phy_cfg.dl_cfg.cqi_report.aperiodic_mode);
|
|
}
|
|
|
|
if (current_phy_cfg.ul_cfg.pucch.sr_configured) {
|
|
rrc_log->info("Set PHY config ded: SR-n_pucch=%d, SR-ConfigIndex=%d, SR-TransMax=%d\n",
|
|
current_phy_cfg.ul_cfg.pucch.n_pucch_sr,
|
|
current_phy_cfg.ul_cfg.pucch.I_sr,
|
|
current_mac_cfg.sr_cfg.dsr_transmax);
|
|
}
|
|
|
|
if (current_phy_cfg.ul_cfg.srs.configured) {
|
|
rrc_log->info("Set PHY config ded: SRS-ConfigIndex=%d, SRS-bw=%d, SRS-Nrcc=%d, SRS-hop=%d, SRS-Ncs=%d\n",
|
|
current_phy_cfg.ul_cfg.srs.I_srs,
|
|
current_phy_cfg.ul_cfg.srs.B,
|
|
current_phy_cfg.ul_cfg.srs.n_rrc,
|
|
current_phy_cfg.ul_cfg.srs.b_hop,
|
|
current_phy_cfg.ul_cfg.srs.n_srs);
|
|
}
|
|
}
|
|
|
|
// Apply default physical common and dedicated configuration
|
|
void rrc::set_phy_default()
|
|
{
|
|
rrc_log->info("Setting default PHY config (common and dedicated)\n");
|
|
|
|
current_phy_cfg.set_defaults();
|
|
|
|
if (phy != nullptr) {
|
|
for (uint32_t i = 0; i < SRSLTE_MAX_CARRIERS; i++) {
|
|
if (i == 0 or current_scell_configured[i]) {
|
|
phy->set_config(current_phy_cfg, i);
|
|
current_scell_configured[i] = false;
|
|
}
|
|
}
|
|
} else {
|
|
rrc_log->info("RRC not initialized. Skipping default PHY config.\n");
|
|
}
|
|
}
|
|
|
|
// Apply default physical channel configs (9.2.4)
|
|
void rrc::set_phy_config_dedicated_default()
|
|
{
|
|
rrc_log->info("Setting default PHY config dedicated\n");
|
|
|
|
current_phy_cfg.set_defaults_dedicated();
|
|
|
|
if (phy != nullptr) {
|
|
for (uint32_t i = 0; i < SRSLTE_MAX_CARRIERS; i++) {
|
|
if (i == 0 or current_scell_configured[i]) {
|
|
phy->set_config(current_phy_cfg, i);
|
|
current_scell_configured[i] = false;
|
|
}
|
|
}
|
|
} else {
|
|
rrc_log->info("RRC not initialized. Skipping default PHY config.\n");
|
|
}
|
|
}
|
|
|
|
// Apply provided PHY config
|
|
void rrc::apply_phy_config_dedicated(const phys_cfg_ded_s& phy_cnfg, bool is_handover)
|
|
{
|
|
rrc_log->info("Applying PHY config dedicated\n");
|
|
|
|
set_phy_cfg_t_dedicated_cfg(¤t_phy_cfg, phy_cnfg);
|
|
if (is_handover) {
|
|
current_phy_cfg.ul_cfg.pucch.sr_configured = false;
|
|
current_phy_cfg.dl_cfg.cqi_report.periodic_configured = false;
|
|
current_phy_cfg.dl_cfg.cqi_report.aperiodic_configured = false;
|
|
}
|
|
|
|
log_phy_config_dedicated();
|
|
|
|
if (phy != nullptr) {
|
|
phy->set_config(current_phy_cfg);
|
|
} else {
|
|
rrc_log->info("RRC not initialized. Skipping PHY config.\n");
|
|
}
|
|
}
|
|
|
|
void rrc::apply_phy_scell_config(const scell_to_add_mod_r10_s& scell_config, bool enable_cqi)
|
|
{
|
|
srslte_cell_t scell = {};
|
|
uint32_t earfcn = 0;
|
|
|
|
if (phy == nullptr) {
|
|
rrc_log->info("RRC not initialized. Skipping PHY config.\n");
|
|
return;
|
|
}
|
|
|
|
rrc_log->info("Applying PHY config to scell\n");
|
|
|
|
// Initialise default parameters from primary cell
|
|
earfcn = meas_cells.serving_cell().get_earfcn();
|
|
|
|
// Parse identification
|
|
if (scell_config.cell_identif_r10_present) {
|
|
scell.id = scell_config.cell_identif_r10.pci_r10;
|
|
earfcn = scell_config.cell_identif_r10.dl_carrier_freq_r10;
|
|
}
|
|
|
|
// Parse radio resource
|
|
if (scell_config.rr_cfg_common_scell_r10_present) {
|
|
const rr_cfg_common_scell_r10_s* rr_cfg = &scell_config.rr_cfg_common_scell_r10;
|
|
auto non_ul_cfg = &rr_cfg->non_ul_cfg_r10;
|
|
scell.frame_type = (rr_cfg->tdd_cfg_v1130.is_present()) ? SRSLTE_TDD : SRSLTE_FDD;
|
|
scell.nof_prb = non_ul_cfg->dl_bw_r10.to_number();
|
|
scell.nof_ports = non_ul_cfg->ant_info_common_r10.ant_ports_count.to_number();
|
|
scell.phich_length = (non_ul_cfg->phich_cfg_r10.phich_dur.value == phich_cfg_s::phich_dur_opts::normal)
|
|
? SRSLTE_PHICH_NORM
|
|
: SRSLTE_PHICH_EXT;
|
|
|
|
// Avoid direct conversion between different phich resource enum
|
|
switch (non_ul_cfg->phich_cfg_r10.phich_res.value) {
|
|
case phich_cfg_s::phich_res_opts::one_sixth:
|
|
scell.phich_resources = SRSLTE_PHICH_R_1_6;
|
|
break;
|
|
case phich_cfg_s::phich_res_opts::half:
|
|
scell.phich_resources = SRSLTE_PHICH_R_1_2;
|
|
break;
|
|
case phich_cfg_s::phich_res_opts::one:
|
|
scell.phich_resources = SRSLTE_PHICH_R_1;
|
|
break;
|
|
case phich_cfg_s::phich_res_opts::two:
|
|
case phich_cfg_s::phich_res_opts::nulltype:
|
|
scell.phich_resources = SRSLTE_PHICH_R_2;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Initialize scell config with pcell cfg
|
|
srslte::phy_cfg_t scell_cfg = current_phy_cfg;
|
|
set_phy_cfg_t_scell_config(&scell_cfg, scell_config);
|
|
|
|
if (not enable_cqi) {
|
|
scell_cfg.dl_cfg.cqi_report.periodic_configured = false;
|
|
scell_cfg.dl_cfg.cqi_report.aperiodic_configured = false;
|
|
}
|
|
|
|
if (!phy->set_scell(scell, scell_config.scell_idx_r10, earfcn)) {
|
|
rrc_log->error("Adding SCell cc_idx=%d\n", scell_config.scell_idx_r10);
|
|
} else if (!phy->set_config(scell_cfg, scell_config.scell_idx_r10)) {
|
|
rrc_log->error("Setting SCell configuration for cc_idx=%d\n", scell_config.scell_idx_r10);
|
|
}
|
|
|
|
current_scell_configured[scell_config.scell_idx_r10] = true;
|
|
}
|
|
|
|
void rrc::log_mac_config_dedicated()
|
|
{
|
|
rrc_log->info("Set MAC main config: harq-MaxReTX=%d, bsr-TimerReTX=%d, bsr-TimerPeriodic=%d\n",
|
|
current_mac_cfg.harq_cfg.max_harq_msg3_tx,
|
|
current_mac_cfg.bsr_cfg.retx_timer,
|
|
current_mac_cfg.bsr_cfg.periodic_timer);
|
|
if (current_mac_cfg.phr_cfg.enabled) {
|
|
rrc_log->info("Set MAC PHR config: periodicPHR-Timer=%d, prohibitPHR-Timer=%d, dl-PathlossChange=%d\n",
|
|
current_mac_cfg.phr_cfg.periodic_timer,
|
|
current_mac_cfg.phr_cfg.prohibit_timer,
|
|
current_mac_cfg.phr_cfg.db_pathloss_change);
|
|
}
|
|
}
|
|
|
|
// 3GPP 36.331 v10 9.2.2 Default MAC main configuration
|
|
void rrc::apply_mac_config_dedicated_default()
|
|
{
|
|
rrc_log->info("Setting MAC default configuration\n");
|
|
current_mac_cfg.set_mac_main_cfg_default();
|
|
mac->set_config(current_mac_cfg);
|
|
log_mac_config_dedicated();
|
|
}
|
|
|
|
/**
|
|
* Applies RadioResource Config changes to lower layers
|
|
* @param cnfg
|
|
* @param is_handover - whether the SR, CQI, SRS, measurement configs take effect immediately (see TS
|
|
* 36.331 5.3.5.4)
|
|
* @return
|
|
*/
|
|
bool rrc::apply_rr_config_dedicated(const rr_cfg_ded_s* cnfg, bool is_handover)
|
|
{
|
|
if (cnfg->phys_cfg_ded_present) {
|
|
apply_phy_config_dedicated(cnfg->phys_cfg_ded, is_handover);
|
|
// Apply SR configuration to MAC
|
|
if (not is_handover and cnfg->phys_cfg_ded.sched_request_cfg_present) {
|
|
set_mac_cfg_t_sched_request_cfg(¤t_mac_cfg, cnfg->phys_cfg_ded.sched_request_cfg);
|
|
}
|
|
}
|
|
|
|
rrc_log->info("Applying MAC config dedicated\n");
|
|
|
|
if (cnfg->mac_main_cfg_present) {
|
|
if (cnfg->mac_main_cfg.type() == rr_cfg_ded_s::mac_main_cfg_c_::types::default_value) {
|
|
current_mac_cfg.set_mac_main_cfg_default();
|
|
} else {
|
|
set_mac_cfg_t_main_cfg(¤t_mac_cfg, cnfg->mac_main_cfg.explicit_value());
|
|
}
|
|
mac->set_config(current_mac_cfg);
|
|
} else if (not is_handover and cnfg->phys_cfg_ded.sched_request_cfg_present) {
|
|
// If MAC-main not set but SR config is set, use directly mac->set_config to update config
|
|
mac->set_config(current_mac_cfg);
|
|
log_mac_config_dedicated();
|
|
}
|
|
|
|
if (cnfg->sps_cfg_present) {
|
|
// TODO
|
|
}
|
|
if (cnfg->rlf_timers_and_consts_r9.is_present() and cnfg->rlf_timers_and_consts_r9->type() == setup_e::setup) {
|
|
auto timer_expire_func = [this](uint32_t tid) { timer_expired(tid); };
|
|
t301.set(cnfg->rlf_timers_and_consts_r9->setup().t301_r9.to_number(), timer_expire_func);
|
|
t310.set(cnfg->rlf_timers_and_consts_r9->setup().t310_r9.to_number(), timer_expire_func);
|
|
t311.set(cnfg->rlf_timers_and_consts_r9->setup().t311_r9.to_number(), timer_expire_func);
|
|
N310 = cnfg->rlf_timers_and_consts_r9->setup().n310_r9.to_number();
|
|
N311 = cnfg->rlf_timers_and_consts_r9->setup().n311_r9.to_number();
|
|
|
|
rrc_log->info("Updated Constants and Timers: N310=%d, N311=%d, t300=%u, t301=%u, t310=%u, t311=%u\n",
|
|
N310,
|
|
N311,
|
|
t300.duration(),
|
|
t301.duration(),
|
|
t310.duration(),
|
|
t311.duration());
|
|
}
|
|
for (uint32_t i = 0; i < cnfg->srb_to_add_mod_list.size(); i++) {
|
|
// TODO: handle SRB modification
|
|
add_srb(cnfg->srb_to_add_mod_list[i]);
|
|
}
|
|
for (uint32_t i = 0; i < cnfg->drb_to_release_list.size(); i++) {
|
|
release_drb(cnfg->drb_to_release_list[i]);
|
|
}
|
|
for (uint32_t i = 0; i < cnfg->drb_to_add_mod_list.size(); i++) {
|
|
// TODO: handle DRB modification
|
|
add_drb(cnfg->drb_to_add_mod_list[i]);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool rrc::apply_rr_config_dedicated_on_ho_complete(const rr_cfg_ded_s& cnfg)
|
|
{
|
|
rrc_log->info("Applying MAC/PHY config dedicated on HO complete\n");
|
|
|
|
// Apply SR+CQI configuration to PHY
|
|
if (cnfg.phys_cfg_ded_present) {
|
|
apply_phy_config_dedicated(cnfg.phys_cfg_ded, false);
|
|
}
|
|
|
|
// Apply SR configuration to MAC
|
|
if (cnfg.phys_cfg_ded.sched_request_cfg_present) {
|
|
set_mac_cfg_t_sched_request_cfg(¤t_mac_cfg, cnfg.phys_cfg_ded.sched_request_cfg);
|
|
mac->set_config(current_mac_cfg);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Extracts and applies SCell configuration from an ASN.1 reconfiguration struct
|
|
*/
|
|
void rrc::apply_scell_config(rrc_conn_recfg_r8_ies_s* reconfig_r8, bool enable_cqi)
|
|
{
|
|
if (reconfig_r8->non_crit_ext_present) {
|
|
auto reconfig_r890 = &reconfig_r8->non_crit_ext;
|
|
if (reconfig_r890->non_crit_ext_present) {
|
|
auto* reconfig_r920 = &reconfig_r890->non_crit_ext;
|
|
if (reconfig_r920->non_crit_ext_present) {
|
|
auto* reconfig_r1020 = &reconfig_r920->non_crit_ext;
|
|
|
|
// Handle Add/Modify SCell list
|
|
if (reconfig_r1020->scell_to_add_mod_list_r10_present) {
|
|
for (uint32_t i = 0; i < reconfig_r1020->scell_to_add_mod_list_r10.size(); i++) {
|
|
auto scell_config = &reconfig_r1020->scell_to_add_mod_list_r10[i];
|
|
|
|
// Limit enable64_qam, if the ue does not
|
|
// since the phy does not have information about the RRC category and release, the RRC shall limit the
|
|
if (scell_config->rr_cfg_common_scell_r10_present) {
|
|
// enable64_qam
|
|
auto rr_cfg_common_scell = &scell_config->rr_cfg_common_scell_r10;
|
|
if (rr_cfg_common_scell->ul_cfg_r10_present) {
|
|
auto ul_cfg = &rr_cfg_common_scell->ul_cfg_r10;
|
|
auto pusch_cfg_common = &ul_cfg->pusch_cfg_common_r10;
|
|
|
|
// According to 3GPP 36.331 v12 UE-EUTRA-Capability field descriptions
|
|
// Allow 64QAM for:
|
|
// ue-Category 5 and 8 when enable64QAM (without suffix)
|
|
if (pusch_cfg_common->pusch_cfg_basic.enable64_qam) {
|
|
if (args.ue_category != 5 && args.ue_category != 8 && args.ue_category != 13) {
|
|
pusch_cfg_common->pusch_cfg_basic.enable64_qam = false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Call mac reconfiguration
|
|
mac->reconfiguration(scell_config->scell_idx_r10, true);
|
|
|
|
// Call phy reconfiguration
|
|
apply_phy_scell_config(*scell_config, enable_cqi);
|
|
}
|
|
}
|
|
|
|
// Handle Remove SCell list
|
|
if (reconfig_r1020->scell_to_release_list_r10_present) {
|
|
for (uint32_t i = 0; i < reconfig_r1020->scell_to_release_list_r10.size(); i++) {
|
|
// Call mac reconfiguration
|
|
mac->reconfiguration(reconfig_r1020->scell_to_release_list_r10[i], false);
|
|
|
|
// Call phy reconfiguration
|
|
// TODO: Implement phy layer cell removal
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool rrc::apply_scell_config_on_ho_complete(const asn1::rrc::rrc_conn_recfg_r8_ies_s& reconfig_r8)
|
|
{
|
|
if (reconfig_r8.non_crit_ext_present) {
|
|
auto& reconfig_r890 = reconfig_r8.non_crit_ext;
|
|
if (reconfig_r890.non_crit_ext_present) {
|
|
auto& reconfig_r920 = reconfig_r890.non_crit_ext;
|
|
if (reconfig_r920.non_crit_ext_present) {
|
|
auto& reconfig_r1020 = reconfig_r920.non_crit_ext;
|
|
|
|
// Handle Add/Modify SCell list
|
|
if (reconfig_r1020.scell_to_add_mod_list_r10_present) {
|
|
for (const auto& scell_config : reconfig_r1020.scell_to_add_mod_list_r10) {
|
|
// Call phy reconfiguration
|
|
apply_phy_scell_config(scell_config, true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void rrc::handle_con_setup(const rrc_conn_setup_s& setup)
|
|
{
|
|
// Must enter CONNECT before stopping T300
|
|
state = RRC_STATE_CONNECTED;
|
|
t300.stop();
|
|
t302.stop();
|
|
rrc_log->console("RRC Connected\n");
|
|
|
|
// Apply the Radio Resource configuration
|
|
apply_rr_config_dedicated(&setup.crit_exts.c1().rrc_conn_setup_r8().rr_cfg_ded);
|
|
|
|
nas->set_barring(srslte::barring_t::none);
|
|
|
|
if (dedicated_info_nas.get()) {
|
|
send_con_setup_complete(std::move(dedicated_info_nas));
|
|
} else {
|
|
rrc_log->error("Pending to transmit a ConnectionSetupComplete but no dedicatedInfoNAS was in queue\n");
|
|
}
|
|
}
|
|
|
|
/* Reception of RRCConnectionReestablishment by the UE 5.3.7.5 */
|
|
void rrc::handle_con_reest(const rrc_conn_reest_s& reest)
|
|
{
|
|
connection_reest.trigger(reest);
|
|
}
|
|
|
|
void rrc::add_srb(const srb_to_add_mod_s& srb_cnfg)
|
|
{
|
|
// Setup PDCP
|
|
pdcp->add_bearer(srb_cnfg.srb_id, make_srb_pdcp_config_t(srb_cnfg.srb_id, true));
|
|
if (RB_ID_SRB2 == srb_cnfg.srb_id) {
|
|
pdcp->config_security(srb_cnfg.srb_id, sec_cfg);
|
|
pdcp->enable_integrity(srb_cnfg.srb_id, DIRECTION_TXRX);
|
|
pdcp->enable_encryption(srb_cnfg.srb_id, DIRECTION_TXRX);
|
|
}
|
|
|
|
// Setup RLC
|
|
if (srb_cnfg.rlc_cfg_present) {
|
|
rlc->add_bearer(srb_cnfg.srb_id, make_rlc_config_t(srb_cnfg));
|
|
}
|
|
|
|
// Setup MAC
|
|
uint8_t log_chan_group = 0;
|
|
uint8_t priority = 0;
|
|
int prioritized_bit_rate = 0;
|
|
int bucket_size_duration = 0;
|
|
|
|
// TODO: Move this configuration to mac_interface_rrc
|
|
if (srb_cnfg.lc_ch_cfg_present) {
|
|
if (srb_cnfg.lc_ch_cfg.type() == srb_to_add_mod_s::lc_ch_cfg_c_::types::default_value) {
|
|
// Set default SRB values as defined in Table 9.2.1
|
|
switch (srb_cnfg.srb_id) {
|
|
case RB_ID_SRB0:
|
|
rrc_log->error("Setting SRB0: Should not be set by RRC\n");
|
|
break;
|
|
case RB_ID_SRB1:
|
|
priority = 1;
|
|
prioritized_bit_rate = -1;
|
|
bucket_size_duration = 0;
|
|
break;
|
|
case RB_ID_SRB2:
|
|
priority = 3;
|
|
prioritized_bit_rate = -1;
|
|
bucket_size_duration = 0;
|
|
break;
|
|
}
|
|
} else {
|
|
if (srb_cnfg.lc_ch_cfg.explicit_value().lc_ch_sr_mask_r9_present) {
|
|
// TODO
|
|
}
|
|
if (srb_cnfg.lc_ch_cfg.explicit_value().ul_specific_params_present) {
|
|
if (srb_cnfg.lc_ch_cfg.explicit_value().ul_specific_params.lc_ch_group_present)
|
|
log_chan_group = srb_cnfg.lc_ch_cfg.explicit_value().ul_specific_params.lc_ch_group;
|
|
|
|
priority = srb_cnfg.lc_ch_cfg.explicit_value().ul_specific_params.prio;
|
|
prioritized_bit_rate = srb_cnfg.lc_ch_cfg.explicit_value().ul_specific_params.prioritised_bit_rate.to_number();
|
|
bucket_size_duration = srb_cnfg.lc_ch_cfg.explicit_value().ul_specific_params.bucket_size_dur.to_number();
|
|
}
|
|
}
|
|
mac->setup_lcid(srb_cnfg.srb_id, log_chan_group, priority, prioritized_bit_rate, bucket_size_duration);
|
|
}
|
|
|
|
srbs[srb_cnfg.srb_id] = srb_cnfg;
|
|
rrc_log->info("Added radio bearer %s\n", get_rb_name(srb_cnfg.srb_id).c_str());
|
|
}
|
|
|
|
void rrc::add_drb(const drb_to_add_mod_s& drb_cnfg)
|
|
{
|
|
if (!drb_cnfg.pdcp_cfg_present || !drb_cnfg.rlc_cfg_present || !drb_cnfg.lc_ch_cfg_present) {
|
|
rrc_log->error("Cannot add DRB - incomplete configuration\n");
|
|
return;
|
|
}
|
|
uint32_t lcid = 0;
|
|
if (drb_cnfg.lc_ch_id_present) {
|
|
lcid = drb_cnfg.lc_ch_id;
|
|
} else {
|
|
lcid = RB_ID_SRB2 + drb_cnfg.drb_id;
|
|
rrc_log->warning("LCID not present, using %d\n", lcid);
|
|
}
|
|
|
|
// Setup RLC
|
|
rlc->add_bearer(lcid, make_rlc_config_t(drb_cnfg.rlc_cfg));
|
|
|
|
// Setup PDCP
|
|
pdcp_config_t pdcp_cfg = make_drb_pdcp_config_t(drb_cnfg.drb_id, true, drb_cnfg.pdcp_cfg);
|
|
pdcp->add_bearer(lcid, pdcp_cfg);
|
|
pdcp->config_security(lcid, sec_cfg);
|
|
pdcp->enable_encryption(lcid);
|
|
|
|
// Setup MAC
|
|
uint8_t log_chan_group = 0;
|
|
uint8_t priority = 1;
|
|
int prioritized_bit_rate = -1;
|
|
int bucket_size_duration = -1;
|
|
if (drb_cnfg.lc_ch_cfg.ul_specific_params_present) {
|
|
if (drb_cnfg.lc_ch_cfg.ul_specific_params.lc_ch_group_present) {
|
|
log_chan_group = drb_cnfg.lc_ch_cfg.ul_specific_params.lc_ch_group;
|
|
} else {
|
|
rrc_log->warning("LCG not present, setting to 0\n");
|
|
}
|
|
priority = drb_cnfg.lc_ch_cfg.ul_specific_params.prio;
|
|
prioritized_bit_rate = drb_cnfg.lc_ch_cfg.ul_specific_params.prioritised_bit_rate.to_number();
|
|
bucket_size_duration = drb_cnfg.lc_ch_cfg.ul_specific_params.bucket_size_dur.to_number();
|
|
}
|
|
mac->setup_lcid(lcid, log_chan_group, priority, prioritized_bit_rate, bucket_size_duration);
|
|
|
|
drbs[lcid] = drb_cnfg;
|
|
drb_up = true;
|
|
rrc_log->info("Added radio bearer %s (LCID=%d)\n", get_rb_name(lcid).c_str(), lcid);
|
|
}
|
|
|
|
void rrc::release_drb(uint32_t drb_id)
|
|
{
|
|
uint32_t lcid = RB_ID_SRB2 + drb_id;
|
|
|
|
if (drbs.find(drb_id) != drbs.end()) {
|
|
rrc_log->info("Releasing radio bearer %s\n", get_rb_name(lcid).c_str());
|
|
drbs.erase(lcid);
|
|
} else {
|
|
rrc_log->error("Couldn't release radio bearer %s. Doesn't exist.\n", get_rb_name(lcid).c_str());
|
|
}
|
|
}
|
|
|
|
uint32_t rrc::get_lcid_for_eps_bearer(const uint32_t& eps_bearer_id)
|
|
{
|
|
// check if this bearer id exists and return it's LCID
|
|
for (auto& drb : drbs) {
|
|
if (drb.second.eps_bearer_id == eps_bearer_id) {
|
|
return drb.first;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void rrc::add_mrb(uint32_t lcid, uint32_t port)
|
|
{
|
|
gw->add_mch_port(lcid, port);
|
|
rlc->add_bearer_mrb(lcid);
|
|
mac->mch_start_rx(lcid);
|
|
rrc_log->info("Added MRB bearer for lcid:%d\n", lcid);
|
|
}
|
|
|
|
// PHY CONFIG DEDICATED Defaults (3GPP 36.331 v10 9.2.4)
|
|
void rrc::set_phy_default_pucch_srs()
|
|
{
|
|
set_phy_config_dedicated_default();
|
|
|
|
// SR configuration affects to MAC SR too
|
|
current_mac_cfg.sr_cfg.reset();
|
|
mac->set_config(current_mac_cfg);
|
|
}
|
|
|
|
void rrc::set_mac_default()
|
|
{
|
|
apply_mac_config_dedicated_default();
|
|
}
|
|
|
|
void rrc::set_rrc_default()
|
|
{
|
|
N310 = 1;
|
|
N311 = 1;
|
|
auto timer_expire_func = [this](uint32_t tid) { timer_expired(tid); };
|
|
t304.set(1000, timer_expire_func);
|
|
t310.set(1000, timer_expire_func);
|
|
t311.set(1000, timer_expire_func);
|
|
}
|
|
|
|
const std::string rrc::rb_id_str[] =
|
|
{"SRB0", "SRB1", "SRB2", "DRB1", "DRB2", "DRB3", "DRB4", "DRB5", "DRB6", "DRB7", "DRB8"};
|
|
|
|
} // namespace srsue
|