diff --git a/doc/nrf/protocols/thread/configuring.rst b/doc/nrf/protocols/thread/configuring.rst index 88003deb0603..076efe52b28d 100644 --- a/doc/nrf/protocols/thread/configuring.rst +++ b/doc/nrf/protocols/thread/configuring.rst @@ -150,6 +150,7 @@ For a list of all supported features in the |NCS|, see the :ref:`thread_ug_featu .. _ug_thread_configuring_eui64: +//TODO: IEEE 802.15.4 EUI-64 configuration options ========================================== diff --git a/modules/openthread/CMakeLists.txt b/modules/openthread/CMakeLists.txt index a8c73335bce2..b3b5bbc5f258 100644 --- a/modules/openthread/CMakeLists.txt +++ b/modules/openthread/CMakeLists.txt @@ -274,6 +274,14 @@ target_link_libraries(zephyr PRIVATE ${ot_libs}) endif() +# Create a library for the OpenThread Zephyr utils +zephyr_library_named(openthread_utils) +zephyr_library_sources( + openthread.c +) +zephyr_library_sources_ifdef(CONFIG_OPENTHREAD_SHELL shell.c) +zephyr_include_directories(include) + add_subdirectory(platform) endif() diff --git a/modules/openthread/Kconfig b/modules/openthread/Kconfig index 3297a346a8f0..315ff92efdef 100644 --- a/modules/openthread/Kconfig +++ b/modules/openthread/Kconfig @@ -13,12 +13,27 @@ config OPENTHREAD select ENTROPY_GENERATOR select NRF_802154_RADIO_DRIVER if HAS_HW_NRF_RADIO_IEEE802154 && !NET_L2_OPENTHREAD select NRF_802154_SER_HOST if !HAS_HW_NRF_RADIO_IEEE802154 && !NET_L2_OPENTHREAD - select NRF_802154_RADIO_CONFIG if !NET_L2_OPENTHREAD + select NRF_802154_RADIO_CONFIG if HAS_HW_NRF_RADIO_IEEE802154 && !NET_L2_OPENTHREAD help This option enables the OpenThread library if OPENTHREAD +config OPENTHREAD_SYS_INIT + bool "Initialize OpenThread stack during system initialization" + default y + depends on !NET_L2_OPENTHREAD + help + This option initializes the OpenThread automatically by calling the openthread_init() + function during system initialization. + +config OPENTHREAD_SYS_INIT_PRIORITY + int "OpenThread system initialization priority" + default 90 + depends on OPENTHREAD_SYS_INIT + help + This option sets the priority of the OpenThread system initialization. + choice OPENTHREAD_IMPLEMENTATION prompt "OpenThread origin selection" help @@ -319,6 +334,21 @@ config OPENTHREAD_INTERFACE_EARLY_UP Otherwise, OpenThread interface will be marked operational UP only after the device joins a Thread network. +config OPENTHREAD_PLATFORM_PKT_TXTIME + bool + default y if NET_PKT_TXTIME + default y if !NET_L2_OPENTHREAD + help + Enable packet TX time support. This is needed for when the application + wants to set the exact time when the packet should be sent. + +config OPENTHREAD_PLATFORM_CARRIER_FUNCTIONS + bool + default y if OPENTHREAD_DIAG && IEEE802154_CARRIER_FUNCTIONS + default y if OPENTHREAD_DIAG && NRF5_CARRIER_FUNCTIONS + help + Enable support for functions such as modulated carrier and continuous carrier. + menu "OpenThread stack features" rsource "Kconfig.features" endmenu @@ -327,4 +357,8 @@ menu "Thread Network configuration" rsource "Kconfig.thread" endmenu +menu "NRF5 radio configuration" +rsource "Kconfig.nrf5" +endmenu + endif # OPENTHREAD diff --git a/modules/openthread/Kconfig.nrf5 b/modules/openthread/Kconfig.nrf5 new file mode 100644 index 000000000000..689af318070e --- /dev/null +++ b/modules/openthread/Kconfig.nrf5 @@ -0,0 +1,69 @@ +# Copyright (c) 2025 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +if !CONFIG_NET_L2_OPENTHREAD + +config NRF5_SELECTIVE_TXCHANNEL + bool "Support for selective TX channel setting" + help + Enable support for selectively setting TX channel for every timed transmission request. + +config NRF5_CARRIER_FUNCTIONS + bool "Support for carrier functions" + default y if OPENTHREAD_DIAG + help + Enable support for functions such as modulated carrier and continuous carrier. + +config NRF5_VENDOR_OUI_ENABLE + bool "Support setting Vendor Organizationally Unique Identifier" + help + This option enables setting custom vendor + OUI using NRF5_VENDOR_OUI . After enabling, + user is obliged to set NRF5_VENDOR_OUI value, + as this option has no default value. + +if NRF5_VENDOR_OUI_ENABLE + +config NRF5_VENDOR_OUI + int "Vendor Organizationally Unique Identifier" + help + Custom vendor OUI, which makes 24 most-significant + bits of MAC address + +endif # NRF5_VENDOR_OUI_ENABLE + +config NRF5_UICR_EUI64_ENABLE + bool "Support usage of EUI64 value stored in UICR registers" + depends on !NRF5_VENDOR_OUI_ENABLE + depends on SOC_SERIES_NRF52X || SOC_SERIES_NRF53X + help + This option enables setting custom vendor EUI64 value + stored in User information configuration registers (UICR). + Notice that this disables the default setting of EUI64 + value from Factory information configuration registers + (FICR). + +if NRF5_UICR_EUI64_ENABLE + +config NRF5_UICR_EUI64_REG + int "UICR base register for the EUI64 value" + range 0 30 if SOC_SERIES_NRF52X + range 0 190 if SOC_SERIES_NRF53X + default 0 + help + Base of the two consecutive registers from the UICR customer + section in which custom EUI64 is stored. + +endif # NRF5_UICR_EUI64_ENABLE + +config NRF5_LOG_RX_FAILURES + bool "Frame reception failures logging" + help + There are few cases where the frame reception failure can happen because of + internal cause. These cases are reported forward by general code error. + + This options enables logging the reason of frame reception failure. + It can be helpful for the network traffic analyze but it generates also + a lot of log records in a stress environment. + +endif # !CONFIG_NET_L2_OPENTHREAD diff --git a/modules/openthread/include/openthread.h b/modules/openthread/include/openthread.h new file mode 100644 index 000000000000..36706e5cb125 --- /dev/null +++ b/modules/openthread/include/openthread.h @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef ZEPHYR_MODULES_OPENTHREAD_OPENTHREAD_H_ +#define ZEPHYR_MODULES_OPENTHREAD_OPENTHREAD_H_ + +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief The common callback type for receiving IPv4 (translated by NAT64) and IPv6 datagrams. + * + * This callback is called when a datagram is received. + * + * @param message The message to receive. + * @param context The context to pass to the callback. + */ +typedef void (*openthread_receive_cb)(struct otMessage *message, void *context); + +/** OpenThread state change callback */ + +/** + * @brief OpenThread state change callback structure + * + * Used to register a callback in the callback list. As many + * callbacks as needed can be added as long as each of them + * are unique pointers of struct openthread_state_changed_callback. + * + * @note You may destroy the object only after it is unregistered from the callback list. + */ +struct openthread_state_changed_callback { + /** + * @brief Callback for notifying configuration or state changes. + * + * @param otCallback OpenThread callback to register. + * See https://openthread.io/reference/group/api-instance#otstatechangedcallback for + * details. + */ + otStateChangedCallback otCallback; + + /** User data if required */ + void *user_data; + + /** + * Internally used field for list handling + * - user must not directly modify + */ + sys_snode_t node; +}; + +/** + * @brief Register callbacks that will be called when a certain configuration + * or state changes occur within OpenThread. + * + * @param cb Callback struct to register. + */ +int openthread_state_changed_callback_register(struct openthread_state_changed_callback *cb); + +/** + * @brief Unregister OpenThread configuration or state changed callbacks. + * + * @param cb Callback struct to unregister. + */ +int openthread_state_changed_callback_unregister(struct openthread_state_changed_callback *cb); + +/** + * @brief Get OpenThread thread identification. + */ +k_tid_t openthread_thread_id_get(void); + +/** + * @brief Get pointer to default OpenThread instance. + * + * @retval !NULL On success. + * @retval NULL On failure. + */ +struct otInstance *openthread_get_default_instance(void); + +/** + * @brief Initialize the OpenThread module. + * + * This function: + * - Initializes the OpenThread module. + * - Creates an OpenThread single instance. + * - Starts the shell. + * - Enables the UART and NCP HDLC for coprocessor purposes. + * - Initializes the NAT64 translator. + * - Creates a work queue for the OpenThread module. + * + * @note This function is automatically called by Zephyr's networking layer. + * If you want to initialize the OpenThread independently, call this function + * in your application init code. + * + * @retval 0 On success. + * @retval -EIO On failure. + */ +int openthread_init(void); + +/** + * @brief Run the OpenThread network. + * + * @details Prepares the OpenThread network and enables it. + * Depends on active settings: it uses the stored network configuration, + * starts the joining procedure or uses the default network configuration. + * Additionally, when the device is MTD, it sets the SED mode to properly + * attach the network. + */ +int openthread_run(void); + +/** + * @brief Disable the OpenThread network. + */ +int openthread_stop(void); + +/** + * @brief Set the additional callback for receiving packets. + * + * @details This callback is called once a packet is received and can be + * used to inject packets into the Zephyr networking stack. + * Setting this callback is optional. + * + * @param cb Callback to set. + * @param context Context to pass to the callback. + */ +void openthread_set_receive_cb(openthread_receive_cb cb, void *context); + +/** + * @brief Lock internal mutex before accessing OpenThread API. + * + * @details OpenThread API is not thread-safe. Therefore, before accessing any + * API function, you need to lock the internal mutex, to prevent the + * OpenThread thread from pre-empting the API call. + */ +void openthread_mutex_lock(void); + +/** + * @brief Try to lock internal mutex before accessing OpenThread API. + * + * @details This function behaves like openthread_mutex_lock(), provided that + * the internal mutex is unlocked. Otherwise, it returns a negative value without + * waiting. + */ +int openthread_mutex_try_lock(void); + +/** + * @brief Unlock internal mutex after accessing OpenThread API. + */ +void openthread_mutex_unlock(void); + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_MODULES_OPENTHREAD_OPENTHREAD_H_ */ diff --git a/modules/openthread/openthread.c b/modules/openthread/openthread.c new file mode 100644 index 000000000000..622d30dbba9d --- /dev/null +++ b/modules/openthread/openthread.c @@ -0,0 +1,527 @@ +/* + * 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 +LOG_MODULE_REGISTER(net_openthread_platform, CONFIG_OPENTHREAD_PLATFORM_LOG_LEVEL); + +#include + +#include +#include +#include +#include + +#include "platform-zephyr.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(CONFIG_OPENTHREAD_NAT64_TRANSLATOR) +#include +#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 int bytes_from_str(uint8_t *buf, int buf_len, const char *src) +{ + size_t i; + size_t src_len = strlen(src); + char *endptr; + + for (i = 0U; i < src_len; i++) { + if (!isxdigit((unsigned char)src[i]) && src[i] != ':') { + return -EINVAL; + } + } + + (void)memset(buf, 0, buf_len); + + for (i = 0U; i < (size_t)buf_len; i++) { + buf[i] = (uint8_t)strtol(src, &endptr, 16); + src = ++endptr; + } + + return 0; +} + +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; + } + + bytes_from_str(xpanid.m8, 8, (char *)OT_XPANID); + 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)) { + bytes_from_str(networkKey.m8, OT_NETWORK_KEY_SIZE, (char *)OT_NETWORKKEY); + 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; + + 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 */ diff --git a/modules/openthread/platform/CMakeLists.txt b/modules/openthread/platform/CMakeLists.txt index d67fe96e8fed..6fe86d13b5aa 100644 --- a/modules/openthread/platform/CMakeLists.txt +++ b/modules/openthread/platform/CMakeLists.txt @@ -6,7 +6,6 @@ zephyr_library_sources( entropy.c misc.c platform.c - radio.c spi.c ) @@ -15,10 +14,11 @@ zephyr_library_sources_ifdef(CONFIG_OPENTHREAD_BLE_TCAT ble.c) zephyr_library_sources_ifdef(CONFIG_OPENTHREAD_DIAG diag.c) zephyr_library_sources_ifdef(CONFIG_OPENTHREAD_COPROCESSOR uart.c) zephyr_library_sources_ifdef(CONFIG_OPENTHREAD_CRYPTO_PSA crypto_psa.c) -zephyr_library_sources_ifdef(CONFIG_OPENTHREAD_SHELL shell.c) zephyr_library_sources_ifdef(CONFIG_OPENTHREAD_EXTERNAL_HEAP memory.c) zephyr_library_sources_ifdef(CONFIG_OPENTHREAD_PLATFORM_MESSAGE_MANAGEMENT messagepool.c) zephyr_library_sources_ifdef(CONFIG_SETTINGS settings.c) zephyr_library_sources_ifndef(CONFIG_LOG_BACKEND_SPINEL logging.c) +zephyr_library_sources_ifndef(CONFIG_NET_L2_OPENTHREAD radio_nrf5.c) +zephyr_library_sources_ifdef(CONFIG_NET_L2_OPENTHREAD radio.c) zephyr_include_directories(.) diff --git a/modules/openthread/platform/alarm.c b/modules/openthread/platform/alarm.c index 13f3f9110478..fa8bac9eaa40 100644 --- a/modules/openthread/platform/alarm.c +++ b/modules/openthread/platform/alarm.c @@ -50,7 +50,7 @@ K_TIMER_DEFINE(ot_us_timer, ot_timer_us_fired, NULL); void platformAlarmInit(void) { -#if defined(CONFIG_NET_PKT_TXTIME) +#if defined(CONFIG_OPENTHREAD_PLATFORM_PKT_TXTIME) time_offset_us = (int32_t)((int64_t)otPlatAlarmMicroGetNow() - (uint32_t)otPlatRadioGetNow(NULL)); time_offset_ms = time_offset_us / 1000; diff --git a/modules/openthread/platform/ble.c b/modules/openthread/platform/ble.c index ca40ae117be9..ea5427abc4b3 100644 --- a/modules/openthread/platform/ble.c +++ b/modules/openthread/platform/ble.c @@ -19,8 +19,7 @@ #include #include -/* Zephyr OpenThread integration Library */ -#include +#include /* OpenThread BLE driver API */ #include @@ -163,7 +162,7 @@ static void ot_plat_ble_thread(void *unused1, void *unused2, void *unused3) ring_buf_get(&ot_plat_ble_ring_buf, ot_plat_ble_msg_buf, len); } - openthread_api_mutex_lock(openthread_get_default_context()); + openthread_mutex_lock(); if (len <= PLAT_BLE_MSG_DATA_MAX) { /* The packet parameter in otPlatBleGattServerOnWriteRequest is not const. @@ -178,7 +177,7 @@ static void ot_plat_ble_thread(void *unused1, void *unused2, void *unused3) } else if (len == PLAT_BLE_MSG_DISCONNECT) { otPlatBleGapOnDisconnected(ble_openthread_instance, 0); } - openthread_api_mutex_unlock(openthread_get_default_context()); + openthread_mutex_unlock(); } } diff --git a/modules/openthread/platform/diag.c b/modules/openthread/platform/diag.c index 3bd4b549536b..74781cad4a1d 100644 --- a/modules/openthread/platform/diag.c +++ b/modules/openthread/platform/diag.c @@ -37,7 +37,10 @@ static uint32_t sTxPeriod = 1; static int32_t sTxCount; static int32_t sTxRequestedCount = 1; +#if defined(CONFIG_OPENTHREAD_PLATFORM_CARRIER_FUNCTIONS) static otError startModCarrier(otInstance *aInstance, uint8_t aArgsLength, char *aArgs[]); +#endif /* CONFIG_OPENTHREAD_PLATFORM_CARRIER_FUNCTIONS */ + static otError processTransmit(otInstance *aInstance, uint8_t aArgsLength, char *aArgs[]); static otError parse_long(char *aArgs, long *aValue) @@ -72,11 +75,11 @@ void otPlatDiagSetOutputCallback(otInstance *aInstance, otError otPlatDiagProcess(otInstance *aInstance, uint8_t aArgsLength, char *aArgs[]) { -#if defined(CONFIG_IEEE802154_CARRIER_FUNCTIONS) +#if defined(CONFIG_OPENTHREAD_PLATFORM_CARRIER_FUNCTIONS) if (strcmp(aArgs[0], "modcarrier") == 0) { return startModCarrier(aInstance, aArgsLength - 1, aArgs + 1); } -#endif +#endif /* CONFIG_OPENTHREAD_PLATFORM_CARRIER_FUNCTIONS */ if (strcmp(aArgs[0], "transmit") == 0) { return processTransmit(aInstance, aArgsLength - 1, aArgs + 1); @@ -128,7 +131,7 @@ void otPlatDiagRadioReceived(otInstance *aInstance, ARG_UNUSED(aError); } -#if defined(CONFIG_IEEE802154_CARRIER_FUNCTIONS) +#if defined(CONFIG_OPENTHREAD_PLATFORM_CARRIER_FUNCTIONS) otError otPlatDiagRadioTransmitCarrier(otInstance *aInstance, bool aEnable) { if (sTransmitMode != DIAG_TRANSMIT_MODE_IDLE && @@ -144,7 +147,7 @@ otError otPlatDiagRadioTransmitCarrier(otInstance *aInstance, bool aEnable) return platformRadioTransmitCarrier(aInstance, aEnable); } -#endif /* CONFIG_IEEE802154_CARRIER_FUNCTIONS */ +#endif /* CONFIG_OPENTHREAD_PLATFORM_CARRIER_FUNCTIONS */ /* * To enable gpio diag commands, in Devicetree create `openthread` node in `/options/` path @@ -317,7 +320,7 @@ otError otPlatDiagGpioGetMode(uint32_t aGpio, otGpioMode *aMode) * DT_NODE_HAS_PROP(DT_COMPAT_GET_ANY_STATUS_OKAY(openthread_config), diag_gpios) */ -#if defined(CONFIG_IEEE802154_CARRIER_FUNCTIONS) +#if defined(CONFIG_OPENTHREAD_PLATFORM_CARRIER_FUNCTIONS) static otError startModCarrier(otInstance *aInstance, uint8_t aArgsLength, char *aArgs[]) { @@ -346,7 +349,7 @@ static otError startModCarrier(otInstance *aInstance, uint8_t aArgsLength, char return platformRadioTransmitModulatedCarrier(aInstance, enable, data); } -#endif +#endif /* CONFIG_OPENTHREAD_PLATFORM_CARRIER_FUNCTIONS */ void otPlatDiagAlarmCallback(otInstance *aInstance) { diff --git a/modules/openthread/platform/entropy.c b/modules/openthread/platform/entropy.c index a52f885e3063..843d6a533d0b 100644 --- a/modules/openthread/platform/entropy.c +++ b/modules/openthread/platform/entropy.c @@ -10,7 +10,7 @@ #include -LOG_MODULE_REGISTER(net_otPlat_entropy, CONFIG_OPENTHREAD_L2_LOG_LEVEL); +LOG_MODULE_REGISTER(net_otPlat_entropy, CONFIG_OPENTHREAD_PLATFORM_LOG_LEVEL); #if !defined(CONFIG_CSPRNG_ENABLED) #error OpenThread requires an entropy source for a TRNG diff --git a/modules/openthread/platform/platform-zephyr.h b/modules/openthread/platform/platform-zephyr.h index d6dd26adea04..4d61197b2b42 100644 --- a/modules/openthread/platform/platform-zephyr.h +++ b/modules/openthread/platform/platform-zephyr.h @@ -80,20 +80,20 @@ uint16_t platformRadioChannelGet(otInstance *aInstance); void platformRadioChannelSet(uint8_t aChannel); #endif /* CONFIG_OPENTHREAD_DIAG */ -#if defined(CONFIG_IEEE802154_CARRIER_FUNCTIONS) +#if defined(CONFIG_OPENTHREAD_PLATFORM_CARRIER_FUNCTIONS) /** * Start/stop continuous carrier wave transmission. */ otError platformRadioTransmitCarrier(otInstance *aInstance, bool aEnable); -#endif /* CONFIG_IEEE802154_CARRIER_FUNCTIONS */ +#endif /* CONFIG_OPENTHREAD_PLATFORM_CARRIER_FUNCTIONS */ -#if defined(CONFIG_IEEE802154_CARRIER_FUNCTIONS) +#if defined(CONFIG_OPENTHREAD_PLATFORM_CARRIER_FUNCTIONS ) /** * Start/stop modulated carrier wave transmission. */ otError platformRadioTransmitModulatedCarrier(otInstance *aInstance, bool aEnable, const uint8_t *aData); -#endif +#endif /* CONFIG_OPENTHREAD_PLATFORM_CARRIER_FUNCTIONS */ /** * This function initializes the random number service used by OpenThread. diff --git a/modules/openthread/platform/radio.c b/modules/openthread/platform/radio.c index 80673f65e64e..e9516d452b22 100644 --- a/modules/openthread/platform/radio.c +++ b/modules/openthread/platform/radio.c @@ -15,7 +15,7 @@ #define LOG_MODULE_NAME net_otPlat_radio #include -LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_OPENTHREAD_L2_LOG_LEVEL); +LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_OPENTHREAD_PLATFORM_LOG_LEVEL); #include #include @@ -244,7 +244,7 @@ void handle_radio_event(const struct device *dev, enum ieee802154_event evt, } } -#if defined(CONFIG_NET_PKT_TXTIME) || defined(CONFIG_OPENTHREAD_CSL_RECEIVER) +#if defined(CONFIG_OPENTHREAD_PLATFORM_PKT_TXTIME) || defined(CONFIG_OPENTHREAD_CSL_RECEIVER) /** * @brief Convert 32-bit (potentially wrapped) OpenThread microsecond timestamps * to 64-bit Zephyr network subsystem nanosecond timestamps. @@ -320,7 +320,7 @@ static net_time_t convert_32bit_us_wrapped_to_64bit_ns(uint32_t target_time_us_w __ASSERT_NO_MSG(result <= INT64_MAX / NSEC_PER_USEC); return (net_time_t)result * NSEC_PER_USEC; } -#endif /* CONFIG_NET_PKT_TXTIME || CONFIG_OPENTHREAD_CSL_RECEIVER */ +#endif /* CONFIG_OPENTHREAD_PLATFORM_PKT_TXTIME || CONFIG_OPENTHREAD_CSL_RECEIVER */ static void dataInit(void) { @@ -416,7 +416,7 @@ void transmit_message(struct k_work *tx_job) if ((radio_caps & IEEE802154_HW_TXTIME) && (sTransmitFrame.mInfo.mTxInfo.mTxDelay != 0)) { -#if defined(CONFIG_NET_PKT_TXTIME) +#if defined(CONFIG_OPENTHREAD_PLATFORM_PKT_TXTIME) uint32_t tx_at = sTransmitFrame.mInfo.mTxInfo.mTxDelayBaseTime + sTransmitFrame.mInfo.mTxInfo.mTxDelay; net_pkt_set_timestamp_ns(tx_pkt, convert_32bit_us_wrapped_to_64bit_ns(tx_at)); @@ -834,7 +834,7 @@ otError otPlatRadioReceiveAt(otInstance *aInstance, uint8_t aChannel, } #endif -#if defined(CONFIG_IEEE802154_CARRIER_FUNCTIONS) +#if defined(CONFIG_OPENTHREAD_PLATFORM_CARRIER_FUNCTIONS) otError platformRadioTransmitCarrier(otInstance *aInstance, bool aEnable) { if (radio_api->continuous_carrier == NULL) { @@ -885,7 +885,7 @@ otError platformRadioTransmitModulatedCarrier(otInstance *aInstance, bool aEnabl return OT_ERROR_NONE; } -#endif /* CONFIG_IEEE802154_CARRIER_FUNCTIONS */ +#endif /* CONFIG_OPENTHREAD_PLATFORM_CARRIER_FUNCTIONS */ otRadioState otPlatRadioGetState(otInstance *aInstance) { @@ -1000,7 +1000,7 @@ otRadioCaps otPlatRadioGetCaps(otInstance *aInstance) } #endif -#if defined(CONFIG_NET_PKT_TXTIME) +#if defined(CONFIG_OPENTHREAD_PLATFORM_PKT_TXTIME) if (radio_caps & IEEE802154_HW_TXTIME) { caps |= OT_RADIO_CAPS_TRANSMIT_TIMING; } @@ -1265,7 +1265,7 @@ uint64_t otPlatTimeGet(void) } } -#if defined(CONFIG_NET_PKT_TXTIME) +#if defined(CONFIG_OPENTHREAD_PLATFORM_PKT_TXTIME) uint64_t otPlatRadioGetNow(otInstance *aInstance) { ARG_UNUSED(aInstance); diff --git a/modules/openthread/platform/radio_nrf5.c b/modules/openthread/platform/radio_nrf5.c new file mode 100644 index 000000000000..9ecab4cf61be --- /dev/null +++ b/modules/openthread/platform/radio_nrf5.c @@ -0,0 +1,1791 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +/** + * @file + * This file implements the OpenThread platform abstraction + * for radio communication utilizing nRF IEEE802.15.4 radio driver. + * + */ + +#include +#define LOG_MODULE_NAME net_otPlat_radio + +#include +LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_OPENTHREAD_PLATFORM_LOG_LEVEL); + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#if defined(CONFIG_OPENTHREAD_NAT64_TRANSLATOR) +#include +#endif + +#include "nrf_802154.h" +#include "nrf_802154_const.h" + +#if defined(CONFIG_NRF_802154_SER_HOST) +#include "nrf_802154_serialization_error.h" +#endif + +#if defined(CONFIG_TRUSTED_EXECUTION_NONSECURE) && defined(NRF_FICR_S) +#include +#else +#include +#endif + +#if defined(CONFIG_OPENTHREAD_THREAD_VERSION_1_1) +#define ACK_PKT_LENGTH 5 +#else +#define ACK_PKT_LENGTH MAX_PACKET_SIZE +#endif + +#if defined(CONFIG_NRF5_UICR_EUI64_ENABLE) +#if defined(CONFIG_SOC_NRF5340_CPUAPP) +#if defined(CONFIG_TRUSTED_EXECUTION_NONSECURE) +#error "NRF_UICR->OTP is not supported to read from non-secure" +#else +#define EUI64_ADDR (NRF_UICR->OTP) +#endif /* CONFIG_TRUSTED_EXECUTION_NONSECURE */ +#else +#define EUI64_ADDR (NRF_UICR->CUSTOMER) +#endif /* CONFIG_SOC_NRF5340_CPUAPP */ +#endif /* CONFIG_NRF5_UICR_EUI64_ENABLE */ + +#if defined(CONFIG_NRF5_UICR_EUI64_ENABLE) +#define EUI64_ADDR_HIGH CONFIG_NRF5_UICR_EUI64_REG +#define EUI64_ADDR_LOW (CONFIG_NRF5_UICR_EUI64_REG + 1) +#else +#define EUI64_ADDR_HIGH 0 +#define EUI64_ADDR_LOW 1 +#endif /* CONFIG_NRF5_UICR_EUI64_ENABLE */ + +#if defined(CONFIG_NRF5_VENDOR_OUI_ENABLE) +#define NRF5_VENDOR_OUI CONFIG_NRF5_VENDOR_OUI +#else +#define NRF5_VENDOR_OUI (uint32_t)0xF4CE36 +#endif + +#define CHANNEL_COUNT OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MAX - OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MIN + 1 +#define DRX_SLOT_RX 0 /* Delayed reception window ID */ +#define PHR_DURATION_US 32U +#define NSEC_PER_TEN_SYMBOLS ((uint64_t)PHY_US_PER_SYMBOL * 1000 * 10) +#define NRF5_BROADCAST_ADDRESS 0xffff +#define NRF5_NO_SHORT_ADDRESS_ASSIGNED 0xfffe + +#if defined(CONFIG_COOP_ENABLED) +#define OT_WORKER_PRIORITY K_PRIO_COOP(CONFIG_OPENTHREAD_THREAD_PRIORITY) +#else +#define OT_WORKER_PRIORITY K_PRIO_PREEMPT(CONFIG_OPENTHREAD_THREAD_PRIORITY) +#endif + +enum nrf5_pending_events { + PENDING_EVENT_FRAME_RECEIVED, /* Radio has received new frame */ + PENDING_EVENT_RX_FAILED, /* The RX failed */ + PENDING_EVENT_TX_DONE, /* Radio transmission finished */ + PENDING_EVENT_DETECT_ENERGY, /* Requested to start Energy Detection procedure */ + PENDING_EVENT_DETECT_ENERGY_DONE, /* Energy Detection finished */ + PENDING_EVENT_SLEEP, /* Sleep if idle */ + PENDING_EVENT_COUNT /* Keep last */ +}; + +enum nrf5_ie_type { + NRF5_IE_TYPE_HEADER = 0x0, + NRF5_IE_TYPE_PAYLOAD, +}; + +enum nrf5_header_ie_element_id { + NRF5_HEADER_IE_ELEMENT_ID_VENDOR_SPECIFIC_IE = 0x00, + NRF5_HEADER_IE_ELEMENT_ID_CSL_IE = 0x1a, +}; + +struct nrf5_header_ie_csl_reduced { + uint16_t csl_phase; + uint16_t csl_period; +} __packed; + +struct nrf5_header_ie_link_metrics { + uint8_t vendor_oui[IE_VENDOR_SIZE_MIN]; + uint8_t lqi_token; + uint8_t link_margin_token; + uint8_t rssi_token; +} __packed; + +struct nrf5_header_ie { +#if CONFIG_LITTLE_ENDIAN + uint16_t length: 7; + uint16_t element_id_low: 1; + uint16_t element_id_high: 7; + uint16_t type: 1; +#else + uint16_t element_id_low; + uint16_t length: 7; + uint16_t type: 1; + uint16_t element_id_high: 7; +#endif + union { + struct nrf5_header_ie_link_metrics link_metrics; + struct nrf5_header_ie_csl_reduced csl_reduced; + } content; +} __packed; + +struct nrf5_rx_frame { + void *fifo_reserved; /* 1st word reserved for use by fifo. */ + uint8_t *psdu; /* Pointer to a received frame. */ + uint64_t time; /* RX timestamp. */ + uint8_t lqi; /* Last received frame LQI value. */ + int8_t rssi; /* Last received frame RSSI value. */ + bool ack_fpb; /* FPB value in ACK sent for the received frame. */ + bool ack_seb; /* SEB value in ACK sent for the received frame. */ +}; + +/** Energy detection callback */ +typedef void (*nrf5_energy_detection_done_cb_t)(int16_t max_ed); + +struct nrf5_data { + ATOMIC_DEFINE(pending_events, PENDING_EVENT_COUNT); + + /* Radio state */ + otRadioState state; + + /* TX power */ + int8_t tx_power; + + /* Max TX power for channel */ + int8_t max_tx_power_table[CHANNEL_COUNT]; + + /* Enable/disable RxOnWhenIdle MAC PIB attribute (Table 8-94). */ + bool rx_on_when_idle; + + /* Radio channel */ + uint8_t channel; + + /* Promiscuous mode */ + bool promiscuous; + + /* 802.15.4 HW address. */ + uint8_t mac[EXTENDED_ADDRESS_SIZE]; + + /* Radio capabilities */ + otRadioCaps capabilities; + + struct { + /* Buffers for passing received frame pointers and data to the + * RX thread via rx fifo object. + */ + struct nrf5_rx_frame frames[CONFIG_NRF_802154_RX_BUFFERS]; + + /* RX fifo queue. */ + struct k_fifo fifo; + + /* Frame pending bit value in ACK sent for the last received frame. */ + bool last_frame_ack_fpb; + + /* Security Enabled bit value in ACK sent for the last received frame. */ + bool last_frame_ack_seb; + + /* RX result, updated in radio transmit callbacks. */ + otError result; + } rx; + + struct { + /* TX frame */ + otRadioFrame frame; + +#if defined(CONFIG_OPENTHREAD_TIME_SYNC) + otRadioIeInfo ie_info; +#endif + + /* TX buffer. First byte is PHR (length), remaining bytes are + * MPDU data. + */ + uint8_t psdu[PHR_SIZE + MAX_PACKET_SIZE]; + + /* TX result, updated in radio transmit callbacks. */ + otError result; + } tx; + + struct { + /* A descriptor for the received ACK frame. psdu pointer be NULL if no + * ACK was requested/received. + */ + struct nrf5_rx_frame desc; + + /* ACK frame */ + otRadioFrame frame; + + /* ACK PSDU buffer */ + uint8_t psdu[ACK_PKT_LENGTH]; + + } ack; + + struct { + /* Energy detection callback */ + nrf5_energy_detection_done_cb_t cb; + + /* Duration of energy detection procedure */ + uint16_t time; + + /* Energy detection channel */ + uint8_t channel; + + /* Maximum energy detected value in dBm*/ + int16_t value; + } energy_detection; + + /* Get RSSI complete semaphore. Unlocked when energy detect is complete. */ + struct k_sem rssi_wait; + +#if defined(CONFIG_NRF_802154_SER_HOST) && defined(CONFIG_OPENTHREAD_CSL_RECEIVER) + struct { + /* The last configured value of CSL period in units of 10 symbols. */ + uint32_t period; + /* The last configured value of CSL phase time in nanoseconds. */ + uint64_t rx_time; + } csl; +#endif /* CONFIG_NRF_802154_SER_HOST && CONFIG_OPENTHREAD_CSL_RECEIVER */ +}; + +static struct nrf5_data nrf5_data; + +static inline bool is_pending_event_set(enum nrf5_pending_events event) +{ + return atomic_test_bit(nrf5_data.pending_events, event); +} + +static void set_pending_event(enum nrf5_pending_events event) +{ + atomic_set_bit(nrf5_data.pending_events, event); + otSysEventSignalPending(); +} + +static void reset_pending_event(enum nrf5_pending_events event) +{ + atomic_clear_bit(nrf5_data.pending_events, event); +} + +static int nrf5_set_channel(uint16_t channel) +{ + LOG_DBG("set channel %u", channel); + + if (channel < 11 || channel > 26) { + return channel < 11 ? -ENOTSUP : -EINVAL; + } + + nrf_802154_channel_set(channel); + + return 0; +} + +static int nrf5_energy_detection_start(uint16_t duration, nrf5_energy_detection_done_cb_t done_cb) +{ + int err = 0; + + if (nrf5_data.energy_detection.cb == NULL) { + nrf5_data.energy_detection.cb = done_cb; + + if (nrf_802154_energy_detection((uint32_t)duration * 1000) == false) { + nrf5_data.energy_detection.cb = NULL; + err = -EBUSY; + } + } else { + err = -EALREADY; + } + + return err; +} + +static int8_t get_transmit_power_for_channel(uint8_t aChannel) +{ + int8_t channel_max_power = OT_RADIO_POWER_INVALID; + int8_t power = 0; /* 0 dbm as default value */ + + if (aChannel >= OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MIN && + aChannel <= OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MAX) { + channel_max_power = + nrf5_data.max_tx_power_table[aChannel - OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MIN]; + } + + if (nrf5_data.tx_power != OT_RADIO_POWER_INVALID) { + power = (channel_max_power < nrf5_data.tx_power) ? channel_max_power + : nrf5_data.tx_power; + } else if (channel_max_power != OT_RADIO_POWER_INVALID) { + power = channel_max_power; + } + + return power; +} + +static int nrf5_set_tx_power(uint16_t channel) +{ + int8_t tx_power = get_transmit_power_for_channel(channel); + + LOG_DBG("set tx_power %d", tx_power); + + nrf_802154_tx_power_set(tx_power); + + return 0; +} + +#if defined(CONFIG_OPENTHREAD_CSL_RECEIVER) || defined(CONFIG_OPENTHREAD_LINK_METRICS_SUBJECT) +static int nrf5_ack_data_set(uint16_t short_addr, const otExtAddress *ext_addr, + const struct nrf5_header_ie *header_ie) + +{ + uint8_t ext_addr_le[EXTENDED_ADDRESS_SIZE]; + uint8_t short_addr_le[SHORT_ADDRESS_SIZE]; + + if (short_addr == NRF5_BROADCAST_ADDRESS || ext_addr == NULL) { + return -ENOTSUP; + } + + sys_put_le16(short_addr, short_addr_le); + sys_memcpy_swap(ext_addr_le, ext_addr->m8, EXTENDED_ADDRESS_SIZE); + + if (short_addr != NRF5_NO_SHORT_ADDRESS_ASSIGNED) { + nrf_802154_ack_data_set(short_addr_le, false, header_ie, + header_ie->length + IE_HEADER_SIZE, NRF_802154_ACK_DATA_IE); + } + nrf_802154_ack_data_set(ext_addr_le, true, header_ie, header_ie->length + IE_HEADER_SIZE, + NRF_802154_ACK_DATA_IE); + + return 0; +} +#endif + +#if defined(CONFIG_OPENTHREAD_CSL_RECEIVER) +static int nrf5_ack_data_clear(uint16_t short_addr, const otExtAddress *ext_addr) +{ + uint8_t ext_addr_le[EXTENDED_ADDRESS_SIZE]; + uint8_t short_addr_le[SHORT_ADDRESS_SIZE]; + + if (short_addr == NRF5_BROADCAST_ADDRESS || ext_addr == NULL) { + return -ENOTSUP; + } + + sys_put_le16(short_addr, short_addr_le); + sys_memcpy_swap(ext_addr_le, ext_addr->m8, EXTENDED_ADDRESS_SIZE); + + if (short_addr != NRF5_NO_SHORT_ADDRESS_ASSIGNED) { + nrf_802154_ack_data_clear(short_addr_le, false, NRF_802154_ACK_DATA_IE); + } + nrf_802154_ack_data_clear(ext_addr_le, true, NRF_802154_ACK_DATA_IE); + + return 0; +} +#endif + +static void nrf5_get_eui64(uint8_t *mac) +{ + uint64_t factoryAddress; + uint32_t index = 0; + +#if !defined(CONFIG_NRF5_UICR_EUI64_ENABLE) + uint32_t deviceid[2]; + + /* Set the MAC Address Block Larger (MA-L) formerly called OUI. */ + mac[index++] = (NRF5_VENDOR_OUI >> 16) & 0xff; + mac[index++] = (NRF5_VENDOR_OUI >> 8) & 0xff; + mac[index++] = NRF5_VENDOR_OUI & 0xff; + +#if defined(NRF54H_SERIES) + /* Can't access SICR with device id on a radio core. Use BLE.ADDR. */ + deviceid[0] = NRF_FICR->BLE.ADDR[0]; + deviceid[1] = NRF_FICR->BLE.ADDR[1]; +#elif defined(CONFIG_TRUSTED_EXECUTION_NONSECURE) && defined(NRF_FICR_S) + soc_secure_read_deviceid(deviceid); +#else + deviceid[0] = nrf_ficr_deviceid_get(NRF_FICR, 0); + deviceid[1] = nrf_ficr_deviceid_get(NRF_FICR, 1); +#endif + + factoryAddress = (uint64_t)deviceid[EUI64_ADDR_HIGH] << 32; + factoryAddress |= deviceid[EUI64_ADDR_LOW]; +#else + /* Use device identifier assigned during the production. */ + factoryAddress = (uint64_t)EUI64_ADDR[EUI64_ADDR_HIGH] << 32; + factoryAddress |= EUI64_ADDR[EUI64_ADDR_LOW]; +#endif + memcpy(mac + index, &factoryAddress, sizeof(factoryAddress) - index); +} + +static otRadioCaps nrf5_get_caps(void) +{ + otRadioCaps caps = OT_RADIO_CAPS_NONE; + + nrf_802154_capabilities_t radio_caps = nrf_802154_capabilities_get(); + + caps |= OT_RADIO_CAPS_ENERGY_SCAN | OT_RADIO_CAPS_ACK_TIMEOUT | OT_RADIO_CAPS_SLEEP_TO_TX | + OT_RADIO_CAPS_RX_ON_WHEN_IDLE; + + if (radio_caps & NRF_802154_CAPABILITY_CSMA) { + caps |= OT_RADIO_CAPS_CSMA_BACKOFF; + } + +#if !defined(CONFIG_OPENTHREAD_THREAD_VERSION_1_1) + if (radio_caps & NRF_802154_CAPABILITY_SECURITY) { + caps |= OT_RADIO_CAPS_TRANSMIT_SEC; + } +#endif + + if (radio_caps & NRF_802154_CAPABILITY_DELAYED_TX) { + caps |= OT_RADIO_CAPS_TRANSMIT_TIMING; + } + + if (radio_caps & NRF_802154_CAPABILITY_DELAYED_RX) { + caps |= OT_RADIO_CAPS_RECEIVE_TIMING; + } + + return caps; +} + +/** + * @brief Convert 32-bit (potentially wrapped) OpenThread microsecond timestamps + * to 64-bit Zephyr network subsystem nanosecond timestamps. + * + * This is a workaround until OpenThread is able to schedule 64-bit RX/TX time. + * + * @param target_time_ns_wrapped time in nanoseconds referred to the radio clock + * modulo UINT32_MAX. + * + * @return 64-bit nanosecond timestamp + */ +static uint64_t convert_32bit_us_wrapped_to_64bit_ns(uint32_t target_time_us_wrapped) +{ + /** + * OpenThread provides target time as a (potentially wrapped) 32-bit + * integer defining a moment in time in the microsecond domain. + * + * The target time can point to a moment in the future, but can be + * overdue as well. In order to determine what's the case and correctly + * set the absolute (non-wrapped) target time, it's necessary to compare + * the least significant 32 bits of the current 64-bit network subsystem + * time with the provided 32-bit target time. Let's assume that half of + * the 32-bit range can be used for specifying target times in the + * future, and the other half - in the past. + */ + uint64_t now_us = otPlatTimeGet(); + uint32_t now_us_wrapped = (uint32_t)now_us; + uint32_t time_diff = target_time_us_wrapped - now_us_wrapped; + uint64_t result = UINT64_C(0); + + if (time_diff < 0x80000000) { + /** + * Target time is assumed to be in the future. Check if a 32-bit overflow + * occurs between the current time and the target time. + */ + if (now_us_wrapped > target_time_us_wrapped) { + /** + * Add a 32-bit overflow and replace the least significant 32 bits + * with the provided target time. + */ + result = now_us + UINT32_MAX + 1; + result &= ~(uint64_t)UINT32_MAX; + result |= target_time_us_wrapped; + } else { + /** + * Leave the most significant 32 bits and replace the least significant + * 32 bits with the provided target time. + */ + result = (now_us & (~(uint64_t)UINT32_MAX)) | target_time_us_wrapped; + } + } else { + /** + * Target time is assumed to be in the past. Check if a 32-bit overflow + * occurs between the target time and the current time. + */ + if (now_us_wrapped > target_time_us_wrapped) { + /** + * Leave the most significant 32 bits and replace the least significant + * 32 bits with the provided target time. + */ + result = (now_us & (~(uint64_t)UINT32_MAX)) | target_time_us_wrapped; + } else { + /** + * Subtract a 32-bit overflow and replace the least significant + * 32 bits with the provided target time. + */ + result = now_us - UINT32_MAX - 1; + result &= ~(uint64_t)UINT32_MAX; + result |= target_time_us_wrapped; + } + } + + __ASSERT_NO_MSG(result <= INT64_MAX / NSEC_PER_USEC); + return result * NSEC_PER_USEC; +} + +void platformRadioInit(void) +{ + nrf5_data.state = OT_RADIO_STATE_DISABLED; + + /* Get the default tx output power from Kconfig */ + nrf5_data.tx_power = CONFIG_OPENTHREAD_DEFAULT_TX_POWER; + + for (size_t i = 0; i < CHANNEL_COUNT; i++) { + nrf5_data.max_tx_power_table[i] = OT_RADIO_POWER_INVALID; + } + + nrf5_data.rx_on_when_idle = true; + + nrf5_get_eui64(nrf5_data.mac); + + k_fifo_init(&nrf5_data.rx.fifo); + + nrf5_data.tx.frame.mPsdu = &nrf5_data.tx.psdu[1]; +#if defined(CONFIG_OPENTHREAD_TIME_SYNC) + nrf5_data.tx.frame.mInfo.mTxInfo.mIeInfo = &nrf5_data.tx.ie_info; +#endif + + k_sem_init(&nrf5_data.rssi_wait, 0, 1); + + nrf_802154_init(); + + nrf5_data.capabilities = nrf5_get_caps(); +} + +static void openthread_handle_received_frame(otInstance *instance, struct nrf5_rx_frame *rx_frame) +{ + otRadioFrame recv_frame; + uint8_t *psdu; + + ARG_UNUSED(instance); + + __ASSERT_NO_MSG(rx_frame->psdu != NULL); + + memset(&recv_frame, 0, sizeof(otRadioFrame)); + + recv_frame.mPsdu = &rx_frame->psdu[1]; + /* Length inc. CRC. */ + recv_frame.mLength = rx_frame->psdu[0]; + recv_frame.mChannel = nrf5_data.channel; + recv_frame.mInfo.mRxInfo.mLqi = rx_frame->lqi; + recv_frame.mInfo.mRxInfo.mRssi = rx_frame->rssi; + recv_frame.mInfo.mRxInfo.mAckedWithFramePending = rx_frame->ack_fpb; + recv_frame.mInfo.mRxInfo.mTimestamp = rx_frame->time; + recv_frame.mInfo.mRxInfo.mAckedWithSecEnhAck = rx_frame->ack_seb; + + LOG_DBG("RX %p len: %u, ch: %u, rssi: %d", (void *)recv_frame.mPsdu, recv_frame.mLength, + recv_frame.mChannel, recv_frame.mInfo.mRxInfo.mRssi); + + if (IS_ENABLED(CONFIG_OPENTHREAD_DIAG) && otPlatDiagModeGet()) { + otPlatDiagRadioReceiveDone(instance, &recv_frame, OT_ERROR_NONE); + } else { + otPlatRadioReceiveDone(instance, &recv_frame, OT_ERROR_NONE); + } + + psdu = rx_frame->psdu; + rx_frame->psdu = NULL; + nrf_802154_buffer_free_raw(psdu); +} + +static void energy_detected(int16_t max_ed) +{ + nrf5_data.energy_detection.value = max_ed; + set_pending_event(PENDING_EVENT_DETECT_ENERGY_DONE); +} + +static bool nrf5_tx(otRadioFrame *frame, uint8_t *payload, bool cca) +{ + nrf_802154_transmit_metadata_t metadata = { + .frame_props = + { + .is_secured = frame->mInfo.mTxInfo.mIsSecurityProcessed, + .dynamic_data_is_set = frame->mInfo.mTxInfo.mIsHeaderUpdated, + }, + .cca = cca, + .tx_power = + { + .use_metadata_value = true, + .power = get_transmit_power_for_channel(frame->mChannel), + }, + }; + + return nrf_802154_transmit_raw(payload, &metadata); +} + +#if NRF_802154_CSMA_CA_ENABLED +static bool nrf5_tx_csma_ca(otRadioFrame *frame, uint8_t *payload) +{ + nrf_802154_transmit_csma_ca_metadata_t metadata = { + .frame_props = + { + .is_secured = frame->mInfo.mTxInfo.mIsSecurityProcessed, + .dynamic_data_is_set = frame->mInfo.mTxInfo.mIsHeaderUpdated, + }, + .tx_power = + { + .use_metadata_value = true, + .power = get_transmit_power_for_channel(frame->mChannel), + }, + + }; + + nrf_802154_csma_ca_max_backoffs_set(frame->mInfo.mTxInfo.mMaxCsmaBackoffs); + return nrf_802154_transmit_csma_ca_raw(payload, &metadata); +} +#endif + +static bool nrf5_tx_at(otRadioFrame *frame, uint8_t *payload) +{ + nrf_802154_transmit_at_metadata_t metadata = { + .frame_props = + { + .is_secured = frame->mInfo.mTxInfo.mIsSecurityProcessed, + .dynamic_data_is_set = frame->mInfo.mTxInfo.mIsHeaderUpdated, + }, + .cca = true, +#if defined(CONFIG_NRF5_SELECTIVE_TXCHANNEL) + .channel = frame->mChannel, +#else + .channel = nrf_802154_channel_get(), +#endif + .tx_power = + { + .use_metadata_value = true, + .power = get_transmit_power_for_channel(frame->mChannel), + }, + }; + + /* The timestamp points to the start of PHR but `nrf_802154_transmit_raw_at` + * expects a timestamp pointing to start of SHR. + */ + uint64_t tx_at = nrf_802154_timestamp_phr_to_shr_convert( + convert_32bit_us_wrapped_to_64bit_ns( + nrf5_data.tx.frame.mInfo.mTxInfo.mTxDelayBaseTime + + nrf5_data.tx.frame.mInfo.mTxInfo.mTxDelay) / + NSEC_PER_USEC); + + return nrf_802154_transmit_raw_at(payload, tx_at, &metadata); +} + +static void handle_frame_received(otInstance *aInstance) +{ + struct nrf5_rx_frame *rx_frame; + + while ((rx_frame = (struct nrf5_rx_frame *)k_fifo_get(&nrf5_data.rx.fifo, K_NO_WAIT)) != + NULL) { + openthread_handle_received_frame(aInstance, rx_frame); + } +} + +static void handle_rx_failed(otInstance *aInstance) +{ + if (IS_ENABLED(CONFIG_OPENTHREAD_DIAG) && otPlatDiagModeGet()) { + otPlatDiagRadioReceiveDone(aInstance, NULL, nrf5_data.rx.result); + } else { + otPlatRadioReceiveDone(aInstance, NULL, nrf5_data.rx.result); + } +} + +static otError transmit_frame(otInstance *aInstance) +{ + bool result = true; + + ARG_UNUSED(aInstance); + + if (nrf5_data.tx.frame.mLength > MAX_PACKET_SIZE) { + LOG_ERR("Payload (with FCS) too large: %d", nrf5_data.tx.frame.mLength); + return OT_ERROR_INVALID_ARGS; + } + + LOG_DBG("TX %p len: %u", (void *)nrf5_data.tx.frame.mPsdu, nrf5_data.tx.frame.mLength); + + nrf5_data.tx.psdu[0] = nrf5_data.tx.frame.mLength; + +#if defined(CONFIG_OPENTHREAD_TIME_SYNC) + if (nrf5_data.tx.frame.mInfo.mTxInfo.mIeInfo->mTimeIeOffset != 0) { + uint8_t *time_ie = nrf5_data.tx.frame.mPsdu + + nrf5_data.tx.frame.mInfo.mTxInfo.mIeInfo->mTimeIeOffset; + uint64_t offset_plat_time = + otPlatTimeGet() + + nrf5_data.tx.frame.mInfo.mTxInfo.mIeInfo->mNetworkTimeOffset; + + *(time_ie++) = nrf5_data.tx.frame.mInfo.mTxInfo.mIeInfo->mTimeSyncSeq; + sys_put_le64(offset_plat_time, time_ie); + } +#endif + + if ((nrf5_data.capabilities & OT_RADIO_CAPS_TRANSMIT_TIMING) && + (nrf5_data.tx.frame.mInfo.mTxInfo.mTxDelay != 0)) { +#if !defined(CONFIG_NRF5_SELECTIVE_TXCHANNEL) + nrf5_set_channel(nrf5_data.tx.frame.mChannel); +#endif + if (!nrf5_tx_at(&nrf5_data.tx.frame, nrf5_data.tx.psdu)) { + LOG_ERR("TX at failed"); + return OT_ERROR_INVALID_STATE; + } + } else if (nrf5_data.tx.frame.mInfo.mTxInfo.mCsmaCaEnabled) { + nrf5_set_channel(nrf5_data.tx.frame.mChannel); + if (nrf5_data.capabilities & OT_RADIO_CAPS_CSMA_BACKOFF) { + result = nrf5_tx_csma_ca(&nrf5_data.tx.frame, nrf5_data.tx.psdu); + } else { + result = nrf5_tx(&nrf5_data.tx.frame, nrf5_data.tx.psdu, true); + } + } else { + nrf5_set_channel(nrf5_data.tx.frame.mChannel); + result = nrf5_tx(&nrf5_data.tx.frame, nrf5_data.tx.psdu, false); + } + + otPlatRadioTxStarted(aInstance, &nrf5_data.tx.frame); + + if (!result) { + LOG_ERR("TX failed"); + nrf5_data.tx.result = OT_ERROR_CHANNEL_ACCESS_FAILURE; + set_pending_event(PENDING_EVENT_TX_DONE); + } + + return OT_ERROR_NONE; +} + +static otError handle_ack(void) +{ + uint8_t ack_len; + uint8_t frame_type; + otError err = OT_ERROR_NONE; + + if (nrf5_data.ack.desc.time == NRF_802154_NO_TIMESTAMP) { + /* Ack timestamp is invalid and cannot be used by the upper layer. + * Report the transmission as failed as if the Ack was not received at all. + */ + LOG_WRN("Invalid ACK timestamp."); + err = OT_ERROR_NO_ACK; + goto free_nrf_ack; + } + + ack_len = nrf5_data.ack.desc.psdu[0]; + if (ack_len > ACK_PKT_LENGTH) { + LOG_ERR("Invalid ACK length %u", ack_len); + err = OT_ERROR_NO_ACK; + goto free_nrf_ack; + } + + frame_type = nrf5_data.ack.desc.psdu[1] & FRAME_TYPE_MASK; + if (frame_type != FRAME_TYPE_ACK) { + LOG_ERR("Invalid frame type %u", frame_type); + err = OT_ERROR_NO_ACK; + goto free_nrf_ack; + } + + if (nrf5_data.ack.frame.mLength != 0) { + LOG_ERR("Overwriting unhandled ACK frame."); + } + + /* Upper layers expect the frame to start at the MAC header, skip the + * PHY header (1 byte). + */ + memcpy(nrf5_data.ack.psdu, nrf5_data.ack.desc.psdu + 1, ack_len); + + nrf5_data.ack.frame.mPsdu = nrf5_data.ack.psdu; + nrf5_data.ack.frame.mLength = ack_len; + nrf5_data.ack.frame.mInfo.mRxInfo.mLqi = nrf5_data.ack.desc.lqi; + nrf5_data.ack.frame.mInfo.mRxInfo.mRssi = nrf5_data.ack.desc.rssi; + nrf5_data.ack.frame.mInfo.mRxInfo.mTimestamp = nrf5_data.ack.desc.time; + +free_nrf_ack: + nrf_802154_buffer_free_raw(nrf5_data.ack.desc.psdu); + nrf5_data.ack.desc.psdu = NULL; + + return err; +} + +static void handle_tx_done(otInstance *aInstance) +{ + if (nrf5_data.state == OT_RADIO_STATE_TRANSMIT) { + nrf5_data.state = OT_RADIO_STATE_RECEIVE; + + if (nrf5_data.ack.desc.psdu == NULL) { + /* No ACK was requested. */ + nrf5_data.tx.result = OT_ERROR_NONE; + } else { + + /* Handle ACK packet. */ + nrf5_data.tx.result = handle_ack(); + } + + if (IS_ENABLED(CONFIG_OPENTHREAD_DIAG) && otPlatDiagModeGet()) { + otPlatDiagRadioTransmitDone(aInstance, &nrf5_data.tx.frame, + nrf5_data.tx.result); + } else { + otPlatRadioTxDone(aInstance, &nrf5_data.tx.frame, + nrf5_data.ack.frame.mLength ? &nrf5_data.ack.frame : NULL, + nrf5_data.tx.result); + nrf5_data.ack.frame.mLength = 0; + } + } +} + +static void handle_sleep(otInstance *aInstance) +{ + ARG_UNUSED(otPlatRadioSleep(aInstance)); +} + +static bool handle_detect_energy(otInstance *aInstance) +{ + int error; + + nrf5_set_channel(nrf5_data.energy_detection.channel); + + error = nrf5_energy_detection_start(nrf5_data.energy_detection.time, energy_detected); + return error == 0; +} + +static void handle_detect_energy_done(otInstance *aInstance) +{ + otPlatRadioEnergyScanDone(aInstance, (int8_t)nrf5_data.energy_detection.value); +} + +void platformRadioProcess(otInstance *aInstance) +{ + bool event_pending = false; + + if (is_pending_event_set(PENDING_EVENT_FRAME_RECEIVED)) { + reset_pending_event(PENDING_EVENT_FRAME_RECEIVED); + handle_frame_received(aInstance); + } + + if (is_pending_event_set(PENDING_EVENT_RX_FAILED)) { + reset_pending_event(PENDING_EVENT_RX_FAILED); + handle_rx_failed(aInstance); + } + + if (is_pending_event_set(PENDING_EVENT_TX_DONE)) { + reset_pending_event(PENDING_EVENT_TX_DONE); + handle_tx_done(aInstance); + } + + if (is_pending_event_set(PENDING_EVENT_SLEEP)) { + reset_pending_event(PENDING_EVENT_SLEEP); + handle_sleep(aInstance); + } + + /* handle events that can't run during transmission */ + if (nrf5_data.state != OT_RADIO_STATE_TRANSMIT) { + if (is_pending_event_set(PENDING_EVENT_DETECT_ENERGY)) { + if (handle_detect_energy(aInstance)) { + reset_pending_event(PENDING_EVENT_DETECT_ENERGY); + } else { + event_pending = true; + } + } + + if (is_pending_event_set(PENDING_EVENT_DETECT_ENERGY_DONE)) { + handle_detect_energy_done(aInstance); + reset_pending_event(PENDING_EVENT_DETECT_ENERGY_DONE); + } + } + + if (event_pending) { + otSysEventSignalPending(); + } +} + +uint16_t platformRadioChannelGet(otInstance *aInstance) +{ + ARG_UNUSED(aInstance); + + return nrf5_data.channel; +} + +#if defined(CONFIG_OPENTHREAD_DIAG) +void platformRadioChannelSet(uint8_t aChannel) +{ + nrf5_data.channel = aChannel; +} +#endif + +/* Radio configuration */ + +otRadioCaps otPlatRadioGetCaps(otInstance *aInstance) +{ + ARG_UNUSED(aInstance); + + return nrf5_data.capabilities; +} + +int8_t otPlatRadioGetReceiveSensitivity(otInstance *aInstance) +{ + ARG_UNUSED(aInstance); + + return CONFIG_OPENTHREAD_DEFAULT_RX_SENSITIVITY; +} + +void otPlatRadioGetIeeeEui64(otInstance *aInstance, uint8_t *aIeeeEui64) +{ + ARG_UNUSED(aInstance); + + memcpy(aIeeeEui64, nrf5_data.mac, EXTENDED_ADDRESS_SIZE); +} + +void otPlatRadioSetPanId(otInstance *aInstance, otPanId aPanId) +{ + uint8_t pan_id_le[2]; + + ARG_UNUSED(aInstance); + + LOG_DBG("PanId: 0x%x", aPanId); + + sys_put_le16(aPanId, pan_id_le); + nrf_802154_pan_id_set(pan_id_le); +} + +void otPlatRadioSetExtendedAddress(otInstance *aInstance, const otExtAddress *aExtAddress) +{ + const uint8_t *ieee_addr = aExtAddress->m8; + + ARG_UNUSED(aInstance); + + LOG_DBG("IEEE address %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", ieee_addr[7], ieee_addr[6], + ieee_addr[5], ieee_addr[4], ieee_addr[3], ieee_addr[2], ieee_addr[1], ieee_addr[0]); + + nrf_802154_extended_address_set(ieee_addr); +} + +void otPlatRadioSetShortAddress(otInstance *aInstance, otShortAddress aShortAddress) +{ + uint8_t short_addr_le[2]; + + ARG_UNUSED(aInstance); + + LOG_DBG("Short Address: 0x%x", aShortAddress); + + sys_put_le16(aShortAddress, short_addr_le); + nrf_802154_short_address_set(short_addr_le); +} + +otError otPlatRadioGetTransmitPower(otInstance *aInstance, int8_t *aPower) +{ + ARG_UNUSED(aInstance); + + if (aPower == NULL) { + return OT_ERROR_INVALID_ARGS; + } + + *aPower = nrf5_data.tx_power; + + return OT_ERROR_NONE; +} + +otError otPlatRadioSetTransmitPower(otInstance *aInstance, int8_t aPower) +{ + ARG_UNUSED(aInstance); + + nrf5_data.tx_power = aPower; + + return OT_ERROR_NONE; +} + +otError otPlatRadioGetCcaEnergyDetectThreshold(otInstance *aInstance, int8_t *aThreshold) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aThreshold); + + return OT_ERROR_NOT_IMPLEMENTED; +} + +otError otPlatRadioSetCcaEnergyDetectThreshold(otInstance *aInstance, int8_t aThreshold) +{ + OT_UNUSED_VARIABLE(aInstance); + OT_UNUSED_VARIABLE(aThreshold); + + return OT_ERROR_NOT_IMPLEMENTED; +} + +bool otPlatRadioGetPromiscuous(otInstance *aInstance) +{ + ARG_UNUSED(aInstance); + + LOG_DBG("PromiscuousMode=%d", nrf5_data.promiscuous ? 1 : 0); + + return nrf5_data.promiscuous; +} + +void otPlatRadioSetPromiscuous(otInstance *aInstance, bool aEnable) +{ + ARG_UNUSED(aInstance); + + LOG_DBG("PromiscuousMode=%d", aEnable ? 1 : 0); + + nrf5_data.promiscuous = aEnable; + + nrf_802154_promiscuous_set(nrf5_data.promiscuous); +} + +void otPlatRadioSetRxOnWhenIdle(otInstance *aInstance, bool aEnable) +{ + ARG_UNUSED(aInstance); + + LOG_DBG("RxOnWhenIdle=%d", aEnable ? 1 : 0); + + nrf5_data.rx_on_when_idle = aEnable; + nrf_802154_rx_on_when_idle_set(nrf5_data.rx_on_when_idle); + + if (!nrf5_data.rx_on_when_idle) { + (void)nrf_802154_sleep_if_idle(); + } +} + +#if !defined(CONFIG_OPENTHREAD_THREAD_VERSION_1_1) +static void nrf5_key_store(uint8_t *key_value, nrf_802154_key_id_mode_t key_id_mode, + uint8_t *key_id) +{ + nrf_802154_key_t key = { + .value.p_cleartext_key = key_value, + .id.mode = key_id_mode, + .id.p_key_id = key_id, + .type = NRF_802154_KEY_CLEARTEXT, + .frame_counter = 0, + .use_global_frame_counter = true, + }; + + __ASSERT_EVAL((void)nrf_802154_security_key_store(&key), + nrf_802154_security_error_t err = nrf_802154_security_key_store(&key), + err == NRF_802154_SECURITY_ERROR_NONE || + err == NRF_802154_SECURITY_ERROR_ALREADY_PRESENT, + "Storing key failed, err: %d", err); +} + +void otPlatRadioSetMacKey(otInstance *aInstance, uint8_t aKeyIdMode, uint8_t aKeyId, + const otMacKeyMaterial *aPrevKey, const otMacKeyMaterial *aCurrKey, + const otMacKeyMaterial *aNextKey, otRadioKeyType aKeyType) +{ + ARG_UNUSED(aInstance); + __ASSERT_NO_MSG(aPrevKey != NULL && aCurrKey != NULL && aNextKey != NULL); + +#if defined(CONFIG_OPENTHREAD_PLATFORM_KEYS_EXPORTABLE_ENABLE) + __ASSERT_NO_MSG(aKeyType == OT_KEY_TYPE_KEY_REF); + size_t keyLen; + otError error; + + error = otPlatCryptoExportKey(aPrevKey->mKeyMaterial.mKeyRef, + (uint8_t *)aPrevKey->mKeyMaterial.mKey.m8, OT_MAC_KEY_SIZE, + &keyLen); + __ASSERT_NO_MSG(error == OT_ERROR_NONE); + error = otPlatCryptoExportKey(aCurrKey->mKeyMaterial.mKeyRef, + (uint8_t *)aCurrKey->mKeyMaterial.mKey.m8, OT_MAC_KEY_SIZE, + &keyLen); + __ASSERT_NO_MSG(error == OT_ERROR_NONE); + error = otPlatCryptoExportKey(aNextKey->mKeyMaterial.mKeyRef, + (uint8_t *)aNextKey->mKeyMaterial.mKey.m8, OT_MAC_KEY_SIZE, + &keyLen); + __ASSERT_NO_MSG(error == OT_ERROR_NONE); +#else + __ASSERT_NO_MSG(aKeyType == OT_KEY_TYPE_LITERAL_KEY); +#endif + + uint8_t key_id_mode = aKeyIdMode >> 3; + + if (key_id_mode == 1) { + __ASSERT_NO_MSG(NRF_802154_SECURITY_KEY_STORAGE_SIZE >= 3); + + /* aKeyId in range: (1, 0x80) means valid keys */ + uint8_t prev_key_id = aKeyId == 1 ? 0x80 : aKeyId - 1; + uint8_t next_key_id = aKeyId == 0x80 ? 1 : aKeyId + 1; + + nrf_802154_security_key_remove_all(); + + nrf5_key_store((uint8_t *)aPrevKey->mKeyMaterial.mKey.m8, key_id_mode, + &prev_key_id); + nrf5_key_store((uint8_t *)aCurrKey->mKeyMaterial.mKey.m8, key_id_mode, &aKeyId); + nrf5_key_store((uint8_t *)aNextKey->mKeyMaterial.mKey.m8, key_id_mode, + &next_key_id); + + } else { + /* aKeyId == 0 is used only to clear keys for stack reset in RCP */ + __ASSERT_NO_MSG((key_id_mode == 0) && (aKeyId == 0)); + + nrf_802154_security_key_remove_all(); + } +} + +void otPlatRadioSetMacFrameCounter(otInstance *aInstance, uint32_t aMacFrameCounter) +{ + ARG_UNUSED(aInstance); + + nrf_802154_security_global_frame_counter_set(aMacFrameCounter); +} + +void otPlatRadioSetMacFrameCounterIfLarger(otInstance *aInstance, uint32_t aMacFrameCounter) +{ + ARG_UNUSED(aInstance); + + nrf_802154_security_global_frame_counter_set_if_larger(aMacFrameCounter); +} +#endif + +/* Radio operations */ + +uint64_t otPlatTimeGet(void) +{ + return nrf_802154_time_get() * NSEC_PER_USEC; +} + +uint64_t otPlatRadioGetNow(otInstance *aInstance) +{ + ARG_UNUSED(aInstance); + + return otPlatTimeGet(); +} + +otRadioState otPlatRadioGetState(otInstance *aInstance) +{ + ARG_UNUSED(aInstance); + + return nrf5_data.state; +} + +otError otPlatRadioEnable(otInstance *aInstance) +{ + ARG_UNUSED(aInstance); + + if (nrf5_data.state != OT_RADIO_STATE_DISABLED && nrf5_data.state != OT_RADIO_STATE_SLEEP) { + return OT_ERROR_INVALID_STATE; + } + + nrf5_data.state = OT_RADIO_STATE_SLEEP; + return OT_ERROR_NONE; +} + +otError otPlatRadioDisable(otInstance *aInstance) +{ + ARG_UNUSED(aInstance); + + if (nrf5_data.state != OT_RADIO_STATE_DISABLED && nrf5_data.state != OT_RADIO_STATE_SLEEP) { + return OT_ERROR_INVALID_STATE; + } + + nrf5_data.state = OT_RADIO_STATE_DISABLED; + return OT_ERROR_NONE; +} + +bool otPlatRadioIsEnabled(otInstance *aInstance) +{ + ARG_UNUSED(aInstance); + + return (nrf5_data.state != OT_RADIO_STATE_DISABLED) ? true : false; +} + +otError otPlatRadioSleep(otInstance *aInstance) +{ + ARG_UNUSED(aInstance); + + if (nrf5_data.state != OT_RADIO_STATE_SLEEP && nrf5_data.state != OT_RADIO_STATE_RECEIVE) { + return OT_ERROR_INVALID_STATE; + } + +#if defined(CONFIG_OPENTHREAD_CSL_RECEIVER) + if (nrf_802154_sleep_if_idle() != NRF_802154_SLEEP_ERROR_NONE) { + set_pending_event(PENDING_EVENT_SLEEP); + Z_SPIN_DELAY(1); + } +#else + if (!nrf_802154_sleep()) { + LOG_ERR("Error while stopping radio"); + } +#endif + + LOG_DBG("nRF5 802154 radio stopped"); + + nrf5_data.state = OT_RADIO_STATE_SLEEP; + + return OT_ERROR_NONE; +} + +otError otPlatRadioReceive(otInstance *aInstance, uint8_t aChannel) +{ + ARG_UNUSED(aInstance); + + if (nrf5_data.state == OT_RADIO_STATE_DISABLED) { + return OT_ERROR_INVALID_STATE; + } + + nrf5_data.channel = aChannel; + + nrf5_set_channel(nrf5_data.channel); + nrf5_set_tx_power(nrf5_data.channel); + + if (!nrf_802154_receive()) { + LOG_ERR("Failed to enter receive state"); + return OT_ERROR_FAILED; + } + + LOG_DBG("nRF5 802154 radio started (channel: %d)", nrf_802154_channel_get()); + + nrf5_data.state = OT_RADIO_STATE_RECEIVE; + + return OT_ERROR_NONE; +} + +#if defined(CONFIG_OPENTHREAD_CSL_RECEIVER) || defined(CONFIG_OPENTHREAD_WAKEUP_END_DEVICE) +otError otPlatRadioReceiveAt(otInstance *aInstance, uint8_t aChannel, uint32_t aStart, + + uint32_t aDuration) +{ + bool result; + + ARG_UNUSED(aInstance); + + /* Note that even if the nrf_802154_receive_at function is not called in time + * (for example due to the call being blocked by higher priority threads) and + * the delayed reception window is not scheduled, the CSL phase will still be + * calculated as if the following reception windows were at times + * anchor_time + n * csl_period. The previously set + * anchor_time will be used for calculations. + */ + result = nrf_802154_receive_at(convert_32bit_us_wrapped_to_64bit_ns(aStart) / NSEC_PER_USEC, + aDuration / NSEC_PER_USEC, aChannel, DRX_SLOT_RX); + + return result ? OT_ERROR_FAILED : OT_ERROR_NONE; +} +#endif + +otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *aInstance) +{ + ARG_UNUSED(aInstance); + + return &nrf5_data.tx.frame; +} + +otError otPlatRadioTransmit(otInstance *aInstance, otRadioFrame *aFrame) +{ + otError error = OT_ERROR_INVALID_STATE; + + ARG_UNUSED(aInstance); + ARG_UNUSED(aFrame); + + __ASSERT_NO_MSG(aFrame == &nrf5_data.tx.frame); + + if (nrf5_data.state == OT_RADIO_STATE_RECEIVE || nrf5_data.state == OT_RADIO_STATE_SLEEP) { + nrf5_data.state = OT_RADIO_STATE_TRANSMIT; + error = transmit_frame(aInstance); + } + + return error; +} + +static void get_rssi_energy_detected(int16_t max_ed) +{ + nrf5_data.energy_detection.value = max_ed; + k_sem_give(&nrf5_data.rssi_wait); +} + +int8_t otPlatRadioGetRssi(otInstance *aInstance) +{ + int8_t ret_rssi = INT8_MAX; + int error = 0; + const uint16_t detection_time = 1; + + ARG_UNUSED(aInstance); + + /* + * Blocking implementation of get RSSI + * using no-blocking nrf5_energy_detection_start + */ + error = nrf5_energy_detection_start(detection_time, get_rssi_energy_detected); + + if (error == 0) { + k_sem_take(&nrf5_data.rssi_wait, K_FOREVER); + ret_rssi = (int8_t)nrf5_data.energy_detection.value; + } + + return ret_rssi; +} + +otError otPlatRadioEnergyScan(otInstance *aInstance, uint8_t aScanChannel, uint16_t aScanDuration) +{ + int error = 0; + + nrf5_data.energy_detection.time = aScanDuration; + nrf5_data.energy_detection.channel = aScanChannel; + + reset_pending_event(PENDING_EVENT_DETECT_ENERGY); + reset_pending_event(PENDING_EVENT_DETECT_ENERGY_DONE); + + nrf5_set_channel(nrf5_data.energy_detection.channel); + + error = nrf5_energy_detection_start(nrf5_data.energy_detection.time, energy_detected); + + if (error != 0) { + /* + * OpenThread API does not accept failure of this function, + * it can return 'No Error' or 'Not Implemented' error only. + * If ed_scan start failed event is set to schedule the scan at + * later time. + */ + LOG_ERR("Failed do start energy scan, scheduling for later"); + set_pending_event(PENDING_EVENT_DETECT_ENERGY); + } + + return OT_ERROR_NONE; +} + +void otPlatRadioEnableSrcMatch(otInstance *aInstance, bool aEnable) +{ + ARG_UNUSED(aInstance); + + if (aEnable) { + nrf_802154_src_addr_matching_method_set(NRF_802154_SRC_ADDR_MATCH_THREAD); + } + + nrf_802154_auto_pending_bit_set(aEnable); +} + +otError otPlatRadioAddSrcMatchShortEntry(otInstance *aInstance, otShortAddress aShortAddress) +{ + ARG_UNUSED(aInstance); + + uint8_t short_address[SHORT_ADDRESS_SIZE]; + + sys_put_le16(aShortAddress, short_address); + + if (!nrf_802154_pending_bit_for_addr_set(short_address, false)) { + return OT_ERROR_NO_BUFS; + } + + return OT_ERROR_NONE; +} + +otError otPlatRadioAddSrcMatchExtEntry(otInstance *aInstance, const otExtAddress *aExtAddress) +{ + ARG_UNUSED(aInstance); + + if (!nrf_802154_pending_bit_for_addr_set((uint8_t *)aExtAddress->m8, true)) { + return OT_ERROR_NO_BUFS; + } + + return OT_ERROR_NONE; +} + +otError otPlatRadioClearSrcMatchShortEntry(otInstance *aInstance, otShortAddress aShortAddress) +{ + ARG_UNUSED(aInstance); + + uint8_t short_address[SHORT_ADDRESS_SIZE]; + + sys_put_le16(aShortAddress, short_address); + + if (!nrf_802154_pending_bit_for_addr_clear(short_address, false)) { + return OT_ERROR_NO_ADDRESS; + } + + return OT_ERROR_NONE; +} + +otError otPlatRadioClearSrcMatchExtEntry(otInstance *aInstance, const otExtAddress *aExtAddress) +{ + ARG_UNUSED(aInstance); + + if (!nrf_802154_pending_bit_for_addr_clear((uint8_t *)aExtAddress->m8, true)) { + return OT_ERROR_NO_ADDRESS; + } + + return OT_ERROR_NONE; +} + +void otPlatRadioClearSrcMatchShortEntries(otInstance *aInstance) +{ + ARG_UNUSED(aInstance); + + nrf_802154_pending_bit_for_addr_reset(false); +} + +void otPlatRadioClearSrcMatchExtEntries(otInstance *aInstance) +{ + ARG_UNUSED(aInstance); + + nrf_802154_pending_bit_for_addr_reset(true); +} + +#if defined(CONFIG_OPENTHREAD_CSL_RECEIVER) +otError otPlatRadioEnableCsl(otInstance *aInstance, uint32_t aCslPeriod, otShortAddress aShortAddr, + const otExtAddress *aExtAddr) +{ + int result; + + ARG_UNUSED(aInstance); + + const struct nrf5_header_ie header_ie = { + .length = sizeof(struct nrf5_header_ie_csl_reduced), + .element_id_high = (NRF5_HEADER_IE_ELEMENT_ID_CSL_IE) >> 1U, + .element_id_low = (NRF5_HEADER_IE_ELEMENT_ID_CSL_IE) & 0x01, + .type = NRF5_IE_TYPE_HEADER, + .content.csl_reduced = + { + .csl_phase = 0, + .csl_period = aCslPeriod, + }, + }; + + nrf_802154_csl_writer_period_set(aCslPeriod); +#if defined(CONFIG_NRF_802154_SER_HOST) + nrf5_data.csl.period = aCslPeriod; +#endif + + if (aCslPeriod == 0) { + result = nrf5_ack_data_clear(aShortAddr, aExtAddr); + } else { + result = nrf5_ack_data_set(aShortAddr, aExtAddr, &header_ie); + } + + return result ? OT_ERROR_FAILED : OT_ERROR_NONE; +} + +otError otPlatRadioResetCsl(otInstance *aInstance) +{ + ARG_UNUSED(aInstance); + + nrf_802154_csl_writer_period_set(0); +#if defined(CONFIG_NRF_802154_SER_HOST) + nrf5_data.csl.period = 0; +#endif + + nrf_802154_ack_data_remove_all(false, NRF_802154_ACK_DATA_IE); + nrf_802154_ack_data_remove_all(true, NRF_802154_ACK_DATA_IE); + + return OT_ERROR_NONE; +} + +void otPlatRadioUpdateCslSampleTime(otInstance *aInstance, uint32_t aCslSampleTime) +{ + ARG_UNUSED(aInstance); + + /* CSL sample time points to "start of MAC" while the expected RX time + * refers to "end of SFD". + */ + uint64_t expected_rx_time = + convert_32bit_us_wrapped_to_64bit_ns(aCslSampleTime - PHR_DURATION_US); + +#if defined(CONFIG_NRF_802154_SER_HOST) + uint64_t period_ns = (uint64_t)nrf5_data.csl.period * NSEC_PER_TEN_SYMBOLS; + bool changed = (expected_rx_time - nrf5_data.csl.rx_time) % period_ns; + + nrf5_data.csl.rx_time = expected_rx_time; + + if (changed) +#endif /* CONFIG_NRF_802154_SER_HOST */ + { + nrf_802154_csl_writer_anchor_time_set( + nrf_802154_timestamp_phr_to_mhr_convert(expected_rx_time / NSEC_PER_USEC)); + } +} +#endif + +uint8_t otPlatRadioGetCslAccuracy(otInstance *aInstance) +{ + ARG_UNUSED(aInstance); + + return CONFIG_CLOCK_CONTROL_NRF_ACCURACY; +} + +#if defined(CONFIG_OPENTHREAD_PLATFORM_CSL_UNCERT) +uint8_t otPlatRadioGetCslUncertainty(otInstance *aInstance) +{ + ARG_UNUSED(aInstance); + + return CONFIG_OPENTHREAD_PLATFORM_CSL_UNCERT; +} +#endif + +otError otPlatRadioSetChannelMaxTransmitPower(otInstance *aInstance, uint8_t aChannel, + int8_t aMaxPower) +{ + ARG_UNUSED(aInstance); + + if (aChannel < OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MIN || + aChannel > OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MAX) { + return OT_ERROR_INVALID_ARGS; + } + + nrf5_data.max_tx_power_table[aChannel - OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MIN] = aMaxPower; + + if (nrf5_data.channel == aChannel) { + nrf5_data.tx_power = get_transmit_power_for_channel(nrf5_data.channel); + } + + return OT_ERROR_NONE; +} + +#if defined(CONFIG_OPENTHREAD_LINK_METRICS_SUBJECT) +otError otPlatRadioConfigureEnhAckProbing(otInstance *aInstance, otLinkMetrics aLinkMetrics, + otShortAddress aShortAddress, + const otExtAddress *aExtAddress) +{ + int result; + + ARG_UNUSED(aInstance); + + const struct nrf5_header_ie header_ie = { + .length = sizeof(struct nrf5_header_ie_link_metrics), + .element_id_high = (NRF5_HEADER_IE_ELEMENT_ID_VENDOR_SPECIFIC_IE) >> 1U, + .element_id_low = (NRF5_HEADER_IE_ELEMENT_ID_VENDOR_SPECIFIC_IE) & 0x01, + .type = NRF5_IE_TYPE_HEADER, + .content.link_metrics = + { + .vendor_oui[0] = (IE_VENDOR_THREAD_OUI >> 0 & 0xff), + .vendor_oui[1] = (IE_VENDOR_THREAD_OUI >> 8 & 0xff), + .vendor_oui[2] = (IE_VENDOR_THREAD_OUI >> 16 & 0xff), + .lqi_token = aLinkMetrics.mLqi ? IE_VENDOR_THREAD_LQI_TOKEN : 0, + .link_margin_token = aLinkMetrics.mLinkMargin + ? IE_VENDOR_THREAD_MARGIN_TOKEN + : 0, + .rssi_token = aLinkMetrics.mRssi ? IE_VENDOR_THREAD_RSSI_TOKEN : 0, + }, + }; + + result = nrf5_ack_data_set(aShortAddress, aExtAddress, &header_ie); + + return result ? OT_ERROR_FAILED : OT_ERROR_NONE; +} +#endif + +/* Platform related */ + +#if defined(CONFIG_NRF5_CARRIER_FUNCTIONS) +otError platformRadioTransmitCarrier(otInstance *aInstance, bool aEnable) +{ + ARG_UNUSED(aInstance); + + if ((aEnable) && (nrf5_data.state == OT_RADIO_STATE_RECEIVE)) { + nrf_802154_tx_power_set(get_transmit_power_for_channel(nrf5_data.channel)); + + if (!nrf_802154_continuous_carrier()) { + LOG_ERR("Failed to enter continuous carrier state"); + return OT_ERROR_FAILED; + } + + LOG_DBG("Continuous carrier wave transmission started (channel: %d)", + nrf5_data.channel); + + nrf5_data.state = OT_RADIO_STATE_TRANSMIT; + + return OT_ERROR_NONE; + } else if ((!aEnable) && (nrf5_data.state == OT_RADIO_STATE_TRANSMIT)) { + return otPlatRadioReceive(aInstance, nrf5_data.channel); + } else { + return OT_ERROR_INVALID_STATE; + } +} + +otError platformRadioTransmitModulatedCarrier(otInstance *aInstance, bool aEnable, + const uint8_t *aData) +{ + ARG_UNUSED(aInstance); + + if (aEnable && nrf5_data.state == OT_RADIO_STATE_RECEIVE) { + if (aData == NULL) { + return OT_ERROR_INVALID_ARGS; + } + + nrf_802154_tx_power_set(get_transmit_power_for_channel(nrf5_data.channel)); + + if (!nrf_802154_modulated_carrier(aData)) { + LOG_ERR("Failed to enter modulated carrier state"); + return OT_ERROR_FAILED; + } + + LOG_DBG("Modulated carrier wave transmission started (channel: %d)", + nrf5_data.channel); + + nrf5_data.state = OT_RADIO_STATE_TRANSMIT; + + return OT_ERROR_NONE; + } else if ((!aEnable) && nrf5_data.state == OT_RADIO_STATE_TRANSMIT) { + return otPlatRadioReceive(aInstance, nrf5_data.channel); + } else { + return OT_ERROR_INVALID_STATE; + } +} + +#endif /* CONFIG_NRF5_CARRIER_FUNCTIONS */ + +/* nRF5 radio driver callbacks */ + +void nrf_802154_received_timestamp_raw(uint8_t *data, int8_t power, uint8_t lqi, uint64_t time) +{ + for (uint32_t i = 0; i < ARRAY_SIZE(nrf5_data.rx.frames); i++) { + if (nrf5_data.rx.frames[i].psdu != NULL) { + continue; + } + + nrf5_data.rx.frames[i].psdu = data; + nrf5_data.rx.frames[i].rssi = power; + nrf5_data.rx.frames[i].lqi = lqi; + + nrf5_data.rx.frames[i].time = + nrf_802154_timestamp_end_to_phr_convert(time, data[0]); + + nrf5_data.rx.frames[i].ack_fpb = nrf5_data.rx.last_frame_ack_fpb; + nrf5_data.rx.frames[i].ack_seb = nrf5_data.rx.last_frame_ack_seb; + nrf5_data.rx.last_frame_ack_fpb = false; + nrf5_data.rx.last_frame_ack_seb = false; + + k_fifo_put(&nrf5_data.rx.fifo, &nrf5_data.rx.frames[i]); + set_pending_event(PENDING_EVENT_FRAME_RECEIVED); + + return; + } + + __ASSERT(false, "Not enough rx frames allocated for nrf5 radio"); +} + +void nrf_802154_receive_failed(nrf_802154_rx_error_t error, uint32_t id) +{ +#if defined(CONFIG_OPENTHREAD_CSL_RECEIVER) + if (id == DRX_SLOT_RX && error == NRF_802154_RX_ERROR_DELAYED_TIMEOUT) { + if (!nrf5_data.rx_on_when_idle) { + /* Transition to RxOff done automatically by the driver */ + return; + } + set_pending_event(PENDING_EVENT_SLEEP); + } +#else + ARG_UNUSED(id); +#endif + + if (IS_ENABLED(CONFIG_NRF5_LOG_RX_FAILURES)) { + LOG_INF("Rx failed, error = %d", error); + } + + nrf5_data.rx.last_frame_ack_fpb = false; + nrf5_data.rx.last_frame_ack_seb = false; + + if (nrf5_data.state == OT_RADIO_STATE_RECEIVE) { + switch (error) { + case NRF_802154_RX_ERROR_INVALID_FRAME: + case NRF_802154_RX_ERROR_DELAYED_TIMEOUT: + nrf5_data.rx.result = OT_ERROR_NO_FRAME_RECEIVED; + break; + + case NRF_802154_RX_ERROR_INVALID_FCS: + nrf5_data.rx.result = OT_ERROR_FCS; + break; + + case NRF_802154_RX_ERROR_INVALID_DEST_ADDR: + nrf5_data.rx.result = OT_ERROR_DESTINATION_ADDRESS_FILTERED; + break; + + case NRF_802154_RX_ERROR_ABORTED: + case NRF_802154_RX_ERROR_DELAYED_ABORTED: + nrf5_data.rx.result = OT_ERROR_ABORT; + break; + + case NRF_802154_RX_ERROR_NO_BUFFER: + nrf5_data.rx.result = OT_ERROR_NO_BUFS; + break; + + default: + nrf5_data.rx.result = OT_ERROR_FAILED; + break; + } + set_pending_event(PENDING_EVENT_RX_FAILED); + } +} + +void nrf_802154_tx_ack_started(const uint8_t *data) +{ + nrf5_data.rx.last_frame_ack_fpb = data[FRAME_PENDING_OFFSET] & FRAME_PENDING_BIT; + nrf5_data.rx.last_frame_ack_seb = data[SECURITY_ENABLED_OFFSET] & SECURITY_ENABLED_BIT; +} + +static void update_tx_frame_info(otRadioFrame *frame, + const nrf_802154_transmit_done_metadata_t *metadata) +{ + frame->mInfo.mTxInfo.mIsSecurityProcessed = metadata->frame_props.is_secured; + frame->mInfo.mTxInfo.mIsHeaderUpdated = metadata->frame_props.dynamic_data_is_set; +} + +void nrf_802154_transmitted_raw(uint8_t *frame, const nrf_802154_transmit_done_metadata_t *metadata) +{ + ARG_UNUSED(frame); + + nrf5_data.ack.desc.psdu = metadata->data.transmitted.p_ack; + + if (nrf5_data.ack.desc.psdu) { + nrf5_data.ack.desc.rssi = metadata->data.transmitted.power; + nrf5_data.ack.desc.lqi = metadata->data.transmitted.lqi; + + if (metadata->data.transmitted.time == NRF_802154_NO_TIMESTAMP) { + /* Ack timestamp is invalid. Keep this value to detect it when handling Ack + */ + nrf5_data.ack.desc.time = NRF_802154_NO_TIMESTAMP; + } else { + nrf5_data.ack.desc.time = nrf_802154_timestamp_end_to_phr_convert( + metadata->data.transmitted.time, nrf5_data.ack.desc.psdu[0]); + } + } + + update_tx_frame_info(&nrf5_data.tx.frame, metadata); + + set_pending_event(PENDING_EVENT_TX_DONE); +} + +static otError nrf5_tx_error_to_ot_error(nrf_802154_tx_error_t error) +{ + switch (error) { + case NRF_802154_TX_ERROR_BUSY_CHANNEL: + case NRF_802154_TX_ERROR_TIMESLOT_ENDED: + case NRF_802154_TX_ERROR_TIMESLOT_DENIED: + return OT_ERROR_CHANNEL_ACCESS_FAILURE; + case NRF_802154_TX_ERROR_INVALID_ACK: + case NRF_802154_TX_ERROR_NO_MEM: + case NRF_802154_TX_ERROR_NO_ACK: + return OT_ERROR_NO_ACK; + case NRF_802154_TX_ERROR_ABORTED: + default: + return OT_ERROR_ABORT; + } +} + +void nrf_802154_transmit_failed(uint8_t *frame, nrf_802154_tx_error_t error, + const nrf_802154_transmit_done_metadata_t *metadata) +{ + ARG_UNUSED(frame); + + nrf5_data.tx.result = nrf5_tx_error_to_ot_error(error); + + LOG_DBG("nrf_802154_transmit_failed: %u, tx result: %u", error, nrf5_data.tx.result); + + update_tx_frame_info(&nrf5_data.tx.frame, metadata); + + set_pending_event(PENDING_EVENT_TX_DONE); +} + +void nrf_802154_energy_detected(const nrf_802154_energy_detected_t *result) +{ + if (nrf5_data.energy_detection.cb != NULL) { + nrf5_energy_detection_done_cb_t callback = nrf5_data.energy_detection.cb; + + nrf5_data.energy_detection.cb = NULL; + callback(result->ed_dbm); + } +} + +void nrf_802154_energy_detection_failed(nrf_802154_ed_error_t error) +{ + if (nrf5_data.energy_detection.cb != NULL) { + nrf5_energy_detection_done_cb_t callback = nrf5_data.energy_detection.cb; + + nrf5_data.energy_detection.cb = NULL; + callback(SHRT_MAX); + } +} + +#if defined(CONFIG_NRF_802154_SER_HOST) +void nrf_802154_serialization_error(const nrf_802154_ser_err_data_t *err) +{ + __ASSERT(false, "802.15.4 serialization error: %d", err->reason); + k_oops(); +} +#endif diff --git a/modules/openthread/platform/settings.c b/modules/openthread/platform/settings.c index ce5fefde0c8d..4fdb77c6f9f0 100644 --- a/modules/openthread/platform/settings.c +++ b/modules/openthread/platform/settings.c @@ -11,7 +11,7 @@ #include -LOG_MODULE_REGISTER(net_otPlat_settings, CONFIG_OPENTHREAD_L2_LOG_LEVEL); +LOG_MODULE_REGISTER(net_otPlat_settings, CONFIG_OPENTHREAD_PLATFORM_LOG_LEVEL); #define OT_SETTINGS_ROOT_KEY "ot" #define OT_SETTINGS_MAX_PATH_LEN 32 diff --git a/modules/openthread/platform/shell.c b/modules/openthread/shell.c similarity index 91% rename from modules/openthread/platform/shell.c rename to modules/openthread/shell.c index 22f11d31f3eb..f82e0b77c55c 100644 --- a/modules/openthread/platform/shell.c +++ b/modules/openthread/shell.c @@ -6,7 +6,6 @@ #include #include -#include #include #include #include @@ -16,6 +15,8 @@ #include "platform-zephyr.h" +#include + #define OT_SHELL_BUFFER_SIZE CONFIG_SHELL_CMD_BUFF_SIZE static char rx_buffer[OT_SHELL_BUFFER_SIZE]; @@ -74,9 +75,9 @@ static int ot_cmd(const struct shell *sh, size_t argc, char *argv[]) shell_p = sh; - openthread_api_mutex_lock(openthread_get_default_context()); + openthread_mutex_lock(); otCliInputLine(rx_buffer); - openthread_api_mutex_unlock(openthread_get_default_context()); + openthread_mutex_unlock(); return 0; } diff --git a/samples/matter/common/src/app/matter_init.cpp b/samples/matter/common/src/app/matter_init.cpp index b711c9647869..c360ed8364e1 100644 --- a/samples/matter/common/src/app/matter_init.cpp +++ b/samples/matter/common/src/app/matter_init.cpp @@ -56,6 +56,12 @@ #include #include +#if defined(CONFIG_OPENTHREAD) +#include +#endif + +#include + LOG_MODULE_DECLARE(app, CONFIG_CHIP_APP_LOG_LEVEL); using namespace ::chip::DeviceLayer; @@ -279,6 +285,15 @@ void DoInitChipServer(intptr_t /* unused */) /* The default CommissionableDataProvider is set internally in the GenericConfigurationManagerImpl::Init(). */ #endif +#if CHIP_SYSTEM_CONFIG_USE_OPEN_THREAD_ENDPOINT + // Set up OpenThread configuration when OpenThread is included + chip::Inet::EndPointStateOpenThread::OpenThreadEndpointInitParam nativeParams; + nativeParams.lockCb = openthread_mutex_lock; + nativeParams.unlockCb = openthread_mutex_unlock; + nativeParams.openThreadInstancePtr = chip::DeviceLayer::ThreadStackMgrImpl().OTInstance(); + sLocalInitData.mServerInitParams->endpointNativeParams = static_cast(&nativeParams); +#endif + #ifdef CONFIG_NCS_SAMPLE_MATTER_SETTINGS_SHELL VerifyOrReturn(Nrf::PersistentStorageShell::Init(), LOG_ERR("Matter settings shell has been enabled, but it cannot be initialized.")); diff --git a/samples/matter/lock/Kconfig b/samples/matter/lock/Kconfig index 1cec46320352..d25f81d78d8b 100644 --- a/samples/matter/lock/Kconfig +++ b/samples/matter/lock/Kconfig @@ -88,6 +88,7 @@ config STATE_LEDS config THREAD_WIFI_SWITCHING bool "Switching between Thread and Wi-Fi" depends on SOC_SERIES_NRF53X + # TODO: depends on NET_L2_OPENTHREAD depends on CHIP_WIFI depends on !CHIP_FACTORY_RESET_ERASE_SETTINGS diff --git a/samples/matter/lock/prj_thread_wifi_switched.conf b/samples/matter/lock/prj_thread_wifi_switched.conf index a7338bb33e06..408e5953bfaf 100644 --- a/samples/matter/lock/prj_thread_wifi_switched.conf +++ b/samples/matter/lock/prj_thread_wifi_switched.conf @@ -33,6 +33,7 @@ CONFIG_RESET_ON_FATAL_ERROR=n CONFIG_THREAD_WIFI_SWITCHING=y CONFIG_THREAD_WIFI_SWITCHING_SHELL=y CONFIG_CHIP_WIFI=y +# TODO: CONFIG_NET_L2_OPENTHREAD=y CONFIG_CHIP_FACTORY_RESET_ERASE_SETTINGS=n # Do not restore initial transport on factory reset diff --git a/samples/matter/lock/src/access/access_data_types.h b/samples/matter/lock/src/access/access_data_types.h index c27f59dcb05f..a8b11b9865ad 100644 --- a/samples/matter/lock/src/access/access_data_types.h +++ b/samples/matter/lock/src/access/access_data_types.h @@ -9,6 +9,9 @@ #include #include +#include +#include + namespace DoorLockData { diff --git a/samples/matter/thermostat/src/temp_sensor_manager.cpp b/samples/matter/thermostat/src/temp_sensor_manager.cpp index 2dd8ce7111ba..234dfefb477d 100644 --- a/samples/matter/thermostat/src/temp_sensor_manager.cpp +++ b/samples/matter/thermostat/src/temp_sensor_manager.cpp @@ -8,6 +8,8 @@ #include "app/task_executor.h" #include "temperature_measurement/sensor.h" +#include + LOG_MODULE_DECLARE(app, CONFIG_CHIP_APP_LOG_LEVEL); using namespace chip; diff --git a/samples/matter/thermostat/src/zcl_callbacks.cpp b/samples/matter/thermostat/src/zcl_callbacks.cpp index 53f07945e4f2..8f34c55801b4 100644 --- a/samples/matter/thermostat/src/zcl_callbacks.cpp +++ b/samples/matter/thermostat/src/zcl_callbacks.cpp @@ -14,6 +14,8 @@ #include #include +#include + LOG_MODULE_DECLARE(app, CONFIG_CHIP_APP_LOG_LEVEL); using namespace chip; diff --git a/samples/openthread/cli/prj.conf b/samples/openthread/cli/prj.conf index c0937bbb4200..65cdc43a201f 100644 --- a/samples/openthread/cli/prj.conf +++ b/samples/openthread/cli/prj.conf @@ -4,6 +4,9 @@ # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause # +# Enable OpenThread +CONFIG_OPENTHREAD=y + # Network shell CONFIG_SHELL=y CONFIG_OPENTHREAD_SHELL=y @@ -16,9 +19,4 @@ CONFIG_PM_PARTITION_SIZE_SETTINGS_STORAGE=0x8000 # Enable OpenThread features set CONFIG_OPENTHREAD_NORDIC_LIBRARY_MASTER=y -CONFIG_NET_L2_OPENTHREAD=y - -# Generic networking options -CONFIG_NETWORKING=y - CONFIG_GPIO_SHELL=y diff --git a/samples/openthread/cli/snippets/ci/ci.conf b/samples/openthread/cli/snippets/ci/ci.conf index 026dcad676ba..a1d9fc6024d1 100644 --- a/samples/openthread/cli/snippets/ci/ci.conf +++ b/samples/openthread/cli/snippets/ci/ci.conf @@ -16,12 +16,3 @@ CONFIG_SHELL_DEFAULT_TERMINAL_WIDTH=618 CONFIG_ASSERT=y CONFIG_ASSERT_NO_COND_INFO=y CONFIG_RESET_ON_FATAL_ERROR=n - -# Enable Zperf -CONFIG_NET_SHELL=y -CONFIG_NET_ZPERF=y -CONFIG_NET_ZPERF_SERVER=y -CONFIG_NET_CONTEXT_RCVTIMEO=y -CONFIG_NET_SOCKETS=y -CONFIG_ZVFS_OPEN_MAX=9 -CONFIG_NET_SOCKETS_POLL_MAX=9 diff --git a/samples/openthread/cli/snippets/logging/logging.conf b/samples/openthread/cli/snippets/logging/logging.conf index ef57d6cb6c6a..60eda7ca6001 100644 --- a/samples/openthread/cli/snippets/logging/logging.conf +++ b/samples/openthread/cli/snippets/logging/logging.conf @@ -5,9 +5,6 @@ # Enable Zephyr logging CONFIG_LOG=y -# Increase the default RX stack size -CONFIG_IEEE802154_NRF5_RX_STACK_SIZE=832 - # Use separate thread for logging CONFIG_LOG_MODE_DEFERRED=y @@ -17,17 +14,6 @@ CONFIG_LOG_MAX_LEVEL=3 # Option for configuring log level in this sample CONFIG_OT_COMMAND_LINE_INTERFACE_LOG_LEVEL_INF=y -# Enable net module logging -# CONFIG_NET_LOG=y -# Option for configuring log level in net config library -# CONFIG_NET_CONFIG_LOG_LEVEL_INF=y - -# Option for configuring log level in Zephyr L2 logging -# CONFIG_OPENTHREAD_L2_DEBUG=y -# CONFIG_OPENTHREAD_L2_LOG_LEVEL_DBG=y -# CONFIG_OPENTHREAD_L2_DEBUG_DUMP_15_4=y -# CONFIG_OPENTHREAD_L2_DEBUG_DUMP_IPV6=y - # Enable Mbed TLS logging # CONFIG_OPENTHREAD_MBEDTLS_DEBUG=y # CONFIG_MBEDTLS_DEBUG_LEVEL=3 diff --git a/samples/openthread/cli/src/low_power.c b/samples/openthread/cli/src/low_power.c index 35fd08ac31eb..eb177373fa49 100644 --- a/samples/openthread/cli/src/low_power.c +++ b/samples/openthread/cli/src/low_power.c @@ -4,8 +4,8 @@ * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause */ +#include #include -#include #include #include #if defined(CONFIG_RAM_POWER_DOWN_LIBRARY) @@ -14,11 +14,12 @@ #include "low_power.h" -static void on_thread_state_changed(otChangedFlags flags, struct openthread_context *ot_context, - void *user_data) +static void on_thread_state_changed(otChangedFlags flags, void *user_data) { + struct otInstance *instance = openthread_get_default_instance(); + if (flags & OT_CHANGED_THREAD_ROLE) { - if (otThreadGetDeviceRole(ot_context->instance) == OT_DEVICE_ROLE_CHILD) { + if (otThreadGetDeviceRole(instance) == OT_DEVICE_ROLE_CHILD) { const struct device *cons = DEVICE_DT_GET(DT_CHOSEN(zephyr_console)); if (!device_is_ready(cons)) { @@ -33,11 +34,10 @@ static void on_thread_state_changed(otChangedFlags flags, struct openthread_cont } } -static struct openthread_state_changed_cb ot_state_chaged_cb = { - .state_changed_cb = on_thread_state_changed -}; +static struct openthread_state_changed_callback ot_state_chaged_cb = { + .otCallback = on_thread_state_changed}; void low_power_enable(void) { - openthread_state_changed_cb_register(openthread_get_default_context(), &ot_state_chaged_cb); + openthread_state_changed_callback_register(&ot_state_chaged_cb); } diff --git a/samples/openthread/coap_client/src/coap_client_utils.c b/samples/openthread/coap_client/src/coap_client_utils.c index 79d4522edd84..e05d1997678c 100644 --- a/samples/openthread/coap_client/src/coap_client_utils.c +++ b/samples/openthread/coap_client/src/coap_client_utils.c @@ -8,8 +8,8 @@ #include #include #include -#include #include +#include #include #include "coap_client_utils.h" @@ -195,15 +195,13 @@ static void toggle_minimal_sleepy_end_device(struct k_work *item) { otError error; otLinkModeConfig mode; - struct openthread_context *context = openthread_get_default_context(); - - __ASSERT_NO_MSG(context != NULL); + struct otInstance *instance = openthread_get_default_instance(); - openthread_api_mutex_lock(context); - mode = otThreadGetLinkMode(context->instance); + openthread_mutex_lock(); + mode = otThreadGetLinkMode(instance); mode.mRxOnWhenIdle = !mode.mRxOnWhenIdle; - error = otThreadSetLinkMode(context->instance, mode); - openthread_api_mutex_unlock(context); + error = otThreadSetLinkMode(instance, mode); + openthread_mutex_unlock(); if (error != OT_ERROR_NONE) { LOG_ERR("Failed to set MLE link mode configuration"); @@ -219,11 +217,12 @@ static void update_device_state(void) on_mtd_mode_toggle(mode.mRxOnWhenIdle); } -static void on_thread_state_changed(otChangedFlags flags, struct openthread_context *ot_context, - void *user_data) +static void on_thread_state_changed(otChangedFlags flags, void *user_data) { + struct otInstance *instance = openthread_get_default_instance(); + if (flags & OT_CHANGED_THREAD_ROLE) { - switch (otThreadGetDeviceRole(ot_context->instance)) { + switch (otThreadGetDeviceRole(instance)) { case OT_DEVICE_ROLE_CHILD: case OT_DEVICE_ROLE_ROUTER: case OT_DEVICE_ROLE_LEADER: @@ -240,9 +239,8 @@ static void on_thread_state_changed(otChangedFlags flags, struct openthread_cont } } } -static struct openthread_state_changed_cb ot_state_chaged_cb = { - .state_changed_cb = on_thread_state_changed -}; +static struct openthread_state_changed_callback ot_state_chaged_cb = { + .otCallback = on_thread_state_changed}; static void submit_work_if_connected(struct k_work *work) { @@ -274,8 +272,8 @@ void coap_client_utils_init(ot_connection_cb_t on_connect, k_work_init(&multicast_light_work, toggle_mesh_lights); k_work_init(&provisioning_work, send_provisioning_request); - openthread_state_changed_cb_register(openthread_get_default_context(), &ot_state_chaged_cb); - openthread_start(openthread_get_default_context()); + openthread_state_changed_callback_register(&ot_state_chaged_cb); + openthread_run(); if (IS_ENABLED(CONFIG_OPENTHREAD_MTD_SED)) { k_work_init(&toggle_MTD_SED_work, diff --git a/samples/openthread/coap_server/prj.conf b/samples/openthread/coap_server/prj.conf index 448651968caf..ce5b348bbd6b 100644 --- a/samples/openthread/coap_server/prj.conf +++ b/samples/openthread/coap_server/prj.conf @@ -4,6 +4,9 @@ # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause # +# Enable OpenThread +CONFIG_OPENTHREAD=y + # nRF board library CONFIG_DK_LIBRARY=y @@ -19,18 +22,8 @@ CONFIG_SHELL_CMD_BUFF_SIZE=416 # Increase Settings storage size CONFIG_PM_PARTITION_SIZE_SETTINGS_STORAGE=0x8000 -# Network sockets -CONFIG_NET_SOCKETS=y -CONFIG_NET_SOCKETS_POLL_MAX=4 - # Same network Master Key for client and server CONFIG_OPENTHREAD_NETWORKKEY="00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff" -# L2 OpenThread enabling -CONFIG_NET_L2_OPENTHREAD=y - -# Generic networking options -CONFIG_NETWORKING=y - CONFIG_MBEDTLS_SHA1_C=n CONFIG_FPU=y diff --git a/samples/openthread/coap_server/snippets/logging/logging.conf b/samples/openthread/coap_server/snippets/logging/logging.conf index 0ec3bcce2fc6..934eedbd49cb 100644 --- a/samples/openthread/coap_server/snippets/logging/logging.conf +++ b/samples/openthread/coap_server/snippets/logging/logging.conf @@ -3,17 +3,6 @@ # # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause -# Enable net module logging -# CONFIG_NET_LOG=y -# Option for configuring log level in net config library -# CONFIG_NET_CONFIG_LOG_LEVEL_INF=y - -# Option for configuring log level in Zephyr L2 logging -# CONFIG_OPENTHREAD_L2_DEBUG=y -# CONFIG_OPENTHREAD_L2_LOG_LEVEL_DBG=y -# CONFIG_OPENTHREAD_L2_DEBUG_DUMP_15_4=y -# CONFIG_OPENTHREAD_L2_DEBUG_DUMP_IPV6=y - # Configure sample logging setting CONFIG_LOG=y CONFIG_COAP_SERVER_LOG_LEVEL_DBG=y diff --git a/samples/openthread/coap_server/src/coap_server.c b/samples/openthread/coap_server/src/coap_server.c index 249d6940d5ea..5b5ba9af8f29 100644 --- a/samples/openthread/coap_server/src/coap_server.c +++ b/samples/openthread/coap_server/src/coap_server.c @@ -110,11 +110,10 @@ static void on_button_changed(uint32_t button_state, uint32_t has_changed) } } -static void on_thread_state_changed(otChangedFlags flags, struct openthread_context *ot_context, - void *user_data) +static void on_thread_state_changed(otChangedFlags flags, void *user_data) { if (flags & OT_CHANGED_THREAD_ROLE) { - switch (otThreadGetDeviceRole(ot_context->instance)) { + switch (otThreadGetDeviceRole(openthread_get_default_instance())) { case OT_DEVICE_ROLE_CHILD: case OT_DEVICE_ROLE_ROUTER: case OT_DEVICE_ROLE_LEADER: @@ -130,8 +129,9 @@ static void on_thread_state_changed(otChangedFlags flags, struct openthread_cont } } } -static struct openthread_state_changed_cb ot_state_chaged_cb = { .state_changed_cb = - on_thread_state_changed }; + +static struct openthread_state_changed_callback ot_state_chaged_cb = { + .otCallback = on_thread_state_changed}; int main(void) { @@ -166,8 +166,8 @@ int main(void) goto end; } - openthread_state_changed_cb_register(openthread_get_default_context(), &ot_state_chaged_cb); - openthread_start(openthread_get_default_context()); + openthread_state_changed_callback_register(&ot_state_chaged_cb); + openthread_run(); end: return 0; diff --git a/samples/openthread/coprocessor/prj.conf b/samples/openthread/coprocessor/prj.conf index 7e512ed9912d..ea93eb5635cb 100644 --- a/samples/openthread/coprocessor/prj.conf +++ b/samples/openthread/coprocessor/prj.conf @@ -4,6 +4,9 @@ # SPDX-License-Identifier: LicenseRef-Nordic-5-Clause # +# Enable OpenThread +CONFIG_OPENTHREAD=y + # Set OpenThread NCP architecture CONFIG_OPENTHREAD_COPROCESSOR=y CONFIG_OPENTHREAD_COPROCESSOR_RCP=y @@ -11,11 +14,6 @@ CONFIG_OPENTHREAD_COPROCESSOR_RCP=y # Nordic feature set CONFIG_OPENTHREAD_NORDIC_LIBRARY_RCP=y -CONFIG_NET_L2_OPENTHREAD=y - -# Generic networking options -CONFIG_NETWORKING=y - # Increase Settings storage size CONFIG_PM_PARTITION_SIZE_SETTINGS_STORAGE=0x8000 diff --git a/samples/openthread/coprocessor/src/nrf_802154_radio_wrapper.c b/samples/openthread/coprocessor/src/nrf_802154_radio_wrapper.c index f793385b0aea..3d9cd908151c 100644 --- a/samples/openthread/coprocessor/src/nrf_802154_radio_wrapper.c +++ b/samples/openthread/coprocessor/src/nrf_802154_radio_wrapper.c @@ -5,12 +5,14 @@ */ #include "nrf_802154_radio_wrapper.h" #include + +#ifdef CONFIG_NETWORKING #include #include -static const struct device *const radio_dev = - DEVICE_DT_GET(DT_CHOSEN(zephyr_ieee802154)); +static const struct device *const radio_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_ieee802154)); static struct ieee802154_radio_api *radio_api; +#endif bool nrf_802154_radio_wrapper_auto_ack_get(void) { @@ -24,10 +26,16 @@ void nrf_802154_radio_wrapper_auto_ack_set(bool enabled) uint16_t nrf_802154_radio_wrapper_hw_capabilities_get(void) { +#ifdef CONFIG_NETWORKING __ASSERT_NO_MSG(device_is_ready(radio_dev)); radio_api = (struct ieee802154_radio_api *)radio_dev->api; __ASSERT_NO_MSG(radio_api != NULL); return radio_api->get_capabilities(radio_dev); +#else + // TODO: verify that this is correct + nrf_802154_capabilities_t caps = nrf_802154_capabilities_get(); + return (uint16_t)caps; +#endif } diff --git a/samples/wifi/thread_coex/src/ot_utils.c b/samples/wifi/thread_coex/src/ot_utils.c index fbf24654bcd2..cf9d4303f4ed 100644 --- a/samples/wifi/thread_coex/src/ot_utils.c +++ b/samples/wifi/thread_coex/src/ot_utils.c @@ -51,34 +51,34 @@ static void ot_device_dettached(void *ptr) static void ot_commissioner_state_changed(otCommissionerState aState, void *aContext) { + struct otInstance *instance = openthread_get_default_instance(); + LOG_INF("OT commissioner state changed"); if (aState == OT_COMMISSIONER_STATE_ACTIVE) { LOG_INF("ot commissioner joiner add * FEDCBA9876543210 2000"); - otCommissionerAddJoiner(openthread_get_default_instance(), NULL, - "FEDCBA9876543210", 2000); + otCommissionerAddJoiner(instance, NULL, "FEDCBA9876543210", 2000); LOG_INF("\n\nRun thread application on client\n\n"); } } -static void ot_thread_state_changed(otChangedFlags flags, struct openthread_context *ot_context, - void *user_data) +static void ot_thread_state_changed(otChangedFlags flags, void *user_data) { + struct otInstance *instance = openthread_get_default_instance(); + LOG_INF("OT device state changed"); if (flags & OT_CHANGED_THREAD_ROLE) { - otDeviceRole ot_role = otThreadGetDeviceRole(ot_context->instance); + otDeviceRole ot_role = otThreadGetDeviceRole(instance); if (ot_role != OT_DEVICE_ROLE_DETACHED && ot_role != OT_DEVICE_ROLE_DISABLED) { /* ot commissioner start */ LOG_INF("ot commissioner start"); - otCommissionerStart(ot_context->instance, &ot_commissioner_state_changed, - NULL, NULL); + otCommissionerStart(instance, &ot_commissioner_state_changed, NULL, NULL); } } } -static struct openthread_state_changed_cb ot_state_chaged_cb = { - .state_changed_cb = ot_thread_state_changed -}; +static struct openthread_state_changed_callback ot_state_chaged_cb = { + .otCallback = ot_thread_state_changed}; /* call back Thread device joiner */ static void ot_joiner_start_handler(otError error, void *context) @@ -100,31 +100,30 @@ int ot_throughput_client_init(void) { otError err = 0; uint32_t ot_role_non_child = 0; + struct otInstance *instance = openthread_get_default_instance(); ot_start_joiner("FEDCBA9876543210"); err = k_sem_take(&connected_sem, WAIT_TIME_FOR_OT_CON); - struct openthread_context *context = openthread_get_default_context(); LOG_INF("Starting openthread."); - openthread_api_mutex_lock(context); + openthread_mutex_lock(); /* ot thread start */ - err = otThreadSetEnabled(openthread_get_default_instance(), true); + err = otThreadSetEnabled(instance, true); if (err != OT_ERROR_NONE) { LOG_ERR("Starting openthread: %d (%s)", err, otThreadErrorToString(err)); } - otDeviceRole current_role = - otThreadGetDeviceRole(openthread_get_default_instance()); - openthread_api_mutex_unlock(context); + otDeviceRole current_role = otThreadGetDeviceRole(instance); + openthread_mutex_unlock(); LOG_INF("Current role: %s. Waiting to get child role", otThreadDeviceRoleToString(current_role)); while (current_role != OT_DEVICE_ROLE_CHILD) { k_sleep(K_MSEC(CHECK_OT_ROLE_WAIT_TIME)); - openthread_api_mutex_lock(context); - current_role = otThreadGetDeviceRole(openthread_get_default_instance()); - openthread_api_mutex_unlock(context); + openthread_mutex_lock(); + current_role = otThreadGetDeviceRole(instance); + openthread_mutex_unlock(); /* Avoid infinite waiting if the device role is not child */ if (current_role == OT_DEVICE_ROLE_ROUTER) { ot_role_non_child = 1; @@ -160,9 +159,8 @@ void ot_start_joiner(const char *pskd) LOG_INF("Starting joiner"); otInstance *instance = openthread_get_default_instance(); - struct openthread_context *context = openthread_get_default_context(); - openthread_api_mutex_lock(context); + openthread_mutex_lock(); /** Step1: Set null network key i.e, * ot networkkey 00000000000000000000000000000000 @@ -179,7 +177,7 @@ void ot_start_joiner(const char *pskd) "Zephyr", "Zephyr", KERNEL_VERSION_STRING, NULL, &ot_joiner_start_handler, NULL); - openthread_api_mutex_unlock(context); + openthread_mutex_unlock(); /* LOG_INF("Thread start joiner Done."); */ } @@ -196,8 +194,7 @@ int ot_throughput_test_init(bool is_ot_client, bool is_ot_zperf_udp) } if (!is_ot_client) { /* for server */ ot_initialization(); - openthread_state_changed_cb_register(openthread_get_default_context(), - &ot_state_chaged_cb); + openthread_state_changed_callback_register(&ot_state_chaged_cb); LOG_INF("Starting zperf server"); ot_start_zperf_test_recv(is_ot_zperf_udp); @@ -281,14 +278,12 @@ void ot_setNetworkConfiguration(otInstance *aInstance) int ot_initialization(void) { - struct openthread_context *context = openthread_get_default_context(); - otInstance *instance = openthread_get_default_instance(); /* LOG_INF("Updating thread parameters"); */ ot_setNetworkConfiguration(instance); /* LOG_INF("Enabling thread"); */ - otError err = openthread_start(context); /* 'ifconfig up && thread start' */ + otError err = openthread_run(); /* 'ifconfig up && thread start' */ if (err != OT_ERROR_NONE) { LOG_ERR("Starting openthread: %d (%s)", err, otThreadErrorToString(err)); @@ -325,10 +320,10 @@ void ot_get_peer_address(uint64_t timeout_ms) memset(&config, 0, sizeof(config)); config.mReplyCallback = ot_handle_ping_reply; - openthread_api_mutex_lock(openthread_get_default_context()); + openthread_mutex_lock(); otIp6AddressFromString(dest, &config.mDestination); otPingSenderPing(openthread_get_default_instance(), &config); - openthread_api_mutex_unlock(openthread_get_default_context()); + openthread_mutex_unlock(); start_time = k_uptime_get(); while (!peer_address_info.address_found && k_uptime_get() < start_time + timeout_ms) { diff --git a/subsys/caf/modules/net_state_ot.c b/subsys/caf/modules/net_state_ot.c index ec6196bb8e4f..a0e1d3fba5d2 100644 --- a/subsys/caf/modules/net_state_ot.c +++ b/subsys/caf/modules/net_state_ot.c @@ -144,19 +144,19 @@ static void set_net_state(enum net_state state) send_net_state_event(state); } -static void on_thread_state_changed(otChangedFlags flags, struct openthread_context *ot_context, - void *user_data) +static void on_thread_state_changed(otChangedFlags flags, void *user_data) { static bool has_role; + struct otInstance *instance = openthread_get_default_instance(); - bool has_neighbors = check_neighbors(ot_context->instance); - bool route_available = check_routes(ot_context->instance); + bool has_neighbors = check_neighbors(instance); + bool route_available = check_routes(instance); LOG_INF("state: 0x%.8x has_neighbours:%s route_available:%s", flags, (has_neighbors)?("yes"):("no"), (route_available)?("yes"):("no")); if (flags & OT_CHANGED_THREAD_ROLE) { - switch (otThreadGetDeviceRole(ot_context->instance)) { + switch (otThreadGetDeviceRole(instance)) { case OT_DEVICE_ROLE_LEADER: LOG_INF("Leader role set"); has_role = true; @@ -187,14 +187,13 @@ static void on_thread_state_changed(otChangedFlags flags, struct openthread_cont set_net_state(NET_STATE_DISCONNECTED); } } -static struct openthread_state_changed_cb ot_state_chaged_cb = { - .state_changed_cb = on_thread_state_changed -}; +static struct openthread_state_changed_callback ot_state_chaged_cb = { + .otCallback = on_thread_state_changed}; static void connect_ot(void) { - openthread_state_changed_cb_register(openthread_get_default_context(), &ot_state_chaged_cb); - openthread_start(openthread_get_default_context()); + openthread_state_changed_callback_register(&ot_state_chaged_cb); + openthread_run(); LOG_INF("OT connection requested"); } @@ -222,16 +221,15 @@ static bool handle_state_event(const struct module_state_event *event) static bool handle_reset_event(void) { - struct openthread_context *ot_context = openthread_get_default_context(); otError err; /* This event has to apear before initialization */ __ASSERT_NO_MSG(!initialized); LOG_WRN("Storage reset requested"); - openthread_api_mutex_lock(ot_context); - err = otInstanceErasePersistentInfo(ot_context->instance); - openthread_api_mutex_unlock(ot_context); + openthread_mutex_lock(); + err = otInstanceErasePersistentInfo(openthread_get_default_instance()); + openthread_mutex_unlock(); /* It can fail only if called with OpenThread stack enabled. * This event should not appear after the OpenThread is started. * If it does - there is some huge coding error. diff --git a/subsys/net/openthread/rpc/common/ot_rpc_lock_net_l2.c b/subsys/net/openthread/rpc/common/ot_rpc_lock_net_l2.c index 4ac05c8893de..8ac98fdac627 100644 --- a/subsys/net/openthread/rpc/common/ot_rpc_lock_net_l2.c +++ b/subsys/net/openthread/rpc/common/ot_rpc_lock_net_l2.c @@ -6,14 +6,14 @@ #include "ot_rpc_lock.h" -#include +#include void ot_rpc_mutex_lock(void) { - openthread_api_mutex_lock(openthread_get_default_context()); + openthread_mutex_lock(); } void ot_rpc_mutex_unlock(void) { - openthread_api_mutex_unlock(openthread_get_default_context()); + openthread_mutex_unlock(); } diff --git a/tests/subsys/net/openthread/rpc/server/CMakeLists.txt b/tests/subsys/net/openthread/rpc/server/CMakeLists.txt index 92ba379120e4..968f81eb5031 100644 --- a/tests/subsys/net/openthread/rpc/server/CMakeLists.txt +++ b/tests/subsys/net/openthread/rpc/server/CMakeLists.txt @@ -25,6 +25,7 @@ target_sources(app PRIVATE # Fill the gaps due to not setting NET_L2_OPENTHREAD. zephyr_include_directories( ${ZEPHYR_OPENTHREAD_MODULE_DIR}/include + ${ZEPHYR_BASE}/modules/openthread/include ) zephyr_compile_definitions( diff --git a/tests/subsys/net/openthread/rpc/server/src/instance_suite.c b/tests/subsys/net/openthread/rpc/server/src/instance_suite.c index 1061f38dc760..f1d6355d9fb3 100644 --- a/tests/subsys/net/openthread/rpc/server/src/instance_suite.c +++ b/tests/subsys/net/openthread/rpc/server/src/instance_suite.c @@ -10,9 +10,9 @@ #include #include -#include #include +#include #include /* Fake functions */ diff --git a/tests/subsys/net/openthread/rpc/server/src/mocks.c b/tests/subsys/net/openthread/rpc/server/src/mocks.c index e732b6e1eb6e..66fe617ac5a2 100644 --- a/tests/subsys/net/openthread/rpc/server/src/mocks.c +++ b/tests/subsys/net/openthread/rpc/server/src/mocks.c @@ -11,21 +11,19 @@ #include -#include +#include -static struct openthread_context ot_context; +static int dummy; struct otInstance *openthread_get_default_instance(void) { - return ot_context.instance; + return (struct otInstance *)&dummy; } void ot_rpc_mutex_lock(void) { - (void)k_mutex_lock(&ot_context.api_lock, K_FOREVER); } void ot_rpc_mutex_unlock(void) { - (void)k_mutex_unlock(&ot_context.api_lock); } diff --git a/west.yml b/west.yml index 38f8f1c04303..98ca54c2a01a 100644 --- a/west.yml +++ b/west.yml @@ -65,7 +65,7 @@ manifest: # https://developer.nordicsemi.com/nRF_Connect_SDK/doc/latest/zephyr/guides/modules.html - name: zephyr repo-path: sdk-zephyr - revision: 6131774ed5598fde0d09eb266fec686c95e2f24b + revision: pull/2792/head import: # In addition to the zephyr repository itself, NCS also # imports the contents of zephyr/west.yml at the above @@ -156,7 +156,7 @@ manifest: - name: matter repo-path: sdk-connectedhomeip path: modules/lib/matter - revision: ab711a4c10ce17f498c9f804444e12546855cb37 + revision: pull/616/head west-commands: scripts/west/west-commands.yml submodules: - name: nlio