1274 lines
37 KiB
C
1274 lines
37 KiB
C
/*
|
|
* 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/logging.h>
|
|
#include <openthread/platform/alarm-milli.h>
|
|
#include <openthread/platform/diag.h>
|
|
#include <openthread/platform/radio.h>
|
|
|
|
#include "platform-cc2538.h"
|
|
#include "utils/code_utils.h"
|
|
|
|
#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
|
|
|
|
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 = OPENTHREAD_CONFIG_CC2538_RSSI_OFFSET,
|
|
// TI AN130 (SWRA447) Table 4 (bottom of page 3)
|
|
CC2592_RSSI_OFFSET_HGM = 85,
|
|
CC2592_RSSI_OFFSET_LGM = 81,
|
|
CC2538_CRC_BIT_MASK = 0x80,
|
|
CC2538_LQI_BIT_MASK = 0x7f,
|
|
};
|
|
|
|
// All values in dBm
|
|
enum
|
|
{
|
|
CC2538_RECEIVE_SENSITIVITY = OPENTHREAD_CONFIG_CC2538_RECEIVE_SENSITIVITY,
|
|
// TI AN130 (SWRA447) Table 3 (middle of page 3)
|
|
CC2592_RECEIVE_SENSITIVITY_LGM = -99,
|
|
CC2592_RECEIVE_SENSITIVITY_HGM = -101,
|
|
};
|
|
|
|
typedef struct TxPowerTable
|
|
{
|
|
int8_t mTxPowerVal;
|
|
uint8_t mTxPowerReg;
|
|
} TxPowerTable;
|
|
|
|
// The transmit power table.
|
|
static const TxPowerTable sTxPowerTable[] = {
|
|
#if OPENTHREAD_CONFIG_CC2538_WITH_CC2592
|
|
// CC2538 using CC2592 PA
|
|
// Values are from AN130 table 6 (page 4)
|
|
{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
|
|
#else
|
|
// CC2538 operating "bare foot"
|
|
// Values are from SmartRF Studio 2.4.0
|
|
{7, 0xFF}, //
|
|
{5, 0xED}, //
|
|
{3, 0xD5}, //
|
|
{1, 0xC5}, //
|
|
{0, 0xB6}, //
|
|
{-1, 0xB0}, //
|
|
{-3, 0xA1}, //
|
|
{-5, 0x91}, //
|
|
{-7, 0x88}, //
|
|
{-9, 0x72}, //
|
|
{-11, 0x62}, //
|
|
{-13, 0x58}, //
|
|
{-15, 0x42}, //
|
|
{-24, 0x00}, //
|
|
#endif
|
|
};
|
|
|
|
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;
|
|
|
|
#if OPENTHREAD_CONFIG_LOG_PLATFORM && OPENTHREAD_CONFIG_CC2538_USE_RADIO_RX_INTERRUPT
|
|
// Debugging _and_ logging are enabled, so if there's a dropped frame
|
|
// we'll need to store the length here as using snprintf from an interrupt
|
|
// handler is not a good idea.
|
|
static uint8_t sDroppedFrameLength = 0;
|
|
#endif
|
|
|
|
static int8_t cc2538RadioGetRssiOffset(void);
|
|
|
|
void enableReceiver(void)
|
|
{
|
|
if (!sIsReceiverEnabled)
|
|
{
|
|
otLogInfoPlat("Enabling receiver", 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("Disabling receiver", 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)
|
|
{
|
|
disableReceiver();
|
|
enabled = true;
|
|
}
|
|
|
|
otLogInfoPlat("Channel=%d", aChannel);
|
|
|
|
HWREG(RFCORE_XREG_FREQCTRL) = 11 + (aChannel - 11) * 5;
|
|
sChannel = aChannel;
|
|
|
|
if (enabled)
|
|
{
|
|
enableReceiver();
|
|
}
|
|
}
|
|
}
|
|
|
|
void setTxPower(int8_t aTxPower)
|
|
{
|
|
uint8_t i = 0;
|
|
|
|
if (sTxPower != aTxPower)
|
|
{
|
|
otLogInfoPlat("TxPower=%d", 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;
|
|
}
|
|
}
|
|
|
|
static bool cc2538SrcMatchEnabled(void)
|
|
{
|
|
return (HWREG(RFCORE_XREG_FRMCTRL1) & RFCORE_XREG_FRMCTRL1_PENDING_OR) == 0;
|
|
}
|
|
|
|
static bool cc2538GetSrcMatchFoundIntFlag(void)
|
|
{
|
|
bool flag = (HWREG(RFCORE_SFR_RFIRQF0) & RFCORE_SFR_RFIRQF0_SRC_MATCH_FOUND) != 0;
|
|
if (flag)
|
|
{
|
|
HWREG(RFCORE_SFR_RFIRQF0) &= ~RFCORE_SFR_RFIRQF0_SRC_MATCH_FOUND;
|
|
}
|
|
return flag;
|
|
}
|
|
|
|
void otPlatRadioGetIeeeEui64(otInstance *aInstance, uint8_t *aIeeeEui64)
|
|
{
|
|
OT_UNUSED_VARIABLE(aInstance);
|
|
|
|
// EUI64 is in a mixed-endian format. Split in two halves, each 32-bit
|
|
// half is in little-endian format (machine endian). However, the
|
|
// most significant part of the EUI64 comes first, so we can't cheat
|
|
// with a uint64_t!
|
|
//
|
|
// See https://e2e.ti.com/support/wireless_connectivity/low_power_rf_tools/f/155/p/307344/1072252
|
|
|
|
volatile uint32_t *eui64 = &HWREG(IEEE_EUI64);
|
|
|
|
// Read first 32-bits
|
|
uint32_t part = eui64[0];
|
|
for (uint8_t i = 0; i < (OT_EXT_ADDRESS_SIZE / 2); i++)
|
|
{
|
|
aIeeeEui64[3 - i] = part;
|
|
part >>= 8;
|
|
}
|
|
|
|
// Read the last 32-bits
|
|
part = eui64[1];
|
|
for (uint8_t i = 0; i < (OT_EXT_ADDRESS_SIZE / 2); i++)
|
|
{
|
|
aIeeeEui64[7 - i] = part;
|
|
part >>= 8;
|
|
}
|
|
}
|
|
|
|
void otPlatRadioSetPanId(otInstance *aInstance, uint16_t aPanid)
|
|
{
|
|
OT_UNUSED_VARIABLE(aInstance);
|
|
|
|
otLogInfoPlat("PANID=%X", aPanid);
|
|
|
|
HWREG(RFCORE_FFSM_PAN_ID0) = aPanid & 0xFF;
|
|
HWREG(RFCORE_FFSM_PAN_ID1) = aPanid >> 8;
|
|
}
|
|
|
|
void otPlatRadioSetExtendedAddress(otInstance *aInstance, const otExtAddress *aAddress)
|
|
{
|
|
OT_UNUSED_VARIABLE(aInstance);
|
|
|
|
otLogInfoPlat("ExtAddr=%X%X%X%X%X%X%X%X", aAddress->m8[7], aAddress->m8[6], aAddress->m8[5], aAddress->m8[4],
|
|
aAddress->m8[3], aAddress->m8[2], aAddress->m8[1], aAddress->m8[0]);
|
|
|
|
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)
|
|
{
|
|
OT_UNUSED_VARIABLE(aInstance);
|
|
|
|
otLogInfoPlat("ShortAddr=%X", 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;
|
|
|
|
#if OPENTHREAD_CONFIG_CC2538_USE_RADIO_RX_INTERRUPT
|
|
// Enable interrupts for RX/TX, interrupt 26.
|
|
// That's NVIC index 0 (26 >> 5) bit 26 (26 & 0x1f).
|
|
HWREG(NVIC_EN0 + (0 * 4)) = (1 << 26);
|
|
HWREG(RFCORE_XREG_RFIRQM0) |= RFCORE_XREG_RFIRQM0_RXPKTDONE;
|
|
#endif
|
|
|
|
// 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;
|
|
|
|
#if OPENTHREAD_CONFIG_CC2538_WITH_CC2592
|
|
// PA_EN pin configuration.
|
|
// Step 1. make it an output
|
|
HWREG(GPIO_C_BASE | GPIO_O_DIR) |= GPIO_PIN(OPENTHREAD_CONFIG_CC2592_PA_EN_PIN);
|
|
// Step 2. Route PA_PD to OBS0 and invert it to produce PA_EN
|
|
HWREG_ARR(RFCORE_XREG_RFC_OBS_CTRL, 0) = RFCORE_XREG_RFC_OBS_POL_INV // Invert the output
|
|
| RFCORE_XREG_RFC_OBS_MUX_PA_PD; // PA "power down" signal
|
|
// Step 3. Connect the selected pin to OBS0 and enable OBS0.
|
|
HWREG_ARR(CCTEST_OBSSEL, OPENTHREAD_CONFIG_CC2592_PA_EN_PIN) = CCTEST_OBSSEL_EN // Enable the output
|
|
| CCTEST_OBSSEL_SEL_OBS0; // Select OBS0
|
|
|
|
// LNA_EN pin configuration.
|
|
HWREG(GPIO_C_BASE | GPIO_O_DIR) |= GPIO_PIN(OPENTHREAD_CONFIG_CC2592_LNA_EN_PIN);
|
|
HWREG_ARR(RFCORE_XREG_RFC_OBS_CTRL, 1) = RFCORE_XREG_RFC_OBS_POL_INV | RFCORE_XREG_RFC_OBS_MUX_LNA_PD;
|
|
HWREG_ARR(CCTEST_OBSSEL, OPENTHREAD_CONFIG_CC2592_LNA_EN_PIN) = CCTEST_OBSSEL_EN | CCTEST_OBSSEL_SEL_OBS1;
|
|
|
|
#if OPENTHREAD_CONFIG_CC2592_USE_HGM
|
|
// HGM pin configuration. Set the pin state first so we don't glitch.
|
|
cc2538RadioSetHgm(OPENTHREAD_CONFIG_CC2592_HGM_DEFAULT_STATE);
|
|
HWREG(OPENTHREAD_CONFIG_CC2592_HGM_PORT | GPIO_O_DIR) |= GPIO_PIN(OPENTHREAD_CONFIG_CC2592_HGM_PIN);
|
|
#endif // OPENTHREAD_CONFIG_CC2592_USE_HGM
|
|
#endif // OPENTHREAD_CONFIG_CC2538_WITH_CC2592
|
|
|
|
otLogInfoPlat("Initialized", NULL);
|
|
}
|
|
|
|
#if OPENTHREAD_CONFIG_CC2538_WITH_CC2592 && OPENTHREAD_CONFIG_CC2592_USE_HGM
|
|
void cc2538RadioSetHgm(bool aState)
|
|
{
|
|
if (aState)
|
|
{
|
|
HWREG_ARR(OPENTHREAD_CONFIG_CC2592_HGM_PORT, GPIO_PIN(OPENTHREAD_CONFIG_CC2592_HGM_PIN)) =
|
|
GPIO_PIN(OPENTHREAD_CONFIG_CC2592_HGM_PIN);
|
|
}
|
|
else
|
|
{
|
|
HWREG_ARR(OPENTHREAD_CONFIG_CC2592_HGM_PORT, GPIO_PIN(OPENTHREAD_CONFIG_CC2592_HGM_PIN)) = 0;
|
|
}
|
|
}
|
|
|
|
bool cc2538RadioGetHgm(void)
|
|
{
|
|
if (HWREG_ARR(OPENTHREAD_CONFIG_CC2592_HGM_PORT, GPIO_PIN(OPENTHREAD_CONFIG_CC2592_HGM_PIN)) &
|
|
GPIO_PIN(OPENTHREAD_CONFIG_CC2592_HGM_PIN))
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
#endif // OPENTHREAD_CONFIG_CC2538_WITH_CC2592 && OPENTHREAD_CONFIG_CC2592_USE_HGM
|
|
|
|
bool otPlatRadioIsEnabled(otInstance *aInstance)
|
|
{
|
|
OT_UNUSED_VARIABLE(aInstance);
|
|
|
|
return (sState != OT_RADIO_STATE_DISABLED) ? true : false;
|
|
}
|
|
|
|
otError otPlatRadioEnable(otInstance *aInstance)
|
|
{
|
|
if (!otPlatRadioIsEnabled(aInstance))
|
|
{
|
|
otLogDebgPlat("State=OT_RADIO_STATE_SLEEP", NULL);
|
|
sState = OT_RADIO_STATE_SLEEP;
|
|
}
|
|
|
|
return OT_ERROR_NONE;
|
|
}
|
|
|
|
otError otPlatRadioDisable(otInstance *aInstance)
|
|
{
|
|
if (otPlatRadioIsEnabled(aInstance))
|
|
{
|
|
otLogDebgPlat("State=OT_RADIO_STATE_DISABLED", NULL);
|
|
sState = OT_RADIO_STATE_DISABLED;
|
|
}
|
|
|
|
return OT_ERROR_NONE;
|
|
}
|
|
|
|
otError otPlatRadioSleep(otInstance *aInstance)
|
|
{
|
|
OT_UNUSED_VARIABLE(aInstance);
|
|
|
|
otError error = OT_ERROR_INVALID_STATE;
|
|
|
|
if (sState == OT_RADIO_STATE_SLEEP || sState == OT_RADIO_STATE_RECEIVE)
|
|
{
|
|
otLogDebgPlat("State=OT_RADIO_STATE_SLEEP", NULL);
|
|
error = OT_ERROR_NONE;
|
|
sState = OT_RADIO_STATE_SLEEP;
|
|
disableReceiver();
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
otError otPlatRadioReceive(otInstance *aInstance, uint8_t aChannel)
|
|
{
|
|
OT_UNUSED_VARIABLE(aInstance);
|
|
|
|
otError error = OT_ERROR_INVALID_STATE;
|
|
|
|
if (sState != OT_RADIO_STATE_DISABLED)
|
|
{
|
|
otLogDebgPlat("State=OT_RADIO_STATE_RECEIVE", NULL);
|
|
|
|
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)
|
|
{
|
|
OT_UNUSED_VARIABLE(aInstance);
|
|
|
|
otError error = OT_ERROR_INVALID_STATE;
|
|
|
|
if (sState == OT_RADIO_STATE_RECEIVE)
|
|
{
|
|
int i;
|
|
|
|
error = OT_ERROR_NONE;
|
|
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("Radio is stuck!!! FSMSTAT0=0x%08x FSMSTAT1=0x%08x RFERRF=0x%08x",
|
|
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("RX Buffer Overflow detected", NULL);
|
|
}
|
|
|
|
if (HWREG(RFCORE_SFR_RFERRF) & RFCORE_SFR_RFERRF_RXABO)
|
|
{
|
|
otLogCritPlat("Aborted RX detected", 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("Idle state detected", 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("Transmitted %d bytes", aFrame->mLength);
|
|
}
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *aInstance)
|
|
{
|
|
OT_UNUSED_VARIABLE(aInstance);
|
|
|
|
return &sTransmitFrame;
|
|
}
|
|
|
|
int8_t otPlatRadioGetRssi(otInstance *aInstance)
|
|
{
|
|
OT_UNUSED_VARIABLE(aInstance);
|
|
|
|
int8_t rssi = OT_RADIO_RSSI_INVALID;
|
|
|
|
if ((HWREG(RFCORE_XREG_RSSISTAT) & RFCORE_XREG_RSSISTAT_RSSI_VALID) != 0)
|
|
{
|
|
rssi = HWREG(RFCORE_XREG_RSSI) & 0xff;
|
|
|
|
if (rssi > cc2538RadioGetRssiOffset() - 128)
|
|
{
|
|
rssi -= cc2538RadioGetRssiOffset();
|
|
}
|
|
else
|
|
{
|
|
rssi = -128;
|
|
}
|
|
}
|
|
|
|
return rssi;
|
|
}
|
|
|
|
otRadioCaps otPlatRadioGetCaps(otInstance *aInstance)
|
|
{
|
|
OT_UNUSED_VARIABLE(aInstance);
|
|
|
|
return OT_RADIO_CAPS_ENERGY_SCAN;
|
|
}
|
|
|
|
static bool cc2538RadioGetPromiscuous(void)
|
|
{
|
|
return (HWREG(RFCORE_XREG_FRMFILT0) & RFCORE_XREG_FRMFILT0_FRAME_FILTER_EN) == 0;
|
|
}
|
|
|
|
bool otPlatRadioGetPromiscuous(otInstance *aInstance)
|
|
{
|
|
OT_UNUSED_VARIABLE(aInstance);
|
|
|
|
return cc2538RadioGetPromiscuous();
|
|
}
|
|
|
|
static int8_t cc2538RadioGetRssiOffset(void)
|
|
{
|
|
#if OPENTHREAD_CONFIG_CC2538_WITH_CC2592 && OPENTHREAD_CONFIG_CC2592_USE_HGM
|
|
if (cc2538RadioGetHgm())
|
|
{
|
|
return CC2592_RSSI_OFFSET_HGM;
|
|
}
|
|
else
|
|
{
|
|
return CC2592_RSSI_OFFSET_LGM;
|
|
}
|
|
#else // OPENTHREAD_CONFIG_CC2538_WITH_CC2592 && OPENTHREAD_CONFIG_CC2592_USE_HGM
|
|
return CC2538_RSSI_OFFSET;
|
|
#endif // OPENTHREAD_CONFIG_CC2538_WITH_CC2592 && OPENTHREAD_CONFIG_CC2592_USE_HGM
|
|
}
|
|
|
|
void otPlatRadioSetPromiscuous(otInstance *aInstance, bool aEnable)
|
|
{
|
|
OT_UNUSED_VARIABLE(aInstance);
|
|
|
|
otLogInfoPlat("PromiscuousMode=%d", 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;
|
|
}
|
|
}
|
|
|
|
static void readFrame(void)
|
|
{
|
|
uint8_t length;
|
|
uint8_t crcCorr;
|
|
int i;
|
|
|
|
/*
|
|
* There is already a frame present in the buffer, return early so
|
|
* we do not overwrite it (hopefully we'll catch it on the next run).
|
|
*/
|
|
otEXPECT(sReceiveFrame.mLength == 0);
|
|
|
|
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_CONFIG_TIME_SYNC_ENABLE
|
|
#error Time sync requires the timestamp of SFD rather than that of rx done!
|
|
#else
|
|
// Timestamp
|
|
if (cc2538RadioGetPromiscuous())
|
|
#endif
|
|
{
|
|
// The current driver only supports milliseconds resolution.
|
|
sReceiveFrame.mInfo.mRxInfo.mTimestamp = otPlatAlarmMilliGetNow() * 1000;
|
|
}
|
|
|
|
// read psdu
|
|
for (i = 0; i < length - 2; i++)
|
|
{
|
|
sReceiveFrame.mPsdu[i] = HWREG(RFCORE_SFR_RFDATA);
|
|
}
|
|
|
|
sReceiveFrame.mInfo.mRxInfo.mRssi = (int8_t)HWREG(RFCORE_SFR_RFDATA) - cc2538RadioGetRssiOffset();
|
|
crcCorr = HWREG(RFCORE_SFR_RFDATA);
|
|
|
|
if (crcCorr & CC2538_CRC_BIT_MASK)
|
|
{
|
|
sReceiveFrame.mLength = length;
|
|
sReceiveFrame.mInfo.mRxInfo.mLqi = crcCorr & CC2538_LQI_BIT_MASK;
|
|
|
|
if (length > IEEE802154_ACK_LENGTH)
|
|
{
|
|
// Set ACK FP flag for the received frame according to whether SRC_MATCH_FOUND was triggered just before
|
|
// if SRC MATCH is not enabled, SRC_MATCH_FOUND is not triggered and all ACK FP is always set
|
|
sReceiveFrame.mInfo.mRxInfo.mAckedWithFramePending =
|
|
cc2538SrcMatchEnabled() ? cc2538GetSrcMatchFoundIntFlag() : true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// resets rxfifo
|
|
HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX;
|
|
HWREG(RFCORE_SFR_RFST) = RFCORE_SFR_RFST_INSTR_FLUSHRX;
|
|
#if OPENTHREAD_CONFIG_LOG_PLATFORM && OPENTHREAD_CONFIG_CC2538_USE_RADIO_RX_INTERRUPT
|
|
// Debugging _and_ logging are enabled, it may not be safe to do
|
|
// logging if we're in the interrupt context, so just stash the
|
|
// length and do the logging later.
|
|
sDroppedFrameLength = length;
|
|
#else
|
|
otLogDebgPlat("Dropping %d received bytes (Invalid CRC)", length);
|
|
#endif
|
|
}
|
|
|
|
// 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)
|
|
{
|
|
#if OPENTHREAD_CONFIG_CC2538_USE_RADIO_RX_INTERRUPT
|
|
// Disable the receive interrupt so that sReceiveFrame doesn't get
|
|
// blatted by the interrupt handler while we're polling.
|
|
HWREG(RFCORE_XREG_RFIRQM0) &= ~RFCORE_XREG_RFIRQM0_RXPKTDONE;
|
|
#endif
|
|
|
|
readFrame();
|
|
|
|
#if OPENTHREAD_CONFIG_LOG_PLATFORM && OPENTHREAD_CONFIG_CC2538_USE_RADIO_RX_INTERRUPT
|
|
if (sDroppedFrameLength != 0)
|
|
{
|
|
otLogDebgPlat("Dropping %d received bytes (Invalid CRC)", sDroppedFrameLength);
|
|
sDroppedFrameLength = 0;
|
|
}
|
|
#endif
|
|
|
|
if ((sState == OT_RADIO_STATE_RECEIVE && sReceiveFrame.mLength > 0) ||
|
|
(sState == OT_RADIO_STATE_TRANSMIT && sReceiveFrame.mLength > IEEE802154_ACK_LENGTH))
|
|
{
|
|
#if OPENTHREAD_CONFIG_DIAG_ENABLE
|
|
|
|
if (otPlatDiagModeGet())
|
|
{
|
|
otPlatDiagRadioReceiveDone(aInstance, &sReceiveFrame, sReceiveError);
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
// signal MAC layer for each received frame if promiscuous 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("Received %d bytes", 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("Transmit failed ErrorCode=%d", sTransmitError);
|
|
}
|
|
|
|
sState = OT_RADIO_STATE_RECEIVE;
|
|
|
|
#if OPENTHREAD_CONFIG_DIAG_ENABLE
|
|
|
|
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]))
|
|
{
|
|
sState = OT_RADIO_STATE_RECEIVE;
|
|
|
|
otPlatRadioTxDone(aInstance, &sTransmitFrame, &sReceiveFrame, sTransmitError);
|
|
}
|
|
}
|
|
|
|
sReceiveFrame.mLength = 0;
|
|
|
|
#if OPENTHREAD_CONFIG_CC2538_USE_RADIO_RX_INTERRUPT
|
|
// Turn the receive interrupt handler back on now the buffer is clear.
|
|
HWREG(RFCORE_XREG_RFIRQM0) |= RFCORE_XREG_RFIRQM0_RXPKTDONE;
|
|
#endif
|
|
}
|
|
|
|
void RFCoreRxTxIntHandler(void)
|
|
{
|
|
#if OPENTHREAD_CONFIG_CC2538_USE_RADIO_RX_INTERRUPT
|
|
if (HWREG(RFCORE_SFR_RFIRQF0) & RFCORE_SFR_RFIRQF0_RXPKTDONE)
|
|
{
|
|
readFrame();
|
|
|
|
if (sReceiveFrame.mLength > 0)
|
|
{
|
|
// A frame has been received, disable the interrupt handler
|
|
// until the main loop has dealt with this previous frame,
|
|
// otherwise we might overwrite it whilst it is being read.
|
|
HWREG(RFCORE_XREG_RFIRQM0) &= ~RFCORE_XREG_RFIRQM0_RXPKTDONE;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
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(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("Short enable status: 0x%x", shortEnableStatus);
|
|
otLogDebgPlat("Ext enable status: 0x%x", 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 cc2538EnergyScanTimerHandler(void)
|
|
{
|
|
int8_t rssi = otPlatRadioGetRssi(sInstance);
|
|
|
|
disableReceiver();
|
|
|
|
HWREG(RFCORE_XREG_FRMCTRL0) &= ~RFCORE_XREG_FRMCTRL0_ENERGY_SCAN;
|
|
HWREG(RFCORE_XREG_FREQCTRL) = 11 + (sChannel - 11) * 5;
|
|
|
|
if (sIsReceiverEnabled)
|
|
{
|
|
enableReceiver();
|
|
}
|
|
|
|
otPlatRadioEnergyScanDone(sInstance, rssi);
|
|
}
|
|
|
|
void otPlatRadioEnableSrcMatch(otInstance *aInstance, bool aEnable)
|
|
{
|
|
OT_UNUSED_VARIABLE(aInstance);
|
|
|
|
otLogInfoPlat("EnableSrcMatch=%d", 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, uint16_t aShortAddress)
|
|
{
|
|
OT_UNUSED_VARIABLE(aInstance);
|
|
|
|
otError error = OT_ERROR_NONE;
|
|
int8_t entry = findSrcMatchAvailEntry(true);
|
|
uint32_t *addr = (uint32_t *)RFCORE_FFSM_SRCADDRESS_TABLE;
|
|
|
|
otLogDebgPlat("Add ShortAddr entry: %d", 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)
|
|
{
|
|
OT_UNUSED_VARIABLE(aInstance);
|
|
|
|
otError error = OT_ERROR_NONE;
|
|
int8_t entry = findSrcMatchAvailEntry(false);
|
|
uint32_t *addr = (uint32_t *)RFCORE_FFSM_SRCADDRESS_TABLE;
|
|
|
|
otLogDebgPlat("Add ExtAddr entry: %d", 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, uint16_t aShortAddress)
|
|
{
|
|
OT_UNUSED_VARIABLE(aInstance);
|
|
|
|
otError error = OT_ERROR_NONE;
|
|
int8_t entry = findSrcMatchShortEntry(aShortAddress);
|
|
|
|
otLogDebgPlat("Clear ShortAddr entry: %d", 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)
|
|
{
|
|
OT_UNUSED_VARIABLE(aInstance);
|
|
|
|
otError error = OT_ERROR_NONE;
|
|
int8_t entry = findSrcMatchExtEntry(aExtAddress);
|
|
|
|
otLogDebgPlat("Clear ExtAddr entry: %d", entry);
|
|
|
|
otEXPECT_ACTION(entry >= 0, error = OT_ERROR_NO_ADDRESS);
|
|
|
|
setSrcMatchEntryEnableStatus(false, (uint8_t)(entry), false);
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
void otPlatRadioClearSrcMatchShortEntries(otInstance *aInstance)
|
|
{
|
|
OT_UNUSED_VARIABLE(aInstance);
|
|
|
|
uint32_t *addrEn = (uint32_t *)RFCORE_XREG_SRCSHORTEN0;
|
|
uint32_t *addrAutoPendEn = (uint32_t *)RFCORE_FFSM_SRCSHORTPENDEN0;
|
|
|
|
otLogDebgPlat("Clear ShortAddr entries", NULL);
|
|
|
|
for (uint8_t i = 0; i < RFCORE_XREG_SRCMATCH_ENABLE_STATUS_SIZE; i++)
|
|
{
|
|
HWREG(addrEn++) = 0;
|
|
HWREG(addrAutoPendEn++) = 0;
|
|
}
|
|
}
|
|
|
|
void otPlatRadioClearSrcMatchExtEntries(otInstance *aInstance)
|
|
{
|
|
OT_UNUSED_VARIABLE(aInstance);
|
|
|
|
uint32_t *addrEn = (uint32_t *)RFCORE_XREG_SRCEXTEN0;
|
|
uint32_t *addrAutoPendEn = (uint32_t *)RFCORE_FFSM_SRCEXTPENDEN0;
|
|
|
|
otLogDebgPlat("Clear ExtAddr entries", 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)
|
|
{
|
|
OT_UNUSED_VARIABLE(aInstance);
|
|
|
|
otLogInfoPlat("ScanChannel=%d", aScanChannel);
|
|
|
|
if (aScanChannel != sChannel)
|
|
{
|
|
if (sIsReceiverEnabled)
|
|
{
|
|
disableReceiver();
|
|
}
|
|
|
|
HWREG(RFCORE_XREG_FREQCTRL) = 11 + (aScanChannel - 11) * 5;
|
|
|
|
enableReceiver();
|
|
}
|
|
else if (!sIsReceiverEnabled)
|
|
{
|
|
enableReceiver();
|
|
}
|
|
|
|
// Collect peak signal strength
|
|
HWREG(RFCORE_XREG_FRMCTRL0) |= RFCORE_XREG_FRMCTRL0_ENERGY_SCAN;
|
|
|
|
cc2538SetTimer(OT_CC2538_TIMER_ENERGY_SCAN, aScanDuration);
|
|
|
|
return OT_ERROR_NONE;
|
|
}
|
|
|
|
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 = sTxPower;
|
|
|
|
exit:
|
|
return error;
|
|
}
|
|
|
|
otError otPlatRadioSetTransmitPower(otInstance *aInstance, int8_t aPower)
|
|
{
|
|
OT_UNUSED_VARIABLE(aInstance);
|
|
|
|
setTxPower(aPower);
|
|
return OT_ERROR_NONE;
|
|
}
|
|
|
|
otError otPlatRadioGetCcaEnergyDetectThreshold(otInstance *aInstance, int8_t *aThreshold)
|
|
{
|
|
OT_UNUSED_VARIABLE(aInstance);
|
|
OT_UNUSED_VARIABLE(aThreshold);
|
|
|
|
return OT_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
otError otPlatRadioSetCcaEnergyDetectThreshold(otInstance *aInstance, int8_t aThreshold)
|
|
{
|
|
OT_UNUSED_VARIABLE(aInstance);
|
|
OT_UNUSED_VARIABLE(aThreshold);
|
|
|
|
return OT_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
int8_t otPlatRadioGetReceiveSensitivity(otInstance *aInstance)
|
|
{
|
|
OT_UNUSED_VARIABLE(aInstance);
|
|
|
|
#if OPENTHREAD_CONFIG_CC2538_WITH_CC2592 && OPENTHREAD_CONFIG_CC2592_USE_HGM
|
|
if (cc2538RadioGetHgm())
|
|
{
|
|
return CC2592_RECEIVE_SENSITIVITY_HGM;
|
|
}
|
|
else
|
|
{
|
|
return CC2592_RECEIVE_SENSITIVITY_LGM;
|
|
}
|
|
#else // OPENTHREAD_CONFIG_CC2538_WITH_CC2592 && OPENTHREAD_CONFIG_CC2592_USE_HGM
|
|
return CC2538_RECEIVE_SENSITIVITY;
|
|
#endif // OPENTHREAD_CONFIG_CC2538_WITH_CC2592 && OPENTHREAD_CONFIG_CC2592_USE_HGM
|
|
}
|