mirror of https://github.com/pvnis/srsRAN_4G.git
added resumable procedures to make RRC and NAS non-blocking
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
|
@ -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;
|
||||
}
|
@ -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
|
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
|
Loading…
Reference in New Issue