You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

236 lines
6.5 KiB
C++

/**
* Copyright 2013-2020 Software Radio Systems Limited
*
* This file is part of srsLTE.
*
* srsLTE is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* srsLTE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* A copy of the GNU Affero General Public License can be found in
* the LICENSE file in the top-level directory of this distribution
* and at http://www.gnu.org/licenses/.
*
*/
#ifndef SRSLTE_SCELL_STATE_H
#define SRSLTE_SCELL_STATE_H
#include <cinttypes>
#include <mutex>
#include <srslte/common/common.h>
namespace srsue {
namespace scell {
/**
* References
*
* According to 3GPP 36.321 R10 (MAC procedures) section 5.13 Activation/Deactivation of SCells
* An activated cell operation shall include (Summarised):
* - SRS transmissions on the SCell;
* - CQI/PMI/RI/PTI reporting for the SCell;
* - PDCCH monitoring on the SCell;
* - PDCCH monitoring for the SCell
*
* According to 3GPP 36.213 R10 (PHY procedures) section 4.3 Timing for Secondary Cell Activation / Deactivation
* When a UE receives an activation command for a secondary cell in subframe n, the corresponding actions shall be
* applied at subframe n+8. (Summarised)
*/
class state
{
private:
static constexpr uint32_t activation_delay_tti = FDD_HARQ_DELAY_DL_MS + FDD_HARQ_DELAY_UL_MS;
static constexpr uint32_t activation_margin_tti = FDD_HARQ_DELAY_DL_MS;
// SCell EARFCN, PCI, configured and enabled list
struct cfg {
uint32_t earfcn = 0;
uint32_t pci = 0;
enum { none = 0, inactive, active } status = none;
};
std::array<cfg, SRSLTE_MAX_CARRIERS> scell_cfg;
enum { idle = 0, waiting, transition } activation_state = idle;
uint32_t activation_cmd = 0;
uint32_t activation_tti = 0;
mutable std::mutex mutex;
bool _get_cmd_activation(uint32_t cc_idx) const { return ((activation_cmd >> cc_idx) & 0x1) == 0x1; }
bool _tti_greater_or_equal_than(uint32_t a, uint32_t b) const { return TTI_SUB(a, b) < 10240 / 2; }
public:
/**
* A SCell Activation/Deactivation command is received. Stores the new command and the TTI. Also, the internal state
* goes to waiting.
*
* If a previous command was received and not applied, it will discard it.
*
* @param cmd SCell Activation/Deactivation command
* @param tti TTI in which the command was received
*/
void set_activation_deactivation(uint32_t cmd, uint32_t tti)
{
std::unique_lock<std::mutex> lock(mutex);
// Store command
activation_cmd = cmd;
// Command is waiting
activation_state = waiting;
activation_tti = TTI_ADD(tti, activation_delay_tti);
}
/**
* @brief Deactivates all the active SCells
*/
void deactivate_all()
{
std::unique_lock<std::mutex> lock(mutex);
for (cfg& e : scell_cfg) {
if (e.status == cfg::active) {
e.status = cfg::inactive;
}
}
}
void run_tti(uint32_t tti)
{
std::unique_lock<std::mutex> lock(mutex);
switch (activation_state) {
case idle:
// waiting for receiving a command, do nothing
break;
case waiting:
// Detect that TTI when the CMD needs to be applied, the activation cannot be done instantly because some
// workers might be currently ongoing, so only update state
if (_tti_greater_or_equal_than(tti, activation_tti)) {
activation_state = transition;
}
break;
case transition:
// Detect when the TTI has increased enough to make sure there arent workers, set the configuration
if (TTI_SUB(tti, activation_tti) >= activation_margin_tti) {
// Reload cell states
for (uint32_t i = 1; i < SRSLTE_MAX_CARRIERS; i++) {
// Get Activation command value
bool activate = _get_cmd_activation(i);
// Apply activation only if the cell was configured
if (scell_cfg[i].status != cfg::none) {
scell_cfg[i].status = activate ? cfg::active : cfg::inactive;
}
}
// Go back to initial state
activation_state = idle;
}
break;
}
}
void configure(uint32_t cc_idx, uint32_t earfcn, uint32_t pci)
{
std::unique_lock<std::mutex> lock(mutex);
if (cc_idx == 0 or cc_idx >= SRSLTE_MAX_CARRIERS) {
ERROR("CC IDX %d out-of-range\n", cc_idx);
return;
}
scell_cfg[cc_idx].status = cfg::inactive;
scell_cfg[cc_idx].earfcn = earfcn;
scell_cfg[cc_idx].pci = pci;
}
bool is_active(uint32_t cc_idx, uint32_t tti) const
{
if (cc_idx == 0) {
return true;
}
if (cc_idx >= SRSLTE_MAX_CARRIERS) {
return false;
}
std::unique_lock<std::mutex> lock(mutex);
// Use stashed activation if the activation is transitioning and the current TTI requires new value
if (activation_state == transition and scell_cfg[cc_idx].status != cfg::none and
_tti_greater_or_equal_than(tti, activation_tti)) {
return _get_cmd_activation(cc_idx);
}
return scell_cfg[cc_idx].status == cfg::active;
}
bool is_configured(uint32_t cc_idx) const
{
if (cc_idx == 0) {
return true;
}
if (cc_idx >= SRSLTE_MAX_CARRIERS) {
return false;
}
std::unique_lock<std::mutex> lock(mutex);
return scell_cfg[cc_idx].status != cfg::none;
}
void reset()
{
std::unique_lock<std::mutex> lock(mutex);
activation_state = idle;
for (cfg& e : scell_cfg) {
e.status = cfg::none;
e.earfcn = 0;
e.pci = UINT32_MAX;
}
}
uint32_t get_pci(uint32_t cc_idx)
{
std::unique_lock<std::mutex> lock(mutex);
if (cc_idx == 0 or cc_idx >= SRSLTE_MAX_CARRIERS) {
ERROR("CC IDX %d out-of-range\n", cc_idx);
return 0;
}
return scell_cfg[cc_idx].pci;
}
uint32_t get_earfcn(uint32_t cc_idx)
{
std::unique_lock<std::mutex> lock(mutex);
if (cc_idx == 0 or cc_idx >= SRSLTE_MAX_CARRIERS) {
ERROR("CC IDX %d out-of-range\n", cc_idx);
return 0;
}
return scell_cfg[cc_idx].earfcn;
}
};
} // namespace scell
} // namespace srsue
#endif // SRSLTE_SCELL_STATE_H