| /* |
| * Copyright (c) 2020, 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-system.h" |
| #include <openthread/config.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 "common/logging.hpp" |
| #include "utils/code_utils.h" |
| #include "utils/mac_frame.h" |
| |
| #include "utils/soft_source_match_table.h" |
| |
| #include "antenna.h" |
| #include "board_config.h" |
| #include "em_core.h" |
| #include "em_system.h" |
| #include "ieee802154mac.h" |
| #include "openthread-core-efr32-config.h" |
| #include "pa_conversions_efr32.h" |
| #include "platform-band.h" |
| #include "rail.h" |
| #include "rail_config.h" |
| #include "rail_ieee802154.h" |
| |
| #ifdef SL_COMPONENT_CATALOG_PRESENT |
| #include "sl_component_catalog.h" |
| #endif // SL_COMPONENT_CATALOG_PRESENT |
| |
| #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" |
| #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 |
| //------------------------------------------------------------------------------ |
| // Enums, macros and static variables |
| |
| #define LOW_BYTE(n) ((uint8_t)((n)&0xFF)) |
| #define HIGH_BYTE(n) ((uint8_t)(LOW_BYTE((n) >> 8))) |
| |
| #define EFR32_RECEIVE_SENSITIVITY -100 // dBm |
| #define EFR32_RSSI_AVERAGING_TIME 16 // us |
| #define EFR32_RSSI_AVERAGING_TIMEOUT 300 // us |
| |
| // Internal flags |
| #define FLAG_RADIO_INIT_DONE 0x0001 |
| #define FLAG_ONGOING_TX_DATA 0x0002 |
| #define FLAG_ONGOING_TX_ACK 0x0004 |
| #define FLAG_WAITING_FOR_ACK 0x0008 |
| #define FLAG_SYMBOL_TIMER_RUNNING 0x0010 // Not used |
| #define FLAG_CURRENT_TX_USE_CSMA 0x0020 |
| #define FLAG_DATA_POLL_FRAME_PENDING_SET 0x0040 |
| #define FLAG_CALIBRATION_NEEDED 0x0080 // Not used |
| #define FLAG_IDLE_PENDING 0x0100 // Not used |
| |
| #define TX_COMPLETE_RESULT_SUCCESS 0x00 // Not used |
| #define TX_COMPLETE_RESULT_CCA_FAIL 0x01 |
| #define TX_COMPLETE_RESULT_OTHER_FAIL 0x02 |
| #define TX_COMPLETE_RESULT_NONE 0xFF // Not used |
| |
| #define TX_WAITING_FOR_ACK 0x00 |
| #define TX_NO_ACK 0x01 |
| |
| #define ONGOING_TX_FLAGS (FLAG_ONGOING_TX_DATA | FLAG_ONGOING_TX_ACK) |
| |
| #define QUARTER_DBM_IN_DBM 4 |
| #define US_IN_MS 1000 |
| |
| enum |
| { |
| #if RADIO_CONFIG_2P4GHZ_OQPSK_SUPPORT && RADIO_CONFIG_915MHZ_OQPSK_SUPPORT |
| EFR32_NUM_BAND_CONFIGS = 2, |
| #else |
| EFR32_NUM_BAND_CONFIGS = 1, |
| #endif |
| }; |
| |
| // 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; |
| |
| static volatile energyScanStatus sEnergyScanStatus; |
| static volatile int8_t sEnergyScanResultDbm; |
| static energyScanMode sEnergyScanMode; |
| |
| static bool sIsSrcMatchEnabled = false; |
| |
| // Receive |
| static uint8_t sReceivePsdu[IEEE802154_MAX_LENGTH]; |
| static uint8_t sReceiveAckPsdu[IEEE802154_ACK_LENGTH]; |
| static otRadioFrame sReceiveFrame; |
| static otRadioFrame sReceiveAckFrame; |
| static otError sReceiveError; |
| |
| // Transmit |
| static otRadioFrame sTransmitFrame; |
| static uint8_t sTransmitPsdu[IEEE802154_MAX_LENGTH]; |
| static volatile otError sTransmitError; |
| static volatile bool sTransmitBusy = false; |
| static otRadioFrame * sTxFrame = NULL; |
| |
| // Radio |
| #define CCA_THRESHOLD_UNINIT 127 |
| #define CCA_THRESHOLD_DEFAULT -75 // dBm - default for 2.4GHz 802.15.4 |
| |
| static bool sPromiscuous = false; |
| static otRadioState sState = OT_RADIO_STATE_DISABLED; |
| |
| static efr32CommonConfig sCommonConfig; |
| static efr32BandConfig sBandConfig[EFR32_NUM_BAND_CONFIGS]; |
| static efr32BandConfig * sCurrentBandConfig = NULL; |
| |
| static int8_t sCcaThresholdDbm = CCA_THRESHOLD_DEFAULT; |
| |
| #if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT |
| static efr32RadioCounters sRailDebugCounters; |
| #endif |
| |
| // RAIL |
| RAIL_Handle_t emPhyRailHandle; |
| #define gRailHandle emPhyRailHandle |
| |
| static const RAIL_IEEE802154_Config_t sRailIeee802154Config = { |
| NULL, // addresses |
| { |
| // ackConfig |
| true, // ackConfig.enable |
| 672, // ackConfig.ackTimeout |
| { |
| // ackConfig.rxTransitions |
| RAIL_RF_STATE_RX, // ackConfig.rxTransitions.success |
| RAIL_RF_STATE_RX, // ackConfig.rxTransitions.error |
| }, |
| { |
| // ackConfig.txTransitions |
| RAIL_RF_STATE_RX, // ackConfig.txTransitions.success |
| RAIL_RF_STATE_RX, // ackConfig.txTransitions.error |
| }, |
| }, |
| { |
| // timings |
| 100, // timings.idleToRx |
| 192 - 10, // timings.txToRx |
| 100, // timings.idleToTx |
| 192, // timings.rxToTx |
| 0, // timings.rxSearchTimeout |
| 0, // timings.txToRxSearchTimeout |
| }, |
| RAIL_IEEE802154_ACCEPT_STANDARD_FRAMES, // framesMask |
| false, // promiscuousMode |
| false, // isPanCoordinator |
| false, // defaultFramePendingInOutgoingAcks |
| }; |
| |
| // Misc |
| static volatile uint16_t miscInternalFlags = 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 |
| static uint32_t sCoexCounters[SL_RAIL_UTIL_COEX_EVENT_COUNT] = {0}; |
| #endif // SL_OPENTHREAD_COEX_COUNTER_ENABLE |
| |
| #endif // SL_CATALOG_RAIL_UTIL_COEX_PRESENT |
| |
| #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) |
| // Transmit Security |
| static uint32_t sMacFrameCounter; |
| static uint8_t sKeyId; |
| static struct otMacKey sPrevKey; |
| static struct otMacKey sCurrKey; |
| static struct otMacKey sNextKey; |
| |
| #if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT |
| // IE support |
| static otExtAddress sExtAddress; |
| static otRadioIeInfo sTransmitIeInfo; |
| #endif |
| |
| #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE |
| // Enhanced ACKs, CSL |
| static bool sAckedWithSecEnhAck; |
| static uint32_t sAckFrameCounter; |
| static uint8_t sAckKeyId; |
| |
| static uint8_t sAckIeData[OT_ACK_IE_MAX_SIZE]; |
| static uint8_t sAckIeDataLength = 0; |
| |
| static uint32_t sCslPeriod; |
| static uint32_t sCslSampleTime; |
| static const uint8_t sCslIeHeader[OT_IE_HEADER_SIZE] = {CSL_IE_HEADER_BYTES_LO, CSL_IE_HEADER_BYTES_HI}; |
| |
| static void processSecurityForEnhancedAck(uint8_t *aAckFrame) |
| { |
| otRadioFrame ackFrame; |
| struct otMacKey *key = NULL; |
| uint8_t keyId; |
| |
| sAckedWithSecEnhAck = false; |
| otEXPECT(aAckFrame[1] & IEEE802154_FRAME_FLAG_SECURITY_ENABLED); |
| |
| memset(&ackFrame, 0, sizeof(ackFrame)); |
| ackFrame.mPsdu = &aAckFrame[1]; |
| ackFrame.mLength = aAckFrame[0]; |
| |
| keyId = otMacFrameGetKeyId(&ackFrame); |
| |
| otEXPECT(otMacFrameIsKeyIdMode1(&ackFrame) && keyId != 0); |
| |
| if (keyId == sKeyId) |
| { |
| key = &sCurrKey; |
| } |
| else if (keyId == sKeyId - 1) |
| { |
| key = &sPrevKey; |
| } |
| else if (keyId == sKeyId + 1) |
| { |
| key = &sNextKey; |
| } |
| else |
| { |
| otEXPECT(false); |
| } |
| |
| sAckFrameCounter = sMacFrameCounter; |
| sAckKeyId = keyId; |
| sAckedWithSecEnhAck = true; |
| |
| ackFrame.mInfo.mTxInfo.mAesKey = key; |
| |
| otMacFrameSetKeyId(&ackFrame, keyId); |
| otMacFrameSetFrameCounter(&ackFrame, sMacFrameCounter++); |
| |
| // Perform AES-CCM encryption on the frame which is going to be sent. |
| otMacFrameProcessTransmitAesCcm(&ackFrame, &sExtAddress); |
| |
| exit: |
| return; |
| } |
| |
| static uint16_t getCslPhase() |
| { |
| uint32_t curTime = otPlatAlarmMicroGetNow(); |
| uint32_t cslPeriodInUs = sCslPeriod * OT_US_PER_TEN_SYMBOLS; |
| uint32_t diff = ((sCslSampleTime % cslPeriodInUs) - (curTime % cslPeriodInUs) + cslPeriodInUs) % cslPeriodInUs; |
| |
| return (uint16_t)(diff / OT_US_PER_TEN_SYMBOLS); |
| } |
| |
| static void updateIeData(void) |
| { |
| // The CSL IE Content field: |
| // ___________________________________________________ |
| // | Octets: 2 | Octets: 2 | Octets: 0/2 | |
| // |______________|______________|_____________________| |
| // | CSL Phase | CSL Period | Rendezvous time | |
| // |______________|______________|_____________________| |
| // |
| // Note: The rendezvous time is included right when sending the packet, |
| // (in txCurrentPacket), before updating the 802.15.4 header with CSL IEs. |
| // The tx frame is modified at the right offset (see mInfo.mTxInfo.mIeInfo->mTimeIeOffset) |
| |
| int8_t offset = 0; |
| if (sCslPeriod > 0) |
| { |
| uint8_t *finger = sAckIeData; |
| memcpy(finger, sCslIeHeader, OT_IE_HEADER_SIZE); |
| finger += OT_IE_HEADER_SIZE; |
| |
| uint16_t cslPhase = getCslPhase(); |
| *finger++ = HIGH_BYTE(cslPhase); |
| *finger++ = LOW_BYTE(cslPhase); |
| |
| *finger++ = HIGH_BYTE((uint16_t)sCslPeriod); |
| *finger++ = LOW_BYTE((uint16_t)sCslPeriod); |
| |
| offset = finger - sAckIeData; |
| } |
| |
| sAckIeDataLength = offset; |
| } |
| #endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE |
| #endif // (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) |
| |
| //------------------------------------------------------------------------------ |
| // Forward Declarations |
| |
| static void RAILCb_Generic(RAIL_Handle_t aRailHandle, RAIL_Events_t aEvents); |
| |
| static void efr32PhyStackInit(void); |
| |
| #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, uint8_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 updateRxFrameDetails(RAIL_RxPacketDetails_t *pPacketDetails, bool framePendingSetInOutgoingAck); |
| |
| //------------------------------------------------------------------------------ |
| // Helper Functions |
| |
| #ifdef SL_CATALOG_RAIL_UTIL_IEEE802154_STACK_EVENT_PRESENT |
| |
| 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 |
| result |= (sl_rail_util_coex_is_enabled() && sRadioCoexEnabled); |
| #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); |
| assert(status == RAIL_STATUS_NO_ERROR); |
| } |
| } |
| |
| static sl_rail_util_ieee802154_stack_event_t handlePhyStackEvent(sl_rail_util_ieee802154_stack_event_t stackEvent, |
| uint32_t supplement) |
| { |
| return (phyStackEventIsEnabled() ? sl_rail_util_ieee802154_on_event(stackEvent, supplement) : 0); |
| } |
| #else |
| static void updateEvents(RAIL_Events_t mask, RAIL_Events_t values) |
| { |
| RAIL_Status_t status; |
| status = RAIL_ConfigEvents(gRailHandle, mask, values); |
| 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. |
| static inline void setInternalFlag(uint16_t flag, bool val) |
| { |
| CORE_DECLARE_IRQ_STATE; |
| CORE_ENTER_ATOMIC(); |
| miscInternalFlags = (val ? (miscInternalFlags | flag) : (miscInternalFlags & ~flag)); |
| CORE_EXIT_ATOMIC(); |
| } |
| // Returns true if the passed flag is set, false otherwise. |
| static inline bool getInternalFlag(uint16_t flag) |
| { |
| return ((miscInternalFlags & flag) != 0); |
| } |
| |
| static inline bool txWaitingForAck(void) |
| { |
| return (sTransmitBusy == true && ((sTransmitFrame.mPsdu[0] & IEEE802154_FRAME_FLAG_ACK_REQUIRED) != 0)); |
| } |
| |
| static bool txIsDataRequest(void) |
| { |
| uint16_t fcf = sTransmitFrame.mPsdu[IEEE802154_FCF_OFFSET] | (sTransmitFrame.mPsdu[IEEE802154_FCF_OFFSET + 1] << 8); |
| |
| return (sTransmitBusy == true && (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 |
| |
| 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); |
| sState = OT_RADIO_STATE_SLEEP; |
| } |
| |
| static otError radioSetRx(uint8_t aChannel) |
| { |
| otError error = OT_ERROR_NONE; |
| RAIL_Status_t status; |
| |
| RAIL_SchedulerInfo_t bgRxSchedulerInfo = { |
| .priority = RADIO_SCHEDULER_BACKGROUND_RX_PRIORITY, |
| // sliptime/transaction time is not used for bg rx |
| }; |
| |
| 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); |
| sState = OT_RADIO_STATE_RECEIVE; |
| |
| otLogInfoPlat("State=OT_RADIO_STATE_RECEIVE", NULL); |
| exit: |
| return error; |
| } |
| |
| //------------------------------------------------------------------------------ |
| // Radio Initialization |
| |
| static RAIL_Handle_t efr32RailInit(efr32CommonConfig *aCommonConfig) |
| { |
| RAIL_Status_t status; |
| RAIL_Handle_t handle; |
| |
| handle = RAIL_Init(&aCommonConfig->mRailConfig, NULL); |
| assert(handle != NULL); |
| |
| #if defined(SL_CATALOG_POWER_MANAGER_PRESENT) |
| status = RAIL_InitPowerManager(); |
| assert(status == RAIL_STATUS_NO_ERROR); |
| #endif // SL_CATALOG_POWER_MANAGER_PRESENT |
| |
| status = RAIL_ConfigCal(handle, RAIL_CAL_ALL); |
| assert(status == RAIL_STATUS_NO_ERROR); |
| |
| status = RAIL_IEEE802154_Init(handle, &sRailIeee802154Config); |
| 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); |
| // assert(status == RAIL_STATUS_NO_ERROR); |
| |
| // status = RAIL_IEEE802154_EnableDataFramePending(handle, true); |
| // assert(status == RAIL_STATUS_NO_ERROR); |
| |
| // Enhanced ACKs (only on platforms that support it, so error checking is disabled) |
| RAIL_IEEE802154_ConfigEOptions(handle, (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) |
| |
| uint16_t actualLenth = RAIL_SetTxFifo(handle, aCommonConfig->mRailTxFifo, 0, sizeof(aCommonConfig->mRailTxFifo)); |
| assert(actualLenth == sizeof(aCommonConfig->mRailTxFifo)); |
| |
| return handle; |
| } |
| |
| static void efr32RailConfigLoad(efr32BandConfig *aBandConfig) |
| { |
| RAIL_Status_t status; |
| RAIL_TxPowerConfig_t txPowerConfig = {SL_RAIL_UTIL_PA_SELECTION_2P4GHZ, SL_RAIL_UTIL_PA_VOLTAGE_MV, 10}; |
| |
| #if RADIO_CONFIG_915MHZ_OQPSK_SUPPORT |
| if (aBandConfig->mChannelConfig != NULL) |
| { |
| uint16_t firstChannel = RAIL_ConfigChannels(gRailHandle, aBandConfig->mChannelConfig, NULL); |
| assert(firstChannel == aBandConfig->mChannelMin); |
| |
| txPowerConfig.mode = RAIL_TX_POWER_MODE_SUBGIG; |
| } |
| else |
| #endif // RADIO_CONFIG_915MHZ_OQPSK_SUPPORT |
| { |
| #ifdef SL_CATALOG_RAIL_UTIL_IEEE802154_PHY_SELECT_PRESENT |
| status = sl_rail_util_plugin_config_2p4ghz_radio(gRailHandle); |
| #else |
| status = RAIL_IEEE802154_Config2p4GHzRadio(gRailHandle); |
| #endif // SL_CATALOG_RAIL_UTIL_IEEE802154_PHY_SELECT_PRESENT |
| assert(status == RAIL_STATUS_NO_ERROR); |
| } |
| |
| status = RAIL_ConfigTxPower(gRailHandle, &txPowerConfig); |
| assert(status == RAIL_STATUS_NO_ERROR); |
| } |
| |
| static void efr32RadioSetTxPower(int8_t aPowerDbm) |
| { |
| RAIL_Status_t status; |
| sl_rail_util_pa_init(); |
| |
| status = RAIL_SetTxPowerDbm(gRailHandle, ((RAIL_TxPower_t)aPowerDbm) * 10); |
| assert(status == RAIL_STATUS_NO_ERROR); |
| } |
| |
| static efr32BandConfig *efr32RadioGetBandConfig(uint8_t aChannel) |
| { |
| efr32BandConfig *config = NULL; |
| |
| for (uint8_t i = 0; i < EFR32_NUM_BAND_CONFIGS; i++) |
| { |
| if ((sBandConfig[i].mChannelMin <= aChannel) && (aChannel <= sBandConfig[i].mChannelMax)) |
| { |
| config = &sBandConfig[i]; |
| break; |
| } |
| } |
| |
| 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 |
| |
| uint8_t index = 0; |
| |
| #if RADIO_CONFIG_2P4GHZ_OQPSK_SUPPORT |
| sBandConfig[index].mChannelConfig = NULL; |
| sBandConfig[index].mChannelMin = OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MIN; |
| sBandConfig[index].mChannelMax = OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MAX; |
| |
| index++; |
| #endif |
| |
| #if RADIO_CONFIG_915MHZ_OQPSK_SUPPORT |
| sBandConfig[index].mChannelConfig = channelConfigs[0]; // TO DO: channel config?? |
| sBandConfig[index].mChannelMin = OT_RADIO_915MHZ_OQPSK_CHANNEL_MIN; |
| sBandConfig[index].mChannelMax = OT_RADIO_915MHZ_OQPSK_CHANNEL_MAX; |
| #endif |
| |
| #if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT |
| memset(&sRailDebugCounters, 0x00, sizeof(efr32RadioCounters)); |
| #endif |
| |
| gRailHandle = efr32RailInit(&sCommonConfig); |
| assert(gRailHandle != NULL); |
| |
| updateEvents(RAIL_EVENTS_ALL, |
| (0 | RAIL_EVENT_RX_ACK_TIMEOUT | RAIL_EVENT_RX_PACKET_RECEIVED | RAIL_EVENTS_TXACK_COMPLETION | |
| RAIL_EVENTS_TX_COMPLETION | RAIL_EVENT_RSSI_AVERAGE_DONE | 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[0])); |
| } |
| |
| void efr32RadioInit(void) |
| { |
| if (getInternalFlag(FLAG_RADIO_INIT_DONE)) |
| { |
| return; |
| } |
| RAIL_Status_t status; |
| |
| // check if RAIL_TX_FIFO_SIZE is power of two.. |
| assert((RAIL_TX_FIFO_SIZE & (RAIL_TX_FIFO_SIZE - 1)) == 0); |
| |
| // check the limits of the RAIL_TX_FIFO_SIZE. |
| 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); |
| assert(status == RAIL_STATUS_NO_ERROR); |
| |
| sReceiveFrame.mLength = 0; |
| sReceiveFrame.mPsdu = sReceivePsdu; |
| sReceiveAckFrame.mLength = 0; |
| sReceiveAckFrame.mPsdu = sReceiveAckPsdu; |
| sTransmitFrame.mLength = 0; |
| sTransmitFrame.mPsdu = sTransmitPsdu; |
| |
| #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) |
| #if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT |
| sTransmitFrame.mInfo.mTxInfo.mIeInfo = &sTransmitIeInfo; |
| #endif |
| #endif |
| |
| sCurrentBandConfig = efr32RadioGetBandConfig(OPENTHREAD_CONFIG_DEFAULT_CHANNEL); |
| assert(sCurrentBandConfig != NULL); |
| |
| efr32RadioSetTxPower(OPENTHREAD_CONFIG_DEFAULT_TRANSMIT_POWER); |
| |
| assert(RAIL_ConfigRxOptions(gRailHandle, RAIL_RX_OPTION_TRACK_ABORTED_FRAMES, |
| RAIL_RX_OPTION_TRACK_ABORTED_FRAMES) == RAIL_STATUS_NO_ERROR); |
| efr32PhyStackInit(); |
| |
| sEnergyScanStatus = ENERGY_SCAN_STATUS_IDLE; |
| sTransmitError = OT_ERROR_NONE; |
| sTransmitBusy = false; |
| |
| otLogInfoPlat("Initialized", NULL); |
| } |
| |
| void efr32RadioDeinit(void) |
| { |
| RAIL_Status_t status; |
| |
| RAIL_Idle(gRailHandle, RAIL_IDLE_ABORT, true); |
| status = RAIL_ConfigEvents(gRailHandle, RAIL_EVENTS_ALL, 0); |
| assert(status == RAIL_STATUS_NO_ERROR); |
| |
| sCurrentBandConfig = NULL; |
| } |
| |
| //------------------------------------------------------------------------------ |
| // Energy Scan support |
| |
| static void energyScanComplete(int8_t scanResultDbm) |
| { |
| sEnergyScanResultDbm = scanResultDbm; |
| sEnergyScanStatus = ENERGY_SCAN_STATUS_COMPLETED; |
| } |
| |
| 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); |
| sCurrentBandConfig = config; |
| } |
| |
| RAIL_SchedulerInfo_t scanSchedulerInfo = {.priority = RADIO_SCHEDULER_CHANNEL_SCAN_PRIORITY, |
| .slipTime = RADIO_SCHEDULER_CHANNEL_SLIP_TIME, |
| .transactionTime = aAveragingTimeUs}; |
| |
| status = RAIL_StartAverageRssi(gRailHandle, aChannel, aAveragingTimeUs, &scanSchedulerInfo); |
| 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 |
| |
| void otPlatRadioGetIeeeEui64(otInstance *aInstance, uint8_t *aIeeeEui64) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| uint64_t eui64; |
| uint8_t *eui64Ptr = NULL; |
| |
| eui64 = SYSTEM_GetUnique(); |
| 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) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| RAIL_Status_t status; |
| |
| otLogInfoPlat("PANID=%X", aPanId); |
| |
| utilsSoftSrcMatchSetPanId(aPanId); |
| |
| status = RAIL_IEEE802154_SetPanId(gRailHandle, aPanId, 0); |
| assert(status == RAIL_STATUS_NO_ERROR); |
| } |
| |
| void otPlatRadioSetExtendedAddress(otInstance *aInstance, const otExtAddress *aAddress) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) |
| #if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT |
| for (size_t i = 0; i < sizeof(*aAddress); i++) |
| { |
| sExtAddress.m8[i] = aAddress->m8[sizeof(*aAddress) - 1 - i]; |
| } |
| #endif |
| #endif |
| |
| RAIL_Status_t status; |
| |
| otLogInfoPlat("ExtAddr=%X%X%X%X%X%X%X%X", aAddress->m8[7], aAddress->m8[6], aAddress->m8[5], aAddress->m8[4], |
| aAddress->m8[3], aAddress->m8[2], aAddress->m8[1], aAddress->m8[0]); |
| |
| status = RAIL_IEEE802154_SetLongAddress(gRailHandle, (uint8_t *)aAddress->m8, 0); |
| assert(status == RAIL_STATUS_NO_ERROR); |
| } |
| |
| void otPlatRadioSetShortAddress(otInstance *aInstance, uint16_t aAddress) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| RAIL_Status_t status; |
| |
| otLogInfoPlat("ShortAddr=%X", aAddress); |
| |
| status = RAIL_IEEE802154_SetShortAddress(gRailHandle, aAddress, 0); |
| assert(status == RAIL_STATUS_NO_ERROR); |
| } |
| |
| bool otPlatRadioIsEnabled(otInstance *aInstance) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| return (sState != OT_RADIO_STATE_DISABLED); |
| } |
| |
| otError otPlatRadioEnable(otInstance *aInstance) |
| { |
| otEXPECT(!otPlatRadioIsEnabled(aInstance)); |
| |
| otLogInfoPlat("State=OT_RADIO_STATE_SLEEP", NULL); |
| sState = OT_RADIO_STATE_SLEEP; |
| |
| exit: |
| return OT_ERROR_NONE; |
| } |
| |
| otError otPlatRadioDisable(otInstance *aInstance) |
| { |
| otEXPECT(otPlatRadioIsEnabled(aInstance)); |
| |
| otLogInfoPlat("State=OT_RADIO_STATE_DISABLED", NULL); |
| sState = OT_RADIO_STATE_DISABLED; |
| |
| exit: |
| return OT_ERROR_NONE; |
| } |
| |
| otError otPlatRadioSleep(otInstance *aInstance) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| otError error = OT_ERROR_NONE; |
| |
| otEXPECT_ACTION((sState != OT_RADIO_STATE_TRANSMIT) && (sState != OT_RADIO_STATE_DISABLED), |
| error = OT_ERROR_INVALID_STATE); |
| |
| otLogInfoPlat("State=OT_RADIO_STATE_SLEEP", NULL); |
| radioSetIdle(); |
| exit: |
| return error; |
| } |
| |
| otError otPlatRadioReceive(otInstance *aInstance, uint8_t aChannel) |
| { |
| otError error = OT_ERROR_NONE; |
| RAIL_Status_t status; |
| efr32BandConfig *config; |
| |
| OT_UNUSED_VARIABLE(aInstance); |
| otEXPECT_ACTION(sState != OT_RADIO_STATE_DISABLED, error = OT_ERROR_INVALID_STATE); |
| |
| config = efr32RadioGetBandConfig(aChannel); |
| otEXPECT_ACTION(config != NULL, error = OT_ERROR_INVALID_ARGS); |
| |
| if (sCurrentBandConfig != config) |
| { |
| RAIL_Idle(gRailHandle, RAIL_IDLE, true); |
| efr32RailConfigLoad(config); |
| sCurrentBandConfig = config; |
| } |
| |
| status = radioSetRx(aChannel); |
| otEXPECT_ACTION(status == RAIL_STATUS_NO_ERROR, error = OT_ERROR_FAILED); |
| |
| sReceiveFrame.mChannel = aChannel; |
| sReceiveAckFrame.mChannel = aChannel; |
| |
| exit: |
| return error; |
| } |
| |
| otError otPlatRadioTransmit(otInstance *aInstance, otRadioFrame *aFrame) |
| { |
| otError error = OT_ERROR_NONE; |
| efr32BandConfig *config; |
| |
| otEXPECT_ACTION((sState != OT_RADIO_STATE_DISABLED) && (sState != OT_RADIO_STATE_TRANSMIT), |
| error = OT_ERROR_INVALID_STATE); |
| |
| config = efr32RadioGetBandConfig(aFrame->mChannel); |
| otEXPECT_ACTION(config != NULL, error = OT_ERROR_INVALID_ARGS); |
| if (sCurrentBandConfig != config) |
| { |
| RAIL_Idle(gRailHandle, RAIL_IDLE, true); |
| efr32RailConfigLoad(config); |
| sCurrentBandConfig = config; |
| } |
| |
| assert(sTransmitBusy == false); |
| sState = OT_RADIO_STATE_TRANSMIT; |
| sTransmitError = OT_ERROR_NONE; |
| sTransmitBusy = true; |
| sTxFrame = aFrame; |
| |
| setInternalFlag(FLAG_CURRENT_TX_USE_CSMA, aFrame->mInfo.mTxInfo.mCsmaCaEnabled); |
| |
| CORE_DECLARE_IRQ_STATE; |
| CORE_ENTER_ATOMIC(); |
| setInternalFlag(FLAG_ONGOING_TX_DATA, true); |
| tryTxCurrentPacket(); |
| CORE_EXIT_ATOMIC(); |
| |
| if (sTransmitError == OT_ERROR_NONE) |
| { |
| otPlatRadioTxStarted(aInstance, aFrame); |
| } |
| exit: |
| return error; |
| } |
| |
| void txCurrentPacket(void) |
| { |
| assert(getInternalFlag(FLAG_ONGOING_TX_DATA)); |
| assert(sTxFrame != NULL); |
| |
| RAIL_CsmaConfig_t csmaConfig = RAIL_CSMA_CONFIG_802_15_4_2003_2p4_GHz_OQPSK_CSMA; |
| RAIL_TxOptions_t txOptions = RAIL_TX_OPTIONS_DEFAULT; |
| RAIL_Status_t status; |
| uint8_t frameLength; |
| bool ackRequested; |
| |
| #if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT |
| sRailDebugCounters.mRailPlatTxTriggered++; |
| #endif |
| |
| // signalling this event earlier, as this event can 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)sTxFrame->mLength; |
| |
| #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 |
| #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) |
| { |
| otMacFrameSetCslIe(sTxFrame, (uint16_t)sCslPeriod, getCslPhase()); |
| } |
| #endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE |
| |
| #if OPENTHREAD_CONFIG_MAC_SOFTWARE_TX_SECURITY_ENABLE |
| bool processSecurity = false; |
| #endif |
| |
| #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE |
| // Seek the time sync offset and update the rendezvous time |
| if (sTxFrame->mInfo.mTxInfo.mIeInfo->mTimeIeOffset != 0) |
| { |
| uint8_t *timeIe = sTxFrame->mPsdu + sTxFrame->mInfo.mTxInfo.mIeInfo->mTimeIeOffset; |
| uint64_t time = otPlatTimeGet() + sTxFrame->mInfo.mTxInfo.mIeInfo->mNetworkTimeOffset; |
| |
| *timeIe = sTxFrame->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); |
| } |
| |
| #if OPENTHREAD_CONFIG_MAC_SOFTWARE_TX_SECURITY_ENABLE |
| processSecurity = true; |
| #endif |
| } |
| #endif // OPENTHREAD_CONFIG_TIME_SYNC_ENABLE |
| |
| #if OPENTHREAD_CONFIG_MAC_SOFTWARE_TX_SECURITY_ENABLE |
| if (otMacFrameIsSecurityEnabled(sTxFrame) && otMacFrameIsKeyIdMode1(sTxFrame) && |
| !sTxFrame->mInfo.mTxInfo.mIsSecurityProcessed) |
| { |
| sTxFrame->mInfo.mTxInfo.mAesKey = &sCurrKey; |
| |
| if (!sTxFrame->mInfo.mTxInfo.mIsARetx) |
| { |
| otMacFrameSetKeyId(sTxFrame, sKeyId); |
| otMacFrameSetFrameCounter(sTxFrame, sMacFrameCounter++); |
| } |
| |
| processSecurity = true; |
| } |
| |
| if (processSecurity) |
| { |
| otMacFrameProcessTransmitAesCcm(sTxFrame, &sExtAddress); |
| } |
| #endif // OPENTHREAD_CONFIG_MAC_SOFTWARE_TX_SECURITY_ENABLE |
| #endif // OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 |
| |
| RAIL_WriteTxFifo(gRailHandle, &frameLength, sizeof frameLength, true); |
| RAIL_WriteTxFifo(gRailHandle, sTxFrame->mPsdu, frameLength - 2, false); |
| |
| RAIL_SchedulerInfo_t txSchedulerInfo = { |
| .priority = RADIO_SCHEDULER_TX_PRIORITY, |
| .slipTime = RADIO_SCHEDULER_CHANNEL_SLIP_TIME, |
| .transactionTime = 0, // will be calculated later if DMP is used |
| }; |
| |
| ackRequested = (sTxFrame->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_antenna_mode() != SL_RAIL_UTIL_ANT_DIV_DISABLED) |
| { |
| txOptions |= ((sl_rail_util_ant_div_get_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 |
| |
| 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 = sTxFrame->mInfo.mTxInfo.mMaxCsmaBackoffs; |
| csmaConfig.ccaThreshold = sCcaThresholdDbm; |
| status = RAIL_StartCcaCsmaTx(gRailHandle, sTxFrame->mChannel, txOptions, &csmaConfig, &txSchedulerInfo); |
| } |
| else |
| { |
| status = RAIL_StartTx(gRailHandle, sTxFrame->mChannel, txOptions, &txSchedulerInfo); |
| if (status == RAIL_STATUS_NO_ERROR) |
| { |
| (void)handlePhyStackEvent(SL_RAIL_UTIL_IEEE802154_STACK_EVENT_TX_STARTED, 0U); |
| } |
| } |
| if (status == RAIL_STATUS_NO_ERROR) |
| { |
| #if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT |
| sRailDebugCounters.mRailTxStarted++; |
| #endif |
| } |
| else |
| { |
| #if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT |
| sRailDebugCounters.mRailTxStartFailed++; |
| #endif |
| (void)handlePhyStackEvent(SL_RAIL_UTIL_IEEE802154_STACK_EVENT_TX_BLOCKED, (uint32_t)ackRequested); |
| txFailedCallback(false, TX_COMPLETE_RESULT_OTHER_FAIL); |
| |
| otSysEventSignalPending(); |
| } |
| } |
| |
| otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *aInstance) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| return &sTransmitFrame; |
| } |
| |
| int8_t otPlatRadioGetRssi(otInstance *aInstance) |
| { |
| otError error; |
| uint32_t start; |
| int8_t rssi = OT_RADIO_RSSI_INVALID; |
| |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| error = efr32StartEnergyScan(ENERGY_SCAN_MODE_SYNC, sReceiveFrame.mChannel, EFR32_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) < EFR32_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); |
| |
| otRadioCaps capabilities = (OT_RADIO_CAPS_ACK_TIMEOUT | OT_RADIO_CAPS_CSMA_BACKOFF | OT_RADIO_CAPS_ENERGY_SCAN | |
| OT_RADIO_CAPS_SLEEP_TO_TX); |
| |
| #if OPENTHREAD_CONFIG_MAC_SOFTWARE_TX_SECURITY_ENABLE |
| capabilities |= OT_RADIO_CAPS_TRANSMIT_SEC; |
| #endif |
| |
| #if OPENTHREAD_CONFIG_MAC_SOFTWARE_TX_TIMING_ENABLE |
| capabilities |= OT_RADIO_CAPS_TRANSMIT_TIMING; |
| #endif |
| |
| return capabilities; |
| } |
| |
| bool otPlatRadioGetPromiscuous(otInstance *aInstance) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| return sPromiscuous; |
| } |
| |
| void otPlatRadioSetPromiscuous(otInstance *aInstance, bool aEnable) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| RAIL_Status_t status; |
| |
| sPromiscuous = aEnable; |
| |
| status = RAIL_IEEE802154_SetPromiscuousMode(gRailHandle, aEnable); |
| assert(status == RAIL_STATUS_NO_ERROR); |
| } |
| |
| void otPlatRadioEnableSrcMatch(otInstance *aInstance, bool aEnable) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| // set Frame Pending bit for all outgoing ACKs if aEnable is false |
| sIsSrcMatchEnabled = aEnable; |
| } |
| |
| 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) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| RAIL_Status_t status; |
| |
| // RAIL_SetTxPowerDbm() takes power in units of deci-dBm (0.1dBm) |
| // Divide by 10 because aPower is supposed be in units dBm |
| status = RAIL_SetTxPowerDbm(gRailHandle, ((RAIL_TxPower_t)aPower) * 10); |
| assert(status == RAIL_STATUS_NO_ERROR); |
| |
| return OT_ERROR_NONE; |
| } |
| |
| 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); |
| |
| sCcaThresholdDbm = aThreshold; |
| |
| return OT_ERROR_NONE; |
| } |
| |
| int8_t otPlatRadioGetReceiveSensitivity(otInstance *aInstance) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| return EFR32_RECEIVE_SENSITIVITY; |
| } |
| |
| otError otPlatRadioEnergyScan(otInstance *aInstance, uint8_t aScanChannel, uint16_t aScanDuration) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| return efr32StartEnergyScan(ENERGY_SCAN_MODE_ASYNC, aScanChannel, (RAIL_Time_t)aScanDuration * US_IN_MS); |
| } |
| |
| #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) |
| //------------------------------------------------------------------------------ |
| // Radio Config: Thread 1.2 transmit security support |
| |
| void otPlatRadioSetMacKey(otInstance * aInstance, |
| uint8_t aKeyIdMode, |
| uint8_t aKeyId, |
| const otMacKey *aPrevKey, |
| const otMacKey *aCurrKey, |
| const otMacKey *aNextKey) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| OT_UNUSED_VARIABLE(aKeyIdMode); |
| |
| assert(aPrevKey != NULL && aCurrKey != NULL && aNextKey != NULL); |
| |
| CORE_DECLARE_IRQ_STATE; |
| CORE_ENTER_ATOMIC(); |
| |
| sKeyId = aKeyId; |
| memcpy(sPrevKey.m8, aPrevKey->m8, OT_MAC_KEY_SIZE); |
| memcpy(sCurrKey.m8, aCurrKey->m8, OT_MAC_KEY_SIZE); |
| memcpy(sNextKey.m8, aNextKey->m8, OT_MAC_KEY_SIZE); |
| |
| CORE_EXIT_ATOMIC(); |
| } |
| |
| void otPlatRadioSetMacFrameCounter(otInstance *aInstance, uint32_t aMacFrameCounter) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| CORE_DECLARE_IRQ_STATE; |
| CORE_ENTER_ATOMIC(); |
| |
| sMacFrameCounter = aMacFrameCounter; |
| |
| CORE_EXIT_ATOMIC(); |
| } |
| |
| //------------------------------------------------------------------------------ |
| // Radio Config: Enhanced Acks, CSL |
| |
| #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE |
| otError otPlatRadioEnableCsl(otInstance *aInstance, uint32_t aCslPeriod, const otExtAddress *aExtAddr) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| OT_UNUSED_VARIABLE(aExtAddr); |
| |
| sCslPeriod = aCslPeriod; |
| updateIeData(); |
| |
| return OT_ERROR_NONE; |
| } |
| |
| void otPlatRadioUpdateCslSampleTime(otInstance *aInstance, uint32_t aCslSampleTime) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| sCslSampleTime = aCslSampleTime; |
| } |
| #endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE |
| |
| //------------------------------------------------------------------------------ |
| // Radio Config: Link Metrics |
| |
| #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE |
| otError otPlatRadioConfigureEnhAckProbing(otInstance * aInstance, |
| otLinkMetrics aLinkMetrics, |
| const otShortAddress aShortAddress, |
| const otExtAddress * aExtAddress) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| otError error = otLinkMetricsConfigureEnhAckProbing(aShortAddress, aExtAddress, aLinkMetrics); |
| #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE |
| if (error == OT_ERROR_NONE) |
| { |
| updateIeData(); |
| } |
| #endif |
| |
| 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) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| if (aEnabled && !sl_rail_util_coex_is_enabled()) |
| { |
| otLogInfoPlat("Coexistence GPIO configurations not set"); |
| return OT_ERROR_FAILED; |
| } |
| sRadioCoexEnabled = aEnabled; |
| return OT_ERROR_NONE; |
| } |
| |
| bool otPlatRadioIsCoexEnabled(otInstance *aInstance) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| return (sRadioCoexEnabled && sl_rail_util_coex_is_enabled()); |
| } |
| |
| otError otPlatRadioGetCoexMetrics(otInstance *aInstance, otRadioCoexMetrics *aCoexMetrics) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| otError error = OT_ERROR_NONE; |
| |
| otEXPECT_ACTION(aCoexMetrics != NULL, error = OT_ERROR_INVALID_ARGS); |
| |
| memset(aCoexMetrics, 0, sizeof(otRadioCoexMetrics)); |
| // TO DO: |
| // Tracking coex metrics with detailed granularity currently |
| // not implemented. |
| // memcpy(aCoexMetrics, &sCoexMetrics, sizeof(otRadioCoexMetrics)); |
| exit: |
| return error; |
| } |
| #endif // OPENTHREAD_CONFIG_PLATFORM_RADIO_COEX_ENABLE |
| |
| #if (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) |
| //------------------------------------------------------------------------------ |
| // Radio implementation: Enhanced ACKs, CSL |
| |
| #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE |
| static uint8_t getKeySourceLength(uint8_t keyIdMode) |
| { |
| uint8_t len = 0; |
| |
| switch (keyIdMode) |
| { |
| case IEEE802154_KEYID_MODE_0: |
| len = IEEE802154_KEYID_MODE_0_SIZE; |
| break; |
| |
| case IEEE802154_KEYID_MODE_1: |
| len = IEEE802154_KEYID_MODE_1_SIZE; |
| break; |
| |
| case IEEE802154_KEYID_MODE_2: |
| len = IEEE802154_KEYID_MODE_2_SIZE; |
| break; |
| |
| case IEEE802154_KEYID_MODE_3: |
| len = IEEE802154_KEYID_MODE_3_SIZE; |
| break; |
| } |
| |
| return len; |
| } |
| |
| static bool writeIeee802154EnhancedAck(RAIL_Handle_t aRailHandle, const uint8_t *aIeData, uint8_t aIeLength) |
| { |
| // This table is derived from 802.15.4-2015 Section 7.2.1.5 PAN ID |
| // Compression field and Table 7-2 for both 2003/2006 and 2015 |
| // frame versions. It is indexed by 6 bits of the MacFCF: |
| // SrcAdrMode FrameVer<msbit> DstAdrMode PanIdCompression |
| // and each address' length is encoded in a nibble: |
| // 15:12 11:8 7:4 3:0 |
| // SrcAdr SrcPan DstAdr DstPan |
| // Illegal combinations are indicated by 0xFFFFU. |
| |
| #define ADDRSIZE_DST_PAN_SHIFT 0 |
| #define ADDRSIZE_DST_PAN_MASK (0x0FU << ADDRSIZE_DST_PAN_SHIFT) |
| #define ADDRSIZE_DST_ADR_SHIFT 4 |
| #define ADDRSIZE_DST_ADR_MASK (0x0FU << ADDRSIZE_DST_ADR_SHIFT) |
| #define ADDRSIZE_SRC_PAN_SHIFT 8 |
| #define ADDRSIZE_SRC_PAN_MASK (0x0FU << ADDRSIZE_SRC_PAN_SHIFT) |
| #define ADDRSIZE_SRC_ADR_SHIFT 12 |
| #define ADDRSIZE_SRC_ADR_MASK (0x0FU << ADDRSIZE_SRC_ADR_SHIFT) |
| |
| static const uint16_t ieee802154Table7p2[64] = { |
| 0x0000U, 0x0000U, 0xFFFFU, 0xFFFFU, 0x0022U, 0x0022U, 0x0082U, 0x0082U, 0x0000U, 0x0002U, 0xFFFFU, |
| 0xFFFFU, 0x0022U, 0x0020U, 0x0082U, 0x0080U, 0xFFFFU, 0xFFFFU, 0xFFFFU, 0xFFFFU, 0xFFFFU, 0xFFFFU, |
| 0xFFFFU, 0xFFFFU, 0xFFFFU, 0xFFFFU, 0xFFFFU, 0xFFFFU, 0xFFFFU, 0xFFFFU, 0xFFFFU, 0xFFFFU, 0x2200U, |
| 0x2200U, 0xFFFFU, 0xFFFFU, 0x2222U, 0x2022U, 0x2282U, 0x2082U, 0x2200U, 0x2000U, 0xFFFFU, 0xFFFFU, |
| 0x2222U, 0x2022U, 0x2282U, 0x2082U, 0x8200U, 0x8200U, 0xFFFFU, 0xFFFFU, 0x8222U, 0x8022U, 0x8282U, |
| 0x8082U, 0x8200U, 0x8000U, 0xFFFFU, 0xFFFFU, 0x8222U, 0x8022U, 0x8082U, 0x8080U, |
| }; |
| |
| // For an Enhanced ACK, we need to generate that ourselves; |
| // RAIL will generate an Immediate ACK for us, though we can |
| // tell it to go out with its FramePending bit set. |
| // 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 the packet, |
| // not the SecHdr which hasn't been received yet. |
| |
| #define EARLY_FRAME_PENDING_EXPECTED_BYTES (2U + 2U + 1U + 2U + 8U + 2U + 8U) |
| #define MAX_SECURED_EXPECTED_RECEIVED_BYTES (EARLY_FRAME_PENDING_EXPECTED_BYTES + 14U) |
| #define FINAL_PACKET_LENGTH_WITH_IE (MAX_SECURED_EXPECTED_RECEIVED_BYTES + aIeLength) |
| |
| RAIL_RxPacketInfo_t packetInfo; |
| uint8_t pkt[FINAL_PACKET_LENGTH_WITH_IE]; |
| |
| // TODO, in the original prototype for this code, this check was made to |
| // determine the PHY header length. |
| // When we add Sub-Ghz support, and we call RAIL_IEEE802154_ConfigGOptions, |
| // we should check for a 2-byte PHR. |
| // #if ("mac has channel pages" && IEEE802154_GB868_SUPPORTED) |
| // #define PHRLen ((emRadioChannelPageInUse == 0) ? 1 : 2) |
| // #else |
| // #define PHRLen 1 |
| uint8_t PHRLen = 1; |
| |
| uint8_t pktOffset = PHRLen; // No need to parse the PHR byte(s) |
| RAIL_GetRxIncomingPacketInfo(gRailHandle, &packetInfo); |
| |
| // Check if packetinfo.packetBytes includes the info we want, |
| // and if not, spin-wait calling RAIL_GetRxIncomingPacketInfo() |
| // until it has arrived |
| uint32_t startMs = otPlatAlarmMilliGetNow(); |
| while (packetInfo.packetBytes < MAX_SECURED_EXPECTED_RECEIVED_BYTES) |
| { |
| RAIL_GetRxIncomingPacketInfo(gRailHandle, &packetInfo); |
| if (otPlatAlarmMilliGetNow() - startMs > 100u) |
| { |
| // More than 100 ms. has elapsed. |
| break; |
| } |
| } |
| |
| if (packetInfo.packetBytes < (pktOffset + 2U)) |
| { |
| return false; |
| } |
| |
| // Only extract what we care about |
| if (packetInfo.packetBytes > MAX_SECURED_EXPECTED_RECEIVED_BYTES) |
| { |
| packetInfo.packetBytes = MAX_SECURED_EXPECTED_RECEIVED_BYTES; |
| if (packetInfo.firstPortionBytes >= MAX_SECURED_EXPECTED_RECEIVED_BYTES) |
| { |
| packetInfo.firstPortionBytes = MAX_SECURED_EXPECTED_RECEIVED_BYTES; |
| packetInfo.lastPortionData = NULL; |
| } |
| } |
| |
| RAIL_CopyRxPacket(pkt, &packetInfo); |
| uint16_t macFcf = pkt[pktOffset++]; |
| |
| if ((macFcf & IEEE802154_FRAME_TYPE_MASK) == IEEE802154_FRAME_TYPE_MULTIPURPOSE) |
| { |
| // Multipurpose frames have an arcane FCF structure |
| if ((macFcf & IEEE802154_MP_FRAME_FLAG_LONG_FCF) != 0U) |
| { |
| macFcf |= (pkt[pktOffset++] << 8); |
| } |
| |
| // Map Multipurpose FCF to a 'normal' Version FCF as best we can. |
| macFcf = |
| (IEEE802154_FRAME_TYPE_MULTIPURPOSE | |
| ((macFcf & (IEEE802154_MP_FRAME_FLAG_SECURITY_ENABLED | IEEE802154_MP_FRAME_FLAG_IE_LIST_PRESENT)) >> 6) | |
| ((macFcf & IEEE802154_MP_FRAME_FLAG_FRAME_PENDING) >> 7) | |
| ((macFcf & IEEE802154_MP_FRAME_FLAG_ACK_REQUIRED) >> 9) | |
| ((macFcf & (IEEE802154_MP_FRAME_FLAG_PANID_PRESENT | IEEE802154_MP_FRAME_FLAG_SEQ_SUPPRESSION)) >> 2) | |
| ((macFcf & IEEE802154_MP_FRAME_DESTINATION_MODE_MASK) << 6) | IEEE802154_MP_FRAME_VERSION_2015 | |
| ((macFcf & IEEE802154_MP_FRAME_SOURCE_MODE_MASK) << 8)); |
| |
| // MultiPurpose's PANID_PRESENT is not equivalent to 2012/5's |
| // PANID_COMPRESSION so we map it best we can by flipping it |
| // in the following address-combination situations: |
| uint16_t addrCombo = (macFcf & (IEEE802154_FRAME_SOURCE_MODE_MASK | IEEE802154_FRAME_DESTINATION_MODE_MASK)); |
| if ((addrCombo == (IEEE802154_FRAME_SOURCE_MODE_NONE | IEEE802154_FRAME_DESTINATION_MODE_NONE)) || |
| (addrCombo == (IEEE802154_FRAME_SOURCE_MODE_SHORT | IEEE802154_FRAME_DESTINATION_MODE_SHORT)) || |
| (addrCombo == (IEEE802154_FRAME_SOURCE_MODE_SHORT | IEEE802154_FRAME_DESTINATION_MODE_LONG)) || |
| (addrCombo == (IEEE802154_FRAME_SOURCE_MODE_LONG | IEEE802154_FRAME_DESTINATION_MODE_SHORT))) |
| { |
| // 802.15.4-2015 PANID_COMPRESSION = MP PANID_PRESENT |
| } |
| else |
| { |
| // 802.15.4-2015 PANID_COMPRESSION = !MP PANID_PRESENT |
| macFcf ^= IEEE802154_FRAME_FLAG_PANID_COMPRESSION; // Flip it |
| } |
| } |
| else |
| { |
| macFcf |= (pkt[pktOffset++] << 8); |
| } |
| |
| bool enhAck = ((macFcf & IEEE802154_FRAME_VERSION_MASK) == IEEE802154_FRAME_VERSION_2015); |
| if (!enhAck) |
| { |
| return false; |
| } |
| |
| // Compress MAC FCF to index into 64-entry address-length table: |
| // SrcAdrMode FrameVer<msbit> DstAdrMode PanIdCompression |
| // |
| // Note: Use IEEE802154_FRAME_VERSION_2012 rather than _MASK so the |
| // low-order bit of the version field isn't used in deriving the index. |
| uint16_t index = (((macFcf & (IEEE802154_FRAME_SOURCE_MODE_MASK | IEEE802154_FRAME_VERSION_2012)) >> 10) | |
| ((macFcf & IEEE802154_FRAME_DESTINATION_MODE_MASK) >> 9) | |
| ((macFcf & IEEE802154_FRAME_FLAG_PANID_COMPRESSION) >> 6)); |
| |
| uint16_t addrSizes = ieee802154Table7p2[index]; |
| // Illegal combinations mean illegal packets which we ignore |
| if (addrSizes == 0xFFFFU) |
| { |
| return false; |
| } |
| |
| uint8_t seqNo = |
| ((enhAck && ((macFcf & IEEE802154_FRAME_FLAG_SEQ_SUPPRESSION) != 0U)) ? 0U : pkt[pktOffset++]); // Seq# |
| |
| // Start writing the enhanced ACK -- we need to construct it since RAIL cannot. |
| |
| // First extract addresses from incoming packet since we may |
| // need to reflect them in a different order in the outgoing ACK. |
| // Use byte[0] to hold each one's length. |
| uint8_t dstPan[3] = { |
| 0, |
| }; // Initialized only to eliminate false gcc warning |
| dstPan[0] = ((addrSizes & ADDRSIZE_DST_PAN_MASK) >> ADDRSIZE_DST_PAN_SHIFT); |
| if ((dstPan[0] + pktOffset) > packetInfo.packetBytes) |
| { |
| return false; |
| } |
| |
| if (dstPan[0] > 0U) |
| { |
| dstPan[1] = pkt[pktOffset++]; |
| dstPan[2] = pkt[pktOffset++]; |
| } |
| |
| uint8_t dstAdr[9]; |
| dstAdr[0] = ((addrSizes & ADDRSIZE_DST_ADR_MASK) >> ADDRSIZE_DST_ADR_SHIFT); |
| if ((dstAdr[0] + pktOffset) > packetInfo.packetBytes) |
| { |
| return false; |
| } |
| |
| for (uint8_t i = 1U; i <= dstAdr[0]; i++) |
| { |
| dstAdr[i] = pkt[pktOffset++]; |
| } |
| |
| uint8_t srcPan[3]; |
| srcPan[0] = ((addrSizes & ADDRSIZE_SRC_PAN_MASK) >> ADDRSIZE_SRC_PAN_SHIFT); |
| if ((srcPan[0] + pktOffset) > packetInfo.packetBytes) |
| { |
| return false; |
| } |
| |
| if (srcPan[0] > 0U) |
| { |
| srcPan[1] = pkt[pktOffset++]; |
| srcPan[2] = pkt[pktOffset++]; |
| } |
| |
| uint8_t srcAdr[9]; |
| srcAdr[0] = ((addrSizes & ADDRSIZE_SRC_ADR_MASK) >> ADDRSIZE_SRC_ADR_SHIFT); |
| if ((srcAdr[0] + pktOffset) > packetInfo.packetBytes) |
| { |
| return false; |
| } |
| |
| for (uint8_t i = 1U; i <= srcAdr[0]; i++) |
| { |
| srcAdr[i] = pkt[pktOffset++]; |
| } |
| |
| // Once done with address fields, pick the security control (if present) |
| uint8_t securityHeader[1 + 4 + 8 + 1]; // max len: control + fc + key source + key ID |
| uint8_t securityHeaderLength = 0; |
| |
| if (macFcf & IEEE802154_FRAME_FLAG_SECURITY_ENABLED) |
| { |
| uint8_t securityControl = pkt[pktOffset]; |
| // Then the key ID |
| uint8_t keySourceLength = getKeySourceLength(securityControl & IEEE802154_KEYID_MODE_MASK); |
| securityHeaderLength += (sizeof(uint8_t) /* security control */ |
| + sizeof(uint32_t) /* frame counter */ |
| + keySourceLength + sizeof(uint8_t)); /* key ID */ |
| memcpy(securityHeader, pkt + pktOffset, securityHeaderLength); |
| pktOffset += securityHeaderLength; |
| } |
| |
| // Reuse packet[] buffer for outgoing Enhanced ACK. |
| // Phr1 Phr2 FcfL FcfH [Seq#] [DstPan] [DstAdr] [SrcPan] [SrcAdr] |
| // Will fill in PHR later. |
| // MAC Fcf: |
| // - Frame Type = ACK |
| // - Security Enabled, as appropriate |
| // - Frame Pending = 0 or as appropriate |
| // - ACK Request = 0 |
| // - PanId compression = incoming packet's |
| // - Seq# suppression = incoming packet's |
| // - IE Present = 0 in this implementation |
| // - DstAdrMode = SrcAdrMode of incoming packet's |
| // - Frame Version = 2 (154E) |
| // - SrcAdrMode = DstAdrMode of incoming packet's (for convenience) |
| uint16_t ackFcf = |
| (IEEE802154_FRAME_TYPE_ACK | (macFcf & IEEE802154_FRAME_FLAG_PANID_COMPRESSION) | |
| (macFcf & IEEE802154_FRAME_FLAG_SEQ_SUPPRESSION) | (macFcf & IEEE802154_FRAME_FLAG_SECURITY_ENABLED) | |
| IEEE802154_FRAME_VERSION_2015 | ((macFcf & IEEE802154_FRAME_SOURCE_MODE_MASK) >> 4) | |
| ((macFcf & IEEE802154_FRAME_DESTINATION_MODE_MASK) << 4)); |
| |
| // Do frame-pending check now |
| if (sIsSrcMatchEnabled) |
| { |
| bool setFramePending = true; |
| if (srcAdr[0] > 0U) |
| { |
| if (srcAdr[0] == 8U) |
| { |
| setFramePending = (utilsSoftSrcMatchExtFindEntry((otExtAddress *)&srcAdr[1]) >= 0); |
| } |
| else |
| { |
| uint16_t srcAdrShort = srcAdr[1] | (srcAdr[2] << 8); |
| setFramePending = (utilsSoftSrcMatchShortFindEntry(srcAdrShort) >= 0); |
| } |
| } |
| if (setFramePending) |
| { |
| ackFcf |= IEEE802154_FRAME_FLAG_FRAME_PENDING; |
| } |
| } |
| |
| pktOffset = PHRLen; |
| pkt[pktOffset++] = (uint8_t)ackFcf; |
| pkt[pktOffset++] = (uint8_t)(ackFcf >> 8); |
| |
| if ((macFcf & IEEE802154_FRAME_FLAG_SEQ_SUPPRESSION) == 0U) |
| { |
| pkt[pktOffset++] = seqNo; |
| } |
| |
| // Determine outgoing ACK's address field sizes |
| index = (((ackFcf & (IEEE802154_FRAME_SOURCE_MODE_MASK | IEEE802154_FRAME_VERSION_2012)) >> 10) | |
| ((ackFcf & IEEE802154_FRAME_DESTINATION_MODE_MASK) >> 9) | |
| ((ackFcf & IEEE802154_FRAME_FLAG_PANID_COMPRESSION) >> 6)); |
| |
| addrSizes = ieee802154Table7p2[index]; |
| if (addrSizes == 0xFFFFU) |
| { |
| // Uh-oh! Enh-ACK would be malformed?! Something funky happened! |
| // Possibly a latency-induced issue. |
| return false; |
| } |
| |
| // DstPan = SrcPan of incoming if avail otherwise DstPan of incoming |
| if ((addrSizes & ADDRSIZE_DST_PAN_MASK) != 0U) |
| { |
| if (srcPan[0] > 0U) |
| { |
| pkt[pktOffset++] = srcPan[1]; |
| pkt[pktOffset++] = srcPan[2]; |
| } |
| else if (dstPan[0] > 0U) |
| { |
| pkt[pktOffset++] = dstPan[1]; |
| pkt[pktOffset++] = dstPan[2]; |
| } |
| else |
| { |
| // Uh-oh! Outgoing packet needs a DstPanId but incoming had neither! |
| // Possibly a latency-induced issue. |
| return false; |
| } |
| } |
| |
| // DstAdr = SrcAdr of incoming packet -- their sizes should match |
| if ((addrSizes & ADDRSIZE_DST_ADR_MASK) != 0U) |
| { |
| for (uint8_t i = 1U; i <= srcAdr[0]; i++) |
| { |
| pkt[pktOffset++] = srcAdr[i]; |
| } |
| } |
| |
| // SrcPan = DstPan of incoming if avail otherwise SrcPan of incoming |
| if ((addrSizes & ADDRSIZE_SRC_PAN_MASK) != 0U) |
| { |
| if (dstPan[0] > 0U) |
| { |
| pkt[pktOffset++] = dstPan[1]; |
| pkt[pktOffset++] = dstPan[2]; |
| } |
| else if (srcPan[0] > 0U) |
| { |
| pkt[pktOffset++] = srcPan[1]; |
| pkt[pktOffset++] = srcPan[2]; |
| } |
| else |
| { |
| // Uh-oh! Outgoing packet needs a SrcPanId but incoming had neither! |
| // Possibly a latency-induced issue. |
| return false; |
| } |
| } |
| |
| // SrcAdr = DstAdr of incoming packet -- their sizes should match |
| if ((addrSizes & ADDRSIZE_SRC_ADR_MASK) != 0U) |
| { |
| for (uint8_t i = 1U; i <= dstAdr[0]; i++) |
| { |
| pkt[pktOffset++] = dstAdr[i]; |
| } |
| } |
| |
| if (ackFcf & IEEE802154_FRAME_FLAG_SECURITY_ENABLED) |
| { |
| memcpy(pkt + pktOffset, securityHeader, securityHeaderLength); |
| pktOffset += securityHeaderLength; |
| } |
| |
| // Now add the IE data |
| memcpy(pkt + pktOffset, aIeData, aIeLength); |
| pktOffset += aIeLength; |
| |
| // Fill in PHR now that we know Enh-ACK's length |
| if (PHRLen == 2U) // Not true till SubGhz implementation is in place |
| { |
| pkt[0] = (0x08U /*FCS=2byte*/ | 0x10U /*Whiten=enabled*/); |
| pkt[1] = (uint8_t)(__RBIT(pktOffset - 2U /*PHRLen*/ + 2U /*FCS*/) >> 24); |
| } |
| else |
| { |
| pkt[0] = (pktOffset - 1U /*PHRLen*/ + 2U /*FCS*/); |
| } |
| |
| processSecurityForEnhancedAck(pkt); |
| |
| return (RAIL_IEEE802154_WriteEnhAck(aRailHandle, pkt, pktOffset) == RAIL_STATUS_NO_ERROR); |
| } |
| #endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE |
| #endif // (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) |
| |
| //------------------------------------------------------------------------------ |
| // RAIL callbacks |
| |
| static void dataRequestCommandCallback(RAIL_Handle_t aRailHandle) |
| { |
| // 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 && OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE) |
| // Check if we need to write an enhanced ACK. |
| if (writeIeee802154EnhancedAck(aRailHandle, sAckIeData, sAckIeDataLength)) |
| { |
| return; |
| } |
| #endif |
| |
| // If not, RAIL will send an immediate ACK, but we need to do FP lookup. |
| RAIL_Status_t status = RAIL_STATUS_NO_ERROR; |
| |
| bool framePendingSet = false; |
| |
| if (sIsSrcMatchEnabled) |
| { |
| RAIL_IEEE802154_Address_t sourceAddress; |
| |
| status = RAIL_IEEE802154_GetAddress(aRailHandle, &sourceAddress); |
| otEXPECT(status == RAIL_STATUS_NO_ERROR); |
| |
| if ((sourceAddress.length == RAIL_IEEE802154_LongAddress && |
| utilsSoftSrcMatchExtFindEntry((otExtAddress *)sourceAddress.longAddress) >= 0) || |
| (sourceAddress.length == RAIL_IEEE802154_ShortAddress && |
| utilsSoftSrcMatchShortFindEntry(sourceAddress.shortAddress) >= 0)) |
| { |
| status = RAIL_IEEE802154_SetFramePending(aRailHandle); |
| otEXPECT(status == RAIL_STATUS_NO_ERROR); |
| framePendingSet = true; |
| } |
| } |
| else |
| { |
| 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. |
| RAIL_RxPacketInfo_t packetInfo; |
| RAIL_GetRxIncomingPacketInfo(gRailHandle, &packetInfo); |
| |
| // skip length byte |
| otEXPECT(packetInfo.firstPortionBytes > 0); |
| packetInfo.firstPortionData++; |
| packetInfo.firstPortionBytes--; |
| packetInfo.packetBytes--; |
| |
| 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 |
| { |
| assert(status == RAIL_STATUS_NO_ERROR); |
| } |
| } |
| |
| static void packetReceivedCallback(RAIL_RxPacketHandle_t packetHandle) |
| { |
| RAIL_RxPacketInfo_t packetInfo; |
| RAIL_RxPacketDetails_t packetDetails; |
| uint16_t length; |
| bool framePendingInAck = false; |
| bool rxCorrupted = false; |
| |
| packetHandle = RAIL_GetRxPacketInfo(gRailHandle, packetHandle, &packetInfo); |
| otEXPECT_ACTION( |
| (packetHandle != RAIL_RX_PACKET_HANDLE_INVALID && packetInfo.packetStatus == RAIL_RX_PACKET_READY_SUCCESS), |
| rxCorrupted = true); |
| |
| otEXPECT_ACTION(validatePacketDetails(packetHandle, &packetDetails, &packetInfo, &length), rxCorrupted = true); |
| |
| // skip length byte |
| otEXPECT_ACTION(packetInfo.firstPortionBytes > 0, rxCorrupted = true); |
| packetInfo.firstPortionData++; |
| packetInfo.firstPortionBytes--; |
| packetInfo.packetBytes--; |
| |
| uint8_t macFcf = |
| ((packetInfo.firstPortionBytes == 0) ? packetInfo.lastPortionData[0] : packetInfo.firstPortionData[0]); |
| |
| if (packetDetails.isAck) |
| { |
| otEXPECT_ACTION( |
| (length == IEEE802154_ACK_LENGTH && (macFcf & IEEE802154_FRAME_TYPE_MASK) == IEEE802154_FRAME_TYPE_ACK), |
| rxCorrupted = true); |
| |
| // read packet |
| RAIL_CopyRxPacket(sReceiveAckFrame.mPsdu, &packetInfo); |
| sReceiveAckFrame.mLength = length; |
| |
| // Releasing the ACK frames here, ensures that the main thread (processNextRxPacket) |
| // is not wasting cycles, releasing the ACK frames from the Rx FIFO queue. |
| RAIL_ReleaseRxPacket(gRailHandle, packetHandle); |
| |
| (void)handlePhyStackEvent(SL_RAIL_UTIL_IEEE802154_STACK_EVENT_RX_ENDED, (uint32_t)isReceivingFrame()); |
| |
| if (txWaitingForAck() && |
| (sReceiveAckFrame.mPsdu[IEEE802154_DSN_OFFSET] == sTransmitFrame.mPsdu[IEEE802154_DSN_OFFSET])) |
| { |
| otEXPECT_ACTION(validatePacketTimestamp(&packetDetails, length), rxCorrupted = true); |
| updateRxFrameDetails(&packetDetails, false); |
| |
| // 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. |
| sTransmitBusy = false; |
| sTransmitError = OT_ERROR_NONE; |
| setInternalFlag(FLAG_WAITING_FOR_ACK, 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_ACK_LENGTH), rxCorrupted = true); |
| |
| 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 (rxCorrupted) |
| { |
| (void)handlePhyStackEvent(SL_RAIL_UTIL_IEEE802154_STACK_EVENT_RX_CORRUPTED, (uint32_t)isReceivingFrame()); |
| } |
| } |
| |
| 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_ONGOING_TX_DATA, 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); |
| sTransmitError = OT_ERROR_NONE; |
| sTransmitBusy = false; |
| } |
| #if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT |
| sRailDebugCounters.mRailEventPacketSent++; |
| #endif |
| } |
| } |
| |
| static void txFailedCallback(bool isAck, uint8_t status) |
| { |
| if (isAck) |
| { |
| setInternalFlag(FLAG_ONGOING_TX_ACK, false); |
| } |
| else if (getInternalFlag(FLAG_ONGOING_TX_DATA)) |
| { |
| if (status == TX_COMPLETE_RESULT_CCA_FAIL) |
| { |
| sTransmitError = OT_ERROR_CHANNEL_ACCESS_FAILURE; |
| setInternalFlag(FLAG_CURRENT_TX_USE_CSMA, false); |
| #if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT |
| sRailDebugCounters.mRailEventChannelBusy++; |
| #endif |
| } |
| else |
| { |
| sTransmitError = OT_ERROR_ABORT; |
| #if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT |
| sRailDebugCounters.mRailEventTxAbort++; |
| #endif |
| } |
| setInternalFlag(FLAG_ONGOING_TX_DATA, false); |
| RAIL_YieldRadio(gRailHandle); |
| sTransmitBusy = false; |
| } |
| } |
| |
| static void ackTimeoutCallback(void) |
| { |
| assert(txWaitingForAck()); |
| assert(getInternalFlag(FLAG_WAITING_FOR_ACK)); |
| |
| sTransmitError = OT_ERROR_NO_ACK; |
| sTransmitBusy = false; |
| #if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT |
| sRailDebugCounters.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_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; |
| } |
| |
| static void schedulerEventCallback(RAIL_Handle_t aRailHandle) |
| { |
| RAIL_SchedulerStatus_t status = RAIL_GetSchedulerStatus(aRailHandle); |
| assert(status != RAIL_SCHEDULER_STATUS_INTERNAL_ERROR); |
| |
| if (status == RAIL_SCHEDULER_STATUS_CCA_CSMA_TX_FAIL || status == RAIL_SCHEDULER_STATUS_SINGLE_TX_FAIL || |
| status == RAIL_SCHEDULER_STATUS_SCHEDULED_TX_FAIL || |
| (status == RAIL_SCHEDULER_STATUS_SCHEDULE_FAIL && sTransmitBusy) || |
| (status == RAIL_SCHEDULER_STATUS_EVENT_INTERRUPTED && sTransmitBusy)) |
| { |
| if (getInternalFlag(FLAG_ONGOING_TX_ACK)) |
| { |
| (void)handlePhyStackEvent(SL_RAIL_UTIL_IEEE802154_STACK_EVENT_RX_ACK_ABORTED, (uint32_t)isReceivingFrame()); |
| txFailedCallback(true, 0xFF); |
| } |
| // 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, TX_COMPLETE_RESULT_CCA_FAIL); |
| } |
| |
| #if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT |
| sRailDebugCounters.mRailEventSchedulerStatusError++; |
| #endif |
| } |
| else if (status == RAIL_SCHEDULER_STATUS_AVERAGE_RSSI_FAIL || |
| (status == RAIL_SCHEDULER_STATUS_SCHEDULE_FAIL && sEnergyScanStatus == ENERGY_SCAN_STATUS_IN_PROGRESS)) |
| { |
| energyScanComplete(OT_RADIO_RSSI_INVALID); |
| } |
| #if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT |
| else if (sTransmitBusy) |
| { |
| sRailDebugCounters.mRailEventsSchedulerStatusLastStatus = status; |
| sRailDebugCounters.mRailEventsSchedulerStatusTransmitBusy++; |
| } |
| #endif |
| } |
| |
| static void configUnscheduledCallback(void) |
| { |
| // 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(); |
| } |
| |
| // We are about to send an ACK, which it won't happen. |
| if (getInternalFlag(FLAG_ONGOING_TX_ACK)) |
| { |
| txFailedCallback(true, 0xFF); |
| } |
| } |
| |
| 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 |
| |
| 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, TX_COMPLETE_RESULT_CCA_FAIL); |
| } |
| else if (aEvents & RAIL_EVENT_TX_BLOCKED) |
| { |
| (void)handlePhyStackEvent(SL_RAIL_UTIL_IEEE802154_STACK_EVENT_TX_BLOCKED, (uint32_t)txWaitingForAck()); |
| txFailedCallback(false, TX_COMPLETE_RESULT_OTHER_FAIL); |
| } |
| 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, TX_COMPLETE_RESULT_OTHER_FAIL); |
| } |
| 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 (aEvents & RAIL_EVENT_RX_PACKET_RECEIVED) |
| { |
| packetReceivedCallback(RAIL_HoldRxPacket(aRailHandle)); |
| #if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT |
| sRailDebugCounters.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); |
| configUnscheduledCallback(); |
| #if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT |
| sRailDebugCounters.mRailEventConfigUnScheduled++; |
| #endif |
| } |
| |
| if (aEvents & RAIL_EVENT_CONFIG_SCHEDULED) |
| { |
| #if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT |
| sRailDebugCounters.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)) |
| assert(status == RAIL_STATUS_NO_ERROR); |
| #endif |
| |
| #if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT |
| sRailDebugCounters.mRailEventCalNeeded++; |
| #endif |
| } |
| |
| if (aEvents & RAIL_EVENT_RSSI_AVERAGE_DONE) |
| { |
| const int16_t energyScanResultQuarterDbm = RAIL_GetAverageRssi(aRailHandle); |
| RAIL_YieldRadio(aRailHandle); |
| |
| energyScanComplete(energyScanResultQuarterDbm == RAIL_RSSI_INVALID |
| ? OT_RADIO_RSSI_INVALID |
| : (energyScanResultQuarterDbm / QUARTER_DBM_IN_DBM)); |
| #if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT |
| sRailDebugCounters.mRailPlatRadioEnergyScanDoneCbCount++; |
| #endif |
| } |
| |
| otSysEventSignalPending(); |
| } |
| |
| //------------------------------------------------------------------------------ |
| // Main thread packet handling |
| |
| static bool validatePacketDetails(RAIL_RxPacketHandle_t packetHandle, |
| RAIL_RxPacketDetails_t *pPacketDetails, |
| RAIL_RxPacketInfo_t * pPacketInfo, |
| uint16_t * packetLength) |
| { |
| bool pktValid = true; |
| |
| otEXPECT_ACTION((RAIL_GetRxPacketDetailsAlt(gRailHandle, packetHandle, pPacketDetails) == RAIL_STATUS_NO_ERROR), |
| pktValid = false); |
| |
| // RAIL's packetBytes includes the 1-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 1 for PHY header. |
| *packetLength = pPacketInfo->packetBytes + 1; |
| |
| // check the length in recv packet info structure; RAIL should take care of this. |
| otEXPECT_ACTION(*packetLength == pPacketInfo->firstPortionData[0], pktValid = false); |
| |
| // 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); |
| exit: |
| return pktValid; |
| } |
| |
| 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); |
| |
| // + 1 for the 1-byte PHY header |
| pPacketDetails->timeReceived.totalPacketBytes = packetLength + 1; |
| |
| otEXPECT_ACTION((RAIL_GetRxTimeSyncWordEndAlt(gRailHandle, pPacketDetails) == RAIL_STATUS_NO_ERROR), |
| rxTimestampValid = false); |
| exit: |
| return rxTimestampValid; |
| } |
| |
| static void updateRxFrameDetails(RAIL_RxPacketDetails_t *pPacketDetails, bool framePendingSetInOutgoingAck) |
| { |
| assert(pPacketDetails != NULL); |
| |
| if (pPacketDetails->isAck) |
| { |
| sReceiveAckFrame.mInfo.mRxInfo.mRssi = pPacketDetails->rssi; |
| sReceiveAckFrame.mInfo.mRxInfo.mLqi = pPacketDetails->lqi; |
| sReceiveAckFrame.mInfo.mRxInfo.mTimestamp = pPacketDetails->timeReceived.packetTime; |
| } |
| else |
| { |
| sReceiveFrame.mInfo.mRxInfo.mRssi = pPacketDetails->rssi; |
| sReceiveFrame.mInfo.mRxInfo.mLqi = pPacketDetails->lqi; |
| sReceiveFrame.mInfo.mRxInfo.mTimestamp = pPacketDetails->timeReceived.packetTime; |
| // Set this flag only when the packet is really acknowledged with frame pending set. |
| sReceiveFrame.mInfo.mRxInfo.mAckedWithFramePending = framePendingSetInOutgoingAck; |
| } |
| } |
| |
| static void processNextRxPacket(otInstance *aInstance) |
| { |
| RAIL_RxPacketHandle_t packetHandle = RAIL_RX_PACKET_HANDLE_INVALID; |
| RAIL_RxPacketInfo_t packetInfo; |
| RAIL_RxPacketDetails_t packetDetails; |
| RAIL_Status_t status; |
| uint16_t length; |
| bool rxProcessDone = false; |
| |
| CORE_DECLARE_IRQ_STATE; |
| CORE_ENTER_ATOMIC(); |
| |
| packetHandle = RAIL_GetRxPacketInfo(gRailHandle, RAIL_RX_PACKET_HANDLE_OLDEST_COMPLETE, &packetInfo); |
| otEXPECT_ACTION( |
| (packetHandle != RAIL_RX_PACKET_HANDLE_INVALID && packetInfo.packetStatus == RAIL_RX_PACKET_READY_SUCCESS), |
| packetHandle = RAIL_RX_PACKET_HANDLE_INVALID); |
| |
| otEXPECT(validatePacketDetails(packetHandle, &packetDetails, &packetInfo, &length)); |
| |
| // skip length byte |
| otEXPECT(packetInfo.firstPortionBytes > 0); |
| packetInfo.firstPortionData++; |
| packetInfo.firstPortionBytes--; |
| packetInfo.packetBytes--; |
| |
| // As received ACK frames are already processed in packetReceivedCallback, |
| // we only need to read and process the non-ACK frames here. |
| otEXPECT(sPromiscuous || (!packetDetails.isAck && (length != IEEE802154_ACK_LENGTH))); |
| |
| // read packet |
| RAIL_CopyRxPacket(sReceiveFrame.mPsdu, &packetInfo); |
| sReceiveFrame.mLength = length; |
| |
| uint8_t *macFcfPointer = sReceiveFrame.mPsdu; |
| |
| // Check the reserved bit in the MAC header to see whether the frame pending |
| // bit was set in the outgoing ACK |
| // Then, clear it. |
| bool framePendingSetInOutgoingAck = ((*macFcfPointer & IEEE802154_FRAME_PENDING_SET_IN_OUTGOING_ACK) != 0); |
| *macFcfPointer &= ~IEEE802154_FRAME_PENDING_SET_IN_OUTGOING_ACK; |
| |
| status = RAIL_ReleaseRxPacket(gRailHandle, packetHandle); |
| if (status == RAIL_STATUS_NO_ERROR) |
| { |
| packetHandle = RAIL_RX_PACKET_HANDLE_INVALID; |
| } |
| |
| otEXPECT(validatePacketTimestamp(&packetDetails, length)); |
| updateRxFrameDetails(&packetDetails, framePendingSetInOutgoingAck); |
| rxProcessDone = true; |
| |
| exit: |
| if (packetHandle != RAIL_RX_PACKET_HANDLE_INVALID) |
| { |
| RAIL_ReleaseRxPacket(gRailHandle, packetHandle); |
| } |
| CORE_EXIT_ATOMIC(); |
| |
| // signal MAC layer |
| if (rxProcessDone) |
| { |
| sReceiveError = OT_ERROR_NONE; |
| |
| #if OPENTHREAD_CONFIG_DIAG_ENABLE |
| if (otPlatDiagModeGet()) |
| { |
| otPlatDiagRadioReceiveDone(aInstance, &sReceiveFrame, sReceiveError); |
| } |
| else |
| #endif |
| { |
| otLogInfoPlat("Received %d bytes", sReceiveFrame.mLength); |
| otPlatRadioReceiveDone(aInstance, &sReceiveFrame, sReceiveError); |
| #if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT |
| sRailDebugCounters.mRailPlatRadioReceiveDoneCbCount++; |
| #endif |
| } |
| otSysEventSignalPending(); |
| } |
| } |
| |
| static void processTxComplete(otInstance *aInstance) |
| { |
| if (sState == OT_RADIO_STATE_TRANSMIT && sTransmitBusy == false) |
| { |
| if (sTransmitError != OT_ERROR_NONE) |
| { |
| otLogDebgPlat("Transmit failed ErrorCode=%d", sTransmitError); |
| } |
| |
| sState = OT_RADIO_STATE_RECEIVE; |
| #if OPENTHREAD_CONFIG_DIAG_ENABLE |
| if (otPlatDiagModeGet()) |
| { |
| otPlatDiagRadioTransmitDone(aInstance, &sTransmitFrame, sTransmitError); |
| } |
| else |
| #endif |
| if (((sTransmitFrame.mPsdu[0] & IEEE802154_FRAME_FLAG_ACK_REQUIRED) == 0) || |
| (sTransmitError != OT_ERROR_NONE)) |
| { |
| otPlatRadioTxDone(aInstance, &sTransmitFrame, NULL, sTransmitError); |
| } |
| else |
| { |
| otPlatRadioTxDone(aInstance, &sTransmitFrame, &sReceiveAckFrame, sTransmitError); |
| } |
| |
| #if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT |
| sRailDebugCounters.mRailPlatRadioTxDoneCbCount++; |
| #endif |
| otSysEventSignalPending(); |
| } |
| } |
| |
| 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() |
| processNextRxPacket(aInstance); |
| processTxComplete(aInstance); |
| |
| if (sEnergyScanMode == ENERGY_SCAN_MODE_ASYNC && sEnergyScanStatus == ENERGY_SCAN_STATUS_COMPLETED) |
| { |
| sEnergyScanStatus = ENERGY_SCAN_STATUS_IDLE; |
| otPlatRadioEnergyScanDone(aInstance, sEnergyScanResultDbm); |
| otSysEventSignalPending(); |
| |
| #if RADIO_CONFIG_DEBUG_COUNTERS_SUPPORT |
| sRailDebugCounters.mRailEventEnergyScanCompleted++; |
| #endif |
| } |
| } |
| |
| //------------------------------------------------------------------------------ |
| // Antenn Diveristy, Wifi coexistence and Run time PHY select support |
| |
| #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(ONGOING_TX_FLAGS)), error = OT_ERROR_FAILED); |
| |
| switch (state) |
| { |
| case OT_RADIO_STATE_RECEIVE: |
| otEXPECT_ACTION(radioSetRx(sReceiveFrame.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 = sState; |
| otEXPECT(setRadioState(OT_RADIO_STATE_SLEEP) == OT_ERROR_NONE); |
| sl_rail_util_plugin_config_2p4ghz_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(); |
| 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) |
| { |
| 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_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. |
| assert(RAIL_ConfigRxOptions(gRailHandle, RAIL_RX_OPTION_TRACK_ABORTED_FRAMES, |
| (enable ? RAIL_RX_OPTIONS_NONE : RAIL_RX_OPTION_TRACK_ABORTED_FRAMES)) == |
| RAIL_STATUS_NO_ERROR); |
| } |
| |
| static void efr32CoexInit(void) |
| { |
| 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; |
| #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; |
| |
| 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) |
| { |
| 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, TX_COMPLETE_RESULT_OTHER_FAIL); |
| } |
| } |
| |
| // 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; |
| } |
| assert(RAIL_SetCcaThreshold(gRailHandle, thresholddBm) == 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 |
| void emRadioHoldOffIsr(bool active) |
| { |
| emRadioHoldOffInternalIsr((uint8_t)active | (rhoActive & ~RHO_EXT_ACTIVE)); |
| } |
| |
| #if SL_OPENTHREAD_COEX_COUNTER_ENABLE |
| |
| void sl_rail_util_coex_counter_on_event(sl_rail_util_coex_event_t event) |
| { |
| otEXPECT(event < SL_RAIL_UTIL_COEX_EVENT_COUNT); |
| sCoexCounters[event] += 1; |
| exit: |
| return; |
| } |
| |
| void efr32RadioGetCoexCounters(uint32_t (*aCoexCounters)[SL_RAIL_UTIL_COEX_EVENT_COUNT]) |
| { |
| memset((void *)aCoexCounters, 0, sizeof(*aCoexCounters)); |
| memcpy(aCoexCounters, sCoexCounters, sizeof(*aCoexCounters)); |
| } |
| |
| void efr32RadioClearCoexCounters(void) |
| { |
| memset((void *)sCoexCounters, 0, sizeof(sCoexCounters)); |
| } |
| |
| #endif // SL_OPENTHREAD_COEX_COUNTER_ENABLE |
| #endif // SL_CATALOG_RAIL_UTIL_COEX_PRESENT |
| |
| RAIL_AntennaConfig_t halAntennaConfig; |
| |
| void initAntenna(void) |
| { |
| #if (HAL_ANTDIV_ENABLE && defined(BSP_ANTDIV_SEL_PORT) && defined(BSP_ANTDIV_SEL_PIN) && defined(BSP_ANTDIV_SEL_LOC)) |
| halAntennaConfig.ant0PinEn = true; |
| halAntennaConfig.ant0Port = (uint8_t)BSP_ANTDIV_SEL_PORT; |
| halAntennaConfig.ant0Pin = BSP_ANTDIV_SEL_PIN; |
| halAntennaConfig.ant0Loc = BSP_ANTDIV_SEL_LOC; |
| #endif |
| #ifdef _SILICON_LABS_32B_SERIES_2 |
| halAntennaConfig.defaultPath = BSP_ANTDIV_SEL_LOC; |
| #endif |
| #if (HAL_ANTDIV_ENABLE && defined(BSP_ANTDIV_NSEL_PORT) && defined(BSP_ANTDIV_NSEL_PIN) && defined(BSP_ANTDIV_NSEL_LOC)) |
| halAntennaConfig.ant1PinEn = true; |
| halAntennaConfig.ant1Port = (uint8_t)BSP_ANTDIV_NSEL_PORT; |
| halAntennaConfig.ant1Pin = BSP_ANTDIV_NSEL_PIN; |
| halAntennaConfig.ant1Loc = BSP_ANTDIV_NSEL_LOC; |
| #endif |
| #if (HAL_ANTDIV_ENABLE || defined(_SILICON_LABS_32B_SERIES_2)) |
| (void)RAIL_ConfigAntenna(RAIL_EFR32_HANDLE, &halAntennaConfig); |
| #endif |
| } |