added resumable procedures to make RRC and NAS non-blocking

master
Francisco Paisana 6 years ago committed by Andre Puschmann
parent e42449532b
commit c9d3b61038

@ -0,0 +1,317 @@
/*
* 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 <functional>
#include <list>
#include <memory>
#include <mutex>
#ifndef SRSLTE_RESUMABLE_PROCEDURES_H
#define SRSLTE_RESUMABLE_PROCEDURES_H
namespace srslte {
enum class proc_state_t { on_going, success, error, inactive };
enum class proc_outcome_t { repeat, yield, success, error };
/**************************************************************************************
* class: proc_impl_t
* Provides an polymorphic interface for resumable procedures. This base can then be used
* by a task dispatch queue via the method "run()".
* Every procedure starts in inactive state, and finishes with success or error.
* With methods:
* - run() - executes a procedure, returning true if the procedure is still running
* or false, if it has completed
* - step() - method overriden by child class that will be called by run(). step()
* executes a procedure "action" based on its current internal state,
* and return a proc_outcome_t variable with possible values:
* - yield - the procedure performed the action but hasn't completed yet.
* - repeat - the same as yield, but explicitly asking that run() should
* recall step() again (probably the procedure state has changed)
* - error - the procedure has finished unsuccessfully
* - success - the procedure has completed successfully
* - stop() - called automatically when a procedure has finished. Useful for actions
* upon procedure completion, like sending back a response.
* - set_proc_state() / is_#() - setter and getters for current procedure state
************************************************************************************/
class proc_impl_t
{
public:
proc_impl_t() : proc_state(proc_state_t::inactive) {}
virtual ~proc_impl_t() = default;
virtual proc_outcome_t step() = 0;
virtual void stop() {} // may be overloaded
bool run()
{
proc_outcome_t outcome = proc_outcome_t::repeat;
while (is_running() and outcome == proc_outcome_t::repeat) {
outcome = step();
if (outcome == proc_outcome_t::error) {
set_proc_state(proc_state_t::error);
} else if (outcome == proc_outcome_t::success) {
set_proc_state(proc_state_t::success);
}
}
return is_running();
}
void set_proc_state(proc_state_t new_state)
{
proc_state = new_state;
if (proc_state == proc_state_t::error or proc_state == proc_state_t::success) {
stop();
}
}
bool is_error() const { return proc_state == proc_state_t::error; }
bool is_success() const { return proc_state == proc_state_t::success; }
bool is_running() const { return proc_state == proc_state_t::on_going; }
bool is_complete() const { return is_success() or is_error(); }
bool is_active() const { return proc_state != proc_state_t::inactive; }
private:
proc_state_t proc_state;
};
/**************************************************************************************
* class: proc_t<T>
* Handles the lifetime, of a procedure T that derives from proc_impl_t, including
* its alloc, initialization, and reset back to initial state once the procedure has been
* completed and the user has extracted its results.
* Can only be re-launched when a procedure T becomes inactive.
* It uses a unique_ptr<T> to allow the use of procedures that are forward declared.
* It provides the following methods:
* - run() - calls proc_impl_t::run(). See above.
* - launch() - initializes the procedure T by calling T::init(...). Handles the case
* of failed initialization, and forbids the initialization of procedures
* that are already active.
* - pop() - extracts the result of the procedure if it has finished, and sets
* proc_t<T> back to inactive
* - trigger_event(Event) - used for handling external events. The procedure T will
* have to define a method "trigger_event(Event)" as well,
* specifying how each event type should be handled.
************************************************************************************/
template <class T>
class proc_t
{
public:
explicit proc_t() : proc_impl_ptr(new T()) {}
T* get() { return proc_impl_ptr.get(); }
bool is_active() const { return proc_impl_ptr->is_active(); }
bool is_complete() const { return proc_impl_ptr->is_complete(); }
T* release() { return proc_impl_ptr.release(); }
bool run() { return proc_impl_ptr->run(); }
void clear()
{
// Destructs the current object, and calls default ctor (which sets proc back to inactive and ready for another run)
proc_impl_ptr->~T();
new (proc_impl_ptr.get()) T();
}
template <class Event>
void trigger_event(Event&& e)
{
if (proc_impl_ptr->is_running()) {
proc_outcome_t outcome = proc_impl_ptr->trigger_event(std::forward<Event>(e));
if (outcome == proc_outcome_t::error) {
proc_impl_ptr->set_proc_state(proc_state_t::error);
} else if (outcome == proc_outcome_t::success) {
proc_impl_ptr->set_proc_state(proc_state_t::success);
}
}
}
T pop()
{
if (not proc_impl_ptr->is_complete()) {
return T();
}
T ret(std::move(*proc_impl_ptr));
clear();
return ret;
}
template <class... Args>
bool launch(Args&&... args)
{
if (is_active()) {
// if already active
return false;
}
proc_impl_ptr->set_proc_state(proc_state_t::on_going);
proc_outcome_t init_ret = proc_impl_ptr->init(std::forward<Args>(args)...);
switch (init_ret) {
case proc_outcome_t::error:
proc_impl_ptr->set_proc_state(proc_state_t::error); // call stop as an error
clear();
return false;
case proc_outcome_t::success:
proc_impl_ptr->set_proc_state(proc_state_t::success);
break;
case proc_outcome_t::repeat:
run(); // call run right away
break;
case proc_outcome_t::yield:
break;
}
return true;
}
private:
std::unique_ptr<T> proc_impl_ptr;
};
/**************************************************************************************
* class: func_proc_t
* A proc_impl_t used to store lambda functions and other function pointers as a step()
* method, avoiding this way, always having to create a new class per procedure.
************************************************************************************/
class func_proc_t : public proc_impl_t
{
public:
proc_outcome_t init(std::function<proc_outcome_t()> step_func_)
{
step_func = std::move(step_func_);
return proc_outcome_t::yield;
}
proc_outcome_t step() final { return step_func(); }
private:
std::function<proc_outcome_t()> step_func;
};
/**************************************************************************************
* class: func_proc_t
* A helper proc_impl_t whose step()/stop() are no op, but has a trigger_event() that
* signals that the method has finished and store a result of type OutcomeType.
************************************************************************************/
template <class OutcomeType>
class query_proc_t : public proc_impl_t
{
public:
proc_outcome_t init() { return proc_outcome_t::yield; }
proc_outcome_t step() final { return proc_outcome_t::yield; }
proc_outcome_t trigger_event(const OutcomeType& outcome_)
{
outcome = outcome_;
return proc_outcome_t::success;
}
const OutcomeType& result() const { return outcome; }
private:
OutcomeType outcome;
};
/**************************************************************************************
* class: callback_list_t
* Stores procedures that derive from proc_impl_t. Its run() method calls sequentially
* all the stored procedures run() method, and removes the procedures if they have
* completed.
* There are different ways to add a procedure to the list:
* - add_proc(...) - adds a proc_t<T>, and once the procedure has completed, takes it
* out of the container without resetting it back to its initial state
* or deleting. This is useful, if the user wants to extract the
* procedure result via proc_t<T>::pop()
* - consume_proc(...) - receives a proc_t<T> as a rvalue, and calls the proc_t<T>
* destructor once the procedure has ended. Useful, for procedures
* for which the user is not interested in the result, or reusing
* - defer_proc(...) - same as add_proc(...), but once the procedure has finished, it
* automatically sets the procedure back to its initial state.
* Useful if the user is not interested in handling the result
* - defer_task(...) - same as consume_proc(...) but takes a function pointer that
* specifies a proc_impl_t step() function
************************************************************************************/
class callback_list_t
{
public:
typedef std::function<void(proc_impl_t*)> proc_deleter_t;
typedef std::unique_ptr<proc_impl_t, proc_deleter_t> callback_obj_t;
template <class T>
struct recycle_deleter_t {
void operator()(proc_impl_t* p)
{
if (p != nullptr) {
T* Tp = static_cast<T*>(p);
Tp->~T();
new (Tp) T();
}
}
};
template <class T>
void add_proc(proc_t<T>& proc)
{
if (proc.is_complete()) {
return;
}
callback_obj_t ptr(proc.get(), [](proc_impl_t* p) { /* do nothing */ });
callbacks.push_back(std::move(ptr));
}
template <class T>
void consume_proc(proc_t<T>&& proc)
{
if (proc.is_complete()) {
return;
}
callback_obj_t ptr(proc.release(), std::default_delete<proc_impl_t>());
callbacks.push_back(std::move(ptr));
}
template <class T>
void defer_proc(proc_t<T>& proc)
{
if (proc.is_complete()) {
proc.pop();
return;
}
callback_obj_t ptr(proc.get(), recycle_deleter_t<T>());
callbacks.push_back(std::move(ptr));
}
bool defer_task(std::function<proc_outcome_t()> step_func)
{
proc_t<func_proc_t> proc;
if (not proc.launch(std::move(step_func))) {
return false;
}
consume_proc(std::move(proc));
return true;
}
void run()
{
// Calls run for all callbacks. Remove the ones that have finished. The proc dtor is called.
callbacks.remove_if([](callback_obj_t& elem) { return not elem->run(); });
}
size_t size() const { return callbacks.size(); }
private:
std::list<callback_obj_t> callbacks;
};
} // namespace srslte
#endif // SRSLTE_RESUMABLE_PROCEDURES_H

@ -124,6 +124,7 @@ struct plmn_id_t {
struct s_tmsi_t {
uint8_t mmec = 0;
uint32_t m_tmsi = 0;
bool operator==(const s_tmsi_t& other) const { return mmec == other.mmec and m_tmsi == other.m_tmsi; }
};
/***************************

@ -36,6 +36,7 @@
#include "srslte/common/common.h"
#include "srslte/common/interfaces_common.h"
#include "srslte/common/security.h"
#include "srslte/common/stack_procedure.h"
#include "srslte/phy/channel/channel.h"
#include "srslte/phy/rf/rf.h"
@ -118,43 +119,6 @@ public:
virtual void write_pdu_mch(uint32_t lcid, srslte::unique_byte_buffer_t pdu) = 0;
};
// NAS interface for RRC
class nas_interface_rrc
{
public:
typedef enum {
BARRING_NONE = 0,
BARRING_MO_DATA,
BARRING_MO_SIGNALLING,
BARRING_MT,
BARRING_ALL
} barring_t;
virtual void leave_connected() = 0;
virtual void set_barring(barring_t barring) = 0;
virtual void paging(srslte::s_tmsi_t* ue_identity) = 0;
virtual bool is_attached() = 0;
virtual void write_pdu(uint32_t lcid, srslte::unique_byte_buffer_t pdu) = 0;
virtual uint32_t get_k_enb_count() = 0;
virtual bool get_k_asme(uint8_t* k_asme_, uint32_t n) = 0;
virtual uint32_t get_ipv4_addr() = 0;
virtual bool get_ipv6_addr(uint8_t* ipv6_addr) = 0;
};
// NAS interface for UE
class nas_interface_ue
{
public:
virtual bool attach_request() = 0;
virtual bool detach_request() = 0;
};
// NAS interface for UE
class nas_interface_gw
{
public:
virtual bool attach_request() = 0;
};
// RRC interface for MAC
class rrc_interface_mac_common
{
@ -189,18 +153,19 @@ public:
const static int MAX_FOUND_PLMNS = 16;
virtual void write_sdu(srslte::unique_byte_buffer_t sdu) = 0;
virtual uint16_t get_mcc() = 0;
virtual uint16_t get_mnc() = 0;
virtual void enable_capabilities() = 0;
virtual int plmn_search(found_plmn_t found_plmns[MAX_FOUND_PLMNS]) = 0;
virtual void plmn_select(srslte::plmn_id_t plmn_id) = 0;
virtual void write_sdu(srslte::unique_byte_buffer_t sdu) = 0;
virtual uint16_t get_mcc() = 0;
virtual uint16_t get_mnc() = 0;
virtual void enable_capabilities() = 0;
virtual bool plmn_search() = 0;
virtual void plmn_select(srslte::plmn_id_t plmn_id) = 0;
virtual bool connection_request(srslte::establishment_cause_t cause,
srslte::unique_byte_buffer_t dedicatedInfoNAS) = 0;
virtual void set_ue_identity(srslte::s_tmsi_t s_tmsi) = 0;
virtual bool is_connected() = 0;
virtual std::string get_rb_name(uint32_t lcid) = 0;
virtual uint32_t get_lcid_for_eps_bearer(const uint32_t& eps_bearer_id) = 0;
srslte::unique_byte_buffer_t dedicatedInfoNAS) = 0;
virtual void set_ue_identity(srslte::s_tmsi_t s_tmsi) = 0;
virtual bool is_connected() = 0;
virtual void paging_completed(bool outcome) = 0;
virtual std::string get_rb_name(uint32_t lcid) = 0;
virtual uint32_t get_lcid_for_eps_bearer(const uint32_t& eps_bearer_id) = 0;
};
// RRC interface for PDCP
@ -224,6 +189,34 @@ public:
virtual void write_pdu(uint32_t lcid, srslte::unique_byte_buffer_t pdu) = 0;
};
// NAS interface for RRC
class nas_interface_rrc
{
public:
typedef enum { BARRING_NONE = 0, BARRING_MO_DATA, BARRING_MO_SIGNALLING, BARRING_MT, BARRING_ALL } barring_t;
virtual void leave_connected() = 0;
virtual void set_barring(barring_t barring) = 0;
virtual void paging(srslte::s_tmsi_t* ue_identity) = 0;
virtual bool is_attached() = 0;
virtual void write_pdu(uint32_t lcid, srslte::unique_byte_buffer_t pdu) = 0;
virtual uint32_t get_k_enb_count() = 0;
virtual bool get_k_asme(uint8_t* k_asme_, uint32_t n) = 0;
virtual uint32_t get_ipv4_addr() = 0;
virtual bool get_ipv6_addr(uint8_t* ipv6_addr) = 0;
virtual void plmn_search_completed(rrc_interface_nas::found_plmn_t found_plmns[rrc_interface_nas::MAX_FOUND_PLMNS],
int nof_plmns) = 0;
virtual bool connection_request_completed(bool outcome) = 0;
virtual void run_tti(uint32_t tti) = 0;
};
// NAS interface for UE
class nas_interface_ue
{
public:
virtual void start_attach_request(srslte::proc_state_t* proc_result) = 0;
virtual bool detach_request() = 0;
};
// PDCP interface for RRC
class pdcp_interface_rrc
{

@ -29,7 +29,7 @@
namespace srslte {
class pdcp : public srsue::pdcp_interface_gw, public srsue::pdcp_interface_rlc, public srsue::pdcp_interface_rrc
class pdcp : public srsue::pdcp_interface_rlc, public srsue::pdcp_interface_rrc
{
public:
pdcp(log* log_);

@ -56,3 +56,5 @@ add_executable(bcd_helpers_test bcd_helpers_test.cc)
add_executable(pdu_test pdu_test.cc)
target_link_libraries(pdu_test srslte_phy srslte_common ${CMAKE_THREAD_LIBS_INIT})
add_test(pdu_test pdu_test)
add_executable(stack_procedure_test stack_procedure_test.cc)

@ -0,0 +1,312 @@
/*
* 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 "srslte/common/stack_procedure.h"
#include <iostream>
#define TESTASSERT(cond) \
{ \
if (!(cond)) { \
std::cout << "[" << __FUNCTION__ << "][Line " << __LINE__ << "]: FAIL at " << (#cond) << std::endl; \
return -1; \
} \
}
using srslte::proc_outcome_t;
enum class obj_state_t { default_ctor, move_ctor, copy_ctor, from_move_ctor, from_move_assign };
const char* to_string(obj_state_t o)
{
switch (o) {
case obj_state_t::default_ctor:
return "default_ctor";
case obj_state_t::move_ctor:
return "move_ctor";
case obj_state_t::copy_ctor:
return "copy_ctor";
case obj_state_t::from_move_ctor:
return "from_move_ctor";
case obj_state_t::from_move_assign:
return "from_move_assign";
}
return "";
}
class TestObj
{
public:
TestObj() = default;
TestObj(const TestObj& other)
{
printf("TestObj copy ctor called: {%s,%d} <- {%s,%d}!!!\n", to_string(state), id, to_string(other.state), other.id);
id = other.id;
state = obj_state_t::copy_ctor;
copy_counter++;
}
TestObj(TestObj&& other) noexcept
{
printf("TestObj move ctor called: {%s,%d} <- {%s,%d}!!!\n", to_string(state), id, to_string(other.state), other.id);
id = other.id;
state = obj_state_t::move_ctor;
other.state = obj_state_t::from_move_ctor;
move_counter++;
}
~TestObj()
{
dtor_counter++;
printf("TestObj {%s,%d} dtor called!!!\n", to_string(state), id);
}
TestObj& operator=(const TestObj& other)
{
printf("TestObj copy operator called: {%s,%d} <- {%s,%d}!!!\n",
to_string(state),
id,
to_string(other.state),
other.id);
id = other.id;
state = other.state;
copy_counter++;
return *this;
}
TestObj& operator=(TestObj&& other) noexcept
{
printf("TestObj move operator called: {%s,%d} <- {%s,%d}!!!\n",
to_string(state),
id,
to_string(other.state),
other.id);
if (&other != this) {
id = other.id;
state = other.state;
other.state = obj_state_t::from_move_assign;
move_counter++;
}
return *this;
}
obj_state_t state = obj_state_t::default_ctor;
int id = 0;
static int copy_counter;
static int move_counter;
static int dtor_counter;
};
int TestObj::copy_counter = 0;
int TestObj::move_counter = 0;
int TestObj::dtor_counter = 0;
void new_test()
{
TestObj::copy_counter = 0;
TestObj::move_counter = 0;
TestObj::dtor_counter = 0;
}
class custom_proc : public srslte::proc_impl_t
{
public:
proc_outcome_t init(int a_)
{
if (a_ < 0) {
printf("Failed to initiate custom_proc\n");
return proc_outcome_t::error;
}
obj.id = a_;
return proc_outcome_t::yield;
}
proc_outcome_t step() final
{
if (counter++ > 5) {
return proc_outcome_t::success;
}
return proc_outcome_t::yield;
}
void stop() final { printf("TestObj %d stop() was called\n", obj.id); }
const char* name() const { return "custom proc"; }
TestObj obj;
private:
int counter = 0;
};
int test_local_1()
{
new_test();
printf("\n--- Test %s ---\n", __func__);
srslte::proc_t<custom_proc> proc;
TESTASSERT(not proc.is_active());
proc.launch(1);
TESTASSERT(proc.is_active());
TESTASSERT(not proc.is_complete());
while (proc.run()) {
}
TESTASSERT(proc.is_active());
TESTASSERT(proc.is_complete());
printf("pop being called\n");
custom_proc procobj = proc.pop();
TESTASSERT(procobj.obj.id == 1);
TESTASSERT(procobj.is_success());
TESTASSERT(not proc.is_active());
TESTASSERT(proc.get()->obj.id == 0); // Proc is ready to be reused
printf("EXIT\n");
TESTASSERT(TestObj::copy_counter == 0);
TESTASSERT(TestObj::move_counter == 2); // pop() makes a swap which causes 2 moves
TESTASSERT(TestObj::dtor_counter == 2); // 2 dtors inside pop() (handler and popped obj not yet destructed)
return 0;
}
int test_callback_1()
{
new_test();
printf("\n--- Test %s ---\n", __func__);
srslte::callback_list_t callbacks;
srslte::proc_t<custom_proc> proc;
TESTASSERT(not proc.is_active());
proc.launch(2);
callbacks.add_proc(proc); // We have to call pop() explicitly to take the result
TESTASSERT(callbacks.size() == 1);
while (callbacks.size() > 0) {
TESTASSERT(proc.is_active());
TESTASSERT(not proc.is_complete());
callbacks.run();
}
TESTASSERT(proc.is_active());
TESTASSERT(proc.is_complete());
printf("pop being called\n");
custom_proc procobj = proc.pop();
TESTASSERT(procobj.is_success());
TESTASSERT(procobj.obj.id == 2);
TESTASSERT(not proc.is_active());
TESTASSERT(proc.get()->obj.id == 0); // Proc is ready to be reused
printf("EXIT\n");
TESTASSERT(TestObj::copy_counter == 0);
TESTASSERT(TestObj::move_counter == 2); // pop makes two moves
TESTASSERT(TestObj::dtor_counter == 2); // handler not yet destructed
return 0;
}
int test_callback_2()
{
new_test();
printf("\n--- Test %s ---\n", __func__);
srslte::callback_list_t callbacks;
srslte::proc_t<custom_proc> proc;
TESTASSERT(not proc.is_active());
proc.launch(3);
TESTASSERT(proc.is_active());
TESTASSERT(not proc.is_complete());
callbacks.consume_proc(std::move(proc));
TESTASSERT(callbacks.size() == 1);
while (callbacks.size() > 0) {
callbacks.run();
}
// since the proc was consumed, it is erased without the need for pop()
printf("EXIT\n");
TESTASSERT(TestObj::copy_counter == 0);
TESTASSERT(TestObj::move_counter == 0); // no pop()
TESTASSERT(TestObj::dtor_counter == 1); // handler not yet destructed
return 0;
}
int test_callback_3()
{
new_test();
printf("\n--- Test %s ---\n", __func__);
srslte::callback_list_t callbacks;
srslte::proc_t<custom_proc> proc;
TESTASSERT(not proc.is_active());
proc.launch(4);
TESTASSERT(proc.is_active());
TESTASSERT(not proc.is_complete());
callbacks.defer_proc(proc); // we still have access to proc, but we do not need to call pop()
TESTASSERT(callbacks.size() == 1);
TESTASSERT(proc.is_active());
TESTASSERT(not proc.is_complete());
while (callbacks.size() > 0) {
TESTASSERT(proc.is_active());
TESTASSERT(not proc.is_complete());
callbacks.run();
}
TESTASSERT(not proc.is_active());
TESTASSERT(not proc.is_complete());
TESTASSERT(proc.get()->obj.id == 0); // Proc is ready to be reused
printf("EXIT\n");
TESTASSERT(TestObj::copy_counter == 0);
TESTASSERT(TestObj::move_counter == 0);
TESTASSERT(TestObj::dtor_counter == 1); // handler not yet destructed
return 0;
}
int test_callback_4()
{
new_test();
printf("\n--- Test %s ---\n", __func__);
srslte::callback_list_t callbacks;
int* counter = new int(5);
{
callbacks.defer_task([counter]() {
printf("current counter=%d\n", *counter);
if (--(*counter) == 0) {
return proc_outcome_t::success;
}
return proc_outcome_t::yield;
});
}
while (callbacks.size() > 0) {
callbacks.run();
}
// printf("counter=%d\n", counter);
TESTASSERT(*counter == 0);
delete counter;
return 0;
}
int main()
{
TESTASSERT(test_local_1() == 0);
TESTASSERT(test_callback_1() == 0);
TESTASSERT(test_callback_2() == 0);
TESTASSERT(test_callback_3() == 0);
TESTASSERT(test_callback_4() == 0);
return 0;
}

@ -33,6 +33,7 @@
#include "srslte/common/common.h"
#include "srslte/common/log.h"
#include "srslte/common/security.h"
#include "srslte/common/stack_procedure.h"
#include "srslte/common/threads.h"
#include "srslte/interfaces/ue_interfaces.h"
@ -282,8 +283,7 @@ class rrc : public rrc_interface_nas,
public rrc_interface_mac,
public rrc_interface_pdcp,
public rrc_interface_rlc,
public srslte::timer_callback,
public thread
public srslte::timer_callback
{
public:
rrc(srslte::log* rrc_log_);
@ -319,10 +319,11 @@ public:
void enable_capabilities();
uint16_t get_mcc();
uint16_t get_mnc();
int plmn_search(found_plmn_t found_plmns[MAX_FOUND_PLMNS]);
bool plmn_search() final;
void plmn_select(srslte::plmn_id_t plmn_id);
bool connection_request(srslte::establishment_cause_t cause, srslte::unique_byte_buffer_t dedicated_info_nas);
void set_ue_identity(srslte::s_tmsi_t s_tmsi);
void paging_completed(bool outcome) final;
// PHY interface
void in_sync();
@ -349,14 +350,13 @@ public:
private:
typedef struct {
enum { PDU, PCCH, STOP, MBMS_START } command;
enum { PDU, PCCH, PDU_MCH, RLF, PDU_BCCH_DLSCH, STOP } command;
srslte::unique_byte_buffer_t pdu;
uint16_t lcid;
} cmd_msg_t;
bool running = false;
srslte::block_queue<cmd_msg_t> cmd_q;
void run_thread();
void process_pcch(srslte::unique_byte_buffer_t pdu);
@ -377,8 +377,6 @@ private:
srslte::bit_buffer_t bit_buf;
pthread_mutex_t mutex;
rrc_state_t state, last_state = RRC_STATE_IDLE;
uint8_t transaction_id = 0;
srslte::s_tmsi_t ue_identity;
@ -458,23 +456,12 @@ private:
std::vector<cell_t*>::iterator delete_neighbour(std::vector<cell_t*>::iterator it);
void delete_neighbour(uint32_t cell_idx);
bool configure_serving_cell();
bool si_acquire(uint32_t index);
uint32_t sib_start_tti(uint32_t tti, uint32_t period, uint32_t offset, uint32_t sf);
const static int SIB_SEARCH_TIMEOUT_MS = 1000;
bool initiated = false;
bool ho_start = false;
bool go_idle = false;
bool initiated = false;
asn1::rrc::reest_cause_e m_reest_cause = {};
uint16_t m_reest_rnti = 0;
bool reestablishment_started = false;
bool reestablishment_started = false;
bool reestablishment_successful = false;
uint32_t rlc_flush_counter = 0;
uint32_t rlc_flush_timeout = 0;
// Measurements sub-class
class rrc_meas {
public:
@ -602,20 +589,38 @@ private:
float get_srxlev(float Qrxlevmeas);
float get_squal(float Qqualmeas);
typedef enum {
CHANGED_CELL = 0,
SAME_CELL = 1,
NO_CELL = 2
} cs_ret_t;
cs_ret_t cell_selection();
bool cell_selection_criteria(float rsrp, float rsrq = 0);
void cell_reselection(float rsrp, float rsrq);
phy_interface_rrc_lte::cell_search_ret_t cell_search();
srslte::plmn_id_t selected_plmn_id = {};
bool plmn_is_selected = false;
/********************
* RRC Procedures
*******************/
enum class cs_result_t { changed_cell, same_cell, no_cell };
// RRC procedures (fwd declared)
class cell_search_proc;
class si_acquire_proc;
class serving_cell_config_proc;
class cell_selection_proc;
class connection_request_proc;
class plmn_search_proc;
class process_pcch_proc;
class go_idle_proc;
srslte::proc_t<cell_search_proc> cell_searcher;
srslte::proc_t<si_acquire_proc> si_acquirer;
srslte::proc_t<serving_cell_config_proc> serv_cell_cfg;
srslte::proc_t<cell_selection_proc> cell_selector;
srslte::proc_t<go_idle_proc> idle_setter;
srslte::proc_t<process_pcch_proc> pcch_processor;
srslte::proc_t<connection_request_proc> conn_req_proc;
srslte::proc_t<plmn_search_proc> plmn_searcher;
srslte::callback_list_t callback_list;
bool cell_selection_criteria(float rsrp, float rsrq = 0);
void cell_reselection(float rsrp, float rsrq);
std::vector<uint32_t> ue_required_sibs;
srslte::plmn_id_t selected_plmn_id = {};
bool plmn_is_selected = false;
bool security_is_activated = false;
@ -623,7 +628,7 @@ private:
void max_retx_attempted();
// Senders
void send_con_request(asn1::rrc::establishment_cause_e cause);
void send_con_request(srslte::establishment_cause_t cause);
void send_con_restablish_request();
void send_con_restablish_complete();
void send_con_setup_complete(srslte::unique_byte_buffer_t nas_msg);
@ -636,6 +641,8 @@ private:
void parse_dl_ccch(srslte::unique_byte_buffer_t pdu);
void parse_dl_dcch(uint32_t lcid, srslte::unique_byte_buffer_t pdu);
void parse_dl_info_transfer(uint32_t lcid, srslte::unique_byte_buffer_t pdu);
void parse_pdu_bcch_dlsch(srslte::unique_byte_buffer_t pdu);
void parse_pdu_mch(uint32_t lcid, srslte::unique_byte_buffer_t pdu);
// Helpers
bool con_reconfig(asn1::rrc::rrc_conn_recfg_s* reconfig);
@ -643,12 +650,15 @@ private:
bool con_reconfig_ho(asn1::rrc::rrc_conn_recfg_s* reconfig);
bool ho_prepare();
void ho_failed();
void start_ho();
void start_go_idle();
void rrc_connection_release();
void radio_link_failure();
void leave_connected();
void stop_timers();
void init_con_restablish_request(asn1::rrc::reest_cause_e cause);
void proc_con_restablish_request();
void start_cell_reselection();
void log_rr_config_common();
void log_phy_config_dedicated();

@ -0,0 +1,198 @@
/*
* 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 "srslte/common/log.h"
#include "srsue/hdr/stack/rrc/rrc.h"
#include <map>
#include <string>
#ifndef SRSLTE_RRC_PROCEDURES_H
#define SRSLTE_RRC_PROCEDURES_H
namespace srsue {
class rrc::cell_search_proc : public srslte::proc_impl_t
{
public:
enum class state_t { phy_cell_search, si_acquire };
srslte::proc_outcome_t init(rrc* parent_);
srslte::proc_outcome_t step() final;
phy_interface_rrc_lte::cell_search_ret_t get_cs_ret() { return cs_ret; }
static const char* name() { return "Cell Search"; }
private:
srslte::proc_outcome_t handle_cell_found(const phy_interface_rrc_lte::phy_cell_t& new_cell);
// conts
rrc* rrc_ptr;
srslte::log* log_h;
// state vars
phy_interface_rrc_lte::cell_search_ret_t cs_ret;
state_t state;
};
class rrc::si_acquire_proc : public srslte::proc_impl_t
{
public:
const static int SIB_SEARCH_TIMEOUT_MS = 1000;
srslte::proc_outcome_t init(rrc* parent_, uint32_t sib_index_);
srslte::proc_outcome_t step() final;
static const char* name() { return "SI Acquire"; }
private:
static uint32_t sib_start_tti(uint32_t tti, uint32_t period, uint32_t offset, uint32_t sf);
// conts
rrc* rrc_ptr;
srslte::log* log_h;
// state
uint32_t period, sched_index;
uint32_t start_tti = 0;
uint32_t sib_index = 0;
uint32_t last_win_start = 0;
};
class rrc::serving_cell_config_proc : public srslte::proc_impl_t
{
public:
srslte::proc_outcome_t init(rrc* parent_, const std::vector<uint32_t>& required_sibs_);
srslte::proc_outcome_t step() final;
static const char* name() { return "Serving Cell Configuration"; }
private:
// consts
std::vector<uint32_t> required_sibs;
rrc* rrc_ptr;
srslte::log* log_h;
// state variables
enum class search_state_t { next_sib, si_acquire } search_state;
uint32_t req_idx = 0;
};
class rrc::cell_selection_proc : public srslte::proc_impl_t
{
public:
srslte::proc_outcome_t init(rrc* parent_);
srslte::proc_outcome_t step() final;
cs_result_t get_cs_result() { return cs_result; }
static const char* name() { return "Cell Selection"; }
private:
srslte::proc_outcome_t step_cell_selection();
srslte::proc_outcome_t step_cell_search();
srslte::proc_outcome_t step_cell_config();
// consts
rrc* rrc_ptr;
srslte::log* log_h;
// state variables
enum class search_state_t { cell_selection, cell_config, cell_search };
cs_result_t cs_result;
search_state_t state;
uint32_t neigh_index;
};
class rrc::plmn_search_proc : public srslte::proc_impl_t
{
public:
srslte::proc_outcome_t init(rrc* parent_);
srslte::proc_outcome_t step() final;
void stop() final;
static const char* name() { return "PLMN Search"; }
private:
// consts
rrc* rrc_ptr;
srslte::log* log_h;
// state variables
found_plmn_t found_plmns[MAX_FOUND_PLMNS];
int nof_plmns;
};
class rrc::connection_request_proc : public srslte::proc_impl_t
{
public:
srslte::proc_outcome_t
init(rrc* parent_, srslte::establishment_cause_t cause_, srslte::unique_byte_buffer_t dedicated_info_nas_);
srslte::proc_outcome_t step() final;
void stop() final;
static const char* name() { return "Connection Request"; }
private:
// args
rrc* rrc_ptr;
srslte::log* log_h;
srslte::establishment_cause_t cause;
srslte::unique_byte_buffer_t dedicated_info_nas;
// state variables
enum class state_t { cell_selection, config_serving_cell, wait_t300 } state;
cs_result_t cs_ret;
};
class rrc::process_pcch_proc : public srslte::proc_impl_t
{
public:
struct paging_complete {
bool outcome;
};
srslte::proc_outcome_t init(rrc* parent_, const asn1::rrc::paging_s& paging_);
srslte::proc_outcome_t step() final;
srslte::proc_outcome_t trigger_event(paging_complete e);
static const char* name() { return "Process PCCH"; }
private:
// args
rrc* rrc_ptr;
srslte::log* log_h;
asn1::rrc::paging_s paging;
// vars
uint32_t paging_idx = 0;
enum class state_t { next_record, nas_paging, serv_cell_cfg } state;
};
class rrc::go_idle_proc : public srslte::proc_impl_t
{
public:
srslte::proc_outcome_t init(rrc* rrc_);
srslte::proc_outcome_t step() final;
static const char* name() { return "Go Idle"; }
private:
rrc* rrc_ptr;
static const uint32_t rlc_flush_timeout = 2000;
uint32_t rlc_flush_counter;
};
} // namespace srsue
#endif // SRSLTE_RRC_PROCEDURES_H

@ -28,6 +28,7 @@
#include "srslte/common/log.h"
#include "srslte/common/nas_pcap.h"
#include "srslte/common/security.h"
#include "srslte/common/stack_procedure.h"
#include "srslte/interfaces/ue_interfaces.h"
#include "srsue/hdr/stack/upper/nas_common.h"
#include "srsue/hdr/stack/upper/nas_metrics.h"
@ -36,15 +37,13 @@ using srslte::byte_buffer_t;
namespace srsue {
class nas
: public nas_interface_rrc,
public nas_interface_ue,
public nas_interface_gw
class nas : public nas_interface_rrc, public nas_interface_ue
{
public:
nas(srslte::log* log_);
void init(usim_interface_nas* usim_, rrc_interface_nas* rrc_, gw_interface_nas* gw_, const nas_args_t& args_);
void stop();
void run_tti(uint32_t tti) final;
void get_metrics(nas_metrics_t* m);
emm_state_t get_state();
@ -61,8 +60,14 @@ public:
bool get_ipv6_addr(uint8_t* ipv6_addr);
// UE interface
bool attach_request();
bool detach_request();
void start_attach_request(srslte::proc_state_t* result) final;
bool detach_request() final;
void plmn_search_completed(rrc_interface_nas::found_plmn_t found_plmns[rrc_interface_nas::MAX_FOUND_PLMNS],
int nof_plmns) final;
bool start_connection_request(srslte::establishment_cause_t establish_cause,
srslte::unique_byte_buffer_t ded_info_nas);
bool connection_request_completed(bool outcome) final;
// PCAP
void start_pcap(srslte::nas_pcap *pcap_);
@ -135,8 +140,6 @@ private:
bool running = false;
bool rrc_connect();
void integrity_generate(uint8_t *key_128,
uint32_t count,
uint8_t direction,
@ -240,6 +243,50 @@ private:
}
return list;
}
class rrc_connect_proc : public srslte::proc_impl_t
{
public:
struct connection_request_completed_t {
bool outcome;
};
srslte::proc_outcome_t init(nas* nas_ptr_);
srslte::proc_outcome_t step() final;
static const char* name() { return "RRC Connect"; }
private:
nas* nas_ptr;
enum class state_t { conn_req, wait_attach } state;
uint32_t wait_timeout;
};
class plmn_search_proc : public srslte::proc_impl_t
{
public:
struct plmn_search_complete_t {
rrc_interface_nas::found_plmn_t found_plmns[rrc_interface_nas::MAX_FOUND_PLMNS];
int nof_plmns;
plmn_search_complete_t(rrc_interface_nas::found_plmn_t* plmns_, int nof_plmns_) : nof_plmns(nof_plmns_)
{
if (nof_plmns > 0) {
std::copy(&plmns_[0], &plmns_[nof_plmns], found_plmns);
}
}
};
srslte::proc_outcome_t init(nas* nas_ptr_);
srslte::proc_outcome_t step() final;
srslte::proc_outcome_t trigger_event(const plmn_search_complete_t& t);
static const char* name() { return "PLMN Search"; }
private:
nas* nas_ptr;
enum class state_t { plmn_search, rrc_connect } state;
};
srslte::callback_list_t callbacks;
srslte::proc_t<plmn_search_proc> plmn_searcher;
srslte::proc_t<rrc_connect_proc> rrc_connector;
srslte::proc_t<srslte::query_proc_t<bool> > conn_req_proc;
};
} // namespace srsue

@ -18,9 +18,8 @@
# and at http://www.gnu.org/licenses/.
#
set(SOURCES rrc.cc)
set(SOURCES rrc.cc rrc_procedures.cc)
add_library(srsue_rrc STATIC ${SOURCES})
install(TARGETS srsue_rrc DESTINATION ${LIBRARY_DIR})

File diff suppressed because it is too large Load Diff

@ -0,0 +1,795 @@
/*
* 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/rrc/rrc_procedures.h"
#include <inttypes.h> // for printing uint64_t
#define Error(fmt, ...) rrc_ptr->rrc_log->error("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__)
#define Warning(fmt, ...) rrc_ptr->rrc_log->warning("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__)
#define Info(fmt, ...) rrc_ptr->rrc_log->info("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__)
#define Debug(fmt, ...) rrc_ptr->rrc_log->debug("Proc \"%s\" - " fmt, name(), ##__VA_ARGS__)
namespace srsue {
using srslte::proc_outcome_t;
/**************************************
* Cell Search Procedure
*************************************/
/* Searches for a cell in the current frequency and retrieves SIB1 if not retrieved yet */
proc_outcome_t rrc::cell_search_proc::init(srsue::rrc* parent_)
{
rrc_ptr = parent_;
log_h = parent_->rrc_log;
Info("Starting...\n");
state = state_t::phy_cell_search;
return proc_outcome_t::repeat;
}
/* Implements the SI acquisition procedure. Configures MAC/PHY scheduling to retrieve SI messages.*/
proc_outcome_t rrc::cell_search_proc::step()
{
if (state == state_t::phy_cell_search) {
// Blocks waiting for result of phy cell search
phy_interface_rrc_lte::phy_cell_t new_cell;
// BLOCKING CALL
cs_ret = rrc_ptr->phy->cell_search(&new_cell);
switch (cs_ret.found) {
case phy_interface_rrc_lte::cell_search_ret_t::CELL_FOUND: {
return handle_cell_found(new_cell);
}
case phy_interface_rrc_lte::cell_search_ret_t::CELL_NOT_FOUND:
Info("No cells found.\n");
// do nothing
return proc_outcome_t::success;
case phy_interface_rrc_lte::cell_search_ret_t::ERROR:
Error("Error while performing cell search\n");
// TODO: check what errors can happen (currently not handled in our code)
return proc_outcome_t::error;
}
} else if (state == state_t::si_acquire) {
if (not rrc_ptr->si_acquirer.run()) {
// SI Acquire has completed
si_acquire_proc ret = rrc_ptr->si_acquirer.pop();
if (ret.is_error()) {
Error("Failed to trigger SI acquire for SIB0\n");
return proc_outcome_t::error;
} else {
// if(parent->serving_cell->has_sib(0)) {
// }
return proc_outcome_t::success;
}
}
}
return proc_outcome_t::yield;
}
proc_outcome_t rrc::cell_search_proc::handle_cell_found(const phy_interface_rrc_lte::phy_cell_t& new_cell)
{
Info("Cell found in this frequency. Setting new serving cell...\n");
// Create a cell with NaN RSRP. Will be updated by new_phy_meas() during SIB search.
if (not rrc_ptr->add_neighbour_cell(new_cell, NAN)) {
Info("No more space for neighbour cells\n");
return proc_outcome_t::success;
}
rrc_ptr->set_serving_cell(new_cell);
if (not rrc_ptr->phy->cell_is_camping()) {
log_h->warning("Could not camp on found cell.\n");
return proc_outcome_t::error;
}
if (rrc_ptr->serving_cell->has_sib1()) {
Info("Cell has SIB1\n");
// What do we do????
return proc_outcome_t::success;
}
if (not rrc_ptr->si_acquirer.launch(rrc_ptr, 0)) {
// disallow concurrent si_acquire
Error("SI Acquire is already running...\n");
return proc_outcome_t::error;
}
// instruct MAC to look for SIB1
Info("Cell has no SIB1. Obtaining SIB1...\n");
state = state_t::si_acquire;
return proc_outcome_t::repeat;
}
/**************************************
* SI Acquire Procedure
*************************************/
proc_outcome_t rrc::si_acquire_proc::init(rrc* parent_, uint32_t sib_index_)
{
rrc_ptr = parent_;
log_h = parent_->rrc_log;
Info("Starting SI Acquire procedure for SIB%d\n", sib_index_ + 1);
sib_index = sib_index_;
start_tti = rrc_ptr->mac->get_current_tti();
// set period/sched_index
if (sib_index == 0) {
period = 20;
sched_index = 0;
} else {
// Instruct MAC to look for SIB2..13
if (not rrc_ptr->serving_cell->has_sib1()) {
Error("Trying to acquire SIB%d but SIB1 not received yet\n", sib_index + 1);
return proc_outcome_t::error;
}
asn1::rrc::sib_type1_s* sib1 = rrc_ptr->serving_cell->sib1ptr();
if (sib_index == 1) {
// SIB2 scheduling
period = sib1->sched_info_list[0].si_periodicity.to_number();
sched_index = 0;
} else {
// SIB3+ scheduling Section 5.2.3
bool found = false;
for (uint32_t i = 0; i < sib1->sched_info_list.size() && not found; ++i) {
for (uint32_t j = 0; j < sib1->sched_info_list[i].sib_map_info.size() && not found; ++j) {
if (sib1->sched_info_list[i].sib_map_info[j].to_number() == sib_index + 1) {
period = sib1->sched_info_list[i].si_periodicity.to_number();
sched_index = i;
found = true;
}
}
}
if (not found) {
Info("Could not find SIB%d scheduling in SIB1\n", sib_index + 1);
return proc_outcome_t::success;
}
}
}
return proc_outcome_t::repeat;
}
proc_outcome_t rrc::si_acquire_proc::step()
{
uint32_t tti = rrc_ptr->mac->get_current_tti();
uint32_t tti_diff1 = srslte_tti_interval(tti, start_tti);
bool has_timeout = tti_diff1 >= SIB_SEARCH_TIMEOUT_MS and tti_diff1 < 10240 / 2;
if (has_timeout or rrc_ptr->serving_cell->has_sib(sib_index)) {
if (rrc_ptr->serving_cell->has_sib(sib_index)) {
Info("SIB%d acquired successfully\n", sib_index + 1);
return proc_outcome_t::success;
} else {
Error("Timeout while acquiring SIB1\n");
return proc_outcome_t::error;
}
} else {
uint32_t tti_diff2 = srslte_tti_interval(tti, last_win_start);
// If first time or inside retry window, trigger MAC to decode SIB
uint32_t retry_period = (sib_index == 0) ? 20 : period * 5;
if (last_win_start == 0 or (tti_diff2 > retry_period and tti_diff2 < 1000)) {
// compute win start and len
uint32_t si_win_start, si_win_len;
if (sib_index == 0) {
si_win_len = 1;
si_win_start = sib_start_tti(tti, 2, 0, 5);
} else {
asn1::rrc::sib_type1_s* sib1 = rrc_ptr->serving_cell->sib1ptr();
si_win_len = sib1->si_win_len.to_number();
uint32_t x = sched_index * si_win_len;
uint32_t sf = x % 10, offset = x / 10;
si_win_start = sib_start_tti(tti, period, offset, sf);
}
last_win_start = si_win_start;
// Instruct MAC to decode SIB
rrc_ptr->mac->bcch_start_rx(si_win_start, si_win_len);
Info("Instructed MAC to search for SIB%d, win_start=%d, win_len=%d, period=%d, sched_index=%d\n",
sib_index + 1,
si_win_start,
si_win_len,
period,
sched_index);
}
}
return proc_outcome_t::yield;
}
// Determine SI messages scheduling as in 36.331 5.2.3 Acquisition of an SI message
uint32_t rrc::si_acquire_proc::sib_start_tti(uint32_t tti, uint32_t period, uint32_t offset, uint32_t sf)
{
return (period * 10 * (1 + tti / (period * 10)) + (offset * 10) + sf) % 10240; // the 1 means next opportunity
}
/**************************************
* Serving Cell Config Procedure
*************************************/
/*
* Retrieves all required SIB or configures them if already retrieved before
*/
proc_outcome_t rrc::serving_cell_config_proc::init(srsue::rrc* parent_, const std::vector<uint32_t>& required_sibs_)
{
rrc_ptr = parent_;
log_h = parent_->rrc_log;
required_sibs = required_sibs_;
Info("Starting a Serving Cell Configuration Procedure\n");
if (not rrc_ptr->phy->cell_is_camping()) {
Error("Trying to configure Cell while not camping on it\n");
return proc_outcome_t::error;
}
rrc_ptr->serving_cell->has_mcch = false;
req_idx = 0;
search_state = search_state_t::next_sib;
return proc_outcome_t::repeat;
}
proc_outcome_t rrc::serving_cell_config_proc::step()
{
if (search_state == search_state_t::next_sib) {
// Obtain the SIBs if not available or apply the configuration if available
for (; req_idx < required_sibs.size(); req_idx++) {
uint32_t required_sib = required_sibs[req_idx];
if (not rrc_ptr->serving_cell->has_sib(required_sib)) {
Info("Cell has no SIB%d. Obtaining SIB%d\n", required_sib + 1, required_sib + 1);
if (not rrc_ptr->si_acquirer.launch(rrc_ptr, required_sib)) {
Error("SI Acquire is already running...\n");
return proc_outcome_t::error;
}
search_state = search_state_t::si_acquire;
return proc_outcome_t::repeat;
} else {
// UE had SIB already. Handle its SIB
Info("Cell has SIB%d\n", required_sib + 1);
switch (required_sib) {
case 1:
rrc_ptr->handle_sib2();
break;
case 12:
rrc_ptr->handle_sib13();
break;
default:
break;
}
}
}
if (req_idx == required_sibs.size()) {
Info("Serving Cell Configuration Procedure has finished successfully\n");
return proc_outcome_t::success;
}
} else if (search_state == search_state_t::si_acquire) {
uint32_t required_sib = required_sibs[req_idx];
if (not rrc_ptr->si_acquirer.run()) {
si_acquire_proc ret = rrc_ptr->si_acquirer.pop();
if (ret.is_error() or not rrc_ptr->serving_cell->has_sib(required_sib)) {
if (required_sib < 2) {
log_h->warning("Serving Cell Configuration has failed\n");
return proc_outcome_t::error;
}
}
// continue with remaining SIBs
search_state = search_state_t::next_sib;
req_idx++;
return proc_outcome_t::repeat;
}
}
return proc_outcome_t::yield;
}
/**************************************
* Cell Selection Procedure
*************************************/
/*
* Cell selection procedure 36.304 5.2.3
* Select the best cell to camp on among the list of known cells
*/
proc_outcome_t rrc::cell_selection_proc::init(srsue::rrc* parent_)
{
rrc_ptr = parent_;
log_h = parent_->rrc_log;
if (rrc_ptr->neighbour_cells.empty() and rrc_ptr->serving_cell->in_sync and rrc_ptr->phy->cell_is_camping()) {
// don't bother with cell selection if there are no neighbours and we are already camping
return proc_outcome_t::success;
}
Info("Starting a Cell Selection Procedure...\n");
neigh_index = 0;
cs_result = cs_result_t::no_cell;
state = search_state_t::cell_selection;
return proc_outcome_t::repeat;
}
proc_outcome_t rrc::cell_selection_proc::step_cell_selection()
{
// Neighbour cells are sorted in descending order of RSRP
for (; neigh_index < rrc_ptr->neighbour_cells.size(); ++neigh_index) {
/*TODO: CHECK that PLMN matches. Currently we don't receive SIB1 of neighbour cells
* neighbour_cells[i]->plmn_equals(selected_plmn_id) && */
if (rrc_ptr->neighbour_cells[neigh_index]->in_sync) {
// Matches S criteria
float rsrp = rrc_ptr->neighbour_cells[neigh_index]->get_rsrp();
cell_t* serv_cell = rrc_ptr->serving_cell;
if (not serv_cell->in_sync or
(rrc_ptr->cell_selection_criteria(rsrp) and rsrp > rrc_ptr->serving_cell->get_rsrp() + 5)) {
// currently connected and verifies cell selection criteria
// Try to select Cell
rrc_ptr->set_serving_cell(neigh_index);
Info("Selected cell idx=%d, PCI=%d, EARFCN=%d\n", neigh_index, serv_cell->get_pci(), serv_cell->get_earfcn());
log_h->console("Selected cell PCI=%d, EARFCN=%d\n", serv_cell->get_pci(), serv_cell->get_earfcn());
/* BLOCKING CALL */
if (rrc_ptr->phy->cell_select(&serv_cell->phy_cell)) {
if (not rrc_ptr->serv_cell_cfg.launch(rrc_ptr, rrc_ptr->ue_required_sibs)) {
return proc_outcome_t::error;
}
state = search_state_t::cell_config;
return proc_outcome_t::repeat;
} else {
serv_cell->in_sync = false;
Error("Could not camp on serving cell.\n");
// Continue to next neighbour cell
}
}
}
}
if (rrc_ptr->serving_cell->in_sync) {
if (not rrc_ptr->phy->cell_is_camping()) {
Info("Serving cell is in-sync but not camping. Selecting it...\n");
/* BLOCKING CALL */
if (rrc_ptr->phy->cell_select(&rrc_ptr->serving_cell->phy_cell)) {
Info("Selected serving cell OK.\n");
} else {
rrc_ptr->serving_cell->in_sync = false;
Error("Could not camp on serving cell.\n");
}
}
cs_result = cs_result_t::same_cell;
return proc_outcome_t::success;
}
// If can not find any suitable cell, search again
Info("Cell selection and reselection in IDLE did not find any suitable cell. Searching again\n");
if (not rrc_ptr->cell_searcher.launch(rrc_ptr)) {
return proc_outcome_t::error;
}
state = search_state_t::cell_search;
return proc_outcome_t::repeat;
}
proc_outcome_t rrc::cell_selection_proc::step_cell_search()
{
if (rrc_ptr->cell_searcher.run()) {
return proc_outcome_t::yield;
}
cell_search_proc ret = rrc_ptr->cell_searcher.pop();
if (ret.is_error()) {
cs_result = cs_result_t::no_cell;
return proc_outcome_t::error;
} else {
cs_result = (ret.get_cs_ret().found == phy_interface_rrc_lte::cell_search_ret_t::CELL_FOUND)
? cs_result_t::changed_cell
: cs_result_t::no_cell;
Info("Cell Search of cell selection run successfully\n");
return proc_outcome_t::success;
}
}
proc_outcome_t rrc::cell_selection_proc::step_cell_config()
{
if (rrc_ptr->serv_cell_cfg.run()) {
return proc_outcome_t::yield;
}
serving_cell_config_proc ret = rrc_ptr->serv_cell_cfg.pop();
if (ret.is_success()) {
Info("All SIBs of serving cell obtained successfully\n");
cs_result = cs_result_t::changed_cell;
return proc_outcome_t::success;
} else {
Error("While configuring serving cell\n");
// resume cell selection
state = search_state_t::cell_selection;
++neigh_index;
return proc_outcome_t::repeat;
}
}
proc_outcome_t rrc::cell_selection_proc::step()
{
switch (state) {
case search_state_t::cell_selection:
return step_cell_selection();
case search_state_t::cell_config:
return step_cell_config();
case search_state_t::cell_search:
return step_cell_search();
}
return proc_outcome_t::error;
}
/**************************************
* PLMN search Procedure
*************************************/
proc_outcome_t rrc::plmn_search_proc::init(srsue::rrc* parent_)
{
rrc_ptr = parent_;
log_h = parent_->rrc_log;
Info("Starting PLMN search\n");
nof_plmns = 0;
if (not rrc_ptr->cell_searcher.launch(rrc_ptr)) {
Error("Failed due to fail to init cell search...\n");
return proc_outcome_t::error;
}
return proc_outcome_t::repeat;
}
/* 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.
*/
proc_outcome_t rrc::plmn_search_proc::step()
{
if (rrc_ptr->cell_searcher.run()) {
// wait for new TTI
return proc_outcome_t::yield;
}
cell_search_proc ret = rrc_ptr->cell_searcher.pop();
phy_interface_rrc_lte::cell_search_ret_t cs_ret = ret.get_cs_ret();
if (ret.is_error() or cs_ret.found == phy_interface_rrc_lte::cell_search_ret_t::ERROR) {
// stop search
nof_plmns = -1;
Error("Failed due to failed cell search sub-procedure\n");
return proc_outcome_t::error;
}
if (cs_ret.found == phy_interface_rrc_lte::cell_search_ret_t::CELL_FOUND) {
if (rrc_ptr->serving_cell->has_sib1()) {
// Save PLMN and TAC to NAS
for (uint32_t i = 0; i < rrc_ptr->serving_cell->nof_plmns(); i++) {
if (nof_plmns < MAX_FOUND_PLMNS) {
found_plmns[nof_plmns].plmn_id = rrc_ptr->serving_cell->get_plmn(i);
found_plmns[nof_plmns].tac = rrc_ptr->serving_cell->get_tac();
nof_plmns++;
} else {
Error("No more space for plmns (%d)\n", nof_plmns);
}
}
} else {
Error("SIB1 not acquired\n");
}
}
if (cs_ret.last_freq == phy_interface_rrc_lte::cell_search_ret_t::NO_MORE_FREQS) {
Info("completed PLMN search\n");
return proc_outcome_t::success;
}
if (not rrc_ptr->cell_searcher.launch(rrc_ptr)) {
Error("Failed due to fail to init cell search...\n");
return proc_outcome_t::error;
}
// run again
return proc_outcome_t::repeat;
}
void rrc::plmn_search_proc::stop()
{
// on cleanup, call plmn_search_completed
if (is_success()) {
Info("completed with success\n");
rrc_ptr->nas->plmn_search_completed(found_plmns, nof_plmns);
} else if (is_error()) {
Error("PLMN Search completed with an error\n");
rrc_ptr->nas->plmn_search_completed(nullptr, -1);
}
}
/**************************************
* Connection Request Procedure
*************************************/
proc_outcome_t rrc::connection_request_proc::init(rrc* parent_,
srslte::establishment_cause_t cause_,
srslte::unique_byte_buffer_t dedicated_info_nas_)
{
rrc_ptr = parent_;
log_h = parent_->rrc_log;
cause = cause_;
dedicated_info_nas = std::move(dedicated_info_nas_);
if (!rrc_ptr->plmn_is_selected) {
Error("Trying to connect but PLMN not selected.\n");
return proc_outcome_t::error;
}
if (rrc_ptr->state != RRC_STATE_IDLE) {
log_h->warning("Requested RRC connection establishment while not in IDLE\n");
return proc_outcome_t::error;
}
if (rrc_ptr->mac_timers->timer_get(rrc_ptr->t302)->is_running()) {
Info("Requested RRC connection establishment while T302 is running\n");
rrc_ptr->nas->set_barring(nas_interface_rrc::BARRING_MO_DATA);
return proc_outcome_t::error;
}
Info("Initiation of Connection establishment procedure\n");
cs_ret = cs_result_t::no_cell;
if (not rrc_ptr->cell_selector.launch(rrc_ptr)) {
Error("Failed to initiate cell selection procedure...\n");
return proc_outcome_t::error;
}
state = state_t::cell_selection;
return proc_outcome_t::repeat;
}
proc_outcome_t rrc::connection_request_proc::step()
{
if (state == state_t::cell_selection) {
// Perform cell selection & reselection for the selected PLMN
if (rrc_ptr->cell_selector.run()) {
return proc_outcome_t::yield;
}
cell_selection_proc result = rrc_ptr->cell_selector.pop();
if (result.is_error()) {
return proc_outcome_t::error;
}
cs_ret = result.get_cs_result();
// .. and SI acquisition
if (rrc_ptr->phy->cell_is_camping()) {
// Set default configurations
rrc_ptr->set_phy_default();
rrc_ptr->set_mac_default();
// CCCH configuration applied already at start
// timeAlignmentCommon applied in configure_serving_cell
Info("Configuring serving cell...\n");
if (not rrc_ptr->serv_cell_cfg.launch(rrc_ptr, rrc_ptr->ue_required_sibs)) {
Error("Attach request failed to configure serving cell...\n");
return proc_outcome_t::error;
}
state = state_t::config_serving_cell;
return proc_outcome_t::repeat;
} else {
switch (cs_ret) {
case cs_result_t::same_cell:
log_h->warning("Did not reselect cell but serving cell is out-of-sync.\n");
rrc_ptr->serving_cell->in_sync = false;
break;
case cs_result_t::changed_cell:
log_h->warning("Selected a new cell but could not camp on. Setting out-of-sync.\n");
rrc_ptr->serving_cell->in_sync = false;
break;
default:
log_h->warning("Could not find any suitable cell to connect\n");
}
return proc_outcome_t::error;
}
} else if (state == state_t::config_serving_cell) {
if (rrc_ptr->serv_cell_cfg.run()) {
return proc_outcome_t::yield;
}
serving_cell_config_proc ret = rrc_ptr->serv_cell_cfg.pop();
if (ret.is_error()) {
Error("Configuring serving cell\n");
return proc_outcome_t::error;
}
rrc_ptr->mac_timers->timer_get(rrc_ptr->t300)->reset();
rrc_ptr->mac_timers->timer_get(rrc_ptr->t300)->run();
// Send connectionRequest message to lower layers
rrc_ptr->send_con_request(cause);
// Save dedicatedInfoNAS SDU
if (rrc_ptr->dedicated_info_nas.get()) {
log_h->warning("Received a new dedicatedInfoNAS SDU but there was one still in queue. Removing it\n");
}
Info("Waiting for RRCConnectionSetup/Reject or expiry\n");
rrc_ptr->dedicated_info_nas = std::move(dedicated_info_nas);
state = state_t::wait_t300;
return proc_outcome_t::repeat;
} else if (state == state_t::wait_t300) {
// Wait until t300 stops due to RRCConnectionSetup/Reject or expiry
if (rrc_ptr->mac_timers->timer_get(rrc_ptr->t300)->is_running()) {
return proc_outcome_t::yield;
}
if (rrc_ptr->state == RRC_STATE_CONNECTED) {
// Received ConnectionSetup
return proc_outcome_t::success;
} else if (rrc_ptr->mac_timers->timer_get(rrc_ptr->t300)->is_expired()) {
// T300 is expired: 5.3.3.6
Info("Timer T300 expired: ConnectionRequest timed out\n");
rrc_ptr->mac->reset();
rrc_ptr->set_mac_default();
rrc_ptr->rlc->reestablish();
} else {
// T300 is stopped but RRC not Connected is because received Reject: Section 5.3.3.8
Info("Timer T300 stopped: Received ConnectionReject\n");
rrc_ptr->mac->reset();
rrc_ptr->set_mac_default();
}
}
return proc_outcome_t::error;
}
void rrc::connection_request_proc::stop()
{
if (is_error()) {
log_h->warning("Could not establish connection. Deallocating dedicatedInfoNAS PDU\n");
this->dedicated_info_nas.reset();
rrc_ptr->nas->connection_request_completed(false);
} else if (is_success()) {
Info("Finished connection request procedure successfully.\n");
rrc_ptr->nas->connection_request_completed(true);
}
}
/**************************************
* Process PCCH procedure
*************************************/
proc_outcome_t rrc::process_pcch_proc::init(rrc* parent_, const asn1::rrc::paging_s& paging_)
{
rrc_ptr = parent_;
log_h = parent_->rrc_log;
paging = paging_;
paging_idx = 0;
state = state_t::next_record;
Info("starting...\n");
return proc_outcome_t::repeat;
}
proc_outcome_t rrc::process_pcch_proc::step()
{
if (state == state_t::next_record) {
for (; paging_idx < paging.paging_record_list.size(); ++paging_idx) {
srslte::s_tmsi_t s_tmsi_paged = srslte::make_s_tmsi_t(paging.paging_record_list[paging_idx].ue_id.s_tmsi());
Info("Received paging (%d/%d) for UE %" PRIu64 ":%" PRIu64 "\n",
paging_idx + 1,
paging.paging_record_list.size(),
paging.paging_record_list[paging_idx].ue_id.s_tmsi().mmec.to_number(),
paging.paging_record_list[paging_idx].ue_id.s_tmsi().m_tmsi.to_number());
if (rrc_ptr->ue_identity == s_tmsi_paged) {
if (RRC_STATE_IDLE == rrc_ptr->state) {
Info("S-TMS->I match in paging message\n");
log_h->console("S-TMSI match in paging message\n");
rrc_ptr->nas->paging(&s_tmsi_paged);
state = state_t::nas_paging;
return proc_outcome_t::repeat;
} else {
log_h->warning("Received paging while in CONNECT\n");
}
} else {
Info("Received paging for unknown identity\n");
}
}
if (paging.sys_info_mod_present) {
Info("Received System Information notification update request.\n");
// invalidate and then update all SIBs of serving cell
rrc_ptr->serving_cell->reset_sibs();
// create a serving cell config procedure and push it to callback list
if (not rrc_ptr->serv_cell_cfg.launch(rrc_ptr, rrc_ptr->ue_required_sibs)) {
Error("Failed to initiate a serving cell configuration procedure\n");
return proc_outcome_t::error;
}
} else {
Info("Completed successfully\n");
return proc_outcome_t::success;
}
state = state_t::serv_cell_cfg;
return proc_outcome_t::repeat;
} else if (state == state_t::nas_paging) {
// wait for trigger
return proc_outcome_t::yield;
} else if (state == state_t::serv_cell_cfg) {
if (rrc_ptr->serv_cell_cfg.run()) {
return proc_outcome_t::yield;
}
serving_cell_config_proc ret = rrc_ptr->serv_cell_cfg.pop();
if (ret.is_success()) {
Info("All SIBs of serving cell obtained successfully\n");
return proc_outcome_t::success;
} else {
Error("While obtaining SIBs of serving cell\n");
return proc_outcome_t::error;
}
}
return proc_outcome_t::yield;
}
proc_outcome_t rrc::process_pcch_proc::trigger_event(paging_complete e)
{
if (not e.outcome) {
Info("NAS Paging has failed\n");
return proc_outcome_t::error;
}
paging_idx++;
state = state_t::next_record;
Info("Received paging complete event\n");
return proc_outcome_t::repeat;
}
/**************************************
* Go Idle procedure
*************************************/
proc_outcome_t rrc::go_idle_proc::init(rrc* rrc_)
{
rrc_ptr = rrc_;
rlc_flush_counter = 0;
Info("Starting...\n");
return proc_outcome_t::yield;
}
proc_outcome_t rrc::go_idle_proc::step()
{
if (rrc_ptr->state == RRC_STATE_IDLE) {
Info("Interrupting as RRC has already became IDLE\n");
return proc_outcome_t::success;
}
// wait for max. 2s for RLC on SRB1 to be flushed
if (not rrc_ptr->rlc->has_data(RB_ID_SRB1) || ++rlc_flush_counter > rlc_flush_timeout) {
rrc_ptr->leave_connected();
return proc_outcome_t::success;
} else {
rrc_ptr->rrc_log->debug(
"Postponing transition to RRC IDLE (%d ms < %d ms)\n", rlc_flush_counter, rlc_flush_timeout);
}
return proc_outcome_t::yield;
}
} // namespace srsue

