Primary Git Repository for the Zephyr Project. Zephyr is a new generation, scalable, optimized, secure RTOS for multiple hardware architectures.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

511 lines
12 KiB

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