srsRAN/srsue/src/stack/ue_stack_lte.cc

345 lines
9.1 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/ue_stack_lte.h"
#include "srslte/common/logmap.h"
#include "srslte/srslte.h"
#include <algorithm>
#include <chrono>
#include <numeric>
#include <thread>
using namespace srslte;
namespace srsue {
ue_stack_lte::ue_stack_lte() :
running(false),
args(),
logger(nullptr),
usim(nullptr),
phy(nullptr),
nas(nullptr),
rlc("RLC"),
mac("MAC", &task_sched),
rrc(this, &task_sched),
pdcp(&task_sched, "PDCP"),
thread("STACK"),
task_sched(512, 2, 64),
tti_tprof("tti_tprof", "STCK", TTI_STAT_PERIOD)
{
ue_task_queue = task_sched.make_task_queue();
gw_queue_id = task_sched.make_task_queue();
cfg_task_queue = task_sched.make_task_queue();
// sync_queue is added in init()
}
ue_stack_lte::~ue_stack_lte()
{
stop();
}
std::string ue_stack_lte::get_type()
{
return "lte";
}
int ue_stack_lte::init(const stack_args_t& args_,
srslte::logger* logger_,
phy_interface_stack_lte* phy_,
gw_interface_stack* gw_)
{
phy = phy_;
gw = gw_;
if (init(args_, logger_)) {
return SRSLTE_ERROR;
}
return SRSLTE_SUCCESS;
}
int ue_stack_lte::init(const stack_args_t& args_, srslte::logger* logger_)
{
args = args_;
logger = logger_;
// init own log
stack_log->set_level(args.log.stack_level);
stack_log->set_hex_limit(args.log.stack_hex_limit);
pool_log->set_level(srslte::LOG_LEVEL_WARNING);
byte_buffer_pool::get_instance()->set_log(pool_log.get());
// init layer logs
srslte::logmap::register_log(std::unique_ptr<srslte::log>{new srslte::log_filter{"MAC", logger, true}});
mac_log->set_level(args.log.mac_level);
mac_log->set_hex_limit(args.log.mac_hex_limit);
rlc_log->set_level(args.log.rlc_level);
rlc_log->set_hex_limit(args.log.rlc_hex_limit);
pdcp_log->set_level(args.log.pdcp_level);
pdcp_log->set_hex_limit(args.log.pdcp_hex_limit);
rrc_log->set_level(args.log.rrc_level);
rrc_log->set_hex_limit(args.log.rrc_hex_limit);
usim_log->set_level(args.log.usim_level);
usim_log->set_hex_limit(args.log.usim_hex_limit);
nas_log->set_level(args.log.nas_level);
nas_log->set_hex_limit(args.log.nas_hex_limit);
extif_log->set_level(args.log.extif_level);
extif_log->set_hex_limit(args.log.extif_hex_limit);
// Should we use the built-in NAS implementation
if (!args.nas_ext.enable) {
std::unique_ptr<srsue::nas> nas_impl(new srsue::nas(&task_sched, args.nas));
nas = std::move(nas_impl);
} else { // ... or provide an external interface (RRCTL)?
std::unique_ptr<srsue::nas_ext> nas_impl(new srsue::nas_ext(&task_sched, args.nas_ext));
nas = std::move(nas_impl);
}
// Set up pcap
if (args.pcap.enable) {
mac_pcap.open(args.pcap.filename.c_str());
mac.start_pcap(&mac_pcap);
}
if (args.pcap.nas_enable) {
nas_pcap.open(args.pcap.nas_filename.c_str());
nas->start_pcap(&nas_pcap);
}
// Init USIM first to allow early exit in case reader couldn't be found
usim = usim_base::get_instance(&args.usim, usim_log.get());
if (usim->init(&args.usim)) {
srslte::console("Failed to initialize USIM.\n");
return SRSLTE_ERROR;
}
// add sync queue
sync_task_queue = task_sched.make_task_queue(args.sync_queue_size);
mac.init(phy, &rlc, &rrc);
rlc.init(&pdcp, &rrc, task_sched.get_timer_handler(), 0 /* RB_ID_SRB0 */);
pdcp.init(&rlc, &rrc, gw);
nas->init(usim.get(), &rrc, gw);
rrc.init(phy, &mac, &rlc, &pdcp, nas.get(), usim.get(), gw, args.rrc);
running = true;
start(STACK_MAIN_THREAD_PRIO);
return SRSLTE_SUCCESS;
}
void ue_stack_lte::stop()
{
if (running) {
ue_task_queue.try_push([this]() { stop_impl(); });
wait_thread_finish();
}
}
void ue_stack_lte::stop_impl()
{
running = false;
usim->stop();
nas->stop();
rrc.stop();
rlc.stop();
pdcp.stop();
mac.stop();
if (args.pcap.enable) {
mac_pcap.close();
}
if (args.pcap.nas_enable) {
nas_pcap.close();
}
}
bool ue_stack_lte::switch_on()
{
if (running) {
ue_task_queue.try_push([this]() { nas->start_attach_proc(nullptr, srslte::establishment_cause_t::mo_sig); });
return true;
}
return false;
}
bool ue_stack_lte::switch_off()
{
// generate detach request with switch-off flag
nas->detach_request(true);
// wait for max. 5s for it to be sent (according to TS 24.301 Sec 25.5.2.2)
int cnt = 0, timeout_ms = 5000;
while (not rrc.srbs_flushed() && ++cnt <= timeout_ms) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
bool detach_sent = true;
if (not rrc.srbs_flushed()) {
logmap::get("NAS ")->warning("Detach couldn't be sent after %dms.\n", timeout_ms);
detach_sent = false;
}
return detach_sent;
}
bool ue_stack_lte::enable_data()
{
// perform attach request
srslte::console("Turning off airplane mode.\n");
return switch_on();
}
bool ue_stack_lte::disable_data()
{
// generate detach request
srslte::console("Turning on airplane mode.\n");
return nas->detach_request(false);
}
bool ue_stack_lte::get_metrics(stack_metrics_t* metrics)
{
// use stack thread to query metrics
ue_task_queue.try_push([this]() {
stack_metrics_t metrics{};
mac.get_metrics(metrics.mac);
rlc.get_metrics(metrics.rlc);
nas->get_metrics(&metrics.nas);
rrc.get_metrics(metrics.rrc);
pending_stack_metrics.push(metrics);
});
// wait for result
*metrics = pending_stack_metrics.wait_pop();
return (metrics->nas.state == EMM_STATE_REGISTERED && metrics->rrc.state == RRC_STATE_CONNECTED);
}
void ue_stack_lte::run_thread()
{
while (running) {
task_sched.run_next_task();
}
}
/***********************************************************************************************************************
* Stack Interfaces
**********************************************************************************************************************/
/********************
* GW Interface
*******************/
/**
* Push GW SDU to stack
* @param lcid
* @param sdu
* @param blocking
*/
void ue_stack_lte::write_sdu(uint32_t lcid, srslte::unique_byte_buffer_t sdu)
{
auto task = [this, lcid](srslte::unique_byte_buffer_t& sdu) { pdcp.write_sdu(lcid, std::move(sdu)); };
bool ret = gw_queue_id.try_push(std::bind(task, std::move(sdu))).first;
if (not ret) {
pdcp_log->warning("GW SDU with lcid=%d was discarded.\n", lcid);
}
}
/********************
* PHY Interface
*******************/
void ue_stack_lte::cell_search_complete(cell_search_ret_t ret, phy_cell_t found_cell)
{
cfg_task_queue.push([this, ret, found_cell]() { rrc.cell_search_complete(ret, found_cell); });
}
void ue_stack_lte::cell_select_complete(bool status)
{
cfg_task_queue.push([this, status]() { rrc.cell_select_complete(status); });
}
void ue_stack_lte::set_config_complete(bool status)
{
cfg_task_queue.push([this, status]() { rrc.set_config_complete(status); });
}
void ue_stack_lte::set_scell_complete(bool status)
{
cfg_task_queue.push([this, status]() { rrc.set_scell_complete(status); });
}
/********************
* SYNC Interface
*******************/
/**
* Sync thread signal that it is in sync
*/
void ue_stack_lte::in_sync()
{
sync_task_queue.push([this]() { rrc.in_sync(); });
}
void ue_stack_lte::out_of_sync()
{
sync_task_queue.push([this]() { rrc.out_of_sync(); });
}
void ue_stack_lte::run_tti(uint32_t tti, uint32_t tti_jump)
{
if (running) {
sync_task_queue.push([this, tti, tti_jump]() { run_tti_impl(tti, tti_jump); });
}
}
void ue_stack_lte::run_tti_impl(uint32_t tti, uint32_t tti_jump)
{
if (args.have_tti_time_stats) {
tti_tprof.start();
}
current_tti = tti_point{tti};
// perform tasks for the received TTI range
for (uint32_t i = 0; i < tti_jump; ++i) {
uint32_t next_tti = TTI_SUB(tti, (tti_jump - i - 1));
mac.run_tti(next_tti);
task_sched.tic();
}
rrc.run_tti();
nas->run_tti();
if (args.have_tti_time_stats) {
std::chrono::nanoseconds dur = tti_tprof.stop();
if (dur > TTI_WARN_THRESHOLD_MS) {
mac_log->warning("%s: detected long duration=%" PRId64 "ms\n",
"proc_time",
std::chrono::duration_cast<std::chrono::milliseconds>(dur).count());
}
}
// print warning if PHY pushes new TTI messages faster than we process them
if (sync_task_queue.size() > SYNC_QUEUE_WARN_THRESHOLD) {
stack_log->warning("Detected slow task processing (sync_queue_len=%zd).\n", sync_task_queue.size());
}
}
} // namespace srsue