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) 2016 Intel Corporation.
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#define SYS_LOG_LEVEL CONFIG_SYS_LOG_IEEE802154_DRIVER_LEVEL
#define SYS_LOG_DOMAIN "net/ieee802154/upipe/"
#include <logging/sys_log.h>

#include <errno.h>

#include <kernel.h>
#include <arch/cpu.h>

#include <board.h>
#include <device.h>
#include <init.h>
#include <net/net_if.h>
#include <net/net_pkt.h>

#include <console/uart_pipe.h>
#include <net/ieee802154_radio.h>

#include "ieee802154_uart_pipe.h"

/** Singleton device used in uart pipe callback */
static struct device *upipe_dev;

static u8_t *upipe_rx(u8_t *buf, size_t *off)
{
	struct upipe_context *upipe = upipe_dev->driver_data;
	struct net_pkt *pkt = NULL;
	struct net_buf *frag = NULL;

	if (!upipe_dev) {
		goto done;
	}

	if (!upipe->rx && *buf == UART_PIPE_RADIO_15_4_FRAME_TYPE) {
		upipe->rx = true;
		goto done;
	}

	if (!upipe->rx_len) {
		if (*buf > 127) {
			goto flush;
		}

		upipe->rx_len = *buf;
		goto done;
	}

	upipe->rx_buf[upipe->rx_off++] = *buf;

	if (upipe->rx_len == upipe->rx_off) {
		pkt = net_pkt_get_reserve_rx(0, K_NO_WAIT);
		if (!pkt) {
			SYS_LOG_DBG("No pkt available");
			goto flush;
		}

		frag = net_pkt_get_frag(pkt, K_NO_WAIT);
		if (!frag) {
			SYS_LOG_DBG("No fragment available");
			goto out;
		}

		net_pkt_frag_insert(pkt, frag);

		memcpy(frag->data, upipe->rx_buf, upipe->rx_len - 2);
		net_buf_add(frag, upipe->rx_len - 2);

		if (ieee802154_radio_handle_ack(upipe->iface, pkt) == NET_OK) {
			SYS_LOG_DBG("ACK packet handled");
			goto out;
		}

		SYS_LOG_DBG("Caught a packet (%u)", upipe->rx_len);
		if (net_recv_data(upipe->iface, pkt) < 0) {
			SYS_LOG_DBG("Packet dropped by NET stack");
			goto out;
		}

		goto flush;
out:
		net_pkt_unref(pkt);
flush:
		upipe->rx = false;
		upipe->rx_len = 0;
		upipe->rx_off = 0;
	}
done:
	*off = 0;

	return pkt;
}

static int upipe_cca(struct device *dev)
{
	struct upipe_context *upipe = dev->driver_data;

	if (upipe->stopped) {
		return -EIO;
	}

	return 0;
}

static int upipe_set_channel(struct device *dev, u16_t channel)
{
	struct upipe_context *upipe = dev->driver_data;

	if (upipe->stopped) {
		return -EIO;
	}

	return 0;
}

static int upipe_set_pan_id(struct device *dev, u16_t pan_id)
{
	struct upipe_context *upipe = dev->driver_data;

	if (upipe->stopped) {
		return -EIO;
	}

	return 0;
}

static int upipe_set_short_addr(struct device *dev, u16_t short_addr)
{
	struct upipe_context *upipe = dev->driver_data;

	if (upipe->stopped) {
		return -EIO;
	}

	return 0;
}

static int upipe_set_ieee_addr(struct device *dev, const u8_t *ieee_addr)
{
	struct upipe_context *upipe = dev->driver_data;

	if (upipe->stopped) {
		return -EIO;
	}

	return 0;
}

static int upipe_set_txpower(struct device *dev, s16_t dbm)
{
	struct upipe_context *upipe = dev->driver_data;

	if (upipe->stopped) {
		return -EIO;
	}

	return 0;
}

static int upipe_tx(struct device *dev,
		    struct net_pkt *pkt,
		    struct net_buf *frag)
{
	u8_t *pkt_buf = frag->data - net_pkt_ll_reserve(pkt);
	u8_t len = net_pkt_ll_reserve(pkt) + frag->len;
	struct upipe_context *upipe = dev->driver_data;
	u8_t i, data;

	SYS_LOG_DBG("%p (%u)", frag, len);

	if (upipe->stopped) {
		return -EIO;
	}

	data = UART_PIPE_RADIO_15_4_FRAME_TYPE;
	uart_pipe_send(&data, 1);

	data = len + 2;
	uart_pipe_send(&data, 1);

	for (i = 0; i < len; i++) {
		uart_pipe_send(pkt_buf+i, 1);
	}

	/* The 2 dummy bytes representing FCS */
	data = 0xFF;
	uart_pipe_send(&data, 1);
	uart_pipe_send(&data, 1);

	return 0;
}

static int upipe_start(struct device *dev)
{
	struct upipe_context *upipe = dev->driver_data;

	if (!upipe->stopped) {
		return -EALREADY;
	}

	upipe->stopped = false;

	return 0;
}

static int upipe_stop(struct device *dev)
{
	struct upipe_context *upipe = dev->driver_data;

	if (upipe->stopped) {
		return -EALREADY;
	}

	upipe->stopped = true;

	return 0;
}

static int upipe_init(struct device *dev)
{
	struct upipe_context *upipe = dev->driver_data;

	memset(upipe, 0, sizeof(struct upipe_context));

	uart_pipe_register(upipe->uart_pipe_buf, 1, upipe_rx);

	upipe_stop(dev);

	return 0;
}

static inline u8_t *get_mac(struct device *dev)
{
	struct upipe_context *upipe = dev->driver_data;

	upipe->mac_addr[0] = 0x00;
	upipe->mac_addr[1] = 0x10;
	upipe->mac_addr[2] = 0x20;
	upipe->mac_addr[3] = 0x30;

	UNALIGNED_PUT(sys_cpu_to_be32(sys_rand32_get()),
		      (u32_t *) ((void *)upipe->mac_addr+4));

	return upipe->mac_addr;
}

static void upipe_iface_init(struct net_if *iface)
{
	struct device *dev = net_if_get_device(iface);
	struct upipe_context *upipe = dev->driver_data;
	u8_t *mac = get_mac(dev);

	SYS_LOG_DBG("");

	net_if_set_link_addr(iface, mac, 8, NET_LINK_IEEE802154);

	upipe_dev = dev;
	upipe->iface = iface;

	ieee802154_init(iface);
}

static struct upipe_context upipe_context_data;

static struct ieee802154_radio_api upipe_radio_api = {
	.iface_api.init		= upipe_iface_init,
	.iface_api.send		= ieee802154_radio_send,

	.cca			= upipe_cca,
	.set_channel		= upipe_set_channel,
	.set_pan_id		= upipe_set_pan_id,
	.set_short_addr		= upipe_set_short_addr,
	.set_ieee_addr		= upipe_set_ieee_addr,
	.set_txpower		= upipe_set_txpower,
	.tx			= upipe_tx,
	.start			= upipe_start,
	.stop			= upipe_stop,
};

NET_DEVICE_INIT(upipe_15_4, CONFIG_IEEE802154_UPIPE_DRV_NAME,
		upipe_init, &upipe_context_data, NULL,
		CONFIG_KERNEL_INIT_PRIORITY_DEFAULT,
		&upipe_radio_api, IEEE802154_L2,
		NET_L2_GET_CTX_TYPE(IEEE802154_L2), 125);