If CONFIG_OPENTHREAD_SYS_INIT is enabled, OpenThread initialisation should also consider running OpenThread automatically if CONFIG_OPENTHREAD_MANUAL_START is disabled. Removed also dependency on the `net_bytes_from_str` functions from the openthread.h file. Now, in the OpenThread module, there is an additional `openthread_utils.c/.h` file that can implement useful utilities for the OpenThread platform. Currently, it implements the `bytes_from_str` function to use it instead of `net_bytes_from_str`. Signed-off-by: Arkadiusz Balys <arkadiusz.balys@nordicsemi.no>
511 lines
12 KiB
C
511 lines
12 KiB
C
/*
|
|
* Copyright (c) 2025 Nordic Semiconductor ASA
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
/**
|
|
* @file
|
|
* This file implements the OpenThread module initialization and state change handling.
|
|
*
|
|
*/
|
|
|
|
#include <zephyr/logging/log.h>
|
|
LOG_MODULE_REGISTER(net_openthread_platform, CONFIG_OPENTHREAD_PLATFORM_LOG_LEVEL);
|
|
|
|
#include <zephyr/kernel.h>
|
|
#include <zephyr/init.h>
|
|
#include <zephyr/version.h>
|
|
#include <zephyr/sys/check.h>
|
|
|
|
#include "platform/platform-zephyr.h"
|
|
|
|
#include <openthread.h>
|
|
#include <openthread_utils.h>
|
|
|
|
#include <openthread/child_supervision.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>
|
|
|
|
#if defined(CONFIG_OPENTHREAD_NAT64_TRANSLATOR)
|
|
#include <openthread/nat64.h>
|
|
#endif /* CONFIG_OPENTHREAD_NAT64_TRANSLATOR */
|
|
|
|
#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 ZEPHYR_PACKAGE_NAME "Zephyr"
|
|
#define PACKAGE_VERSION KERNEL_VERSION_STRING
|
|
|
|
static void openthread_process(struct k_work *work);
|
|
|
|
/* Global variables to store the OpenThread module context */
|
|
static otInstance *openthread_instance;
|
|
static sys_slist_t openthread_state_change_cbs = SYS_SLIST_STATIC_INIT(openthread_state_change_cbs);
|
|
static struct k_work_q openthread_work_q;
|
|
|
|
static K_WORK_DEFINE(openthread_work, openthread_process);
|
|
static K_MUTEX_DEFINE(openthread_lock);
|
|
K_KERNEL_STACK_DEFINE(ot_stack_area, OT_STACK_SIZE);
|
|
|
|
k_tid_t openthread_thread_id_get(void)
|
|
{
|
|
return (k_tid_t)&openthread_work_q.thread;
|
|
}
|
|
|
|
static int ncp_hdlc_send(const uint8_t *buf, uint16_t len)
|
|
{
|
|
otError err = OT_ERROR_NONE;
|
|
|
|
err = otPlatUartSend(buf, len);
|
|
if (err != OT_ERROR_NONE) {
|
|
return 0;
|
|
}
|
|
|
|
return len;
|
|
}
|
|
|
|
static void openthread_process(struct k_work *work)
|
|
{
|
|
ARG_UNUSED(work);
|
|
|
|
openthread_mutex_lock();
|
|
|
|
while (otTaskletsArePending(openthread_instance)) {
|
|
otTaskletsProcess(openthread_instance);
|
|
}
|
|
|
|
otSysProcessDrivers(openthread_instance);
|
|
|
|
openthread_mutex_unlock();
|
|
}
|
|
|
|
static void ot_joiner_start_handler(otError error, void *context)
|
|
{
|
|
ARG_UNUSED(context);
|
|
|
|
if (error != OT_ERROR_NONE) {
|
|
LOG_ERR("Join failed [%d]", error);
|
|
} else {
|
|
LOG_INF("Join success");
|
|
error = otThreadSetEnabled(openthread_instance, true);
|
|
if (error != OT_ERROR_NONE) {
|
|
LOG_ERR("Failed to start the OpenThread network [%d]", error);
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool ot_setup_default_configuration(void)
|
|
{
|
|
otExtendedPanId xpanid = {0};
|
|
otNetworkKey networkKey = {0};
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
error = otThreadSetNetworkName(openthread_instance, OT_NETWORK_NAME);
|
|
if (error != OT_ERROR_NONE) {
|
|
LOG_ERR("Failed to set %s [%d]", "network name", error);
|
|
return false;
|
|
}
|
|
|
|
error = otLinkSetChannel(openthread_instance, OT_CHANNEL);
|
|
if (error != OT_ERROR_NONE) {
|
|
LOG_ERR("Failed to set %s [%d]", "channel", error);
|
|
return false;
|
|
}
|
|
|
|
error = otLinkSetPanId(openthread_instance, OT_PANID);
|
|
if (error != OT_ERROR_NONE) {
|
|
LOG_ERR("Failed to set %s [%d]", "PAN ID", error);
|
|
return false;
|
|
}
|
|
|
|
if (bytes_from_str(xpanid.m8, 8, (char *)OT_XPANID) != 0) {
|
|
LOG_ERR("Failed to parse extended PAN ID");
|
|
return false;
|
|
}
|
|
error = otThreadSetExtendedPanId(openthread_instance, &xpanid);
|
|
if (error != OT_ERROR_NONE) {
|
|
LOG_ERR("Failed to set %s [%d]", "ext PAN ID", error);
|
|
return false;
|
|
}
|
|
|
|
if (strlen(OT_NETWORKKEY)) {
|
|
if (bytes_from_str(networkKey.m8, OT_NETWORK_KEY_SIZE, (char *)OT_NETWORKKEY) !=
|
|
0) {
|
|
LOG_ERR("Failed to parse network key");
|
|
return false;
|
|
}
|
|
error = otThreadSetNetworkKey(openthread_instance, &networkKey);
|
|
if (error != OT_ERROR_NONE) {
|
|
LOG_ERR("Failed to set %s [%d]", "network key", error);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void ot_state_changed_handler(uint32_t flags, void *context)
|
|
{
|
|
ARG_UNUSED(context);
|
|
|
|
struct openthread_state_changed_callback *entry, *next;
|
|
|
|
bool is_up = otIp6IsEnabled(openthread_instance);
|
|
|
|
LOG_INF("State changed! Flags: 0x%08" PRIx32 " Current role: %s Ip6: %s", flags,
|
|
otThreadDeviceRoleToString(otThreadGetDeviceRole(openthread_instance)),
|
|
(is_up ? "up" : "down"));
|
|
|
|
SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&openthread_state_change_cbs, entry, next, node) {
|
|
if (entry->otCallback != NULL) {
|
|
entry->otCallback(flags, entry->user_data);
|
|
}
|
|
}
|
|
}
|
|
|
|
void otTaskletsSignalPending(otInstance *instance)
|
|
{
|
|
ARG_UNUSED(instance);
|
|
|
|
int error = k_work_submit_to_queue(&openthread_work_q, &openthread_work);
|
|
|
|
if (error < 0) {
|
|
LOG_ERR("Failed to submit work to queue, error: %d", error);
|
|
}
|
|
}
|
|
|
|
void otSysEventSignalPending(void)
|
|
{
|
|
otTaskletsSignalPending(NULL);
|
|
}
|
|
|
|
int openthread_state_changed_callback_register(struct openthread_state_changed_callback *cb)
|
|
{
|
|
CHECKIF(cb == NULL || cb->otCallback == NULL) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
openthread_mutex_lock();
|
|
sys_slist_append(&openthread_state_change_cbs, &cb->node);
|
|
openthread_mutex_unlock();
|
|
|
|
return 0;
|
|
}
|
|
|
|
int openthread_state_changed_callback_unregister(struct openthread_state_changed_callback *cb)
|
|
{
|
|
bool removed = false;
|
|
|
|
CHECKIF(cb == NULL) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
openthread_mutex_lock();
|
|
removed = sys_slist_find_and_remove(&openthread_state_change_cbs, &cb->node);
|
|
openthread_mutex_unlock();
|
|
|
|
if (!removed) {
|
|
return -EALREADY;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct otInstance *openthread_get_default_instance(void)
|
|
{
|
|
__ASSERT(openthread_instance, "OT instance is not initialized");
|
|
return openthread_instance;
|
|
}
|
|
|
|
int openthread_init(void)
|
|
{
|
|
struct k_work_queue_config q_cfg = {
|
|
.name = "openthread",
|
|
.no_yield = true,
|
|
};
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
/* Prevent multiple initializations */
|
|
if (openthread_instance) {
|
|
return 0;
|
|
}
|
|
|
|
openthread_mutex_lock();
|
|
|
|
otSysInit(0, NULL);
|
|
openthread_instance = otInstanceInitSingle();
|
|
|
|
__ASSERT(openthread_instance, "OT instance initialization failed");
|
|
|
|
if (IS_ENABLED(CONFIG_OPENTHREAD_SHELL)) {
|
|
platformShellInit(openthread_instance);
|
|
}
|
|
|
|
if (IS_ENABLED(CONFIG_OPENTHREAD_COPROCESSOR)) {
|
|
error = otPlatUartEnable();
|
|
if (error != OT_ERROR_NONE) {
|
|
LOG_ERR("Failed to enable UART: [%d]", error);
|
|
}
|
|
|
|
otNcpHdlcInit(openthread_instance, ncp_hdlc_send);
|
|
} else {
|
|
otIp6SetReceiveFilterEnabled(openthread_instance, true);
|
|
|
|
#if defined(CONFIG_OPENTHREAD_NAT64_TRANSLATOR)
|
|
|
|
otIp4Cidr nat64_cidr;
|
|
|
|
if (otIp4CidrFromString(CONFIG_OPENTHREAD_NAT64_CIDR, &nat64_cidr) ==
|
|
OT_ERROR_NONE) {
|
|
if (otNat64SetIp4Cidr(openthread_instance, &nat64_cidr) != OT_ERROR_NONE) {
|
|
LOG_ERR("Incorrect NAT64 CIDR");
|
|
return -EIO;
|
|
}
|
|
} else {
|
|
LOG_ERR("Failed to parse NAT64 CIDR");
|
|
return -EIO;
|
|
}
|
|
#endif /* CONFIG_OPENTHREAD_NAT64_TRANSLATOR */
|
|
|
|
error = otSetStateChangedCallback(openthread_instance, &ot_state_changed_handler,
|
|
NULL);
|
|
if (error != OT_ERROR_NONE) {
|
|
LOG_ERR("Could not set state changed callback: %d", error);
|
|
return -EIO;
|
|
}
|
|
}
|
|
|
|
openthread_mutex_unlock();
|
|
|
|
/* Start work queue for the OpenThread module */
|
|
k_work_queue_start(&openthread_work_q, ot_stack_area, K_KERNEL_STACK_SIZEOF(ot_stack_area),
|
|
OT_PRIORITY, &q_cfg);
|
|
|
|
(void)k_work_submit_to_queue(&openthread_work_q, &openthread_work);
|
|
|
|
return error == OT_ERROR_NONE ? 0 : -EIO;
|
|
}
|
|
|
|
int openthread_run(void)
|
|
{
|
|
openthread_mutex_lock();
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
LOG_INF("OpenThread version: %s", otGetVersionString());
|
|
|
|
if (IS_ENABLED(CONFIG_OPENTHREAD_COPROCESSOR)) {
|
|
LOG_DBG("OpenThread co-processor.");
|
|
goto exit;
|
|
}
|
|
|
|
error = otIp6SetEnabled(openthread_instance, true);
|
|
if (error != OT_ERROR_NONE) {
|
|
LOG_ERR("Failed to set %s [%d]", "IPv6 support", error);
|
|
goto exit;
|
|
}
|
|
|
|
/* Sleepy End Device specific configuration. */
|
|
if (IS_ENABLED(CONFIG_OPENTHREAD_MTD_SED)) {
|
|
otLinkModeConfig ot_mode = otThreadGetLinkMode(openthread_instance);
|
|
|
|
/* A SED should always attach the network as a SED to indicate
|
|
* increased buffer requirement to a parent.
|
|
*/
|
|
ot_mode.mRxOnWhenIdle = false;
|
|
|
|
error = otThreadSetLinkMode(openthread_instance, ot_mode);
|
|
if (error != OT_ERROR_NONE) {
|
|
LOG_ERR("Failed to set %s [%d]", "link mode", error);
|
|
goto exit;
|
|
}
|
|
|
|
error = otLinkSetPollPeriod(openthread_instance, OT_POLL_PERIOD);
|
|
if (error != OT_ERROR_NONE) {
|
|
LOG_ERR("Failed to set %s [%d]", "poll period", error);
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
/* Configure Child Supervision and MLE Child timeouts. */
|
|
otChildSupervisionSetInterval(openthread_instance,
|
|
CONFIG_OPENTHREAD_CHILD_SUPERVISION_INTERVAL);
|
|
otChildSupervisionSetCheckTimeout(openthread_instance,
|
|
CONFIG_OPENTHREAD_CHILD_SUPERVISION_CHECK_TIMEOUT);
|
|
otThreadSetChildTimeout(openthread_instance, CONFIG_OPENTHREAD_MLE_CHILD_TIMEOUT);
|
|
|
|
if (otDatasetIsCommissioned(openthread_instance)) {
|
|
/* OpenThread already has dataset stored - skip the
|
|
* configuration.
|
|
*/
|
|
LOG_DBG("OpenThread already commissioned.");
|
|
} else if (IS_ENABLED(CONFIG_OPENTHREAD_JOINER_AUTOSTART)) {
|
|
/* No dataset - initiate network join procedure. */
|
|
LOG_DBG("Starting OpenThread join procedure.");
|
|
|
|
error = otJoinerStart(openthread_instance, OT_JOINER_PSKD, NULL,
|
|
ZEPHYR_PACKAGE_NAME, OT_PLATFORM_INFO, PACKAGE_VERSION, NULL,
|
|
&ot_joiner_start_handler, NULL);
|
|
|
|
if (error != OT_ERROR_NONE) {
|
|
LOG_ERR("Failed to start joiner [%d]", error);
|
|
}
|
|
|
|
goto exit;
|
|
} else {
|
|
/* No dataset - load the default configuration. */
|
|
LOG_DBG("Loading OpenThread default configuration.");
|
|
|
|
if (!ot_setup_default_configuration()) {
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
LOG_INF("Network name: %s", otThreadGetNetworkName(openthread_instance));
|
|
|
|
/* Start the network. */
|
|
error = otThreadSetEnabled(openthread_instance, true);
|
|
if (error != OT_ERROR_NONE) {
|
|
LOG_ERR("Failed to start the OpenThread network [%d]", error);
|
|
}
|
|
|
|
exit:
|
|
|
|
openthread_mutex_unlock();
|
|
|
|
return error == OT_ERROR_NONE ? 0 : -EIO;
|
|
}
|
|
|
|
int openthread_stop(void)
|
|
{
|
|
otError error = OT_ERROR_NONE;
|
|
|
|
if (IS_ENABLED(CONFIG_OPENTHREAD_COPROCESSOR)) {
|
|
return 0;
|
|
}
|
|
|
|
openthread_mutex_lock();
|
|
|
|
error = otThreadSetEnabled(openthread_instance, false);
|
|
if (error == OT_ERROR_INVALID_STATE) {
|
|
LOG_DBG("Openthread interface was not up [%d]", error);
|
|
}
|
|
|
|
openthread_mutex_unlock();
|
|
|
|
return 0;
|
|
}
|
|
|
|
void openthread_set_receive_cb(openthread_receive_cb cb, void *context)
|
|
{
|
|
__ASSERT(cb != NULL, "Receive callback is not set");
|
|
__ASSERT(openthread_instance != NULL, "OpenThread instance is not initialized");
|
|
|
|
if (!IS_ENABLED(CONFIG_OPENTHREAD_COPROCESSOR)) {
|
|
openthread_mutex_lock();
|
|
otIp6SetReceiveCallback(openthread_instance, cb, context);
|
|
|
|
#if defined(CONFIG_OPENTHREAD_NAT64_TRANSLATOR)
|
|
otNat64SetReceiveIp4Callback(openthread_instance, cb, context);
|
|
#endif /* CONFIG_OPENTHREAD_NAT64_TRANSLATOR */
|
|
|
|
openthread_mutex_unlock();
|
|
}
|
|
}
|
|
|
|
void openthread_mutex_lock(void)
|
|
{
|
|
(void)k_mutex_lock(&openthread_lock, K_FOREVER);
|
|
}
|
|
|
|
int openthread_mutex_try_lock(void)
|
|
{
|
|
return k_mutex_lock(&openthread_lock, K_NO_WAIT);
|
|
}
|
|
|
|
void openthread_mutex_unlock(void)
|
|
{
|
|
(void)k_mutex_unlock(&openthread_lock);
|
|
}
|
|
|
|
#ifdef CONFIG_OPENTHREAD_SYS_INIT
|
|
static int openthread_sys_init(void)
|
|
{
|
|
int error = openthread_init();
|
|
|
|
if (error == 0) {
|
|
#ifndef CONFIG_OPENTHREAD_MANUAL_START
|
|
error = openthread_run();
|
|
#endif
|
|
}
|
|
|
|
return error;
|
|
}
|
|
|
|
SYS_INIT(openthread_sys_init, POST_KERNEL, CONFIG_OPENTHREAD_SYS_INIT_PRIORITY);
|
|
#endif /* CONFIG_OPENTHREAD_SYS_INIT */
|