Browse Source

Merge branch 'feature/simplify-openthread-init' into 'master'

openthread: simplify esp_openthread initialization

See merge request espressif/esp-idf!13900
Shu Chen 4 years ago
parent
commit
2ed6e269e7

+ 17 - 37
components/openthread/include/esp_openthread.h

@@ -24,11 +24,9 @@ extern "C" {
 #endif
 
 /**
- * @brief   Initializes the platform-specific support for the OpenThread stack.
+ * @brief   Initializes the full 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.
+ * @note The OpenThread instance will also be initialized in this function.
  *
  * @param[in]  init_config      The initialization configuration.
  *
@@ -39,58 +37,40 @@ extern "C" {
  *      - ESP_ERR_INVALID_STATE if already initialized
  *
  */
-esp_err_t esp_openthread_platform_init(const esp_openthread_platform_config_t *init_config);
+esp_err_t esp_openthread_init(const esp_openthread_platform_config_t *init_config);
 
 /**
- * This function performs all platform-specific deinitialization for OpenThread's drivers.
+ * @brief   Launches the OpenThread main loop.
  *
- * @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.
+ * @note Thie function will not return unless error happens when running the OpenThread stack.
  *
  * @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
+ *      - ESP_ERR_NO_MEM if allocation has failed
+ *      - ESP_FAIL on other failures
  *
  */
-otInstance *esp_openthread_get_instance(void);
+esp_err_t esp_openthread_launch_mainloop(void);
 
 /**
- * @brief This function updates the platform fds and timeouts
+ * @brief This function performs OpenThread stack and platform driver deinitialization.
  *
- * @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.
+ * @return
+ *      - ESP_OK on success
+ *      - ESP_ERR_INVALID_STATE if not initialized
  *
  */
-void esp_openthread_platform_update(esp_openthread_mainloop_context_t *mainloop);
+esp_err_t esp_openthread_deinit(void);
 
 /**
- * @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.
+ * @brief This function acquires the underlying OpenThread instance.
  *
- * @param[in]    instance   The OpenThread instance.
- * @param[in]    mainloop   The main loop context.
+ * @note This function can be called on other tasks without lock.
  *
- * @return
- *      - ESP_OK on success
- *      - ESP_FAIL on failure
+ * @return The OpenThread instance pointer
  *
  */
-esp_err_t esp_openthread_platform_process(otInstance *instance, const esp_openthread_mainloop_context_t *mainloop);
+otInstance *esp_openthread_get_instance(void);
 
 #ifdef __cplusplus
 } // end of extern "C"

+ 57 - 0
components/openthread/include/esp_openthread_defaults.h

@@ -0,0 +1,57 @@
+// 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"
+
+#define ESP_OPENTHREAD_DEFAULT_RADIO_UART_RCP_CONFIG(pin_rx, pin_tx) \
+    {                                                                \
+        .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 = pin_rx,                                        \
+            .tx_pin = pin_tx,                                        \
+        },                                                           \
+    }
+
+#define ESP_OPENTHREAD_DEFAULT_UART_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,                  \
+        },                                                 \
+    }

+ 0 - 21
components/openthread/include/esp_openthread_netif_glue.h

@@ -38,27 +38,6 @@ void *esp_openthread_netif_glue_init(void);
  */
 void esp_openthread_netif_glue_deinit(void);
 
-/**
- * @brief This function updates the netif fds and timeouts to the main loop.
- *
- * @param[inout]    mainloop    The main loop context.
- *
- */
-void esp_openthread_netif_glue_update(esp_openthread_mainloop_context_t *mainloop);
-
-/**
- * @brief This function performs the netif process.
- *
- * @param[in]    instance   The OpenThread instance.
- *
- * @return
- *      - ESP_OK on success
- *      - ESP_FAIL on OpenThread failure
- *      - ESP_ERR_NO_MEM on memory allocation failure
- *
- */
-esp_err_t esp_openthread_netif_glue_process(otInstance *instance, const esp_openthread_mainloop_context_t *mainloop);
-
 #ifdef __cplusplus
 }
 #endif

+ 58 - 71
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_platform.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)
+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();
+    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;
 }
 
-esp_err_t esp_openthread_platform_deinit(void)
+esp_err_t esp_openthread_init(const esp_openthread_platform_config_t *config)
 {
-    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;
+    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();
 }
 
-void esp_openthread_platform_update(esp_openthread_mainloop_context_t *mainloop)
+esp_err_t esp_openthread_launch_mainloop(void)
 {
-    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_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_update(mainloop);
-    esp_openthread_netif_glue_update(mainloop);
+    return error;
 }
 
