blob: 2f54791cee9833c5c65033bed8aa30f16903b9be [file] [log] [blame]
/*
* 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/platform/alarm-milli.h>
#include <openthread/platform/radio.h>
#include <string.h>
#include "platform-da15000.h"
#include "common/logging.hpp"
#include "utils/code_utils.h"
#include "ad_ftdf.h"
#include "ad_ftdf_phy_api.h"
#include "hw_otpc.h"
#include "hw_rf.h"
#include "internal.h"
// clang-format off
#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 RADIO_DEFAULT_CHANNEL (11)
#define RADIO_EUI64_TABLE_SIZE (8)
#define RADIO_FRAMES_BUFFER_SIZE (32)
// clang-format on
enum
{
DA15000_RECEIVE_SENSITIVITY = -100, // dBm
};
static ftdf_dbm sRssiReal = -100; // Initialize with the worst power
static otInstance * sThreadInstance;
static otRadioState sRadioState = OT_RADIO_STATE_DISABLED;
static otRadioFrame sReceiveFrame[RADIO_FRAMES_BUFFER_SIZE];
static otRadioFrame *sReceiveFrameAck;
static otRadioFrame sTransmitFrame;
static otError sTransmitStatus;
static bool sAckFrame = false;
static bool sDropFrame = false;
static bool sRadioPromiscuous = false;
static bool sTransmitDoneFrame = false;
static uint8_t sChannel = RADIO_DEFAULT_CHANNEL;
static uint8_t sEnableRX = 0;
static int8_t sTxPower = OPENTHREAD_CONFIG_DEFAULT_TRANSMIT_POWER;
static uint8_t sReadFrame = 0;
static uint8_t sWriteFrame = 0;
static uint32_t sSleepInitDelay = 0;
static uint8_t sEui64[RADIO_EUI64_TABLE_SIZE];
static uint8_t sReceivePsdu[RADIO_FRAMES_BUFFER_SIZE][OT_RADIO_FRAME_MAX_SIZE];
static uint8_t sTransmitPsdu[OT_RADIO_FRAME_MAX_SIZE];
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();
sEui64[0] = 0x80; // 80-EA-CA is for Dialog Semiconductor
sEui64[1] = 0xEA;
sEui64[2] = 0xCA;
sEui64[3] = (*factoryTestId >> 8) & 0xff;
sEui64[4] = (*factoryTestTimeStamp >> 24) & 0xff;
sEui64[5] = (*factoryTestTimeStamp >> 16) & 0xff;
sEui64[6] = (*factoryTestTimeStamp >> 8) & 0xff;
sEui64[7] = *factoryTestTimeStamp & 0xff;
hw_otpc_manual_read_off();
hw_otpc_disable();
}
void da15000RadioInit(void)
{
uint16_t ptr;
/* 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 = RADIO_DEFAULT_CHANNEL;
sTransmitFrame.mPsdu = sTransmitPsdu;
for (ptr = 0; ptr != RADIO_FRAMES_BUFFER_SIZE; ptr++)
{
sReceiveFrame[ptr].mPsdu = &sReceivePsdu[ptr][0];
}
otLogInfoPlat(sThreadInstance, "Radio initialized", NULL);
}
void otPlatRadioGetIeeeEui64(otInstance *aInstance, uint8_t *aIeeeEui64)
{
(void)aInstance;
memcpy(aIeeeEui64, sEui64, RADIO_EUI64_TABLE_SIZE);
}
void otPlatRadioSetPanId(otInstance *aInstance, uint16_t aPanid)
{
otLogInfoPlat(aInstance, "Set PanId: %X", aPanid);
ftdf_set_value(FTDF_PIB_PAN_ID, &aPanid);
}
void otPlatRadioSetExtendedAddress(otInstance *aInstance, const otExtAddress *aAddress)
{
otLogInfoPlat(aInstance, "Set Extended Address: %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]);
ftdf_set_value(FTDF_PIB_EXTENDED_ADDRESS, aAddress->m8);
}
void otPlatRadioSetShortAddress(otInstance *aInstance, uint16_t aAddress)
{
otLogInfoPlat(aInstance, "Set Short Address: %X", aAddress);
ftdf_set_value(FTDF_PIB_SHORT_ADDRESS, &aAddress);
}
otError otPlatRadioEnable(otInstance *aInstance)
{
ftdf_bitmap32_t options;
otError error = OT_ERROR_NONE;
uint8_t modeCCA;
otEXPECT_ACTION(sRadioState == OT_RADIO_STATE_DISABLED, error = OT_ERROR_INVALID_STATE);
sThreadInstance = aInstance;
sEnableRX = 1;
ftdf_set_value(FTDF_PIB_RX_ON_WHEN_IDLE, &sEnableRX);
ftdf_set_value(FTDF_PIB_CURRENT_CHANNEL, &sChannel);
modeCCA = FTDF_CCA_MODE_2;
ftdf_set_value(FTDF_PIB_CCA_MODE, &modeCCA);
options = FTDF_TRANSPARENT_ENABLE_FCS_GENERATION;
options |= FTDF_TRANSPARENT_WAIT_FOR_ACK;
options |= FTDF_TRANSPARENT_AUTO_ACK;
ftdf_enable_transparent_mode(FTDF_TRUE, options);
otPlatRadioSetPromiscuous(aInstance, false);
otLogDebgPlat(aInstance, "Radio state: OT_RADIO_STATE_SLEEP", NULL);
sRadioState = OT_RADIO_STATE_SLEEP;
exit:
return error;
}
otError otPlatRadioDisable(otInstance *aInstance)
{
sEnableRX = 0;
ftdf_set_value(FTDF_PIB_RX_ON_WHEN_IDLE, &sEnableRX);
ad_ftdf_sleep_when_possible(FTDF_TRUE);
ftdf_fppr_reset();
otLogDebgPlat(aInstance, "Radio state: OT_RADIO_STATE_DISABLED", NULL);
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)
{
otError error = OT_ERROR_NONE;
if (sRadioState == OT_RADIO_STATE_RECEIVE && sSleepInitDelay == 0)
{
sSleepInitDelay = otPlatAlarmMilliGetNow();
return OT_ERROR_NONE;
}
else if ((otPlatAlarmMilliGetNow() - sSleepInitDelay) < dg_configINITIAL_SLEEP_DELAY_TIME)
{
return OT_ERROR_NONE;
}
otEXPECT_ACTION(sRadioState == OT_RADIO_STATE_RECEIVE, error = OT_ERROR_INVALID_STATE);
otLogDebgPlat(aInstance, "Radio state: OT_RADIO_STATE_SLEEP", NULL);
sRadioState = OT_RADIO_STATE_SLEEP;
sEnableRX = 0;
ftdf_set_value(FTDF_PIB_RX_ON_WHEN_IDLE, &sEnableRX);
ad_ftdf_sleep_when_possible(FTDF_TRUE);
exit:
return error;
}
otError otPlatRadioReceive(otInstance *aInstance, uint8_t aChannel)
{
otError error = OT_ERROR_NONE;
otEXPECT_ACTION(sRadioState != OT_RADIO_STATE_DISABLED, error = OT_ERROR_INVALID_STATE);
ad_ftdf_wake_up();
sEnableRX = 0;
ftdf_set_value(FTDF_PIB_RX_ON_WHEN_IDLE, &sEnableRX);
sChannel = aChannel;
ftdf_set_value(FTDF_PIB_CURRENT_CHANNEL, &aChannel);
sEnableRX = 1;
ftdf_set_value(FTDF_PIB_RX_ON_WHEN_IDLE, &sEnableRX);
otLogDebgPlat(aInstance, "Radio state: OT_RADIO_STATE_RECEIVE", NULL);
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)
{
otError error = OT_ERROR_NONE;
uint8_t entry;
uint8_t entryIdx;
// check if address already stored
otEXPECT(!ftdf_fppr_lookup_short_address(aShortAddress, &entry, &entryIdx));
otEXPECT_ACTION(ftdf_fppr_get_free_short_address(&entry, &entryIdx), error = OT_ERROR_NO_BUFS);
otLogDebgPlat(aInstance, "Add ShortAddress entry: %d", entry);
ftdf_fppr_set_short_address(entry, entryIdx, aShortAddress);
ftdf_fppr_set_short_address_valid(entry, entryIdx, FTDF_TRUE);
exit:
return error;
}
otError otPlatRadioAddSrcMatchExtEntry(otInstance *aInstance, const otExtAddress *aExtAddress)
{
otError error = OT_ERROR_NONE;
uint8_t entry;
ftdf_ext_address_t addr;
uint32_t addrL;
uint64_t addrH;
addrL =
(aExtAddress->m8[3] << 24) | (aExtAddress->m8[2] << 16) | (aExtAddress->m8[1] << 8) | (aExtAddress->m8[0] << 0);
addrH =
(aExtAddress->m8[7] << 24) | (aExtAddress->m8[6] << 16) | (aExtAddress->m8[5] << 8) | (aExtAddress->m8[4] << 0);
addr = addrL | (addrH << 32);
// check if address already stored
otEXPECT(!ftdf_fppr_lookup_ext_address(addr, &entry));
otEXPECT_ACTION(ftdf_fppr_get_free_ext_address(&entry), error = OT_ERROR_NO_BUFS);
otLogDebgPlat(aInstance, "Add ExtAddress entry: %d", entry);
ftdf_fppr_set_ext_address(entry, addr);
ftdf_fppr_set_ext_address_valid(entry, FTDF_TRUE);
exit:
return error;
}
otError otPlatRadioClearSrcMatchShortEntry(otInstance *aInstance, const uint16_t aShortAddress)
{
otError error = OT_ERROR_NONE;
uint8_t entry;
uint8_t entryIdx;
otEXPECT_ACTION(ftdf_fppr_lookup_short_address(aShortAddress, &entry, &entryIdx), error = OT_ERROR_NO_ADDRESS);
otLogDebgPlat(aInstance, "Clear ShortAddress entry: %d", entry);
ftdf_fppr_set_short_address(entry, entryIdx, 0);
ftdf_fppr_set_short_address_valid(entry, entryIdx, FTDF_FALSE);
exit:
return error;
}
otError otPlatRadioClearSrcMatchExtEntry(otInstance *aInstance, const otExtAddress *aExtAddress)
{
otError error = OT_ERROR_NONE;
uint8_t entry;
ftdf_ext_address_t addr;
uint32_t addrL;
uint64_t addrH;
addrL =
(aExtAddress->m8[3] << 24) | (aExtAddress->m8[2] << 16) | (aExtAddress->m8[1] << 8) | (aExtAddress->m8[0] << 0);
addrH =
(aExtAddress->m8[7] << 24) | (aExtAddress->m8[6] << 16) | (aExtAddress->m8[5] << 8) | (aExtAddress->m8[4] << 0);
addr = addrL | (addrH << 32);
otEXPECT_ACTION(ftdf_fppr_lookup_ext_address(addr, &entry), error = OT_ERROR_NO_ADDRESS);
otLogDebgPlat(aInstance, "Clear ExtAddress entry: %d", entry);
ftdf_fppr_set_ext_address(entry, 0);
ftdf_fppr_set_ext_address_valid(entry, FTDF_FALSE);
exit:
return error;
}
void otPlatRadioClearSrcMatchShortEntries(otInstance *aInstance)
{
uint8_t i, j;
otLogDebgPlat(aInstance, "Clear ShortAddress entries", NULL);
for (i = 0; i < FTDF_FPPR_TABLE_ENTRIES; i++)
{
for (j = 0; j < 4; j++)
{
if (ftdf_fppr_get_short_address_valid(i, j))
{
ftdf_fppr_set_short_address_valid(i, j, FTDF_FALSE);
}
}
}
}
void otPlatRadioClearSrcMatchExtEntries(otInstance *aInstance)
{
uint8_t i;
otLogDebgPlat(aInstance, "Clear ExtAddress entries", NULL);
for (i = 0; i < FTDF_FPPR_TABLE_ENTRIES; i++)
{
if (ftdf_fppr_get_ext_address_valid(i))
{
ftdf_fppr_set_ext_address_valid(i, FTDF_FALSE);
}
}
}
otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *aInstance)
{
(void)aInstance;
return &sTransmitFrame;
}
otError otPlatRadioTransmit(otInstance *aInstance, otRadioFrame *aFrame)
{
otError error = OT_ERROR_NONE;
otEXPECT_ACTION(sRadioState != OT_RADIO_STATE_DISABLED, error = OT_ERROR_INVALID_STATE);
otLogDebgPlat(aInstance, "Radio start transmit: %d bytes on channel: %d", aFrame->mLength, aFrame->mChannel);
ad_ftdf_send_frame_simple(aFrame->mLength, aFrame->mPsdu, aFrame->mChannel, 0, FTDF_TRUE);
otLogDebgPlat(aInstance, "Radio state: OT_RADIO_STATE_TRANSMIT", NULL);
sRadioState = OT_RADIO_STATE_TRANSMIT;
otPlatRadioTxStarted(aInstance, aFrame);
exit:
return error;
}
int8_t otPlatRadioGetRssi(otInstance *aInstance)
{
(void)aInstance;
return sRssiReal;
}
otRadioCaps otPlatRadioGetCaps(otInstance *aInstance)
{
(void)aInstance;
return OT_RADIO_CAPS_ACK_TIMEOUT | OT_RADIO_CAPS_TRANSMIT_RETRIES | OT_RADIO_CAPS_CSMA_BACKOFF;
}
bool otPlatRadioGetPromiscuous(otInstance *aInstance)
{
(void)aInstance;
return sRadioPromiscuous;
}
void otPlatRadioSetPromiscuous(otInstance *aInstance, bool aEnable)
{
otLogInfoPlat(aInstance, "Set Promiscuous: %d", aEnable ? 1 : 0);
ftdf_set_value(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;
}
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)
{
otLogInfoPlat(aInstance, "Set DefaultTxPower: %d", aPower);
sTxPower = aPower;
ftdf_set_value(FTDF_PIB_TX_POWER, &aPower);
return OT_ERROR_NONE;
}
void da15000RadioProcess(otInstance *aInstance)
{
ftdf_frame_header_t frameHeader;
if (sReadFrame != sWriteFrame)
{
ftdf_get_frame_header(sReceiveFrame[sReadFrame].mPsdu, &frameHeader);
otLogDebgPlat(aInstance, "Radio received: %d bytes", sReceiveFrame[sReadFrame].mLength);
if (frameHeader.frame_type == FTDF_ACKNOWLEDGEMENT_FRAME)
{
sReceiveFrameAck = &sReceiveFrame[sReadFrame];
sAckFrame = true;
}
otPlatRadioReceiveDone(sThreadInstance, &sReceiveFrame[sReadFrame], OT_ERROR_NONE);
sReadFrame = (sReadFrame + 1) % RADIO_FRAMES_BUFFER_SIZE;
sDropFrame = false;
}
if (sTransmitDoneFrame)
{
otLogDebgPlat(aInstance, "Radio transmit status: %s", otThreadErrorToString(sTransmitStatus));
ftdf_get_frame_header(sTransmitFrame.mPsdu, &frameHeader);
if ((frameHeader.options & FTDF_OPT_ACK_REQUESTED) == 0 || sTransmitStatus != OT_ERROR_NONE)
{
sRadioState = OT_RADIO_STATE_RECEIVE;
otPlatRadioTxDone(sThreadInstance, &sTransmitFrame, NULL, sTransmitStatus);
}
else
{
otEXPECT(sAckFrame);
sRadioState = OT_RADIO_STATE_RECEIVE;
otPlatRadioTxDone(sThreadInstance, &sTransmitFrame, sReceiveFrameAck, sTransmitStatus);
sAckFrame = false;
}
sTransmitDoneFrame = false;
otLogDebgPlat(aInstance, "Radio state: OT_RADIO_STATE_RECEIVE", NULL);
}
exit:
return;
}
void ftdf_send_frame_transparent_confirm(void *handle, ftdf_bitmap32_t 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;
}
sTransmitDoneFrame = true;
}
static void radioRssiCalc(ftdf_link_quality_t link_quality)
{
sRssiReal = (ftdf_dbm)((0.5239 * (float)link_quality) - 114.8604);
}
void ftdf_rcv_frame_transparent(ftdf_data_length_t frame_length,
ftdf_octet_t * frame,
ftdf_bitmap32_t status,
ftdf_link_quality_t link_quality)
{
otEXPECT(frame_length <= OT_RADIO_FRAME_MAX_SIZE);
otEXPECT(sRadioState != OT_RADIO_STATE_DISABLED);
otEXPECT(!sDropFrame);
if (status == FTDF_TRANSPARENT_RCV_SUCCESSFUL)
{
radioRssiCalc(link_quality);
if (otPlatRadioGetPromiscuous(sThreadInstance))
{
// Timestamp
sReceiveFrame[sWriteFrame].mInfo.mRxInfo.mMsec = otPlatAlarmMilliGetNow();
sReceiveFrame[sWriteFrame].mInfo.mRxInfo.mUsec = 0; // Don't support microsecond timer for now.
}
sReceiveFrame[sWriteFrame].mChannel = sChannel;
sReceiveFrame[sWriteFrame].mLength = frame_length;
sReceiveFrame[sWriteFrame].mInfo.mRxInfo.mLqi = OT_RADIO_LQI_NONE;
sReceiveFrame[sWriteFrame].mInfo.mRxInfo.mRssi = otPlatRadioGetRssi(sThreadInstance);
memcpy(sReceiveFrame[sWriteFrame].mPsdu, frame, frame_length);
sWriteFrame = (sWriteFrame + 1) % RADIO_FRAMES_BUFFER_SIZE;
if (sWriteFrame == sReadFrame)
{
sDropFrame = true;
}
}
exit:
return;
}
int8_t otPlatRadioGetReceiveSensitivity(otInstance *aInstance)
{
(void)aInstance;
return DA15000_RECEIVE_SENSITIVITY;
}