| /* |
| * Copyright (c) 2018, 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/config.h> |
| |
| #include <assert.h> |
| #include <utils/code_utils.h> |
| #include <openthread/random_noncrypto.h> /* to seed the CSMA-CA funciton */ |
| #include <openthread/platform/alarm-milli.h> |
| #include <openthread/platform/diag.h> |
| #include <openthread/platform/radio.h> |
| |
| #include "cc1352_radio.h" |
| #include <driverlib/chipinfo.h> |
| #include <driverlib/gpio.h> |
| #include <driverlib/ioc.h> |
| #include <driverlib/osc.h> |
| #include <driverlib/prcm.h> |
| #include <driverlib/rf_common_cmd.h> |
| #include <driverlib/rf_data_entry.h> |
| #include <driverlib/rf_ieee_cmd.h> |
| #include <driverlib/rf_ieee_mailbox.h> |
| #include <driverlib/rf_mailbox.h> |
| #include <driverlib/rfc.h> |
| #include <inc/hw_ccfg.h> |
| #include <inc/hw_fcfg1.h> |
| #include <inc/hw_memmap.h> |
| #include <inc/hw_prcm.h> |
| #include <rf_patches/rf_patch_cpe_ieee_802_15_4.h> |
| |
| enum |
| { |
| CC1352_RECEIVE_SENSITIVITY = -100, // dBm |
| CC1352_RF_CMD0 = 0x0607, |
| }; |
| |
| enum |
| { |
| CC1352_CHANNEL_MIN = OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MIN, |
| }; |
| |
| /* phy state as defined by openthread */ |
| static volatile cc1352_PhyState_t sState; |
| |
| /* set to max transmit power by default */ |
| static output_config_t const *sCurrentOutputPower = &(rgOutputPower[0]); |
| |
| /* Overrides from SmartRF Studio 7 2.10.0#94 */ |
| static uint32_t sIEEEOverrides[] = { |
| // override_ieee_802_15_4.xml |
| // DC/DC regulator: In Tx, use DCDCCTL5[3:0]=0x3 (DITHER_EN=0 and IPEAK=3). |
| (uint32_t)0x00F388D3, |
| // Rx: Set LNA bias current offset to +15 to saturate trim to max (default: 0) |
| (uint32_t)0x000F8883, (uint32_t)0xFFFFFFFF}; |
| |
| /* |
| * Number of retry counts left to the currently transmitting frame. |
| * |
| * Initialized when a frame is passed to be sent over the air, and decremented |
| * by the radio ISR every time the transmit command string fails to receive a |
| * corresponding ack. |
| */ |
| static volatile unsigned int sTransmitRetryCount = 0; |
| |
| /* |
| * Offset of the radio timer from the rtc. |
| * |
| * Used when we start and stop the RAT on enabling and disabling of the rf |
| * core. |
| */ |
| static uint32_t sRatOffset = 0; |
| |
| /* |
| * Radio command structures that run on the CM0. |
| */ |
| // clang-format off |
| static volatile __attribute__((aligned(4))) rfc_CMD_SYNC_START_RAT_t sStartRatCmd; |
| static volatile __attribute__((aligned(4))) rfc_CMD_RADIO_SETUP_t sRadioSetupCmd; |
| |
| static volatile __attribute__((aligned(4))) rfc_CMD_FS_POWERDOWN_t sFsPowerdownCmd; |
| static volatile __attribute__((aligned(4))) rfc_CMD_SYNC_STOP_RAT_t sStopRatCmd; |
| |
| static volatile __attribute__((aligned(4))) rfc_CMD_CLEAR_RX_t sClearReceiveQueueCmd; |
| static volatile __attribute__((aligned(4))) rfc_CMD_IEEE_MOD_FILT_t sModifyReceiveFilterCmd; |
| static volatile __attribute__((aligned(4))) rfc_CMD_IEEE_MOD_SRC_MATCH_t sModifyReceiveSrcMatchCmd; |
| |
| static volatile __attribute__((aligned(4))) rfc_CMD_IEEE_ED_SCAN_t sEdScanCmd; |
| |
| static volatile __attribute__((aligned(4))) rfc_CMD_IEEE_RX_t sReceiveCmd; |
| |
| static volatile __attribute__((aligned(4))) rfc_CMD_IEEE_CSMA_t sCsmacaBackoffCmd; |
| static volatile __attribute__((aligned(4))) rfc_CMD_IEEE_TX_t sTransmitCmd; |
| static volatile __attribute__((aligned(4))) rfc_CMD_IEEE_RX_ACK_t sTransmitRxAckCmd; |
| |
| static volatile __attribute__((aligned(4))) ext_src_match_data_t sSrcMatchExtData; |
| static volatile __attribute__((aligned(4))) short_src_match_data_t sSrcMatchShortData; |
| // clang-format on |
| |
| /* |
| * Structure containing radio statistics. |
| */ |
| static __attribute__((aligned(4))) rfc_ieeeRxOutput_t sRfStats; |
| |
| /* |
| * Two receive buffers entries with room for 1 max IEEE802.15.4 frame in each |
| * |
| * These will be setup in a circular buffer configuration by /ref sRxDataQueue. |
| */ |
| #define RX_BUF_SIZE 144 |
| static __attribute__((aligned(4))) uint8_t sRxBuf0[RX_BUF_SIZE]; |
| static __attribute__((aligned(4))) uint8_t sRxBuf1[RX_BUF_SIZE]; |
| static __attribute__((aligned(4))) uint8_t sRxBuf2[RX_BUF_SIZE]; |
| static __attribute__((aligned(4))) uint8_t sRxBuf3[RX_BUF_SIZE]; |
| |
| /* |
| * The RX Data Queue used by @ref sReceiveCmd. |
| */ |
| static __attribute__((aligned(4))) dataQueue_t sRxDataQueue = {0}; |
| |
| /* |
| * OpenThread data primitives |
| */ |
| static otRadioFrame sTransmitFrame; |
| static otError sTransmitError; |
| |
| static __attribute__((aligned(4))) uint8_t sTransmitPsdu[OT_RADIO_FRAME_MAX_SIZE]; |
| |
| static volatile bool sTxCmdChainDone = false; |
| |
| /* |
| * Interrupt handlers forward declared for register functions. |
| */ |
| void RFCCPE0IntHandler(void); |
| void RFCCPE1IntHandler(void); |
| |
| /** |
| * Initialize the RX/TX buffers. |
| * |
| * Zeros out the receive and transmit buffers and sets up the data structures |
| * of the receive queue. |
| */ |
| static void rfCoreInitBufs(void) |
| { |
| rfc_dataEntry_t *entry; |
| memset(sRxBuf0, 0x00, sizeof(sRxBuf0)); |
| memset(sRxBuf1, 0x00, sizeof(sRxBuf1)); |
| memset(sRxBuf2, 0x00, sizeof(sRxBuf2)); |
| memset(sRxBuf3, 0x00, sizeof(sRxBuf3)); |
| |
| entry = (rfc_dataEntry_t *)sRxBuf0; |
| entry->pNextEntry = sRxBuf1; |
| entry->config.lenSz = DATA_ENTRY_LENSZ_BYTE; |
| entry->length = sizeof(sRxBuf0) - sizeof(rfc_dataEntry_t); |
| |
| entry = (rfc_dataEntry_t *)sRxBuf1; |
| entry->pNextEntry = sRxBuf2; |
| entry->config.lenSz = DATA_ENTRY_LENSZ_BYTE; |
| entry->length = sizeof(sRxBuf1) - sizeof(rfc_dataEntry_t); |
| |
| entry = (rfc_dataEntry_t *)sRxBuf2; |
| entry->pNextEntry = sRxBuf3; |
| entry->config.lenSz = DATA_ENTRY_LENSZ_BYTE; |
| entry->length = sizeof(sRxBuf2) - sizeof(rfc_dataEntry_t); |
| |
| entry = (rfc_dataEntry_t *)sRxBuf3; |
| entry->pNextEntry = sRxBuf0; |
| entry->config.lenSz = DATA_ENTRY_LENSZ_BYTE; |
| entry->length = sizeof(sRxBuf3) - sizeof(rfc_dataEntry_t); |
| |
| sTransmitFrame.mPsdu = sTransmitPsdu; |
| sTransmitFrame.mLength = 0; |
| } |
| |
| /** |
| * Initialize the RX command structure. |
| * |
| * Sets the default values for the receive command structure. |
| */ |
| static void rfCoreInitReceiveParams(void) |
| { |
| // clang-format off |
| static const rfc_CMD_IEEE_RX_t cReceiveCmd = |
| { |
| .commandNo = CMD_IEEE_RX, |
| .status = IDLE, |
| .pNextOp = NULL, |
| .startTime = 0u, |
| .startTrigger = |
| { |
| .triggerType = TRIG_NOW, |
| }, |
| .condition = { |
| .rule = COND_NEVER, |
| }, |
| .channel = CC1352_CHANNEL_MIN, |
| .rxConfig = |
| { |
| .bAutoFlushCrc = 1, |
| .bAutoFlushIgn = 0, |
| .bIncludePhyHdr = 0, |
| .bIncludeCrc = 0, |
| .bAppendRssi = 1, |
| .bAppendCorrCrc = 1, |
| .bAppendSrcInd = 0, |
| .bAppendTimestamp = 0, |
| }, |
| .frameFiltOpt = |
| { |
| .frameFiltEn = 1, |
| .frameFiltStop = 1, |
| .autoAckEn = 1, |
| .slottedAckEn = 0, |
| .autoPendEn = 0, |
| .defaultPend = 0, |
| .bPendDataReqOnly = 0, |
| .bPanCoord = 0, |
| .maxFrameVersion = 3, |
| .bStrictLenFilter = 1, |
| }, |
| .frameTypes = |
| { |
| .bAcceptFt0Beacon = 1, |
| .bAcceptFt1Data = 1, |
| .bAcceptFt2Ack = 1, |
| .bAcceptFt3MacCmd = 1, |
| .bAcceptFt4Reserved = 1, |
| .bAcceptFt5Reserved = 1, |
| .bAcceptFt6Reserved = 1, |
| .bAcceptFt7Reserved = 1, |
| }, |
| .ccaOpt = |
| { |
| .ccaEnEnergy = 1, |
| .ccaEnCorr = 1, |
| .ccaEnSync = 1, |
| .ccaCorrOp = 1, |
| .ccaSyncOp = 0, |
| .ccaCorrThr = 3, |
| }, |
| .ccaRssiThr = -90, |
| .endTrigger = |
| { |
| .triggerType = TRIG_NEVER, |
| }, |
| .endTime = 0u, |
| }; |
| // clang-format on |
| sReceiveCmd = cReceiveCmd; |
| |
| sReceiveCmd.pRxQ = &sRxDataQueue; |
| sReceiveCmd.pOutput = &sRfStats; |
| |
| sReceiveCmd.numShortEntries = CC1352_SHORTADD_SRC_MATCH_NUM; |
| sReceiveCmd.pShortEntryList = (void *)&sSrcMatchShortData; |
| |
| sReceiveCmd.numExtEntries = CC1352_EXTADD_SRC_MATCH_NUM; |
| sReceiveCmd.pExtEntryList = (uint32_t *)&sSrcMatchExtData; |
| } |
| |
| /** |
| * Sends the direct abort command to the radio core. |
| * |
| * @return The value from the command status register. |
| * @retval CMDSTA_Done The command completed correctly. |
| */ |
| static uint_fast8_t rfCoreExecuteAbortCmd(void) |
| { |
| return (RFCDoorbellSendTo(CMDR_DIR_CMD(CMD_ABORT)) & 0xFF); |
| } |
| |
| /** |
| * Sends the direct ping command to the radio core. |
| * |
| * Check that the Radio core is alive and able to respond to commands. |
| * |
| * @return The value from the command status register. |
| * @retval CMDSTA_Done The command completed correctly. |
| */ |
| static uint_fast8_t rfCoreExecutePingCmd(void) |
| { |
| return (RFCDoorbellSendTo(CMDR_DIR_CMD(CMD_PING)) & 0xFF); |
| } |
| |
| /** |
| * Sends the immediate clear rx queue command to the radio core. |
| * |
| * Uses the radio core to mark all of the entries in the receive queue as |
| * pending. This is used instead of clearing the entries manually to avoid race |
| * conditions between the main processor and the radio core. |
| * |
| * @param [in] aQueue A pointer to the receive queue to be cleared. |
| * |
| * @return The value from the command status register. |
| * @retval CMDSTA_Done The command completed correctly. |
| */ |
| static uint_fast8_t rfCoreClearReceiveQueue(dataQueue_t *aQueue) |
| { |
| /* memset skipped because sClearReceiveQueueCmd has only 2 members and padding */ |
| sClearReceiveQueueCmd.commandNo = CMD_CLEAR_RX; |
| sClearReceiveQueueCmd.pQueue = aQueue; |
| |
| return (RFCDoorbellSendTo((uint32_t)&sClearReceiveQueueCmd) & 0xFF); |
| } |
| |
| /** |
| * Enable/disable frame filtering. |
| * |
| * Uses the radio core to alter the current running RX command filtering |
| * options. This ensures there is no access fault between the CM3 and CM0 for |
| * the RX command. |
| * |
| * This function leaves the type of frames to be filtered the same as the |
| * receive command. |
| * |
| * @note An IEEE RX command *must* be running while this command executes. |
| * |
| * @param [in] aEnable TRUE: enable frame filtering, |
| * FALSE: disable frame filtering. |
| * |
| * @return The value from the command status register. |
| * @retval CMDSTA_Done The command completed correctly. |
| */ |
| static uint_fast8_t rfCoreModifyRxFrameFilter(bool aEnable) |
| { |
| /* memset skipped because sModifyReceiveFilterCmd has only 3 members */ |
| sModifyReceiveFilterCmd.commandNo = CMD_IEEE_MOD_FILT; |
| /* copy current frame filtering and frame types from running RX command */ |
| memcpy((void *)&sModifyReceiveFilterCmd.newFrameFiltOpt, (void *)&sReceiveCmd.frameFiltOpt, |
| sizeof(sModifyReceiveFilterCmd.newFrameFiltOpt)); |
| memcpy((void *)&sModifyReceiveFilterCmd.newFrameTypes, (void *)&sReceiveCmd.frameTypes, |
| sizeof(sModifyReceiveFilterCmd.newFrameTypes)); |
| |
| sModifyReceiveFilterCmd.newFrameFiltOpt.frameFiltEn = aEnable ? 1 : 0; |
| |
| return (RFCDoorbellSendTo((uint32_t)&sModifyReceiveFilterCmd) & 0xFF); |
| } |
| |
| /** |
| * Enable/disable autoPend feature. |
| * |
| * Uses the radio core to alter the current running RX command filtering |
| * options. This ensures there is no access fault between the CM3 and CM0 for |
| * the RX command. |
| * |
| * This function leaves the type of frames to be filtered the same as the |
| * receive command. |
| * |
| * @note An IEEE RX command *must* be running while this command executes. |
| * |
| * @param [in] aEnable TRUE: enable autoPend, |
| * FALSE: disable autoPend. |
| * |
| * @return The value from the command status register. |
| * @retval CMDSTA_Done The command completed correctly. |
| */ |
| static uint_fast8_t rfCoreModifyRxAutoPend(bool aEnable) |
| { |
| /* memset skipped because sModifyReceiveFilterCmd has only 3 members */ |
| sModifyReceiveFilterCmd.commandNo = CMD_IEEE_MOD_FILT; |
| /* copy current frame filtering and frame types from running RX command */ |
| memcpy((void *)&sModifyReceiveFilterCmd.newFrameFiltOpt, (void *)&sReceiveCmd.frameFiltOpt, |
| sizeof(sModifyReceiveFilterCmd.newFrameFiltOpt)); |
| memcpy((void *)&sModifyReceiveFilterCmd.newFrameTypes, (void *)&sReceiveCmd.frameTypes, |
| sizeof(sModifyReceiveFilterCmd.newFrameTypes)); |
| |
| sModifyReceiveFilterCmd.newFrameFiltOpt.autoPendEn = aEnable ? 1 : 0; |
| |
| return (RFCDoorbellSendTo((uint32_t)&sModifyReceiveFilterCmd) & 0xFF); |
| } |
| |
| /** |
| * Sends the immediate modify source matching command to the radio core. |
| * |
| * Uses the radio core to alter the current source matching parameters used by |
| * the running RX command. This ensures there is no access fault between the |
| * CM3 and CM0, and ensures that the RX command has cohesive view of the data. |
| * The CM3 may make alterations to the source matching entries if the entry is |
| * marked as disabled. |
| * |
| * @note An IEEE RX command *must* be running while this command executes. |
| * |
| * @param [in] aEntryNo The index of the entry to alter. |
| * @param [in] aType TRUE: the entry is a short address, |
| * FALSE: the entry is an extended address. |
| * @param [in] aEnable Whether the given entry is to be enabled or disabled. |
| * |
| * @return The value from the command status register. |
| * @retval CMDSTA_Done The command completed correctly. |
| */ |
| static uint_fast8_t rfCoreModifySourceMatchEntry(uint8_t aEntryNo, cc1352_address_t aType, bool aEnable) |
| { |
| /* memset kept to save 60 bytes of text space, gcc can't optimize the |
| * following bitfield operation if it doesn't know the fields are zero |
| * already. |
| */ |
| memset((void *)&sModifyReceiveSrcMatchCmd, 0, sizeof(sModifyReceiveSrcMatchCmd)); |
| |
| sModifyReceiveSrcMatchCmd.commandNo = CMD_IEEE_MOD_SRC_MATCH; |
| |
| /* we only use source matching for pending data bit, so enabling and |
| * pending are the same to us. |
| */ |
| if (aEnable) |
| { |
| sModifyReceiveSrcMatchCmd.options.bEnable = 1; |
| sModifyReceiveSrcMatchCmd.options.srcPend = 1; |
| } |
| else |
| { |
| sModifyReceiveSrcMatchCmd.options.bEnable = 0; |
| sModifyReceiveSrcMatchCmd.options.srcPend = 0; |
| } |
| |
| sModifyReceiveSrcMatchCmd.options.entryType = aType; |
| sModifyReceiveSrcMatchCmd.entryNo = aEntryNo; |
| |
| return (RFCDoorbellSendTo((uint32_t)&sModifyReceiveSrcMatchCmd) & 0xFF); |
| } |
| |
| /** |
| * Walks the short address source match list to find an address. |
| * |
| * @param [in] aAddress The short address to search for. |
| * |
| * @return The index where the address was found. |
| * @retval CC1352_SRC_MATCH_NONE The address was not found. |
| */ |
| static uint8_t rfCoreFindShortSrcMatchIdx(const uint16_t aAddress) |
| { |
| uint8_t i; |
| uint8_t ret = CC1352_SRC_MATCH_NONE; |
| |
| for (i = 0; i < CC1352_SHORTADD_SRC_MATCH_NUM; i++) |
| { |
| if (sSrcMatchShortData.extAddrEnt[i].shortAddr == aAddress) |
| { |
| ret = i; |
| break; |
| } |
| } |
| |
| return ret; |
| } |
| |
| /** |
| * Walks the short address source match list to find an empty slot. |
| * |
| * @return The index of an unused address slot. |
| * @retval CC1352_SRC_MATCH_NONE No unused slots available. |
| */ |
| static uint8_t rfCoreFindEmptyShortSrcMatchIdx(void) |
| { |
| uint8_t i; |
| uint8_t ret = CC1352_SRC_MATCH_NONE; |
| |
| for (i = 0; i < CC1352_SHORTADD_SRC_MATCH_NUM; i++) |
| { |
| if ((sSrcMatchShortData.srcMatchEn[i / 32] & (1 << (i % 32))) == 0u) |
| { |
| ret = i; |
| break; |
| } |
| } |
| |
| return ret; |
| } |
| |
| /** |
| * Walks the extended address source match list to find an address. |
| * |
| * @param [in] aAddress The extended address to search for. |
| * |
| * @return The index where the address was found. |
| * @retval CC1352_SRC_MATCH_NONE The address was not found. |
| */ |
| static uint8_t rfCoreFindExtSrcMatchIdx(const uint64_t *aAddress) |
| { |
| uint8_t i; |
| uint8_t ret = CC1352_SRC_MATCH_NONE; |
| |
| for (i = 0; i < CC1352_EXTADD_SRC_MATCH_NUM; i++) |
| { |
| if (sSrcMatchExtData.extAddrEnt[i] == *aAddress) |
| { |
| ret = i; |
| break; |
| } |
| } |
| |
| return ret; |
| } |
| |
| /** |
| * Walks the extended address source match list to find an empty slot. |
| * |
| * @return The index of an unused address slot. |
| * @retval CC1352_SRC_MATCH_NONE No unused slots available. |
| */ |
| static uint8_t rfCoreFindEmptyExtSrcMatchIdx(void) |
| { |
| uint8_t i; |
| uint8_t ret = CC1352_SRC_MATCH_NONE; |
| |
| for (i = 0; i < CC1352_EXTADD_SRC_MATCH_NUM; i++) |
| { |
| if ((sSrcMatchExtData.srcMatchEn[i / 32] & (1 << (i % 32))) != 0u) |
| { |
| ret = i; |
| break; |
| } |
| } |
| |
| return ret; |
| } |
| |
| /** |
| * Sends the tx command to the radio core. |
| * |
| * Sends the packet to the radio core to be sent asynchronously. |
| * |
| * @note @ref aPsdu *must* be 4 byte aligned and not include the FCS. |
| * |
| * @param [in] aPsdu A pointer to the data to be sent. |
| * @param [in] aLen The length in bytes of data pointed to by PSDU. |
| * |
| * @return The value from the command status register. |
| * @retval CMDSTA_Done The command completed correctly. |
| */ |
| static uint_fast8_t rfCoreSendTransmitCmd(uint8_t *aPsdu, uint8_t aLen) |
| { |
| // clang-format off |
| static const rfc_CMD_IEEE_CSMA_t cCsmacaBackoffCmd = |
| { |
| .commandNo = CMD_IEEE_CSMA, |
| .status = IDLE, |
| .startTrigger = |
| { |
| .triggerType = TRIG_NOW, |
| }, |
| .condition = { |
| .rule = COND_ALWAYS, |
| }, |
| .macMaxBE = IEEE802154_MAC_MAX_BE, |
| .macMaxCSMABackoffs = IEEE802154_MAC_MAX_CSMA_BACKOFFS, |
| .csmaConfig = |
| { |
| .initCW = 1, |
| .bSlotted = 0, |
| .rxOffMode = 0, |
| }, |
| .NB = 0, |
| .BE = IEEE802154_MAC_MIN_BE, |
| .remainingPeriods = 0, |
| .endTrigger = |
| { |
| .triggerType = TRIG_NEVER, |
| }, |
| .endTime = 0x00000000, |
| }; |
| static const rfc_CMD_IEEE_TX_t cTransmitCmd = |
| { |
| .commandNo = CMD_IEEE_TX, |
| .status = IDLE, |
| .startTrigger = |
| { |
| .triggerType = TRIG_NOW, |
| }, |
| .condition = { |
| .rule = COND_NEVER, |
| }, |
| .pNextOp = NULL, |
| }; |
| static const rfc_CMD_IEEE_RX_ACK_t cTransmitRxAckCmd = |
| { |
| .commandNo = CMD_IEEE_RX_ACK, |
| .status = IDLE, |
| .startTrigger = |
| { |
| .triggerType = TRIG_NOW, |
| }, |
| .endTrigger = |
| { |
| .triggerType = TRIG_REL_START, |
| .pastTrig = 1, |
| }, |
| .condition = { |
| .rule = COND_NEVER, |
| }, |
| .pNextOp = NULL, |
| /* number of RAT ticks to wait before claiming we haven't received an ack */ |
| .endTime = ((IEEE802154_MAC_ACK_WAIT_DURATION * CC1352_RAT_TICKS_PER_SEC) / IEEE802154_SYMBOLS_PER_SEC), |
| }; |
| // clang-format on |
| |
| /* reset retry count */ |
| sTransmitRetryCount = 0; |
| |
| sCsmacaBackoffCmd = cCsmacaBackoffCmd; |
| /* initialize the random state with a true random seed for the radio core's |
| * psudo rng */ |
| sCsmacaBackoffCmd.randomState = otRandomNonCryptoGetUint16(); |
| sCsmacaBackoffCmd.pNextOp = (rfc_radioOp_t *)&sTransmitCmd; |
| |
| sTransmitCmd = cTransmitCmd; |
| /* no need to look for an ack if the tx operation was stopped */ |
| sTransmitCmd.payloadLen = aLen; |
| sTransmitCmd.pPayload = aPsdu; |
| |
| if (aPsdu[0] & IEEE802154_ACK_REQUEST) |
| { |
| /* setup the receive ack command to follow the tx command */ |
| sTransmitCmd.condition.rule = COND_STOP_ON_FALSE; |
| sTransmitCmd.pNextOp = (rfc_radioOp_t *)&sTransmitRxAckCmd; |
| |
| sTransmitRxAckCmd = cTransmitRxAckCmd; |
| sTransmitRxAckCmd.seqNo = aPsdu[IEEE802154_DSN_OFFSET]; |
| } |
| |
| return (RFCDoorbellSendTo((uint32_t)&sCsmacaBackoffCmd) & 0xFF); |
| } |
| |
| /** |
| * Sends the rx command to the radio core. |
| * |
| * Sends the pre-built receive command to the radio core. This sets up the |
| * radio to receive packets according to the settings in the global rx command. |
| * |
| * @note This function does not alter any of the parameters of the rx command. |
| * It is only concerned with sending the command to the radio core. See @ref |
| * otPlatRadioSetPanId for an example of how the rx settings are set changed. |
| * |
| * @return The value from the command status register. |
| * @retval CMDSTA_Done The command completed correctly. |
| */ |
| static uint_fast8_t rfCoreSendReceiveCmd(void) |
| { |
| sReceiveCmd.status = IDLE; |
| return (RFCDoorbellSendTo((uint32_t)&sReceiveCmd) & 0xFF); |
| } |
| |
| static uint_fast8_t rfCoreSendEdScanCmd(uint8_t aChannel, uint16_t aDurration) |
| { |
| // clang-format off |
| static const rfc_CMD_IEEE_ED_SCAN_t cEdScanCmd = |
| { |
| .commandNo = CMD_IEEE_ED_SCAN, |
| .startTrigger = |
| { |
| .triggerType = TRIG_NOW, |
| }, |
| .condition = { |
| .rule = COND_NEVER, |
| }, |
| .ccaOpt = |
| { |
| .ccaEnEnergy = 1, |
| .ccaEnCorr = 1, |
| .ccaEnSync = 1, |
| .ccaCorrOp = 1, |
| .ccaSyncOp = 0, |
| .ccaCorrThr = 3, |
| }, |
| .ccaRssiThr = -90, |
| .endTrigger = |
| { |
| .triggerType = TRIG_REL_START, |
| .pastTrig = 1, |
| }, |
| }; |
| // clang-format on |
| sEdScanCmd = cEdScanCmd; |
| |
| sEdScanCmd.channel = aChannel; |
| |
| /* durration is in ms */ |
| sEdScanCmd.endTime = aDurration * (CC1352_RAT_TICKS_PER_SEC / 1000); |
| |
| return (RFCDoorbellSendTo((uint32_t)&sEdScanCmd) & 0xFF); |
| } |
| |
| /** |
| * Enables the cpe0 and cpe1 radio interrupts. |
| * |
| * Enables the @ref IRQ_LAST_COMMAND_DONE and @ref IRQ_LAST_FG_COMMAND_DONE to |
| * be handled by the @ref RFCCPE0IntHandler interrupt handler. |
| */ |
| static void rfCoreSetupInt(void) |
| { |
| bool interruptsWereDisabled; |
| |
| otEXPECT(PRCMRfReady()); |
| |
| interruptsWereDisabled = IntMasterDisable(); |
| |
| /* Set all interrupt channels to CPE0 channel, error to CPE1 */ |
| HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEISL) = IRQ_INTERNAL_ERROR; |
| HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIEN) = IRQ_LAST_COMMAND_DONE | IRQ_LAST_FG_COMMAND_DONE; |
| |
| IntRegister(INT_RFC_CPE_0, RFCCPE0IntHandler); |
| IntRegister(INT_RFC_CPE_1, RFCCPE1IntHandler); |
| IntPendClear(INT_RFC_CPE_0); |
| IntPendClear(INT_RFC_CPE_1); |
| IntEnable(INT_RFC_CPE_0); |
| IntEnable(INT_RFC_CPE_1); |
| |
| if (!interruptsWereDisabled) |
| { |
| IntMasterEnable(); |
| } |
| |
| exit: |
| return; |
| } |
| |
| /** |
| * Disables and clears the cpe0 and cpe1 radio interrupts. |
| */ |
| static void rfCoreStopInt(void) |
| { |
| bool interruptsWereDisabled; |
| |
| interruptsWereDisabled = IntMasterDisable(); |
| |
| /* clear and disable interrupts */ |
| HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIFG) = 0x0; |
| HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIEN) = 0x0; |
| |
| IntUnregister(INT_RFC_CPE_0); |
| IntUnregister(INT_RFC_CPE_1); |
| IntPendClear(INT_RFC_CPE_0); |
| IntPendClear(INT_RFC_CPE_1); |
| IntDisable(INT_RFC_CPE_0); |
| IntDisable(INT_RFC_CPE_1); |
| |
| if (!interruptsWereDisabled) |
| { |
| IntMasterEnable(); |
| } |
| } |
| |
| /** |
| * Turns on the radio core. |
| * |
| * Sets up the power and resources for the radio core. |
| * - switches the high frequency clock to the xosc crystal |
| * - sets the mode for the radio core to IEEE 802.15.4 |
| * - initializes the rx buffers and command |
| * - powers on the radio core power domain |
| * - enables the radio core power domain |
| * - sets up the interrupts |
| * - sends the ping command to the radio core to make sure it is running |
| * |
| * @return The value from the command status register. |
| * @retval CMDSTA_Done The command was received. |
| */ |
| static uint_fast8_t rfCorePowerOn(void) |
| { |
| bool interruptsWereDisabled; |
| bool oscSourceSwitch = false; |
| |
| /* Request the HF XOSC as the source for the HF clock. Needed before we can |
| * use the FS. This will only request, it will _not_ perform the switch. |
| */ |
| if (OSCClockSourceGet(OSC_SRC_CLK_HF) != OSC_XOSC_HF) |
| { |
| /* Request to switch to the crystal to enable radio operation. It takes a |
| * while for the XTAL to be ready so instead of performing the actual |
| * switch, we do other stuff while the XOSC is getting ready. |
| */ |
| OSCClockSourceSet(OSC_SRC_CLK_HF, OSC_XOSC_HF); |
| oscSourceSwitch = true; |
| } |
| |
| /* Set of RF Core data queue. Circular buffer, no last entry */ |
| sRxDataQueue.pCurrEntry = sRxBuf0; |
| sRxDataQueue.pLastEntry = NULL; |
| |
| rfCoreInitBufs(); |
| |
| /* |
| * Trigger a switch to the XOSC, so that we can subsequently use the RF FS |
| * This will block until the XOSC is actually ready, but give how we |
| * requested it early on, this won't be too long a wait. |
| * This should be done before starting the RAT. |
| */ |
| if (oscSourceSwitch) |
| { |
| /* Block until the high frequency clock source is ready */ |
| while (!OSCHfSourceReady()) |
| ; |
| |
| /* Switch the HF clock source (cc26xxware executes this from ROM) */ |
| OSCHfSourceSwitch(); |
| } |
| |
| interruptsWereDisabled = IntMasterDisable(); |
| |
| /* Enable RF Core power domain */ |
| PRCMPowerDomainOn(PRCM_DOMAIN_RFCORE); |
| |
| while (PRCMPowerDomainStatus(PRCM_DOMAIN_RFCORE) != PRCM_DOMAIN_POWER_ON) |
| ; |
| |
| PRCMDomainEnable(PRCM_DOMAIN_RFCORE); |
| PRCMLoadSet(); |
| |
| while (!PRCMLoadGet()) |
| ; |
| |
| rfCoreSetupInt(); |
| |
| if (!interruptsWereDisabled) |
| { |
| IntMasterEnable(); |
| } |
| |
| /* Let CPE boot */ |
| RFCClockEnable(); |
| |
| /* Enable ram clocks for patches */ |
| RFCDoorbellSendTo(CMDR_DIR_CMD_2BYTE(CC1352_RF_CMD0, RFC_PWR_PWMCLKEN_MDMRAM | RFC_PWR_PWMCLKEN_RFERAM)); |
| |
| /* Send ping (to verify RFCore is ready and alive) */ |
| return rfCoreExecutePingCmd(); |
| } |
| |
| /** |
| * Turns off the radio core. |
| * |
| * Switches off the power and resources for the radio core. |
| * - disables the interrupts |
| * - disables the radio core power domain |
| * - powers off the radio core power domain |
| * - switches the high frequency clock to the rcosc to save power |
| */ |
| static void rfCorePowerOff(void) |
| { |
| rfCoreStopInt(); |
| |
| PRCMDomainDisable(PRCM_DOMAIN_RFCORE); |
| PRCMLoadSet(); |
| |
| while (!PRCMLoadGet()) |
| ; |
| |
| PRCMPowerDomainOff(PRCM_DOMAIN_RFCORE); |
| |
| while (PRCMPowerDomainStatus(PRCM_DOMAIN_RFCORE) != PRCM_DOMAIN_POWER_OFF) |
| ; |
| |
| if (OSCClockSourceGet(OSC_SRC_CLK_HF) != OSC_RCOSC_HF) |
| { |
| /* Request to switch to the RC osc for low power mode. */ |
| OSCClockSourceSet(OSC_SRC_CLK_HF, OSC_RCOSC_HF); |
| /* Switch the HF clock source (cc26xxware executes this from ROM) */ |
| OSCHfSourceSwitch(); |
| } |
| } |
| |
| /** |
| * Applies CPE patche to the radio. |
| */ |
| static void rfCoreApplyPatch(void) |
| { |
| rf_patch_cpe_ieee_802_15_4(); |
| |
| /* disable ram bus clocks */ |
| RFCDoorbellSendTo(CMDR_DIR_CMD_2BYTE(CC1352_RF_CMD0, 0)); |
| } |
| |
| /** |
| * Sends the setup command string to the radio core. |
| * |
| * Enables the clock line from the RTC to the RF core RAT. Enables the RAT |
| * timer and sets up the radio in IEEE mode. |
| * |
| * @return The value from the command status register. |
| * @retval CMDSTA_Done The command was received. |
| */ |
| static uint_fast16_t rfCoreSendEnableCmd(void) |
| { |
| uint8_t doorbellRet; |
| bool interruptsWereDisabled; |
| uint_fast16_t ret; |
| |
| // clang-format off |
| static const rfc_CMD_SYNC_START_RAT_t cStartRatCmd = |
| { |
| .commandNo = CMD_SYNC_START_RAT, |
| .startTrigger = |
| { |
| .triggerType = TRIG_NOW, |
| }, |
| .condition = { |
| .rule = COND_STOP_ON_FALSE, |
| }, |
| }; |
| static const rfc_CMD_RADIO_SETUP_t cRadioSetupCmd = |
| { |
| .commandNo = CMD_RADIO_SETUP, |
| .startTrigger = |
| { |
| .triggerType = TRIG_NOW, |
| }, |
| .condition = { |
| .rule = COND_NEVER, |
| }, |
| .mode = 1, // IEEE 802.15.4 mode |
| }; |
| // clang-format on |
| /* turn on the clock line to the radio core */ |
| HWREGBITW(AON_RTC_BASE + AON_RTC_O_CTL, AON_RTC_CTL_RTC_UPD_EN_BITN) = 1; |
| |
| /* initialize the rat start command */ |
| sStartRatCmd = cStartRatCmd; |
| sStartRatCmd.pNextOp = (rfc_radioOp_t *)&sRadioSetupCmd; |
| sStartRatCmd.rat0 = sRatOffset; |
| |
| /* initialize radio setup command */ |
| sRadioSetupCmd = cRadioSetupCmd; |
| /* initally set the radio tx power to the max */ |
| sRadioSetupCmd.txPower = sCurrentOutputPower->value; |
| sRadioSetupCmd.pRegOverride = sIEEEOverrides; |
| |
| interruptsWereDisabled = IntMasterDisable(); |
| |
| rfCoreApplyPatch(); |
| |
| doorbellRet = (RFCDoorbellSendTo((uint32_t)&sStartRatCmd) & 0xFF); |
| otEXPECT_ACTION(CMDSTA_Done == doorbellRet, ret = doorbellRet); |
| |
| /* synchronously wait for the CM0 to stop executing */ |
| while ((HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIFG) & IRQ_LAST_COMMAND_DONE) == 0x00) |
| ; |
| |
| ret = sRadioSetupCmd.status; |
| |
| exit: |
| |
| if (!interruptsWereDisabled) |
| { |
| IntMasterEnable(); |
| } |
| |
| return ret; |
| } |
| |
| /** |
| * Sends the shutdown command string to the radio core. |
| * |
| * Powers down the frequency synthesizer and stops the RAT. |
| * |
| * @note synchronously waits until the command string completes. |
| * |
| * @return The status of the RAT stop command. |
| * @retval DONE_OK The command string executed properly. |
| */ |
| static uint_fast16_t rfCoreSendDisableCmd(void) |
| { |
| uint8_t doorbellRet; |
| bool interruptsWereDisabled; |
| uint_fast16_t ret; |
| |
| // clang-format off |
| static const rfc_CMD_FS_POWERDOWN_t cFsPowerdownCmd = |
| { |
| .commandNo = CMD_FS_POWERDOWN, |
| .startTrigger = |
| { |
| .triggerType = TRIG_NOW, |
| }, |
| .condition = { |
| .rule = COND_ALWAYS, |
| }, |
| }; |
| static const rfc_CMD_SYNC_STOP_RAT_t cStopRatCmd = |
| { |
| .commandNo = CMD_SYNC_STOP_RAT, |
| .startTrigger = |
| { |
| .triggerType = TRIG_NOW, |
| }, |
| .condition = { |
| .rule = COND_NEVER, |
| }, |
| }; |
| // clang-format on |
| |
| HWREGBITW(AON_RTC_BASE + AON_RTC_O_CTL, AON_RTC_CTL_RTC_UPD_EN_BITN) = 1; |
| |
| /* initialize the command to power down the frequency synth */ |
| sFsPowerdownCmd = cFsPowerdownCmd; |
| sFsPowerdownCmd.pNextOp = (rfc_radioOp_t *)&sStopRatCmd; |
| |
| sStopRatCmd = cStopRatCmd; |
| |
| interruptsWereDisabled = IntMasterDisable(); |
| |
| HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIFG) = ~IRQ_LAST_COMMAND_DONE; |
| |
| doorbellRet = (RFCDoorbellSendTo((uint32_t)&sFsPowerdownCmd) & 0xFF); |
| otEXPECT_ACTION(CMDSTA_Done == doorbellRet, ret = doorbellRet); |
| |
| /* synchronously wait for the CM0 to stop */ |
| while ((HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIFG) & IRQ_LAST_COMMAND_DONE) == 0x00) |
| ; |
| |
| ret = sStopRatCmd.status; |
| |
| if (sStopRatCmd.status == DONE_OK) |
| { |
| sRatOffset = sStopRatCmd.rat0; |
| } |
| |
| exit: |
| |
| if (!interruptsWereDisabled) |
| { |
| IntMasterEnable(); |
| } |
| |
| return ret; |
| } |
| |
| /** |
| * Error interrupt handler. |
| */ |
| void RFCCPE1IntHandler(void) |
| { |
| /* Clear INTERNAL_ERROR interrupt flag */ |
| HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIFG) = 0x7FFFFFFF; |
| } |
| |
| /** |
| * Command done handler. |
| */ |
| void RFCCPE0IntHandler(void) |
| { |
| if (HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIFG) & IRQ_LAST_COMMAND_DONE) |
| { |
| HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIFG) = ~IRQ_LAST_COMMAND_DONE; |
| |
| if (sState == cc1352_stateReceive && sReceiveCmd.status != ACTIVE && sReceiveCmd.status != IEEE_SUSPENDED) |
| { |
| /* the rx command was probably aborted to change the channel */ |
| sState = cc1352_stateSleep; |
| } |
| else if (sState == cc1352_stateEdScan) |
| { |
| sState = cc1352_stateSleep; |
| } |
| } |
| |
| if (HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIFG) & IRQ_LAST_FG_COMMAND_DONE) |
| { |
| HWREG(RFC_DBELL_NONBUF_BASE + RFC_DBELL_O_RFCPEIFG) = ~IRQ_LAST_FG_COMMAND_DONE; |
| |
| if (sState == cc1352_stateTransmit) |
| { |
| if (sTransmitCmd.pPayload[0] & IEEE802154_ACK_REQUEST) |
| { |
| /* we are looking for an ack */ |
| switch (sTransmitRxAckCmd.status) |
| { |
| case IEEE_DONE_TIMEOUT: |
| if (sTransmitRetryCount < IEEE802154_MAC_MAX_FRAMES_RETRIES) |
| { |
| /* re-submit the tx command chain */ |
| sTransmitRetryCount++; |
| RFCDoorbellSendTo((uint32_t)&sCsmacaBackoffCmd); |
| } |
| else |
| { |
| sTransmitError = OT_ERROR_NO_ACK; |
| /* signal polling function we are done transmitting, we failed to send the packet */ |
| sTxCmdChainDone = true; |
| } |
| |
| break; |
| |
| case IEEE_DONE_ACK: |
| sTransmitError = OT_ERROR_NONE; |
| /* signal polling function we are done transmitting */ |
| sTxCmdChainDone = true; |
| break; |
| |
| case IEEE_DONE_ACKPEND: |
| sTransmitError = OT_ERROR_NONE; |
| /* signal polling function we are done transmitting */ |
| sTxCmdChainDone = true; |
| break; |
| |
| default: |
| sTransmitError = OT_ERROR_FAILED; |
| /* signal polling function we are done transmitting */ |
| sTxCmdChainDone = true; |
| break; |
| } |
| } |
| else |
| { |
| /* The TX command was either stopped or we are not looking for |
| * an ack */ |
| switch (sTransmitCmd.status) |
| { |
| case IEEE_DONE_OK: |
| sTransmitError = OT_ERROR_NONE; |
| break; |
| |
| case IEEE_DONE_TIMEOUT: |
| sTransmitError = OT_ERROR_CHANNEL_ACCESS_FAILURE; |
| break; |
| |
| case IEEE_ERROR_NO_SETUP: |
| case IEEE_ERROR_NO_FS: |
| case IEEE_ERROR_SYNTH_PROG: |
| sTransmitError = OT_ERROR_INVALID_STATE; |
| break; |
| |
| case IEEE_ERROR_TXUNF: |
| sTransmitError = OT_ERROR_NO_BUFS; |
| break; |
| |
| default: |
| sTransmitError = OT_ERROR_FAILED; |
| break; |
| } |
| |
| /* signal polling function we are done transmitting */ |
| sTxCmdChainDone = true; |
| } |
| } |
| } |
| } |
| |
| /** |
| * Function documented in platform-cc1352.h |
| */ |
| void cc1352RadioInit(void) |
| { |
| /* Populate the RX parameters data structure with default values */ |
| rfCoreInitReceiveParams(); |
| |
| sState = cc1352_stateDisabled; |
| } |
| |
| /** |
| * Function documented in platform/radio.h |
| */ |
| otError otPlatRadioEnable(otInstance *aInstance) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| otError error = OT_ERROR_BUSY; |
| |
| if (sState == cc1352_stateSleep) |
| { |
| error = OT_ERROR_NONE; |
| } |
| else if (sState == cc1352_stateDisabled) |
| { |
| otEXPECT_ACTION(rfCorePowerOn() == CMDSTA_Done, error = OT_ERROR_FAILED); |
| otEXPECT_ACTION(rfCoreSendEnableCmd() == DONE_OK, error = OT_ERROR_FAILED); |
| |
| /* The CC1352R1_LAUNCHXL development board has an RF switch to enable |
| * usage of the same antenna between the 2.4G and sub-1G RF paths. This |
| * is connected to IOID 30 on the LaunchPad, and a low voltage will set |
| * the switch to the 2.4G path. |
| */ |
| |
| /* Enable the peripheral domain for the GPIO module */ |
| PRCMPowerDomainOn(PRCM_DOMAIN_PERIPH); |
| /* Enable the GPIO module in all power modes, this will not be turned off */ |
| PRCMPeripheralRunEnable(PRCM_PERIPH_GPIO); |
| PRCMPeripheralSleepEnable(PRCM_PERIPH_GPIO); |
| PRCMPeripheralDeepSleepEnable(PRCM_PERIPH_GPIO); |
| /* Ensure the PRCM module is synced with these settings */ |
| PRCMLoadSet(); |
| /* Enable output on RF switch */ |
| IOCPinTypeGpioOutput(IOID_30); |
| /* Set the RF Switch to the 2.4 GHz path */ |
| GPIO_writeDio(IOID_30, 0); |
| |
| sState = cc1352_stateSleep; |
| error = OT_ERROR_NONE; |
| } |
| |
| exit: |
| |
| if (error == OT_ERROR_FAILED) |
| { |
| rfCorePowerOff(); |
| sState = cc1352_stateDisabled; |
| } |
| |
| return error; |
| } |
| |
| /** |
| * Function documented in platform/radio.h |
| */ |
| bool otPlatRadioIsEnabled(otInstance *aInstance) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| return (sState != cc1352_stateDisabled); |
| } |
| |
| /** |
| * Function documented in platform/radio.h |
| */ |
| otError otPlatRadioDisable(otInstance *aInstance) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| otError error = OT_ERROR_BUSY; |
| |
| if (sState == cc1352_stateDisabled) |
| { |
| error = OT_ERROR_NONE; |
| } |
| else if (sState == cc1352_stateSleep) |
| { |
| rfCoreSendDisableCmd(); |
| /* we don't want to fail if this command string doesn't work, just turn |
| * off the whole core |
| */ |
| rfCorePowerOff(); |
| sState = cc1352_stateDisabled; |
| error = OT_ERROR_NONE; |
| } |
| |
| return error; |
| } |
| |
| /** |
| * Function documented in platform/radio.h |
| */ |
| otError otPlatRadioEnergyScan(otInstance *aInstance, uint8_t aScanChannel, uint16_t aScanDuration) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| otError error = OT_ERROR_BUSY; |
| |
| if (sState == cc1352_stateSleep) |
| { |
| sState = cc1352_stateEdScan; |
| otEXPECT_ACTION(rfCoreSendEdScanCmd(aScanChannel, aScanDuration) == CMDSTA_Done, error = OT_ERROR_FAILED); |
| error = OT_ERROR_NONE; |
| } |
| |
| exit: |
| return error; |
| } |
| |
| /** |
| * Function documented in platform/radio.h |
| */ |
| 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); |
| *aPower = sCurrentOutputPower->dbm; |
| |
| exit: |
| return error; |
| } |
| |
| /** |
| * Function documented in platform/radio.h |
| */ |
| otError otPlatRadioSetTransmitPower(otInstance *aInstance, int8_t aPower) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| unsigned int i; |
| output_config_t const *powerCfg = &(rgOutputPower[0]); |
| |
| for (i = 1; i < OUTPUT_CONFIG_COUNT; i++) |
| { |
| if (rgOutputPower[i].dbm >= aPower) |
| { |
| powerCfg = &(rgOutputPower[i]); |
| } |
| else |
| { |
| break; |
| } |
| } |
| |
| sCurrentOutputPower = powerCfg; |
| |
| return OT_ERROR_NONE; |
| } |
| |
| /** |
| * Function documented in platform/radio.h |
| */ |
| otError otPlatRadioGetCcaEnergyDetectThreshold(otInstance *aInstance, int8_t *aThreshold) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| OT_UNUSED_VARIABLE(aThreshold); |
| |
| return OT_ERROR_NOT_IMPLEMENTED; |
| } |
| |
| /** |
| * Function documented in platform/radio.h |
| */ |
| otError otPlatRadioSetCcaEnergyDetectThreshold(otInstance *aInstance, int8_t aThreshold) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| OT_UNUSED_VARIABLE(aThreshold); |
| |
| return OT_ERROR_NOT_IMPLEMENTED; |
| } |
| |
| /** |
| * Function documented in platform/radio.h |
| */ |
| otError otPlatRadioReceive(otInstance *aInstance, uint8_t aChannel) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| otError error = OT_ERROR_BUSY; |
| |
| if (sState == cc1352_stateSleep) |
| { |
| sState = cc1352_stateReceive; |
| |
| /* |
| * initialize the receive command |
| * XXX: no memset here because we assume init has been called and we |
| * may have changed some values in the rx command |
| */ |
| sReceiveCmd.channel = aChannel; |
| otEXPECT_ACTION(rfCoreSendReceiveCmd() == CMDSTA_Done, error = OT_ERROR_FAILED); |
| error = OT_ERROR_NONE; |
| } |
| else if (sState == cc1352_stateReceive) |
| { |
| if (sReceiveCmd.status == ACTIVE && sReceiveCmd.channel == aChannel) |
| { |
| /* we are already running on the right channel */ |
| sState = cc1352_stateReceive; |
| error = OT_ERROR_NONE; |
| } |
| else |
| { |
| /* |
| * we have either not fallen back into our receive command or |
| * we are running on the wrong channel. Either way assume the |
| * caller correctly called us and abort all running commands. |
| */ |
| otEXPECT_ACTION(rfCoreExecuteAbortCmd() == CMDSTA_Done, error = OT_ERROR_FAILED); |
| |
| /* any frames in the queue will be for the old channel */ |
| otEXPECT_ACTION(rfCoreClearReceiveQueue(&sRxDataQueue) == CMDSTA_Done, error = OT_ERROR_FAILED); |
| |
| sReceiveCmd.channel = aChannel; |
| otEXPECT_ACTION(rfCoreSendReceiveCmd() == CMDSTA_Done, error = OT_ERROR_FAILED); |
| |
| sState = cc1352_stateReceive; |
| error = OT_ERROR_NONE; |
| } |
| } |
| |
| exit: |
| return error; |
| } |
| |
| /** |
| * Function documented in platform/radio.h |
| */ |
| otError otPlatRadioSleep(otInstance *aInstance) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| otError error = OT_ERROR_BUSY; |
| |
| if (sState == cc1352_stateSleep) |
| { |
| error = OT_ERROR_NONE; |
| } |
| else if (sState == cc1352_stateReceive) |
| { |
| if (rfCoreExecuteAbortCmd() != CMDSTA_Done) |
| { |
| error = OT_ERROR_BUSY; |
| } |
| else |
| { |
| sState = cc1352_stateSleep; |
| } |
| } |
| |
| return error; |
| } |
| |
| /** |
| * Function documented in platform/radio.h |
| */ |
| otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *aInstance) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| return &sTransmitFrame; |
| } |
| |
| /** |
| * Function documented in platform/radio.h |
| */ |
| otError otPlatRadioTransmit(otInstance *aInstance, otRadioFrame *aFrame) |
| { |
| otError error = OT_ERROR_BUSY; |
| |
| if (sState == cc1352_stateReceive) |
| { |
| sState = cc1352_stateTransmit; |
| |
| /* removing 2 bytes of CRC placeholder because we generate that in hardware */ |
| otEXPECT_ACTION(rfCoreSendTransmitCmd(aFrame->mPsdu, aFrame->mLength - 2) == CMDSTA_Done, |
| error = OT_ERROR_FAILED); |
| error = OT_ERROR_NONE; |
| sTransmitError = OT_ERROR_NONE; |
| sTxCmdChainDone = false; |
| otPlatRadioTxStarted(aInstance, aFrame); |
| } |
| |
| exit: |
| return error; |
| } |
| |
| /** |
| * Function documented in platform/radio.h |
| */ |
| int8_t otPlatRadioGetRssi(otInstance *aInstance) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| return sRfStats.maxRssi; |
| } |
| |
| /** |
| * Function documented in platform/radio.h |
| */ |
| otRadioCaps otPlatRadioGetCaps(otInstance *aInstance) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| return OT_RADIO_CAPS_ACK_TIMEOUT | OT_RADIO_CAPS_ENERGY_SCAN | OT_RADIO_CAPS_TRANSMIT_RETRIES; |
| } |
| |
| /** |
| * Function documented in platform/radio.h |
| */ |
| void otPlatRadioEnableSrcMatch(otInstance *aInstance, bool aEnable) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| if (sReceiveCmd.status == ACTIVE || sReceiveCmd.status == IEEE_SUSPENDED) |
| { |
| /* we have a running or backgrounded rx command */ |
| rfCoreModifyRxAutoPend(aEnable); |
| } |
| else |
| { |
| /* if we are promiscuous, then frame filtering should be disabled */ |
| sReceiveCmd.frameFiltOpt.autoPendEn = aEnable ? 1 : 0; |
| } |
| } |
| |
| /** |
| * Function documented in platform/radio.h |
| */ |
| otError otPlatRadioAddSrcMatchShortEntry(otInstance *aInstance, const uint16_t aShortAddress) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| otError error = OT_ERROR_NONE; |
| uint8_t idx = rfCoreFindShortSrcMatchIdx(aShortAddress); |
| |
| if (idx == CC1352_SRC_MATCH_NONE) |
| { |
| /* the entry does not exist already, add it */ |
| otEXPECT_ACTION((idx = rfCoreFindEmptyShortSrcMatchIdx()) != CC1352_SRC_MATCH_NONE, error = OT_ERROR_NO_BUFS); |
| sSrcMatchShortData.extAddrEnt[idx].shortAddr = aShortAddress; |
| sSrcMatchShortData.extAddrEnt[idx].panId = sReceiveCmd.localPanID; |
| } |
| |
| if (sReceiveCmd.status == ACTIVE || sReceiveCmd.status == IEEE_SUSPENDED) |
| { |
| /* we have a running or backgrounded rx command */ |
| otEXPECT_ACTION(rfCoreModifySourceMatchEntry(idx, SHORT_ADDRESS, true) == CMDSTA_Done, error = OT_ERROR_FAILED); |
| } |
| else |
| { |
| /* we are not running, so we must update the values ourselves */ |
| sSrcMatchShortData.srcPendEn[idx / 32] |= (1 << (idx % 32)); |
| sSrcMatchShortData.srcMatchEn[idx / 32] |= (1 << (idx % 32)); |
| } |
| |
| exit: |
| return error; |
| } |
| |
| /** |
| * Function documented in platform/radio.h |
| */ |
| otError otPlatRadioClearSrcMatchShortEntry(otInstance *aInstance, const uint16_t aShortAddress) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| otError error = OT_ERROR_NONE; |
| uint8_t idx; |
| |
| otEXPECT_ACTION((idx = rfCoreFindShortSrcMatchIdx(aShortAddress)) != CC1352_SRC_MATCH_NONE, |
| error = OT_ERROR_NO_ADDRESS); |
| |
| if (sReceiveCmd.status == ACTIVE || sReceiveCmd.status == IEEE_SUSPENDED) |
| { |
| /* we have a running or backgrounded rx command */ |
| otEXPECT_ACTION(rfCoreModifySourceMatchEntry(idx, SHORT_ADDRESS, false) == CMDSTA_Done, |
| error = OT_ERROR_FAILED); |
| } |
| else |
| { |
| /* we are not running, so we must update the values ourselves */ |
| sSrcMatchShortData.srcPendEn[idx / 32] &= ~(1 << (idx % 32)); |
| sSrcMatchShortData.srcMatchEn[idx / 32] &= ~(1 << (idx % 32)); |
| } |
| |
| exit: |
| return error; |
| } |
| |
| /** |
| * Function documented in platform/radio.h |
| */ |
| otError otPlatRadioAddSrcMatchExtEntry(otInstance *aInstance, const otExtAddress *aExtAddress) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| otError error = OT_ERROR_NONE; |
| uint8_t idx = rfCoreFindExtSrcMatchIdx((uint64_t *)aExtAddress); |
| |
| if (idx == CC1352_SRC_MATCH_NONE) |
| { |
| /* the entry does not exist already, add it */ |
| otEXPECT_ACTION((idx = rfCoreFindEmptyExtSrcMatchIdx()) != CC1352_SRC_MATCH_NONE, error = OT_ERROR_NO_BUFS); |
| sSrcMatchExtData.extAddrEnt[idx] = *((uint64_t *)aExtAddress); |
| } |
| |
| if (sReceiveCmd.status == ACTIVE || sReceiveCmd.status == IEEE_SUSPENDED) |
| { |
| /* we have a running or backgrounded rx command */ |
| otEXPECT_ACTION(rfCoreModifySourceMatchEntry(idx, EXT_ADDRESS, true) == CMDSTA_Done, error = OT_ERROR_FAILED); |
| } |
| else |
| { |
| /* we are not running, so we must update the values ourselves */ |
| sSrcMatchExtData.srcPendEn[idx / 32] |= (1 << (idx % 32)); |
| sSrcMatchExtData.srcMatchEn[idx / 32] |= (1 << (idx % 32)); |
| } |
| |
| exit: |
| return error; |
| } |
| |
| /** |
| * Function documented in platform/radio.h |
| */ |
| otError otPlatRadioClearSrcMatchExtEntry(otInstance *aInstance, const otExtAddress *aExtAddress) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| otError error = OT_ERROR_NONE; |
| uint8_t idx; |
| |
| otEXPECT_ACTION((idx = rfCoreFindExtSrcMatchIdx((uint64_t *)aExtAddress)) != CC1352_SRC_MATCH_NONE, |
| error = OT_ERROR_NO_ADDRESS); |
| |
| if (sReceiveCmd.status == ACTIVE || sReceiveCmd.status == IEEE_SUSPENDED) |
| { |
| /* we have a running or backgrounded rx command */ |
| otEXPECT_ACTION(rfCoreModifySourceMatchEntry(idx, EXT_ADDRESS, false) == CMDSTA_Done, error = OT_ERROR_FAILED); |
| } |
| else |
| { |
| /* we are not running, so we must update the values ourselves */ |
| sSrcMatchExtData.srcPendEn[idx / 32] &= ~(1 << (idx % 32)); |
| sSrcMatchExtData.srcMatchEn[idx / 32] &= ~(1 << (idx % 32)); |
| } |
| |
| exit: |
| return error; |
| } |
| |
| /** |
| * Function documented in platform/radio.h |
| */ |
| void otPlatRadioClearSrcMatchShortEntries(otInstance *aInstance) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| if (sReceiveCmd.status == ACTIVE || sReceiveCmd.status == IEEE_SUSPENDED) |
| { |
| unsigned int i; |
| |
| for (i = 0; i < CC1352_SHORTADD_SRC_MATCH_NUM; i++) |
| { |
| /* we have a running or backgrounded rx command */ |
| otEXPECT(rfCoreModifySourceMatchEntry(i, SHORT_ADDRESS, false) == CMDSTA_Done); |
| } |
| } |
| else |
| { |
| /* we are not running, so we can erase them ourselves */ |
| memset((void *)&sSrcMatchShortData, 0, sizeof(sSrcMatchShortData)); |
| } |
| |
| exit: |
| return; |
| } |
| |
| /** |
| * Function documented in platform/radio.h |
| */ |
| void otPlatRadioClearSrcMatchExtEntries(otInstance *aInstance) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| if (sReceiveCmd.status == ACTIVE || sReceiveCmd.status == IEEE_SUSPENDED) |
| { |
| unsigned int i; |
| |
| for (i = 0; i < CC1352_EXTADD_SRC_MATCH_NUM; i++) |
| { |
| /* we have a running or backgrounded rx command */ |
| otEXPECT(rfCoreModifySourceMatchEntry(i, EXT_ADDRESS, false) == CMDSTA_Done); |
| } |
| } |
| else |
| { |
| /* we are not running, so we can erase them ourselves */ |
| memset((void *)&sSrcMatchExtData, 0, sizeof(sSrcMatchExtData)); |
| } |
| |
| exit: |
| return; |
| } |
| |
| /** |
| * Function documented in platform/radio.h |
| */ |
| bool otPlatRadioGetPromiscuous(otInstance *aInstance) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| /* we are promiscuous if we are not filtering */ |
| return sReceiveCmd.frameFiltOpt.frameFiltEn == 0; |
| } |
| |
| /** |
| * Function documented in platform/radio.h |
| */ |
| void otPlatRadioSetPromiscuous(otInstance *aInstance, bool aEnable) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| if (sReceiveCmd.status == ACTIVE || sReceiveCmd.status == IEEE_SUSPENDED) |
| { |
| /* we have a running or backgrounded rx command */ |
| /* if we are promiscuous, then frame filtering should be disabled */ |
| rfCoreModifyRxFrameFilter(!aEnable); |
| /* XXX should we dump any queued messages ? */ |
| } |
| else |
| { |
| /* if we are promiscuous, then frame filtering should be disabled */ |
| sReceiveCmd.frameFiltOpt.frameFiltEn = aEnable ? 0 : 1; |
| } |
| } |
| |
| /** |
| * Function documented in platform/radio.h |
| */ |
| void otPlatRadioGetIeeeEui64(otInstance *aInstance, uint8_t *aIeeeEui64) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| uint8_t * eui64; |
| unsigned int i; |
| |
| /* |
| * The IEEE MAC address can be stored two places. We check the Customer |
| * Configuration was not set before defaulting to the Factory |
| * Configuration. |
| */ |
| eui64 = (uint8_t *)(CCFG_BASE + CCFG_O_IEEE_MAC_0); |
| |
| for (i = 0; i < OT_EXT_ADDRESS_SIZE; i++) |
| { |
| if (eui64[i] != CC1352_UNKNOWN_EUI64) |
| { |
| break; |
| } |
| } |
| |
| if (i >= OT_EXT_ADDRESS_SIZE) |
| { |
| /* The ccfg address was all 0xFF, switch to the fcfg */ |
| eui64 = (uint8_t *)(FCFG1_BASE + FCFG1_O_MAC_15_4_0); |
| } |
| |
| /* |
| * The IEEE MAC address is stored in network byte order. The caller seems |
| * to want the address stored in little endian format, which is backwards |
| * of the conventions setup by @ref otPlatRadioSetExtendedAddress. |
| * otPlatRadioSetExtendedAddress assumes that the address being passed to |
| * it is in network byte order, so the caller of |
| * otPlatRadioSetExtendedAddress must swap the endianness before calling. |
| * |
| * It may be easier to have the caller of this function store the IEEE |
| * address in network byte order. |
| */ |
| for (i = 0; i < OT_EXT_ADDRESS_SIZE; i++) |
| { |
| aIeeeEui64[i] = eui64[(OT_EXT_ADDRESS_SIZE - 1) - i]; |
| } |
| } |
| |
| /** |
| * Function documented in platform/radio.h |
| * |
| * @note it is entirely possible for this function to fail, but there is no |
| * valid way to return that error since the function prototype was changed. |
| */ |
| void otPlatRadioSetPanId(otInstance *aInstance, uint16_t aPanid) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| /* XXX: if the pan id is the broadcast pan id (0xFFFF) the auto ack will |
| * not work. This is due to the design of the CM0 and follows IEEE 802.15.4 |
| */ |
| if (sState == cc1352_stateReceive) |
| { |
| otEXPECT(rfCoreExecuteAbortCmd() == CMDSTA_Done); |
| sReceiveCmd.localPanID = aPanid; |
| otEXPECT(rfCoreClearReceiveQueue(&sRxDataQueue) == CMDSTA_Done); |
| otEXPECT(rfCoreSendReceiveCmd() == CMDSTA_Done); |
| /* the interrupt from abort changed our state to sleep */ |
| sState = cc1352_stateReceive; |
| } |
| else if (sState != cc1352_stateTransmit) |
| { |
| sReceiveCmd.localPanID = aPanid; |
| } |
| |
| exit: |
| return; |
| } |
| |
| /** |
| * Function documented in platform/radio.h |
| * |
| * @note it is entirely possible for this function to fail, but there is no |
| * valid way to return that error since the function prototype was changed. |
| */ |
| void otPlatRadioSetExtendedAddress(otInstance *aInstance, const otExtAddress *aAddress) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| /* XXX: assuming little endian format */ |
| if (sState == cc1352_stateReceive) |
| { |
| otEXPECT(rfCoreExecuteAbortCmd() == CMDSTA_Done); |
| sReceiveCmd.localExtAddr = *((uint64_t *)(aAddress)); |
| otEXPECT(rfCoreClearReceiveQueue(&sRxDataQueue) == CMDSTA_Done); |
| otEXPECT(rfCoreSendReceiveCmd() == CMDSTA_Done); |
| /* the interrupt from abort changed our state to sleep */ |
| sState = cc1352_stateReceive; |
| } |
| else if (sState != cc1352_stateTransmit) |
| { |
| sReceiveCmd.localExtAddr = *((uint64_t *)(aAddress)); |
| } |
| |
| exit: |
| return; |
| } |
| |
| /** |
| * Function documented in platform/radio.h |
| * |
| * @note it is entirely possible for this function to fail, but there is no |
| * valid way to return that error since the function prototype was changed. |
| */ |
| void otPlatRadioSetShortAddress(otInstance *aInstance, uint16_t aAddress) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| if (sState == cc1352_stateReceive) |
| { |
| otEXPECT(rfCoreExecuteAbortCmd() == CMDSTA_Done); |
| sReceiveCmd.localShortAddr = aAddress; |
| otEXPECT(rfCoreClearReceiveQueue(&sRxDataQueue) == CMDSTA_Done); |
| otEXPECT(rfCoreSendReceiveCmd() == CMDSTA_Done); |
| /* the interrupt from abort changed our state to sleep */ |
| sState = cc1352_stateReceive; |
| } |
| else if (sState != cc1352_stateTransmit) |
| { |
| sReceiveCmd.localShortAddr = aAddress; |
| } |
| |
| exit: |
| return; |
| } |
| |
| static void cc1352RadioProcessTransmitDone(otInstance * aInstance, |
| otRadioFrame *aTransmitFrame, |
| otRadioFrame *aAckFrame, |
| otError aTransmitError) |
| { |
| #if OPENTHREAD_CONFIG_DIAG_ENABLE |
| |
| if (otPlatDiagModeGet()) |
| { |
| otPlatDiagRadioTransmitDone(aInstance, aTransmitFrame, aTransmitError); |
| } |
| else |
| #endif /* OPENTHREAD_CONFIG_DIAG_ENABLE */ |
| { |
| otPlatRadioTxDone(aInstance, aTransmitFrame, aAckFrame, aTransmitError); |
| } |
| } |
| |
| static void cc1352RadioProcessReceiveDone(otInstance *aInstance, otRadioFrame *aReceiveFrame, otError aReceiveError) |
| { |
| // TODO Set this flag only when the packet is really acknowledged with frame pending set. |
| // See https://github.com/openthread/openthread/pull/3785 |
| aReceiveFrame->mInfo.mRxInfo.mAckedWithFramePending = true; |
| #if OPENTHREAD_CONFIG_DIAG_ENABLE |
| |
| if (otPlatDiagModeGet()) |
| { |
| otPlatDiagRadioReceiveDone(aInstance, aReceiveFrame, aReceiveError); |
| } |
| else |
| #endif /* OPENTHREAD_CONFIG_DIAG_ENABLE */ |
| { |
| otPlatRadioReceiveDone(aInstance, aReceiveFrame, aReceiveError); |
| } |
| } |
| |
| static void cc1352RadioProcessReceiveQueue(otInstance *aInstance) |
| { |
| rfc_ieeeRxCorrCrc_t * crcCorr; |
| rfc_dataEntryGeneral_t *curEntry, *startEntry; |
| uint8_t rssi; |
| |
| startEntry = (rfc_dataEntryGeneral_t *)sRxDataQueue.pCurrEntry; |
| curEntry = startEntry; |
| |
| /* loop through receive queue */ |
| do |
| { |
| uint8_t *payload = &(curEntry->data); |
| |
| if (curEntry->status == DATA_ENTRY_FINISHED) |
| { |
| uint8_t len; |
| otError receiveError; |
| otRadioFrame receiveFrame; |
| |
| /* get the information appended to the end of the frame. |
| * This array access looks like it is a fencepost error, but the |
| * first byte is the number of bytes that follow. |
| */ |
| len = payload[0]; |
| crcCorr = (rfc_ieeeRxCorrCrc_t *)&payload[len]; |
| rssi = payload[len - 1]; |
| |
| if (crcCorr->status.bCrcErr == 0 && (len - 2) < OT_RADIO_FRAME_MAX_SIZE) |
| { |
| #if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE |
| #error Time sync requires the timestamp of SFD rather than that of rx done! |
| #else |
| if (otPlatRadioGetPromiscuous(aInstance)) |
| #endif |
| { |
| // TODO: Propagate CM0 timestamp |
| // The current driver only supports milliseconds resolution. |
| receiveFrame.mInfo.mRxInfo.mTimestamp = otPlatAlarmMilliGetNow() * 1000; |
| } |
| |
| receiveFrame.mLength = len; |
| receiveFrame.mPsdu = &(payload[1]); |
| receiveFrame.mChannel = sReceiveCmd.channel; |
| receiveFrame.mInfo.mRxInfo.mRssi = rssi; |
| receiveFrame.mInfo.mRxInfo.mLqi = crcCorr->status.corr; |
| |
| receiveError = OT_ERROR_NONE; |
| } |
| else |
| { |
| receiveError = OT_ERROR_FCS; |
| } |
| |
| if ((receiveFrame.mPsdu[0] & IEEE802154_FRAME_TYPE_MASK) == IEEE802154_FRAME_TYPE_ACK) |
| { |
| if (receiveFrame.mPsdu[IEEE802154_DSN_OFFSET] == sTransmitFrame.mPsdu[IEEE802154_DSN_OFFSET]) |
| { |
| sState = cc1352_stateReceive; |
| cc1352RadioProcessTransmitDone(aInstance, &sTransmitFrame, &receiveFrame, receiveError); |
| } |
| } |
| else |
| { |
| cc1352RadioProcessReceiveDone(aInstance, &receiveFrame, receiveError); |
| } |
| |
| curEntry->status = DATA_ENTRY_PENDING; |
| break; |
| } |
| else if (curEntry->status == DATA_ENTRY_UNFINISHED) |
| { |
| curEntry->status = DATA_ENTRY_PENDING; |
| } |
| |
| curEntry = (rfc_dataEntryGeneral_t *)(curEntry->pNextEntry); |
| } while (curEntry != startEntry); |
| } |
| |
| /** |
| * Function documented in platform-cc1352.h |
| */ |
| void cc1352RadioProcess(otInstance *aInstance) |
| { |
| if (sState == cc1352_stateEdScan) |
| { |
| if (sEdScanCmd.status == IEEE_DONE_OK) |
| { |
| otPlatRadioEnergyScanDone(aInstance, sEdScanCmd.maxRssi); |
| } |
| else if (sEdScanCmd.status == ACTIVE) |
| { |
| otPlatRadioEnergyScanDone(aInstance, CC1352_INVALID_RSSI); |
| } |
| } |
| |
| if (sState == cc1352_stateReceive || sState == cc1352_stateTransmit) |
| { |
| cc1352RadioProcessReceiveQueue(aInstance); |
| } |
| |
| if (sTxCmdChainDone) |
| { |
| if (sState == cc1352_stateTransmit) |
| { |
| /* we are not looking for an ACK packet, or failed */ |
| sState = cc1352_stateReceive; |
| cc1352RadioProcessTransmitDone(aInstance, &sTransmitFrame, NULL, sTransmitError); |
| } |
| |
| sTransmitError = OT_ERROR_NONE; |
| sTxCmdChainDone = false; |
| } |
| } |
| |
| int8_t otPlatRadioGetReceiveSensitivity(otInstance *aInstance) |
| { |
| OT_UNUSED_VARIABLE(aInstance); |
| |
| return CC1352_RECEIVE_SENSITIVITY; |
| } |