Commit 95cb365f authored by Kamil Sroka's avatar Kamil Sroka Committed by Anas Nashif
Browse files

subsys: net: ip: l2: Add OpenThread L2



Add OpenThread to Zephyrs net stack as data link layer.
OpenThread requires to call process function when an event occurs.
This process function is called from cooperative thread.

Packet conversion and dispaching is implemented in openthread.c
as well as addresses forwarding.

Signed-off-by: default avatarKamil Sroka <kamil.sroka@nordicsemi.no>
parent 32907813
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -21,3 +21,7 @@ endif()
if(CONFIG_NET_L2_IEEE802154)
  add_subdirectory(ieee802154)
endif()

if(CONFIG_NET_L2_OPENTHREAD)
  add_subdirectory(openthread)
endif()
+2 −0
Original line number Diff line number Diff line
@@ -132,4 +132,6 @@ config NET_DEBUG_ARP
	help
	  Enables core ARP code part to output debug messages

source "subsys/net/ip/l2/openthread/Kconfig"

endmenu
+14 −0
Original line number Diff line number Diff line
zephyr_library_named(subsys__net__ip__l2__openthread)
zephyr_library_include_directories(. ../..)
zephyr_library_compile_definitions_ifdef(
  CONFIG_NEWLIB_LIBC __LINUX_ERRNO_EXTENSIONS__
  )

zephyr_library_sources(
  openthread.c
  openthread_utils.c
  )

add_dependencies(subsys__net__ip__l2__openthread
  ot
  )
+190 −0
Original line number Diff line number Diff line
# Kconfig - OpenThread driver configuration options
#
#
# Copyright (c) 2018 Nordic Semiconductor ASA
#
# SPDX-License-Identifier: Apache-2.0
#

#
# OpenThread options
#
menuconfig NET_L2_OPENTHREAD
	bool
	prompt "OpenThread L2"
	depends on FLASH
	depends on FLASH_PAGE_LAYOUT
	depends on ENTROPY_GENERATOR
	select OPENTHREAD_PLAT

if NET_L2_OPENTHREAD

config OPENTHREAD_PLAT
	bool
	depends on NET_L2_OPENTHREAD
	default n
	help
	This option enables OpenThread platform

if OPENTHREAD_PLAT

menuconfig OPENTHREAD_DEBUG
	bool
	default n
	prompt "OpenThread stack log support"
	help
	  This option enables log support for OpenThread

if OPENTHREAD_DEBUG

choice
	prompt "OpenThread stack log level"
	help
	  This option selects log level for OpenThread stack.

config OPENTHREAD_LOG_LEVEL_ERROR
	bool "Error"
config OPENTHREAD_LOG_LEVEL_WARNING
	bool "Warning"
config OPENTHREAD_LOG_LEVEL_INFO
	bool "Info"
config OPENTHREAD_LOG_LEVEL_DEBUG
	bool "Debug"
endchoice

endif

endif

config	OPENTHREAD_LOG_LEVEL
	int
	default 0
	default 1 if OPENTHREAD_LOG_LEVEL_ERROR
	default 2 if OPENTHREAD_LOG_LEVEL_WARNING
	default 3 if OPENTHREAD_LOG_LEVEL_INFO
	default 4 if OPENTHREAD_LOG_LEVEL_DEBUG

menuconfig OPENTHREAD_L2_DEBUG
	bool
	prompt "OpenThread L2 log support"
	help
	  This option enables log support for OpenThread

if OPENTHREAD_L2_DEBUG
choice
	prompt "OpenThread L2 log level"
	depends on OPENTHREAD_L2_DEBUG
	help
	  This option selects log level for OpenThread driver.

config OPENTHREAD_L2_LOG_LEVEL_ERROR
	bool "Error"
config OPENTHREAD_L2_LOG_LEVEL_WARNING
	bool "Warning"
config OPENTHREAD_L2_LOG_LEVEL_INFO
	bool "Info"
config OPENTHREAD_L2_LOG_LEVEL_DEBUG
	bool "Debug"
endchoice

config OPENTHREAD_L2_DEBUG_DUMP_15_4
	bool
	prompt "Dump 802.15.4 packets"
	help
	  This option enables dumping of 802.15.4 packets

config OPENTHREAD_L2_DEBUG_DUMP_IPV6
	bool
	prompt "Dump IPv6 packets"
	help
	  This option enables dumping of IPv6 packets

endif

