mirror of https://github.com/ARMmbed/mbed-os.git
890 lines
28 KiB
C++
890 lines
28 KiB
C++
/*
|
|
* Copyright (c) 2016 Silicon Laboratories, Inc. http://www.silabs.com
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
* Licensed under the Apache License, Version 2.0 (the License); you may
|
|
* not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an AS IS BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
#include "NanostackRfPhyEfr32.h"
|
|
#include "ns_types.h"
|
|
#include "platform/arm_hal_interrupt.h"
|
|
#include "nanostack/platform/arm_hal_phy.h"
|
|
#include "mbed_toolchain.h"
|
|
#include <string.h>
|
|
|
|
#include "mbed-trace/mbed_trace.h"
|
|
#define TRACE_GROUP "SLRF"
|
|
|
|
/* Silicon Labs headers */
|
|
extern "C" {
|
|
#include "rail/rail.h"
|
|
#include "rail/pa.h"
|
|
#include "rail/pti.h"
|
|
#include "rail/ieee802154/rail_ieee802154.h"
|
|
#include "buffer-pool-memory-manager/buffer_pool_allocator.h"
|
|
}
|
|
|
|
/* RF driver data */
|
|
static phy_device_driver_s device_driver;
|
|
static int8_t rf_radio_driver_id = -1;
|
|
static uint8_t MAC_address[8];
|
|
static uint16_t PAN_address;
|
|
static uint16_t short_address;
|
|
|
|
/* Driver instance handle */
|
|
static NanostackRfPhyEfr32 *rf = NULL;
|
|
|
|
/* Channel configurations */
|
|
static const phy_rf_channel_configuration_s phy_24ghz = {2405000000U, 5000000U, 250000U, 16U, M_OQPSK};
|
|
static const phy_rf_channel_configuration_s phy_subghz = {868300000U, 2000000U, 250000U, 11U, M_OQPSK};
|
|
|
|
static const phy_device_channel_page_s phy_channel_pages[] = {
|
|
{ CHANNEL_PAGE_0, &phy_24ghz},
|
|
{ CHANNEL_PAGE_2, &phy_subghz},
|
|
{ CHANNEL_PAGE_0, NULL}
|
|
};
|
|
|
|
/* Driver structures */
|
|
typedef enum {
|
|
RADIO_UNINIT,
|
|
RADIO_INITING,
|
|
RADIO_IDLE,
|
|
RADIO_TX,
|
|
RADIO_RX,
|
|
RADIO_CALIBRATION
|
|
} siliconlabs_modem_state_t;
|
|
|
|
static const RAIL_CsmaConfig_t csma_config = RAIL_CSMA_CONFIG_802_15_4_2003_2p4_GHz_OQPSK_CSMA;
|
|
|
|
#if defined(TARGET_EFR32MG1)
|
|
#include "ieee802154_subg_efr32xg1_configurator_out.h"
|
|
#include "ieee802154_efr32xg1_configurator_out.h"
|
|
#elif defined(TARGET_EFR32MG12)
|
|
#include "ieee802154_efr32xg12_configurator_out.h"
|
|
#else
|
|
#error "Not a valid target."
|
|
#endif
|
|
|
|
static const RAIL_ChannelConfigEntry_t entry[] = {
|
|
{0U, 0U, 600000U, 868300000U},
|
|
{1U, 10U, 2000000U, 906000000U},
|
|
{11U, 26U, 5000000U, 2405000000U}
|
|
};
|
|
|
|
#if MBED_CONF_SL_RAIL_BAND == 868
|
|
#ifndef MBED_CONF_SL_RAIL_HAS_SUBGIG
|
|
#error "Sub-Gigahertz band is not supported on this target."
|
|
#endif
|
|
static const RAIL_ChannelConfig_t channels = {
|
|
(RAIL_ChannelConfigEntry_t *) &entry[0],
|
|
1
|
|
};
|
|
#elif MBED_CONF_SL_RAIL_BAND == 915
|
|
#ifndef MBED_CONF_SL_RAIL_HAS_SUBGIG
|
|
#error "Sub-Gigahertz band is not supported on this target."
|
|
#endif
|
|
static const RAIL_ChannelConfig_t channels = {
|
|
(RAIL_ChannelConfigEntry_t *) &entry[1],
|
|
1
|
|
};
|
|
#elif MBED_CONF_SL_RAIL_BAND == 2400
|
|
#ifndef MBED_CONF_SL_RAIL_HAS_2P4
|
|
#error "2.4GHz band is not supported on this target."
|
|
#endif
|
|
static const RAIL_ChannelConfig_t channels = {
|
|
(RAIL_ChannelConfigEntry_t *) &entry[2],
|
|
1
|
|
};
|
|
#else
|
|
#error "sl-rail.band is not correctly defined"
|
|
#endif
|
|
|
|
static const RAIL_IEEE802154_Config_t config = { false, false,
|
|
RAIL_IEEE802154_ACCEPT_STANDARD_FRAMES,
|
|
RAIL_RF_STATE_RX, 100, 192, 894, NULL };
|
|
|
|
static const RAIL_Init_t railInitParams = { 140, 38400000, RAIL_CAL_ALL_PENDING };
|
|
|
|
#if defined (MBED_CONF_SL_RAIL_HAS_2P4)
|
|
// Set up the PA for 2.4 GHz operation
|
|
static const RADIO_PAInit_t paInit2p4 = {
|
|
PA_SEL_2P4_HP, /* Power Amplifier mode */
|
|
PA_VOLTMODE_DCDC, /* Power Amplifier vPA Voltage mode */
|
|
100, /* Desired output power in dBm * 10 */
|
|
0, /* Output power offset in dBm * 10 */
|
|
10 /* Desired ramp time in us */
|
|
};
|
|
#endif
|
|
|
|
#if defined (MBED_CONF_SL_RAIL_HAS_SUBGIG)
|
|
// Set up the PA for sub-GHz operation
|
|
static const RADIO_PAInit_t paInitSubGhz = {
|
|
PA_SEL_SUBGIG, /* Power Amplifier mode */
|
|
PA_VOLTMODE_DCDC, /* Power Amplifier vPA Voltage mode */
|
|
100, /* Desired output power in dBm * 10 */
|
|
0, /* Output power offset in dBm * 10 */
|
|
10 /* Desired ramp time in us */
|
|
};
|
|
#endif
|
|
|
|
static volatile siliconlabs_modem_state_t radio_state = RADIO_UNINIT;
|
|
static volatile int8_t channel = -1;
|
|
static volatile uint8_t current_tx_handle = 0;
|
|
static volatile uint8_t current_tx_sequence = 0;
|
|
static volatile bool waiting_for_ack = false;
|
|
static volatile bool data_pending = false, last_ack_pending_bit = false;
|
|
static volatile uint32_t last_tx = 0;
|
|
|
|
/* ARM_NWK_HAL prototypes */
|
|
static int8_t rf_extension(phy_extension_type_e extension_type, uint8_t *data_ptr);
|
|
static int8_t rf_interface_state_control(phy_interface_state_e new_state, uint8_t rf_channel);
|
|
static int8_t rf_address_write(phy_address_type_e address_type, uint8_t *address_ptr);
|
|
static int8_t rf_start_cca(uint8_t *data_ptr, uint16_t data_length, uint8_t tx_handle, data_protocol_e data_protocol );
|
|
|
|
/* Local function prototypes */
|
|
static bool rail_checkAndSwitchChannel(uint8_t channel);
|
|
|
|
/*============ CODE =========*/
|
|
|
|
/*
|
|
* \brief Function initialises and registers the RF driver.
|
|
*
|
|
* \param none
|
|
*
|
|
* \return rf_radio_driver_id Driver ID given by NET library
|
|
*/
|
|
static int8_t rf_device_register(void)
|
|
{
|
|
// If we already exist, bail.
|
|
if(radio_state != RADIO_UNINIT) {
|
|
return -1;
|
|
}
|
|
|
|
#if MBED_CONF_SL_RAIL_BAND == 2400
|
|
RADIO_PA_Init((RADIO_PAInit_t*)&paInit2p4);
|
|
#elif (MBED_CONF_SL_RAIL_BAND == 915) || (MBED_CONF_SL_RAIL_BAND == 868)
|
|
RADIO_PA_Init((RADIO_PAInit_t*)&paInitSubGhz);
|
|
#endif
|
|
|
|
// Set up PTI since it makes life so much easier
|
|
#if defined(MBED_CONF_SL_RAIL_PTI) && (MBED_CONF_SL_RAIL_PTI == 1)
|
|
RADIO_PTIInit_t ptiInit = {
|
|
MBED_CONF_SL_RAIL_PTI_MODE,
|
|
MBED_CONF_SL_RAIL_PTI_BAUDRATE,
|
|
MBED_CONF_SL_RAIL_PTI_DOUT_LOCATION,
|
|
MBED_CONF_SL_RAIL_PTI_DOUT_PORT,
|
|
MBED_CONF_SL_RAIL_PTI_DOUT_PIN,
|
|
MBED_CONF_SL_RAIL_PTI_DCLK_LOCATION,
|
|
MBED_CONF_SL_RAIL_PTI_DCLK_PORT,
|
|
MBED_CONF_SL_RAIL_PTI_DCLK_PIN,
|
|
MBED_CONF_SL_RAIL_PTI_DFRAME_LOCATION,
|
|
MBED_CONF_SL_RAIL_PTI_DFRAME_PORT,
|
|
MBED_CONF_SL_RAIL_PTI_DFRAME_PIN
|
|
};
|
|
|
|
RADIO_PTI_Init(&ptiInit);
|
|
#endif
|
|
|
|
// Set up RAIL
|
|
RAIL_RfInit(&railInitParams);
|
|
RAIL_ChannelConfig(&channels);
|
|
#if (MBED_CONF_SL_RAIL_BAND == 2400)
|
|
RAIL_RadioConfig((void*) ieee802154_config_base);
|
|
channel = 11;
|
|
#elif (MBED_CONF_SL_RAIL_BAND == 915)
|
|
RAIL_RadioConfig((void*) ieee802154_config_915);
|
|
channel = 1;
|
|
#elif (MBED_CONF_SL_RAIL_BAND == 868)
|
|
RAIL_RadioConfig((void*) ieee802154_config_863);
|
|
channel = 0;
|
|
#endif
|
|
RAIL_IEEE802154_Init((RAIL_IEEE802154_Config_t*)&config);
|
|
|
|
/* Get real MAC address */
|
|
/* MAC is stored MSB first */
|
|
memcpy(MAC_address, (const void*)&DEVINFO->UNIQUEH, 4);
|
|
memcpy(&MAC_address[4], (const void*)&DEVINFO->UNIQUEL, 4);
|
|
|
|
/*Set pointer to MAC address*/
|
|
device_driver.PHY_MAC = MAC_address;
|
|
device_driver.driver_description = (char*)"EFR32_154";
|
|
|
|
/*Type of RF PHY*/
|
|
#if MBED_CONF_SL_RAIL_BAND == 2400
|
|
device_driver.link_type = PHY_LINK_15_4_2_4GHZ_TYPE;
|
|
#elif (MBED_CONF_SL_RAIL_BAND == 915) || (MBED_CONF_SL_RAIL_BAND == 868)
|
|
device_driver.link_type = PHY_LINK_15_4_SUBGHZ_TYPE;
|
|
#endif
|
|
|
|
device_driver.phy_channel_pages = phy_channel_pages;
|
|
/*Maximum size of payload is 127*/
|
|
device_driver.phy_MTU = 127;
|
|
/*1 byte header in PHY layer (length)*/
|
|
device_driver.phy_header_length = 1;
|
|
/*No tail in PHY layer*/
|
|
device_driver.phy_tail_length = 0;
|
|
/*Set address write function*/
|
|
device_driver.address_write = &rf_address_write;
|
|
/*Set RF extension function*/
|
|
device_driver.extension = &rf_extension;
|
|
/*Set RF state control function*/
|
|
device_driver.state_control = &rf_interface_state_control;
|
|
/*Set transmit function*/
|
|
device_driver.tx = &rf_start_cca;
|
|
/*Upper layer callbacks init to NULL, get populated by arm_net_phy_register*/
|
|
device_driver.phy_rx_cb = NULL;
|
|
device_driver.phy_tx_done_cb = NULL;
|
|
/*Virtual upper data callback init to NULL*/
|
|
device_driver.arm_net_virtual_rx_cb = NULL;
|
|
device_driver.arm_net_virtual_tx_cb = NULL;
|
|
|
|
/*Register device driver*/
|
|
rf_radio_driver_id = arm_net_phy_register(&device_driver);
|
|
|
|
// If the radio hasn't called the ready callback by now, place it in the initing state
|
|
if(radio_state == RADIO_UNINIT) {
|
|
radio_state = RADIO_INITING;
|
|
}
|
|
|
|
return rf_radio_driver_id;
|
|
}
|
|
|
|
/*
|
|
* \brief Function unregisters the RF driver.
|
|
*
|
|
* \param none
|
|
*
|
|
* \return none
|
|
*/
|
|
static void rf_device_unregister(void)
|
|
{
|
|
arm_net_phy_unregister(rf_radio_driver_id);
|
|
}
|
|
|
|
/*
|
|
* \brief Function starts the CCA process before starting data transmission and copies the data to RF TX FIFO.
|
|
*
|
|
* \param data_ptr Pointer to TX data
|
|
* \param data_length Length of the TX data
|
|
* \param tx_handle Handle to transmission
|
|
* \return 0 Success
|
|
* \return -1 Busy
|
|
*/
|
|
static int8_t rf_start_cca(uint8_t *data_ptr, uint16_t data_length, uint8_t tx_handle, data_protocol_e data_protocol )
|
|
{
|
|
|
|
RAIL_TxData_t txData = {
|
|
data_ptr,
|
|
data_length + 3
|
|
};
|
|
|
|
switch(radio_state) {
|
|
case RADIO_UNINIT:
|
|
tr_debug("Radio uninit\n");
|
|
return -1;
|
|
case RADIO_INITING:
|
|
tr_debug("Radio initing\n");
|
|
return -1;
|
|
case RADIO_CALIBRATION:
|
|
tr_debug("Radio calibrating\n");
|
|
return -1;
|
|
case RADIO_TX:
|
|
tr_debug("Radio in TX mode\n");
|
|
return -1;
|
|
case RADIO_IDLE:
|
|
case RADIO_RX:
|
|
// If we're still waiting for an ACK, don't mess up the internal state
|
|
if(waiting_for_ack || RAIL_RfStateGet() == RAIL_RF_STATE_TX) {
|
|
if((RAIL_GetTime() - last_tx) < 30000) {
|
|
tr_debug("Still waiting on previous ACK\n");
|
|
return -1;
|
|
} else {
|
|
tr_debug("TXerr\n");
|
|
}
|
|
}
|
|
|
|
data_ptr[0] = data_length + 2;
|
|
RAIL_RfIdleExt(RAIL_IDLE_ABORT , true);
|
|
RAIL_TxDataLoad(&txData);
|
|
radio_state = RADIO_TX;
|
|
|
|
RAIL_TxOptions_t txOpt;
|
|
//Check to see whether we'll be waiting for an ACK
|
|
if(data_ptr[1] & (1 << 5)) {
|
|
txOpt.waitForAck = true;
|
|
waiting_for_ack = true;
|
|
} else {
|
|
txOpt.waitForAck = false;
|
|
}
|
|
|
|
//tr_debug("Called TX, len %d, chan %d, ack %d\n", data_length, channel, waiting_for_ack ? 1 : 0);
|
|
|
|
if(RAIL_TxStartWithOptions(channel, &txOpt, &RAIL_CcaCsma, (RAIL_CsmaConfig_t*) &csma_config) == 0) {
|
|
//Save packet number and sequence
|
|
current_tx_handle = tx_handle;
|
|
current_tx_sequence = data_ptr[3];
|
|
return 0;
|
|
} else {
|
|
RAIL_RfIdle();
|
|
RAIL_RxStart(channel);
|
|
radio_state = RADIO_RX;
|
|
return -1;
|
|
}
|
|
}
|
|
//Should never get here...
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* \brief Function gives the control of RF states to MAC.
|
|
*
|
|
* \param new_state RF state
|
|
* \param rf_channel RF channel
|
|
*
|
|
* \return 0 Success
|
|
*/
|
|
static int8_t rf_interface_state_control(phy_interface_state_e new_state, uint8_t rf_channel)
|
|
{
|
|
int8_t ret_val = 0;
|
|
switch (new_state)
|
|
{
|
|
/* Reset PHY driver and set to idle */
|
|
case PHY_INTERFACE_RESET:
|
|
RAIL_RfIdle();
|
|
radio_state = RADIO_IDLE;
|
|
break;
|
|
/* Disable PHY Interface driver */
|
|
case PHY_INTERFACE_DOWN:
|
|
RAIL_RfIdle();
|
|
radio_state = RADIO_IDLE;
|
|
break;
|
|
/* Enable RX */
|
|
case PHY_INTERFACE_UP:
|
|
if(rail_checkAndSwitchChannel(rf_channel)) {
|
|
RAIL_IEEE802154_SetPromiscuousMode(false);
|
|
RAIL_RxStart(channel);
|
|
radio_state = RADIO_RX;
|
|
} else {
|
|
ret_val = -1;
|
|
}
|
|
break;
|
|
/* Enable wireless interface ED scan mode */
|
|
case PHY_INTERFACE_RX_ENERGY_STATE:
|
|
tr_debug("Energy det req\n");
|
|
// TODO: implement energy detection
|
|
break;
|
|
/* Enable RX in promiscuous mode (aka no address filtering) */
|
|
case PHY_INTERFACE_SNIFFER_STATE:
|
|
if(rail_checkAndSwitchChannel(rf_channel)) {
|
|
RAIL_IEEE802154_SetPromiscuousMode(true);
|
|
RAIL_RxStart(channel);
|
|
radio_state = RADIO_RX;
|
|
} else {
|
|
ret_val = -1;
|
|
}
|
|
break;
|
|
}
|
|
return ret_val;
|
|
}
|
|
|
|
/*
|
|
* \brief Function controls the ACK pending, channel setting and energy detection.
|
|
*
|
|
* \param extension_type Type of control
|
|
* \param data_ptr Data from NET library
|
|
*
|
|
* \return 0 Success
|
|
*/
|
|
static int8_t rf_extension(phy_extension_type_e extension_type, uint8_t *data_ptr)
|
|
{
|
|
switch (extension_type)
|
|
{
|
|
/* Control MAC pending bit for Indirect data transmission */
|
|
case PHY_EXTENSION_CTRL_PENDING_BIT:
|
|
if(*data_ptr) {
|
|
data_pending = true;
|
|
} else {
|
|
data_pending = false;
|
|
}
|
|
break;
|
|
/* Return frame pending bit from last received ACK */
|
|
case PHY_EXTENSION_READ_LAST_ACK_PENDING_STATUS:
|
|
if(last_ack_pending_bit) {
|
|
*data_ptr = 0xFF;
|
|
} else {
|
|
*data_ptr = 0;
|
|
}
|
|
break;
|
|
/* Set channel */
|
|
case PHY_EXTENSION_SET_CHANNEL:
|
|
channel = *data_ptr;
|
|
break;
|
|
/* Read energy on the channel */
|
|
case PHY_EXTENSION_READ_CHANNEL_ENERGY:
|
|
// TODO: implement energy detection
|
|
*data_ptr = 0;
|
|
break;
|
|
/* Read status of the link */
|
|
case PHY_EXTENSION_READ_LINK_STATUS:
|
|
// TODO: return accurate value here
|
|
tr_debug("Trying to read link status\n");
|
|
break;
|
|
/* Convert between LQI and RSSI */
|
|
case PHY_EXTENSION_CONVERT_SIGNAL_INFO:
|
|
// TODO: return accurate value here
|
|
tr_debug("Trying to read signal info\n");
|
|
break;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* \brief Function sets the addresses to RF address filters.
|
|
*
|
|
* \param address_type Type of address
|
|
* \param address_ptr Pointer to given address
|
|
*
|
|
* \return 0 Success
|
|
*/
|
|
static int8_t rf_address_write(phy_address_type_e address_type, uint8_t *address_ptr)
|
|
{
|
|
int8_t ret_val = 0;
|
|
switch (address_type)
|
|
{
|
|
/*Set 48-bit address*/
|
|
case PHY_MAC_48BIT:
|
|
// 15.4 does not support 48-bit addressing
|
|
ret_val = -1;
|
|
break;
|
|
/*Set 64-bit MAC address*/
|
|
case PHY_MAC_64BIT:
|
|
/* Store MAC in MSB order */
|
|
memcpy(MAC_address, address_ptr, 8);
|
|
tr_debug("MACw ");
|
|
for(unsigned int i = 0; i < sizeof(MAC_address); i ++) {
|
|
tr_debug("%02x:", MAC_address[i]);
|
|
}
|
|
tr_debug("\n");
|
|
/* Pass MAC to the RF driver in LSB order */
|
|
uint8_t MAC_reversed[8];
|
|
for(unsigned int i = 0; i < sizeof(MAC_address); i ++) {
|
|
MAC_reversed[i] = MAC_address[sizeof(MAC_address) - 1 - i];
|
|
}
|
|
RAIL_IEEE802154_SetLongAddress(MAC_reversed);
|
|
break;
|
|
/*Set 16-bit address*/
|
|
case PHY_MAC_16BIT:
|
|
short_address = address_ptr[0] << 8 | address_ptr[1];
|
|
tr_debug("Filter EUI16 %04x\n", short_address);
|
|
RAIL_IEEE802154_SetShortAddress(short_address);
|
|
break;
|
|
/*Set PAN Id*/
|
|
case PHY_MAC_PANID:
|
|
PAN_address = address_ptr[0] << 8 | address_ptr[1];
|
|
tr_debug("Filter PAN %04x\n", PAN_address);
|
|
RAIL_IEEE802154_SetPanId(PAN_address);
|
|
break;
|
|
}
|
|
return ret_val;
|
|
}
|
|
|
|
/*****************************************************************************/
|
|
/*****************************************************************************/
|
|
|
|
static void rf_if_lock(void)
|
|
{
|
|
platform_enter_critical();
|
|
}
|
|
|
|
static void rf_if_unlock(void)
|
|
{
|
|
platform_exit_critical();
|
|
}
|
|
|
|
NanostackRfPhyEfr32::NanostackRfPhyEfr32() : NanostackRfPhy()
|
|
{
|
|
// Do nothing
|
|
}
|
|
|
|
NanostackRfPhyEfr32::~NanostackRfPhyEfr32()
|
|
{
|
|
rf_unregister();
|
|
}
|
|
|
|
int8_t NanostackRfPhyEfr32::rf_register()
|
|
{
|
|
|
|
rf_if_lock();
|
|
|
|
if (rf != NULL) {
|
|
rf_if_unlock();
|
|
error("Multiple registrations of NanostackRfPhyEfr32 not supported");
|
|
return -1;
|
|
}
|
|
|
|
int8_t radio_id = rf_device_register();
|
|
if (radio_id < 0) {
|
|
rf = NULL;
|
|
} else {
|
|
rf = this;
|
|
}
|
|
|
|
rf_if_unlock();
|
|
return radio_id;
|
|
}
|
|
|
|
void NanostackRfPhyEfr32::rf_unregister()
|
|
{
|
|
rf_if_lock();
|
|
|
|
if (rf != this) {
|
|
rf_if_unlock();
|
|
return;
|
|
}
|
|
|
|
rf_device_unregister();
|
|
rf = NULL;
|
|
|
|
rf_if_unlock();
|
|
}
|
|
|
|
void NanostackRfPhyEfr32::get_mac_address(uint8_t *mac)
|
|
{
|
|
rf_if_lock();
|
|
|
|
memcpy(mac, MAC_address, sizeof(MAC_address));
|
|
|
|
rf_if_unlock();
|
|
}
|
|
|
|
void NanostackRfPhyEfr32::set_mac_address(uint8_t *mac)
|
|
{
|
|
rf_if_lock();
|
|
|
|
if (NULL != rf) {
|
|
error("NanostackRfPhyEfr32 cannot change mac address when running");
|
|
rf_if_unlock();
|
|
return;
|
|
}
|
|
|
|
memcpy(MAC_address, mac, sizeof(MAC_address));
|
|
|
|
rf_if_unlock();
|
|
}
|
|
|
|
uint32_t NanostackRfPhyEfr32::get_driver_version()
|
|
{
|
|
RAIL_Version_t railversion;
|
|
RAIL_VersionGet(&railversion, true);
|
|
|
|
return (railversion.major << 24) |
|
|
(railversion.minor << 16) |
|
|
(railversion.rev << 8) |
|
|
(railversion.build);
|
|
}
|
|
|
|
|
|
//====================== RAIL-defined callbacks =========================
|
|
/**
|
|
* Callback that lets the app know when the radio has finished init
|
|
* and is ready.
|
|
*/
|
|
void RAILCb_RfReady(void) {
|
|
radio_state = RADIO_IDLE;
|
|
}
|
|
|
|
/**
|
|
* Interrupt level callback
|
|
* Allows the user finer granularity in tx radio events.
|
|
*
|
|
* Radio Statuses:
|
|
* RAIL_TX_CONFIG_BUFFER_UNDERFLOW
|
|
* RAIL_TX_CONFIG_CHANNEL_BUSY
|
|
*
|
|
* @param[in] status A bit field that defines what event caused the callback
|
|
*/
|
|
void RAILCb_TxRadioStatus(uint8_t status) {
|
|
if(device_driver.phy_tx_done_cb != NULL) {
|
|
if(status == RAIL_TX_CONFIG_BUFFER_UNDERFLOW ||
|
|
status == RAIL_TX_CONFIG_CHANNEL_BUSY ||
|
|
status == RAIL_TX_CONFIG_TX_ABORTED ||
|
|
status == RAIL_TX_CONFIG_TX_BLOCKED) {
|
|
waiting_for_ack = false;
|
|
device_driver.phy_tx_done_cb( rf_radio_driver_id,
|
|
current_tx_handle,
|
|
PHY_LINK_CCA_FAIL,
|
|
8,
|
|
1);
|
|
} else {
|
|
tr_debug("Packet TX error %d\n", status);
|
|
}
|
|
}
|
|
radio_state = RADIO_RX;
|
|
}
|
|
|
|
/**
|
|
* Called whenever an enabled radio status event occurs
|
|
*
|
|
* Triggers:
|
|
* RAIL_RX_CONFIG_PREAMBLE_DETECT
|
|
* RAIL_RX_CONFIG_SYNC1_DETECT
|
|
* RAIL_RX_CONFIG_SYNC2_DETECT
|
|
* RAIL_RX_CONFIG_INVALID_CRC
|
|
* RAIL_RX_CONFIG_BUFFER_OVERFLOW
|
|
* RAIL_RX_CONFIG_ADDRESS_FILTERED
|
|
*
|
|
* @param[in] status The event that triggered this callback
|
|
*/
|
|
void RAILCb_RxRadioStatus(uint8_t status) {
|
|
switch(status) {
|
|
case RAIL_RX_CONFIG_ADDRESS_FILTERED:
|
|
break;
|
|
default:
|
|
tr_debug("RXE %d\n", status);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Callback that notifies the application that a calibration is needed.
|
|
*
|
|
* This callback function is called whenever the RAIL library detects that a
|
|
* calibration is needed. It is up to the application to determine a valid
|
|
* window to call RAIL_CalStart().
|
|
*
|
|
*/
|
|
void RAILCb_CalNeeded(void) {
|
|
// TODO: Implement on-the-fly recalibration
|
|
tr_debug("!!!! Calling for calibration\n");
|
|
}
|
|
|
|
/**
|
|
* Interrupt level callback to signify when the radio changes state.
|
|
*
|
|
* @param[in] state Current state of the radio, as defined by EFR32 data sheet
|
|
*/
|
|
void RAILCb_RadioStateChanged(uint8_t state) {
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* This function is called when the RAIL timer expires
|
|
*
|
|
* You must implement a stub for this in your RAIL application even if you
|
|
* don't use the timer.
|
|
*/
|
|
void RAILCb_TimerExpired(void) {
|
|
}
|
|
|
|
/**
|
|
* Interrupt level callback to signify when the packet was sent
|
|
* @param txPacketInfo Information about the packet that was transmitted.
|
|
* @note that this structure is only valid during the timeframe of the
|
|
* callback.
|
|
*/
|
|
void RAILCb_TxPacketSent(RAIL_TxPacketInfo_t *txPacketInfo) {
|
|
if(device_driver.phy_tx_done_cb != NULL) {
|
|
device_driver.phy_tx_done_cb( rf_radio_driver_id,
|
|
current_tx_handle,
|
|
// Normally we'd switch on ACK requested here, but Nanostack does that for us.
|
|
PHY_LINK_TX_SUCCESS,
|
|
// Succeeded, so how many times we tried is really not relevant.
|
|
1,
|
|
1);
|
|
}
|
|
last_tx = RAIL_GetTime();
|
|
radio_state = RADIO_RX;
|
|
}
|
|
|
|
/**
|
|
* Receive packet callback.
|
|
*
|
|
* @param[in] rxPacketHandle Contains a handle that points to the memory that
|
|
* the packet was stored in. This handle will be the same as something
|
|
* returned by the RAILCb_AllocateMemory() API. To convert this into a receive
|
|
* packet info struct use the *** function.
|
|
*
|
|
* This function is called whenever a packet is received and returns to you the
|
|
* memory handle for where this received packet and its appended information was
|
|
* stored. After this callback is done we will release the memory handle so you
|
|
* must somehow increment a reference count or copy the data out within this
|
|
* function.
|
|
*/
|
|
void RAILCb_RxPacketReceived(void *rxPacketHandle) {
|
|
RAIL_RxPacketInfo_t* rxPacketInfo = (RAIL_RxPacketInfo_t*) memoryPtrFromHandle(rxPacketHandle);
|
|
if(rxPacketInfo->appendedInfo.crcStatus) {
|
|
/* If this is an ACK, deal with it */
|
|
if( rxPacketInfo->dataLength == 4 &&
|
|
rxPacketInfo->dataPtr[3] == (current_tx_sequence) &&
|
|
waiting_for_ack) {
|
|
/* Tell the radio to not ACK an ACK */
|
|
RAIL_AutoAckCancelAck();
|
|
waiting_for_ack = false;
|
|
/* Save the pending bit */
|
|
last_ack_pending_bit = (rxPacketInfo->dataPtr[1] & (1 << 4)) != 0;
|
|
/* Tell the stack we got an ACK */
|
|
//tr_debug("rACK\n");
|
|
device_driver.phy_tx_done_cb( rf_radio_driver_id,
|
|
current_tx_handle,
|
|
last_ack_pending_bit ? PHY_LINK_TX_DONE_PENDING : PHY_LINK_TX_DONE,
|
|
1,
|
|
1);
|
|
} else {
|
|
/* Figure out whether we want to not ACK this packet */
|
|
|
|
/*
|
|
* dataPtr[0] = length
|
|
* dataLength = length w/o length byte
|
|
* dataptr[1:2] = 0x61C9 -> 0b01100001 0b1100 1001 (version 1, dest 3, src 2, ACKreq, type = 1)
|
|
* [1] => b[0:2] frame type, b[3] = security enabled, b[4] = frame pending, b[5] = ACKreq, b[6] = intrapan
|
|
* [2] => b[2:3] destmode, b[4:5] version, b[6:7] srcmode
|
|
*/
|
|
if( (rxPacketInfo->dataPtr[1] & (1 << 5)) == 0 ) {
|
|
/* Cancel the ACK if the sender did not request one */
|
|
RAIL_AutoAckCancelAck();
|
|
}
|
|
|
|
//tr_debug("rPKT %d\n", rxPacketInfo->dataLength);
|
|
/* Feed the received packet into the stack */
|
|
device_driver.phy_rx_cb(rxPacketInfo->dataPtr + 1,
|
|
rxPacketInfo->dataLength - 1,
|
|
//TODO: take a new RAIL release that exposes LQI, or have LQI as function of RSSI
|
|
255,
|
|
rxPacketInfo->appendedInfo.rssiLatch,
|
|
rf_radio_driver_id);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Callback for when a Data Request is being received
|
|
*
|
|
* @param address The source address of the data request command
|
|
*
|
|
* This function is called when the command byte of an incoming frame is for a
|
|
* data request, which requests an ACK. This callback will be called before the
|
|
* packet is fully received, to allow the node to have more time to decide
|
|
* whether to set frame pending in the outgoing ACK.
|
|
*/
|
|
void RAILCb_IEEE802154_DataRequestCommand(RAIL_IEEE802154_Address_t *address) {
|
|
if(data_pending) {
|
|
RAIL_IEEE802154_SetFramePending();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Callback that notifies the application when searching for an ACK has timed
|
|
* out.
|
|
*
|
|
* @return void
|
|
*
|
|
* This callback function is called whenever the timeout for searching for an
|
|
* ack is exceeded.
|
|
*/
|
|
void RAILCb_RxAckTimeout(void) {
|
|
if(waiting_for_ack) {
|
|
tr_debug("nACK\n");
|
|
waiting_for_ack = false;
|
|
device_driver.phy_tx_done_cb( rf_radio_driver_id,
|
|
current_tx_handle,
|
|
PHY_LINK_TX_FAIL,
|
|
1,
|
|
1);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Function to check the requested channel against the current channel,
|
|
* and change the radio configuration if necessary.
|
|
*
|
|
* @param channel The new channel number requested
|
|
* @return bool True if able to switch to the requested channel
|
|
*
|
|
*/
|
|
static bool rail_checkAndSwitchChannel(uint8_t newChannel) {
|
|
if(channel == newChannel) {
|
|
return true;
|
|
}
|
|
|
|
if(newChannel > 0 && newChannel < 11) {
|
|
if(MBED_CONF_SL_RAIL_BAND == 915) {
|
|
channel = newChannel;
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
} else if(newChannel >= 11 && newChannel <= 26) {
|
|
if(MBED_CONF_SL_RAIL_BAND == 2400) {
|
|
channel = newChannel;
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Callback that fires when the receive fifo exceeds the configured threshold
|
|
* value
|
|
*
|
|
* @param[in] bytesAvailable Number of bytes available in the receive fifo at
|
|
* the time of the callback dispatch
|
|
*
|
|
* @return void
|
|
* @warning You must implement a stub for this in your RAIL application.
|
|
*
|
|
* Callback that fires when the receive fifo exceeds the configured threshold
|
|
* value. Provides the number of bytes available in the receive fifo at the
|
|
* time of the callback dispatch.
|
|
*/
|
|
void RAILCb_RxFifoAlmostFull(uint16_t bytesAvailable) {
|
|
tr_debug("RX near full (%d)\n", bytesAvailable);
|
|
}
|
|
|
|
/**
|
|
* Callback that fires when the transmit fifo falls under the configured
|
|
* threshold value
|
|
*
|
|
* @param[in] spaceAvailable Number of bytes open in the transmit fifo at the
|
|
* time of the callback dispatch
|
|
*
|
|
* @return void
|
|
* @warning You must implement a stub for this in your RAIL application.
|
|
*
|
|
* Callback that fires when the transmit fifo falls under the configured
|
|
* threshold value. It only fires if a rising edge occurs across this
|
|
* threshold. This callback will not fire on initailization nor after resetting
|
|
* the transmit fifo with RAIL_ResetFifo().
|
|
*
|
|
* Provides the number of bytes open in the transmit fifo at the time of the
|
|
* callback dispatch.
|
|
*/
|
|
void RAILCb_TxFifoAlmostEmpty(uint16_t spaceAvailable) {
|
|
tr_debug("TX near empty (%d)\n", spaceAvailable);
|
|
}
|
|
|
|
/**
|
|
* Callback for when AGC averaged RSSI is done
|
|
*
|
|
* @param avgRssi Contains the the RSSI in quarter dBm (dbm*4) on success and
|
|
* returns \ref RAIL_RSSI_INVALID if there was a problem computing the result.
|
|
*
|
|
* Called in response to RAIL_StartAverageRSSI() to indicate that the hardware
|
|
* has completed averaging. If you would like you can instead use the
|
|
* RAIL_AverageRSSIReady() to wait for completion and RAIL_GetAverageRSSI() to
|
|
* get the result.
|
|
*/
|
|
void RAILCb_RssiAverageDone(int16_t avgRssi) {
|
|
tr_debug("RSSI done (%d)\n", avgRssi);
|
|
} |