| /* |
| * Copyright (c) 2019, 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 <openthread-core-config.h> |
| #include <openthread/config.h> |
| |
| #include <assert.h> |
| #include <stdbool.h> |
| #include <stddef.h> |
| #include <stdint.h> |
| #include <string.h> |
| |
| #include <openthread/link.h> |
| |
| #include "common/code_utils.hpp" |
| #include "utils/code_utils.h" |
| #include "utils/link_metrics.h" |
| #include "utils/mac_frame.h" |
| |
| #include <platform-config.h> |
| #include <openthread/platform/alarm-micro.h> |
| #include <openthread/platform/diag.h> |
| #include <openthread/platform/radio.h> |
| #include <openthread/platform/time.h> |
| |
| #include "openthread-system.h" |
| #include "platform-fem.h" |
| #include "platform-nrf5.h" |
| |
| #include <nrf.h> |
| #include <nrf_802154.h> |
| #include <nrf_802154_pib.h> |
| |
| #include <openthread-core-config.h> |
| #include <openthread/config.h> |
| #include <openthread/random_noncrypto.h> |
| |
| // clang-format off |
| |
| #define SHORT_ADDRESS_SIZE 2 ///< Size of MAC short address. |
| #define US_PER_MS 1000ULL ///< Microseconds in millisecond. |
| |
| #define ACK_REQUEST_OFFSET 1 ///< Byte containing Ack request bit (+1 for frame length byte). |
| #define ACK_REQUEST_BIT (1 << 5) ///< Ack request bit. |
| #define FRAME_PENDING_OFFSET 1 ///< Byte containing pending bit (+1 for frame length byte). |
| #define FRAME_PENDING_BIT (1 << 4) ///< Frame Pending bit. |
| #define SECURITY_ENABLED_OFFSET 1 ///< Byte containing security enabled bit (+1 for frame length byte). |
| #define SECURITY_ENABLED_BIT (1 << 3) ///< Security enabled bit. |
| |
| #define RSSI_SETTLE_TIME_US 40 ///< RSSI settle time in microseconds. |
| |
| #if defined(__ICCARM__) |
| _Pragma("diag_suppress=Pe167") |
| #endif |
| |
| enum |
| { |
| NRF528XX_RECEIVE_SENSITIVITY = -100, // dBm |
| NRF528XX_MIN_CCA_ED_THRESHOLD = -94, // dBm |
| }; |
| |
| // clang-format on |
| |
| static bool sDisabled; |
| |
| static otError sReceiveError = OT_ERROR_NONE; |
| static otRadioFrame sReceivedFrames[NRF_802154_RX_BUFFERS]; |
| static otRadioFrame sTransmitFrame; |
| static uint8_t sTransmitPsdu[OT_RADIO_FRAME_MAX_SIZE + 1]; |
| |
| #if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT |
| static otExtAddress sExtAddress; |
| static otRadioIeInfo sTransmitIeInfo; |
| static otInstance * sInstance = NULL; |
| #endif |
| |
| static otRadioFrame sAckFrame; |
| static bool sAckedWithFramePending; |
| |
| static int8_t sMaxTxPowerTable[OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MAX - OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MIN + 1]; |
| static int8_t sDefaultTxPower; |
| static int8_t sLnaGain = 0; |
| static uint16_t sRegionCode = 0; |
| |
| static uint32_t sEnergyDetectionTime; |
| static uint8_t sEnergyDetectionChannel; |
| static int8_t sEnergyDetected; |
| |
| #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE |
| static uint32_t sCslPeriod; |
| static uint32_t sCslSampleTime; |
| static const uint32_t sCslSampleDur = OPENTHREAD_CONFIG_CSL_SAMPLE_WINDOW * OT_US_PER_TEN_SYMBOLS; |
| static const uint8_t sCslIeHeader[OT_IE_HEADER_SIZE] = {CSL_IE_HEADER_BYTES_LO, CSL_IE_HEADER_BYTES_HI}; |
| #endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE |
| |
| typedef enum |
| { |
| kPendingEventSleep, // Requested to enter Sleep state. |
| kPendingEventFrameTransmitted, // Transmitted frame and received ACK (if requested). |
| kPendingEventChannelAccessFailure, // Failed to transmit frame (channel busy). |
| kPendingEventInvalidOrNoAck, // Failed to transmit frame (received invalid or no ACK). |
| kPendingEventReceiveFailed, // Failed to receive a valid frame. |
| kPendingEventEnergyDetectionStart, // Requested to start Energy Detection procedure. |
| kPendingEventEnergyDetected, // Energy Detection finished. |
| } RadioPendingEvents; |
| |
| static uint32_t sPendingEvents; |
| |
| #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 |
| static uint32_t sMacFrameCounter; |
| static uint8_t sKeyId; |
| static struct otMacKey sPrevKey; |
| static struct otMacKey sCurrKey; |
| static struct otMacKey sNextKey; |
| static bool sAckedWithSecEnhAck; |
| static uint32_t sAckFrameCounter; |
| static uint8_t sAckKeyId; |
| #endif |
| |
| static int8_t GetTransmitPowerForChannel(uint8_t aChannel) |
| { |
| int8_t channelMaxPower = nrf5GetChannelMaxTransmitPower(aChannel); |
| int8_t power = 0; // 0 dbm as default value |
| |
| if (sDefaultTxPower != OT_RADIO_POWER_INVALID) |
| { |
| power = OT_MIN(channelMaxPower, sDefaultTxPower); |
| } |
| else if (channelMaxPower != OT_RADIO_POWER_INVALID) |
| { |
| power = channelMaxPower; |
| } |
| |
| return power; |
| } |
| |
| static void dataInit(void) |
| { |
| sDisabled = true; |
| |
| sDefaultTxPower = OT_RADIO_POWER_INVALID; |
| sTransmitFrame.mPsdu = sTransmitPsdu + 1; |
| #if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT |
| sTransmitFrame.mInfo.mTxInfo.mIeInfo = &sTransmitIeInfo; |
| #endif |
| |
| sReceiveError = OT_ERROR_NONE; |
| |
| for (uint32_t i = 0; i < NRF_802154_RX_BUFFERS; i++) |
| { |
| sReceivedFrames[i].mPsdu = NULL; |
| } |
| |
| for (size_t i = 0; i < OT_ARRAY_LENGTH(sMaxTxPowerTable); i++) |
| { |
| sMaxTxPowerTable[i] = OT_RADIO_POWER_INVALID; |
| } |
| |
| memset(&sAckFrame, 0, sizeof(sAckFrame)); |
| } |
| |
| static void convertShortAddress(uint8_t *aTo, uint16_t aFrom) |
| { |
| aTo[0] = (uint8_t)aFrom; |
| aTo[1] = (uint8_t)(aFrom >> 8); |
| } |
| |
| #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE |
| static void convertExtAddress(uint8_t *aTo, const otExtAddress *aFrom) |
| { |
| for (uint8_t i = 0; i < sizeof(otExtAddress); i++) |
| { |
| aTo[i] = aFrom->m8[sizeof(otExtAddress) - i - 1]; |
| } |
| } |
| #endif |
| |
| static inline bool isPendingEventSet(RadioPendingEvents aEvent) |
| { |
| return sPendingEvents & (1UL << aEvent); |
| } |
| |
| static void setPendingEvent(RadioPendingEvents aEvent) |
| { |
| volatile uint32_t pendingEvents; |
| uint32_t bitToSet = 1UL << aEvent; |
| |
| do |
| { |
| pendingEvents = __LDREXW((uint32_t *)&sPendingEvents); |
| pendingEvents |= bitToSet; |
| } while (__STREXW(pendingEvents, (uint32_t *)&sPendingEvents)); |
| |
| otSysEventSignalPending(); |
| } |
| |
| static void resetPendingEvent(RadioPendingEvents aEvent) |
| { |
| volatile uint32_t pendingEvents; |
| uint32_t bitsToRemain = ~(1UL << aEvent); |
| |
| do |
| { |
| pendingEvents = __LDREXW((uint32_t *)&sPendingEvents); |
| pendingEvents &= bitsToRemain; |
| } while (__STREXW(pendingEvents, (uint32_t *)&sPendingEvents)); |
| } |
| |
| static inline void clearPendingEvents(void) |
| { |
| // Clear pending events that could cause race in the MAC layer. |
| volatile uint32_t pendingEvents; |
| uint32_t bitsToRemain = ~(0UL); |
| |
| bitsToRemain &= ~(1UL << kPendingEventSleep); |
| |
| do |
| { |
| pendingEvents = __LDREXW((uint32_t *)&sPendingEvents); |
| pendingEvents &= bitsToRemain; |
| } while (__STREXW(pendingEvents, (uint32_t *)&sPendingEvents)); |
| } |
| |
| #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 |
| static void txAckProcessSecurity(uint8_t *aAckFrame) |
| { |
| otRadioFrame ackFrame; |
| struct otMacKey *key = NULL; |
| uint8_t keyId; |
| |
| sAckedWithSecEnhAck = false; |
| otEXPECT(aAckFrame[SECURITY_ENABLED_OFFSET] & SECURITY_ENABLED_BIT); |
| |
| 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++); |
| |
| otMacFrameProcessTransmitAesCcm(&ackFrame, &sExtAddress); |
| |
| exit: |
| return; |
| } |
| #endif // OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 |
| |
| #if !OPENTHREAD_CONFIG_ENABLE_PLATFORM_EUI64_CUSTOM_SOURCE |
| void otPlatRadioGetIeeeEui64(otInstance *aInstance, uint8_t *aIeeeEui64) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| uint64_t factoryAddress; |
| uint32_t index = 0; |
| |
| // Set the MAC Address Block Larger (MA-L) formerly called OUI. |
| aIeeeEui64[index++] = (OPENTHREAD_CONFIG_STACK_VENDOR_OUI >> 16) & 0xff; |
| aIeeeEui64[index++] = (OPENTHREAD_CONFIG_STACK_VENDOR_OUI >> 8) & 0xff; |
| aIeeeEui64[index++] = OPENTHREAD_CONFIG_STACK_VENDOR_OUI & 0xff; |
| |
| // Use device identifier assigned during the production. |
| factoryAddress = (uint64_t)NRF_FICR->DEVICEID[0] << 32; |
| factoryAddress |= NRF_FICR->DEVICEID[1]; |
| memcpy(aIeeeEui64 + index, &factoryAddress, sizeof(factoryAddress) - index); |
| } |
| #endif // OPENTHREAD_CONFIG_ENABLE_PLATFORM_EUI64_CUSTOM_SOURCE |
| |
| void otPlatRadioSetPanId(otInstance *aInstance, uint16_t aPanId) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| uint8_t address[SHORT_ADDRESS_SIZE]; |
| convertShortAddress(address, aPanId); |
| |
| nrf_802154_pan_id_set(address); |
| } |
| |
| void otPlatRadioSetExtendedAddress(otInstance *aInstance, const otExtAddress *aExtAddress) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| #if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT |
| for (size_t i = 0; i < sizeof(*aExtAddress); i++) |
| { |
| sExtAddress.m8[i] = aExtAddress->m8[sizeof(*aExtAddress) - 1 - i]; |
| } |
| #endif |
| nrf_802154_extended_address_set(aExtAddress->m8); |
| } |
| |
| void otPlatRadioSetShortAddress(otInstance *aInstance, uint16_t aShortAddress) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| uint8_t address[SHORT_ADDRESS_SIZE]; |
| convertShortAddress(address, aShortAddress); |
| |
| nrf_802154_short_address_set(address); |
| } |
| |
| void nrf5RadioInit(void) |
| { |
| dataInit(); |
| nrf_802154_init(); |
| } |
| |
| void nrf5RadioDeinit(void) |
| { |
| nrf_802154_sleep(); |
| nrf_802154_deinit(); |
| sPendingEvents = 0; |
| } |
| |
| void nrf5RadioClearPendingEvents(void) |
| { |
| sPendingEvents = 0; |
| |
| for (uint32_t i = 0; i < NRF_802154_RX_BUFFERS; i++) |
| { |
| if (sReceivedFrames[i].mPsdu != NULL) |
| { |
| uint8_t *bufferAddress = &sReceivedFrames[i].mPsdu[-1]; |
| sReceivedFrames[i].mPsdu = NULL; |
| nrf_802154_buffer_free_raw(bufferAddress); |
| } |
| } |
| } |
| |
| otRadioState otPlatRadioGetState(otInstance *aInstance) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| if (sDisabled) |
| { |
| return OT_RADIO_STATE_DISABLED; |
| } |
| |
| switch (nrf_802154_state_get()) |
| { |
| case NRF_802154_STATE_SLEEP: |
| return OT_RADIO_STATE_SLEEP; |
| |
| case NRF_802154_STATE_RECEIVE: |
| case NRF_802154_STATE_ENERGY_DETECTION: |
| return OT_RADIO_STATE_RECEIVE; |
| |
| case NRF_802154_STATE_TRANSMIT: |
| case NRF_802154_STATE_CCA: |
| case NRF_802154_STATE_CONTINUOUS_CARRIER: |
| return OT_RADIO_STATE_TRANSMIT; |
| |
| default: |
| assert(false); // Make sure driver returned valid state. |
| } |
| |
| return OT_RADIO_STATE_RECEIVE; // It is the default state. Return it in case of unknown. |
| } |
| |
| bool otPlatRadioIsEnabled(otInstance *aInstance) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| return !sDisabled; |
| } |
| |
| otError otPlatRadioEnable(otInstance *aInstance) |
| { |
| otError error; |
| |
| #if !OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT |
| OT_UNUSED_VARIABLE(aInstance); |
| #else |
| sInstance = aInstance; |
| #endif |
| |
| if (sDisabled) |
| { |
| sDisabled = false; |
| error = OT_ERROR_NONE; |
| } |
| else |
| { |
| error = OT_ERROR_INVALID_STATE; |
| } |
| |
| return error; |
| } |
| |
| otError otPlatRadioDisable(otInstance *aInstance) |
| { |
| otError error = OT_ERROR_NONE; |
| |
| otEXPECT(otPlatRadioIsEnabled(aInstance)); |
| otEXPECT_ACTION(otPlatRadioGetState(aInstance) == OT_RADIO_STATE_SLEEP || isPendingEventSet(kPendingEventSleep), |
| error = OT_ERROR_INVALID_STATE); |
| |
| sDisabled = true; |
| |
| exit: |
| return error; |
| } |
| |
| otError otPlatRadioSleep(otInstance *aInstance) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| if (nrf_802154_sleep_if_idle() == NRF_802154_SLEEP_ERROR_NONE) |
| { |
| nrf5FemDisable(); |
| clearPendingEvents(); |
| } |
| else |
| { |
| clearPendingEvents(); |
| setPendingEvent(kPendingEventSleep); |
| } |
| |
| return OT_ERROR_NONE; |
| } |
| |
| otError otPlatRadioReceive(otInstance *aInstance, uint8_t aChannel) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| bool result; |
| |
| nrf_802154_channel_set(aChannel); |
| if (nrf_802154_state_get() == NRF_802154_STATE_SLEEP) |
| { |
| // Enable FEM before RADIO leaving SLEEP state. |
| nrf5FemEnable(); |
| } |
| |
| nrf_802154_tx_power_set(GetTransmitPowerForChannel(aChannel)); |
| |
| result = nrf_802154_receive(); |
| clearPendingEvents(); |
| |
| return result ? OT_ERROR_NONE : OT_ERROR_INVALID_STATE; |
| } |
| |
| otError otPlatRadioTransmit(otInstance *aInstance, otRadioFrame *aFrame) |
| { |
| bool result = true; |
| otError error = OT_ERROR_NONE; |
| |
| aFrame->mPsdu[-1] = aFrame->mLength; |
| |
| if (nrf_802154_state_get() == NRF_802154_STATE_SLEEP) |
| { |
| // Enable FEM before RADIO leaving SLEEP state. |
| nrf5FemEnable(); |
| } |
| |
| #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 |
| if (aFrame->mInfo.mTxInfo.mTxDelay != 0) |
| { |
| if (!nrf_802154_transmit_raw_at(&aFrame->mPsdu[-1], true, aFrame->mInfo.mTxInfo.mTxDelayBaseTime, |
| aFrame->mInfo.mTxInfo.mTxDelay, aFrame->mChannel)) |
| { |
| error = OT_ERROR_INVALID_STATE; |
| } |
| } |
| else |
| #endif |
| { |
| nrf_802154_channel_set(aFrame->mChannel); |
| |
| if (aFrame->mInfo.mTxInfo.mCsmaCaEnabled) |
| { |
| nrf_802154_transmit_csma_ca_raw(&aFrame->mPsdu[-1]); |
| } |
| else |
| { |
| result = nrf_802154_transmit_raw(&aFrame->mPsdu[-1], false); |
| } |
| } |
| |
| clearPendingEvents(); |
| otPlatRadioTxStarted(aInstance, aFrame); |
| |
| if (!result) |
| { |
| setPendingEvent(kPendingEventChannelAccessFailure); |
| } |
| |
| return error; |
| } |
| |
| otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *aInstance) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| return &sTransmitFrame; |
| } |
| |
| int8_t otPlatRadioGetRssi(otInstance *aInstance) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| // Ensure the RSSI measurement is done after RSSI settling time. |
| // This is necessary for the Channel Monitor feature which quickly switches between channels. |
| NRFX_DELAY_US(RSSI_SETTLE_TIME_US); |
| |
| nrf_802154_rssi_measure_begin(); |
| |
| return nrf_802154_rssi_last_get(); |
| } |
| |
| otRadioCaps otPlatRadioGetCaps(otInstance *aInstance) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| return (otRadioCaps)(OT_RADIO_CAPS_ENERGY_SCAN | OT_RADIO_CAPS_ACK_TIMEOUT | OT_RADIO_CAPS_CSMA_BACKOFF | |
| #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 |
| OT_RADIO_CAPS_TRANSMIT_SEC | OT_RADIO_CAPS_TRANSMIT_TIMING | |
| #endif |
| OT_RADIO_CAPS_SLEEP_TO_TX); |
| } |
| |
| bool otPlatRadioGetPromiscuous(otInstance *aInstance) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| return nrf_802154_promiscuous_get(); |
| } |
| |
| void otPlatRadioSetPromiscuous(otInstance *aInstance, bool aEnable) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| nrf_802154_promiscuous_set(aEnable); |
| } |
| |
| void otPlatRadioEnableSrcMatch(otInstance *aInstance, bool aEnable) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| nrf_802154_auto_pending_bit_set(aEnable); |
| } |
| |
| otError otPlatRadioAddSrcMatchShortEntry(otInstance *aInstance, uint16_t aShortAddress) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| otError error; |
| |
| uint8_t shortAddress[SHORT_ADDRESS_SIZE]; |
| convertShortAddress(shortAddress, aShortAddress); |
| |
| if (nrf_802154_pending_bit_for_addr_set(shortAddress, false)) |
| { |
| error = OT_ERROR_NONE; |
| } |
| else |
| { |
| error = OT_ERROR_NO_BUFS; |
| } |
| |
| return error; |
| } |
| |
| otError otPlatRadioAddSrcMatchExtEntry(otInstance *aInstance, const otExtAddress *aExtAddress) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| otError error; |
| |
| if (nrf_802154_pending_bit_for_addr_set(aExtAddress->m8, true)) |
| { |
| error = OT_ERROR_NONE; |
| } |
| else |
| { |
| error = OT_ERROR_NO_BUFS; |
| } |
| |
| return error; |
| } |
| |
| otError otPlatRadioClearSrcMatchShortEntry(otInstance *aInstance, uint16_t aShortAddress) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| otError error; |
| |
| uint8_t shortAddress[SHORT_ADDRESS_SIZE]; |
| convertShortAddress(shortAddress, aShortAddress); |
| |
| if (nrf_802154_pending_bit_for_addr_clear(shortAddress, false)) |
| { |
| error = OT_ERROR_NONE; |
| } |
| else |
| { |
| error = OT_ERROR_NO_ADDRESS; |
| } |
| |
| return error; |
| } |
| |
| otError otPlatRadioClearSrcMatchExtEntry(otInstance *aInstance, const otExtAddress *aExtAddress) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| otError error; |
| |
| if (nrf_802154_pending_bit_for_addr_clear(aExtAddress->m8, true)) |
| { |
| error = OT_ERROR_NONE; |
| } |
| else |
| { |
| error = OT_ERROR_NO_ADDRESS; |
| } |
| |
| return error; |
| } |
| |
| void otPlatRadioClearSrcMatchShortEntries(otInstance *aInstance) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| nrf_802154_pending_bit_for_addr_reset(false); |
| } |
| |
| void otPlatRadioClearSrcMatchExtEntries(otInstance *aInstance) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| nrf_802154_pending_bit_for_addr_reset(true); |
| } |
| |
| otError otPlatRadioEnergyScan(otInstance *aInstance, uint8_t aScanChannel, uint16_t aScanDuration) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| sEnergyDetectionTime = (uint32_t)aScanDuration * 1000UL; |
| sEnergyDetectionChannel = aScanChannel; |
| |
| clearPendingEvents(); |
| |
| nrf_802154_channel_set(aScanChannel); |
| |
| if (nrf_802154_energy_detection(sEnergyDetectionTime)) |
| { |
| resetPendingEvent(kPendingEventEnergyDetectionStart); |
| } |
| else |
| { |
| setPendingEvent(kPendingEventEnergyDetectionStart); |
| } |
| |
| return OT_ERROR_NONE; |
| } |
| |
| otError otPlatRadioGetTransmitPower(otInstance *aInstance, int8_t *aPower) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| otError error = OT_ERROR_NONE; |
| |
| if (aPower == NULL) |
| { |
| error = OT_ERROR_INVALID_ARGS; |
| } |
| else |
| { |
| *aPower = nrf_802154_tx_power_get(); |
| } |
| |
| return error; |
| } |
| |
| otError otPlatRadioSetTransmitPower(otInstance *aInstance, int8_t aPower) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| uint8_t channel = nrf_802154_channel_get(); |
| otError error = OT_ERROR_NONE; |
| |
| VerifyOrExit(aPower != OT_RADIO_POWER_INVALID, error = OT_ERROR_INVALID_ARGS); |
| sDefaultTxPower = aPower; |
| nrf_802154_tx_power_set(GetTransmitPowerForChannel(channel)); |
| |
| exit: |
| return error; |
| } |
| |
| otError otPlatRadioGetCcaEnergyDetectThreshold(otInstance *aInstance, int8_t *aThreshold) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| otError error = OT_ERROR_NONE; |
| nrf_802154_cca_cfg_t ccaConfig; |
| |
| if (aThreshold == NULL) |
| { |
| error = OT_ERROR_INVALID_ARGS; |
| } |
| else |
| { |
| nrf_802154_cca_cfg_get(&ccaConfig); |
| // The radio driver has no function to convert ED threshold to dBm |
| *aThreshold = (int8_t)ccaConfig.ed_threshold + NRF528XX_MIN_CCA_ED_THRESHOLD - sLnaGain; |
| } |
| |
| return error; |
| } |
| |
| otError otPlatRadioSetCcaEnergyDetectThreshold(otInstance *aInstance, int8_t aThreshold) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| otError error = OT_ERROR_NONE; |
| nrf_802154_cca_cfg_t ccaConfig; |
| |
| aThreshold += sLnaGain; |
| |
| // The minimum value of ED threshold for radio driver is -94 dBm |
| if (aThreshold < NRF528XX_MIN_CCA_ED_THRESHOLD) |
| { |
| error = OT_ERROR_INVALID_ARGS; |
| } |
| else |
| { |
| memset(&ccaConfig, 0, sizeof(ccaConfig)); |
| ccaConfig.mode = NRF_RADIO_CCA_MODE_ED; |
| ccaConfig.ed_threshold = nrf_802154_ccaedthres_from_dbm_calculate(aThreshold); |
| |
| nrf_802154_cca_cfg_set(&ccaConfig); |
| } |
| |
| return error; |
| } |
| |
| otError otPlatRadioGetFemLnaGain(otInstance *aInstance, int8_t *aGain) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| otError error = OT_ERROR_NONE; |
| |
| if (aGain == NULL) |
| { |
| error = OT_ERROR_INVALID_ARGS; |
| } |
| else |
| { |
| *aGain = sLnaGain; |
| } |
| |
| return error; |
| } |
| |
| otError otPlatRadioSetFemLnaGain(otInstance *aInstance, int8_t aGain) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| int8_t threshold; |
| int8_t oldLnaGain = sLnaGain; |
| otError error = OT_ERROR_NONE; |
| |
| error = otPlatRadioGetCcaEnergyDetectThreshold(aInstance, &threshold); |
| otEXPECT(error == OT_ERROR_NONE); |
| |
| sLnaGain = aGain; |
| error = otPlatRadioSetCcaEnergyDetectThreshold(aInstance, threshold); |
| otEXPECT_ACTION(error == OT_ERROR_NONE, sLnaGain = oldLnaGain); |
| |
| exit: |
| return error; |
| } |
| |
| void nrf5RadioProcess(otInstance *aInstance) |
| { |
| bool isEventPending = false; |
| |
| for (uint32_t i = 0; i < NRF_802154_RX_BUFFERS; i++) |
| { |
| if (sReceivedFrames[i].mPsdu != NULL) |
| { |
| #if OPENTHREAD_CONFIG_DIAG_ENABLE |
| |
| if (otPlatDiagModeGet()) |
| { |
| otPlatDiagRadioReceiveDone(aInstance, &sReceivedFrames[i], OT_ERROR_NONE); |
| } |
| else |
| #endif |
| { |
| otPlatRadioReceiveDone(aInstance, &sReceivedFrames[i], OT_ERROR_NONE); |
| } |
| |
| uint8_t *bufferAddress = &sReceivedFrames[i].mPsdu[-1]; |
| sReceivedFrames[i].mPsdu = NULL; |
| nrf_802154_buffer_free_raw(bufferAddress); |
| } |
| } |
| |
| if (isPendingEventSet(kPendingEventFrameTransmitted)) |
| { |
| resetPendingEvent(kPendingEventFrameTransmitted); |
| |
| #if OPENTHREAD_CONFIG_DIAG_ENABLE |
| |
| if (otPlatDiagModeGet()) |
| { |
| otPlatDiagRadioTransmitDone(aInstance, &sTransmitFrame, OT_ERROR_NONE); |
| } |
| else |
| #endif |
| { |
| otRadioFrame *ackPtr = (sAckFrame.mPsdu == NULL) ? NULL : &sAckFrame; |
| otPlatRadioTxDone(aInstance, &sTransmitFrame, ackPtr, OT_ERROR_NONE); |
| } |
| |
| if (sAckFrame.mPsdu != NULL) |
| { |
| nrf_802154_buffer_free_raw(sAckFrame.mPsdu - 1); |
| sAckFrame.mPsdu = NULL; |
| } |
| } |
| |
| if (isPendingEventSet(kPendingEventChannelAccessFailure)) |
| { |
| resetPendingEvent(kPendingEventChannelAccessFailure); |
| |
| #if OPENTHREAD_CONFIG_DIAG_ENABLE |
| |
| if (otPlatDiagModeGet()) |
| { |
| otPlatDiagRadioTransmitDone(aInstance, &sTransmitFrame, OT_ERROR_CHANNEL_ACCESS_FAILURE); |
| } |
| else |
| #endif |
| { |
| otPlatRadioTxDone(aInstance, &sTransmitFrame, NULL, OT_ERROR_CHANNEL_ACCESS_FAILURE); |
| } |
| } |
| |
| if (isPendingEventSet(kPendingEventInvalidOrNoAck)) |
| { |
| resetPendingEvent(kPendingEventInvalidOrNoAck); |
| |
| #if OPENTHREAD_CONFIG_DIAG_ENABLE |
| |
| if (otPlatDiagModeGet()) |
| { |
| otPlatDiagRadioTransmitDone(aInstance, &sTransmitFrame, OT_ERROR_NO_ACK); |
| } |
| else |
| #endif |
| { |
| otPlatRadioTxDone(aInstance, &sTransmitFrame, NULL, OT_ERROR_NO_ACK); |
| } |
| } |
| |
| if (isPendingEventSet(kPendingEventReceiveFailed)) |
| { |
| resetPendingEvent(kPendingEventReceiveFailed); |
| |
| #if OPENTHREAD_CONFIG_DIAG_ENABLE |
| |
| if (otPlatDiagModeGet()) |
| { |
| otPlatDiagRadioReceiveDone(aInstance, NULL, sReceiveError); |
| } |
| else |
| #endif |
| { |
| otPlatRadioReceiveDone(aInstance, NULL, sReceiveError); |
| } |
| } |
| |
| if (isPendingEventSet(kPendingEventEnergyDetected)) |
| { |
| resetPendingEvent(kPendingEventEnergyDetected); |
| |
| otPlatRadioEnergyScanDone(aInstance, sEnergyDetected); |
| } |
| |
| if (isPendingEventSet(kPendingEventSleep)) |
| { |
| if (nrf_802154_sleep_if_idle() == NRF_802154_SLEEP_ERROR_NONE) |
| { |
| nrf5FemDisable(); |
| resetPendingEvent(kPendingEventSleep); |
| } |
| else |
| { |
| isEventPending = true; |
| } |
| } |
| |
| if (isPendingEventSet(kPendingEventEnergyDetectionStart)) |
| { |
| nrf_802154_channel_set(sEnergyDetectionChannel); |
| |
| if (nrf_802154_energy_detection(sEnergyDetectionTime)) |
| { |
| resetPendingEvent(kPendingEventEnergyDetectionStart); |
| } |
| else |
| { |
| isEventPending = true; |
| } |
| } |
| |
| if (isEventPending) |
| { |
| otSysEventSignalPending(); |
| } |
| } |
| |
| void nrf_802154_received_timestamp_raw(uint8_t *p_data, int8_t power, uint8_t lqi, uint32_t time) |
| { |
| otRadioFrame *receivedFrame = NULL; |
| |
| for (uint32_t i = 0; i < NRF_802154_RX_BUFFERS; i++) |
| { |
| if (sReceivedFrames[i].mPsdu == NULL) |
| { |
| receivedFrame = &sReceivedFrames[i]; |
| |
| memset(receivedFrame, 0, sizeof(*receivedFrame)); |
| break; |
| } |
| } |
| |
| assert(receivedFrame != NULL); |
| |
| receivedFrame->mPsdu = &p_data[1]; |
| receivedFrame->mLength = p_data[0]; |
| receivedFrame->mInfo.mRxInfo.mRssi = power; |
| receivedFrame->mInfo.mRxInfo.mLqi = lqi; |
| receivedFrame->mChannel = nrf_802154_channel_get(); |
| |
| // Inform if this frame was acknowledged with frame pending set. |
| if (p_data[ACK_REQUEST_OFFSET] & ACK_REQUEST_BIT) |
| { |
| receivedFrame->mInfo.mRxInfo.mAckedWithFramePending = sAckedWithFramePending; |
| } |
| else |
| { |
| receivedFrame->mInfo.mRxInfo.mAckedWithFramePending = false; |
| } |
| |
| // Get the timestamp when the SFD was received. |
| #if !NRF_802154_TX_STARTED_NOTIFY_ENABLED |
| #error "NRF_802154_TX_STARTED_NOTIFY_ENABLED is required!" |
| #endif |
| uint32_t offset = |
| (int32_t)otPlatAlarmMicroGetNow() - (int32_t)nrf_802154_first_symbol_timestamp_get(time, p_data[0]); |
| receivedFrame->mInfo.mRxInfo.mTimestamp = nrf5AlarmGetCurrentTime() - offset; |
| |
| sAckedWithFramePending = false; |
| |
| #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 |
| // Inform if this frame was acknowledged with secured Enh-ACK. |
| if (p_data[ACK_REQUEST_OFFSET] & ACK_REQUEST_BIT && otMacFrameIsVersion2015(receivedFrame)) |
| { |
| receivedFrame->mInfo.mRxInfo.mAckedWithSecEnhAck = sAckedWithSecEnhAck; |
| receivedFrame->mInfo.mRxInfo.mAckFrameCounter = sAckFrameCounter; |
| receivedFrame->mInfo.mRxInfo.mAckKeyId = sAckKeyId; |
| } |
| |
| sAckedWithSecEnhAck = false; |
| #endif |
| |
| otSysEventSignalPending(); |
| } |
| |
| void nrf_802154_receive_failed(nrf_802154_rx_error_t error) |
| { |
| switch (error) |
| { |
| case NRF_802154_RX_ERROR_INVALID_FRAME: |
| case NRF_802154_RX_ERROR_DELAYED_TIMEOUT: |
| sReceiveError = OT_ERROR_NO_FRAME_RECEIVED; |
| break; |
| |
| case NRF_802154_RX_ERROR_INVALID_FCS: |
| sReceiveError = OT_ERROR_FCS; |
| break; |
| |
| case NRF_802154_RX_ERROR_INVALID_DEST_ADDR: |
| sReceiveError = OT_ERROR_DESTINATION_ADDRESS_FILTERED; |
| break; |
| |
| case NRF_802154_RX_ERROR_RUNTIME: |
| case NRF_802154_RX_ERROR_TIMESLOT_ENDED: |
| case NRF_802154_RX_ERROR_ABORTED: |
| case NRF_802154_RX_ERROR_DELAYED_TIMESLOT_DENIED: |
| case NRF_802154_RX_ERROR_INVALID_LENGTH: |
| sReceiveError = OT_ERROR_FAILED; |
| break; |
| |
| default: |
| assert(false); |
| } |
| |
| sAckedWithFramePending = false; |
| #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 |
| sAckedWithSecEnhAck = false; |
| #endif |
| |
| setPendingEvent(kPendingEventReceiveFailed); |
| } |
| |
| #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE |
| static uint16_t getCslPhase() |
| { |
| uint32_t curTime = otPlatAlarmMicroGetNow(); |
| uint32_t cslPeriodInUs = sCslPeriod * OT_US_PER_TEN_SYMBOLS; |
| uint32_t diff = |
| (cslPeriodInUs - (curTime % cslPeriodInUs) + (sCslSampleTime % cslPeriodInUs) + (sCslSampleDur / 2)) % |
| cslPeriodInUs; |
| return (uint16_t)(diff / OT_US_PER_TEN_SYMBOLS + 1); |
| } |
| #endif |
| |
| void nrf_802154_tx_ack_started(uint8_t *p_data, int8_t power, uint8_t lqi) |
| { |
| otRadioFrame ackFrame; |
| #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE |
| uint8_t linkMetricsDataLen = 0; |
| uint8_t linkMetricsData[OT_ENH_PROBING_IE_DATA_MAX_SIZE]; |
| otMacAddress macAddress; |
| #else |
| OT_UNUSED_VARIABLE(power); |
| OT_UNUSED_VARIABLE(lqi); |
| #endif |
| |
| OT_UNUSED_VARIABLE(ackFrame); |
| |
| ackFrame.mPsdu = (uint8_t *)(p_data + 1); |
| ackFrame.mLength = p_data[0]; |
| |
| // Check if the frame pending bit is set in ACK frame. |
| sAckedWithFramePending = p_data[FRAME_PENDING_OFFSET] & FRAME_PENDING_BIT; |
| |
| #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 |
| // Update IE and secure Enh-ACK. |
| #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE |
| if (sCslPeriod > 0) |
| { |
| otMacFrameSetCslIe(&ackFrame, sCslPeriod, getCslPhase()); |
| } |
| #endif |
| |
| #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE |
| otMacFrameGetDstAddr(&ackFrame, &macAddress); |
| if ((linkMetricsDataLen = otLinkMetricsEnhAckGenData(&macAddress, lqi, power, linkMetricsData)) > 0) |
| { |
| otMacFrameSetEnhAckProbingIe(&ackFrame, linkMetricsData, linkMetricsDataLen); |
| } |
| #endif |
| |
| txAckProcessSecurity(p_data); |
| #endif |
| } |
| |
| void nrf_802154_transmitted_timestamp_raw(const uint8_t *aFrame, |
| uint8_t * aAckPsdu, |
| int8_t aPower, |
| uint8_t aLqi, |
| uint32_t ack_time) |
| { |
| OT_UNUSED_VARIABLE(aFrame); // For ARM gcc |
| assert(aFrame == sTransmitPsdu); |
| |
| if (aAckPsdu == NULL) |
| { |
| sAckFrame.mPsdu = NULL; |
| } |
| else |
| { |
| uint32_t offset = |
| (int32_t)otPlatAlarmMicroGetNow() - (int32_t)nrf_802154_first_symbol_timestamp_get(ack_time, aAckPsdu[0]); |
| |
| sAckFrame.mInfo.mRxInfo.mTimestamp = nrf5AlarmGetCurrentTime() - offset; |
| sAckFrame.mPsdu = &aAckPsdu[1]; |
| sAckFrame.mLength = aAckPsdu[0]; |
| sAckFrame.mInfo.mRxInfo.mRssi = aPower; |
| sAckFrame.mInfo.mRxInfo.mLqi = aLqi; |
| sAckFrame.mChannel = nrf_802154_channel_get(); |
| } |
| |
| setPendingEvent(kPendingEventFrameTransmitted); |
| } |
| |
| void nrf_802154_transmit_failed(const uint8_t *aFrame, nrf_802154_tx_error_t error) |
| { |
| OT_UNUSED_VARIABLE(aFrame); // For ARM gcc |
| assert(aFrame == sTransmitPsdu); |
| |
| switch (error) |
| { |
| case NRF_802154_TX_ERROR_BUSY_CHANNEL: |
| case NRF_802154_TX_ERROR_TIMESLOT_ENDED: |
| case NRF_802154_TX_ERROR_ABORTED: |
| case NRF_802154_TX_ERROR_TIMESLOT_DENIED: |
| setPendingEvent(kPendingEventChannelAccessFailure); |
| break; |
| |
| case NRF_802154_TX_ERROR_INVALID_ACK: |
| case NRF_802154_TX_ERROR_NO_ACK: |
| case NRF_802154_TX_ERROR_NO_MEM: |
| setPendingEvent(kPendingEventInvalidOrNoAck); |
| break; |
| |
| default: |
| assert(false); |
| } |
| } |
| |
| void nrf_802154_energy_detected(uint8_t result) |
| { |
| sEnergyDetected = nrf_802154_dbm_from_energy_level_calculate(result); |
| |
| setPendingEvent(kPendingEventEnergyDetected); |
| } |
| |
| int8_t otPlatRadioGetReceiveSensitivity(otInstance *aInstance) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| return NRF528XX_RECEIVE_SENSITIVITY; |
| } |
| |
| #if OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT |
| void nrf_802154_tx_started(const uint8_t *aFrame) |
| { |
| bool processSecurity = false; |
| |
| assert(aFrame == sTransmitPsdu); |
| OT_UNUSED_VARIABLE(aFrame); |
| |
| #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE |
| if (sCslPeriod > 0) |
| { |
| otMacFrameSetCslIe(&sTransmitFrame, (uint16_t)sCslPeriod, getCslPhase()); |
| } |
| #endif |
| |
| // Update IE and secure transmit frame |
| #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE |
| if (sTransmitFrame.mInfo.mTxInfo.mIeInfo->mTimeIeOffset != 0) |
| { |
| uint8_t *timeIe = sTransmitFrame.mPsdu + sTransmitFrame.mInfo.mTxInfo.mIeInfo->mTimeIeOffset; |
| uint64_t time = otPlatTimeGet() + sTransmitFrame.mInfo.mTxInfo.mIeInfo->mNetworkTimeOffset; |
| |
| *timeIe = sTransmitFrame.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); |
| } |
| |
| processSecurity = true; |
| } |
| #endif // OPENTHREAD_CONFIG_TIME_SYNC_ENABLE |
| |
| #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 |
| otEXPECT(otMacFrameIsSecurityEnabled(&sTransmitFrame) && otMacFrameIsKeyIdMode1(&sTransmitFrame) && |
| !sTransmitFrame.mInfo.mTxInfo.mIsSecurityProcessed); |
| |
| sTransmitFrame.mInfo.mTxInfo.mAesKey = &sCurrKey; |
| |
| if (!sTransmitFrame.mInfo.mTxInfo.mIsARetx) |
| { |
| otMacFrameSetKeyId(&sTransmitFrame, sKeyId); |
| otMacFrameSetFrameCounter(&sTransmitFrame, sMacFrameCounter++); |
| } |
| |
| processSecurity = true; |
| #endif // OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 |
| |
| otEXPECT(processSecurity); |
| otMacFrameProcessTransmitAesCcm(&sTransmitFrame, &sExtAddress); |
| |
| exit: |
| return; |
| } |
| #endif |
| |
| void nrf_802154_random_init(void) |
| { |
| // Intentionally empty |
| } |
| |
| void nrf_802154_random_deinit(void) |
| { |
| // Intentionally empty |
| } |
| |
| uint32_t nrf_802154_random_get(void) |
| { |
| return otRandomNonCryptoGetUint32(); |
| } |
| |
| uint64_t otPlatRadioGetNow(otInstance *aInstance) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| return otPlatTimeGet(); |
| } |
| |
| #if OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 |
| 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); |
| |
| CRITICAL_REGION_ENTER(); |
| |
| 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); |
| |
| CRITICAL_REGION_EXIT(); |
| } |
| |
| void otPlatRadioSetMacFrameCounter(otInstance *aInstance, uint32_t aMacFrameCounter) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| CRITICAL_REGION_ENTER(); |
| |
| sMacFrameCounter = aMacFrameCounter; |
| |
| CRITICAL_REGION_EXIT(); |
| } |
| #endif // OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2 |
| |
| #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE |
| static void updateIeData(otInstance *aInstance, otShortAddress aShortAddr, const otExtAddress *aExtAddr) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| int8_t offset = 0; |
| uint8_t ackIeData[OT_ACK_IE_MAX_SIZE]; |
| uint8_t extAddr[OT_EXT_ADDRESS_SIZE]; |
| uint8_t shortAddr[SHORT_ADDRESS_SIZE]; |
| #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE |
| uint8_t enhAckProbingDataLen = 0; |
| otMacAddress macAddress; |
| macAddress.mType = OT_MAC_ADDRESS_TYPE_SHORT; |
| macAddress.mAddress.mShortAddress = aShortAddr; |
| #endif |
| |
| #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE |
| if (sCslPeriod > 0) |
| { |
| memcpy(ackIeData, sCslIeHeader, OT_IE_HEADER_SIZE); |
| offset += OT_IE_HEADER_SIZE + OT_CSL_IE_SIZE; // reserve space for CSL IE |
| } |
| #endif |
| |
| #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE |
| if ((enhAckProbingDataLen = otLinkMetricsEnhAckGetDataLen(&macAddress)) > 0) |
| { |
| offset += otMacFrameGenerateEnhAckProbingIe(ackIeData + offset, NULL, enhAckProbingDataLen); |
| } |
| #endif |
| |
| convertShortAddress(shortAddr, aShortAddr); |
| convertExtAddress(extAddr, aExtAddr); |
| |
| if (offset > 0) |
| { |
| nrf_802154_ack_data_set(shortAddr, false, ackIeData, offset, NRF_802154_ACK_DATA_IE); |
| nrf_802154_ack_data_set(extAddr, true, ackIeData, offset, NRF_802154_ACK_DATA_IE); |
| } |
| else |
| { |
| nrf_802154_ack_data_clear(shortAddr, false, NRF_802154_ACK_DATA_IE); |
| nrf_802154_ack_data_clear(extAddr, true, NRF_802154_ACK_DATA_IE); |
| } |
| } |
| #endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE || OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE |
| |
| #if OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE |
| otError otPlatRadioEnableCsl(otInstance *aInstance, uint32_t aCslPeriod, const otExtAddress *aExtAddr) |
| { |
| otError error = OT_ERROR_NONE; |
| otShortAddress shortAddress; |
| |
| sCslPeriod = aCslPeriod; |
| |
| shortAddress = nrf_802154_pib_short_address_get()[1] |
| << 8; // Don't need the other byte because this is parent's short address |
| shortAddress &= 0xfc00; |
| |
| updateIeData(aInstance, shortAddress, aExtAddr); |
| |
| return error; |
| } |
| |
| void otPlatRadioUpdateCslSampleTime(otInstance *aInstance, uint32_t aCslSampleTime) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| sCslSampleTime = aCslSampleTime; |
| } |
| #endif // OPENTHREAD_CONFIG_MAC_CSL_RECEIVER_ENABLE |
| |
| #if OPENTHREAD_CONFIG_MLE_LINK_METRICS_ENABLE |
| otError otPlatRadioConfigureEnhAckProbing(otInstance * aInstance, |
| otLinkMetrics aLinkMetrics, |
| const otShortAddress aShortAddress, |
| const otExtAddress * aExtAddress) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| OT_UNUSED_VARIABLE(aLinkMetrics); |
| OT_UNUSED_VARIABLE(aShortAddress); |
| OT_UNUSED_VARIABLE(aExtAddress); |
| |
| otError error = OT_ERROR_NONE; |
| |
| SuccessOrExit(error = otLinkMetricsConfigureEnhAckProbing(aShortAddress, aExtAddress, aLinkMetrics)); |
| updateIeData(aInstance, aShortAddress, aExtAddress); |
| |
| exit: |
| return error; |
| } |
| #endif |
| |
| otError otPlatRadioSetChannelMaxTransmitPower(otInstance *aInstance, uint8_t aChannel, int8_t aMaxPower) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| otError error = OT_ERROR_NONE; |
| |
| otEXPECT_ACTION(aChannel >= OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MIN && aChannel <= OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MAX, |
| error = OT_ERROR_INVALID_ARGS); |
| |
| sMaxTxPowerTable[aChannel - OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MIN] = aMaxPower; |
| if (aChannel == nrf_802154_channel_get()) |
| { |
| nrf_802154_tx_power_set(GetTransmitPowerForChannel(aChannel)); |
| } |
| |
| exit: |
| return error; |
| } |
| |
| int8_t nrf5GetChannelMaxTransmitPower(uint8_t aChannel) |
| { |
| int8_t power; |
| |
| if (aChannel < OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MIN || aChannel > OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MAX) |
| { |
| power = OT_RADIO_POWER_INVALID; |
| } |
| else |
| { |
| power = sMaxTxPowerTable[aChannel - OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MIN]; |
| } |
| |
| return power; |
| } |
| |
| otError otPlatRadioSetRegion(otInstance *aInstance, uint16_t aRegionCode) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| sRegionCode = aRegionCode; |
| nrf5HandleRegionChanged(aRegionCode); |
| return OT_ERROR_NONE; |
| } |
| |
| otError otPlatRadioGetRegion(otInstance *aInstance, uint16_t *aRegionCode) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| otError error = OT_ERROR_NONE; |
| |
| VerifyOrExit(aRegionCode != NULL, error = OT_ERROR_INVALID_ARGS); |
| |
| *aRegionCode = sRegionCode; |
| exit: |
| return error; |
| } |
| |
| OT_TOOL_WEAK void nrf5HandleRegionChanged(uint16_t aRegionCode) |
| { |
| OT_UNUSED_VARIABLE(aRegionCode); |
| } |