config	OPENTHREAD_L2_LOG_LEVEL
	int
	default 0
	default 1 if OPENTHREAD_L2_LOG_LEVEL_ERROR
	default 2 if OPENTHREAD_L2_LOG_LEVEL_WARNING
	default 3 if OPENTHREAD_L2_LOG_LEVEL_INFO
	default 4 if OPENTHREAD_L2_LOG_LEVEL_DEBUG

config OPENTHREAD_THREAD_PRIORITY
	int
	prompt "OpenThread thread priority"
	default 8

config OPENTHREAD_THREAD_STACK_SIZE
	int
	prompt "OpenThread thread stack size"
	default 3072

config OPENTHREAD_PKT_LIST_SIZE
	int
	prompt "List size for Ip6 packet buffering"
	default 10

config OPENTHREAD_PANID
	int
	prompt "Default PAN ID"
	default 43981

config OPENTHREAD_CHANNEL
	int
	prompt "Default Channel"
	default 11

choice
	prompt "OpenThread device type"
	help
	  This option selects Thread network device type

config OPENTHREAD_FTD
	bool "FTD - Full Thread Device"
config OPENTHREAD_MTD
	bool "MTD - Minimal Thread Device"
endchoice

config OPENTHREAD_SHELL
	bool
	prompt "Enable OpenThread shell"
	select CONSOLE_SHELL
	default y

config OPENTHREAD_DIAG
	bool
	prompt "Diagnostic functions support"
	help
	  Enable OpenThread CLI diagnostic commands

config OPENTHREAD_COMMISSIONER
	bool
	prompt "Commisioner functions support"
	help
	  Enable commissioner capability in OpenThread stack

config OPENTHREAD_JOINER
	bool
	prompt "Joiner functions support"
	help
	  Enable joiner capability in OpenThread stack

config OPENTHREAD_JAM_DETECTION
	bool
	prompt "Jam detection support"
	help
	  Enable jam detection in OpenThread stack

config OT_PLAT_RADIO_DEVICE_NAME
	string
	prompt "IEEE802.15.4 radio device name"
	default "IEEE802154_nrf5"

config OT_PLAT_FLASH_PAGES_COUNT
	int
	prompt "Flash pages count used by OpenThread platform"
	default 4
	help
	  This option sets flash pages count used by OpenThread to store its settings. They are located at the end of flash.

endif
+349 −0
Original line number Diff line number Diff line
/*
 * Copyright (c) 2018 Nordic Semiconductor ASA
 *
 * SPDX-License-Identifier: Apache-2.0
 */

#define NET_SYS_LOG_LEVEL CONFIG_OPENTHREAD_L2_LOG_LEVEL

#if defined(CONFIG_OPENTHREAD_L2_DEBUG)
#define NET_DOMAIN "net/openthread_l2"
#define NET_LOG_ENABLED 1
#endif

#include <net/net_core.h>
#include <net/net_pkt.h>
#include <net/net_mgmt.h>
#include <net/openthread.h>

#include <net_private.h>

#include <init.h>
#include <misc/util.h>
#include <misc/__assert.h>
#include <openthread/openthread.h>
#include <openthread/cli.h>
#include <openthread/platform/platform.h>

#include "openthread_utils.h"

#define OT_STACK_SIZE (CONFIG_OPENTHREAD_THREAD_STACK_SIZE)
#define OT_PRIORITY K_PRIO_COOP(CONFIG_OPENTHREAD_THREAD_PRIORITY)

extern void platformShellInit(otInstance *aInstance);

K_SEM_DEFINE(ot_sem, 0, 1);

K_THREAD_STACK_DEFINE(ot_stack_area, OT_STACK_SIZE);
static struct k_thread ot_thread_data;
static k_tid_t ot_tid;
static struct net_linkaddr *ll_addr;

static struct net_mgmt_event_callback ip6_addr_cb;

static void ipv6_addr_event_handler(struct net_mgmt_event_callback *cb,
				    u32_t mgmt_event, struct net_if *iface)
{
	struct openthread_context *ot_context = net_if_l2_data(iface);

	if (mgmt_event == NET_EVENT_IPV6_ADDR_ADD) {
		add_ipv6_addr_to_ot(ot_context);
	} else if (mgmt_event == NET_EVENT_IPV6_MADDR_ADD) {
		add_ipv6_maddr_to_ot(ot_context);
	}
}

void otPlatRadioGetIeeeEui64(otInstance *instance, uint8_t *ieee_eui64)
{
	ARG_UNUSED(instance);

	memcpy(ieee_eui64, ll_addr->addr, ll_addr->len);
}

void otTaskletsSignalPending(otInstance *instance)
{
	k_sem_give(&ot_sem);
}

void PlatformEventSignalPending(void)
{
	k_sem_give(&ot_sem);
}

void ot_state_changed_handler(u32_t flags, void *context)
{
	struct openthread_context *ot_context = context;

	NET_INFO("State changed! Flags: 0x%08x Current role: %d",
		    flags, otThreadGetDeviceRole(ot_context->instance));

	if (flags & OT_CHANGED_IP6_ADDRESS_REMOVED) {
		NET_DBG("Ipv6 address removed");
		rm_ipv6_maddr_from_zephyr(ot_context);
	}

	if (flags & OT_CHANGED_IP6_ADDRESS_ADDED) {
		NET_DBG("Ipv6 address added");
		add_ipv6_maddr_to_zephyr(ot_context);
	}

}

void ot_receive_handler(otMessage *aMessage, void *context)
{
	struct openthread_context *ot_context = context;

	u16_t offset = 0;
	u16_t read_len;
	struct net_pkt *pkt;
	struct net_buf *prev_buf = NULL;

	pkt = net_pkt_get_reserve_rx(0, K_NO_WAIT);
	if (!pkt) {
		NET_ERR("Failed to reserve net pkt");
		goto out;
	}

	while (1) {
		struct net_buf *pkt_buf;

		pkt_buf = net_pkt_get_frag(pkt, K_NO_WAIT);
		if (!pkt_buf) {
			NET_ERR("Failed to get fragment buf");
			net_pkt_unref(pkt);
			goto out;
		}

		read_len = otMessageRead(aMessage,
					 offset,
					 pkt_buf->data,
					 net_buf_tailroom(pkt_buf));

		if (!read_len) {
			net_buf_unref(pkt_buf);
			break;
		}

		net_buf_add(pkt_buf, read_len);

		if (!prev_buf) {
			net_pkt_frag_insert(pkt, pkt_buf);
		} else {
			net_buf_frag_insert(prev_buf, pkt_buf);
		}

		prev_buf = pkt_buf;

		offset += read_len;
	}

	NET_DBG("Injecting Ip6 packet to Zephyr net stack");

#if defined(CONFIG_OPENTHREAD_L2_DEBUG_DUMP_IPV6)
	net_hexdump_frags("Received IPv6 packet", pkt, true);
#endif

	if (!pkt_list_is_full(ot_context)) {
		if (net_recv_data(ot_context->iface, pkt) < 0) {
			NET_ERR("net_recv_data failed");
			goto out;
		}

		pkt_list_add(ot_context, pkt);
		pkt = NULL;
	} else {
		NET_INFO("Pacet list is full");
	}
out:
	if (pkt) {
		net_pkt_unref(pkt);
	}

	otMessageFree(aMessage);
}

static void openthread_process(void *context, void *arg2, void *arg3)
{
	struct openthread_context *ot_context = context;

	while (1) {
		while (otTaskletsArePending(ot_context->instance)) {
			otTaskletsProcess(ot_context->instance);
		}

		PlatformProcessDrivers(ot_context->instance);

		k_sem_take(&ot_sem, K_FOREVER);
	}
}

static enum net_verdict openthread_recv(struct net_if *iface,
					struct net_pkt *pkt)
{
	struct openthread_context *ot_context = net_if_l2_data(iface);

	if (pkt_list_peek(ot_context) == pkt) {
		pkt_list_remove_last(ot_context);
		NET_DBG("Got injected Ip6 packet, "
			    "sending to upper layers");
#if defined(CONFIG_OPENTHREAD_L2_DEBUG_DUMP_IPV6)
		net_hexdump_frags("Injected IPv6 packet", pkt, true);
#endif
		return NET_CONTINUE;
	}

	NET_DBG("Got 802.15.4 packet, sending to OT");

	otRadioFrame recv_frame;

	recv_frame.mPsdu = net_buf_frag_last(pkt->frags)->data;
	/* Length inc. CRC. */
	recv_frame.mLength = net_buf_frags_len(pkt->frags);
	/* TODO: get channel from packet */
	recv_frame.mChannel = CONFIG_OPENTHREAD_CHANNEL;
	recv_frame.mLqi = net_pkt_ieee802154_lqi(pkt);
	recv_frame.mPower = net_pkt_ieee802154_rssi(pkt);

#if defined(CONFIG_OPENTHREAD_L2_DEBUG_DUMP_15_4)
	net_hexdump_frags("Received 802.15.4 frame", pkt, true);
#endif

#if OPENTHREAD_ENABLE_DIAG
	if (otPlatDiagModeGet()) {
		otPlatDiagRadioReceiveDone(ot_context->instance,
					   &recv_frame, OT_ERROR_NONE);
	} else
#endif
	{
		otPlatRadioReceiveDone(ot_context->instance,
				       &recv_frame, OT_ERROR_NONE);
	}

