From 7c389893090cbd884a7bcfaa4f2e9ffd308893f3 Mon Sep 17 00:00:00 2001 From: Guo Jia Cheng Date: Fri, 2 Apr 2021 14:49:49 +0800 Subject: [PATCH] OpenThread: add OpenThread and porting * Adds the OpenThread submodule. * Adds porting on ESP32. * Adds the OpenThread cli example. --- .gitlab/CODEOWNERS | 2 + .gitmodules | 4 + components/openthread/CMakeLists.txt | 67 ++++ components/openthread/Kconfig | 68 ++++ components/openthread/component.mk | 59 ++++ .../openthread/include/esp_openthread.h | 97 ++++++ .../openthread/include/esp_openthread_lock.h | 67 ++++ .../openthread/include/esp_openthread_types.h | 93 ++++++ .../include/openthread-core-esp32x-config.h | 281 +++++++++++++++++ components/openthread/openthread | 1 + components/openthread/port/esp_openthread.cpp | 106 +++++++ .../openthread/port/esp_openthread_alarm.c | 103 ++++++ .../openthread/port/esp_openthread_flash.c | 70 +++++ .../openthread/port/esp_openthread_lock.c | 52 +++ .../openthread/port/esp_openthread_logging.c | 68 ++++ .../openthread/port/esp_openthread_misc.c | 87 ++++++ .../port/esp_openthread_radio_uart.cpp | 284 +++++++++++++++++ .../openthread/port/esp_openthread_uart.c | 129 ++++++++ .../port/esp_uart_spinel_interface.cpp | 295 ++++++++++++++++++ .../private_include/esp_openthread_alarm.h | 43 +++ .../esp_openthread_common_macro.h | 31 ++ .../esp_openthread_radio_uart.h | 66 ++++ .../private_include/esp_openthread_uart.h | 76 +++++ .../esp_uart_spinel_interface.hpp | 157 ++++++++++ docs/doxygen/Doxyfile_common | 5 + docs/en/COPYRIGHT.rst | 2 + examples/openthread/ot_cli/CMakeLists.txt | 6 + examples/openthread/ot_cli/Makefile | 8 + examples/openthread/ot_cli/README.md | 86 +++++ .../openthread/ot_cli/main/CMakeLists.txt | 2 + examples/openthread/ot_cli/main/component.mk | 8 + examples/openthread/ot_cli/main/ot_esp_cli.c | 129 ++++++++ examples/openthread/ot_cli/partitions.csv | 6 + examples/openthread/ot_cli/sdkconfig.defaults | 33 ++ tools/ci/check_public_headers_exceptions.txt | 2 + 35 files changed, 2593 insertions(+) create mode 100644 components/openthread/CMakeLists.txt create mode 100644 components/openthread/Kconfig create mode 100644 components/openthread/component.mk create mode 100644 components/openthread/include/esp_openthread.h create mode 100644 components/openthread/include/esp_openthread_lock.h create mode 100644 components/openthread/include/esp_openthread_types.h create mode 100644 components/openthread/include/openthread-core-esp32x-config.h create mode 160000 components/openthread/openthread create mode 100644 components/openthread/port/esp_openthread.cpp create mode 100644 components/openthread/port/esp_openthread_alarm.c create mode 100644 components/openthread/port/esp_openthread_flash.c create mode 100644 components/openthread/port/esp_openthread_lock.c create mode 100644 components/openthread/port/esp_openthread_logging.c create mode 100644 components/openthread/port/esp_openthread_misc.c create mode 100644 components/openthread/port/esp_openthread_radio_uart.cpp create mode 100644 components/openthread/port/esp_openthread_uart.c create mode 100644 components/openthread/port/esp_uart_spinel_interface.cpp create mode 100644 components/openthread/private_include/esp_openthread_alarm.h create mode 100644 components/openthread/private_include/esp_openthread_common_macro.h create mode 100644 components/openthread/private_include/esp_openthread_radio_uart.h create mode 100644 components/openthread/private_include/esp_openthread_uart.h create mode 100644 components/openthread/private_include/esp_uart_spinel_interface.hpp create mode 100644 examples/openthread/ot_cli/CMakeLists.txt create mode 100644 examples/openthread/ot_cli/Makefile create mode 100644 examples/openthread/ot_cli/README.md create mode 100644 examples/openthread/ot_cli/main/CMakeLists.txt create mode 100644 examples/openthread/ot_cli/main/component.mk create mode 100644 examples/openthread/ot_cli/main/ot_esp_cli.c create mode 100644 examples/openthread/ot_cli/partitions.csv create mode 100644 examples/openthread/ot_cli/sdkconfig.defaults diff --git a/.gitlab/CODEOWNERS b/.gitlab/CODEOWNERS index 061ffe5cb6..f680964b8b 100644 --- a/.gitlab/CODEOWNERS +++ b/.gitlab/CODEOWNERS @@ -116,6 +116,7 @@ /components/nghttp/ @esp-idf-codeowners/app-utilities /components/nvs_flash/ @esp-idf-codeowners/storage /components/openssl/ @esp-idf-codeowners/network +/components/openthread/ @esp-idf-codeowners/ieee802154 /components/partition_table/ @esp-idf-codeowners/system /components/perfmon/ @esp-idf-codeowners/tools /components/protobuf-c/ @esp-idf-codeowners/app-utilities @@ -147,6 +148,7 @@ /examples/ethernet/ @esp-idf-codeowners/network /examples/get-started/ @esp-idf-codeowners/system /examples/mesh/ @esp-idf-codeowners/wifi +/examples/openthread/ @esp-idf-codeowners/ieee802154 /examples/peripherals/ @esp-idf-codeowners/peripherals /examples/protocols/ @esp-idf-codeowners/network @esp-idf-codeowners/app-utilities /examples/provisioning/ @esp-idf-codeowners/app-utilities diff --git a/.gitmodules b/.gitmodules index 9b3e61bf5b..7ec551cf9b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -90,3 +90,7 @@ [submodule "components/cmock/CMock"] path = components/cmock/CMock url = ../../ThrowTheSwitch/CMock.git + +[submodule "components/openthread/openthread"] + path = components/openthread/openthread + url = ../../espressif/openthread.git diff --git a/components/openthread/CMakeLists.txt b/components/openthread/CMakeLists.txt new file mode 100644 index 0000000000..5776d0a2ec --- /dev/null +++ b/components/openthread/CMakeLists.txt @@ -0,0 +1,67 @@ +if(CONFIG_OPENTHREAD_ENABLED) + set(public_include_dirs + "include" + "openthread/include") + + set(private_include_dirs + "openthread/include/openthread" + "openthread/src" + "openthread/src/core" + "openthread/src/lib/hdlc" + "openthread/src/lib/spinel" + "openthread/src/ncp" + "private_include") + + set(src_dirs + "openthread/src/cli" + "openthread/src/core/api" + "openthread/src/core/coap" + "openthread/src/core/common" + "openthread/src/core/crypto" + "openthread/src/core/diags" + "openthread/src/core/mac" + "openthread/src/core/meshcop" + "openthread/src/core/net" + "openthread/src/core/radio" + "openthread/src/core/thread" + "openthread/src/core/utils" + "openthread/src/lib/platform" + "openthread/src/lib/hdlc" + "openthread/src/lib/spinel" + "port") + + set(exclude_srcs "openthread/src/core/common/extension_example.cpp") + + if(CONFIG_OPENTHREAD_FTD) + set(device_type "OPENTHREAD_FTD=1") + elseif(CONFIG_OPENTHREAD_MTD) + set(device_type "OPENTHREAD_MTD=1") + elseif(CONFIG_OPENTHREAD_RADIO) + set(device_type "OPENTHREAD_RADIO=1") + endif() +endif() + +execute_process( + COMMAND git rev-parse --short HEAD + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + OUTPUT_VARIABLE IDF_VERSION_FOR_OPENTHREAD_PACKAGE OUTPUT_STRIP_TRAILING_WHITESPACE +) + +execute_process( + COMMAND git rev-parse --short HEAD + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/openthread + OUTPUT_VARIABLE OPENTHREAD_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE +) + +idf_component_register(SRC_DIRS "${src_dirs}" + EXCLUDE_SRCS "${exclude_srcs}" + INCLUDE_DIRS "${public_include_dirs}" + PRIV_INCLUDE_DIRS "${private_include_dirs}" + REQUIRES mbedtls spi_flash) + +if(CONFIG_OPENTHREAD_ENABLED) + target_compile_definitions(${COMPONENT_LIB} PRIVATE + "OPENTHREAD_CONFIG_FILE=\"openthread-core-esp32x-config.h\"" + "PACKAGE_VERSION=\"${IDF_VERSION_FOR_OPENTHREAD_PACKAGE}-${OPENTHREAD_VERSION}\"" + "${device_type}") +endif() diff --git a/components/openthread/Kconfig b/components/openthread/Kconfig new file mode 100644 index 0000000000..750e291c3e --- /dev/null +++ b/components/openthread/Kconfig @@ -0,0 +1,68 @@ +menu "OpenThread" + + config OPENTHREAD_ENABLED + bool "OpenThread" + default n + help + Select this option to enable OpenThread and show the submenu with OpenThread configuration choices. + + choice OPENTHREAD_DEVICE_TYPE + prompt "Config the Thread device type" + depends on OPENTHREAD_ENABLED + default OPENTHREAD_FTD + help + OpenThread can be configured to different device types (FTD, MTD, Radio) + + config OPENTHREAD_FTD + bool "Full Thread Device" + help + Select this to enable Full Thread Device which can act as router and leader in a Thread network. + + config OPENTHREAD_MTD + bool "Minimal Thread Device" + help + Select this to enable Minimal Thread Device which can only act as end device in a Thread network. + This will reduce the code size of the OpenThread stack. + + config OPENTHREAD_RADIO + bool "Radio Only Device" + help + Select this to enable Radio Only Device which cannot can only forward 15.4 packets to the host. + The OpenThread stack will be run on the host and OpenThread will have minimal footprint on the + radio only device. + endchoice + + config OPENTHREAD_DIAG + bool "Enable diag" + depends on OPENTHREAD_ENABLED + default y + help + Select this option to enable Diag in OpenThread. This will enable diag mode and a series of diag commands + in the OpenThread command line. These commands allow users to manipulate low-level features of the storage + and 15.4 radio. + + config OPENTHREAD_COMMISSIONER + bool "Enable Commissioner" + depends on OPENTHREAD_ENABLED + default n + help + Select this option to enable commissioner in OpenThread. This will enable the device to act as a + commissioner in the Thread network. A commissioner checks the pre-shared key from a joining device with + the Thread commissioning protocol and shares the network parameter with the joining device upon success. + + config OPENTHREAD_JOINER + bool "Enable Joiner" + depends on OPENTHREAD_ENABLED + default n + help + Select this option to enable Joiner in OpenThread. This allows a device to join the Thread network with a + pre-shared key using the Thread commissioning protocol. + + config OPENTHREAD_PARTITION_NAME + string "The partition for OpenThread to store its network data" + depends on OPENTHREAD_ENABLED + default "ot_storage" + help + The storage size should be at least 8192 bytes. + +endmenu diff --git a/components/openthread/component.mk b/components/openthread/component.mk new file mode 100644 index 0000000000..2920a0b3e3 --- /dev/null +++ b/components/openthread/component.mk @@ -0,0 +1,59 @@ +ifdef CONFIG_OPENTHREAD_ENABLED + +COMPONENT_ADD_INCLUDEDIRS := \ + openthread/include \ + include + +COMPONENT_PRIV_INCLUDEDIRS := \ + openthread/src \ + openthread/src/core \ + openthread/src/lib/hdlc \ + openthread/src/lib/spinel \ + openthread/src/ncp \ + private_include + +COMPONENT_SRCDIRS := \ + openthread/src/cli \ + openthread/src/core \ + openthread/src/core/api \ + openthread/src/core/coap \ + openthread/src/core/common \ + openthread/src/core/crypto \ + openthread/src/core/diags \ + openthread/src/core/mac \ + openthread/src/core/meshcop \ + openthread/src/core/net \ + openthread/src/core/radio \ + openthread/src/core/thread \ + openthread/src/core/utils \ + openthread/src/lib/hdlc \ + openthread/src/lib/platform \ + openthread/src/lib/spinel \ + port + +COMPONENT_OBJEXCLUDE := \ + openthread/src/core/common/extension_example.o + +IDF_VERSION_FOR_OPENTHREAD_PACKAGE := $(shell git -C $(COMPONENT_PATH) rev-parse --short HEAD) +OPENTHREAD_VERSION := $(shell git -C $(COMPONENT_PATH)/openthread rev-parse --short HEAD) +OPENTHREAD_PACKAGE_VERSION := $(IDF_VERSION_FOR_OPENTHREAD_PACKAGE)-$(OPENTHREAD_VERSION) + +COMMON_FLAGS := \ + -DOPENTHREAD_CONFIG_FILE=\ \ + -DPACKAGE_VERSION=\"OPENTHREAD_PACKAGE_VERSION\" + +ifdef CONFIG_OPENTHREAD_FTD + COMMON_FLAGS += -DOPENTHREAD_FTD=1 +else ifdef CONFIG_OPENTHREAD_MTD + COMMON_FLAGS += -DOPENTHREAD_MTD=1 +else ifdef CONFIG_OPENTHREAD_RADIO + COMMON_FLAGS += -DOPENTHREAD_RADIO=1 +endif + +CFLAGS += $(COMMON_FLAGS) + +CXXFLAGS += $(COMMON_FLAGS) + +CPPFLAGS += $(COMMON_FLAGS) + +endif diff --git a/components/openthread/include/esp_openthread.h b/components/openthread/include/esp_openthread.h new file mode 100644 index 0000000000..35a7d44383 --- /dev/null +++ b/components/openthread/include/esp_openthread.h @@ -0,0 +1,97 @@ +// Copyright 2021 Espressif Systems (Shanghai) CO LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License + +#pragma once + +#include "esp_err.h" +#include "esp_openthread_types.h" +#include "openthread/error.h" +#include "openthread/instance.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initializes the platform-specific support for the OpenThread stack. + * + * @note This function is not called by and will not call the OpenThread library. + * The user needs to call otInstanceInitSingle to intialize the OpenThread + * stack after calling this fucntion. + * + * @param[in] init_config The initialization configuration. + * + * @return + * - ESP_OK on success + * - ESP_ERR_NO_MEM if allocation has failed + * - ESP_ERR_INVALID_ARG if radio or host connection mode not supported + * - ESP_ERR_INVALID_STATE if already initialized + * + */ +esp_err_t esp_openthread_platform_init(const esp_openthread_platform_config_t *init_config); + +/** + * This function performs all platform-specific deinitialization for OpenThread's drivers. + * + * @note This function is not called by the OpenThread library. Instead, the user should + * call this function when deinitialization of OpenThread's drivers is most appropriate. + * + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if not initialized + * + */ +esp_err_t esp_openthread_platform_deinit(void); + +/** + * @brief This function acquires the underlying OpenThread instance. + * + * @note This function can be called on other tasks without lock. + * + * @return The OpenThread instance pointer + * + */ +otInstance *esp_openthread_get_instance(void); + +/** + * @brief This function updates the platform fds and timeouts + * + * @note This function will not update the OpenThread core stack pending events. + * The users need to call `otTaskletsArePending` to check whether there being + * pending OpenThread tasks. + * + * @param[inout] mainloop The main loop context. + * + */ +void esp_openthread_platform_update(esp_openthread_mainloop_context_t *mainloop); + +/** + * @brief This function performs the OpenThread related platform process (radio, uart, alarm etc.) + * + * @note This function will call the OpenThread core stack process functions. + * The users need to call `otTaskletsProcess` by self. + * + * @param[in] instance The OpenThread instance. + * @param[in] mainloop The main loop context. + * + * @return + * - ESP_OK on success + * - ESP_FAIL on failure + * + */ +esp_err_t esp_openthread_platform_process(otInstance *instance, const esp_openthread_mainloop_context_t *mainloop); + +#ifdef __cplusplus +} // end of extern "C" +#endif diff --git a/components/openthread/include/esp_openthread_lock.h b/components/openthread/include/esp_openthread_lock.h new file mode 100644 index 0000000000..96da77b70c --- /dev/null +++ b/components/openthread/include/esp_openthread_lock.h @@ -0,0 +1,67 @@ +// Copyright 2021 Espressif Systems (Shanghai) CO LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License + +#pragma once + +#include + +#include "esp_err.h" +#include "freertos/FreeRTOS.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief This function initializes the OpenThread API lock. + * + * @return + * - ESP_OK on success + * - ESP_ERR_NO_MEM if allocation has failed + * - ESP_ERR_INVALID_STATE if already initialized + * + */ +esp_err_t esp_openthread_lock_init(void); + +/** + * This function deinitializes the OpenThread API lock. + * + */ +void esp_openthread_lock_deinit(void); + +/** + * @brief This functions acquires the OpenThread API lock. + * + * @note Every OT APIs that takes an otInstance argument MUST be protected with this API lock + * except that the call site is in OT callbacks. + * + * @param[in] block_ticks The maxinum number of RTOS ticks to wait for the lock. + * + * @return + * - True on lock acquired + * - False on failing to acquire the lock with the timeout. + * + */ +bool esp_openthread_lock_acquire(TickType_t block_ticks); + +/** + * @brief This function releases the OpenThread API lock. + * + */ +void esp_openthread_lock_release(void); + + +#ifdef __cplusplus +} +#endif diff --git a/components/openthread/include/esp_openthread_types.h b/components/openthread/include/esp_openthread_types.h new file mode 100644 index 0000000000..83b7ff8b17 --- /dev/null +++ b/components/openthread/include/esp_openthread_types.h @@ -0,0 +1,93 @@ +// Copyright 2021 Espressif Systems (Shanghai) CO LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License + +#pragma once + +#include "hal/uart_types.h" +#include "sys/select.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * This structure represents a context for a select() based mainloop. + * + */ +typedef struct { + fd_set read_fds; /*!< The read file descriptors.*/ + fd_set write_fds; /*!< The write file descriptors.*/ + fd_set error_fds; /*!< The error file descriptors.*/ + int max_fd; /*!< The max file descriptor.*/ + struct timeval timeout; /*!< The timeout.*/ +} esp_openthread_mainloop_context_t; + +/** + * @brief The uart port config for OpenThread. + * + */ +typedef struct { + uart_port_t port; /*!< UART port number*/ + uart_config_t uart_config; /*!< UART configuration, see uart_config_t docs*/ + int rx_pin; /*!< UART RX pin */ + int tx_pin; /*!< UART TX pin */ +} esp_openthread_uart_config_t; + +/** + * @brief The radio mode of OpenThread. + * + */ +typedef enum { + RADIO_MODE_UART_RCP = 0x0, /*!< UART connection to a 15.4 capable radio co-processor(RCP)*/ +} esp_openthread_radio_mode_t; + +/** + * @brief How OpenThread connects to the host. + * + */ +typedef enum { + HOST_CONNECTION_MODE_NONE = 0x0, /*!< Disable host connection*/ + HOST_CONNECTION_MODE_UART = 0x1, /*!< UART connection to the host*/ +} esp_openthread_host_connection_mode_t; + +/** + * @brief The OpenThread radio configuration + * + */ +typedef struct { + esp_openthread_radio_mode_t radio_mode; /*!< The radio mode*/ + esp_openthread_uart_config_t radio_uart_config; /*!< The uart configuration to RCP*/ +} esp_openthread_radio_config_t; + +/** + * @brief The OpenThread host connection configuration + * + */ +typedef struct { + esp_openthread_host_connection_mode_t host_connection_mode; /*!< The host connection mode*/ + esp_openthread_uart_config_t host_uart_config; /*!< The uart configuration to host*/ +} esp_openthread_host_connection_config_t; + +/** + * @brief The OpenThread platform configuration + * + */ +typedef struct { + esp_openthread_radio_config_t radio_config; /*!< The radio configuration*/ + esp_openthread_host_connection_config_t host_config; /*!< The host connection configuration*/ +} esp_openthread_platform_config_t; + +#ifdef __cplusplus +} +#endif diff --git a/components/openthread/include/openthread-core-esp32x-config.h b/components/openthread/include/openthread-core-esp32x-config.h new file mode 100644 index 0000000000..15ceae99e5 --- /dev/null +++ b/components/openthread/include/openthread-core-esp32x-config.h @@ -0,0 +1,281 @@ +// Copyright 2021 Espressif Systems (Shanghai) CO LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License + +#pragma once + +#include "sdkconfig.h" + +/** + * @def OPENTHREAD_CONFIG_PLATFORM_INFO + * + * The platform-specific string to insert into the OpenThread version string. + * + */ +#define OPENTHREAD_CONFIG_PLATFORM_INFO CONFIG_IDF_TARGET + +/** + * @def OPENTHREAD_CONFIG_PLATFORM_ASSERT_MANAGEMENT + * + * The assert is managed by platform defined logic when this flag is set. + * + */ +#define OPENTHREAD_CONFIG_PLATFORM_ASSERT_MANAGEMENT 1 + +/** + * @def OPENTHREAD_CONFIG_PLATFORM_FLASH_API_ENABLE + * + * Define to 1 to enable otPlatFlash* APIs to support non-volatile storage. + * + * When defined to 1, the platform MUST implement the otPlatFlash* APIs instead of the otPlatSettings* APIs. + * + */ +#define OPENTHREAD_CONFIG_PLATFORM_FLASH_API_ENABLE 1 + +/** + * @def OPENTHREAD_CONFIG_LOG_OUTPUT + * + * The ESP-IDF platform provides an otPlatLog() function. + */ +#define OPENTHREAD_CONFIG_LOG_OUTPUT OPENTHREAD_CONFIG_LOG_OUTPUT_PLATFORM_DEFINED + +/** + * @def OPENTHREAD_CONFIG_LOG_LEVEL + * + * The log level (used at compile time). If `OPENTHREAD_CONFIG_LOG_LEVEL_DYNAMIC_ENABLE` is set, this defines the most + * verbose log level possible. See `OPENTHREAD_CONFIG_LOG_LEVEL_INIT` to set the initial log level. + * + */ +#ifndef OPENTHREAD_CONFIG_LOG_LEVEL +#if CONFIG_LOG_DEFAULT_LEVEL_NONE +#define OPENTHREAD_CONFIG_LOG_LEVEL OT_LOG_LEVEL_NONE +#elif CONFIG_LOG_DEFAULT_LEVEL_ERROR +#define OPENTHREAD_CONFIG_LOG_LEVEL OT_LOG_LEVEL_CRIT +#elif CONFIG_LOG_DEFAULT_LEVEL_WARN +#define OPENTHREAD_CONFIG_LOG_LEVEL OT_LOG_LEVEL_WARN +#elif CONFIG_LOG_DEFAULT_LEVEL_INFO +#define OPENTHREAD_CONFIG_LOG_LEVEL OT_LOG_LEVEL_INFO +#elif CONFIG_LOG_DEFAULT_LEVEL_DEBUG +#define OPENTHREAD_CONFIG_LOG_LEVEL OT_LOG_LEVEL_DEBG +#elif CONFIG_LOG_DEFAULT_LEVEL_VERBOSE +#define OPENTHREAD_CONFIG_LOG_LEVEL OT_LOG_LEVEL_DEBG +#endif +#endif + +#define OPENTHREAD_CONFIG_LOG_API 1 +#define OPENTHREAD_CONFIG_LOG_ARP 1 +#define OPENTHREAD_CONFIG_LOG_BBR 1 +#define OPENTHREAD_CONFIG_LOG_CLI 1 +#define OPENTHREAD_CONFIG_LOG_COAP 1 +#define OPENTHREAD_CONFIG_LOG_DUA 1 +#define OPENTHREAD_CONFIG_LOG_ICMP 1 +#define OPENTHREAD_CONFIG_LOG_IP6 1 +#define OPENTHREAD_CONFIG_LOG_MAC 1 +#define OPENTHREAD_CONFIG_LOG_MEM 1 +#define OPENTHREAD_CONFIG_LOG_MESHCOP 1 +#define OPENTHREAD_CONFIG_LOG_MLE 1 +#define OPENTHREAD_CONFIG_LOG_MLR 1 +#define OPENTHREAD_CONFIG_LOG_NETDATA 1 +#define OPENTHREAD_CONFIG_LOG_NETDIAG 1 +#define OPENTHREAD_CONFIG_LOG_PKT_DUMP 1 +#define OPENTHREAD_CONFIG_LOG_PLATFORM 1 + +/** + * @def OPENTHREAD_CONFIG_NUM_MESSAGE_BUFFERS + * + * The number of message buffers in buffer pool + */ +#define OPENTHREAD_CONFIG_NUM_MESSAGE_BUFFERS 50 + +/** + * @def OPENTHREAD_CONFIG_COAP_API_ENABLE + * + * Define to 1 to enable the CoAP API. + * + */ +#define OPENTHREAD_CONFIG_COAP_API_ENABLE 1 + +/** + * @def OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE + * + * Define to 1 to enable Border Router support. + * + */ +#ifndef OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE +#define OPENTHREAD_CONFIG_BORDER_ROUTER_ENABLE 1 +#endif + +/** + * @def OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE + * + * Define to 1 to enable Thread Test Harness reference device support. + * + */ +#define OPENTHREAD_CONFIG_REFERENCE_DEVICE_ENABLE 1 + +/** + * @def OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE + * + * Define to 1 to enable Child Supervision support. + * + */ +#ifndef OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE +#define OPENTHREAD_CONFIG_CHILD_SUPERVISION_ENABLE 1 +#endif + +/** + * @def OPENTHREAD_CONFIG_DHCP6_CLIENT_ENABLE + * + * Define to 1 to enable DHCPv6 Client support. + * + */ +#ifndef OPENTHREAD_CONFIG_DHCP6_CLIENT_ENABLE +#define OPENTHREAD_CONFIG_DHCP6_CLIENT_ENABLE 1 +#endif + +/** + * @def OPENTHREAD_CONFIG_DHCP6_SERVER_ENABLE + * + * Define to 1 to enable DHCPv6 Server support. + * + */ +#ifndef OPENTHREAD_CONFIG_DHCP6_SERVER_ENABLE +#define OPENTHREAD_CONFIG_DHCP6_SERVER_ENABLE 1 +#endif + +/** + * @def OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE + * + * Define to 1 to enable DNS Client support. + * + */ +#ifndef OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE +#define OPENTHREAD_CONFIG_DNS_CLIENT_ENABLE 1 +#endif + +/** + * @def OPENTHREAD_CONFIG_NCP_SPI_ENABLE + * + * Define to 1 to enable NCP SPI support. + * + */ +#define OPENTHREAD_CONFIG_NCP_SPI_ENABLE 0 + +/** + * @def OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER + * + * Define to 1 to enable NCP Spinel Encrypter. + * + */ +#define OPENTHREAD_ENABLE_NCP_SPINEL_ENCRYPTER 0 + +/** + * @def OPENTHREAD_CONFIG_NCP_UART_ENABLE + * + * Define to 1 to enable NCP UART support. + * + */ +#define OPENTHREAD_CONFIG_NCP_UART_ENABLE 1 + +/** + * @def OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE + * + * Define to 1 to support injecting Service entries into the Thread Network Data. + * + */ +#define OPENTHREAD_CONFIG_TMF_NETDATA_SERVICE_ENABLE 1 + +/** + * @def PACKAGE_NAME + * + * Define to the full name of this package. + * + */ +#define PACKAGE_NAME "openthread-esp32" + +/** + * @def PACKAGE_STRING + * + * Define to the full name and version of this package. + * + */ +#define PACKAGE_STRING (PACKAGE_NAME " - " PACKAGE_VERSION) + +/** + * @def OPENTHREAD_CONFIG_ENABLE_BUILTIN_MBEDTLS + * + * Define as 1 to enable bultin-mbedtls. + * + * Note that the OPENTHREAD_CONFIG_ENABLE_BUILTIN_MBEDTLS determines whether to use bultin-mbedtls as well as + * whether to manage mbedTLS internally, such as memory allocation and debug. + * + */ +#define OPENTHREAD_CONFIG_ENABLE_BUILTIN_MBEDTLS 0 + +/** + * @def OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE + * + * Define as 1 to enable support for adding of auto-configured SLAAC addresses by OpenThread. + * + */ +#define OPENTHREAD_CONFIG_IP6_SLAAC_ENABLE 1 + +/** + * @def OPENTHREAD_CONFIG_MAX_STATECHANGE_HANDLERS + * + * The maximum number of state-changed callback handlers (set using `otSetStateChangedCallback()`). + * + */ +#define OPENTHREAD_CONFIG_MAX_STATECHANGE_HANDLERS 3 + +/** + * @def OPENTHREAD_CONFIG_PLATFORM_RADIO_SPINEL_RX_FRAME_BUFFER_SIZE + * + * Specifies the rx frame buffer size used by `SpinelInterface` in RCP host code. This is applicable/used when + * `RadioSpinel` platform is used. + * + */ +#define OPENTHREAD_CONFIG_PLATFORM_RADIO_SPINEL_RX_FRAME_BUFFER_SIZE 1024 + +/** + * @def OPENTHREAD_CONFIG_DTLS_MAX_CONTENT_LEN + * + * The max length of the OpenThread dtls content buffer. + * + */ +#ifndef OPENTHREAD_CONFIG_DTLS_MAX_CONTENT_LEN +#define OPENTHREAD_CONFIG_DTLS_MAX_CONTENT_LEN 768 +#endif + +/** + * The configurable definitions via Kconfig + */ +#if CONFIG_OPENTHREAD_COMMISSIONER +#define OPENTHREAD_CONFIG_COMMISSIONER_ENABLE 1 +#endif + +#if CONFIG_OPENTHREAD_JOINER +#define OPENTHREAD_CONFIG_JOINER_ENABLE 1 +#endif + +#if CONFIG_OPENTHREAD_DIAG +#define OPENTHREAD_CONFIG_DIAG_ENABLE 1 +#endif + +#if CONFIG_OPENTHREAD_FTD +#define OPENTHREAD_FTD 1 +#elif CONFIG_OPENTHREAD_MTD +#define OPENTHREAD_MTD 1 +#elif CONFIG_OPENTHREAD_RADIO +#define OPENTHREAD_RADIO 1 +#endif diff --git a/components/openthread/openthread b/components/openthread/openthread new file mode 160000 index 0000000000..93b6fea3aa --- /dev/null +++ b/components/openthread/openthread @@ -0,0 +1 @@ +Subproject commit 93b6fea3aa10e26f17ad2b877f002ca2cd81b745 diff --git a/components/openthread/port/esp_openthread.cpp b/components/openthread/port/esp_openthread.cpp new file mode 100644 index 0000000000..95e2ed56ba --- /dev/null +++ b/components/openthread/port/esp_openthread.cpp @@ -0,0 +1,106 @@ +// Copyright 2021 Espressif Systems (Shanghai) CO LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License + +#include "esp_openthread.h" + +#include "esp_check.h" +#include "esp_err.h" +#include "esp_log.h" +#include "esp_openthread_alarm.h" +#include "esp_openthread_common_macro.h" +#include "esp_openthread_lock.h" +#include "esp_openthread_radio_uart.h" +#include "esp_openthread_types.h" +#include "esp_openthread_uart.h" +#include "common/code_utils.hpp" +#include "common/logging.hpp" +#include "core/common/instance.hpp" +#include "freertos/FreeRTOS.h" +#include "freertos/queue.h" +#include "openthread/cli.h" +#include "openthread/instance.h" +#include "openthread/platform/alarm-milli.h" +#include "openthread/platform/time.h" +#include "openthread/tasklet.h" + +static esp_openthread_platform_config_t s_platform_config; +static bool s_openthread_platform_initialized = false; + +esp_err_t esp_openthread_platform_init(const esp_openthread_platform_config_t *config) +{ + if (config->radio_config.radio_mode != RADIO_MODE_UART_RCP) { + otLogCritPlat("Radio mode not supported"); + return ESP_ERR_INVALID_ARG; + } + if (config->host_config.host_connection_mode != HOST_CONNECTION_MODE_NONE && + config->host_config.host_connection_mode != HOST_CONNECTION_MODE_UART) { + otLogCritPlat("Host connection mode not supported"); + return ESP_ERR_INVALID_ARG; + } + if (s_openthread_platform_initialized) { + return ESP_ERR_INVALID_STATE; + } + + esp_err_t error = ESP_OK; + + s_platform_config = *config; + SuccessOrExit(error = esp_openthread_lock_init()); + if (config->host_config.host_connection_mode == HOST_CONNECTION_MODE_UART) { + SuccessOrExit(error = esp_openthread_uart_init(config)); + } + SuccessOrExit(error = esp_openthread_radio_init(config)); +exit: + if (error != ESP_OK) { + esp_openthread_platform_deinit(); + } + + return error; +} + +otInstance *esp_openthread_get_instance(void) +{ + return &ot::Instance::Get(); +} + +esp_err_t esp_openthread_platform_deinit(void) +{ + if (!s_openthread_platform_initialized) { + return ESP_ERR_INVALID_STATE; + } + esp_openthread_radio_deinit(); + if (s_platform_config.host_config.host_connection_mode == HOST_CONNECTION_MODE_UART) { + esp_openthread_uart_deinit(); + } + esp_openthread_lock_deinit(); + return ESP_OK; +} + +void esp_openthread_platform_update(esp_openthread_mainloop_context_t *mainloop) +{ + esp_openthread_alarm_update(mainloop); + if (s_platform_config.host_config.host_connection_mode == HOST_CONNECTION_MODE_UART) { + esp_openthread_uart_update(mainloop); + } + esp_openthread_radio_update(mainloop); +} + +esp_err_t esp_openthread_platform_process(otInstance *instance, const esp_openthread_mainloop_context_t *mainloop) +{ + if (s_platform_config.host_config.host_connection_mode == HOST_CONNECTION_MODE_UART) { + ESP_RETURN_ON_ERROR(esp_openthread_uart_process(), OT_PLAT_LOG_TAG, "esp_openthread_uart_process failed"); + } + esp_openthread_radio_process(instance, mainloop); + esp_openthread_alarm_process(instance); + + return ESP_OK; +} diff --git a/components/openthread/port/esp_openthread_alarm.c b/components/openthread/port/esp_openthread_alarm.c new file mode 100644 index 0000000000..46fd1619da --- /dev/null +++ b/components/openthread/port/esp_openthread_alarm.c @@ -0,0 +1,103 @@ +// Copyright 2021 Espressif Systems (Shanghai) CO LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License + + +#include "esp_openthread_alarm.h" + +#include +#include +#include + +#include "esp_log.h" +#include "esp_openthread_common_macro.h" +#include "esp_timer.h" +#include "common/logging.hpp" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "openthread/platform/alarm-milli.h" +#include "openthread/platform/diag.h" +#include "openthread/platform/time.h" + +static uint64_t s_alarm_t0 = 0; +static uint64_t s_alarm_dt = 0; +static bool s_is_running = false; + +uint64_t otPlatTimeGet(void) +{ + struct timeval tv_now; + + int err = gettimeofday(&tv_now, NULL); + assert(err == 0); + + return (uint64_t)tv_now.tv_sec * US_PER_S + tv_now.tv_usec; +} + +void otPlatAlarmMilliStartAt(otInstance *aInstance, uint32_t aT0, uint32_t aDt) +{ + OT_UNUSED_VARIABLE(aInstance); + + s_alarm_t0 = aT0; + s_alarm_dt = aDt; + s_is_running = true; + + otLogDebgPlat("alarm start running, t0=%llu, dt=%llu", s_alarm_t0, s_alarm_dt); +} + +void otPlatAlarmMilliStop(otInstance *aInstance) +{ + OT_UNUSED_VARIABLE(aInstance); + + s_is_running = false; +} + +uint32_t otPlatAlarmMilliGetNow(void) +{ + return esp_timer_get_time() / US_PER_MS; +} + +void esp_openthread_alarm_update(esp_openthread_mainloop_context_t *mainloop) +{ + struct timeval *timeout = &mainloop->timeout; + uint32_t now = otPlatAlarmMilliGetNow(); + + if (!s_is_running) { + timeout->tv_sec = INT32_MAX; + timeout->tv_usec = 0; + } else if (s_alarm_t0 + s_alarm_dt > now) { + uint64_t remaining = s_alarm_dt + s_alarm_t0 - now; + timeout->tv_sec = remaining / MS_PER_S; + timeout->tv_usec = (remaining % MS_PER_S) * US_PER_MS; + } else { + timeout->tv_sec = 0; + timeout->tv_usec = 0; + } +} + +void esp_openthread_alarm_process(otInstance *aInstance) +{ + if (s_is_running && s_alarm_t0 + s_alarm_dt <= otPlatAlarmMilliGetNow()) { + s_is_running = false; + +#if OPENTHREAD_CONFIG_DIAG_ENABLE + if (otPlatDiagModeGet()) { + otPlatDiagAlarmFired(aInstance); + } else +#endif + { + otPlatAlarmMilliFired(aInstance); + } + + otLogDebgPlat("alarm fired"); + } +} diff --git a/components/openthread/port/esp_openthread_flash.c b/components/openthread/port/esp_openthread_flash.c new file mode 100644 index 0000000000..ca57dcd07c --- /dev/null +++ b/components/openthread/port/esp_openthread_flash.c @@ -0,0 +1,70 @@ +// Copyright 2021 Espressif Systems (Shanghai) CO LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the Licens + +#include "esp_partition.h" +#include "esp_spi_flash.h" +#include "openthread/instance.h" +#include "openthread/platform/flash.h" +#include "openthread/platform/settings.h" + +#define ESP_OT_FLASH_PAGE_NUM 2 +#define ESP_OT_FLASH_PAGE_SIZE 4096 + +static const esp_partition_t *s_ot_partition = NULL; + +void otPlatFlashInit(otInstance *instance) +{ + s_ot_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, CONFIG_OPENTHREAD_PARTITION_NAME); + + assert(s_ot_partition != NULL); + assert(s_ot_partition->size >= otPlatFlashGetSwapSize(instance)); +} + +uint32_t otPlatFlashGetSwapSize(otInstance *instance) +{ + return ESP_OT_FLASH_PAGE_SIZE; +} + +void otPlatFlashErase(otInstance *instance, uint8_t index) +{ + uint32_t address = ESP_OT_FLASH_PAGE_SIZE * (index != 0); + uint32_t size = ESP_OT_FLASH_PAGE_SIZE; + esp_err_t err = ESP_OK; + + err = esp_partition_erase_range(s_ot_partition, address, size); + + assert(err == ESP_OK); +} + +void otPlatFlashRead(otInstance *instance, uint8_t index, uint32_t offset, void *data, uint32_t size) +{ + esp_err_t err = ESP_OK; + + offset += ESP_OT_FLASH_PAGE_SIZE * (index != 0); + + err = esp_partition_read(s_ot_partition, offset, data, size); + + assert(err == ESP_OK); +} + +void otPlatFlashWrite(otInstance *instance, uint8_t index, uint32_t offset, const void *data, uint32_t size) +{ + esp_err_t err = ESP_OK; + + offset += ESP_OT_FLASH_PAGE_SIZE * (index != 0); + + err = esp_partition_write(s_ot_partition, offset, data, size); + + assert(err == ESP_OK); +} diff --git a/components/openthread/port/esp_openthread_lock.c b/components/openthread/port/esp_openthread_lock.c new file mode 100644 index 0000000000..a0fe2f0aaa --- /dev/null +++ b/components/openthread/port/esp_openthread_lock.c @@ -0,0 +1,52 @@ +// Copyright 2021 Espressif Systems (Shanghai) CO LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License + +#include "esp_openthread_lock.h" + +#include "esp_err.h" +#include "freertos/FreeRTOS.h" +#include "freertos/semphr.h" + +static SemaphoreHandle_t s_openthread_mutex = NULL; + +bool esp_openthread_lock_acquire(TickType_t block_ticks) +{ + BaseType_t ret = xSemaphoreTake(s_openthread_mutex, block_ticks); + return (ret == pdTRUE); +} + +void esp_openthread_lock_release(void) +{ + xSemaphoreGive(s_openthread_mutex); +} + +esp_err_t esp_openthread_lock_init(void) +{ + if (s_openthread_mutex != NULL) { + return ESP_ERR_INVALID_STATE; + } + s_openthread_mutex = xSemaphoreCreateMutex(); + if (s_openthread_mutex == NULL) { + return ESP_ERR_NO_MEM; + } + return ESP_OK; +} + +void esp_openthread_lock_deinit(void) +{ + if (s_openthread_mutex) { + vSemaphoreDelete(s_openthread_mutex); + s_openthread_mutex = NULL; + } +} diff --git a/components/openthread/port/esp_openthread_logging.c b/components/openthread/port/esp_openthread_logging.c new file mode 100644 index 0000000000..08011ce7b1 --- /dev/null +++ b/components/openthread/port/esp_openthread_logging.c @@ -0,0 +1,68 @@ +// Copyright 2021 Espressif Systems (Shanghai) CO LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License + +#include "esp_openthread.h" + +#include + +#include "esp_log.h" +#include "esp_openthread_common_macro.h" +#include "openthread/platform/logging.h" + +/** + * The default platform logging tag. + * + */ +#if (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_PLATFORM_DEFINED) || \ + (OPENTHREAD_CONFIG_LOG_OUTPUT == OPENTHREAD_CONFIG_LOG_OUTPUT_NCP_SPINEL) +OT_TOOL_WEAK void otPlatLog(otLogLevel log_level, otLogRegion log_region, const char *format, ...) +{ + va_list args; + + va_start(args, format); + + switch (log_level) { + case OT_LOG_LEVEL_CRIT: + if (LOG_LOCAL_LEVEL >= ESP_LOG_ERROR) { + esp_log_write(ESP_LOG_ERROR, OT_PLAT_LOG_TAG, LOG_COLOR_E "E(%u) %s:", esp_log_timestamp(), OT_PLAT_LOG_TAG); + esp_log_writev(ESP_LOG_ERROR, OT_PLAT_LOG_TAG, format, args); + esp_log_write(ESP_LOG_ERROR, OT_PLAT_LOG_TAG, LOG_RESET_COLOR "\n"); + } + break; + case OT_LOG_LEVEL_WARN: + if (LOG_LOCAL_LEVEL >= ESP_LOG_WARN) { + esp_log_write(ESP_LOG_WARN, OT_PLAT_LOG_TAG, LOG_COLOR_W "W(%u) %s:", esp_log_timestamp(), OT_PLAT_LOG_TAG); + esp_log_writev(ESP_LOG_WARN, OT_PLAT_LOG_TAG, format, args); + esp_log_write(ESP_LOG_WARN, OT_PLAT_LOG_TAG, LOG_RESET_COLOR "\n"); + } + break; + case OT_LOG_LEVEL_NOTE: + case OT_LOG_LEVEL_INFO: + if (LOG_LOCAL_LEVEL >= ESP_LOG_INFO) { + esp_log_write(ESP_LOG_INFO, OT_PLAT_LOG_TAG, LOG_COLOR_I "I(%u) %s:", esp_log_timestamp(), OT_PLAT_LOG_TAG); + esp_log_writev(ESP_LOG_INFO, OT_PLAT_LOG_TAG, format, args); + esp_log_write(ESP_LOG_INFO, OT_PLAT_LOG_TAG, LOG_RESET_COLOR "\n"); + } + break; + default: + if (LOG_LOCAL_LEVEL >= ESP_LOG_DEBUG) { + esp_log_write(ESP_LOG_DEBUG, OT_PLAT_LOG_TAG, LOG_COLOR_D "D(%u) %s:", esp_log_timestamp(), OT_PLAT_LOG_TAG); + esp_log_writev(ESP_LOG_DEBUG, OT_PLAT_LOG_TAG, format, args); + esp_log_write(ESP_LOG_DEBUG, OT_PLAT_LOG_TAG, LOG_RESET_COLOR "\n"); + } + break; + } + va_end(args); +} +#endif diff --git a/components/openthread/port/esp_openthread_misc.c b/components/openthread/port/esp_openthread_misc.c new file mode 100644 index 0000000000..36785a1a2e --- /dev/null +++ b/components/openthread/port/esp_openthread_misc.c @@ -0,0 +1,87 @@ +// Copyright 2021 Espressif Systems (Shanghai) CO LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License + +#include "esp_log.h" +#include "esp_openthread.h" +#include "esp_system.h" +#include "common/logging.hpp" +#include "openthread/platform/misc.h" + +static otPlatMcuPowerState s_mcu_power_state = OT_PLAT_MCU_POWER_STATE_ON; + +void otPlatReset(otInstance *aInstance) +{ + esp_restart(); +} + +otPlatResetReason otPlatGetResetReason(otInstance *instance) +{ + switch (esp_reset_reason()) { + case ESP_RST_UNKNOWN: + return OT_PLAT_RESET_REASON_UNKNOWN; + case ESP_RST_POWERON: + return OT_PLAT_RESET_REASON_POWER_ON; + case ESP_RST_EXT: + return OT_PLAT_RESET_REASON_EXTERNAL; + case ESP_RST_SW: + return OT_PLAT_RESET_REASON_SOFTWARE; + case ESP_RST_PANIC: + return OT_PLAT_RESET_REASON_FAULT; + case ESP_RST_INT_WDT: + return OT_PLAT_RESET_REASON_WATCHDOG; + case ESP_RST_TASK_WDT: + return OT_PLAT_RESET_REASON_WATCHDOG; + case ESP_RST_WDT: + return OT_PLAT_RESET_REASON_WATCHDOG; + default: + return OT_PLAT_RESET_REASON_OTHER; + } +} + +void otPlatWakeHost(void) +{ + // Not Implemented. +} + +otError otPlatSetMcuPowerState(otInstance *instance, otPlatMcuPowerState state) +{ + otError error = OT_ERROR_NONE; + + OT_UNUSED_VARIABLE(instance); + + switch (state) { + case OT_PLAT_MCU_POWER_STATE_ON: + case OT_PLAT_MCU_POWER_STATE_LOW_POWER: + s_mcu_power_state = state; + break; + + default: + error = OT_ERROR_FAILED; + break; + } + + return error; +} + +otPlatMcuPowerState otPlatGetMcuPowerState(otInstance *instance) +{ + OT_UNUSED_VARIABLE(instance); + return s_mcu_power_state; +} + +void otPlatAssertFail(const char *filename, int line) +{ + otLogCritPlat("Assert failed at %s:%d", filename, line); + assert(false); +} diff --git a/components/openthread/port/esp_openthread_radio_uart.cpp b/components/openthread/port/esp_openthread_radio_uart.cpp new file mode 100644 index 0000000000..86c8e93d8d --- /dev/null +++ b/components/openthread/port/esp_openthread_radio_uart.cpp @@ -0,0 +1,284 @@ +// Copyright 2021 Espressif Systems (Shanghai) CO LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License + +#include "esp_openthread_radio_uart.h" + +#include "esp_check.h" +#include "esp_err.h" +#include "esp_openthread_common_macro.h" +#include "esp_openthread_types.h" +#include "esp_uart_spinel_interface.hpp" +#include "lib/spinel/radio_spinel.hpp" +#include "openthread/platform/diag.h" +#include "openthread/platform/radio.h" + +using ot::Spinel::RadioSpinel; +using esp::openthread::UartSpinelInterface; + +static RadioSpinel s_radio; + +esp_err_t esp_openthread_radio_init(const esp_openthread_platform_config_t *config) +{ + ESP_RETURN_ON_ERROR(s_radio.GetSpinelInterface().Init(config->radio_config.radio_uart_config), OT_PLAT_LOG_TAG, + "Spinel interface init falied"); + s_radio.Init(/*reset_radio=*/true, /*restore_dataset_from_ncp=*/false, /*skip_rcp_compatibility_check=*/false); + return ESP_OK; +} + +void esp_openthread_radio_deinit(void) +{ + s_radio.Deinit(); +} + +esp_err_t esp_openthread_radio_process(otInstance *instance, const esp_openthread_mainloop_context_t *mainloop) +{ + s_radio.Process(*mainloop); + + return ESP_OK; +} + +void esp_openthread_radio_update(esp_openthread_mainloop_context_t *mainloop) +{ + s_radio.GetSpinelInterface().Update(*mainloop); +} + +void otPlatRadioGetIeeeEui64(otInstance *instance, uint8_t *ieee_eui64) +{ + SuccessOrDie(s_radio.GetIeeeEui64(ieee_eui64)); +} + +void otPlatRadioSetPanId(otInstance *instance, uint16_t pan_id) +{ + SuccessOrDie(s_radio.SetPanId(pan_id)); +} + +void otPlatRadioSetExtendedAddress(otInstance *instance, const otExtAddress *address) +{ + otExtAddress addr; + + for (size_t i = 0; i < sizeof(addr); i++) { + addr.m8[i] = address->m8[sizeof(addr) - 1 - i]; + } + + SuccessOrDie(s_radio.SetExtendedAddress(addr)); +} + +void otPlatRadioSetShortAddress(otInstance *instance, uint16_t address) +{ + SuccessOrDie(s_radio.SetShortAddress(address)); +} + +void otPlatRadioSetPromiscuous(otInstance *instance, bool enable) +{ + SuccessOrDie(s_radio.SetPromiscuous(enable)); +} + +bool otPlatRadioIsEnabled(otInstance *instance) +{ + return s_radio.IsEnabled(); +} + +otError otPlatRadioEnable(otInstance *instance) +{ + return s_radio.Enable(instance); +} + +otError otPlatRadioDisable(otInstance *instance) +{ + return s_radio.Disable(); +} + +otError otPlatRadioSleep(otInstance *instance) +{ + return s_radio.Sleep(); +} + +otError otPlatRadioReceive(otInstance *instance, uint8_t channel) +{ + return s_radio.Receive(channel); +} + +otError otPlatRadioTransmit(otInstance *instance, otRadioFrame *frame) +{ + return s_radio.Transmit(*frame); +} + +otRadioFrame *otPlatRadioGetTransmitBuffer(otInstance *instance) +{ + return &s_radio.GetTransmitFrame(); +} + +int8_t otPlatRadioGetRssi(otInstance *instance) +{ + return s_radio.GetRssi(); +} + +otRadioCaps otPlatRadioGetCaps(otInstance *instance) +{ + return s_radio.GetRadioCaps(); +} + +bool otPlatRadioGetPromiscuous(otInstance *instance) +{ + return s_radio.IsPromiscuous(); +} + +void otPlatRadioEnableSrcMatch(otInstance *instance, bool enable) +{ + SuccessOrDie(s_radio.EnableSrcMatch(enable)); +} + +otError otPlatRadioAddSrcMatchShortEntry(otInstance *instance, uint16_t short_address) +{ + return s_radio.AddSrcMatchShortEntry(short_address); +} + +otError otPlatRadioAddSrcMatchExtEntry(otInstance *instance, const otExtAddress *ext_address) +{ + otExtAddress addr; + + for (size_t i = 0; i < sizeof(addr); i++) { + addr.m8[i] = ext_address->m8[sizeof(addr) - 1 - i]; + } + + return s_radio.AddSrcMatchExtEntry(addr); +} + +otError otPlatRadioClearSrcMatchShortEntry(otInstance *instance, uint16_t short_address) +{ + return s_radio.ClearSrcMatchShortEntry(short_address); +} + +otError otPlatRadioClearSrcMatchExtEntry(otInstance *instance, const otExtAddress *ext_address) +{ + otExtAddress addr; + + for (size_t i = 0; i < sizeof(addr); i++) { + addr.m8[i] = ext_address->m8[sizeof(addr) - 1 - i]; + } + + return s_radio.ClearSrcMatchExtEntry(addr); +} + +void otPlatRadioClearSrcMatchShortEntries(otInstance *instance) +{ + SuccessOrDie(s_radio.ClearSrcMatchShortEntries()); +} + +void otPlatRadioClearSrcMatchExtEntries(otInstance *instance) +{ + SuccessOrDie(s_radio.ClearSrcMatchExtEntries()); +} + +otError otPlatRadioEnergyScan(otInstance *instance, uint8_t channel, uint16_t duration) +{ + return s_radio.EnergyScan(channel, duration); +} + +otError otPlatRadioGetTransmitPower(otInstance *instance, int8_t *power) +{ + otError error; + + VerifyOrExit(power != NULL, error = OT_ERROR_INVALID_ARGS); + error = s_radio.GetTransmitPower(*power); + +exit: + return error; +} + +otError otPlatRadioSetTransmitPower(otInstance *instance, int8_t power) +{ + return s_radio.SetTransmitPower(power); +} + +otError otPlatRadioGetCcaEnergyDetectThreshold(otInstance *instance, int8_t *threshold) +{ + otError error; + + VerifyOrExit(threshold != NULL, error = OT_ERROR_INVALID_ARGS); + error = s_radio.GetCcaEnergyDetectThreshold(*threshold); + +exit: + return error; +} + +otError otPlatRadioSetCcaEnergyDetectThreshold(otInstance *instance, int8_t threshold) +{ + return s_radio.SetCcaEnergyDetectThreshold(threshold); +} + +int8_t otPlatRadioGetReceiveSensitivity(otInstance *instance) +{ + return s_radio.GetReceiveSensitivity(); +} + +#if OPENTHREAD_CONFIG_DIAG_ENABLE +otError otPlatDiagProcess(otInstance *instance, int argc, char *argv[], char *output, size_t output_max_len) +{ + // deliver the platform specific diags commands to radio only ncp. + char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE] = {'\0'}; + char *cur = cmd; + char *end = cmd + sizeof(cmd); + + for (int index = 0; index < argc; index++) { + cur += snprintf(cur, static_cast(end - cur), "%s ", argv[index]); + } + + return s_radio.PlatDiagProcess(cmd, output, output_max_len); +} + +void otPlatDiagModeSet(bool aMode) +{ + SuccessOrExit(s_radio.PlatDiagProcess(aMode ? "start" : "stop", NULL, 0)); + s_radio.SetDiagEnabled(aMode); + +exit: + return; +} + +bool otPlatDiagModeGet(void) +{ + return s_radio.IsDiagEnabled(); +} + +void otPlatDiagTxPowerSet(int8_t tx_power) +{ + char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE]; + + snprintf(cmd, sizeof(cmd), "power %d", tx_power); + SuccessOrExit(s_radio.PlatDiagProcess(cmd, NULL, 0)); + +exit: + return; +} + +void otPlatDiagChannelSet(uint8_t channel) +{ + char cmd[OPENTHREAD_CONFIG_DIAG_CMD_LINE_BUFFER_SIZE]; + + snprintf(cmd, sizeof(cmd), "channel %d", channel); + SuccessOrExit(s_radio.PlatDiagProcess(cmd, NULL, 0)); + +exit: + return; +} + +void otPlatDiagRadioReceived(otInstance *instance, otRadioFrame *frame, otError error) +{ +} + +void otPlatDiagAlarmCallback(otInstance *instance) +{ +} +#endif // OPENTHREAD_CONFIG_DIAG_ENABLE diff --git a/components/openthread/port/esp_openthread_uart.c b/components/openthread/port/esp_openthread_uart.c new file mode 100644 index 0000000000..80e5e4988f --- /dev/null +++ b/components/openthread/port/esp_openthread_uart.c @@ -0,0 +1,129 @@ +// Copyright 2021 Espressif Systems (Shanghai) CO LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License + +#include "esp_openthread_uart.h" + +#include +#include +#include + +#include "esp_check.h" +#include "esp_err.h" +#include "esp_log.h" +#include "esp_openthread.h" +#include "esp_openthread_common_macro.h" +#include "esp_openthread_types.h" +#include "esp_vfs_dev.h" +#include "common/logging.hpp" +#include "driver/uart.h" +#include "openthread/platform/uart.h" + +static int s_uart_port; +static int s_uart_fd; +static uint8_t s_uart_buffer[ESP_OPENTHREAD_UART_BUFFER_SIZE]; + +otError otPlatUartEnable(void) +{ + return OT_ERROR_NONE; +} + +otError otPlatUartDisable(void) +{ + return OT_ERROR_NONE; +} + +otError otPlatUartFlush(void) +{ + return OT_ERROR_NONE; +} + +otError otPlatUartSend(const uint8_t *buf, uint16_t buf_length) +{ + int rval = write(s_uart_fd, buf, buf_length); + + if (rval != (int)buf_length) { + return OT_ERROR_FAILED; + } + + otPlatUartSendDone(); + + return OT_ERROR_NONE; +} + +esp_err_t esp_openthread_uart_init_port(const esp_openthread_uart_config_t *config) +{ + ESP_RETURN_ON_ERROR(uart_param_config(config->port, &config->uart_config), OT_PLAT_LOG_TAG, + "uart_param_config failed"); + ESP_RETURN_ON_ERROR( + uart_set_pin(config->port, config->tx_pin, config->rx_pin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE), + OT_PLAT_LOG_TAG, "uart_set_pin failed"); + ESP_RETURN_ON_ERROR(uart_driver_install(config->port, ESP_OPENTHREAD_UART_BUFFER_SIZE, 0, 0, NULL, 0), + OT_PLAT_LOG_TAG, "uart_driver_install failed"); + esp_vfs_dev_uart_use_driver(config->port); + return ESP_OK; +} + +esp_err_t esp_openthread_uart_init(const esp_openthread_platform_config_t *config) +{ + char uart_path[16]; + + // Disable IO buffer. + setvbuf(stdin, NULL, _IONBF, 0); + setvbuf(stdout, NULL, _IONBF, 0); + + // Install UART driver for interrupt-driven reads and writes. + s_uart_port = config->host_config.host_uart_config.port; + ESP_RETURN_ON_ERROR(esp_openthread_uart_init_port(&config->host_config.host_uart_config), OT_PLAT_LOG_TAG, + "esp_openthread_uart_init_port failed"); + + esp_vfs_dev_uart_port_set_rx_line_endings(s_uart_port, ESP_LINE_ENDINGS_LF); + esp_vfs_dev_uart_port_set_tx_line_endings(s_uart_port, ESP_LINE_ENDINGS_CRLF); + + snprintf(uart_path, sizeof(uart_path), "/dev/uart/%d", s_uart_port); + s_uart_fd = open(uart_path, O_RDWR | O_NONBLOCK); + + return s_uart_fd >= 0 ? ESP_OK : ESP_FAIL; +} + +void esp_openthread_uart_deinit() +{ + if (s_uart_fd != -1) { + close(s_uart_fd); + s_uart_fd = -1; + } + uart_driver_delete(s_uart_port); +} + +void esp_openthread_uart_update(esp_openthread_mainloop_context_t *mainloop) +{ + FD_SET(s_uart_fd, &mainloop->read_fds); + if (s_uart_fd > mainloop->max_fd) { + mainloop->max_fd = s_uart_fd; + } +} + +esp_err_t esp_openthread_uart_process() +{ + int rval = read(s_uart_fd, s_uart_buffer, sizeof(s_uart_buffer)); + + if (rval > 0) { + otPlatUartReceived(s_uart_buffer, (uint16_t)rval); + } else if (rval > 0) { + if (errno != EAGAIN) { + otLogWarnPlat("read uart failed: %d", errno); + return ESP_FAIL; + } + } + return ESP_OK; +} diff --git a/components/openthread/port/esp_uart_spinel_interface.cpp b/components/openthread/port/esp_uart_spinel_interface.cpp new file mode 100644 index 0000000000..83296cbce2 --- /dev/null +++ b/components/openthread/port/esp_uart_spinel_interface.cpp @@ -0,0 +1,295 @@ +// Copyright 2021 Espressif Systems (Shanghai) CO LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License + + +#include "esp_uart_spinel_interface.hpp" + +#include +#include +#include +#include + +#include "esp_check.h" +#include "esp_err.h" +#include "esp_log.h" +#include "esp_openthread_common_macro.h" +#include "esp_openthread_types.h" +#include "esp_openthread_uart.h" +#include "esp_vfs_dev.h" +#include "core/common/code_utils.hpp" +#include "core/common/logging.hpp" +#include "driver/uart.h" +#include "lib/platform/exit_code.h" +#include "openthread/platform/time.h" + +namespace esp { +namespace openthread { + +UartSpinelInterface::UartSpinelInterface( + ot::Spinel::SpinelInterface::ReceiveFrameCallback callback, + void *callback_context, + ot::Spinel::SpinelInterface::RxFrameBuffer &frame_buffer) + : m_receiver_frame_callback(callback) + , m_receiver_frame_context(callback_context) + , m_receive_frame_buffer(frame_buffer) + , m_hdlc_decoder(frame_buffer, HandleHdlcFrame, this) + , m_uart_fd(-1) +{ +} + +UartSpinelInterface::~UartSpinelInterface(void) +{ +} + +esp_err_t UartSpinelInterface::Init(const esp_openthread_uart_config_t &radio_uart_config) +{ + m_uart_rx_buffer = static_cast(heap_caps_malloc(kMaxFrameSize, MALLOC_CAP_8BIT)); + if (m_uart_rx_buffer == NULL) { + return ESP_ERR_NO_MEM; + } + + return InitUart(radio_uart_config); +} + +esp_err_t UartSpinelInterface::Deinit(void) +{ + if (m_uart_rx_buffer) { + heap_caps_free(m_uart_rx_buffer); + } + m_uart_rx_buffer = NULL; + + return DeinitUart(); +} + +otError UartSpinelInterface::SendFrame(const uint8_t *frame, uint16_t length) +{ + otError error = OT_ERROR_NONE; + ot::Hdlc::FrameBuffer encoder_buffer; + ot::Hdlc::Encoder hdlc_encoder(encoder_buffer); + + SuccessOrExit(error = hdlc_encoder.BeginFrame()); + SuccessOrExit(error = hdlc_encoder.Encode(frame, length)); + SuccessOrExit(error = hdlc_encoder.EndFrame()); + + SuccessOrExit(error = Write(encoder_buffer.GetFrame(), encoder_buffer.GetLength())); + +exit: + if (error != OT_ERROR_NONE) { + otLogCritPlat("send radio frame failed"); + } else { + otLogDebgPlat("sent radio frame"); + } + + return error; +} + +void UartSpinelInterface::Process(const esp_openthread_mainloop_context_t &mainloop) +{ + if (FD_ISSET(m_uart_fd, &mainloop.read_fds)) { + otLogDebgPlat("radio uart read event"); + TryReadAndDecode(); + } +} + +void UartSpinelInterface::Update(esp_openthread_mainloop_context_t &mainloop) +{ + // Register only READ events for radio UART and always wait + // for a radio WRITE to complete. + FD_SET(m_uart_fd, &mainloop.read_fds); + if (m_uart_fd > mainloop.max_fd) { + mainloop.max_fd = m_uart_fd; + } +} + +int UartSpinelInterface::TryReadAndDecode(void) +{ + uint8_t buffer[UART_FIFO_LEN]; + ssize_t rval; + + do { + rval = read(m_uart_fd, buffer, sizeof(buffer)); + if (rval > 0) { + m_hdlc_decoder.Decode(buffer, static_cast(rval)); + } + } while (rval > 0); + + if ((rval < 0) && (errno != EAGAIN) && (errno != EWOULDBLOCK)) { + ESP_ERROR_CHECK(TryRecoverUart()); + } + + return rval; +} + +otError UartSpinelInterface::WaitForWritable(void) +{ + otError error = OT_ERROR_NONE; + struct timeval timeout = {kMaxWaitTime / MS_PER_S, (kMaxWaitTime % MS_PER_S) *US_PER_MS}; + uint64_t now = otPlatTimeGet(); + uint64_t end = now + kMaxWaitTime * US_PER_MS; + fd_set write_fds; + fd_set error_fds; + int rval; + + while (true) { + FD_ZERO(&write_fds); + FD_ZERO(&error_fds); + FD_SET(m_uart_fd, &write_fds); + FD_SET(m_uart_fd, &error_fds); + + rval = select(m_uart_fd + 1, NULL, &write_fds, &error_fds, &timeout); + + if (rval > 0) { + if (FD_ISSET(m_uart_fd, &write_fds)) { + ExitNow(); + } else if (FD_ISSET(m_uart_fd, &error_fds)) { + ExitNow(error = OT_ERROR_FAILED); + } + } else if ((rval < 0) && (errno != EINTR)) { + ESP_ERROR_CHECK(TryRecoverUart()); + ExitNow(error = OT_ERROR_FAILED); + } + + now = otPlatTimeGet(); + + if (end > now) { + uint64_t remain = end - now; + + timeout.tv_sec = static_cast(remain / 1000000); + timeout.tv_usec = static_cast(remain % 1000000); + } else { + break; + } + } + + error = OT_ERROR_FAILED; + +exit: + return error; +} + +otError UartSpinelInterface::Write(const uint8_t *aFrame, uint16_t length) +{ + otError error = OT_ERROR_NONE; + + while (length) { + ssize_t rval; + + rval = write(m_uart_fd, aFrame, length); + + if (rval > 0) { + assert(rval <= length); + length -= static_cast(rval); + aFrame += static_cast(rval); + continue; + } else if (rval < 0) { + ESP_ERROR_CHECK(TryRecoverUart()); + ExitNow(error = OT_ERROR_FAILED); + } + + SuccessOrExit(error = WaitForWritable()); + } + +exit: + return error; +} + +otError UartSpinelInterface::WaitForFrame(uint64_t timeout_us) +{ + otError error = OT_ERROR_NONE; + struct timeval timeout; + fd_set read_fds; + fd_set error_fds; + int rval; + + FD_ZERO(&read_fds); + FD_ZERO(&error_fds); + FD_SET(m_uart_fd, &read_fds); + FD_SET(m_uart_fd, &error_fds); + + timeout.tv_sec = static_cast(timeout_us / US_PER_S); + timeout.tv_usec = static_cast(timeout_us % US_PER_S); + + rval = select(m_uart_fd + 1, &read_fds, NULL, &error_fds, &timeout); + + if (rval > 0) { + if (FD_ISSET(m_uart_fd, &read_fds)) { + TryReadAndDecode(); + } else if (FD_ISSET(m_uart_fd, &error_fds)) { + ESP_ERROR_CHECK(TryRecoverUart()); + ExitNow(error = OT_ERROR_FAILED); + } + } else if (rval == 0) { + ExitNow(error = OT_ERROR_RESPONSE_TIMEOUT); + } else { + ESP_ERROR_CHECK(TryRecoverUart()); + ExitNow(error = OT_ERROR_FAILED); + } + +exit: + return error; +} + +void UartSpinelInterface::HandleHdlcFrame(void *context, otError error) +{ + static_cast(context)->HandleHdlcFrame(error); +} + +void UartSpinelInterface::HandleHdlcFrame(otError error) +{ + if (error == OT_ERROR_NONE) { + otLogDebgPlat("received hdlc radio frame"); + m_receiver_frame_callback(m_receiver_frame_context); + } else { + otLogCritPlat("dropping radio frame: %s", otThreadErrorToString(error)); + m_receive_frame_buffer.DiscardFrame(); + } +} + +esp_err_t UartSpinelInterface::InitUart(const esp_openthread_uart_config_t &radio_uart_config) +{ + char uart_path[16]; + + m_uart_config = radio_uart_config; + ESP_RETURN_ON_ERROR(esp_openthread_uart_init_port(&radio_uart_config), OT_PLAT_LOG_TAG, + "esp_openthread_uart_init_port failed"); + // We have a driver now installed so set up the read/write functions to use driver also. + esp_vfs_dev_uart_port_set_tx_line_endings(m_uart_config.port, ESP_LINE_ENDINGS_LF); + esp_vfs_dev_uart_port_set_rx_line_endings(m_uart_config.port, ESP_LINE_ENDINGS_LF); + + snprintf(uart_path, sizeof(uart_path), "/dev/uart/%d", radio_uart_config.port); + m_uart_fd = open(uart_path, O_RDWR | O_NONBLOCK); + + return m_uart_fd >= 0 ? ESP_OK : ESP_FAIL; +} + +esp_err_t UartSpinelInterface::DeinitUart(void) +{ + if (m_uart_fd != -1) { + close(m_uart_fd); + m_uart_fd = -1; + return uart_driver_delete(m_uart_config.port); + } else { + return ESP_ERR_INVALID_STATE; + } +} + +esp_err_t UartSpinelInterface::TryRecoverUart(void) +{ + ESP_RETURN_ON_ERROR(DeinitUart(), OT_PLAT_LOG_TAG, "DeInitUart failed"); + ESP_RETURN_ON_ERROR(InitUart(m_uart_config), OT_PLAT_LOG_TAG, "InitUart failed"); + return ESP_OK; +} + +} // namespace openthread +} // namespace esp diff --git a/components/openthread/private_include/esp_openthread_alarm.h b/components/openthread/private_include/esp_openthread_alarm.h new file mode 100644 index 0000000000..0992bf77dc --- /dev/null +++ b/components/openthread/private_include/esp_openthread_alarm.h @@ -0,0 +1,43 @@ +// Copyright 2021 Espressif Systems (Shanghai) CO LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License + +#pragma once + +#include "esp_openthread_types.h" +#include "openthread/instance.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Updates the file descriptor with the OpenThread alarm timeout. + * + * @param[inout] mainloop The main loop context. + * + */ +void esp_openthread_alarm_update(esp_openthread_mainloop_context_t *mainloop); + +/** + * @brief Performs the alarm process and triggers the fired timers for OpenThread. + * + * @param[in] instance The OpenThread instance. + * + */ +void esp_openthread_alarm_process(otInstance *instance); + + +#ifdef __cplusplus +} +#endif diff --git a/components/openthread/private_include/esp_openthread_common_macro.h b/components/openthread/private_include/esp_openthread_common_macro.h new file mode 100644 index 0000000000..f134bbd87d --- /dev/null +++ b/components/openthread/private_include/esp_openthread_common_macro.h @@ -0,0 +1,31 @@ +// Copyright 2021 Espressif Systems (Shanghai) CO LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License + +#pragma once + +#define OT_PLAT_LOG_TAG "OPENTHREAD" + +#ifndef MS_PER_S +#define MS_PER_S 1000 +#endif + +#ifndef US_PER_MS +#define US_PER_MS 1000 +#endif + +#ifndef US_PER_S +#define US_PER_S (MS_PER_S * US_PER_MS) +#endif + +#define ESP_OPENTHREAD_UART_BUFFER_SIZE (UART_FIFO_LEN * 2) diff --git a/components/openthread/private_include/esp_openthread_radio_uart.h b/components/openthread/private_include/esp_openthread_radio_uart.h new file mode 100644 index 0000000000..8041b1ccaf --- /dev/null +++ b/components/openthread/private_include/esp_openthread_radio_uart.h @@ -0,0 +1,66 @@ +// Copyright 2021 Espressif Systems (Shanghai) CO LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License + +#pragma once + +#include + +#include "esp_err.h" +#include "esp_openthread_types.h" +#include "openthread/instance.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief This function initializes the OpenThread radio. + * + * @return + * - ESP_OK on success + * - ESP_ERR_NO_MEM if allocation has failed + * + */ +esp_err_t esp_openthread_radio_init(const esp_openthread_platform_config_t *config); + +/** + * @brief This function deinitializes the OpenThread radio. + * + */ +void esp_openthread_radio_deinit(void); + +/** + * @brief This function updates the radio fds and timeouts to the main loop. + * + * @param[inout] mainloop The main loop context. + * + */ +void esp_openthread_radio_update(esp_openthread_mainloop_context_t *mainloop); + +/** + * @brief This function performs the OpenThread radio process. + * + * @param[in] instance The OpenThread instance. + * @param[in] mainloop The main loop context. + * + * @return + * - ESP_OK on success + * - ESP_FAIL on failure + * + */ +esp_err_t esp_openthread_radio_process(otInstance *instance, const esp_openthread_mainloop_context_t *mainloop); + +#ifdef __cplusplus +} +#endif diff --git a/components/openthread/private_include/esp_openthread_uart.h b/components/openthread/private_include/esp_openthread_uart.h new file mode 100644 index 0000000000..b4903b17b6 --- /dev/null +++ b/components/openthread/private_include/esp_openthread_uart.h @@ -0,0 +1,76 @@ +// Copyright 2021 Espressif Systems (Shanghai) CO LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License + +#pragma once + +#include "esp_err.h" +#include "esp_openthread_types.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Initializes an uart port with the given config. + * + * @note The user still needs to open the file descriptor by self. + * + * @param[in] config The uart configuration. + * + * @return + * - ESP_OK on success + * - ESP_ERROR on failure + * + */ +esp_err_t esp_openthread_uart_init_port(const esp_openthread_uart_config_t *config); + +/** + * @brief Initializes the uart for OpenThread host connection. + * + * @param[in] config The uart configuration. + * + * @return + * - ESP_OK on success + * - ESP_ERROR on failure + * + */ +esp_err_t esp_openthread_uart_init(const esp_openthread_platform_config_t *config); + +/** + * @brief Deintializes the uart for OpenThread host connection. + * + */ +void esp_openthread_uart_deinit(void); + +/** + * @brief Deintializes the uart for OpenThread host connection. + * + * @param[inout] mainloop The main loop context. + * + */ +void esp_openthread_uart_update(esp_openthread_mainloop_context_t *context); + +/** + * @brief Performs the uart I/O for OpenThread. + * + * @return + * - ESP_OK on success + * - ESP_ERROR on failure + * + */ +esp_err_t esp_openthread_uart_process(void); + +#ifdef __cplusplus +} +#endif diff --git a/components/openthread/private_include/esp_uart_spinel_interface.hpp b/components/openthread/private_include/esp_uart_spinel_interface.hpp new file mode 100644 index 0000000000..3703d81d8e --- /dev/null +++ b/components/openthread/private_include/esp_uart_spinel_interface.hpp @@ -0,0 +1,157 @@ +// Copyright 2021 Espressif Systems (Shanghai) CO LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License + +#pragma once + +#include "esp_err.h" +#include "esp_openthread.h" +#include "esp_openthread_types.h" +#include "hal/uart_types.h" +#include "lib/spinel/spinel_interface.hpp" + +namespace esp { +namespace openthread { + +/** + * This class defines an UART interface to the Radio Co-processor (RCP). + * + */ +class UartSpinelInterface { +public: + /** + * @brief This constructor of object. + * + * @param[in] callback Callback on frame received + * @param[in] callback_context Callback context + * @param[in] frame_buffer A reference to a `RxFrameBuffer` object. + * + */ + UartSpinelInterface(ot::Spinel::SpinelInterface::ReceiveFrameCallback callback, + void *callback_context, + ot::Spinel::SpinelInterface::RxFrameBuffer &frame_buffer); + + /** + * @brief This destructor of the object. + * + */ + ~UartSpinelInterface(void); + + /** + * @brief This method initializes the HDLC interface. + * + * @return + * - ESP_OK on success + * - ESP_ERR_NO_MEM if allocation has failed + * - ESP_ERROR on failure + */ + esp_err_t Init(const esp_openthread_uart_config_t &radio_uart_config); + + /** + * @brief This method deinitializes the HDLC interface. + * + */ + esp_err_t Deinit(void); + + /** + * @brief This method encodes and sends a spinel frame to Radio Co-processor (RCP) over the socket. + * + * @note This is blocking call, i.e., if the socket is not writable, this method waits for it to become writable for + * up to `kMaxWaitTime` interval. + * + * @param[in] frame A pointer to buffer containing the spinel frame to send. + * @param[in] length The length (number of bytes) in the frame. + * + * @return + * -OT_ERROR_NONE Successfully encoded and sent the spinel frame. + * -OT_ERROR_NO_BUFS Insufficient buffer space available to encode the frame. + * -OT_ERROR_FAILED Failed to send due to socket not becoming writable within `kMaxWaitTime`. + * + */ + otError SendFrame(const uint8_t *frame, uint16_t length); + + /** + * This method waits for receiving part or all of spinel frame within specified timeout. + * + * @param[in] timeout_us The timeout value in microseconds. + * + * @return + * -OT_ERROR_NONE Part or all of spinel frame is received. + * -OT_ERROR_RESPONSE_TIMEOUT No spinel frame is received within @p timeout_us. + * + */ + otError WaitForFrame(uint64_t timeout_us); + + /** + * This method performs uart processing to the RCP. + * + * @param[in] mainloop The mainloop context + * + */ + void Process(const esp_openthread_mainloop_context_t &mainloop); + + /** + * This methods updates the mainloop context. + * + * @param[inout] mainloop The mainloop context. + * + */ + void Update(esp_openthread_mainloop_context_t &mainloop); + +private: + enum { + /** + * Maximum spinel frame size. + * + */ + kMaxFrameSize = ot::Spinel::SpinelInterface::kMaxFrameSize, + + /** + * Maximum wait time in Milliseconds for socket to become writable (see `SendFrame`). + * + */ + kMaxWaitTime = 2000, + }; + + esp_err_t InitUart(const esp_openthread_uart_config_t &radio_uart_config); + + esp_err_t DeinitUart(void); + + int TryReadAndDecode(void); + + otError WaitForWritable(void); + + otError Write(const uint8_t *frame, uint16_t length); + + esp_err_t TryRecoverUart(void); + + static void HandleHdlcFrame(void *context, otError error); + void HandleHdlcFrame(otError error); + + ot::Spinel::SpinelInterface::ReceiveFrameCallback m_receiver_frame_callback; + void *m_receiver_frame_context; + ot::Spinel::SpinelInterface::RxFrameBuffer &m_receive_frame_buffer; + + ot::Hdlc::Decoder m_hdlc_decoder; + uint8_t *m_uart_rx_buffer; + + esp_openthread_uart_config_t m_uart_config; + int m_uart_fd; + + // Non-copyable, intentionally not implemented. + UartSpinelInterface(const UartSpinelInterface &); + UartSpinelInterface &operator=(const UartSpinelInterface &); +}; + +} // namespace openthread +} // namespace esp diff --git a/docs/doxygen/Doxyfile_common b/docs/doxygen/Doxyfile_common index 2bb56ab17c..774071eeb1 100644 --- a/docs/doxygen/Doxyfile_common +++ b/docs/doxygen/Doxyfile_common @@ -199,6 +199,11 @@ INPUT = \ ## Non-Volatile Storage $(IDF_PATH)/components/nvs_flash/include/nvs.h \ $(IDF_PATH)/components/nvs_flash/include/nvs_flash.h \ + ## OpenThread + $(IDF_PATH)/components/openthread/include/esp_openthread.h \ + $(IDF_PATH)/components/openthread/include/esp_openthread_lock.h \ + $(IDF_PATH)/components/openthread/include/esp_openthread_types.h \ + $(IDF_PATH)/components/openthread/include/openthread-core-esp32x-config.h \ ## Virtual Filesystem $(IDF_PATH)/components/vfs/include/esp_vfs.h \ $(IDF_PATH)/components/vfs/include/esp_vfs_dev.h \ diff --git a/docs/en/COPYRIGHT.rst b/docs/en/COPYRIGHT.rst index 47f43daed7..d0d8a26b18 100644 --- a/docs/en/COPYRIGHT.rst +++ b/docs/en/COPYRIGHT.rst @@ -69,6 +69,8 @@ These third party libraries can be included into the application (firmware) prod * `qrcode`_ QR Code generator library Copyright (c) Project Nayuki, is licensed under MIT license. +* :component:`openthread`, Copyright (c) The OpenThread Authors, is licensed under Apache License 2.0 as described in :component_file:`LICENSE file`. + Build Tools ----------- diff --git a/examples/openthread/ot_cli/CMakeLists.txt b/examples/openthread/ot_cli/CMakeLists.txt new file mode 100644 index 0000000000..297967cb27 --- /dev/null +++ b/examples/openthread/ot_cli/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.5) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(ot_esp_cli) diff --git a/examples/openthread/ot_cli/Makefile b/examples/openthread/ot_cli/Makefile new file mode 100644 index 0000000000..5cfeb53892 --- /dev/null +++ b/examples/openthread/ot_cli/Makefile @@ -0,0 +1,8 @@ +# +# This is a project Makefile. It is assumed the directory this Makefile resides in is a +# project subdirectory. +# + +PROJECT_NAME := ot_esp_cli + +include $(IDF_PATH)/make/project.mk diff --git a/examples/openthread/ot_cli/README.md b/examples/openthread/ot_cli/README.md new file mode 100644 index 0000000000..d732f85f5c --- /dev/null +++ b/examples/openthread/ot_cli/README.md @@ -0,0 +1,86 @@ +# OpenThread command line example + +## Overview + +This example demonstrates a [basic OpenThread command line](https://github.com/openthread/openthread/blob/master/src/cli/README.md). + +## How to use example + +### Hardware connection + +To run this example, it's used to use an DevKit C board and connect PIN4 and PIN5 to the UART TX and RX port of another 15.4 capable radio co-processor ([RCP](https://openthread.io/platforms/co-processor?hl=en)) + +### Configure the project + +``` +idf.py menuconfig +``` + +The example can run with the default configuration. + +### Build, Flash, and Run + +Build the project and flash it to the board, then run monitor tool to view serial output: + +``` +idf.py -p PORT build flash monitor +``` + +Now you'll get an interactive OpenThread command line shell. + +## Example Output + +```bash +I (1540) OPENTHREAD: [INFO]-PLAT----: RCP reset: RESET_POWER_ON +I (1610) OPENTHREAD: [NOTE]-PLAT----: RCP API Version: 3 +I (1840) OPENTHREAD: [INFO]-CORE----: Non-volatile: Read NetworkInfo {rloc:0x7404, extaddr:aee4a5cc7ed1ad88, role:Child, mode:0x0f, version:2, keyseq:0x0, ... +I (1850) OPENTHREAD: [INFO]-CORE----: Non-volatile: ... pid:0x1161bcdc, mlecntr:0x5a17, maccntr:0x5a91, mliid:33158c466ab576d4} +I (1900) OPENTHREAD: [INFO]-CORE----: Non-volatile: Read ParentInfo {extaddr:36505631b12ea5e3, version:2} +I (1900) OPENTHREAD: [INFO]-CORE----: Notifier: StateChanged (0x1007c300) [KeySeqCntr NetData Channel PanId NetName ExtPanId MstrKey ActDset] + +> ifconfig up +I (11320) OPENTHREAD: [INFO]-CLI-----: execute command: ifconfig up +Done +I (11340) OPENTHREAD: [INFO]-CORE----: Notifier: StateChanged (0x01001009) [Ip6+ LLAddr Ip6Mult+ NetifState] +> dataset init new +I (105650) OPENTHREAD: [INFO]-CLI-----: execute command: dataset init new +Done + +> dataset +I (107460) OPENTHREAD: [INFO]-CLI-----: execute command: dataset +Active Timestamp: 1 +Channel: 14 +Channel Mask: 0x07fff800 +Ext PAN ID: d9d69bf6535735ec +Mesh Local Prefix: fd73:192f:f27:2a5c::/64 +Master Key: 7ad0ec87abbd8c41f07d004922b480bf +Network Name: OpenThread-a5fe +PAN ID: 0xa5fe +PSKc: ef028c933febdeb226f6681cc780272a +Security Policy: 672, onrcb +Done + +> dataset commit active +I (134350) OPENTHREAD: [INFO]-CLI-----: execute command: dataset commit active +I (134350) OPENTHREAD: [INFO]-MESH-CP-: Active dataset set +Done +I (134380) OPENTHREAD: [INFO]-CORE----: Notifier: StateChanged (0x101fc110) [MLAddr KeySeqCntr Channel PanId NetName ExtPanId MstrKey PSKc SecPolicy ... +I (134390) OPENTHREAD: [INFO]-CORE----: Notifier: StateChanged (0x101fc110) ... ActDset] + +>thread start +I (177250) OPENTHREAD: [INFO]-CLI-----: execute command: thread start +I (177250) OPENTHREAD: [NOTE]-MLE-----: Role Disabled -> Detached +I (177280) OPENTHREAD: [INFO]-CORE----: Non-volatile: Read NetworkInfo {rloc:0x7404, extaddr:aee4a5cc7ed1ad88, role:Child, mode:0x0f, version:2, keyseq:0x0, ... +I (177290) OPENTHREAD: [INFO]-CORE----: Non-volatile: ... pid:0x1161bcdc, mlecntr:0x5a17, maccntr:0x5a91, mliid:33158c466ab576d4} +I (194054) OPENTHREAD: [INFO]-CORE----: Non-volatile: Saved NetworkInfo {rloc:0x7404, extaddr:aee4a5cc7ed1ad88, role:Child, mode:0x0f, version:2, keyseq:0x0, ... +I (194064) OPENTHREAD: [INFO]-CORE----: Non-volatile: ... pid:0x1161bcdc, mlecntr:0x5e00, maccntr:0x5e79, mliid:33158c466ab576d4} +I (194074) OPENTHREAD: [INFO]-MLE-----: Send Child Update Request to parent (fe80:0:0:0:3450:5631:b12e:a5e3) +Done + +# After some seconds + +> state +leader +Done +``` + diff --git a/examples/openthread/ot_cli/main/CMakeLists.txt b/examples/openthread/ot_cli/main/CMakeLists.txt new file mode 100644 index 0000000000..c28d070fcd --- /dev/null +++ b/examples/openthread/ot_cli/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "ot_esp_cli.c" + INCLUDE_DIRS ".") diff --git a/examples/openthread/ot_cli/main/component.mk b/examples/openthread/ot_cli/main/component.mk new file mode 100644 index 0000000000..b928c2f543 --- /dev/null +++ b/examples/openthread/ot_cli/main/component.mk @@ -0,0 +1,8 @@ +# +# "main" pseudo-component makefile. +# +# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.) + +COMPONENT_ADD_INCLUDEDIRS := . + +COMPONENT_PRIV_INCLUDEDIRS := . diff --git a/examples/openthread/ot_cli/main/ot_esp_cli.c b/examples/openthread/ot_cli/main/ot_esp_cli.c new file mode 100644 index 0000000000..fdc3d21e9b --- /dev/null +++ b/examples/openthread/ot_cli/main/ot_esp_cli.c @@ -0,0 +1,129 @@ +// Copyright 2020 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include +#include + +#include "esp_err.h" +#include "esp_log.h" +#include "esp_netif.h" +#include "esp_openthread.h" +#include "esp_openthread_lock.h" +#include "esp_openthread_types.h" +#include "sdkconfig.h" +#include "driver/uart.h" +#include "freertos/FreeRTOS.h" +#include "freertos/portmacro.h" +#include "freertos/task.h" +#include "hal/uart_types.h" +#include "openthread/cli.h" +#include "openthread/instance.h" +#include "openthread/tasklet.h" + +#define TAG "ot_esp_cli" + +static void ot_task_worker(void *aContext) +{ + esp_openthread_platform_config_t config = { + .radio_config = + { + .radio_mode = RADIO_MODE_UART_RCP, + .radio_uart_config = + { + .port = 1, + .uart_config = + { + .baud_rate = 115200, + .data_bits = UART_DATA_8_BITS, + .parity = UART_PARITY_DISABLE, + .stop_bits = UART_STOP_BITS_1, + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, + .rx_flow_ctrl_thresh = 0, + .source_clk = UART_SCLK_APB, + }, + .rx_pin = 4, + .tx_pin = 5, + }, + }, + .host_config = + { + .host_connection_mode = HOST_CONNECTION_MODE_UART, + .host_uart_config = + { + .port = 0, + .uart_config = + { + .baud_rate = 115200, + .data_bits = UART_DATA_8_BITS, + .parity = UART_PARITY_DISABLE, + .stop_bits = UART_STOP_BITS_1, + .flow_ctrl = UART_HW_FLOWCTRL_DISABLE, + .rx_flow_ctrl_thresh = 0, + .source_clk = UART_SCLK_APB, + }, + .rx_pin = UART_PIN_NO_CHANGE, + .tx_pin = UART_PIN_NO_CHANGE, + }, + }, + }; + esp_openthread_mainloop_context_t mainloop; + + ESP_ERROR_CHECK(esp_openthread_platform_init(&config)); + otInstance *instance = otInstanceInitSingle(); + assert(instance != NULL); + + esp_openthread_lock_acquire(portMAX_DELAY); + otCliUartInit(instance); + esp_openthread_lock_release(); + + while (true) { + FD_ZERO(&mainloop.read_fds); + FD_ZERO(&mainloop.write_fds); + FD_ZERO(&mainloop.error_fds); + + mainloop.max_fd = -1; + mainloop.timeout.tv_sec = 10; + mainloop.timeout.tv_usec = 0; + + esp_openthread_lock_acquire(portMAX_DELAY); + esp_openthread_platform_update(&mainloop); + if (otTaskletsArePending(instance)) { + mainloop.timeout.tv_sec = 0; + mainloop.timeout.tv_usec = 0; + } + esp_openthread_lock_release(); + + if (select(mainloop.max_fd + 1, &mainloop.read_fds, &mainloop.write_fds, &mainloop.error_fds, + &mainloop.timeout) >= 0) { + esp_openthread_lock_acquire(portMAX_DELAY); + otTaskletsProcess(instance); + if (esp_openthread_platform_process(instance, &mainloop)) { + ESP_LOGE(TAG, "esp_openthread_platform_process failed"); + } + esp_openthread_lock_release(); + } else { + ESP_LOGE(TAG, "OpenThread system polling failed"); + break; + } + } + + otInstanceFinalize(instance); + esp_openthread_platform_deinit(); + vTaskDelete(NULL); +} + +void app_main(void) +{ + xTaskCreate(ot_task_worker, "ot_cli_main", 10240, xTaskGetCurrentTaskHandle(), 5, NULL); +} diff --git a/examples/openthread/ot_cli/partitions.csv b/examples/openthread/ot_cli/partitions.csv new file mode 100644 index 0000000000..dad8b3ef5e --- /dev/null +++ b/examples/openthread/ot_cli/partitions.csv @@ -0,0 +1,6 @@ +# Name, Type, SubType, Offset, Size, Flags +# Note: if you have increased the bootloader size, make sure to update the offsets to avoid overlap +nvs, data, nvs, 0x9000, 0x6000, +phy_init, data, phy, 0xf000, 0x1000, +factory, app, factory, 0x10000, 1M, +ot_storage, data, 0x3a, , 0x2000, diff --git a/examples/openthread/ot_cli/sdkconfig.defaults b/examples/openthread/ot_cli/sdkconfig.defaults new file mode 100644 index 0000000000..334f459b73 --- /dev/null +++ b/examples/openthread/ot_cli/sdkconfig.defaults @@ -0,0 +1,33 @@ +# +# libsodium +# +CONFIG_LIBSODIUM_USE_MBEDTLS_SHA=y +# end of libsodium + +# +# Partition Table +# +CONFIG_PARTITION_TABLE_CUSTOM=y +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" +CONFIG_PARTITION_TABLE_FILENAME="partitions.csv" +CONFIG_PARTITION_TABLE_OFFSET=0x8000 +CONFIG_PARTITION_TABLE_MD5=y +# end of Partition Table + +# +# mbedTLS +# + +CONFIG_MBEDTLS_CMAC_C=y +CONFIG_MBEDTLS_SSL_PROTO_DTLS=y +CONFIG_MBEDTLS_KEY_EXCHANGE_ECJPAKE=y +# end of TLS Key Exchange Methods + +CONFIG_MBEDTLS_ECJPAKE_C=y +# end of mbedTLS + +# +# OpenThread +# +CONFIG_OPENTHREAD_ENABLED=y +# end of OpenThread diff --git a/tools/ci/check_public_headers_exceptions.txt b/tools/ci/check_public_headers_exceptions.txt index a82bcdb81c..c8ab4fdd9e 100644 --- a/tools/ci/check_public_headers_exceptions.txt +++ b/tools/ci/check_public_headers_exceptions.txt @@ -92,6 +92,8 @@ components/cmock/CMock/src/cmock_internals.h components/efuse/include/esp_efuse.h +components/openthread/openthread/ + ### Here are the files that do not compile for some reason # components/app_trace/include/esp_sysview_trace.h