srsRAN/srsue/src/stack/ue_stack_lte.cc

319 lines
7.9 KiB
C++

/*
* Copyright 2013-2019 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/srslte.h"
using namespace srslte;
namespace srsue {
ue_stack_lte::ue_stack_lte() :
timers(64),
running(false),
args(),
logger(nullptr),
usim(nullptr),
phy(nullptr),
rlc(&rlc_log),
mac(&mac_log),
rrc(&rrc_log),
pdcp(&pdcp_log),
nas(&nas_log, &timers),
thread("STACK"),
pending_tasks(1024),
background_tasks(2)
{
ue_queue_id = pending_tasks.add_queue();
sync_queue_id = pending_tasks.add_queue();
gw_queue_id = pending_tasks.add_queue();
mac_queue_id = pending_tasks.add_queue();
background_queue_id = pending_tasks.add_queue();
background_tasks.start();
}
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_;
// setup logging for each layer
mac_log.init("MAC ", logger, true);
rlc_log.init("RLC ", logger);
pdcp_log.init("PDCP", logger);
rrc_log.init("RRC ", logger);
nas_log.init("NAS ", logger);
usim_log.init("USIM", logger);
pool_log.init("POOL", logger);
pool_log.set_level(srslte::LOG_LEVEL_ERROR);
byte_buffer_pool::get_instance()->set_log(&pool_log);
mac_log.set_level(args.log.mac_level);
rlc_log.set_level(args.log.rlc_level);
pdcp_log.set_level(args.log.pdcp_level);
rrc_log.set_level(args.log.rrc_level);
nas_log.set_level(args.log.nas_level);
usim_log.set_level(args.log.usim_level);
mac_log.set_hex_limit(args.log.mac_hex_limit);
rlc_log.set_hex_limit(args.log.rlc_hex_limit);
pdcp_log.set_hex_limit(args.log.pdcp_hex_limit);
rrc_log.set_hex_limit(args.log.rrc_hex_limit);
nas_log.set_hex_limit(args.log.nas_hex_limit);
usim_log.set_hex_limit(args.log.usim_hex_limit);
// 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);
if (usim->init(&args.usim)) {
usim_log.console("Failed to initialize USIM.\n");
return SRSLTE_ERROR;
}
mac.init(phy, &rlc, &rrc, &timers, this);
rlc.init(&pdcp, &rrc, &timers, 0 /* RB_ID_SRB0 */);
pdcp.init(&rlc, &rrc, gw);
nas.init(usim.get(), &rrc, gw, args.nas);
rrc.init(phy, &mac, &rlc, &pdcp, &nas, usim.get(), gw, &timers, this, args.rrc);
running = true;
start(STACK_MAIN_THREAD_PRIO);
return SRSLTE_SUCCESS;
}
void ue_stack_lte::stop()
{
if (running) {
pending_tasks.try_push(ue_queue_id, task_t{[this](task_t*) { 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) {
pending_tasks.try_push(ue_queue_id, task_t{[this](task_t*) {
nas.start_attach_request(nullptr, srslte::establishment_cause_t::mo_data);
}});
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)
const uint32_t RB_ID_SRB1 = 1;
int cnt = 0, timeout = 5000;
while (rlc.has_data(RB_ID_SRB1) && ++cnt <= timeout) {
usleep(1000);
}
bool detach_sent = true;
if (rlc.has_data(RB_ID_SRB1)) {
nas_log.warning("Detach couldn't be sent after %ds.\n", timeout);
detach_sent = false;
}
return detach_sent;
}
bool ue_stack_lte::enable_data()
{
// perform attach request
return switch_on();
}
bool ue_stack_lte::disable_data()
{
// generate detach request
return nas.detach_request(false);
}
bool ue_stack_lte::get_metrics(stack_metrics_t* metrics)
{
mac.get_metrics(metrics->mac);
rlc.get_metrics(metrics->rlc);
nas.get_metrics(&metrics->nas);
rrc.get_metrics(metrics->rrc);
return (metrics->nas.state == EMM_STATE_REGISTERED && metrics->rrc.state == RRC_STATE_CONNECTED);
}
void ue_stack_lte::run_thread()
{
while (running) {
task_t task{};
if (pending_tasks.wait_pop(&task) >= 0) {
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, bool blocking)
{
task_t task{};
task.pdu = std::move(sdu);
task.func = [this, lcid, blocking](task_t* task_ctxt) { pdcp.write_sdu(lcid, std::move(task_ctxt->pdu), blocking); };
std::pair<bool, task_t> ret = pending_tasks.try_push(gw_queue_id, std::move(task));
if (not ret.first) {
pdcp_log.warning("GW SDU with lcid=%d was discarded.\n", lcid);
}
}
/********************
* SYNC Interface
*******************/
/**
* Sync thread signal that it is in sync
*/
void ue_stack_lte::in_sync()
{
pending_tasks.push(sync_queue_id, task_t{[this](task_t*) { rrc.in_sync(); }});
}
void ue_stack_lte::out_of_sync()
{
pending_tasks.push(sync_queue_id, task_t{[this](task_t*) { rrc.out_of_sync(); }});
}
void ue_stack_lte::run_tti(uint32_t tti)
{
pending_tasks.push(sync_queue_id, task_t{[this, tti](task_t*) { run_tti_impl(tti); }});
}
void ue_stack_lte::run_tti_impl(uint32_t tti)
{
mac.run_tti(tti);
rrc.run_tti(tti);
nas.run_tti(tti);
timers.step_all();
}
/********************
* low MAC Interface
*******************/
void ue_stack_lte::process_pdus()
{
pending_tasks.push(mac_queue_id, task_t{[this](task_t*) { mac.process_pdus(); }});
}
void ue_stack_lte::wait_ra_completion(uint16_t rnti)
{
background_tasks.push_task([this, rnti](uint32_t worker_id) {
phy->set_crnti(rnti);
// signal MAC RA proc to go back to idle
mac.notify_ra_completed();
});
}
/********************
* RRC Interface
*******************/
void ue_stack_lte::start_cell_search()
{
background_tasks.push_task([this](uint32_t worker_id) {
phy_interface_rrc_lte::phy_cell_t found_cell;
phy_interface_rrc_lte::cell_search_ret_t ret = phy->cell_search(&found_cell);
// notify back RRC
pending_tasks.push(background_queue_id,
task_t{[this, found_cell, ret](task_t*) { rrc.cell_search_completed(ret, found_cell); }});
});
}
} // namespace srsue