blob: 0455ba22844ea359f18e3615fbb6eda66abe6b31 [file] [log] [blame]
/* Copyright (c) 2017, Nordic Semiconductor ASA
* All rights reserved.
*
* 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.
*
*/
#include "nrf_drv_radio802154_fsm.h"
#include <assert.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include "nrf_drv_radio802154.h"
#include "nrf_drv_radio802154_ack_pending_bit.h"
#include "nrf_drv_radio802154_config.h"
#include "nrf_drv_radio802154_const.h"
#include "nrf_drv_radio802154_debug.h"
#include "nrf_drv_radio802154_notification.h"
#include "nrf_drv_radio802154_pib.h"
#include "nrf_drv_radio802154_priority_drop.h"
#include "nrf_drv_radio802154_procedures_duration.h"
#include "nrf_drv_radio802154_rx_buffer.h"
#include "hal/nrf_radio.h"
#include "raal/nrf_raal_api.h"
/// Value set to SHORTS register when no shorts should be enabled.
#define SHORTS_IDLE 0
/// Value set to SHORTS register when receiver is waiting for incoming frame.
#define SHORTS_RX_INITIAL (NRF_RADIO_SHORT_END_DISABLE_MASK | NRF_RADIO_SHORT_DISABLED_TXEN_MASK | \
NRF_RADIO_SHORT_FRAMESTART_BCSTART_MASK)
/// Value set to SHORTS register when receiver started receiving a frame.
#define SHORTS_RX_FOLLOWING (NRF_RADIO_SHORT_END_DISABLE_MASK | NRF_RADIO_SHORT_READY_START_MASK | \
NRF_RADIO_SHORT_FRAMESTART_BCSTART_MASK)
/// Value set to SHORTS register when received frame should be acknowledged.
#define SHORTS_TX_ACK (NRF_RADIO_SHORT_END_DISABLE_MASK)
#if RADIO_SHORT_CCAIDLE_TXEN
/// Value set to SHORTS register during transmission of a frame
#define SHORTS_TX_FRAME (NRF_RADIO_SHORT_END_DISABLE_MASK | NRF_RADIO_SHORT_READY_START_MASK | \
NRF_RADIO_SHORT_CCAIDLE_TXEN_MASK)
#else
/// Value set to SHORTS register during transmission of a frame
#define SHORTS_TX_FRAME (NRF_RADIO_SHORT_END_DISABLE_MASK | NRF_RADIO_SHORT_READY_START_MASK)
#endif
/// Delay before sending ACK (12sym = 192uS)
#define TIFS_ACK_US 192
/// Delay before first check of received frame: 16 bits is MAC Frame Control field.
#define BCC_INIT (2 * 8)
/// Delay before second check of received frame if destination address is short.
#define BCC_SHORT_ADDR ((DEST_ADDR_OFFSET + SHORT_ADDRESS_SIZE) * 8)
/// Delay before second check of received frame if destination address is extended.
#define BCC_EXTENDED_ADDR ((DEST_ADDR_OFFSET + EXTENDED_ADDRESS_SIZE) * 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 CRC_LENGTH 2 ///< Length of CRC in 802.15.4 frames [bytes]
#define CRC_POLYNOMIAL 0x011021 ///< Polynomial used for CRC calculation in 802.15.4 frames
#define MHMU_MASK 0xff000700 ///< Mask of known bytes in ACK packet
#define MHMU_PATTERN 0x00000200 ///< Values of known bytes in ACK packet
#define MHMU_PATTERN_DSN_OFFSET 24 ///< Offset of DSN in MHMU_PATTER [bits]
/** 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(psdu) ((psdu)[(psdu)[0] - 1])
#if RADIO_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_drv_radio802154_rx_buffers[0];
#endif
#if defined ( __GNUC__ )
/// Ack frame buffer (EasyDMA cannot address whole RAM. Place buffer in the special section.)
static uint8_t m_ack_psdu[ACK_LENGTH + 1]
__attribute__ ((section ("nrf_radio_buffer.m_ack_psdu")));
#elif defined ( __ICCARM__ )
#pragma location="NRF_RADIO_BUFFER"
static uint8_t m_ack_psdu[ACK_LENGTH + 1];
#endif
static const uint8_t * mp_tx_data; ///< Pointer to data to transmit.
static uint32_t m_ed_time_left; ///< Remaining time of current energy detection procedure [us].
static uint8_t m_ed_result; ///< Result of current energy detection procedure.
static volatile radio_state_t m_state = RADIO_STATE_SLEEP; ///< State of the radio driver
typedef struct
{
bool prevent_ack :1; ///< If frame being received is not destined to this node (promiscuous mode).
} nrf_radio802154_flags_t;
static nrf_radio802154_flags_t m_flags; ///< Flags used to store current driver state.
static volatile uint8_t m_mutex; ///< Mutex preventing race condition.
/***************************************************************************************************
* @section Mutex
**************************************************************************************************/
/// Lock mutex to prevent race conditions.
static bool mutex_lock(void)
{
do
{
volatile uint8_t mutex_value = __LDREXB(&m_mutex);
if (mutex_value)
{
return false;
}
}
while (__STREXB(1, &m_mutex));
__DMB();
// Driver may be in WAITING_TIMESLOT state if timeslot ended during mutex locking.
assert(m_state == RADIO_STATE_WAITING_RX_FRAME ||
m_state == RADIO_STATE_SLEEP ||
m_state == RADIO_STATE_WAITING_TIMESLOT);
nrf_drv_radio802154_log(EVENT_MUTEX_LOCK, 0);
return true;
}
/// Unlock mutex.
static void mutex_unlock(void)
{
assert(m_state == RADIO_STATE_SLEEP ||
m_state == RADIO_STATE_WAITING_RX_FRAME);
nrf_drv_radio802154_log(EVENT_MUTEX_UNLOCK, 0);
__DMB();
m_mutex = 0;
}
/***************************************************************************************************
* @section FSM common operations
**************************************************************************************************/
/** Set driver state.
*
* @param[in] state Driver state to set.
*/
static inline void state_set(radio_state_t state)
{
m_state = state;
nrf_drv_radio802154_log(EVENT_SET_STATE, (uint32_t)state);
}
/// Common procedure when the driver enters SLEEP state.
static inline void sleep_start(void)
{
state_set(RADIO_STATE_SLEEP);
nrf_drv_radio802154_priority_drop_timeslot_exit();
mutex_unlock();
}
/// Start receiver to wait for frames.
static inline void rx_start(void)
{
nrf_radio_packet_ptr_set(mp_current_rx_buffer->psdu);
nrf_radio_task_trigger(NRF_RADIO_TASK_START);
}
/// Start receiver to wait for frame that can be acknowledged.
static inline void rx_frame_start(void)
{
rx_start();
// Just after starting receiving to receive buffer set packet pointer to ACK frame that can be
// sent automatically.
nrf_radio_packet_ptr_set(m_ack_psdu);
}
/** Get result of last RSSI measurement.
*
* @returns Result of last RSSI measurement [dBm].
*/
static inline int8_t rssi_last_measurement_get(void)
{
return -((int8_t)nrf_radio_rssi_sample_get());
}
/// Notify MAC layer that a frame was received.
static inline void received_frame_notify(void)
{
mp_current_rx_buffer->free = false;
nrf_drv_radio802154_notify_received(mp_current_rx_buffer->psdu, // data
rssi_last_measurement_get(), // rssi
RX_FRAME_LQI(mp_current_rx_buffer->psdu)); // lqi
}
/** Set currently used rx buffer to given address.
*
* @param[in] p_rx_buffer Pointer to receive buffer that should be used now.
*/
static inline void rx_buffer_in_use_set(rx_buffer_t * p_rx_buffer)
{
#if RADIO_RX_BUFFERS > 1
mp_current_rx_buffer = p_rx_buffer;
#else
(void) p_rx_buffer;
#endif
}
/***************************************************************************************************
* @section Radio parameters calculators
**************************************************************************************************/
/** Set radio channel
*
* @param[in] channel Channel number to set (11-26).
*/
static void channel_set(uint8_t channel)
{
assert(channel >= 11 && channel <= 26);
nrf_radio_frequency_set(5 + (5 * (channel - 11)));
}
/** Get radio channel.
*
* @returns Currently set channel number.
*/
static uint8_t channel_get(void)
{
return ((nrf_radio_frequency_get() - 5) / 5) + 11;
}
/** Set transmit power.
*
* @param[in] dbm Transmit power to set [dbm].
*/
static void tx_power_set(int8_t dbm)
{
const int8_t allowed_values[] = {-40, -20, -16, -12, -8, -4, 0, 2, 3, 4, 5, 6, 7, 8, 9};
const int8_t highest_value = allowed_values[(sizeof(allowed_values) / sizeof(allowed_values[0])) - 1];
if (dbm > highest_value)
{
dbm = highest_value;
}
else
{
for (uint32_t i = 0; i < sizeof(allowed_values) / sizeof(allowed_values[0]); i++)
{
if (dbm <= allowed_values[i])
{
dbm = allowed_values[i];
break;
}
}
}
nrf_radio_tx_power_set(dbm);
}
/***************************************************************************************************
* @section Shorts management
**************************************************************************************************/
/// Disable peripheral shorts.
static inline void shorts_disable(void)
{
nrf_radio_shorts_set(SHORTS_IDLE);
nrf_radio_ifs_set(0);
}
/// Enable peripheral shorts used during data frame transmission.
static inline void shorts_tx_frame_set(void)
{
nrf_radio_shorts_set(SHORTS_TX_FRAME);
}
/// Enable peripheral shorts used in receive state to enable automatic ACK procedure.
static inline void shorts_rx_initial_set(void)
{
nrf_radio_ifs_set(TIFS_ACK_US);
nrf_radio_bcc_set(BCC_INIT);
nrf_radio_shorts_set(SHORTS_RX_INITIAL);
}
/// Enable peripheral shorts used during automatic ACK transmission.
static inline void shorts_rx_following_set(void)
{
nrf_radio_shorts_set(SHORTS_RX_FOLLOWING);
}
/// Disable peripheral shorts used during automatic ACK transmission when ACK is being transmitted
static inline void shorts_tx_ack_set(void)
{
// If ACK is sent END_DISABLE short should persist to disable transmitter automatically.
nrf_radio_shorts_set(SHORTS_TX_ACK);
nrf_radio_ifs_set(0);
}
/***************************************************************************************************
* @section ACK transmission management
**************************************************************************************************/
/// Set valid sequence number in ACK frame.
static inline void ack_prepare(void)
{
// Copy sequence number from received frame to ACK frame.
m_ack_psdu[DSN_OFFSET] = mp_current_rx_buffer->psdu[DSN_OFFSET];
}
/// Set pending bit in ACK frame.
static inline void ack_pending_bit_set(void)
{
m_ack_psdu[FRAME_PENDING_OFFSET] = ACK_HEADER_WITH_PENDING;
if (!nrf_drv_radio802154_ack_pending_bit_should_be_set(mp_current_rx_buffer->psdu))
{
m_ack_psdu[FRAME_PENDING_OFFSET] = ACK_HEADER_WITHOUT_PENDING;
}
}
/** 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 inline bool ack_is_requested(const uint8_t * p_frame)
{
return (p_frame[ACK_REQUEST_OFFSET] & ACK_REQUEST_BIT) ? true : false;
}
/** Abort automatic ACK procedure.
*
* @param[in] state_to_set Driver state that shall be set after the procedure is aborted.
*/
static void auto_ack_abort(radio_state_t state_to_set)
{
nrf_drv_radio802154_log(EVENT_TRACE_ENTER, FUNCTION_AUTO_ACK_ABORT);
shorts_disable();
switch (nrf_radio_state_get())
{
case NRF_RADIO_STATE_RX: // When stopping before whole frame received.
case NRF_RADIO_STATE_RX_RU: // When transmission is initialized during receiver ramp up.
case NRF_RADIO_STATE_RX_IDLE:
case NRF_RADIO_STATE_TX_RU:
case NRF_RADIO_STATE_TX_IDLE:
case NRF_RADIO_STATE_TX:
nrf_radio_event_clear(NRF_RADIO_EVENT_DISABLED); // Clear disabled event that was set by short.
state_set(state_to_set);
nrf_radio_task_trigger(NRF_RADIO_TASK_DISABLE);
break;
case NRF_RADIO_STATE_RX_DISABLE:
case NRF_RADIO_STATE_DISABLED:
// Do not trigger DISABLE task in those states to prevent double DISABLED events.
state_set(state_to_set);
break;
default:
assert(false);
}
nrf_drv_radio802154_log(EVENT_TRACE_EXIT, FUNCTION_AUTO_ACK_ABORT);
}
/***************************************************************************************************
* @section ACK receiving management
**************************************************************************************************/
/// Enable hardware ACK matching accelerator.
static inline void ack_matching_enable(void)
{
nrf_radio_event_clear(NRF_RADIO_EVENT_MHRMATCH);
nrf_radio_mhmu_search_pattern_set(MHMU_PATTERN |
((uint32_t) mp_tx_data[DSN_OFFSET] <<
MHMU_PATTERN_DSN_OFFSET));
}
/// Disable hardware ACK matching accelerator.
static inline void ack_matching_disable(void)
{
nrf_radio_mhmu_search_pattern_set(0);
nrf_radio_event_clear(NRF_RADIO_EVENT_MHRMATCH);
}
/** Check if hardware ACK matching accelerator matched ACK pattern in received frame.
*
* @retval true ACK matching accelerator matched ACK pattern.
* @retval false ACK matching accelerator did not match ACK pattern.
*/
static inline bool ack_is_matched(void)
{
return (nrf_radio_event_get(NRF_RADIO_EVENT_MHRMATCH)) &&
(nrf_radio_crc_status_get() == NRF_RADIO_CRC_STATUS_OK);
}
/// Start receiver to receive data after receiving of ACK frame.
static inline void frame_rx_start_after_ack_rx(void)
{
ack_matching_disable();
state_set(RADIO_STATE_WAITING_RX_FRAME);
nrf_radio_task_trigger(NRF_RADIO_TASK_DISABLE); // Errata [110]
}
/***************************************************************************************************
* @section RADIO peripheral management
**************************************************************************************************/
/// Initialize radio peripheral
static void nrf_radio_init(void)
{
nrf_radio_mode_set(NRF_RADIO_MODE_IEEE802154_250KBIT);
nrf_radio_config_length_field_length_set(8);
nrf_radio_config_preamble_length_set(NRF_RADIO_PREAMBLE_LENGTH_32BIT_ZERO);
nrf_radio_config_crc_included_set(true);
nrf_radio_config_max_length_set(MAX_PACKET_SIZE);
// Configure CRC
nrf_radio_crc_length_set(CRC_LENGTH);
nrf_radio_crc_includes_address_set(NRF_RADIO_CRC_INCLUDES_ADDR_IEEE802154);
nrf_radio_crc_polynominal_set(CRC_POLYNOMIAL);
// Configure CCA
nrf_radio_cca_mode_set(RADIO_CCA_MODE);
nrf_radio_cca_ed_threshold_set(RADIO_CCA_ED_THRESHOLD);
nrf_radio_cca_corr_threshold_set(RADIO_CCA_CORR_THRESHOLD);
nrf_radio_cca_corr_counter_set(RADIO_CCA_CORR_LIMIT);
// Configure MAC Header Match Unit
nrf_radio_mhmu_search_pattern_set(0);
nrf_radio_mhmu_pattern_mask_set(MHMU_MASK);
nrf_radio_int_enable(NRF_RADIO_INT_FRAMESTART_MASK);
nrf_radio_int_enable(NRF_RADIO_INT_END_MASK);
nrf_radio_int_enable(NRF_RADIO_INT_DISABLED_MASK);
#if !RADIO_SHORT_CCAIDLE_TXEN
nrf_radio_int_enable(NRF_RADIO_INT_CCAIDLE_MASK);
#endif
nrf_radio_int_enable(NRF_RADIO_INT_CCABUSY_MASK);
nrf_radio_int_enable(NRF_RADIO_INT_READY_MASK);
nrf_radio_int_enable(NRF_RADIO_INT_BCMATCH_MASK);
nrf_radio_int_enable(NRF_RADIO_INT_EDEND_MASK);
}
/// Reset radio peripheral
static void nrf_radio_reset(void)
{
nrf_radio_power_set(false);
nrf_radio_power_set(true);
nrf_drv_radio802154_log(EVENT_RADIO_RESET, 0);
}
/// Initialize interrupts for radio peripheral
static void irq_init(void)
{
NVIC_SetPriority(RADIO_IRQn, RADIO_IRQ_PRIORITY);
NVIC_ClearPendingIRQ(RADIO_IRQn);
NVIC_EnableIRQ(RADIO_IRQn);
}
/// Deinitialize interrupts for radio peripheral
static void irq_deinit(void)
{
NVIC_DisableIRQ(RADIO_IRQn);
NVIC_ClearPendingIRQ(RADIO_IRQn);
NVIC_SetPriority(RADIO_IRQn, 0);
__DSB();
__ISB();
}
/***************************************************************************************************
* @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[in] Remaining time of energy detection procedure [us].
*
* @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 inline bool ed_iter_setup(uint32_t time_us)
{
uint32_t us_left_in_timeslot = nrf_raal_timeslot_us_left_get();
uint32_t next_ed_iters = us_left_in_timeslot / ED_ITER_DURATION;
if (next_ed_iters > ED_ITERS_OVERHEAD)
{
next_ed_iters -= ED_ITERS_OVERHEAD;
if ((time_us / ED_ITER_DURATION) < next_ed_iters)
{
m_ed_time_left = 0;
next_ed_iters = time_us / ED_ITER_DURATION;
}
else
{
m_ed_time_left = time_us - (next_ed_iters * ED_ITER_DURATION);
next_ed_iters--; // Time of ED procedure is (next_ed_iters + 1) * 128us
}
nrf_radio_ed_loop_count_set(next_ed_iters);
return true;
}
else
{
irq_deinit();
nrf_radio_reset();
m_ed_time_left = time_us;
return false;
}
}
/***************************************************************************************************
* @section RAAL notification handlers
**************************************************************************************************/
void nrf_raal_timeslot_started(void)
{
nrf_drv_radio802154_log(EVENT_TRACE_ENTER, FUNCTION_TIMESLOT_STARTED);
nrf_radio_reset();
nrf_radio_init();
irq_init();
assert(nrf_radio_shorts_get() == SHORTS_IDLE);
channel_set(nrf_drv_radio802154_pib_channel_get());
switch (m_state)
{
case RADIO_STATE_WAITING_TIMESLOT:
state_set(RADIO_STATE_WAITING_RX_FRAME);
nrf_radio_task_trigger(NRF_RADIO_TASK_DISABLE);
break;
case RADIO_STATE_ED:
if (ed_iter_setup(m_ed_time_left))
{
nrf_radio_task_trigger(NRF_RADIO_TASK_DISABLE);
}
break;
default:
assert(false);
}
nrf_drv_radio802154_log(EVENT_TRACE_EXIT, FUNCTION_TIMESLOT_STARTED);
}
void nrf_raal_timeslot_ended(void)
{
nrf_drv_radio802154_log(EVENT_TRACE_ENTER, FUNCTION_TIMESLOT_ENDED);
irq_deinit();
nrf_radio_reset();
switch (m_state)
{
case RADIO_STATE_CCA:
case RADIO_STATE_TX_FRAME:
case RADIO_STATE_RX_ACK:
nrf_drv_radio802154_notify_busy_channel();
break;
default:
break;
}
switch (m_state)
{
case RADIO_STATE_DISABLING:
sleep_start();
break;
case RADIO_STATE_SLEEP:
case RADIO_STATE_WAITING_TIMESLOT:
case RADIO_STATE_ED:
// Intentionally empty.
break;
case RADIO_STATE_WAITING_RX_FRAME:
case RADIO_STATE_RX_HEADER:
case RADIO_STATE_RX_FRAME:
case RADIO_STATE_TX_ACK:
case RADIO_STATE_CCA:
case RADIO_STATE_TX_FRAME:
case RADIO_STATE_RX_ACK:
mutex_lock();
state_set(RADIO_STATE_WAITING_TIMESLOT);
break;
}
nrf_drv_radio802154_log(EVENT_TRACE_EXIT, FUNCTION_TIMESLOT_ENDED);
}
/***************************************************************************************************
* @section RADIO interrupt handler
**************************************************************************************************/
/// This event is handled when the radio starts receiving a frame.
static inline void irq_framestart_state_waiting_rx_frame(void)
{
if (mutex_lock())
{
state_set(RADIO_STATE_RX_HEADER);
assert(nrf_radio_shorts_get() == SHORTS_RX_INITIAL);
if ((mp_current_rx_buffer->psdu[0] < ACK_LENGTH) ||
(mp_current_rx_buffer->psdu[0] > MAX_PACKET_SIZE))
{
auto_ack_abort(RADIO_STATE_WAITING_RX_FRAME);
nrf_radio_event_clear(NRF_RADIO_EVENT_BCMATCH);
nrf_radio_event_clear(NRF_RADIO_EVENT_END);
nrf_radio_event_clear(NRF_RADIO_EVENT_READY);
}
else
{
nrf_radio_task_trigger(NRF_RADIO_TASK_RSSISTART);
nrf_drv_radio802154_rx_started();
}
}
switch (nrf_radio_state_get())
{
case NRF_RADIO_STATE_RX:
// If the received frame was short the radio could have changed it's state.
case NRF_RADIO_STATE_RX_IDLE:
// The radio could have changed state to one of the following due to enabled shorts.
case NRF_RADIO_STATE_RX_DISABLE:
case NRF_RADIO_STATE_DISABLED:
case NRF_RADIO_STATE_TX_RU:
break;
// If something had stopped the CPU too long. Try to recover radio state.
case NRF_RADIO_STATE_TX_IDLE:
case NRF_RADIO_STATE_TX_DISABLE:
auto_ack_abort(RADIO_STATE_WAITING_RX_FRAME);
nrf_radio_event_clear(NRF_RADIO_EVENT_BCMATCH);
nrf_radio_event_clear(NRF_RADIO_EVENT_END);
nrf_radio_event_clear(NRF_RADIO_EVENT_READY);
break;
default:
assert(false);
}
}
/// This event is handled when the radio starts receiving an ACK frame.
static inline void irq_framestart_state_rx_ack(void)
{
assert(nrf_radio_shorts_get() == SHORTS_IDLE);
if ((mp_current_rx_buffer->psdu[0] < ACK_LENGTH) ||
(mp_current_rx_buffer->psdu[0] > MAX_PACKET_SIZE))
{
nrf_drv_radio802154_notify_busy_channel();
frame_rx_start_after_ack_rx();
nrf_radio_event_clear(NRF_RADIO_EVENT_END); // In case frame ended before task DISABLE
}
else
{
nrf_radio_task_trigger(NRF_RADIO_TASK_RSSISTART);
}
}
/// This event is handled when the radio starts transmitting a requested frame.
static inline void irq_framestart_state_tx_frame(void)
{
nrf_drv_radio802154_tx_started();
}
/// This event is handled when MHR is received
static inline void irq_bcmatch_mhr(void)
{
// Verify if time slot for receiving is available.
if (nrf_raal_timeslot_request(nrf_drv_radio802154_rx_duration_get(
mp_current_rx_buffer->psdu[0],
ack_is_requested(mp_current_rx_buffer->psdu))))
{
// Check Frame Control field.
switch (mp_current_rx_buffer->psdu[FRAME_TYPE_OFFSET] & FRAME_TYPE_MASK)
{
case FRAME_TYPE_BEACON:
// Beacon is broadcast frame.
m_flags.prevent_ack = false;
state_set(RADIO_STATE_RX_FRAME);
break;
case FRAME_TYPE_DATA:
case FRAME_TYPE_COMMAND:
// For data or command check destination address.
switch (mp_current_rx_buffer->psdu[DEST_ADDR_TYPE_OFFSET] &
DEST_ADDR_TYPE_MASK)
{
case DEST_ADDR_TYPE_SHORT:
nrf_radio_bcc_set(BCC_SHORT_ADDR);
break;
case DEST_ADDR_TYPE_EXTENDED:
nrf_radio_bcc_set(BCC_EXTENDED_ADDR);
break;
default:
auto_ack_abort(RADIO_STATE_WAITING_RX_FRAME);
nrf_radio_event_clear(NRF_RADIO_EVENT_END);
nrf_radio_event_clear(NRF_RADIO_EVENT_READY);
}
break;
default:
// For ACK and other types: in promiscuous mode accept it as broadcast;
// in normal mode drop the frame.
if (nrf_drv_radio802154_pib_promiscuous_get())
{
m_flags.prevent_ack = true;
state_set(RADIO_STATE_RX_FRAME);
}
else
{
auto_ack_abort(RADIO_STATE_WAITING_RX_FRAME);
nrf_radio_event_clear(NRF_RADIO_EVENT_END);
nrf_radio_event_clear(NRF_RADIO_EVENT_READY);
}
}
}
else
{
irq_deinit();
nrf_radio_reset();
state_set(RADIO_STATE_WAITING_TIMESLOT);
}
}
/// This event is generated when destination address fields are received.
static inline void irq_bcmatch_address(void)
{
if (nrf_drv_radio802154_pib_dest_addr_matches(mp_current_rx_buffer->psdu))
{
m_flags.prevent_ack = false;
state_set(RADIO_STATE_RX_FRAME);
}
else
{
if (nrf_drv_radio802154_pib_promiscuous_get())
{
m_flags.prevent_ack = true;
state_set(RADIO_STATE_RX_FRAME);
}
else
{
auto_ack_abort(RADIO_STATE_WAITING_RX_FRAME);
nrf_radio_event_clear(NRF_RADIO_EVENT_END);
nrf_radio_event_clear(NRF_RADIO_EVENT_READY);
}
}
}
/** This event is generated twice during frame reception:
* when MHR is received and when destination address fields are received.
*/
static inline void irq_bcmatch_state_rx_header(void)
{
assert(nrf_radio_shorts_get() == SHORTS_RX_INITIAL);
switch (nrf_radio_state_get())
{
case NRF_RADIO_STATE_RX:
case NRF_RADIO_STATE_RX_IDLE:
case NRF_RADIO_STATE_RX_DISABLE:
case NRF_RADIO_STATE_DISABLED:
case NRF_RADIO_STATE_TX_RU: // A lot of states due to shorts.
switch (nrf_radio_bcc_get())
{
case BCC_INIT: // Received MHR
irq_bcmatch_mhr();
break;
case BCC_SHORT_ADDR: // Received short destination address
case BCC_EXTENDED_ADDR: // Received extended destination address
// Check destination address during second match.
irq_bcmatch_address();
break;
default:
assert(false);
}
break;
case NRF_RADIO_STATE_TX_IDLE:
// Something had stopped the CPU too long. Start receiving again.
auto_ack_abort(RADIO_STATE_WAITING_RX_FRAME);
nrf_radio_event_clear(NRF_RADIO_EVENT_END);
nrf_radio_event_clear(NRF_RADIO_EVENT_READY);
break;
default:
assert(false);
}
}
/** This event is generated when radio receives invalid frame with length set to 0 or started
* receiving a frame after other procedure locked mutex.
*/
static inline void irq_end_state_waiting_rx_frame(void)
{
// Radio state is not asserted here. It can be a lot of states due to shorts.
if (mp_current_rx_buffer->psdu[0] == 0)
{
// If length of the frame is 0 there was no FRAMESTART event. Lock mutex now and abort sending ACK.
if (mutex_lock())
{
assert(nrf_radio_shorts_get() == SHORTS_RX_INITIAL);
auto_ack_abort(RADIO_STATE_WAITING_RX_FRAME);
nrf_radio_event_clear(NRF_RADIO_EVENT_READY);
}
}
else
{
// Do nothing. Whoever took mutex shall stop sending ACK.
}
}
/// This event is generated if frame being received is shorter than expected MAC header length.
static inline void irq_end_state_rx_header(void)
{
// Frame ended before header was received.
auto_ack_abort(RADIO_STATE_WAITING_RX_FRAME);
nrf_radio_event_clear(NRF_RADIO_EVENT_READY);
}
/// This event is generated when radio peripheral ends receiving of a complete frame.
static inline void irq_end_state_rx_frame(void)
{
assert(nrf_radio_shorts_get() == SHORTS_RX_INITIAL);
switch (nrf_radio_state_get())
{
case NRF_RADIO_STATE_RX_IDLE:
case NRF_RADIO_STATE_RX_DISABLE:
case NRF_RADIO_STATE_DISABLED:
case NRF_RADIO_STATE_TX_RU:
if (nrf_radio_crc_status_get() == NRF_RADIO_CRC_STATUS_OK)
{
if ((!ack_is_requested(mp_current_rx_buffer->psdu)) ||
(!nrf_drv_radio802154_pib_auto_ack_get()) ||
m_flags.prevent_ack)
{
auto_ack_abort(RADIO_STATE_WAITING_RX_FRAME);
nrf_radio_event_clear(NRF_RADIO_EVENT_READY);
received_frame_notify();
}
else
{
ack_prepare();
state_set(RADIO_STATE_TX_ACK);
}
}
else
{
auto_ack_abort(RADIO_STATE_WAITING_RX_FRAME);
nrf_radio_event_clear(NRF_RADIO_EVENT_READY);
}
break;
case NRF_RADIO_STATE_TX_IDLE:
// CPU was hold too long.
auto_ack_abort(RADIO_STATE_WAITING_RX_FRAME);
nrf_radio_event_clear(NRF_RADIO_EVENT_READY);
break;
default:
assert(false);
}
}
/// This event is generated when the radio ends transmission of ACK frame.
static inline void irq_end_state_tx_ack(void)
{
assert(nrf_radio_shorts_get() == SHORTS_TX_ACK);
shorts_disable();
received_frame_notify();
state_set(RADIO_STATE_WAITING_RX_FRAME);
// Receiver is enabled by shorts.
}
/** This event may occur at the beginning of transmission procedure (the procedure already has
* disabled shorts).
*/
static inline void irq_end_state_cca(void)
{
assert(nrf_radio_shorts_get() == SHORTS_IDLE);
}
/// This event is generated when the radio ends transmission of a frame.
static inline void irq_end_state_tx_frame(void)
{
shorts_disable();
assert(nrf_radio_shorts_get() == SHORTS_IDLE);
if (!ack_is_requested(mp_tx_data))
{
nrf_drv_radio802154_notify_transmitted(NULL, 0, 0);
state_set(RADIO_STATE_WAITING_RX_FRAME);
}
else
{
state_set(RADIO_STATE_RX_ACK);
ack_matching_enable();
}
// Task DISABLE is triggered by shorts.
}
/// This event is generated when the radio ends receiving of ACK frame.
static inline void irq_end_state_rx_ack(void)
{
assert(nrf_radio_shorts_get() == SHORTS_IDLE);
assert(nrf_radio_state_get() == NRF_RADIO_STATE_RX_IDLE);
if (ack_is_matched())
{
mp_current_rx_buffer->free = false;
nrf_drv_radio802154_notify_transmitted(mp_current_rx_buffer->psdu, // psdu
rssi_last_measurement_get(), // rssi
RX_FRAME_LQI(mp_current_rx_buffer->psdu)); // lqi
}
else
{
nrf_drv_radio802154_notify_busy_channel();
}
frame_rx_start_after_ack_rx();
}
/// This event is generated when radio peripheral disables in order to enter sleep state.
static inline void irq_disabled_state_disabling(void)
{
assert(nrf_radio_shorts_get() == SHORTS_IDLE);
assert(nrf_radio_state_get() == NRF_RADIO_STATE_DISABLED);
sleep_start();
}
/** This event is generated when the driver enters receive state.
*
* The radio is now disabled and the driver starts enabling receiver.
*/
static inline void irq_disabled_state_waiting_rx_frame(void)
{
assert(nrf_radio_shorts_get() == SHORTS_IDLE);
while (nrf_radio_state_get() == NRF_RADIO_STATE_TX_DISABLE)
{
// This event can be handled in TXDISABLE state due to double DISABLE event (IC-15879).
// This busy loop waits to the end of this state.
}
assert(nrf_radio_state_get() == NRF_RADIO_STATE_DISABLED);
nrf_radio_task_trigger(NRF_RADIO_TASK_RXEN);
mutex_unlock();
rx_buffer_in_use_set(nrf_drv_radio802154_rx_buffer_free_find());
tx_power_set(nrf_drv_radio802154_pib_tx_power_get());
// Clear this event after RXEN task in case event is triggered just before.
nrf_radio_event_clear(NRF_RADIO_EVENT_DISABLED);
}
/** This event is generated in case both END and DISABLED events are notified between
* checking if END event was notified and checking if DISABLED event was notified.
*
* In this invalid and rare case just drop received frame.
*/
static inline void irq_disabled_state_rx_frame(void)
{
assert(nrf_radio_event_get(NRF_RADIO_EVENT_END));
assert(nrf_radio_shorts_get() == SHORTS_RX_INITIAL);
auto_ack_abort(RADIO_STATE_WAITING_RX_FRAME);
nrf_radio_event_clear(NRF_RADIO_EVENT_END);
nrf_radio_event_clear(NRF_RADIO_EVENT_READY);
}
/** This event is generated during automatic ACK transmission.
*
* Receiver is disabled and transmitter is being enabled by shorts.
*/
static inline void irq_disabled_state_tx_ack(void)
{
assert(nrf_radio_shorts_get() == SHORTS_RX_INITIAL);
shorts_rx_following_set();
ack_pending_bit_set();
// IC-15879
nrf_radio_event_clear(NRF_RADIO_EVENT_DISABLED);
if (nrf_radio_state_get() == NRF_RADIO_STATE_TX_IDLE)
{
// CPU was hold too long.
auto_ack_abort(RADIO_STATE_WAITING_RX_FRAME);
nrf_radio_event_clear(NRF_RADIO_EVENT_READY);
}
}
/** This event is generated before CCA procedure starts.
*
* The radio is disabled and the drivers enables receiver in order to start CCA procedure.
*/
static inline void irq_disabled_state_cca(void)
{
assert(nrf_radio_shorts_get() == SHORTS_IDLE);
assert(nrf_radio_state_get() == NRF_RADIO_STATE_DISABLED);
nrf_radio_task_trigger(NRF_RADIO_TASK_RXEN);
}
/** This event is generated before transmission of a frame starts.
*
* The radio is disabled and enabling transmitter is requested by shorts or by the driver.
*/
static inline void irq_disabled_state_tx_frame(void)
{
if (nrf_radio_state_get() == NRF_RADIO_STATE_DISABLED)
{
shorts_tx_frame_set();
nrf_radio_task_trigger(NRF_RADIO_TASK_TXEN);
}
assert(nrf_radio_shorts_get() == SHORTS_TX_FRAME);
}
/** This event is generated when radio is disabled after transmission of a frame with ACK request.
*
* The driver enables receiver to receive ACK frame.
*/
static inline void irq_disabled_state_rx_ack(void)
{
assert(nrf_radio_shorts_get() == SHORTS_IDLE);
assert(nrf_radio_state_get() == NRF_RADIO_STATE_DISABLED);
nrf_radio_task_trigger(NRF_RADIO_TASK_RXEN);
if (mp_current_rx_buffer == NULL || (!mp_current_rx_buffer->free))
{
rx_buffer_in_use_set(nrf_drv_radio802154_rx_buffer_free_find());
}
}
/** This event is generated before energy detection procedure.
*
* The radio is disabled and the driver enables receiver in order to start energy detection
* procedure.
*/
static inline void irq_disabled_state_ed(void)
{
assert(nrf_radio_shorts_get() == SHORTS_IDLE);
assert(nrf_radio_state_get() == NRF_RADIO_STATE_DISABLED);
nrf_radio_task_trigger(NRF_RADIO_TASK_RXEN);
}
/** This event is generated when receiver is ready to start receiving a frame.
*
* Driver checks if buffer for a frame is available and starts receiver.
*/
static inline void irq_ready_state_waiting_rx_frame(void)
{
assert(nrf_radio_shorts_get() == SHORTS_IDLE);
assert(nrf_radio_state_get() == NRF_RADIO_STATE_RX_IDLE);
if ((mp_current_rx_buffer != NULL) && (mp_current_rx_buffer->free))
{
if (mutex_lock())
{
shorts_rx_initial_set();
rx_frame_start();
mutex_unlock();
}
}
}
/** This event is generated when transmitter is ready to transmit ACK frame.
*
* Transmission is started by shorts. The driver sets shorts to gracefully end transmission of ACK.
*/
static inline void irq_ready_state_tx_ack(void)
{
assert(nrf_radio_shorts_get() == SHORTS_RX_FOLLOWING);
shorts_tx_ack_set();
}
/** This event is generated when receiver is ready to start CCA procedure.
*
* The driver prepares for transmission and starts CCA procedure.
*/
static inline void irq_ready_state_cca(void)
{
assert(nrf_radio_shorts_get() == SHORTS_IDLE);
assert(nrf_radio_state_get() == NRF_RADIO_STATE_RX_IDLE);
state_set(RADIO_STATE_TX_FRAME);
shorts_tx_frame_set();
nrf_radio_task_trigger(NRF_RADIO_TASK_CCASTART);
}
/** This event is generated when transmitter is ready to transmit a frame.
*
* Transmission is started by shorts.
*/
static inline void irq_ready_state_tx_frame(void)
{
assert(nrf_radio_shorts_get() == SHORTS_TX_FRAME);
}
/// This event is generated when receiver is ready to receive an ACK frame.
static inline void irq_ready_state_rx_ack(void)
{
assert(nrf_radio_shorts_get() == SHORTS_IDLE);
assert(nrf_radio_state_get() == NRF_RADIO_STATE_RX_IDLE);
if (mp_current_rx_buffer == NULL || (!mp_current_rx_buffer->free))
{
nrf_drv_radio802154_notify_busy_channel();
frame_rx_start_after_ack_rx();
}
else
{
rx_start();
}
}
/// This event is generated when receiver is ready to start energy detection procedure.
static inline void irq_ready_state_ed(void)
{
assert(nrf_radio_shorts_get() == SHORTS_IDLE);
assert(nrf_radio_state_get() == NRF_RADIO_STATE_RX_IDLE);
nrf_radio_task_trigger(NRF_RADIO_TASK_EDSTART);
}
#if !RADIO_SHORT_CCAIDLE_TXEN
/// This event is generated when CCA reports that channel is idle.
static inline void irq_ccaidle(void)
{
assert(m_state == RADIO_STATE_TX_FRAME);
assert(nrf_radio_shorts_get() == SHORTS_TX_FRAME);
nrf_radio_task_trigger(NRF_RADIO_TASK_DISABLE);
}
#endif // RADIO_SHORT_CCAIDLE_TXEN
/// This event is generated when CCA reports busy channel prior to transmission.
static inline void irq_ccabusy_state_tx_frame(void)
{
assert(nrf_radio_shorts_get() == SHORTS_TX_FRAME);
assert(nrf_radio_state_get() == NRF_RADIO_STATE_RX_IDLE);
shorts_disable();
nrf_drv_radio802154_notify_busy_channel();
state_set(RADIO_STATE_WAITING_RX_FRAME);
nrf_radio_task_trigger(NRF_RADIO_TASK_DISABLE);
}
/// This event is generated when energy detection procedure ends.
static inline void irq_edend(void)
{
assert(m_state == RADIO_STATE_ED);
assert(nrf_radio_state_get() == NRF_RADIO_STATE_RX_IDLE);
assert(nrf_radio_shorts_get() == SHORTS_IDLE);
uint32_t result = nrf_radio_ed_sample_get();
m_ed_result = result > m_ed_result ? result : m_ed_result;
if (m_ed_time_left)
{
if (ed_iter_setup(m_ed_time_left))
{
nrf_radio_task_trigger(NRF_RADIO_TASK_EDSTART);
}
}
else
{
nrf_drv_radio802154_notify_energy_detected(m_ed_result);
state_set(RADIO_STATE_WAITING_RX_FRAME);
nrf_radio_task_trigger(NRF_RADIO_TASK_DISABLE);
}
}
/// Handler of radio interrupts.
void RADIO_IRQHandler(void)
{
nrf_drv_radio802154_log(EVENT_TRACE_ENTER, FUNCTION_IRQ_HANDLER);
if (nrf_radio_event_get(NRF_RADIO_EVENT_FRAMESTART))
{
nrf_drv_radio802154_log(EVENT_TRACE_ENTER, FUNCTION_EVENT_FRAMESTART);
nrf_radio_event_clear(NRF_RADIO_EVENT_FRAMESTART);
switch (m_state)
{
case RADIO_STATE_WAITING_RX_FRAME:
irq_framestart_state_waiting_rx_frame();
break;
case RADIO_STATE_RX_ACK:
irq_framestart_state_rx_ack();
break;
case RADIO_STATE_TX_FRAME:
irq_framestart_state_tx_frame();
break;
case RADIO_STATE_TX_ACK:
case RADIO_STATE_CCA: // This could happen at the beginning of transmission procedure.
case RADIO_STATE_WAITING_TIMESLOT:
break;
default:
assert(false);
}
nrf_drv_radio802154_log(EVENT_TRACE_EXIT, FUNCTION_EVENT_FRAMESTART);
}
// Check MAC frame header.
if (nrf_radio_event_get(NRF_RADIO_EVENT_BCMATCH))
{
nrf_drv_radio802154_log(EVENT_TRACE_ENTER, FUNCTION_EVENT_BCMATCH);
nrf_radio_event_clear(NRF_RADIO_EVENT_BCMATCH);
switch (m_state)
{
case RADIO_STATE_RX_HEADER:
irq_bcmatch_state_rx_header();
break;
case RADIO_STATE_WAITING_RX_FRAME: // Didn't lock mutex - other procedure manages radio.
case RADIO_STATE_WAITING_TIMESLOT: // Radio is out of timeslot now.
assert(m_mutex);
break;
default:
assert(false);
}
nrf_drv_radio802154_log(EVENT_TRACE_EXIT, FUNCTION_EVENT_BCMATCH);
}
if (nrf_radio_event_get(NRF_RADIO_EVENT_END))
{
nrf_drv_radio802154_log(EVENT_TRACE_ENTER, FUNCTION_EVENT_END);
nrf_radio_event_clear(NRF_RADIO_EVENT_END);
switch (m_state)
{
case RADIO_STATE_WAITING_RX_FRAME:
irq_end_state_waiting_rx_frame();
break;
case RADIO_STATE_RX_HEADER:
irq_end_state_rx_header();
break;
case RADIO_STATE_RX_FRAME:
irq_end_state_rx_frame();
break;
case RADIO_STATE_TX_ACK:
irq_end_state_tx_ack();
break;
case RADIO_STATE_CCA: // This could happen at the beginning of transmission procedure
// (the procedure already has disabled shorts).
irq_end_state_cca();
break;
case RADIO_STATE_TX_FRAME:
irq_end_state_tx_frame();
break;
case RADIO_STATE_RX_ACK: // Ended receiving of ACK.
irq_end_state_rx_ack();
break;
case RADIO_STATE_WAITING_TIMESLOT:
// Exit as soon as possible when waiting for timeslot.
break;
default:
assert(false);
}
nrf_drv_radio802154_log(EVENT_TRACE_EXIT, FUNCTION_EVENT_END);
}
if (nrf_radio_event_get(NRF_RADIO_EVENT_DISABLED))
{
nrf_drv_radio802154_log(EVENT_TRACE_ENTER, FUNCTION_EVENT_DISABLED);
nrf_radio_event_clear(NRF_RADIO_EVENT_DISABLED);
switch (m_state)
{
case RADIO_STATE_DISABLING:
irq_disabled_state_disabling();
break;
case RADIO_STATE_WAITING_RX_FRAME:
irq_disabled_state_waiting_rx_frame();
break;
case RADIO_STATE_RX_HEADER:
case RADIO_STATE_RX_FRAME:
irq_disabled_state_rx_frame();
break;
case RADIO_STATE_TX_ACK:
irq_disabled_state_tx_ack();
break;
case RADIO_STATE_CCA:
irq_disabled_state_cca();
break;
case RADIO_STATE_TX_FRAME:
irq_disabled_state_tx_frame();
break;
case RADIO_STATE_RX_ACK:
irq_disabled_state_rx_ack();
break;
case RADIO_STATE_ED:
irq_disabled_state_ed();
break;
case RADIO_STATE_WAITING_TIMESLOT:
// Exit as soon as possible when waiting for timeslot.
break;
default:
assert(false);
}
nrf_drv_radio802154_log(EVENT_TRACE_EXIT, FUNCTION_EVENT_DISABLED);
}
if (nrf_radio_event_get(NRF_RADIO_EVENT_READY))
{
nrf_drv_radio802154_log(EVENT_TRACE_ENTER, FUNCTION_EVENT_READY);
nrf_radio_event_clear(NRF_RADIO_EVENT_READY);
switch (m_state)
{
case RADIO_STATE_WAITING_RX_FRAME:
irq_ready_state_waiting_rx_frame();
break;
case RADIO_STATE_TX_ACK:
irq_ready_state_tx_ack();
break;
case RADIO_STATE_CCA:
irq_ready_state_cca();
break;
case RADIO_STATE_TX_FRAME:
irq_ready_state_tx_frame();
break;
case RADIO_STATE_RX_ACK:
irq_ready_state_rx_ack();
break;
case RADIO_STATE_ED:
irq_ready_state_ed();
break;
case RADIO_STATE_WAITING_TIMESLOT:
// Exit as soon as possible when waiting for timeslot.
break;
default:
assert(false);
}
nrf_drv_radio802154_log(EVENT_TRACE_EXIT, FUNCTION_EVENT_READY);
}
#if !RADIO_SHORT_CCAIDLE_TXEN
if (nrf_radio_event_get(NRF_RADIO_EVENT_CCAIDLE))
{
nrf_drv_radio802154_log(EVENT_TRACE_ENTER, FUNCTION_EVENT_CCAIDLE);
nrf_radio_event_clear(NRF_RADIO_EVENT_CCAIDLE);
irq_ccaidle();
nrf_drv_radio802154_log(EVENT_TRACE_EXIT, FUNCTION_EVENT_CCAIDLE);
}
#endif
if (nrf_radio_event_get(NRF_RADIO_EVENT_CCABUSY))
{
nrf_drv_radio802154_log(EVENT_TRACE_ENTER, FUNCTION_EVENT_CCABUSY);
nrf_radio_event_clear(NRF_RADIO_EVENT_CCABUSY);
switch (m_state)
{
case RADIO_STATE_TX_FRAME:
irq_ccabusy_state_tx_frame();
break;
case RADIO_STATE_WAITING_TIMESLOT:
// Exit as soon as possible when waiting for timeslot.
break;
default:
assert(false);
}
nrf_drv_radio802154_log(EVENT_TRACE_EXIT, FUNCTION_EVENT_CCABUSY);
}
if (nrf_radio_event_get(NRF_RADIO_EVENT_EDEND))
{
nrf_drv_radio802154_log(EVENT_TRACE_ENTER, FUNCTION_EVENT_EDEND);
nrf_radio_event_clear(NRF_RADIO_EVENT_EDEND);
irq_edend();
nrf_drv_radio802154_log(EVENT_TRACE_EXIT, FUNCTION_EVENT_EDEND);
}
nrf_drv_radio802154_log(EVENT_TRACE_EXIT, FUNCTION_IRQ_HANDLER);
}
/***************************************************************************************************
* @section FSM transition request sub-procedures
**************************************************************************************************/
/** Abort transmission procedure.
*
* This function is called when MAC layer requests transition from transmit to receive state.
*/
static inline void tx_procedure_abort(void)
{
shorts_disable();
assert(nrf_radio_shorts_get() == SHORTS_IDLE);
assert(m_mutex);
state_set(RADIO_STATE_WAITING_RX_FRAME);
switch (nrf_radio_state_get())
{
case NRF_RADIO_STATE_TX_DISABLE:
case NRF_RADIO_STATE_RX_DISABLE:
// Do not enabled receiver. It will be enabled in DISABLED handler.
break;
default:
nrf_radio_event_clear(NRF_RADIO_EVENT_DISABLED);
nrf_radio_task_trigger(NRF_RADIO_TASK_DISABLE);
}
nrf_radio_mhmu_search_pattern_set(0);
nrf_radio_event_clear(NRF_RADIO_EVENT_MHRMATCH);
// Clear events that could have happened in critical section due to receiving frame.
nrf_radio_event_clear(NRF_RADIO_EVENT_READY);
nrf_radio_event_clear(NRF_RADIO_EVENT_FRAMESTART);
nrf_radio_event_clear(NRF_RADIO_EVENT_END);
}
/***************************************************************************************************
* @section API functions
**************************************************************************************************/
void nrf_drv_radio802154_fsm_init(void)
{
const uint8_t ack_psdu[] = {0x05, ACK_HEADER_WITH_PENDING, 0x00, 0x00, 0x00, 0x00};
memcpy(m_ack_psdu, ack_psdu, sizeof(ack_psdu));
}
void nrf_drv_radio802154_fsm_deinit(void)
{
if (m_state != RADIO_STATE_WAITING_TIMESLOT &&
m_state != RADIO_STATE_SLEEP)
{
nrf_radio_reset();
}
irq_deinit();
}
radio_state_t nrf_drv_radio802154_fsm_state_get(void)
{
return m_state;
}
bool nrf_drv_radio802154_fsm_sleep(void)
{
bool result = false;
switch (m_state)
{
case RADIO_STATE_WAITING_TIMESLOT:
assert(m_mutex);
sleep_start();
result = true;
break;
case RADIO_STATE_WAITING_RX_FRAME:
if (mutex_lock())
{
auto_ack_abort(RADIO_STATE_DISABLING);
assert(nrf_radio_shorts_get() == SHORTS_IDLE);
// Clear events that could have happened in critical section due to receiving frame
// or RX ramp up.
nrf_radio_event_clear(NRF_RADIO_EVENT_FRAMESTART);
nrf_radio_event_clear(NRF_RADIO_EVENT_BCMATCH);
nrf_radio_event_clear(NRF_RADIO_EVENT_END);
nrf_radio_event_clear(NRF_RADIO_EVENT_READY);
result = true;
}
break;
case RADIO_STATE_RX_HEADER:
case RADIO_STATE_RX_FRAME:
case RADIO_STATE_TX_ACK:
break;
default:
assert(false); // This should not happen.
}
return result;
}
bool nrf_drv_radio802154_fsm_receive(uint8_t channel)
{
bool result = false;
switch (m_state)
{
case RADIO_STATE_WAITING_RX_FRAME:
case RADIO_STATE_RX_HEADER:
case RADIO_STATE_RX_FRAME:
case RADIO_STATE_TX_ACK:
result = true;
if (channel_get() != channel)
{
channel_set(channel);
nrf_drv_radio802154_pib_channel_set(channel);
if (mutex_lock())
{
auto_ack_abort(RADIO_STATE_WAITING_RX_FRAME);
}
}
break;
case RADIO_STATE_DISABLING:
channel_set(channel);
nrf_drv_radio802154_pib_channel_set(channel);
state_set(RADIO_STATE_WAITING_RX_FRAME);
result = true;
// TASK DISABLE was already triggered. Wait for event DISABLED.
break;
case RADIO_STATE_SLEEP:
assert(mutex_lock());
state_set(RADIO_STATE_WAITING_TIMESLOT);
nrf_drv_radio802154_pib_channel_set(channel);
nrf_raal_continuous_mode_enter();
result = true;
break;
case RADIO_STATE_CCA:
case RADIO_STATE_TX_FRAME:
case RADIO_STATE_RX_ACK:
tx_procedure_abort();
result = true;
break;
case RADIO_STATE_ED:
// Ignore receive function during energy detection procedure.
break;
case RADIO_STATE_WAITING_TIMESLOT:
// Ignore receive function in WAITING_TIMESLOT state - the radio will receive when time
// slot starts.
result = true;
break;
default:
assert(false);
}
return result;
}
bool nrf_drv_radio802154_fsm_transmit(const uint8_t * p_data, uint8_t channel, int8_t power, bool cca)
{
bool result = false;
mp_tx_data = p_data;
if (mutex_lock())
{
if (nrf_raal_timeslot_request(nrf_drv_radio802154_tx_duration_get(p_data[0],
ack_is_requested(p_data))))
{
assert(m_state == RADIO_STATE_WAITING_RX_FRAME);
channel_set(channel);
auto_ack_abort(cca ? RADIO_STATE_CCA : RADIO_STATE_TX_FRAME);
tx_power_set(power);
nrf_radio_packet_ptr_set(p_data);
// Clear events that could have happened in critical section due to receiving frame or RX ramp up.
nrf_radio_event_clear(NRF_RADIO_EVENT_FRAMESTART);
nrf_radio_event_clear(NRF_RADIO_EVENT_BCMATCH);
nrf_radio_event_clear(NRF_RADIO_EVENT_END);
nrf_radio_event_clear(NRF_RADIO_EVENT_READY);
// Check 2nd time in case this procedure was interrupted.
if (nrf_raal_timeslot_request(
nrf_drv_radio802154_tx_duration_get(p_data[0], ack_is_requested(p_data))))
{
result = true;
}
else
{
irq_deinit();
nrf_radio_reset();
state_set(RADIO_STATE_WAITING_TIMESLOT);
}
}
else
{
mutex_unlock();
}
}
return result;
}
bool nrf_drv_radio802154_fsm_energy_detection(uint8_t tx_channel, uint32_t time_us)
{
bool result = false;
switch (m_state)
{
case RADIO_STATE_SLEEP:
if (mutex_lock())
{
state_set(RADIO_STATE_ED);
nrf_drv_radio802154_pib_channel_set(tx_channel);
m_ed_time_left = time_us;
m_ed_result = 0;
nrf_raal_continuous_mode_enter();
result = true;
}
break;
case RADIO_STATE_WAITING_RX_FRAME:
if (mutex_lock())
{
m_ed_result = 0;
if (ed_iter_setup(time_us))
{
nrf_drv_radio802154_pib_channel_set(tx_channel);
channel_set(tx_channel);
auto_ack_abort(RADIO_STATE_ED);
assert(nrf_radio_shorts_get() == SHORTS_IDLE);
// Clear events that could have happened in critical section due to receiving
// frame or RX ramp up.
nrf_radio_event_clear(NRF_RADIO_EVENT_FRAMESTART);
nrf_radio_event_clear(NRF_RADIO_EVENT_BCMATCH);
nrf_radio_event_clear(NRF_RADIO_EVENT_END);
nrf_radio_event_clear(NRF_RADIO_EVENT_READY);
}
else
{
state_set(RADIO_STATE_ED);
}
result = true;
}
break;
case RADIO_STATE_DISABLING:
case RADIO_STATE_RX_HEADER:
case RADIO_STATE_RX_FRAME:
case RADIO_STATE_TX_ACK:
case RADIO_STATE_WAITING_TIMESLOT:
break;
default:
assert(false); // This should not happen.
}
return result;
}
void nrf_drv_radio802154_fsm_notify_buffer_free(rx_buffer_t * p_buffer)
{
p_buffer->free = true;
switch (m_state)
{
case RADIO_STATE_WAITING_RX_FRAME:
switch (nrf_radio_state_get())
{
case NRF_RADIO_STATE_RX_DISABLE: // This one could happen after receive of broadcast frame.
case NRF_RADIO_STATE_TX_DISABLE: // This one could happen due to stopping ACK.
case NRF_RADIO_STATE_DISABLED: // This one could happen during stopping ACK.
case NRF_RADIO_STATE_RX_RU: // This one could happen during enabling receiver (after sending ACK).
case NRF_RADIO_STATE_RX: // This one could happen if any other buffer is in use.
case NRF_RADIO_STATE_TX_RU: // This one could happen if received a short frame.
case NRF_RADIO_STATE_TX_IDLE: // This one could happen if received a short frame.
break;
case NRF_RADIO_STATE_RX_IDLE:
// Mutex to make sure Radio State did not change between IRQ and this process.
// Check shorts to make sure RX_IDLE state is due to occupied buffers - not
// during END/DISABLE short.
// If API call changed Radio state leave Radio as it is.
if (mutex_lock() && nrf_radio_shorts_get() == SHORTS_IDLE)
{
shorts_rx_initial_set();
rx_buffer_in_use_set(p_buffer);
rx_frame_start();
// Clear events that could have happened in critical section due to RX
// ramp up.
nrf_radio_event_clear(NRF_RADIO_EVENT_READY);
mutex_unlock();
}
break;
default:
assert(false);
}
break;
default:
// Don't perform any action in any other state (receiver should not be started).
break;
}
}