Last active
January 8, 2018 06:07
-
-
Save sjlongland/5a3f8a3a9878396da1356c264f7fd87f to your computer and use it in GitHub Desktop.
Debugging the CC2538 radio driver.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (c) 2016, 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 <openthread/openthread.h> | |
#include <openthread/platform/alarm-milli.h> | |
#include <openthread/platform/diag.h> | |
#include <openthread/platform/platform.h> | |
#include <openthread/platform/radio.h> | |
#include "platform-cc2538.h" | |
#include "common/logging.hpp" | |
#include "utils/code_utils.h" | |
#include "fs.h" | |
#include <sys/types.h> | |
#include <sys/stat.h> | |
#include <fcntl.h> | |
#include <sys/time.h> | |
#include <time.h> | |
enum | |
{ | |
IEEE802154_MIN_LENGTH = 5, | |
IEEE802154_MAX_LENGTH = 127, | |
IEEE802154_ACK_LENGTH = 5, | |
IEEE802154_FRAME_TYPE_MASK = 0x7, | |
IEEE802154_FRAME_TYPE_ACK = 0x2, | |
IEEE802154_FRAME_PENDING = 1 << 4, | |
IEEE802154_ACK_REQUEST = 1 << 5, | |
IEEE802154_DSN_OFFSET = 2, | |
}; | |
enum | |
{ | |
CC2538_RSSI_OFFSET = 73, | |
CC2538_CRC_BIT_MASK = 0x80, | |
CC2538_LQI_BIT_MASK = 0x7f, | |
}; | |
enum | |
{ | |
CC2538_RECEIVE_SENSITIVITY = -100, // dBm | |
}; | |
typedef struct TxPowerTable | |
{ | |
int8_t mTxPowerVal; | |
uint8_t mTxPowerReg; | |
} TxPowerTable; | |
// The transmit power table, the values are from application note 130 | |
static const TxPowerTable sTxPowerTable[] = | |
{ | |
{ 22, 0xFF }, /* 22.0dBm =~ 158.5mW */ | |
{ 21, 0xD5 }, /* 20.9dBm =~ 123.0mW */ | |
{ 20, 0xC5 }, /* 20.1dBm =~ 102.3mW */ | |
{ 19, 0xB0 }, /* 19.0dBm =~ 79.4mW */ | |
{ 18, 0xA1 }, /* 17.8dBm =~ 60.3mW */ | |
{ 16, 0x91 }, /* 16.4dBm =~ 43.7mW */ | |
{ 15, 0x88 }, /* 14.9dBm =~ 30.9mW */ | |
{ 13, 0x72 }, /* 13.0dBm =~ 20.0mW */ | |
{ 11, 0x62 }, /* 11.0dBm =~ 12.6mW */ | |
{ 10, 0x58 }, /* 9.5dBm =~ 8.9mW */ | |
{ 8, 0x42 }, /* 7.5dBm =~ 5.6mW */ | |
}; | |
static otRadioFrame sTransmitFrame; | |
static otRadioFrame sReceiveFrame; | |
static otError sTransmitError; | |
static otError sReceiveError; | |
static uint8_t sTransmitPsdu[IEEE802154_MAX_LENGTH]; | |
static uint8_t sReceivePsdu[IEEE802154_MAX_LENGTH]; | |
static uint8_t sChannel = 0; | |
static int8_t sTxPower = 0; | |
static otRadioState sState = OT_RADIO_STATE_DISABLED; | |
static bool sIsReceiverEnabled = false; | |
static const char* get_time() { | |
static char _time[32]; | |
struct timeval tv; | |
gettimeofday(&tv, NULL); | |
ctime_r(&tv.tv_sec, _time); | |
const char* end = rindex(_time, '\n'); | |
if (end) | |
end = 0; | |
return _time; | |
} | |
void enableReceiver(void) | |
{ | |
if (!sIsReceiverEnabled) | |
{ | |
otLogInfoPlat(sInstance, "%s Enabling receiver", get_time(), NULL); | |
// flush rxfifo | |
HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX; | |
HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX; | |
// enable receiver | |
HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_RXON; | |
sIsReceiverEnabled = true; | |
} | |
} | |
void disableReceiver(void) | |
{ | |
if (sIsReceiverEnabled) | |
{ | |
otLogInfoPlat(sInstance, "%s Disabling receiver", get_time(), NULL); | |
while (HWREG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_TX_ACTIVE); | |
// flush rxfifo | |
HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX; | |
HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX; | |
if (HWREG(RFCORE_XREG_RXENABLE) != 0) | |
{ | |
// disable receiver | |
HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_RFOFF; | |
} | |
sIsReceiverEnabled = false; | |
} | |
} | |
void setChannel(uint8_t aChannel) | |
{ | |
if (sChannel != aChannel) | |
{ | |
bool enabled = false; | |
if (sIsReceiverEnabled) | |
{ | |
otLogDebgPlat(sInstance, "%s Set channel(%d), need to disable receiver", get_time(), aChannel); | |
disableReceiver(); | |
enabled = true; | |
} | |
otLogInfoPlat(sInstance, "%s Channel=%d", get_time(), aChannel); | |
HWREG(RFCORE_XREG_FREQCTRL) = 11 + (aChannel - 11) * 5; | |
sChannel = aChannel; | |
if (enabled) | |
{ | |
otLogDebgPlat(sInstance, "%s Set channel(%d), re-enabling receiver", get_time(), aChannel); | |
enableReceiver(); | |
} | |
} | |
} | |
void setTxPower(int8_t aTxPower) | |
{ | |
uint8_t i = 0; | |
if (sTxPower != aTxPower) | |
{ | |
otLogInfoPlat(sInstance, "%s TxPower=%d", get_time(), aTxPower); | |
for (i = sizeof(sTxPowerTable) / sizeof(TxPowerTable) - 1; i > 0; i--) | |
{ | |
if (aTxPower < sTxPowerTable[i].mTxPowerVal) | |
{ | |
break; | |
} | |
} | |
HWREG(RFCORE_XREG_TXPOWER) = sTxPowerTable[i].mTxPowerReg; | |
sTxPower = aTxPower; | |
} | |
} | |
void otPlatRadioGetIeeeEui64(otInstance *aInstance, uint8_t *aIeeeEui64) | |
{ | |
uint8_t *eui64 = (uint8_t *)IEEE_EUI64; | |
(void)aInstance; | |
for (uint8_t i = 0; i < OT_EXT_ADDRESS_SIZE; i++) | |
{ | |
aIeeeEui64[i] = eui64[7 - i]; | |
} | |
} | |
void otPlatRadioSetPanId(otInstance *aInstance, uint16_t aPanid) | |
{ | |
(void)aInstance; | |
otLogInfoPlat(sInstance, "%s PANID=%X", get_time(), aPanid); | |
HWREG(RFCORE_FFSM_PAN_ID0) = aPanid & 0xFF; | |
HWREG(RFCORE_FFSM_PAN_ID1) = aPanid >> 8; | |
} | |
void otPlatRadioSetExtendedAddress(otInstance *aInstance, const otExtAddress *aAddress) | |
{ | |
(void)aInstance; | |
otLogInfoPlat(sInstance, "%s ExtAddr=%X%X%X%X%X%X%X%X", get_time(), | |
aAddress->m8[7], aAddress->m8[6], aAddress->m8[5], aAddress->m8[4], | |
aAddress->m8[3], aAddress->m8[2], aAddress->m8[1], aAddress->m8[0]); | |
for (int i = 0; i < 8; i++) | |
{ | |
((volatile uint32_t *)RFCORE_FFSM_EXT_ADDR0)[i] = aAddress->m8[i]; | |
} | |
} | |
void otPlatRadioSetShortAddress(otInstance *aInstance, uint16_t aAddress) | |
{ | |
(void)aInstance; | |
otLogInfoPlat(sInstance, "%s ShortAddr=%X", get_time(), aAddress); | |
HWREG(RFCORE_FFSM_SHORT_ADDR0) = aAddress & 0xFF; | |
HWREG(RFCORE_FFSM_SHORT_ADDR1) = aAddress >> 8; | |
} | |
void cc2538RadioInit(void) | |
{ | |
sTransmitFrame.mLength = 0; | |
sTransmitFrame.mPsdu = sTransmitPsdu; | |
sReceiveFrame.mLength = 0; | |
sReceiveFrame.mPsdu = sReceivePsdu; | |
// enable clock | |
HWREG(SYS_CTRL_RCGCRFC) = SYS_CTRL_RCGCRFC_RFC0; | |
HWREG(SYS_CTRL_SCGCRFC) = SYS_CTRL_SCGCRFC_RFC0; | |
HWREG(SYS_CTRL_DCGCRFC) = SYS_CTRL_DCGCRFC_RFC0; | |
// Table 23-7. | |
HWREG(RFCORE_XREG_AGCCTRL1) = 0x15; | |
HWREG(RFCORE_XREG_TXFILTCFG) = 0x09; | |
HWREG(ANA_REGS_BASE + ANA_REGS_O_IVCTRL) = 0x0b; | |
HWREG(RFCORE_XREG_CCACTRL0) = 0xf8; | |
HWREG(RFCORE_XREG_FIFOPCTRL) = IEEE802154_MAX_LENGTH; | |
HWREG(RFCORE_XREG_FRMCTRL0) = RFCORE_XREG_FRMCTRL0_AUTOCRC | RFCORE_XREG_FRMCTRL0_AUTOACK; | |
// default: SRCMATCH.SRC_MATCH_EN(1), SRCMATCH.AUTOPEND(1), | |
// SRCMATCH.PEND_DATAREQ_ONLY(1), RFCORE_XREG_FRMCTRL1_PENDING_OR(0) | |
HWREG(RFCORE_XREG_TXPOWER) = sTxPowerTable[0].mTxPowerReg; | |
sTxPower = sTxPowerTable[0].mTxPowerVal; | |
otLogInfoPlat(sInstance, "%s Initialized", get_time()); | |
} | |
bool otPlatRadioIsEnabled(otInstance *aInstance) | |
{ | |
(void)aInstance; | |
return (sState != OT_RADIO_STATE_DISABLED) ? true : false; | |
} | |
otError otPlatRadioEnable(otInstance *aInstance) | |
{ | |
if (!otPlatRadioIsEnabled(aInstance)) | |
{ | |
otLogDebgPlat(sInstance, "%s State=OT_RADIO_STATE_SLEEP [%d]", get_time(), __LINE__); | |
sState = OT_RADIO_STATE_SLEEP; | |
} | |
return OT_ERROR_NONE; | |
} | |
otError otPlatRadioDisable(otInstance *aInstance) | |
{ | |
if (otPlatRadioIsEnabled(aInstance)) | |
{ | |
otLogDebgPlat(sInstance, "%s State=OT_RADIO_STATE_DISABLED [%d]", get_time(), __LINE__); | |
sState = OT_RADIO_STATE_DISABLED; | |
} | |
return OT_ERROR_NONE; | |
} | |
otError otPlatRadioSleep(otInstance *aInstance) | |
{ | |
otError error = OT_ERROR_INVALID_STATE; | |
(void)aInstance; | |
if (sState == OT_RADIO_STATE_SLEEP || sState == OT_RADIO_STATE_RECEIVE) | |
{ | |
otLogDebgPlat(sInstance, "%s State=OT_RADIO_STATE_SLEEP [%d]", get_time(), __LINE__); | |
error = OT_ERROR_NONE; | |
sState = OT_RADIO_STATE_SLEEP; | |
disableReceiver(); | |
} | |
return error; | |
} | |
otError otPlatRadioReceive(otInstance *aInstance, uint8_t aChannel) | |
{ | |
otError error = OT_ERROR_INVALID_STATE; | |
(void)aInstance; | |
if (sState != OT_RADIO_STATE_DISABLED) | |
{ | |
otLogDebgPlat(sInstance, "%s State=OT_RADIO_STATE_RECEIVE [%d]", get_time(), __LINE__); | |
error = OT_ERROR_NONE; | |
sState = OT_RADIO_STATE_RECEIVE; | |
setChannel(aChannel); | |
sReceiveFrame.mChannel = aChannel; | |
enableReceiver(); | |
} | |
return error; | |
} | |
static void setupTransmit(otRadioFrame *aFrame) | |
{ | |
int i; | |
// wait for current TX operation to complete, if any. | |
while (HWREG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_TX_ACTIVE); | |
// flush txfifo | |
HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHTX; | |
HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHTX; | |
// frame length | |
HWREG(RFCORE_SFR_RFDATA) = aFrame->mLength; | |
// frame data | |
for (i = 0; i < aFrame->mLength; i++) | |
{ | |
HWREG(RFCORE_SFR_RFDATA) = aFrame->mPsdu[i]; | |
} | |
setChannel(aFrame->mChannel); | |
} | |
otError otPlatRadioTransmit(otInstance *aInstance, otRadioFrame *aFrame) | |
{ | |
otError error = OT_ERROR_INVALID_STATE; | |
(void)aInstance; | |
if (sState == OT_RADIO_STATE_RECEIVE) | |
{ | |
int i; | |
error = OT_ERROR_NONE; | |
otLogDebgPlat(sInstance, "%s State=OT_RADIO_STATE_TRANSMIT [%d]", get_time(), __LINE__); | |
sState = OT_RADIO_STATE_TRANSMIT; | |
sTransmitError = OT_ERROR_NONE; | |
setupTransmit(aFrame); | |
// Set up a counter to inform us if we get stuck. | |
i = 1000000; | |
// Wait for radio to enter receive state. | |
while ((HWREG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_RX_ACTIVE) == 0) | |
{ | |
// Count down the cycles, and emit a message if we get to zero. | |
// Ideally, we should never get there! | |
if (i) | |
{ | |
i--; | |
} | |
else | |
{ | |
otLogCritPlat(sInstance, "Radio is stuck!!! FSMSTAT0=0x%08x FSMSTAT1=0x%08x RFERRF=0x%08x", get_time(), | |
HWREG(RFCORE_XREG_FSMSTAT0), HWREG(RFCORE_XREG_FSMSTAT1), HWREG(RFCORE_SFR_RFERRF)); | |
i = 1000000; | |
} | |
// Ensure we haven't overflowed the RX buffer in the mean time, as this | |
// will cause a deadlock here otherwise. Similarly, if we see an aborted | |
// RX, handle that here too to prevent deadlock. | |
if (HWREG(RFCORE_SFR_RFERRF) & | |
(RFCORE_SFR_RFERRF_RXOVERF | RFCORE_SFR_RFERRF_RXABO)) | |
{ | |
if (HWREG(RFCORE_SFR_RFERRF) & RFCORE_SFR_RFERRF_RXOVERF) | |
{ | |
otLogCritPlat(sInstance, "RX Buffer Overflow detected", get_time(), NULL); | |
} | |
if (HWREG(RFCORE_SFR_RFERRF) & RFCORE_SFR_RFERRF_RXABO) | |
{ | |
otLogCritPlat(sInstance, "Aborted RX detected", get_time(), NULL); | |
} | |
// Flush the RX buffer | |
HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX; | |
HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX; | |
} | |
// Check for idle state. After flushing the RX buffer, we may wind up here. | |
if (!(HWREG(RFCORE_XREG_FSMSTAT1) | |
& (RFCORE_XREG_FSMSTAT1_TX_ACTIVE | RFCORE_XREG_FSMSTAT1_RX_ACTIVE))) | |
{ | |
otLogCritPlat(sInstance, "Idle state detected", get_time(), NULL); | |
// In this case, the state of our driver mis-matches our state. So force | |
// matters by clearing our channel variable and calling setChannel. This | |
// should bring our radio into the RX state, which should allow us to go | |
// into TX. | |
sChannel = 0; | |
setupTransmit(aFrame); | |
} | |
} | |
// wait for valid rssi | |
while ((HWREG(RFCORE_XREG_RSSISTAT) & RFCORE_XREG_RSSISTAT_RSSI_VALID) == 0); | |
otEXPECT_ACTION(((HWREG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_CCA) && | |
!((HWREG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_SFD))), | |
sTransmitError = OT_ERROR_CHANNEL_ACCESS_FAILURE); | |
// begin transmit | |
HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_TXON; | |
otPlatRadioTxStarted(aInstance, aFrame); | |
while (HWREG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_TX_ACTIVE); | |
otLogDebgPlat(sInstance, "%s Transmitted %d bytes", get_time(), aFrame->mLength); | |
} | |
else | |
{ | |
otLogDebgPlat(sInstance, "%s Not in RECEIVE state (state %d)", get_time(), sState); | |
} | |
exit: | |
return error; | |
} | |
otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *aInstance) | |
{ | |
(void)aInstance; | |
return &sTransmitFrame; | |
} | |
int8_t otPlatRadioGetRssi(otInstance *aInstance) | |
{ | |
(void)aInstance; | |
return 0; | |
} | |
otRadioCaps otPlatRadioGetCaps(otInstance *aInstance) | |
{ | |
(void)aInstance; | |
return OT_RADIO_CAPS_NONE; | |
} | |
bool otPlatRadioGetPromiscuous(otInstance *aInstance) | |
{ | |
(void)aInstance; | |
return (HWREG(RFCORE_XREG_FRMFILT0) & RFCORE_XREG_FRMFILT0_FRAME_FILTER_EN) == 0; | |
} | |
void otPlatRadioSetPromiscuous(otInstance *aInstance, bool aEnable) | |
{ | |
(void)aInstance; | |
otLogInfoPlat(sInstance, "%s PromiscuousMode=%d", get_time(), aEnable ? 1 : 0); | |
if (aEnable) | |
{ | |
HWREG(RFCORE_XREG_FRMFILT0) &= ~RFCORE_XREG_FRMFILT0_FRAME_FILTER_EN; | |
} | |
else | |
{ | |
HWREG(RFCORE_XREG_FRMFILT0) |= RFCORE_XREG_FRMFILT0_FRAME_FILTER_EN; | |
} | |
} | |
void readFrame(void) | |
{ | |
uint8_t length; | |
uint8_t crcCorr; | |
int i; | |
otEXPECT(sState == OT_RADIO_STATE_RECEIVE || sState == OT_RADIO_STATE_TRANSMIT); | |
otEXPECT((HWREG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_FIFOP) != 0); | |
// read length | |
length = HWREG(RFCORE_SFR_RFDATA); | |
otEXPECT(IEEE802154_MIN_LENGTH <= length && length <= IEEE802154_MAX_LENGTH); | |
#if OPENTHREAD_ENABLE_RAW_LINK_API | |
// Timestamp | |
sReceiveFrame.mMsec = otPlatAlarmMilliGetNow(); | |
sReceiveFrame.mUsec = 0; // Don't support microsecond timer for now. | |
#endif | |
// read psdu | |
for (i = 0; i < length - 2; i++) | |
{ | |
sReceiveFrame.mPsdu[i] = HWREG(RFCORE_SFR_RFDATA); | |
} | |
sReceiveFrame.mRssi = (int8_t)HWREG(RFCORE_SFR_RFDATA) - CC2538_RSSI_OFFSET; | |
crcCorr = HWREG(RFCORE_SFR_RFDATA); | |
if (crcCorr & CC2538_CRC_BIT_MASK) | |
{ | |
sReceiveFrame.mLength = length; | |
sReceiveFrame.mLqi = crcCorr & CC2538_LQI_BIT_MASK; | |
} | |
else | |
{ | |
// resets rxfifo | |
HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX; | |
HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX; | |
otLogDebgPlat(sInstance, "%s Dropping %d received bytes (Invalid CRC)", get_time(), length); | |
} | |
// check for rxfifo overflow | |
if ((HWREG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_FIFOP) != 0 && | |
(HWREG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_FIFO) == 0) | |
{ | |
HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX; | |
HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX; | |
} | |
exit: | |
return; | |
} | |
void cc2538RadioProcess(otInstance *aInstance) | |
{ | |
readFrame(); | |
if ((sState == OT_RADIO_STATE_RECEIVE && sReceiveFrame.mLength > 0) || | |
(sState == OT_RADIO_STATE_TRANSMIT && sReceiveFrame.mLength > IEEE802154_ACK_LENGTH)) | |
{ | |
#if OPENTHREAD_ENABLE_DIAG | |
if (otPlatDiagModeGet()) | |
{ | |
otPlatDiagRadioReceiveDone(aInstance, &sReceiveFrame, sReceiveError); | |
} | |
else | |
#endif | |
{ | |
// signal MAC layer for each received frame if promiscous is enabled | |
// otherwise only signal MAC layer for non-ACK frame | |
if (((HWREG(RFCORE_XREG_FRMFILT0) & RFCORE_XREG_FRMFILT0_FRAME_FILTER_EN) == 0) || | |
(sReceiveFrame.mLength > IEEE802154_ACK_LENGTH)) | |
{ | |
otLogDebgPlat(sInstance, "%s Received %d bytes", get_time(), sReceiveFrame.mLength); | |
otPlatRadioReceiveDone(aInstance, &sReceiveFrame, sReceiveError); | |
} | |
} | |
} | |
if (sState == OT_RADIO_STATE_TRANSMIT) | |
{ | |
if (sTransmitError != OT_ERROR_NONE || (sTransmitFrame.mPsdu[0] & IEEE802154_ACK_REQUEST) == 0) | |
{ | |
if (sTransmitError != OT_ERROR_NONE) | |
{ | |
otLogDebgPlat(sInstance, "%s Transmit failed ErrorCode=%s", get_time(), | |
otThreadErrorToString(sTransmitError)); | |
} | |
otLogDebgPlat(sInstance, "%s State=OT_RADIO_STATE_RECEIVE [%d]", get_time(), __LINE__); | |
sState = OT_RADIO_STATE_RECEIVE; | |
#if OPENTHREAD_ENABLE_DIAG | |
if (otPlatDiagModeGet()) | |
{ | |
otPlatDiagRadioTransmitDone(aInstance, &sTransmitFrame, sTransmitError); | |
} | |
else | |
#endif | |
{ | |
otPlatRadioTxDone(aInstance, &sTransmitFrame, NULL, sTransmitError); | |
} | |
} | |
else if (sReceiveFrame.mLength == IEEE802154_ACK_LENGTH && | |
(sReceiveFrame.mPsdu[0] & IEEE802154_FRAME_TYPE_MASK) == IEEE802154_FRAME_TYPE_ACK && | |
(sReceiveFrame.mPsdu[IEEE802154_DSN_OFFSET] == sTransmitFrame.mPsdu[IEEE802154_DSN_OFFSET])) | |
{ | |
otLogDebgPlat(sInstance, "%s State=OT_RADIO_STATE_RECEIVE [%d]", get_time(), __LINE__); | |
sState = OT_RADIO_STATE_RECEIVE; | |
otPlatRadioTxDone(aInstance, &sTransmitFrame, &sReceiveFrame, sTransmitError); | |
} | |
} | |
sReceiveFrame.mLength = 0; | |
} | |
void RFCoreRxTxIntHandler(void) | |
{ | |
HWREG(RFCORE_SFR_RFIRQF0) = 0; | |
} | |
void RFCoreErrIntHandler(void) | |
{ | |
HWREG(RFCORE_SFR_RFERRF) = 0; | |
} | |
uint32_t getSrcMatchEntriesEnableStatus(bool aShort) | |
{ | |
uint32_t status = 0; | |
uint32_t *addr = aShort ? (uint32_t *) RFCORE_XREG_SRCSHORTEN0 : (uint32_t *) RFCORE_XREG_SRCEXTEN0; | |
for (uint8_t i = 0; i < RFCORE_XREG_SRCMATCH_ENABLE_STATUS_SIZE; i++) | |
{ | |
status |= HWREG(addr++) << (i * 8); | |
} | |
return status; | |
} | |
int8_t findSrcMatchShortEntry(const uint16_t aShortAddress) | |
{ | |
int8_t entry = -1; | |
uint16_t shortAddr; | |
uint32_t bitMask; | |
uint32_t *addr = NULL; | |
uint32_t status = getSrcMatchEntriesEnableStatus(true); | |
for (uint8_t i = 0; i < RFCORE_XREG_SRCMATCH_SHORT_ENTRIES; i++) | |
{ | |
bitMask = 0x00000001 << i; | |
if ((status & bitMask) == 0) | |
{ | |
continue; | |
} | |
addr = (uint32_t *)RFCORE_FFSM_SRCADDRESS_TABLE + (i * RFCORE_XREG_SRCMATCH_SHORT_ENTRY_OFFSET); | |
shortAddr = HWREG(addr + 2); | |
shortAddr |= HWREG(addr + 3) << 8; | |
if ((shortAddr == aShortAddress)) | |
{ | |
entry = i; | |
break; | |
} | |
} | |
return entry; | |
} | |
int8_t findSrcMatchExtEntry(const otExtAddress *aExtAddress) | |
{ | |
int8_t entry = -1; | |
uint32_t bitMask; | |
uint32_t *addr = NULL; | |
uint32_t status = getSrcMatchEntriesEnableStatus(false); | |
for (uint8_t i = 0; i < RFCORE_XREG_SRCMATCH_EXT_ENTRIES; i++) | |
{ | |
uint8_t j = 0; | |
bitMask = 0x00000001 << 2 * i; | |
if ((status & bitMask) == 0) | |
{ | |
continue; | |
} | |
addr = (uint32_t *)RFCORE_FFSM_SRCADDRESS_TABLE + (i * RFCORE_XREG_SRCMATCH_EXT_ENTRY_OFFSET); | |
for (j = 0; j < sizeof(otExtAddress); j++) | |
{ | |
if (HWREG(addr + j) != aExtAddress->m8[j]) | |
{ | |
break; | |
} | |
} | |
if (j == sizeof(otExtAddress)) | |
{ | |
entry = i; | |
break; | |
} | |
} | |
return entry; | |
} | |
void setSrcMatchEntryEnableStatus(bool aShort, uint8_t aEntry, bool aEnable) | |
{ | |
uint8_t entry = aShort ? aEntry : (2 * aEntry); | |
uint8_t index = entry / 8; | |
uint32_t *addrEn = aShort ? (uint32_t *)RFCORE_XREG_SRCSHORTEN0 : (uint32_t *)RFCORE_XREG_SRCEXTEN0; | |
uint32_t *addrAutoPendEn = aShort ? (uint32_t *)RFCORE_FFSM_SRCSHORTPENDEN0 : (uint32_t *)RFCORE_FFSM_SRCEXTPENDEN0; | |
uint32_t bitMask = 0x00000001; | |
if (aEnable) | |
{ | |
HWREG(addrEn + index) |= (bitMask) << (entry % 8); | |
HWREG(addrAutoPendEn + index) |= (bitMask) << (entry % 8); | |
} | |
else | |
{ | |
HWREG(addrEn + index) &= ~((bitMask) << (entry % 8)); | |
HWREG(addrAutoPendEn + index) &= ~((bitMask) << (entry % 8)); | |
} | |
} | |
int8_t findSrcMatchAvailEntry(bool aShort) | |
{ | |
int8_t entry = -1; | |
uint32_t bitMask; | |
uint32_t shortEnableStatus = getSrcMatchEntriesEnableStatus(true); | |
uint32_t extEnableStatus = getSrcMatchEntriesEnableStatus(false); | |
otLogDebgPlat(sInstance, "%s Short enable status: 0x%x", get_time(), shortEnableStatus); | |
otLogDebgPlat(sInstance, "%s Ext enable status: 0x%x", get_time(), extEnableStatus); | |
if (aShort) | |
{ | |
bitMask = 0x00000001; | |
for (uint8_t i = 0; i < RFCORE_XREG_SRCMATCH_SHORT_ENTRIES; i++) | |
{ | |
if ((extEnableStatus & bitMask) == 0) | |
{ | |
if ((shortEnableStatus & bitMask) == 0) | |
{ | |
entry = i; | |
break; | |
} | |
} | |
if (i % 2 == 1) | |
{ | |
extEnableStatus = extEnableStatus >> 2; | |
} | |
shortEnableStatus = shortEnableStatus >> 1; | |
} | |
} | |
else | |
{ | |
bitMask = 0x00000003; | |
for (uint8_t i = 0; i < RFCORE_XREG_SRCMATCH_EXT_ENTRIES; i++) | |
{ | |
if (((extEnableStatus | shortEnableStatus) & bitMask) == 0) | |
{ | |
entry = i; | |
break; | |
} | |
extEnableStatus = extEnableStatus >> 2; | |
shortEnableStatus = shortEnableStatus >> 2; | |
} | |
} | |
return entry; | |
} | |
void otPlatRadioEnableSrcMatch(otInstance *aInstance, bool aEnable) | |
{ | |
(void)aInstance; | |
otLogInfoPlat(sInstance, "%s EnableSrcMatch=%d", get_time(), aEnable ? 1 : 0); | |
if (aEnable) | |
{ | |
// only set FramePending when ack for data poll if there are queued messages | |
// for entries in the source match table. | |
HWREG(RFCORE_XREG_FRMCTRL1) &= ~RFCORE_XREG_FRMCTRL1_PENDING_OR; | |
} | |
else | |
{ | |
// set FramePending for all ack. | |
HWREG(RFCORE_XREG_FRMCTRL1) |= RFCORE_XREG_FRMCTRL1_PENDING_OR; | |
} | |
} | |
otError otPlatRadioAddSrcMatchShortEntry(otInstance *aInstance, const uint16_t aShortAddress) | |
{ | |
otError error = OT_ERROR_NONE; | |
int8_t entry = findSrcMatchAvailEntry(true); | |
uint32_t *addr = (uint32_t *)RFCORE_FFSM_SRCADDRESS_TABLE; | |
(void)aInstance; | |
otLogDebgPlat(sInstance, "%s Add ShortAddr entry: %d", get_time(), entry); | |
otEXPECT_ACTION(entry >= 0, error = OT_ERROR_NO_BUFS); | |
addr += (entry * RFCORE_XREG_SRCMATCH_SHORT_ENTRY_OFFSET); | |
HWREG(addr++) = HWREG(RFCORE_FFSM_PAN_ID0); | |
HWREG(addr++) = HWREG(RFCORE_FFSM_PAN_ID1); | |
HWREG(addr++) = aShortAddress & 0xFF; | |
HWREG(addr++) = aShortAddress >> 8; | |
setSrcMatchEntryEnableStatus(true, (uint8_t)(entry), true); | |
exit: | |
return error; | |
} | |
otError otPlatRadioAddSrcMatchExtEntry(otInstance *aInstance, const otExtAddress *aExtAddress) | |
{ | |
otError error = OT_ERROR_NONE; | |
int8_t entry = findSrcMatchAvailEntry(false); | |
uint32_t *addr = (uint32_t *)RFCORE_FFSM_SRCADDRESS_TABLE; | |
(void)aInstance; | |
otLogDebgPlat(sInstance, "%s Add ExtAddr entry: %d", get_time(), entry); | |
otEXPECT_ACTION(entry >= 0, error = OT_ERROR_NO_BUFS); | |
addr += (entry * RFCORE_XREG_SRCMATCH_EXT_ENTRY_OFFSET); | |
for (uint8_t i = 0; i < sizeof(otExtAddress); i++) | |
{ | |
HWREG(addr++) = aExtAddress->m8[i]; | |
} | |
setSrcMatchEntryEnableStatus(false, (uint8_t)(entry), true); | |
exit: | |
return error; | |
} | |
otError otPlatRadioClearSrcMatchShortEntry(otInstance *aInstance, const uint16_t aShortAddress) | |
{ | |
otError error = OT_ERROR_NONE; | |
int8_t entry = findSrcMatchShortEntry(aShortAddress); | |
(void)aInstance; | |
otLogDebgPlat(sInstance, "%s Clear ShortAddr entry: %d", get_time(), entry); | |
otEXPECT_ACTION(entry >= 0, error = OT_ERROR_NO_ADDRESS); | |
setSrcMatchEntryEnableStatus(true, (uint8_t)(entry), false); | |
exit: | |
return error; | |
} | |
otError otPlatRadioClearSrcMatchExtEntry(otInstance *aInstance, const otExtAddress *aExtAddress) | |
{ | |
otError error = OT_ERROR_NONE; | |
int8_t entry = findSrcMatchExtEntry(aExtAddress); | |
(void)aInstance; | |
otLogDebgPlat(sInstance, "%s Clear ExtAddr entry: %d", get_time(), entry); | |
otEXPECT_ACTION(entry >= 0, error = OT_ERROR_NO_ADDRESS); | |
setSrcMatchEntryEnableStatus(false, (uint8_t)(entry), false); | |
exit: | |
return error; | |
} | |
void otPlatRadioClearSrcMatchShortEntries(otInstance *aInstance) | |
{ | |
uint32_t *addrEn = (uint32_t *)RFCORE_XREG_SRCSHORTEN0; | |
uint32_t *addrAutoPendEn = (uint32_t *)RFCORE_FFSM_SRCSHORTPENDEN0; | |
(void)aInstance; | |
otLogDebgPlat(sInstance, "%s Clear ShortAddr entries", get_time(), NULL); | |
for (uint8_t i = 0; i < RFCORE_XREG_SRCMATCH_ENABLE_STATUS_SIZE; i++) | |
{ | |
HWREG(addrEn++) = 0; | |
HWREG(addrAutoPendEn++) = 0; | |
} | |
} | |
void otPlatRadioClearSrcMatchExtEntries(otInstance *aInstance) | |
{ | |
uint32_t *addrEn = (uint32_t *)RFCORE_XREG_SRCEXTEN0; | |
uint32_t *addrAutoPendEn = (uint32_t *)RFCORE_FFSM_SRCEXTPENDEN0; | |
(void)aInstance; | |
otLogDebgPlat(sInstance, "%s Clear ExtAddr entries", get_time(), NULL); | |
for (uint8_t i = 0; i < RFCORE_XREG_SRCMATCH_ENABLE_STATUS_SIZE; i++) | |
{ | |
HWREG(addrEn++) = 0; | |
HWREG(addrAutoPendEn++) = 0; | |
} | |
} | |
otError otPlatRadioEnergyScan(otInstance *aInstance, uint8_t aScanChannel, uint16_t aScanDuration) | |
{ | |
(void)aInstance; | |
(void)aScanChannel; | |
(void)aScanDuration; | |
return OT_ERROR_NOT_IMPLEMENTED; | |
} | |
otError otPlatRadioGetTransmitPower(otInstance *aInstance, int8_t *aPower) | |
{ | |
otError error = OT_ERROR_NONE; | |
(void)aInstance; | |
otEXPECT_ACTION(aPower != NULL, error = OT_ERROR_INVALID_ARGS); | |
*aPower = sTxPower; | |
exit: | |
return error; | |
} | |
otError otPlatRadioSetTransmitPower(otInstance *aInstance, int8_t aPower) | |
{ | |
(void)aInstance; | |
setTxPower(aPower); | |
return OT_ERROR_NONE; | |
} | |
int8_t otPlatRadioGetReceiveSensitivity(otInstance *aInstance) | |
{ | |
(void)aInstance; | |
return CC2538_RECEIVE_SENSITIVITY; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (c) 2016, 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 <openthread/openthread.h> | |
#include <openthread/platform/alarm-milli.h> | |
#include <openthread/platform/diag.h> | |
#include <openthread/platform/platform.h> | |
#include <openthread/platform/radio.h> | |
#include "platform-cc2538.h" | |
#include "common/logging.hpp" | |
#include "utils/code_utils.h" | |
#define RFCORE_RXTX_INT (141) | |
#define RFCORE_XREG_RFIRQM0 0x4008868C // RF interrupt masks | |
#define RFCORE_XREG_RFIRQM1 0x40088690 // RF interrupt masks | |
#define RFCORE_XREG_RFERRM 0x40088694 // RF error interrupt mask | |
#define RFCORE_SFR_RFIRQF0_RXMASKZERO 0x00000080 // RXENABLE is now completely clear | |
#define RFCORE_SFR_RFIRQF0_RXPKTDONE 0x00000040 // A complete frame has been received | |
#define RFCORE_SFR_RFIRQF0_FRAME_ACCEPTED 0x00000020 // Frame has passed frame filtering | |
#define RFCORE_SFR_RFIRQF0_SRC_MATCH_FOUND 0x00000010 // Source match is found | |
#define RFCORE_SFR_RFIRQF0_SRC_MATCH_DONE 0x00000008 // Source matching is complete | |
#define RFCORE_SFR_RFIRQF0_FIFOP 0x00000004 // The number of bytes in the RX fifo is above threshold | |
#define RFCORE_SFR_RFIRQF0_SFD 0x00000002 // SFD has been received or transmitted | |
#define RFCORE_SFR_RFIRQF0_ACT_UNUSED 0x00000001 // Reserved | |
#define RFCORE_XREG_RFIRQM0_RXMASKZERO 0x00000080 | |
#define RFCORE_XREG_RFIRQM0_RXPKTDONE 0x00000040 | |
#define RFCORE_XREG_RFIRQM0_FRAME_ACCEPTED 0x00000020 | |
#define RFCORE_XREG_RFIRQM0_SRC_MATCH_FOUND 0x00000010 | |
#define RFCORE_XREG_RFIRQM0_SRC_MATCH_DONE 0x00000008 | |
#define RFCORE_XREG_RFIRQM0_FIFOP 0x00000004 | |
#define RFCORE_XREG_RFIRQM0_SFD 0x00000002 | |
#define RFCORE_XREG_RFIRQM0_ACT_UNUSED 0x00000001 | |
#define RFCORE_SFR_RFIRQF1_CSP_WAIT 0x00000020 | |
#define RFCORE_SFR_RFIRQF1_CSP_STOP 0x00000010 | |
#define RFCORE_SFR_RFIRQF1_CSP_MANINT 0x00000008 | |
#define RFCORE_SFR_RFIRQF1_RF_IDLE 0x00000004 | |
#define RFCORE_SFR_RFIRQF1_TXDONE 0x00000002 | |
#define RFCORE_SFR_RFIRQF1_TXACKDONE 0x00000001 | |
#define RFCORE_XREG_RFIRQM1_CSP_WAIT 0x00000020 | |
#define RFCORE_XREG_RFIRQM1_CSP_STOP 0x00000010 | |
#define RFCORE_XREG_RFIRQM1_CSP_MANINT 0x00000008 | |
#define RFCORE_XREG_RFIRQM1_RF_IDLE 0x00000004 | |
#define RFCORE_XREG_RFIRQM1_TXDONE 0x00000002 | |
#define RFCORE_XREG_RFIRQM1_TXACKDONE 0x00000001 | |
#define RFCORE_XREG_RFERRM_STROBE_ERR 0x00000040 | |
#define RFCORE_XREG_RFERRM_TXUNDERF 0x00000020 | |
#define RFCORE_XREG_RFERRM_TXOVERF 0x00000010 | |
#define RFCORE_XREG_RFERRM_RXUNDERF 0x00000008 | |
#define RFCORE_XREG_RFERRM_RXOVERF 0x00000004 | |
#define RFCORE_XREG_RFERRM_RXABO 0x00000002 | |
#define RFCORE_XREG_RFERRM_NLOCK 0x00000001 | |
#include "interrupt.h" | |
#include "fs.h" | |
#include <sys/types.h> | |
#include <sys/stat.h> | |
#include <fcntl.h> | |
#include <sys/time.h> | |
#include <time.h> | |
enum | |
{ | |
IEEE802154_MIN_LENGTH = 5, | |
IEEE802154_MAX_LENGTH = 127, | |
IEEE802154_ACK_LENGTH = 5, | |
IEEE802154_FRAME_TYPE_MASK = 0x7, | |
IEEE802154_FRAME_TYPE_ACK = 0x2, | |
IEEE802154_FRAME_PENDING = 1 << 4, | |
IEEE802154_ACK_REQUEST = 1 << 5, | |
IEEE802154_DSN_OFFSET = 2, | |
}; | |
enum | |
{ | |
CC2538_RSSI_OFFSET = 73, | |
CC2538_CRC_BIT_MASK = 0x80, | |
CC2538_LQI_BIT_MASK = 0x7f, | |
}; | |
enum | |
{ | |
CC2538_RECEIVE_SENSITIVITY = -100, // dBm | |
}; | |
typedef struct TxPowerTable | |
{ | |
int8_t mTxPowerVal; | |
uint8_t mTxPowerReg; | |
} TxPowerTable; | |
// The transmit power table, the values are from application note 130 | |
static const TxPowerTable sTxPowerTable[] = | |
{ | |
{ 22, 0xFF }, /* 22.0dBm =~ 158.5mW */ | |
{ 21, 0xD5 }, /* 20.9dBm =~ 123.0mW */ | |
{ 20, 0xC5 }, /* 20.1dBm =~ 102.3mW */ | |
{ 19, 0xB0 }, /* 19.0dBm =~ 79.4mW */ | |
{ 18, 0xA1 }, /* 17.8dBm =~ 60.3mW */ | |
{ 16, 0x91 }, /* 16.4dBm =~ 43.7mW */ | |
{ 15, 0x88 }, /* 14.9dBm =~ 30.9mW */ | |
{ 13, 0x72 }, /* 13.0dBm =~ 20.0mW */ | |
{ 11, 0x62 }, /* 11.0dBm =~ 12.6mW */ | |
{ 10, 0x58 }, /* 9.5dBm =~ 8.9mW */ | |
{ 8, 0x42 }, /* 7.5dBm =~ 5.6mW */ | |
}; | |
static otRadioFrame sTransmitFrame; | |
static otRadioFrame sReceiveFrame; | |
static otError sTransmitError; | |
static otError sReceiveError; | |
static uint8_t sTransmitPsdu[IEEE802154_MAX_LENGTH]; | |
static uint8_t sReceivePsdu[IEEE802154_MAX_LENGTH]; | |
static uint8_t sChannel = 0; | |
static int8_t sTxPower = 0; | |
static otRadioState sState = OT_RADIO_STATE_DISABLED; | |
static bool sIsReceiverEnabled = false; | |
static const char* get_time() { | |
static char _time[32]; | |
struct timeval tv; | |
gettimeofday(&tv, NULL); | |
ctime_r(&tv.tv_sec, _time); | |
const char* end = rindex(_time, '\n'); | |
if (end) | |
end = 0; | |
return _time; | |
} | |
void enableReceiver(void) | |
{ | |
if (!sIsReceiverEnabled) | |
{ | |
otLogInfoPlat(sInstance, "%s Enabling receiver", get_time(), NULL); | |
// flush rxfifo | |
HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX; | |
HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX; | |
// enable receiver | |
HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_RXON; | |
sIsReceiverEnabled = true; | |
} | |
} | |
void disableReceiver(void) | |
{ | |
if (sIsReceiverEnabled) | |
{ | |
otLogInfoPlat(sInstance, "%s Disabling receiver", get_time(), NULL); | |
while (HWREG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_TX_ACTIVE); | |
// flush rxfifo | |
HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX; | |
HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX; | |
if (HWREG(RFCORE_XREG_RXENABLE) != 0) | |
{ | |
// disable receiver | |
HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_RFOFF; | |
} | |
sIsReceiverEnabled = false; | |
} | |
} | |
void setChannel(uint8_t aChannel) | |
{ | |
if (sChannel != aChannel) | |
{ | |
bool enabled = false; | |
if (sIsReceiverEnabled) | |
{ | |
otLogDebgPlat(sInstance, "%s Set channel(%d), need to disable receiver", get_time(), aChannel); | |
disableReceiver(); | |
enabled = true; | |
} | |
otLogInfoPlat(sInstance, "%s Channel=%d", get_time(), aChannel); | |
HWREG(RFCORE_XREG_FREQCTRL) = 11 + (aChannel - 11) * 5; | |
sChannel = aChannel; | |
if (enabled) | |
{ | |
otLogDebgPlat(sInstance, "%s Set channel(%d), re-enabling receiver", get_time(), aChannel); | |
enableReceiver(); | |
} | |
} | |
} | |
void setTxPower(int8_t aTxPower) | |
{ | |
uint8_t i = 0; | |
if (sTxPower != aTxPower) | |
{ | |
otLogInfoPlat(sInstance, "%s TxPower=%d", get_time(), aTxPower); | |
for (i = sizeof(sTxPowerTable) / sizeof(TxPowerTable) - 1; i > 0; i--) | |
{ | |
if (aTxPower < sTxPowerTable[i].mTxPowerVal) | |
{ | |
break; | |
} | |
} | |
HWREG(RFCORE_XREG_TXPOWER) = sTxPowerTable[i].mTxPowerReg; | |
sTxPower = aTxPower; | |
} | |
} | |
void otPlatRadioGetIeeeEui64(otInstance *aInstance, uint8_t *aIeeeEui64) | |
{ | |
uint8_t *eui64 = (uint8_t *)IEEE_EUI64; | |
(void)aInstance; | |
for (uint8_t i = 0; i < OT_EXT_ADDRESS_SIZE; i++) | |
{ | |
aIeeeEui64[i] = eui64[7 - i]; | |
} | |
} | |
void otPlatRadioSetPanId(otInstance *aInstance, uint16_t aPanid) | |
{ | |
(void)aInstance; | |
otLogInfoPlat(sInstance, "%s PANID=%X", get_time(), aPanid); | |
HWREG(RFCORE_FFSM_PAN_ID0) = aPanid & 0xFF; | |
HWREG(RFCORE_FFSM_PAN_ID1) = aPanid >> 8; | |
} | |
void otPlatRadioSetExtendedAddress(otInstance *aInstance, const otExtAddress *aAddress) | |
{ | |
(void)aInstance; | |
otLogInfoPlat(sInstance, "%s ExtAddr=%X%X%X%X%X%X%X%X", get_time(), | |
aAddress->m8[7], aAddress->m8[6], aAddress->m8[5], aAddress->m8[4], | |
aAddress->m8[3], aAddress->m8[2], aAddress->m8[1], aAddress->m8[0]); | |
for (int i = 0; i < 8; i++) | |
{ | |
((volatile uint32_t *)RFCORE_FFSM_EXT_ADDR0)[i] = aAddress->m8[i]; | |
} | |
} | |
void otPlatRadioSetShortAddress(otInstance *aInstance, uint16_t aAddress) | |
{ | |
(void)aInstance; | |
otLogInfoPlat(sInstance, "%s ShortAddr=%X", get_time(), aAddress); | |
HWREG(RFCORE_FFSM_SHORT_ADDR0) = aAddress & 0xFF; | |
HWREG(RFCORE_FFSM_SHORT_ADDR1) = aAddress >> 8; | |
} | |
void cc2538RadioInit(void) | |
{ | |
sTransmitFrame.mLength = 0; | |
sTransmitFrame.mPsdu = sTransmitPsdu; | |
sReceiveFrame.mLength = 0; | |
sReceiveFrame.mPsdu = sReceivePsdu; | |
// Enable interrupts for RX/TX, not sure which is being used. | |
nvic_enable(RFCORE_RXTX_INT); | |
HWREG(RFCORE_XREG_RFIRQM0) |= RFCORE_XREG_RFIRQM0_RXPKTDONE; | |
// enable clock | |
HWREG(SYS_CTRL_RCGCRFC) = SYS_CTRL_RCGCRFC_RFC0; | |
HWREG(SYS_CTRL_SCGCRFC) = SYS_CTRL_SCGCRFC_RFC0; | |
HWREG(SYS_CTRL_DCGCRFC) = SYS_CTRL_DCGCRFC_RFC0; | |
// Table 23-7. | |
HWREG(RFCORE_XREG_AGCCTRL1) = 0x15; | |
HWREG(RFCORE_XREG_TXFILTCFG) = 0x09; | |
HWREG(ANA_REGS_BASE + ANA_REGS_O_IVCTRL) = 0x0b; | |
HWREG(RFCORE_XREG_CCACTRL0) = 0xf8; | |
HWREG(RFCORE_XREG_FIFOPCTRL) = IEEE802154_MAX_LENGTH; | |
HWREG(RFCORE_XREG_FRMCTRL0) = RFCORE_XREG_FRMCTRL0_AUTOCRC | RFCORE_XREG_FRMCTRL0_AUTOACK; | |
// default: SRCMATCH.SRC_MATCH_EN(1), SRCMATCH.AUTOPEND(1), | |
// SRCMATCH.PEND_DATAREQ_ONLY(1), RFCORE_XREG_FRMCTRL1_PENDING_OR(0) | |
HWREG(RFCORE_XREG_TXPOWER) = sTxPowerTable[0].mTxPowerReg; | |
sTxPower = sTxPowerTable[0].mTxPowerVal; | |
otLogInfoPlat(sInstance, "%s Initialized", get_time()); | |
} | |
bool otPlatRadioIsEnabled(otInstance *aInstance) | |
{ | |
(void)aInstance; | |
return (sState != OT_RADIO_STATE_DISABLED) ? true : false; | |
} | |
otError otPlatRadioEnable(otInstance *aInstance) | |
{ | |
if (!otPlatRadioIsEnabled(aInstance)) | |
{ | |
otLogDebgPlat(sInstance, "%s State=OT_RADIO_STATE_SLEEP [%d]", get_time(), __LINE__); | |
sState = OT_RADIO_STATE_SLEEP; | |
} | |
return OT_ERROR_NONE; | |
} | |
otError otPlatRadioDisable(otInstance *aInstance) | |
{ | |
if (otPlatRadioIsEnabled(aInstance)) | |
{ | |
otLogDebgPlat(sInstance, "%s State=OT_RADIO_STATE_DISABLED [%d]", get_time(), __LINE__); | |
sState = OT_RADIO_STATE_DISABLED; | |
} | |
return OT_ERROR_NONE; | |
} | |
otError otPlatRadioSleep(otInstance *aInstance) | |
{ | |
otError error = OT_ERROR_INVALID_STATE; | |
(void)aInstance; | |
if (sState == OT_RADIO_STATE_SLEEP || sState == OT_RADIO_STATE_RECEIVE) | |
{ | |
otLogDebgPlat(sInstance, "%s State=OT_RADIO_STATE_SLEEP [%d]", get_time(), __LINE__); | |
error = OT_ERROR_NONE; | |
sState = OT_RADIO_STATE_SLEEP; | |
disableReceiver(); | |
} | |
return error; | |
} | |
otError otPlatRadioReceive(otInstance *aInstance, uint8_t aChannel) | |
{ | |
otError error = OT_ERROR_INVALID_STATE; | |
(void)aInstance; | |
if (sState != OT_RADIO_STATE_DISABLED) | |
{ | |
otLogDebgPlat(sInstance, "%s State=OT_RADIO_STATE_RECEIVE [%d]", get_time(), __LINE__); | |
error = OT_ERROR_NONE; | |
sState = OT_RADIO_STATE_RECEIVE; | |
setChannel(aChannel); | |
sReceiveFrame.mChannel = aChannel; | |
enableReceiver(); | |
} | |
return error; | |
} | |
static void setupTransmit(otRadioFrame *aFrame) | |
{ | |
int i; | |
// wait for current TX operation to complete, if any. | |
while (HWREG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_TX_ACTIVE); | |
// flush txfifo | |
HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHTX; | |
HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHTX; | |
// frame length | |
HWREG(RFCORE_SFR_RFDATA) = aFrame->mLength; | |
// frame data | |
for (i = 0; i < aFrame->mLength; i++) | |
{ | |
HWREG(RFCORE_SFR_RFDATA) = aFrame->mPsdu[i]; | |
} | |
setChannel(aFrame->mChannel); | |
} | |
otError otPlatRadioTransmit(otInstance *aInstance, otRadioFrame *aFrame) | |
{ | |
otError error = OT_ERROR_INVALID_STATE; | |
(void)aInstance; | |
if (sState == OT_RADIO_STATE_RECEIVE) | |
{ | |
int i; | |
error = OT_ERROR_NONE; | |
otLogDebgPlat(sInstance, "%s State=OT_RADIO_STATE_TRANSMIT [%d]", get_time(), __LINE__); | |
sState = OT_RADIO_STATE_TRANSMIT; | |
sTransmitError = OT_ERROR_NONE; | |
setupTransmit(aFrame); | |
// Set up a counter to inform us if we get stuck. | |
i = 1000000; | |
// Wait for radio to enter receive state. | |
while ((HWREG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_RX_ACTIVE) == 0) | |
{ | |
// Count down the cycles, and emit a message if we get to zero. | |
// Ideally, we should never get there! | |
if (i) | |
{ | |
i--; | |
} | |
else | |
{ | |
otLogCritPlat(sInstance, "Radio is stuck!!! FSMSTAT0=0x%08x FSMSTAT1=0x%08x RFERRF=0x%08x", get_time(), | |
HWREG(RFCORE_XREG_FSMSTAT0), HWREG(RFCORE_XREG_FSMSTAT1), HWREG(RFCORE_SFR_RFERRF)); | |
i = 1000000; | |
} | |
// Ensure we haven't overflowed the RX buffer in the mean time, as this | |
// will cause a deadlock here otherwise. Similarly, if we see an aborted | |
// RX, handle that here too to prevent deadlock. | |
if (HWREG(RFCORE_SFR_RFERRF) & | |
(RFCORE_SFR_RFERRF_RXOVERF | RFCORE_SFR_RFERRF_RXABO)) | |
{ | |
if (HWREG(RFCORE_SFR_RFERRF) & RFCORE_SFR_RFERRF_RXOVERF) | |
{ | |
otLogCritPlat(sInstance, "RX Buffer Overflow detected", get_time(), NULL); | |
} | |
if (HWREG(RFCORE_SFR_RFERRF) & RFCORE_SFR_RFERRF_RXABO) | |
{ | |
otLogCritPlat(sInstance, "Aborted RX detected", get_time(), NULL); | |
} | |
// Flush the RX buffer | |
HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX; | |
HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX; | |
} | |
// Check for idle state. After flushing the RX buffer, we may wind up here. | |
if (!(HWREG(RFCORE_XREG_FSMSTAT1) | |
& (RFCORE_XREG_FSMSTAT1_TX_ACTIVE | RFCORE_XREG_FSMSTAT1_RX_ACTIVE))) | |
{ | |
otLogCritPlat(sInstance, "Idle state detected", get_time(), NULL); | |
// In this case, the state of our driver mis-matches our state. So force | |
// matters by clearing our channel variable and calling setChannel. This | |
// should bring our radio into the RX state, which should allow us to go | |
// into TX. | |
sChannel = 0; | |
setupTransmit(aFrame); | |
} | |
} | |
// wait for valid rssi | |
while ((HWREG(RFCORE_XREG_RSSISTAT) & RFCORE_XREG_RSSISTAT_RSSI_VALID) == 0); | |
otEXPECT_ACTION(((HWREG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_CCA) && | |
!((HWREG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_SFD))), | |
sTransmitError = OT_ERROR_CHANNEL_ACCESS_FAILURE); | |
// begin transmit | |
HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_TXON; | |
otPlatRadioTxStarted(aInstance, aFrame); | |
while (HWREG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_TX_ACTIVE); | |
otLogDebgPlat(sInstance, "%s Transmitted %d bytes", get_time(), aFrame->mLength); | |
} | |
else | |
{ | |
otLogDebgPlat(sInstance, "%s Not in RECEIVE state (state %d)", get_time(), sState); | |
} | |
exit: | |
return error; | |
} | |
otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *aInstance) | |
{ | |
(void)aInstance; | |
return &sTransmitFrame; | |
} | |
int8_t otPlatRadioGetRssi(otInstance *aInstance) | |
{ | |
(void)aInstance; | |
return 0; | |
} | |
otRadioCaps otPlatRadioGetCaps(otInstance *aInstance) | |
{ | |
(void)aInstance; | |
return OT_RADIO_CAPS_NONE; | |
} | |
bool otPlatRadioGetPromiscuous(otInstance *aInstance) | |
{ | |
(void)aInstance; | |
return (HWREG(RFCORE_XREG_FRMFILT0) & RFCORE_XREG_FRMFILT0_FRAME_FILTER_EN) == 0; | |
} | |
void otPlatRadioSetPromiscuous(otInstance *aInstance, bool aEnable) | |
{ | |
(void)aInstance; | |
otLogInfoPlat(sInstance, "%s PromiscuousMode=%d", get_time(), aEnable ? 1 : 0); | |
if (aEnable) | |
{ | |
HWREG(RFCORE_XREG_FRMFILT0) &= ~RFCORE_XREG_FRMFILT0_FRAME_FILTER_EN; | |
} | |
else | |
{ | |
HWREG(RFCORE_XREG_FRMFILT0) |= RFCORE_XREG_FRMFILT0_FRAME_FILTER_EN; | |
} | |
} | |
void readFrame(void) | |
{ | |
uint8_t length; | |
uint8_t crcCorr; | |
int i; | |
otEXPECT(sState == OT_RADIO_STATE_RECEIVE || sState == OT_RADIO_STATE_TRANSMIT); | |
otEXPECT((HWREG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_FIFOP) != 0); | |
otEXPECT(sReceiveFrame.mLength == 0); | |
// read length | |
length = HWREG(RFCORE_SFR_RFDATA); | |
otEXPECT(IEEE802154_MIN_LENGTH <= length && length <= IEEE802154_MAX_LENGTH); | |
#if OPENTHREAD_ENABLE_RAW_LINK_API | |
// Timestamp | |
sReceiveFrame.mMsec = otPlatAlarmMilliGetNow(); | |
sReceiveFrame.mUsec = 0; // Don't support microsecond timer for now. | |
#endif | |
// read psdu | |
for (i = 0; i < length - 2; i++) | |
{ | |
sReceiveFrame.mPsdu[i] = HWREG(RFCORE_SFR_RFDATA); | |
} | |
sReceiveFrame.mRssi = (int8_t)HWREG(RFCORE_SFR_RFDATA) - CC2538_RSSI_OFFSET; | |
crcCorr = HWREG(RFCORE_SFR_RFDATA); | |
if (crcCorr & CC2538_CRC_BIT_MASK) | |
{ | |
sReceiveFrame.mLength = length; | |
sReceiveFrame.mLqi = crcCorr & CC2538_LQI_BIT_MASK; | |
} | |
else | |
{ | |
// resets rxfifo | |
HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX; | |
HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX; | |
otLogDebgPlat(sInstance, "%s Dropping %d received bytes (Invalid CRC)", get_time(), length); | |
} | |
// check for rxfifo overflow | |
if ((HWREG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_FIFOP) != 0 && | |
(HWREG(RFCORE_XREG_FSMSTAT1) & RFCORE_XREG_FSMSTAT1_FIFO) == 0) | |
{ | |
HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX; | |
HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX; | |
} | |
exit: | |
return; | |
} | |
void cc2538RadioProcess(otInstance *aInstance) | |
{ | |
HWREG(RFCORE_XREG_RFIRQM0) &= ~RFCORE_XREG_RFIRQM0_RXPKTDONE; | |
readFrame(); | |
HWREG(RFCORE_XREG_RFIRQM0) |= RFCORE_XREG_RFIRQM0_RXPKTDONE; | |
if ((sState == OT_RADIO_STATE_RECEIVE && sReceiveFrame.mLength > 0) || | |
(sState == OT_RADIO_STATE_TRANSMIT && sReceiveFrame.mLength > IEEE802154_ACK_LENGTH)) | |
{ | |
#if OPENTHREAD_ENABLE_DIAG | |
if (otPlatDiagModeGet()) | |
{ | |
otPlatDiagRadioReceiveDone(aInstance, &sReceiveFrame, sReceiveError); | |
} | |
else | |
#endif | |
{ | |
// signal MAC layer for each received frame if promiscous is enabled | |
// otherwise only signal MAC layer for non-ACK frame | |
if (((HWREG(RFCORE_XREG_FRMFILT0) & RFCORE_XREG_FRMFILT0_FRAME_FILTER_EN) == 0) || | |
(sReceiveFrame.mLength > IEEE802154_ACK_LENGTH)) | |
{ | |
otLogDebgPlat(sInstance, "%s Received %d bytes", get_time(), sReceiveFrame.mLength); | |
otPlatRadioReceiveDone(aInstance, &sReceiveFrame, sReceiveError); | |
} | |
} | |
} | |
if (sState == OT_RADIO_STATE_TRANSMIT) | |
{ | |
if (sTransmitError != OT_ERROR_NONE || (sTransmitFrame.mPsdu[0] & IEEE802154_ACK_REQUEST) == 0) | |
{ | |
if (sTransmitError != OT_ERROR_NONE) | |
{ | |
otLogDebgPlat(sInstance, "%s Transmit failed ErrorCode=%s", get_time(), | |
otThreadErrorToString(sTransmitError)); | |
} | |
otLogDebgPlat(sInstance, "%s State=OT_RADIO_STATE_RECEIVE [%d]", get_time(), __LINE__); | |
sState = OT_RADIO_STATE_RECEIVE; | |
#if OPENTHREAD_ENABLE_DIAG | |
if (otPlatDiagModeGet()) | |
{ | |
otPlatDiagRadioTransmitDone(aInstance, &sTransmitFrame, sTransmitError); | |
} | |
else | |
#endif | |
{ | |
otPlatRadioTxDone(aInstance, &sTransmitFrame, NULL, sTransmitError); | |
} | |
} | |
else if (sReceiveFrame.mLength == IEEE802154_ACK_LENGTH && | |
(sReceiveFrame.mPsdu[0] & IEEE802154_FRAME_TYPE_MASK) == IEEE802154_FRAME_TYPE_ACK && | |
(sReceiveFrame.mPsdu[IEEE802154_DSN_OFFSET] == sTransmitFrame.mPsdu[IEEE802154_DSN_OFFSET])) | |
{ | |
otLogDebgPlat(sInstance, "%s State=OT_RADIO_STATE_RECEIVE [%d]", get_time(), __LINE__); | |
sState = OT_RADIO_STATE_RECEIVE; | |
otPlatRadioTxDone(aInstance, &sTransmitFrame, &sReceiveFrame, sTransmitError); | |
} | |
} | |
sReceiveFrame.mLength = 0; | |
} | |
void RFCoreRxTxIntHandler(void) | |
{ | |
if (HWREG(RFCORE_SFR_RFIRQF0) & RFCORE_SFR_RFIRQF0_RXPKTDONE) | |
{ | |
readFrame(); | |
} | |
HWREG(RFCORE_SFR_RFIRQF0) = 0; | |
} | |
void RFCoreErrIntHandler(void) | |
{ | |
HWREG(RFCORE_SFR_RFERRF) = 0; | |
} | |
uint32_t getSrcMatchEntriesEnableStatus(bool aShort) | |
{ | |
uint32_t status = 0; | |
uint32_t *addr = aShort ? (uint32_t *) RFCORE_XREG_SRCSHORTEN0 : (uint32_t *) RFCORE_XREG_SRCEXTEN0; | |
for (uint8_t i = 0; i < RFCORE_XREG_SRCMATCH_ENABLE_STATUS_SIZE; i++) | |
{ | |
status |= HWREG(addr++) << (i * 8); | |
} | |
return status; | |
} | |
int8_t findSrcMatchShortEntry(const uint16_t aShortAddress) | |
{ | |
int8_t entry = -1; | |
uint16_t shortAddr; | |
uint32_t bitMask; | |
uint32_t *addr = NULL; | |
uint32_t status = getSrcMatchEntriesEnableStatus(true); | |
for (uint8_t i = 0; i < RFCORE_XREG_SRCMATCH_SHORT_ENTRIES; i++) | |
{ | |
bitMask = 0x00000001 << i; | |
if ((status & bitMask) == 0) | |
{ | |
continue; | |
} | |
addr = (uint32_t *)RFCORE_FFSM_SRCADDRESS_TABLE + (i * RFCORE_XREG_SRCMATCH_SHORT_ENTRY_OFFSET); | |
shortAddr = HWREG(addr + 2); | |
shortAddr |= HWREG(addr + 3) << 8; | |
if ((shortAddr == aShortAddress)) | |
{ | |
entry = i; | |
break; | |
} | |
} | |
return entry; | |
} | |
int8_t findSrcMatchExtEntry(const otExtAddress *aExtAddress) | |
{ | |
int8_t entry = -1; | |
uint32_t bitMask; | |
uint32_t *addr = NULL; | |
uint32_t status = getSrcMatchEntriesEnableStatus(false); | |
for (uint8_t i = 0; i < RFCORE_XREG_SRCMATCH_EXT_ENTRIES; i++) | |
{ | |
uint8_t j = 0; | |
bitMask = 0x00000001 << 2 * i; | |
if ((status & bitMask) == 0) | |
{ | |
continue; | |
} | |
addr = (uint32_t *)RFCORE_FFSM_SRCADDRESS_TABLE + (i * RFCORE_XREG_SRCMATCH_EXT_ENTRY_OFFSET); | |
for (j = 0; j < sizeof(otExtAddress); j++) | |
{ | |
if (HWREG(addr + j) != aExtAddress->m8[j]) | |
{ | |
break; | |
} | |
} | |
if (j == sizeof(otExtAddress)) | |
{ | |
entry = i; | |
break; | |
} | |
} | |
return entry; | |
} | |
void setSrcMatchEntryEnableStatus(bool aShort, uint8_t aEntry, bool aEnable) | |
{ | |
uint8_t entry = aShort ? aEntry : (2 * aEntry); | |
uint8_t index = entry / 8; | |
uint32_t *addrEn = aShort ? (uint32_t *)RFCORE_XREG_SRCSHORTEN0 : (uint32_t *)RFCORE_XREG_SRCEXTEN0; | |
uint32_t *addrAutoPendEn = aShort ? (uint32_t *)RFCORE_FFSM_SRCSHORTPENDEN0 : (uint32_t *)RFCORE_FFSM_SRCEXTPENDEN0; | |
uint32_t bitMask = 0x00000001; | |
if (aEnable) | |
{ | |
HWREG(addrEn + index) |= (bitMask) << (entry % 8); | |
HWREG(addrAutoPendEn + index) |= (bitMask) << (entry % 8); | |
} | |
else | |
{ | |
HWREG(addrEn + index) &= ~((bitMask) << (entry % 8)); | |
HWREG(addrAutoPendEn + index) &= ~((bitMask) << (entry % 8)); | |
} | |
} | |
int8_t findSrcMatchAvailEntry(bool aShort) | |
{ | |
int8_t entry = -1; | |
uint32_t bitMask; | |
uint32_t shortEnableStatus = getSrcMatchEntriesEnableStatus(true); | |
uint32_t extEnableStatus = getSrcMatchEntriesEnableStatus(false); | |
otLogDebgPlat(sInstance, "%s Short enable status: 0x%x", get_time(), shortEnableStatus); | |
otLogDebgPlat(sInstance, "%s Ext enable status: 0x%x", get_time(), extEnableStatus); | |
if (aShort) | |
{ | |
bitMask = 0x00000001; | |
for (uint8_t i = 0; i < RFCORE_XREG_SRCMATCH_SHORT_ENTRIES; i++) | |
{ | |
if ((extEnableStatus & bitMask) == 0) | |
{ | |
if ((shortEnableStatus & bitMask) == 0) | |
{ | |
entry = i; | |
break; | |
} | |
} | |
if (i % 2 == 1) | |
{ | |
extEnableStatus = extEnableStatus >> 2; | |
} | |
shortEnableStatus = shortEnableStatus >> 1; | |
} | |
} | |
else | |
{ | |
bitMask = 0x00000003; | |
for (uint8_t i = 0; i < RFCORE_XREG_SRCMATCH_EXT_ENTRIES; i++) | |
{ | |
if (((extEnableStatus | shortEnableStatus) & bitMask) == 0) | |
{ | |
entry = i; | |
break; | |
} | |
extEnableStatus = extEnableStatus >> 2; | |
shortEnableStatus = shortEnableStatus >> 2; | |
} | |
} | |
return entry; | |
} | |
void otPlatRadioEnableSrcMatch(otInstance *aInstance, bool aEnable) | |
{ | |
(void)aInstance; | |
otLogInfoPlat(sInstance, "%s EnableSrcMatch=%d", get_time(), aEnable ? 1 : 0); | |
if (aEnable) | |
{ | |
// only set FramePending when ack for data poll if there are queued messages | |
// for entries in the source match table. | |
HWREG(RFCORE_XREG_FRMCTRL1) &= ~RFCORE_XREG_FRMCTRL1_PENDING_OR; | |
} | |
else | |
{ | |
// set FramePending for all ack. | |
HWREG(RFCORE_XREG_FRMCTRL1) |= RFCORE_XREG_FRMCTRL1_PENDING_OR; | |
} | |
} | |
otError otPlatRadioAddSrcMatchShortEntry(otInstance *aInstance, const uint16_t aShortAddress) | |
{ | |
otError error = OT_ERROR_NONE; | |
int8_t entry = findSrcMatchAvailEntry(true); | |
uint32_t *addr = (uint32_t *)RFCORE_FFSM_SRCADDRESS_TABLE; | |
(void)aInstance; | |
otLogDebgPlat(sInstance, "%s Add ShortAddr entry: %d", get_time(), entry); | |
otEXPECT_ACTION(entry >= 0, error = OT_ERROR_NO_BUFS); | |
addr += (entry * RFCORE_XREG_SRCMATCH_SHORT_ENTRY_OFFSET); | |
HWREG(addr++) = HWREG(RFCORE_FFSM_PAN_ID0); | |
HWREG(addr++) = HWREG(RFCORE_FFSM_PAN_ID1); | |
HWREG(addr++) = aShortAddress & 0xFF; | |
HWREG(addr++) = aShortAddress >> 8; | |
setSrcMatchEntryEnableStatus(true, (uint8_t)(entry), true); | |
exit: | |
return error; | |
} | |
otError otPlatRadioAddSrcMatchExtEntry(otInstance *aInstance, const otExtAddress *aExtAddress) | |
{ | |
otError error = OT_ERROR_NONE; | |
int8_t entry = findSrcMatchAvailEntry(false); | |
uint32_t *addr = (uint32_t *)RFCORE_FFSM_SRCADDRESS_TABLE; | |
(void)aInstance; | |
otLogDebgPlat(sInstance, "%s Add ExtAddr entry: %d", get_time(), entry); | |
otEXPECT_ACTION(entry >= 0, error = OT_ERROR_NO_BUFS); | |
addr += (entry * RFCORE_XREG_SRCMATCH_EXT_ENTRY_OFFSET); | |
for (uint8_t i = 0; i < sizeof(otExtAddress); i++) | |
{ | |
HWREG(addr++) = aExtAddress->m8[i]; | |
} | |
setSrcMatchEntryEnableStatus(false, (uint8_t)(entry), true); | |
exit: | |
return error; | |
} | |
otError otPlatRadioClearSrcMatchShortEntry(otInstance *aInstance, const uint16_t aShortAddress) | |
{ | |
otError error = OT_ERROR_NONE; | |
int8_t entry = findSrcMatchShortEntry(aShortAddress); | |
(void)aInstance; | |
otLogDebgPlat(sInstance, "%s Clear ShortAddr entry: %d", get_time(), entry); | |
otEXPECT_ACTION(entry >= 0, error = OT_ERROR_NO_ADDRESS); | |
setSrcMatchEntryEnableStatus(true, (uint8_t)(entry), false); | |
exit: | |
return error; | |
} | |
otError otPlatRadioClearSrcMatchExtEntry(otInstance *aInstance, const otExtAddress *aExtAddress) | |
{ | |
otError error = OT_ERROR_NONE; | |
int8_t entry = findSrcMatchExtEntry(aExtAddress); | |
(void)aInstance; | |
otLogDebgPlat(sInstance, "%s Clear ExtAddr entry: %d", get_time(), entry); | |
otEXPECT_ACTION(entry >= 0, error = OT_ERROR_NO_ADDRESS); | |
setSrcMatchEntryEnableStatus(false, (uint8_t)(entry), false); | |
exit: | |
return error; | |
} | |
void otPlatRadioClearSrcMatchShortEntries(otInstance *aInstance) | |
{ | |
uint32_t *addrEn = (uint32_t *)RFCORE_XREG_SRCSHORTEN0; | |
uint32_t *addrAutoPendEn = (uint32_t *)RFCORE_FFSM_SRCSHORTPENDEN0; | |
(void)aInstance; | |
otLogDebgPlat(sInstance, "%s Clear ShortAddr entries", get_time(), NULL); | |
for (uint8_t i = 0; i < RFCORE_XREG_SRCMATCH_ENABLE_STATUS_SIZE; i++) | |
{ | |
HWREG(addrEn++) = 0; | |
HWREG(addrAutoPendEn++) = 0; | |
} | |
} | |
void otPlatRadioClearSrcMatchExtEntries(otInstance *aInstance) | |
{ | |
uint32_t *addrEn = (uint32_t *)RFCORE_XREG_SRCEXTEN0; | |
uint32_t *addrAutoPendEn = (uint32_t *)RFCORE_FFSM_SRCEXTPENDEN0; | |
(void)aInstance; | |
otLogDebgPlat(sInstance, "%s Clear ExtAddr entries", get_time(), NULL); | |
for (uint8_t i = 0; i < RFCORE_XREG_SRCMATCH_ENABLE_STATUS_SIZE; i++) | |
{ | |
HWREG(addrEn++) = 0; | |
HWREG(addrAutoPendEn++) = 0; | |
} | |
} | |
otError otPlatRadioEnergyScan(otInstance *aInstance, uint8_t aScanChannel, uint16_t aScanDuration) | |
{ | |
(void)aInstance; | |
(void)aScanChannel; | |
(void)aScanDuration; | |
return OT_ERROR_NOT_IMPLEMENTED; | |
} | |
otError otPlatRadioGetTransmitPower(otInstance *aInstance, int8_t *aPower) | |
{ | |
otError error = OT_ERROR_NONE; | |
(void)aInstance; | |
otEXPECT_ACTION(aPower != NULL, error = OT_ERROR_INVALID_ARGS); | |
*aPower = sTxPower; | |
exit: | |
return error; | |
} | |
otError otPlatRadioSetTransmitPower(otInstance *aInstance, int8_t aPower) | |
{ | |
(void)aInstance; | |
setTxPower(aPower); | |
return OT_ERROR_NONE; | |
} | |
int8_t otPlatRadioGetReceiveSensitivity(otInstance *aInstance) | |
{ | |
(void)aInstance; | |
return CC2538_RECEIVE_SENSITIVITY; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (c) 2016, 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 gcc-specific startup code for the cc2538. | |
*/ | |
#include <stdint.h> | |
#include <string.h> | |
#include "sysctrl.h" | |
#include "uart.h" | |
#include "gpio.h" | |
#include "pulsein.h" | |
#include "powerctl.h" | |
extern uint8_t _ldata; | |
extern uint8_t _data; | |
extern uint8_t _edata; | |
extern uint8_t _bss; | |
extern uint8_t _ebss; | |
extern uint8_t _init_array; | |
extern uint8_t _einit_array; | |
__extension__ typedef int __guard __attribute__((mode(__DI__))); | |
int __cxa_guard_acquire(__guard *g) { return !*(char *)(g); } | |
void __cxa_guard_release(__guard *g) { *(char *)g = 1; } | |
void __cxa_guard_abort(__guard *g) { (void)g; } | |
void __cxa_pure_virtual(void) { while (1); } | |
void HardFaultHandler(uint32_t* hardfault_args); | |
void IntDefaultHandler(void); | |
void ResetHandler(void); | |
extern void SysTick_Handler(void); | |
extern void UART0IntHandler(void); | |
extern void RFCoreRxTxIntHandler(void); | |
extern void RFCoreErrIntHandler(void); | |
extern void main(void); | |
static uint64_t stack[512] __attribute__((section(".stack"))); | |
__attribute__((section(".vectors"), used)) | |
void (*const vectors[])(void) = | |
{ | |
(void (*)(void))((unsigned long)stack + sizeof(stack)), // Initial Stack Pointer | |
ResetHandler, // 1 The reset handler | |
ResetHandler, // 2 The NMI handler | |
(void (*)(void))HardFaultHandler, // 3 The hard fault handler | |
(void (*)(void))HardFaultHandler, // 4 The MPU fault handler | |
(void (*)(void))HardFaultHandler, // 5 The bus fault handler | |
(void (*)(void))HardFaultHandler, // 6 The usage fault handler | |
0, // 7 Reserved | |
0, // 8 Reserved | |
0, // 9 Reserved | |
0, // 10 Reserved | |
IntDefaultHandler, // 11 SVCall handler | |
IntDefaultHandler, // 12 Debug monitor handler | |
0, // 13 Reserved | |
IntDefaultHandler, // 14 The PendSV handler | |
SysTick_Handler, // 15 The SysTick handler | |
gpio_a_int, // 16 GPIO Port A | |
gpio_b_int, // 17 GPIO Port B | |
gpio_c_int, // 18 GPIO Port C | |
gpio_d_int, // 19 GPIO Port D | |
0, // 20 none | |
cc2538_uart0_handler, // 21 UART0 Rx and Tx | |
IntDefaultHandler, // 22 UART1 Rx and Tx | |
IntDefaultHandler, // 23 SSI0 Rx and Tx | |
IntDefaultHandler, // 24 I2C Master and Slave | |
0, // 25 Reserved | |
0, // 26 Reserved | |
0, // 27 Reserved | |
0, // 28 Reserved | |
0, // 29 Reserved | |
IntDefaultHandler, // 30 ADC Sequence 0 | |
0, // 31 Reserved | |
0, // 32 Reserved | |
0, // 33 Reserved | |
IntDefaultHandler, // 34 Watchdog timer, timer 0 | |
IntDefaultHandler, // 35 Timer 0 subtimer A | |
IntDefaultHandler, // 36 Timer 0 subtimer B | |
powerctl_fault_timeout, // 37 Timer 1 subtimer A | |
IntDefaultHandler, // 38 Timer 1 subtimer B | |
IntDefaultHandler, // 39 Timer 2 subtimer A | |
IntDefaultHandler, // 40 Timer 2 subtimer B | |
IntDefaultHandler, // 41 Analog Comparator 0 | |
RFCoreRxTxIntHandler, // 42 RFCore Rx/Tx | |
RFCoreErrIntHandler, // 43 RFCore Error | |
IntDefaultHandler, // 44 IcePick | |
IntDefaultHandler, // 45 FLASH Control | |
IntDefaultHandler, // 46 AES | |
IntDefaultHandler, // 47 PKA | |
IntDefaultHandler, // 48 Sleep Timer | |
IntDefaultHandler, // 49 MacTimer | |
IntDefaultHandler, // 50 SSI1 Rx and Tx | |
pulsein_timeout, // 51 Timer 3 subtimer A | |
IntDefaultHandler, // 52 Timer 3 subtimer B | |
0, // 53 Reserved | |
0, // 54 Reserved | |
0, // 55 Reserved | |
0, // 56 Reserved | |
0, // 57 Reserved | |
0, // 58 Reserved | |
0, // 59 Reserved | |
IntDefaultHandler, // 60 USB 2538 | |
0, // 61 Reserved | |
IntDefaultHandler, // 62 uDMA | |
IntDefaultHandler, // 63 uDMA Error | |
0, // 64 Reserved | |
0, // 65 Reserved | |
0, // 66 Reserved | |
0, // 67 Reserved | |
0, // 68 Reserved | |
0, // 69 Reserved | |
0, // 70 Reserved | |
0, // 71 Reserved | |
0, // 72 Reserved | |
0, // 73 Reserved | |
0, // 74 Reserved | |
0, // 75 Reserved | |
0, // 76 Reserved | |
0, // 77 Reserved | |
0, // 78 Reserved | |
0, // 79 Reserved | |
0, // 80 Reserved | |
0, // 81 Reserved | |
0, // 82 Reserved | |
0, // 83 Reserved | |
0, // 84 Reserved | |
0, // 85 Reserved | |
0, // 86 Reserved | |
0, // 87 Reserved | |
0, // 88 Reserved | |
0, // 89 Reserved | |
0, // 90 Reserved | |
0, // 91 Reserved | |
0, // 92 Reserved | |
0, // 93 Reserved | |
0, // 94 Reserved | |
0, // 95 Reserved | |
0, // 96 Reserved | |
0, // 97 Reserved | |
0, // 98 Reserved | |
0, // 99 Reserved | |
0, // 100 Reserved | |
0, // 101 Reserved | |
0, // 102 Reserved | |
0, // 103 Reserved | |
0, // 104 Reserved | |
0, // 105 Reserved | |
0, // 106 Reserved | |
0, // 107 Reserved | |
0, // 108 Reserved | |
0, // 109 Reserved | |
0, // 110 Reserved | |
0, // 111 Reserved | |
0, // 112 Reserved | |
0, // 113 Reserved | |
0, // 114 Reserved | |
0, // 115 Reserved | |
0, // 116 Reserved | |
0, // 117 Reserved | |
0, // 118 Reserved | |
0, // 119 Reserved | |
0, // 120 Reserved | |
0, // 121 Reserved | |
0, // 122 Reserved | |
0, // 123 Reserved | |
0, // 124 Reserved | |
0, // 125 Reserved | |
0, // 126 Reserved | |
0, // 127 Reserved | |
0, // 128 Reserved | |
0, // 129 Reserved | |
0, // 130 Reserved | |
0, // 131 Reserved | |
0, // 132 Reserved | |
0, // 133 Reserved | |
0, // 134 Reserved | |
0, // 135 Reserved | |
0, // 136 Reserved | |
0, // 137 Reserved | |
0, // 138 Reserved | |
0, // 139 Reserved | |
0, // 140 Reserved | |
0, // 141 Reserved | |
0, // 142 Reserved | |
0, // 143 Reserved | |
0, // 144 Reserved | |
0, // 145 Reserved | |
0, // 146 Reserved | |
0, // 147 Reserved | |
0, // 148 Reserved | |
0, // 149 Reserved | |
0, // 150 Reserved | |
0, // 151 Reserved | |
0, // 152 Reserved | |
0, // 153 Reserved | |
0, // 154 Reserved | |
0, // 155 Reserved | |
0, // 156 USB | |
RFCoreRxTxIntHandler, // 157 RF Core Rx/Tx | |
RFCoreErrIntHandler, // 158 RF Core Error | |
0, // 159 AES | |
0, // 160 PKA | |
0, // 161 SM Timer | |
0, // 162 MAC Timer | |
0, // 163 Reserved | |
}; | |
void IntDefaultHandler(void) { | |
while(1); | |
} | |
void HardFaultHandler(uint32_t *hardfault_args) | |
{ | |
#if 0 | |
static volatile struct fault_regs { | |
uint32_t r0; | |
uint32_t r1; | |
uint32_t r2; | |
uint32_t r3; | |
uint32_t r12; | |
uint32_t lr; | |
uint32_t pc; | |
uint32_t psr; | |
uint32_t bfar; | |
uint32_t cfsr; | |
uint32_t hfsr; | |
uint32_t dfsr; | |
uint32_t afsr; | |
uint32_t shcsr; | |
} fault_regs; | |
fault_regs.r0 = hardfault_args[0]; | |
fault_regs.r1 = hardfault_args[1]; | |
fault_regs.r2 = hardfault_args[2]; | |
fault_regs.r3 = hardfault_args[3]; | |
fault_regs.r12 = hardfault_args[4]; | |
fault_regs.lr = hardfault_args[5]; | |
fault_regs.pc = hardfault_args[6]; | |
fault_regs.psr = hardfault_args[7]; | |
fault_regs.bfar = HWREG(0xe000ed38); | |
fault_regs.cfsr = HWREG(0xe000ed28); | |
fault_regs.hfsr = HWREG(0xe000ed2c); | |
fault_regs.dfsr = HWREG(0xe000ed30); | |
fault_regs.afsr = HWREG(0xe000ed3c); | |
fault_regs.shcsr = HWREG(0xe000ed24); | |
(void)fault_regs; | |
#else | |
(void)hardfault_args; | |
#endif | |
while (1); | |
} | |
#define FLASH_CCA_BOOTLDR_CFG_DISABLE 0xEFFFFFFF ///< Disable backdoor function | |
#define FLASH_CCA_BOOTLDR_CFG_ENABLE 0xF0FFFFFF ///< Enable backdoor function | |
#define FLASH_CCA_BOOTLDR_CFG_ACTIVE_HIGH 0x08000000 ///< Selected pin on pad A active high | |
#define FLASH_CCA_BOOTLDR_CFG_PORT_A_PIN_M 0x07000000 ///< Selected pin on pad A mask | |
#define FLASH_CCA_BOOTLDR_CFG_PORT_A_PIN_S 24 ///< Selected pin on pad A shift | |
#define FLASH_CCA_IMAGE_VALID 0x00000000 ///< Indicates valid image in flash | |
#define FLASH_CCA_CONF_BOOTLDR_BACKDOOR_PORT_A_PIN 6 ///< Select Button on SmartRF06 Eval Board | |
typedef struct | |
{ | |
uint32_t ui32BootldrCfg; | |
uint32_t ui32ImageValid; | |
uint32_t ui32ImageVectorAddr; | |
uint8_t ui8lock[32]; | |
} flash_cca_lock_page_t; | |
__attribute__((__section__(".flashcca"), used)) | |
const flash_cca_lock_page_t flash_cca_lock_page = | |
{ | |
FLASH_CCA_BOOTLDR_CFG_ENABLE | |
| (FLASH_CCA_CONF_BOOTLDR_BACKDOOR_PORT_A_PIN | |
<< FLASH_CCA_BOOTLDR_CFG_PORT_A_PIN_S), | |
FLASH_CCA_IMAGE_VALID, | |
(uint32_t) &vectors, | |
{ | |
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, | |
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, | |
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, | |
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF | |
} | |
}; | |
typedef void (*init_fn_t)(void); | |
void ResetHandler(void) | |
{ | |
SYS_CTRL_EMUOVR = 0xFF; | |
// configure clocks | |
SYS_CTRL_CLOCK_CTRL |= SYS_CTRL_CLOCK_CTRL_AMP_DET; | |
SYS_CTRL_CLOCK_CTRL = (0 << SYS_CTRL_CLOCK_CTRL_SYS_DIV) | | |
(1 << SYS_CTRL_CLOCK_CTRL_IO_DIV) | | |
SYS_CTRL_CLOCK_CTRL_OSC_PD; | |
// alternate map | |
SYS_CTRL_I_MAP |= SYS_CTRL_I_MAP_ALTMAP; | |
// copy the data segment initializers from flash to SRAM | |
memcpy(&_data, &_ldata, &_edata - &_data); | |
// zero-fill the bss segment | |
memset(&_bss, 0, &_ebss - &_bss); | |
// C++ runtime initialization (BSS, Data, relocation, etc.) | |
init_fn_t *fp; | |
for (fp = (init_fn_t *)&_init_array; fp < (init_fn_t *)&_einit_array; fp++) | |
{ | |
(*fp)(); | |
} | |
// call the application's entry point | |
main(); | |
// end here if main() returns | |
while (1); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment