/* * 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 #include #include #include #include #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 }