-esp_err_t esp_openthread_platform_process(otInstance *instance, const esp_openthread_mainloop_context_t *mainloop)
+esp_err_t esp_openthread_deinit(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_radio_process(instance, mainloop);
-    esp_openthread_alarm_process(instance);
-    return esp_openthread_netif_glue_process(instance, mainloop);
+    otInstanceFinalize(esp_openthread_get_instance());
+    return esp_openthread_platform_deinit();
 }

+ 4 - 10
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) {

+ 103 - 0
components/openthread/port/esp_openthread_platform.cpp

@@ -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_platform.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_netif_glue_priv.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/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)
+{
+    ESP_RETURN_ON_FALSE(config->radio_config.radio_mode == RADIO_MODE_UART_RCP, ESP_ERR_INVALID_ARG, OT_PLAT_LOG_TAG,
+                        "Radio mode not supported");
+    ESP_RETURN_ON_FALSE(config->host_config.host_connection_mode == HOST_CONNECTION_MODE_NONE ||
+                        config->host_config.host_connection_mode == HOST_CONNECTION_MODE_UART,
+                        ESP_ERR_INVALID_ARG, OT_PLAT_LOG_TAG, "Host connection mode not supported");
+    ESP_RETURN_ON_FALSE(!s_openthread_platform_initialized, ESP_ERR_INVALID_STATE, OT_PLAT_LOG_TAG,
+                        "OpenThread platform already initialized");
+
+    esp_err_t ret = ESP_OK;
+
+    s_platform_config = *config;
+    ESP_GOTO_ON_ERROR(esp_openthread_lock_init(), exit, OT_PLAT_LOG_TAG, "esp_openthread_lock_init failed");
+    if (config->host_config.host_connection_mode == HOST_CONNECTION_MODE_UART) {
+        ESP_GOTO_ON_ERROR(esp_openthread_uart_init(config), exit, OT_PLAT_LOG_TAG, "esp_openthread_uart_init failed");
+    }
+    ESP_GOTO_ON_ERROR(esp_openthread_radio_init(config), exit, OT_PLAT_LOG_TAG, "esp_openthread_radio_init failed");
+
+exit:
+    if (ret != ESP_OK) {
+        esp_openthread_platform_deinit();
+    }
+
+    return ret;
+}
+
+otInstance *esp_openthread_get_instance(void)
+{
+    return (otInstance *)&ot::Instance::Get();
+}
+
+esp_err_t esp_openthread_platform_deinit(void)
+{
+    ESP_RETURN_ON_FALSE(s_openthread_platform_initialized, ESP_ERR_INVALID_STATE, OT_PLAT_LOG_TAG,
+                        "OpenThread platform not initialized");
+
+    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);
+}

+ 55 - 0
components/openthread/private_include/esp_openthread_netif_glue_priv.h

@@ -0,0 +1,55 @@
+// 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);
+
+/**
+ * @brief This function updates the netif fds and timeouts to the main loop.
+ *
+ * @param[inout]    mainloop    The main loop context.
+ *
+ */
+void esp_openthread_netif_glue_update(esp_openthread_mainloop_context_t *mainloop);
+
+/**
+ * @brief This function performs the netif process.
+ *
+ * @param[in]    instance   The OpenThread instance.
+ *
+ * @return
+ *      - ESP_OK on success
+ *      - ESP_FAIL on OpenThread failure
+ *      - ESP_ERR_NO_MEM on memory allocation failure
+ *
+ */
+esp_err_t esp_openthread_netif_glue_process(otInstance *instance, const esp_openthread_mainloop_context_t *mainloop);
+
+#ifdef __cplusplus
+}
+#endif

+ 87 - 0
components/openthread/private_include/esp_openthread_platform.h

@@ -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
+
+#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 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_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 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

+ 34 - 89
examples/openthread/ot_cli/main/esp_ot_cli.c

@@ -19,7 +19,9 @@
 #include "esp_event.h"
 #include "esp_log.h"
 #include "esp_netif.h"
+#include "esp_netif_types.h"
 #include "esp_openthread.h"
+#include "esp_openthread_defaults.h"
 #include "esp_openthread_lock.h"
 #include "esp_openthread_netif_glue.h"
 #include "esp_openthread_types.h"
@@ -41,115 +43,58 @@
 
 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 = {
-        .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_vfs_eventfd_config_t eventfd_config = {
-        .max_fds = 2,
+        .radio_config = ESP_OPENTHREAD_DEFAULT_RADIO_UART_RCP_CONFIG(4, 5),
+        .host_config = ESP_OPENTHREAD_DEFAULT_UART_HOST_CONFIG(),
     };
+    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);
-
-        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);
+    // Run the main loop
+    esp_openthread_launch_mainloop();
+
+    // 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)
 {
+    // Used eventfds:
+    // * netif
+    // * radio driver
+    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);
 }