1647 lines
49 KiB
C
1647 lines
49 KiB
C
/* 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;
|
|
}
|