Files
GClarkson 9d06f983af Imported more library files
Not compiling currently
2025-04-12 23:37:19 +01:00

3859 lines
132 KiB
C

/*
* Copyright (c) 2023, The OpenThread Authors.
* 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 the copyright holder 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 OpenThread platform abstraction for radio communication.
*
*/
#include <assert.h>
#include <openthread-core-config.h>
#include <openthread-system.h>
#include <openthread/link.h>
#include <openthread/platform/alarm-micro.h>
#include <openthread/platform/alarm-milli.h>
#include <openthread/platform/diag.h>
#include <openthread/platform/radio.h>
#include <openthread/platform/time.h>
#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE
#include <openthread/platform/multipan.h>
#endif
#include "common/code_utils.hpp"
#include "common/debug.hpp"
#include "common/logging.hpp"
#include "utils/code_utils.h"
#include "utils/link_metrics.h"
#include "utils/mac_frame.h"
#include "circular_queue.h"
#include "em_device.h"
#include "sl_core.h"
#if defined _SILICON_LABS_32B_SERIES_2
#include "em_system.h"
#else
#include "sl_hal_system.h"
#endif
#include "ieee802154mac.h"
#include "pa_conversions_efr32.h"
#include "platform-band.h"
#include "platform-efr32.h"
#include "radio_coex.h"
#include "radio_multi_channel.h"
#include "radio_power_manager.h"
#include "rail.h"
#include "rail_config.h"
#include "rail_ieee802154.h"
#include "sl_memory_manager.h"
#include "sl_multipan.h"
#include "sl_packet_utils.h"
#include "soft_source_match_table.h"
#include "sl_openthread_radio_config.h"
#ifdef SL_COMPONENT_CATALOG_PRESENT
#include "sl_component_catalog.h"
#endif // SL_COMPONENT_CATALOG_PRESENT
#ifdef SL_CATALOG_RAIL_MULTIPLEXER_PRESENT
#include "sl_rail_mux_rename.h"
#endif
#ifdef SL_CATALOG_RAIL_UTIL_ANT_DIV_PRESENT
#include "sl_rail_util_ant_div.h"
#include "sl_rail_util_ant_div_config.h"
#endif // SL_CATALOG_RAIL_UTIL_ANT_DIV_PRESENT
#ifdef SL_CATALOG_RAIL_UTIL_COEX_PRESENT
#include "coexistence-802154.h"
#include "coexistence-ot.h"
#endif // SL_CATALOG_RAIL_UTIL_COEX_PRESENT
#ifdef SL_CATALOG_RAIL_UTIL_IEEE802154_STACK_EVENT_PRESENT
#include "sl_rail_util_ieee802154_stack_event.h"
#endif // SL_CATALOG_RAIL_UTIL_IEEE802154_STACK_EVENT_PRESENT
#ifdef SL_CATALOG_RAIL_UTIL_IEEE802154_PHY_SELECT_PRESENT
#include "sl_rail_util_ieee802154_phy_select.h"
#endif // #ifdef SL_CATALOG_RAIL_UTIL_IEEE802154_PHY_SELECT_PRESENT
#include "sl_gp_interface.h"
#ifdef SL_CATALOG_RAIL_UTIL_IEEE802154_FAST_CHANNEL_SWITCHING_PRESENT
#include "sl_rail_util_ieee802154_fast_channel_switching_config.h"
#endif // SL_CATALOG_RAIL_UTIL_IEEE802154_FAST_CHANNEL_SWITCHING_PRESENT
//------------------------------------------------------------------------------
// Enums, macros and static variables
#ifndef LOW_BYTE
#define LOW_BYTE(n) ((uint8_t)((n)&0xFF))
#endif // LOW_BTE
#ifndef HIGH_BYTE
#define HIGH_BYTE(n) ((uint8_t)(LOW_BYTE((n) >> 8)))
#endif // HIGH_BYTE
#ifndef BIT32
#define BIT32(x) (((uint32_t)1) << (x))
#endif // BIT32
// Intentionally maintaining separate groups for the different device series.
// This gives flexibility to add new elements to be read, like CCA Thresholds.
#if defined(_SILICON_LABS_32B_SERIES_2)
#define USERDATA_MFG_CUSTOM_EUI_64 (2)
#endif
#ifndef USERDATA_END
#define USERDATA_END (USERDATA_BASE + FLASH_PAGE_SIZE)
#endif
#if SL_OPENTHREAD_RADIO_RX_BUFFER_COUNT > CIRCULAR_QUEUE_LEN_MAX
#error "Rx buffer count cannot be greater than max circular queue length."
#endif
// Internal flags
#define FLAG_RADIO_INIT_DONE 0x00000001
#define FLAG_ONGOING_TX_DATA 0x00000002
#define FLAG_ONGOING_TX_ACK 0x00000004
#define FLAG_WAITING_FOR_ACK 0x00000008
#define FLAG_CURRENT_TX_USE_CSMA 0x00000010
#define FLAG_SCHEDULED_RX_PENDING 0x00000020
// Radio Events
#define EVENT_TX_SUCCESS 0x00000100
#define EVENT_TX_CCA_FAILED 0x00000200
#define EVENT_TX_NO_ACK 0x00000400
#define EVENT_TX_SCHEDULER_ERROR 0x00000800
#define EVENT_TX_FAILED 0x00001000
#define EVENT_ACK_SENT_WITH_FP_SET 0x00002000
#define EVENT_SECURED_ACK_SENT 0x00004000
#define EVENT_SCHEDULED_RX_STARTED 0x00008000
#define EVENT_SCHEDULED_TX_STARTED 0x00010000
#define TX_WAITING_FOR_ACK 0x00
#define TX_NO_ACK 0x01
#define RADIO_TX_EVENTS \
(EVENT_TX_SUCCESS | EVENT_TX_CCA_FAILED | EVENT_TX_NO_ACK | EVENT_TX_SCHEDULER_ERROR | EVENT_TX_FAILED)
#define QUARTER_DBM_IN_DBM 4
#define US_IN_MS 1000
/* FilterMask provided by RAIL is structured as follows:
* | Bit:7 | Bit:6 | Bit:5 | Bit:4 | Bit:3 | Bit:2 | Bit:1 | Bit:0 |
* | Addr2 | Addr1 | Addr0 | Bcast Addr | Pan Id2 | Pan Id1 | Pan Id0 | Bcast PanId |
* | Matched | Matched | Matched | Matched | Matched | Matched | Matched | Matched |
*/
#define RADIO_BCAST_IID (0)
#define RADIO_GET_FILTER_MASK(iid) (1 << (iid))
#define RADIO_PANID_FILTER_SHIFT (0)
#define RADIO_ADDR_FILTER_SHIFT (4)
#define RADIO_BCAST_PANID_FILTER_MASK RADIO_GET_FILTER_MASK(0)
#define RADIO_INDEX0_PANID_FILTER_MASK RADIO_GET_FILTER_MASK(1)
#define RADIO_INDEX1_PANID_FILTER_MASK RADIO_GET_FILTER_MASK(2)
#define RADIO_INDEX2_PANID_FILTER_MASK RADIO_GET_FILTER_MASK(3)
#define RADIO_GET_PANID_FILTER_MASK(filter) \
(filter \
& (RADIO_BCAST_PANID_FILTER_MASK | RADIO_INDEX0_PANID_FILTER_MASK | RADIO_INDEX1_PANID_FILTER_MASK \
| RADIO_INDEX2_PANID_FILTER_MASK))
#define RADIO_BCAST_ADDR_FILTER_MASK (RADIO_GET_FILTER_MASK(0) << RADIO_ADDR_FILTER_SHIFT)
#define RADIO_INDEX0_ADDR_FILTER_MASK (RADIO_GET_FILTER_MASK(1) << RADIO_ADDR_FILTER_SHIFT)
#define RADIO_INDEX1_ADDR_FILTER_MASK (RADIO_GET_FILTER_MASK(2) << RADIO_ADDR_FILTER_SHIFT)
#define RADIO_INDEX2_ADDR_FILTER_MASK (RADIO_GET_FILTER_MASK(3) << RADIO_ADDR_FILTER_SHIFT)
#define RADIO_GET_ADDR_FILTER_MASK(filter) \
(filter \
& (RADIO_BCAST_ADDR_FILTER_MASK | RADIO_INDEX0_ADDR_FILTER_MASK | RADIO_INDEX1_ADDR_FILTER_MASK \
| RADIO_INDEX2_ADDR_FILTER_MASK))
#define RADIO_BCAST_PANID (0xFFFF)
#if defined(_SILICON_LABS_32B_SERIES_2)
#define DEVICE_CAPABILITY_MCU_EN (DEVINFO->SWCAPA1 & _DEVINFO_SWCAPA1_RFMCUEN_MASK)
#else
#define DEVICE_CAPABILITY_MCU_EN (DEVINFO->SWCAPA & _DEVINFO_SWCAPA_RFMCUEN_MASK)
#endif
#define ENERGY_READS_MAX -128
#define SYMBOLS_PER_ENERGY_READING 8 // we take a reading every 8 symbols
static otRadioCaps sRadioCapabilities =
(OT_RADIO_CAPS_ACK_TIMEOUT | OT_RADIO_CAPS_CSMA_BACKOFF | OT_RADIO_CAPS_ENERGY_SCAN | OT_RADIO_CAPS_SLEEP_TO_TX
#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
| OT_RADIO_CAPS_TRANSMIT_SEC
// When scheduled tx is required, we support RAIL_StartScheduledCcaCsmaTx
// (delay is indicated in tx frame info set in MAC)
| OT_RADIO_CAPS_TRANSMIT_TIMING
// When scheduled rx is required, we support RAIL_ScheduleRx in our
// implementation of otPlatRadioReceiveAt
| OT_RADIO_CAPS_RECEIVE_TIMING
#endif
);
static uint32_t sScanFrameCounter = 0; // How many 'frames' left in the current scan.
static uint32_t sScanFrameCounterMax = 0; // Max 'frames' when performing scan
static int8_t sEnergyReadsMax = ENERGY_READS_MAX;
static RAIL_MultiTimer_t rail_timer;
// Energy Scan
typedef enum
{
ENERGY_SCAN_STATUS_IDLE,
ENERGY_SCAN_STATUS_IN_PROGRESS,
ENERGY_SCAN_STATUS_COMPLETED
} energyScanStatus;
typedef enum
{
ENERGY_SCAN_MODE_SYNC,
ENERGY_SCAN_MODE_ASYNC
} energyScanMode;
typedef struct
{
uint8_t length;
uint8_t channel;
uint8_t lqi;
int8_t rssi;
uint8_t iid;
RAIL_Time_t timestamp;
} rxPacketDetails;
typedef struct
{
rxPacketDetails packetInfo;
uint8_t psdu[IEEE802154_MAX_LENGTH];
} rxBuffer;
typedef uint8_t rxBufferIndex_t;
static volatile energyScanStatus sEnergyScanStatus;
static volatile int8_t sEnergyScanResultDbm;
static energyScanMode sEnergyScanMode;
// To track active interface the energy scan is being performed.
static uint8_t sEnergyScanActiveInterface = INVALID_INTERFACE_INDEX;
static bool sIsSrcMatchEnabled = false;
#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE
#define RADIO_EXT_ADDR_COUNT (RADIO_INTERFACE_COUNT - 1)
// To hold transmit/energy-scan requests from the hosts i.e. one per instance/host,
// if radio is busy.
#define RADIO_REQUEST_BUFFER_COUNT OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM
#else
#define RADIO_EXT_ADDR_COUNT (RADIO_INTERFACE_COUNT)
#define RADIO_REQUEST_BUFFER_COUNT 1
#endif
typedef struct
{
otRadioFrame frame;
uint8_t iid;
uint8_t currentRadioTxPriority;
} radioFrame;
// Receive
static Queue_t sRxPacketQueue;
static sl_memory_pool_t sRxPacketMemPoolHandle = {0};
static uint8_t sReceiveAckPsdu[IEEE802154_MAX_LENGTH];
static radioFrame sReceive;
static radioFrame sReceiveAck;
static otError sReceiveError;
// Transmit
// One of the IID is reserved for broadcast hence we need RADIO_INTERFACE_COUNT - 1.
// IID zero is for broadcast, so request from host1 (i.e. iid = 1) will use tx buffer
// index 0 (i.e. iid - 1) and so on.
static radioFrame sTransmitBuffer[RADIO_REQUEST_BUFFER_COUNT];
static uint8_t sTransmitPsdu[RADIO_REQUEST_BUFFER_COUNT][IEEE802154_MAX_LENGTH];
static radioFrame *sCurrentTxPacket = NULL;
static uint8_t sLastLqi = 0;
static int8_t sLastRssi = 0;
static otExtAddress sExtAddress[RADIO_EXT_ADDR_COUNT];
// CSMA config: Should be globally scoped
#define CSL_CSMA_BACKOFF_TIME_IN_US 150
RAIL_CsmaConfig_t csmaConfig = RAIL_CSMA_CONFIG_802_15_4_2003_2p4_GHz_OQPSK_CSMA;
RAIL_CsmaConfig_t cslCsmaConfig = RAIL_CSMA_CONFIG_SINGLE_CCA;
#if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT
static otRadioIeInfo sTransmitIeInfo[RADIO_REQUEST_BUFFER_COUNT];
#endif
// Radio
#define CCA_THRESHOLD_UNINIT 127
#define CCA_THRESHOLD_DEFAULT -75 // dBm - default for 2.4GHz 802.15.4
#define UNINITIALIZED_CHANNEL 0xFF
static bool sPromiscuous = false;
static efr32CommonConfig sCommonConfig;
static efr32BandConfig sBandConfig;
static efr32BandConfig *sCurrentBandConfig = NULL;
static int8_t sCcaThresholdDbm = CCA_THRESHOLD_DEFAULT;
#if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT
efr32RadioCounters railDebugCounters;
// Create an alias for readability of RX debug tracking
#define rxDebugStep (railDebugCounters.mRadioDebugData.m8[RX_DEBUG_COUNTER0])
#endif
#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE
// Type of commands that can be added to the pending command buffer.
typedef enum
{
kPendingCommandTypeTransmit,
kPendingCommandTypeEnergyScan,
} pendingCommandType;
typedef struct
{
// Energy scan channel.
uint8_t scanChannel;
// Energy scan duration.
uint16_t scanDuration;
} energyScanParams;
// The structure representing pending transmit and energy-scan command requests.
typedef struct
{
// The union of transmit and energy-scan requests parameters.
union
{
// A pointer to the transmit radio frame.
otRadioFrame *txFrame;
// The structure of energy-scan request parameters.
energyScanParams energyScan;
} request;
// The pending command type.
pendingCommandType cmdType : 2;
// The interface iid of the pending command.
uint8_t iid : 2;
} pendingCommandEntry;
static Queue_t sPendingCommandQueue;
extern otInstance *sInstances[OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM];
static uint8_t sRailFilterMask = RADIO_BCAST_PANID_FILTER_MASK;
static bool isRadioTransmittingOrScanning(void);
#if FAST_CHANNEL_SWITCHING_SUPPORT
static RAIL_IEEE802154_RxChannelSwitchingCfg_t sChannelSwitchingCfg;
static RAIL_IEEE802154_RX_CHANNEL_SWITCHING_BUF_ALIGNMENT_TYPE
sChannelSwitchingBuffer[RAIL_IEEE802154_RX_CHANNEL_SWITCHING_BUF_BYTES
/ RAIL_IEEE802154_RX_CHANNEL_SWITCHING_BUF_ALIGNMENT];
bool sl_is_multi_channel_enabled(void)
{
uint8_t firstChannel = UNINITIALIZED_CHANNEL;
for (uint8_t i = 0U; i < RAIL_IEEE802154_RX_CHANNEL_SWITCHING_NUM_CHANNELS; i++)
{
if (sChannelSwitchingCfg.channels[i] != UNINITIALIZED_CHANNEL)
{
if (firstChannel == UNINITIALIZED_CHANNEL)
{
firstChannel = sChannelSwitchingCfg.channels[i];
}
else if (firstChannel != sChannelSwitchingCfg.channels[i])
{
return true;
}
}
}
return false;
}
otError sl_get_channel_switching_cfg(RAIL_IEEE802154_RxChannelSwitchingCfg_t *channelSwitchingCfg)
{
otError error = OT_ERROR_NONE;
otEXPECT_ACTION(channelSwitchingCfg != NULL, error = OT_ERROR_INVALID_ARGS);
memcpy(channelSwitchingCfg, &sChannelSwitchingCfg, sizeof(sChannelSwitchingCfg));
exit:
return error;
}
static uint8_t fastChannelIndex(uint8_t aChannel)
{
for (uint8_t i = 0U; i < RAIL_IEEE802154_RX_CHANNEL_SWITCHING_NUM_CHANNELS; i++)
{
if (sChannelSwitchingCfg.channels[i] == aChannel)
{
return i;
}
}
return INVALID_INTERFACE_INDEX;
}
#endif // FAST_CHANNEL_SWITCHING_SUPPORT
#endif // OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE
static void efr32ScanTimerHandler(struct RAIL_MultiTimer *tmr, RAIL_Time_t expectedTimeOfEvent, void *cbArg);
static RAIL_Status_t efr32ScanDwell(uint16_t symbols);
// RAIL
#ifdef SL_CATALOG_RAIL_MULTIPLEXER_PRESENT
RAIL_Handle_t gRailHandle;
#if SL_OPENTHREAD_COEX_COUNTER_ENABLE
static void sl_ot_coex_counter_on_event(sl_rail_util_coex_event_t event);
#endif // SL_OPENTHREAD_COEX_COUNTER_ENABLE
#else
RAIL_Handle_t emPhyRailHandle;
#endif // SL_CATALOG_RAIL_MULTIPLEXER_PRESENT
static const RAIL_IEEE802154_Config_t sRailIeee802154Config = {
.addresses = NULL,
.ackConfig =
{
.enable = true,
.ackTimeout = 672,
.rxTransitions =
{
.success = RAIL_RF_STATE_RX,
.error = RAIL_RF_STATE_RX,
},
.txTransitions =
{
.success = RAIL_RF_STATE_RX,
.error = RAIL_RF_STATE_RX,
},
},
.timings =
{
.idleToRx = 100,
.txToRx = 192 - 10,
.idleToTx = 100,
#if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
.rxToTx = 256, // accommodate enhanced ACKs
#else
.rxToTx = 192,
#endif
.rxSearchTimeout = 0,
.txToRxSearchTimeout = 0,
.txToTx = 0,
},
.framesMask = RAIL_IEEE802154_ACCEPT_STANDARD_FRAMES,
.promiscuousMode = false,
.isPanCoordinator = false,
.defaultFramePendingInOutgoingAcks = false,
};
#if RADIO_CONFIG_SUBGHZ_SUPPORT
#define PHY_HEADER_SIZE 2
// SHR: 4 bytes preamble, 2 bytes SFD
// 802.15.4 spec describes GFSK SHR to be the same format as SUN O-QPSK
// except preamble is 32 symbols (4 octets).
#define SHR_SIZE 6
#else
#define PHY_HEADER_SIZE 1
#define SHR_SIZE 5 // 4 bytes of preamble, 1 byte sync-word
#endif
#define SHR_DURATION_US 160 // Duration of SHR in us.
// Misc
static volatile uint32_t miscRadioState = 0;
static bool emPendingData = false;
#ifdef SL_CATALOG_RAIL_UTIL_COEX_PRESENT
enum
{
RHO_INACTIVE = 0,
RHO_EXT_ACTIVE,
RHO_INT_ACTIVE, // Not used
RHO_BOTH_ACTIVE,
};
static uint8_t rhoActive = RHO_INACTIVE;
static bool ptaGntEventReported;
static bool sRadioCoexEnabled = true;
#if SL_OPENTHREAD_COEX_COUNTER_ENABLE
uint32_t efr32RadioCoexCounters[SL_RAIL_UTIL_COEX_EVENT_COUNT] = {0};
#endif // SL_OPENTHREAD_COEX_COUNTER_ENABLE
#endif // SL_CATALOG_RAIL_UTIL_COEX_PRESENT
// Note: This callback can be called from ISR context.
SL_CODE_CLASSIFY(SL_CODE_COMPONENT_OT_PLATFORM_ABSTRACTION, SL_CODE_CLASS_TIME_CRITICAL)
static bool rxPacketQueueOverflowCallback(const Queue_t *queue, void *data)
{
OT_UNUSED_VARIABLE(queue);
OT_UNUSED_VARIABLE(data);
// True to discard the queue item being considered for removal.
// False for nothing to be discarded from the queue.
// Do not discard the oldest entry from the queue, rather drop
// the new received packet, hence, return false.
return false;
}
#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE
static bool pendingCommandQueueOverflowCallback(const Queue_t *queue, void *data)
{
OT_UNUSED_VARIABLE(queue);
OT_UNUSED_VARIABLE(data);
// We should never hit this callback because a host can only request
// one command at a time. Hence, added a assert if it happens.
OT_ASSERT(false);
return false;
}
#endif
#if RADIO_CONFIG_ENABLE_CUSTOM_EUI_SUPPORT && defined(_SILICON_LABS_32B_SERIES_2)
/*
* This API reads the UserData page on the given EFR device.
*/
static int readUserData(void *buffer, uint16_t index, int len, bool changeByteOrder)
{
uint8_t *readLocation = (uint8_t *)USERDATA_BASE + index;
uint8_t *writeLocation = (uint8_t *)buffer;
// Sanity check to verify if the ouput buffer is valid and the index and len are valid.
// If invalid, change the len to -1 and return.
otEXPECT_ACTION((writeLocation != NULL) && ((readLocation + len) <= (uint8_t *)USERDATA_END), len = -1);
// Copy the contents of flash into output buffer.
for (int idx = 0; idx < len; idx++)
{
if (changeByteOrder)
{
writeLocation[idx] = readLocation[(len - 1) - idx];
}
else
{
writeLocation[idx] = readLocation[idx];
}
}
exit:
// Return len, len was changed to -1 to indicate failure.
return len;
}
#endif
/*
* This API converts the FilterMask to appropriate IID. If there are any errors, it will fallback on bcast IID.
*
*/
SL_CODE_CLASSIFY(SL_CODE_COMPONENT_OT_PLATFORM_ABSTRACTION, SL_CODE_CLASS_TIME_CRITICAL)
static inline uint8_t getIidFromFilterMask(uint8_t mask)
{
uint8_t iid = INVALID_INTERFACE_INDEX;
#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE
// We need only the Pan Id masks here, as we are not matching the addresses.
// Also mask all the unused indices.
mask &= sRailFilterMask;
// The only acceptable values for mask at this point are:
// 1 - BCAST PANID - IID(0)
// 2 - INDEX 0 - IID(1)
// 4 - INDEX 1 - IID(2)
// 8 - INDEX 2 - IID(3)
//
// The packet should either be directed to one of the PANs or Bcast.
// (mask & (mask -1) is a simplistic way of testing if the mask is a power of 2.
otEXPECT_ACTION(((mask != 0) && (mask & (mask - 1)) == 0), iid = 0);
while (mask)
{
iid++;
mask >>= 1;
}
exit:
#else
(void)mask;
iid = RADIO_BCAST_IID;
#endif
return iid;
}
/*
* This API validates the received FilterMask by checking if the destination address
* in the received packet corresponds to destination PanID.
*/
SL_CODE_CLASSIFY(SL_CODE_COMPONENT_OT_PLATFORM_ABSTRACTION, SL_CODE_CLASS_TIME_CRITICAL)
static bool isFilterMaskValid(uint8_t mask)
{
bool valid = false;
#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE
/* Packet will be considered as a valid packet in 3 cases:
* Case 1: If the packet was directed towards bcast address or bcast panid
*
* Case 2: If the packet was directed to one of our valid address/PANID combos
* (Compare all non-bcast PANID filters against their corresponding
* address filters for same IID and see if any match)
*
* Case 3: We don't have either the destination addressing field or destination PanId
* in the received packet to determine if the dest address and dest pan match.
*/
if (((mask & RADIO_BCAST_PANID_FILTER_MASK) || (mask & RADIO_BCAST_ADDR_FILTER_MASK))
|| // Case 1
// Find any non-broadcast PAN ID match and get ready to compare it
((((mask & (RADIO_INDEX0_PANID_FILTER_MASK | RADIO_INDEX1_PANID_FILTER_MASK | RADIO_INDEX2_PANID_FILTER_MASK))
>> RADIO_PANID_FILTER_SHIFT)
&
// ...To see if it coincides with any address matches for same IID
(RADIO_GET_ADDR_FILTER_MASK(mask) >> RADIO_ADDR_FILTER_SHIFT))
!= 0)
|| // Case 2
(((RADIO_GET_PANID_FILTER_MASK(mask)) == 0) || ((RADIO_GET_ADDR_FILTER_MASK(mask)) == 0)) // Case 3
)
{
valid = true;
}
#else
(void)mask;
valid = true;
#endif
return valid;
}
#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
enum
{
MAC_KEY_PREV,
MAC_KEY_CURRENT,
MAC_KEY_NEXT,
MAC_KEY_COUNT
};
typedef struct securityMaterial
{
uint8_t ackKeyId;
uint8_t keyId;
uint32_t macFrameCounter;
uint32_t ackFrameCounter;
otMacKeyMaterial keys[MAC_KEY_COUNT];
} securityMaterial;
// Transmit Security
static securityMaterial sMacKeys[RADIO_INTERFACE_COUNT];
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
// CSL parameters
static uint32_t sCslPeriod;
static uint32_t sCslSampleTime;
SL_CODE_CLASSIFY(SL_CODE_COMPONENT_OT_PLATFORM_ABSTRACTION, SL_CODE_CLASS_TIME_CRITICAL)
static uint16_t getCslPhase(uint32_t shrTxTime)
{
uint32_t cslPeriodInUs = sCslPeriod * OT_US_PER_TEN_SYMBOLS;
uint32_t diff;
if (shrTxTime == 0U)
{
shrTxTime = otPlatAlarmMicroGetNow();
}
diff = ((sCslSampleTime % cslPeriodInUs) - (shrTxTime % cslPeriodInUs) + cslPeriodInUs) % cslPeriodInUs;
return (uint16_t)(diff / OT_US_PER_TEN_SYMBOLS);
}
#endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
// Enhanced ACK IE data
static uint8_t sAckIeData[OT_ACK_IE_MAX_SIZE];
static uint8_t sAckIeDataLength = 0;
SL_CODE_CLASSIFY(SL_CODE_COMPONENT_OT_PLATFORM_ABSTRACTION, SL_CODE_CLASS_TIME_CRITICAL)
static uint8_t generateAckIeData(uint8_t *aLinkMetricsIeData, uint8_t aLinkMetricsIeDataLen)
{
OT_UNUSED_VARIABLE(aLinkMetricsIeData);
OT_UNUSED_VARIABLE(aLinkMetricsIeDataLen);
uint8_t offset = 0;
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
if (sCslPeriod > 0)
{
offset += otMacFrameGenerateCslIeTemplate(sAckIeData);
}
#endif
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
if (aLinkMetricsIeData != NULL && aLinkMetricsIeDataLen > 0)
{
offset += otMacFrameGenerateEnhAckProbingIe(sAckIeData, aLinkMetricsIeData, aLinkMetricsIeDataLen);
}
#endif
return offset;
}
SL_CODE_CLASSIFY(SL_CODE_COMPONENT_OT_PLATFORM_ABSTRACTION, SL_CODE_CLASS_TIME_CRITICAL)
static otError radioProcessTransmitSecurity(otRadioFrame *aFrame, uint8_t iid)
{
otError error = OT_ERROR_NONE;
uint8_t keyId;
uint8_t keyToUse;
uint8_t panIndex = efr32GetPanIndexFromIid(iid);
otEXPECT(otMacFrameIsSecurityEnabled(aFrame) && otMacFrameIsKeyIdMode1(aFrame)
&& !aFrame->mInfo.mTxInfo.mIsSecurityProcessed);
OT_ASSERT(panIndex != INVALID_INTERFACE_INDEX);
if (otMacFrameIsAck(aFrame))
{
keyId = otMacFrameGetKeyId(aFrame);
otEXPECT_ACTION(keyId != 0, error = OT_ERROR_FAILED);
if (keyId == sMacKeys[iid].keyId - 1)
{
keyToUse = MAC_KEY_PREV;
}
else if (keyId == sMacKeys[iid].keyId)
{
keyToUse = MAC_KEY_CURRENT;
}
else if (keyId == sMacKeys[iid].keyId + 1)
{
keyToUse = MAC_KEY_NEXT;
}
else
{
error = OT_ERROR_SECURITY;
otEXPECT(false);
}
}
else
{
keyId = sMacKeys[iid].keyId;
keyToUse = MAC_KEY_CURRENT;
}
aFrame->mInfo.mTxInfo.mAesKey = &sMacKeys[iid].keys[keyToUse];
if (!aFrame->mInfo.mTxInfo.mIsHeaderUpdated)
{
if (otMacFrameIsAck(aFrame))
{
// Store ack frame counter and ack key ID for receive frame
sMacKeys[iid].ackKeyId = keyId;
sMacKeys[iid].ackFrameCounter = sMacKeys[iid].macFrameCounter;
}
otMacFrameSetKeyId(aFrame, keyId);
otMacFrameSetFrameCounter(aFrame, sMacKeys[iid].macFrameCounter++);
}
efr32PlatProcessTransmitAesCcm(aFrame, &sExtAddress[panIndex]);
exit:
return error;
}
#endif // (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
SL_CODE_CLASSIFY(SL_CODE_COMPONENT_OT_PLATFORM_ABSTRACTION, SL_CODE_CLASS_TIME_CRITICAL)
static uint8_t readInitialPacketData(RAIL_RxPacketInfo_t *packetInfo,
uint8_t expected_data_bytes_max,
uint8_t expected_data_bytes_min,
uint8_t *buffer,
uint8_t buffer_len)
{
uint8_t packetBytesRead = 0;
RAIL_RxPacketInfo_t adjustedPacketInfo;
// Check if we have enough buffer
OT_ASSERT((buffer_len >= expected_data_bytes_max) || (packetInfo != NULL));
// Read the packet info
RAIL_GetRxIncomingPacketInfo(gRailHandle, packetInfo);
// We are trying to get the packet info of a packet before it is completely received.
// We do this to evaluate the FP bit in response and add IEs to ACK if needed.
// Check to see if we have received atleast minimum number of bytes requested.
otEXPECT_ACTION(packetInfo->packetBytes >= expected_data_bytes_min, packetBytesRead = 0);
adjustedPacketInfo = *packetInfo;
// Only extract what we care about
if (packetInfo->packetBytes > expected_data_bytes_max)
{
adjustedPacketInfo.packetBytes = expected_data_bytes_max;
// Check if the initial portion of the packet received so far exceeds the max value requested.
if (packetInfo->firstPortionBytes >= expected_data_bytes_max)
{
// If we have received more, make sure to copy only the required bytes into the buffer.
adjustedPacketInfo.firstPortionBytes = expected_data_bytes_max;
adjustedPacketInfo.lastPortionData = NULL;
}
}
// Copy number of bytes as indicated in `packetInfo->firstPortionBytes` into the buffer.
RAIL_CopyRxPacket(buffer, &adjustedPacketInfo);
// Put it back to packetBytes.
packetBytesRead = (uint8_t)adjustedPacketInfo.packetBytes;
exit:
return packetBytesRead;
}
//------------------------------------------------------------------------------
// Forward Declarations
static void RAILCb_Generic(RAIL_Handle_t aRailHandle, RAIL_Events_t aEvents);
static void efr32PhyStackInit(void);
#if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
static void updateIeInfoTxFrame(uint32_t shrTxTime);
#endif
#ifdef SL_CATALOG_RAIL_UTIL_COEX_PRESENT
static void efr32CoexInit(void);
// Try to transmit the current outgoing frame subject to MAC-level PTA
static void tryTxCurrentPacket(void);
#else
// Transmit the current outgoing frame.
void txCurrentPacket(void);
#define tryTxCurrentPacket txCurrentPacket
#endif // SL_CATALOG_RAIL_UTIL_COEX_PRESENT
static void txFailedCallback(bool isAck, uint32_t status);
static bool validatePacketDetails(RAIL_RxPacketHandle_t packetHandle,
RAIL_RxPacketDetails_t *pPacketDetails,
RAIL_RxPacketInfo_t *pPacketInfo,
uint16_t *packetLength);
static bool validatePacketTimestamp(RAIL_RxPacketDetails_t *pPacketDetails, uint16_t packetLength);
static void updateRxFrameTimestamp(bool aIsAckFrame, RAIL_Time_t aTimestamp);
static otError skipRxPacketLengthBytes(RAIL_RxPacketInfo_t *pPacketInfo);
//------------------------------------------------------------------------------
// Helper Functions
#ifdef SL_CATALOG_RAIL_UTIL_IEEE802154_STACK_EVENT_PRESENT
SL_CODE_CLASSIFY(SL_CODE_COMPONENT_OT_PLATFORM_ABSTRACTION, SL_CODE_CLASS_TIME_CRITICAL)
static bool phyStackEventIsEnabled(void)
{
bool result = false;
#if (defined(SL_CATALOG_RAIL_UTIL_ANT_DIV_PRESENT) && SL_RAIL_UTIL_ANT_DIV_RX_RUNTIME_PHY_SELECT)
result = true;
#endif // SL_CATALOG_RAIL_UTIL_ANT_DIV_PRESENT
#ifdef SL_CATALOG_RAIL_UTIL_COEX_PRESENT
if (sRadioCoexEnabled)
{
result |= sl_rail_util_coex_is_enabled();
#ifdef SL_RAIL_UTIL_COEX_RUNTIME_PHY_SELECT
result |= SL_RAIL_UTIL_COEX_RUNTIME_PHY_SELECT;
#endif
}
#endif // SL_CATALOG_RAIL_UTIL_COEX_PRESENT
return result;
}
static RAIL_Events_t currentEventConfig = RAIL_EVENTS_NONE;
static void updateEvents(RAIL_Events_t mask, RAIL_Events_t values)
{
RAIL_Status_t status;
RAIL_Events_t newEventConfig = (currentEventConfig & ~mask) | (values & mask);
if (newEventConfig != currentEventConfig)
{
currentEventConfig = newEventConfig;
status = RAIL_ConfigEvents(gRailHandle, mask, values);
OT_ASSERT(status == RAIL_STATUS_NO_ERROR);
}
}
SL_CODE_CLASSIFY(SL_CODE_COMPONENT_OT_PLATFORM_ABSTRACTION, SL_CODE_CLASS_TIME_CRITICAL)
static sl_rail_util_ieee802154_stack_event_t handlePhyStackEvent(sl_rail_util_ieee802154_stack_event_t stackEvent,
uint32_t supplement)
{
return (phyStackEventIsEnabled()
#ifdef SL_CATALOG_RAIL_MULTIPLEXER_PRESENT
? sl_rail_mux_ieee802154_on_event(gRailHandle, stackEvent, supplement)
#else
? sl_rail_util_ieee802154_on_event(stackEvent, supplement)
#endif
: 0);
}
#else
static void updateEvents(RAIL_Events_t mask, RAIL_Events_t values)
{
RAIL_Status_t status;
status = RAIL_ConfigEvents(gRailHandle, mask, values);
OT_ASSERT(status == RAIL_STATUS_NO_ERROR);
}
#define handlePhyStackEvent(event, supplement) 0
#endif // SL_CATALOG_RAIL_UTIL_IEEE802154_STACK_EVENT_PRESENT
// Set or clear the passed flag.
SL_CODE_CLASSIFY(SL_CODE_COMPONENT_OT_PLATFORM_ABSTRACTION, SL_CODE_CLASS_TIME_CRITICAL)
static inline void setInternalFlag(uint32_t flag, bool val)
{
CORE_DECLARE_IRQ_STATE;
CORE_ENTER_ATOMIC();
miscRadioState = (val ? (miscRadioState | flag) : (miscRadioState & ~flag));
CORE_EXIT_ATOMIC();
}
// Returns true if the passed flag is set, false otherwise.
SL_CODE_CLASSIFY(SL_CODE_COMPONENT_OT_PLATFORM_ABSTRACTION, SL_CODE_CLASS_TIME_CRITICAL)
static inline bool getInternalFlag(uint32_t flag)
{
bool isFlagSet;
CORE_DECLARE_IRQ_STATE;
CORE_ENTER_ATOMIC();
isFlagSet = (miscRadioState & flag) ? true : false;
CORE_EXIT_ATOMIC();
return isFlagSet;
}
SL_CODE_CLASSIFY(SL_CODE_COMPONENT_OT_PLATFORM_ABSTRACTION, SL_CODE_CLASS_TIME_CRITICAL)
static inline bool txWaitingForAck(void)
{
return (getInternalFlag(FLAG_ONGOING_TX_DATA)
&& ((sCurrentTxPacket->frame.mPsdu[0] & IEEE802154_FRAME_FLAG_ACK_REQUIRED) != 0));
}
static inline bool isRadioTransmittingOrScanning(void)
{
return ((sEnergyScanStatus != ENERGY_SCAN_STATUS_IDLE) || getInternalFlag(FLAG_ONGOING_TX_DATA));
}
SL_CODE_CLASSIFY(SL_CODE_COMPONENT_OT_PLATFORM_ABSTRACTION, SL_CODE_CLASS_TIME_CRITICAL)
static bool txIsDataRequest(void)
{
uint16_t fcf = sCurrentTxPacket->frame.mPsdu[IEEE802154_FCF_OFFSET]
| (sCurrentTxPacket->frame.mPsdu[IEEE802154_FCF_OFFSET + 1] << 8);
return (getInternalFlag(FLAG_ONGOING_TX_DATA)
&& (fcf & IEEE802154_FRAME_TYPE_MASK) == IEEE802154_FRAME_TYPE_COMMAND);
}
#ifdef SL_CATALOG_RAIL_UTIL_IEEE802154_STACK_EVENT_PRESENT
static inline bool isReceivingFrame(void)
{
return (RAIL_GetRadioState(gRailHandle) & RAIL_RF_STATE_RX_ACTIVE) == RAIL_RF_STATE_RX_ACTIVE;
}
#endif
SL_CODE_CLASSIFY(SL_CODE_COMPONENT_OT_PLATFORM_ABSTRACTION, SL_CODE_CLASS_TIME_CRITICAL)
static void radioSetIdle(void)
{
if (RAIL_GetRadioState(gRailHandle) != RAIL_RF_STATE_IDLE)
{
RAIL_Idle(gRailHandle, RAIL_IDLE, true);
(void)handlePhyStackEvent(SL_RAIL_UTIL_IEEE802154_STACK_EVENT_TX_IDLED, 0U);
(void)handlePhyStackEvent(SL_RAIL_UTIL_IEEE802154_STACK_EVENT_RX_IDLED, 0U);
}
RAIL_YieldRadio(gRailHandle);
}
static otError radioSetRx(uint8_t aChannel)
{
otError error = OT_ERROR_NONE;
RAIL_Status_t status;
RAIL_SchedulerInfo_t bgRxSchedulerInfo = {
.priority = SL_802154_RADIO_PRIO_BACKGROUND_RX_VALUE,
// sliptime/transaction time is not used for bg rx
};
#if FAST_CHANNEL_SWITCHING_SUPPORT && OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE
if (sl_is_multi_channel_enabled())
{
// Calling RAIL_StartRx with a channel not listed in the channel
// switching config is a bug.
OT_ASSERT(fastChannelIndex(aChannel) != INVALID_INTERFACE_INDEX);
radioSetIdle();
status = RAIL_IEEE802154_ConfigRxChannelSwitching(gRailHandle, &sChannelSwitchingCfg);
OT_ASSERT(status == RAIL_STATUS_NO_ERROR);
status = RAIL_ConfigRxOptions(gRailHandle, RAIL_RX_OPTION_CHANNEL_SWITCHING, RAIL_RX_OPTION_CHANNEL_SWITCHING);
OT_ASSERT(status == RAIL_STATUS_NO_ERROR);
}
else
{
status = RAIL_ConfigRxOptions(gRailHandle, RAIL_RX_OPTION_CHANNEL_SWITCHING, RAIL_RX_OPTIONS_NONE);
OT_ASSERT(status == RAIL_STATUS_NO_ERROR);
}
#endif
status = RAIL_StartRx(gRailHandle, aChannel, &bgRxSchedulerInfo);
otEXPECT_ACTION(status == RAIL_STATUS_NO_ERROR, error = OT_ERROR_FAILED);
(void)handlePhyStackEvent(SL_RAIL_UTIL_IEEE802154_STACK_EVENT_RX_LISTEN, 0U);
otLogInfoPlat("State=OT_RADIO_STATE_RECEIVE");
exit:
return error;
}
#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
static otError radioScheduleRx(uint8_t aChannel, uint32_t aStart, uint32_t aDuration)
{
otError error = OT_ERROR_NONE;
RAIL_Status_t status;
#if FAST_CHANNEL_SWITCHING_SUPPORT && OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE
// UID 1327639: Schedule Rx and Concurrent listening when used together
// will cause undefined behavior. Therefore it was decided to assert upon detecting
// this condition
assert(0);
#endif // FAST_CHANNEL_SWITCHING_SUPPORT && OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE
RAIL_SchedulerInfo_t bgRxSchedulerInfo = {
.priority = SL_802154_RADIO_PRIO_BACKGROUND_RX_VALUE,
// sliptime/transaction time is not used for bg rx
};
// Configure scheduled receive as requested
RAIL_ScheduleRxConfig_t rxCfg = {.start = aStart,
.startMode = RAIL_TIME_ABSOLUTE,
.end = aDuration,
.endMode = RAIL_TIME_DELAY,
.rxTransitionEndSchedule = 1, // This lets us idle after a scheduled-rx
.hardWindowEnd = 0}; // This lets us receive a packet near a window-end-event
status = RAIL_ScheduleRx(gRailHandle, aChannel, &rxCfg, &bgRxSchedulerInfo);
otEXPECT_ACTION(status == RAIL_STATUS_NO_ERROR, error = OT_ERROR_FAILED);
(void)handlePhyStackEvent(SL_RAIL_UTIL_IEEE802154_STACK_EVENT_RX_LISTEN, 0U);
exit:
return error;
}
#endif // (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
//------------------------------------------------------------------------------
// Radio Initialization
static RAIL_Handle_t efr32RailInit(efr32CommonConfig *aCommonConfig)
{
RAIL_Status_t status;
RAIL_Handle_t handle;
#if !OPENTHREAD_RADIO
OT_ASSERT(DEVICE_CAPABILITY_MCU_EN);
#endif
handle = RAIL_Init(&aCommonConfig->mRailConfig, NULL);
OT_ASSERT(handle != NULL);
#if defined(SL_CATALOG_POWER_MANAGER_PRESENT)
status = RAIL_InitPowerManager();
OT_ASSERT(status == RAIL_STATUS_NO_ERROR);
#endif // SL_CATALOG_POWER_MANAGER_PRESENT
status = RAIL_ConfigCal(handle, RAIL_CAL_ALL);
OT_ASSERT(status == RAIL_STATUS_NO_ERROR);
status = RAIL_SetPtiProtocol(handle, RAIL_PTI_PROTOCOL_THREAD);
OT_ASSERT(status == RAIL_STATUS_NO_ERROR);
status = RAIL_IEEE802154_Init(handle, &sRailIeee802154Config);
OT_ASSERT(status == RAIL_STATUS_NO_ERROR);
#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
// Enhanced Frame Pending
status = RAIL_IEEE802154_EnableEarlyFramePending(handle, true);
OT_ASSERT(status == RAIL_STATUS_NO_ERROR);
status = RAIL_IEEE802154_EnableDataFramePending(handle, true);
OT_ASSERT(status == RAIL_STATUS_NO_ERROR);
// Copies of MAC keys for encrypting at the radio layer
memset(sMacKeys, 0, sizeof(sMacKeys));
#endif // (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
uint16_t actualLength = 0;
actualLength = RAIL_SetTxFifo(handle, aCommonConfig->mRailTxFifo.fifo, 0, sizeof(aCommonConfig->mRailTxFifo.fifo));
OT_ASSERT(actualLength == sizeof(aCommonConfig->mRailTxFifo.fifo));
// Enable RAIL multi-timer
RAIL_ConfigMultiTimer(true);
return handle;
}
static void efr32RailConfigLoad(efr32BandConfig *aBandConfig, int8_t aTxPower)
{
RAIL_Status_t status;
RAIL_TxPowerConfig_t *txPowerConfig = NULL;
if (aBandConfig->mChannelConfig != NULL)
{
status = RAIL_IEEE802154_SetPtiRadioConfig(gRailHandle, RAIL_IEEE802154_PTI_RADIO_CONFIG_915MHZ_R23_NA_EXT);
OT_ASSERT(status == RAIL_STATUS_NO_ERROR);
uint16_t firstChannel = UNINITIALIZED_CHANNEL;
firstChannel = RAIL_ConfigChannels(gRailHandle, aBandConfig->mChannelConfig, NULL);
OT_ASSERT(firstChannel == aBandConfig->mChannelMin);
status =
RAIL_IEEE802154_ConfigGOptions(gRailHandle, RAIL_IEEE802154_G_OPTION_GB868, RAIL_IEEE802154_G_OPTION_GB868);
OT_ASSERT(status == RAIL_STATUS_NO_ERROR);
txPowerConfig = sl_rail_util_pa_get_tx_power_config_subghz();
}
else
{
#if defined(SL_CATALOG_RAIL_UTIL_IEEE802154_PHY_SELECT_PRESENT)
status = sl_rail_util_ieee802154_config_radio(gRailHandle);
#else
status = RAIL_IEEE802154_Config2p4GHzRadio(gRailHandle);
#endif // SL_CATALOG_RAIL_UTIL_IEEE802154_PHY_SELECT_PRESENT
OT_ASSERT(status == RAIL_STATUS_NO_ERROR);
txPowerConfig = sl_rail_util_pa_get_tx_power_config_2p4ghz();
}
#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
// 802.15.4E support (only on platforms that support it, so error checking is disabled)
// Note: This has to be called after RAIL_IEEE802154_Config2p4GHzRadio due to a bug where this call
// can overwrite options set below.
RAIL_IEEE802154_ConfigEOptions(gRailHandle,
(RAIL_IEEE802154_E_OPTION_GB868 | RAIL_IEEE802154_E_OPTION_ENH_ACK),
(RAIL_IEEE802154_E_OPTION_GB868 | RAIL_IEEE802154_E_OPTION_ENH_ACK));
#endif // (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
if (aTxPower != SL_INVALID_TX_POWER)
{
sli_update_tx_power_after_config_update(txPowerConfig, aTxPower);
}
}
static efr32BandConfig *efr32RadioGetBandConfig(uint8_t aChannel)
{
efr32BandConfig *config = NULL;
if ((sBandConfig.mChannelMin <= aChannel) && (aChannel <= sBandConfig.mChannelMax))
{
config = &sBandConfig;
}
return config;
}
static void efr32ConfigInit(void (*aEventCallback)(RAIL_Handle_t railHandle, RAIL_Events_t events))
{
sCommonConfig.mRailConfig.eventsCallback = aEventCallback;
sCommonConfig.mRailConfig.protocol = NULL; // only used by Bluetooth stack
#if RADIO_CONFIG_DMP_SUPPORT
sCommonConfig.mRailConfig.scheduler = &(sCommonConfig.mRailSchedState);
#else
sCommonConfig.mRailConfig.scheduler = NULL; // only needed for DMP
#endif
#if RADIO_CONFIG_2P4GHZ_OQPSK_SUPPORT
sBandConfig.mChannelConfig = NULL;
#else
sBandConfig.mChannelConfig = channelConfigs[0];
#endif
sBandConfig.mChannelMin = SL_CHANNEL_MIN;
sBandConfig.mChannelMax = SL_CHANNEL_MAX;
#if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT
memset(&railDebugCounters, 0x00, sizeof(efr32RadioCounters));
#endif
sli_init_power_manager();
gRailHandle = efr32RailInit(&sCommonConfig);
OT_ASSERT(gRailHandle != NULL);
updateEvents(RAIL_EVENTS_ALL,
(0 | RAIL_EVENT_RX_ACK_TIMEOUT | RAIL_EVENT_RX_PACKET_RECEIVED
#if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
| RAIL_EVENT_SCHEDULED_TX_STARTED | RAIL_EVENT_TX_SCHEDULED_TX_MISSED
| RAIL_EVENT_SCHEDULED_RX_STARTED | RAIL_EVENT_RX_SCHEDULED_RX_END | RAIL_EVENT_RX_SCHEDULED_RX_MISSED
#endif
| RAIL_EVENTS_TXACK_COMPLETION | RAIL_EVENTS_TX_COMPLETION
| RAIL_EVENT_IEEE802154_DATA_REQUEST_COMMAND
#if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT || RADIO_CONFIG_DMP_SUPPORT
| RAIL_EVENT_CONFIG_SCHEDULED | RAIL_EVENT_CONFIG_UNSCHEDULED | RAIL_EVENT_SCHEDULER_STATUS
#endif
| RAIL_EVENT_CAL_NEEDED));
efr32RailConfigLoad(&(sBandConfig), OPENTHREAD_CONFIG_DEFAULT_TRANSMIT_POWER);
}
void efr32RadioInit(void)
{
if (getInternalFlag(FLAG_RADIO_INIT_DONE))
{
return;
}
RAIL_Status_t status;
sl_status_t rxMemPoolStatus;
bool queueStatus;
// check if RAIL_TX_FIFO_SIZE is power of two..
OT_ASSERT((RAIL_TX_FIFO_SIZE & (RAIL_TX_FIFO_SIZE - 1)) == 0);
// check the limits of the RAIL_TX_FIFO_SIZE.
OT_ASSERT((RAIL_TX_FIFO_SIZE >= 64) || (RAIL_TX_FIFO_SIZE <= 4096));
efr32ConfigInit(RAILCb_Generic);
setInternalFlag(FLAG_RADIO_INIT_DONE, true);
status = RAIL_ConfigSleep(gRailHandle, RAIL_SLEEP_CONFIG_TIMERSYNC_ENABLED);
OT_ASSERT(status == RAIL_STATUS_NO_ERROR);
sReceive.frame.mLength = 0;
sReceive.frame.mPsdu = NULL;
sReceiveAck.frame.mLength = 0;
sReceiveAck.frame.mPsdu = sReceiveAckPsdu;
#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE
// Initialize the queue for received packets.
queueStatus = queueInit(&sPendingCommandQueue, RADIO_REQUEST_BUFFER_COUNT);
OT_ASSERT(queueStatus);
// Specify a callback to be called upon queue overflow.
queueStatus = queueOverflow(&sPendingCommandQueue, &pendingCommandQueueOverflowCallback);
OT_ASSERT(queueStatus);
#endif
for (uint8_t i = 0; i < RADIO_REQUEST_BUFFER_COUNT; i++)
{
// Initialize the tx buffer params.
sTransmitBuffer[i].iid = INVALID_INTERFACE_INDEX;
sTransmitBuffer[i].frame.mLength = 0;
sTransmitBuffer[i].frame.mPsdu = sTransmitPsdu[i];
#if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT
sTransmitBuffer[i].frame.mInfo.mTxInfo.mIeInfo = &sTransmitIeInfo[i];
#endif
}
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
otLinkMetricsInit(SL_OPENTHREAD_RECEIVE_SENSITIVITY);
#endif
sCurrentBandConfig = efr32RadioGetBandConfig(OPENTHREAD_CONFIG_DEFAULT_CHANNEL);
OT_ASSERT(sCurrentBandConfig != NULL);
sl_rail_util_pa_init();
sli_set_tx_power_in_rail(OPENTHREAD_CONFIG_DEFAULT_TRANSMIT_POWER);
status =
RAIL_ConfigRxOptions(gRailHandle, RAIL_RX_OPTION_TRACK_ABORTED_FRAMES, RAIL_RX_OPTION_TRACK_ABORTED_FRAMES);
OT_ASSERT(status == RAIL_STATUS_NO_ERROR);
efr32PhyStackInit();
efr32RadioSetCcaMode(SL_OPENTHREAD_RADIO_CCA_MODE);
sEnergyScanStatus = ENERGY_SCAN_STATUS_IDLE;
#if FAST_CHANNEL_SWITCHING_SUPPORT && OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE
sChannelSwitchingCfg.bufferBytes = RAIL_IEEE802154_RX_CHANNEL_SWITCHING_BUF_BYTES;
sChannelSwitchingCfg.buffer = sChannelSwitchingBuffer;
for (uint8_t i = 0U; i < RAIL_IEEE802154_RX_CHANNEL_SWITCHING_NUM_CHANNELS; i++)
{
sChannelSwitchingCfg.channels[i] = UNINITIALIZED_CHANNEL;
}
#endif
// Initialize the queue for received packets.
queueStatus = queueInit(&sRxPacketQueue, SL_OPENTHREAD_RADIO_RX_BUFFER_COUNT);
OT_ASSERT(queueStatus);
// Specify a callback to be called upon queue overflow.
queueStatus = queueOverflow(&sRxPacketQueue, &rxPacketQueueOverflowCallback);
OT_ASSERT(queueStatus);
// Initialize the memory pool for rx packets.
rxMemPoolStatus =
sl_memory_create_pool(sizeof(rxBuffer), SL_OPENTHREAD_RADIO_RX_BUFFER_COUNT, &sRxPacketMemPoolHandle);
OT_ASSERT(rxMemPoolStatus == SL_STATUS_OK);
otLogInfoPlat("Initialized");
}
void efr32RadioDeinit(void)
{
RAIL_Status_t status;
RAIL_Idle(gRailHandle, RAIL_IDLE_ABORT, true);
status = RAIL_ConfigEvents(gRailHandle, RAIL_EVENTS_ALL, 0);
OT_ASSERT(status == RAIL_STATUS_NO_ERROR);
sCurrentBandConfig = NULL;
sl_memory_delete_pool(&sRxPacketMemPoolHandle);
}
//------------------------------------------------------------------------------
// Energy Scan support
SL_CODE_CLASSIFY(SL_CODE_COMPONENT_OT_PLATFORM_ABSTRACTION, SL_CODE_CLASS_TIME_CRITICAL)
static void energyScanComplete(int8_t scanResultDbm)
{
sEnergyScanResultDbm = scanResultDbm;
sEnergyScanStatus = ENERGY_SCAN_STATUS_COMPLETED;
}
static uint16_t efr32GetSymbolDurationUs(void)
{
uint32_t symbolRate = RAIL_GetSymbolRate(gRailHandle);
if (symbolRate)
{
symbolRate = (uint16_t)(1000000 / symbolRate);
}
return (symbolRate > 0) ? (uint16_t)symbolRate : 1;
}
SL_CODE_CLASSIFY(SL_CODE_COMPONENT_OT_PLATFORM_ABSTRACTION, SL_CODE_CLASS_TIME_CRITICAL)
static void efr32ScanTimerHandler(struct RAIL_MultiTimer *tmr, RAIL_Time_t expectedTimeOfEvent, void *cbArg)
{
OT_UNUSED_VARIABLE(tmr);
OT_UNUSED_VARIABLE(expectedTimeOfEvent);
OT_UNUSED_VARIABLE(cbArg);
int16_t currentRSSIQuarterdBm =
RAIL_GetRssi(gRailHandle, (sScanFrameCounter == sScanFrameCounterMax ? true : false));
if (currentRSSIQuarterdBm != RAIL_RSSI_INVALID)
{
int8_t currentRSSI = (int8_t)(currentRSSIQuarterdBm / QUARTER_DBM_IN_DBM);
if (sEnergyReadsMax < currentRSSI)
{
sEnergyReadsMax = currentRSSI;
}
}
if (sScanFrameCounter != 0)
{
efr32ScanDwell(SYMBOLS_PER_ENERGY_READING);
sScanFrameCounter--;
}
else
{
// done energy scan, reporting rssi value
energyScanComplete(sEnergyReadsMax);
OT_ASSERT(RAIL_ConfigRxOptions(gRailHandle, RAIL_RX_OPTION_DISABLE_FRAME_DETECTION, RAIL_RX_OPTIONS_NONE)
== RAIL_STATUS_NO_ERROR);
#if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT
railDebugCounters.mRailPlatRadioEnergyScanDoneCbCount++;
#endif
}
}
static RAIL_Status_t efr32ScanDwell(uint16_t symbols)
{
RAIL_CancelMultiTimer(&rail_timer);
return RAIL_SetMultiTimer(&rail_timer,
symbols * efr32GetSymbolDurationUs(),
RAIL_TIME_DELAY,
efr32ScanTimerHandler,
NULL);
}
static otError efr32StartEnergyScan(energyScanMode aMode, uint16_t aChannel, RAIL_Time_t aAveragingTimeUs)
{
RAIL_Status_t status = RAIL_STATUS_NO_ERROR;
otError error = OT_ERROR_NONE;
efr32BandConfig *config = NULL;
otEXPECT_ACTION(sEnergyScanStatus == ENERGY_SCAN_STATUS_IDLE, error = OT_ERROR_BUSY);
sEnergyScanStatus = ENERGY_SCAN_STATUS_IN_PROGRESS;
sEnergyScanMode = aMode;
RAIL_Idle(gRailHandle, RAIL_IDLE, true);
config = efr32RadioGetBandConfig(aChannel);
otEXPECT_ACTION(config != NULL, error = OT_ERROR_INVALID_ARGS);
if (sCurrentBandConfig != config)
{
efr32RailConfigLoad(config, SL_INVALID_TX_POWER);
sCurrentBandConfig = config;
}
if (sScanFrameCounter != 0)
{
return OT_ERROR_FAILED;
}
sEnergyReadsMax = ENERGY_READS_MAX;
sScanFrameCounterMax = aAveragingTimeUs / (efr32GetSymbolDurationUs() * SYMBOLS_PER_ENERGY_READING);
sScanFrameCounter = sScanFrameCounterMax;
status = RAIL_ConfigRxOptions(gRailHandle,
RAIL_RX_OPTION_DISABLE_FRAME_DETECTION,
RAIL_RX_OPTION_DISABLE_FRAME_DETECTION);
otEXPECT_ACTION(status == RAIL_STATUS_NO_ERROR, error = OT_ERROR_FAILED);
// set radio to Rx
otEXPECT_ACTION(radioSetRx(aChannel) == OT_ERROR_NONE, error = OT_ERROR_FAILED);
// start energy scan
status = efr32ScanDwell(SYMBOLS_PER_ENERGY_READING);
otEXPECT_ACTION(status == RAIL_STATUS_NO_ERROR, error = OT_ERROR_FAILED);
exit:
if (status != RAIL_STATUS_NO_ERROR)
{
energyScanComplete(OT_RADIO_RSSI_INVALID);
}
return error;
}
//------------------------------------------------------------------------------
// Stack support
uint64_t otPlatRadioGetNow(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
return otPlatTimeGet();
}
void otPlatRadioGetIeeeEui64(otInstance *aInstance, uint8_t *aIeeeEui64)
{
OT_UNUSED_VARIABLE(aInstance);
#if (RADIO_CONFIG_ENABLE_CUSTOM_EUI_SUPPORT) && defined(_SILICON_LABS_32B_SERIES_2)
// Invalid EUI
uint8_t nullEui[] = {0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU, 0xFFU};
// Read the Custom EUI and compare it to nullEui
if ((readUserData(aIeeeEui64, USERDATA_MFG_CUSTOM_EUI_64, OT_EXT_ADDRESS_SIZE, true) == -1)
|| (memcmp(aIeeeEui64, nullEui, OT_EXT_ADDRESS_SIZE) == 0))
#endif
{
uint64_t eui64;
uint8_t *eui64Ptr = NULL;
#if defined(_SILICON_LABS_32B_SERIES_2)
eui64 = SYSTEM_GetUnique();
#else
eui64 = sl_hal_system_get_unique();
#endif
eui64Ptr = (uint8_t *)&eui64;
for (uint8_t i = 0; i < OT_EXT_ADDRESS_SIZE; i++)
{
aIeeeEui64[i] = eui64Ptr[(OT_EXT_ADDRESS_SIZE - 1) - i];
}
}
}
void otPlatRadioSetPanId(otInstance *aInstance, uint16_t aPanId)
{
RAIL_Status_t status;
uint8_t iid = efr32GetIidFromInstance(aInstance);
uint8_t panIndex = efr32GetPanIndexFromIid(iid);
otEXPECT(sl_ot_rtos_task_can_access_pal());
OT_ASSERT(panIndex != INVALID_INTERFACE_INDEX);
otLogInfoPlat("PANID=%X index=%u IID=%d", aPanId, panIndex, iid);
utilsSoftSrcMatchSetPanId(iid, aPanId);
status = RAIL_IEEE802154_SetPanId(gRailHandle, aPanId, panIndex);
OT_ASSERT(status == RAIL_STATUS_NO_ERROR);
#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE
// We already have IID 0 enabled in filtermask to track BCAST Packets, so
// track only unique PanIds.
if (aPanId != RADIO_BCAST_PANID)
{
sRailFilterMask |= RADIO_GET_FILTER_MASK(iid);
}
#endif
exit:
return;
}
void otPlatRadioSetExtendedAddress(otInstance *aInstance, const otExtAddress *aAddress)
{
RAIL_Status_t status;
uint8_t iid = efr32GetIidFromInstance(aInstance);
uint8_t panIndex = efr32GetPanIndexFromIid(iid);
otEXPECT(sl_ot_rtos_task_can_access_pal());
OT_ASSERT(panIndex != INVALID_INTERFACE_INDEX);
for (size_t i = 0; i < sizeof(*aAddress); i++)
{
sExtAddress[panIndex].m8[i] = aAddress->m8[sizeof(*aAddress) - 1 - i];
}
otLogInfoPlat("ExtAddr=%X%X%X%X%X%X%X%X index=%u",
aAddress->m8[7],
aAddress->m8[6],
aAddress->m8[5],
aAddress->m8[4],
aAddress->m8[3],
aAddress->m8[2],
aAddress->m8[1],
aAddress->m8[0],
panIndex);
status = RAIL_IEEE802154_SetLongAddress(gRailHandle, (uint8_t *)aAddress->m8, panIndex);
OT_ASSERT(status == RAIL_STATUS_NO_ERROR);
exit:
return;
}
void otPlatRadioSetShortAddress(otInstance *aInstance, uint16_t aAddress)
{
RAIL_Status_t status;
uint8_t iid = efr32GetIidFromInstance(aInstance);
uint8_t panIndex = efr32GetPanIndexFromIid(iid);
otEXPECT(sl_ot_rtos_task_can_access_pal());
OT_ASSERT(panIndex != INVALID_INTERFACE_INDEX);
otLogInfoPlat("ShortAddr=%X index=%u", aAddress, panIndex);
status = RAIL_IEEE802154_SetShortAddress(gRailHandle, aAddress, panIndex);
OT_ASSERT(status == RAIL_STATUS_NO_ERROR);
exit:
return;
}
otRadioState otPlatRadioGetState(otInstance *aInstance)
{
otRadioState radioState = OT_RADIO_STATE_INVALID;
OT_UNUSED_VARIABLE(aInstance);
switch (RAIL_GetRadioState(gRailHandle))
{
case RAIL_RF_STATE_RX_ACTIVE:
radioState = OT_RADIO_STATE_RECEIVE;
break;
case RAIL_RF_STATE_TX_ACTIVE:
radioState = OT_RADIO_STATE_TRANSMIT;
break;
case RAIL_RF_STATE_IDLE:
radioState = OT_RADIO_STATE_SLEEP;
break;
case RAIL_RF_STATE_INACTIVE:
radioState = OT_RADIO_STATE_DISABLED;
break;
}
return radioState;
}
bool otPlatRadioIsEnabled(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
return (getInternalFlag(FLAG_RADIO_INIT_DONE));
}
otError otPlatRadioEnable(otInstance *aInstance)
{
otError error = OT_ERROR_NONE;
otEXPECT(!otPlatRadioIsEnabled(aInstance));
otLogInfoPlat("State=OT_RADIO_STATE_SLEEP");
exit:
return error;
}
otError otPlatRadioDisable(otInstance *aInstance)
{
otError error = OT_ERROR_NONE;
otEXPECT_ACTION(sl_ot_rtos_task_can_access_pal(), error = OT_ERROR_REJECTED);
otEXPECT_ACTION(otPlatRadioIsEnabled(aInstance), error = OT_ERROR_INVALID_STATE);
otLogInfoPlat("State=OT_RADIO_STATE_DISABLED");
exit:
return error;
}
otError otPlatRadioSleep(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
otError error = OT_ERROR_NONE;
otEXPECT_ACTION(!getInternalFlag(FLAG_ONGOING_TX_DATA), error = OT_ERROR_BUSY);
otLogInfoPlat("State=OT_RADIO_STATE_SLEEP");
setInternalFlag(FLAG_SCHEDULED_RX_PENDING, false);
radioSetIdle();
exit:
return error;
}
otError efr32RadioLoadChannelConfig(uint8_t aChannel, int8_t aTxPower)
{
otError error = OT_ERROR_NONE;
efr32BandConfig *config;
config = efr32RadioGetBandConfig(aChannel);
otEXPECT_ACTION(config != NULL, error = OT_ERROR_INVALID_ARGS);
if (sCurrentBandConfig != config)
{
RAIL_Idle(gRailHandle, RAIL_IDLE, true);
efr32RailConfigLoad(config, aTxPower);
sCurrentBandConfig = config;
}
else
{
sli_set_tx_power_in_rail(aTxPower);
}
exit:
return error;
}
otError otPlatRadioReceive(otInstance *aInstance, uint8_t aChannel)
{
otError error = OT_ERROR_NONE;
RAIL_Status_t status;
int8_t txPower;
uint8_t iid = efr32GetIidFromInstance(aInstance);
otEXPECT_ACTION(sl_ot_rtos_task_can_access_pal(), error = OT_ERROR_REJECTED);
otEXPECT_ACTION(!getInternalFlag(FLAG_ONGOING_TX_DATA) && sEnergyScanStatus != ENERGY_SCAN_STATUS_IN_PROGRESS,
error = OT_ERROR_INVALID_STATE);
OT_UNUSED_VARIABLE(iid);
#if FAST_CHANNEL_SWITCHING_SUPPORT && OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE
uint8_t index = efr32GetPanIndexFromIid(iid);
OT_ASSERT(index < RAIL_IEEE802154_RX_CHANNEL_SWITCHING_NUM_CHANNELS);
sChannelSwitchingCfg.channels[index] = aChannel;
#endif
txPower = sl_get_tx_power_for_current_channel(aInstance);
error = efr32RadioLoadChannelConfig(aChannel, txPower);
otEXPECT(error == OT_ERROR_NONE);
status = radioSetRx(aChannel);
otEXPECT_ACTION(status == RAIL_STATUS_NO_ERROR, error = OT_ERROR_FAILED);
setInternalFlag(FLAG_SCHEDULED_RX_PENDING, false);
sReceive.frame.mChannel = aChannel;
sReceiveAck.frame.mChannel = aChannel;
exit:
return error;
}
#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
otError otPlatRadioReceiveAt(otInstance *aInstance, uint8_t aChannel, uint32_t aStart, uint32_t aDuration)
{
otError error = OT_ERROR_NONE;
RAIL_Status_t status;
int8_t txPower = sl_get_tx_power_for_current_channel(aInstance);
// We can only have one schedule request i.e. either Rx or Tx as they use the
// same RAIL resources.
otEXPECT_ACTION(!getInternalFlag(EVENT_SCHEDULED_TX_STARTED), error = OT_ERROR_FAILED);
otEXPECT_ACTION(sl_ot_rtos_task_can_access_pal(), error = OT_ERROR_REJECTED);
OT_UNUSED_VARIABLE(aInstance);
error = efr32RadioLoadChannelConfig(aChannel, txPower);
otEXPECT(error == OT_ERROR_NONE);
status = radioScheduleRx(aChannel, aStart, aDuration);
otEXPECT_ACTION(status == RAIL_STATUS_NO_ERROR, error = OT_ERROR_FAILED);
setInternalFlag(FLAG_SCHEDULED_RX_PENDING, true);
sReceive.frame.mChannel = aChannel;
sReceiveAck.frame.mChannel = aChannel;
exit:
return error;
}
#endif // (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE
inline static void pushPendingCommand(pendingCommandType aCmdType, uint8_t aIid, void *aCmdParams)
{
pendingCommandEntry *pendingCommand = (pendingCommandEntry *)sl_malloc(sizeof(pendingCommandEntry));
OT_ASSERT(pendingCommand != NULL);
pendingCommand->cmdType = aCmdType;
pendingCommand->iid = aIid;
if (aCmdType == kPendingCommandTypeTransmit)
{
otRadioFrame *txFrame = (otRadioFrame *)aCmdParams;
pendingCommand->request.txFrame = txFrame;
}
else if (aCmdType == kPendingCommandTypeEnergyScan)
{
energyScanParams *energyScanReq = (energyScanParams *)aCmdParams;
pendingCommand->request.energyScan.scanChannel = energyScanReq->scanChannel;
pendingCommand->request.energyScan.scanDuration = energyScanReq->scanDuration;
}
if (!queueAdd(&sPendingCommandQueue, (void *)pendingCommand))
{
sl_free(pendingCommand);
}
}
#endif
otError otPlatRadioTransmit(otInstance *aInstance, otRadioFrame *aFrame)
{
otError error = OT_ERROR_NONE;
int8_t txPower = sl_get_tx_power_for_current_channel(aInstance);
uint8_t iid = efr32GetIidFromInstance(aInstance);
// sTransmitBuffer's index 0 corresponds to host 1 i.e. iid 1 and reason is,
// iid zero is reserved for broadcast frames in multipan case.
uint8_t txBufIndex = iid ? (iid - 1) : 0;
otEXPECT_ACTION(sl_ot_rtos_task_can_access_pal(), error = OT_ERROR_REJECTED);
#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE
// Accept GP packets even if radio is not in required state.
if ((sl_gp_intf_get_state() != SL_GP_STATE_SEND_RESPONSE) && sl_gp_intf_should_buffer_pkt(aInstance, aFrame, false))
{
sl_gp_intf_buffer_pkt(aInstance);
}
else
#endif
{
OT_ASSERT(txBufIndex < RADIO_REQUEST_BUFFER_COUNT);
OT_ASSERT(aFrame == &sTransmitBuffer[txBufIndex].frame);
OT_ASSERT(aFrame->mPsdu == sTransmitPsdu[txBufIndex]);
if (!aFrame->mInfo.mTxInfo.mIsARetx)
sTransmitBuffer[txBufIndex].currentRadioTxPriority = SL_802154_RADIO_PRIO_TX_MIN;
else if (sTransmitBuffer[txBufIndex].currentRadioTxPriority > SL_802154_RADIO_PRIO_TX_STEP)
sTransmitBuffer[txBufIndex].currentRadioTxPriority -= SL_802154_RADIO_PRIO_TX_STEP;
// TX priority is always bounded by the maximum priority configured
if (sTransmitBuffer[txBufIndex].currentRadioTxPriority < SL_802154_RADIO_PRIO_TX_MAX)
sTransmitBuffer[txBufIndex].currentRadioTxPriority = SL_802154_RADIO_PRIO_TX_MAX;
#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE
OT_ASSERT((iid != RADIO_BCAST_IID) && (iid < RADIO_INTERFACE_COUNT));
// Push pending transmit and exit if radio is busy.
if (isRadioTransmittingOrScanning())
{
pushPendingCommand(kPendingCommandTypeTransmit, iid, aFrame);
ExitNow(error = OT_ERROR_NONE);
}
#endif
error = efr32RadioLoadChannelConfig(aFrame->mChannel, txPower);
otEXPECT(error == OT_ERROR_NONE);
OT_ASSERT(!getInternalFlag(FLAG_ONGOING_TX_DATA));
setInternalFlag(RADIO_TX_EVENTS, false);
sTransmitBuffer[txBufIndex].iid = iid;
sCurrentTxPacket = &sTransmitBuffer[txBufIndex];
setInternalFlag(FLAG_CURRENT_TX_USE_CSMA, aFrame->mInfo.mTxInfo.mCsmaCaEnabled);
#if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
if (sCslPeriod > 0 && sCurrentTxPacket->frame.mInfo.mTxInfo.mTxDelay == 0)
{
// Only called for CSL children (sCslPeriod > 0)
// Note: Our SSEDs "schedule" transmissions to their parent in order to know
// exactly when in the future the data packets go out so they can calculate
// the accurate CSL phase to send to their parent.
sCurrentTxPacket->frame.mInfo.mTxInfo.mTxDelayBaseTime = RAIL_GetTime();
sCurrentTxPacket->frame.mInfo.mTxInfo.mTxDelay = 3000; // Chosen after internal certification testing
}
#endif
updateIeInfoTxFrame(sCurrentTxPacket->frame.mInfo.mTxInfo.mTxDelayBaseTime
+ sCurrentTxPacket->frame.mInfo.mTxInfo.mTxDelay + SHR_DURATION_US);
// Note - we need to call this outside of txCurrentPacket as for Series 2,
// this results in calling the SE interface from a critical section which is not permitted.
radioProcessTransmitSecurity(&sCurrentTxPacket->frame, sCurrentTxPacket->iid);
#endif // OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
CORE_DECLARE_IRQ_STATE;
CORE_ENTER_ATOMIC();
setInternalFlag(FLAG_ONGOING_TX_DATA, true);
tryTxCurrentPacket();
CORE_EXIT_ATOMIC();
if (getInternalFlag(EVENT_TX_FAILED))
{
otPlatRadioTxStarted(aInstance, aFrame);
}
}
exit:
return error;
}
#if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
SL_CODE_CLASSIFY(SL_CODE_COMPONENT_OT_PLATFORM_ABSTRACTION, SL_CODE_CLASS_TIME_CRITICAL)
void updateIeInfoTxFrame(uint32_t shrTxTime)
{
OT_ASSERT(sCurrentTxPacket != NULL);
#if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT && OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
// Seek the time sync offset and update the rendezvous time
if (sCurrentTxPacket->frame.mInfo.mTxInfo.mIeInfo->mTimeIeOffset != 0)
{
uint8_t *timeIe = sCurrentTxPacket->frame.mPsdu + sCurrentTxPacket->frame.mInfo.mTxInfo.mIeInfo->mTimeIeOffset;
uint64_t time = otPlatTimeGet() + sCurrentTxPacket->frame.mInfo.mTxInfo.mIeInfo->mNetworkTimeOffset;
*timeIe = sCurrentTxPacket->frame.mInfo.mTxInfo.mIeInfo->mTimeSyncSeq;
*(++timeIe) = (uint8_t)(time & 0xff);
for (uint8_t i = 1; i < sizeof(uint64_t); i++)
{
time = time >> 8;
*(++timeIe) = (uint8_t)(time & 0xff);
}
}
#endif // OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT && OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
// Update IE data in the 802.15.4 header with the newest CSL period / phase
if (sCslPeriod > 0 && !sCurrentTxPacket->frame.mInfo.mTxInfo.mIsHeaderUpdated)
{
otMacFrameSetCslIe(&sCurrentTxPacket->frame, (uint16_t)sCslPeriod, getCslPhase(shrTxTime));
}
#else
OT_UNUSED_VARIABLE(shrTxTime);
#endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
}
#endif // OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
void txCurrentPacket(void)
{
OT_ASSERT(getInternalFlag(FLAG_ONGOING_TX_DATA));
OT_ASSERT(sCurrentTxPacket != NULL);
RAIL_TxOptions_t txOptions = RAIL_TX_OPTIONS_DEFAULT;
RAIL_Status_t status = RAIL_STATUS_INVALID_STATE;
uint8_t frameLength;
bool ackRequested;
#if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT
railDebugCounters.mRailPlatTxTriggered++;
#endif
// signalling this event earlier, as this event can OT_ASSERT REQ (expecially for a
// non-CSMA transmit) giving the Coex master a little more time to grant or deny.
if (getInternalFlag(FLAG_CURRENT_TX_USE_CSMA))
{
(void)handlePhyStackEvent(SL_RAIL_UTIL_IEEE802154_STACK_EVENT_TX_PENDED_PHY, (uint32_t) true);
}
else
{
(void)handlePhyStackEvent(SL_RAIL_UTIL_IEEE802154_STACK_EVENT_TX_PENDED_PHY, (uint32_t) false);
}
frameLength = (uint8_t)sCurrentTxPacket->frame.mLength;
if (PHY_HEADER_SIZE == 1)
{
RAIL_WriteTxFifo(gRailHandle, &frameLength, sizeof frameLength, true);
}
else
{ // 2 byte PHR for Sub-GHz
uint8_t PHRByte1 = (0x08U /*FCS=2byte*/ | 0x10U /*Whiten=enabled*/);
uint8_t PHRByte2 = (uint8_t)(__RBIT(frameLength) >> 24);
RAIL_WriteTxFifo(gRailHandle, &PHRByte1, sizeof PHRByte1, true);
RAIL_WriteTxFifo(gRailHandle, &PHRByte2, sizeof PHRByte2, false);
}
RAIL_WriteTxFifo(gRailHandle, sCurrentTxPacket->frame.mPsdu, frameLength - 2, false);
RAIL_SchedulerInfo_t txSchedulerInfo = {
.priority = sCurrentTxPacket->currentRadioTxPriority,
.slipTime = RADIO_SCHEDULER_CHANNEL_SLIP_TIME,
.transactionTime = 0, // will be calculated later if DMP is used
};
ackRequested = (sCurrentTxPacket->frame.mPsdu[0] & IEEE802154_FRAME_FLAG_ACK_REQUIRED);
if (ackRequested)
{
txOptions |= RAIL_TX_OPTION_WAIT_FOR_ACK;
#if RADIO_CONFIG_DMP_SUPPORT
// time we wait for ACK
if (RAIL_GetSymbolRate(gRailHandle) > 0)
{
txSchedulerInfo.transactionTime += 12 * 1e6 / RAIL_GetSymbolRate(gRailHandle);
}
else
{
txSchedulerInfo.transactionTime += 12 * RADIO_TIMING_DEFAULT_SYMBOLTIME_US;
}
#endif
}
#ifdef SL_CATALOG_RAIL_UTIL_ANT_DIV_PRESENT
// Update Tx options to use currently-selected antenna.
// If antenna diverisity on Tx is disabled, leave both options 0
// so Tx antenna tracks Rx antenna.
if (sl_rail_util_ant_div_get_tx_antenna_mode() != SL_RAIL_UTIL_ANT_DIV_DISABLED)
{
txOptions |= ((sl_rail_util_ant_div_get_tx_antenna_selected() == SL_RAIL_UTIL_ANTENNA_SELECT_ANTENNA1)
? RAIL_TX_OPTION_ANTENNA0
: RAIL_TX_OPTION_ANTENNA1);
}
#endif // SL_CATALOG_RAIL_UTIL_ANT_DIV_PRESENT
#if RADIO_CONFIG_DMP_SUPPORT
// time needed for the frame itself
// 4B preamble, 1B SFD, 1B PHR is not counted in frameLength
if (RAIL_GetBitRate(gRailHandle) > 0)
{
txSchedulerInfo.transactionTime += (frameLength + 4 + 1 + 1) * 8 * 1e6 / RAIL_GetBitRate(gRailHandle);
}
else
{ // assume 250kbps
txSchedulerInfo.transactionTime += (frameLength + 4 + 1 + 1) * RADIO_TIMING_DEFAULT_BYTETIME_US;
}
#endif
// We can only have one schedule request i.e. either Rx or Tx as they use the same RAIL resources.
// Reject the transmit request if there is scheduled Rx.
otEXPECT_ACTION(!getInternalFlag(FLAG_SCHEDULED_RX_PENDING), status = RAIL_STATUS_INVALID_STATE);
if (sCurrentTxPacket->frame.mInfo.mTxInfo.mTxDelay == 0)
{
if (getInternalFlag(FLAG_CURRENT_TX_USE_CSMA))
{
#if RADIO_CONFIG_DMP_SUPPORT
// time needed for CSMA/CA
txSchedulerInfo.transactionTime += RADIO_TIMING_CSMA_OVERHEAD_US;
#endif
csmaConfig.csmaTries = sCurrentTxPacket->frame.mInfo.mTxInfo.mMaxCsmaBackoffs;
csmaConfig.ccaThreshold = sCcaThresholdDbm;
status = RAIL_StartCcaCsmaTx(gRailHandle,
sCurrentTxPacket->frame.mChannel,
txOptions,
&csmaConfig,
&txSchedulerInfo);
}
else
{
status = RAIL_StartTx(gRailHandle, sCurrentTxPacket->frame.mChannel, txOptions, &txSchedulerInfo);
}
if (status == RAIL_STATUS_NO_ERROR)
{
(void)handlePhyStackEvent(SL_RAIL_UTIL_IEEE802154_STACK_EVENT_TX_STARTED, 0U);
}
}
else
{
// For CSL transmitters (FTDs):
// mTxDelayBaseTime = rx-timestamp (end of sync word) when we received CSL-sync with IEs
// mTxDelay = Delay starting from mTxDelayBaseTime
//
// For CSL receivers (SSEDs):
// mTxDelayBaseTime = timestamp when otPlatRadioTransmit is called
// mTxDelay = Chosen value in the future where transmit is scheduled, so we know exactly
// when to calculate the phase (we can't do this on-the-fly as the packet is going out
// due to platform limitations. see radioScheduleRx)
//
// Note that both use single CCA config, overriding any CCA/CSMA configs from the stack
//
#if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
RAIL_ScheduleTxConfig_t scheduleTxOptions = {.when = sCurrentTxPacket->frame.mInfo.mTxInfo.mTxDelayBaseTime
+ sCurrentTxPacket->frame.mInfo.mTxInfo.mTxDelay
- SHR_DURATION_US,
.mode = RAIL_TIME_ABSOLUTE,
.txDuringRx = RAIL_SCHEDULED_TX_DURING_RX_POSTPONE_TX};
// Set ccaBackoff to some constant value, so we have predictable radio warmup time for schedule tx.
cslCsmaConfig.ccaBackoff = CSL_CSMA_BACKOFF_TIME_IN_US;
scheduleTxOptions.when -= cslCsmaConfig.ccaBackoff;
// CSL transmissions don't use CSMA but MAC accounts for single CCA time.
// cslCsmaConfig is set to RAIL_CSMA_CONFIG_SINGLE_CCA above.
status = RAIL_StartScheduledCcaCsmaTx(gRailHandle,
sCurrentTxPacket->frame.mChannel,
txOptions,
&scheduleTxOptions,
&cslCsmaConfig,
&txSchedulerInfo);
if (status == RAIL_STATUS_NO_ERROR)
{
#if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT
railDebugCounters.mRailEventsScheduledTxTriggeredCount++;
#endif
(void)handlePhyStackEvent(SL_RAIL_UTIL_IEEE802154_STACK_EVENT_TX_STARTED, 0U);
}
#endif
}
exit:
if (status == RAIL_STATUS_NO_ERROR)
{
#if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT
railDebugCounters.mRailTxStarted++;
#endif
}
else
{
#if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT
railDebugCounters.mRailTxStartFailed++;
#endif
(void)handlePhyStackEvent(SL_RAIL_UTIL_IEEE802154_STACK_EVENT_TX_BLOCKED, (uint32_t)ackRequested);
txFailedCallback(false, EVENT_TX_FAILED);
otSysEventSignalPending();
}
}
// This API gets called from init procedure so instance to IID mapping does not exist
// at that point. Also this api will get called sequentially so assign new transmit
// buffer if aInstance does not exist in sInstances.
otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
uint8_t index = 0;
otRadioFrame *aRadioFrame = NULL;
otEXPECT(sl_ot_rtos_task_can_access_pal());
#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE
for (index = 0; index < OPENTHREAD_CONFIG_MULTIPLE_INSTANCE_NUM; index++)
{
if (sInstances[index] == aInstance || sInstances[index] == NULL)
{
break;
}
}
#endif // OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE
aRadioFrame = &sTransmitBuffer[index].frame;
exit:
return aRadioFrame;
}
int8_t otPlatRadioGetRssi(otInstance *aInstance)
{
otError error;
uint32_t start;
int8_t rssi = OT_RADIO_RSSI_INVALID;
uint8_t aChannel = sReceive.frame.mChannel;
uint8_t iid = efr32GetIidFromInstance(aInstance);
otEXPECT(sl_ot_rtos_task_can_access_pal());
otEXPECT(!getInternalFlag(FLAG_ONGOING_TX_DATA));
OT_UNUSED_VARIABLE(iid);
#if FAST_CHANNEL_SWITCHING_SUPPORT && OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE
uint8_t index = efr32GetPanIndexFromIid(iid);
OT_ASSERT(index < RAIL_IEEE802154_RX_CHANNEL_SWITCHING_NUM_CHANNELS);
if (sChannelSwitchingCfg.channels[index] != UNINITIALIZED_CHANNEL)
{
aChannel = sChannelSwitchingCfg.channels[index];
}
#endif
error = efr32StartEnergyScan(ENERGY_SCAN_MODE_SYNC, aChannel, SL_OPENTHREAD_RSSI_AVERAGING_TIME);
otEXPECT(error == OT_ERROR_NONE);
start = RAIL_GetTime();
// waiting for the event RAIL_EVENT_RSSI_AVERAGE_DONE
while (sEnergyScanStatus == ENERGY_SCAN_STATUS_IN_PROGRESS
&& ((RAIL_GetTime() - start) < SL_OPENTHREAD_RSSI_AVERAGING_TIMEOUT))
;
if (sEnergyScanStatus == ENERGY_SCAN_STATUS_COMPLETED)
{
rssi = sEnergyScanResultDbm;
}
sEnergyScanStatus = ENERGY_SCAN_STATUS_IDLE;
exit:
return rssi;
}
otRadioCaps otPlatRadioGetCaps(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
return sRadioCapabilities;
}
bool otPlatRadioGetPromiscuous(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
return sPromiscuous;
}
void otPlatRadioSetPromiscuous(otInstance *aInstance, bool aEnable)
{
OT_UNUSED_VARIABLE(aInstance);
RAIL_Status_t status;
otEXPECT(sl_ot_rtos_task_can_access_pal());
sPromiscuous = aEnable;
status = RAIL_IEEE802154_SetPromiscuousMode(gRailHandle, aEnable);
OT_ASSERT(status == RAIL_STATUS_NO_ERROR);
exit:
return;
}
void otPlatRadioEnableSrcMatch(otInstance *aInstance, bool aEnable)
{
OT_UNUSED_VARIABLE(aInstance);
otEXPECT(sl_ot_rtos_task_can_access_pal());
// set Frame Pending bit for all outgoing ACKs if aEnable is false
sIsSrcMatchEnabled = aEnable;
exit:
return;
}
otError otPlatRadioGetTransmitPower(otInstance *aInstance, int8_t *aPower)
{
OT_UNUSED_VARIABLE(aInstance);
otError error = OT_ERROR_NONE;
otEXPECT_ACTION(aPower != NULL, error = OT_ERROR_INVALID_ARGS);
// RAIL_GetTxPowerDbm() returns power in deci-dBm (0.1dBm)
// Divide by 10 because aPower is supposed be in units dBm
*aPower = RAIL_GetTxPowerDbm(gRailHandle) / 10;
exit:
return error;
}
otError otPlatRadioSetTransmitPower(otInstance *aInstance, int8_t aPower)
{
return sli_set_default_tx_power(aInstance, aPower);
}
// Required for RCP error recovery
// See src/lib/spinel/radio_spinel.cpp::RestoreProperties()
otError otPlatRadioSetChannelMaxTransmitPower(otInstance *aInstance, uint8_t aChannel, int8_t aMaxPower)
{
return sli_set_channel_max_tx_power(aInstance, aChannel, aMaxPower);
}
otError otPlatRadioGetCcaEnergyDetectThreshold(otInstance *aInstance, int8_t *aThreshold)
{
OT_UNUSED_VARIABLE(aInstance);
otError error = OT_ERROR_NONE;
otEXPECT_ACTION(aThreshold != NULL, error = OT_ERROR_INVALID_ARGS);
*aThreshold = sCcaThresholdDbm;
exit:
return error;
}
otError otPlatRadioSetCcaEnergyDetectThreshold(otInstance *aInstance, int8_t aThreshold)
{
OT_UNUSED_VARIABLE(aInstance);
otError error = OT_ERROR_NONE;
otEXPECT_ACTION(sl_ot_rtos_task_can_access_pal(), error = OT_ERROR_REJECTED);
sCcaThresholdDbm = aThreshold;
exit:
return error;
}
int8_t otPlatRadioGetReceiveSensitivity(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
return SL_OPENTHREAD_RECEIVE_SENSITIVITY;
}
otError otPlatRadioEnergyScan(otInstance *aInstance, uint8_t aScanChannel, uint16_t aScanDuration)
{
otError error = OT_ERROR_NONE;
uint8_t iid = efr32GetIidFromInstance(aInstance);
otEXPECT_ACTION(sl_ot_rtos_task_can_access_pal(), error = OT_ERROR_REJECTED);
#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE
OT_ASSERT((iid != RADIO_BCAST_IID) && (iid < RADIO_INTERFACE_COUNT));
// Push pending energy-scan and exit if radio is busy.
if (isRadioTransmittingOrScanning())
{
energyScanParams params = {aScanChannel, aScanDuration};
pushPendingCommand(kPendingCommandTypeEnergyScan, iid, &params);
ExitNow(error = OT_ERROR_NONE);
}
#endif
sEnergyScanActiveInterface = iid;
error = efr32StartEnergyScan(ENERGY_SCAN_MODE_ASYNC, aScanChannel, (RAIL_Time_t)aScanDuration * US_IN_MS);
exit:
return error;
}
//------------------------------------------------------------------------------
// Radio Config: Thread 1.2 transmit security support
#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
void otPlatRadioSetMacKey(otInstance *aInstance,
uint8_t aKeyIdMode,
uint8_t aKeyId,
const otMacKeyMaterial *aPrevKey,
const otMacKeyMaterial *aCurrKey,
const otMacKeyMaterial *aNextKey,
otRadioKeyType aKeyType)
{
OT_UNUSED_VARIABLE(aKeyIdMode);
OT_UNUSED_VARIABLE(aKeyType);
uint8_t iid = efr32GetIidFromInstance(aInstance);
otEXPECT(sl_ot_rtos_task_can_access_pal());
OT_ASSERT(aPrevKey != NULL && aCurrKey != NULL && aNextKey != NULL);
sMacKeys[iid].keyId = aKeyId;
memcpy(&sMacKeys[iid].keys[MAC_KEY_PREV], aPrevKey, sizeof(otMacKeyMaterial));
memcpy(&sMacKeys[iid].keys[MAC_KEY_CURRENT], aCurrKey, sizeof(otMacKeyMaterial));
memcpy(&sMacKeys[iid].keys[MAC_KEY_NEXT], aNextKey, sizeof(otMacKeyMaterial));
#if (OPENTHREAD_CONFIG_CRYPTO_LIB == OPENTHREAD_CONFIG_CRYPTO_LIB_PSA)
size_t aKeyLen = 0;
otError error = OT_ERROR_NONE;
error = otPlatCryptoExportKey(sMacKeys[iid].keys[MAC_KEY_PREV].mKeyMaterial.mKeyRef,
sMacKeys[iid].keys[MAC_KEY_PREV].mKeyMaterial.mKey.m8,
sizeof(sMacKeys[iid].keys[MAC_KEY_PREV]),
&aKeyLen);
OT_ASSERT(error == OT_ERROR_NONE);
error = otPlatCryptoExportKey(sMacKeys[iid].keys[MAC_KEY_CURRENT].mKeyMaterial.mKeyRef,
sMacKeys[iid].keys[MAC_KEY_CURRENT].mKeyMaterial.mKey.m8,
sizeof(sMacKeys[iid].keys[MAC_KEY_CURRENT]),
&aKeyLen);
OT_ASSERT(error == OT_ERROR_NONE);
error = otPlatCryptoExportKey(sMacKeys[iid].keys[MAC_KEY_NEXT].mKeyMaterial.mKeyRef,
sMacKeys[iid].keys[MAC_KEY_NEXT].mKeyMaterial.mKey.m8,
sizeof(sMacKeys[iid].keys[MAC_KEY_NEXT]),
&aKeyLen);
OT_ASSERT(error == OT_ERROR_NONE);
#endif
exit:
return;
}
void otPlatRadioSetMacFrameCounter(otInstance *aInstance, uint32_t aMacFrameCounter)
{
uint8_t iid = efr32GetIidFromInstance(aInstance);
otEXPECT(sl_ot_rtos_task_can_access_pal());
CORE_DECLARE_IRQ_STATE;
CORE_ENTER_ATOMIC();
sMacKeys[iid].macFrameCounter = aMacFrameCounter;
CORE_EXIT_ATOMIC();
exit:
return;
}
void otPlatRadioSetMacFrameCounterIfLarger(otInstance *aInstance, uint32_t aMacFrameCounter)
{
uint8_t iid = efr32GetIidFromInstance(aInstance);
otEXPECT(sl_ot_rtos_task_can_access_pal());
CORE_DECLARE_IRQ_STATE;
CORE_ENTER_ATOMIC();
if (aMacFrameCounter > sMacKeys[iid].macFrameCounter)
{
sMacKeys[iid].macFrameCounter = aMacFrameCounter;
}
CORE_EXIT_ATOMIC();
exit:
return;
}
//------------------------------------------------------------------------------
// Radio Config: Enhanced Acks, CSL
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
otError otPlatRadioEnableCsl(otInstance *aInstance,
uint32_t aCslPeriod,
otShortAddress aShortAddr,
const otExtAddress *aExtAddr)
{
otError error = OT_ERROR_NONE;
OT_UNUSED_VARIABLE(aInstance);
OT_UNUSED_VARIABLE(aShortAddr);
OT_UNUSED_VARIABLE(aExtAddr);
otEXPECT_ACTION(sl_ot_rtos_task_can_access_pal(), error = OT_ERROR_REJECTED);
sCslPeriod = aCslPeriod;
exit:
return error;
}
void otPlatRadioUpdateCslSampleTime(otInstance *aInstance, uint32_t aCslSampleTime)
{
OT_UNUSED_VARIABLE(aInstance);
otEXPECT(sl_ot_rtos_task_can_access_pal());
sCslSampleTime = aCslSampleTime;
exit:
return;
}
#endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
uint8_t otPlatRadioGetCslAccuracy(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
return otPlatTimeGetXtalAccuracy();
}
uint8_t otPlatRadioGetCslUncertainty(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
return SL_OPENTHREAD_CSL_TX_UNCERTAINTY;
}
//------------------------------------------------------------------------------
// Radio Config: Link Metrics
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
otError otPlatRadioConfigureEnhAckProbing(otInstance *aInstance,
otLinkMetrics aLinkMetrics,
const otShortAddress aShortAddress,
const otExtAddress *aExtAddress)
{
otError error;
OT_UNUSED_VARIABLE(aInstance);
otEXPECT_ACTION(sl_ot_rtos_task_can_access_pal(), error = OT_ERROR_REJECTED);
error = otLinkMetricsConfigureEnhAckProbing(aShortAddress, aExtAddress, aLinkMetrics);
exit:
return error;
}
#endif
#endif // (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
#if OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
otError otPlatRadioSetCoexEnabled(otInstance *aInstance, bool aEnabled)
{
otError error;
OT_UNUSED_VARIABLE(aInstance);
otEXPECT_ACTION(sl_ot_rtos_task_can_access_pal(), error = OT_ERROR_REJECTED);
sl_status_t status = sl_rail_util_coex_set_enable(aEnabled);
if (aEnabled && !sl_rail_util_coex_is_enabled())
{
otLogInfoPlat("Coexistence GPIO configurations not set");
return OT_ERROR_FAILED;
}
sRadioCoexEnabled = aEnabled;
error = (status != SL_STATUS_OK) ? OT_ERROR_FAILED : OT_ERROR_NONE;
exit:
return error;
}
bool otPlatRadioIsCoexEnabled(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
return (sRadioCoexEnabled && sl_rail_util_coex_is_enabled());
}
#endif // OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
//------------------------------------------------------------------------------
// Radio implementation: Enhanced ACKs, CSL
// Return false if we should generate an immediate ACK
// Return true otherwise
SL_CODE_CLASSIFY(SL_CODE_COMPONENT_OT_PLATFORM_ABSTRACTION, SL_CODE_CLASS_TIME_CRITICAL)
static bool writeIeee802154EnhancedAck(RAIL_Handle_t aRailHandle,
RAIL_RxPacketInfo_t *packetInfoForEnhAck,
uint32_t rxTimestamp,
uint8_t *initialPktReadBytes,
uint8_t *receivedPsdu)
{
// RAIL will generate an Immediate ACK for us.
// For an Enhanced ACK, we need to generate the whole packet ourselves.
// An 802.15.4 packet from RAIL should look like:
// 1/2 | 1/2 | 0/1 | 0/2 | 0/2/8 | 0/2 | 0/2/8 | 14
// PHR | MacFCF | Seq# | DstPan | DstAdr | SrcPan | SrcAdr | SecHdr
// With RAIL_IEEE802154_EnableEarlyFramePending(), RAIL_EVENT_IEEE802154_DATA_REQUEST_COMMAND
// is triggered after receiving through the SrcAdr field of Version 0/1 packets,
// and after receiving through the SecHdr for Version 2 packets.
otRadioFrame receivedFrame, enhAckFrame;
uint8_t enhAckPsdu[IEEE802154_MAX_LENGTH];
#define EARLY_FRAME_PENDING_EXPECTED_BYTES (2U + 2U + 1U + 2U + 8U + 2U + 8U + 14U)
#define FINAL_PACKET_LENGTH_WITH_IE (EARLY_FRAME_PENDING_EXPECTED_BYTES + OT_ACK_IE_MAX_SIZE)
otEXPECT((packetInfoForEnhAck != NULL) && (initialPktReadBytes != NULL) && (receivedPsdu != NULL));
*initialPktReadBytes = readInitialPacketData(packetInfoForEnhAck,
EARLY_FRAME_PENDING_EXPECTED_BYTES,
(PHY_HEADER_SIZE + 2),
receivedPsdu,
FINAL_PACKET_LENGTH_WITH_IE);
uint8_t iid = INVALID_INTERFACE_INDEX;
if (*initialPktReadBytes == 0U)
{
return true; // Nothing to read, which means generating an immediate ACK is also pointless
}
receivedFrame.mPsdu = receivedPsdu + PHY_HEADER_SIZE;
// This should be set to the expected length of the packet is being received.
// We consider this while calculating the phase value below.
receivedFrame.mLength = packetInfoForEnhAck->firstPortionData[0];
enhAckFrame.mPsdu = enhAckPsdu + PHY_HEADER_SIZE;
if (!otMacFrameIsVersion2015(&receivedFrame))
{
return false;
}
otMacAddress aSrcAddress;
uint8_t linkMetricsDataLen = 0;
uint8_t *dataPtr = NULL;
bool setFramePending = false;
iid = getIidFromFilterMask(packetInfoForEnhAck->filterMask);
otMacFrameGetSrcAddr(&receivedFrame, &aSrcAddress);
if (sIsSrcMatchEnabled && (aSrcAddress.mType != OT_MAC_ADDRESS_TYPE_NONE))
{
setFramePending = (aSrcAddress.mType == OT_MAC_ADDRESS_TYPE_EXTENDED
? (utilsSoftSrcMatchExtFindEntry(iid, &aSrcAddress.mAddress.mExtAddress) >= 0)
: (utilsSoftSrcMatchShortFindEntry(iid, aSrcAddress.mAddress.mShortAddress) >= 0));
}
// Generate our IE header.
// Write IE data for enhanced ACK (link metrics + allocate bytes for CSL)
#if OPENTHREAD_CONFIG_MLE_LINK_METRICS_SUBJECT_ENABLE
uint8_t linkMetricsData[OT_ENH_PROBING_IE_DATA_MAX_SIZE];
linkMetricsDataLen = otLinkMetricsEnhAckGenData(&aSrcAddress, sLastLqi, sLastRssi, linkMetricsData);
if (linkMetricsDataLen > 0)
{
dataPtr = linkMetricsData;
}
#endif
sAckIeDataLength = generateAckIeData(dataPtr, linkMetricsDataLen);
otEXPECT(otMacFrameGenerateEnhAck(&receivedFrame, setFramePending, sAckIeData, sAckIeDataLength, &enhAckFrame)
== OT_ERROR_NONE);
#if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE
if (sCslPeriod > 0)
{
// Calculate time in the future where the SHR is done being sent out
uint32_t ackShrDoneTime = // Currently partially received packet's SHR time
(rxTimestamp
- (packetInfoForEnhAck->packetBytes * OT_RADIO_SYMBOL_TIME * 2)
// PHR of this packet
+ (PHY_HEADER_SIZE * OT_RADIO_SYMBOL_TIME * 2)
// Received frame's expected time in the PHR
+ (receivedFrame.mLength * OT_RADIO_SYMBOL_TIME * 2)
// rxToTx turnaround time
+ sRailIeee802154Config.timings.rxToTx
// PHR time of the ACK
+ (PHY_HEADER_SIZE * OT_RADIO_SYMBOL_TIME * 2)
// SHR time of the ACK
+ (SHR_SIZE * OT_RADIO_SYMBOL_TIME * 2));
// Update IE data in the 802.15.4 header with the newest CSL period / phase
otMacFrameSetCslIe(&enhAckFrame, (uint16_t)sCslPeriod, getCslPhase(ackShrDoneTime));
}
#else
OT_UNUSED_VARIABLE(rxTimestamp);
#endif
if (otMacFrameIsSecurityEnabled(&enhAckFrame))
{
otEXPECT(radioProcessTransmitSecurity(&enhAckFrame, iid) == OT_ERROR_NONE);
}
// Before we're done, store some important info in reserved bits in the
// MAC header (cleared later)
// Check whether frame pending is set.
// Check whether enhanced ACK is secured.
otEXPECT((skipRxPacketLengthBytes(packetInfoForEnhAck)) == OT_ERROR_NONE);
uint8_t *macFcfPointer =
((packetInfoForEnhAck->firstPortionBytes == 0) ? (uint8_t *)packetInfoForEnhAck->lastPortionData
: (uint8_t *)packetInfoForEnhAck->firstPortionData);
if (otMacFrameIsSecurityEnabled(&enhAckFrame))
{
*macFcfPointer |= IEEE802154_SECURED_OUTGOING_ENHANCED_ACK;
}
if (setFramePending)
{
*macFcfPointer |= IEEE802154_FRAME_PENDING_SET_IN_OUTGOING_ACK;
}
// Fill in PHR now that we know Enh-ACK's length
if (PHY_HEADER_SIZE == 2U) // Not true till SubGhz implementation is in place
{
enhAckPsdu[0] = (0x08U /*FCS=2byte*/ | 0x10U /*Whiten=enabled*/);
enhAckPsdu[1] = (uint8_t)(__RBIT(enhAckFrame.mLength) >> 24);
}
else
{
enhAckPsdu[0] = enhAckFrame.mLength;
}
RAIL_Status_t enhAckStatus = RAIL_IEEE802154_WriteEnhAck(aRailHandle, enhAckPsdu, enhAckFrame.mLength);
#if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT
otEXPECT_ACTION(enhAckStatus == RAIL_STATUS_NO_ERROR, railDebugCounters.mRailEventsEnhAckTxFailed++);
#else
otEXPECT(enhAckStatus == RAIL_STATUS_NO_ERROR);
#endif
exit:
return true;
}
#endif // (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
//------------------------------------------------------------------------------
// RAIL callbacks
SL_CODE_CLASSIFY(SL_CODE_COMPONENT_OT_PLATFORM_ABSTRACTION, SL_CODE_CLASS_TIME_CRITICAL)
static void dataRequestCommandCallback(RAIL_Handle_t aRailHandle)
{
#define MAX_EXPECTED_BYTES (2U + 2U + 1U) // PHR + FCF + DSN
uint8_t receivedPsdu[IEEE802154_MAX_LENGTH];
uint8_t pktOffset = PHY_HEADER_SIZE;
uint8_t initialPktReadBytes;
RAIL_RxPacketInfo_t packetInfo;
uint32_t rxCallbackTimestamp = otPlatAlarmMicroGetNow();
// This callback occurs after the address fields of an incoming
// ACK-requesting CMD or DATA frame have been received and we
// can do a frame pending check. We must also figure out what
// kind of ACK is being requested -- Immediate or Enhanced.
#if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)
if (writeIeee802154EnhancedAck(aRailHandle, &packetInfo, rxCallbackTimestamp, &initialPktReadBytes, receivedPsdu))
{
// We also return true above if there were failures in
// generating an enhanced ACK.
return;
}
#else
OT_UNUSED_VARIABLE(rxCallbackTimestamp);
initialPktReadBytes =
readInitialPacketData(&packetInfo, MAX_EXPECTED_BYTES, pktOffset + 2, receivedPsdu, MAX_EXPECTED_BYTES);
#endif
// Calculate frame pending for immediate-ACK
// If not, RAIL will send an immediate ACK, but we need to do FP lookup.
RAIL_Status_t status = RAIL_STATUS_NO_ERROR;
// Check if we read the FCF, if not, set macFcf to 0
uint16_t macFcf = (initialPktReadBytes <= pktOffset) ? 0U : receivedPsdu[pktOffset];
bool framePendingSet = false;
if (sIsSrcMatchEnabled)
{
RAIL_IEEE802154_Address_t sourceAddress;
status = RAIL_IEEE802154_GetAddress(aRailHandle, &sourceAddress);
otEXPECT(status == RAIL_STATUS_NO_ERROR);
uint8_t iid = getIidFromFilterMask(packetInfo.filterMask);
if ((sourceAddress.length == RAIL_IEEE802154_LongAddress
&& utilsSoftSrcMatchExtFindEntry(iid, (otExtAddress *)sourceAddress.longAddress) >= 0)
|| (sourceAddress.length == RAIL_IEEE802154_ShortAddress
&& utilsSoftSrcMatchShortFindEntry(iid, sourceAddress.shortAddress) >= 0))
{
status = RAIL_IEEE802154_SetFramePending(aRailHandle);
otEXPECT(status == RAIL_STATUS_NO_ERROR);
framePendingSet = true;
}
}
else if ((macFcf & IEEE802154_FRAME_TYPE_MASK) != IEEE802154_FRAME_TYPE_DATA)
{
status = RAIL_IEEE802154_SetFramePending(aRailHandle);
otEXPECT(status == RAIL_STATUS_NO_ERROR);
framePendingSet = true;
}
if (framePendingSet)
{
// Store whether frame pending was set in the outgoing ACK in a reserved
// bit of the MAC header (cleared later)
otEXPECT((skipRxPacketLengthBytes(&packetInfo)) == OT_ERROR_NONE);
uint8_t *macFcfPointer = ((packetInfo.firstPortionBytes == 0) ? (uint8_t *)packetInfo.lastPortionData
: (uint8_t *)packetInfo.firstPortionData);
*macFcfPointer |= IEEE802154_FRAME_PENDING_SET_IN_OUTGOING_ACK;
}
exit:
if (status == RAIL_STATUS_INVALID_STATE)
{
otLogWarnPlat("Too late to modify outgoing FP");
}
else
{
OT_ASSERT(status == RAIL_STATUS_NO_ERROR);
}
}
SL_CODE_CLASSIFY(SL_CODE_COMPONENT_OT_PLATFORM_ABSTRACTION, SL_CODE_CLASS_TIME_CRITICAL)
static void packetReceivedCallback(void)
{
RAIL_RxPacketInfo_t packetInfo;
RAIL_RxPacketDetails_t packetDetails;
uint16_t length = 0;
bool framePendingInAck = false;
bool dropPacket = false;
uint8_t iid = 0;
sl_status_t status;
bool isRxPacketQueued;
rxBuffer *rxPacketBuf = NULL;
RAIL_RxPacketHandle_t packetHandle = RAIL_GetRxPacketInfo(gRailHandle, RAIL_RX_PACKET_HANDLE_NEWEST, &packetInfo);
otEXPECT_ACTION(
(packetHandle != RAIL_RX_PACKET_HANDLE_INVALID && packetInfo.packetStatus == RAIL_RX_PACKET_READY_SUCCESS),
dropPacket = true);
otEXPECT_ACTION(validatePacketDetails(packetHandle, &packetDetails, &packetInfo, &length), dropPacket = true);
otEXPECT_ACTION((skipRxPacketLengthBytes(&packetInfo)) == OT_ERROR_NONE, dropPacket = true);
uint8_t macFcf =
((packetInfo.firstPortionBytes == 0) ? packetInfo.lastPortionData[0] : packetInfo.firstPortionData[0]);
iid = getIidFromFilterMask(packetInfo.filterMask);
if (packetDetails.isAck)
{
otEXPECT_ACTION(
(length >= IEEE802154_MIN_LENGTH && (macFcf & IEEE802154_FRAME_TYPE_MASK) == IEEE802154_FRAME_TYPE_ACK),
dropPacket = true);
// read packet
RAIL_CopyRxPacket(sReceiveAck.frame.mPsdu, &packetInfo);
#if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT
railDebugCounters.mRailEventAcksReceived++;
#endif
sReceiveAck.frame.mLength = length;
(void)handlePhyStackEvent(SL_RAIL_UTIL_IEEE802154_STACK_EVENT_RX_ENDED, (uint32_t)isReceivingFrame());
if (txWaitingForAck()
&& (sReceiveAck.frame.mPsdu[IEEE802154_DSN_OFFSET] == sCurrentTxPacket->frame.mPsdu[IEEE802154_DSN_OFFSET]))
{
otEXPECT_ACTION(validatePacketTimestamp(&packetDetails, length), dropPacket = true);
sReceiveAck.frame.mInfo.mRxInfo.mRssi = packetDetails.rssi;
sReceiveAck.frame.mInfo.mRxInfo.mLqi = packetDetails.lqi;
sReceiveAck.iid = iid;
updateRxFrameTimestamp(true, packetDetails.timeReceived.packetTime);
// Processing the ACK frame in ISR context avoids the Tx state to be messed up,
// in case the Rx FIFO queue gets wiped out in a DMP situation.
setInternalFlag(EVENT_TX_SUCCESS, true);
setInternalFlag(FLAG_WAITING_FOR_ACK | FLAG_ONGOING_TX_DATA | EVENT_SCHEDULED_TX_STARTED, false);
framePendingInAck = ((macFcf & IEEE802154_FRAME_FLAG_FRAME_PENDING) != 0);
(void)handlePhyStackEvent(SL_RAIL_UTIL_IEEE802154_STACK_EVENT_TX_ACK_RECEIVED, (uint32_t)framePendingInAck);
if (txIsDataRequest() && framePendingInAck)
{
emPendingData = true;
}
}
// Yield the radio upon receiving an ACK as long as it is not related to
// a data request.
if (!txIsDataRequest())
{
RAIL_YieldRadio(gRailHandle);
}
}
else
{
otEXPECT_ACTION(sPromiscuous || (length >= IEEE802154_MIN_DATA_LENGTH), dropPacket = true);
otEXPECT_ACTION(validatePacketTimestamp(&packetDetails, length), dropPacket = true);
// Drop the packet if queue is full.
otEXPECT_ACTION(!queueIsFull(&sRxPacketQueue), dropPacket = true);
// Allocate a block from memory pool for the received packet.
status = sl_memory_pool_alloc(&sRxPacketMemPoolHandle, (void **)&rxPacketBuf);
// Drop the packet if no more memory block present in the pool to store it.
otEXPECT_ACTION(status == SL_STATUS_OK && rxPacketBuf != NULL, dropPacket = true);
// read packet
RAIL_CopyRxPacket(rxPacketBuf->psdu, &packetInfo);
rxPacketBuf->packetInfo.length = (uint8_t)length;
rxPacketBuf->packetInfo.channel = (uint8_t)packetDetails.channel;
rxPacketBuf->packetInfo.rssi = packetDetails.rssi;
rxPacketBuf->packetInfo.lqi = packetDetails.lqi;
rxPacketBuf->packetInfo.timestamp = packetDetails.timeReceived.packetTime;
rxPacketBuf->packetInfo.iid = iid;
// Queue the rx packet or drop it if queueing fails and free the memory block.
isRxPacketQueued = queueAdd(&sRxPacketQueue, (void *)rxPacketBuf);
otEXPECT_ACTION(isRxPacketQueued, dropPacket = true);
#if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT
railDebugCounters.mRailPlatRadioReceiveProcessedCount++;
#endif
if (macFcf & IEEE802154_FRAME_FLAG_ACK_REQUIRED)
{
(void)handlePhyStackEvent((RAIL_IsRxAutoAckPaused(gRailHandle)
? SL_RAIL_UTIL_IEEE802154_STACK_EVENT_RX_ACK_BLOCKED
: SL_RAIL_UTIL_IEEE802154_STACK_EVENT_RX_ACKING),
(uint32_t)isReceivingFrame());
setInternalFlag(FLAG_ONGOING_TX_ACK, true);
}
else
{
(void)handlePhyStackEvent(SL_RAIL_UTIL_IEEE802154_STACK_EVENT_RX_ENDED, (uint32_t)isReceivingFrame());
// We received a frame that does not require an ACK as result of a data
// poll: we yield the radio here.
if (emPendingData)
{
RAIL_YieldRadio(gRailHandle);
emPendingData = false;
}
}
}
exit:
if (dropPacket)
{
(void)handlePhyStackEvent(SL_RAIL_UTIL_IEEE802154_STACK_EVENT_RX_CORRUPTED, (uint32_t)isReceivingFrame());
IgnoreError(sl_memory_pool_free(&sRxPacketMemPoolHandle, rxPacketBuf));
}
}
SL_CODE_CLASSIFY(SL_CODE_COMPONENT_OT_PLATFORM_ABSTRACTION, SL_CODE_CLASS_TIME_CRITICAL)
static void packetSentCallback(bool isAck)
{
if (isAck)
{
// We successfully sent out an ACK.
setInternalFlag(FLAG_ONGOING_TX_ACK, false);
// We acked the packet we received after a poll: we can yield now.
if (emPendingData)
{
RAIL_YieldRadio(gRailHandle);
emPendingData = false;
}
}
else if (getInternalFlag(FLAG_ONGOING_TX_DATA))
{
setInternalFlag(FLAG_CURRENT_TX_USE_CSMA, false);
if (txWaitingForAck())
{
setInternalFlag(FLAG_WAITING_FOR_ACK, true);
(void)handlePhyStackEvent(SL_RAIL_UTIL_IEEE802154_STACK_EVENT_TX_ACK_WAITING, 0U);
}
else
{
(void)handlePhyStackEvent(SL_RAIL_UTIL_IEEE802154_STACK_EVENT_TX_ENDED, 0U);
RAIL_YieldRadio(gRailHandle);
setInternalFlag(EVENT_TX_SUCCESS, true);
// Broadcast packet clear the ONGOING flag here.
setInternalFlag(FLAG_ONGOING_TX_DATA | EVENT_SCHEDULED_TX_STARTED, false);
}
#if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT
railDebugCounters.mRailEventPacketSent++;
#endif
}
}
SL_CODE_CLASSIFY(SL_CODE_COMPONENT_OT_PLATFORM_ABSTRACTION, SL_CODE_CLASS_TIME_CRITICAL)
static void txFailedCallback(bool isAck, uint32_t status)
{
if (isAck)
{
setInternalFlag(FLAG_ONGOING_TX_ACK, false);
}
else if (getInternalFlag(FLAG_ONGOING_TX_DATA))
{
if (status == EVENT_TX_CCA_FAILED)
{
setInternalFlag(EVENT_TX_CCA_FAILED, true);
setInternalFlag(FLAG_CURRENT_TX_USE_CSMA, false);
#if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT
railDebugCounters.mRailEventChannelBusy++;
#endif
}
else
{
setInternalFlag(EVENT_TX_FAILED, true);
#if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT
railDebugCounters.mRailEventTxAbort++;
#endif
}
setInternalFlag((FLAG_ONGOING_TX_DATA | FLAG_WAITING_FOR_ACK | EVENT_SCHEDULED_TX_STARTED), false);
RAIL_YieldRadio(gRailHandle);
}
}
SL_CODE_CLASSIFY(SL_CODE_COMPONENT_OT_PLATFORM_ABSTRACTION, SL_CODE_CLASS_TIME_CRITICAL)
static void ackTimeoutCallback(void)
{
OT_ASSERT(txWaitingForAck());
OT_ASSERT(getInternalFlag(FLAG_WAITING_FOR_ACK));
setInternalFlag(EVENT_TX_NO_ACK, true);
setInternalFlag(FLAG_ONGOING_TX_DATA | EVENT_SCHEDULED_TX_STARTED, false);
#if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT
railDebugCounters.mRailEventNoAck++;
#endif
#ifdef SL_CATALOG_RAIL_UTIL_ANT_DIV_PRESENT
// If antenna diversity is enabled toggle the selected antenna.
sl_rail_util_ant_div_toggle_tx_antenna();
#endif // SL_CATALOG_RAIL_UTIL_ANT_DIV_PRESENT
// TO DO: Check if we have an OT function that
// provides the number of mac retry attempts left
(void)handlePhyStackEvent(SL_RAIL_UTIL_IEEE802154_STACK_EVENT_TX_ACK_TIMEDOUT, 0);
setInternalFlag(FLAG_WAITING_FOR_ACK, false);
RAIL_YieldRadio(gRailHandle);
emPendingData = false;
}
SL_CODE_CLASSIFY(SL_CODE_COMPONENT_OT_PLATFORM_ABSTRACTION, SL_CODE_CLASS_TIME_CRITICAL)
static void schedulerEventCallback(RAIL_Handle_t aRailHandle)
{
RAIL_SchedulerStatus_t status = RAIL_GetSchedulerStatus(aRailHandle);
switch (status)
{
case RAIL_SCHEDULER_STATUS_SCHEDULE_FAIL:
case RAIL_SCHEDULER_STATUS_CCA_CSMA_TX_FAIL:
case RAIL_SCHEDULER_STATUS_CCA_LBT_TX_FAIL:
case RAIL_SCHEDULER_STATUS_SINGLE_TX_FAIL:
case RAIL_SCHEDULER_STATUS_SCHEDULED_TX_FAIL:
case RAIL_SCHEDULER_STATUS_UNSUPPORTED:
case RAIL_SCHEDULER_STATUS_SCHEDULED_RX_FAIL:
case RAIL_SCHEDULER_STATUS_INTERNAL_ERROR:
case RAIL_SCHEDULER_STATUS_TASK_FAIL:
case RAIL_SCHEDULER_STATUS_EVENT_INTERRUPTED:
if (getInternalFlag(FLAG_ONGOING_TX_ACK))
{
(void)handlePhyStackEvent(SL_RAIL_UTIL_IEEE802154_STACK_EVENT_RX_ACK_ABORTED, (uint32_t)isReceivingFrame());
txFailedCallback(true, EVENT_TX_FAILED);
}
// We were in the process of TXing a data frame, treat it as a CCA_FAIL.
if (getInternalFlag(FLAG_ONGOING_TX_DATA))
{
(void)handlePhyStackEvent(SL_RAIL_UTIL_IEEE802154_STACK_EVENT_TX_BLOCKED, (uint32_t)txWaitingForAck());
txFailedCallback(false, EVENT_TX_CCA_FAILED);
}
// We are waiting for an ACK: we will never get the ACK we were waiting for.
// We want to call ackTimeoutCallback() only if the PACKET_SENT event
// already fired (which would clear the FLAG_ONGOING_TX_DATA flag).
if (getInternalFlag(FLAG_WAITING_FOR_ACK))
{
ackTimeoutCallback();
}
#if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT
railDebugCounters.mRailEventSchedulerStatusError++;
#endif
break;
}
}
SL_CODE_CLASSIFY(SL_CODE_COMPONENT_OT_PLATFORM_ABSTRACTION, SL_CODE_CLASS_TIME_CRITICAL)
static void RAILCb_Generic(RAIL_Handle_t aRailHandle, RAIL_Events_t aEvents)
{
#ifdef SL_CATALOG_RAIL_UTIL_IEEE802154_STACK_EVENT_PRESENT
if (aEvents & (RAIL_EVENT_RX_SYNC1_DETECT | RAIL_EVENT_RX_SYNC2_DETECT))
{
(void)handlePhyStackEvent(SL_RAIL_UTIL_IEEE802154_STACK_EVENT_RX_STARTED, (uint32_t)isReceivingFrame());
}
#endif // SL_CATALOG_RAIL_UTIL_IEEE802154_STACK_EVENT_PRESENT
#ifdef SL_CATALOG_RAIL_UTIL_COEX_PRESENT
if (aEvents & RAIL_EVENT_SIGNAL_DETECTED)
{
(void)handlePhyStackEvent(SL_RAIL_UTIL_IEEE802154_STACK_EVENT_SIGNAL_DETECTED, 0U);
}
#endif // SL_CATALOG_RAIL_UTIL_COEX_PRESENT
if ((aEvents & RAIL_EVENT_IEEE802154_DATA_REQUEST_COMMAND)
#ifdef SL_CATALOG_RAIL_UTIL_COEX_PRESENT
&& !RAIL_IsRxAutoAckPaused(aRailHandle)
#endif // SL_CATALOG_RAIL_UTIL_COEX_PRESENT
)
{
dataRequestCommandCallback(aRailHandle);
}
#ifdef SL_CATALOG_RAIL_UTIL_IEEE802154_STACK_EVENT_PRESENT
if (aEvents & RAIL_EVENT_RX_FILTER_PASSED)
{
(void)handlePhyStackEvent(SL_RAIL_UTIL_IEEE802154_STACK_EVENT_RX_ACCEPTED, (uint32_t)isReceivingFrame());
}
#endif // SL_CATALOG_RAIL_UTIL_IEEE802154_STACK_EVENT_PRESENT
if (aEvents & RAIL_EVENT_TX_PACKET_SENT)
{
packetSentCallback(false);
}
else if (aEvents & RAIL_EVENT_TX_CHANNEL_BUSY)
{
(void)handlePhyStackEvent(SL_RAIL_UTIL_IEEE802154_STACK_EVENT_TX_BLOCKED, (uint32_t)txWaitingForAck());
txFailedCallback(false, EVENT_TX_CCA_FAILED);
}
else if (aEvents & RAIL_EVENT_TX_BLOCKED)
{
(void)handlePhyStackEvent(SL_RAIL_UTIL_IEEE802154_STACK_EVENT_TX_BLOCKED, (uint32_t)txWaitingForAck());
txFailedCallback(false, EVENT_TX_FAILED);
}
else if (aEvents & (RAIL_EVENT_TX_UNDERFLOW | RAIL_EVENT_TX_ABORTED))
{
(void)handlePhyStackEvent(SL_RAIL_UTIL_IEEE802154_STACK_EVENT_TX_ABORTED, (uint32_t)txWaitingForAck());
txFailedCallback(false, EVENT_TX_FAILED);
}
else
{
// Pre-completion aEvents are processed in their logical order:
#ifdef SL_CATALOG_RAIL_UTIL_IEEE802154_STACK_EVENT_PRESENT
if (aEvents & RAIL_EVENT_TX_START_CCA)
{
// We are starting RXWARM for a CCA check
(void)handlePhyStackEvent(SL_RAIL_UTIL_IEEE802154_STACK_EVENT_TX_CCA_SOON, 0U);
}
if (aEvents & RAIL_EVENT_TX_CCA_RETRY)
{
// We failed a CCA check and need to retry
(void)handlePhyStackEvent(SL_RAIL_UTIL_IEEE802154_STACK_EVENT_TX_CCA_BUSY, 0U);
}
if (aEvents & RAIL_EVENT_TX_CHANNEL_CLEAR)
{
// We're going on-air
(void)handlePhyStackEvent(SL_RAIL_UTIL_IEEE802154_STACK_EVENT_TX_STARTED, 0U);
}
#endif // SL_CATALOG_RAIL_UTIL_IEEE802154_STACK_EVENT_PRESENT
}
#if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
// Note: RAIL_EVENT_SCHEDULED_TX_STARTED and RAIL_EVENT_SCHEDULED_RX_STARTED have the
// same numerical value because one cannot schedule both RX and TX simultaneously.
// Have to take due care to check for these statuses depending on whether we're
// scheduling a transmit or receive. Therefore, we're using an internal flag
// 'FLAG_SCHEDULED_RX_PENDING' to track our scheduled-receive state.
if (getInternalFlag(FLAG_SCHEDULED_RX_PENDING))
{
if (aEvents & RAIL_EVENT_SCHEDULED_RX_STARTED)
{
setInternalFlag(EVENT_SCHEDULED_RX_STARTED, true);
}
// Once done with scheduled receive, clear internal scheduled-rx flag and idle.
// If we miss a scheduled receive, let application schedule another.
if (aEvents & RAIL_EVENT_RX_SCHEDULED_RX_END || aEvents & RAIL_EVENT_RX_SCHEDULED_RX_MISSED)
{
setInternalFlag(FLAG_SCHEDULED_RX_PENDING | EVENT_SCHEDULED_RX_STARTED, false);
radioSetIdle();
}
}
else
{
if (aEvents & RAIL_EVENT_SCHEDULED_TX_STARTED)
{
setInternalFlag(EVENT_SCHEDULED_TX_STARTED, true);
#if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT
railDebugCounters.mRailEventsScheduledTxStartedCount++;
#endif
}
else if (aEvents & RAIL_EVENT_TX_SCHEDULED_TX_MISSED)
{
txFailedCallback(false, EVENT_TX_SCHEDULER_ERROR);
}
}
#endif // OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
if (aEvents & RAIL_EVENT_RX_PACKET_RECEIVED)
{
packetReceivedCallback();
#if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT
railDebugCounters.mRailEventPacketReceived++;
#endif
}
#ifdef SL_CATALOG_RAIL_UTIL_IEEE802154_STACK_EVENT_PRESENT
if (aEvents & RAIL_EVENT_RX_FRAME_ERROR)
{
(void)handlePhyStackEvent(SL_RAIL_UTIL_IEEE802154_STACK_EVENT_RX_CORRUPTED, (uint32_t)isReceivingFrame());
}
// The following 3 events cause us to not receive a packet
if (aEvents & (RAIL_EVENT_RX_PACKET_ABORTED | RAIL_EVENT_RX_ADDRESS_FILTERED | RAIL_EVENT_RX_FIFO_OVERFLOW))
{
(void)handlePhyStackEvent(SL_RAIL_UTIL_IEEE802154_STACK_EVENT_RX_FILTERED, (uint32_t)isReceivingFrame());
}
#endif // SL_CATALOG_RAIL_UTIL_IEEE802154_STACK_EVENT_PRESENT
if (aEvents & RAIL_EVENT_TXACK_PACKET_SENT)
{
(void)handlePhyStackEvent(SL_RAIL_UTIL_IEEE802154_STACK_EVENT_RX_ACK_SENT, (uint32_t)isReceivingFrame());
packetSentCallback(true);
}
if (aEvents & (RAIL_EVENT_TXACK_ABORTED | RAIL_EVENT_TXACK_UNDERFLOW))
{
(void)handlePhyStackEvent(SL_RAIL_UTIL_IEEE802154_STACK_EVENT_RX_ACK_ABORTED, (uint32_t)isReceivingFrame());
txFailedCallback(true, 0xFF);
}
if (aEvents & RAIL_EVENT_TXACK_BLOCKED)
{
(void)handlePhyStackEvent(SL_RAIL_UTIL_IEEE802154_STACK_EVENT_RX_ACK_BLOCKED, (uint32_t)isReceivingFrame());
txFailedCallback(true, 0xFF);
}
// Deal with ACK timeout after possible RX completion in case RAIL
// notifies us of the ACK and the timeout simultaneously -- we want
// the ACK to win over the timeout.
if (aEvents & RAIL_EVENT_RX_ACK_TIMEOUT)
{
if (getInternalFlag(FLAG_WAITING_FOR_ACK))
{
ackTimeoutCallback();
}
}
if (aEvents & RAIL_EVENT_CONFIG_UNSCHEDULED)
{
(void)handlePhyStackEvent(SL_RAIL_UTIL_IEEE802154_STACK_EVENT_RX_IDLED, 0U);
#if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT
railDebugCounters.mRailEventConfigUnScheduled++;
#endif
}
if (aEvents & RAIL_EVENT_CONFIG_SCHEDULED)
{
#if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT
railDebugCounters.mRailEventConfigScheduled++;
#endif
}
if (aEvents & RAIL_EVENT_SCHEDULER_STATUS)
{
schedulerEventCallback(aRailHandle);
}
if (aEvents & RAIL_EVENT_CAL_NEEDED)
{
RAIL_Status_t status;
status = RAIL_Calibrate(aRailHandle, NULL, RAIL_CAL_ALL_PENDING);
// TODO: Non-RTOS DMP case fails
#if (!defined(SL_CATALOG_BLUETOOTH_PRESENT) || defined(SL_CATALOG_KERNEL_PRESENT))
// TEMPORARY - this asserts on Mux - OT_ASSERT(status == RAIL_STATUS_NO_ERROR);
OT_UNUSED_VARIABLE(status);
#else
OT_UNUSED_VARIABLE(status);
#endif
#if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT
railDebugCounters.mRailEventCalNeeded++;
#endif
}
// scheduled and unscheduled config events happen very often,
// especially in a DMP situation where there is an active BLE connection.
// Waking up the OT RTOS task on every one of these occurrences causes
// a lower priority Serial task to starve and makes it appear like a code lockup
// There is no reason to wake the OT task for these events!
if (!(aEvents & RAIL_EVENT_CONFIG_SCHEDULED) && !(aEvents & RAIL_EVENT_CONFIG_UNSCHEDULED))
{
otSysEventSignalPending();
}
}
//------------------------------------------------------------------------------
// Main thread packet handling
SL_CODE_CLASSIFY(SL_CODE_COMPONENT_OT_PLATFORM_ABSTRACTION, SL_CODE_CLASS_TIME_CRITICAL)
static bool validatePacketDetails(RAIL_RxPacketHandle_t packetHandle,
RAIL_RxPacketDetails_t *pPacketDetails,
RAIL_RxPacketInfo_t *pPacketInfo,
uint16_t *packetLength)
{
bool pktValid = true;
RAIL_Status_t rStatus;
#if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT
rxDebugStep = 0;
#endif
rStatus = RAIL_GetRxPacketDetailsAlt(gRailHandle, packetHandle, pPacketDetails);
otEXPECT_ACTION(rStatus == RAIL_STATUS_NO_ERROR, pktValid = false);
#if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT
rxDebugStep++;
#endif
otEXPECT_ACTION(isFilterMaskValid(pPacketInfo->filterMask), pktValid = false);
#if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT
rxDebugStep++;
#endif
// RAIL's packetBytes includes the (1 or 2 byte) PHY header but not the 2-byte CRC.
// We want *packetLength to match the PHY header length so we add 2 for CRC
// and subtract the PHY header size.
*packetLength = pPacketInfo->packetBytes + 2U - PHY_HEADER_SIZE;
if (PHY_HEADER_SIZE == 1)
{
otEXPECT_ACTION(*packetLength == pPacketInfo->firstPortionData[0], pktValid = false);
}
else
{
uint8_t lengthByte =
((pPacketInfo->firstPortionBytes > 1) ? pPacketInfo->firstPortionData[1] : pPacketInfo->lastPortionData[0]);
otEXPECT_ACTION(*packetLength == (uint16_t)(__RBIT(lengthByte) >> 24), pktValid = false);
}
#if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT
rxDebugStep++;
#endif
// check the length validity of recv packet; RAIL should take care of this.
otEXPECT_ACTION((*packetLength >= IEEE802154_MIN_LENGTH && *packetLength <= IEEE802154_MAX_LENGTH),
pktValid = false);
#if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT
rxDebugStep++;
#endif
exit:
#if (OPENTHREAD_CONFIG_LOG_LEVEL == OT_LOG_LEVEL_DEBG)
if (!pktValid)
{
otLogDebgPlat("RX Pkt Invalid: rStatus=0x%lX, filterMask=0x%2X, pktLen=%i",
rStatus,
pPacketInfo->filterMask,
*packetLength);
#if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT
otLogDebgPlat("RX debug step=%i", rxDebugStep);
#endif
}
#endif
return pktValid;
}
SL_CODE_CLASSIFY(SL_CODE_COMPONENT_OT_PLATFORM_ABSTRACTION, SL_CODE_CLASS_TIME_CRITICAL)
static bool validatePacketTimestamp(RAIL_RxPacketDetails_t *pPacketDetails, uint16_t packetLength)
{
bool rxTimestampValid = true;
// Get the timestamp when the SFD was received
otEXPECT_ACTION(pPacketDetails->timeReceived.timePosition != RAIL_PACKET_TIME_INVALID, rxTimestampValid = false);
// + PHY HEADER SIZE for PHY header
// We would not need this if PHR is not included and we want the MHR
pPacketDetails->timeReceived.totalPacketBytes = packetLength + PHY_HEADER_SIZE;
otEXPECT_ACTION((RAIL_GetRxTimeSyncWordEndAlt(gRailHandle, pPacketDetails) == RAIL_STATUS_NO_ERROR),
rxTimestampValid = false);
exit:
return rxTimestampValid;
}
otError otPlatMultipanGetActiveInstance(otInstance **aInstance)
{
otError error = OT_ERROR_NOT_IMPLEMENTED;
OT_UNUSED_VARIABLE(aInstance);
return error;
}
otError otPlatMultipanSetActiveInstance(otInstance *aInstance, bool aCompletePending)
{
otError error = OT_ERROR_NOT_IMPLEMENTED;
OT_UNUSED_VARIABLE(aInstance);
OT_UNUSED_VARIABLE(aCompletePending);
return error;
}
SL_CODE_CLASSIFY(SL_CODE_COMPONENT_OT_PLATFORM_ABSTRACTION, SL_CODE_CLASS_TIME_CRITICAL)
static void updateRxFrameTimestamp(bool aIsAckFrame, RAIL_Time_t aTimestamp)
{
// Current time > sync-receive timestamp
// Therefore lower 32 bits of current time should always be greater than lower 32 bits
// of sync-rx timestamp unless there is a overflow. In such cases, we do not want to
// take overflow into consideration for sync-rx timestamp.
uint64_t railUsTimeNow = otPlatTimeGet();
uint32_t railUsTimerWraps = railUsTimeNow >> 32;
// Address multiple overflows, such as what would happen if the current time overflows
// from 0x00000001FFFFFFFF to 0x0000000200000000 (leave the higher 32 bits as 0)
if ((railUsTimeNow & 0xFFFFFFFF) <= aTimestamp)
{
railUsTimerWraps--;
}
if (aIsAckFrame)
{
sReceiveAck.frame.mInfo.mRxInfo.mTimestamp = aTimestamp + ((uint64_t)railUsTimerWraps << 32);
}
else
{
sReceive.frame.mInfo.mRxInfo.mTimestamp = aTimestamp + ((uint64_t)railUsTimerWraps << 32);
}
}
SL_CODE_CLASSIFY(SL_CODE_COMPONENT_OT_PLATFORM_ABSTRACTION, SL_CODE_CLASS_TIME_CRITICAL)
static otError skipRxPacketLengthBytes(RAIL_RxPacketInfo_t *pPacketInfo)
{
otError error = OT_ERROR_NONE;
otEXPECT_ACTION(pPacketInfo->firstPortionBytes > 0, error = OT_ERROR_FAILED);
pPacketInfo->firstPortionData += PHY_HEADER_SIZE;
pPacketInfo->packetBytes -= PHY_HEADER_SIZE;
if (PHY_HEADER_SIZE == 1 || pPacketInfo->firstPortionBytes > 1)
{
pPacketInfo->firstPortionBytes -= PHY_HEADER_SIZE;
}
else
{
pPacketInfo->firstPortionBytes = 0U;
// Increment lastPortionData to skip the second byte of the PHY header
otEXPECT_ACTION(pPacketInfo->lastPortionData != NULL, error = OT_ERROR_FAILED);
pPacketInfo->lastPortionData++;
}
exit:
return error;
}
// This function dequeues the rx-queue, move the content to sReceive buffer
// and return the memory block which was used to store the received packet.
// So that memory block can be freed after submitting the receiveDone Callback.
static rxBuffer *prepareNextRxPacketforCb(void)
{
rxBuffer *rxPacketBuf = (rxBuffer *)queueRemove(&sRxPacketQueue);
OT_ASSERT(rxPacketBuf != NULL);
uint8_t *psdu = rxPacketBuf->psdu;
// Check the reserved bits in the MAC header, then clear them.
// If we sent an enhanced ACK, check if it was secured.
// Set this flag only when the packet is really acknowledged with a secured enhanced ACK.
sReceive.frame.mInfo.mRxInfo.mAckedWithSecEnhAck = ((*psdu & IEEE802154_SECURED_OUTGOING_ENHANCED_ACK) != 0);
*psdu &= ~IEEE802154_SECURED_OUTGOING_ENHANCED_ACK;
// Check whether frame pendinng bit was set in the outgoing ACK.
// Set this flag only when the packet is really acknowledged with frame pending set.
sReceive.frame.mInfo.mRxInfo.mAckedWithFramePending = ((*psdu & IEEE802154_FRAME_PENDING_SET_IN_OUTGOING_ACK) != 0);
*psdu &= ~IEEE802154_FRAME_PENDING_SET_IN_OUTGOING_ACK;
sReceive.frame.mChannel = rxPacketBuf->packetInfo.channel;
sReceive.frame.mLength = rxPacketBuf->packetInfo.length;
sReceive.frame.mPsdu = rxPacketBuf->psdu;
sReceive.frame.mInfo.mRxInfo.mRssi = rxPacketBuf->packetInfo.rssi;
sLastRssi = rxPacketBuf->packetInfo.rssi;
sReceive.frame.mInfo.mRxInfo.mLqi = rxPacketBuf->packetInfo.lqi;
sLastLqi = rxPacketBuf->packetInfo.lqi;
sReceive.iid = rxPacketBuf->packetInfo.iid;
#if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2
// Use stored values for these
sReceive.frame.mInfo.mRxInfo.mAckKeyId = sMacKeys[sReceive.iid].ackKeyId;
sReceive.frame.mInfo.mRxInfo.mAckFrameCounter = sMacKeys[sReceive.iid].ackFrameCounter;
#endif
updateRxFrameTimestamp(false, rxPacketBuf->packetInfo.timestamp);
return rxPacketBuf;
}
static void processNextRxPacket(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
sReceiveError = OT_ERROR_NONE;
uint8_t interfaceId = INVALID_INTERFACE_INDEX;
otInstance *instance = NULL;
rxBuffer *rxPacketBuf = NULL;
rxPacketBuf = prepareNextRxPacketforCb();
// sReceive buffer gets populated from prepareNextRxPacketforCb.
interfaceId = sReceive.iid;
// Submit broadcast packet to all initilized instances.
do
{
#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE
instance = otPlatMultipanIidToInstance(interfaceId);
#else
instance = aInstance;
#endif
interfaceId++;
if (instance == NULL)
{
continue;
}
#if OPENTHREAD_CONFIG_DIAG_ENABLE
if (otPlatDiagModeGet())
{
otPlatDiagRadioReceiveDone(instance, &sReceive.frame, sReceiveError);
}
else
#endif // OPENTHREAD_CONFIG_DIAG_ENABLE
{
bool isGpPacket = sl_gp_intf_is_gp_pkt(&sReceive.frame);
#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE
// For multipan RCP, continue normal processing.
OT_UNUSED_VARIABLE(isGpPacket);
#else
// Else, we should not receive GP packets.
otEXPECT(!isGpPacket);
#endif
#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE
(void)sl_gp_intf_should_buffer_pkt(instance, &sReceive.frame, true);
#endif
otPlatRadioReceiveDone(instance, &sReceive.frame, sReceiveError);
}
} while (sReceive.iid == RADIO_BCAST_IID && interfaceId < RADIO_INTERFACE_COUNT);
#if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT
railDebugCounters.mRailPlatRadioReceiveDoneCbCount++;
#endif
#if !OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE
exit:
#endif
IgnoreError(sl_memory_pool_free(&sRxPacketMemPoolHandle, rxPacketBuf));
otSysEventSignalPending();
}
static void processRxPackets(otInstance *aInstance)
{
while (!queueIsEmpty(&sRxPacketQueue))
{
processNextRxPacket(aInstance);
}
}
static void processTxComplete(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
otError txStatus;
otRadioFrame *ackFrame = NULL;
if (getInternalFlag(RADIO_TX_EVENTS))
{
if (getInternalFlag(EVENT_TX_SUCCESS))
{
txStatus = OT_ERROR_NONE;
if (sCurrentTxPacket->frame.mPsdu[0] & IEEE802154_FRAME_FLAG_ACK_REQUIRED)
{
ackFrame = &sReceiveAck.frame;
}
setInternalFlag(EVENT_TX_SUCCESS, false);
}
else if (getInternalFlag(EVENT_TX_CCA_FAILED))
{
txStatus = OT_ERROR_CHANNEL_ACCESS_FAILURE;
setInternalFlag(EVENT_TX_CCA_FAILED, false);
}
else if (getInternalFlag(EVENT_TX_NO_ACK))
{
txStatus = OT_ERROR_NO_ACK;
setInternalFlag(EVENT_TX_NO_ACK, false);
}
else
{
txStatus = OT_ERROR_ABORT;
setInternalFlag(EVENT_TX_FAILED, false);
}
if (txStatus != OT_ERROR_NONE)
{
otLogDebgPlat("Transmit failed ErrorCode=%d", txStatus);
}
#if OPENTHREAD_CONFIG_DIAG_ENABLE
if (otPlatDiagModeGet())
{
#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE
otPlatDiagRadioTransmitDone(otPlatMultipanIidToInstance(sCurrentTxPacket->iid),
&sCurrentTxPacket->frame,
txStatus);
#else
otPlatDiagRadioTransmitDone(aInstance, &sCurrentTxPacket->frame, txStatus);
#endif
}
else
#endif
{
// Clear any internally-set txDelays so future transmits are not affected.
sCurrentTxPacket->frame.mInfo.mTxInfo.mTxDelayBaseTime = 0;
sCurrentTxPacket->frame.mInfo.mTxInfo.mTxDelay = 0;
#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE
otPlatRadioTxDone(otPlatMultipanIidToInstance(sCurrentTxPacket->iid),
&sCurrentTxPacket->frame,
ackFrame,
txStatus);
#else
otPlatRadioTxDone(aInstance, &sCurrentTxPacket->frame, ackFrame, txStatus);
#endif
}
#if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT
railDebugCounters.mRailPlatRadioTxDoneCbCount++;
#endif
otSysEventSignalPending();
}
}
#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE
static inline void processPendingCommands(void)
{
// Check and process pending transmit and energy scan commands if radio is not busy.
if (!queueIsEmpty(&sPendingCommandQueue) && (!isRadioTransmittingOrScanning()))
{
// Dequeue the pending command
pendingCommandEntry *pendingCommand = (pendingCommandEntry *)queueRemove(&sPendingCommandQueue);
OT_ASSERT(pendingCommand != NULL);
uint8_t iid = pendingCommand->iid;
switch (pendingCommand->cmdType)
{
case kPendingCommandTypeTransmit:
otPlatRadioTransmit(otPlatMultipanIidToInstance(iid), pendingCommand->request.txFrame);
break;
case kPendingCommandTypeEnergyScan:
otPlatRadioEnergyScan(otPlatMultipanIidToInstance(iid),
pendingCommand->request.energyScan.scanChannel,
pendingCommand->request.energyScan.scanDuration);
break;
default:
OT_ASSERT(false);
break;
}
// Free the allocated memory.
sl_free(pendingCommand);
}
}
#endif // OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE
void efr32RadioProcess(otInstance *aInstance)
{
(void)handlePhyStackEvent(SL_RAIL_UTIL_IEEE802154_STACK_EVENT_TICK, 0U);
// We should process the received packet first. Adding it at the end of this function,
// will delay the stack notification until the next call to efr32RadioProcess()
processRxPackets(aInstance);
processTxComplete(aInstance);
if (sEnergyScanMode == ENERGY_SCAN_MODE_ASYNC && sEnergyScanStatus == ENERGY_SCAN_STATUS_COMPLETED)
{
sEnergyScanStatus = ENERGY_SCAN_STATUS_IDLE;
OT_ASSERT(sEnergyScanActiveInterface != INVALID_INTERFACE_INDEX);
#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE
otPlatRadioEnergyScanDone(otPlatMultipanIidToInstance(sEnergyScanActiveInterface), sEnergyScanResultDbm);
#else
otPlatRadioEnergyScanDone(aInstance, sEnergyScanResultDbm);
#endif
sEnergyScanActiveInterface = INVALID_INTERFACE_INDEX;
otSysEventSignalPending();
#if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT
railDebugCounters.mRailEventEnergyScanCompleted++;
#endif
}
#if OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE
processPendingCommands();
#endif // OPENTHREAD_CONFIG_MULTIPAN_RCP_ENABLE
}
//------------------------------------------------------------------------------
// Antenna Diversity, Wifi coexistence and Runtime PHY select support
RAIL_Status_t efr32RadioSetCcaMode(uint8_t aMode)
{
return RAIL_IEEE802154_ConfigCcaMode(gRailHandle, aMode);
}
RAIL_IEEE802154_PtiRadioConfig_t efr32GetPtiRadioConfig(void)
{
return (RAIL_IEEE802154_GetPtiRadioConfig(gRailHandle));
}
#ifdef SL_CATALOG_RAIL_UTIL_IEEE802154_PHY_SELECT_PRESENT
otError setRadioState(otRadioState state)
{
otError error = OT_ERROR_NONE;
// Defer idling the radio if we have an ongoing TX task
otEXPECT_ACTION(!getInternalFlag(FLAG_ONGOING_TX_DATA), error = OT_ERROR_FAILED);
switch (state)
{
case OT_RADIO_STATE_RECEIVE:
otEXPECT_ACTION(radioSetRx(sReceive.frame.mChannel) == OT_ERROR_NONE, error = OT_ERROR_FAILED);
break;
case OT_RADIO_STATE_SLEEP:
radioSetIdle();
break;
default:
error = OT_ERROR_FAILED;
}
exit:
return error;
}
void sl_ot_update_active_radio_config(void)
{
CORE_DECLARE_IRQ_STATE;
CORE_ENTER_ATOMIC();
// Proceed with PHY selection only if 2.4 GHz band is used
otEXPECT(sBandConfig.mChannelConfig == NULL);
otRadioState currentState = otPlatRadioGetState(NULL);
otEXPECT(setRadioState(OT_RADIO_STATE_SLEEP) == OT_ERROR_NONE);
sl_rail_util_ieee802154_config_radio(gRailHandle);
otEXPECT(setRadioState(currentState) == OT_ERROR_NONE);
exit:
CORE_EXIT_ATOMIC();
return;
}
#endif // SL_CATALOG_RAIL_UTIL_IEEE802154_PHY_SELECT_PRESENT
#ifdef SL_CATALOG_RAIL_UTIL_ANT_DIV_PRESENT
void efr32AntennaConfigInit(void)
{
RAIL_Status_t status;
sl_rail_util_ant_div_init();
status = sl_rail_util_ant_div_update_antenna_config();
OT_ASSERT(status == RAIL_STATUS_NO_ERROR);
}
#endif // SL_CATALOG_RAIL_UTIL_ANT_DIV_PRESENT
#ifdef SL_CATALOG_RAIL_UTIL_IEEE802154_STACK_EVENT_PRESENT
static void changeDynamicEvents(void)
{
/* clang-format off */
const RAIL_Events_t eventMask = RAIL_EVENTS_NONE
| RAIL_EVENT_RX_SYNC1_DETECT
| RAIL_EVENT_RX_SYNC2_DETECT
| RAIL_EVENT_RX_FRAME_ERROR
| RAIL_EVENT_RX_FIFO_OVERFLOW
| RAIL_EVENT_RX_ADDRESS_FILTERED
| RAIL_EVENT_RX_PACKET_ABORTED
| RAIL_EVENT_RX_FILTER_PASSED
| RAIL_EVENT_TX_CHANNEL_CLEAR
| RAIL_EVENT_TX_CCA_RETRY
| RAIL_EVENT_TX_START_CCA
| RAIL_EVENT_SIGNAL_DETECTED;
/* clang-format on */
RAIL_Events_t eventValues = RAIL_EVENTS_NONE;
if (phyStackEventIsEnabled())
{
eventValues |= eventMask;
}
updateEvents(eventMask, eventValues);
}
#endif // SL_CATALOG_RAIL_UTIL_IEEE802154_STACK_EVENT_PRESENT
static void efr32PhyStackInit(void)
{
#ifdef SL_CATALOG_RAIL_UTIL_ANT_DIV_PRESENT
efr32AntennaConfigInit();
#endif // SL_CATALOG_RAIL_UTIL_ANT_DIV_PRESENT
#ifdef SL_CATALOG_RAIL_UTIL_COEX_PRESENT
efr32CoexInit();
#endif // SL_CATALOG_RAIL_UTIL_COEX_PRESENT
#ifdef SL_CATALOG_RAIL_UTIL_IEEE802154_STACK_EVENT_PRESENT
changeDynamicEvents();
#endif
}
#ifdef SL_CATALOG_RAIL_UTIL_COEX_PRESENT
static void emRadioEnableAutoAck(void)
{
CORE_DECLARE_IRQ_STATE;
CORE_ENTER_ATOMIC();
if (getInternalFlag(FLAG_RADIO_INIT_DONE))
{
if ((rhoActive >= RHO_INT_ACTIVE) // Internal always holds ACKs
|| ((rhoActive > RHO_INACTIVE)
&& ((sl_rail_util_coex_get_options() & SL_RAIL_UTIL_COEX_OPT_ACK_HOLDOFF)
!= SL_RAIL_UTIL_COEX_OPT_DISABLED)))
{
RAIL_PauseRxAutoAck(gRailHandle, true);
}
else
{
RAIL_PauseRxAutoAck(gRailHandle, false);
}
}
CORE_EXIT_ATOMIC();
}
static void emRadioEnablePta(bool enable)
{
halInternalInitPta();
// When PTA is enabled, we want to negate PTA_REQ as soon as an incoming
// frame is aborted, e.g. due to filtering. To do that we must turn off
// the TRACKABFRAME feature that's normally on to benefit sniffing on PTI.
RAIL_Status_t status = RAIL_ConfigRxOptions(gRailHandle,
RAIL_RX_OPTION_TRACK_ABORTED_FRAMES,
(enable ? RAIL_RX_OPTIONS_NONE : RAIL_RX_OPTION_TRACK_ABORTED_FRAMES));
OT_ASSERT(status == RAIL_STATUS_NO_ERROR);
}
static void efr32CoexInit(void)
{
#if SL_OPENTHREAD_COEX_COUNTER_ENABLE && defined(SL_CATALOG_RAIL_MULTIPLEXER_PRESENT)
sl_rail_mux_set_coex_counter_handler(gRailHandle, &sl_ot_coex_counter_on_event);
#else
sli_radio_coex_reset();
#endif // SL_OPENTHREAD_COEX_COUNTER_ENABLE && defined(SL_CATALOG_RAIL_MULTIPLEXER_PRESENT)
#if OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
#if defined(SL_RAIL_UTIL_COEX_REQ_GPIO) || defined(SL_RAIL_UTIL_COEX_REQ_PORT) || defined(SL_RAIL_UTIL_COEX_GNT_GPIO) \
|| defined(SL_RAIL_UTIL_COEX_GNT_PORT) || SL_RAIL_UTIL_COEX_RUNTIME_PHY_SELECT
sl_rail_util_ot_enable_coex_state_event_filter();
#endif
#endif // OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE
sl_rail_util_coex_options_t coexOptions = sl_rail_util_coex_get_options();
#if SL_OPENTHREAD_COEX_MAC_HOLDOFF_ENABLE
coexOptions |= SL_RAIL_UTIL_COEX_OPT_MAC_HOLDOFF;
#else
if (sl_rail_util_coex_get_radio_holdoff())
{
coexOptions |= SL_RAIL_UTIL_COEX_OPT_MAC_HOLDOFF;
}
#endif // SL_OPENTHREAD_COEX_MAC_HOLDOFF_ENABLE
sl_rail_util_coex_set_options(coexOptions);
emRadioEnableAutoAck(); // Might suspend AutoACK if RHO already in effect
emRadioEnablePta(sl_rail_util_coex_is_enabled());
}
// Managing radio transmission
static void onPtaGrantTx(sl_rail_util_coex_req_t ptaStatus)
{
// Only pay attention to first PTA Grant callback, ignore any further ones
if (ptaGntEventReported)
{
return;
}
ptaGntEventReported = true;
OT_ASSERT(ptaStatus == SL_RAIL_UTIL_COEX_REQCB_GRANTED);
// PTA is telling us we've gotten GRANT and should send ASAP *without* CSMA
setInternalFlag(FLAG_CURRENT_TX_USE_CSMA, false);
txCurrentPacket();
}
static void tryTxCurrentPacket(void)
{
OT_ASSERT(getInternalFlag(FLAG_ONGOING_TX_DATA));
ptaGntEventReported = false;
sl_rail_util_ieee802154_stack_event_t ptaStatus =
handlePhyStackEvent(SL_RAIL_UTIL_IEEE802154_STACK_EVENT_TX_PENDED_MAC, (uint32_t)&onPtaGrantTx);
if (ptaStatus == SL_RAIL_UTIL_IEEE802154_STACK_STATUS_SUCCESS)
{
// Normal case where PTA allows us to start the (CSMA) transmit below
txCurrentPacket();
}
else if (ptaStatus == SL_RAIL_UTIL_IEEE802154_STACK_STATUS_CB_PENDING)
{
// onPtaGrantTx() callback will take over (and might already have)
}
else if (ptaStatus == SL_RAIL_UTIL_IEEE802154_STACK_STATUS_HOLDOFF)
{
txFailedCallback(false, EVENT_TX_FAILED);
}
}
// Managing CCA Threshold
static void setCcaThreshold(void)
{
if (sCcaThresholdDbm == CCA_THRESHOLD_UNINIT)
{
sCcaThresholdDbm = CCA_THRESHOLD_DEFAULT;
}
CORE_DECLARE_IRQ_STATE;
CORE_ENTER_ATOMIC();
int8_t thresholddBm = sCcaThresholdDbm;
if (getInternalFlag(FLAG_RADIO_INIT_DONE))
{
if (rhoActive > RHO_INACTIVE)
{
thresholddBm = RAIL_RSSI_INVALID_DBM;
}
RAIL_Status_t status = RAIL_SetCcaThreshold(gRailHandle, thresholddBm);
OT_ASSERT(status == RAIL_STATUS_NO_ERROR);
}
CORE_EXIT_ATOMIC();
}
static void emRadioHoldOffInternalIsr(uint8_t active)
{
if (active != rhoActive)
{
rhoActive = active; // Update rhoActive early
if (getInternalFlag(FLAG_RADIO_INIT_DONE))
{
setCcaThreshold();
emRadioEnableAutoAck();
}
}
}
// External API used by Coex Component
SL_WEAK void emRadioHoldOffIsr(bool active)
{
emRadioHoldOffInternalIsr((uint8_t)active | (rhoActive & ~RHO_EXT_ACTIVE));
}
#if SL_OPENTHREAD_COEX_COUNTER_ENABLE
#ifdef SL_CATALOG_RAIL_MULTIPLEXER_PRESENT
static void sl_ot_coex_counter_on_event(sl_rail_util_coex_event_t event)
#else
void sl_rail_util_coex_counter_on_event(sl_rail_util_coex_event_t event)
#endif
{
otEXPECT(event < SL_RAIL_UTIL_COEX_EVENT_COUNT);
efr32RadioCoexCounters[event] += 1;
exit:
return;
}
void efr32RadioClearCoexCounters(void)
{
memset((void *)efr32RadioCoexCounters, 0, sizeof(efr32RadioCoexCounters));
}
#endif // SL_OPENTHREAD_COEX_COUNTER_ENABLE
#endif // SL_CATALOG_RAIL_UTIL_COEX_PRESENT
#if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT
void efr32ClearRadioCounters(void)
{
memset(&railDebugCounters, 0, sizeof(railDebugCounters));
}
#endif // RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT