Boot Linux faster!

Check our new training course

Boot Linux faster!

Check our new training course
and Creative Commons CC-BY-SA
lecture and lab materials

Bootlin logo

Elixir Cross Referencer

/*
 * Copyright (c) 2018 Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: Apache-2.0
 */

/**
 * @file
 *   This file implements the OpenThread platform abstraction
 *   for radio communication.
 *
 */

#define LOG_LEVEL CONFIG_OPENTHREAD_LOG_LEVEL
#define LOG_MODULE_NAME net_otPlat_radio

#include <logging/log.h>
LOG_MODULE_REGISTER(LOG_MODULE_NAME);

#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>

#include <kernel.h>
#include <device.h>
#include <net/ieee802154_radio.h>
#include <net/net_pkt.h>
#include <misc/__assert.h>

#include <openthread-system.h>
#include <openthread/instance.h>
#include <openthread/platform/radio.h>
#include <openthread/platform/diag.h>

#include "platform-zephyr.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 u16_t channel;

static void dataInit(void)
{
	tx_pkt = net_pkt_alloc(K_NO_WAIT);
	__ASSERT_NO_MSG(tx_pkt != NULL);

	tx_payload = net_pkt_get_reserve_tx_data(K_NO_WAIT);
	__ASSERT_NO_MSG(tx_payload != NULL);

	net_pkt_append_buffer(tx_pkt, tx_payload);

	sTransmitFrame.mPsdu = tx_payload->data;
}

void platformRadioInit(void)
{
	dataInit();

	radio_dev = device_get_binding(CONFIG_NET_CONFIG_IEEE802154_DEV_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;

		channel = sTransmitFrame.mChannel;

		radio_api->set_channel(radio_dev, sTransmitFrame.mChannel);
		radio_api->set_txpower(radio_dev, tx_power);

		if (sTransmitFrame.mInfo.mTxInfo.mCsmaCaEnabled) {
			if (radio_api->cca(radio_dev) ||
			    radio_api->tx(radio_dev, tx_pkt, tx_payload)) {
				result = OT_ERROR_CHANNEL_ACCESS_FAILURE;
			}
		} else {
			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.mInfo.mRxInfo.mLqi = 80;
				ackFrame.mInfo.mRxInfo.mRssi = -40;
				ackFrame.mLength = 5;

				otPlatRadioTxDone(aInstance, &sTransmitFrame,
					&ackFrame, result);
			} else {
				otPlatRadioTxDone(aInstance, &sTransmitFrame,
					NULL, result);
			}
		}
	}
}

uint16_t platformRadioChannelGet(otInstance *aInstance)
{
	ARG_UNUSED(aInstance);

	return channel;
}

void otPlatRadioSetPanId(otInstance *aInstance, u16_t aPanId)
{
	ARG_UNUSED(aInstance);

	radio_api->filter(radio_dev, true, IEEE802154_FILTER_TYPE_PAN_ID,
			  (struct ieee802154_filter *) &aPanId);
}

void otPlatRadioSetExtendedAddress(otInstance *aInstance,
				   const otExtAddress *aExtAddress)
{
	ARG_UNUSED(aInstance);

	radio_api->filter(radio_dev, true, IEEE802154_FILTER_TYPE_IEEE_ADDR,
			  (struct ieee802154_filter *) &aExtAddress);
}

void otPlatRadioSetShortAddress(otInstance *aInstance, u16_t aShortAddress)
{
	ARG_UNUSED(aInstance);

	radio_api->filter(radio_dev, true, 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);

	channel = aChannel;

	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;
		otSysEventSignalPending();
	}

	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;

	LOG_DBG("PromiscuousMode=%d", result ? 1 : 0);
	return result;
}

void otPlatRadioSetPromiscuous(otInstance *aInstance, bool aEnable)
{
	ARG_UNUSED(aInstance);
	ARG_UNUSED(aEnable);

	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;
}

otError otPlatRadioGetTransmitPower(otInstance *aInstance, int8_t *aPower)
{
	ARG_UNUSED(aInstance);

	if (aPower == NULL) {
		return OT_ERROR_INVALID_ARGS;
	}

	*aPower = tx_power;

	return OT_ERROR_NONE;
}

otError otPlatRadioSetTransmitPower(otInstance *aInstance, int8_t aPower)
{
	ARG_UNUSED(aInstance);

	tx_power = aPower;

	return OT_ERROR_NONE;
}