From dea281a48e7fd26060cfe21b2af3550a0d376f51 Mon Sep 17 00:00:00 2001 From: Jiacheng Guo Date: Tue, 8 Jun 2021 15:49:05 +0800 Subject: [PATCH] openthread: simplify esp_openthread initialization The current OpenThread initialization is too verbose and error-prone. Using a simple flow will be more user friendly and paves way for introducing more border router features. --- .../openthread/include/esp_openthread.h | 43 +++++- components/openthread/port/esp_openthread.cpp | 133 ++++++++---------- .../port/esp_openthread_netif_glue.c | 14 +- .../port/esp_openthread_platform.cpp | 107 ++++++++++++++ .../esp_openthread_netif_glue_priv.h | 34 +++++ examples/openthread/ot_cli/main/esp_ot_cli.c | 75 ++++------ 6 files changed, 274 insertions(+), 132 deletions(-) create mode 100644 components/openthread/port/esp_openthread_platform.cpp create mode 100644 components/openthread/private_include/esp_openthread_netif_glue_priv.h diff --git a/components/openthread/include/esp_openthread.h b/components/openthread/include/esp_openthread.h index 35a7d44383..916555f0a6 100644 --- a/components/openthread/include/esp_openthread.h +++ b/components/openthread/include/esp_openthread.h @@ -23,12 +23,53 @@ extern "C" { #endif +/** + * @brief Initializes the full OpenThread stack. + * + * @note The OpenThread instance will also be initialized in this function. + * + * @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_init(const esp_openthread_platform_config_t *init_config); + +/** + * @brief Launches the OpenThread main loop. + * + * @note Thie function will not return unless error happens when running the OpenThread stack. + * + * @param[in] init_config The initialization configuration. + * + * @return + * - ESP_OK on success + * - ESP_ERR_NO_MEM if allocation has failed + * - ESP_FAIL on other failures + * + */ +esp_err_t esp_openthread_launch_mainloop(void); + +/** + * This function performs OpenThread stack and platform driver deinitialization. + * + * @return + * - ESP_OK on success + * - ESP_ERR_INVALID_STATE if not initialized + * + */ +esp_err_t esp_openthread_deinit(void); + /** * @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. + * stack after calling this function. * * @param[in] init_config The initialization configuration. * diff --git a/components/openthread/port/esp_openthread.cpp b/components/openthread/port/esp_openthread.cpp index 69a859a0b2..67e3504ae7 100644 --- a/components/openthread/port/esp_openthread.cpp +++ b/components/openthread/port/esp_openthread.cpp @@ -11,97 +11,84 @@ // 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.h" #include "esp_openthread_common_macro.h" #include "esp_openthread_lock.h" -#include "esp_openthread_netif_glue.h" -#include "esp_openthread_radio_uart.h" +#include "esp_openthread_netif_glue_priv.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" +#include "platform/logging.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) +static void esp_openthread_state_callback(otChangedFlags changed_flags, void *ctx) { - 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; + esp_openthread_netif_glue_state_callback(changed_flags); } -otInstance *esp_openthread_get_instance(void) +static esp_err_t register_esp_openthread_state_callbacks(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(); + otInstance *instance = esp_openthread_get_instance(); + ESP_RETURN_ON_FALSE(otSetStateChangedCallback(instance, esp_openthread_state_callback, NULL) == OT_ERROR_NONE, + ESP_FAIL, OT_PLAT_LOG_TAG, "Failed to install OpenThread state callback"); return ESP_OK; } -void esp_openthread_platform_update(esp_openthread_mainloop_context_t *mainloop) +esp_err_t esp_openthread_init(const esp_openthread_platform_config_t *config) { - 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_openthread_netif_glue_update(mainloop); + ESP_RETURN_ON_ERROR(esp_openthread_platform_init(config), OT_PLAT_LOG_TAG, + "Failed to initialize OpenThread platform driver"); + ESP_RETURN_ON_FALSE(otInstanceInitSingle() != NULL, ESP_FAIL, OT_PLAT_LOG_TAG, + "Failed to initialize OpenThread instance"); + + return register_esp_openthread_state_callbacks(); } -esp_err_t esp_openthread_platform_process(otInstance *instance, const esp_openthread_mainloop_context_t *mainloop) +esp_err_t esp_openthread_launch_mainloop(void) { - 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_mainloop_context_t mainloop; + otInstance *instance = esp_openthread_get_instance(); + esp_err_t error = ESP_OK; + + 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); + error = esp_openthread_platform_process(instance, &mainloop); + esp_openthread_lock_release(); + if (error != ESP_OK) { + ESP_LOGE(OT_PLAT_LOG_TAG, "esp_openthread_platform_process failed"); + break; + } + } else { + error = ESP_FAIL; + ESP_LOGE(OT_PLAT_LOG_TAG, "OpenThread system polling failed"); + break; + } } - esp_openthread_radio_process(instance, mainloop); - esp_openthread_alarm_process(instance); - return esp_openthread_netif_glue_process(instance, mainloop); + return error; +} + +esp_err_t esp_openthread_deinit(void) +{ + otInstanceFinalize(esp_openthread_get_instance()); + return esp_openthread_platform_deinit(); } diff --git a/components/openthread/port/esp_openthread_netif_glue.c b/components/openthread/port/esp_openthread_netif_glue.c index 514453a629..f9d6bed7dd 100644 --- a/components/openthread/port/esp_openthread_netif_glue.c +++ b/components/openthread/port/esp_openthread_netif_glue.c @@ -24,6 +24,7 @@ #include "esp_openthread.h" #include "esp_openthread_common_macro.h" #include "esp_openthread_lock.h" +#include "esp_openthread_netif_glue_priv.h" #include "esp_vfs_eventfd.h" #include "sdkconfig.h" #include "common/code_utils.hpp" @@ -151,12 +152,12 @@ static esp_err_t process_thread_transmit(otInstance *instance) return error; } -static void process_thread_state(otChangedFlags changed_flags, void *context) +void esp_openthread_netif_glue_state_callback(otChangedFlags changed_flags) { - otInstance *instance = (otInstance *)context; + otInstance *instance = esp_openthread_get_instance(); esp_err_t err = ESP_OK; - if (OT_CHANGED_THREAD_NETIF_STATE & changed_flags) { + if (s_packet_queue != NULL && (OT_CHANGED_THREAD_NETIF_STATE & changed_flags)) { if (otLinkIsEnabled(instance)) { otLogInfoPlat("netif up"); if (esp_event_post(OPENTHREAD_EVENT, OPENTHREAD_EVENT_IF_UP, NULL, 0, 0) != ESP_OK) { @@ -274,7 +275,6 @@ static esp_err_t openthread_netif_post_attach(esp_netif_t *esp_netif, void *args void *esp_openthread_netif_glue_init(void) { otInstance *instance = esp_openthread_get_instance(); - otError ot_err; esp_err_t error = ESP_OK; if (instance == NULL || s_packet_queue || s_openthread_netif_glue.event_fd >= 0) { @@ -289,11 +289,6 @@ void *esp_openthread_netif_glue_init(void) otIp6SetAddressCallback(instance, process_thread_address, instance); otIp6SetReceiveCallback(instance, process_thread_receive, instance); - ot_err = otSetStateChangedCallback(instance, process_thread_state, instance); - if (ot_err != OT_ERROR_NONE) { - otLogCritPlat("Failed to register callback for OpenThread lwip interface: %s", otThreadErrorToString(ot_err)); - ExitNow(error = ESP_FAIL); - } otIp6SetReceiveFilterEnabled(instance, true); otIcmp6SetEchoMode(instance, OT_ICMP6_ECHO_HANDLER_DISABLED); @@ -315,7 +310,6 @@ exit: void esp_openthread_netif_glue_deinit(void) { otInstance *instance = esp_openthread_get_instance(); - otRemoveStateChangeCallback(instance, process_thread_state, instance); otIp6SetAddressCallback(instance, NULL, NULL); otIp6SetReceiveCallback(instance, NULL, NULL); if (s_packet_queue) { diff --git a/components/openthread/port/esp_openthread_platform.cpp b/components/openthread/port/esp_openthread_platform.cpp new file mode 100644 index 0000000000..a489c31c8a --- /dev/null +++ b/components/openthread/port/esp_openthread_platform.cpp @@ -0,0 +1,107 @@ +// 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_netif_glue.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 (otInstance *)&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_openthread_netif_glue_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_openthread_netif_glue_process(instance, mainloop); +} diff --git a/components/openthread/private_include/esp_openthread_netif_glue_priv.h b/components/openthread/private_include/esp_openthread_netif_glue_priv.h new file mode 100644 index 0000000000..4a193474f6 --- /dev/null +++ b/components/openthread/private_include/esp_openthread_netif_glue_priv.h @@ -0,0 +1,34 @@ +// 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.h" +#include "openthread/instance.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief The state handler to be called when OpenThread state changes + * + * @param[in] changed_flags The changed Openthread states + * + */ +void esp_openthread_netif_glue_state_callback(otChangedFlags changed_flags); + +#ifdef __cplusplus +} +#endif diff --git a/examples/openthread/ot_cli/main/esp_ot_cli.c b/examples/openthread/ot_cli/main/esp_ot_cli.c index 8e904aca59..757485f9fd 100644 --- a/examples/openthread/ot_cli/main/esp_ot_cli.c +++ b/examples/openthread/ot_cli/main/esp_ot_cli.c @@ -19,6 +19,7 @@ #include "esp_event.h" #include "esp_log.h" #include "esp_netif.h" +#include "esp_netif_types.h" #include "esp_openthread.h" #include "esp_openthread_lock.h" #include "esp_openthread_netif_glue.h" @@ -41,6 +42,16 @@ extern void otAppCliInit(otInstance *instance); +static esp_netif_t *init_openthread_netif(void) +{ + esp_netif_config_t cfg = ESP_NETIF_DEFAULT_OPENTHREAD(); + esp_netif_t *netif = esp_netif_new(&cfg); + assert(netif != NULL); + ESP_ERROR_CHECK(esp_netif_attach(netif, esp_openthread_netif_glue_init())); + + return netif; +} + static void ot_task_worker(void *aContext) { esp_openthread_platform_config_t config = { @@ -85,71 +96,39 @@ static void ot_task_worker(void *aContext) }, }, }; - esp_vfs_eventfd_config_t eventfd_config = { - .max_fds = 2, - }; + esp_netif_t *openthread_netif; - esp_netif_config_t cfg = ESP_NETIF_DEFAULT_OPENTHREAD(); - esp_netif_t *netif = esp_netif_new(&cfg); - assert(netif != NULL); + // Initialize the OpenThread stack + ESP_ERROR_CHECK(esp_openthread_init(&config)); - esp_openthread_mainloop_context_t mainloop; + // Initialize the OpenThread cli + otAppCliInit(esp_openthread_get_instance()); - ESP_ERROR_CHECK(esp_vfs_eventfd_register(&eventfd_config)); - ESP_ERROR_CHECK(esp_openthread_platform_init(&config)); - otInstance *instance = otInstanceInitSingle(); - ESP_ERROR_CHECK(esp_netif_attach(netif, esp_openthread_netif_glue_init())); - assert(instance != NULL); + // Initialize the esp_netif bindings + openthread_netif = init_openthread_netif(); - esp_openthread_lock_acquire(portMAX_DELAY); - otAppCliInit(instance); - esp_openthread_lock_release(); #if CONFIG_OPENTHREAD_CUSTOM_COMMAND esp_cli_custom_command_init(); #endif // CONFIG_OPENTHREAD_CUSTOM_COMMAND - while (true) { - FD_ZERO(&mainloop.read_fds); - FD_ZERO(&mainloop.write_fds); - FD_ZERO(&mainloop.error_fds); + // Run the main loop + esp_openthread_launch_mainloop(); - 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; - } - } - - esp_netif_destroy(netif); + // Clean up + esp_netif_destroy(openthread_netif); esp_openthread_netif_glue_deinit(); - otInstanceFinalize(instance); - esp_openthread_platform_deinit(); esp_vfs_eventfd_unregister(); vTaskDelete(NULL); } void app_main(void) { + esp_vfs_eventfd_config_t eventfd_config = { + .max_fds = 2, + }; + ESP_ERROR_CHECK(esp_event_loop_create_default()); ESP_ERROR_CHECK(esp_netif_init()); + ESP_ERROR_CHECK(esp_vfs_eventfd_register(&eventfd_config)); xTaskCreate(ot_task_worker, "ot_cli_main", 10240, xTaskGetCurrentTaskHandle(), 5, NULL); }