	net_pkt_unref(pkt);

	return NET_OK;
}

enum net_verdict openthread_send(struct net_if *iface, struct net_pkt *pkt)
{
	struct openthread_context *ot_context = net_if_l2_data(iface);
	enum net_verdict ret = NET_OK;
	otMessage *message;
	struct net_buf *frag;

	NET_DBG("Sending Ip6 packet to ot stack");

	message = otIp6NewMessage(ot_context->instance, true);
	if (message == NULL) {
		goto exit;
	}

	for (frag = pkt->frags; frag; frag = frag->frags) {
		if (otMessageAppend(message, frag->data,
				    frag->len) != OT_ERROR_NONE) {

			NET_ERR("Error while appending to otMessage");
			otMessageFree(message);
			goto exit;
		}
	}

	if (otIp6Send(ot_context->instance, message) != OT_ERROR_NONE) {
		NET_ERR("Error while calling otIp6Send");
		goto exit;
	}

#if defined(CONFIG_OPENTHREAD_L2_DEBUG_DUMP_IPV6)
	net_hexdump_frags("Sent IPv6 packet", pkt, true);
#endif

exit:
	net_pkt_unref(pkt);

	return ret;
}

static u16_t openthread_reserve(struct net_if *iface, void *arg)
{
	ARG_UNUSED(iface);
	ARG_UNUSED(arg);

	return 0;
}

enum net_verdict ieee802154_radio_handle_ack(struct net_if *iface,
					     struct net_buf *buf)
{
	ARG_UNUSED(iface);
	ARG_UNUSED(buf);
	NET_DBG("");

	return NET_CONTINUE;
}

static int openthread_init(struct net_if *iface)
{
	struct openthread_context *ot_context = net_if_l2_data(iface);

	NET_DBG("openthread_init");

	PlatformInit(0, NULL);

	ot_context->instance = otInstanceInitSingle();
	ot_context->iface = iface;

	__ASSERT(ot_context->instance, "OT instance is NULL");

#if defined(CONFIG_OPENTHREAD_SHELL)
	platformShellInit(ot_context->instance);
#endif

	NET_INFO("OpenThread version: %s",
		    otGetVersionString());
	NET_INFO("Network name:   %s",
		    otThreadGetNetworkName(ot_context->instance));

	otLinkSetChannel(ot_context->instance, CONFIG_OPENTHREAD_CHANNEL);
	otLinkSetPanId(ot_context->instance, CONFIG_OPENTHREAD_PANID);
	otIp6SetEnabled(ot_context->instance, true);
	otThreadSetEnabled(ot_context->instance, true);
	otIp6SetReceiveFilterEnabled(ot_context->instance, true);
	otIp6SetReceiveCallback(ot_context->instance,
				ot_receive_handler, ot_context);
	otSetStateChangedCallback(ot_context->instance,
				  &ot_state_changed_handler, ot_context);

	ll_addr = net_if_get_link_addr(iface);

	net_mgmt_init_event_callback(&ip6_addr_cb, ipv6_addr_event_handler,
				     NET_EVENT_IPV6_ADDR_ADD |
				     NET_EVENT_IPV6_MADDR_ADD);
	net_mgmt_add_event_callback(&ip6_addr_cb);

	ot_tid = k_thread_create(&ot_thread_data, ot_stack_area,
				 K_THREAD_STACK_SIZEOF(ot_stack_area),
				 openthread_process,
				 ot_context, NULL, NULL,
				 OT_PRIORITY, 0, K_NO_WAIT);

	return 0;
}

void ieee802154_init(struct net_if *iface)
{
	openthread_init(iface);
}

int ieee802154_radio_send(struct net_if *iface, struct net_pkt *pkt)
{
	ARG_UNUSED(iface);
	ARG_UNUSED(pkt);

	/* Shouldn't be here */
	__ASSERT(false, "OpenThread L2 should never reach here");

	return -EIO;
}

NET_L2_INIT(OPENTHREAD_L2, openthread_recv, openthread_send,
	    openthread_reserve, NULL);
Loading