3094 lines
95 KiB
C
3094 lines
95 KiB
C
/*
|
|
* Copyright (c) 2017 - 2022, Nordic Semiconductor ASA
|
|
* All rights reserved.
|
|
*
|
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
|
* list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* 3. Neither the name of Nordic Semiconductor ASA nor the names of its
|
|
* contributors may be used to endorse or promote products derived from this
|
|
* software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY, AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
*/
|
|
|
|
/**
|
|
* @file
|
|
* This file implements Finite State Machine of nRF 802.15.4 radio driver.
|
|
*
|
|
*/
|
|
|
|
#define NRF_802154_MODULE_ID NRF_802154_DRV_MODULE_ID_CORE
|
|
|
|
#include "nrf_802154_core.h"
|
|
|
|
#include <assert.h>
|
|
#include <stdbool.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
|
|
#include "nrf_802154.h"
|
|
#include "nrf_802154_config.h"
|
|
#include "nrf_802154_const.h"
|
|
#include "nrf_802154_critical_section.h"
|
|
#include "nrf_802154_debug.h"
|
|
#include "nrf_802154_notification.h"
|
|
#include "nrf_802154_pib.h"
|
|
#include "nrf_802154_procedures_duration.h"
|
|
#include "nrf_802154_rssi.h"
|
|
#include "nrf_802154_rx_buffer.h"
|
|
#include "nrf_802154_sl_timer.h"
|
|
#include "nrf_802154_sl_utils.h"
|
|
#include "nrf_802154_stats.h"
|
|
#include "nrf_802154_utils.h"
|
|
#include "nrf_802154_trx.h"
|
|
#include "nrf_802154_tx_work_buffer.h"
|
|
#include "nrf_802154_tx_power.h"
|
|
#include "nrf_802154_types_internal.h"
|
|
#include "nrf_802154_utils.h"
|
|
#include "drivers/nrfx_errors.h"
|
|
#include "hal/nrf_radio.h"
|
|
#include "mac_features/nrf_802154_filter.h"
|
|
#include "mac_features/nrf_802154_frame_parser.h"
|
|
#include "mac_features/ack_generator/nrf_802154_ack_generator.h"
|
|
#include "rsch/nrf_802154_rsch.h"
|
|
#include "rsch/nrf_802154_rsch_crit_sect.h"
|
|
#include "timer/nrf_802154_timer_coord.h"
|
|
#include "platform/nrf_802154_hp_timer.h"
|
|
#include "platform/nrf_802154_irq.h"
|
|
#include "protocol/mpsl_fem_protocol_api.h"
|
|
|
|
#include "nrf_802154_core_hooks.h"
|
|
#include "nrf_802154_sl_ant_div.h"
|
|
|
|
/// Delay before first check of received frame: 24 bits is PHY header and MAC Frame Control field.
|
|
#define BCC_INIT (3 * 8)
|
|
|
|
/// Duration of single iteration of Energy Detection procedure
|
|
#define ED_ITER_DURATION 128U
|
|
/// Overhead of hardware preparation for ED procedure (aTurnaroundTime) [number of iterations]
|
|
#define ED_ITERS_OVERHEAD 2U
|
|
|
|
#define MAX_CRIT_SECT_TIME 60 ///< Maximal time that the driver spends in single critical section.
|
|
|
|
#define LQI_VALUE_FACTOR 4 ///< Factor needed to calculate LQI value based on data from RADIO peripheral
|
|
#define LQI_MAX 0xff ///< Maximal LQI value
|
|
|
|
/** Get LQI of given received packet. If CRC is calculated by hardware LQI is included instead of CRC
|
|
* in the frame. Length is stored in byte with index 0; CRC is 2 last bytes.
|
|
*/
|
|
#define RX_FRAME_LQI(data) ((data)[(data)[0] - 1])
|
|
|
|
/** Timeout value when waiting for ntf_802154_trx_receive_frame_started after ntf_802154_trx_receive_frame_prestarted.
|
|
* Timeout value in microseconds. The time equals to whole synchronization header (SHR) duration of 802.15.4 frame.
|
|
*/
|
|
#define PRESTARTED_TIMER_TIMEOUT_US (160U)
|
|
|
|
#if NRF_802154_RX_BUFFERS > 1
|
|
/// Pointer to currently used receive buffer.
|
|
static rx_buffer_t * mp_current_rx_buffer;
|
|
|
|
#else
|
|
/// If there is only one buffer use const pointer to the receive buffer.
|
|
static rx_buffer_t * const mp_current_rx_buffer = &nrf_802154_rx_buffers[0];
|
|
|
|
#endif
|
|
|
|
static uint8_t * mp_ack; ///< Pointer to Ack frame buffer.
|
|
static uint8_t * mp_tx_data; ///< Pointer to the data to transmit.
|
|
static uint32_t m_ed_time_left; ///< Remaining time of the current energy detection procedure [us].
|
|
static uint8_t m_ed_result; ///< Result of the current energy detection procedure.
|
|
static uint8_t m_last_lqi; ///< LQI of the last received non-ACK frame, corrected for the temperature.
|
|
static nrf_802154_fal_tx_power_split_t m_tx_power; ///< Power to be used to transmit the current frame split into components.
|
|
static int8_t m_last_rssi; ///< RSSI of the last received non-ACK frame, corrected for the temperature.
|
|
|
|
static nrf_802154_frame_parser_data_t m_current_rx_frame_data; ///< RX frame parser data.
|
|
|
|
static volatile radio_state_t m_state; ///< State of the radio driver.
|
|
|
|
typedef struct
|
|
{
|
|
bool frame_filtered : 1; ///< If frame being received passed filtering operation.
|
|
bool frame_parsed : 1; ///< If frame being received has been parsed
|
|
bool rx_timeslot_requested : 1; ///< If timeslot for the frame being received is already requested.
|
|
bool tx_with_cca : 1; ///< If currently transmitted frame is transmitted with cca.
|
|
bool tx_diminished_prio : 1; ///< If priority of the current transmission should be diminished.
|
|
} nrf_802154_flags_t;
|
|
|
|
static nrf_802154_flags_t m_flags; ///< Flags used to store the current driver state.
|
|
|
|
static volatile bool m_rsch_timeslot_is_granted; ///< State of the RSCH timeslot.
|
|
static volatile rsch_prio_t m_rsch_priority = RSCH_PRIO_IDLE; ///< Last notified RSCH priority.
|
|
|
|
/** @brief Value of argument @c notifications_mask to @ref nrf_802154_trx_receive_frame */
|
|
static nrf_802154_trx_receive_notifications_t m_trx_receive_frame_notifications_mask;
|
|
/** @brief Value of argument @c notifications_mask to @ref nrf_802154_trx_transmit_frame */
|
|
static nrf_802154_trx_transmit_notifications_t m_trx_transmit_frame_notifications_mask;
|
|
|
|
static volatile uint8_t m_rx_prestarted_trig_count;
|
|
static nrf_802154_sl_timer_t m_rx_prestarted_timer;
|
|
|
|
/** @brief Value of Coex TX Request mode */
|
|
static nrf_802154_coex_tx_request_mode_t m_coex_tx_request_mode;
|
|
|
|
/** @brief Identifier of currently active reception window. */
|
|
static uint32_t m_rx_window_id;
|
|
|
|
#if NRF_802154_TOTAL_TIMES_MEASUREMENT_ENABLED
|
|
#if !NRF_802154_FRAME_TIMESTAMP_ENABLED
|
|
#error NRF_802154_FRAME_TIMESTAMP_ENABLED == 0 when NRF_802154_TOTAL_TIMES_MEASUREMENT_ENABLED != 0
|
|
#endif
|
|
#if NRF_802154_DISABLE_BCC_MATCHING
|
|
#error NRF_802154_DISABLE_BCC_MATCHING != 0 when NRF_802154_TOTAL_TIMES_MEASUREMENT_ENABLED != 0
|
|
#endif
|
|
#endif // NRF_802154_TOTAL_TIMES_MEASUREMENT_ENABLED
|
|
|
|
#if NRF_802154_TOTAL_TIMES_MEASUREMENT_ENABLED
|
|
static uint32_t m_listening_start_hp_timestamp;
|
|
|
|
#endif
|
|
|
|
static const nrf_802154_transmitted_frame_props_t m_default_frame_props =
|
|
NRF_802154_TRANSMITTED_FRAME_PROPS_DEFAULT_INIT;
|
|
|
|
/***************************************************************************************************
|
|
* @section Common core operations
|
|
**************************************************************************************************/
|
|
|
|
static rsch_prio_t min_required_rsch_prio(radio_state_t state);
|
|
|
|
static void request_preconditions_for_state(radio_state_t state)
|
|
{
|
|
nrf_802154_rsch_crit_sect_prio_request(min_required_rsch_prio(state));
|
|
}
|
|
|
|
/** Set driver state.
|
|
*
|
|
* @param[in] state Driver state to set.
|
|
*/
|
|
static void state_set(radio_state_t state)
|
|
{
|
|
m_state = state;
|
|
|
|
nrf_802154_log_local_event(NRF_802154_LOG_VERBOSITY_LOW,
|
|
NRF_802154_LOG_LOCAL_EVENT_ID_CORE__SET_STATE,
|
|
(uint32_t)state);
|
|
|
|
request_preconditions_for_state(state);
|
|
}
|
|
|
|
/** Clear RX frame data. */
|
|
static void rx_data_clear(void)
|
|
{
|
|
(void)nrf_802154_frame_parser_data_init(mp_current_rx_buffer->data,
|
|
0U,
|
|
PARSE_LEVEL_NONE,
|
|
&m_current_rx_frame_data);
|
|
}
|
|
|
|
/** Clear flags describing frame being received. */
|
|
static void rx_flags_clear(void)
|
|
{
|
|
m_flags.frame_filtered = false;
|
|
m_flags.rx_timeslot_requested = false;
|
|
m_flags.frame_parsed = false;
|
|
}
|
|
|
|
/** Wait for the RSSI measurement. */
|
|
static void rssi_measurement_wait(void)
|
|
{
|
|
while (!nrf_802154_trx_rssi_sample_is_available())
|
|
{
|
|
// Intentionally empty: This function is called from a critical section.
|
|
// WFE would not be waken up by a RADIO event.
|
|
}
|
|
}
|
|
|
|
/** Get the result of the last RSSI measurement.
|
|
*
|
|
* @returns Result of the last RSSI measurement in dBm.
|
|
*/
|
|
static int8_t rssi_last_measurement_get(void)
|
|
{
|
|
uint8_t rssi_sample = nrf_802154_trx_rssi_last_sample_get();
|
|
|
|
rssi_sample = nrf_802154_rssi_sample_corrected_get(rssi_sample);
|
|
|
|
return -((int8_t)rssi_sample);
|
|
}
|
|
|
|
/** Get LQI of a received frame.
|
|
*
|
|
* @param[in] p_data Pointer to buffer containing PHR and PSDU of received frame
|
|
*
|
|
* @returns LQI of given frame.
|
|
*/
|
|
static uint8_t lqi_get(const uint8_t * p_data)
|
|
{
|
|
uint32_t lqi = RX_FRAME_LQI(p_data);
|
|
|
|
lqi = nrf_802154_rssi_lqi_corrected_get(lqi);
|
|
lqi *= LQI_VALUE_FACTOR;
|
|
|
|
if (lqi > LQI_MAX)
|
|
{
|
|
lqi = LQI_MAX;
|
|
}
|
|
|
|
return (uint8_t)lqi;
|
|
}
|
|
|
|
#if (NRF_802154_FRAME_TIMESTAMP_ENABLED)
|
|
/**
|
|
* @brief Get timestamp made by timer coordinator.
|
|
*
|
|
* @note This function increments the returned value by 1 us if the timestamp is equal to the
|
|
* @ref NRF_802154_NO_TIMESTAMP value to indicate that the timestamp is available.
|
|
*
|
|
* @returns Timestamp [us] of the last event captured by timer coordinator frame or
|
|
* @ref NRF_802154_NO_TIMESTAMP if the timestamp is inaccurate.
|
|
*/
|
|
static uint64_t timer_coord_timestamp_get(void)
|
|
{
|
|
uint64_t timestamp = NRF_802154_NO_TIMESTAMP;
|
|
bool timestamp_received = nrf_802154_timer_coord_timestamp_get(×tamp);
|
|
|
|
if (!timestamp_received)
|
|
{
|
|
timestamp = NRF_802154_NO_TIMESTAMP;
|
|
}
|
|
else if (timestamp == NRF_802154_NO_TIMESTAMP)
|
|
{
|
|
timestamp++;
|
|
}
|
|
else
|
|
{
|
|
// Return timestamp without correction
|
|
}
|
|
|
|
return timestamp;
|
|
}
|
|
|
|
#endif
|
|
|
|
static void received_frame_notify(uint8_t * p_data)
|
|
{
|
|
nrf_802154_notify_received(p_data, m_last_rssi, m_last_lqi);
|
|
}
|
|
|
|
/** Allow nesting critical sections and notify MAC layer that a frame was received. */
|
|
static void received_frame_notify_and_nesting_allow(uint8_t * p_data)
|
|
{
|
|
nrf_802154_critical_section_nesting_allow();
|
|
|
|
received_frame_notify(p_data);
|
|
|
|
nrf_802154_critical_section_nesting_deny();
|
|
}
|
|
|
|
/** Notify MAC layer that receive procedure failed. */
|
|
static void receive_failed_notify(nrf_802154_rx_error_t error)
|
|
{
|
|
nrf_802154_critical_section_nesting_allow();
|
|
|
|
// Don't care about the result - if the notification cannot be performed
|
|
// no impact on the device's operation is expected
|
|
(void)nrf_802154_notify_receive_failed(error, m_rx_window_id, true);
|
|
|
|
nrf_802154_critical_section_nesting_deny();
|
|
}
|
|
|
|
/** Notify MAC layer that transmission of requested frame has started. */
|
|
static void transmit_started_notify(void)
|
|
{
|
|
uint8_t * p_frame = mp_tx_data;
|
|
|
|
if (nrf_802154_core_hooks_tx_started(p_frame))
|
|
{
|
|
nrf_802154_tx_started(p_frame);
|
|
}
|
|
|
|
}
|
|
|
|
/** Notify MAC layer that transmission of ACK frame has started. */
|
|
static void transmit_ack_started_notify()
|
|
{
|
|
nrf_802154_core_hooks_tx_ack_started(mp_ack);
|
|
nrf_802154_tx_ack_started(mp_ack);
|
|
}
|
|
|
|
#if !NRF_802154_DISABLE_BCC_MATCHING
|
|
/** Notify that reception of a frame has started. */
|
|
static void receive_started_notify(void)
|
|
{
|
|
const uint8_t * p_frame = mp_current_rx_buffer->data;
|
|
|
|
nrf_802154_core_hooks_rx_started(p_frame);
|
|
}
|
|
|
|
#endif
|
|
|
|
/** Notify MAC layer that a frame was transmitted. */
|
|
static void transmitted_frame_notify(uint8_t * p_ack, int8_t power, uint8_t lqi)
|
|
{
|
|
uint8_t * p_frame = mp_tx_data;
|
|
nrf_802154_transmit_done_metadata_t metadata = {0};
|
|
|
|
metadata.data.transmitted.p_ack = p_ack;
|
|
metadata.data.transmitted.power = power;
|
|
metadata.data.transmitted.lqi = lqi;
|
|
|
|
if (p_ack == NULL)
|
|
{
|
|
metadata.data.transmitted.time = NRF_802154_NO_TIMESTAMP;
|
|
}
|
|
else
|
|
{
|
|
nrf_802154_stat_timestamp_read(&metadata.data.transmitted.time, last_ack_end_timestamp);
|
|
}
|
|
|
|
nrf_802154_critical_section_nesting_allow();
|
|
|
|
nrf_802154_core_hooks_transmitted(p_frame);
|
|
|
|
nrf_802154_notify_transmitted(p_frame, &metadata);
|
|
|
|
nrf_802154_critical_section_nesting_deny();
|
|
}
|
|
|
|
/** Notify MAC layer that transmission procedure failed. */
|
|
static void transmit_failed_notify(uint8_t * p_frame,
|
|
nrf_802154_tx_error_t error,
|
|
const nrf_802154_transmit_done_metadata_t * p_meta)
|
|
{
|
|
if (nrf_802154_core_hooks_tx_failed(p_frame, error))
|
|
{
|
|
nrf_802154_notify_transmit_failed(p_frame, error, p_meta);
|
|
}
|
|
}
|
|
|
|
/** Allow nesting critical sections and notify MAC layer that transmission procedure failed. */
|
|
static void transmit_failed_notify_and_nesting_allow(
|
|
nrf_802154_tx_error_t error,
|
|
const nrf_802154_transmit_done_metadata_t * p_meta)
|
|
{
|
|
nrf_802154_critical_section_nesting_allow();
|
|
|
|
transmit_failed_notify(mp_tx_data, error, p_meta);
|
|
|
|
nrf_802154_critical_section_nesting_deny();
|
|
}
|
|
|
|
/** Notify MAC layer that energy detection procedure ended. */
|
|
static void energy_detected_notify(uint8_t result)
|
|
{
|
|
nrf_802154_critical_section_nesting_allow();
|
|
|
|
nrf_802154_notify_energy_detected(result);
|
|
|
|
nrf_802154_critical_section_nesting_deny();
|
|
}
|
|
|
|
/** Notify MAC layer that CCA procedure ended. */
|
|
static void cca_notify(bool result)
|
|
{
|
|
nrf_802154_critical_section_nesting_allow();
|
|
|
|
nrf_802154_notify_cca(result);
|
|
|
|
nrf_802154_critical_section_nesting_deny();
|
|
}
|
|
|
|
/** Check if timeslot is currently granted.
|
|
*
|
|
* @retval true The timeslot is granted.
|
|
* @retval false The timeslot is not granted.
|
|
*/
|
|
static bool timeslot_is_granted(void)
|
|
{
|
|
return m_rsch_timeslot_is_granted;
|
|
}
|
|
|
|
static bool antenna_diversity_is_enabled(void)
|
|
{
|
|
return (NRF_802154_SL_ANT_DIV_MODE_DISABLED !=
|
|
nrf_802154_sl_ant_div_cfg_mode_get(NRF_802154_SL_ANT_DIV_OP_RX));
|
|
}
|
|
|
|
/***************************************************************************************************
|
|
* @section RX buffer management
|
|
**************************************************************************************************/
|
|
|
|
/** Set currently used rx buffer to given address.
|
|
*
|
|
* @param[in] p_rx_buffer Pointer to receive buffer that should be used now.
|
|
*/
|
|
static void rx_buffer_in_use_set(rx_buffer_t * p_rx_buffer)
|
|
{
|
|
#if NRF_802154_RX_BUFFERS > 1
|
|
mp_current_rx_buffer = p_rx_buffer;
|
|
#else
|
|
(void)p_rx_buffer;
|
|
#endif
|
|
}
|
|
|
|
/** Check if currently there is available rx buffer.
|
|
*
|
|
* @retval true There is available rx buffer.
|
|
* @retval false Currently there is no available rx buffer.
|
|
*/
|
|
static bool rx_buffer_is_available(void)
|
|
{
|
|
return (mp_current_rx_buffer != NULL) && (mp_current_rx_buffer->free);
|
|
}
|
|
|
|
/** Get pointer to available rx buffer.
|
|
*
|
|
* @returns Pointer to available rx buffer or NULL if rx buffer is not available.
|
|
*/
|
|
static uint8_t * rx_buffer_get(void)
|
|
{
|
|
return rx_buffer_is_available() ? mp_current_rx_buffer->data : NULL;
|
|
}
|
|
|
|
/***************************************************************************************************
|
|
* @section ACK transmission management
|
|
**************************************************************************************************/
|
|
|
|
/** Check if ACK is requested in given frame.
|
|
*
|
|
* @param[in] p_frame Pointer to a frame to check.
|
|
*
|
|
* @retval true ACK is requested in given frame.
|
|
* @retval false ACK is not requested in given frame.
|
|
*/
|
|
static bool ack_is_requested(const uint8_t * p_frame)
|
|
{
|
|
nrf_802154_frame_parser_data_t frame_data;
|
|
|
|
bool result = nrf_802154_frame_parser_data_init(p_frame,
|
|
p_frame[PHR_OFFSET] + PHR_SIZE,
|
|
PARSE_LEVEL_FCF_OFFSETS,
|
|
&frame_data);
|
|
|
|
return result && nrf_802154_frame_parser_ar_bit_is_set(&frame_data);
|
|
}
|
|
|
|
/***************************************************************************************************
|
|
* @section Energy detection management
|
|
**************************************************************************************************/
|
|
|
|
/** Setup next iteration of energy detection procedure.
|
|
*
|
|
* Energy detection procedure is performed in iterations to make sure it is performed for requested
|
|
* time regardless radio arbitration.
|
|
*
|
|
* @param[inout] p_requested_ed_time_us Remaining time of energy detection procedure [us]. Value will be updated
|
|
* with time remaining for the next attempt of energy detection.
|
|
* @param[out] p_next_trx_ed_count Number of trx energy detection iterations to perform.
|
|
*
|
|
* @retval true Next iteration of energy detection procedure will be performed now.
|
|
* @retval false Next iteration of energy detection procedure will not be performed now due to
|
|
* ending timeslot.
|
|
*/
|
|
static bool ed_iter_setup(uint32_t * p_requested_ed_time_us, uint32_t * p_next_trx_ed_count)
|
|
{
|
|
uint32_t iters_left_in_timeslot = nrf_802154_rsch_timeslot_us_left_get() / ED_ITER_DURATION;
|
|
|
|
if (iters_left_in_timeslot > ED_ITERS_OVERHEAD)
|
|
{
|
|
/* Note that in single phy iters_left_in_timeslot will always be very big thus we will get here. */
|
|
iters_left_in_timeslot -= ED_ITERS_OVERHEAD;
|
|
|
|
uint32_t requested_iters = *p_requested_ed_time_us / ED_ITER_DURATION;
|
|
|
|
if (requested_iters < iters_left_in_timeslot)
|
|
{
|
|
/* We will finish all iterations before timeslot end, thus no time is left */
|
|
*p_requested_ed_time_us = 0U;
|
|
}
|
|
else
|
|
{
|
|
*p_requested_ed_time_us = *p_requested_ed_time_us -
|
|
(iters_left_in_timeslot * ED_ITER_DURATION);
|
|
requested_iters = iters_left_in_timeslot;
|
|
}
|
|
|
|
*p_next_trx_ed_count = requested_iters;
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/***************************************************************************************************
|
|
* @section FSM transition request sub-procedures
|
|
**************************************************************************************************/
|
|
|
|
static rsch_prio_t min_required_rsch_prio(radio_state_t state)
|
|
{
|
|
switch (state)
|
|
{
|
|
case RADIO_STATE_SLEEP:
|
|
return RSCH_PRIO_IDLE;
|
|
|
|
case RADIO_STATE_FALLING_ASLEEP:
|
|
case RADIO_STATE_RX:
|
|
return RSCH_PRIO_IDLE_LISTENING;
|
|
|
|
case RADIO_STATE_RX_ACK:
|
|
case RADIO_STATE_ED:
|
|
case RADIO_STATE_CCA:
|
|
return RSCH_PRIO_RX;
|
|
|
|
case RADIO_STATE_TX:
|
|
case RADIO_STATE_TX_ACK:
|
|
#if NRF_802154_CARRIER_FUNCTIONS_ENABLED
|
|
case RADIO_STATE_CONTINUOUS_CARRIER:
|
|
case RADIO_STATE_MODULATED_CARRIER:
|
|
#endif // NRF_802154_CARRIER_FUNCTIONS_ENABLED
|
|
return RSCH_PRIO_TX;
|
|
|
|
case RADIO_STATE_CCA_TX:
|
|
if (m_flags.tx_diminished_prio)
|
|
{
|
|
return RSCH_PRIO_IDLE_LISTENING;
|
|
}
|
|
else
|
|
{
|
|
return RSCH_PRIO_TX;
|
|
}
|
|
|
|
default:
|
|
assert(false);
|
|
return RSCH_PRIO_IDLE;
|
|
}
|
|
}
|
|
|
|
static bool is_state_allowed_for_prio(rsch_prio_t prio, radio_state_t state)
|
|
{
|
|
return (min_required_rsch_prio(state) <= prio);
|
|
}
|
|
|
|
static bool are_preconditions_met(void)
|
|
{
|
|
rsch_prio_t current_prio;
|
|
radio_state_t current_state;
|
|
|
|
current_prio = m_rsch_priority;
|
|
current_state = m_state;
|
|
|
|
return is_state_allowed_for_prio(current_prio, current_state);
|
|
}
|
|
|
|
static int_fast8_t action_needed(rsch_prio_t old_prio, rsch_prio_t new_prio, radio_state_t state)
|
|
{
|
|
bool old_prio_allows = is_state_allowed_for_prio(old_prio, state);
|
|
bool new_prio_allows = is_state_allowed_for_prio(new_prio, state);
|
|
|
|
int_fast8_t result = 0;
|
|
|
|
if (old_prio_allows && !new_prio_allows)
|
|
{
|
|
result = -1;
|
|
}
|
|
else if (!old_prio_allows && new_prio_allows)
|
|
{
|
|
result = 1;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/** Check if time remaining in the timeslot is long enough to process whole critical section. */
|
|
static bool remaining_timeslot_time_is_enough_for_crit_sect(void)
|
|
{
|
|
return nrf_802154_rsch_timeslot_us_left_get() >= MAX_CRIT_SECT_TIME;
|
|
}
|
|
|
|
/** Check if critical section can be processed at the moment.
|
|
*
|
|
* @note This function returns valid result only inside critical section.
|
|
*
|
|
* @retval true There is enough time in current timeslot or timeslot is denied at the moment.
|
|
* @retval false Current timeslot ends too shortly to process critical section inside.
|
|
*/
|
|
static bool critical_section_can_be_processed_now(void)
|
|
{
|
|
return !timeslot_is_granted() || remaining_timeslot_time_is_enough_for_crit_sect();
|
|
}
|
|
|
|
/** Enter critical section and verify if there is enough time to complete operations within. */
|
|
static bool critical_section_enter_and_verify_timeslot_length(void)
|
|
{
|
|
bool result = nrf_802154_critical_section_enter();
|
|
|
|
if (result)
|
|
{
|
|
if (!critical_section_can_be_processed_now())
|
|
{
|
|
result = false;
|
|
|
|
nrf_802154_critical_section_exit();
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static bool can_terminate_current_operation(radio_state_t state,
|
|
nrf_802154_term_t term_lvl,
|
|
bool receiving_psdu_now)
|
|
{
|
|
bool result = false;
|
|
|
|
switch (state)
|
|
{
|
|
case RADIO_STATE_SLEEP:
|
|
case RADIO_STATE_FALLING_ASLEEP:
|
|
#if NRF_802154_CARRIER_FUNCTIONS_ENABLED
|
|
case RADIO_STATE_CONTINUOUS_CARRIER:
|
|
case RADIO_STATE_MODULATED_CARRIER:
|
|
#endif // NRF_802154_CARRIER_FUNCTIONS_ENABLED
|
|
result = true;
|
|
break;
|
|
|
|
case RADIO_STATE_RX:
|
|
result = (term_lvl >= NRF_802154_TERM_802154) || !receiving_psdu_now;
|
|
break;
|
|
|
|
case RADIO_STATE_TX_ACK:
|
|
case RADIO_STATE_CCA_TX:
|
|
case RADIO_STATE_TX:
|
|
case RADIO_STATE_RX_ACK:
|
|
case RADIO_STATE_ED:
|
|
case RADIO_STATE_CCA:
|
|
result = (term_lvl >= NRF_802154_TERM_802154);
|
|
break;
|
|
|
|
default:
|
|
assert(false);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static void operation_terminated_notify(radio_state_t state, bool receiving_psdu_now)
|
|
{
|
|
switch (state)
|
|
{
|
|
case RADIO_STATE_SLEEP:
|
|
case RADIO_STATE_FALLING_ASLEEP:
|
|
#if NRF_802154_CARRIER_FUNCTIONS_ENABLED
|
|
case RADIO_STATE_CONTINUOUS_CARRIER:
|
|
case RADIO_STATE_MODULATED_CARRIER:
|
|
#endif // NRF_802154_CARRIER_FUNCTIONS_ENABLED
|
|
break;
|
|
|
|
case RADIO_STATE_RX:
|
|
if (receiving_psdu_now)
|
|
{
|
|
// Don't care about the result - if the notification cannot be performed
|
|
// no impact on the device's operation is expected
|
|
(void)nrf_802154_notify_receive_failed(NRF_802154_RX_ERROR_ABORTED,
|
|
m_rx_window_id,
|
|
true);
|
|
}
|
|
|
|
break;
|
|
|
|
case RADIO_STATE_TX_ACK:
|
|
mp_current_rx_buffer->free = false;
|
|
nrf_802154_core_hooks_tx_ack_failed(mp_ack, NRF_802154_TX_ERROR_ABORTED);
|
|
received_frame_notify(mp_current_rx_buffer->data);
|
|
break;
|
|
|
|
case RADIO_STATE_CCA_TX:
|
|
case RADIO_STATE_TX:
|
|
{
|
|
nrf_802154_transmit_done_metadata_t metadata = {};
|
|
|
|
nrf_802154_tx_work_buffer_original_frame_update(mp_tx_data, &metadata.frame_props);
|
|
transmit_failed_notify(mp_tx_data, NRF_802154_TX_ERROR_ABORTED, &metadata);
|
|
}
|
|
break;
|
|
|
|
case RADIO_STATE_RX_ACK:
|
|
{
|
|
nrf_802154_transmit_done_metadata_t metadata = {};
|
|
|
|
nrf_802154_tx_work_buffer_original_frame_update(mp_tx_data, &metadata.frame_props);
|
|
transmit_failed_notify(mp_tx_data, NRF_802154_TX_ERROR_ABORTED, &metadata);
|
|
}
|
|
break;
|
|
|
|
case RADIO_STATE_ED:
|
|
nrf_802154_notify_energy_detection_failed(NRF_802154_ED_ERROR_ABORTED);
|
|
break;
|
|
|
|
case RADIO_STATE_CCA:
|
|
nrf_802154_notify_cca_failed(NRF_802154_CCA_ERROR_ABORTED);
|
|
break;
|
|
|
|
default:
|
|
assert(false);
|
|
}
|
|
}
|
|
|
|
#if (NRF_802154_TOTAL_TIMES_MEASUREMENT_ENABLED)
|
|
static void operation_terminated_update_total_times(trx_state_t trx_state, uint32_t timestamp)
|
|
{
|
|
uint32_t t;
|
|
|
|
switch (trx_state)
|
|
{
|
|
case TRX_STATE_RXFRAME:
|
|
case TRX_STATE_RXACK:
|
|
t = timestamp - m_listening_start_hp_timestamp;
|
|
nrf_802154_stat_totals_increment(total_listening_time, t);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static bool operation_terminated_update_total_times_is_required(trx_state_t trx_state)
|
|
{
|
|
switch (trx_state)
|
|
{
|
|
case TRX_STATE_RXFRAME:
|
|
case TRX_STATE_RXACK:
|
|
/* These cases must be in-sync with implementation of
|
|
* operation_terminated_update_total_times
|
|
*/
|
|
return true;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
static void trx_abort(void)
|
|
{
|
|
#if (NRF_802154_TOTAL_TIMES_MEASUREMENT_ENABLED)
|
|
trx_state_t trx_state = nrf_802154_trx_state_get();
|
|
bool update_required = operation_terminated_update_total_times_is_required(trx_state);
|
|
|
|
#endif
|
|
|
|
nrf_802154_trx_abort();
|
|
|
|
#if (NRF_802154_TOTAL_TIMES_MEASUREMENT_ENABLED)
|
|
if (update_required)
|
|
{
|
|
uint32_t timestamp = nrf_802154_hp_timer_current_time_get();
|
|
|
|
operation_terminated_update_total_times(trx_state, timestamp);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static void trx_disable(void)
|
|
{
|
|
#if (NRF_802154_TOTAL_TIMES_MEASUREMENT_ENABLED)
|
|
trx_state_t trx_state = nrf_802154_trx_state_get();
|
|
bool update_required = operation_terminated_update_total_times_is_required(trx_state);
|
|
|
|
#endif
|
|
|
|
nrf_802154_trx_disable();
|
|
|
|
#if (NRF_802154_TOTAL_TIMES_MEASUREMENT_ENABLED)
|
|
if (update_required)
|
|
{
|
|
uint32_t timestamp = nrf_802154_hp_timer_current_time_get();
|
|
|
|
operation_terminated_update_total_times(trx_state, timestamp);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/** Terminate ongoing operation.
|
|
*
|
|
* This function is called when MAC layer requests transition to another operation.
|
|
*
|
|
* After calling this function RADIO should enter DISABLED state and Radio Scheduler
|
|
* should be in continuous mode.
|
|
*
|
|
* @param[in] term_lvl Termination level of this request. Selects procedures to abort.
|
|
* @param[in] req_orig Module that originates termination request.
|
|
* @param[in] notify_abort If Termination of current operation shall be notified.
|
|
*
|
|
* @retval true Terminated ongoing operation.
|
|
* @retval false Ongoing operation was not terminated.
|
|
*/
|
|
static bool current_operation_terminate(nrf_802154_term_t term_lvl,
|
|
req_originator_t req_orig,
|
|
bool notify)
|
|
{
|
|
bool result = nrf_802154_core_hooks_terminate(term_lvl, req_orig);
|
|
|
|
if (result)
|
|
{
|
|
bool receiving_psdu_now = false;
|
|
|
|
if (m_state == RADIO_STATE_RX)
|
|
{
|
|
receiving_psdu_now = nrf_802154_trx_psdu_is_being_received();
|
|
}
|
|
|
|
result = can_terminate_current_operation(m_state, term_lvl, receiving_psdu_now);
|
|
|
|
if (result)
|
|
{
|
|
trx_abort();
|
|
|
|
if (m_state == RADIO_STATE_RX)
|
|
{
|
|
/* When in rx mode, nrf_802154_trx_receive_frame_prestarted handler might
|
|
* have already been called. We need to stop counting timeout. */
|
|
m_rx_prestarted_trig_count = 0;
|
|
(void)nrf_802154_sl_timer_remove(&m_rx_prestarted_timer);
|
|
|
|
/* Notify antenna diversity module that RX has been aborted. */
|
|
nrf_802154_sl_ant_div_rx_aborted_notify();
|
|
|
|
/* We might have boosted preconditions (to support coex) above level
|
|
* normally requested for current state by request_preconditions_for_state(m_state).
|
|
* When current operation is terminated we request preconditions back
|
|
* thus ceasing to request to coex. */
|
|
request_preconditions_for_state(m_state);
|
|
}
|
|
|
|
if (m_state == RADIO_STATE_ED)
|
|
{
|
|
nrf_802154_sl_ant_div_energy_detection_aborted_notify();
|
|
}
|
|
|
|
if (notify)
|
|
{
|
|
operation_terminated_notify(m_state, receiving_psdu_now);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/** Enter Sleep state. */
|
|
static void sleep_init(void)
|
|
{
|
|
// This function is always executed from a critical section, so this check is safe.
|
|
if (timeslot_is_granted())
|
|
{
|
|
nrf_802154_timer_coord_stop();
|
|
}
|
|
}
|
|
|
|
/** Initialize Falling Asleep operation. */
|
|
static void falling_asleep_init(void)
|
|
{
|
|
if (nrf_802154_trx_go_idle())
|
|
{
|
|
// There will be nrf_802154_trx_in_idle call, where we will continue processing
|
|
}
|
|
else
|
|
{
|
|
sleep_init();
|
|
state_set(RADIO_STATE_SLEEP);
|
|
}
|
|
}
|
|
|
|
/**@brief Makes value to be passed to @ref nrf_802154_trx_receive_frame as @c notifications_mask parameter */
|
|
static nrf_802154_trx_receive_notifications_t make_trx_frame_receive_notification_mask(void)
|
|
{
|
|
nrf_802154_trx_receive_notifications_t result = TRX_RECEIVE_NOTIFICATION_NONE;
|
|
|
|
#if (NRF_802154_STATS_COUNT_ENERGY_DETECTED_EVENTS)
|
|
result |= TRX_RECEIVE_NOTIFICATION_PRESTARTED;
|
|
#endif
|
|
#if (NRF_802154_STATS_COUNT_RECEIVED_PREAMBLES)
|
|
result |= TRX_RECEIVE_NOTIFICATION_STARTED;
|
|
#endif
|
|
|
|
if (nrf_802154_wifi_coex_is_enabled())
|
|
{
|
|
switch (nrf_802154_pib_coex_rx_request_mode_get())
|
|
{
|
|
case NRF_802154_COEX_RX_REQUEST_MODE_DESTINED:
|
|
/* Coex requesting handled through nrf_802154_trx_receive_frame_bcmatched handler.
|
|
* No additional notifications required. */
|
|
break;
|
|
|
|
case NRF_802154_COEX_RX_REQUEST_MODE_ENERGY_DETECTION:
|
|
result |= TRX_RECEIVE_NOTIFICATION_PRESTARTED | TRX_RECEIVE_NOTIFICATION_STARTED;
|
|
// Note: TRX_RECEIVE_NOTIFICATION_STARTED is required for stopping counting timeout for
|
|
// activity triggered by nrf_802154_trx_receive_frame_prestarted.
|
|
break;
|
|
|
|
case NRF_802154_COEX_RX_REQUEST_MODE_PREAMBLE:
|
|
result |= TRX_RECEIVE_NOTIFICATION_STARTED;
|
|
break;
|
|
|
|
default:
|
|
assert(false);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**@brief Makes value to be passed to @ref nrf_802154_trx_transmit_frame as @c notifications_mask parameter
|
|
*
|
|
* @param[in] cca Pass true, if cca operation it to be performed before transmit.
|
|
* Pass false otherwise.
|
|
*/
|
|
static nrf_802154_trx_transmit_notifications_t make_trx_frame_transmit_notification_mask(bool cca)
|
|
{
|
|
nrf_802154_trx_transmit_notifications_t result = TRX_TRANSMIT_NOTIFICATION_NONE;
|
|
|
|
#if (NRF_802154_FRAME_TIMESTAMP_ENABLED)
|
|
if (cca)
|
|
{
|
|
result |= TRX_TRANSMIT_NOTIFICATION_CCAIDLE;
|
|
}
|
|
#endif
|
|
|
|
if (nrf_802154_wifi_coex_is_enabled())
|
|
{
|
|
switch (nrf_802154_pib_coex_tx_request_mode_get())
|
|
{
|
|
case NRF_802154_COEX_TX_REQUEST_MODE_FRAME_READY:
|
|
case NRF_802154_COEX_TX_REQUEST_MODE_CCA_START:
|
|
case NRF_802154_COEX_TX_REQUEST_MODE_ON_CCA_TOGGLE:
|
|
/* No additional notifications required. */
|
|
break;
|
|
|
|
case NRF_802154_COEX_TX_REQUEST_MODE_CCA_DONE:
|
|
result |= TRX_TRANSMIT_NOTIFICATION_CCAIDLE;
|
|
break;
|
|
|
|
default:
|
|
assert(false);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/** Initialize RX operation. */
|
|
static void rx_init(void)
|
|
{
|
|
bool free_buffer;
|
|
|
|
if (!timeslot_is_granted())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!are_preconditions_met())
|
|
{
|
|
return;
|
|
}
|
|
|
|
// Clear filtering flag
|
|
rx_flags_clear();
|
|
|
|
// Find available RX buffer
|
|
free_buffer = rx_buffer_is_available();
|
|
|
|
nrf_802154_trx_receive_buffer_set(rx_buffer_get());
|
|
|
|
nrf_802154_fal_tx_power_split_t split_power = {0};
|
|
|
|
(void)nrf_802154_tx_power_split_pib_power_get(&split_power);
|
|
|
|
nrf_802154_trx_receive_frame(BCC_INIT / 8U,
|
|
m_trx_receive_frame_notifications_mask,
|
|
&split_power);
|
|
|
|
#if NRF_802154_TOTAL_TIMES_MEASUREMENT_ENABLED
|
|
m_listening_start_hp_timestamp = nrf_802154_hp_timer_current_time_get();
|
|
#endif
|
|
|
|
#if (NRF_802154_FRAME_TIMESTAMP_ENABLED)
|
|
#if (NRF_802154_TOTAL_TIMES_MEASUREMENT_ENABLED)
|
|
// Configure the timer coordinator to get a timestamp of the END event which
|
|
// fires several cycles after CRCOK or CRCERROR events.
|
|
nrf_802154_timer_coord_timestamp_prepare(nrf_802154_trx_radio_end_event_handle_get());
|
|
#else
|
|
// Configure the timer coordinator to get a timestamp of the CRCOK event.
|
|
nrf_802154_timer_coord_timestamp_prepare(nrf_802154_trx_radio_crcok_event_handle_get());
|
|
#endif
|
|
#endif
|
|
|
|
// Find RX buffer if none available
|
|
if (!free_buffer)
|
|
{
|
|
rx_buffer_in_use_set(nrf_802154_rx_buffer_free_find());
|
|
|
|
nrf_802154_trx_receive_buffer_set(rx_buffer_get());
|
|
}
|
|
|
|
rx_data_clear();
|
|
|
|
mp_ack = NULL;
|
|
}
|
|
|
|
/** Initialize TX operation. */
|
|
static bool tx_init(const uint8_t * p_data, bool cca)
|
|
{
|
|
if (!timeslot_is_granted() || !nrf_802154_rsch_timeslot_request(
|
|
nrf_802154_tx_duration_get(p_data[0], cca, ack_is_requested(p_data))))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!are_preconditions_met())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
#if (NRF_802154_FRAME_TIMESTAMP_ENABLED)
|
|
if (cca)
|
|
{
|
|
// Configure the timer coordinator to get a time stamp of the READY event.
|
|
// Note: This event triggers CCASTART, so the time stamp of READY event
|
|
// is the time stamp when CCA started.
|
|
nrf_802154_timer_coord_timestamp_prepare(nrf_802154_trx_radio_ready_event_handle_get());
|
|
}
|
|
else
|
|
{
|
|
// Configure the timer coordinator to get a time stamp of the PHYEND event.
|
|
nrf_802154_timer_coord_timestamp_prepare(nrf_802154_trx_radio_phyend_event_handle_get());
|
|
}
|
|
#endif
|
|
|
|
m_flags.tx_with_cca = cca;
|
|
nrf_802154_trx_transmit_frame(nrf_802154_tx_work_buffer_get(p_data),
|
|
cca,
|
|
&m_tx_power,
|
|
m_trx_transmit_frame_notifications_mask);
|
|
|
|
return true;
|
|
}
|
|
|
|
/** Initialize ED operation */
|
|
static void ed_init(void)
|
|
{
|
|
if (!timeslot_is_granted())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!are_preconditions_met())
|
|
{
|
|
return;
|
|
}
|
|
|
|
uint32_t trx_ed_count = 0U;
|
|
|
|
// Notify antenna diversity about energy detection request. Antenna diversity state
|
|
// will be updated, and m_ed_time_left reduced accordingly.
|
|
nrf_802154_sl_ant_div_energy_detection_requested_notify(&m_ed_time_left);
|
|
|
|
if (!ed_iter_setup(&m_ed_time_left, &trx_ed_count))
|
|
{
|
|
// Just wait for next timeslot if there is not enough time in this one.
|
|
return;
|
|
}
|
|
|
|
nrf_802154_trx_energy_detection(trx_ed_count);
|
|
}
|
|
|
|
/** Initialize CCA operation. */
|
|
static void cca_init(void)
|
|
{
|
|
if (!timeslot_is_granted() || !nrf_802154_rsch_timeslot_request(nrf_802154_cca_duration_get()))
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!are_preconditions_met())
|
|
{
|
|
return;
|
|
}
|
|
|
|
nrf_802154_trx_standalone_cca();
|
|
}
|
|
|
|
#if NRF_802154_CARRIER_FUNCTIONS_ENABLED
|
|
|
|
/** Initialize Continuous Carrier operation. */
|
|
static void continuous_carrier_init(void)
|
|
{
|
|
if (!timeslot_is_granted())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!are_preconditions_met())
|
|
{
|
|
return;
|
|
}
|
|
nrf_802154_fal_tx_power_split_t split_power = {0};
|
|
|
|
(void)nrf_802154_tx_power_split_pib_power_get(&split_power);
|
|
|
|
nrf_802154_trx_continuous_carrier(&split_power);
|
|
}
|
|
|
|
/** Initialize Modulated Carrier operation. */
|
|
static void modulated_carrier_init(const uint8_t * p_data)
|
|
{
|
|
if (!timeslot_is_granted())
|
|
{
|
|
return;
|
|
}
|
|
|
|
if (!are_preconditions_met())
|
|
{
|
|
return;
|
|
}
|
|
|
|
nrf_802154_fal_tx_power_split_t split_power = {0};
|
|
|
|
(void)nrf_802154_tx_power_split_pib_power_get(&split_power);
|
|
|
|
nrf_802154_trx_modulated_carrier(p_data, &split_power);
|
|
}
|
|
|
|
#endif // NRF_802154_CARRIER_FUNCTIONS_ENABLED
|
|
|
|
/***************************************************************************************************
|
|
* @section Radio Scheduler notification handlers
|
|
**************************************************************************************************/
|
|
static void on_timeslot_ended(void)
|
|
{
|
|
bool result;
|
|
|
|
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
if (timeslot_is_granted())
|
|
{
|
|
m_rsch_timeslot_is_granted = false;
|
|
|
|
bool receiving_psdu_now = false;
|
|
|
|
if (m_state == RADIO_STATE_RX)
|
|
{
|
|
receiving_psdu_now = nrf_802154_trx_psdu_is_being_received();
|
|
}
|
|
|
|
trx_disable();
|
|
|
|
nrf_802154_timer_coord_stop();
|
|
|
|
nrf_802154_rsch_continuous_ended();
|
|
|
|
result = nrf_802154_core_hooks_terminate(NRF_802154_TERM_802154, REQ_ORIG_RSCH);
|
|
assert(result);
|
|
(void)result;
|
|
|
|
switch (m_state)
|
|
{
|
|
case RADIO_STATE_FALLING_ASLEEP:
|
|
state_set(RADIO_STATE_SLEEP);
|
|
break;
|
|
|
|
case RADIO_STATE_RX:
|
|
if (receiving_psdu_now)
|
|
{
|
|
receive_failed_notify(NRF_802154_RX_ERROR_TIMESLOT_ENDED);
|
|
}
|
|
|
|
break;
|
|
|
|
case RADIO_STATE_TX_ACK:
|
|
state_set(RADIO_STATE_RX);
|
|
mp_current_rx_buffer->free = false;
|
|
nrf_802154_core_hooks_tx_ack_failed(mp_ack, NRF_802154_TX_ERROR_TIMESLOT_ENDED);
|
|
received_frame_notify_and_nesting_allow(mp_current_rx_buffer->data);
|
|
break;
|
|
|
|
case RADIO_STATE_CCA_TX:
|
|
case RADIO_STATE_TX:
|
|
case RADIO_STATE_RX_ACK:
|
|
{
|
|
state_set(RADIO_STATE_RX);
|
|
nrf_802154_transmit_done_metadata_t metadata = {};
|
|
|
|
nrf_802154_tx_work_buffer_original_frame_update(mp_tx_data, &metadata.frame_props);
|
|
transmit_failed_notify_and_nesting_allow(NRF_802154_TX_ERROR_TIMESLOT_ENDED,
|
|
&metadata);
|
|
}
|
|
break;
|
|
|
|
case RADIO_STATE_ED:
|
|
case RADIO_STATE_CCA:
|
|
#if NRF_802154_CARRIER_FUNCTIONS_ENABLED
|
|
case RADIO_STATE_CONTINUOUS_CARRIER:
|
|
case RADIO_STATE_MODULATED_CARRIER:
|
|
#endif // NRF_802154_CARRIER_FUNCTIONS_ENABLED
|
|
case RADIO_STATE_SLEEP:
|
|
// Intentionally empty.
|
|
break;
|
|
|
|
default:
|
|
assert(false);
|
|
}
|
|
}
|
|
|
|
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
|
|
}
|
|
|
|
static void on_preconditions_denied(radio_state_t state)
|
|
{
|
|
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
bool result;
|
|
|
|
result = nrf_802154_core_hooks_terminate(NRF_802154_TERM_802154, REQ_ORIG_CORE);
|
|
assert(result);
|
|
(void)result;
|
|
|
|
bool receiving_psdu_now = false;
|
|
|
|
if (state == RADIO_STATE_RX)
|
|
{
|
|
receiving_psdu_now = nrf_802154_trx_psdu_is_being_received();
|
|
}
|
|
|
|
trx_abort();
|
|
|
|
switch (state)
|
|
{
|
|
case RADIO_STATE_FALLING_ASLEEP:
|
|
// There should be on_timeslot_ended event
|
|
break;
|
|
|
|
case RADIO_STATE_RX:
|
|
if (receiving_psdu_now)
|
|
{
|
|
receive_failed_notify(NRF_802154_RX_ERROR_ABORTED);
|
|
}
|
|
|
|
break;
|
|
|
|
case RADIO_STATE_TX_ACK:
|
|
state_set(RADIO_STATE_RX);
|
|
break;
|
|
|
|
case RADIO_STATE_CCA_TX:
|
|
m_flags.tx_diminished_prio = false;
|
|
// Fallthrough
|
|
|
|
case RADIO_STATE_TX:
|
|
case RADIO_STATE_RX_ACK:
|
|
state_set(RADIO_STATE_RX);
|
|
break;
|
|
|
|
case RADIO_STATE_ED:
|
|
case RADIO_STATE_CCA:
|
|
#if NRF_802154_CARRIER_FUNCTIONS_ENABLED
|
|
case RADIO_STATE_CONTINUOUS_CARRIER:
|
|
case RADIO_STATE_MODULATED_CARRIER:
|
|
#endif // NRF_802154_CARRIER_FUNCTIONS_ENABLED
|
|
case RADIO_STATE_SLEEP:
|
|
// Intentionally empty.
|
|
break;
|
|
|
|
default:
|
|
assert(false);
|
|
}
|
|
|
|
operation_terminated_notify(state, receiving_psdu_now);
|
|
|
|
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
|
|
}
|
|
|
|
static void on_preconditions_approved(radio_state_t state)
|
|
{
|
|
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
trx_abort();
|
|
|
|
switch (state)
|
|
{
|
|
case RADIO_STATE_SLEEP:
|
|
// Intentionally empty. Appropriate action will be performed on state change.
|
|
break;
|
|
|
|
case RADIO_STATE_RX:
|
|
rx_init();
|
|
break;
|
|
|
|
case RADIO_STATE_CCA_TX:
|
|
(void)tx_init(mp_tx_data, true);
|
|
break;
|
|
|
|
case RADIO_STATE_TX:
|
|
(void)tx_init(mp_tx_data, false);
|
|
break;
|
|
|
|
case RADIO_STATE_ED:
|
|
ed_init();
|
|
break;
|
|
|
|
case RADIO_STATE_CCA:
|
|
cca_init();
|
|
break;
|
|
|
|
#if NRF_802154_CARRIER_FUNCTIONS_ENABLED
|
|
case RADIO_STATE_CONTINUOUS_CARRIER:
|
|
continuous_carrier_init();
|
|
break;
|
|
|
|
case RADIO_STATE_MODULATED_CARRIER:
|
|
modulated_carrier_init(mp_tx_data);
|
|
break;
|
|
#endif // NRF_802154_CARRIER_FUNCTIONS_ENABLED
|
|
|
|
default:
|
|
assert(false);
|
|
}
|
|
|
|
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
|
|
}
|
|
|
|
static void on_timeslot_started(void)
|
|
{
|
|
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
nrf_802154_trx_enable();
|
|
|
|
m_rsch_timeslot_is_granted = true;
|
|
|
|
nrf_802154_timer_coord_start();
|
|
|
|
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
|
|
}
|
|
|
|
static bool preconditions_approved_should_be_ignored(rsch_prio_t previously_approved_prio,
|
|
rsch_prio_t currently_approved_prio)
|
|
{
|
|
// Approved preconditions should only be ignored only all the following conditions are met:
|
|
// * all preconditions apart from Coex had already been approved;
|
|
// * the call is a result of Coex becoming approved at the highest priority;
|
|
// * currently performed operation is transmission with CCA;
|
|
// * Coex for transmission is requested after CCA reports idle channel
|
|
bool only_coex_was_unapproved = (previously_approved_prio == RSCH_PRIO_RX);
|
|
bool all_preconditions_are_approved = (currently_approved_prio == RSCH_PRIO_MAX);
|
|
bool current_state_is_cca_tx = (m_state == RADIO_STATE_CCA_TX);
|
|
bool coex_tx_request_mode_allows = (m_coex_tx_request_mode ==
|
|
NRF_802154_COEX_TX_REQUEST_MODE_CCA_DONE);
|
|
|
|
return (only_coex_was_unapproved &&
|
|
all_preconditions_are_approved &&
|
|
current_state_is_cca_tx &&
|
|
coex_tx_request_mode_allows);
|
|
}
|
|
|
|
void nrf_802154_rsch_crit_sect_prio_changed(rsch_prio_t prio)
|
|
{
|
|
rsch_prio_t old_prio = m_rsch_priority;
|
|
|
|
m_rsch_priority = prio;
|
|
|
|
if ((old_prio == RSCH_PRIO_IDLE) && (prio != RSCH_PRIO_IDLE))
|
|
{
|
|
// We have just got a timeslot.
|
|
on_timeslot_started();
|
|
}
|
|
else if ((old_prio != RSCH_PRIO_IDLE) && (prio == RSCH_PRIO_IDLE))
|
|
{
|
|
// We are giving back timeslot.
|
|
on_timeslot_ended();
|
|
return;
|
|
}
|
|
else if (prio == RSCH_PRIO_IDLE)
|
|
{
|
|
// It might happen that even though IDLE has already been notified, this function is called
|
|
// again as a result of preemptions caused by unexpected timeslot change (e.g. the application
|
|
// requested transition to sleep while out of timeslot and RAAL notified timeslot start
|
|
// in the middle of that sleep request). The following block makes RAAL finish its processing.
|
|
nrf_802154_rsch_continuous_ended();
|
|
}
|
|
else
|
|
{
|
|
// Intentionally empty
|
|
}
|
|
|
|
int_fast8_t transition = action_needed(old_prio, prio, m_state);
|
|
|
|
if (transition == 0)
|
|
{
|
|
return;
|
|
}
|
|
else if (transition < 0)
|
|
{
|
|
on_preconditions_denied(m_state);
|
|
|
|
// After denying preconditions, TRX is disabled. However, it is possible that the existing
|
|
// preconditions are enough for the new state (entered due to denied preconditions) and TRX
|
|
// could be enabled for the new state. If this is the case, on_preconditions_approved() is
|
|
// called to fully switch to the new state.
|
|
radio_state_t new_state = m_state;
|
|
|
|
if (is_state_allowed_for_prio(prio, new_state))
|
|
{
|
|
on_preconditions_approved(new_state);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!preconditions_approved_should_be_ignored(old_prio, prio))
|
|
{
|
|
on_preconditions_approved(m_state);
|
|
}
|
|
}
|
|
}
|
|
|
|
/***************************************************************************************************
|
|
* @section RADIO interrupt handler
|
|
**************************************************************************************************/
|
|
|
|
void nrf_802154_trx_receive_ack_started(void)
|
|
{
|
|
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
assert(m_state == RADIO_STATE_RX_ACK);
|
|
nrf_802154_core_hooks_rx_ack_started();
|
|
|
|
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
|
|
}
|
|
|
|
static void on_rx_prestarted_timeout(nrf_802154_sl_timer_t * p_timer)
|
|
{
|
|
(void)p_timer;
|
|
|
|
/* If we were in critical section this handler would not be called.
|
|
* If we were outside of critical section this handler could be called,
|
|
* and nrf_802154_critical_section_enter must succeed.
|
|
* Justification:
|
|
* - If higher priority interrupt preempts this handler before it takes critical section, that
|
|
* interrupt may enter/exit critical section, but when it returns to this handler
|
|
* entering critical section will succeed.
|
|
* - If we entered critical section here the higher priority interrupt from radio
|
|
* will not occur.
|
|
* - The only related interrupt that can preempt this handler while it owns critical section
|
|
* is from raal timeslot margin, which will fail to enter critical section and schedule
|
|
* priority change to be called by nrf_802154_critical_section_exit.
|
|
*
|
|
* Critical section is entered forcefully here nonetheless, due to a rare issue with
|
|
* nrf_802154_critical_section_exit being preempted before the nested critical section counter
|
|
* could be decremented. Allowing for critical section nesting here resolves the problem.
|
|
* TODO: After the bug is fixed, change to nrf_802154_critical_section_enter and check if
|
|
* critical section was successfully entered.
|
|
*/
|
|
|
|
nrf_802154_critical_section_forcefully_enter();
|
|
|
|
nrf_802154_sl_ant_div_rx_preamble_timeout_notify();
|
|
|
|
do
|
|
{
|
|
/**
|
|
* Multiple, rapidly consecutive reception start events without later moving to the next
|
|
* step are possible. There is a special handling for this case here.
|
|
*/
|
|
if (m_rx_prestarted_trig_count > 1)
|
|
{
|
|
uint64_t now = nrf_802154_sl_timer_current_time_get();
|
|
|
|
/**
|
|
* If the value of trigger_time field is a moment in the future, it means that timer
|
|
* handling has been preempted by HELPER1 radio event after removing the timer from
|
|
* scheduler, but before handling this callback. In that case, process the timeout
|
|
* as usual, but notify antenna diversity module that another preamble was detected
|
|
* in order to repeat RSSI measurements.
|
|
*/
|
|
if (nrf_802154_sl_time64_is_in_future(now, m_rx_prestarted_timer.trigger_time))
|
|
{
|
|
m_rx_prestarted_trig_count = 1;
|
|
nrf_802154_sl_ant_div_rx_preamble_detected_notify();
|
|
break;
|
|
}
|
|
}
|
|
|
|
m_rx_prestarted_trig_count = 0;
|
|
}
|
|
while (0);
|
|
|
|
/* If nrf_802154_trx_receive_frame_prestarted boosted preconditions beyond those normally
|
|
* required by current state, they need to be restored now.
|
|
*/
|
|
if (nrf_802154_pib_coex_rx_request_mode_get() ==
|
|
NRF_802154_COEX_RX_REQUEST_MODE_ENERGY_DETECTION)
|
|
{
|
|
request_preconditions_for_state(m_state);
|
|
}
|
|
nrf_802154_critical_section_exit();
|
|
}
|
|
|
|
void nrf_802154_trx_receive_frame_prestarted(void)
|
|
{
|
|
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
if (!antenna_diversity_is_enabled())
|
|
{
|
|
// Only assert if notifications mask would not allow for calling this function.
|
|
assert((m_trx_receive_frame_notifications_mask & TRX_RECEIVE_NOTIFICATION_PRESTARTED) !=
|
|
0U);
|
|
}
|
|
else
|
|
{
|
|
// Antenna diversity uses this function for detecting possible preamble on air.
|
|
}
|
|
|
|
assert(m_state == RADIO_STATE_RX);
|
|
|
|
#if (NRF_802154_STATS_COUNT_ENERGY_DETECTED_EVENTS)
|
|
nrf_802154_stat_counter_increment(received_energy_events);
|
|
#endif
|
|
|
|
nrf_802154_sl_ant_div_rx_preamble_detected_notify();
|
|
|
|
// Antenna diversity module should be notified if framestart doesn't come.
|
|
bool rx_timeout_should_be_started = antenna_diversity_is_enabled();
|
|
|
|
if (nrf_802154_pib_coex_rx_request_mode_get() ==
|
|
NRF_802154_COEX_RX_REQUEST_MODE_ENERGY_DETECTION)
|
|
{
|
|
// Request boosted preconditions for receive
|
|
nrf_802154_rsch_crit_sect_prio_request(RSCH_PRIO_RX);
|
|
// Boosted preconditions should be reverted if the framestart doesn't come.
|
|
rx_timeout_should_be_started = true;
|
|
}
|
|
|
|
/*
|
|
* This handler might not be followed by nrf_802154_trx_receive_frame_started. The timer
|
|
* below is used for timing out if the framestart doesn't come.
|
|
* There are two reasons for that: reverting boosted preconditions and notifying antenna diversity
|
|
* module.
|
|
*/
|
|
if (rx_timeout_should_be_started)
|
|
{
|
|
|
|
uint64_t now = nrf_802154_sl_timer_current_time_get();
|
|
|
|
(void)nrf_802154_sl_timer_remove(&m_rx_prestarted_timer);
|
|
|
|
m_rx_prestarted_timer.trigger_time = now + PRESTARTED_TIMER_TIMEOUT_US;
|
|
m_rx_prestarted_timer.action_type = NRF_802154_SL_TIMER_ACTION_TYPE_CALLBACK;
|
|
m_rx_prestarted_timer.action.callback.callback = on_rx_prestarted_timeout;
|
|
|
|
nrf_802154_sl_timer_ret_t ret;
|
|
|
|
ret = nrf_802154_sl_timer_add(&m_rx_prestarted_timer);
|
|
assert(ret == NRF_802154_SL_TIMER_RET_SUCCESS);
|
|
(void)ret;
|
|
|
|
m_rx_prestarted_trig_count += 1;
|
|
}
|
|
|
|
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
|
|
}
|
|
|
|
void nrf_802154_trx_receive_frame_started(void)
|
|
{
|
|
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
assert(m_state == RADIO_STATE_RX);
|
|
assert((m_trx_receive_frame_notifications_mask & TRX_RECEIVE_NOTIFICATION_STARTED) != 0U);
|
|
|
|
#if (NRF_802154_STATS_COUNT_RECEIVED_PREAMBLES)
|
|
nrf_802154_stat_counter_increment(received_preambles);
|
|
#endif
|
|
|
|
switch (nrf_802154_pib_coex_rx_request_mode_get())
|
|
{
|
|
case NRF_802154_COEX_RX_REQUEST_MODE_ENERGY_DETECTION:
|
|
m_rx_prestarted_trig_count = 0;
|
|
(void)nrf_802154_sl_timer_remove(&m_rx_prestarted_timer);
|
|
/* Fallthrough */
|
|
|
|
case NRF_802154_COEX_RX_REQUEST_MODE_PREAMBLE:
|
|
/* Request boosted preconditions */
|
|
nrf_802154_rsch_crit_sect_prio_request(RSCH_PRIO_RX);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (antenna_diversity_is_enabled())
|
|
{
|
|
// If antenna diversity is enabled, rx_prestarted_timer would be started even
|
|
// in different coex rx request modes than NRF_802154_COEX_RX_REQUEST_MODE_ENERGY_DETECTION
|
|
m_rx_prestarted_trig_count = 0;
|
|
(void)nrf_802154_sl_timer_remove(&m_rx_prestarted_timer);
|
|
nrf_802154_sl_ant_div_rx_frame_started_notify();
|
|
}
|
|
|
|
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
|
|
}
|
|
|
|
#if !NRF_802154_DISABLE_BCC_MATCHING
|
|
uint8_t nrf_802154_trx_receive_frame_bcmatched(uint8_t bcc)
|
|
{
|
|
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
uint8_t prev_num_data_bytes;
|
|
uint8_t num_data_bytes;
|
|
nrf_802154_rx_error_t filter_result;
|
|
bool frame_accepted = true;
|
|
|
|
num_data_bytes = bcc;
|
|
prev_num_data_bytes = num_data_bytes;
|
|
|
|
assert(num_data_bytes >= PHR_SIZE + FCF_SIZE);
|
|
assert(m_state == RADIO_STATE_RX);
|
|
|
|
if (!m_flags.frame_filtered)
|
|
{
|
|
filter_result = nrf_802154_filter_frame_part(&m_current_rx_frame_data,
|
|
&num_data_bytes);
|
|
|
|
if (filter_result == NRF_802154_RX_ERROR_NONE)
|
|
{
|
|
if (num_data_bytes != prev_num_data_bytes)
|
|
{
|
|
bcc = num_data_bytes;
|
|
}
|
|
else
|
|
{
|
|
m_flags.frame_filtered = true;
|
|
|
|
/* Request boosted preconditions */
|
|
nrf_802154_rsch_crit_sect_prio_request(RSCH_PRIO_RX);
|
|
|
|
/* Request next bcc match event to occur when basic data necessary for Ack
|
|
* generation is received. */
|
|
m_flags.frame_parsed = false;
|
|
nrf_802154_ack_generator_reset();
|
|
|
|
bcc = PHR_SIZE +
|
|
nrf_802154_frame_parser_addressing_end_offset_get(&m_current_rx_frame_data) +
|
|
SECURITY_CONTROL_SIZE;
|
|
}
|
|
}
|
|
else if ((filter_result == NRF_802154_RX_ERROR_INVALID_LENGTH) ||
|
|
(!nrf_802154_pib_promiscuous_get()))
|
|
{
|
|
trx_abort();
|
|
rx_init();
|
|
|
|
frame_accepted = false;
|
|
|
|
/* Release boosted preconditions */
|
|
request_preconditions_for_state(m_state);
|
|
|
|
if ((mp_current_rx_buffer->data[FRAME_TYPE_OFFSET] & FRAME_TYPE_MASK) !=
|
|
FRAME_TYPE_ACK)
|
|
{
|
|
receive_failed_notify(filter_result);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Promiscuous mode, allow incorrect frames. Nothing to do here.
|
|
}
|
|
}
|
|
else if (!m_flags.frame_parsed)
|
|
{
|
|
if (nrf_802154_frame_parser_security_enabled_bit_is_set(&m_current_rx_frame_data))
|
|
{
|
|
if (nrf_802154_frame_parser_valid_data_extend(
|
|
&m_current_rx_frame_data,
|
|
num_data_bytes,
|
|
PARSE_LEVEL_AUX_SEC_HDR_END))
|
|
{
|
|
// All security fields have been received.
|
|
m_flags.frame_parsed = true;
|
|
}
|
|
else if (nrf_802154_frame_parser_valid_data_extend(
|
|
&m_current_rx_frame_data,
|
|
num_data_bytes,
|
|
PARSE_LEVEL_SEC_CTRL_OFFSETS))
|
|
{
|
|
// All addressing fields and Security Control byte have been received.
|
|
// With the Security Control field, length of Auxiliary Security Header can be
|
|
// determined. Set BCC there
|
|
bcc = PHR_SIZE +
|
|
nrf_802154_frame_parser_aux_sec_hdr_end_offset_get(&m_current_rx_frame_data);
|
|
}
|
|
else
|
|
{
|
|
// This code should be unreachable.
|
|
}
|
|
}
|
|
else if (nrf_802154_frame_parser_valid_data_extend(
|
|
&m_current_rx_frame_data,
|
|
num_data_bytes,
|
|
PARSE_LEVEL_ADDRESSING_END))
|
|
{
|
|
m_flags.frame_parsed = true;
|
|
}
|
|
else
|
|
{
|
|
// This code should be unreachable.
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Nothing to do
|
|
}
|
|
|
|
if ((!m_flags.rx_timeslot_requested) && (frame_accepted))
|
|
{
|
|
if (nrf_802154_rsch_timeslot_request(nrf_802154_rx_duration_get(
|
|
mp_current_rx_buffer->data[0],
|
|
ack_is_requested(mp_current_rx_buffer->data))))
|
|
{
|
|
m_flags.rx_timeslot_requested = true;
|
|
|
|
receive_started_notify();
|
|
}
|
|
else
|
|
{
|
|
// Disable receiver and wait for a new timeslot.
|
|
trx_abort();
|
|
|
|
// We should not leave trx in temporary state, let's receive then.
|
|
// We avoid hard reset of radio during TX ACK phase due to timeslot end,
|
|
// which could result in spurious RF emission.
|
|
rx_init();
|
|
|
|
// Don't care about the result - if the notification cannot be performed
|
|
// no impact on the device's operation is expected
|
|
(void)nrf_802154_notify_receive_failed(NRF_802154_RX_ERROR_TIMESLOT_ENDED,
|
|
m_rx_window_id,
|
|
true);
|
|
}
|
|
}
|
|
|
|
if (m_flags.frame_filtered &&
|
|
m_flags.frame_parsed &&
|
|
nrf_802154_frame_parser_ar_bit_is_set(&m_current_rx_frame_data) &&
|
|
nrf_802154_pib_auto_ack_get())
|
|
{
|
|
mp_ack = nrf_802154_ack_generator_create(&m_current_rx_frame_data);
|
|
}
|
|
|
|
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
return bcc;
|
|
}
|
|
|
|
#endif
|
|
|
|
void nrf_802154_trx_go_idle_finished(void)
|
|
{
|
|
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
sleep_init();
|
|
state_set(RADIO_STATE_SLEEP);
|
|
|
|
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
|
|
}
|
|
|
|
static void on_bad_ack(void);
|
|
|
|
#if NRF_802154_TOTAL_TIMES_MEASUREMENT_ENABLED
|
|
static void update_total_times_on_receive_end(uint32_t listening_start_hp_timestamp,
|
|
uint32_t receive_end_hp_timestamp, uint8_t phr)
|
|
{
|
|
uint32_t t_listening;
|
|
uint32_t t_frame;
|
|
|
|
t_frame = nrf_802154_frame_duration_get(phr, true, true);
|
|
t_listening = receive_end_hp_timestamp - listening_start_hp_timestamp;
|
|
|
|
if (t_frame > t_listening)
|
|
{
|
|
t_frame = t_listening;
|
|
}
|
|
|
|
t_listening -= t_frame;
|
|
|
|
nrf_802154_stat_totals_increment(total_listening_time, t_listening);
|
|
nrf_802154_stat_totals_increment(total_receive_time, t_frame);
|
|
}
|
|
|
|
#endif
|
|
|
|
#if NRF_802154_TOTAL_TIMES_MEASUREMENT_ENABLED
|
|
void nrf_802154_stat_totals_get_notify(void)
|
|
{
|
|
// Total times are going to be read, update stat_totals to hold
|
|
// correct times until now.
|
|
|
|
nrf_802154_mcu_critical_state_t mcu_cs;
|
|
|
|
nrf_802154_mcu_critical_enter(mcu_cs);
|
|
|
|
trx_state_t trx_state = nrf_802154_trx_state_get();
|
|
|
|
if ((trx_state == TRX_STATE_RXFRAME) || (trx_state == TRX_STATE_RXACK))
|
|
{
|
|
uint32_t listening_end_timestamp = nrf_802154_hp_timer_current_time_get();
|
|
|
|
if (listening_end_timestamp - m_listening_start_hp_timestamp >= MAX_PHY_FRAME_TIME_US)
|
|
{
|
|
/* m_listening_start_hp_timestamp ... now - MAX_PHY_FRAME_TIME_US must be listening.
|
|
* Last MAX_PHY_FRAME_TIME_US is considered uncertain.
|
|
*/
|
|
listening_end_timestamp -= MAX_PHY_FRAME_TIME_US;
|
|
|
|
uint32_t t_listening = listening_end_timestamp - m_listening_start_hp_timestamp;
|
|
|
|
m_listening_start_hp_timestamp = listening_end_timestamp;
|
|
|
|
nrf_802154_stat_totals_increment(total_listening_time, t_listening);
|
|
}
|
|
else
|
|
{
|
|
/* Too little time passed since m_listening_start_hp_timestamp, we don't know
|
|
* if frame is being received now until it is received. */
|
|
}
|
|
}
|
|
|
|
nrf_802154_mcu_critical_exit(mcu_cs);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
void nrf_802154_trx_receive_frame_crcerror(void)
|
|
{
|
|
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
#if NRF_802154_TOTAL_TIMES_MEASUREMENT_ENABLED
|
|
uint32_t receive_end_hp_timestamp = nrf_802154_hp_timer_timestamp_get();
|
|
uint32_t listening_start_hp_timestamp = m_listening_start_hp_timestamp;
|
|
|
|
#endif
|
|
|
|
assert(m_state == RADIO_STATE_RX);
|
|
rx_flags_clear();
|
|
rx_data_clear();
|
|
|
|
// We don't change receive buffer, receive will go to the same that was already used
|
|
#if !NRF_802154_DISABLE_BCC_MATCHING
|
|
request_preconditions_for_state(m_state);
|
|
|
|
nrf_802154_fal_tx_power_split_t split_power = {0};
|
|
|
|
(void)nrf_802154_tx_power_split_pib_power_get(&split_power);
|
|
|
|
nrf_802154_trx_receive_frame(BCC_INIT / 8U,
|
|
m_trx_receive_frame_notifications_mask,
|
|
&split_power);
|
|
|
|
#if NRF_802154_TOTAL_TIMES_MEASUREMENT_ENABLED
|
|
m_listening_start_hp_timestamp = nrf_802154_hp_timer_current_time_get();
|
|
|
|
// Configure the timer coordinator to get a timestamp of the END event which
|
|
// fires several cycles after CRCOK or CRCERROR events.
|
|
nrf_802154_timer_coord_timestamp_prepare(nrf_802154_trx_radio_end_event_handle_get());
|
|
#endif
|
|
|
|
#else
|
|
// With BCC matching disabled trx module will re-arm automatically
|
|
#endif
|
|
|
|
#if NRF_802154_TOTAL_TIMES_MEASUREMENT_ENABLED
|
|
update_total_times_on_receive_end(listening_start_hp_timestamp,
|
|
receive_end_hp_timestamp,
|
|
mp_current_rx_buffer->data[PHR_OFFSET]);
|
|
#endif
|
|
|
|
#if NRF_802154_NOTIFY_CRCERROR
|
|
receive_failed_notify(NRF_802154_RX_ERROR_INVALID_FCS);
|
|
#endif // NRF_802154_NOTIFY_CRCERROR
|
|
|
|
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
|
|
}
|
|
|
|
void nrf_802154_trx_receive_ack_crcerror(void)
|
|
{
|
|
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
assert(m_state == RADIO_STATE_RX_ACK);
|
|
|
|
#if NRF_802154_TOTAL_TIMES_MEASUREMENT_ENABLED
|
|
uint32_t receive_end_hp_timestamp = nrf_802154_hp_timer_timestamp_get();
|
|
uint32_t listening_start_hp_timestamp = m_listening_start_hp_timestamp;
|
|
|
|
update_total_times_on_receive_end(listening_start_hp_timestamp,
|
|
receive_end_hp_timestamp,
|
|
mp_current_rx_buffer->data[PHR_OFFSET]);
|
|
#endif
|
|
|
|
on_bad_ack();
|
|
|
|
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
|
|
}
|
|
|
|
void nrf_802154_trx_receive_frame_received(void)
|
|
{
|
|
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
uint8_t * p_received_data = mp_current_rx_buffer->data;
|
|
|
|
// Latch RSSI and LQI values before sending ACK
|
|
m_last_rssi = rssi_last_measurement_get();
|
|
m_last_lqi = lqi_get(p_received_data);
|
|
|
|
#if NRF_802154_TOTAL_TIMES_MEASUREMENT_ENABLED
|
|
uint32_t receive_end_hp_timestamp = nrf_802154_hp_timer_timestamp_get();
|
|
uint32_t listening_start_hp_timestamp = m_listening_start_hp_timestamp;
|
|
|
|
update_total_times_on_receive_end(listening_start_hp_timestamp,
|
|
receive_end_hp_timestamp,
|
|
mp_current_rx_buffer->data[PHR_OFFSET]);
|
|
#endif
|
|
|
|
#if NRF_802154_DISABLE_BCC_MATCHING
|
|
uint8_t num_data_bytes = PHR_SIZE + FCF_SIZE;
|
|
uint8_t prev_num_data_bytes = 0;
|
|
nrf_802154_rx_error_t filter_result;
|
|
|
|
// Frame filtering
|
|
while (num_data_bytes != prev_num_data_bytes)
|
|
{
|
|
prev_num_data_bytes = num_data_bytes;
|
|
|
|
// Keep checking consecutive parts of the frame header.
|
|
filter_result = nrf_802154_filter_frame_part(&m_current_rx_frame_data, &num_data_bytes);
|
|
|
|
if (filter_result == NRF_802154_RX_ERROR_NONE)
|
|
{
|
|
if (num_data_bytes == prev_num_data_bytes)
|
|
{
|
|
m_flags.frame_filtered = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Timeslot request
|
|
if (m_flags.frame_filtered &&
|
|
nrf_802154_frame_parser_ar_bit_is_set(&m_current_rx_frame_data) &&
|
|
!nrf_802154_rsch_timeslot_request(nrf_802154_rx_duration_get(0, true)))
|
|
{
|
|
// Frame is destined to this node but there is no timeslot to transmit ACK.
|
|
// Just disable receiver and wait for a new timeslot.
|
|
nrf_802154_trx_abort();
|
|
|
|
rx_flags_clear();
|
|
rx_data_clear();
|
|
|
|
// Filter out received ACK frame if promiscuous mode is disabled.
|
|
if (((p_received_data[FRAME_TYPE_OFFSET] & FRAME_TYPE_MASK) != FRAME_TYPE_ACK) ||
|
|
nrf_802154_pib_promiscuous_get())
|
|
{
|
|
mp_current_rx_buffer->free = false;
|
|
received_frame_notify_and_nesting_allow(p_received_data);
|
|
}
|
|
|
|
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
|
|
return;
|
|
}
|
|
#endif // NRF_802154_DISABLE_BCC_MATCHING
|
|
|
|
if (m_flags.frame_filtered || nrf_802154_pib_promiscuous_get())
|
|
{
|
|
nrf_802154_stat_counter_increment(received_frames);
|
|
|
|
#if (NRF_802154_FRAME_TIMESTAMP_ENABLED)
|
|
uint64_t ts = timer_coord_timestamp_get();
|
|
|
|
nrf_802154_stat_timestamp_write(last_rx_end_timestamp, ts);
|
|
#endif
|
|
|
|
nrf_802154_sl_ant_div_rx_frame_received_notify();
|
|
|
|
bool send_ack = false;
|
|
bool parse_result = nrf_802154_frame_parser_valid_data_extend(
|
|
&m_current_rx_frame_data,
|
|
PHR_SIZE + nrf_802154_frame_parser_frame_length_get(&m_current_rx_frame_data),
|
|
PARSE_LEVEL_FULL);
|
|
|
|
if (m_flags.frame_filtered &&
|
|
parse_result &&
|
|
nrf_802154_frame_parser_ar_bit_is_set(&m_current_rx_frame_data) &&
|
|
nrf_802154_pib_auto_ack_get())
|
|
{
|
|
nrf_802154_tx_work_buffer_reset(&m_default_frame_props);
|
|
mp_ack = nrf_802154_ack_generator_create(&m_current_rx_frame_data);
|
|
send_ack = (mp_ack != NULL);
|
|
}
|
|
|
|
if (send_ack)
|
|
{
|
|
state_set(RADIO_STATE_TX_ACK);
|
|
|
|
if (is_state_allowed_for_prio(m_rsch_priority, RADIO_STATE_TX_ACK))
|
|
{
|
|
if (nrf_802154_trx_transmit_ack(nrf_802154_tx_work_buffer_get(mp_ack), ACK_IFS))
|
|
{
|
|
// Intentionally empty: transmitting ack, because we can
|
|
}
|
|
else
|
|
{
|
|
mp_current_rx_buffer->free = false;
|
|
|
|
state_set(RADIO_STATE_RX);
|
|
rx_init();
|
|
|
|
received_frame_notify_and_nesting_allow(p_received_data);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!nrf_802154_rsch_prec_is_approved(RSCH_PREC_COEX,
|
|
min_required_rsch_prio(RADIO_STATE_TX_ACK)))
|
|
{
|
|
nrf_802154_stat_counter_increment(coex_denied_requests);
|
|
}
|
|
|
|
mp_current_rx_buffer->free = false;
|
|
|
|
state_set(RADIO_STATE_RX);
|
|
rx_init();
|
|
|
|
received_frame_notify_and_nesting_allow(p_received_data);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
request_preconditions_for_state(m_state);
|
|
// Filter out received ACK frame if promiscuous mode is disabled.
|
|
if (((p_received_data[FRAME_TYPE_OFFSET] & FRAME_TYPE_MASK) != FRAME_TYPE_ACK) ||
|
|
nrf_802154_pib_promiscuous_get())
|
|
{
|
|
// Current buffer will be passed to the application
|
|
mp_current_rx_buffer->free = false;
|
|
|
|
// Find new buffer
|
|
rx_buffer_in_use_set(nrf_802154_rx_buffer_free_find());
|
|
|
|
rx_init();
|
|
|
|
received_frame_notify_and_nesting_allow(p_received_data);
|
|
}
|
|
else
|
|
{
|
|
// Receive to the same buffer
|
|
rx_init();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// CRC is OK, but filtering operation did not end - it is invalid frame with valid CRC
|
|
// or problem due to software latency (i.e. handled BCMATCH, CRCERROR, CRCOK from two
|
|
// consecutively received frames).
|
|
request_preconditions_for_state(m_state);
|
|
rx_init();
|
|
|
|
#if NRF_802154_DISABLE_BCC_MATCHING
|
|
if ((p_received_data[FRAME_TYPE_OFFSET] & FRAME_TYPE_MASK) != FRAME_TYPE_ACK)
|
|
{
|
|
receive_failed_notify(filter_result);
|
|
}
|
|
#else // NRF_802154_DISABLE_BCC_MATCHING
|
|
receive_failed_notify(NRF_802154_RX_ERROR_RUNTIME);
|
|
#endif // NRF_802154_DISABLE_BCC_MATCHING
|
|
}
|
|
|
|
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
|
|
}
|
|
|
|
void nrf_802154_trx_transmit_frame_started(void)
|
|
{
|
|
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
assert((m_state == RADIO_STATE_TX) || (m_state == RADIO_STATE_CCA_TX));
|
|
transmit_started_notify();
|
|
|
|
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
|
|
}
|
|
|
|
void nrf_802154_trx_transmit_ack_started(void)
|
|
{
|
|
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
assert(m_state == RADIO_STATE_TX_ACK);
|
|
transmit_ack_started_notify();
|
|
|
|
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
|
|
}
|
|
|
|
void nrf_802154_trx_transmit_ack_transmitted(void)
|
|
{
|
|
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
assert(m_state == RADIO_STATE_TX_ACK);
|
|
|
|
#if (NRF_802154_TOTAL_TIMES_MEASUREMENT_ENABLED)
|
|
uint32_t t_transmit = TX_RAMP_UP_TIME + nrf_802154_frame_duration_get(mp_ack[PHR_OFFSET],
|
|
true,
|
|
true);
|
|
|
|
nrf_802154_stat_totals_increment(total_transmit_time, t_transmit);
|
|
#endif
|
|
|
|
uint8_t * p_received_data = mp_current_rx_buffer->data;
|
|
|
|
// Current buffer used for receive operation will be passed to the application
|
|
mp_current_rx_buffer->free = false;
|
|
|
|
state_set(RADIO_STATE_RX);
|
|
|
|
rx_init();
|
|
|
|
received_frame_notify_and_nesting_allow(p_received_data);
|
|
|
|
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
|
|
}
|
|
|
|
void nrf_802154_trx_transmit_frame_transmitted(void)
|
|
{
|
|
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
#if (NRF_802154_TOTAL_TIMES_MEASUREMENT_ENABLED)
|
|
uint32_t t_listening = 0U;
|
|
uint32_t t_transmit = 0U;
|
|
|
|
#endif
|
|
|
|
#if (NRF_802154_FRAME_TIMESTAMP_ENABLED)
|
|
uint64_t ts = timer_coord_timestamp_get();
|
|
|
|
// ts holds now timestamp of the PHYEND event
|
|
nrf_802154_stat_timestamp_write(last_tx_end_timestamp, ts);
|
|
|
|
if (m_flags.tx_with_cca)
|
|
{
|
|
m_flags.tx_diminished_prio = false;
|
|
|
|
// We calculate the timestamp when ccaidle must happened.
|
|
ts -= nrf_802154_frame_duration_get(mp_tx_data[0], true, true) + RX_TX_TURNAROUND_TIME;
|
|
|
|
nrf_802154_stat_timestamp_write(last_cca_idle_timestamp, ts);
|
|
|
|
#if (NRF_802154_TOTAL_TIMES_MEASUREMENT_ENABLED)
|
|
uint64_t cca_start_ts;
|
|
|
|
nrf_802154_stat_timestamp_read(&cca_start_ts, last_cca_start_timestamp);
|
|
|
|
t_listening += RX_RAMP_UP_TIME + (uint32_t)(ts - cca_start_ts);
|
|
t_transmit += RX_TX_TURNAROUND_TIME;
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
#if (NRF_802154_TOTAL_TIMES_MEASUREMENT_ENABLED)
|
|
t_transmit += TX_RAMP_UP_TIME;
|
|
#endif
|
|
}
|
|
|
|
#if (NRF_802154_TOTAL_TIMES_MEASUREMENT_ENABLED)
|
|
t_transmit += nrf_802154_frame_duration_get(mp_tx_data[PHR_OFFSET], true, true);
|
|
|
|
nrf_802154_stat_totals_increment(total_listening_time, t_listening);
|
|
nrf_802154_stat_totals_increment(total_transmit_time, t_transmit);
|
|
#endif
|
|
#endif
|
|
|
|
if (ack_is_requested(mp_tx_data))
|
|
{
|
|
state_set(RADIO_STATE_RX_ACK);
|
|
|
|
bool rx_buffer_free = rx_buffer_is_available();
|
|
|
|
nrf_802154_trx_receive_buffer_set(rx_buffer_get());
|
|
|
|
#if (NRF_802154_FRAME_TIMESTAMP_ENABLED)
|
|
#if (NRF_802154_TOTAL_TIMES_MEASUREMENT_ENABLED)
|
|
// Configure the timer coordinator to get a timestamp of the END event which
|
|
// fires several cycles after CRCOK or CRCERROR events.
|
|
nrf_802154_timer_coord_timestamp_prepare(nrf_802154_trx_radio_end_event_handle_get());
|
|
#else
|
|
// Configure the timer coordinator to get a timestamp of the CRCOK event.
|
|
nrf_802154_timer_coord_timestamp_prepare(nrf_802154_trx_radio_crcok_event_handle_get());
|
|
#endif
|
|
#endif
|
|
|
|
nrf_802154_trx_receive_ack();
|
|
|
|
#if NRF_802154_TOTAL_TIMES_MEASUREMENT_ENABLED
|
|
m_listening_start_hp_timestamp = nrf_802154_hp_timer_current_time_get();
|
|
#endif
|
|
|
|
if (!rx_buffer_free)
|
|
{
|
|
rx_buffer_in_use_set(nrf_802154_rx_buffer_free_find());
|
|
|
|
nrf_802154_trx_receive_buffer_set(rx_buffer_get());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
state_set(RADIO_STATE_RX);
|
|
|
|
rx_init();
|
|
|
|
transmitted_frame_notify(NULL, 0, 0);
|
|
}
|
|
|
|
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
|
|
}
|
|
|
|
static bool ack_match_check_version_not_2(const uint8_t * p_tx_data, const uint8_t * p_ack_data)
|
|
{
|
|
// Frame Version != 2
|
|
|
|
// Check: Phy length
|
|
if (p_ack_data[PHR_OFFSET] != IMM_ACK_LENGTH)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Check if Frame version is 0 or 1.
|
|
switch (p_ack_data[FRAME_VERSION_OFFSET] & FRAME_VERSION_MASK)
|
|
{
|
|
case FRAME_VERSION_0:
|
|
case FRAME_VERSION_1:
|
|
break;
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
|
|
// Check: Sequence number match
|
|
if (p_ack_data[DSN_OFFSET] != p_tx_data[DSN_OFFSET])
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool ack_match_check_version_2(const uint8_t * p_tx_frame, const uint8_t * p_ack_frame)
|
|
{
|
|
nrf_802154_frame_parser_data_t tx_data;
|
|
nrf_802154_frame_parser_data_t ack_data;
|
|
bool parse_result;
|
|
|
|
parse_result = nrf_802154_frame_parser_data_init(p_tx_frame,
|
|
p_tx_frame[PHR_OFFSET] + PHR_SIZE,
|
|
PARSE_LEVEL_ADDRESSING_END,
|
|
&tx_data);
|
|
if (!parse_result)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
parse_result = nrf_802154_frame_parser_data_init(p_ack_frame,
|
|
p_ack_frame[PHR_OFFSET] + PHR_SIZE,
|
|
PARSE_LEVEL_ADDRESSING_END,
|
|
&ack_data);
|
|
|
|
if (nrf_802154_frame_parser_frame_version_get(&ack_data) != FRAME_VERSION_2)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Transmitted frame was Version 2
|
|
// For frame version 2 sequence number bit may be suppressed and its check fails.
|
|
// Verify ACK frame using its destination address.
|
|
|
|
const uint8_t * p_tx_src_addr = nrf_802154_frame_parser_src_addr_get(&tx_data);
|
|
const uint8_t * p_ack_dst_addr = nrf_802154_frame_parser_dst_addr_get(&ack_data);
|
|
uint8_t tx_src_addr_size = nrf_802154_frame_parser_src_addr_size_get(&tx_data);
|
|
uint8_t ack_dst_addr_size = nrf_802154_frame_parser_dst_addr_size_get(&ack_data);
|
|
|
|
if (!parse_result ||
|
|
(p_tx_src_addr == NULL) ||
|
|
(p_ack_dst_addr == NULL) ||
|
|
(tx_src_addr_size != ack_dst_addr_size) ||
|
|
(0 != memcmp(p_tx_src_addr,
|
|
p_ack_dst_addr,
|
|
tx_src_addr_size)))
|
|
{
|
|
// Mismatch
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool ack_match_check(const uint8_t * p_tx_data, const uint8_t * p_ack_data)
|
|
{
|
|
if ((p_tx_data == NULL) || (p_ack_data == NULL))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Check: Frame Control Field -> Frame type
|
|
if ((p_ack_data[FRAME_TYPE_OFFSET] & FRAME_TYPE_MASK) != FRAME_TYPE_ACK)
|
|
{
|
|
return false; // This is not an ACK frame
|
|
}
|
|
|
|
// Check: Frame Control Field -> Frame version
|
|
if ((p_tx_data[FRAME_VERSION_OFFSET] & FRAME_VERSION_MASK) == FRAME_VERSION_2)
|
|
{
|
|
return ack_match_check_version_2(p_tx_data, p_ack_data);
|
|
}
|
|
|
|
return ack_match_check_version_not_2(p_tx_data, p_ack_data);
|
|
}
|
|
|
|
static void on_bad_ack(void)
|
|
{
|
|
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
// We received either a frame with incorrect CRC or not an ACK frame or not matching ACK
|
|
state_set(RADIO_STATE_RX);
|
|
|
|
rx_init();
|
|
|
|
nrf_802154_transmit_done_metadata_t metadata = {};
|
|
|
|
nrf_802154_tx_work_buffer_original_frame_update(mp_tx_data, &metadata.frame_props);
|
|
transmit_failed_notify_and_nesting_allow(NRF_802154_TX_ERROR_INVALID_ACK, &metadata);
|
|
|
|
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
|
|
}
|
|
|
|
void nrf_802154_trx_receive_ack_received(void)
|
|
{
|
|
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
// CRC of received frame is correct
|
|
uint8_t * p_ack_data = mp_current_rx_buffer->data;
|
|
|
|
#if NRF_802154_TOTAL_TIMES_MEASUREMENT_ENABLED
|
|
uint32_t receive_end_hp_timestamp = nrf_802154_hp_timer_timestamp_get();
|
|
uint32_t listening_start_hp_timestamp = m_listening_start_hp_timestamp;
|
|
|
|
update_total_times_on_receive_end(listening_start_hp_timestamp,
|
|
receive_end_hp_timestamp,
|
|
mp_current_rx_buffer->data[PHR_OFFSET]);
|
|
#endif
|
|
|
|
if (ack_match_check(mp_tx_data, p_ack_data))
|
|
{
|
|
#if (NRF_802154_FRAME_TIMESTAMP_ENABLED)
|
|
uint64_t ts = timer_coord_timestamp_get();
|
|
|
|
nrf_802154_stat_timestamp_write(last_ack_end_timestamp, ts);
|
|
#endif
|
|
|
|
rx_buffer_t * p_ack_buffer = mp_current_rx_buffer;
|
|
|
|
mp_current_rx_buffer->free = false;
|
|
|
|
state_set(RADIO_STATE_RX);
|
|
rx_init();
|
|
|
|
transmitted_frame_notify(p_ack_buffer->data, // phr + psdu
|
|
rssi_last_measurement_get(), // rssi
|
|
lqi_get(p_ack_buffer->data)); // lqi;
|
|
}
|
|
else
|
|
{
|
|
on_bad_ack();
|
|
}
|
|
|
|
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
|
|
}
|
|
|
|
void nrf_802154_trx_standalone_cca_finished(bool channel_was_idle)
|
|
{
|
|
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
state_set(RADIO_STATE_RX);
|
|
rx_init();
|
|
|
|
cca_notify(channel_was_idle);
|
|
|
|
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
|
|
}
|
|
|
|
void nrf_802154_trx_transmit_frame_ccastarted(void)
|
|
{
|
|
// This handler provided by trx is never called because parameter notifications_mask
|
|
// of the nrf_802154_trx_transmit_frame does not contain TRX_TRANSMIT_NOTIFICATION_CCASTARTED.
|
|
assert(false);
|
|
}
|
|
|
|
void nrf_802154_trx_transmit_frame_ccaidle(void)
|
|
{
|
|
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
assert(m_state == RADIO_STATE_CCA_TX);
|
|
assert(m_trx_transmit_frame_notifications_mask & TRX_TRANSMIT_NOTIFICATION_CCAIDLE);
|
|
|
|
#if (NRF_802154_FRAME_TIMESTAMP_ENABLED)
|
|
uint64_t ts = timer_coord_timestamp_get();
|
|
|
|
// Configure the timer coordinator to get a timestamp of the PHYEND event.
|
|
nrf_802154_timer_coord_timestamp_prepare(nrf_802154_trx_radio_phyend_event_handle_get());
|
|
|
|
// Update stat timestamp of CCASTART event
|
|
nrf_802154_stat_timestamp_write(last_cca_start_timestamp, ts);
|
|
#endif
|
|
|
|
if (m_coex_tx_request_mode == NRF_802154_COEX_TX_REQUEST_MODE_CCA_DONE)
|
|
{
|
|
nrf_802154_rsch_crit_sect_prio_request(RSCH_PRIO_TX);
|
|
m_flags.tx_diminished_prio = false;
|
|
}
|
|
|
|
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
|
|
}
|
|
|
|
void nrf_802154_trx_transmit_frame_ccabusy(void)
|
|
{
|
|
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
nrf_802154_stat_counter_increment(cca_failed_attempts);
|
|
|
|
#if (NRF_802154_TOTAL_TIMES_MEASUREMENT_ENABLED)
|
|
uint32_t t_listening = RX_RAMP_UP_TIME + PHY_US_TIME_FROM_SYMBOLS(A_CCA_DURATION_SYMBOLS);
|
|
|
|
nrf_802154_stat_totals_increment(total_listening_time, t_listening);
|
|
#endif
|
|
|
|
state_set(RADIO_STATE_RX);
|
|
rx_init();
|
|
|
|
nrf_802154_transmit_done_metadata_t metadata = {};
|
|
|
|
nrf_802154_tx_work_buffer_original_frame_update(mp_tx_data, &metadata.frame_props);
|
|
transmit_failed_notify_and_nesting_allow(NRF_802154_TX_ERROR_BUSY_CHANNEL, &metadata);
|
|
|
|
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
|
|
}
|
|
|
|
void nrf_802154_trx_energy_detection_finished(uint8_t ed_sample)
|
|
{
|
|
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
if (m_ed_result < ed_sample)
|
|
{
|
|
// Collect maximum value of samples provided by trx
|
|
m_ed_result = ed_sample;
|
|
}
|
|
|
|
if (m_ed_time_left >= ED_ITER_DURATION)
|
|
{
|
|
uint32_t trx_ed_count = 0U;
|
|
|
|
if (ed_iter_setup(&m_ed_time_left, &trx_ed_count))
|
|
{
|
|
nrf_802154_trx_energy_detection(trx_ed_count);
|
|
}
|
|
else
|
|
{
|
|
/* There is too little time in current timeslot, just wait for timeslot end.
|
|
* Operation will be resumed in next timeslot */
|
|
}
|
|
}
|
|
else if (nrf_802154_sl_ant_div_energy_detection_finished_notify())
|
|
{
|
|
ed_init();
|
|
}
|
|
else
|
|
{
|
|
nrf_802154_trx_channel_set(nrf_802154_pib_channel_get());
|
|
|
|
state_set(RADIO_STATE_RX);
|
|
rx_init();
|
|
|
|
energy_detected_notify(nrf_802154_rssi_ed_sample_convert(m_ed_result));
|
|
|
|
}
|
|
|
|
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
|
|
}
|
|
|
|
/***************************************************************************************************
|
|
* @section API functions
|
|
**************************************************************************************************/
|
|
|
|
void nrf_802154_core_init(void)
|
|
{
|
|
m_state = RADIO_STATE_SLEEP;
|
|
m_rsch_timeslot_is_granted = false;
|
|
m_rx_prestarted_trig_count = 0;
|
|
|
|
nrf_802154_sl_timer_init(&m_rx_prestarted_timer);
|
|
|
|
nrf_802154_trx_init();
|
|
nrf_802154_ack_generator_init();
|
|
}
|
|
|
|
void nrf_802154_core_deinit(void)
|
|
{
|
|
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
if (timeslot_is_granted())
|
|
{
|
|
nrf_802154_trx_disable();
|
|
}
|
|
|
|
mpsl_fem_cleanup();
|
|
|
|
nrf_802154_irq_disable(RADIO_IRQn);
|
|
nrf_802154_irq_clear_pending(RADIO_IRQn);
|
|
|
|
nrf_802154_sl_timer_deinit(&m_rx_prestarted_timer);
|
|
|
|
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
|
|
}
|
|
|
|
radio_state_t nrf_802154_core_state_get(void)
|
|
{
|
|
return m_state;
|
|
}
|
|
|
|
bool nrf_802154_core_sleep(nrf_802154_term_t term_lvl)
|
|
{
|
|
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
bool result = nrf_802154_critical_section_enter();
|
|
|
|
if (result)
|
|
{
|
|
if ((m_state != RADIO_STATE_SLEEP) && (m_state != RADIO_STATE_FALLING_ASLEEP))
|
|
{
|
|
result = current_operation_terminate(term_lvl, REQ_ORIG_CORE, true);
|
|
|
|
if (result)
|
|
{
|
|
// The order of calls in the following blocks is inverted to avoid RAAL races.
|
|
if (timeslot_is_granted())
|
|
{
|
|
state_set(RADIO_STATE_FALLING_ASLEEP);
|
|
falling_asleep_init();
|
|
}
|
|
else
|
|
{
|
|
sleep_init();
|
|
state_set(RADIO_STATE_SLEEP);
|
|
}
|
|
}
|
|
}
|
|
|
|
nrf_802154_critical_section_exit();
|
|
}
|
|
|
|
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
return result;
|
|
}
|
|
|
|
bool nrf_802154_core_receive(nrf_802154_term_t term_lvl,
|
|
req_originator_t req_orig,
|
|
nrf_802154_notification_func_t notify_function,
|
|
bool notify_abort,
|
|
uint32_t id)
|
|
{
|
|
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
bool result = nrf_802154_critical_section_enter();
|
|
|
|
if (result)
|
|
{
|
|
if ((m_state != RADIO_STATE_RX) && (m_state != RADIO_STATE_TX_ACK))
|
|
{
|
|
if (critical_section_can_be_processed_now())
|
|
{
|
|
result = current_operation_terminate(term_lvl, req_orig, notify_abort);
|
|
|
|
if (result)
|
|
{
|
|
m_trx_receive_frame_notifications_mask =
|
|
make_trx_frame_receive_notification_mask();
|
|
m_rx_window_id = id;
|
|
state_set(RADIO_STATE_RX);
|
|
rx_init();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result = false;
|
|
}
|
|
}
|
|
|
|
if (notify_function != NULL)
|
|
{
|
|
notify_function(result);
|
|
}
|
|
|
|
nrf_802154_critical_section_exit();
|
|
}
|
|
else
|
|
{
|
|
if (notify_function != NULL)
|
|
{
|
|
notify_function(false);
|
|
}
|
|
}
|
|
|
|
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
return result;
|
|
}
|
|
|
|
bool nrf_802154_core_transmit(nrf_802154_term_t term_lvl,
|
|
req_originator_t req_orig,
|
|
uint8_t * p_data,
|
|
nrf_802154_transmit_params_t * p_params,
|
|
nrf_802154_notification_func_t notify_function)
|
|
{
|
|
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
bool result = critical_section_enter_and_verify_timeslot_length();
|
|
|
|
if (result)
|
|
{
|
|
if (nrf_802154_core_hooks_pre_transmission(p_data, p_params, &transmit_failed_notify))
|
|
{
|
|
result = current_operation_terminate(term_lvl, req_orig, true);
|
|
|
|
if (result)
|
|
{
|
|
nrf_802154_tx_work_buffer_reset(&p_params->frame_props);
|
|
result = nrf_802154_core_hooks_tx_setup(p_data, p_params, &transmit_failed_notify);
|
|
}
|
|
|
|
if (result)
|
|
{
|
|
m_coex_tx_request_mode = nrf_802154_pib_coex_tx_request_mode_get();
|
|
m_trx_transmit_frame_notifications_mask =
|
|
make_trx_frame_transmit_notification_mask(p_params->cca);
|
|
m_flags.tx_diminished_prio =
|
|
m_coex_tx_request_mode == NRF_802154_COEX_TX_REQUEST_MODE_CCA_DONE;
|
|
|
|
state_set(p_params->cca ? RADIO_STATE_CCA_TX : RADIO_STATE_TX);
|
|
mp_tx_data = p_data;
|
|
m_tx_power = p_params->tx_power;
|
|
|
|
// coverity[check_return]
|
|
result = tx_init(p_data, p_params->cca);
|
|
if (p_params->immediate)
|
|
{
|
|
if (!result)
|
|
{
|
|
state_set(RADIO_STATE_RX);
|
|
rx_init();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
result = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (notify_function != NULL)
|
|
{
|
|
notify_function(result);
|
|
}
|
|
|
|
nrf_802154_critical_section_exit();
|
|
}
|
|
else
|
|
{
|
|
if (notify_function != NULL)
|
|
{
|
|
notify_function(false);
|
|
}
|
|
}
|
|
|
|
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
return result;
|
|
}
|
|
|
|
bool nrf_802154_core_energy_detection(nrf_802154_term_t term_lvl, uint32_t time_us)
|
|
{
|
|
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
bool result = critical_section_enter_and_verify_timeslot_length();
|
|
|
|
if (result)
|
|
{
|
|
result = current_operation_terminate(term_lvl, REQ_ORIG_CORE, true);
|
|
|
|
if (result)
|
|
{
|
|
if (time_us < ED_ITER_DURATION)
|
|
{
|
|
time_us = ED_ITER_DURATION;
|
|
}
|
|
|
|
m_ed_time_left = time_us;
|
|
m_ed_result = 0;
|
|
|
|
state_set(RADIO_STATE_ED);
|
|
ed_init();
|
|
}
|
|
|
|
nrf_802154_critical_section_exit();
|
|
}
|
|
|
|
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
return result;
|
|
}
|
|
|
|
bool nrf_802154_core_cca(nrf_802154_term_t term_lvl)
|
|
{
|
|
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
bool result = critical_section_enter_and_verify_timeslot_length();
|
|
|
|
if (result)
|
|
{
|
|
result = current_operation_terminate(term_lvl, REQ_ORIG_CORE, true);
|
|
|
|
if (result)
|
|
{
|
|
state_set(RADIO_STATE_CCA);
|
|
cca_init();
|
|
}
|
|
|
|
nrf_802154_critical_section_exit();
|
|
}
|
|
|
|
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
return result;
|
|
}
|
|
|
|
#if NRF_802154_CARRIER_FUNCTIONS_ENABLED
|
|
|
|
bool nrf_802154_core_continuous_carrier(nrf_802154_term_t term_lvl)
|
|
{
|
|
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
bool result = critical_section_enter_and_verify_timeslot_length();
|
|
|
|
if (result)
|
|
{
|
|
result = current_operation_terminate(term_lvl, REQ_ORIG_CORE, true);
|
|
|
|
if (result)
|
|
{
|
|
state_set(RADIO_STATE_CONTINUOUS_CARRIER);
|
|
continuous_carrier_init();
|
|
}
|
|
|
|
nrf_802154_critical_section_exit();
|
|
}
|
|
|
|
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
return result;
|
|
}
|
|
|
|
bool nrf_802154_core_modulated_carrier(nrf_802154_term_t term_lvl,
|
|
const uint8_t * p_data)
|
|
{
|
|
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
bool result = critical_section_enter_and_verify_timeslot_length();
|
|
|
|
if (result)
|
|
{
|
|
result = current_operation_terminate(term_lvl, REQ_ORIG_CORE, true);
|
|
|
|
if (result)
|
|
{
|
|
state_set(RADIO_STATE_MODULATED_CARRIER);
|
|
mp_tx_data = (uint8_t *)p_data;
|
|
modulated_carrier_init(p_data);
|
|
}
|
|
|
|
nrf_802154_critical_section_exit();
|
|
}
|
|
|
|
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
return result;
|
|
}
|
|
|
|
#endif // NRF_802154_CARRIER_FUNCTIONS_ENABLED
|
|
|
|
bool nrf_802154_core_notify_buffer_free(uint8_t * p_data)
|
|
{
|
|
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
rx_buffer_t * p_buffer = (rx_buffer_t *)p_data;
|
|
bool in_crit_sect = critical_section_enter_and_verify_timeslot_length();
|
|
|
|
p_buffer->free = true;
|
|
|
|
if (in_crit_sect)
|
|
{
|
|
if (timeslot_is_granted())
|
|
{
|
|
if (nrf_802154_trx_receive_is_buffer_missing())
|
|
{
|
|
rx_buffer_in_use_set(p_buffer);
|
|
nrf_802154_trx_receive_buffer_set(rx_buffer_get());
|
|
}
|
|
}
|
|
|
|
nrf_802154_critical_section_exit();
|
|
}
|
|
|
|
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
return true;
|
|
}
|
|
|
|
bool nrf_802154_core_channel_update(req_originator_t req_orig)
|
|
{
|
|
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
bool result = critical_section_enter_and_verify_timeslot_length();
|
|
|
|
if (result)
|
|
{
|
|
if (timeslot_is_granted())
|
|
{
|
|
nrf_802154_trx_channel_set(nrf_802154_pib_channel_get());
|
|
}
|
|
|
|
switch (m_state)
|
|
{
|
|
case RADIO_STATE_RX:
|
|
if (current_operation_terminate(NRF_802154_TERM_802154, req_orig, true))
|
|
{
|
|
rx_init();
|
|
}
|
|
break;
|
|
|
|
#if NRF_802154_CARRIER_FUNCTIONS_ENABLED
|
|
case RADIO_STATE_CONTINUOUS_CARRIER:
|
|
if (timeslot_is_granted())
|
|
{
|
|
nrf_802154_trx_continuous_carrier_restart();
|
|
}
|
|
break;
|
|
|
|
case RADIO_STATE_MODULATED_CARRIER:
|
|
if (timeslot_is_granted())
|
|
{
|
|
nrf_802154_trx_modulated_carrier_restart();
|
|
}
|
|
break;
|
|
|
|
#endif // NRF_802154_CARRIER_FUNCTIONS_ENABLED
|
|
default:
|
|
// Don't perform any additional action in any other state.
|
|
break;
|
|
}
|
|
|
|
nrf_802154_critical_section_exit();
|
|
}
|
|
|
|
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
return result;
|
|
}
|
|
|
|
bool nrf_802154_core_cca_cfg_update(void)
|
|
{
|
|
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
bool result = critical_section_enter_and_verify_timeslot_length();
|
|
|
|
if (result)
|
|
{
|
|
if (timeslot_is_granted())
|
|
{
|
|
nrf_802154_trx_cca_configuration_update();
|
|
}
|
|
|
|
nrf_802154_critical_section_exit();
|
|
}
|
|
|
|
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
return result;
|
|
}
|
|
|
|
bool nrf_802154_core_rssi_measure(void)
|
|
{
|
|
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
bool result = critical_section_enter_and_verify_timeslot_length();
|
|
|
|
if (result)
|
|
{
|
|
if (timeslot_is_granted() && (m_state == RADIO_STATE_RX))
|
|
{
|
|
result = nrf_802154_trx_rssi_measure();
|
|
}
|
|
else
|
|
{
|
|
result = false;
|
|
}
|
|
|
|
nrf_802154_critical_section_exit();
|
|
}
|
|
|
|
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
return result;
|
|
}
|
|
|
|
bool nrf_802154_core_last_rssi_measurement_get(int8_t * p_rssi)
|
|
{
|
|
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
bool result = false;
|
|
bool in_crit_sect = false;
|
|
bool rssi_started = nrf_802154_trx_rssi_measure_is_started();
|
|
|
|
if (rssi_started)
|
|
{
|
|
in_crit_sect = critical_section_enter_and_verify_timeslot_length();
|
|
}
|
|
|
|
if (rssi_started && in_crit_sect)
|
|
{
|
|
// Checking if a timeslot is granted is valid only in a critical section
|
|
if (timeslot_is_granted())
|
|
{
|
|
rssi_started = nrf_802154_trx_rssi_measure_is_started();
|
|
if (rssi_started)
|
|
{
|
|
rssi_measurement_wait();
|
|
*p_rssi = rssi_last_measurement_get();
|
|
result = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (in_crit_sect)
|
|
{
|
|
nrf_802154_critical_section_exit();
|
|
}
|
|
|
|
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
return result;
|
|
}
|
|
|
|
int8_t nrf_802154_core_last_frame_rssi_get(void)
|
|
{
|
|
return m_last_rssi;
|
|
}
|
|
|
|
uint8_t nrf_802154_core_last_frame_lqi_get(void)
|
|
{
|
|
return m_last_lqi;
|
|
}
|
|
|
|
bool nrf_802154_core_antenna_update(void)
|
|
{
|
|
nrf_802154_log_function_enter(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
bool result = critical_section_enter_and_verify_timeslot_length();
|
|
|
|
if (result)
|
|
{
|
|
if (timeslot_is_granted())
|
|
{
|
|
nrf_802154_trx_antenna_update();
|
|
}
|
|
|
|
nrf_802154_critical_section_exit();
|
|
}
|
|
|
|
nrf_802154_log_function_exit(NRF_802154_LOG_VERBOSITY_LOW);
|
|
|
|
return result;
|
|
}
|
|
|
|
int8_t nrf_802154_sl_ant_div_rssi_measure_get(void)
|
|
{
|
|
int8_t result = NRF_802154_RSSI_INVALID;
|
|
|
|
// This function is supposed to be called after detecting frame prestarted event, but before
|
|
// detecting valid frame address. This means that we're currently in critical section, but the
|
|
// timeslot is not yet extended due to detecting valid frame. To avoid invalid timeslot extension
|
|
// due to blocking rssi measurements, antenna check can be aborted here if timeslot is about to end.
|
|
// Antenna switching takes 200 ns (250 ns with safety margin), while rssi measurement - 250,
|
|
// which gives total time of 750 ns.
|
|
// 750 ns is less than safety margin, so timeslot us left different than 0 is sufficient.
|
|
if (!nrf_802154_rsch_timeslot_us_left_get())
|
|
{
|
|
return result;
|
|
}
|
|
|
|
nrf_802154_trx_rssi_measure();
|
|
|
|
if (nrf_802154_trx_rssi_measure_is_started())
|
|
{
|
|
while (!nrf_802154_trx_rssi_sample_is_available())
|
|
{
|
|
// Intentionally empty: This function is called from a critical section.
|
|
// WFE would not be waken up by a RADIO event.
|
|
}
|
|
|
|
uint8_t rssi_sample = nrf_802154_trx_rssi_last_sample_get();
|
|
|
|
rssi_sample = nrf_802154_rssi_sample_corrected_get(rssi_sample);
|
|
|
|
result = -((int8_t)rssi_sample);
|
|
}
|
|
|
|
return result;
|
|
}
|