blob: b5a65ec87f7ad3d52544979eeabdcf5f749647de [file] [log] [blame]
/*
* 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;
}