diff --git a/.github/test-spec.yml b/.github/test-spec.yml index 64501618493a..3d7f5697f2c6 100644 --- a/.github/test-spec.yml +++ b/.github/test-spec.yml @@ -376,6 +376,7 @@ - "drivers/hw_cc3xx/*" - "drivers/entropy/*" - "modules/nrfxlib/nrf_802154/**/*" + - "modules/openthread/**/*" "CI-nfc-test": - "subsys/nfc/**/*" diff --git a/CODEOWNERS b/CODEOWNERS index 4803baac5b02..42d69bc031e1 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -430,6 +430,7 @@ /modules/mcuboot/ @nrfconnect/ncs-pluto /modules/memfault-firmware-sdk/ @nrfconnect/ncs-cia /modules/nrfxlib/ @nrfconnect/ncs-code-owners +/modules/openthread/ @nrfconnect/ncs-thread /modules/trusted-firmware-m/ @nrfconnect/ncs-aegir /modules/wfa-qt/ @nrfconnect/ncs-wifi diff --git a/modules/modules.cmake b/modules/modules.cmake index b274d98f47a9..e9283310701f 100644 --- a/modules/modules.cmake +++ b/modules/modules.cmake @@ -7,6 +7,8 @@ set(ZEPHYR_COREMARK_KCONFIG ${CMAKE_CURRENT_LIST_DIR}/coremark/Kconfig) set(ZEPHYR_TRUSTED_FIRMWARE_M_KCONFIG ${CMAKE_CURRENT_LIST_DIR}/trusted-firmware-m/Kconfig) set(ZEPHYR_AZURE_SDK_FOR_C_KCONFIG ${CMAKE_CURRENT_LIST_DIR}/azure-sdk-for-c/Kconfig) set(ZEPHYR_AZURE_SDK_FOR_C_CMAKE_DIR ${CMAKE_CURRENT_LIST_DIR}/azure-sdk-for-c) +set(ZEPHYR_OPENTHREAD_CMAKE_DIR ${CMAKE_CURRENT_LIST_DIR}/openthread) +set(ZEPHYR_OPENTHREAD_KCONFIG ${CMAKE_CURRENT_LIST_DIR}/openthread/Kconfig) # Those are modules with Kconfig tree's inside the module repo but where # nRF Connect SDK extend those trees. diff --git a/modules/openthread/CMakeLists.txt b/modules/openthread/CMakeLists.txt new file mode 100644 index 000000000000..941c8f1f219d --- /dev/null +++ b/modules/openthread/CMakeLists.txt @@ -0,0 +1,278 @@ +if(CONFIG_OPENTHREAD) +if(CONFIG_OPENTHREAD_SOURCES) + +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) + +macro(kconfig_to_ot_option kconfig_option ot_config description) + if(${kconfig_option}) + set(${ot_config} ON CACHE BOOL "${description}" FORCE) + else() + set(${ot_config} OFF CACHE BOOL "${description}" FORCE) + endif() +endmacro() + +# OpenThread options +set(OT_BUILD_EXECUTABLES OFF CACHE BOOL "Disable OpenThread samples") +set(OT_BUILTIN_MBEDTLS_MANAGEMENT OFF CACHE BOOL "Use Zephyr's mbedTLS heap") +set(OT_PLATFORM "zephyr" CACHE STRING "Zephyr as a target platform") +set(OT_PLATFORM_POWER_CALIBRATION OFF CACHE BOOL "Use Zephyr's power calibration handled by Radio Driver") +set(OT_THREAD_VERSION ${CONFIG_OPENTHREAD_THREAD_VERSION} CACHE STRING "User selected Thread stack version") +set(OT_CLI_TRANSPORT "CONSOLE" CACHE STRING "Set CLI to use console interpreter") + +string(REPLACE " " ";" OT_MBEDTLS_LIB_LIST " ${CONFIG_OPENTHREAD_MBEDTLS_LIB_NAME}") + +set( + OT_EXTERNAL_MBEDTLS + ${OT_MBEDTLS_LIB_LIST} + CACHE STRING + "Specify external mbedtls library" + FORCE +) + +if(CONFIG_OPENTHREAD_FTD) + set(OT_FTD ON CACHE BOOL "Enable FTD" FORCE) + set(OT_MTD OFF CACHE BOOL "Enable MTD" FORCE) +elseif(CONFIG_OPENTHREAD_MTD) + set(OT_FTD OFF CACHE BOOL "Enable FTD" FORCE) + set(OT_MTD ON CACHE BOOL "Enable MTD" FORCE) +endif() + +kconfig_to_ot_option(CONFIG_OPENTHREAD_ANYCAST_LOCATOR OT_ANYCAST_LOCATOR "Enable anycast locator") +kconfig_to_ot_option(CONFIG_ASSERT OT_ASSERT "Enable assert function OT_ASSERT()") +kconfig_to_ot_option(CONFIG_OPENTHREAD_BACKBONE_ROUTER OT_BACKBONE_ROUTER "Enable backbone router functionality") +kconfig_to_ot_option(CONFIG_OPENTHREAD_BACKBONE_ROUTER_DUA_NDPROXYING OT_BACKBONE_ROUTER_DUA_NDPROXYING "Enable BBR DUA ND Proxy support") +kconfig_to_ot_option(CONFIG_OPENTHREAD_BACKBONE_ROUTER_MULTICAST_ROUTING OT_BACKBONE_ROUTER_MULTICAST_ROUTING "Enable BBR MR support") +kconfig_to_ot_option(CONFIG_OPENTHREAD_BLE_TCAT OT_BLE_TCAT "Enable BLE TCAT support") +kconfig_to_ot_option(CONFIG_OPENTHREAD_BORDER_AGENT OT_BORDER_AGENT "Enable Border Agent") +kconfig_to_ot_option(CONFIG_OPENTHREAD_BORDER_AGENT_EPHEMERAL_KEY_ENABLE OT_BORDER_AGENT_EPSKC "Border agent ephemeral PSKc") +kconfig_to_ot_option(CONFIG_OPENTHREAD_BORDER_AGENT_ID OT_BORDER_AGENT_ID "Create and save border agent ID") +kconfig_to_ot_option(CONFIG_OPENTHREAD_BORDER_ROUTER OT_BORDER_ROUTER "Enable Border Router") +kconfig_to_ot_option(CONFIG_OPENTHREAD_BORDER_ROUTING OT_BORDER_ROUTING "Enable Border routing") +kconfig_to_ot_option(CONFIG_OPENTHREAD_BORDER_ROUTING_COUNTERS OT_BORDER_ROUTING_COUNTERS "Enable Border routing counters") +kconfig_to_ot_option(CONFIG_OPENTHREAD_BORDER_ROUTING_DHCP6_PD OT_BORDER_ROUTING_DHCP6_PD "DHCPv6-PD support in border routing") +kconfig_to_ot_option(CONFIG_OPENTHREAD_CHANNEL_MANAGER OT_CHANNEL_MANAGER "Enable channel manager support") +kconfig_to_ot_option(CONFIG_OPENTHREAD_CHANNEL_MANAGER_CSL OT_CHANNEL_MANAGER_CSL "Channel manager for CSL channel") +kconfig_to_ot_option(CONFIG_OPENTHREAD_CHANNEL_MONITOR OT_CHANNEL_MONITOR "Enable channel monitor support") +kconfig_to_ot_option(CONFIG_OPENTHREAD_COAP OT_COAP "Enable CoAP API") +kconfig_to_ot_option(CONFIG_OPENTHREAD_COAP_BLOCK OT_COAP_BLOCK "Enable CoAP Block-wise option support") +kconfig_to_ot_option(CONFIG_OPENTHREAD_COAP_OBSERVE OT_COAP_OBSERVE "Enable CoAP Observe option support") +kconfig_to_ot_option(CONFIG_OPENTHREAD_COAPS OT_COAPS "Enable secure CoAP API support") +kconfig_to_ot_option(CONFIG_OPENTHREAD_COMMISSIONER OT_COMMISSIONER "Enable Commissioner") +kconfig_to_ot_option(CONFIG_OPENTHREAD_CSL_AUTO_SYNC OT_CSL_AUTO_SYNC "Enable csl autosync") +kconfig_to_ot_option(CONFIG_OPENTHREAD_CSL_DEBUG OT_CSL_DEBUG "Enable CSL debug") +kconfig_to_ot_option(CONFIG_OPENTHREAD_CSL_RECEIVER OT_CSL_RECEIVER "Enable CSL receiver feature for Thread 1.2") +kconfig_to_ot_option(CONFIG_OPENTHREAD_CSL_RECEIVER_LOCAL_TIME_SYNC OT_CSL_RECEIVER_LOCAL_TIME_SYNC "Use local time for CSL sync") +kconfig_to_ot_option(CONFIG_OPENTHREAD_DATASET_UPDATER OT_DATASET_UPDATER "Enable Dataset updater") +kconfig_to_ot_option(CONFIG_OPENTHREAD_DEVICE_PROP_LEADER_WEIGHT OT_DEVICE_PROP_LEADER_WEIGHT "Enable device props for leader weight") +kconfig_to_ot_option(CONFIG_OPENTHREAD_DHCP6_CLIENT OT_DHCP6_CLIENT "Enable DHCPv6 Client") +kconfig_to_ot_option(CONFIG_OPENTHREAD_DHCP6_SERVER OT_DHCP6_SERVER "Enable DHCPv6 Server") +kconfig_to_ot_option(CONFIG_OPENTHREAD_DIAG OT_DIAGNOSTIC "Enable Diagnostics support") +kconfig_to_ot_option(CONFIG_OPENTHREAD_DNS_CLIENT OT_DNS_CLIENT "Enable DNS client support") +kconfig_to_ot_option(CONFIG_OPENTHREAD_DNS_CLIENT_OVER_TCP OT_DNS_CLIENT_OVER_TCP "Enable dns query over tcp") +kconfig_to_ot_option(CONFIG_OPENTHREAD_DNS_DSO OT_DNS_DSO "Enable DNS Stateful Operations (DSO) support") +kconfig_to_ot_option(CONFIG_OPENTHREAD_DNS_UPSTREAM_QUERY OT_DNS_UPSTREAM_QUERY "Enable forwarding DNS queries to upstream") +kconfig_to_ot_option(CONFIG_OPENTHREAD_DNSSD_DISCOVERY_PROXY OT_DNSSD_DISCOVERY_PROXY "Enable DNS-SD discovery proxy support") +kconfig_to_ot_option(CONFIG_OPENTHREAD_DNSSD_SERVER OT_DNSSD_SERVER "Enable DNS-SD server support") +kconfig_to_ot_option(CONFIG_OPENTHREAD_DUA OT_DUA "Enable Domain Unicast Address feature for Thread 1.2") +kconfig_to_ot_option(CONFIG_OPENTHREAD_DYNAMIC_STORE_FRAME_AHEAD_COUNTER OT_DYNAMIC_STORE_FRAME_AHEAD_COUNTER "Enable dynamic store frame ahead counter") +kconfig_to_ot_option(CONFIG_OPENTHREAD_ECDSA OT_ECDSA "Enable ECDSA support") +kconfig_to_ot_option(CONFIG_OPENTHREAD_ENABLE_SERVICE OT_SERVICE "Enable Service entries in Thread Network Data") +kconfig_to_ot_option(CONFIG_OPENTHREAD_EXTERNAL_HEAP OT_EXTERNAL_HEAP "Enable external heap support") +kconfig_to_ot_option(CONFIG_OPENTHREAD_FIREWALL OT_FIREWALL "Enable firewall") +kconfig_to_ot_option(CONFIG_OPENTHREAD_FULL_LOGS OT_FULL_LOGS "Enable full logs") +kconfig_to_ot_option(CONFIG_OPENTHREAD_HISTORY_TRACKER OT_HISTORY_TRACKER "Enable history tracker support.") +kconfig_to_ot_option(CONFIG_OPENTHREAD_IP6_FRAGM OT_IP6_FRAGM "Enable IPv6 fragmentation support") +kconfig_to_ot_option(CONFIG_OPENTHREAD_JAM_DETECTION OT_JAM_DETECTION "Enable Jam Detection") +kconfig_to_ot_option(CONFIG_OPENTHREAD_JOINER OT_JOINER "Enable Joiner") +kconfig_to_ot_option(CONFIG_OPENTHREAD_LEGACY OT_LEGACY "Enable legacy network support") +kconfig_to_ot_option(CONFIG_OPENTHREAD_LINK_METRICS_INITIATOR OT_LINK_METRICS_INITIATOR "Enable Link Metrics initiator for Thread 1.2") +kconfig_to_ot_option(CONFIG_OPENTHREAD_LINK_METRICS_MANAGER OT_LINK_METRICS_MANAGER "Enable Link Metrics manager for Thread 1.2") +kconfig_to_ot_option(CONFIG_OPENTHREAD_LINK_METRICS_SUBJECT OT_LINK_METRICS_SUBJECT "Enable Link Metrics subject for Thread 1.2") +kconfig_to_ot_option(CONFIG_OPENTHREAD_PLATFORM_LOG_CRASH_DUMP OT_PLATFORM_LOG_CRASH_DUMP "Platform log crash dump") +kconfig_to_ot_option(CONFIG_OPENTHREAD_LOG_LEVEL_DYNAMIC OT_LOG_LEVEL_DYNAMIC "Enable dynamic log level control") +kconfig_to_ot_option(CONFIG_OPENTHREAD_MAC_FILTER OT_MAC_FILTER "Enable MAC filter support") +kconfig_to_ot_option(CONFIG_OPENTHREAD_MULTICAST_DNS OT_MDNS "multicast DNS (mDNS)") +kconfig_to_ot_option(CONFIG_OPENTHREAD_MESH_DIAG OT_MESH_DIAG "Enable Mesh Diagnostics") +kconfig_to_ot_option(CONFIG_OPENTHREAD_MESSAGE_USE_HEAP OT_MESSAGE_USE_HEAP "Enable heap allocator for message buffers") +kconfig_to_ot_option(CONFIG_OPENTHREAD_MLE_LONG_ROUTES OT_MLE_LONG_ROUTES "Enable MLE long routes support (Experimental)") +kconfig_to_ot_option(CONFIG_OPENTHREAD_MLR OT_MLR "Enable Multicast Listener Registration feature for Thread 1.2") +kconfig_to_ot_option(CONFIG_OPENTHREAD_MULTIPAN_RCP OT_MULTIPAN_RCP "Enable Multi-PAN RCP") +kconfig_to_ot_option(CONFIG_OPENTHREAD_MULTIPLE_INSTANCE OT_MULTIPLE_INSTANCE "Enable multiple instances") +kconfig_to_ot_option(CONFIG_OPENTHREAD_NAT64_BORDER_ROUTING OT_NAT64_BORDER_ROUTING "Enable border routing NAT64 support") +kconfig_to_ot_option(CONFIG_OPENTHREAD_NAT64_TRANSLATOR OT_NAT64_TRANSLATOR "Enable NAT64 translator") +kconfig_to_ot_option(CONFIG_OPENTHREAD_NEIGHBOR_DISCOVERY_AGENT OT_NEIGHBOR_DISCOVERY_AGENT "Enable neighbor discovery agent support") +kconfig_to_ot_option(CONFIG_OPENTHREAD_NETDIAG_CLIENT OT_NETDIAG_CLIENT "Enable TMF network diagnostics on clients") +kconfig_to_ot_option(CONFIG_OPENTHREAD_NETDIAG_VENDOR_INFO OT_NETDIAG_VENDOR_INFO "Allow setting vendor info at runtime") +kconfig_to_ot_option(CONFIG_OPENTHREAD_NETDATA_PUBLISHER OT_NETDATA_PUBLISHER "Enable Thread Network Data publisher") +kconfig_to_ot_option(CONFIG_OPENTHREAD_OPERATIONAL_DATASET_AUTO_INIT OT_OPERATIONAL_DATASET_AUTO_INIT "Enable operational dataset auto init") +kconfig_to_ot_option(CONFIG_OPENTHREAD_OTNS OT_OTNS "Enable OTNS support") +kconfig_to_ot_option(CONFIG_OPENTHREAD_PING_SENDER OT_PING_SENDER "Enable ping sender support") +kconfig_to_ot_option(CONFIG_OPENTHREAD_PLATFORM_BOOTLOADER_MODE OT_PLATFORM_BOOTLOADER_MODE "Enable platform bootloader mode support") +kconfig_to_ot_option(CONFIG_OPENTHREAD_PLATFORM_KEY_REF OT_PLATFORM_KEY_REF "Enable platform key reference support") +kconfig_to_ot_option(CONFIG_OPENTHREAD_PLATFORM_NETIF OT_PLATFORM_NETIF "Enable platform netif support") +kconfig_to_ot_option(CONFIG_OPENTHREAD_PLATFORM_UDP OT_PLATFORM_UDP "Enable platform UDP support") +kconfig_to_ot_option(CONFIG_OPENTHREAD_RADIO_LINK_IEEE_802_15_4_ENABLE OT_15_4 "Enable 802.15.4 radio") +kconfig_to_ot_option(CONFIG_OPENTHREAD_RAW OT_LINK_RAW "Enable Link Raw") +kconfig_to_ot_option(CONFIG_OPENTHREAD_REFERENCE_DEVICE OT_REFERENCE_DEVICE "Enable Thread Certification Reference Device") +kconfig_to_ot_option(CONFIG_OPENTHREAD_SETTINGS_RAM OT_SETTINGS_RAM "Enable volatile-only storage of settings") +kconfig_to_ot_option(CONFIG_OPENTHREAD_SLAAC OT_SLAAC "Enable SLAAC") +kconfig_to_ot_option(CONFIG_OPENTHREAD_SNTP_CLIENT OT_SNTP_CLIENT "Enable SNTP Client support") +kconfig_to_ot_option(CONFIG_OPENTHREAD_SRP_ADV_PROXY OT_SRP_ADV_PROXY "Enable SRP Server Advertising Proxy support") +kconfig_to_ot_option(CONFIG_OPENTHREAD_SRP_CLIENT OT_SRP_CLIENT "Enable SRP Client support") +kconfig_to_ot_option(CONFIG_OPENTHREAD_SRP_SERVER OT_SRP_SERVER "Enable SRP Server support") +kconfig_to_ot_option(CONFIG_OPENTHREAD_TCP_ENABLE OT_TCP "Enable TCP support") +kconfig_to_ot_option(CONFIG_OPENTHREAD_TIME_SYNC OT_TIME_SYNC "Enable the time synchronization service feature") +kconfig_to_ot_option(CONFIG_OPENTHREAD_TREL OT_TREL "Enable TREL radio link for Thread over Infrastructure feature") +kconfig_to_ot_option(CONFIG_OPENTHREAD_TX_BEACON_PAYLOAD OT_TX_BEACON_PAYLOAD "Enable tx beacon payload support") +kconfig_to_ot_option(CONFIG_OPENTHREAD_TX_QUEUE_STATISTICS OT_TX_QUEUE_STATS "Enable tx queue statistics") +kconfig_to_ot_option(CONFIG_OPENTHREAD_UDP_FORWARD OT_UDP_FORWARD "Enable UDP forward feature") +kconfig_to_ot_option(CONFIG_OPENTHREAD_UPTIME OT_UPTIME "Enable support for tracking OpenThread instance's uptime") +kconfig_to_ot_option(CONFIG_OPENTHREAD_VERHOEFF_CHECKSUM OT_VERHOEFF_CHECKSUM "Verhoeff checksum") +kconfig_to_ot_option(CONFIG_OPENTHREAD_WAKEUP_COORDINATOR OT_WAKEUP_COORDINATOR "Enable Wake-up Coordinator role") +kconfig_to_ot_option(CONFIG_OPENTHREAD_WAKEUP_END_DEVICE OT_WAKEUP_END_DEVICE "Enable Wake-up End Device role") + +if(CONFIG_OPENTHREAD_COPROCESSOR_VENDOR_HOOK_SOURCE) + set(OT_NCP_VENDOR_HOOK_SOURCE ${CONFIG_OPENTHREAD_COPROCESSOR_VENDOR_HOOK_SOURCE} CACHE STRING "NCP vendor hook source file name" FORCE) +endif() + +if(CONFIG_OPENTHREAD_POWER_SUPPLY) + set(OT_POWER_SUPPLY ${CONFIG_OPENTHREAD_POWER_SUPPLY} CACHE STRING "Power supply configuration" FORCE) +endif() + +if (CONFIG_OPENTHREAD_CLI_VENDOR_EXTENSION) + set(OT_CLI_VENDOR_EXTENSION ${CONFIG_OPENTHREAD_CLI_VENDOR_EXTENSION} CACHE STRING "Path to CMake file to define and link Openthread CLI vendor extension" FORCE) +endif() + +set(BUILD_TESTING OFF CACHE BOOL "Disable openthread cmake testing targets" FORCE) + +# Zephyr logging options + +if(CONFIG_LOG_BACKEND_SPINEL) + add_definitions( + -DOPENTHREAD_CONFIG_LOG_OUTPUT=OPENTHREAD_CONFIG_LOG_OUTPUT_APP + ) +endif() + +# Other options +add_definitions( + -DOPENTHREAD_CONFIG_LOG_LEVEL=${CONFIG_OPENTHREAD_LOG_LEVEL} + -DOPENTHREAD_PROJECT_CORE_CONFIG_FILE="openthread-core-zephyr-config.h" +) + +# Need to specify build directory as well +add_subdirectory(${ZEPHYR_CURRENT_MODULE_DIR} build) + +zephyr_get_targets(${ZEPHYR_CURRENT_MODULE_DIR} "STATIC_LIBRARY;OBJECT_LIBRARY" ALL_TARGETS) +foreach(target ${ALL_TARGETS}) + # We don't want to build all openthread libraries per default. + # Setting EXCLUDE_FROM_ALL ensures that only libraries that are linked + # into Zephyr will be built due to dependencies. + set_property(TARGET ${target} PROPERTY EXCLUDE_FROM_ALL TRUE) +endforeach() + +string(REPLACE " " ";" OT_PARAM_LIST " ${CONFIG_OPENTHREAD_CUSTOM_PARAMETERS}") +target_compile_definitions(ot-config INTERFACE ${OT_PARAM_LIST}) + +# Since Mbed TLS 3.1.0 MBEDTLS_SSL_EXPORT_KEYS was removed as build symbol and +# it's always assumed to be enabled. Corresponding kconfig was removed from +# Zephyr as well, but OpenThread code still uses it, so we add it here. +target_compile_definitions(ot-config INTERFACE -DMBEDTLS_SSL_EXPORT_KEYS) + +# Zephyr compiler options +target_include_directories(ot-config INTERFACE + $<TARGET_PROPERTY:zephyr_interface,INTERFACE_INCLUDE_DIRECTORIES> +) + +target_include_directories(ot-config SYSTEM INTERFACE + $<TARGET_PROPERTY:zephyr_interface,INTERFACE_SYSTEM_INCLUDE_DIRECTORIES> +) + +target_compile_definitions(ot-config INTERFACE + $<TARGET_PROPERTY:zephyr_interface,INTERFACE_COMPILE_DEFINITIONS> +) + +# Openthread can use minimal libc, which requires autoconf.h +# (specifically CONFIG_ARM and friends). autoconf.h can't be included +# through openthread-*-config.h because openthread third-party +# libraries do not include this header. So we add the defines to all +# OpenThread files through the gcc flag -imacros instead. +target_compile_options(ot-config INTERFACE + $<TARGET_PROPERTY:zephyr_interface,INTERFACE_COMPILE_OPTIONS> + $<TARGET_PROPERTY:compiler,no_builtin> + -imacros ${AUTOCONF_H} +) + +# Openthread depends on errno.h, which includes errno_private.h in minimal libc. +# errno_private.h is generated as part of ${SYSCALL_LIST_H_TARGET} target. +add_dependencies(ot-config ${SYSCALL_LIST_H_TARGET}) + +# Make sure C library, in case of newlib, is linked after OpenThread libraries +# (to prevent linker errors) +if(CONFIG_NEWLIB_LIBC) + target_link_libraries(ot-config INTERFACE -lc) +endif() + +# Include OpenThread headers +zephyr_system_include_directories(${ZEPHYR_CURRENT_MODULE_DIR}/include) +zephyr_system_include_directories(${ZEPHYR_CURRENT_MODULE_DIR}/examples/platforms) + +# Determine which libs should be linked in +set(ot_libs "") + +if(CONFIG_OPENTHREAD_FTD) +set(cli_lib openthread-cli-ftd) +elseif(CONFIG_OPENTHREAD_MTD) +set(cli_lib openthread-cli-mtd) +endif() + +if(CONFIG_OPENTHREAD_SHELL) +list(APPEND ot_libs ${cli_lib}) +endif() + +if(CONFIG_OPENTHREAD_COPROCESSOR_RCP) +list(APPEND ot_libs openthread-rcp) +endif() + +if(CONFIG_OPENTHREAD_COPROCESSOR_NCP) +if(CONFIG_OPENTHREAD_FTD) +list(APPEND ot_libs openthread-ncp-ftd) +elseif(CONFIG_OPENTHREAD_MTD) +list(APPEND ot_libs openthread-ncp-mtd) +endif() +endif() + +if(NOT CONFIG_OPENTHREAD_COPROCESSOR_RCP) +if(CONFIG_OPENTHREAD_FTD) +list(APPEND ot_libs openthread-ftd) +elseif(CONFIG_OPENTHREAD_MTD) +list(APPEND ot_libs openthread-mtd) +endif() +endif() + +if(CONFIG_HDLC_RCP_IF) +list(APPEND ot_libs + ot-config + openthread-platform + openthread-radio-spinel + openthread-spinel-ncp + openthread-url + openthread-hdlc +) +endif() + +if(CONFIG_OPENTHREAD_SETTINGS_RAM) + target_compile_options(openthread-platform-utils PRIVATE + $<TARGET_PROPERTY:zephyr_interface,INTERFACE_COMPILE_OPTIONS> + $<TARGET_PROPERTY:compiler,no_builtin>) + add_dependencies(openthread-platform-utils syscall_list_h_target) + + list(APPEND ot_libs openthread-platform-utils-static) +endif() + +zephyr_link_libraries(${ot_libs}) + +endif() + +add_subdirectory(platform) + +endif() diff --git a/modules/openthread/Kconfig b/modules/openthread/Kconfig new file mode 100644 index 000000000000..4ff059de486e --- /dev/null +++ b/modules/openthread/Kconfig @@ -0,0 +1,19 @@ +# Copyright (c) 2022 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +config OPENTHREAD + bool "OpenThread Support" + help + This option enables the OpenThread library + +if OPENTHREAD + +menu "OpenThread stack features" +rsource "Kconfig.features" +endmenu + +menu "Thread Network configuration" +rsource "Kconfig.thread" +endmenu + +endif # OPENTHREAD diff --git a/modules/openthread/Kconfig.features b/modules/openthread/Kconfig.features new file mode 100644 index 000000000000..17031467b7c5 --- /dev/null +++ b/modules/openthread/Kconfig.features @@ -0,0 +1,402 @@ +# OpenThread stack features selection + +# Copyright (c) 2020 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +choice OPENTHREAD_STACK_VERSION + prompt "OpenThread stack version" + default OPENTHREAD_THREAD_VERSION_1_1 + help + This option selects version of Thread stack + +config OPENTHREAD_THREAD_VERSION_1_1 + bool "Version 1.1" +config OPENTHREAD_THREAD_VERSION_1_2 + bool "Version 1.2" +config OPENTHREAD_THREAD_VERSION_1_3 + bool "Version 1.3" +config OPENTHREAD_THREAD_VERSION_1_3_1 + bool "Version 1.3.1" +config OPENTHREAD_THREAD_VERSION_1_4 + bool "Version 1.4" +endchoice # OPENTHREAD_STACK_VERSION + +config OPENTHREAD_THREAD_VERSION + string + default "1.1" if OPENTHREAD_THREAD_VERSION_1_1 + default "1.2" if OPENTHREAD_THREAD_VERSION_1_2 + default "1.3" if OPENTHREAD_THREAD_VERSION_1_3 + default "1.3.1" if OPENTHREAD_THREAD_VERSION_1_3_1 + default "1.4" if OPENTHREAD_THREAD_VERSION_1_4 + default "unknown" + +config OPENTHREAD_ANYCAST_LOCATOR + bool "Anycast locator support" + +config OPENTHREAD_BACKBONE_ROUTER + bool "Backbone Router functionality" + +config OPENTHREAD_BACKBONE_ROUTER_DUA_NDPROXYING + bool "BBR DUA ND Proxy support" + +config OPENTHREAD_BACKBONE_ROUTER_MULTICAST_ROUTING + bool "BBR MR support" + +config OPENTHREAD_BLE_TCAT + bool "BLE TCAT support" + select EXPERIMENTAL + +config OPENTHREAD_BORDER_AGENT + bool "Border Agent support" + +config OPENTHREAD_BORDER_AGENT_EPHEMERAL_KEY_ENABLE + bool "Border agent ephemeral PSKc" + +config OPENTHREAD_BORDER_AGENT_ID + bool "Create and save border agent ID" + +config OPENTHREAD_BORDER_ROUTER + bool "Border Router support" + +config OPENTHREAD_BORDER_ROUTING + bool "Border routing support" + +config OPENTHREAD_BORDER_ROUTING_COUNTERS + bool "Border routing counters support" + +config OPENTHREAD_BORDER_ROUTING_DHCP6_PD + bool "DHCPv6-PD support in border routing" + +config OPENTHREAD_CHANNEL_MONITOR + bool "Channel monitor support" + +config OPENTHREAD_CHANNEL_MANAGER + bool "Channel manager support" + depends on OPENTHREAD_CHANNEL_MONITOR + +config OPENTHREAD_CHANNEL_MANAGER_CSL + bool "Channel manager for CSL channel" + +config OPENTHREAD_COAP + bool "OpenThread CoAP support" + help + Enable CoAP API for the application with use of OpenThread stack + +config OPENTHREAD_COAP_BLOCK + bool "CoAP Block-wise option support" + +config OPENTHREAD_COAP_OBSERVE + bool "CoAP Observe option support" + +config OPENTHREAD_COAPS + bool "Secure CoAP API support" + depends on OPENTHREAD_COAP + +config OPENTHREAD_COMMISSIONER + bool "Commissioner functions support" + help + Enable commissioner capability in OpenThread stack. Note, that DTLS + handshake used in the commissioning procedure requires a larger + mbedTLS heap than the default value. A minimum recommended value of + CONFIG_MBEDTLS_HEAP_SIZE for the commissioning is 10KB. + +config OPENTHREAD_CSL_DEBUG + bool "CSL debugging" + +config OPENTHREAD_CSL_RECEIVER + bool "CSL Receiver support" + help + Enable CSL Receiver support for Thread 1.2 + +config OPENTHREAD_CSL_RECEIVER_LOCAL_TIME_SYNC + bool "Use local time for CSL synchronization" + help + Use host time rather than radio platform time to track elapsed time + since last CSL synchronization. This reduces the usage of radio API + calls, and it is useful for platforms in which those are costly. + +config OPENTHREAD_DEVICE_PROP_LEADER_WEIGHT + bool "Device props for leader weight" + default n if (OPENTHREAD_THREAD_VERSION_1_1 || \ + OPENTHREAD_THREAD_VERSION_1_2 || \ + OPENTHREAD_THREAD_VERSION_1_3) + default y + help + Enable the device properties which are then used to determine and set + the Leader Weight. + +config OPENTHREAD_DATASET_UPDATER + bool "Dataset updater" + +config OPENTHREAD_WAKEUP_COORDINATOR + bool "Wake-up Coordinator support" + select OPENTHREAD_CSL_RECEIVER + +config OPENTHREAD_WAKEUP_END_DEVICE + bool "Wake-up End Device support" + imply OPENTHREAD_CSL_RECEIVER + +config OPENTHREAD_DHCP6_CLIENT + bool "DHCPv6 client support" + +config OPENTHREAD_DHCP6_SERVER + bool "DHCPv6 server support" + +config OPENTHREAD_DIAG + bool "Diagnostic functions support" + help + Enable OpenThread CLI diagnostic commands + +config OPENTHREAD_DNS_CLIENT + bool "DNS client support" + +config OPENTHREAD_DNS_CLIENT_OVER_TCP + bool "DNS query over tcp" + +config OPENTHREAD_DNS_DSO + bool "DNS Stateful Operations (DSO) support" + +config OPENTHREAD_DNS_UPSTREAM_QUERY + bool "Forwarding DNS queries to upstream" + help + Enable forwarding DNS queries to platform DNS upstream API + +config OPENTHREAD_DNSSD_DISCOVERY_PROXY + bool "DNS-SD discovery proxy support" + +config OPENTHREAD_DNSSD_SERVER + bool "DNS-SD server support" + +config OPENTHREAD_DUA + bool "Domain Unicast Address support" + help + Enable Domain Unicast Address feature for Thread 1.2 + +config OPENTHREAD_DYNAMIC_STORE_FRAME_AHEAD_COUNTER + bool "Dynamic store frame ahead counter" + +config OPENTHREAD_ECDSA + bool "ECDSA support" + +config OPENTHREAD_ENABLE_SERVICE + bool "Service support" + help + Enable Thread Services capability in OpenThread stack + +config OPENTHREAD_EXTERNAL_HEAP + bool "External heap support" + +config OPENTHREAD_FIREWALL + bool "Firewall support" + +config OPENTHREAD_FULL_LOGS + bool "OpenThread full logs" + +config OPENTHREAD_IP6_FRAGM + bool "IPv6 fragmentation support" + +config OPENTHREAD_JAM_DETECTION + bool "Jam detection support" + +config OPENTHREAD_JOINER + bool "Joiner functions support" + help + Enable joiner capability in OpenThread stack. Note, that DTLS + handshake used in the commissioning procedure requires a larger + mbedTLS heap than the default value. A minimum recommended value of + CONFIG_MBEDTLS_HEAP_SIZE for the commissioning is 10KB. + +config OPENTHREAD_LEGACY + bool "Legacy network support" + +config OPENTHREAD_LINK_METRICS_INITIATOR + bool "Link Metrics initiator" + +config OPENTHREAD_LINK_METRICS_MANAGER + bool "Link Metrics manager" + +config OPENTHREAD_LINK_METRICS_SUBJECT + bool "Link Metrics subject" + +config OPENTHREAD_PLATFORM_LOG_CRASH_DUMP + bool "Platform log crash dump" + +config OPENTHREAD_LOG_LEVEL_DYNAMIC + bool "Dynamic log level control" + +config OPENTHREAD_MAC_FILTER + bool "MAC filter support" + +config OPENTHREAD_MULTICAST_DNS + bool "Multicast DNS (mDNS)" + +config OPENTHREAD_MESH_DIAG + bool "Mesh Diagnostics" + depends on OPENTHREAD_FTD + help + Enable Mesh Diagnostics + +config OPENTHREAD_MESSAGE_USE_HEAP + bool "Heap allocator for message buffers" + +config OPENTHREAD_MLE_LONG_ROUTES + bool "MLE long routes extension (experimental)" + select EXPERIMENTAL + help + Enable MLE long routes extension (experimental, breaks Thread conformance) + +config OPENTHREAD_MLR + bool "Multicast Listener Registration support" + help + Enable Multicast Listener Registration support for Thread 1.2 + +config OPENTHREAD_MULTIPAN_RCP + bool "OpenThread multipan rcp" + +config OPENTHREAD_MULTIPLE_INSTANCE + bool "OpenThread multiple instances" + +config OPENTHREAD_NAT64_BORDER_ROUTING + bool "Border routing NAT64 support" + +config OPENTHREAD_NAT64_TRANSLATOR + bool "NAT64 translator support" + +config OPENTHREAD_NETDIAG_CLIENT + bool "TMF network diagnostics on client" + +config OPENTHREAD_NETDIAG_VENDOR_INFO + bool "Allow setting vendor info at runtime" + +config OPENTHREAD_NEIGHBOR_DISCOVERY_AGENT + bool "Neighbor discovery agent support" + +config OPENTHREAD_NETDATA_PUBLISHER + bool "Thread Network Data publisher" + +config OPENTHREAD_OPERATIONAL_DATASET_AUTO_INIT + bool "Operational dataset auto init" + default y + +config OPENTHREAD_OTNS + bool "OTNS support" + +config OPENTHREAD_PING_SENDER + bool "Ping sender support" + +config OPENTHREAD_PLATFORM_KEY_REF + bool "Platform cryptographic key reference support" + help + Enable usage of cryptographic key references instead of literal keys. + This requires a crypto backend library that supports key references. + +choice OPENTHREAD_PLATFORM_BOOTLOADER_MODE_CHOICE + prompt "Platform bootloader mode configuration" + optional + +config OPENTHREAD_PLATFORM_BOOTLOADER_MODE_RETENTION + bool "Bootloader mode support with boot mode retention API" + depends on RETENTION_BOOT_MODE && REBOOT + select OPENTHREAD_PLATFORM_BOOTLOADER_MODE + +config OPENTHREAD_PLATFORM_BOOTLOADER_MODE_GPIO + bool "Bootloader mode support with GPIO pin trigger" + select OPENTHREAD_PLATFORM_BOOTLOADER_MODE +endchoice # OPENTHREAD_PLATFORM_BOOTLOADER_MODE + +config OPENTHREAD_PLATFORM_BOOTLOADER_MODE + bool + help + Platform bootloader mode support + +config OPENTHREAD_PLATFORM_NETIF + bool "Platform netif support" + +config OPENTHREAD_PLATFORM_UDP + bool "Platform UDP support" + +choice OPENTHREAD_POWER_SUPPLY_CHOICE + prompt "Power supply configuration" + default OPENTHREAD_POWER_SUPPLY_EXTERNAL + +config OPENTHREAD_POWER_SUPPLY_BATTERY + bool "OT_POWER_SUPPLY_BATTERY" + +config OPENTHREAD_POWER_SUPPLY_EXTERNAL + bool "OT_POWER_SUPPLY_EXTERNAL" + +config OPENTHREAD_POWER_SUPPLY_EXTERNAL_STABLE + bool "OT_POWER_SUPPLY_EXTERNAL_STABLE" + +config OPENTHREAD_POWER_SUPPLY_EXTERNAL_UNSTABLE + bool "OT_POWER_SUPPLY_EXTERNAL_UNSTABLE" +endchoice # OPENTHREAD_POWER_SUPPLY_CHOICE + +config OPENTHREAD_POWER_SUPPLY + string + prompt "Power supply configuration" + default "BATTERY" if OPENTHREAD_POWER_SUPPLY_BATTERY + default "EXTERNAL" if OPENTHREAD_POWER_SUPPLY_EXTERNAL + default "EXTERNAL_STABLE" if OPENTHREAD_POWER_SUPPLY_EXTERNAL_STABLE + default "EXTERNAL_UNSTABLE" if OPENTHREAD_POWER_SUPPLY_EXTERNAL_UNSTABLE + default "" + +config OPENTHREAD_RADIO_STATS + bool "Support for Radio Statistics" + +config OPENTHREAD_RAW + bool "Raw Link support" + +config OPENTHREAD_REFERENCE_DEVICE + bool "Reference Device support" + help + Enable Thread Certification reference device support in OpenThread stack + +config OPENTHREAD_SETTINGS_RAM + bool "Volatile-only storage of settings" + +config OPENTHREAD_SLAAC + bool "SLAAC support" + +config OPENTHREAD_SNTP_CLIENT + bool "SNTP Client support" + +config OPENTHREAD_SRP_ADV_PROXY + bool "SRP Server Advertising Proxy support" + depends on OPENTHREAD_SRP_SERVER + depends on OPENTHREAD_BORDER_ROUTING + +config OPENTHREAD_SRP_CLIENT + bool "SRP Client support" + select OPENTHREAD_ECDSA + +config OPENTHREAD_SRP_SERVER + bool "SRP Server support" + select OPENTHREAD_NETDATA_PUBLISHER + select OPENTHREAD_ECDSA + +config OPENTHREAD_TIME_SYNC + bool "The time synchronization service feature [EXPERIMENTAL]" + select EXPERIMENTAL + +config OPENTHREAD_TREL + bool "TREL radio link for Thread over Infrastructure feature" + +config OPENTHREAD_TX_BEACON_PAYLOAD + bool "TX beacon payload support" + +config OPENTHREAD_TX_QUEUE_STATISTICS + bool "TX queue statistics support" + +config OPENTHREAD_UDP_FORWARD + bool "UDP forward support" + +config OPENTHREAD_UPTIME + bool "Openthread uptime counter" + default y if OPENTHREAD_FTD + +config OPENTHREAD_VERHOEFF_CHECKSUM + bool "Verhoeff checksum" + +config OPENTHREAD_CLI_VENDOR_EXTENSION + string "Path to CMake file to define and link Openthread CLI vendor extension" diff --git a/modules/openthread/Kconfig.thread b/modules/openthread/Kconfig.thread new file mode 100644 index 000000000000..2bababd4fee5 --- /dev/null +++ b/modules/openthread/Kconfig.thread @@ -0,0 +1,252 @@ +# Thread network configuration options + +# Copyright (c) 2020 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +config OPENTHREAD_PANID + int "Default PAN ID" + default 43981 + +config OPENTHREAD_CHANNEL + int "Default Channel" + default 11 + +config OPENTHREAD_NETWORK_NAME + string "Default network name" + default "ot_zephyr" + help + Network name for OpenThread + +config OPENTHREAD_XPANID + string "Default Extended PAN ID" + default "de:ad:00:be:ef:00:ca:fe" + help + Extended PAN ID for OpenThread with + format "de:ad:00:be:ef:00:ca:fe" + +config OPENTHREAD_NETWORKKEY + string "Default Thread Network Key" + help + Network Key for OpenThread with format + "00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd:ee:ff" + +config OPENTHREAD_JOINER_AUTOSTART + bool "Automatic joiner start" + depends on OPENTHREAD_JOINER + +config OPENTHREAD_JOINER_PSKD + string "Default pre shared key for the Joiner" + depends on OPENTHREAD_JOINER_AUTOSTART + default "J01NME" + +choice OPENTHREAD_DEVICE_TYPE + prompt "OpenThread device type" + help + This option selects Thread network device type + +config OPENTHREAD_FTD + bool "FTD - Full Thread Device" +config OPENTHREAD_MTD + bool "MTD - Minimal Thread Device" +endchoice + +config OPENTHREAD_MTD_SED + bool "SED - Sleepy End Device" + depends on OPENTHREAD_MTD + +config OPENTHREAD_POLL_PERIOD + int "Poll period for sleepy end devices [ms]" + default 236000 + depends on OPENTHREAD_MTD_SED + +config OPENTHREAD_MAX_CHILDREN + int "The maximum number of children" + range 1 511 + default 32 + +config OPENTHREAD_MAX_IP_ADDR_PER_CHILD + int "The maximum number of IPv6 address registrations per child" + range 4 $(UINT8_MAX) + default 6 + +config OPENTHREAD_CONFIG_PLATFORM_INFO + string "The platform-specific string to insert into the OpenThread version string" + default "Zephyr" + +config OPENTHREAD_RADIO_LINK_IEEE_802_15_4_ENABLE + bool "Support for IEEE802.15.4 radio link" + default y + +config OPENTHREAD_CSL_AUTO_SYNC + bool "CSL autosync" + default y if OPENTHREAD_CSL_RECEIVER + +config OPENTHREAD_CSL_REQUEST_TIME_AHEAD + int "CSL transmitter request time ahead" + default 2000 + help + Defines how many microseconds ahead should MAC deliver a CSL frame to the sub-MAC layer. + +config OPENTHREAD_CSL_RECEIVE_TIME_AHEAD + int "CSL receiver wake up margin in microseconds" + default 5000 + +config OPENTHREAD_MIN_RECEIVE_ON_AHEAD + int "Minimum receiving time before start of MHR" + default 192 + help + The minimum time (microseconds) that radio has to be in receive mode before the start of the MHR. + +config OPENTHREAD_MIN_RECEIVE_ON_AFTER + int "Minimum receiving time after start of MHR" + default 5504 + help + The minimum time (microseconds) that radio should be in receive mode after the start of the MHR. + +config OPENTHREAD_PLATFORM_CSL_UNCERT + int "CSL uncertainty" + default $(UINT8_MAX) + range 0 $(UINT8_MAX) + help + The fixed uncertainty of the Device for scheduling CSL Transmissions in units of 10 microseconds. + +config OPENTHREAD_CSL_TIMEOUT + int "CSL timeout in seconds" + default 100 + help + The default CSL timeout in seconds. + +config OPENTHREAD_MAC_SOFTWARE_TX_SECURITY_ENABLE + bool "Software transmission security logic" + default y if !OPENTHREAD_THREAD_VERSION_1_1 + +config OPENTHREAD_MLE_INFORM_PREVIOUS_PARENT_ON_REATTACH + bool "Inform previous parent on reattach" + default y if OPENTHREAD_PARENT_SEARCH + help + To allow end devices (EDs) in a Thread network to inform their + previous parent router that they have attached to a new parent + router, enable the Inform Previous Parent on Reattach feature. + +config OPENTHREAD_PARENT_SEARCH + bool "Periodic parent search support" + help + To allow end devices (EDs) in a Thread network to switch to a + better parent router than their current one—while still attached + to the network—enable the Periodic Parent Search feature. + +config OPENTHREAD_PARENT_SEARCH_CHECK_INTERVAL + int "Interval to trigger parent search in seconds" + default 540 + depends on OPENTHREAD_PARENT_SEARCH + +config OPENTHREAD_PARENT_SEARCH_BACKOFF_INTERVAL + int "Backoff interval to prevent parent search retry in seconds" + default 36000 + depends on OPENTHREAD_PARENT_SEARCH + +config OPENTHREAD_PARENT_SEARCH_RSS_THRESHOLD + int "RSSI threshold to trigger parent search" + default -65 + depends on OPENTHREAD_PARENT_SEARCH + +config OPENTHREAD_CLI_MAX_LINE_LENGTH + int "The maximum size of the CLI line in bytes" + range 16 $(UINT16_MAX) + default 384 + +config OPENTHREAD_IP6_MAX_EXT_UCAST_ADDRS + int "The maximum number of supported IPv6 addresses allows to be externally added" + range 0 32 + default 4 + +config OPENTHREAD_IP6_MAX_EXT_MCAST_ADDRS + int "The maximum number of supported IPv6 multicast addresses allows to be externally added" + range 0 32 + default 2 + +config OPENTHREAD_TCP_ENABLE + bool "TCP support" + +config OPENTHREAD_CLI_TCP_ENABLE + bool "TCP in the CLI tool" + default y if SHELL + depends on OPENTHREAD_TCP_ENABLE + +config OPENTHREAD_HISTORY_TRACKER + bool "History tracker support" + +config OPENTHREAD_MAC_STAY_AWAKE_BETWEEN_FRAGMENTS + bool "Stay awake between packet fragments" + help + This optimization is done at the expense of power consumption on SED/SSED devices. + +config OPENTHREAD_DEFAULT_RX_SENSITIVITY + int "OpenThread default RX sensitivity in dBm" + range $(INT8_MIN) $(INT8_MAX) + default -100 + help + Set the default receive sensitivity [dBm] in radio driver. + +config OPENTHREAD_DEFAULT_TX_POWER + int "OpenThread default tx power in dBm" + range -40 20 if NRF_802154_RADIO_DRIVER + default 0 + help + Set the default TX output power [dBm] in radio driver for OpenThread purpose. + +config OPENTHREAD_TCAT_MULTIRADIO_CAPABILITIES + bool "Openthread multiradio capability" + default y if OPENTHREAD_BLE_TCAT + help + Openthread multiradio capability. + +config OPENTHREAD_BLE_TCAT_THREAD_STACK_SIZE + int "Openthread default TCAT stack size" + default 5120 if OPENTHREAD_CRYPTO_PSA + default 4200 + help + Openthread default TCAT stack size. + +config OPENTHREAD_BLE_TCAT_RING_BUF_SIZE + int "Openthread BLE ringbuffer size" + default 512 + help + Openthread BLE TCAT ringbuffer size. + +config OPENTHREAD_NAT64_CIDR + string "Set IPv4 CIDR used by NAT64" + default "192.168.255.0/24" + depends on OPENTHREAD_BORDER_ROUTING && OPENTHREAD_NAT64_TRANSLATOR + help + Set the IPv4 CIDR (Classless Inter-Domain Routing) used by NAT64 + to set source address of the outgoing translated IPv4 packets. + The CIDR must have four bytes in the address with the + non-zero length of prefix (e.g., "127.0.0.1/24"). + +config OPENTHREAD_STORE_FRAME_COUNTER_AHEAD + int "Openthread frame counter ahead value" + default 100000 + help + Openthread value ahead of the current frame counter for persistent storage. + +config OPENTHREAD_CHILD_SUPERVISION_CHECK_TIMEOUT + int "Openthread child supervision check timeout in seconds" + default 190 + help + The supervision check timeout interval in seconds used by a device in child state. + Set to zero to disable the supervision check process on the child. + +config OPENTHREAD_CHILD_SUPERVISION_INTERVAL + int "Openthread child supervision interval in seconds" + default 129 + help + The supervision interval used by a parent device to send a supervision message + to the child, if there is no transmission to the child within this interval. + Set to zero to disable the supervision check process on the child. + +config OPENTHREAD_MLE_CHILD_TIMEOUT + int "Openthread MLE child timeout in seconds" + default 240 + help + The value of MLE child timeout in seconds. diff --git a/modules/openthread/platform/CMakeLists.txt b/modules/openthread/platform/CMakeLists.txt new file mode 100644 index 000000000000..d67fe96e8fed --- /dev/null +++ b/modules/openthread/platform/CMakeLists.txt @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: Apache-2.0 + +zephyr_library_named(openthread_platform) +zephyr_library_sources( + alarm.c + entropy.c + misc.c + platform.c + radio.c + spi.c + ) + + +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_include_directories(.) diff --git a/modules/openthread/platform/alarm.c b/modules/openthread/platform/alarm.c new file mode 100644 index 000000000000..13f3f9110478 --- /dev/null +++ b/modules/openthread/platform/alarm.c @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * Copyright (c) 2024 NXP. + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define LOG_MODULE_NAME net_openthread_alarm +#define LOG_LEVEL CONFIG_OPENTHREAD_PLATFORM_LOG_LEVEL + +#include <zephyr/logging/log.h> +LOG_MODULE_REGISTER(LOG_MODULE_NAME); + +#include <zephyr/kernel.h> +#include <string.h> +#include <inttypes.h> + +#include <openthread/platform/alarm-milli.h> +#include <openthread/platform/alarm-micro.h> +#include <openthread/platform/diag.h> +#include <openthread-system.h> + +#include <stdio.h> + +#include "platform-zephyr.h" +#include "openthread-core-zephyr-config.h" + +static bool timer_ms_fired, timer_us_fired; +static int32_t time_offset_us; +static int32_t time_offset_ms; + +static void ot_timer_ms_fired(struct k_timer *timer) +{ + ARG_UNUSED(timer); + + timer_ms_fired = true; + otSysEventSignalPending(); +} + +static void ot_timer_us_fired(struct k_timer *timer) +{ + ARG_UNUSED(timer); + + timer_us_fired = true; + otSysEventSignalPending(); +} + +K_TIMER_DEFINE(ot_ms_timer, ot_timer_ms_fired, NULL); +K_TIMER_DEFINE(ot_us_timer, ot_timer_us_fired, NULL); + +void platformAlarmInit(void) +{ +#if defined(CONFIG_NET_PKT_TXTIME) + time_offset_us = + (int32_t)((int64_t)otPlatAlarmMicroGetNow() - (uint32_t)otPlatRadioGetNow(NULL)); + time_offset_ms = time_offset_us / 1000; +#endif +} + +void platformAlarmProcess(otInstance *aInstance) +{ +#if OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE + if (timer_us_fired) { + timer_us_fired = false; + otPlatAlarmMicroFired(aInstance); + } +#endif + if (timer_ms_fired) { + timer_ms_fired = false; + if (IS_ENABLED(CONFIG_OPENTHREAD_DIAG) && otPlatDiagModeGet()) { + otPlatDiagAlarmFired(aInstance); + } else { + otPlatAlarmMilliFired(aInstance); + } + } +} + +uint32_t otPlatAlarmMilliGetNow(void) +{ + return k_uptime_get_32() - time_offset_ms; +} + +void otPlatAlarmMilliStartAt(otInstance *aInstance, uint32_t aT0, uint32_t aDt) +{ + ARG_UNUSED(aInstance); + + int32_t delta = (int32_t)(aT0 + aDt - otPlatAlarmMilliGetNow()); + + if (delta > 0) { + k_timer_start(&ot_ms_timer, K_MSEC(delta), K_NO_WAIT); + } else { + ot_timer_ms_fired(NULL); + } +} + +void otPlatAlarmMilliStop(otInstance *aInstance) +{ + ARG_UNUSED(aInstance); + + k_timer_stop(&ot_ms_timer); +} + +void otPlatAlarmMicroStartAt(otInstance *aInstance, uint32_t aT0, uint32_t aDt) +{ + ARG_UNUSED(aInstance); + + int32_t delta = (int32_t)(aT0 + aDt - otPlatAlarmMicroGetNow()); + + if (delta > 0) { + k_timer_start(&ot_us_timer, K_USEC(delta), K_NO_WAIT); + } else { + ot_timer_us_fired(NULL); + } +} + +void otPlatAlarmMicroStop(otInstance *aInstance) +{ + ARG_UNUSED(aInstance); + + k_timer_stop(&ot_us_timer); +} + +uint32_t otPlatAlarmMicroGetNow(void) +{ + return (uint32_t)(k_ticks_to_us_floor64(k_uptime_ticks()) - time_offset_us); +} + +uint16_t otPlatTimeGetXtalAccuracy(void) +{ + return otPlatRadioGetCslAccuracy(NULL); +} + +#ifdef CONFIG_HDLC_RCP_IF +uint64_t otPlatTimeGet(void) +{ + return k_ticks_to_us_floor64(k_uptime_ticks()); +} +#endif diff --git a/modules/openthread/platform/ble.c b/modules/openthread/platform/ble.c new file mode 100644 index 000000000000..b3d841d46d5c --- /dev/null +++ b/modules/openthread/platform/ble.c @@ -0,0 +1,502 @@ +/* + * Copyright (c) 2023 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include <stddef.h> +#include <string.h> +#include <errno.h> + +#include <zephyr/kernel.h> +#include <zephyr/logging/log.h> +#include <zephyr/types.h> +#include <zephyr/sys/ring_buffer.h> + +#include <zephyr/bluetooth/bluetooth.h> +#include <zephyr/bluetooth/hci.h> +#include <zephyr/bluetooth/conn.h> +#include <zephyr/bluetooth/uuid.h> +#include <zephyr/bluetooth/gatt.h> + +/* Zephyr OpenThread integration Library */ +#include <zephyr/net/openthread.h> + +/* OpenThread BLE driver API */ +#include <openthread/error.h> +#include <openthread/platform/ble.h> +#include <openthread/tcat.h> + +/* Zephyr Logging */ + +#define LOG_MODULE_NAME net_openthread_tcat +#define LOG_LEVEL CONFIG_OPENTHREAD_PLATFORM_LOG_LEVEL + +LOG_MODULE_REGISTER(LOG_MODULE_NAME); + +/* BLE connection constants as defined in thread specification. */ +#define TOBLE_SERVICE_UUID 0xfffb +#define RX_CHARACTERISTIC_UUID \ + BT_UUID_128_ENCODE(0x6bd10d8b, 0x85a7, 0x4e5a, 0xba2d, 0xc83558a5f220) +#define TX_CHARACTERISTIC_UUID \ + BT_UUID_128_ENCODE(0x7fddf61f, 0x280a, 0x4773, 0xb448, 0xba1b8fe0dd69) + +#define BT_UUID_TCAT_SERVICE BT_UUID_DECLARE_16(TOBLE_SERVICE_UUID) +#define BT_UUID_TCAT_SERVICE_RX BT_UUID_DECLARE_128(RX_CHARACTERISTIC_UUID) +#define BT_UUID_TCAT_SERVICE_TX BT_UUID_DECLARE_128(TX_CHARACTERISTIC_UUID) + +#define PLAT_BLE_THREAD_DEALY 500 +#define PLAT_BLE_MSG_DATA_MAX CONFIG_BT_L2CAP_TX_MTU /* must match the maximum MTU size used */ + +#define PLAT_BLE_MSG_CONNECT (PLAT_BLE_MSG_DATA_MAX + 1U) +#define PLAT_BLE_MSG_DISCONNECT (PLAT_BLE_MSG_CONNECT + 1U) + +/* Zephyr Kernel Objects */ + +static void ot_plat_ble_thread(void *, void *, void *); +static uint8_t ot_plat_ble_msg_buf[PLAT_BLE_MSG_DATA_MAX]; + +static K_SEM_DEFINE(ot_plat_ble_init_semaphore, 0, 1); +static K_SEM_DEFINE(ot_plat_ble_event_semaphore, 0, K_SEM_MAX_LIMIT); +RING_BUF_DECLARE(ot_plat_ble_ring_buf, CONFIG_OPENTHREAD_BLE_TCAT_RING_BUF_SIZE); +static K_THREAD_DEFINE(ot_plat_ble_tid, CONFIG_OPENTHREAD_BLE_TCAT_THREAD_STACK_SIZE, + ot_plat_ble_thread, NULL, NULL, NULL, 5, 0, PLAT_BLE_THREAD_DEALY); + +/* OpenThread Objects */ + +static otInstance *ble_openthread_instance; + +/* BLE service Objects */ + +/* forward declaration for callback functions */ +static ssize_t on_receive(struct bt_conn *conn, const struct bt_gatt_attr *attr, const void *buf, + uint16_t len, uint16_t offset, uint8_t flags); +static void on_cccd_changed(const struct bt_gatt_attr *attr, uint16_t value); + +/* Service Declaration and Registration */ +BT_GATT_SERVICE_DEFINE(my_service, BT_GATT_PRIMARY_SERVICE(BT_UUID_TCAT_SERVICE), + BT_GATT_CHARACTERISTIC(BT_UUID_TCAT_SERVICE_RX, + BT_GATT_CHRC_WRITE | BT_GATT_CHRC_WRITE_WITHOUT_RESP, + BT_GATT_PERM_READ | BT_GATT_PERM_WRITE, NULL, + on_receive, NULL), + BT_GATT_CHARACTERISTIC(BT_UUID_TCAT_SERVICE_TX, BT_GATT_CHRC_NOTIFY, + BT_GATT_PERM_READ, NULL, NULL, NULL), + BT_GATT_CCC(on_cccd_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE),); + +/* Zephyr BLE Objects */ + +/* forward declaration for callback functions */ +static void connected(struct bt_conn *conn, uint8_t err); +static void disconnected(struct bt_conn *conn, uint8_t reason); +static bool le_param_req(struct bt_conn *conn, struct bt_le_conn_param *param); +static void le_param_updated(struct bt_conn *conn, uint16_t interval, uint16_t latency, + uint16_t timeout); + +static struct bt_conn *ot_plat_ble_connection; + +static struct bt_conn_cb conn_callbacks = {.connected = connected, + .disconnected = disconnected, + .le_param_req = le_param_req, + .le_param_updated = le_param_updated}; + +static uint8_t service_data[OT_TCAT_ADVERTISEMENT_MAX_LEN] = {0}; +static const uint8_t service_data_size = ARRAY_SIZE(service_data); + +static struct bt_data ad[] = { + BT_DATA_BYTES(BT_DATA_FLAGS, (BT_LE_AD_GENERAL | BT_LE_AD_NO_BREDR)), + BT_DATA(BT_DATA_SVC_DATA16, service_data, service_data_size), +}; + +static struct bt_data sd[] = { + BT_DATA_BYTES(BT_DATA_UUID16_ALL, BT_UUID_16_ENCODE(TOBLE_SERVICE_UUID)), + BT_DATA(BT_DATA_SVC_DATA16, service_data, service_data_size), +}; + +/* Zephyr BLE Message Queue and Thread */ + +static bool ot_plat_ble_queue_msg(const uint8_t *aData, uint16_t aLen, int8_t aRssi) +{ + otError error = OT_ERROR_NONE; + uint16_t len = 0; + + if (aLen <= PLAT_BLE_MSG_DATA_MAX && aData == NULL) { + return OT_ERROR_INVALID_ARGS; + } + + k_sched_lock(); + + len = sizeof(aLen) + sizeof(aRssi) + ((aLen <= PLAT_BLE_MSG_DATA_MAX) ? aLen : 0); + + if (ring_buf_space_get(&ot_plat_ble_ring_buf) >= len) { + ring_buf_put(&ot_plat_ble_ring_buf, (uint8_t *)&aLen, sizeof(aLen)); + ring_buf_put(&ot_plat_ble_ring_buf, &aRssi, sizeof(aRssi)); + if (aLen <= PLAT_BLE_MSG_DATA_MAX) { + ring_buf_put(&ot_plat_ble_ring_buf, aData, aLen); + } + k_sem_give(&ot_plat_ble_event_semaphore); + } else { + error = OT_ERROR_NO_BUFS; + } + + k_sched_unlock(); + + return error; +} + +static void ot_plat_ble_thread(void *unused1, void *unused2, void *unused3) +{ + ARG_UNUSED(unused1); + ARG_UNUSED(unused2); + ARG_UNUSED(unused3); + + uint16_t len; + int8_t rssi; + otBleRadioPacket my_packet; + + LOG_INF("%s started", __func__); + + while (1) { + k_sem_take(&ot_plat_ble_event_semaphore, K_FOREVER); + ring_buf_get(&ot_plat_ble_ring_buf, (uint8_t *)&len, sizeof(len)); + ring_buf_get(&ot_plat_ble_ring_buf, &rssi, sizeof(rssi)); + if (len <= PLAT_BLE_MSG_DATA_MAX) { + ring_buf_get(&ot_plat_ble_ring_buf, ot_plat_ble_msg_buf, len); + } + + openthread_api_mutex_lock(openthread_get_default_context()); + + if (len <= PLAT_BLE_MSG_DATA_MAX) { + /* The packet parameter in otPlatBleGattServerOnWriteRequest is not const. + * Re-write all members. + */ + my_packet.mValue = ot_plat_ble_msg_buf; + my_packet.mPower = rssi; + my_packet.mLength = len; + otPlatBleGattServerOnWriteRequest(ble_openthread_instance, 0, &my_packet); + } else if (len == PLAT_BLE_MSG_CONNECT) { + otPlatBleGapOnConnected(ble_openthread_instance, 0); + } else if (len == PLAT_BLE_MSG_DISCONNECT) { + otPlatBleGapOnDisconnected(ble_openthread_instance, 0); + } + openthread_api_mutex_unlock(openthread_get_default_context()); + } +} + +/* Zephyr BLE service callbacks */ + +/* This function is called whenever the RX Characteristic has been written to by a Client */ +static ssize_t on_receive(struct bt_conn *conn, const struct bt_gatt_attr *attr, const void *buf, + uint16_t len, uint16_t offset, uint8_t flags) +{ + LOG_DBG("Received data, handle %" PRIu16 ", len %" PRIu16, attr->handle, len); + + otError error = ot_plat_ble_queue_msg(buf, len, 0); + + if (error != OT_ERROR_NONE) { + LOG_WRN("Error queuing message: %s", otThreadErrorToString(error)); + } + + return len; +} + +/* This function is called whenever a Notification has been sent by the TX Characteristic */ +static void on_sent(struct bt_conn *conn, void *user_data) +{ + ARG_UNUSED(conn); + ARG_UNUSED(user_data); + + LOG_DBG("Data sent"); +} + +/* This function is called whenever the CCCD register has been changed by the client */ +void on_cccd_changed(const struct bt_gatt_attr *attr, uint16_t value) +{ + uint16_t mtu; + otError error = OT_ERROR_NONE; + + ARG_UNUSED(attr); + + switch (value) { + case BT_GATT_CCC_NOTIFY: + + error = ot_plat_ble_queue_msg(NULL, PLAT_BLE_MSG_CONNECT, 0); + if (error != OT_ERROR_NONE) { + LOG_WRN("Error queuing message: %s", otThreadErrorToString(error)); + } + + error = otPlatBleGattMtuGet(ble_openthread_instance, &mtu); + if (error != OT_ERROR_NONE) { + LOG_WRN("Error retrieving mtu: %s", otThreadErrorToString(error)); + } + + LOG_INF("CCCD update (mtu=%" PRIu16 ")!", mtu); + + break; + + default: + break; + } +} + +otError otPlatBleGattServerIndicate(otInstance *aInstance, uint16_t aHandle, + const otBleRadioPacket *aPacket) +{ + ARG_UNUSED(aInstance); + + /* TO DO change to indications. */ + const struct bt_gatt_attr *attr = &my_service.attrs[3]; + + struct bt_gatt_notify_params params = {.uuid = BT_UUID_TCAT_SERVICE_TX, + .attr = attr, + .data = aPacket->mValue, + .len = aPacket->mLength, + .func = on_sent}; + + LOG_DBG("Send data, handle %d, len %d", attr->handle, aPacket->mLength); + + /* Only one connection supported */ + if (aHandle != 0) { + return OT_ERROR_INVALID_ARGS; + } + + if (ot_plat_ble_connection == NULL) { + return OT_ERROR_INVALID_STATE; + } + + /* Check whether notifications are enabled or not */ + if (bt_gatt_is_subscribed(ot_plat_ble_connection, attr, BT_GATT_CCC_NOTIFY)) { + if (bt_gatt_notify_cb(ot_plat_ble_connection, ¶ms)) { + LOG_WRN("Error, unable to send notification"); + return OT_ERROR_INVALID_ARGS; + } + } else { + LOG_WRN("Warning, notification not enabled on the selected attribute"); + return OT_ERROR_INVALID_STATE; + } + + return OT_ERROR_NONE; +} + +otError otPlatBleGattMtuGet(otInstance *aInstance, uint16_t *aMtu) +{ + ARG_UNUSED(aInstance); + + if (ot_plat_ble_connection == NULL) { + return OT_ERROR_FAILED; + } + + if (aMtu != NULL) { + *aMtu = bt_gatt_get_mtu(ot_plat_ble_connection); + } + + return OT_ERROR_NONE; +} + +otError otPlatBleGapDisconnect(otInstance *aInstance) +{ + ARG_UNUSED(aInstance); + + if (ot_plat_ble_connection == NULL) { + return OT_ERROR_INVALID_STATE; + } + + if (bt_conn_disconnect(ot_plat_ble_connection, BT_HCI_ERR_REMOTE_USER_TERM_CONN)) { + return OT_ERROR_INVALID_STATE; + } + + return OT_ERROR_NONE; +} + +/* Zephyr BLE callbacks */ + +static void connected(struct bt_conn *conn, uint8_t err) +{ + struct bt_conn_info info; + char addr[BT_ADDR_LE_STR_LEN]; + uint16_t mtu; + otError error = OT_ERROR_NONE; + + ot_plat_ble_connection = bt_conn_ref(conn); + + if (err) { + LOG_WRN("Connection failed err %u %s", + err, bt_hci_err_to_str(err)); + return; + } else if (bt_conn_get_info(conn, &info)) { + LOG_WRN("Could not parse connection info"); + } else { + bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); + + error = otPlatBleGattMtuGet(ble_openthread_instance, &mtu); + if (error != OT_ERROR_NONE) { + LOG_WRN("Error retrieving mtu: %s", otThreadErrorToString(error)); + } + + LOG_INF("Connection established (mtu=%" PRIu16 ")!", mtu); + } +} + +static void disconnected(struct bt_conn *conn, uint8_t reason) +{ + otError error = OT_ERROR_NONE; + + LOG_INF("Disconnected, reason 0x%02x %s", reason, bt_hci_err_to_str(reason)); + + if (ot_plat_ble_connection) { + bt_conn_unref(ot_plat_ble_connection); + ot_plat_ble_connection = NULL; + + error = ot_plat_ble_queue_msg(NULL, PLAT_BLE_MSG_DISCONNECT, 0); + if (error != OT_ERROR_NONE) { + LOG_WRN("Error queuing message: %s", otThreadErrorToString(error)); + } + } +} + +static bool le_param_req(struct bt_conn *conn, struct bt_le_conn_param *param) +{ + return true; +} + +static void le_param_updated(struct bt_conn *conn, uint16_t interval, uint16_t latency, + uint16_t timeout) +{ + struct bt_conn_info info; + char addr[BT_ADDR_LE_STR_LEN]; + uint16_t mtu; + otError error = OT_ERROR_NONE; + + if (bt_conn_get_info(conn, &info)) { + LOG_INF("Could not parse connection info"); + } else { + bt_addr_le_to_str(bt_conn_get_dst(conn), addr, sizeof(addr)); + + error = otPlatBleGattMtuGet(ble_openthread_instance, &mtu); + + if (error != OT_ERROR_NONE) { + LOG_WRN("Error retrieving mtu: %s", otThreadErrorToString(error)); + } + + LOG_INF("Connection parameters updated (mtu=%" PRIu16 ")!", mtu); + } +} + +static void bt_ready(int err) +{ + if (err) { + LOG_WRN("BLE init failed with error code %d", err); + return; + } + + bt_conn_cb_register(&conn_callbacks); + k_sem_give(&ot_plat_ble_init_semaphore); /* BLE stack up an running */ +} + +void otPlatBleGetLinkCapabilities(otInstance *aInstance, + otBleLinkCapabilities *aBleLinkCapabilities) +{ + ARG_UNUSED(aInstance); + + aBleLinkCapabilities->mGattNotifications = 1; + aBleLinkCapabilities->mL2CapDirect = 0; + aBleLinkCapabilities->mRsv = 0; +} + +bool otPlatBleSupportsMultiRadio(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + + return IS_ENABLED(CONFIG_OPENTHREAD_TCAT_MULTIRADIO_CAPABILITIES); +} + +otError otPlatBleGetAdvertisementBuffer(otInstance *aInstance, uint8_t **aAdvertisementBuffer) +{ + ARG_UNUSED(aInstance); + + *aAdvertisementBuffer = service_data; + + return OT_ERROR_NONE; +} + +otError otPlatBleGapAdvSetData(otInstance *aInstance, uint8_t *aAdvertisementData, + uint16_t aAdvertisementLen) +{ + ARG_UNUSED(aInstance); + + if (aAdvertisementLen > OT_TCAT_ADVERTISEMENT_MAX_LEN || aAdvertisementData == NULL) { + LOG_ERR("Invalid TCAT Advertisement parameters advlen: %d", aAdvertisementLen); + return OT_ERROR_INVALID_ARGS; + } + + ad[1].data_len = (uint8_t)aAdvertisementLen; + sd[1].data_len = (uint8_t)aAdvertisementLen; + return OT_ERROR_NONE; +} + +otError otPlatBleGapAdvStart(otInstance *aInstance, uint16_t aInterval) +{ + ARG_UNUSED(aInstance); + ARG_UNUSED(aInterval); + + int err = bt_le_adv_start(BT_LE_ADV_CONN_FAST_2, ad, ARRAY_SIZE(ad), sd, ARRAY_SIZE(sd)); + + if (err != 0 && err != -EALREADY) { + LOG_WRN("Advertising failed to start (err %d)", err); + return OT_ERROR_INVALID_STATE; + } + + LOG_INF("Advertising successfully started"); + + return OT_ERROR_NONE; +} + +otError otPlatBleGapAdvStop(otInstance *aInstance) +{ + ARG_UNUSED(aInstance); + + int err = bt_le_adv_stop(); + + if (err != 0 && err != -EALREADY) { + LOG_WRN("Advertisement failed to stop (err %d)", err); + return OT_ERROR_FAILED; + } + return OT_ERROR_NONE; +} + +/* Zephyr BLE initialization */ + +otError otPlatBleEnable(otInstance *aInstance) +{ + int err; + + ble_openthread_instance = aInstance; + err = bt_enable(bt_ready); + + if (err != 0 && err != -EALREADY) { + LOG_WRN("BLE enable failed with error code %d", err); + return OT_ERROR_FAILED; + } else if (err == -EALREADY) { + bt_conn_cb_register(&conn_callbacks); + return OT_ERROR_NONE; + } + + err = k_sem_take(&ot_plat_ble_init_semaphore, K_MSEC(500)); + + if (!err) { + LOG_INF("Bluetooth initialized"); + } else { + LOG_INF("BLE initialization did not complete in time"); + return OT_ERROR_FAILED; + } + + return OT_ERROR_NONE; +} + +otError otPlatBleDisable(otInstance *aInstance) +{ + ARG_UNUSED(aInstance); + /* This function intentionally does nothing since disabling advertisement disables BLE + * stack. + */ + return OT_ERROR_NONE; +} diff --git a/modules/openthread/platform/crypto_psa.c b/modules/openthread/platform/crypto_psa.c new file mode 100644 index 000000000000..6ad286f3734f --- /dev/null +++ b/modules/openthread/platform/crypto_psa.c @@ -0,0 +1,668 @@ +/* + * Copyright (c) 2021 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include <openthread/platform/crypto.h> + +#include <psa/crypto.h> + +#include <zephyr/sys/__assert.h> + +#if !defined(CONFIG_BUILD_WITH_TFM) && defined(CONFIG_OPENTHREAD_CRYPTO_PSA) +#include <zephyr/settings/settings.h> +#endif + +#if defined(CONFIG_OPENTHREAD_ECDSA) +#include <string.h> +#include <mbedtls/asn1.h> +#endif + +static otError psaToOtError(psa_status_t aStatus) +{ + switch (aStatus) { + case PSA_SUCCESS: + return OT_ERROR_NONE; + case PSA_ERROR_INVALID_ARGUMENT: + return OT_ERROR_INVALID_ARGS; + case PSA_ERROR_BUFFER_TOO_SMALL: + return OT_ERROR_NO_BUFS; + default: + return OT_ERROR_FAILED; + } +} + +static psa_key_type_t toPsaKeyType(otCryptoKeyType aType) +{ + switch (aType) { + case OT_CRYPTO_KEY_TYPE_RAW: + return PSA_KEY_TYPE_RAW_DATA; + case OT_CRYPTO_KEY_TYPE_AES: + return PSA_KEY_TYPE_AES; + case OT_CRYPTO_KEY_TYPE_HMAC: + return PSA_KEY_TYPE_HMAC; + case OT_CRYPTO_KEY_TYPE_ECDSA: + return PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1); + default: + return PSA_KEY_TYPE_NONE; + } +} + +static psa_algorithm_t toPsaAlgorithm(otCryptoKeyAlgorithm aAlgorithm) +{ + switch (aAlgorithm) { + case OT_CRYPTO_KEY_ALG_AES_ECB: + return PSA_ALG_ECB_NO_PADDING; + case OT_CRYPTO_KEY_ALG_HMAC_SHA_256: + return PSA_ALG_HMAC(PSA_ALG_SHA_256); + case OT_CRYPTO_KEY_ALG_ECDSA: + return PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_256); + default: + /* + * There is currently no constant like PSA_ALG_NONE, but 0 is used + * to indicate an unknown algorithm. + */ + return (psa_algorithm_t)0; + } +} + +static psa_key_usage_t toPsaKeyUsage(int aUsage) +{ + psa_key_usage_t usage = 0; + + if (aUsage & OT_CRYPTO_KEY_USAGE_EXPORT) { + usage |= PSA_KEY_USAGE_EXPORT; + } + + if (aUsage & OT_CRYPTO_KEY_USAGE_ENCRYPT) { + usage |= PSA_KEY_USAGE_ENCRYPT; + } + + if (aUsage & OT_CRYPTO_KEY_USAGE_DECRYPT) { + usage |= PSA_KEY_USAGE_DECRYPT; + } + + if (aUsage & OT_CRYPTO_KEY_USAGE_SIGN_HASH) { + usage |= PSA_KEY_USAGE_SIGN_HASH; + } + + if (aUsage & OT_CRYPTO_KEY_USAGE_VERIFY_HASH) { + usage |= PSA_KEY_USAGE_VERIFY_HASH; + } + + return usage; +} + +static bool checkKeyUsage(int aUsage) +{ + /* Check if only supported flags have been passed */ + int supported_flags = OT_CRYPTO_KEY_USAGE_EXPORT | OT_CRYPTO_KEY_USAGE_ENCRYPT | + OT_CRYPTO_KEY_USAGE_DECRYPT | OT_CRYPTO_KEY_USAGE_SIGN_HASH | + OT_CRYPTO_KEY_USAGE_VERIFY_HASH; + + return (aUsage & ~supported_flags) == 0; +} + +static bool checkContext(otCryptoContext *aContext, size_t aMinSize) +{ + /* Verify that the passed context is initialized and points to a big enough buffer */ + return aContext != NULL && aContext->mContext != NULL && aContext->mContextSize >= aMinSize; +} + +void otPlatCryptoInit(void) +{ + psa_crypto_init(); + +#if !defined(CONFIG_BUILD_WITH_TFM) && defined(CONFIG_OPENTHREAD_CRYPTO_PSA) + /* + * In OpenThread, Settings are initialized after KeyManager by default. If device uses + * PSA with emulated TFM, Settings have to be initialized at the end of otPlatCryptoInit(), + * to be available before storing Network Key. + */ + __ASSERT_EVAL((void)settings_subsys_init(), int err = settings_subsys_init(), !err, + "Failed to initialize settings"); +#endif +} + +otError otPlatCryptoImportKey(otCryptoKeyRef *aKeyRef, otCryptoKeyType aKeyType, + otCryptoKeyAlgorithm aKeyAlgorithm, int aKeyUsage, + otCryptoKeyStorage aKeyPersistence, const uint8_t *aKey, + size_t aKeyLen) +{ +#if defined(CONFIG_OPENTHREAD_ECDSA) + int version; + size_t len; + unsigned char *p = (unsigned char *)aKey; + unsigned char *end; +#endif + + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + psa_status_t status = 0; + + if (aKeyRef == NULL || aKey == NULL || !checkKeyUsage(aKeyUsage)) { + return OT_ERROR_INVALID_ARGS; + } + +#if defined(CONFIG_OPENTHREAD_ECDSA) + /* Check if key is ECDSA pair and extract private key from it since PSA expects it. */ + if (aKeyType == OT_CRYPTO_KEY_TYPE_ECDSA) { + + end = p + aKeyLen; + status = mbedtls_asn1_get_tag(&p, end, &len, + MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); + if (status != 0) { + return OT_ERROR_FAILED; + } + + end = p + len; + status = mbedtls_asn1_get_int(&p, end, &version); + if (status != 0) { + return OT_ERROR_FAILED; + } + + status = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OCTET_STRING); + if (status != 0 || len != 32) { + return OT_ERROR_FAILED; + } + + aKey = p; + aKeyLen = len; + } +#endif + + psa_set_key_type(&attributes, toPsaKeyType(aKeyType)); + psa_set_key_algorithm(&attributes, toPsaAlgorithm(aKeyAlgorithm)); + psa_set_key_usage_flags(&attributes, toPsaKeyUsage(aKeyUsage)); + + switch (aKeyPersistence) { + case OT_CRYPTO_KEY_STORAGE_PERSISTENT: + psa_set_key_lifetime(&attributes, PSA_KEY_LIFETIME_PERSISTENT); + psa_set_key_id(&attributes, *aKeyRef); + break; + case OT_CRYPTO_KEY_STORAGE_VOLATILE: + psa_set_key_lifetime(&attributes, PSA_KEY_LIFETIME_VOLATILE); + break; + } + + status = psa_import_key(&attributes, aKey, aKeyLen, aKeyRef); + psa_reset_key_attributes(&attributes); + + return psaToOtError(status); +} + +otError otPlatCryptoExportKey(otCryptoKeyRef aKeyRef, uint8_t *aBuffer, size_t aBufferLen, + size_t *aKeyLen) +{ + if (aBuffer == NULL) { + return OT_ERROR_INVALID_ARGS; + } + + return psaToOtError(psa_export_key(aKeyRef, aBuffer, aBufferLen, aKeyLen)); +} + +otError otPlatCryptoDestroyKey(otCryptoKeyRef aKeyRef) +{ + return psaToOtError(psa_destroy_key(aKeyRef)); +} + +bool otPlatCryptoHasKey(otCryptoKeyRef aKeyRef) +{ + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + psa_status_t status; + + status = psa_get_key_attributes(aKeyRef, &attributes); + psa_reset_key_attributes(&attributes); + + return status == PSA_SUCCESS; +} + +otError otPlatCryptoHmacSha256Init(otCryptoContext *aContext) +{ + psa_mac_operation_t *operation; + + if (!checkContext(aContext, sizeof(psa_mac_operation_t))) { + return OT_ERROR_INVALID_ARGS; + } + + operation = aContext->mContext; + *operation = psa_mac_operation_init(); + + return OT_ERROR_NONE; +} + +otError otPlatCryptoHmacSha256Deinit(otCryptoContext *aContext) +{ + psa_mac_operation_t *operation; + + if (!checkContext(aContext, sizeof(psa_mac_operation_t))) { + return OT_ERROR_INVALID_ARGS; + } + + operation = aContext->mContext; + + return psaToOtError(psa_mac_abort(operation)); +} + +otError otPlatCryptoHmacSha256Start(otCryptoContext *aContext, const otCryptoKey *aKey) +{ + psa_mac_operation_t *operation; + psa_status_t status; + + if (aKey == NULL || !checkContext(aContext, sizeof(psa_mac_operation_t))) { + return OT_ERROR_INVALID_ARGS; + } + + operation = aContext->mContext; + status = psa_mac_sign_setup(operation, aKey->mKeyRef, PSA_ALG_HMAC(PSA_ALG_SHA_256)); + + return psaToOtError(status); +} + +otError otPlatCryptoHmacSha256Update(otCryptoContext *aContext, const void *aBuf, + uint16_t aBufLength) +{ + psa_mac_operation_t *operation; + + if (aBuf == NULL || !checkContext(aContext, sizeof(psa_mac_operation_t))) { + return OT_ERROR_INVALID_ARGS; + } + + operation = aContext->mContext; + + return psaToOtError(psa_mac_update(operation, (const uint8_t *)aBuf, aBufLength)); +} + +otError otPlatCryptoHmacSha256Finish(otCryptoContext *aContext, uint8_t *aBuf, size_t aBufLength) +{ + psa_mac_operation_t *operation; + size_t mac_length; + + if (aBuf == NULL || !checkContext(aContext, sizeof(psa_mac_operation_t))) { + return OT_ERROR_INVALID_ARGS; + } + + operation = aContext->mContext; + + return psaToOtError(psa_mac_sign_finish(operation, aBuf, aBufLength, &mac_length)); +} + +otError otPlatCryptoAesInit(otCryptoContext *aContext) +{ + psa_key_id_t *key_ref; + + if (!checkContext(aContext, sizeof(psa_key_id_t))) { + return OT_ERROR_INVALID_ARGS; + } + + key_ref = aContext->mContext; + *key_ref = (psa_key_id_t)0; /* In TF-M 1.5.0 this can be replaced with PSA_KEY_ID_NULL */ + + return OT_ERROR_NONE; +} + +otError otPlatCryptoAesSetKey(otCryptoContext *aContext, const otCryptoKey *aKey) +{ + psa_key_id_t *key_ref; + + if (aKey == NULL || !checkContext(aContext, sizeof(psa_key_id_t))) { + return OT_ERROR_INVALID_ARGS; + } + + key_ref = aContext->mContext; + *key_ref = aKey->mKeyRef; + + return OT_ERROR_NONE; +} + +otError otPlatCryptoAesEncrypt(otCryptoContext *aContext, const uint8_t *aInput, uint8_t *aOutput) +{ + const size_t block_size = PSA_BLOCK_CIPHER_BLOCK_LENGTH(PSA_KEY_TYPE_AES); + psa_status_t status = PSA_SUCCESS; + psa_key_id_t *key_ref; + size_t cipher_length; + + if (aInput == NULL || aOutput == NULL || !checkContext(aContext, sizeof(psa_key_id_t))) { + return OT_ERROR_INVALID_ARGS; + } + + key_ref = aContext->mContext; + status = psa_cipher_encrypt(*key_ref, PSA_ALG_ECB_NO_PADDING, aInput, block_size, aOutput, + block_size, &cipher_length); + + return psaToOtError(status); +} + +otError otPlatCryptoAesFree(otCryptoContext *aContext) +{ + return OT_ERROR_NONE; +} + +otError otPlatCryptoSha256Init(otCryptoContext *aContext) +{ + psa_hash_operation_t *operation; + + if (!checkContext(aContext, sizeof(psa_hash_operation_t))) { + return OT_ERROR_INVALID_ARGS; + } + + operation = aContext->mContext; + *operation = psa_hash_operation_init(); + + return OT_ERROR_NONE; +} + +otError otPlatCryptoSha256Deinit(otCryptoContext *aContext) +{ + psa_hash_operation_t *operation; + + if (!checkContext(aContext, sizeof(psa_hash_operation_t))) { + return OT_ERROR_INVALID_ARGS; + } + + operation = aContext->mContext; + + return psaToOtError(psa_hash_abort(operation)); +} + +otError otPlatCryptoSha256Start(otCryptoContext *aContext) +{ + psa_hash_operation_t *operation; + + if (!checkContext(aContext, sizeof(psa_hash_operation_t))) { + return OT_ERROR_INVALID_ARGS; + } + + operation = aContext->mContext; + + return psaToOtError(psa_hash_setup(operation, PSA_ALG_SHA_256)); +} + +otError otPlatCryptoSha256Update(otCryptoContext *aContext, const void *aBuf, uint16_t aBufLength) +{ + psa_hash_operation_t *operation; + + if (aBuf == NULL || !checkContext(aContext, sizeof(psa_hash_operation_t))) { + return OT_ERROR_INVALID_ARGS; + } + + operation = aContext->mContext; + + return psaToOtError(psa_hash_update(operation, (const uint8_t *)aBuf, aBufLength)); +} + +otError otPlatCryptoSha256Finish(otCryptoContext *aContext, uint8_t *aHash, uint16_t aHashSize) +{ + psa_hash_operation_t *operation; + size_t hash_size; + + if (aHash == NULL || !checkContext(aContext, sizeof(psa_hash_operation_t))) { + return OT_ERROR_INVALID_ARGS; + } + + operation = aContext->mContext; + + return psaToOtError(psa_hash_finish(operation, aHash, aHashSize, &hash_size)); +} + +void otPlatCryptoRandomInit(void) +{ + psa_crypto_init(); +} + +void otPlatCryptoRandomDeinit(void) +{ +} + +otError otPlatCryptoRandomGet(uint8_t *aBuffer, uint16_t aSize) +{ + return psaToOtError(psa_generate_random(aBuffer, aSize)); +} + +#if defined(CONFIG_OPENTHREAD_ECDSA) + +otError otPlatCryptoEcdsaGenerateKey(otPlatCryptoEcdsaKeyPair *aKeyPair) +{ + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + psa_key_id_t key_id = 0; + psa_status_t status; + size_t exported_length; + + psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_EXPORT); + psa_set_key_algorithm(&attributes, PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_256)); + psa_set_key_type(&attributes, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1)); + psa_set_key_bits(&attributes, 256); + + status = psa_generate_key(&attributes, &key_id); + if (status != PSA_SUCCESS) { + goto out; + } + + status = psa_export_key(key_id, aKeyPair->mDerBytes, OT_CRYPTO_ECDSA_MAX_DER_SIZE, + &exported_length); + if (status != PSA_SUCCESS) { + goto out; + } + aKeyPair->mDerLength = exported_length; + +out: + psa_reset_key_attributes(&attributes); + psa_destroy_key(key_id); + + return psaToOtError(status); +} + +otError otPlatCryptoEcdsaSign(const otPlatCryptoEcdsaKeyPair *aKeyPair, + const otPlatCryptoSha256Hash *aHash, + otPlatCryptoEcdsaSignature *aSignature) +{ + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + psa_key_id_t key_id; + psa_status_t status; + size_t signature_length; + + psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_SIGN_HASH); + psa_set_key_algorithm(&attributes, PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_256)); + psa_set_key_type(&attributes, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1)); + psa_set_key_bits(&attributes, 256); + + status = psa_import_key(&attributes, aKeyPair->mDerBytes, aKeyPair->mDerLength, &key_id); + if (status != PSA_SUCCESS) { + goto out; + } + + status = psa_sign_hash(key_id, PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_256), aHash->m8, + OT_CRYPTO_SHA256_HASH_SIZE, aSignature->m8, + OT_CRYPTO_ECDSA_SIGNATURE_SIZE, &signature_length); + if (status != PSA_SUCCESS) { + goto out; + } + +out: + psa_reset_key_attributes(&attributes); + psa_destroy_key(key_id); + + return psaToOtError(status); +} + +otError otPlatCryptoEcdsaVerify(const otPlatCryptoEcdsaPublicKey *aPublicKey, + const otPlatCryptoSha256Hash *aHash, + const otPlatCryptoEcdsaSignature *aSignature) +{ + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + psa_key_id_t key_id; + psa_status_t status; + uint8_t buffer[1 + OT_CRYPTO_ECDSA_PUBLIC_KEY_SIZE]; + + psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_VERIFY_HASH); + psa_set_key_algorithm(&attributes, PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_256)); + psa_set_key_type(&attributes, PSA_KEY_TYPE_ECC_PUBLIC_KEY(PSA_ECC_FAMILY_SECP_R1)); + psa_set_key_bits(&attributes, 256); + + /* + * `psa_import_key` expects a key format as specified by SEC1 §2.3.3 for the + * uncompressed representation of the ECPoint. + */ + buffer[0] = 0x04; + memcpy(buffer + 1, aPublicKey->m8, OT_CRYPTO_ECDSA_PUBLIC_KEY_SIZE); + status = psa_import_key(&attributes, buffer, sizeof(buffer), &key_id); + if (status != PSA_SUCCESS) { + goto out; + } + + status = psa_verify_hash(key_id, PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_256), aHash->m8, + OT_CRYPTO_SHA256_HASH_SIZE, aSignature->m8, + OT_CRYPTO_ECDSA_SIGNATURE_SIZE); + if (status != PSA_SUCCESS) { + goto out; + } + +out: + psa_reset_key_attributes(&attributes); + psa_destroy_key(key_id); + + return psaToOtError(status); +} + +otError otPlatCryptoEcdsaSignUsingKeyRef(otCryptoKeyRef aKeyRef, + const otPlatCryptoSha256Hash *aHash, + otPlatCryptoEcdsaSignature *aSignature) +{ + psa_status_t status; + size_t signature_length; + + status = psa_sign_hash(aKeyRef, PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_256), aHash->m8, + OT_CRYPTO_SHA256_HASH_SIZE, aSignature->m8, + OT_CRYPTO_ECDSA_SIGNATURE_SIZE, &signature_length); + if (status != PSA_SUCCESS) { + goto out; + } + + __ASSERT_NO_MSG(signature_length == OT_CRYPTO_ECDSA_SIGNATURE_SIZE); +out: + return psaToOtError(status); +} + +otError otPlatCryptoEcdsaVerifyUsingKeyRef(otCryptoKeyRef aKeyRef, + const otPlatCryptoSha256Hash *aHash, + const otPlatCryptoEcdsaSignature *aSignature) +{ + psa_status_t status; + + status = psa_verify_hash(aKeyRef, PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_256), aHash->m8, + OT_CRYPTO_SHA256_HASH_SIZE, aSignature->m8, + OT_CRYPTO_ECDSA_SIGNATURE_SIZE); + if (status != PSA_SUCCESS) { + goto out; + } + +out: + return psaToOtError(status); +} + +otError otPlatCryptoEcdsaExportPublicKey(otCryptoKeyRef aKeyRef, + otPlatCryptoEcdsaPublicKey *aPublicKey) +{ + psa_status_t status; + size_t exported_length; + uint8_t buffer[1 + OT_CRYPTO_ECDSA_PUBLIC_KEY_SIZE]; + + status = psa_export_public_key(aKeyRef, buffer, sizeof(buffer), &exported_length); + if (status != PSA_SUCCESS) { + goto out; + } + + __ASSERT_NO_MSG(exported_length == sizeof(buffer)); + memcpy(aPublicKey->m8, buffer + 1, OT_CRYPTO_ECDSA_PUBLIC_KEY_SIZE); + +out: + return psaToOtError(status); +} + +otError otPlatCryptoEcdsaGenerateAndImportKey(otCryptoKeyRef aKeyRef) +{ + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + psa_status_t status; + psa_key_id_t key_id = (psa_key_id_t)aKeyRef; + + psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_VERIFY_HASH | PSA_KEY_USAGE_SIGN_HASH); + psa_set_key_algorithm(&attributes, PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_256)); + psa_set_key_type(&attributes, PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1)); + psa_set_key_lifetime(&attributes, PSA_KEY_LIFETIME_PERSISTENT); + psa_set_key_id(&attributes, key_id); + psa_set_key_bits(&attributes, 256); + + status = psa_generate_key(&attributes, &key_id); + if (status != PSA_SUCCESS) { + goto out; + } + +out: + psa_reset_key_attributes(&attributes); + + return psaToOtError(status); +} + +otError otPlatCryptoPbkdf2GenerateKey(const uint8_t *aPassword, + uint16_t aPasswordLen, + const uint8_t *aSalt, + uint16_t aSaltLen, + uint32_t aIterationCounter, + uint16_t aKeyLen, + uint8_t *aKey) +{ + psa_status_t status = PSA_SUCCESS; + psa_key_id_t key_id = PSA_KEY_ID_NULL; + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + psa_algorithm_t algorithm = PSA_ALG_PBKDF2_AES_CMAC_PRF_128; + psa_key_derivation_operation_t operation = PSA_KEY_DERIVATION_OPERATION_INIT; + + psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_DERIVE); + psa_set_key_lifetime(&attributes, PSA_KEY_LIFETIME_VOLATILE); + psa_set_key_algorithm(&attributes, algorithm); + psa_set_key_type(&attributes, PSA_KEY_TYPE_PASSWORD); + psa_set_key_bits(&attributes, PSA_BYTES_TO_BITS(aPasswordLen)); + + status = psa_import_key(&attributes, aPassword, aPasswordLen, &key_id); + if (status != PSA_SUCCESS) { + goto out; + } + + status = psa_key_derivation_setup(&operation, algorithm); + if (status != PSA_SUCCESS) { + goto out; + } + + status = psa_key_derivation_input_integer(&operation, PSA_KEY_DERIVATION_INPUT_COST, + aIterationCounter); + if (status != PSA_SUCCESS) { + goto out; + } + + status = psa_key_derivation_input_bytes(&operation, PSA_KEY_DERIVATION_INPUT_SALT, + aSalt, aSaltLen); + if (status != PSA_SUCCESS) { + goto out; + } + + status = psa_key_derivation_input_key(&operation, PSA_KEY_DERIVATION_INPUT_PASSWORD, + key_id); + if (status != PSA_SUCCESS) { + goto out; + } + + status = psa_key_derivation_output_bytes(&operation, aKey, aKeyLen); + if (status != PSA_SUCCESS) { + goto out; + } + +out: + psa_reset_key_attributes(&attributes); + psa_key_derivation_abort(&operation); + psa_destroy_key(key_id); + + __ASSERT_NO_MSG(status == PSA_SUCCESS); + return psaToOtError(status); +} + +#endif /* #if CONFIG_OPENTHREAD_ECDSA */ diff --git a/modules/openthread/platform/diag.c b/modules/openthread/platform/diag.c new file mode 100644 index 000000000000..79f4e38984af --- /dev/null +++ b/modules/openthread/platform/diag.c @@ -0,0 +1,464 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include <zephyr/kernel.h> +#include <zephyr/drivers/gpio.h> + +#include <openthread/error.h> +#include <openthread/platform/alarm-milli.h> +#include <openthread/platform/diag.h> +#include <openthread/platform/radio.h> + +#include "platform-zephyr.h" +#include "zephyr/sys/util.h" + +enum { + DIAG_TRANSMIT_MODE_IDLE, + DIAG_TRANSMIT_MODE_PACKETS, + DIAG_TRANSMIT_MODE_CARRIER, + DIAG_TRANSMIT_MODE_MODCARRIER + +} diag_trasmit_mode; + +/** + * Diagnostics mode variables. + * + */ + +static bool sDiagMode; +static void *sDiagCallbackContext; +static otPlatDiagOutputCallback sDiagOutputCallback; +static uint8_t sTransmitMode = DIAG_TRANSMIT_MODE_IDLE; +static uint8_t sChannel = 20; +static uint32_t sTxPeriod = 1; +static int32_t sTxCount; +static int32_t sTxRequestedCount = 1; + +static otError startModCarrier(otInstance *aInstance, uint8_t aArgsLength, char *aArgs[]); +static otError processTransmit(otInstance *aInstance, uint8_t aArgsLength, char *aArgs[]); + +static otError parse_long(char *aArgs, long *aValue) +{ + char *endptr; + *aValue = strtol(aArgs, &endptr, 0); + return (*endptr == '\0') ? OT_ERROR_NONE : OT_ERROR_PARSE; +} + +static void diag_output(const char *aFormat, ...) +{ + va_list args; + + va_start(args, aFormat); + + if (sDiagOutputCallback != NULL) { + sDiagOutputCallback(aFormat, args, sDiagCallbackContext); + } + + va_end(args); +} + +void otPlatDiagSetOutputCallback(otInstance *aInstance, + otPlatDiagOutputCallback aCallback, + void *aContext) +{ + OT_UNUSED_VARIABLE(aInstance); + + sDiagOutputCallback = aCallback; + sDiagCallbackContext = aContext; +} + +otError otPlatDiagProcess(otInstance *aInstance, uint8_t aArgsLength, char *aArgs[]) +{ +#if defined(CONFIG_IEEE802154_CARRIER_FUNCTIONS) + if (strcmp(aArgs[0], "modcarrier") == 0) { + return startModCarrier(aInstance, aArgsLength - 1, aArgs + 1); + } +#endif + + if (strcmp(aArgs[0], "transmit") == 0) { + return processTransmit(aInstance, aArgsLength - 1, aArgs + 1); + } + + /* Add more platform specific diagnostics features here. */ + diag_output("diag feature '%s' is not supported\r\n", aArgs[0]); + + return OT_ERROR_NOT_IMPLEMENTED; +} + +void otPlatDiagModeSet(bool aMode) +{ + sDiagMode = aMode; + + if (!sDiagMode) { + otPlatRadioSleep(NULL); + } +} + +bool otPlatDiagModeGet(void) +{ + return sDiagMode; +} + +void otPlatDiagChannelSet(uint8_t aChannel) +{ + sChannel = aChannel; + platformRadioChannelSet(aChannel); +} + +void otPlatDiagTxPowerSet(int8_t aTxPower) +{ + ARG_UNUSED(aTxPower); +} + +void otPlatDiagRadioReceived(otInstance *aInstance, + otRadioFrame *aFrame, + otError aError) +{ + ARG_UNUSED(aInstance); + ARG_UNUSED(aFrame); + ARG_UNUSED(aError); +} + +#if defined(CONFIG_IEEE802154_CARRIER_FUNCTIONS) +otError otPlatDiagRadioTransmitCarrier(otInstance *aInstance, bool aEnable) +{ + if (sTransmitMode != DIAG_TRANSMIT_MODE_IDLE && + sTransmitMode != DIAG_TRANSMIT_MODE_CARRIER) { + return OT_ERROR_INVALID_STATE; + } + + if (aEnable) { + sTransmitMode = DIAG_TRANSMIT_MODE_CARRIER; + } else { + sTransmitMode = DIAG_TRANSMIT_MODE_IDLE; + } + + return platformRadioTransmitCarrier(aInstance, aEnable); +} +#endif /* CONFIG_IEEE802154_CARRIER_FUNCTIONS */ + +/* + * To enable gpio diag commands, in Devicetree create `openthread` node in `/options/` path + * with `compatible = "openthread,config"` property and `diag-gpios` property, + * which should contain array of GPIO pin's configuration properties containing controller phandles, + * pin numbers and pin flags. e.g: + * + * options { + * openthread { + * compatible = "openthread,config"; + * diag-gpios = <&gpio0 0 GPIO_ACTIVE_HIGH>, + * <&gpio1 0 GPIO_ACTIVE_LOW>; + * }; + * }; + * + * To enable reading current gpio pin mode, define + * `CONFIG_GPIO_GET_DIRECTION` in prj.conf. + * + * Note: `<gpio>` in `diag gpio` commands is an index of diag-gpios array. For example shown above, + * `ot diag gpio mode 0` will return current mode of pin nmb 0 controlled by `gpio0` controller. + */ +#if DT_HAS_COMPAT_STATUS_OKAY(openthread_config) && \ + DT_NODE_HAS_PROP(DT_COMPAT_GET_ANY_STATUS_OKAY(openthread_config), diag_gpios) + +static const struct gpio_dt_spec gpio_spec[] = { + DT_FOREACH_PROP_ELEM_SEP(DT_COMPAT_GET_ANY_STATUS_OKAY(openthread_config), + diag_gpios, GPIO_DT_SPEC_GET_BY_IDX, (,))}; + +static otError gpio_get_spec(uint32_t gpio_idx, const struct gpio_dt_spec **spec) +{ + if (gpio_idx >= ARRAY_SIZE(gpio_spec)) { + return OT_ERROR_INVALID_ARGS; + } + + *spec = &gpio_spec[gpio_idx]; + + if (!gpio_is_ready_dt(*spec)) { + return OT_ERROR_INVALID_ARGS; + } + + const struct gpio_driver_config *const cfg = + (const struct gpio_driver_config *)((*spec)->port->config); + + if ((cfg->port_pin_mask & (gpio_port_pins_t)BIT((*spec)->pin)) == 0U) { + return OT_ERROR_INVALID_ARGS; + } + + return OT_ERROR_NONE; +} + +otError otPlatDiagGpioSet(uint32_t aGpio, bool aValue) +{ + const struct gpio_dt_spec *spec; + otError error; + + error = gpio_get_spec(aGpio, &spec); + + if (error != OT_ERROR_NONE) { + return error; + } + +#if defined(CONFIG_GPIO_GET_DIRECTION) + if (gpio_pin_is_output_dt(spec) != 1) { + return OT_ERROR_INVALID_STATE; + } +#endif + + if (gpio_pin_set_dt(spec, (int)aValue) != 0) { + return OT_ERROR_FAILED; + } + + return OT_ERROR_NONE; +} + +otError otPlatDiagGpioGet(uint32_t aGpio, bool *aValue) +{ + const struct gpio_dt_spec *spec; + otError error; + int rv; + + error = gpio_get_spec(aGpio, &spec); + + if (error != OT_ERROR_NONE) { + return error; + } + + if (aValue == NULL) { + return OT_ERROR_INVALID_ARGS; + } + +#if defined(CONFIG_GPIO_GET_DIRECTION) + if (gpio_pin_is_input_dt(spec) != 1) { + return OT_ERROR_INVALID_STATE; + } +#endif + + rv = gpio_pin_get_dt(spec); + if (rv < 0) { + return OT_ERROR_FAILED; + } + *aValue = (bool)rv; + + return OT_ERROR_NONE; +} + +otError otPlatDiagGpioSetMode(uint32_t aGpio, otGpioMode aMode) +{ + const struct gpio_dt_spec *spec; + otError error; + int rv = 0; + + error = gpio_get_spec(aGpio, &spec); + + if (error != OT_ERROR_NONE) { + return error; + } + + switch (aMode) { + case OT_GPIO_MODE_INPUT: + rv = gpio_pin_configure_dt(spec, GPIO_INPUT); + break; + + case OT_GPIO_MODE_OUTPUT: + rv = gpio_pin_configure_dt(spec, GPIO_OUTPUT); + break; + + default: + return OT_ERROR_INVALID_ARGS; + } + + if (rv != 0) { + return OT_ERROR_FAILED; + } + + return OT_ERROR_NONE; +} + +#if defined(CONFIG_GPIO_GET_DIRECTION) +otError otPlatDiagGpioGetMode(uint32_t aGpio, otGpioMode *aMode) +{ + const struct gpio_dt_spec *spec; + otError error; + gpio_port_pins_t pins_in, pins_out; + + error = gpio_get_spec(aGpio, &spec); + + if (error != OT_ERROR_NONE) { + return error; + } + if (aMode == NULL) { + return OT_ERROR_INVALID_ARGS; + } + + if (gpio_port_get_direction(spec->port, BIT(spec->pin), &pins_in, &pins_out) < 0) { + return OT_ERROR_FAILED; + } + + if (((gpio_port_pins_t)BIT(spec->pin) & pins_in) != 0U) { + *aMode = OT_GPIO_MODE_INPUT; + } else if (((gpio_port_pins_t)BIT(spec->pin) & pins_out) != 0U) { + *aMode = OT_GPIO_MODE_OUTPUT; + } else { + return OT_ERROR_FAILED; + } + + return OT_ERROR_NONE; +} +#endif /* CONFIG_GPIO_GET_DIRECTION */ +#endif /* DT_HAS_COMPAT_STATUS_OKAY(openthread_config) && \ + * DT_NODE_HAS_PROP(DT_COMPAT_GET_ANY_STATUS_OKAY(openthread_config), diag_gpios) + */ + +#if defined(CONFIG_IEEE802154_CARRIER_FUNCTIONS) + +static otError startModCarrier(otInstance *aInstance, uint8_t aArgsLength, char *aArgs[]) +{ + bool enable = true; + uint8_t data[OT_RADIO_FRAME_MAX_SIZE + 1]; + + if (aArgsLength <= 0) { + return OT_ERROR_INVALID_ARGS; + } + + if (sTransmitMode != DIAG_TRANSMIT_MODE_IDLE && + sTransmitMode != DIAG_TRANSMIT_MODE_MODCARRIER) { + return OT_ERROR_INVALID_STATE; + } + + if (strcmp(aArgs[0], "stop") == 0) { + enable = false; + sTransmitMode = DIAG_TRANSMIT_MODE_IDLE; + } else { + if (hex2bin(aArgs[0], strlen(aArgs[0]), data, ARRAY_SIZE(data)) == 0) { + return OT_ERROR_INVALID_ARGS; + } + sTransmitMode = DIAG_TRANSMIT_MODE_MODCARRIER; + } + + return platformRadioTransmitModulatedCarrier(aInstance, enable, data); +} + +#endif + +void otPlatDiagAlarmCallback(otInstance *aInstance) +{ + uint32_t now; + otRadioFrame *txPacket; + const uint16_t diag_packet_len = 30; + + if (sTransmitMode == DIAG_TRANSMIT_MODE_PACKETS) { + if ((sTxCount > 0) || (sTxCount == -1)) { + txPacket = otPlatRadioGetTransmitBuffer(aInstance); + + txPacket->mInfo.mTxInfo.mTxDelayBaseTime = 0; + txPacket->mInfo.mTxInfo.mTxDelay = 0; + txPacket->mInfo.mTxInfo.mMaxCsmaBackoffs = 0; + txPacket->mInfo.mTxInfo.mMaxFrameRetries = 0; + txPacket->mInfo.mTxInfo.mRxChannelAfterTxDone = sChannel; + txPacket->mInfo.mTxInfo.mTxPower = OT_RADIO_POWER_INVALID; + txPacket->mInfo.mTxInfo.mIsHeaderUpdated = false; + txPacket->mInfo.mTxInfo.mIsARetx = false; + txPacket->mInfo.mTxInfo.mCsmaCaEnabled = false; + txPacket->mInfo.mTxInfo.mCslPresent = false; + txPacket->mInfo.mTxInfo.mIsSecurityProcessed = false; + + txPacket->mLength = diag_packet_len; + + for (uint8_t i = 0; i < diag_packet_len; i++) { + txPacket->mPsdu[i] = i; + } + + otPlatRadioTransmit(aInstance, txPacket); + + if (sTxCount != -1) { + sTxCount--; + } + + now = otPlatAlarmMilliGetNow(); + otPlatAlarmMilliStartAt(aInstance, now, sTxPeriod); + } else { + sTransmitMode = DIAG_TRANSMIT_MODE_IDLE; + otPlatAlarmMilliStop(aInstance); + otPlatLog(OT_LOG_LEVEL_DEBG, OT_LOG_REGION_PLATFORM, "Transmit done"); + } + } +} + +static otError processTransmit(otInstance *aInstance, uint8_t aArgsLength, char *aArgs[]) +{ + otError error = OT_ERROR_NONE; + long value; + uint32_t now; + + if (aArgsLength == 0) { + diag_output("transmit will send %" PRId32 " diagnostic messages with %" PRIu32 + " ms interval\r\n", + sTxRequestedCount, sTxPeriod); + + } else if (strcmp(aArgs[0], "stop") == 0) { + if (sTransmitMode == DIAG_TRANSMIT_MODE_IDLE) { + return OT_ERROR_INVALID_STATE; + } + + otPlatAlarmMilliStop(aInstance); + diag_output("diagnostic message transmission is stopped\r\n"); + sTransmitMode = DIAG_TRANSMIT_MODE_IDLE; + otPlatRadioReceive(aInstance, sChannel); + + } else if (strcmp(aArgs[0], "start") == 0) { + if (sTransmitMode != DIAG_TRANSMIT_MODE_IDLE) { + return OT_ERROR_INVALID_STATE; + } + + otPlatAlarmMilliStop(aInstance); + sTransmitMode = DIAG_TRANSMIT_MODE_PACKETS; + sTxCount = sTxRequestedCount; + now = otPlatAlarmMilliGetNow(); + otPlatAlarmMilliStartAt(aInstance, now, sTxPeriod); + diag_output("sending %" PRId32 " diagnostic messages with %" PRIu32 + " ms interval\r\n", + sTxRequestedCount, sTxPeriod); + } else if (strcmp(aArgs[0], "interval") == 0) { + + if (aArgsLength != 2) { + return OT_ERROR_INVALID_ARGS; + } + + error = parse_long(aArgs[1], &value); + if (error != OT_ERROR_NONE) { + return error; + } + + if (value <= 0) { + return OT_ERROR_INVALID_ARGS; + } + sTxPeriod = (uint32_t)(value); + diag_output("set diagnostic messages interval to %" PRIu32 + " ms\r\n", sTxPeriod); + + } else if (strcmp(aArgs[0], "count") == 0) { + + if (aArgsLength != 2) { + return OT_ERROR_INVALID_ARGS; + } + + error = parse_long(aArgs[1], &value); + if (error != OT_ERROR_NONE) { + return error; + } + + if ((value <= 0) && (value != -1)) { + return OT_ERROR_INVALID_ARGS; + } + + sTxRequestedCount = (uint32_t)(value); + diag_output("set diagnostic messages count to %" PRId32 "\r\n", + sTxRequestedCount); + } else { + return OT_ERROR_INVALID_ARGS; + } + + return error; +} diff --git a/modules/openthread/platform/entropy.c b/modules/openthread/platform/entropy.c new file mode 100644 index 000000000000..a52f885e3063 --- /dev/null +++ b/modules/openthread/platform/entropy.c @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include <zephyr/kernel.h> +#include <zephyr/random/random.h> +#include <zephyr/logging/log.h> + +#include <openthread/platform/entropy.h> + +LOG_MODULE_REGISTER(net_otPlat_entropy, CONFIG_OPENTHREAD_L2_LOG_LEVEL); + +#if !defined(CONFIG_CSPRNG_ENABLED) +#error OpenThread requires an entropy source for a TRNG +#endif + +otError otPlatEntropyGet(uint8_t *aOutput, uint16_t aOutputLength) +{ + int err; + + if ((aOutput == NULL) || (aOutputLength == 0)) { + return OT_ERROR_INVALID_ARGS; + } + + err = sys_csrand_get(aOutput, aOutputLength); + if (err != 0) { + LOG_ERR("Failed to obtain entropy, err %d", err); + return OT_ERROR_FAILED; + } + + return OT_ERROR_NONE; +} diff --git a/modules/openthread/platform/logging.c b/modules/openthread/platform/logging.c new file mode 100644 index 000000000000..8adf98de053c --- /dev/null +++ b/modules/openthread/platform/logging.c @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2018 - 2020 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include <zephyr/kernel.h> +#include <stdarg.h> +#include <stdio.h> + +#include <openthread/platform/logging.h> +#include "openthread-core-zephyr-config.h" + +#define LOG_MODULE_NAME net_openthread +#define LOG_LEVEL LOG_LEVEL_DBG +#include <zephyr/logging/log.h> +LOG_MODULE_REGISTER(LOG_MODULE_NAME); + +#include "platform-zephyr.h" + +/* Convert OT log level to zephyr log level. */ +static inline int log_translate(otLogLevel aLogLevel) +{ + switch (aLogLevel) { + case OT_LOG_LEVEL_NONE: + case OT_LOG_LEVEL_CRIT: + return LOG_LEVEL_ERR; + case OT_LOG_LEVEL_WARN: + return LOG_LEVEL_WRN; + case OT_LOG_LEVEL_NOTE: + case OT_LOG_LEVEL_INFO: + return LOG_LEVEL_INF; + case OT_LOG_LEVEL_DEBG: + return LOG_LEVEL_DBG; + default: + break; + } + + return -1; +} + +void otPlatLog(otLogLevel aLogLevel, otLogRegion aLogRegion, const char *aFormat, ...) +{ + ARG_UNUSED(aLogRegion); + +#if defined(CONFIG_LOG) + int level = log_translate(aLogLevel); + va_list param_list; + + if (level < 0) { + return; + } + + va_start(param_list, aFormat); + log_generic(level, aFormat, param_list); + va_end(param_list); +#else + ARG_UNUSED(aLogLevel); + ARG_UNUSED(aFormat); +#endif + +} diff --git a/modules/openthread/platform/memory.c b/modules/openthread/platform/memory.c new file mode 100644 index 000000000000..acfc72d0cd39 --- /dev/null +++ b/modules/openthread/platform/memory.c @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2021 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include <zephyr/kernel.h> + +#include <openthread/platform/memory.h> + +#include <stdlib.h> + +void *otPlatCAlloc(size_t aNum, size_t aSize) +{ + return calloc(aNum, aSize); +} + +void otPlatFree(void *aPtr) +{ + free(aPtr); +} diff --git a/modules/openthread/platform/messagepool.c b/modules/openthread/platform/messagepool.c new file mode 100644 index 000000000000..c085aeb9a582 --- /dev/null +++ b/modules/openthread/platform/messagepool.c @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2024 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include <zephyr/kernel.h> +#include <zephyr/logging/log.h> + +#include <openthread/platform/messagepool.h> + +#define LOG_MODULE_NAME net_otPlat_messagepool +#define LOG_LEVEL CONFIG_OPENTHREAD_PLATFORM_LOG_LEVEL + +LOG_MODULE_REGISTER(LOG_MODULE_NAME); + +#define BUF_TIMEOUT K_MSEC(50) + +#define MESSAGE_POOL_SIZE \ + (CONFIG_OPENTHREAD_NUM_MESSAGE_BUFFERS * CONFIG_OPENTHREAD_MESSAGE_BUFFER_SIZE) +#define MAX_ALIGNMENT __alignof__(z_max_align_t) + +BUILD_ASSERT(CONFIG_OPENTHREAD_MESSAGE_BUFFER_SIZE % MAX_ALIGNMENT == 0, + "Invalid message buffer size"); + +static struct k_mem_slab message_pool; +__aligned(MAX_ALIGNMENT) static uint8_t message_pool_buffer[MESSAGE_POOL_SIZE]; + +void otPlatMessagePoolInit(otInstance *aInstance, uint16_t aMinNumFreeBuffers, size_t aBufferSize) +{ + ARG_UNUSED(aInstance); + + __ASSERT(aBufferSize == CONFIG_OPENTHREAD_MESSAGE_BUFFER_SIZE, + "Message buffer size does not match configuration"); + + if (aMinNumFreeBuffers > CONFIG_OPENTHREAD_NUM_MESSAGE_BUFFERS) { + LOG_WRN("Minimum number of free buffers (%d) is greater than number of allocated " + "buffers (%d)", + aMinNumFreeBuffers, CONFIG_OPENTHREAD_NUM_MESSAGE_BUFFERS); + } + + if (k_mem_slab_init(&message_pool, message_pool_buffer, aBufferSize, + CONFIG_OPENTHREAD_NUM_MESSAGE_BUFFERS) != 0) { + __ASSERT(false, "Failed to initialize message pool"); + } +} + +otMessageBuffer *otPlatMessagePoolNew(otInstance *aInstance) +{ + ARG_UNUSED(aInstance); + + otMessageBuffer *buffer; + + if (k_mem_slab_alloc(&message_pool, (void **)&buffer, BUF_TIMEOUT) != 0) { + LOG_ERR("Failed to allocate message buffer"); + return NULL; + } + + buffer->mNext = NULL; + return buffer; +} + +void otPlatMessagePoolFree(otInstance *aInstance, otMessageBuffer *aBuffer) +{ + ARG_UNUSED(aInstance); + + k_mem_slab_free(&message_pool, (void *)aBuffer); +} diff --git a/modules/openthread/platform/misc.c b/modules/openthread/platform/misc.c new file mode 100644 index 000000000000..5f9043dfa27b --- /dev/null +++ b/modules/openthread/platform/misc.c @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include <zephyr/kernel.h> +#include <zephyr/sys/reboot.h> +#include <openthread/instance.h> +#include <openthread/platform/misc.h> + +#if defined(CONFIG_OPENTHREAD_PLATFORM_BOOTLOADER_MODE_RETENTION) + +#include <zephyr/retention/bootmode.h> + +#elif defined(CONFIG_OPENTHREAD_PLATFORM_BOOTLOADER_MODE_GPIO) + +BUILD_ASSERT(DT_HAS_COMPAT_STATUS_OKAY(openthread_config), + "`openthread,config` compatible node not found"); +BUILD_ASSERT(DT_NODE_HAS_PROP(DT_COMPAT_GET_ANY_STATUS_OKAY(openthread_config), bootloader_gpios), + "`bootloader-gpios` property missing from `openthread,config` compatible node"); + +#include <zephyr/drivers/gpio.h> + +static const struct gpio_dt_spec bootloader_gpio = + GPIO_DT_SPEC_GET(DT_COMPAT_GET_ANY_STATUS_OKAY(openthread_config), + bootloader_gpios); +#endif + +#include "platform-zephyr.h" + +void otPlatReset(otInstance *aInstance) +{ + ARG_UNUSED(aInstance); + + /* This function does nothing on the Posix platform. */ + sys_reboot(SYS_REBOOT_WARM); +} + +#if defined(CONFIG_OPENTHREAD_PLATFORM_BOOTLOADER_MODE) +otError otPlatResetToBootloader(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + +#if defined(CONFIG_OPENTHREAD_PLATFORM_BOOTLOADER_MODE_RETENTION) + if (bootmode_set(BOOT_MODE_TYPE_BOOTLOADER)) { + return OT_ERROR_NOT_CAPABLE; + } + sys_reboot(SYS_REBOOT_WARM); + +#elif defined(CONFIG_OPENTHREAD_PLATFORM_BOOTLOADER_MODE_GPIO) + /* + * To enable resetting to bootloader by triggering gpio pin, + * select `CONFIG_OPENTHREAD_PLATFORM_BOOTLOADER_MODE_GPIO=y`, + * and in Devicetree create `openthread` node in `/options/` path with + * `compatible = "openthread,config"` property and `bootloader-gpios` property, + * which should represent GPIO pin's configuration, + * containing controller phandle, pin number and pin flags. e.g: + * + * options { + * openthread { + * compatible = "openthread,config"; + * bootloader-gpios = <&gpio0 0 GPIO_ACTIVE_HIGH>; + * }; + * }; + * + * Note: in below implementation, chosen GPIO pin is configured as output + * and initialized to active state (logical value ‘1’). + * Configuring pin flags in `bootloader-gpios` allows to choose + * if pin should be active in high or in low state. + */ + + if (!gpio_is_ready_dt(&bootloader_gpio)) { + return OT_ERROR_NOT_CAPABLE; + } + gpio_pin_configure_dt(&bootloader_gpio, GPIO_OUTPUT_ACTIVE); + +#endif + + /* + * Return OT_ERROR_NOT_CAPABLE if resetting has been unsuccessful (invalid configuration or + * triggering reset had no effect) + */ + return OT_ERROR_NOT_CAPABLE; +} +#endif /* defined(CONFIG_OPENTHREAD_PLATFORM_BOOTLOADER_MODE) */ + +otPlatResetReason otPlatGetResetReason(otInstance *aInstance) +{ + ARG_UNUSED(aInstance); + + return OT_PLAT_RESET_REASON_POWER_ON; +} + +void otPlatWakeHost(void) +{ + /* TODO */ +} + +void otPlatAssertFail(const char *aFilename, int aLineNumber) +{ + /* + * The code below is used instead of __ASSERT(false) to print the actual assert + * location instead of __FILE__:__LINE__, which would point to this function. + */ + __ASSERT_PRINT("OpenThread ASSERT @ %s:%d\n", aFilename, aLineNumber); + __ASSERT_POST_ACTION(); +} diff --git a/modules/openthread/platform/openthread-core-zephyr-config.h b/modules/openthread/platform/openthread-core-zephyr-config.h new file mode 100644 index 000000000000..dc2e46b1e701 --- /dev/null +++ b/modules/openthread/platform/openthread-core-zephyr-config.h @@ -0,0 +1,513 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * This file includes Zephyr compile-time configuration constants + * for OpenThread. + */ + +#ifndef OPENTHREAD_CORE_ZEPHYR_CONFIG_H_ +#define OPENTHREAD_CORE_ZEPHYR_CONFIG_H_ + +#include <zephyr/devicetree.h> +#include <zephyr/toolchain.h> + +/** + * @def OPENTHREAD_CONFIG_PLATFORM_ASSERT_MANAGEMENT + * + * The assert is managed by platform defined logic when this flag is set. + * + */ +#ifndef OPENTHREAD_CONFIG_PLATFORM_ASSERT_MANAGEMENT +#define OPENTHREAD_CONFIG_PLATFORM_ASSERT_MANAGEMENT 1 +#endif + +/** + * @def OPENTHREAD_CONFIG_NUM_MESSAGE_BUFFERS + * + * The number of message buffers in the buffer pool. + * + */ +#ifdef CONFIG_OPENTHREAD_NUM_MESSAGE_BUFFERS +#define OPENTHREAD_CONFIG_NUM_MESSAGE_BUFFERS \ + CONFIG_OPENTHREAD_NUM_MESSAGE_BUFFERS +#endif + +/** + * @def OPENTHREAD_CONFIG_MAX_STATECHANGE_HANDLERS + * + * The maximum number of state-changed callback handlers + * (set using `otSetStateChangedCallback()`). + * + */ +#ifdef CONFIG_OPENTHREAD_MAX_STATECHANGE_HANDLERS +#define OPENTHREAD_CONFIG_MAX_STATECHANGE_HANDLERS \ + CONFIG_OPENTHREAD_MAX_STATECHANGE_HANDLERS +#endif + +/** + * @def OPENTHREAD_CONFIG_TMF_ADDRESS_CACHE_ENTRIES + * + * The number of EID-to-RLOC cache entries. + * + */ +#ifdef CONFIG_OPENTHREAD_TMF_ADDRESS_CACHE_ENTRIES +#define OPENTHREAD_CONFIG_TMF_ADDRESS_CACHE_ENTRIES \ + CONFIG_OPENTHREAD_TMF_ADDRESS_CACHE_ENTRIES +#endif + +/** + * @def CONFIG_OPENTHREAD_TMF_ADDRESS_CACHE_MAX_SNOOP_ENTRIES + * + * The maximum number of EID-to-RLOC cache entries that can be used for + * "snoop optimization" where an entry is created by inspecting a received message. + * + */ +#ifdef CONFIG_OPENTHREAD_TMF_ADDRESS_CACHE_MAX_SNOOP_ENTRIES +#define OPENTHREAD_CONFIG_TMF_ADDRESS_CACHE_MAX_SNOOP_ENTRIES \ + CONFIG_OPENTHREAD_TMF_ADDRESS_CACHE_MAX_SNOOP_ENTRIES +#endif + +/** + * @def OPENTHREAD_CONFIG_LOG_PREPEND_LEVEL + * + * Define to prepend the log level to all log messages. + * + */ +#ifdef CONFIG_OPENTHREAD_LOG_PREPEND_LEVEL_ENABLE +#define OPENTHREAD_CONFIG_LOG_PREPEND_LEVEL 1 +#endif + +/** + * @def OPENTHREAD_CONFIG_MAC_SOFTWARE_ACK_TIMEOUT_ENABLE + * + * Define to 1 to enable software ACK timeout logic. + * + */ +#ifdef CONFIG_OPENTHREAD_MAC_SOFTWARE_ACK_TIMEOUT_ENABLE +#define OPENTHREAD_CONFIG_MAC_SOFTWARE_ACK_TIMEOUT_ENABLE 1 +#endif + +/** + * @def OPENTHREAD_CONFIG_MAC_SOFTWARE_RETRANSMIT_ENABLE + * + * Define to 1 to enable software retransmission logic. + * + */ +#ifdef CONFIG_OPENTHREAD_MAC_SOFTWARE_RETRANSMIT_ENABLE +#define OPENTHREAD_CONFIG_MAC_SOFTWARE_RETRANSMIT_ENABLE 1 +#endif + +/** + * @def OPENTHREAD_CONFIG_MAC_SOFTWARE_CSMA_BACKOFF_ENABLE + * + * Define to 1 if you want to enable software CSMA-CA backoff logic. + * + */ +#ifdef CONFIG_OPENTHREAD_MAC_SOFTWARE_CSMA_BACKOFF_ENABLE +#define OPENTHREAD_CONFIG_MAC_SOFTWARE_CSMA_BACKOFF_ENABLE 1 +#endif + +/** + * @def OPENTHREAD_CONFIG_MLE_INFORM_PREVIOUS_PARENT_ON_REATTACH + * + * Define as 1 for a child to inform its previous parent when it attaches to a new parent. + * + */ +#ifdef CONFIG_OPENTHREAD_MLE_INFORM_PREVIOUS_PARENT_ON_REATTACH +#define OPENTHREAD_CONFIG_MLE_INFORM_PREVIOUS_PARENT_ON_REATTACH 1 +#endif + +/** + * @def OPENTHREAD_CONFIG_PARENT_SEARCH_ENABLE + * + * Define as 1 to enable periodic parent search feature. + * + */ +#ifdef CONFIG_OPENTHREAD_PARENT_SEARCH +#define OPENTHREAD_CONFIG_PARENT_SEARCH_ENABLE 1 + +/** + * @def OPENTHREAD_CONFIG_PARENT_SEARCH_CHECK_INTERVAL + * + * Specifies the interval in seconds for a child to check the trigger condition + * to perform a parent search. + * + */ +#define OPENTHREAD_CONFIG_PARENT_SEARCH_CHECK_INTERVAL \ + CONFIG_OPENTHREAD_PARENT_SEARCH_CHECK_INTERVAL + +/** + * @def OPENTHREAD_CONFIG_PARENT_SEARCH_BACKOFF_INTERVAL + * + * Specifies the backoff interval in seconds for a child to not perform a parent + * search after triggering one. + * + */ +#define OPENTHREAD_CONFIG_PARENT_SEARCH_BACKOFF_INTERVAL \ + CONFIG_OPENTHREAD_PARENT_SEARCH_BACKOFF_INTERVAL + +/** + * @def OPENTHREAD_CONFIG_PARENT_SEARCH_RSS_THRESHOLD + * + * Specifies the RSS threshold used to trigger a parent search. + * + */ +#define OPENTHREAD_CONFIG_PARENT_SEARCH_RSS_THRESHOLD \ + CONFIG_OPENTHREAD_PARENT_SEARCH_RSS_THRESHOLD +#endif + +/** + * @def OPENTHREAD_CONFIG_MAC_SOFTWARE_TX_TIMING_ENABLE + * + * Define to 1 to enable software transmission target time logic. + * + */ +#ifndef OPENTHREAD_CONFIG_MAC_SOFTWARE_TX_TIMING_ENABLE +#define OPENTHREAD_CONFIG_MAC_SOFTWARE_TX_TIMING_ENABLE \ + (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) +#endif + +/** + * @def OPENTHREAD_CONFIG_MAC_SOFTWARE_RX_TIMING_ENABLE + * + * Define to 1 to enable software reception target time logic. + * + */ +#ifndef OPENTHREAD_CONFIG_MAC_SOFTWARE_RX_TIMING_ENABLE +#define OPENTHREAD_CONFIG_MAC_SOFTWARE_RX_TIMING_ENABLE \ + (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) +#endif + +/** + * @def OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT + * + * Define as 1 to support IEEE 802.15.4-2015 Header IE (Information Element) generation and parsing, + * it must be set to support following features: + * 1. Time synchronization service feature (i.e., OPENTHREAD_CONFIG_TIME_SYNC_ENABLE is set). + * 2. Thread 1.2. + * + * @note If it's enabled, platform must support interrupt context and concurrent access AES. + * + */ +#ifndef OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT +#if OPENTHREAD_CONFIG_TIME_SYNC_ENABLE || \ + (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2) +#define OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT 1 +#else +#define OPENTHREAD_CONFIG_MAC_HEADER_IE_SUPPORT 0 +#endif +#endif + +/** + * @def OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE + * + * Define to 1 if you want to enable microsecond backoff timer implemented + * in platform. + * + */ +#define OPENTHREAD_CONFIG_PLATFORM_USEC_TIMER_ENABLE \ + ((CONFIG_OPENTHREAD_CSL_RECEIVER && \ + (OPENTHREAD_CONFIG_THREAD_VERSION >= OT_THREAD_VERSION_1_2)) || \ + CONFIG_OPENTHREAD_WAKEUP_END_DEVICE) + +/* Zephyr does not use OpenThread's heap. mbedTLS will use heap memory allocated + * by Zephyr. Here, we use some dummy values to prevent OpenThread warnings. + */ + +/** + * @def OPENTHREAD_CONFIG_HEAP_SIZE + * + * The size of heap buffer when DTLS is enabled. + * + */ +#define OPENTHREAD_CONFIG_HEAP_INTERNAL_SIZE (4 * sizeof(void *)) + +/** + * @def OPENTHREAD_CONFIG_HEAP_SIZE_NO_DTLS + * + * The size of heap buffer when DTLS is disabled. + * + */ +#define OPENTHREAD_CONFIG_HEAP_INTERNAL_SIZE_NO_DTLS (4 * sizeof(void *)) + +/* Disable software source address matching. */ + +/** + * @def RADIO_CONFIG_SRC_MATCH_SHORT_ENTRY_NUM + * + * The number of short source address table entries. + * + */ +#define RADIO_CONFIG_SRC_MATCH_SHORT_ENTRY_NUM 0 + +/** + * @def OPENTHREAD_CONFIG_PLATFORM_INFO + * + * The platform-specific string to insert into the OpenThread version string. + * + */ +#ifdef CONFIG_OPENTHREAD_CONFIG_PLATFORM_INFO +#define OPENTHREAD_CONFIG_PLATFORM_INFO CONFIG_OPENTHREAD_CONFIG_PLATFORM_INFO +#endif /* CONFIG_OPENTHREAD_CONFIG_PLATFORM_INFO */ + +/** + * @def OPENTHREAD_CONFIG_MLE_MAX_CHILDREN + * + * The maximum number of children. + * + */ +#ifdef CONFIG_OPENTHREAD_MAX_CHILDREN +#define OPENTHREAD_CONFIG_MLE_MAX_CHILDREN CONFIG_OPENTHREAD_MAX_CHILDREN +#endif /* CONFIG_OPENTHREAD_MAX_CHILDREN */ + +/** + * @def OPENTHREAD_CONFIG_MLE_IP_ADDRS_PER_CHILD + * + * The maximum number of supported IPv6 address registrations per child. + * + */ +#ifdef CONFIG_OPENTHREAD_MAX_IP_ADDR_PER_CHILD +#define OPENTHREAD_CONFIG_MLE_IP_ADDRS_PER_CHILD \ + CONFIG_OPENTHREAD_MAX_IP_ADDR_PER_CHILD +#endif /* CONFIG_OPENTHREAD_MAX_IP_ADDR_PER_CHILD */ + +/** + * @def RADIO_CONFIG_SRC_MATCH_EXT_ENTRY_NUM + * + * The number of extended source address table entries. + * + */ +#define RADIO_CONFIG_SRC_MATCH_EXT_ENTRY_NUM 0 + +/** + * @def OPENTHREAD_CONFIG_MAC_CSL_REQUEST_AHEAD_US + * + * Define how many microseconds ahead should MAC deliver CSL frame to SubMac. + * + */ +#ifdef CONFIG_OPENTHREAD_CSL_REQUEST_TIME_AHEAD +#define OPENTHREAD_CONFIG_MAC_CSL_REQUEST_AHEAD_US CONFIG_OPENTHREAD_CSL_REQUEST_TIME_AHEAD +#endif /* CONFIG_OPENTHREAD_CSL_REQUEST_TIME_AHEAD */ + +/** + * @def OPENTHREAD_CONFIG_CSL_RECEIVE_TIME_AHEAD + * + * For some reasons, CSL receivers wake up a little later than expected. This + * variable specifies how much time that CSL receiver would wake up earlier + * than the expected sample window. The time is in unit of microseconds. + * + */ +#ifdef CONFIG_OPENTHREAD_CSL_RECEIVE_TIME_AHEAD +#define OPENTHREAD_CONFIG_CSL_RECEIVE_TIME_AHEAD \ + CONFIG_OPENTHREAD_CSL_RECEIVE_TIME_AHEAD +#endif /* CONFIG_OPENTHREAD_CSL_RECEIVE_TIME_AHEAD */ + +/** + * @def OPENTHREAD_CONFIG_MIN_RECEIVE_ON_AHEAD + * + * The minimum time (microseconds) that radio has to be in receive mode before the start of the MHR. + * + */ +#ifdef CONFIG_OPENTHREAD_MIN_RECEIVE_ON_AHEAD +#define OPENTHREAD_CONFIG_MIN_RECEIVE_ON_AHEAD CONFIG_OPENTHREAD_MIN_RECEIVE_ON_AHEAD +#endif /* CONFIG_OPENTHREAD_MIN_RECEIVE_ON_AHEAD */ + +/** + * @def OPENTHREAD_CONFIG_MIN_RECEIVE_ON_AFTER + * + * The minimum time (microseconds) that radio has to be in receive mode after the start of the MHR . + * + */ +#ifdef CONFIG_OPENTHREAD_MIN_RECEIVE_ON_AFTER +#define OPENTHREAD_CONFIG_MIN_RECEIVE_ON_AFTER CONFIG_OPENTHREAD_MIN_RECEIVE_ON_AFTER +#endif /* CONFIG_OPENTHREAD_MIN_RECEIVE_ON_AFTER */ + +/** + * @def OPENTHREAD_CONFIG_CSL_TIMEOUT + * + * The default CSL timeout in seconds. + * + */ +#ifdef CONFIG_OPENTHREAD_CSL_TIMEOUT +#define OPENTHREAD_CONFIG_CSL_TIMEOUT CONFIG_OPENTHREAD_CSL_TIMEOUT +#endif /* CONFIG_OPENTHREAD_CSL_TIMEOUT */ + +/** + * @def OPENTHREAD_CONFIG_MAC_SOFTWARE_TX_SECURITY_ENABLE + * + * Set to 1 to enable software transmission security logic. + * + */ +#ifdef CONFIG_OPENTHREAD_MAC_SOFTWARE_TX_SECURITY_ENABLE +#define OPENTHREAD_CONFIG_MAC_SOFTWARE_TX_SECURITY_ENABLE \ + CONFIG_OPENTHREAD_MAC_SOFTWARE_TX_SECURITY_ENABLE +#endif /* CONFIG_OPENTHREAD_MAC_SOFTWARE_TX_SECURITY_ENABLE */ + +/** + * @def OPENTHREAD_CONFIG_CLI_MAX_LINE_LENGTH + * + * The maximum size of the CLI line in bytes. + * + */ +#ifdef CONFIG_OPENTHREAD_CLI_MAX_LINE_LENGTH +#define OPENTHREAD_CONFIG_CLI_MAX_LINE_LENGTH CONFIG_OPENTHREAD_CLI_MAX_LINE_LENGTH +#endif /* CONFIG_OPENTHREAD_CLI_MAX_LINE_LENGTH */ + +/** + * @def OPENTHREAD_CONFIG_CLI_PROMPT_ENABLE + * + * Enable CLI prompt. + * + * When enabled, the CLI will print prompt on the output after processing a command. + * Otherwise, no prompt is added to the output. + * + */ +#define OPENTHREAD_CONFIG_CLI_PROMPT_ENABLE 0 + +/** + * @def OPENTHREAD_CONFIG_IP6_MAX_EXT_UCAST_ADDRS + * + * The maximum number of supported IPv6 addresses allows to be externally added. + * + */ +#ifdef CONFIG_OPENTHREAD_IP6_MAX_EXT_UCAST_ADDRS +#define OPENTHREAD_CONFIG_IP6_MAX_EXT_UCAST_ADDRS CONFIG_OPENTHREAD_IP6_MAX_EXT_UCAST_ADDRS +#endif /* CONFIG_OPENTHREAD_IP6_MAX_EXT_UCAST_ADDRS */ + +/** + * @def OPENTHREAD_CONFIG_IP6_MAX_EXT_MCAST_ADDRS + * + * The maximum number of supported IPv6 multicast addresses allows to be externally added. + * + */ +#ifdef CONFIG_OPENTHREAD_IP6_MAX_EXT_MCAST_ADDRS +#define OPENTHREAD_CONFIG_IP6_MAX_EXT_MCAST_ADDRS CONFIG_OPENTHREAD_IP6_MAX_EXT_MCAST_ADDRS +#endif /* CONFIG_OPENTHREAD_IP6_MAX_EXT_MCAST_ADDRS */ + +/** + * @def OPENTHREAD_CONFIG_CLI_TCP_ENABLE + * + * Enable TCP in the CLI tool. + * + */ +#define OPENTHREAD_CONFIG_CLI_TCP_ENABLE IS_ENABLED(CONFIG_OPENTHREAD_CLI_TCP_ENABLE) + +/** + * @def OPENTHREAD_CONFIG_CRYPTO_LIB + * + * Selects crypto backend library for OpenThread. + * + */ +#ifdef CONFIG_OPENTHREAD_CRYPTO_PSA +#define OPENTHREAD_CONFIG_CRYPTO_LIB OPENTHREAD_CONFIG_CRYPTO_LIB_PSA +#endif + +/** + * @def OPENTHREAD_CONFIG_PLATFORM_MAC_KEYS_EXPORTABLE_ENABLE + * + * Set to 1 if you want to make MAC keys exportable. + * + */ +#ifdef CONFIG_OPENTHREAD_PLATFORM_KEYS_EXPORTABLE_ENABLE +#define OPENTHREAD_CONFIG_PLATFORM_MAC_KEYS_EXPORTABLE_ENABLE 1 +#endif + +/** + * @def OPENTHREAD_CONFIG_MESSAGE_BUFFER_SIZE + * + * The size of a message buffer in bytes. + * + */ +#ifdef CONFIG_OPENTHREAD_MESSAGE_BUFFER_SIZE +#define OPENTHREAD_CONFIG_MESSAGE_BUFFER_SIZE CONFIG_OPENTHREAD_MESSAGE_BUFFER_SIZE +#endif + +/** + * @def OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT + * + * The message pool is managed by platform defined logic. + * + */ +#ifdef CONFIG_OPENTHREAD_PLATFORM_MESSAGE_MANAGEMENT +#define OPENTHREAD_CONFIG_PLATFORM_MESSAGE_MANAGEMENT CONFIG_OPENTHREAD_PLATFORM_MESSAGE_MANAGEMENT +#endif + +/** + * @def OPENTHREAD_CONFIG_MAC_STAY_AWAKE_BETWEEN_FRAGMENTS + * + * Enable to stay awake between fragments while transmitting a large packet, + * and to stay awake after receiving a packet with frame pending set to true. + * + */ +#ifdef CONFIG_OPENTHREAD_MAC_STAY_AWAKE_BETWEEN_FRAGMENTS +#define OPENTHREAD_CONFIG_MAC_STAY_AWAKE_BETWEEN_FRAGMENTS \ + CONFIG_OPENTHREAD_MAC_STAY_AWAKE_BETWEEN_FRAGMENTS +#endif + +/** + * @def OPENTHREAD_CONFIG_POWER_CALIBRATION_ENABLE + * + * In Zephyr, power calibration is handled by Radio Driver, so it can't be handled on OT level. + * + */ +#ifndef OPENTHREAD_CONFIG_POWER_CALIBRATION_ENABLE +#define OPENTHREAD_CONFIG_POWER_CALIBRATION_ENABLE 0 +#endif + +/** + * @def OPENTHREAD_CONFIG_RADIO_STATS + * + * Enable support for Radio Statistics. + * + */ +#ifdef CONFIG_OPENTHREAD_RADIO_STATS +#define OPENTHREAD_CONFIG_RADIO_STATS_ENABLE CONFIG_OPENTHREAD_RADIO_STATS +#endif + +/** + * @def OPENTHREAD_CONFIG_STORE_FRAME_COUNTER_AHEAD + * + * The value ahead of the current frame counter for persistent storage. + * + */ +#ifdef CONFIG_OPENTHREAD_STORE_FRAME_COUNTER_AHEAD +#define OPENTHREAD_CONFIG_STORE_FRAME_COUNTER_AHEAD CONFIG_OPENTHREAD_STORE_FRAME_COUNTER_AHEAD +#endif + +/** + * @def OPENTHREAD_CONFIG_CHILD_SUPERVISION_CHECK_TIMEOUT + * + * The value of the child supervision check timeout in seconds. + * + */ +#ifdef CONFIG_OPENTHREAD_CHILD_SUPERVISION_CHECK_TIMEOUT +#define OPENTHREAD_CONFIG_CHILD_SUPERVISION_CHECK_TIMEOUT \ + CONFIG_OPENTHREAD_CHILD_SUPERVISION_CHECK_TIMEOUT +#endif + +/** + * @def OPENTHREAD_CONFIG_CHILD_SUPERVISION_INTERVAL + * + * The value of the child supervision interval in seconds. + * + */ +#ifdef CONFIG_OPENTHREAD_CHILD_SUPERVISION_INTERVAL +#define OPENTHREAD_CONFIG_CHILD_SUPERVISION_INTERVAL CONFIG_OPENTHREAD_CHILD_SUPERVISION_INTERVAL +#endif + +/** + * @def OPENTHREAD_CONFIG_MLE_CHILD_TIMEOUT_DEFAULT + * + * The value of the MLE child timeout in seconds. + * + */ +#ifdef CONFIG_OPENTHREAD_MLE_CHILD_TIMEOUT +#define OPENTHREAD_CONFIG_MLE_CHILD_TIMEOUT_DEFAULT CONFIG_OPENTHREAD_MLE_CHILD_TIMEOUT +#endif + +#endif /* OPENTHREAD_CORE_ZEPHYR_CONFIG_H_ */ diff --git a/modules/openthread/platform/platform-zephyr.h b/modules/openthread/platform/platform-zephyr.h new file mode 100644 index 000000000000..d6dd26adea04 --- /dev/null +++ b/modules/openthread/platform/platform-zephyr.h @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief + * This file includes the Zephyr platform-specific initializers. + */ + +#ifndef PLATFORM_ZEPHYR_H_ +#define PLATFORM_ZEPHYR_H_ + +#include <stdint.h> + +#include <openthread/instance.h> +#include <zephyr/net/net_pkt.h> + +/** + * This function initializes the alarm service used by OpenThread. + * + */ +void platformAlarmInit(void); + +/** + * This function performs alarm driver processing. + * + * @param[in] aInstance The OpenThread instance structure. + * + */ +void platformAlarmProcess(otInstance *aInstance); + +/** + * This function initializes the radio service used by OpenThread. + * + */ +void platformRadioInit(void); + +/** + * This function performs radio driver processing. + * + * @param[in] aInstance The OpenThread instance structure. + * + */ +void platformRadioProcess(otInstance *aInstance); + +/** + * This function performs UART driver processing. + * + * @param[in] aInstance The OpenThread instance structure. + * + */ +void platformUartProcess(otInstance *aInstance); + +/** + * Outer component calls this method to notify UART driver that it should + * switch to panic mode and work in synchronous way. + */ +void platformUartPanic(void); + +/** + * Get current channel from radio driver. + * + * @param[in] aInstance The OpenThread instance structure. + * + * @return Current channel radio driver operates on. + * + */ +uint16_t platformRadioChannelGet(otInstance *aInstance); + +#if defined(CONFIG_OPENTHREAD_DIAG) +/** + * Set channel on radio driver. + * + * @param[in] aChannel The channel that the radio driver should use for operation. + * + */ +void platformRadioChannelSet(uint8_t aChannel); +#endif /* CONFIG_OPENTHREAD_DIAG */ + +#if defined(CONFIG_IEEE802154_CARRIER_FUNCTIONS) +/** + * Start/stop continuous carrier wave transmission. + */ +otError platformRadioTransmitCarrier(otInstance *aInstance, bool aEnable); +#endif /* CONFIG_IEEE802154_CARRIER_FUNCTIONS */ + +#if defined(CONFIG_IEEE802154_CARRIER_FUNCTIONS) +/** + * Start/stop modulated carrier wave transmission. + */ +otError platformRadioTransmitModulatedCarrier(otInstance *aInstance, bool aEnable, + const uint8_t *aData); +#endif + +/** + * This function initializes the random number service used by OpenThread. + * + */ +void platformRandomInit(void); + +/** + * Initialize platform Shell driver. + */ +void platformShellInit(otInstance *aInstance); + + +/** + * Notify OpenThread task about new rx message. + */ +int notify_new_rx_frame(struct net_pkt *pkt); + +/** + * Notify OpenThread task about new tx message. + */ +int notify_new_tx_frame(struct net_pkt *pkt); + +#endif /* PLATFORM_POSIX_H_ */ diff --git a/modules/openthread/platform/platform.c b/modules/openthread/platform/platform.c new file mode 100644 index 000000000000..854cdf5c9b8b --- /dev/null +++ b/modules/openthread/platform/platform.c @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * @brief + * This file includes the platform-specific initializers. + */ + +#include <zephyr/kernel.h> +#include <openthread/instance.h> +#include <openthread/tasklet.h> + +#include "platform-zephyr.h" + +void otSysInit(int argc, char *argv[]) +{ + ARG_UNUSED(argc); + ARG_UNUSED(argv); + + platformRadioInit(); + platformAlarmInit(); +} + +void otSysProcessDrivers(otInstance *aInstance) +{ + platformRadioProcess(aInstance); + platformAlarmProcess(aInstance); + + if (IS_ENABLED(CONFIG_OPENTHREAD_COPROCESSOR)) { + platformUartProcess(aInstance); + } +} diff --git a/modules/openthread/platform/radio.c b/modules/openthread/platform/radio.c new file mode 100644 index 000000000000..5a0d8eb012b4 --- /dev/null +++ b/modules/openthread/platform/radio.c @@ -0,0 +1,1653 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/** + * @file + * This file implements the OpenThread platform abstraction + * for radio communication. + * + */ + +#include <openthread/error.h> +#define LOG_MODULE_NAME net_otPlat_radio + +#include <zephyr/logging/log.h> +LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_OPENTHREAD_L2_LOG_LEVEL); + +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> + +#include <zephyr/kernel.h> +#include <zephyr/device.h> +#include <zephyr/net/ieee802154_radio.h> +#include <zephyr/net/net_pkt.h> +#include <zephyr/net/net_time.h> +#include <zephyr/sys/__assert.h> + +#include <openthread/ip6.h> +#include <openthread-system.h> +#include <openthread/instance.h> +#include <openthread/platform/radio.h> +#include <openthread/platform/diag.h> +#include <openthread/platform/time.h> +#include <openthread/message.h> + +#include "platform-zephyr.h" + +#if defined(CONFIG_OPENTHREAD_NAT64_TRANSLATOR) +#include <openthread/nat64.h> +#endif + +#define PKT_IS_IPv6(_p) ((NET_IPV6_HDR(_p)->vtc & 0xf0) == 0x60) + +#define SHORT_ADDRESS_SIZE 2 + +#define FCS_SIZE 2 +#if defined(CONFIG_OPENTHREAD_THREAD_VERSION_1_1) +#define ACK_PKT_LENGTH 5 +#else +#define ACK_PKT_LENGTH 127 +#endif + +#define FRAME_TYPE_MASK 0x07 +#define FRAME_TYPE_ACK 0x02 + +#if defined(CONFIG_NET_TC_THREAD_COOPERATIVE) +#define OT_WORKER_PRIORITY K_PRIO_COOP(CONFIG_OPENTHREAD_THREAD_PRIORITY) +#else +#define OT_WORKER_PRIORITY K_PRIO_PREEMPT(CONFIG_OPENTHREAD_THREAD_PRIORITY) +#endif + +#define CHANNEL_COUNT OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MAX - OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MIN + 1 + +/* PHY header duration in us (i.e. 2 symbol periods @ 62.5k symbol rate), see + * IEEE 802.15.4, sections 12.1.3.1, 12.2.5 and 12.3.3. + */ +#define PHR_DURATION_US 32U + +enum pending_events { + PENDING_EVENT_FRAME_TO_SEND, /* There is a tx frame to send */ + PENDING_EVENT_FRAME_RECEIVED, /* Radio has received new frame */ + PENDING_EVENT_RX_FAILED, /* The RX failed */ + PENDING_EVENT_TX_STARTED, /* Radio has started transmitting */ + 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 */ +}; + +K_SEM_DEFINE(radio_sem, 0, 1); + +static otRadioState sState = OT_RADIO_STATE_DISABLED; + +static otRadioFrame sTransmitFrame; +static otRadioFrame ack_frame; +static uint8_t ack_psdu[ACK_PKT_LENGTH]; + +#if defined(CONFIG_OPENTHREAD_TIME_SYNC) +static otRadioIeInfo tx_ie_info; +#endif + +static struct net_pkt *tx_pkt; +static struct net_buf *tx_payload; + +static const struct device *const radio_dev = + DEVICE_DT_GET(DT_CHOSEN(zephyr_ieee802154)); +static struct ieee802154_radio_api *radio_api; + +/* Get the default tx output power from Kconfig */ +static int8_t tx_power = CONFIG_OPENTHREAD_DEFAULT_TX_POWER; +static uint16_t channel; +static bool promiscuous; + +static uint16_t energy_detection_time; +static uint8_t energy_detection_channel; +static int16_t energy_detected_value; + +static int8_t max_tx_power_table[CHANNEL_COUNT]; + +ATOMIC_DEFINE(pending_events, PENDING_EVENT_COUNT); +K_KERNEL_STACK_DEFINE(ot_task_stack, + CONFIG_OPENTHREAD_RADIO_WORKQUEUE_STACK_SIZE); +static struct k_work_q ot_work_q; +static otError rx_result; +static otError tx_result; + +K_FIFO_DEFINE(rx_pkt_fifo); +K_FIFO_DEFINE(tx_pkt_fifo); + +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 = + max_tx_power_table[aChannel - OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MIN]; + } + + if (tx_power != OT_RADIO_POWER_INVALID) { + power = (channel_max_power < tx_power) ? channel_max_power : tx_power; + } else if (channel_max_power != OT_RADIO_POWER_INVALID) { + power = channel_max_power; + } + + return power; +} + +static inline bool is_pending_event_set(enum pending_events event) +{ + return atomic_test_bit(pending_events, event); +} + +static void set_pending_event(enum pending_events event) +{ + atomic_set_bit(pending_events, event); + otSysEventSignalPending(); +} + +static void reset_pending_event(enum pending_events event) +{ + atomic_clear_bit(pending_events, event); +} + +static inline void clear_pending_events(void) +{ + atomic_clear(pending_events); +} + +void energy_detected(const struct device *dev, int16_t max_ed) +{ + if (dev == radio_dev) { + energy_detected_value = max_ed; + set_pending_event(PENDING_EVENT_DETECT_ENERGY_DONE); + } +} + +enum net_verdict ieee802154_handle_ack(struct net_if *iface, struct net_pkt *pkt) +{ + ARG_UNUSED(iface); + + size_t ack_len = net_pkt_get_len(pkt); + + if (ack_len > ACK_PKT_LENGTH) { + return NET_CONTINUE; + } + + if ((*net_pkt_data(pkt) & FRAME_TYPE_MASK) != FRAME_TYPE_ACK) { + return NET_CONTINUE; + } + + if (ack_frame.mLength != 0) { + LOG_ERR("Overwriting unhandled ACK frame."); + } + + if (net_pkt_read(pkt, ack_psdu, ack_len) < 0) { + LOG_ERR("Failed to read ACK frame."); + return NET_CONTINUE; + } + + ack_frame.mPsdu = ack_psdu; + ack_frame.mLength = ack_len; + ack_frame.mInfo.mRxInfo.mLqi = net_pkt_ieee802154_lqi(pkt); + ack_frame.mInfo.mRxInfo.mRssi = net_pkt_ieee802154_rssi_dbm(pkt); + +#if defined(CONFIG_NET_PKT_TIMESTAMP) + ack_frame.mInfo.mRxInfo.mTimestamp = net_pkt_timestamp_ns(pkt) / NSEC_PER_USEC; +#endif + + return NET_OK; +} + +void handle_radio_event(const struct device *dev, enum ieee802154_event evt, + void *event_params) +{ + ARG_UNUSED(event_params); + + switch (evt) { + case IEEE802154_EVENT_TX_STARTED: + if (sState == OT_RADIO_STATE_TRANSMIT) { + set_pending_event(PENDING_EVENT_TX_STARTED); + } + break; + case IEEE802154_EVENT_RX_FAILED: + if (sState == OT_RADIO_STATE_RECEIVE) { + switch (*(enum ieee802154_rx_fail_reason *) event_params) { + case IEEE802154_RX_FAIL_NOT_RECEIVED: + rx_result = OT_ERROR_NO_FRAME_RECEIVED; + break; + + case IEEE802154_RX_FAIL_INVALID_FCS: + rx_result = OT_ERROR_FCS; + break; + + case IEEE802154_RX_FAIL_ADDR_FILTERED: + rx_result = OT_ERROR_DESTINATION_ADDRESS_FILTERED; + break; + + case IEEE802154_RX_FAIL_OTHER: + default: + rx_result = OT_ERROR_FAILED; + break; + } + set_pending_event(PENDING_EVENT_RX_FAILED); + } + break; + case IEEE802154_EVENT_RX_OFF: + set_pending_event(PENDING_EVENT_SLEEP); + break; + default: + /* do nothing - ignore event */ + break; + } +} + +#if defined(CONFIG_NET_PKT_TXTIME) || defined(CONFIG_OPENTHREAD_CSL_RECEIVER) +/** + * @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 net_time_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 (net_time_t)result * NSEC_PER_USEC; +} +#endif /* CONFIG_NET_PKT_TXTIME || CONFIG_OPENTHREAD_CSL_RECEIVER */ + +static void dataInit(void) +{ + tx_pkt = net_pkt_alloc(K_NO_WAIT); + __ASSERT_NO_MSG(tx_pkt != NULL); + + tx_payload = net_pkt_get_reserve_tx_data(IEEE802154_MAX_PHY_PACKET_SIZE, + K_NO_WAIT); + __ASSERT_NO_MSG(tx_payload != NULL); + + net_pkt_append_buffer(tx_pkt, tx_payload); + + sTransmitFrame.mPsdu = tx_payload->data; + + for (size_t i = 0; i < CHANNEL_COUNT; i++) { + max_tx_power_table[i] = OT_RADIO_POWER_INVALID; + } + +#if defined(CONFIG_OPENTHREAD_TIME_SYNC) + sTransmitFrame.mInfo.mTxInfo.mIeInfo = &tx_ie_info; +#endif +} + +void platformRadioInit(void) +{ + struct ieee802154_config cfg; + + dataInit(); + + __ASSERT_NO_MSG(device_is_ready(radio_dev)); + + radio_api = (struct ieee802154_radio_api *)radio_dev->api; + if (!radio_api) { + return; + } + + k_work_queue_start(&ot_work_q, ot_task_stack, + K_KERNEL_STACK_SIZEOF(ot_task_stack), + OT_WORKER_PRIORITY, NULL); + k_thread_name_set(&ot_work_q.thread, "ot_radio_workq"); + + if ((radio_api->get_capabilities(radio_dev) & + IEEE802154_HW_TX_RX_ACK) != IEEE802154_HW_TX_RX_ACK) { + LOG_ERR("Only radios with automatic ack handling " + "are currently supported"); + k_panic(); + } + + cfg.event_handler = handle_radio_event; + radio_api->configure(radio_dev, IEEE802154_CONFIG_EVENT_HANDLER, &cfg); +} + +static void radio_set_channel(uint16_t ch) +{ + channel = ch; + radio_api->set_channel(radio_dev, ch); +} + +void transmit_message(struct k_work *tx_job) +{ + int tx_err; + + ARG_UNUSED(tx_job); + + enum ieee802154_hw_caps radio_caps = radio_api->get_capabilities(radio_dev); + + /* + * The payload is already in tx_payload->data, + * but we need to set the length field + * according to sTransmitFrame.length. + * We subtract the FCS size as radio driver + * adds CRC and increases frame length on its own. + */ + tx_payload->len = sTransmitFrame.mLength - FCS_SIZE; + + radio_api->set_txpower(radio_dev, get_transmit_power_for_channel(sTransmitFrame.mChannel)); + +#if defined(CONFIG_OPENTHREAD_TIME_SYNC) + if (sTransmitFrame.mInfo.mTxInfo.mIeInfo->mTimeIeOffset != 0) { + uint8_t *time_ie = + sTransmitFrame.mPsdu + sTransmitFrame.mInfo.mTxInfo.mIeInfo->mTimeIeOffset; + uint64_t offset_plat_time = + otPlatTimeGet() + sTransmitFrame.mInfo.mTxInfo.mIeInfo->mNetworkTimeOffset; + + *(time_ie++) = sTransmitFrame.mInfo.mTxInfo.mIeInfo->mTimeSyncSeq; + sys_put_le64(offset_plat_time, time_ie); + } +#endif + + net_pkt_set_ieee802154_frame_secured(tx_pkt, + sTransmitFrame.mInfo.mTxInfo.mIsSecurityProcessed); + net_pkt_set_ieee802154_mac_hdr_rdy(tx_pkt, sTransmitFrame.mInfo.mTxInfo.mIsHeaderUpdated); + + if ((radio_caps & IEEE802154_HW_TXTIME) && + (sTransmitFrame.mInfo.mTxInfo.mTxDelay != 0)) { +#if defined(CONFIG_NET_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)); +#endif +#if defined(CONFIG_IEEE802154_SELECTIVE_TXCHANNEL) + if (radio_caps & IEEE802154_HW_SELECTIVE_TXCHANNEL) { + net_pkt_set_ieee802154_txchannel(tx_pkt, sTransmitFrame.mChannel); + } else { + radio_set_channel(sTransmitFrame.mChannel); + } +#else + radio_set_channel(sTransmitFrame.mChannel); +#endif + tx_err = + radio_api->tx(radio_dev, IEEE802154_TX_MODE_TXTIME_CCA, tx_pkt, tx_payload); + } else if (sTransmitFrame.mInfo.mTxInfo.mCsmaCaEnabled) { + radio_set_channel(sTransmitFrame.mChannel); + if (radio_caps & IEEE802154_HW_CSMA) { + tx_err = radio_api->tx(radio_dev, IEEE802154_TX_MODE_CSMA_CA, tx_pkt, + tx_payload); + } else { + tx_err = radio_api->cca(radio_dev); + if (tx_err == 0) { + tx_err = radio_api->tx(radio_dev, IEEE802154_TX_MODE_DIRECT, tx_pkt, + tx_payload); + } + } + } else { + radio_set_channel(sTransmitFrame.mChannel); + tx_err = radio_api->tx(radio_dev, IEEE802154_TX_MODE_DIRECT, tx_pkt, tx_payload); + } + + /* + * OpenThread handles the following errors: + * - OT_ERROR_NONE + * - OT_ERROR_NO_ACK + * - OT_ERROR_CHANNEL_ACCESS_FAILURE + * - OT_ERROR_ABORT + * Any other error passed to `otPlatRadioTxDone` will result in assertion. + */ + switch (tx_err) { + case 0: + tx_result = OT_ERROR_NONE; + break; + case -ENOMSG: + tx_result = OT_ERROR_NO_ACK; + break; + case -EBUSY: + tx_result = OT_ERROR_CHANNEL_ACCESS_FAILURE; + break; + case -EIO: + tx_result = OT_ERROR_ABORT; + break; + default: + tx_result = OT_ERROR_CHANNEL_ACCESS_FAILURE; + break; + } + + set_pending_event(PENDING_EVENT_TX_DONE); +} + +static inline void handle_tx_done(otInstance *aInstance) +{ + sTransmitFrame.mInfo.mTxInfo.mIsSecurityProcessed = + net_pkt_ieee802154_frame_secured(tx_pkt); + sTransmitFrame.mInfo.mTxInfo.mIsHeaderUpdated = net_pkt_ieee802154_mac_hdr_rdy(tx_pkt); + + if (IS_ENABLED(CONFIG_OPENTHREAD_DIAG) && otPlatDiagModeGet()) { + otPlatDiagRadioTransmitDone(aInstance, &sTransmitFrame, tx_result); + } else { + otPlatRadioTxDone(aInstance, &sTransmitFrame, ack_frame.mLength ? &ack_frame : NULL, + tx_result); + ack_frame.mLength = 0; + } +} + +static void openthread_handle_received_frame(otInstance *instance, + struct net_pkt *pkt) +{ + otRadioFrame recv_frame; + + memset(&recv_frame, 0, sizeof(otRadioFrame)); + + recv_frame.mPsdu = net_buf_frag_last(pkt->buffer)->data; + /* Length inc. CRC. */ + recv_frame.mLength = net_buf_frags_len(pkt->buffer); + recv_frame.mChannel = platformRadioChannelGet(instance); + recv_frame.mInfo.mRxInfo.mLqi = net_pkt_ieee802154_lqi(pkt); + recv_frame.mInfo.mRxInfo.mRssi = net_pkt_ieee802154_rssi_dbm(pkt); + recv_frame.mInfo.mRxInfo.mAckedWithFramePending = net_pkt_ieee802154_ack_fpb(pkt); + +#if defined(CONFIG_NET_PKT_TIMESTAMP) + recv_frame.mInfo.mRxInfo.mTimestamp = net_pkt_timestamp_ns(pkt) / NSEC_PER_USEC; +#endif + + recv_frame.mInfo.mRxInfo.mAckedWithSecEnhAck = net_pkt_ieee802154_ack_seb(pkt); + recv_frame.mInfo.mRxInfo.mAckFrameCounter = net_pkt_ieee802154_ack_fc(pkt); + recv_frame.mInfo.mRxInfo.mAckKeyId = net_pkt_ieee802154_ack_keyid(pkt); + + if (IS_ENABLED(CONFIG_OPENTHREAD_DIAG) && otPlatDiagModeGet()) { + otPlatDiagRadioReceiveDone(instance, &recv_frame, OT_ERROR_NONE); + } else { + otPlatRadioReceiveDone(instance, &recv_frame, OT_ERROR_NONE); + } + + net_pkt_unref(pkt); +} + +#if defined(CONFIG_OPENTHREAD_NAT64_TRANSLATOR) + +static otMessage *openthread_ip4_new_msg(otInstance *instance, otMessageSettings *settings) +{ + return otIp4NewMessage(instance, settings); +} + +static otError openthread_nat64_send(otInstance *instance, otMessage *message) +{ + return otNat64Send(instance, message); +} + +#else /* CONFIG_OPENTHREAD_NAT64_TRANSLATOR */ + +static otMessage *openthread_ip4_new_msg(otInstance *instance, otMessageSettings *settings) +{ + return NULL; +} + +static otError openthread_nat64_send(otInstance *instance, otMessage *message) +{ + return OT_ERROR_DROP; +} + +#endif /* CONFIG_OPENTHREAD_NAT64_TRANSLATOR */ + +static void openthread_handle_frame_to_send(otInstance *instance, struct net_pkt *pkt) +{ + otError error; + struct net_buf *buf; + otMessage *message; + otMessageSettings settings; + bool is_ip6 = PKT_IS_IPv6(pkt); + + NET_DBG("Sending %s packet to ot stack", is_ip6 ? "IPv6" : "IPv4"); + + settings.mPriority = OT_MESSAGE_PRIORITY_NORMAL; + settings.mLinkSecurityEnabled = true; + + message = is_ip6 ? otIp6NewMessage(instance, &settings) + : openthread_ip4_new_msg(instance, &settings); + if (!message) { + NET_ERR("Cannot allocate new message buffer"); + goto exit; + } + + if (IS_ENABLED(CONFIG_OPENTHREAD)) { + /* Set multicast loop so the stack can process multicast packets for + * subscribed addresses. + */ + otMessageSetMulticastLoopEnabled(message, true); + } + + for (buf = pkt->buffer; buf; buf = buf->frags) { + if (otMessageAppend(message, buf->data, buf->len) != OT_ERROR_NONE) { + NET_ERR("Error while appending to otMessage"); + otMessageFree(message); + goto exit; + } + } + + error = is_ip6 ? otIp6Send(instance, message) : openthread_nat64_send(instance, message); + + if (error != OT_ERROR_NONE) { + NET_ERR("Error while calling %s [error: %d]", + is_ip6 ? "otIp6Send" : "openthread_nat64_send", error); + } + +exit: + net_pkt_unref(pkt); +} + +int notify_new_rx_frame(struct net_pkt *pkt) +{ + k_fifo_put(&rx_pkt_fifo, pkt); + set_pending_event(PENDING_EVENT_FRAME_RECEIVED); + + return 0; +} + +int notify_new_tx_frame(struct net_pkt *pkt) +{ + k_fifo_put(&tx_pkt_fifo, pkt); + set_pending_event(PENDING_EVENT_FRAME_TO_SEND); + + return 0; +} + +static int run_tx_task(otInstance *aInstance) +{ + static K_WORK_DEFINE(tx_job, transmit_message); + + ARG_UNUSED(aInstance); + + if (!k_work_is_pending(&tx_job)) { + sState = OT_RADIO_STATE_TRANSMIT; + + k_work_submit_to_queue(&ot_work_q, &tx_job); + return 0; + } else { + return -EBUSY; + } +} + +void platformRadioProcess(otInstance *aInstance) +{ + bool event_pending = false; + + if (is_pending_event_set(PENDING_EVENT_FRAME_TO_SEND)) { + struct net_pkt *evt_pkt; + + reset_pending_event(PENDING_EVENT_FRAME_TO_SEND); + while ((evt_pkt = (struct net_pkt *) k_fifo_get(&tx_pkt_fifo, K_NO_WAIT)) != NULL) { + if (IS_ENABLED(CONFIG_OPENTHREAD_COPROCESSOR_RCP)) { + net_pkt_unref(evt_pkt); + } else { + openthread_handle_frame_to_send(aInstance, evt_pkt); + } + } + } + + if (is_pending_event_set(PENDING_EVENT_FRAME_RECEIVED)) { + struct net_pkt *rx_pkt; + + reset_pending_event(PENDING_EVENT_FRAME_RECEIVED); + while ((rx_pkt = (struct net_pkt *) k_fifo_get(&rx_pkt_fifo, K_NO_WAIT)) != NULL) { + openthread_handle_received_frame(aInstance, rx_pkt); + } + } + + if (is_pending_event_set(PENDING_EVENT_RX_FAILED)) { + reset_pending_event(PENDING_EVENT_RX_FAILED); + if (IS_ENABLED(CONFIG_OPENTHREAD_DIAG) && otPlatDiagModeGet()) { + otPlatDiagRadioReceiveDone(aInstance, NULL, rx_result); + } else { + otPlatRadioReceiveDone(aInstance, NULL, rx_result); + } + } + + if (is_pending_event_set(PENDING_EVENT_TX_STARTED)) { + reset_pending_event(PENDING_EVENT_TX_STARTED); + otPlatRadioTxStarted(aInstance, &sTransmitFrame); + } + + if (is_pending_event_set(PENDING_EVENT_TX_DONE)) { + reset_pending_event(PENDING_EVENT_TX_DONE); + + if (sState == OT_RADIO_STATE_TRANSMIT || + radio_api->get_capabilities(radio_dev) & IEEE802154_HW_SLEEP_TO_TX) { + sState = OT_RADIO_STATE_RECEIVE; + handle_tx_done(aInstance); + } + } + + if (is_pending_event_set(PENDING_EVENT_SLEEP)) { + reset_pending_event(PENDING_EVENT_SLEEP); + ARG_UNUSED(otPlatRadioSleep(aInstance)); + } + + /* handle events that can't run during transmission */ + if (sState != OT_RADIO_STATE_TRANSMIT) { + if (is_pending_event_set(PENDING_EVENT_DETECT_ENERGY)) { + radio_api->set_channel(radio_dev, + energy_detection_channel); + + if (!radio_api->ed_scan(radio_dev, + energy_detection_time, + energy_detected)) { + reset_pending_event( + PENDING_EVENT_DETECT_ENERGY); + } else { + event_pending = true; + } + } + + if (is_pending_event_set(PENDING_EVENT_DETECT_ENERGY_DONE)) { + otPlatRadioEnergyScanDone(aInstance, (int8_t) energy_detected_value); + reset_pending_event(PENDING_EVENT_DETECT_ENERGY_DONE); + } + } + + if (event_pending) { + otSysEventSignalPending(); + } +} + +uint16_t platformRadioChannelGet(otInstance *aInstance) +{ + ARG_UNUSED(aInstance); + + return channel; +} + +#if defined(CONFIG_OPENTHREAD_DIAG) +void platformRadioChannelSet(uint8_t aChannel) +{ + channel = aChannel; +} +#endif + +void otPlatRadioSetPanId(otInstance *aInstance, uint16_t aPanId) +{ + ARG_UNUSED(aInstance); + + radio_api->filter(radio_dev, true, IEEE802154_FILTER_TYPE_PAN_ID, + (struct ieee802154_filter *) &aPanId); +} + +void otPlatRadioSetExtendedAddress(otInstance *aInstance, + const otExtAddress *aExtAddress) +{ + ARG_UNUSED(aInstance); + + radio_api->filter(radio_dev, true, IEEE802154_FILTER_TYPE_IEEE_ADDR, + (struct ieee802154_filter *) &aExtAddress); +} + +void otPlatRadioSetShortAddress(otInstance *aInstance, uint16_t aShortAddress) +{ + ARG_UNUSED(aInstance); + + radio_api->filter(radio_dev, true, IEEE802154_FILTER_TYPE_SHORT_ADDR, + (struct ieee802154_filter *) &aShortAddress); +} + +bool otPlatRadioIsEnabled(otInstance *aInstance) +{ + ARG_UNUSED(aInstance); + + return (sState != OT_RADIO_STATE_DISABLED) ? true : false; +} + +otError otPlatRadioEnable(otInstance *aInstance) +{ + ARG_UNUSED(aInstance); + + if (sState != OT_RADIO_STATE_DISABLED && sState != OT_RADIO_STATE_SLEEP) { + return OT_ERROR_INVALID_STATE; + } + + sState = OT_RADIO_STATE_SLEEP; + return OT_ERROR_NONE; +} + +otError otPlatRadioDisable(otInstance *aInstance) +{ + ARG_UNUSED(aInstance); + + if (sState != OT_RADIO_STATE_DISABLED && sState != OT_RADIO_STATE_SLEEP) { + return OT_ERROR_INVALID_STATE; + } + + sState = OT_RADIO_STATE_DISABLED; + return OT_ERROR_NONE; +} + +otError otPlatRadioSleep(otInstance *aInstance) +{ + ARG_UNUSED(aInstance); + + if (sState != OT_RADIO_STATE_SLEEP && sState != OT_RADIO_STATE_RECEIVE) { + return OT_ERROR_INVALID_STATE; + } + + radio_api->stop(radio_dev); + sState = OT_RADIO_STATE_SLEEP; + + return OT_ERROR_NONE; +} + +otError otPlatRadioReceive(otInstance *aInstance, uint8_t aChannel) +{ + ARG_UNUSED(aInstance); + + if (sState == OT_RADIO_STATE_DISABLED) { + return OT_ERROR_INVALID_STATE; + } + + channel = aChannel; + + radio_api->set_channel(radio_dev, aChannel); + radio_api->set_txpower(radio_dev, get_transmit_power_for_channel(channel)); + radio_api->start(radio_dev); + sState = 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) +{ + int result; + + ARG_UNUSED(aInstance); + + struct ieee802154_config config = { + .rx_slot.channel = aChannel, + .rx_slot.start = convert_32bit_us_wrapped_to_64bit_ns(aStart), + .rx_slot.duration = (net_time_t)aDuration * NSEC_PER_USEC, + }; + + result = radio_api->configure(radio_dev, IEEE802154_CONFIG_RX_SLOT, + &config); + + return result ? OT_ERROR_FAILED : OT_ERROR_NONE; +} +#endif + +#if defined(CONFIG_IEEE802154_CARRIER_FUNCTIONS) +otError platformRadioTransmitCarrier(otInstance *aInstance, bool aEnable) +{ + if (radio_api->continuous_carrier == NULL) { + return OT_ERROR_NOT_IMPLEMENTED; + } + + if ((aEnable) && (sState == OT_RADIO_STATE_RECEIVE)) { + radio_api->set_txpower(radio_dev, get_transmit_power_for_channel(channel)); + + if (radio_api->continuous_carrier(radio_dev) != 0) { + return OT_ERROR_FAILED; + } + + sState = OT_RADIO_STATE_TRANSMIT; + } else if ((!aEnable) && (sState == OT_RADIO_STATE_TRANSMIT)) { + return otPlatRadioReceive(aInstance, channel); + } else { + return OT_ERROR_INVALID_STATE; + } + + return OT_ERROR_NONE; +} + +otError platformRadioTransmitModulatedCarrier(otInstance *aInstance, bool aEnable, + const uint8_t *aData) +{ + if (radio_api->modulated_carrier == NULL) { + return OT_ERROR_NOT_IMPLEMENTED; + } + + if (aEnable && sState == OT_RADIO_STATE_RECEIVE) { + if (aData == NULL) { + return OT_ERROR_INVALID_ARGS; + } + + radio_api->set_txpower(radio_dev, get_transmit_power_for_channel(channel)); + + if (radio_api->modulated_carrier(radio_dev, aData) != 0) { + return OT_ERROR_FAILED; + } + sState = OT_RADIO_STATE_TRANSMIT; + } else if ((!aEnable) && sState == OT_RADIO_STATE_TRANSMIT) { + return otPlatRadioReceive(aInstance, channel); + } else { + return OT_ERROR_INVALID_STATE; + } + + return OT_ERROR_NONE; +} + +#endif /* CONFIG_IEEE802154_CARRIER_FUNCTIONS */ + +otRadioState otPlatRadioGetState(otInstance *aInstance) +{ + ARG_UNUSED(aInstance); + + return sState; +} + +otError otPlatRadioTransmit(otInstance *aInstance, otRadioFrame *aPacket) +{ + otError error = OT_ERROR_INVALID_STATE; + + ARG_UNUSED(aInstance); + ARG_UNUSED(aPacket); + + __ASSERT_NO_MSG(aPacket == &sTransmitFrame); + + enum ieee802154_hw_caps radio_caps; + + radio_caps = radio_api->get_capabilities(radio_dev); + + if (sState == OT_RADIO_STATE_RECEIVE || + (sState == OT_RADIO_STATE_SLEEP && + radio_caps & IEEE802154_HW_SLEEP_TO_TX)) { + if (run_tx_task(aInstance) == 0) { + error = OT_ERROR_NONE; + } + } + + return error; +} + +otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *aInstance) +{ + ARG_UNUSED(aInstance); + + return &sTransmitFrame; +} + +static void get_rssi_energy_detected(const struct device *dev, int16_t max_ed) +{ + ARG_UNUSED(dev); + energy_detected_value = max_ed; + k_sem_give(&radio_sem); +} + +int8_t otPlatRadioGetRssi(otInstance *aInstance) +{ + int8_t ret_rssi = INT8_MAX; + int error = 0; + const uint16_t detection_time = 1; + enum ieee802154_hw_caps radio_caps; + + ARG_UNUSED(aInstance); + + radio_caps = radio_api->get_capabilities(radio_dev); + + if (!(radio_caps & IEEE802154_HW_ENERGY_SCAN)) { + /* + * TODO: No API in Zephyr to get the RSSI + * when IEEE802154_HW_ENERGY_SCAN is not available + */ + ret_rssi = 0; + } else { + /* + * Blocking implementation of get RSSI + * using no-blocking ed_scan + */ + error = radio_api->ed_scan(radio_dev, detection_time, + get_rssi_energy_detected); + + if (error == 0) { + k_sem_take(&radio_sem, K_FOREVER); + + ret_rssi = (int8_t)energy_detected_value; + } + } + + return ret_rssi; +} + +otRadioCaps otPlatRadioGetCaps(otInstance *aInstance) +{ + otRadioCaps caps = OT_RADIO_CAPS_NONE; + enum ieee802154_hw_caps radio_caps; + + ARG_UNUSED(aInstance); + __ASSERT(radio_api, + "platformRadioInit needs to be called prior to otPlatRadioGetCaps"); + + radio_caps = radio_api->get_capabilities(radio_dev); + + if (radio_caps & IEEE802154_HW_ENERGY_SCAN) { + caps |= OT_RADIO_CAPS_ENERGY_SCAN; + } + + if (radio_caps & IEEE802154_HW_CSMA) { + caps |= OT_RADIO_CAPS_CSMA_BACKOFF; + } + + if (radio_caps & IEEE802154_HW_TX_RX_ACK) { + caps |= OT_RADIO_CAPS_ACK_TIMEOUT; + } + + if (radio_caps & IEEE802154_HW_SLEEP_TO_TX) { + caps |= OT_RADIO_CAPS_SLEEP_TO_TX; + } + +#if !defined(CONFIG_OPENTHREAD_THREAD_VERSION_1_1) + if (radio_caps & IEEE802154_HW_TX_SEC) { + caps |= OT_RADIO_CAPS_TRANSMIT_SEC; + } +#endif + +#if defined(CONFIG_NET_PKT_TXTIME) + if (radio_caps & IEEE802154_HW_TXTIME) { + caps |= OT_RADIO_CAPS_TRANSMIT_TIMING; + } +#endif + + if (radio_caps & IEEE802154_HW_RXTIME) { + caps |= OT_RADIO_CAPS_RECEIVE_TIMING; + } + + if (radio_caps & IEEE802154_RX_ON_WHEN_IDLE) { + caps |= OT_RADIO_CAPS_RX_ON_WHEN_IDLE; + } + + return caps; +} + +void otPlatRadioSetRxOnWhenIdle(otInstance *aInstance, bool aRxOnWhenIdle) +{ + struct ieee802154_config config = { + .rx_on_when_idle = aRxOnWhenIdle + }; + + ARG_UNUSED(aInstance); + + LOG_DBG("RxOnWhenIdle=%d", aRxOnWhenIdle ? 1 : 0); + + radio_api->configure(radio_dev, IEEE802154_CONFIG_RX_ON_WHEN_IDLE, &config); +} + +bool otPlatRadioGetPromiscuous(otInstance *aInstance) +{ + ARG_UNUSED(aInstance); + + LOG_DBG("PromiscuousMode=%d", promiscuous ? 1 : 0); + + return promiscuous; +} + +void otPlatRadioSetPromiscuous(otInstance *aInstance, bool aEnable) +{ + struct ieee802154_config config = { + .promiscuous = aEnable + }; + + ARG_UNUSED(aInstance); + + LOG_DBG("PromiscuousMode=%d", aEnable ? 1 : 0); + + promiscuous = aEnable; + /* TODO: Should check whether the radio driver actually supports + * promiscuous mode, see net_if_l2(iface)->get_flags() and + * ieee802154_radio_get_hw_capabilities(iface). + */ + radio_api->configure(radio_dev, IEEE802154_CONFIG_PROMISCUOUS, &config); +} + +otError otPlatRadioEnergyScan(otInstance *aInstance, uint8_t aScanChannel, + uint16_t aScanDuration) +{ + energy_detection_time = aScanDuration; + energy_detection_channel = aScanChannel; + + if (radio_api->ed_scan == NULL) { + return OT_ERROR_NOT_IMPLEMENTED; + } + + reset_pending_event(PENDING_EVENT_DETECT_ENERGY); + reset_pending_event(PENDING_EVENT_DETECT_ENERGY_DONE); + + radio_api->set_channel(radio_dev, aScanChannel); + + if (radio_api->ed_scan(radio_dev, energy_detection_time, energy_detected) != 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; +} + +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; +} + +void otPlatRadioEnableSrcMatch(otInstance *aInstance, bool aEnable) +{ + ARG_UNUSED(aInstance); + + struct ieee802154_config config = { + .auto_ack_fpb.enabled = aEnable, + .auto_ack_fpb.mode = IEEE802154_FPB_ADDR_MATCH_THREAD, + }; + + (void)radio_api->configure(radio_dev, IEEE802154_CONFIG_AUTO_ACK_FPB, + &config); +} + +otError otPlatRadioAddSrcMatchShortEntry(otInstance *aInstance, + const uint16_t aShortAddress) +{ + ARG_UNUSED(aInstance); + + uint8_t short_address[SHORT_ADDRESS_SIZE]; + struct ieee802154_config config = { + .ack_fpb.enabled = true, + .ack_fpb.addr = short_address, + .ack_fpb.extended = false + }; + + sys_put_le16(aShortAddress, short_address); + + if (radio_api->configure(radio_dev, IEEE802154_CONFIG_ACK_FPB, + &config) != 0) { + return OT_ERROR_NO_BUFS; + } + + return OT_ERROR_NONE; +} + +otError otPlatRadioAddSrcMatchExtEntry(otInstance *aInstance, + const otExtAddress *aExtAddress) +{ + ARG_UNUSED(aInstance); + + struct ieee802154_config config = { + .ack_fpb.enabled = true, + .ack_fpb.addr = (uint8_t *)aExtAddress->m8, + .ack_fpb.extended = true + }; + + if (radio_api->configure(radio_dev, IEEE802154_CONFIG_ACK_FPB, + &config) != 0) { + return OT_ERROR_NO_BUFS; + } + + return OT_ERROR_NONE; +} + +otError otPlatRadioClearSrcMatchShortEntry(otInstance *aInstance, + const uint16_t aShortAddress) +{ + ARG_UNUSED(aInstance); + + uint8_t short_address[SHORT_ADDRESS_SIZE]; + struct ieee802154_config config = { + .ack_fpb.enabled = false, + .ack_fpb.addr = short_address, + .ack_fpb.extended = false + }; + + sys_put_le16(aShortAddress, short_address); + + if (radio_api->configure(radio_dev, IEEE802154_CONFIG_ACK_FPB, + &config) != 0) { + return OT_ERROR_NO_BUFS; + } + + return OT_ERROR_NONE; +} + +otError otPlatRadioClearSrcMatchExtEntry(otInstance *aInstance, + const otExtAddress *aExtAddress) +{ + ARG_UNUSED(aInstance); + + struct ieee802154_config config = { + .ack_fpb.enabled = false, + .ack_fpb.addr = (uint8_t *)aExtAddress->m8, + .ack_fpb.extended = true + }; + + if (radio_api->configure(radio_dev, IEEE802154_CONFIG_ACK_FPB, + &config) != 0) { + return OT_ERROR_NO_BUFS; + } + + return OT_ERROR_NONE; +} + +void otPlatRadioClearSrcMatchShortEntries(otInstance *aInstance) +{ + ARG_UNUSED(aInstance); + + struct ieee802154_config config = { + .ack_fpb.enabled = false, + .ack_fpb.addr = NULL, + .ack_fpb.extended = false + }; + + (void)radio_api->configure(radio_dev, IEEE802154_CONFIG_ACK_FPB, + &config); +} + +void otPlatRadioClearSrcMatchExtEntries(otInstance *aInstance) +{ + ARG_UNUSED(aInstance); + + struct ieee802154_config config = { + .ack_fpb.enabled = false, + .ack_fpb.addr = NULL, + .ack_fpb.extended = true + }; + + (void)radio_api->configure(radio_dev, IEEE802154_CONFIG_ACK_FPB, + &config); +} + +int8_t otPlatRadioGetReceiveSensitivity(otInstance *aInstance) +{ + ARG_UNUSED(aInstance); + + return CONFIG_OPENTHREAD_DEFAULT_RX_SENSITIVITY; +} + +otError otPlatRadioGetTransmitPower(otInstance *aInstance, int8_t *aPower) +{ + ARG_UNUSED(aInstance); + + if (aPower == NULL) { + return OT_ERROR_INVALID_ARGS; + } + + *aPower = tx_power; + + return OT_ERROR_NONE; +} + +otError otPlatRadioSetTransmitPower(otInstance *aInstance, int8_t aPower) +{ + ARG_UNUSED(aInstance); + + tx_power = aPower; + + return OT_ERROR_NONE; +} + +uint64_t otPlatTimeGet(void) +{ + if (radio_api == NULL || radio_api->get_time == NULL) { + return k_ticks_to_us_floor64(k_uptime_ticks()); + } else { + return radio_api->get_time(radio_dev) / NSEC_PER_USEC; + } +} + +#if defined(CONFIG_NET_PKT_TXTIME) +uint64_t otPlatRadioGetNow(otInstance *aInstance) +{ + ARG_UNUSED(aInstance); + + return otPlatTimeGet(); +} +#endif + +#if !defined(CONFIG_OPENTHREAD_THREAD_VERSION_1_1) +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; + + struct ieee802154_key keys[] = { + { + .key_id_mode = key_id_mode, + .frame_counter_per_key = false, + }, + { + .key_id_mode = key_id_mode, + .frame_counter_per_key = false, + }, + { + .key_id_mode = key_id_mode, + .frame_counter_per_key = false, + }, + { + .key_value = NULL, + }, + }; + + struct ieee802154_key clear_keys[] = { + { + .key_value = NULL, + }, + }; + + if (key_id_mode == 1) { + /* 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; + + keys[0].key_id = &prev_key_id; + keys[0].key_value = (uint8_t *)aPrevKey->mKeyMaterial.mKey.m8; + + keys[1].key_id = &aKeyId; + keys[1].key_value = (uint8_t *)aCurrKey->mKeyMaterial.mKey.m8; + + keys[2].key_id = &next_key_id; + keys[2].key_value = (uint8_t *)aNextKey->mKeyMaterial.mKey.m8; + } else { + /* aKeyId == 0 is used only to clear keys for stack reset in RCP */ + __ASSERT_NO_MSG((key_id_mode == 0) && (aKeyId == 0)); + } + + struct ieee802154_config config = { + .mac_keys = aKeyId == 0 ? clear_keys : keys, + }; + + (void)radio_api->configure(radio_dev, IEEE802154_CONFIG_MAC_KEYS, + &config); +} + +void otPlatRadioSetMacFrameCounter(otInstance *aInstance, + uint32_t aMacFrameCounter) +{ + ARG_UNUSED(aInstance); + + struct ieee802154_config config = { .frame_counter = aMacFrameCounter }; + + (void)radio_api->configure(radio_dev, IEEE802154_CONFIG_FRAME_COUNTER, + &config); +} + +void otPlatRadioSetMacFrameCounterIfLarger(otInstance *aInstance, uint32_t aMacFrameCounter) +{ + ARG_UNUSED(aInstance); + + struct ieee802154_config config = { .frame_counter = aMacFrameCounter }; + (void)radio_api->configure(radio_dev, IEEE802154_CONFIG_FRAME_COUNTER_IF_LARGER, + &config); +} +#endif + +#if defined(CONFIG_OPENTHREAD_CSL_RECEIVER) +otError otPlatRadioEnableCsl(otInstance *aInstance, uint32_t aCslPeriod, otShortAddress aShortAddr, + const otExtAddress *aExtAddr) +{ + struct ieee802154_config config; + /* CSL phase will be injected on-the-fly by the driver. */ + struct ieee802154_header_ie header_ie = + IEEE802154_DEFINE_HEADER_IE_CSL_REDUCED(/* phase */ 0, aCslPeriod); + int result; + + ARG_UNUSED(aInstance); + + /* Configure the CSL period first to give drivers a chance to validate + * the IE for consistency if they wish to. + */ + config.csl_period = aCslPeriod; + result = radio_api->configure(radio_dev, IEEE802154_CONFIG_CSL_PERIOD, &config); + if (result) { + return OT_ERROR_FAILED; + } + + /* Configure the CSL IE. */ + config.ack_ie.header_ie = aCslPeriod > 0 ? &header_ie : NULL; + config.ack_ie.short_addr = aShortAddr; + config.ack_ie.ext_addr = aExtAddr != NULL ? aExtAddr->m8 : NULL; + config.ack_ie.purge_ie = false; + + result = radio_api->configure(radio_dev, IEEE802154_CONFIG_ENH_ACK_HEADER_IE, &config); + + return result ? OT_ERROR_FAILED : OT_ERROR_NONE; +} + +otError otPlatRadioResetCsl(otInstance *aInstance) +{ + struct ieee802154_config config = { 0 }; + int result; + + result = radio_api->configure(radio_dev, IEEE802154_CONFIG_CSL_PERIOD, &config); + if (result) { + return OT_ERROR_FAILED; + } + + config.ack_ie.purge_ie = true; + result = radio_api->configure(radio_dev, IEEE802154_CONFIG_ENH_ACK_HEADER_IE, &config); + + return result ? OT_ERROR_FAILED : 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". + */ + struct ieee802154_config config = { + .expected_rx_time = + convert_32bit_us_wrapped_to_64bit_ns(aCslSampleTime - PHR_DURATION_US), + }; + + (void)radio_api->configure(radio_dev, IEEE802154_CONFIG_EXPECTED_RX_TIME, &config); +} +#endif /* CONFIG_OPENTHREAD_CSL_RECEIVER */ + +#if defined(CONFIG_OPENTHREAD_WAKEUP_COORDINATOR) +otError otPlatRadioEnableCst(otInstance *aInstance, uint32_t aCstPeriod, otShortAddress aShortAddr, + const otExtAddress *aExtAddr) +{ + struct ieee802154_config config; + int result; + uint8_t header_ie[OT_IE_HEADER_SIZE + OT_THREAD_IE_SIZE + OT_CST_IE_SIZE] = { 0 }; + size_t index = 0; + + ARG_UNUSED(aInstance); + + /* Configure the CST period first to give drivers a chance to validate + * the IE for consistency if they wish to. + */ + config.cst_period = aCstPeriod; + result = radio_api->configure(radio_dev, IEEE802154_OPENTHREAD_CONFIG_CST_PERIOD, &config); + if (result) { + return OT_ERROR_FAILED; + } + + /* Configure the CST IE. */ + header_ie[index++] = OT_THREAD_IE_SIZE + OT_CST_IE_SIZE; + header_ie[index++] = 0; + sys_put_le24(THREAD_IE_VENDOR_OUI, &header_ie[index]); + index += 3; + header_ie[index++] = THREAD_IE_SUBTYPE_CST; + /* Leave CST Phase empty intentionally */ + index += 2; + sys_put_le16(aCstPeriod, &header_ie[index]); + index += 2; + + config.ack_ie.header_ie = aCstPeriod > 0 ? (struct ieee802154_header_ie *)header_ie : NULL; + config.ack_ie.short_addr = aShortAddr; + config.ack_ie.ext_addr = aExtAddr != NULL ? aExtAddr->m8 : NULL; + config.ack_ie.purge_ie = false; + + result = radio_api->configure(radio_dev, IEEE802154_CONFIG_ENH_ACK_HEADER_IE, &config); + + return result ? OT_ERROR_FAILED : OT_ERROR_NONE; +} + +void otPlatRadioUpdateCstSampleTime(otInstance *aInstance, uint32_t aCstSampleTime) +{ + int result; + + ARG_UNUSED(aInstance); + + struct ieee802154_config config = { + .expected_tx_time = convert_32bit_us_wrapped_to_64bit_ns( + aCstSampleTime - PHR_DURATION_US), + }; + + result = radio_api->configure(radio_dev, IEEE802154_OPENTHREAD_CONFIG_EXPECTED_TX_TIME, + &config); + __ASSERT_NO_MSG(result == 0); + (void)result; +} +#endif /* CONFIG_OPENTHREAD_WAKEUP_COORDINATOR */ + +uint8_t otPlatRadioGetCslAccuracy(otInstance *aInstance) +{ + ARG_UNUSED(aInstance); + + return radio_api->get_sch_acc(radio_dev); +} + +#if defined(CONFIG_OPENTHREAD_PLATFORM_CSL_UNCERT) +uint8_t otPlatRadioGetCslUncertainty(otInstance *aInstance) +{ + ARG_UNUSED(aInstance); + + return CONFIG_OPENTHREAD_PLATFORM_CSL_UNCERT; +} +#endif + +#if defined(CONFIG_OPENTHREAD_LINK_METRICS_SUBJECT) +/** + * Header IE format - IEEE Std. 802.15.4-2015, 7.4.2.1 && 7.4.2.2 + * + * +---------------------------------+----------------------+ + * | Length | Element ID | Type=0 | Vendor OUI | + * +-----------+------------+--------+----------------------+ + * | Bytes: 0-1 | 2-4 | + * +-----------+---------------------+----------------------+ + * | Bits: 0-6 | 7-14 | 15 | IE_VENDOR_THREAD_OUI | + * +-----------+------------+--------+----------------------| + * + * Thread v1.2.1 Spec., 4.11.3.4.4.6 + * +---------------------------------+-------------------+------------------+ + * | Vendor Specific Information | + * +---------------------------------+-------------------+------------------+ + * | 5 | 6 | 7 (optional) | + * +---------------------------------+-------------------+------------------+ + * | IE_VENDOR_THREAD_ACK_PROBING_ID | LINK_METRIC_TOKEN | LINK_METRIC_TOKEN| + * |---------------------------------|-------------------|------------------| + */ +static void set_vendor_ie_header_lm(bool lqi, bool link_margin, bool rssi, uint8_t *ie_header) +{ + /* Vendor-specific IE identifier */ + const uint8_t ie_vendor_id = 0x00; + /* Thread Vendor-specific ACK Probing IE subtype ID */ + const uint8_t ie_vendor_thread_ack_probing_id = 0x00; + /* Thread Vendor-specific IE OUI */ + const uint32_t ie_vendor_thread_oui = 0xeab89b; + /* Thread Vendor-specific ACK Probing IE RSSI value placeholder */ + const uint8_t ie_vendor_thread_rssi_token = 0x01; + /* Thread Vendor-specific ACK Probing IE Link margin value placeholder */ + const uint8_t ie_vendor_thread_margin_token = 0x02; + /* Thread Vendor-specific ACK Probing IE LQI value placeholder */ + const uint8_t ie_vendor_thread_lqi_token = 0x03; + const uint8_t oui_size = 3; + const uint8_t sub_type = 1; + const uint8_t id_offset = 7; + const uint16_t id_mask = 0x00ff << id_offset; + const uint8_t type = 0x00; + const uint8_t type_offset = 7; + const uint8_t type_mask = 0x01 << type_offset; + const uint8_t length_mask = 0x7f; + uint8_t content_len; + uint16_t element_id = 0x0000; + uint8_t link_metrics_idx = 6; + uint8_t link_metrics_data_len = (uint8_t)lqi + (uint8_t)link_margin + (uint8_t)rssi; + + __ASSERT(link_metrics_data_len <= 2, "Thread limits to 2 metrics at most"); + __ASSERT(ie_header, "Invalid argument"); + + if (link_metrics_data_len == 0) { + ie_header[0] = 0; + return; + } + + /* Set Element ID */ + element_id = (((uint16_t)ie_vendor_id) << id_offset) & id_mask; + sys_put_le16(element_id, &ie_header[0]); + + /* Set Length - number of octets in content field. */ + content_len = oui_size + sub_type + link_metrics_data_len; + ie_header[0] = (ie_header[0] & ~length_mask) | (content_len & length_mask); + + /* Set Type */ + ie_header[1] = (ie_header[1] & ~type_mask) | (type & type_mask); + + /* Set Vendor Oui */ + sys_put_le24(ie_vendor_thread_oui, &ie_header[2]); + + /* Set SubType */ + ie_header[5] = ie_vendor_thread_ack_probing_id; + + /* Set Link Metrics Tokens + * TODO: Thread requires the order of requested metrics by the Link Metrics Initiator + * to be kept by the Link Metrics Subject in the ACKs. + */ + if (lqi) { + ie_header[link_metrics_idx++] = ie_vendor_thread_lqi_token; + } + + if (link_margin) { + ie_header[link_metrics_idx++] = ie_vendor_thread_margin_token; + } + + if (rssi) { + ie_header[link_metrics_idx++] = ie_vendor_thread_rssi_token; + } +} + +otError otPlatRadioConfigureEnhAckProbing(otInstance *aInstance, otLinkMetrics aLinkMetrics, + const otShortAddress aShortAddress, + const otExtAddress *aExtAddress) +{ + struct ieee802154_config config = { + .ack_ie.short_addr = aShortAddress, + .ack_ie.ext_addr = aExtAddress->m8, + }; + uint8_t header_ie_buf[OT_ACK_IE_MAX_SIZE]; + int result; + + ARG_UNUSED(aInstance); + + set_vendor_ie_header_lm(aLinkMetrics.mLqi, aLinkMetrics.mLinkMargin, + aLinkMetrics.mRssi, header_ie_buf); + config.ack_ie.header_ie = (struct ieee802154_header_ie *)header_ie_buf; + result = radio_api->configure(radio_dev, IEEE802154_CONFIG_ENH_ACK_HEADER_IE, &config); + + return result ? OT_ERROR_FAILED : OT_ERROR_NONE; +} + +#endif /* CONFIG_OPENTHREAD_LINK_METRICS_SUBJECT */ + +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; + } + + max_tx_power_table[aChannel - OT_RADIO_2P4GHZ_OQPSK_CHANNEL_MIN] = aMaxPower; + + if (aChannel == channel) { + radio_api->set_txpower(radio_dev, get_transmit_power_for_channel(aChannel)); + } + + return OT_ERROR_NONE; +} diff --git a/modules/openthread/platform/settings.c b/modules/openthread/platform/settings.c new file mode 100644 index 000000000000..ce5fefde0c8d --- /dev/null +++ b/modules/openthread/platform/settings.c @@ -0,0 +1,329 @@ +/* + * Copyright (c) 2019 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include <zephyr/kernel.h> +#include <zephyr/logging/log.h> +#include <zephyr/settings/settings.h> +#include <zephyr/random/random.h> + +#include <openthread/platform/settings.h> + +LOG_MODULE_REGISTER(net_otPlat_settings, CONFIG_OPENTHREAD_L2_LOG_LEVEL); + +#define OT_SETTINGS_ROOT_KEY "ot" +#define OT_SETTINGS_MAX_PATH_LEN 32 + +struct ot_setting_delete_ctx { + /* Setting subtree to delete. */ + const char *subtree; + + /* Current entry index, used to iterate over multiple setting + * instances. + */ + int index; + + /* Target index to delete. -1 to delete entire subtree. */ + int target_index; + + /* Operation result. */ + int status; + + /* Indicates if delete subtree root. */ + bool delete_subtree_root; +}; + +static int ot_setting_delete_cb(const char *key, size_t len, + settings_read_cb read_cb, void *cb_arg, + void *param) +{ + int ret; + char path[OT_SETTINGS_MAX_PATH_LEN]; + struct ot_setting_delete_ctx *ctx = + (struct ot_setting_delete_ctx *)param; + + ARG_UNUSED(len); + ARG_UNUSED(read_cb); + ARG_UNUSED(cb_arg); + + if ((ctx->target_index != -1) && (ctx->target_index != ctx->index)) { + ctx->index++; + return 0; + } + + if (key == NULL && ctx->delete_subtree_root == false) { + return 0; + } + + ret = snprintk(path, sizeof(path), "%s%s%s", ctx->subtree, + key ? "/" : "", key ? key : ""); + __ASSERT(ret < sizeof(path), "Setting path buffer too small."); + + LOG_DBG("Removing: %s", path); + + ret = settings_delete(path); + if (ret != 0) { + LOG_ERR("Failed to remove setting %s, ret %d", path, + ret); + __ASSERT_NO_MSG(false); + } + + ctx->status = 0; + + if (ctx->target_index == ctx->index) { + /* Break the loop on index match, otherwise it was -1 + * (delete all). + */ + return 1; + } + + return 0; +} + +static int ot_setting_delete_subtree(int key, int index, bool delete_subtree_root) +{ + int ret; + char subtree[OT_SETTINGS_MAX_PATH_LEN]; + struct ot_setting_delete_ctx delete_ctx = { + .subtree = subtree, + .status = -ENOENT, + .target_index = index, + .delete_subtree_root = delete_subtree_root, + }; + + if (key == -1) { + ret = snprintk(subtree, sizeof(subtree), "%s", + OT_SETTINGS_ROOT_KEY); + } else { + ret = snprintk(subtree, sizeof(subtree), "%s/%x", + OT_SETTINGS_ROOT_KEY, key); + } + __ASSERT(ret < sizeof(subtree), "Setting path buffer too small."); + + ret = settings_load_subtree_direct(subtree, ot_setting_delete_cb, + &delete_ctx); + if (ret != 0) { + LOG_ERR("Failed to delete OT subtree %s, index %d, ret %d", + subtree, index, ret); + __ASSERT_NO_MSG(false); + } + + return delete_ctx.status; +} + +static int ot_setting_exists_cb(const char *key, size_t len, + settings_read_cb read_cb, void *cb_arg, + void *param) +{ + bool *exists = (bool *)param; + + ARG_UNUSED(len); + ARG_UNUSED(read_cb); + ARG_UNUSED(cb_arg); + ARG_UNUSED(key); + + *exists = true; + + return 1; +} + +static bool ot_setting_exists(const char *path) +{ + bool exists = false; + + (void)settings_load_subtree_direct(path, ot_setting_exists_cb, &exists); + + return exists; +} + +struct ot_setting_read_ctx { + /* Buffer for the setting. */ + uint8_t *value; + + /* Buffer length on input, setting length read on output. */ + uint16_t *length; + + /* Current entry index, used to iterate over multiple setting + * instances. + */ + int index; + + /* Target instance to read. */ + int target_index; + + /* Operation result. */ + int status; +}; + +static int ot_setting_read_cb(const char *key, size_t len, + settings_read_cb read_cb, void *cb_arg, + void *param) +{ + int ret; + struct ot_setting_read_ctx *ctx = (struct ot_setting_read_ctx *)param; + + ARG_UNUSED(len); + ARG_UNUSED(read_cb); + ARG_UNUSED(cb_arg); + + if (ctx->target_index != ctx->index) { + ctx->index++; + return 0; + } + + /* Found setting, break the loop. */ + + if ((ctx->value == NULL) || (ctx->length == NULL)) { + goto out; + } + + if (*(ctx->length) < len) { + len = *(ctx->length); + } + + ret = read_cb(cb_arg, ctx->value, len); + if (ret <= 0) { + LOG_ERR("Failed to read the setting, ret: %d", ret); + ctx->status = -EIO; + return 1; + } + +out: + if (ctx->length != NULL) { + *(ctx->length) = len; + } + + ctx->status = 0; + + return 1; +} + +/* OpenThread APIs */ + +void otPlatSettingsInit(otInstance *aInstance, const uint16_t *aSensitiveKeys, + uint16_t aSensitiveKeysLength) +{ + int ret; + + ARG_UNUSED(aInstance); + ARG_UNUSED(aSensitiveKeys); + ARG_UNUSED(aSensitiveKeysLength); + + ret = settings_subsys_init(); + if (ret != 0) { + LOG_ERR("settings_subsys_init failed (ret %d)", ret); + } +} + +otError otPlatSettingsGet(otInstance *aInstance, uint16_t aKey, int aIndex, + uint8_t *aValue, uint16_t *aValueLength) +{ + int ret; + char path[OT_SETTINGS_MAX_PATH_LEN]; + struct ot_setting_read_ctx read_ctx = { + .value = aValue, + .length = (uint16_t *)aValueLength, + .status = -ENOENT, + .target_index = aIndex + }; + + ARG_UNUSED(aInstance); + + LOG_DBG("%s Entry aKey %u aIndex %d", __func__, aKey, aIndex); + + ret = snprintk(path, sizeof(path), "%s/%x", OT_SETTINGS_ROOT_KEY, aKey); + __ASSERT(ret < sizeof(path), "Setting path buffer too small."); + + ret = settings_load_subtree_direct(path, ot_setting_read_cb, &read_ctx); + if (ret != 0) { + LOG_ERR("Failed to load OT setting aKey %d, aIndex %d, ret %d", + aKey, aIndex, ret); + } + + if (read_ctx.status != 0) { + LOG_DBG("aKey %u aIndex %d not found", aKey, aIndex); + return OT_ERROR_NOT_FOUND; + } + + return OT_ERROR_NONE; +} + +otError otPlatSettingsSet(otInstance *aInstance, uint16_t aKey, + const uint8_t *aValue, uint16_t aValueLength) +{ + int ret; + char path[OT_SETTINGS_MAX_PATH_LEN]; + + ARG_UNUSED(aInstance); + + LOG_DBG("%s Entry aKey %u", __func__, aKey); + + (void)ot_setting_delete_subtree(aKey, -1, false); + + ret = snprintk(path, sizeof(path), "%s/%x", OT_SETTINGS_ROOT_KEY, aKey); + __ASSERT(ret < sizeof(path), "Setting path buffer too small."); + + ret = settings_save_one(path, aValue, aValueLength); + if (ret != 0) { + LOG_ERR("Failed to store setting %d, ret %d", aKey, ret); + return OT_ERROR_NO_BUFS; + } + + return OT_ERROR_NONE; +} + +otError otPlatSettingsAdd(otInstance *aInstance, uint16_t aKey, + const uint8_t *aValue, uint16_t aValueLength) +{ + int ret; + char path[OT_SETTINGS_MAX_PATH_LEN]; + + ARG_UNUSED(aInstance); + + LOG_DBG("%s Entry aKey %u", __func__, aKey); + + do { + ret = snprintk(path, sizeof(path), "%s/%x/%08x", + OT_SETTINGS_ROOT_KEY, aKey, sys_rand32_get()); + __ASSERT(ret < sizeof(path), "Setting path buffer too small."); + } while (ot_setting_exists(path)); + + ret = settings_save_one(path, aValue, aValueLength); + if (ret != 0) { + LOG_ERR("Failed to store setting %d, ret %d", aKey, ret); + return OT_ERROR_NO_BUFS; + } + + return OT_ERROR_NONE; +} + +otError otPlatSettingsDelete(otInstance *aInstance, uint16_t aKey, int aIndex) +{ + int ret; + + ARG_UNUSED(aInstance); + + LOG_DBG("%s Entry aKey %u aIndex %d", __func__, aKey, aIndex); + + ret = ot_setting_delete_subtree(aKey, aIndex, true); + if (ret != 0) { + LOG_DBG("Entry not found aKey %u aIndex %d", aKey, aIndex); + return OT_ERROR_NOT_FOUND; + } + + return OT_ERROR_NONE; +} + +void otPlatSettingsWipe(otInstance *aInstance) +{ + ARG_UNUSED(aInstance); + + (void)ot_setting_delete_subtree(-1, -1, true); +} + +void otPlatSettingsDeinit(otInstance *aInstance) +{ + ARG_UNUSED(aInstance); +} diff --git a/modules/openthread/platform/shell.c b/modules/openthread/platform/shell.c new file mode 100644 index 000000000000..22f11d31f3eb --- /dev/null +++ b/modules/openthread/platform/shell.c @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include <zephyr/kernel.h> +#include <stdio.h> +#include <zephyr/net/openthread.h> +#include <zephyr/sys/printk.h> +#include <zephyr/shell/shell.h> +#include <zephyr/shell/shell_uart.h> + +#include <openthread/cli.h> +#include <openthread/instance.h> + +#include "platform-zephyr.h" + +#define OT_SHELL_BUFFER_SIZE CONFIG_SHELL_CMD_BUFF_SIZE + +static char rx_buffer[OT_SHELL_BUFFER_SIZE]; + +static const struct shell *shell_p; +static bool is_shell_initialized; + +static int ot_console_cb(void *context, const char *format, va_list arg) +{ + ARG_UNUSED(context); + + if (shell_p == NULL) { + return 0; + } + + shell_vfprintf(shell_p, SHELL_NORMAL, format, arg); + + return 0; +} + +#define SHELL_HELP_OT "OpenThread subcommands\n" \ + "Use \"ot help\" to get the list of subcommands" + +static int ot_cmd(const struct shell *sh, size_t argc, char *argv[]) +{ + char *buf_ptr = rx_buffer; + size_t buf_len = OT_SHELL_BUFFER_SIZE; + size_t arg_len = 0; + int i; + + if (!is_shell_initialized) { + return -ENOEXEC; + } + + for (i = 1; i < argc; i++) { + if (arg_len) { + buf_len -= arg_len + 1; + if (buf_len) { + buf_ptr[arg_len] = ' '; + } + buf_ptr += arg_len + 1; + } + + arg_len = snprintk(buf_ptr, buf_len, "%s", argv[i]); + + if (arg_len >= buf_len) { + shell_fprintf(sh, SHELL_WARNING, + "OT shell buffer full\n"); + return -ENOEXEC; + } + } + + if (i == argc) { + buf_len -= arg_len; + } + + shell_p = sh; + + openthread_api_mutex_lock(openthread_get_default_context()); + otCliInputLine(rx_buffer); + openthread_api_mutex_unlock(openthread_get_default_context()); + + return 0; +} + +SHELL_CMD_ARG_REGISTER(ot, NULL, SHELL_HELP_OT, ot_cmd, 2, 255); + +void platformShellInit(otInstance *aInstance) +{ + if (IS_ENABLED(CONFIG_SHELL_BACKEND_SERIAL)) { + shell_p = shell_backend_uart_get_ptr(); + } else { + shell_p = NULL; + } + + otCliInit(aInstance, ot_console_cb, NULL); + is_shell_initialized = true; +} diff --git a/modules/openthread/platform/spi.c b/modules/openthread/platform/spi.c new file mode 100644 index 000000000000..954a01d1a3bb --- /dev/null +++ b/modules/openthread/platform/spi.c @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2018 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include <zephyr/kernel.h> +#include <stdio.h> +#include <stdlib.h> +#include <openthread/platform/spi-slave.h> + +#include "platform-zephyr.h" + +/* Spi-slave stubs */ + +otError otPlatSpiSlaveEnable( + otPlatSpiSlaveTransactionCompleteCallback aCompleteCallback, + otPlatSpiSlaveTransactionProcessCallback aProcessCallback, + void *aContext) +{ + ARG_UNUSED(aCompleteCallback); + ARG_UNUSED(aProcessCallback); + ARG_UNUSED(aContext); + + return OT_ERROR_NOT_IMPLEMENTED; +} + +void otPlatSpiSlaveDisable(void) +{ + /* Intentionally empty */ +} + +otError otPlatSpiSlavePrepareTransaction( + uint8_t *anOutputBuf, + uint16_t anOutputBufLen, + uint8_t *anInputBuf, + uint16_t anInputBufLen, + bool aRequestTransactionFlag +) +{ + ARG_UNUSED(anOutputBuf); + ARG_UNUSED(anOutputBufLen); + ARG_UNUSED(anInputBuf); + ARG_UNUSED(anInputBufLen); + ARG_UNUSED(aRequestTransactionFlag); + + return OT_ERROR_NOT_IMPLEMENTED; +} diff --git a/modules/openthread/platform/uart.c b/modules/openthread/platform/uart.c new file mode 100644 index 000000000000..31afdac6bac8 --- /dev/null +++ b/modules/openthread/platform/uart.c @@ -0,0 +1,254 @@ +/* + * Copyright (c) 2020 Tridonic GmbH & Co KG + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define LOG_LEVEL CONFIG_OPENTHREAD_PLATFORM_LOG_LEVEL +#define LOG_MODULE_NAME net_otPlat_uart + +#include <zephyr/logging/log.h> +LOG_MODULE_REGISTER(LOG_MODULE_NAME); + +#include <zephyr/kernel.h> +#include <stdio.h> +#include <stdlib.h> + +#include <zephyr/drivers/uart.h> + +#include <zephyr/sys/ring_buffer.h> +#include <zephyr/sys/atomic.h> + +#include <zephyr/usb/usb_device.h> + +#include <openthread/ncp.h> +#include <openthread-system.h> +#include <utils/uart.h> + +#include "platform-zephyr.h" + +struct openthread_uart { + struct ring_buf *rx_ringbuf; + const struct device *dev; + atomic_t tx_busy; + atomic_t tx_finished; +}; + +#define OT_UART_DEFINE(_name, _ringbuf_size) \ + RING_BUF_DECLARE(_name##_rx_ringbuf, _ringbuf_size); \ + static struct openthread_uart _name = { \ + .rx_ringbuf = &_name##_rx_ringbuf, \ + } + +OT_UART_DEFINE(ot_uart, CONFIG_OPENTHREAD_COPROCESSOR_UART_RING_BUFFER_SIZE); + +#define RX_FIFO_SIZE 128 + +static bool is_panic_mode; +static const uint8_t *write_buffer; +static uint16_t write_length; + +static void uart_rx_handle(const struct device *dev) +{ + uint8_t *data; + uint32_t len; + uint32_t rd_len; + bool new_data = false; + + do { + len = ring_buf_put_claim( + ot_uart.rx_ringbuf, &data, + ot_uart.rx_ringbuf->size); + if (len > 0) { + rd_len = uart_fifo_read(dev, data, len); + if (rd_len > 0) { + new_data = true; + } + + int err = ring_buf_put_finish( + ot_uart.rx_ringbuf, rd_len); + (void)err; + __ASSERT_NO_MSG(err == 0); + } else { + uint8_t dummy; + + /* No space in the ring buffer - consume byte. */ + LOG_WRN("RX ring buffer full."); + + rd_len = uart_fifo_read(dev, &dummy, 1); + } + } while (rd_len && (rd_len == len)); + + if (new_data) { + otSysEventSignalPending(); + } +} + +static void uart_tx_handle(const struct device *dev) +{ + uint32_t len; + + if (write_length) { + len = uart_fifo_fill(dev, write_buffer, write_length); + write_buffer += len; + write_length -= len; + } else { + uart_irq_tx_disable(dev); + ot_uart.tx_busy = 0; + atomic_set(&(ot_uart.tx_finished), 1); + otSysEventSignalPending(); + } +} + +static void uart_callback(const struct device *dev, void *user_data) +{ + ARG_UNUSED(user_data); + + while (uart_irq_update(dev) && uart_irq_is_pending(dev)) { + + if (uart_irq_rx_ready(dev)) { + uart_rx_handle(dev); + } + + if (uart_irq_tx_ready(dev) && + atomic_get(&ot_uart.tx_busy) == 1) { + uart_tx_handle(dev); + } + } +} + +void otPlatUartReceived(const uint8_t *aBuf, uint16_t aBufLength) +{ + otNcpHdlcReceive(aBuf, aBufLength); +} + +void otPlatUartSendDone(void) +{ + otNcpHdlcSendDone(); +} + +void platformUartProcess(otInstance *aInstance) +{ + uint32_t len = 0; + const uint8_t *data; + + /* Process UART RX */ + while ((len = ring_buf_get_claim( + ot_uart.rx_ringbuf, + (uint8_t **)&data, + ot_uart.rx_ringbuf->size)) > 0) { + int err; + + otPlatUartReceived(data, len); + err = ring_buf_get_finish( + ot_uart.rx_ringbuf, + len); + (void)err; + __ASSERT_NO_MSG(err == 0); + } + + /* Process UART TX */ + if (ot_uart.tx_finished) { + LOG_DBG("UART TX done"); + otPlatUartSendDone(); + ot_uart.tx_finished = 0; + } +} + +otError otPlatUartEnable(void) +{ + ot_uart.dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_ot_uart)); + + if (!device_is_ready(ot_uart.dev)) { + LOG_ERR("UART device not ready"); + return OT_ERROR_FAILED; + } + + uart_irq_callback_user_data_set(ot_uart.dev, + uart_callback, + (void *)&ot_uart); + + if (DT_NODE_HAS_COMPAT(DT_CHOSEN(zephyr_ot_uart), zephyr_cdc_acm_uart)) { + int ret; + + ret = usb_enable(NULL); + if (ret != 0 && ret != -EALREADY) { + LOG_ERR("Failed to enable USB"); + return OT_ERROR_FAILED; + } + + /* Data Carrier Detect Modem - mark connection as established */ + (void)uart_line_ctrl_set(ot_uart.dev, UART_LINE_CTRL_DCD, 1); + /* Data Set Ready - the NCP SoC is ready to communicate */ + (void)uart_line_ctrl_set(ot_uart.dev, UART_LINE_CTRL_DSR, 1); + } + + uart_irq_rx_enable(ot_uart.dev); + + return OT_ERROR_NONE; +} + +otError otPlatUartDisable(void) +{ + if (DT_NODE_HAS_COMPAT(DT_CHOSEN(zephyr_ot_uart), zephyr_cdc_acm_uart)) { + int ret = usb_disable(); + + if (ret) { + LOG_WRN("Failed to disable USB (%d)", ret); + } + } + + uart_irq_tx_disable(ot_uart.dev); + uart_irq_rx_disable(ot_uart.dev); + return OT_ERROR_NONE; +} + +otError otPlatUartSend(const uint8_t *aBuf, uint16_t aBufLength) +{ + if (aBuf == NULL) { + return OT_ERROR_FAILED; + } + + if (atomic_cas(&(ot_uart.tx_busy), 0, 1)) { + write_buffer = aBuf; + write_length = aBufLength; + + if (is_panic_mode) { + /* In panic mode all data have to be send immediately + * without using interrupts + */ + otPlatUartFlush(); + } else { + uart_irq_tx_enable(ot_uart.dev); + } + return OT_ERROR_NONE; + } + + return OT_ERROR_BUSY; +} + +otError otPlatUartFlush(void) +{ + otError result = OT_ERROR_NONE; + + if (write_length) { + for (size_t i = 0; i < write_length; i++) { + uart_poll_out(ot_uart.dev, *(write_buffer+i)); + } + } + + ot_uart.tx_busy = 0; + atomic_set(&(ot_uart.tx_finished), 1); + otSysEventSignalPending(); + return result; +} + +void platformUartPanic(void) +{ + is_panic_mode = true; + /* In panic mode data are send without using interrupts. + * Reception in this mode is not supported. + */ + uart_irq_tx_disable(ot_uart.dev); + uart_irq_rx_disable(ot_uart.dev); +} diff --git a/scripts/ci/license_allow_list.yaml b/scripts/ci/license_allow_list.yaml index 387dae7d8d0d..60c35b5a8338 100644 --- a/scripts/ci/license_allow_list.yaml +++ b/scripts/ci/license_allow_list.yaml @@ -48,6 +48,7 @@ Apache-2.0: | ^nrf/tests/bluetooth/tester/src/bttester.c$ ^nrf/subsys/nrf_security/src/legacy ^nrf/modules/trusted-firmware-m/fault.c + ^nrf/modules/openthread/ ^nrf/samples/net/http_server/src/main.c ^nrf/tests/subsys/suit/common/tls_config/user-tls-conf.h curl: "^nrf/ext/" diff --git a/scripts/ci/tags.yaml b/scripts/ci/tags.yaml index 9ebedd5b69fa..3b0fbf0104d7 100644 --- a/scripts/ci/tags.yaml +++ b/scripts/ci/tags.yaml @@ -275,6 +275,7 @@ ci_samples_openthread: - nrf/drivers/hw_cc3xx/ - nrf/drivers/mpsl/ - nrf/modules/nrfxlib/nrf_802154/ + - nrf/modules/openthread/ - nrf/samples/openthread/ - nrf/subsys/ieee802154/ - nrf/subsys/mpsl/ @@ -290,7 +291,6 @@ ci_samples_openthread: - nrfxlib/openthread/ - zephyr/include/zephyr/net/ - zephyr/modules/mbedtls/ - - zephyr/modules/openthread/ - zephyr/soc/nordic/ - zephyr/subsys/net/ - zephyr/subsys/settings/ @@ -1461,6 +1461,7 @@ ci_applications_protocols_serialization: - nrf/applications/protocols_serialization/ - nrf/drivers/mpsl/ - nrf/modules/nrfxlib/nrf_802154/ + - nrf/modules/openthread/ - nrf/subsys/bluetooth/ - nrf/subsys/ieee802154/ - nrf/subsys/logging/ @@ -1473,7 +1474,6 @@ ci_applications_protocols_serialization: - nrfxlib/nrf_802154/ - nrfxlib/nrf_rpc/ - zephyr/drivers/ieee802154/ - - zephyr/modules/openthread/ - zephyr/subsys/bluetooth/ - zephyr/subsys/net/ - zephyr/subsys/retention/