| /* |
| * 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 radio.cpp |
| * Platform abstraction for radio communication. |
| */ |
| |
| #include <openthread/openthread.h> |
| #include <openthread/platform/alarm.h> |
| #include <openthread/platform/radio.h> |
| #include "utils/code_utils.h" |
| |
| #include <string.h> |
| |
| #include "platform-da15000.h" |
| |
| #include "ad_ftdf.h" |
| #include "ad_ftdf_phy_api.h" |
| #include "hw_otpc.h" |
| #include "hw_rf.h" |
| #include "internal.h" |
| #include "regmap.h" |
| |
| #define FACTORY_TEST_TIMESTAMP (0x7F8EA08) // Register holds a timestamp of facotry test of a chip |
| #define FACTORY_TESTER_ID (0x7F8EA0C) // Register holds test machine ID used for factory test |
| |
| #define DEFAULT_CHANNEL (11) |
| |
| #define PRIVILEGED_DATA __attribute__((section("privileged_data_zi"))) |
| |
| #define IEEE802154_ACK_LENGTH 5 |
| #define IEEE802154_FRAME_TYPE_MASK 0x07 |
| #define IEEE802154_FRAME_TYPE_ACK 0x02 |
| #define IEEE802154_FRAME_PENDING 1 << 4 |
| #define IEEE802154_ACK_REQUEST 1 << 5 |
| #define IEEE802154_DSN_OFFSET 2 |
| |
| #define RSSI_TABLE_SIZE 8 |
| #define EUI64_TABLE_SIZE 8 |
| |
| enum |
| { |
| DA15000_RECEIVE_SENSITIVITY = -100, // dBm |
| }; |
| |
| static void da15000OtpRead(void); |
| static uint8_t eui64[EUI64_TABLE_SIZE] = {0}; |
| |
| static otInstance *sThreadInstance; |
| static otRadioState sRadioState = OT_RADIO_STATE_DISABLED; |
| static otRadioFrame sReceiveFrame; |
| static otRadioFrame sTransmitFrame; |
| static otError sTransmitStatus; |
| static bool sFramePending = false; |
| static bool sRadioPromiscuous = false; |
| static bool sRssiInit = true; |
| static uint8_t sChannel = DEFAULT_CHANNEL; |
| static uint8_t sRssiCtr = 0; |
| static uint32_t sSleepInitDelay = 0; |
| |
| static uint8_t sReceivePsdu[OT_RADIO_FRAME_MAX_SIZE]; |
| static uint8_t sRssiTable[RSSI_TABLE_SIZE]; |
| static uint8_t sTransmitPsdu[OT_RADIO_FRAME_MAX_SIZE]; |
| |
| PRIVILEGED_DATA static uint8_t sEnableRX; |
| |
| static void da15000OtpRead(void) |
| { |
| hw_otpc_init(); // Start clock. |
| hw_otpc_disable(); // Make sure it is in standby mode. |
| hw_otpc_init(); // Restart clock. |
| hw_otpc_manual_read_on(false); |
| |
| __DMB(); |
| uint32_t *factoryTestTimeStamp = (uint32_t *) FACTORY_TEST_TIMESTAMP; |
| uint32_t *factoryTestId = (uint32_t *) FACTORY_TESTER_ID; |
| __DMB(); |
| |
| eui64[0] = 0x80; //80-EA-CA is for Dialog Semiconductor |
| eui64[1] = 0xEA; |
| eui64[2] = 0xCA; |
| eui64[3] = (*factoryTestId >> 8) & 0xff; |
| eui64[4] = (*factoryTestTimeStamp >> 24) & 0xff; |
| eui64[5] = (*factoryTestTimeStamp >> 16) & 0xff; |
| eui64[6] = (*factoryTestTimeStamp >> 8) & 0xff; |
| eui64[7] = *factoryTestTimeStamp & 0xff; |
| |
| hw_otpc_manual_read_off(); |
| hw_otpc_disable(); |
| } |
| |
| void da15000RadioInit(void) |
| { |
| /* Wake up power domains */ |
| REG_CLR_BIT(CRG_TOP, PMU_CTRL_REG, FTDF_SLEEP); |
| |
| while (REG_GETF(CRG_TOP, SYS_STAT_REG, FTDF_IS_UP) == 0x0); |
| |
| REG_CLR_BIT(CRG_TOP, PMU_CTRL_REG, RADIO_SLEEP); |
| |
| while (REG_GETF(CRG_TOP, SYS_STAT_REG, RAD_IS_UP) == 0x0); |
| |
| REG_SETF(CRG_TOP, CLK_RADIO_REG, FTDF_MAC_ENABLE, 1); |
| REG_SETF(CRG_TOP, CLK_RADIO_REG, FTDF_MAC_DIV, 0); |
| |
| hw_rf_poweron(); |
| hw_rf_system_init(); |
| |
| ad_ftdf_init_phy_api(); |
| |
| da15000OtpRead(); |
| |
| sChannel = DEFAULT_CHANNEL; |
| sTransmitFrame.mPsdu = sTransmitPsdu; |
| sReceiveFrame.mPsdu = sReceivePsdu; |
| } |
| |
| void otPlatRadioGetIeeeEui64(otInstance *aInstance, uint8_t *aIeeeEui64) |
| { |
| (void)aInstance; |
| memcpy(aIeeeEui64, eui64, EUI64_TABLE_SIZE); |
| } |
| |
| void otPlatRadioSetPanId(otInstance *aInstance, uint16_t panid) |
| { |
| (void)aInstance; |
| |
| FTDF_setValue(FTDF_PIB_PAN_ID, &panid); |
| } |
| |
| void otPlatRadioSetExtendedAddress(otInstance *aInstance, uint8_t *address) |
| { |
| (void)aInstance; |
| |
| FTDF_setValue(FTDF_PIB_EXTENDED_ADDRESS, address); |
| } |
| |
| void otPlatRadioSetShortAddress(otInstance *aInstance, uint16_t address) |
| { |
| (void)aInstance; |
| |
| FTDF_setValue(FTDF_PIB_SHORT_ADDRESS, &address); |
| } |
| |
| otError otPlatRadioEnable(otInstance *aInstance) |
| { |
| uint8_t maxRetries; |
| |
| otError error = OT_ERROR_NONE; |
| otEXPECT_ACTION(sRadioState == OT_RADIO_STATE_DISABLED, error = OT_ERROR_INVALID_STATE); |
| |
| sThreadInstance = aInstance; |
| |
| sEnableRX = 1; |
| FTDF_setValue(FTDF_PIB_RX_ON_WHEN_IDLE, &sEnableRX); |
| FTDF_setValue(FTDF_PIB_CURRENT_CHANNEL, &sChannel); |
| maxRetries = 0; |
| FTDF_setValue(FTDF_PIB_MAX_FRAME_RETRIES, &maxRetries); |
| |
| FTDF_Bitmap32 options = FTDF_TRANSPARENT_ENABLE_FCS_GENERATION; |
| options |= FTDF_TRANSPARENT_WAIT_FOR_ACK; |
| options |= FTDF_TRANSPARENT_AUTO_ACK; |
| |
| FTDF_enableTransparentMode(FTDF_TRUE, options); |
| otPlatRadioSetPromiscuous(aInstance, false); |
| sRadioState = OT_RADIO_STATE_SLEEP; |
| |
| exit: |
| return error; |
| } |
| |
| otError otPlatRadioDisable(otInstance *aInstance) |
| { |
| (void)aInstance; |
| sEnableRX = 0; |
| FTDF_setValue(FTDF_PIB_RX_ON_WHEN_IDLE, &sEnableRX); |
| ad_ftdf_sleep_when_possible(FTDF_TRUE); |
| |
| FTDF_fpprReset(); |
| |
| sRadioState = OT_RADIO_STATE_DISABLED; |
| |
| return OT_ERROR_NONE; |
| } |
| |
| bool otPlatRadioIsEnabled(otInstance *aInstance) |
| { |
| (void)aInstance; |
| |
| return (sRadioState != OT_RADIO_STATE_DISABLED); |
| } |
| |
| otError otPlatRadioSleep(otInstance *aInstance) |
| { |
| (void)aInstance; |
| |
| if (sRadioState == OT_RADIO_STATE_RECEIVE && sSleepInitDelay == 0) |
| { |
| sSleepInitDelay = otPlatAlarmGetNow(); |
| return OT_ERROR_NONE; |
| } |
| else if ((otPlatAlarmGetNow() - sSleepInitDelay) < dg_configINITIAL_SLEEP_DELAY_TIME) |
| { |
| return OT_ERROR_NONE; |
| } |
| |
| otError error = OT_ERROR_NONE; |
| otEXPECT_ACTION(sRadioState == OT_RADIO_STATE_RECEIVE, error = OT_ERROR_INVALID_STATE); |
| |
| sRadioState = OT_RADIO_STATE_SLEEP; |
| sEnableRX = 0; |
| FTDF_setValue(FTDF_PIB_RX_ON_WHEN_IDLE, &sEnableRX); |
| ad_ftdf_sleep_when_possible(FTDF_TRUE); |
| |
| exit: |
| return error; |
| } |
| |
| otError otPlatRadioReceive(otInstance *aInstance, uint8_t aChannel) |
| { |
| (void)aInstance; |
| otError error = OT_ERROR_NONE; |
| |
| otEXPECT_ACTION(sRadioState != OT_RADIO_STATE_DISABLED, error = OT_ERROR_INVALID_STATE); |
| |
| ad_ftdf_wake_up(); |
| |
| sEnableRX = 0; |
| FTDF_setValue(FTDF_PIB_RX_ON_WHEN_IDLE, &sEnableRX); |
| |
| sChannel = aChannel; |
| FTDF_setValue(FTDF_PIB_CURRENT_CHANNEL, &aChannel); |
| |
| sEnableRX = 1; |
| FTDF_setValue(FTDF_PIB_RX_ON_WHEN_IDLE, &sEnableRX); |
| sRadioState = OT_RADIO_STATE_RECEIVE; |
| |
| exit: |
| return error; |
| } |
| |
| void otPlatRadioEnableSrcMatch(otInstance *aInstance, bool aEnable) |
| { |
| (void)aInstance; |
| (void)aEnable; |
| } |
| |
| otError otPlatRadioAddSrcMatchShortEntry(otInstance *aInstance, const uint16_t aShortAddress) |
| { |
| (void)aInstance; |
| otError error = OT_ERROR_NONE; |
| uint8_t entry; |
| uint8_t entryIdx; |
| |
| // check if address already stored |
| |
| otEXPECT(!FTDF_fpprLookupShortAddress(aShortAddress, &entry, &entryIdx)); |
| |
| otEXPECT_ACTION(FTDF_fpprGetFreeShortAddress(&entry, &entryIdx), error = OT_ERROR_NO_BUFS); |
| |
| FTDF_fpprSetShortAddress(entry, entryIdx, aShortAddress); |
| FTDF_fpprSetShortAddressValid(entry, entryIdx, FTDF_TRUE); |
| |
| exit: |
| return error; |
| |
| } |
| |
| otError otPlatRadioAddSrcMatchExtEntry(otInstance *aInstance, const uint8_t *aExtAddress) |
| { |
| (void)aInstance; |
| |
| uint8_t entry; |
| FTDF_ExtAddress addr; |
| uint32_t addrL; |
| uint64_t addrH; |
| |
| addrL = (aExtAddress[3] << 24) | (aExtAddress[2] << 16) | (aExtAddress[1] << 8) | (aExtAddress[0] << 0); |
| addrH = (aExtAddress[7] << 24) | (aExtAddress[6] << 16) | (aExtAddress[5] << 8) | (aExtAddress[4] << 0); |
| addr = addrL | (addrH << 32); |
| |
| // check if address already stored |
| otError error = OT_ERROR_NONE; |
| otEXPECT(!FTDF_fpprLookupExtAddress(addr, &entry)); |
| |
| otEXPECT_ACTION(FTDF_fpprGetFreeExtAddress(&entry), error = OT_ERROR_NO_BUFS); |
| |
| FTDF_fpprSetExtAddress(entry, addr); |
| FTDF_fpprSetExtAddressValid(entry, FTDF_TRUE); |
| |
| exit: |
| return error; |
| } |
| |
| otError otPlatRadioClearSrcMatchShortEntry(otInstance *aInstance, const uint16_t aShortAddress) |
| { |
| (void)aInstance; |
| otError error = OT_ERROR_NONE; |
| uint8_t entry; |
| uint8_t entryIdx; |
| |
| |
| otEXPECT_ACTION(FTDF_fpprLookupShortAddress(aShortAddress, &entry, &entryIdx), error = OT_ERROR_NO_ADDRESS); |
| |
| FTDF_fpprSetShortAddress(entry, entryIdx, 0); |
| FTDF_fpprSetShortAddressValid(entry, entryIdx, FTDF_FALSE); |
| |
| exit: |
| return error; |
| } |
| |
| otError otPlatRadioClearSrcMatchExtEntry(otInstance *aInstance, const uint8_t *aExtAddress) |
| { |
| (void)aInstance; |
| |
| uint8_t entry; |
| FTDF_ExtAddress addr; |
| uint32_t addrL; |
| uint64_t addrH; |
| |
| addrL = (aExtAddress[3] << 24) | (aExtAddress[2] << 16) | (aExtAddress[1] << 8) | (aExtAddress[0] << 0); |
| addrH = (aExtAddress[7] << 24) | (aExtAddress[6] << 16) | (aExtAddress[5] << 8) | (aExtAddress[4] << 0); |
| addr = addrL | (addrH << 32); |
| |
| otError error = OT_ERROR_NONE; |
| otEXPECT_ACTION(FTDF_fpprLookupExtAddress(addr, &entry), error = OT_ERROR_NO_ADDRESS); |
| |
| FTDF_fpprSetExtAddress(entry, 0); |
| FTDF_fpprSetExtAddressValid(entry, FTDF_FALSE); |
| |
| exit: |
| return error; |
| } |
| |
| void otPlatRadioClearSrcMatchShortEntries(otInstance *aInstance) |
| { |
| (void)aInstance; |
| |
| uint8_t i, j; |
| |
| for (i = 0; i < FTDF_FPPR_TABLE_ENTRIES; i++) |
| { |
| for (j = 0; j < 4; j++) |
| { |
| if (FTDF_fpprGetShortAddressValid(i, j)) |
| { |
| FTDF_fpprSetShortAddressValid(i, j, FTDF_FALSE); |
| } |
| } |
| } |
| } |
| |
| void otPlatRadioClearSrcMatchExtEntries(otInstance *aInstance) |
| { |
| (void)aInstance; |
| |
| uint8_t i; |
| |
| for (i = 0; i < FTDF_FPPR_TABLE_ENTRIES; i++) |
| { |
| if (FTDF_fpprGetExtAddressValid(i)) |
| { |
| FTDF_fpprSetExtAddressValid(i, FTDF_FALSE); |
| } |
| } |
| } |
| |
| otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *aInstance) |
| { |
| (void)aInstance; |
| |
| return &sTransmitFrame; |
| } |
| |
| otError otPlatRadioTransmit(otInstance *aInstance, otRadioFrame *aFrame) |
| { |
| (void)aInstance; |
| |
| uint8_t csmaSuppress; |
| |
| otError error = OT_ERROR_NONE; |
| otEXPECT_ACTION(sRadioState != OT_RADIO_STATE_DISABLED, error = OT_ERROR_INVALID_STATE); |
| |
| csmaSuppress = 0; |
| ad_ftdf_send_frame_simple(aFrame->mLength, aFrame->mPsdu, aFrame->mChannel, 0, csmaSuppress); //Prio 0 for all. |
| sRadioState = OT_RADIO_STATE_TRANSMIT; |
| |
| exit: |
| return error; |
| } |
| |
| int8_t otPlatRadioGetRssi(otInstance *aInstance) |
| { |
| (void)aInstance; |
| uint16_t result = 0; |
| |
| // fill table with same value for init |
| if (sRssiInit) |
| { |
| for (uint8_t i = 0; i < RSSI_TABLE_SIZE; i++) |
| { |
| sRssiTable[i] = sReceiveFrame.mLqi; |
| } |
| |
| sRssiInit = false; |
| } |
| else |
| { |
| ASSERT_ERROR(sRssiCtr < RSSI_TABLE_SIZE); |
| sRssiTable[sRssiCtr] = sReceiveFrame.mLqi; |
| sRssiCtr++; |
| |
| if (sRssiCtr >= RSSI_TABLE_SIZE) |
| { |
| sRssiCtr = 0; |
| } |
| } |
| |
| // average of 8 last lqi |
| for (uint8_t i = 0; i < RSSI_TABLE_SIZE; i++) |
| { |
| result += sRssiTable[i]; |
| } |
| |
| result = result / RSSI_TABLE_SIZE; |
| |
| // Approximation to dB scale by divide by 9 |
| return result / 9; |
| } |
| |
| otRadioCaps otPlatRadioGetCaps(otInstance *aInstance) |
| { |
| (void)aInstance; |
| |
| return OT_RADIO_CAPS_ACK_TIMEOUT; |
| } |
| |
| bool otPlatRadioGetPromiscuous(otInstance *aInstance) |
| { |
| (void)aInstance; |
| |
| return sRadioPromiscuous; |
| } |
| |
| void otPlatRadioSetPromiscuous(otInstance *aInstance, bool aEnable) |
| { |
| (void)aInstance; |
| |
| FTDF_setValue(FTDF_PIB_PROMISCUOUS_MODE, &aEnable); |
| sRadioPromiscuous = aEnable; |
| } |
| |
| otError otPlatRadioEnergyScan(otInstance *aInstance, uint8_t aScanChannel, uint16_t aScanDuration) |
| { |
| (void)aInstance; |
| (void)aScanChannel; |
| (void)aScanDuration; |
| |
| return OT_ERROR_NOT_IMPLEMENTED; |
| } |
| |
| void otPlatRadioSetDefaultTxPower(otInstance *aInstance, int8_t aPower) |
| { |
| (void)aInstance; |
| |
| FTDF_setValue(FTDF_PIB_TX_POWER, &aPower); |
| } |
| |
| void da15000RadioProcess(otInstance *aInstance) |
| { |
| (void)aInstance; |
| uint32_t ftdfCe = *FTDF_GET_REG_ADDR(ON_OFF_REGMAP_FTDF_CE); |
| |
| FTDF_confirmLmacInterrupt(); |
| |
| if (ftdfCe & FTDF_MSK_RX_CE) |
| { |
| FTDF_processRxEvent(); |
| } |
| |
| if (ftdfCe & FTDF_MSK_TX_CE) |
| { |
| FTDF_processTxEvent(); |
| } |
| |
| volatile uint32_t *ftdfCm = FTDF_GET_REG_ADDR(ON_OFF_REGMAP_FTDF_CM); |
| *ftdfCm = FTDF_MSK_TX_CE | FTDF_MSK_RX_CE | FTDF_MSK_SYMBOL_TMR_CE; |
| } |
| |
| void FTDF_sendFrameTransparentConfirm(void *handle, FTDF_Bitmap32 status) |
| { |
| (void)handle; |
| |
| switch (status) |
| { |
| case FTDF_TRANSPARENT_SEND_SUCCESSFUL: |
| sTransmitStatus = OT_ERROR_NONE; |
| break; |
| |
| case FTDF_TRANSPARENT_CSMACA_FAILURE: |
| sTransmitStatus = OT_ERROR_CHANNEL_ACCESS_FAILURE; |
| break; |
| |
| case FTDF_TRANSPARENT_NO_ACK: |
| sTransmitStatus = OT_ERROR_NO_ACK; |
| break; |
| |
| default: |
| sTransmitStatus = OT_ERROR_ABORT; |
| break; |
| } |
| |
| sRadioState = OT_RADIO_STATE_RECEIVE; |
| |
| if (((sTransmitFrame.mPsdu[0] & IEEE802154_ACK_REQUEST) == 0) || sTransmitStatus != OT_ERROR_NONE) |
| { |
| otPlatRadioTxDone(sThreadInstance, &sTransmitFrame, NULL, sTransmitStatus); |
| } |
| else |
| { |
| otPlatRadioTxDone(sThreadInstance, &sTransmitFrame, &sReceiveFrame, sTransmitStatus); |
| } |
| } |
| |
| void FTDF_rcvFrameTransparent(FTDF_DataLength frameLength, |
| FTDF_Octet *frame, |
| FTDF_Bitmap32 status, |
| FTDF_LinkQuality lqi) |
| { |
| sReceiveFrame.mChannel = sChannel; |
| sReceiveFrame.mLength = frameLength; |
| sReceiveFrame.mPsdu = frame; |
| sReceiveFrame.mLqi = lqi; |
| sReceiveFrame.mPower = otPlatRadioGetRssi(sThreadInstance); |
| |
| if (sRadioState != OT_RADIO_STATE_DISABLED) |
| { |
| sRadioState = OT_RADIO_STATE_RECEIVE; |
| } |
| |
| if ((sReceiveFrame.mPsdu[0] & IEEE802154_FRAME_TYPE_MASK) == IEEE802154_FRAME_TYPE_ACK && |
| ((sReceiveFrame.mPsdu[0] & IEEE802154_FRAME_PENDING) != 0)) |
| { |
| sFramePending = true; |
| } |
| |
| if (status == FTDF_TRANSPARENT_RCV_SUCCESSFUL) |
| { |
| otPlatRadioReceiveDone(sThreadInstance, &sReceiveFrame, OT_ERROR_NONE); |
| } |
| else |
| { |
| otPlatRadioReceiveDone(sThreadInstance, &sReceiveFrame, OT_ERROR_ABORT); |
| } |
| |
| } |
| |
| int8_t otPlatRadioGetReceiveSensitivity(otInstance *aInstance) |
| { |
| (void)aInstance; |
| return DA15000_RECEIVE_SENSITIVITY; |
| } |