Implementation of functionality whitch configure CIDR for OT NAT64. Signed-off-by: Marcin Gasiorek <marcin.gasiorek@nordicsemi.no>
725 lines
17 KiB
C
725 lines
17 KiB
C
/*
|
|
* Copyright (c) 2018 Nordic Semiconductor ASA
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <zephyr/logging/log.h>
|
|
LOG_MODULE_REGISTER(net_l2_openthread, CONFIG_OPENTHREAD_L2_LOG_LEVEL);
|
|
|
|
#include <zephyr/net/net_core.h>
|
|
#include <zephyr/net/net_pkt.h>
|
|
#include <zephyr/net/net_mgmt.h>
|
|
#include <zephyr/net/openthread.h>
|
|
|
|
#include <net_private.h>
|
|
|
|
#include <zephyr/init.h>
|
|
#include <zephyr/sys/check.h>
|
|
#include <zephyr/sys/slist.h>
|
|
#include <zephyr/sys/util.h>
|
|
#include <zephyr/sys/__assert.h>
|
|
#include <zephyr/version.h>
|
|
|
|
#include <openthread/cli.h>
|
|
#include <openthread/ip6.h>
|
|
#include <openthread/link.h>
|
|
#include <openthread/link_raw.h>
|
|
#include <openthread/ncp.h>
|
|
#include <openthread/message.h>
|
|
#include <openthread/platform/diag.h>
|
|
#include <openthread/tasklet.h>
|
|
#include <openthread/thread.h>
|
|
#include <openthread/dataset.h>
|
|
#include <openthread/joiner.h>
|
|
#include <openthread-system.h>
|
|
#include <utils/uart.h>
|
|
|
|
#include <platform-zephyr.h>
|
|
|
|
#include "openthread_utils.h"
|
|
|
|
#if defined(CONFIG_OPENTHREAD_NAT64_TRANSLATOR)
|
|
#include <openthread/nat64.h>
|
|
#endif /* CONFIG_OPENTHREAD_NAT64_TRANSLATOR */
|
|
|
|
#define PKT_IS_IPv4(_p) ((NET_IPV6_HDR(_p)->vtc & 0xf0) == 0x40)
|
|
|
|
#define OT_STACK_SIZE (CONFIG_OPENTHREAD_THREAD_STACK_SIZE)
|
|
|
|
#if defined(CONFIG_OPENTHREAD_THREAD_PREEMPTIVE)
|
|
#define OT_PRIORITY K_PRIO_PREEMPT(CONFIG_OPENTHREAD_THREAD_PRIORITY)
|
|
#else
|
|
#define OT_PRIORITY K_PRIO_COOP(CONFIG_OPENTHREAD_THREAD_PRIORITY)
|
|
#endif
|
|
|
|
#if defined(CONFIG_OPENTHREAD_NETWORK_NAME)
|
|
#define OT_NETWORK_NAME CONFIG_OPENTHREAD_NETWORK_NAME
|
|
#else
|
|
#define OT_NETWORK_NAME ""
|
|
#endif
|
|
|
|
#if defined(CONFIG_OPENTHREAD_CHANNEL)
|
|
#define OT_CHANNEL CONFIG_OPENTHREAD_CHANNEL
|
|
#else
|
|
#define OT_CHANNEL 0
|
|
#endif
|
|
|
|
#if defined(CONFIG_OPENTHREAD_PANID)
|
|
#define OT_PANID CONFIG_OPENTHREAD_PANID
|
|
#else
|
|
#define OT_PANID 0
|
|
#endif
|
|
|
|
#if defined(CONFIG_OPENTHREAD_XPANID)
|
|
#define OT_XPANID CONFIG_OPENTHREAD_XPANID
|
|
#else
|
|
#define OT_XPANID ""
|
|
#endif
|
|
|
|
#if defined(CONFIG_OPENTHREAD_NETWORKKEY)
|
|
#define OT_NETWORKKEY CONFIG_OPENTHREAD_NETWORKKEY
|
|
#else
|
|
#define OT_NETWORKKEY ""
|
|
#endif
|
|
|
|
#if defined(CONFIG_OPENTHREAD_JOINER_PSKD)
|
|
#define OT_JOINER_PSKD CONFIG_OPENTHREAD_JOINER_PSKD
|
|
#else
|
|
#define OT_JOINER_PSKD ""
|
|
#endif
|
|
|
|
#if defined(CONFIG_OPENTHREAD_PLATFORM_INFO)
|
|
#define OT_PLATFORM_INFO CONFIG_OPENTHREAD_PLATFORM_INFO
|
|
#else
|
|
#define OT_PLATFORM_INFO ""
|
|
#endif
|
|
|
|
#if defined(CONFIG_OPENTHREAD_POLL_PERIOD)
|
|
#define OT_POLL_PERIOD CONFIG_OPENTHREAD_POLL_PERIOD
|
|
#else
|
|
#define OT_POLL_PERIOD 0
|
|
#endif
|
|
|
|
#define PACKAGE_NAME "Zephyr"
|
|
#define PACKAGE_VERSION KERNEL_VERSION_STRING
|
|
|
|
extern void platformShellInit(otInstance *aInstance);
|
|
|
|
K_KERNEL_STACK_DEFINE(ot_stack_area, OT_STACK_SIZE);
|
|
|
|
static struct net_linkaddr *ll_addr;
|
|
static otStateChangedCallback state_changed_cb;
|
|
|
|
k_tid_t openthread_thread_id_get(void)
|
|
{
|
|
struct openthread_context *ot_context = openthread_get_default_context();
|
|
|
|
return ot_context ? (k_tid_t)&ot_context->work_q.thread : 0;
|
|
}
|
|
|
|
#ifdef CONFIG_NET_MGMT_EVENT
|
|
static struct net_mgmt_event_callback ip6_addr_cb;
|
|
|
|
static void ipv6_addr_event_handler(struct net_mgmt_event_callback *cb,
|
|
uint32_t mgmt_event, struct net_if *iface)
|
|
{
|
|
if (net_if_l2(iface) != &NET_L2_GET_NAME(OPENTHREAD)) {
|
|
return;
|
|
}
|
|
|
|
#ifdef CONFIG_NET_MGMT_EVENT_INFO
|
|
struct openthread_context *ot_context = net_if_l2_data(iface);
|
|
|
|
if (cb->info == NULL || cb->info_length != sizeof(struct in6_addr)) {
|
|
return;
|
|
}
|
|
|
|
if (mgmt_event == NET_EVENT_IPV6_ADDR_ADD) {
|
|
add_ipv6_addr_to_ot(ot_context, (const struct in6_addr *)cb->info);
|
|
} else if (mgmt_event == NET_EVENT_IPV6_MADDR_ADD) {
|
|
add_ipv6_maddr_to_ot(ot_context, (const struct in6_addr *)cb->info);
|
|
}
|
|
#else
|
|
NET_WARN("No address info provided with event, "
|
|
"please enable CONFIG_NET_MGMT_EVENT_INFO");
|
|
#endif /* CONFIG_NET_MGMT_EVENT_INFO */
|
|
}
|
|
#endif /* CONFIG_NET_MGMT_EVENT */
|
|
|
|
static int ncp_hdlc_send(const uint8_t *buf, uint16_t len)
|
|
{
|
|
otError err;
|
|
|
|
err = otPlatUartSend(buf, len);
|
|
if (err != OT_ERROR_NONE) {
|
|
return 0;
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
void otPlatRadioGetIeeeEui64(otInstance *instance, uint8_t *ieee_eui64)
|
|
{
|
|
ARG_UNUSED(instance);
|
|
|
|
memcpy(ieee_eui64, ll_addr->addr, ll_addr->len);
|
|
}
|
|
|
|
void otTaskletsSignalPending(otInstance *instance)
|
|
{
|
|
struct openthread_context *ot_context = openthread_get_default_context();
|
|
|
|
if (ot_context) {
|
|
k_work_submit_to_queue(&ot_context->work_q, &ot_context->api_work);
|
|
}
|
|
}
|
|
|
|
void otSysEventSignalPending(void)
|
|
{
|
|
otTaskletsSignalPending(NULL);
|
|
}
|
|
|
|
static void ot_state_changed_handler(uint32_t flags, void *context)
|
|
{
|
|
struct openthread_state_changed_cb *entry, *next;
|
|
struct openthread_context *ot_context = context;
|
|
|
|
NET_INFO("State changed! Flags: 0x%08" PRIx32 " Current role: %s",
|
|
flags,
|
|
otThreadDeviceRoleToString(otThreadGetDeviceRole(ot_context->instance))
|
|
);
|
|
|
|
if (flags & OT_CHANGED_THREAD_ROLE) {
|
|
switch (otThreadGetDeviceRole(ot_context->instance)) {
|
|
case OT_DEVICE_ROLE_CHILD:
|
|
case OT_DEVICE_ROLE_ROUTER:
|
|
case OT_DEVICE_ROLE_LEADER:
|
|
net_if_dormant_off(ot_context->iface);
|
|
break;
|
|
|
|
case OT_DEVICE_ROLE_DISABLED:
|
|
case OT_DEVICE_ROLE_DETACHED:
|
|
default:
|
|
net_if_dormant_on(ot_context->iface);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (flags & OT_CHANGED_IP6_ADDRESS_REMOVED) {
|
|
NET_DBG("Ipv6 address removed");
|
|
rm_ipv6_addr_from_zephyr(ot_context);
|
|
}
|
|
|
|
if (flags & OT_CHANGED_IP6_ADDRESS_ADDED) {
|
|
NET_DBG("Ipv6 address added");
|
|
add_ipv6_addr_to_zephyr(ot_context);
|
|
}
|
|
|
|
if (flags & OT_CHANGED_IP6_MULTICAST_UNSUBSCRIBED) {
|
|
NET_DBG("Ipv6 multicast address removed");
|
|
rm_ipv6_maddr_from_zephyr(ot_context);
|
|
}
|
|
|
|
if (flags & OT_CHANGED_IP6_MULTICAST_SUBSCRIBED) {
|
|
NET_DBG("Ipv6 multicast address added");
|
|
add_ipv6_maddr_to_zephyr(ot_context);
|
|
}
|
|
|
|
#if defined(CONFIG_OPENTHREAD_NAT64_TRANSLATOR)
|
|
|
|
if (flags & OT_CHANGED_NAT64_TRANSLATOR_STATE) {
|
|
NET_DBG("Nat64 translator state changed to %x",
|
|
otNat64GetTranslatorState(ot_context->instance));
|
|
}
|
|
|
|
#endif /* CONFIG_OPENTHREAD_NAT64_TRANSLATOR */
|
|
|
|
if (state_changed_cb) {
|
|
state_changed_cb(flags, context);
|
|
}
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&ot_context->state_change_cbs, entry, next, node) {
|
|
if (entry->state_changed_cb != NULL) {
|
|
entry->state_changed_cb(flags, ot_context, entry->user_data);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ot_receive_handler(otMessage *aMessage, void *context)
|
|
{
|
|
struct openthread_context *ot_context = context;
|
|
|
|
uint16_t offset = 0U;
|
|
uint16_t read_len;
|
|
struct net_pkt *pkt;
|
|
struct net_buf *pkt_buf;
|
|
|
|
pkt = net_pkt_rx_alloc_with_buffer(ot_context->iface,
|
|
otMessageGetLength(aMessage),
|
|
AF_UNSPEC, 0, K_NO_WAIT);
|
|
if (!pkt) {
|
|
NET_ERR("Failed to reserve net pkt");
|
|
goto out;
|
|
}
|
|
|
|
pkt_buf = pkt->buffer;
|
|
|
|
while (1) {
|
|
read_len = otMessageRead(aMessage, offset, pkt_buf->data,
|
|
net_buf_tailroom(pkt_buf));
|
|
if (!read_len) {
|
|
break;
|
|
}
|
|
|
|
net_buf_add(pkt_buf, read_len);
|
|
|
|
offset += read_len;
|
|
|
|
if (!net_buf_tailroom(pkt_buf)) {
|
|
pkt_buf = pkt_buf->frags;
|
|
if (!pkt_buf) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
NET_DBG("Injecting %s packet to Zephyr net stack",
|
|
PKT_IS_IPv4(pkt) ? "translated IPv4" : "Ip6");
|
|
|
|
if (IS_ENABLED(CONFIG_OPENTHREAD_L2_DEBUG_DUMP_IPV6)) {
|
|
if (IS_ENABLED(CONFIG_OPENTHREAD_NAT64_TRANSLATOR) && PKT_IS_IPv4(pkt)) {
|
|
net_pkt_hexdump(pkt, "Received NAT64 IPv4 packet");
|
|
} else {
|
|
net_pkt_hexdump(pkt, "Received IPv6 packet");
|
|
}
|
|
}
|
|
|
|
if (!pkt_list_is_full(ot_context)) {
|
|
if (pkt_list_add(ot_context, pkt) != 0) {
|
|
NET_ERR("pkt_list_add failed");
|
|
goto out;
|
|
}
|
|
|
|
if (net_recv_data(ot_context->iface, pkt) < 0) {
|
|
NET_ERR("net_recv_data failed");
|
|
pkt_list_remove_first(ot_context);
|
|
goto out;
|
|
}
|
|
|
|
pkt = NULL;
|
|
} else {
|
|
NET_INFO("Packet list is full");
|
|
}
|
|
out:
|
|
if (pkt) {
|
|
net_pkt_unref(pkt);
|
|
}
|
|
|
|
otMessageFree(aMessage);
|
|
}
|
|
|
|
static void ot_joiner_start_handler(otError error, void *context)
|
|
{
|
|
struct openthread_context *ot_context = context;
|
|
|
|
switch (error) {
|
|
case OT_ERROR_NONE:
|
|
NET_INFO("Join success");
|
|
otThreadSetEnabled(ot_context->instance, true);
|
|
break;
|
|
default:
|
|
NET_ERR("Join failed [%d]", error);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void openthread_process(struct k_work *work)
|
|
{
|
|
struct openthread_context *ot_context
|
|
= CONTAINER_OF(work, struct openthread_context, api_work);
|
|
|
|
openthread_api_mutex_lock(ot_context);
|
|
|
|
while (otTaskletsArePending(ot_context->instance)) {
|
|
otTaskletsProcess(ot_context->instance);
|
|
}
|
|
|
|
otSysProcessDrivers(ot_context->instance);
|
|
|
|
openthread_api_mutex_unlock(ot_context);
|
|
}
|
|
|
|
static bool is_ipv6_frag(struct net_pkt *pkt)
|
|
{
|
|
NET_PKT_DATA_ACCESS_CONTIGUOUS_DEFINE(ipv6_access, struct net_ipv6_hdr);
|
|
struct net_ipv6_hdr *hdr;
|
|
|
|
hdr = (struct net_ipv6_hdr *)net_pkt_get_data(pkt, &ipv6_access);
|
|
if (!hdr) {
|
|
return false;
|
|
}
|
|
|
|
return hdr->nexthdr == NET_IPV6_NEXTHDR_FRAG ? true : false;
|
|
}
|
|
|
|
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 (IS_ENABLED(CONFIG_OPENTHREAD_L2_DEBUG_DUMP_IPV6)) {
|
|
net_pkt_hexdump(pkt, "Injected IPv6 packet");
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_OPENTHREAD_IP6_FRAGM) && is_ipv6_frag(pkt)) {
|
|
return NET_DROP;
|
|
}
|
|
|
|
return NET_CONTINUE;
|
|
}
|
|
|
|
NET_DBG("Got 802.15.4 packet, sending to OT");
|
|
|
|
if (IS_ENABLED(CONFIG_OPENTHREAD_L2_DEBUG_DUMP_IPV6)) {
|
|
net_pkt_hexdump(pkt, "Received 802.15.4 frame");
|
|
}
|
|
|
|
if (notify_new_rx_frame(pkt) != 0) {
|
|
NET_ERR("Failed to queue RX packet for OpenThread");
|
|
return NET_DROP;
|
|
}
|
|
|
|
return NET_OK;
|
|
}
|
|
|
|
int openthread_send(struct net_if *iface, struct net_pkt *pkt)
|
|
{
|
|
int len = net_pkt_get_len(pkt);
|
|
|
|
if (IS_ENABLED(CONFIG_OPENTHREAD_L2_DEBUG_DUMP_IPV6)) {
|
|
net_pkt_hexdump(pkt, "IPv6 packet to send");
|
|
}
|
|
|
|
net_capture_pkt(iface, pkt);
|
|
|
|
if (notify_new_tx_frame(pkt) != 0) {
|
|
net_pkt_unref(pkt);
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
int openthread_start(struct openthread_context *ot_context)
|
|
{
|
|
otInstance *ot_instance = ot_context->instance;
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
openthread_api_mutex_lock(ot_context);
|
|
|
|
NET_INFO("OpenThread version: %s", otGetVersionString());
|
|
|
|
if (IS_ENABLED(CONFIG_OPENTHREAD_COPROCESSOR)) {
|
|
NET_DBG("OpenThread co-processor.");
|
|
goto exit;
|
|
}
|
|
|
|
otIp6SetEnabled(ot_context->instance, true);
|
|
|
|
/* Sleepy End Device specific configuration. */
|
|
if (IS_ENABLED(CONFIG_OPENTHREAD_MTD_SED)) {
|
|
otLinkModeConfig ot_mode = otThreadGetLinkMode(ot_instance);
|
|
|
|
/* A SED should always attach the network as a SED to indicate
|
|
* increased buffer requirement to a parent.
|
|
*/
|
|
ot_mode.mRxOnWhenIdle = false;
|
|
|
|
otThreadSetLinkMode(ot_context->instance, ot_mode);
|
|
otLinkSetPollPeriod(ot_context->instance, OT_POLL_PERIOD);
|
|
}
|
|
|
|
if (otDatasetIsCommissioned(ot_instance)) {
|
|
/* OpenThread already has dataset stored - skip the
|
|
* configuration.
|
|
*/
|
|
NET_DBG("OpenThread already commissioned.");
|
|
} else if (IS_ENABLED(CONFIG_OPENTHREAD_JOINER_AUTOSTART)) {
|
|
/* No dataset - initiate network join procedure. */
|
|
NET_DBG("Starting OpenThread join procedure.");
|
|
|
|
error = otJoinerStart(ot_instance, OT_JOINER_PSKD, NULL,
|
|
PACKAGE_NAME, OT_PLATFORM_INFO,
|
|
PACKAGE_VERSION, NULL,
|
|
&ot_joiner_start_handler, ot_context);
|
|
|
|
if (error != OT_ERROR_NONE) {
|
|
NET_ERR("Failed to start joiner [%d]", error);
|
|
}
|
|
|
|
goto exit;
|
|
} else {
|
|
/* No dataset - load the default configuration. */
|
|
NET_DBG("Loading OpenThread default configuration.");
|
|
|
|
otExtendedPanId xpanid;
|
|
otNetworkKey networkKey;
|
|
|
|
otThreadSetNetworkName(ot_instance, OT_NETWORK_NAME);
|
|
otLinkSetChannel(ot_instance, OT_CHANNEL);
|
|
otLinkSetPanId(ot_instance, OT_PANID);
|
|
net_bytes_from_str(xpanid.m8, 8, (char *)OT_XPANID);
|
|
otThreadSetExtendedPanId(ot_instance, &xpanid);
|
|
|
|
if (strlen(OT_NETWORKKEY)) {
|
|
net_bytes_from_str(networkKey.m8, OT_NETWORK_KEY_SIZE,
|
|
(char *)OT_NETWORKKEY);
|
|
otThreadSetNetworkKey(ot_instance, &networkKey);
|
|
}
|
|
}
|
|
|
|
NET_INFO("Network name: %s",
|
|
otThreadGetNetworkName(ot_instance));
|
|
|
|
/* Start the network. */
|
|
error = otThreadSetEnabled(ot_instance, true);
|
|
if (error != OT_ERROR_NONE) {
|
|
NET_ERR("Failed to start the OpenThread network [%d]", error);
|
|
}
|
|
|
|
exit:
|
|
|
|
openthread_api_mutex_unlock(ot_context);
|
|
|
|
return error == OT_ERROR_NONE ? 0 : -EIO;
|
|
}
|
|
|
|
int openthread_stop(struct openthread_context *ot_context)
|
|
{
|
|
otError error;
|
|
|
|
if (IS_ENABLED(CONFIG_OPENTHREAD_COPROCESSOR)) {
|
|
return 0;
|
|
}
|
|
|
|
openthread_api_mutex_lock(ot_context);
|
|
|
|
error = otThreadSetEnabled(ot_context->instance, false);
|
|
if (error == OT_ERROR_INVALID_STATE) {
|
|
NET_DBG("Openthread interface was not up [%d]", error);
|
|
}
|
|
|
|
openthread_api_mutex_unlock(ot_context);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int openthread_init(struct net_if *iface)
|
|
{
|
|
struct openthread_context *ot_context = net_if_l2_data(iface);
|
|
struct k_work_queue_config q_cfg = {
|
|
.name = "openthread",
|
|
.no_yield = true,
|
|
};
|
|
otError err;
|
|
|
|
NET_DBG("openthread_init");
|
|
|
|
k_mutex_init(&ot_context->api_lock);
|
|
k_work_init(&ot_context->api_work, openthread_process);
|
|
|
|
ll_addr = net_if_get_link_addr(iface);
|
|
|
|
openthread_api_mutex_lock(ot_context);
|
|
|
|
otSysInit(0, NULL);
|
|
|
|
ot_context->instance = otInstanceInitSingle();
|
|
ot_context->iface = iface;
|
|
|
|
__ASSERT(ot_context->instance, "OT instance is NULL");
|
|
|
|
if (IS_ENABLED(CONFIG_OPENTHREAD_SHELL)) {
|
|
platformShellInit(ot_context->instance);
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_OPENTHREAD_COPROCESSOR)) {
|
|
otPlatUartEnable();
|
|
otNcpHdlcInit(ot_context->instance, ncp_hdlc_send);
|
|
} else {
|
|
otIp6SetReceiveFilterEnabled(ot_context->instance, true);
|
|
otIp6SetReceiveCallback(ot_context->instance,
|
|
ot_receive_handler, ot_context);
|
|
|
|
#if defined(CONFIG_OPENTHREAD_NAT64_TRANSLATOR)
|
|
|
|
otIp4Cidr nat64_cidr;
|
|
|
|
if (otIp4CidrFromString(CONFIG_OPENTHREAD_NAT64_CIDR, &nat64_cidr) ==
|
|
OT_ERROR_NONE) {
|
|
if (otNat64SetIp4Cidr(openthread_get_default_instance(), &nat64_cidr) !=
|
|
OT_ERROR_NONE) {
|
|
NET_ERR("Incorrect NAT64 CIDR");
|
|
}
|
|
} else {
|
|
NET_ERR("Failed to parse NAT64 CIDR");
|
|
}
|
|
otNat64SetReceiveIp4Callback(ot_context->instance, ot_receive_handler, ot_context);
|
|
|
|
#endif /* CONFIG_OPENTHREAD_NAT64_TRANSLATOR */
|
|
|
|
sys_slist_init(&ot_context->state_change_cbs);
|
|
err = otSetStateChangedCallback(ot_context->instance,
|
|
&ot_state_changed_handler,
|
|
ot_context);
|
|
if (err != OT_ERROR_NONE) {
|
|
NET_ERR("Could not set state changed callback: %d", err);
|
|
}
|
|
|
|
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);
|
|
|
|
net_if_dormant_on(iface);
|
|
}
|
|
|
|
openthread_api_mutex_unlock(ot_context);
|
|
|
|
k_work_queue_start(&ot_context->work_q, ot_stack_area,
|
|
K_KERNEL_STACK_SIZEOF(ot_stack_area),
|
|
OT_PRIORITY, &q_cfg);
|
|
|
|
(void)k_work_submit_to_queue(&ot_context->work_q, &ot_context->api_work);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void ieee802154_init(struct net_if *iface)
|
|
{
|
|
if (IS_ENABLED(CONFIG_IEEE802154_NET_IF_NO_AUTO_START)) {
|
|
LOG_DBG("Interface auto start disabled.");
|
|
net_if_flag_set(iface, NET_IF_NO_AUTO_START);
|
|
}
|
|
|
|
net_if_flag_set(iface, NET_IF_IPV6_NO_ND);
|
|
net_if_flag_set(iface, NET_IF_IPV6_NO_MLD);
|
|
|
|
openthread_init(iface);
|
|
}
|
|
|
|
static enum net_l2_flags openthread_flags(struct net_if *iface)
|
|
{
|
|
/* TODO: Should report NET_L2_PROMISC_MODE if the radio driver
|
|
* reports the IEEE802154_HW_PROMISC capability.
|
|
*/
|
|
return NET_L2_MULTICAST | NET_L2_MULTICAST_SKIP_JOIN_SOLICIT_NODE;
|
|
}
|
|
|
|
static int openthread_enable(struct net_if *iface, bool state)
|
|
{
|
|
struct openthread_context *ot_context = net_if_l2_data(iface);
|
|
|
|
NET_DBG("iface %p %s", iface, state ? "up" : "down");
|
|
|
|
if (state) {
|
|
if (IS_ENABLED(CONFIG_OPENTHREAD_MANUAL_START)) {
|
|
NET_DBG("OpenThread manual start.");
|
|
return 0;
|
|
}
|
|
|
|
return openthread_start(ot_context);
|
|
}
|
|
|
|
return openthread_stop(ot_context);
|
|
}
|
|
|
|
struct openthread_context *openthread_get_default_context(void)
|
|
{
|
|
struct net_if *iface;
|
|
struct openthread_context *ot_context = NULL;
|
|
|
|
iface = net_if_get_first_by_type(&NET_L2_GET_NAME(OPENTHREAD));
|
|
if (!iface) {
|
|
NET_ERR("There is no net interface for OpenThread");
|
|
goto exit;
|
|
}
|
|
|
|
ot_context = net_if_l2_data(iface);
|
|
if (!ot_context) {
|
|
NET_ERR("There is no Openthread context in net interface data");
|
|
goto exit;
|
|
}
|
|
|
|
exit:
|
|
return ot_context;
|
|
}
|
|
|
|
struct otInstance *openthread_get_default_instance(void)
|
|
{
|
|
struct openthread_context *ot_context =
|
|
openthread_get_default_context();
|
|
|
|
return ot_context ? ot_context->instance : NULL;
|
|
}
|
|
|
|
int openthread_state_changed_cb_register(struct openthread_context *ot_context,
|
|
struct openthread_state_changed_cb *cb)
|
|
{
|
|
CHECKIF(cb == NULL || cb->state_changed_cb == NULL) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
openthread_api_mutex_lock(ot_context);
|
|
sys_slist_append(&ot_context->state_change_cbs, &cb->node);
|
|
openthread_api_mutex_unlock(ot_context);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int openthread_state_changed_cb_unregister(struct openthread_context *ot_context,
|
|
struct openthread_state_changed_cb *cb)
|
|
{
|
|
bool removed;
|
|
|
|
CHECKIF(cb == NULL) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
openthread_api_mutex_lock(ot_context);
|
|
removed = sys_slist_find_and_remove(&ot_context->state_change_cbs, &cb->node);
|
|
openthread_api_mutex_unlock(ot_context);
|
|
|
|
if (!removed) {
|
|
return -EALREADY;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void openthread_set_state_changed_cb(otStateChangedCallback cb)
|
|
{
|
|
state_changed_cb = cb;
|
|
}
|
|
|
|
void openthread_api_mutex_lock(struct openthread_context *ot_context)
|
|
{
|
|
(void)k_mutex_lock(&ot_context->api_lock, K_FOREVER);
|
|
}
|
|
|
|
int openthread_api_mutex_try_lock(struct openthread_context *ot_context)
|
|
{
|
|
return k_mutex_lock(&ot_context->api_lock, K_NO_WAIT);
|
|
}
|
|
|
|
void openthread_api_mutex_unlock(struct openthread_context *ot_context)
|
|
{
|
|
(void)k_mutex_unlock(&ot_context->api_lock);
|
|
}
|
|
|
|
NET_L2_INIT(OPENTHREAD_L2, openthread_recv, openthread_send, openthread_enable,
|
|
openthread_flags);
|