blob: ed6d61065d129c1e9cf4270c9ec6890be76be9be [file] [log] [blame]
/*
* Copyright (c) 2018 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* This file implements the OpenThread platform abstraction
* for radio communication.
*
*/
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#define SYS_LOG_LEVEL CONFIG_OPENTHREAD_LOG_LEVEL
#define SYS_LOG_DOMAIN "otPlat/radio"
#include <logging/sys_log.h>
#include <kernel.h>
#include <device.h>
#include <net/ieee802154_radio.h>
#include <net/net_pkt.h>
#include <misc/__assert.h>
#include <openthread/platform/radio.h>
#include <openthread/platform/diag.h>
#include <openthread/platform/platform.h>
#include <openthread/types.h>
#define FCS_SIZE 2
static otRadioState sState = OT_RADIO_STATE_DISABLED;
static otRadioFrame sTransmitFrame;
static struct net_pkt *tx_pkt;
static struct net_buf *tx_payload;
static struct device *radio_dev;
static struct ieee802154_radio_api *radio_api;
static s8_t tx_power;
static void dataInit(void)
{
tx_pkt = net_pkt_get_reserve_tx(0, K_NO_WAIT);
__ASSERT_NO_MSG(tx_pkt != NULL);
tx_payload = net_pkt_get_reserve_tx_data(0, K_NO_WAIT);
__ASSERT_NO_MSG(tx_payload != NULL);
net_pkt_frag_insert(tx_pkt, tx_payload);
sTransmitFrame.mPsdu = tx_payload->data;
}
void platformRadioInit(void)
{
dataInit();
radio_dev = device_get_binding(CONFIG_OT_PLAT_RADIO_DEVICE_NAME);
__ASSERT_NO_MSG(radio_dev != NULL);
radio_api = (struct ieee802154_radio_api *)radio_dev->driver_api;
__ASSERT(radio_api->get_capabilities(radio_dev)
& IEEE802154_HW_TX_RX_ACK,
"Only radios with automatic ack handling "
"are currently supported");
}
void platformRadioProcess(otInstance *aInstance)
{
if (sState == OT_RADIO_STATE_TRANSMIT) {
otError result = OT_ERROR_NONE;
/*
* The payload is already in tx_payload->data,
* but we need to set the length field
* according to sTransmitFrame.length.
* We subtract the FCS size as radio driver
* adds CRC and increases frame length on its own.
*/
tx_payload->len = sTransmitFrame.mLength - FCS_SIZE;
radio_api->set_channel(radio_dev, sTransmitFrame.mChannel);
radio_api->set_txpower(radio_dev, sTransmitFrame.mPower);
if (radio_api->tx(radio_dev, tx_pkt, tx_payload)) {
result = OT_ERROR_CHANNEL_ACCESS_FAILURE;
}
sState = OT_RADIO_STATE_RECEIVE;
#if OPENTHREAD_ENABLE_DIAG
if (otPlatDiagModeGet()) {
otPlatDiagRadioTransmitDone(aInstance, &sTransmitFrame,
result);
} else
#endif
{
if (sTransmitFrame.mPsdu[0] & 0x20) {
/*
* TODO: Get real ACK frame
* instead of making a spoofed one
*/
otRadioFrame ackFrame;
u8_t ackPsdu[] = {0x02, 0x00, 0x00, 0x00, 0x00};
ackPsdu[2] = sTransmitFrame.mPsdu[2];
ackFrame.mPsdu = ackPsdu;
ackFrame.mLqi = 80;
ackFrame.mPower = -40;
ackFrame.mLength = 5;
otPlatRadioTxDone(aInstance, &sTransmitFrame,
&ackFrame, result);
} else {
otPlatRadioTxDone(aInstance, &sTransmitFrame,
NULL, result);
}
}
}
}
void otPlatRadioSetPanId(otInstance *aInstance, u16_t aPanId)
{
ARG_UNUSED(aInstance);
radio_api->set_filter(radio_dev, IEEE802154_FILTER_TYPE_PAN_ID,
(struct ieee802154_filter *) &aPanId);
}
void otPlatRadioSetExtendedAddress(otInstance *aInstance,
const otExtAddress *aExtAddress)
{
ARG_UNUSED(aInstance);
radio_api->set_filter(radio_dev, IEEE802154_FILTER_TYPE_IEEE_ADDR,
(struct ieee802154_filter *) &aExtAddress);
}
void otPlatRadioSetShortAddress(otInstance *aInstance, u16_t aShortAddress)
{
ARG_UNUSED(aInstance);
radio_api->set_filter(radio_dev, IEEE802154_FILTER_TYPE_SHORT_ADDR,
(struct ieee802154_filter *) &aShortAddress);
}
bool otPlatRadioIsEnabled(otInstance *aInstance)
{
ARG_UNUSED(aInstance);
return (sState != OT_RADIO_STATE_DISABLED) ? true : false;
}
otError otPlatRadioEnable(otInstance *aInstance)
{
if (!otPlatRadioIsEnabled(aInstance)) {
sState = OT_RADIO_STATE_SLEEP;
}
return OT_ERROR_NONE;
}
otError otPlatRadioDisable(otInstance *aInstance)
{
if (otPlatRadioIsEnabled(aInstance)) {
sState = OT_RADIO_STATE_DISABLED;
}
return OT_ERROR_NONE;
}
otError otPlatRadioSleep(otInstance *aInstance)
{
ARG_UNUSED(aInstance);
otError error = OT_ERROR_INVALID_STATE;
if (sState == OT_RADIO_STATE_SLEEP ||
sState == OT_RADIO_STATE_RECEIVE ||
sState == OT_RADIO_STATE_TRANSMIT) {
error = OT_ERROR_NONE;
sState = OT_RADIO_STATE_SLEEP;
radio_api->stop(radio_dev);
}
return error;
}
otError otPlatRadioReceive(otInstance *aInstance, u8_t aChannel)
{
ARG_UNUSED(aInstance);
radio_api->set_channel(radio_dev, aChannel);
radio_api->set_txpower(radio_dev, tx_power);
radio_api->start(radio_dev);
sState = OT_RADIO_STATE_RECEIVE;
return OT_ERROR_NONE;
}
otError otPlatRadioTransmit(otInstance *aInstance, otRadioFrame *aPacket)
{
otError error = OT_ERROR_INVALID_STATE;
ARG_UNUSED(aInstance);
ARG_UNUSED(aPacket);
__ASSERT_NO_MSG(aPacket == &sTransmitFrame);
if (sState == OT_RADIO_STATE_RECEIVE) {
error = OT_ERROR_NONE;
sState = OT_RADIO_STATE_TRANSMIT;
PlatformEventSignalPending();
}
return error;
}
otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *aInstance)
{
ARG_UNUSED(aInstance);
return &sTransmitFrame;
}
int8_t otPlatRadioGetRssi(otInstance *aInstance)
{
ARG_UNUSED(aInstance);
/* TODO: No API in Zephyr to get the RSSI. */
return 0;
}
otRadioCaps otPlatRadioGetCaps(otInstance *aInstance)
{
ARG_UNUSED(aInstance);
return OT_RADIO_CAPS_NONE;
}
bool otPlatRadioGetPromiscuous(otInstance *aInstance)
{
ARG_UNUSED(aInstance);
/* TODO: No API in Zephyr to get promiscuous mode. */
bool result = false;
SYS_LOG_DBG("PromiscuousMode=%d", result ? 1 : 0);
return result;
}
void otPlatRadioSetPromiscuous(otInstance *aInstance, bool aEnable)
{
ARG_UNUSED(aInstance);
ARG_UNUSED(aEnable);
SYS_LOG_DBG("PromiscuousMode=%d", aEnable ? 1 : 0);
/* TODO: No API in Zephyr to set promiscuous mode. */
}
otError otPlatRadioEnergyScan(otInstance *aInstance, u8_t aScanChannel,
u16_t aScanDuration)
{
ARG_UNUSED(aInstance);
ARG_UNUSED(aScanChannel);
ARG_UNUSED(aScanDuration);
return OT_ERROR_NOT_IMPLEMENTED;
}
void otPlatRadioEnableSrcMatch(otInstance *aInstance, bool aEnable)
{
ARG_UNUSED(aInstance);
ARG_UNUSED(aEnable);
}
otError otPlatRadioAddSrcMatchShortEntry(otInstance *aInstance,
const u16_t aShortAddress)
{
ARG_UNUSED(aInstance);
ARG_UNUSED(aShortAddress);
return OT_ERROR_NONE;
}
otError otPlatRadioAddSrcMatchExtEntry(otInstance *aInstance,
const otExtAddress *aExtAddress)
{
ARG_UNUSED(aInstance);
ARG_UNUSED(aExtAddress);
return OT_ERROR_NONE;
}
otError otPlatRadioClearSrcMatchShortEntry(otInstance *aInstance,
const u16_t aShortAddress)
{
ARG_UNUSED(aInstance);
ARG_UNUSED(aShortAddress);
return OT_ERROR_NONE;
}
otError otPlatRadioClearSrcMatchExtEntry(otInstance *aInstance,
const otExtAddress *aExtAddress)
{
ARG_UNUSED(aInstance);
ARG_UNUSED(aExtAddress);
return OT_ERROR_NONE;
}
void otPlatRadioClearSrcMatchShortEntries(otInstance *aInstance)
{
ARG_UNUSED(aInstance);
}
void otPlatRadioClearSrcMatchExtEntries(otInstance *aInstance)
{
ARG_UNUSED(aInstance);
}
int8_t otPlatRadioGetReceiveSensitivity(otInstance *aInstance)
{
ARG_UNUSED(aInstance);
return -100;
}
void otPlatRadioSetDefaultTxPower(otInstance *aInstance, int8_t aPower)
{
ARG_UNUSED(aInstance);
tx_power = aPower;
}