@ -157,7 +157,12 @@ void ue_stack_lte::stop_impl()
bool ue_stack_lte::switch_on()
{
if (running) {
return nas.attach_request();
proc_state_t proc_result = proc_state_t::on_going;
pending_tasks.push([this, &proc_result]() { nas.start_attach_request(&proc_result); });
while (proc_result == proc_state_t::on_going) {
usleep(1000);
}
return proc_result == proc_state_t::success;
}
return false;
@ -221,6 +226,7 @@ void ue_stack_lte::run_tti_impl(uint32_t tti)
{
mac.run_tti(tti);
rrc.run_tti(tti);
nas.run_tti(tti);
}
} // namespace srsue

@ -36,8 +36,176 @@
using namespace srslte;
#define Error(fmt, ...) nas_ptr->nas_log->error("%s - " fmt, name(), ##__VA_ARGS__)
#define Warning(fmt, ...) nas_ptr->nas_log->warning("%s - " fmt, name(), ##__VA_ARGS__)
#define Info(fmt, ...) nas_ptr->nas_log->info("%s - " fmt, name(), ##__VA_ARGS__)
namespace srsue {
using srslte::proc_outcome_t;
proc_outcome_t nas::plmn_search_proc::init(nas* nas_ptr_)
{
nas_ptr = nas_ptr_;
// start RRC
state = state_t::plmn_search;
if (not nas_ptr->rrc->plmn_search()) {
Error("Error while searching for PLMNs\n");
return proc_outcome_t::error;
}
Info("Starting...\n");
return proc_outcome_t::yield;
}
proc_outcome_t nas::plmn_search_proc::step()
{
if (state == state_t::rrc_connect) {
if (nas_ptr->rrc_connector.run()) {
return proc_outcome_t::yield;
}
rrc_connect_proc ret = nas_ptr->rrc_connector.pop();
if (ret.is_success()) {
return proc_outcome_t::success;
}
return proc_outcome_t::error;
}
return proc_outcome_t::yield;
}
proc_outcome_t nas::plmn_search_proc::trigger_event(const plmn_search_complete_t& t)
{
if (state != state_t::plmn_search) {
Warning("PLMN Search Complete was received but PLMN Search is not running.\n");
return proc_outcome_t::yield; // ignore
}
// check whether the state hasn't changed
if (nas_ptr->state != EMM_STATE_DEREGISTERED or nas_ptr->plmn_is_selected) {
Error("Error while searching for PLMNs\n");
return proc_outcome_t::error;
}
if (t.nof_plmns < 0) {
Error("Error while searching for PLMNs\n");
return proc_outcome_t::error;
} else if (t.nof_plmns == 0) {
Warning("Did not find any PLMN in the set of frequencies.\n");
return proc_outcome_t::error;
}
// Save PLMNs
nas_ptr->known_plmns.clear();
for (int i = 0; i < t.nof_plmns; i++) {
nas_ptr->known_plmns.push_back(t.found_plmns[i].plmn_id);
nas_ptr->nas_log->info(
"Found PLMN: Id=%s, TAC=%d\n", t.found_plmns[i].plmn_id.to_string().c_str(), t.found_plmns[i].tac);
nas_ptr->nas_log->console(
"Found PLMN: Id=%s, TAC=%d\n", t.found_plmns[i].plmn_id.to_string().c_str(), t.found_plmns[i].tac);
}
nas_ptr->select_plmn();
// Select PLMN in request establishment of RRC connection
if (not nas_ptr->plmn_is_selected) {
Error("PLMN is not selected because no suitable PLMN was found\n");
return proc_outcome_t::error;
}
nas_ptr->rrc->plmn_select(nas_ptr->current_plmn);
if (not nas_ptr->rrc_connector.launch(nas_ptr)) {
Error("Unable to initiate RRC connection.\n");
return proc_outcome_t::error;
}
state = state_t::rrc_connect;
return proc_outcome_t::yield;
}
proc_outcome_t nas::rrc_connect_proc::init(nas* nas_ptr_)
{
nas_ptr = nas_ptr_;
if (nas_ptr->rrc->is_connected()) {
Info("Stopping. Reason: Already connected\n");
return proc_outcome_t::success;
}
// Generate service request or attach request message
unique_byte_buffer_t dedicatedInfoNAS = srslte::allocate_unique_buffer(*nas_ptr->pool, true);
if (!dedicatedInfoNAS) {
Error("Fatal Error: Couldn't allocate PDU.\n");
return proc_outcome_t::error;
}
if (nas_ptr->state == EMM_STATE_REGISTERED) {
nas_ptr->gen_service_request(dedicatedInfoNAS.get());
} else {
nas_ptr->gen_attach_request(dedicatedInfoNAS.get());
}
// Provide UE-Identity to RRC if have one
if (nas_ptr->have_guti) {
srslte::s_tmsi_t s_tmsi;
s_tmsi.mmec = nas_ptr->ctxt.guti.mme_code;
s_tmsi.m_tmsi = nas_ptr->ctxt.guti.m_tmsi;
nas_ptr->rrc->set_ue_identity(s_tmsi);
}
establishment_cause_t establish_cause = establishment_cause_t::mo_sig;
if (nas_ptr->state == EMM_STATE_REGISTERED) {
// FIXME: only need to use MT_ACCESS for establishment after paging
establish_cause = establishment_cause_t::mt_access;
}
state = state_t::conn_req;
if (not nas_ptr->start_connection_request(establish_cause, std::move(dedicatedInfoNAS))) {
return proc_outcome_t::error;
}
Info("Starting...\n");
return proc_outcome_t::yield;
}
proc_outcome_t nas::rrc_connect_proc::step()
{
if (state == state_t::conn_req) {
if (nas_ptr->conn_req_proc.run()) {
return proc_outcome_t::yield;
}
query_proc_t<bool> ret = nas_ptr->conn_req_proc.pop();
if (not ret.result()) {
Error("Could not establish RRC connection\n");
return proc_outcome_t::error;
}
Info("Connection established correctly. Waiting for Attach\n");
wait_timeout = 0;
// Wait until attachment. If doing a service request is already attached
state = state_t::wait_attach;
return proc_outcome_t::repeat;
} else if (state == state_t::wait_attach) {
wait_timeout++;
// Wait until attachment. If doing a service request is already attached
if (wait_timeout >= 5000 or nas_ptr->state == EMM_STATE_REGISTERED or not nas_ptr->running or
not nas_ptr->rrc->is_connected()) {
if (nas_ptr->state == EMM_STATE_REGISTERED) {
Info("EMM Registered correctly\n");
return proc_outcome_t::success;
} else if (nas_ptr->state == EMM_STATE_DEREGISTERED) {
Error("Timeout or received attach reject while trying to attach\n");
nas_ptr->nas_log->console("Failed to Attach\n");
} else if (!nas_ptr->rrc->is_connected()) {
Error("Was disconnected while attaching\n");
} else {
Error("Timed out while trying to attach\n");
}
return proc_outcome_t::error;
}
}
return proc_outcome_t::yield;
}
/*********************************************************************
* NAS
********************************************************************/
@ -113,6 +281,11 @@ emm_state_t nas::get_state() {
return state;
}
void nas::run_tti(uint32_t tti)
{
callbacks.run();
}
/*******************************************************************************
* UE interface
******************************************************************************/
@ -121,9 +294,8 @@ emm_state_t nas::get_state() {
* The function returns true if the UE could attach correctly or false in case of error or timeout during attachment.
*
*/
bool nas::attach_request() {
rrc_interface_nas::found_plmn_t found_plmns[rrc_interface_nas::MAX_FOUND_PLMNS];
int nof_plmns = 0;
void nas::start_attach_request(srslte::proc_state_t* result)
{
nas_log->info("Attach Request\n");
switch (state) {
@ -132,57 +304,59 @@ bool nas::attach_request() {
// Search PLMN is not selected
if (!plmn_is_selected) {
nas_log->info("No PLMN selected. Starting PLMN Search...\n");
nof_plmns = rrc->plmn_search(found_plmns);
if (nof_plmns > 0) {
// Save PLMNs
known_plmns.clear();
for (int i=0;i<nof_plmns;i++) {
known_plmns.push_back(found_plmns[i].plmn_id);
nas_log->info(
"Found PLMN: Id=%s, TAC=%d\n", found_plmns[i].plmn_id.to_string().c_str(), found_plmns[i].tac);
nas_log->console(
"Found PLMN: Id=%s, TAC=%d\n", found_plmns[i].plmn_id.to_string().c_str(), found_plmns[i].tac);
}
select_plmn();
} else if (nof_plmns == 0) {
nas_log->warning("Did not find any PLMN in the set of frequencies\n");
return false;
} else if (nof_plmns < 0) {
nas_log->error("Error while searching for PLMNs\n");
return false;
}
}
// Select PLMN in request establishment of RRC connection
if (plmn_is_selected) {
rrc->plmn_select(current_plmn);
if (rrc_connect()) {
nas_log->info("NAS attached successfully.\n");
return true;
} else {
nas_log->error("Could not attach in attach request\n");
if (not plmn_searcher.launch(this)) {
*result = proc_state_t::error;
return;
}
callbacks.defer_task([this, result]() {
if (plmn_searcher.run()) {
return proc_outcome_t::yield;
}
plmn_search_proc p = plmn_searcher.pop();
nas_log->info("Attach Request from PLMN Search %s\n", p.is_success() ? "finished successfully" : "failed");
*result = p.is_success() ? proc_state_t::success : proc_state_t::error;
return proc_outcome_t::success;
});
} else {
nas_log->error("PLMN is not selected because no suitable PLMN was found\n");
*result = proc_state_t::error;
}
break;
case EMM_STATE_REGISTERED:
if (rrc->is_connected()) {
nas_log->info("NAS is already registered and RRC connected\n");
return true;
*result = proc_state_t::success;
} else {
nas_log->info("NAS is already registered but RRC disconnected. Connecting now...\n");
if (rrc_connect()) {
nas_log->info("NAS attached successfully.\n");
return true;
} else {
nas_log->error("Could not attach from attach_request\n");
if (not rrc_connector.launch(this)) {
nas_log->error("Cannot initiate concurrent rrc connection procedures\n");
*result = proc_state_t::error;
return;
}
callbacks.defer_task([this, result]() {
if (rrc_connector.run()) {
return proc_outcome_t::yield;
}
rrc_connect_proc proc = rrc_connector.pop();
if (proc.is_success()) {
nas_log->info("NAS attached successfully.\n");
} else {
nas_log->error("Could not attach from attach_request\n");
}
*result = proc.is_success() ? proc_state_t::success : proc_state_t::error;
return proc_outcome_t::success;
});
}
break;
default:
nas_log->info("Attach request ignored. State = %s\n", emm_state_text[state]);
*result = proc_state_t::error;
}
return false;
}
void nas::plmn_search_completed(rrc_interface_nas::found_plmn_t found_plmns[rrc_interface_nas::MAX_FOUND_PLMNS],
int nof_plmns)
{
plmn_searcher.trigger_event(plmn_search_proc::plmn_search_complete_t(found_plmns, nof_plmns));
}
bool nas::detach_request() {
@ -220,11 +394,24 @@ void nas::paging(s_tmsi_t* ue_identity)
{
if (state == EMM_STATE_REGISTERED) {
nas_log->info("Received paging: requesting RRC connection establishment\n");
if (rrc_connect()) {
nas_log->info("Attached successfully\n");
} else {
nas_log->error("Could not attach from paging\n");
if (rrc_connector.is_active()) {
nas_log->error("Cannot initiate concurrent RRC connection establishment procedures\n");
return;
}
if (not rrc_connector.launch(this)) {
nas_log->error("Could not launch RRC Connect()\n");
return;
}
// once completed, call paging complete
callbacks.defer_task([this]() {
if (rrc_connector.run()) {
return proc_outcome_t::yield;
}
bool success = rrc_connector.pop().is_success();
rrc->paging_completed(success);
return proc_outcome_t::success;
});
} else {
nas_log->warning("Received paging while in state %s\n", emm_state_text[state]);
}
@ -234,66 +421,25 @@ void nas::set_barring(barring_t barring) {
current_barring = barring;
}
/* Internal function that requests RRC connection, waits for positive or negative response and returns true/false
*/
bool nas::rrc_connect() {
if (rrc->is_connected()) {
nas_log->info("Already connected\n");
return true;
}
// Generate service request or attach request message
unique_byte_buffer_t dedicatedInfoNAS = srslte::allocate_unique_buffer(*pool, true);
if (!dedicatedInfoNAS) {
nas_log->error("Fatal Error: Couldn't allocate PDU in rrc_connect().\n");
bool nas::start_connection_request(srslte::establishment_cause_t establish_cause,
srslte::unique_byte_buffer_t ded_info_nas)
{
if (not conn_req_proc.launch()) {
nas_log->error("Failed to initiate a connection request procedure\n");
return false;
}
if (state == EMM_STATE_REGISTERED) {
gen_service_request(dedicatedInfoNAS.get());
} else {
gen_attach_request(dedicatedInfoNAS.get());
}
// Provide UE-Identity to RRC if have one
if (have_guti) {
s_tmsi_t s_tmsi;
s_tmsi.m_tmsi = ctxt.guti.m_tmsi;
s_tmsi.mmec = ctxt.guti.mme_code;
rrc->set_ue_identity(s_tmsi);
}
// Set establishment cause
srslte::establishment_cause_t establish_cause = srslte::establishment_cause_t::mo_sig;
if (state == EMM_STATE_REGISTERED) {
// FIXME: only need to use MT_ACCESS for establishment after paging
establish_cause = establishment_cause_t::mt_access;
if (not rrc->connection_request(establish_cause, std::move(ded_info_nas))) {
nas_log->error("Failed to initiate a connection request procedure\n");
conn_req_proc.pop();
return false;
}
return true;
}
if (rrc->connection_request(establish_cause, std::move(dedicatedInfoNAS))) {
nas_log->info("Connection established correctly. Waiting for Attach\n");
// Wait until attachment. If doing a service request is already attached
uint32_t tout = 0;
while (tout < 5000 && state != EMM_STATE_REGISTERED && running && rrc->is_connected()) {
usleep(1000);
tout++;
}
if (state == EMM_STATE_REGISTERED) {
nas_log->info("EMM Registered correctly\n");
return true;
} else if (state == EMM_STATE_DEREGISTERED) {
nas_log->error("Timeout or received attach reject while trying to attach\n");
nas_log->console("Failed to Attach\n");
} else if (!rrc->is_connected()) {
nas_log->error("Was disconnected while attaching\n");
} else {
nas_log->error("Timed out while trying to attach\n");
}
} else {
nas_log->error("Could not establish RRC connection\n");
}
return false;
bool nas::connection_request_completed(bool outcome)
{
conn_req_proc.trigger_event(outcome);
return conn_req_proc.is_active();
}
void nas::select_plmn() {
@ -311,7 +457,7 @@ void nas::select_plmn() {
}
// If not, select the first available PLMN
if (known_plmns.size() > 0) {
if (not known_plmns.empty()) {
nas_log->info("Could not find Home PLMN Id=%s, trying to connect to PLMN Id=%s\n",
home_plmn.to_string().c_str(),
known_plmns[0].to_string().c_str());
@ -359,7 +505,7 @@ void nas::write_pdu(uint32_t lcid, unique_byte_buffer_t pdu)
}
// Write NAS pcap
if(pcap != NULL) {
if (pcap != nullptr) {
pcap->write_nas(pdu->msg, pdu->N_bytes);
}
@ -424,7 +570,6 @@ void nas::set_k_enb_count(uint32_t count) {
// UL count for RRC key derivation depends on UL Count of the Attach Request or Service Request.
// On the case of an Authentication Request, the UL count used to generate K_enb must be reset to zero.
ctxt.k_enb_count = count;
return;
}
uint32_t nas::get_k_enb_count() {
@ -436,7 +581,7 @@ bool nas::get_k_asme(uint8_t *k_asme_, uint32_t n) {
nas_log->error("K_asme requested before security context established\n");
return false;
}
if(NULL == k_asme_ || n < 32) {
if (nullptr == k_asme_ || n < 32) {
nas_log->error("Invalid parameters to get_k_asme");
return false;
}
@ -1007,7 +1152,7 @@ void nas::parse_security_mode_command(uint32_t lcid, unique_byte_buffer_t pdu)
nas_log->debug("Generating integrity check. integ_algo:%d, count_dl:%d, lcid:%d\n",
ctxt.integ_algo, ctxt.rx_count, lcid);
if (integrity_check(pdu.get()) != true) {
if (not integrity_check(pdu.get())) {
nas_log->warning("Sending Security Mode Reject due to integrity check failure\n");
send_security_mode_reject(LIBLTE_MME_EMM_CAUSE_MAC_FAILURE);
return;
@ -1516,7 +1661,9 @@ void nas::send_detach_request(bool switch_off)
if (rrc->is_connected()) {
rrc->write_sdu(std::move(pdu));
} else {
rrc->connection_request(establishment_cause_t::mo_sig, std::move(pdu));
if (not start_connection_request(establishment_cause_t::mo_sig, std::move(pdu))) {
nas_log->info("Failed to initiate RRC Connection Request\n");
}
}
}

@ -100,6 +100,7 @@ public:
plmns.plmn_id.from_number(mcc, mnc);
plmns.tac = 0xffff;
}
void init(nas* nas_) { nas_ptr = nas_; }
void write_sdu(unique_byte_buffer_t sdu)
{
last_sdu_len = sdu->N_bytes;
@ -110,10 +111,11 @@ public:
uint32_t get_last_sdu_len() { return last_sdu_len; }
void reset() { last_sdu_len = 0; }
int plmn_search(srsue::rrc_interface_nas::found_plmn_t* found) {
memcpy(found, &plmns, sizeof(found_plmn_t));
return 1;
};
bool plmn_search()
{
nas_ptr->plmn_search_completed(&plmns, 1);
return true;
}
void plmn_select(srslte::plmn_id_t plmn_id){};
void set_ue_identity(srslte::s_tmsi_t s_tmsi) {}
bool connection_request(srslte::establishment_cause_t cause, srslte::unique_byte_buffer_t sdu)
@ -121,6 +123,7 @@ public:
printf("NAS generated SDU (len=%d):\n", sdu->N_bytes);
last_sdu_len = sdu->N_bytes;
srslte_vec_fprint_byte(stdout, sdu->msg, sdu->N_bytes);
nas_ptr->connection_request_completed(true);
return true;
}
bool is_connected() {return false;}
@ -129,8 +132,10 @@ public:
uint16_t get_mnc() { return mnc; }
void enable_capabilities() {}
uint32_t get_lcid_for_eps_bearer(const uint32_t& eps_bearer_id) { return 0; }
void paging_completed(bool outcome) {}
private:
nas* nas_ptr;
uint32_t last_sdu_len;
found_plmn_t plmns;
};
@ -140,13 +145,28 @@ class stack_dummy : public stack_interface_gw, public thread
public:
stack_dummy(pdcp_interface_gw* pdcp_, srsue::nas* nas_) : pdcp(pdcp_), nas(nas_), thread("DUMMY STACK") {}
void init() { start(-1); }
bool switch_on() final { return nas->attach_request(); }
bool switch_on() final
{
proc_state_t proc_result = proc_state_t::on_going;
nas->start_attach_request(&proc_result);
while (proc_result == proc_state_t::on_going) {
usleep(1000);
}
return proc_result == proc_state_t::success;
}
void write_sdu(uint32_t lcid, srslte::unique_byte_buffer_t sdu, bool blocking)
{
pdcp->write_sdu(lcid, std::move(sdu), blocking);
}
bool is_lcid_enabled(uint32_t lcid) { return pdcp->is_lcid_enabled(lcid); }
void run_thread() { running = true; }
void run_thread()
{
running = true;
uint32_t counter = 0;
while (running) {
nas->run_tti(counter++);
}
}
void stop()
{
running = false;
@ -211,6 +231,7 @@ int security_command_test()
cfg.eia = "1,2";
cfg.eea = "0,1,2";
nas.init(&usim, &rrc_dummy, &gw, cfg);
rrc_dummy.init(&nas);
// push auth request PDU to NAS to generate security context
byte_buffer_pool* pool = byte_buffer_pool::get_instance();
@ -279,6 +300,7 @@ int mme_attach_request_test()
stack_dummy stack(&pdcp_dummy, &nas);
nas.init(&usim, &rrc_dummy, &gw, nas_cfg);
rrc_dummy.init(&nas);
gw_args_t gw_args;
gw_args.tun_dev_name = "tun0";

Loading…
Cancel
Save