| /* Copyright (c) 2016, 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 the nrf 802.15.4 radio driver. |
| * |
| */ |
| |
| #include "nrf_drv_radio802154.h" |
| |
| #include <assert.h> |
| #include <stdbool.h> |
| #include <stddef.h> |
| #include <stdint.h> |
| #include <string.h> |
| |
| #include "nrf.h" |
| #ifdef RADIO_CLOCK_CTRL |
| #include "nrf_drv_clock.h" |
| #endif |
| #include "nrf_peripherals.h" |
| #include "nrf_radio.h" |
| |
| #if RADIO_RX_BUFFERS < 1 |
| #error Not enough rx buffers in the 802.15.4 radio driver. |
| #endif |
| |
| #define ACK_HEADER_WITH_PENDING 0x12 // First byte of ACK frame containing pending bit |
| #define ACK_HEADER_WITHOUT_PENDING 0x02 // First byte of ACK frame without pending bit |
| |
| #define ACK_LENGTH 5 // Length of ACK frame |
| #define ACK_REQUEST_BIT (1 << 5) // Ack request bit |
| #define ACK_REQUEST_OFFSET 1 // Byte containing Ack request bit (+1 for frame length byte) |
| #define DEST_ADDR_TYPE_EXTENDED 0x0c // Bits containing extended destination address type |
| #define DEST_ADDR_TYPE_MASK 0x0c // Mask of bits containing destination address type |
| #define DEST_ADDR_TYPE_OFFSET 2 // Byte containing destination address type (+1 for frame length byte) |
| #define DEST_ADDR_TYPE_SHORT 0x08 // Bits containing short destination address type |
| #define DEST_ADDR_OFFSET 5 // Offset of destination address in Data frame |
| #define DSN_OFFSET 3 // Byte containing DSN value (+1 for frame length byte) |
| #define FRAME_PENDING_BIT (1 << 4) // Pending bit |
| #define FRAME_PENDING_OFFSET 1 // Byte containing pending bit (+1 for frame length byte) |
| #define FRAME_TYPE_ACK 0x02 // Bits containing ACK frame type |
| #define FRAME_TYPE_BEACON 0x00 // Bits containing Beacon frame type |
| #define FRAME_TYPE_COMMAND 0x03 // Bits containing Command frame type |
| #define FRAME_TYPE_DATA 0x01 // Bits containing Data frame type |
| #define FRAME_TYPE_MASK 0x07 // Mask of bits containing frame type |
| #define FRAME_TYPE_OFFSET 1 // Byte containing frame type bits (+1 for frame length byte) |
| #define PAN_ID_COMPR_OFFSET 1 // Byte containing Pan Id compression bit (+1 for frame length byte) |
| #define PAN_ID_COMPR_MASK 0x40 // Pan Id compression bit |
| #define PAN_ID_OFFSET 3 // Offset of Pan Id in Data frame |
| #define SRC_ADDR_TYPE_EXTENDED 0xc0 // Bits containing extended source address type |
| #define SRC_ADDR_TYPE_MASK 0xc0 // Mask of bits containing source address type |
| #define SRC_ADDR_TYPE_OFFSET 2 // Byte containing source address type (+1 for frame length byte) |
| #define SRC_ADDR_TYPE_SHORT 0x80 // Bits containing short source address type |
| #define SRC_ADDR_OFFSET_SHORT_DST 8 // Offset of source address in Data frame if destination address is short |
| #define SRC_ADDR_OFFSET_EXTENDED_DST 14 // Offset of source address in Data frame if destination address is extended |
| |
| #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 0xff0007ff // Mask of known bytes in ACK packet |
| #define MHMU_PATTERN 0x00000205 // Values of known bytes in ACK packet |
| #define MHMU_PATTERN_DSN_OFFSET 24 // Offset of DSN in MHMU_PATTER [bits] |
| |
| #define PAN_ID_SIZE 2 // Size of Pan Id |
| #define SHORT_ADDRESS_SIZE 2 // Size of Short Mac Address |
| #define EXTENDED_ADDRESS_SIZE 8 // Size of Extended Mac Address |
| #define MAX_PACKET_SIZE 127 // Maximal size of radio packet |
| |
| #define BROADCAST_ADDRESS ((uint8_t [SHORT_ADDRESS_SIZE]) {0xff, 0xff}) // Broadcast Short Address |
| |
| // Maximum number of Short Addresses of nodes for which there is pending data in buffer. |
| #define NUM_PENDING_SHORT_ADDRESSES RADIO_PENDING_SHORT_ADDRESSES |
| // Maximum number of Extended Addresses of nodes for which there is pending data in buffer. |
| #define NUM_PENDING_EXTENDED_ADDRESSES RADIO_PENDING_EXTENDED_ADDRESSES |
| // Value used to mark Short Address as unused. |
| #define UNUSED_PENDING_SHORT_ADDRESS ((uint8_t [SHORT_ADDRESS_SIZE]) {0xff, 0xff}) |
| // Value used to mark Extended Address as unused. |
| #define UNUSED_PENDING_EXTENDED_ADDRESS ((uint8_t [EXTENDED_ADDRESS_SIZE]) {0}) |
| |
| // Delay before sending ACK (10sym = 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) |
| |
| // 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]) |
| |
| #define SHORT_CCAIDLE_TXEN 1 // Enable short between CCA Idle and TX Enable states. |
| |
| typedef struct |
| { |
| uint8_t psdu[MAX_PACKET_SIZE + 1]; |
| bool free; // If this buffer is free or contains a frame. |
| } rx_buffer_t; |
| |
| // Receive buffer |
| static rx_buffer_t m_receive_buffers[RADIO_RX_BUFFERS]; |
| |
| #if RADIO_RX_BUFFERS > 1 |
| static rx_buffer_t * mp_current_rx_buffer; // Pointer to currently used receive buffer. |
| #else |
| static rx_buffer_t * const mp_current_rx_buffer = &m_receive_buffers[0]; // If there is only one buffer use const pointer. |
| #endif |
| |
| // Ack frame buffer |
| static uint8_t m_ack_psdu[ACK_LENGTH + 1]; |
| |
| // Radio driver states |
| typedef enum |
| { |
| // Sleep |
| RADIO_STATE_SLEEP, // Low power (DISABLED) mode |
| |
| // Receive |
| RADIO_STATE_WAITING_RX_FRAME, // Waiting for frame in receiver mode |
| RADIO_STATE_RX_HEADER, // Received SFD, receiving MAC header |
| RADIO_STATE_RX_FRAME, // Received MAC destination address, receiving rest of the frame |
| RADIO_STATE_TX_ACK, // Received frame and transmitting ACK |
| |
| // Transmit |
| RADIO_STATE_CCA, // Performing CCA |
| RADIO_STATE_TX_FRAME, // Transmitting data frame (or beacon) |
| RADIO_STATE_RX_ACK, // Receiving ACK after transmitted frame |
| |
| // Energy Detection |
| RADIO_STATE_ED, // Performing Energy Detection procedure |
| } radio_state_t; |
| |
| static bool m_promiscuous = false; // Indicating if radio is in promiscuous mode |
| static volatile radio_state_t m_state = RADIO_STATE_SLEEP; // State of the radio driver |
| |
| static uint8_t m_pan_id[PAN_ID_SIZE] = {0xff, 0xff}; // Pan Id of this node |
| static uint8_t m_short_addr[SHORT_ADDRESS_SIZE] = {0xfe, 0xff}; // Short Address of this node |
| static uint8_t m_extended_addr[EXTENDED_ADDRESS_SIZE]; // Extended Address of this node |
| |
| 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; |
| |
| // Mutex preventing race condition. |
| static volatile uint8_t m_mutex; |
| |
| // If pending bit in ACK frame should be set to valid or default value. |
| static bool m_setting_pending_bit_enabled = true; |
| // Array of Short Addresses of nodes for which there is pending data in the buffer. |
| static uint8_t m_pending_short[NUM_PENDING_SHORT_ADDRESSES][SHORT_ADDRESS_SIZE]; |
| // Array of Extended Addresses of nodes for which there is pending data in the buffer. |
| static uint8_t m_pending_extended[NUM_PENDING_EXTENDED_ADDRESSES][EXTENDED_ADDRESS_SIZE]; |
| |
| static const uint8_t * mp_tx_data; // Pointer to data to transmit. |
| |
| // Lock mutex to prevent run 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(); |
| |
| assert(m_state == RADIO_STATE_WAITING_RX_FRAME); |
| |
| return true; |
| } |
| |
| // Unlock mutex. |
| static void mutex_unlock(void) |
| { |
| switch (m_state) |
| { |
| case RADIO_STATE_SLEEP: |
| case RADIO_STATE_WAITING_RX_FRAME: |
| break; |
| |
| default: |
| assert(false); |
| } |
| |
| __DMB(); |
| m_mutex = 0; |
| } |
| |
| // Enter driver critical section. |
| static void critical_section_enter(void) |
| { |
| NVIC_DisableIRQ(RADIO_IRQn); |
| __DSB(); |
| __ISB(); |
| } |
| |
| // Exit driver critical section. |
| static void critical_section_exit(void) |
| { |
| NVIC_EnableIRQ(RADIO_IRQn); |
| } |
| |
| |
| // Set radio state |
| static inline void state_set(radio_state_t state) |
| { |
| m_state = state; |
| } |
| |
| // Initialize static data. |
| static void data_init(void) |
| { |
| memset(m_pending_extended, 0, sizeof(m_pending_extended)); |
| memset(m_pending_short, 0xff, sizeof(m_pending_short)); |
| |
| const uint8_t ack_psdu[] = {0x05, ACK_HEADER_WITH_PENDING, 0x00, 0x00, 0x00, 0x00}; |
| memcpy(m_ack_psdu, ack_psdu, sizeof(ack_psdu)); |
| |
| for (uint32_t i = 0; i < RADIO_RX_BUFFERS; i++) |
| { |
| m_receive_buffers[i].free = true; |
| } |
| } |
| |
| // Initialize radio peripheral and interrupts. |
| 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 !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); |
| #if SHORT_CCAIDLE_TXEN |
| nrf_radio_shorts_enable(NRF_RADIO_SHORT_CCAIDLE_TXEN_MASK); |
| #endif |
| } |
| |
| #ifdef RADIO_IRQ_CTRL |
| static void irq_init(void) |
| { |
| NVIC_SetPriority(RADIO_IRQn, RADIO_IRQ_PRIORITY); |
| NVIC_ClearPendingIRQ(RADIO_IRQn); |
| NVIC_EnableIRQ(RADIO_IRQn); |
| } |
| #endif |
| |
| // Set radio channel |
| static void channel_set(uint8_t channel) |
| { |
| nrf_radio_frequency_set(5 + (5 * (channel - 11))); |
| } |
| |
| // Get radio channel |
| static uint8_t channel_get(void) |
| { |
| return ((nrf_radio_frequency_get() - 5) / 5) + 11; |
| } |
| |
| // Set transmit power |
| 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); |
| } |
| |
| static inline void rx_enable(void) |
| { |
| nrf_radio_task_trigger(NRF_RADIO_TASK_DISABLE); |
| } |
| |
| static inline void rx_start(void) |
| { |
| nrf_radio_packet_ptr_set(mp_current_rx_buffer->psdu); |
| nrf_radio_task_trigger(NRF_RADIO_TASK_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); |
| } |
| |
| static void received_frame_notify(void) |
| { |
| mp_current_rx_buffer->free = false; |
| nrf_drv_radio802154_received(mp_current_rx_buffer->psdu, // data |
| nrf_drv_radio802154_rssi_last_get(), // rssi |
| RX_FRAME_LQI(mp_current_rx_buffer->psdu)); // lqi |
| } |
| |
| static 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 |
| } |
| |
| static rx_buffer_t * free_rx_buffer_find(void) |
| { |
| for (uint32_t i = 0; i < RADIO_RX_BUFFERS; i++) |
| { |
| if (m_receive_buffers[i].free) |
| { |
| return &m_receive_buffers[i]; |
| } |
| } |
| |
| return NULL; |
| } |
| |
| // 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) |
| { |
| uint8_t * p_src_addr; |
| uint32_t i; |
| bool found = false; |
| |
| m_ack_psdu[FRAME_PENDING_OFFSET] = ACK_HEADER_WITH_PENDING; |
| |
| if (!m_setting_pending_bit_enabled) |
| { |
| return; |
| } |
| |
| switch (mp_current_rx_buffer->psdu[DEST_ADDR_TYPE_OFFSET] & DEST_ADDR_TYPE_MASK) |
| { |
| case DEST_ADDR_TYPE_SHORT: |
| p_src_addr = &mp_current_rx_buffer->psdu[SRC_ADDR_OFFSET_SHORT_DST]; |
| break; |
| |
| case DEST_ADDR_TYPE_EXTENDED: |
| p_src_addr = &mp_current_rx_buffer->psdu[SRC_ADDR_OFFSET_EXTENDED_DST]; |
| break; |
| |
| default: |
| return; |
| } |
| |
| if (0 == (mp_current_rx_buffer->psdu[PAN_ID_COMPR_OFFSET] & PAN_ID_COMPR_MASK)) |
| { |
| p_src_addr += 2; |
| } |
| |
| switch (mp_current_rx_buffer->psdu[SRC_ADDR_TYPE_OFFSET] & SRC_ADDR_TYPE_MASK) |
| { |
| case SRC_ADDR_TYPE_SHORT: |
| for (i = 0; i < NUM_PENDING_SHORT_ADDRESSES; i++) |
| { |
| if (nrf_radio_state_get() != NRF_RADIO_STATE_TX_RU) |
| { |
| break; |
| } |
| |
| if (0 == memcmp(p_src_addr, m_pending_short[i], sizeof(m_pending_short[i]))) |
| { |
| found = true; |
| break; |
| } |
| } |
| |
| break; |
| |
| case SRC_ADDR_TYPE_EXTENDED: |
| for (i = 0; i < NUM_PENDING_EXTENDED_ADDRESSES; i++) |
| { |
| if (nrf_radio_state_get() != NRF_RADIO_STATE_TX_RU) |
| { |
| break; |
| } |
| |
| if (0 == memcmp(p_src_addr, m_pending_extended[i], sizeof(m_pending_extended[i]))) |
| { |
| found = true; |
| break; |
| } |
| } |
| |
| break; |
| |
| default: |
| return; |
| } |
| |
| if (!found) |
| { |
| m_ack_psdu[FRAME_PENDING_OFFSET] = ACK_HEADER_WITHOUT_PENDING; |
| } |
| } |
| |
| // Check if ACK is 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; |
| } |
| |
| // Check if received destination address matches local or broadcast address. |
| static inline bool received_dest_addr_matched(void) |
| { |
| // Check destination PAN Id. |
| // Note that +1 in PSDU offset is added because first byte in PSDU is length. |
| if ((0 != memcmp(&mp_current_rx_buffer->psdu[PAN_ID_OFFSET + 1], m_pan_id, PAN_ID_SIZE)) && |
| (0 != memcmp(&mp_current_rx_buffer->psdu[PAN_ID_OFFSET + 1], BROADCAST_ADDRESS, PAN_ID_SIZE))) |
| { |
| return false; |
| } |
| |
| // Check destination address. |
| switch (mp_current_rx_buffer->psdu[DEST_ADDR_TYPE_OFFSET] & DEST_ADDR_TYPE_MASK) |
| { |
| case DEST_ADDR_TYPE_SHORT: |
| { |
| // Note that +1 in PSDU offset is added because first byte in PSDU is length. |
| if ((0 != memcmp(&mp_current_rx_buffer->psdu[DEST_ADDR_OFFSET + 1], m_short_addr, SHORT_ADDRESS_SIZE)) && |
| (0 != memcmp(&mp_current_rx_buffer->psdu[DEST_ADDR_OFFSET + 1], BROADCAST_ADDRESS, SHORT_ADDRESS_SIZE))) |
| { |
| return false; |
| } |
| |
| break; |
| } |
| |
| case DEST_ADDR_TYPE_EXTENDED: |
| { |
| // Note that +1 in PSDU offset is added because first byte in PSDU is length. |
| if (0 != memcmp(&mp_current_rx_buffer->psdu[DEST_ADDR_OFFSET + 1], m_extended_addr, sizeof(m_extended_addr))) |
| { |
| return false; |
| } |
| |
| break; |
| } |
| |
| default: |
| return false; |
| } |
| |
| return true; |
| } |
| |
| // Get result of last RSSI measurement. |
| static inline int8_t last_ed_result_get(void) |
| { |
| // TODO: Change this conversion to correct one after lab tests. |
| return nrf_radio_ed_sample_get(); |
| } |
| |
| // Enable peripheral shorts used during data frame transmission. |
| static inline void shorts_tx_frame_enable(void) |
| { |
| nrf_radio_shorts_enable(NRF_RADIO_SHORT_END_DISABLE_MASK | NRF_RADIO_SHORT_READY_START_MASK); |
| } |
| |
| // Disable peripheral shorts used during data frame transmission. |
| static inline void shorts_tx_frame_disable(void) |
| { |
| nrf_radio_shorts_disable(NRF_RADIO_SHORT_END_DISABLE_MASK | NRF_RADIO_SHORT_READY_START_MASK); |
| } |
| |
| // Enable peripheral shorts used during automatic ACK transmission. |
| // TIFS shorts are splitted |
| static inline void shorts_tifs_initial_enable(void) |
| { |
| nrf_radio_ifs_set(TIFS_ACK_US); |
| nrf_radio_bcc_set(BCC_INIT); |
| |
| nrf_radio_shorts_enable(NRF_RADIO_SHORT_END_DISABLE_MASK | NRF_RADIO_SHORT_DISABLED_TXEN_MASK | |
| NRF_RADIO_SHORT_FRAMESTART_BCSTART_MASK); |
| } |
| |
| static inline void shorts_tifs_following_enable(void) |
| { |
| nrf_radio_shorts_disable(NRF_RADIO_SHORT_DISABLED_TXEN_MASK); |
| nrf_radio_shorts_enable(NRF_RADIO_SHORT_READY_START_MASK); |
| } |
| |
| // Disable peripheral shorts used during automatic ACK transmission if ACK is transmitted |
| // (short disabling transmitter persists). |
| static inline void shorts_tifs_ack_disable(void) |
| { |
| // If ACK is sent END_DISABLE short should persist to disable transmitter automatically. |
| nrf_radio_shorts_disable(NRF_RADIO_SHORT_DISABLED_TXEN_MASK | |
| NRF_RADIO_SHORT_READY_START_MASK | NRF_RADIO_SHORT_FRAMESTART_BCSTART_MASK); |
| nrf_radio_ifs_set(0); |
| } |
| |
| // Disable peripheral shorts used during automatic ACK transmission if ACK is not transmitted |
| // (all shorts are disabled). |
| static inline void shorts_tifs_no_ack_disable(void) |
| { |
| nrf_radio_shorts_disable(NRF_RADIO_SHORT_END_DISABLE_MASK | NRF_RADIO_SHORT_DISABLED_TXEN_MASK | |
| NRF_RADIO_SHORT_READY_START_MASK | NRF_RADIO_SHORT_FRAMESTART_BCSTART_MASK); |
| nrf_radio_ifs_set(0); |
| } |
| |
| // Assert that peripheral shorts used during automatic ACK transmission are enabled. |
| static inline void assert_tifs_shorts_enabled(void) |
| { |
| assert(NRF_RADIO->SHORTS & NRF_RADIO_SHORT_FRAMESTART_BCSTART_MASK); |
| } |
| |
| // Assert that peripheral shorts used during automatic ACK transmission are disabled. |
| static inline void assert_tifs_shorts_disabled(void) |
| { |
| assert(!(NRF_RADIO->SHORTS & NRF_RADIO_SHORT_FRAMESTART_BCSTART_MASK)); |
| } |
| |
| // Abort automatic sending ACK. |
| static void auto_ack_abort(radio_state_t state_to_set) |
| { |
| shorts_tifs_no_ack_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); |
| } |
| } |
| |
| static inline bool tx_procedure_begin(const uint8_t * p_data, uint8_t channel, int8_t power) |
| { |
| bool result = false; |
| |
| critical_section_enter(); |
| |
| if (mutex_lock()) |
| { |
| switch (m_state) |
| { |
| case RADIO_STATE_WAITING_RX_FRAME: |
| channel_set(channel); |
| auto_ack_abort(RADIO_STATE_CCA); |
| |
| assert_tifs_shorts_disabled(); |
| |
| 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); |
| |
| result = true; |
| |
| break; |
| |
| default: |
| assert(false); // This should not happen. |
| } |
| } |
| |
| critical_section_exit(); |
| |
| return result; |
| } |
| |
| static inline void tx_procedure_abort(void) |
| { |
| critical_section_enter(); |
| |
| switch (m_state) |
| { |
| case RADIO_STATE_CCA: |
| case RADIO_STATE_TX_FRAME: |
| case RADIO_STATE_RX_ACK: |
| shorts_tx_frame_disable(); |
| |
| assert_tifs_shorts_disabled(); |
| 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); |
| rx_enable(); |
| } |
| |
| 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); |
| |
| break; |
| |
| // Just before entering this event handler Ack Frame could be received and |
| // driver enters WatiningRxFrame state. In this case just clear pending events |
| // because change of state was requested. |
| case RADIO_STATE_WAITING_RX_FRAME: |
| break; |
| |
| default: |
| assert(false); |
| } |
| |
| critical_section_exit(); |
| } |
| |
| static inline void enabling_rx_procedure_begin(rx_buffer_t * p_buffer) |
| { |
| assert(p_buffer->free == false); |
| |
| critical_section_enter(); |
| |
| 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. |
| break; |
| |
| case NRF_RADIO_STATE_RX_IDLE: |
| // Mutex to make sure Radio State did not change between IRQ and this process. |
| // If API call changed Radio state leave Radio as it is. |
| if (mutex_lock()) |
| { |
| shorts_tifs_initial_enable(); |
| |
| rx_buffer_in_use_set(p_buffer); |
| rx_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; |
| } |
| |
| critical_section_exit(); |
| } |
| |
| static inline bool energy_detection_procedure_begin(uint8_t tx_channel) |
| { |
| bool result = false; |
| |
| critical_section_enter(); |
| |
| if (mutex_lock()) |
| { |
| switch (m_state) |
| { |
| case RADIO_STATE_WAITING_RX_FRAME: |
| channel_set(tx_channel); |
| auto_ack_abort(RADIO_STATE_ED); |
| |
| assert_tifs_shorts_disabled(); |
| |
| // 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; |
| |
| default: |
| assert(false); // This should not happen. |
| } |
| } |
| |
| critical_section_exit(); |
| |
| return result; |
| } |
| |
| static inline bool sleep_procedure_begin(void) |
| { |
| bool result = false; |
| |
| critical_section_enter(); |
| |
| if (mutex_lock()) |
| { |
| switch (m_state) |
| { |
| case RADIO_STATE_WAITING_RX_FRAME: |
| auto_ack_abort(RADIO_STATE_SLEEP); |
| |
| assert_tifs_shorts_disabled(); |
| |
| // 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; |
| |
| default: |
| assert(false); // This should not happen. |
| } |
| } |
| |
| critical_section_exit(); |
| |
| return result; |
| } |
| |
| static void radio_reset(void) |
| { |
| uint8_t channel = channel_get(); |
| |
| nrf_radio_power_set(false); |
| nrf_radio_power_set(true); |
| |
| nrf_radio_init(); |
| |
| channel_set(channel); |
| |
| switch (m_state) |
| { |
| case RADIO_STATE_WAITING_RX_FRAME: |
| case RADIO_STATE_RX_HEADER: |
| case RADIO_STATE_RX_FRAME: |
| case RADIO_STATE_TX_ACK: |
| state_set(RADIO_STATE_WAITING_RX_FRAME); |
| rx_enable(); |
| break; |
| |
| case RADIO_STATE_CCA: |
| case RADIO_STATE_TX_FRAME: |
| case RADIO_STATE_RX_ACK: |
| nrf_drv_radio802154_busy_channel(); |
| state_set(RADIO_STATE_WAITING_RX_FRAME); |
| rx_enable(); |
| break; |
| |
| case RADIO_STATE_ED: |
| nrf_drv_radio802154_energy_detected(0); |
| state_set(RADIO_STATE_WAITING_RX_FRAME); |
| rx_enable(); |
| break; |
| |
| case RADIO_STATE_SLEEP: |
| mutex_unlock(); |
| break; |
| |
| default: |
| assert(false); |
| } |
| } |
| |
| uint8_t nrf_drv_radio802154_channel_get(void) |
| { |
| return channel_get(); |
| } |
| |
| void nrf_drv_radio802154_pan_id_set(const uint8_t * p_pan_id) |
| { |
| memcpy(m_pan_id, p_pan_id, PAN_ID_SIZE); |
| } |
| |
| void nrf_drv_radio802154_extended_address_set(const uint8_t * p_extended_address) |
| { |
| memcpy(m_extended_addr, p_extended_address, EXTENDED_ADDRESS_SIZE); |
| } |
| |
| void nrf_drv_radio802154_short_address_set(const uint8_t * p_short_address) |
| { |
| memcpy(m_short_addr, p_short_address, SHORT_ADDRESS_SIZE); |
| } |
| |
| void nrf_drv_radio802154_init(void) |
| { |
| data_init(); |
| |
| nrf_radio_init(); |
| #ifdef RADIO_IRQ_CTRL |
| irq_init(); |
| #endif |
| } |
| |
| bool nrf_drv_radio802154_sleep(void) |
| { |
| bool result = true; |
| |
| switch (m_state) |
| { |
| case RADIO_STATE_SLEEP: |
| break; |
| |
| case RADIO_STATE_WAITING_RX_FRAME: |
| case RADIO_STATE_RX_HEADER: |
| case RADIO_STATE_RX_FRAME: |
| case RADIO_STATE_TX_ACK: |
| result = sleep_procedure_begin(); |
| break; |
| |
| default: |
| assert(false); |
| } |
| |
| return result; |
| } |
| |
| bool nrf_drv_radio802154_receive(uint8_t channel, bool force_rx) |
| { |
| bool result = true; |
| |
| switch (m_state) |
| { |
| case RADIO_STATE_WAITING_RX_FRAME: |
| case RADIO_STATE_RX_HEADER: |
| case RADIO_STATE_RX_FRAME: |
| case RADIO_STATE_TX_ACK: |
| if (channel_get() != channel) |
| { |
| if ((m_state != RADIO_STATE_WAITING_RX_FRAME) && !force_rx) |
| { |
| result = false; |
| } |
| else |
| { |
| channel_set(channel); |
| if (mutex_lock()) |
| { |
| auto_ack_abort(RADIO_STATE_WAITING_RX_FRAME); |
| } |
| } |
| } |
| break; |
| |
| case RADIO_STATE_SLEEP: |
| if (nrf_radio_state_get() == NRF_RADIO_STATE_DISABLED) |
| { |
| assert_tifs_shorts_disabled(); |
| |
| state_set(RADIO_STATE_WAITING_RX_FRAME); |
| |
| channel_set(channel); |
| |
| #ifdef RADIO_CLOCK_CTRL |
| // Start HFCLK |
| nrf_drv_clock_hfclk_request(NULL); |
| while(!nrf_drv_clock_hfclk_is_running()) {} |
| #endif |
| |
| rx_enable(); |
| } |
| break; |
| |
| case RADIO_STATE_TX_FRAME: |
| case RADIO_STATE_RX_ACK: |
| if (force_rx) |
| { |
| tx_procedure_abort(); |
| } |
| else |
| { |
| result = false; |
| } |
| break; |
| |
| default: |
| assert(false); |
| } |
| |
| return result; |
| } |
| |
| bool nrf_drv_radio802154_transmit(const uint8_t * p_data, uint8_t channel, int8_t power) |
| { |
| mp_tx_data = p_data; |
| |
| return tx_procedure_begin(p_data, channel, power); |
| } |
| |
| void nrf_drv_radio802154_buffer_free(uint8_t * p_data) |
| { |
| enabling_rx_procedure_begin((rx_buffer_t *)p_data); |
| } |
| |
| int8_t nrf_drv_radio802154_rssi_last_get(void) |
| { |
| uint8_t minusDBm = nrf_radio_rssi_sample_get(); |
| return - (int8_t)minusDBm; |
| } |
| |
| bool nrf_drv_radio802154_promiscuous_get(void) |
| { |
| return m_promiscuous; |
| } |
| |
| void nrf_drv_radio802154_promiscuous_set(bool enabled) |
| { |
| m_promiscuous = enabled; |
| } |
| |
| void nrf_drv_radio802154_auto_pending_bit_set(bool enabled) |
| { |
| m_setting_pending_bit_enabled = enabled; |
| } |
| |
| bool nrf_drv_radio802154_pending_bit_for_addr_set(const uint8_t * p_addr, bool extended) |
| { |
| if (extended) |
| { |
| for (uint32_t i = 0; i < NUM_PENDING_EXTENDED_ADDRESSES; i++) |
| { |
| if (0 == memcmp(m_pending_extended[i], p_addr, sizeof(m_pending_extended[i]))) |
| { |
| return true; |
| } |
| |
| if (0 == memcmp(m_pending_extended[i], UNUSED_PENDING_EXTENDED_ADDRESS, sizeof(m_pending_extended[i]))) |
| { |
| memcpy(m_pending_extended[i], p_addr, sizeof(m_pending_extended[i])); |
| return true; |
| } |
| } |
| } |
| else |
| { |
| for (uint32_t i = 0; i < NUM_PENDING_SHORT_ADDRESSES; i++) |
| { |
| if (0 == memcmp(m_pending_short[i], p_addr, sizeof(m_pending_short[i]))) |
| { |
| return true; |
| } |
| |
| if (0 == memcmp(m_pending_short[i], UNUSED_PENDING_SHORT_ADDRESS, sizeof(m_pending_short[i]))) |
| { |
| memcpy(m_pending_short[i], p_addr, sizeof(m_pending_short[i])); |
| return true; |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| bool nrf_drv_radio802154_pending_bit_for_addr_clear(const uint8_t * p_addr, bool extended) |
| { |
| bool result = false; |
| |
| if (extended) |
| { |
| for (uint32_t i = 0; i < NUM_PENDING_EXTENDED_ADDRESSES; i++) |
| { |
| if (0 == memcmp(m_pending_extended[i], p_addr, sizeof(m_pending_extended[i]))) |
| { |
| memset(m_pending_extended[i], 0, sizeof(m_pending_extended[i])); |
| result = true; |
| } |
| } |
| } |
| else |
| { |
| for (uint32_t i = 0; i < NUM_PENDING_SHORT_ADDRESSES; i++) |
| { |
| if (0 == memcmp(m_pending_short[i], p_addr, sizeof(m_pending_short[i]))) |
| { |
| memset(m_pending_short[i], 0xff, sizeof(m_pending_short[i])); |
| result = true; |
| } |
| } |
| } |
| |
| return result; |
| } |
| |
| void nrf_drv_radio802154_pending_bit_for_addr_reset(bool extended) |
| { |
| if (extended) |
| { |
| memset(m_pending_extended, 0, sizeof(m_pending_extended)); |
| } |
| else |
| { |
| memset(m_pending_short, 0xff, sizeof(m_pending_short)); |
| } |
| } |
| |
| bool nrf_drv_radio802154_energy_detection(uint8_t channel, uint32_t time_us) |
| { |
| |
| nrf_radio_ed_loop_count_set(time_us / 128UL); // multiple of 10 symbols (128 us) |
| |
| return energy_detection_procedure_begin(channel); |
| } |
| |
| void nrf_drv_radio802154_irq_handler(void) |
| { |
| if (nrf_radio_event_get(NRF_RADIO_EVENT_FRAMESTART)) |
| { |
| nrf_radio_event_clear(NRF_RADIO_EVENT_FRAMESTART); |
| |
| switch (m_state) |
| { |
| case RADIO_STATE_WAITING_RX_FRAME: |
| if (mutex_lock()) |
| { |
| state_set(RADIO_STATE_RX_HEADER); |
| assert_tifs_shorts_enabled(); |
| |
| 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); |
| } |
| else |
| { |
| nrf_radio_task_trigger(NRF_RADIO_TASK_RSSISTART); |
| } |
| } |
| |
| 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: |
| radio_reset(); |
| break; |
| |
| default: |
| assert(false); |
| } |
| break; |
| |
| case RADIO_STATE_TX_ACK: |
| case RADIO_STATE_TX_FRAME: |
| case RADIO_STATE_RX_ACK: |
| case RADIO_STATE_CCA: // This could happen at the beginning of transmission procedure. |
| break; |
| |
| default: |
| assert(false); |
| } |
| } |
| |
| |
| // Check MAC frame header. |
| if (nrf_radio_event_get(NRF_RADIO_EVENT_BCMATCH)) |
| { |
| nrf_radio_event_clear(NRF_RADIO_EVENT_BCMATCH); |
| |
| switch (m_state) |
| { |
| case RADIO_STATE_RX_HEADER: |
| assert_tifs_shorts_enabled(); |
| |
| switch (nrf_radio_state_get()) |
| { |
| case NRF_RADIO_STATE_RX: |
| case NRF_RADIO_STATE_RX_IDLE: |
| case NRF_RADIO_STATE_RX_DISABLE: // A lot of states due to shorts. |
| case NRF_RADIO_STATE_DISABLED: |
| case NRF_RADIO_STATE_TX_RU: |
| |
| switch (nrf_radio_bcc_get()) |
| { |
| case BCC_INIT: |
| |
| // 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); |
| } |
| |
| break; |
| |
| default: |
| |
| // For ACK and other types: in promiscous mode accept it as broadcast; |
| // in normal mode drop the frame. |
| if (m_promiscuous) |
| { |
| m_flags.prevent_ack = true; |
| state_set(RADIO_STATE_RX_FRAME); |
| } |
| else |
| { |
| auto_ack_abort(RADIO_STATE_WAITING_RX_FRAME); |
| } |
| } |
| |
| break; |
| |
| case BCC_SHORT_ADDR: |
| case BCC_EXTENDED_ADDR: |
| // Check destination address during second match. |
| if (received_dest_addr_matched()) |
| { |
| m_flags.prevent_ack = false; |
| state_set(RADIO_STATE_RX_FRAME); |
| } |
| else |
| { |
| if (m_promiscuous) |
| { |
| m_flags.prevent_ack = true; |
| state_set(RADIO_STATE_RX_FRAME); |
| } |
| else |
| { |
| auto_ack_abort(RADIO_STATE_WAITING_RX_FRAME); |
| } |
| } |
| |
| break; |
| |
| default: |
| assert(false); |
| } |
| |
| break; |
| |
| case NRF_RADIO_STATE_TX_IDLE: |
| // Something had stopped the CPU too long. Try to recover radio state. |
| radio_reset(); |
| break; |
| |
| default: |
| assert(false); |
| } |
| |
| break; |
| |
| default: |
| assert(false); |
| } |
| } |
| |
| if (nrf_radio_event_get(NRF_RADIO_EVENT_END)) |
| { |
| nrf_radio_event_clear(NRF_RADIO_EVENT_END); |
| |
| switch (m_state) |
| { |
| case RADIO_STATE_WAITING_RX_FRAME: |
| |
| // 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_tifs_shorts_enabled(); |
| auto_ack_abort(RADIO_STATE_WAITING_RX_FRAME); |
| } |
| } |
| else |
| { |
| // Do nothing. Whoever took mutex shall stop sending ACK. |
| } |
| |
| break; |
| |
| case RADIO_STATE_RX_HEADER: |
| // Frame ended before header was received. |
| auto_ack_abort(RADIO_STATE_WAITING_RX_FRAME); |
| break; |
| |
| case RADIO_STATE_RX_FRAME: |
| assert_tifs_shorts_enabled(); |
| |
| 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) |
| { |
| ack_prepare(); |
| |
| if ((!ack_is_requested(mp_current_rx_buffer->psdu)) || m_flags.prevent_ack) |
| { |
| auto_ack_abort(RADIO_STATE_WAITING_RX_FRAME); |
| received_frame_notify(); |
| } |
| else |
| { |
| state_set(RADIO_STATE_TX_ACK); |
| } |
| } |
| else |
| { |
| auto_ack_abort(RADIO_STATE_WAITING_RX_FRAME); |
| } |
| |
| break; |
| |
| case NRF_RADIO_STATE_TX_IDLE: |
| // CPU was hold too long. |
| nrf_radio_event_clear(NRF_RADIO_EVENT_READY); |
| auto_ack_abort(RADIO_STATE_WAITING_RX_FRAME); |
| break; |
| |
| default: |
| assert(false); |
| } |
| |
| break; |
| |
| case RADIO_STATE_TX_ACK: // Ended transmission of ACK. |
| shorts_tifs_no_ack_disable(); |
| |
| received_frame_notify(); |
| |
| state_set(RADIO_STATE_WAITING_RX_FRAME); |
| // Receiver is enabled by shorts. |
| break; |
| |
| case RADIO_STATE_CCA: // This could happen at the beginning of transmission procedure (the procedure already has disabled shorts). |
| assert_tifs_shorts_disabled(); |
| break; |
| |
| case RADIO_STATE_TX_FRAME: |
| shorts_tx_frame_disable(); |
| assert_tifs_shorts_disabled(); |
| |
| if (!ack_is_requested(mp_tx_data)) |
| { |
| nrf_drv_radio802154_transmitted(false); |
| |
| state_set(RADIO_STATE_WAITING_RX_FRAME); |
| } |
| else |
| { |
| state_set(RADIO_STATE_RX_ACK); |
| |
| 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)); |
| } |
| |
| // Task DISABLE is triggered by shorts. |
| break; |
| |
| case RADIO_STATE_RX_ACK: // Ended receiving of ACK. |
| assert_tifs_shorts_disabled(); |
| assert(nrf_radio_state_get() == NRF_RADIO_STATE_RX_IDLE); |
| |
| if ((nrf_radio_event_get(NRF_RADIO_EVENT_MHRMATCH)) && |
| (nrf_radio_crc_status_get() == NRF_RADIO_CRC_STATUS_OK)) |
| { |
| nrf_drv_radio802154_transmitted( |
| (mp_current_rx_buffer->psdu[FRAME_PENDING_OFFSET] & FRAME_PENDING_BIT) != 0); |
| |
| nrf_radio_mhmu_search_pattern_set(0); |
| nrf_radio_event_clear(NRF_RADIO_EVENT_MHRMATCH); |
| state_set(RADIO_STATE_WAITING_RX_FRAME); |
| shorts_tifs_initial_enable(); |
| rx_start(); |
| mutex_unlock(); |
| } |
| else |
| { |
| nrf_radio_event_clear(NRF_RADIO_EVENT_MHRMATCH); // In case CRC is invalid. |
| rx_start(); |
| } |
| |
| break; |
| |
| default: |
| assert(false); |
| } |
| } |
| |
| if (nrf_radio_event_get(NRF_RADIO_EVENT_DISABLED)) |
| { |
| nrf_radio_event_clear(NRF_RADIO_EVENT_DISABLED); |
| |
| switch (m_state) |
| { |
| case RADIO_STATE_SLEEP: |
| assert_tifs_shorts_disabled(); |
| assert(nrf_radio_state_get() == NRF_RADIO_STATE_DISABLED); |
| |
| #if RADIO_CLOCK_CTRL |
| nrf_drv_clock_hfclk_release(); |
| #endif |
| mutex_unlock(); |
| break; |
| |
| case RADIO_STATE_WAITING_RX_FRAME: |
| assert_tifs_shorts_disabled(); |
| |
| 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(free_rx_buffer_find()); |
| |
| // Clear this event after RXEN task in case event is triggered just before. |
| nrf_radio_event_clear(NRF_RADIO_EVENT_DISABLED); |
| break; |
| |
| case RADIO_STATE_TX_ACK: |
| assert_tifs_shorts_enabled(); |
| |
| shorts_tifs_following_enable(); |
| ack_pending_bit_set(); |
| |
| if (nrf_radio_state_get() == NRF_RADIO_STATE_TX_IDLE) |
| { |
| // CPU was hold too long. |
| nrf_radio_event_clear(NRF_RADIO_EVENT_READY); |
| auto_ack_abort(RADIO_STATE_WAITING_RX_FRAME); |
| } |
| break; |
| |
| case RADIO_STATE_CCA: |
| assert_tifs_shorts_disabled(); |
| assert(nrf_radio_state_get() == NRF_RADIO_STATE_DISABLED); |
| nrf_radio_task_trigger(NRF_RADIO_TASK_RXEN); |
| break; |
| |
| case RADIO_STATE_TX_FRAME: |
| assert_tifs_shorts_disabled(); |
| #if !SHORT_CCAIDLE_TXEN |
| nrf_radio_task_trigger(NRF_RADIO_TASK_TXEN); |
| #endif |
| break; |
| |
| case RADIO_STATE_RX_ACK: |
| assert_tifs_shorts_disabled(); |
| assert(nrf_radio_state_get() == NRF_RADIO_STATE_DISABLED); |
| nrf_radio_task_trigger(NRF_RADIO_TASK_RXEN); |
| break; |
| |
| case RADIO_STATE_ED: |
| assert_tifs_shorts_disabled(); |
| assert(nrf_radio_state_get() == NRF_RADIO_STATE_DISABLED); |
| nrf_radio_task_trigger(NRF_RADIO_TASK_RXEN); |
| break; |
| |
| default: |
| assert(false); |
| } |
| } |
| |
| if (nrf_radio_event_get(NRF_RADIO_EVENT_READY)) |
| { |
| nrf_radio_event_clear(NRF_RADIO_EVENT_READY); |
| |
| switch (m_state) |
| { |
| case RADIO_STATE_WAITING_RX_FRAME: |
| assert_tifs_shorts_disabled(); |
| |
| 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_tifs_initial_enable(); |
| rx_start(); |
| |
| mutex_unlock(); |
| } |
| } |
| |
| break; |
| |
| case RADIO_STATE_TX_ACK: |
| assert_tifs_shorts_enabled(); |
| shorts_tifs_ack_disable(); |
| break; |
| |
| case RADIO_STATE_CCA: |
| assert_tifs_shorts_disabled(); |
| |
| if (nrf_radio_state_get() != NRF_RADIO_STATE_RX_IDLE) |
| { |
| assert(false); |
| } |
| |
| state_set(RADIO_STATE_TX_FRAME); |
| |
| shorts_tx_frame_enable(); |
| nrf_radio_task_trigger(NRF_RADIO_TASK_CCASTART); |
| break; |
| |
| case RADIO_STATE_TX_FRAME: |
| assert_tifs_shorts_disabled(); |
| break; |
| |
| case RADIO_STATE_RX_ACK: |
| assert(nrf_radio_state_get() == NRF_RADIO_STATE_RX_IDLE); |
| assert_tifs_shorts_disabled(); |
| rx_start(); // Reuse buffer used by interrupted rx procedure. |
| break; |
| |
| case RADIO_STATE_ED: |
| assert_tifs_shorts_disabled(); |
| assert(nrf_radio_state_get() == NRF_RADIO_STATE_RX_IDLE); |
| nrf_radio_task_trigger(NRF_RADIO_TASK_EDSTART); |
| break; |
| |
| default: |
| assert(false); |
| } |
| } |
| |
| #if !SHORT_CCAIDLE_TXEN |
| |
| if (nrf_radio_event_get(NRF_RADIO_EVENT_CCAIDLE)) |
| { |
| assert (m_state == RADIO_STATE_TX_FRAME); |
| |
| disableTransceiver(); |
| |
| nrf_radio_event_clear(NRF_RADIO_EVENT_CCAIDLE); |
| } |
| |
| #endif |
| |
| if (nrf_radio_event_get(NRF_RADIO_EVENT_CCABUSY)) |
| { |
| assert(nrf_radio_state_get() == NRF_RADIO_STATE_RX_IDLE); |
| assert(m_state == RADIO_STATE_TX_FRAME); |
| assert_tifs_shorts_disabled(); |
| shorts_tx_frame_disable(); |
| |
| nrf_drv_radio802154_busy_channel(); |
| |
| state_set(RADIO_STATE_WAITING_RX_FRAME); |
| rx_enable(); |
| |
| nrf_radio_event_clear(NRF_RADIO_EVENT_CCABUSY); |
| } |
| |
| if (nrf_radio_event_get(NRF_RADIO_EVENT_EDEND)) |
| { |
| nrf_radio_event_clear(NRF_RADIO_EVENT_EDEND); |
| |
| assert(nrf_radio_state_get() == NRF_RADIO_STATE_RX_IDLE); |
| assert(m_state == RADIO_STATE_ED); |
| assert_tifs_shorts_disabled(); |
| |
| nrf_drv_radio802154_energy_detected(last_ed_result_get()); |
| |
| state_set(RADIO_STATE_WAITING_RX_FRAME); |
| rx_enable(); |
| } |
| } |
| |
| #ifdef RADIO_IRQ_CTRL |
| void RADIO_IRQHandler(void) |
| { |
| nrf_drv_radio802154_irq_handler(); |
| } |
| #endif |
| |
| void __attribute__((weak)) nrf_drv_radio802154_received(uint8_t * p_data, int8_t power, int8_t lqi) |
| { |
| (void) p_data; |
| (void) power; |
| (void) lqi; |
| } |
| |
| void __attribute__((weak)) nrf_drv_radio802154_transmitted(bool pending_bit) |
| { |
| (void) pending_bit; |
| } |
| |
| void __attribute__((weak)) nrf_drv_radio802154_busy_channel(void) |
| { |
| |
| } |
| |
| void __attribute__((weak)) nrf_drv_radio802154_energy_detected(int8_t result) |
| { |
| (void) result; |
| } |