blob: f95df25952428202fb1f32aa302645d325d33858 [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.
*
*/
/* Openthread configuration */
#include OPENTHREAD_PROJECT_CORE_CONFIG_FILE
/* memcpy */
#include "string.h"
/* uMac, MMAC, Radio */
#include "MMAC.h"
#include "MicroSpecific_arm_sdk2.h"
#include "radio.h"
/* Openthread general */
#include <utils/code_utils.h>
#include <openthread/platform/alarm-milli.h>
#include <openthread/platform/diag.h>
#include <openthread/platform/radio.h>
#if USE_RTOS
#include "openthread-system.h"
#endif
extern void BOARD_LedDongleToggle(void);
/* Defines */
#define BIT_SET(arg, posn) ((arg) |= (1ULL << (posn)))
#define BIT_CLR(arg, posn) ((arg) &= ~(1ULL << (posn)))
#define BIT_TST(arg, posn) (!!((arg) & (1ULL << (posn))))
#define ALL_FFs_BYTE (0xFF)
#define K32W_RADIO_MIN_TX_POWER_DBM (-30)
#define K32W_RADIO_MAX_TX_POWER_DBM (15)
#define K32W_RADIO_RX_SENSITIVITY_DBM (-100)
#define K32W_RADIO_DEFAULT_CHANNEL (11)
#define US_PER_SYMBOL (16) /* Duration of a single symbol in [us] */
#define SYMBOLS_TO_US(symbols) ((symbols)*US_PER_SYMBOL)
#define US_TO_MILI_DIVIDER (1000)
#define MAX_FP_ADDRS (10) /* max number of frame pending children */
#define K32W_RX_BUFFERS (8) /* max number of RX buffers */
/* check IEEE Std. 802.15.4 - 2015: Table 8-81 - MAC sublayer constants */
#define MAC_TX_ATTEMPTS (4)
#define MAC_TX_CSMA_MIN_BE (3)
#define MAC_TX_CSMA_MAX_BE (5)
#define MAC_TX_CSMA_MAX_BACKOFFS (4)
/* Structures */
typedef struct
{
uint16_t macAddress;
uint16_t panId;
} fpNeighShortAddr;
typedef struct
{
uint32_t u32L;
uint32_t u32H;
} extMacAddr;
typedef struct
{
extMacAddr extAddr;
uint16_t panId;
} fpNeighExtAddr;
typedef struct
{
tsRxFrameFormat *buffer[K32W_RX_BUFFERS];
uint8_t head;
uint8_t tail;
bool isFull;
} rxRingBuffer;
typedef enum
{
kFcfSize = sizeof(uint16_t),
kDsnSize = sizeof(uint8_t),
kSecurityControlSize = sizeof(uint8_t),
kFrameCounterSize = sizeof(uint32_t),
kKeyIndexSize = sizeof(uint8_t),
kMacFcfLowOffset = 0, /* Offset of FCF first byte inside Mac Hdr */
kMacFrameDataReq = 4,
kFcfTypeBeacon = 0,
kFcfTypeMacData = 1,
kFcfTypeAck = 2,
kFcfTypeMacCommand = 3,
kFcfMacFrameTypeMask = 7 << 0,
kFcfAckRequest = 1 << 5,
kFcfPanidCompression = 1 << 6,
kFcfSeqNbSuppresssion = 1 << 8,
kFcfDstAddrNone = 0 << 10,
kFcfDstAddrShort = 2 << 10,
kFcfDstAddrExt = 3 << 10,
kFcfDstAddrMask = 3 << 10,
kFcfSrcAddrNone = 0 << 14,
kFcfSrcAddrShort = 2 << 14,
kFcfSrcAddrExt = 3 << 14,
kFcfSrcAddrMask = 3 << 14,
kSecLevelMask = 7 << 0,
kFrameCounterSuppression = 1 << 5,
kKeyIdMode0 = 0 << 3,
kKeyIdMode1 = 1 << 3,
kKeyIdMode2 = 2 << 3,
kKeyIdMode3 = 3 << 3,
kKeyIdModeMask = 3 << 3,
kKeySourceSizeMode0 = 0,
kKeySourceSizeMode1 = 0,
kKeySourceSizeMode2 = 4,
kKeySourceSizeMode3 = 8,
} macHdr;
typedef enum
{
macToOtFrame, /* RX */
otToMacFrame, /* TX */
} frameConversionType;
/* Private functions declaration */
static void K32WISR(uint32_t u32IntBitmap);
static void K32WProcessMacHeader(tsRxFrameFormat *aRxFrame);
static void K32WProcessRxFrames(otInstance *aInstance);
static void K32WProcessTxFrame(otInstance *aInstance);
static bool K32WCheckIfFpRequired(tsRxFrameFormat *aRxFrame);
static bool_t K32WIsDataReq(tsRxFrameFormat *aRxFrame);
static otError K32WFrameConversion(tsRxFrameFormat * aMacFormatFrame,
otRadioFrame * aOtFrame,
frameConversionType convType);
static void K32WCopy(uint8_t *aFieldValue, uint8_t **aPsdu, uint8_t copySize, frameConversionType convType);
static void K32WResetRxRingBuffer(rxRingBuffer *aRxRing);
static void K32WPushRxRingBuffer(rxRingBuffer *aRxRing, tsRxFrameFormat *aRxFrame);
static tsRxFrameFormat *K32WPopRxRingBuffer(rxRingBuffer *aRxRing);
static bool K32WIsEmptyRxRingBuffer(rxRingBuffer *aRxRing);
static tsRxFrameFormat *K32WGetFrame(tsRxFrameFormat *aRxFrame, uint8_t *aRxFrameIndex);
static void K32WEnableReceive(bool_t isNewFrameNeeded);
static void K32WRestartRx(void);
/* Private variables declaration */
static otRadioState sState = OT_RADIO_STATE_DISABLED;
static otInstance * sInstance; /* Saved OT Instance */
static int8_t sTxPwrLevel; /* Default power is 0 dBm */
static uint8_t sChannel = 0; /* Default channel - must be invalid so it
updates the first time it is set */
static bool_t sIsFpEnabled; /* Frame Pending enabled? */
static uint16_t sPanId; /* PAN ID currently in use */
static uint16_t sShortAddress;
static tsExtAddr sExtAddress;
static uint64_t sCustomExtAddr = 0;
static fpNeighShortAddr sFpShortAddr[MAX_FP_ADDRS]; /* Frame Pending short addresses array */
static uint16_t sFpShortAddrMask; /* Mask - sFpShortAddr valid entries */
static fpNeighExtAddr sFpExtAddr[MAX_FP_ADDRS]; /* Frame Pending extended addresses array */
static uint16_t sFpExtAddrMask; /* Mask - sFpExtAddr is valid */
static rxRingBuffer sRxRing; /* Receive Ring Buffer */
static tsRxFrameFormat sRxFrame[K32W_RX_BUFFERS]; /* RX Buffers */
static tsRxFrameFormat *sRxFrameInProcess; /* RX Frame currently in processing */
static bool_t sIsRxDisabled; /* TRUE if RX was disabled due to no RX bufs */
static uint8_t sRxFrameIndex; /* Index tracking the sRxFrame array */
static teRxOption sRxOpt = E_MMAC_RX_START_NOW | /* RX Options */
E_MMAC_RX_ALIGN_NORMAL | E_MMAC_RX_USE_AUTO_ACK | E_MMAC_RX_NO_MALFORMED |
E_MMAC_RX_NO_FCS_ERROR | E_MMAC_RX_ADDRESS_MATCH;
tsRxFrameFormat sTxMacFrame; /* TX Frame */
static tsRxFrameFormat sRxAckFrame; /* Frame used for keeping the ACK */
static otRadioFrame sRxOtFrame; /* Used for TX/RX frame conversion */
static uint8 sRxData[OT_RADIO_FRAME_MAX_SIZE]; /* mPsdu buffer for sRxOtFrame */
static tsRxFrameFormat *pLastRxFrame;
static bool sRadioInitForLp = FALSE;
static bool sPromiscuousEnable = FALSE;
static bool sTxDone; /* TRUE if a TX frame was sent into the air */
static otError sTxStatus; /* Status of the latest TX operation */
static otRadioFrame sTxOtFrame; /* OT TX Frame to be send */
static uint8_t sTxData[OT_RADIO_FRAME_MAX_SIZE]; /* mPsdu buffer for sTxOtFrame */
/* Stub functions for controlling low power mode */
WEAK void App_AllowDeviceToSleep();
WEAK void App_DisallowDeviceToSleep();
/* Stub functions for controlling LEDs on OT RCP USB dongle */
WEAK void BOARD_LedDongleToggle();
/**
* Stub function used for controlling low power mode
*
*/
WEAK void App_AllowDeviceToSleep()
{
}
/**
* Stub function used for controlling low power mode
*
*/
WEAK void App_DisallowDeviceToSleep()
{
}
/**
* Stub functions for controlling LEDs on OT RCP USB dongle
*
*/
WEAK void BOARD_LedDongleToggle()
{
}
void App_SetCustomEui64(uint8_t *aIeeeEui64)
{
memcpy((uint8_t *)&sCustomExtAddr, aIeeeEui64, sizeof(sCustomExtAddr));
}
void K32WRadioInit(void)
{
/* RX initialization */
memset(sRxFrame, 0, sizeof(tsRxFrameFormat) * K32W_RX_BUFFERS);
sRxFrameIndex = 0;
/* TX initialization */
sTxOtFrame.mPsdu = sTxData;
sRxOtFrame.mPsdu = sRxData;
}
void K32WRadioProcess(otInstance *aInstance)
{
K32WProcessRxFrames(aInstance);
K32WProcessTxFrame(aInstance);
}
otRadioState otPlatRadioGetState(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
return sState;
}
void otPlatRadioGetIeeeEui64(otInstance *aInstance, uint8_t *aIeeeEui64)
{
OT_UNUSED_VARIABLE(aInstance);
if (0 == sCustomExtAddr)
{
tsExtAddr euiAddr;
vMMAC_GetMacAddress(&euiAddr);
memcpy(aIeeeEui64, &euiAddr.u32L, sizeof(uint32_t));
memcpy(aIeeeEui64 + sizeof(uint32_t), &euiAddr.u32H, sizeof(uint32_t));
}
else
{
memcpy(aIeeeEui64, (uint8_t *)&sCustomExtAddr, sizeof(sCustomExtAddr));
}
}
void otPlatRadioSetPanId(otInstance *aInstance, uint16_t aPanId)
{
OT_UNUSED_VARIABLE(aInstance);
sPanId = aPanId;
vMMAC_SetRxPanId(aPanId);
}
void otPlatRadioSetExtendedAddress(otInstance *aInstance, const otExtAddress *aExtAddress)
{
OT_UNUSED_VARIABLE(aInstance);
if (aExtAddress)
{
memcpy(&sExtAddress.u32L, aExtAddress->m8, sizeof(uint32_t));
memcpy(&sExtAddress.u32H, aExtAddress->m8 + sizeof(uint32_t), sizeof(uint32_t));
vMMAC_SetRxExtendedAddr(&sExtAddress);
}
}
void otPlatRadioSetShortAddress(otInstance *aInstance, uint16_t aShortAddress)
{
OT_UNUSED_VARIABLE(aInstance);
sShortAddress = aShortAddress;
vMMAC_SetRxShortAddr(aShortAddress);
}
otError otPlatRadioEnable(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
K32WResetRxRingBuffer(&sRxRing);
sRxFrameIndex = 0;
vMMAC_Enable();
vMMAC_EnableInterrupts(K32WISR);
vMMAC_ConfigureInterruptSources(E_MMAC_INT_TX_COMPLETE | E_MMAC_INT_RX_HEADER | E_MMAC_INT_RX_COMPLETE);
vMMAC_ConfigureRadio();
vMMAC_SetTxParameters(MAC_TX_ATTEMPTS, MAC_TX_CSMA_MIN_BE, MAC_TX_CSMA_MAX_BE, MAC_TX_CSMA_MAX_BACKOFFS);
if (sRadioInitForLp)
{
/* Re-set modem settings after low power exit */
vMMAC_SetChannelAndPower(sChannel, sTxPwrLevel);
vMMAC_SetRxExtendedAddr(&sExtAddress);
vMMAC_SetRxPanId(sPanId);
vMMAC_SetRxShortAddr(sShortAddress);
}
sTxOtFrame.mLength = 0;
sRxOtFrame.mLength = 0;
sInstance = aInstance;
sState = OT_RADIO_STATE_SLEEP;
return OT_ERROR_NONE;
}
otError otPlatRadioDisable(otInstance *aInstance)
{
otError error = OT_ERROR_INVALID_STATE;
otEXPECT(otPlatRadioIsEnabled(aInstance));
K32WResetRxRingBuffer(&sRxRing);
sRxFrameIndex = 0;
vMMAC_Disable();
sState = OT_RADIO_STATE_DISABLED;
error = OT_ERROR_NONE;
exit:
return error;
}
bool otPlatRadioIsEnabled(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
return sState != OT_RADIO_STATE_DISABLED;
}
otError otPlatRadioSleep(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
otError status = OT_ERROR_NONE;
otEXPECT_ACTION(((sState != OT_RADIO_STATE_TRANSMIT) && (sState != OT_RADIO_STATE_DISABLED)),
status = OT_ERROR_INVALID_STATE);
/* The radio has been init and configuration should be restored in otPlatRadioEnable when
exiting low power */
sRadioInitForLp = TRUE;
sState = OT_RADIO_STATE_SLEEP;
vMMAC_RadioToOffAndWait();
App_AllowDeviceToSleep();
exit:
return status;
}
otError otPlatRadioReceive(otInstance *aInstance, uint8_t aChannel)
{
OT_UNUSED_VARIABLE(aInstance);
otError error = OT_ERROR_NONE;
bool_t isNewFrameNeeded = TRUE;
otRadioState tempState = sState;
otEXPECT_ACTION(((sState != OT_RADIO_STATE_TRANSMIT) && (sState != OT_RADIO_STATE_DISABLED)),
error = OT_ERROR_INVALID_STATE);
App_DisallowDeviceToSleep();
/* Check if the channel needs to be changed */
if (sChannel != aChannel)
{
sChannel = aChannel;
/* The state is set to sleep to prevent a lockup caused by an RX interrup firing during
* the radio off command called inside set channel and power */
sState = OT_RADIO_STATE_SLEEP;
vMMAC_SetChannelAndPower(sChannel, sTxPwrLevel);
sState = tempState;
}
if (OT_RADIO_STATE_RECEIVE != sState)
{
sState = OT_RADIO_STATE_RECEIVE;
}
else
{
/* this might happen when the channel is switched
* in the middle of a receive operation */
isNewFrameNeeded = FALSE;
}
K32WEnableReceive(isNewFrameNeeded);
exit:
return error;
}
void otPlatRadioEnableSrcMatch(otInstance *aInstance, bool aEnable)
{
OT_UNUSED_VARIABLE(aInstance);
sIsFpEnabled = aEnable;
}
otError otPlatRadioAddSrcMatchShortEntry(otInstance *aInstance, const uint16_t aShortAddress)
{
OT_UNUSED_VARIABLE(aInstance);
otError error = OT_ERROR_NO_BUFS;
uint8_t idx = 0;
for (; idx < MAX_FP_ADDRS; idx++)
{
if (!BIT_TST(sFpShortAddrMask, idx))
{
sFpShortAddr[idx].panId = sPanId;
sFpShortAddr[idx].macAddress = aShortAddress;
BIT_SET(sFpShortAddrMask, idx);
error = OT_ERROR_NONE;
break;
}
}
return error;
}
otError otPlatRadioAddSrcMatchExtEntry(otInstance *aInstance, const otExtAddress *aExtAddress)
{
OT_UNUSED_VARIABLE(aInstance);
otError error = OT_ERROR_NO_BUFS;
uint8_t idx = 0;
for (; idx < MAX_FP_ADDRS; idx++)
{
if (!BIT_TST(sFpExtAddrMask, idx))
{
sFpExtAddr[idx].panId = sPanId;
memcpy(&sFpExtAddr[idx].extAddr.u32L, aExtAddress->m8, sizeof(uint32_t));
memcpy(&sFpExtAddr[idx].extAddr.u32H, aExtAddress->m8 + sizeof(uint32_t), sizeof(uint32_t));
BIT_SET(sFpExtAddrMask, idx);
error = OT_ERROR_NONE;
break;
}
}
return error;
}
otError otPlatRadioClearSrcMatchShortEntry(otInstance *aInstance, const uint16_t aShortAddress)
{
OT_UNUSED_VARIABLE(aInstance);
otError error = OT_ERROR_NO_ADDRESS;
uint8_t idx = 0;
for (; idx < MAX_FP_ADDRS; idx++)
{
if (BIT_TST(sFpShortAddrMask, idx) && (sFpShortAddr[idx].macAddress == aShortAddress))
{
BIT_CLR(sFpShortAddrMask, idx);
error = OT_ERROR_NONE;
break;
}
}
return error;
}
otError otPlatRadioClearSrcMatchExtEntry(otInstance *aInstance, const otExtAddress *aExtAddress)
{
OT_UNUSED_VARIABLE(aInstance);
otError error = OT_ERROR_NO_ADDRESS;
uint8_t idx = 0;
for (; idx < MAX_FP_ADDRS; idx++)
{
if (BIT_TST(sFpExtAddrMask, idx) && !memcmp(&sFpExtAddr[idx].extAddr.u32L, aExtAddress->m8, sizeof(uint32_t)) &&
!memcmp(&sFpExtAddr[idx].extAddr.u32H, aExtAddress->m8 + sizeof(uint32_t), sizeof(uint32_t)))
{
BIT_CLR(sFpExtAddrMask, idx);
error = OT_ERROR_NONE;
break;
}
}
return error;
}
void otPlatRadioClearSrcMatchShortEntries(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
sFpShortAddrMask = 0;
}
void otPlatRadioClearSrcMatchExtEntries(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
sFpExtAddrMask = 0;
}
otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
return &sTxOtFrame;
}
otError otPlatRadioTransmit(otInstance *aInstance, otRadioFrame *aFrame)
{
otError error = OT_ERROR_NONE;
teTxOption eOptions = E_MMAC_TX_START_NOW | E_MMAC_TX_USE_AUTO_ACK;
otEXPECT_ACTION(OT_RADIO_STATE_RECEIVE == sState, error = OT_ERROR_INVALID_STATE);
/* go to TX state */
sState = OT_RADIO_STATE_TRANSMIT;
sTxStatus = OT_ERROR_NONE;
/* set tx channel */
if (sChannel != aFrame->mChannel)
{
vMMAC_SetChannelAndPower(aFrame->mChannel, sTxPwrLevel);
}
if (aFrame->mInfo.mTxInfo.mCsmaCaEnabled)
{
eOptions |= E_MMAC_TX_USE_CCA;
}
K32WFrameConversion(&sTxMacFrame, aFrame, otToMacFrame);
/* stop rx is handled by uMac tx function */
vMMAC_StartMacTransmit(&sTxMacFrame.sFrameBody, eOptions);
/* Set RX buffer pointer for ACK */
vMMAC_SetRxFrame(&sRxAckFrame);
otPlatRadioTxStarted(aInstance, aFrame);
exit:
return error;
}
int8_t otPlatRadioGetRssi(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
int8_t rssidBm = 127;
int16_t rssiValSigned = 0;
bool_t stateChanged = FALSE;
/* in RCP designs, the RSSI function is called while the radio is in
* OT_RADIO_STATE_RECEIVE. Turn off the radio before reading RSSI,
* otherwise we may end up waiting until a packet is received
* (in i16Radio_GetRSSI, while loop)
*/
if (sState == OT_RADIO_STATE_RECEIVE)
{
sState = OT_RADIO_STATE_SLEEP;
vMMAC_RadioToOffAndWait();
stateChanged = TRUE;
}
rssiValSigned = i16Radio_GetRSSI(0, FALSE, NULL);
if (stateChanged)
{
sState = OT_RADIO_STATE_RECEIVE;
K32WEnableReceive(TRUE);
}
rssiValSigned = i16Radio_BoundRssiValue(rssiValSigned);
/* RSSI reported by radio is in 1/4 dBm step,
* meaning values are 4 times larger than real dBm value.
*/
rssidBm = (int8_t)(rssiValSigned >> 2);
return rssidBm;
}
otRadioCaps otPlatRadioGetCaps(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
return OT_RADIO_CAPS_ACK_TIMEOUT | OT_RADIO_CAPS_TRANSMIT_RETRIES | OT_RADIO_CAPS_CSMA_BACKOFF;
}
bool otPlatRadioGetPromiscuous(otInstance *aInstance)
{
OT_UNUSED_VARIABLE(aInstance);
return sPromiscuousEnable;
}
void otPlatRadioSetPromiscuous(otInstance *aInstance, bool aEnable)
{
OT_UNUSED_VARIABLE(aInstance);
if (sPromiscuousEnable != aEnable)
{
sPromiscuousEnable = aEnable;
if (aEnable)
{
sRxOpt &= ~E_MMAC_RX_ADDRESS_MATCH;
}
else
{
sRxOpt |= E_MMAC_RX_ADDRESS_MATCH;
}
}
}
otError otPlatRadioEnergyScan(otInstance *aInstance, uint8_t aScanChannel, uint16_t aScanDuration)
{
OT_UNUSED_VARIABLE(aInstance);
otError status = OT_ERROR_NOT_IMPLEMENTED;
return status;
}
otError otPlatRadioGetTransmitPower(otInstance *aInstance, int8_t *aPower)
{
otError error = OT_ERROR_NONE;
otEXPECT_ACTION(aPower != NULL, error = OT_ERROR_INVALID_ARGS);
*aPower = i8Radio_GetTxPowerLevel_dBm();
return OT_ERROR_NONE;
exit:
return error;
}
otError otPlatRadioSetTransmitPower(otInstance *aInstance, int8_t aPower)
{
OT_UNUSED_VARIABLE(aInstance);
otRadioState tempState = sState;
sState = OT_RADIO_STATE_SLEEP;
/* trim the values to the radio capabilities */
if (aPower < K32W_RADIO_MIN_TX_POWER_DBM)
{
aPower = K32W_RADIO_MIN_TX_POWER_DBM;
}
else if (aPower > K32W_RADIO_MAX_TX_POWER_DBM)
{
aPower = K32W_RADIO_MAX_TX_POWER_DBM;
}
/* save for later use */
sTxPwrLevel = aPower;
/* The state is set to sleep to prevent a lockup caused by an RX interrup firing during
* the radio off command called inside set channel and power */
if (0 != sChannel)
{
vMMAC_SetChannelAndPower(sChannel, aPower);
}
else
{
/* if the channel has not yet been initialized use K32W_RADIO_DEFAULT_CHANNEL as default */
vMMAC_SetChannelAndPower(K32W_RADIO_DEFAULT_CHANNEL, aPower);
}
sState = tempState;
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 K32W_RADIO_RX_SENSITIVITY_DBM;
}
/**
* Interrupt service routine (e.g.: TX/RX/MAC HDR received)
*
* @param[in] u32IntBitmap Bitmap telling which interrupt fired
*
*/
static void K32WISR(uint32_t u32IntBitmap)
{
tsRxFrameFormat *pRxFrame = NULL;
switch (sState)
{
case OT_RADIO_STATE_RECEIVE:
/* no rx errors */
if (0 == u32MMAC_GetRxErrors())
{
if (u32IntBitmap & E_MMAC_INT_RX_HEADER)
{
/* go back one index from current frame index */
pRxFrame = &sRxFrame[(sRxFrameIndex + K32W_RX_BUFFERS - 1) % K32W_RX_BUFFERS];
/* FP processing first */
K32WProcessMacHeader(pRxFrame);
/* RX interrupt fired so it's safe to consume the frame */
K32WPushRxRingBuffer(&sRxRing, pRxFrame);
if (0 == (pRxFrame->sFrameBody.u16FCF & kFcfAckRequest))
{
K32WEnableReceive(TRUE);
}
}
else if (u32IntBitmap & E_MMAC_INT_RX_COMPLETE)
{
K32WEnableReceive(TRUE);
}
}
else
{
/* restart RX and keep same buffer as data received contains errors */
K32WEnableReceive(FALSE);
}
BOARD_LedDongleToggle();
break;
case OT_RADIO_STATE_TRANSMIT:
if (u32IntBitmap & E_MMAC_INT_TX_COMPLETE)
{
uint32_t txErrors = u32MMAC_GetTxErrors();
sTxDone = TRUE;
if (txErrors & E_MMAC_TXSTAT_CCA_BUSY)
{
sTxStatus = OT_ERROR_CHANNEL_ACCESS_FAILURE;
}
else if (txErrors & E_MMAC_TXSTAT_NO_ACK)
{
sTxStatus = OT_ERROR_NO_ACK;
}
else if (txErrors & E_MMAC_TXSTAT_ABORTED)
{
sTxStatus = OT_ERROR_ABORT;
}
else if ((txErrors & E_MMAC_TXSTAT_TXPCTO) || (txErrors & E_MMAC_TXSTAT_TXTO))
{
/* The JN518x/K32W0x1 has a TXTO timeout that we are using to catch and cope with the curious
hang-up issue */
vMMAC_AbortRadio();
/* Describe failure as a CCA failure for onward processing */
sTxStatus = OT_ERROR_CHANNEL_ACCESS_FAILURE;
}
/* go to RX and restore channel */
if (sChannel != sTxOtFrame.mChannel)
{
vMMAC_SetChannelAndPower(sChannel, sTxPwrLevel);
}
BOARD_LedDongleToggle();
sState = OT_RADIO_STATE_RECEIVE;
K32WEnableReceive(TRUE);
}
break;
default:
break;
}
#if USE_RTOS
otSysEventSignalPending();
#endif
}
/**
* Process the MAC Header of the latest received packet
* We are in interrupt context - we need to compute the FP
* while the hardware generates the ACK.
*
* @param[in] aRxFrame Pointer to the latest received MAC packet
*
*/
static void K32WProcessMacHeader(tsRxFrameFormat *aRxFrame)
{
/* check if frame pending processing is required */
if (aRxFrame && sIsFpEnabled)
{
/* Intra-PAN bit set? */
if ((kFcfPanidCompression & aRxFrame->sFrameBody.u16FCF) &&
(kFcfDstAddrNone != (aRxFrame->sFrameBody.u16FCF & kFcfDstAddrMask)))
{
/* Read destination PAN ID into source PAN ID: they are the same */
aRxFrame->sFrameBody.u16SrcPAN = aRxFrame->sFrameBody.u16DestPAN;
}
if (K32WIsDataReq(aRxFrame))
{
vMMAC_SetTxPend(K32WCheckIfFpRequired(aRxFrame));
}
else
{
/* Make sure this is set to 0 when we are not dealing with a data request */
aRxFrame->sFrameBody.u16Unused = 0;
}
}
}
/**
* Check if aRxFrame is a MAC Data Request Command
*
* @param[in] aRxFrame Pointer to the latest received MAC packet
*
* @return TRUE aRxFrame is a MAC Data Request Frame
* @return FALSE aRxFrame is not a MAC Data Request Frame
*/
static bool_t K32WIsDataReq(tsRxFrameFormat *aRxFrame)
{
bool_t isDataReq = FALSE;
uint8_t offset = 0;
if (kFcfTypeMacCommand == (aRxFrame->sFrameBody.u16FCF & kFcfMacFrameTypeMask))
{
uint8_t secControlField = aRxFrame->sFrameBody.uPayload.au8Byte[0];
if (secControlField & kSecLevelMask)
{
offset += kSecurityControlSize;
}
if (!(secControlField & kFrameCounterSuppression))
{
offset += kFrameCounterSize;
}
switch (secControlField & kKeyIdModeMask)
{
case kKeyIdMode0:
offset += kKeySourceSizeMode0;
break;
case kKeyIdMode1:
offset += kKeySourceSizeMode1 + kKeyIndexSize;
break;
case kKeyIdMode2:
offset += kKeySourceSizeMode2 + kKeyIndexSize;
break;
case kKeyIdMode3:
offset += kKeySourceSizeMode3 + kKeyIndexSize;
break;
}
if (kMacFrameDataReq == aRxFrame->sFrameBody.uPayload.au8Byte[offset])
{
isDataReq = TRUE;
}
}
return isDataReq;
}
/**
* Check if Frame Pending was requested by aPsRxFrame
* We are in interrupt context.
*
* @param[in] aRxFrame Pointer to a Data Request Frame
*
* @return TRUE Frame Pending bit should be set in the reply
* @return FALSE Frame Pending bit shouldn't be set in the reply
*
*/
static bool K32WCheckIfFpRequired(tsRxFrameFormat *aRxFrame)
{
bool isFpRequired = FALSE;
uint16_t panId = aRxFrame->sFrameBody.u16SrcPAN;
uint8_t idx = 0;
if (kFcfSrcAddrShort == (aRxFrame->sFrameBody.u16FCF & kFcfSrcAddrMask))
{
uint16_t shortAddr = aRxFrame->sFrameBody.uSrcAddr.u16Short;
for (idx = 0; idx < MAX_FP_ADDRS; idx++)
{
if (BIT_TST(sFpShortAddrMask, idx) && (sFpShortAddr[idx].macAddress == shortAddr) &&
sFpShortAddr[idx].panId == panId)
{
isFpRequired = TRUE;
break;
}
}
}
else
{
for (idx = 0; idx < MAX_FP_ADDRS; idx++)
{
if (BIT_TST(sFpExtAddrMask, idx) &&
(sFpExtAddr[idx].extAddr.u32L == aRxFrame->sFrameBody.uSrcAddr.sExt.u32L) &&
(sFpExtAddr[idx].extAddr.u32H == aRxFrame->sFrameBody.uSrcAddr.sExt.u32H) &&
sFpExtAddr[idx].panId == panId)
{
isFpRequired = TRUE;
break;
}
}
}
/* use the unsued filed to store if the frame was ack'ed with FP and report this back to OT stack */
aRxFrame->sFrameBody.u16Unused = isFpRequired;
return isFpRequired;
}
/**
* Process RX frames in process context and call the upper layer call-backs
*
* @param[in] aInstance Pointer to OT instance
*/
static void K32WProcessRxFrames(otInstance *aInstance)
{
tsRxFrameFormat *pRxMacFormatFrame = NULL;
uint32_t savedInterrupts;
while ((pRxMacFormatFrame = K32WPopRxRingBuffer(&sRxRing)) != NULL)
{
if (OT_ERROR_NONE == K32WFrameConversion(pRxMacFormatFrame, &sRxOtFrame, macToOtFrame))
{
otPlatRadioReceiveDone(aInstance, &sRxOtFrame, OT_ERROR_NONE);
}
else
{
otPlatRadioReceiveDone(aInstance, NULL, OT_ERROR_ABORT);
}
memset(pRxMacFormatFrame, 0, sizeof(tsRxFrameFormat));
MICRO_DISABLE_AND_SAVE_INTERRUPTS(savedInterrupts);
sRxFrameInProcess = NULL;
if (sIsRxDisabled)
{
K32WEnableReceive(TRUE);
}
MICRO_RESTORE_INTERRUPTS(savedInterrupts);
}
}
/**
* Process TX frame in process context and call the upper layer call-backs
*
* @param[in] aInstance Pointer to OT instance
*/
static void K32WProcessTxFrame(otInstance *aInstance)
{
if (sTxDone)
{
sTxDone = FALSE;
if ((sTxOtFrame.mPsdu[kMacFcfLowOffset] & kFcfAckRequest) && (OT_ERROR_NONE == sTxStatus))
{
K32WFrameConversion(&sRxAckFrame, &sRxOtFrame, macToOtFrame);
otPlatRadioTxDone(aInstance, &sTxOtFrame, &sRxOtFrame, sTxStatus);
}
else
{
otPlatRadioTxDone(aInstance, &sTxOtFrame, NULL, sTxStatus);
}
}
}
/**
* aMacFormatFrame <-> aOtFrame bidirectional conversion
*
* @param[in] aMacFrameFormat Pointer to a MAC Format frame
* @param[in] aOtFrame Pointer to OpenThread Frame
* @param[in] convType Conversion direction
*
* @return OT_ERROR_NONE No conversion error
* @return OT_ERROR_PARSE Conversion failed due to parsing error
*/
static otError K32WFrameConversion(tsRxFrameFormat * aMacFormatFrame,
otRadioFrame * aOtFrame,
frameConversionType convType)
{
tsMacFrame *pMacFrame = &aMacFormatFrame->sFrameBody;
uint8_t * pSavedStartRxPSDU = aOtFrame->mPsdu;
uint8_t * pPsdu = aOtFrame->mPsdu;
uint16_t aFcf = 0;
otError error = OT_ERROR_NONE;
/* frame control field */
K32WCopy((uint8_t *)&pMacFrame->u16FCF, &pPsdu, kFcfSize, convType);
aFcf = pMacFrame->u16FCF;
/* sequence number */
if (0 == (aFcf & kFcfSeqNbSuppresssion))
{
K32WCopy(&pMacFrame->u8SequenceNum, &pPsdu, kDsnSize, convType);
}
/* destination Pan Id + address */
switch (aFcf & kFcfDstAddrMask)
{
case kFcfDstAddrNone:
break;
case kFcfDstAddrShort:
K32WCopy((uint8_t *)&pMacFrame->u16DestPAN, &pPsdu, sizeof(otPanId), convType);
K32WCopy((uint8_t *)&pMacFrame->uDestAddr.u16Short, &pPsdu, sizeof(otShortAddress), convType);
break;
case kFcfDstAddrExt:
K32WCopy((uint8_t *)&pMacFrame->u16DestPAN, &pPsdu, sizeof(otPanId), convType);
K32WCopy((uint8_t *)&pMacFrame->uDestAddr.sExt.u32L, &pPsdu, sizeof(uint32_t), convType);
K32WCopy((uint8_t *)&pMacFrame->uDestAddr.sExt.u32H, &pPsdu, sizeof(uint32_t), convType);
break;
default:
error = OT_ERROR_PARSE;
otEXPECT(false);
}
/* Source Pan Id */
if ((aFcf & kFcfSrcAddrMask) != kFcfSrcAddrNone && (aFcf & kFcfPanidCompression) == 0)
{
K32WCopy((uint8_t *)&pMacFrame->u16SrcPAN, &pPsdu, sizeof(otPanId), convType);
}
/* Source Address */
switch (aFcf & kFcfSrcAddrMask)
{
case kFcfSrcAddrNone:
break;
case kFcfSrcAddrShort:
K32WCopy((uint8_t *)&pMacFrame->uSrcAddr.u16Short, &pPsdu, sizeof(otShortAddress), convType);
break;
case kFcfSrcAddrExt:
K32WCopy((uint8_t *)&pMacFrame->uSrcAddr.sExt.u32L, &pPsdu, sizeof(uint32_t), convType);
K32WCopy((uint8_t *)&pMacFrame->uSrcAddr.sExt.u32H, &pPsdu, sizeof(uint32_t), convType);
break;
default:
error = OT_ERROR_PARSE;
otEXPECT(false);
}
if (convType == otToMacFrame)
{
pMacFrame->u8PayloadLength = aOtFrame->mLength - (pPsdu - pSavedStartRxPSDU) - kFcfSize;
}
else
{
aOtFrame->mInfo.mRxInfo.mAckedWithFramePending = (bool)aMacFormatFrame->sFrameBody.u16Unused;
aOtFrame->mInfo.mRxInfo.mLqi = aMacFormatFrame->u8LinkQuality;
aOtFrame->mInfo.mRxInfo.mRssi = i8Radio_GetLastPacketRSSI();
aOtFrame->mChannel = sChannel;
#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
{
aOtFrame->mInfo.mRxInfo.mTimestamp = otPlatAlarmMilliGetNow() * 1000;
}
aOtFrame->mLength = pPsdu + (pMacFrame->u8PayloadLength) - pSavedStartRxPSDU + sizeof(pMacFrame->u16FCS);
}
K32WCopy((uint8_t *)&pMacFrame->uPayload, &pPsdu, pMacFrame->u8PayloadLength, convType);
exit:
return error;
}
/**
* Helper function for aMacFrame <-> aOtFrame bidirectional conversion
* Copies from/to PSDU to/from specific field
*
* @param[in] aFieldValue Pointer to field Value which needs to be copied/filled
* @param[in] aPsdu Pointer to PSDU part which needs to be copied/filled
* @param[in] copySize Bytes copied
* @param[in] convType Conversion Type
*
* @return OT_ERROR_NONE No conversion error
* @return OT_ERROR_PARSE Conversion failed due to parsing error
*/
static void K32WCopy(uint8_t *aFieldValue, uint8_t **aPsdu, uint8_t copySize, frameConversionType convType)
{
if (convType == macToOtFrame)
{
memcpy(*aPsdu, aFieldValue, copySize);
}
else
{
memcpy(aFieldValue, *aPsdu, copySize);
}
(*aPsdu) += copySize;
}
/**
* Function used to init/reset an RX Ring Buffer
*
* @param[in] aRxRing Pointer to an RX Ring Buffer
*/
static void K32WResetRxRingBuffer(rxRingBuffer *aRxRing)
{
aRxRing->head = 0;
aRxRing->tail = 0;
aRxRing->isFull = FALSE;
}
/**
* Function used to push the address of a received frame to the RX Ring buffer.
* In case the ring buffer is full, the oldest address is overwritten.
*
* @param[in] aRxRing Pointer to the RX Ring Buffer
* @param[in] rxFrame The address for a received frame
*/
static void K32WPushRxRingBuffer(rxRingBuffer *aRxRing, tsRxFrameFormat *aRxFrame)
{
aRxRing->buffer[aRxRing->head] = aRxFrame;
if (aRxRing->isFull)
{
aRxRing->tail = (aRxRing->tail + 1) % K32W_RX_BUFFERS;
}
aRxRing->head = (aRxRing->head + 1) % K32W_RX_BUFFERS;
aRxRing->isFull = (aRxRing->head == aRxRing->tail);
}
/**
* Function used to pop the address of a received frame from the RX Ring buffer
* Process Context: the consumer will pop frames with the interrupts disabled
* to make sure the interrupt context(ISR) doesn't push in
* the middle of a pop.
*
* @param[in] aRxRing Pointer to the RX Ring Buffer
*
* @return tsRxFrameFormat Pointer to a received frame
* @return NULL In case the RX Ring buffer is empty
*/
static tsRxFrameFormat *K32WPopRxRingBuffer(rxRingBuffer *aRxRing)
{
tsRxFrameFormat *rxFrame = NULL;
uint32_t savedInterrupts;
MICRO_DISABLE_AND_SAVE_INTERRUPTS(savedInterrupts);
if (!K32WIsEmptyRxRingBuffer(aRxRing))
{
rxFrame = aRxRing->buffer[aRxRing->tail];
aRxRing->isFull = FALSE;
aRxRing->tail = (aRxRing->tail + 1) % K32W_RX_BUFFERS;
}
MICRO_RESTORE_INTERRUPTS(savedInterrupts);
return rxFrame;
}
/**
* Function used to check if an RX Ring buffer is empty
*
* @param[in] aRxRing Pointer to the RX Ring Buffer
*
* @return TRUE RX Ring Buffer is not empty
* @return FALSE RX Ring Buffer is empty
*/
static bool K32WIsEmptyRxRingBuffer(rxRingBuffer *aRxRing)
{
return (!aRxRing->isFull && (aRxRing->head == aRxRing->tail));
}
/**
* Function used to get the next frame from aRxFrame pointed by aRxFrameIndex.
*
* This is the address where the BBC should DMA a received frame. Once the
* reception is complete (the RX interrupt fires) this address is added to the
* ring buffer and the frame can be consumed by the process context.
*
* @param[in] aRxFrame Pointer to an array of tsRxFrameFormat
* @param[in] aRxFrameIndex Pointer to the current index in aRxFrame array
*
* @return tsRxFrameFormat Pointer to a tsRxFrameFormat
*/
static tsRxFrameFormat *K32WGetFrame(tsRxFrameFormat *aRxFrame, uint8_t *aRxFrameIndex)
{
tsRxFrameFormat *frame = NULL;
frame = &aRxFrame[*aRxFrameIndex];
if (frame != sRxFrameInProcess)
{
*aRxFrameIndex = (*aRxFrameIndex + 1) % K32W_RX_BUFFERS;
}
else
{
/* this can happen only if the RX buffer is full and the
* process context is interrupted right in the middle of
* starting to process a frame. In this case, wait for
* the process context to finish the processing then
* re-enable RX
*/
sIsRxDisabled = TRUE;
frame = NULL;
}
return frame;
}
/**
* Function used to enable the receiving of a frame
*
* @param[in] isNewFrameNeeded FALSE in case the current RX buffer can be
* used for a new RX operation.
*
*/
static void K32WEnableReceive(bool_t isNewFrameNeeded)
{
tsRxFrameFormat *pRxFrame = NULL;
if (isNewFrameNeeded)
{
if ((pRxFrame = K32WGetFrame(sRxFrame, &sRxFrameIndex)) != NULL)
{
pLastRxFrame = pRxFrame;
vMMAC_StartMacReceive(&pRxFrame->sFrameBody, sRxOpt);
}
}
else
{
vMMAC_StartMacReceive(&pLastRxFrame->sFrameBody, sRxOpt);
}
}
/**
* Function used for MMAC-RX Restart
*
*/
static void K32WRestartRx(void)
{
vMMAC_SetRxProm(((uint32)sRxOpt >> 8) & ALL_FFs_BYTE);
vMMAC_RxCtlUpdate(((uint32)sRxOpt) & ALL_FFs_BYTE);
}