blob: 1f951e18b57452e6dfeeecbd8607f66015f7dfcd [file] [log] [blame]
/*
* Copyright (c) 2017, 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 "asf.h"
#include "phy.h"
#include <openthread/config.h>
#include <openthread/platform/alarm-milli.h>
#include <openthread/platform/radio.h>
#include "platform-samr21.h"
#include "common/logging.hpp"
#include "utils/code_utils.h"
enum
{
IEEE802154_FRAME_TYPE_ACK = 0x2,
IEEE802154_DSN_OFFSET = 2,
IEEE802154_FRAME_PENDING = 1 << 4,
IEEE802154_ACK_REQUEST = 1 << 5,
IEEE802154_ACK_LENGTH = 5,
IEEE802154_FCS_SIZE = 2
};
enum
{
SAMR21_RECEIVE_SENSITIVITY = -99 // dBm
};
static otRadioFrame sTransmitFrame;
static uint8_t sTransmitPsdu[OT_RADIO_FRAME_MAX_SIZE + 1];
static otRadioFrame sReceiveFrame;
static bool sSleep = false;
static bool sRxEnable = false;
static bool sTxDone = false;
static bool sRxDone = false;
static uint8_t sTxStatus = PHY_STATUS_SUCCESS;
static int8_t sPower = OPENTHREAD_CONFIG_DEFAULT_TRANSMIT_POWER;
static otRadioState sState = OT_RADIO_STATE_DISABLED;
static bool sPromiscuous = false;
static uint8_t sChannel = 0xFF;
static int8_t sMaxRssi;
static uint32_t sScanStartTime;
static uint16_t sScanDuration;
static bool sStartScan = false;
static int8_t sTxPowerTable[] = {4, 4, 3, 3, 3, 2, 1, 0, -1, -2, -3, -4, -6, -8, -12, -17};
/*******************************************************************************
* Static
******************************************************************************/
static void radioSleep()
{
if (!sSleep)
{
PHY_SetRxState(false);
PHY_Sleep();
sSleep = true;
sRxEnable = false;
}
}
static void radioWakeup()
{
if (sSleep)
{
PHY_Wakeup();
}
}
static void radioRxEnable()
{
if (sSleep)
{
PHY_Wakeup();
sSleep = false;
}
if (!sRxEnable)
{
PHY_SetRxState(true);
sRxEnable = true;
}
}
static void radioTrxOff()
{
if (sSleep)
{
PHY_Wakeup();
}
else if (sRxEnable)
{
PHY_SetRxState(false);
}
}
static void radioRestore()
{
if (sSleep)
{
PHY_Sleep();
}
else if (sRxEnable)
{
PHY_SetRxState(true);
}
}
static void setTxPower(uint8_t aPower)
{
if (aPower != sPower)
{
uint8_t i;
for (i = 0; i < (sizeof(sTxPowerTable) / sizeof(*sTxPowerTable) - 1); i++)
{
if (aPower >= sTxPowerTable[i])
{
break;
}
}
otLogDebgPlat("Radio set tx power: %d, %d", aPower, i);
radioTrxOff();
PHY_SetTxPower(i);
radioRestore();
sPower = aPower;
}
}
static void setChannel(uint8_t aChannel)
{
if (aChannel != sChannel)
{
otLogDebgPlat("Radio set channel: %d", aChannel);
radioTrxOff();
PHY_SetChannel(aChannel);
radioRestore();
sChannel = aChannel;
}
}
static void handleEnergyScan()
{
if (sStartScan)
{
if ((otPlatAlarmMilliGetNow() - sScanStartTime) < sScanDuration)
{
int8_t curRssi = PHY_EdReq();
if (curRssi > sMaxRssi)
{
sMaxRssi = curRssi;
}
}
else
{
sStartScan = false;
otPlatRadioEnergyScanDone(sInstance, sMaxRssi);
radioRestore();
}
}
}
static void handleRx(void)
{
if (sRxDone)
{
sRxDone = false;
#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE
#error Time sync requires the timestamp of SFD rather than that of rx done!
#else
if (otPlatRadioGetPromiscuous(sInstance))
#endif
{
// The current driver only supports milliseconds resolution.
sReceiveFrame.mInfo.mRxInfo.mTimestamp = otPlatAlarmMilliGetNow() * 1000;
}
// TODO Set this flag only when the packet is really acknowledged with frame pending set.
// See https://github.com/openthread/openthread/pull/3785
sReceiveFrame.mInfo.mRxInfo.mAckedWithFramePending = true;
#if OPENTHREAD_CONFIG_DIAG_ENABLE
if (otPlatDiagModeGet())
{
otPlatDiagRadioReceiveDone(sInstance, &sReceiveFrame, OT_ERROR_NONE);
}
else
#endif
{
// signal MAC layer for each received frame if promiscous is enabled
// otherwise only signal MAC layer for non-ACK frame
if (sPromiscuous || sReceiveFrame.mLength > IEEE802154_ACK_LENGTH)
{
otLogDebgPlat("Radio receive done, rssi: %d", sReceiveFrame.mInfo.mRxInfo.mRssi);
otPlatRadioReceiveDone(sInstance, &sReceiveFrame, OT_ERROR_NONE);
}
}
}
}
static void handleTx(void)
{
otError otStatus;
otRadioFrame ackFrame;
uint8_t psdu[IEEE802154_ACK_LENGTH];
if (sTxDone)
{
sTxDone = false;
// SAMR21 RF doesn't provide ACK frame, generate it manually
ackFrame.mPsdu = psdu;
ackFrame.mLength = IEEE802154_ACK_LENGTH;
ackFrame.mPsdu[0] = IEEE802154_FRAME_TYPE_ACK;
ackFrame.mPsdu[1] = 0;
ackFrame.mPsdu[2] = sTransmitFrame.mPsdu[IEEE802154_DSN_OFFSET];
switch (sTxStatus)
{
// This is WA to handle pending bit in ACK.
// SAMR21 phy driver doesn't provide pending status and returns
// PHY_STATUS_ERROR. This status is returned also RF transaction not yet
// finished. This situation should not happens. Currently PHY_STATUS_ERROR
// is only way to detect pending bit.
case PHY_STATUS_ERROR:
ackFrame.mPsdu[0] |= IEEE802154_FRAME_PENDING;
// fall through
case PHY_STATUS_SUCCESS:
otStatus = OT_ERROR_NONE;
break;
case PHY_STATUS_CHANNEL_ACCESS_FAILURE:
otStatus = OT_ERROR_CHANNEL_ACCESS_FAILURE;
break;
case PHY_STATUS_NO_ACK:
otStatus = OT_ERROR_NO_ACK;
break;
default:
otStatus = OT_ERROR_ABORT;
break;
}
sState = OT_RADIO_STATE_RECEIVE;
#if OPENTHREAD_CONFIG_DIAG_ENABLE
if (otPlatDiagModeGet())
{
otPlatDiagRadioTransmitDone(sInstance, &sTransmitFrame, otStatus);
}
else
#endif
{
otLogDebgPlat("Radio transmit done, status: %d", otStatus);
otRadioFrame *ackFramePtr = &ackFrame;
if (((sTransmitFrame.mPsdu[0] & IEEE802154_ACK_REQUEST) == 0) || (otStatus != OT_ERROR_NONE))
{
ackFramePtr = NULL;
}
otPlatRadioTxDone(sInstance, &sTransmitFrame, ackFramePtr, otStatus);
}
}
}
/*******************************************************************************
* PHY
******************************************************************************/
void PHY_DataInd(PHY_DataInd_t *ind)
{
sReceiveFrame.mPsdu = ind->data;
sReceiveFrame.mLength = ind->size + IEEE802154_FCS_SIZE;
sReceiveFrame.mInfo.mRxInfo.mRssi = ind->rssi;
sRxDone = true;
}
void PHY_DataConf(uint8_t status)
{
sTxStatus = status;
sTxDone = true;
}
/*******************************************************************************
* Platform
******************************************************************************/
void samr21RadioInit(void)
{
sTransmitFrame.mLength = 0;
sTransmitFrame.mPsdu = sTransmitPsdu + 1;
sReceiveFrame.mLength = 0;
sReceiveFrame.mPsdu = NULL;
PHY_Init();
sal_init();
}
void samr21RadioProcess(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
PHY_TaskHandler();
handleEnergyScan();
handleRx();
handleTx();
}
uint32_t samr21RadioRandomGet(void)
{
uint32_t result;
radioWakeup();
result = PHY_RandomReq() << 16 | PHY_RandomReq();
radioRestore();
return result;
}
void samr21RadioRandomGetTrue(uint8_t *aOutput, uint16_t aOutputLength)
{
radioWakeup();
for (uint16_t i = 0; i < aOutputLength / sizeof(uint16_t); i++)
{
*((uint16_t *)aOutput) = PHY_RandomReq();
aOutput += sizeof(uint16_t);
}
for (uint16_t i = 0; i < aOutputLength % sizeof(uint16_t); i++)
{
aOutput[i] = PHY_RandomReq();
}
radioRestore();
}
/*******************************************************************************
* Radio
******************************************************************************/
otRadioState otPlatRadioGetState(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
return sState;
}
void otPlatRadioGetIeeeEui64(otInstance *aInstance, uint8_t *aIeeeEui64)
{
samr21GetIeeeEui64(aInstance, aIeeeEui64);
}
void otPlatRadioSetPanId(otInstance *aInstance, uint16_t aPanId)
{
OT_UNUSED_VARIABLE(aInstance);
otLogDebgPlat("Set Pan ID: 0x%04X", aPanId);
radioTrxOff();
PHY_SetPanId(aPanId);
radioRestore();
}
void otPlatRadioSetExtendedAddress(otInstance *aInstance, const otExtAddress *aAddress)
{
OT_UNUSED_VARIABLE(aInstance);
radioTrxOff();
PHY_SetIEEEAddr((uint8_t *)aAddress);
radioRestore();
}
void otPlatRadioSetShortAddress(otInstance *aInstance, uint16_t aAddress)
{
OT_UNUSED_VARIABLE(aInstance);
radioTrxOff();
PHY_SetShortAddr(aAddress);
radioRestore();
}
bool otPlatRadioIsEnabled(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
return (sState != OT_RADIO_STATE_DISABLED);
}
otError otPlatRadioEnable(otInstance *aInstance)
{
otLogDebgPlat("Radio enable");
if (!otPlatRadioIsEnabled(aInstance))
{
radioSleep();
sState = OT_RADIO_STATE_SLEEP;
}
return OT_ERROR_NONE;
}
otError otPlatRadioDisable(otInstance *aInstance)
{
otLogDebgPlat("Radio disable");
if (otPlatRadioIsEnabled(aInstance))
{
radioSleep();
sState = OT_RADIO_STATE_DISABLED;
}
return OT_ERROR_NONE;
}
otError otPlatRadioSleep(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
otLogDebgPlat("Radio sleep");
otError error = OT_ERROR_NONE;
otEXPECT_ACTION(sState == OT_RADIO_STATE_SLEEP || sState == OT_RADIO_STATE_RECEIVE, error = OT_ERROR_INVALID_STATE);
radioSleep();
sState = OT_RADIO_STATE_SLEEP;
exit:
return error;
}
otError otPlatRadioReceive(otInstance *aInstance, uint8_t aChannel)
{
OT_UNUSED_VARIABLE(aInstance);
otLogDebgPlat("Radio receive, channel: %d", aChannel);
otError error = OT_ERROR_NONE;
otEXPECT_ACTION(sState != OT_RADIO_STATE_DISABLED, error = OT_ERROR_INVALID_STATE);
setChannel(aChannel);
radioRxEnable();
sState = OT_RADIO_STATE_RECEIVE;
exit:
return error;
}
otError otPlatRadioTransmit(otInstance *aInstance, otRadioFrame *aFrame)
{
otError error = OT_ERROR_NONE;
OT_UNUSED_VARIABLE(aInstance);
otLogDebgPlat("Radio transmit");
otEXPECT_ACTION(sState == OT_RADIO_STATE_RECEIVE, error = OT_ERROR_INVALID_STATE);
setChannel(aFrame->mChannel);
aFrame->mPsdu[-1] = aFrame->mLength - IEEE802154_FCS_SIZE;
PHY_DataReq(&aFrame->mPsdu[-1]);
otPlatRadioTxStarted(aInstance, aFrame);
sState = OT_RADIO_STATE_TRANSMIT;
exit:
return error;
}
otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
return &sTransmitFrame;
}
int8_t otPlatRadioGetRssi(otInstance *aInstance)
{
return sMaxRssi;
}
otRadioCaps otPlatRadioGetCaps(otInstance *aInstance)
{
return OT_RADIO_CAPS_ENERGY_SCAN | OT_RADIO_CAPS_TRANSMIT_RETRIES | OT_RADIO_CAPS_ACK_TIMEOUT;
}
bool otPlatRadioGetPromiscuous(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
return sPromiscuous;
}
void otPlatRadioSetPromiscuous(otInstance *aInstance, bool aEnable)
{
OT_UNUSED_VARIABLE(aInstance);
sPromiscuous = aEnable;
}
void otPlatRadioEnableSrcMatch(otInstance *aInstance, bool aEnable)
{
OT_UNUSED_VARIABLE(aInstance);
OT_UNUSED_VARIABLE(aEnable);
}
otError otPlatRadioAddSrcMatchShortEntry(otInstance *aInstance, const uint16_t aShortAddress)
{
return OT_ERROR_NOT_IMPLEMENTED;
}
otError otPlatRadioAddSrcMatchExtEntry(otInstance *aInstance, const otExtAddress *aExtAddress)
{
return OT_ERROR_NOT_IMPLEMENTED;
}
otError otPlatRadioClearSrcMatchShortEntry(otInstance *aInstance, const uint16_t aShortAddress)
{
return OT_ERROR_NOT_IMPLEMENTED;
}
otError otPlatRadioClearSrcMatchExtEntry(otInstance *aInstance, const otExtAddress *aExtAddress)
{
return OT_ERROR_NOT_IMPLEMENTED;
}
void otPlatRadioClearSrcMatchShortEntries(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
}
void otPlatRadioClearSrcMatchExtEntries(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
}
otError otPlatRadioEnergyScan(otInstance *aInstance, uint8_t aScanChannel, uint16_t aScanDuration)
{
OT_UNUSED_VARIABLE(aInstance);
sScanStartTime = otPlatAlarmMilliGetNow();
sScanDuration = aScanDuration;
sMaxRssi = PHY_EdReq();
sStartScan = true;
return OT_ERROR_NONE;
}
otError otPlatRadioGetTransmitPower(otInstance *aInstance, int8_t *aPower)
{
otError error = OT_ERROR_NONE;
otEXPECT_ACTION(aPower != NULL, error = OT_ERROR_INVALID_ARGS);
*aPower = sPower;
exit:
return error;
}
otError otPlatRadioSetTransmitPower(otInstance *aInstance, int8_t aPower)
{
OT_UNUSED_VARIABLE(aInstance);
otLogDebgPlat("Radio set default TX power: %d", aPower);
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);
return SAMR21_RECEIVE_SENSITIVITY;
}