| /* 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; |
| } |
| } |