fix(openthread): fix stack deinit by reversing netif glue teardown and hardening workflow cleanup

This commit is contained in:
Xu Si Yu
2026-09-03 20:03:56 +08:00
parent 474d57d858
commit cd31c697c8
8 changed files with 63 additions and 27 deletions
-2
View File
@@ -289,8 +289,6 @@ esp_err_t esp_disable_extern_coex_gpio_pin(void)
#if CONFIG_ESP_COEX_SW_COEXIST_ENABLE && CONFIG_SOC_IEEE802154_SUPPORTED #if CONFIG_ESP_COEX_SW_COEXIST_ENABLE && CONFIG_SOC_IEEE802154_SUPPORTED
esp_err_t esp_coex_wifi_i154_enable(void) esp_err_t esp_coex_wifi_i154_enable(void)
{ {
// TODO: Add a scheme for wifi and 154 coex.
// Remove this function if FCC-50 closes.
coex_enable(); coex_enable();
esp_coex_ieee802154_status_enable(); esp_coex_ieee802154_status_enable();
return ESP_OK; return ESP_OK;
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -106,6 +106,11 @@ esp_err_t esp_openthread_start(const esp_openthread_config_t *config);
/** /**
* @brief This function performs OpenThread stack and platform driver deinitialization and delete the handle task. * @brief This function performs OpenThread stack and platform driver deinitialization and delete the handle task.
*
* @note Thread must already be inactive (`otThreadSetEnabled(false)` and
* `otIp6SetEnabled(false)`). If border routing was initialized, call
* `esp_openthread_border_router_deinit()` first.
*
* @return * @return
* - ESP_OK on success * - ESP_OK on success
* - ESP_ERR_INVALID_STATE if Thread is already active * - ESP_ERR_INVALID_STATE if Thread is already active
@@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@@ -44,6 +44,11 @@ esp_err_t esp_openthread_border_router_init(void);
/** /**
* @brief Deinitializes the border router features of OpenThread. * @brief Deinitializes the border router features of OpenThread.
* *
* @note This function must be called while the OpenThread mainloop is still running
* and after Thread IPv6 has been brought down:
* `otThreadSetEnabled(instance, false)` then `otIp6SetEnabled(instance, false)`.
* Call `esp_openthread_stop()` only after this function returns.
*
* @return * @return
* - ESP_OK on success * - ESP_OK on success
* - ESP_ERR_INVALID_STATE if not initialized * - ESP_ERR_INVALID_STATE if not initialized
@@ -15,7 +15,7 @@
extern "C" { extern "C" {
#endif #endif
#define WORKFLOW_MAX_NAMELEN 16 #define WORKFLOW_MAX_NAMELEN 32
/** /**
* @brief update function declaration * @brief update function declaration
@@ -60,11 +60,13 @@ typedef struct esp_openthread_platform_workflow {
* *
* @param[in] updatefcn The update function of the workflow added to the list. * @param[in] updatefcn The update function of the workflow added to the list.
* @param[in] processfcn The process function of the workflow added to the list. * @param[in] processfcn The process function of the workflow added to the list.
* @param[in] name The name of the added workflow * @param[in] name The name of the added workflow. Must be non-empty and shorter
* than WORKFLOW_MAX_NAMELEN (including the terminating '\\0').
* *
* @return * @return
* - ESP_OK on success * - ESP_OK on success
* - ESP_ERR_NO_MEM on allocation failure * - ESP_ERR_NO_MEM on allocation failure
* - ESP_ERR_INVALID_ARG if name is NULL, empty, or too long
* - ESP_FAIL on other failures * - ESP_FAIL on other failures
* *
*/ */
+1 -4
View File
@@ -225,12 +225,9 @@ esp_err_t esp_openthread_launch_mainloop(void)
esp_openthread_lock_release(); esp_openthread_lock_release();
if (error != ESP_OK) { if (error != ESP_OK) {
ESP_LOGE(OT_PLAT_LOG_TAG, "esp_openthread_platform_process failed"); ESP_LOGE(OT_PLAT_LOG_TAG, "esp_openthread_platform_process failed");
break;
} }
} else { } else {
error = ESP_FAIL; ESP_LOGE(OT_PLAT_LOG_TAG, "OpenThread system polling failed (errno: %d)", errno);
ESP_LOGE(OT_PLAT_LOG_TAG, "OpenThread system polling failed");
break;
} }
} }
#if CONFIG_OPENTHREAD_TASK_BLOCK_MONITOR #if CONFIG_OPENTHREAD_TASK_BLOCK_MONITOR
@@ -150,6 +150,9 @@ static esp_err_t process_thread_transmit(otInstance *instance)
int ret = read(s_openthread_netif_glue.event_fd, &event, sizeof(event)); int ret = read(s_openthread_netif_glue.event_fd, &event, sizeof(event));
assert(ret == sizeof(event)); assert(ret == sizeof(event));
if (s_packet_queue == NULL) {
return ESP_OK;
}
while (xQueueReceive(s_packet_queue, &msg, 0) == pdTRUE) { while (xQueueReceive(s_packet_queue, &msg, 0) == pdTRUE) {
if (msg) { if (msg) {
otError ot_error = otIp6Send(esp_openthread_get_instance(), msg); otError ot_error = otIp6Send(esp_openthread_get_instance(), msg);
@@ -179,9 +182,15 @@ static esp_err_t openthread_netif_transmit(void *handle, void *buffer, size_t le
{ {
esp_err_t error = ESP_OK; esp_err_t error = ESP_OK;
otError ot_error = OT_ERROR_NONE; otError ot_error = OT_ERROR_NONE;
otMessage *message = NULL;
esp_openthread_task_switching_lock_acquire(portMAX_DELAY); esp_openthread_task_switching_lock_acquire(portMAX_DELAY);
if (s_packet_queue == NULL) {
ESP_LOGW(OT_PLAT_LOG_TAG, "Thread netif transmit after glue deinit");
ExitNow(error = ESP_ERR_INVALID_STATE);
}
otMessageSettings settings = {}; otMessageSettings settings = {};
switch (otThreadGetDeviceRole(esp_openthread_get_instance())) switch (otThreadGetDeviceRole(esp_openthread_get_instance()))
{ {
@@ -195,7 +204,7 @@ static esp_err_t openthread_netif_transmit(void *handle, void *buffer, size_t le
break; break;
} }
otMessage *message = otIp6NewMessage(esp_openthread_get_instance(), &settings); message = otIp6NewMessage(esp_openthread_get_instance(), &settings);
if (message == NULL) { if (message == NULL) {
ESP_LOGE(OT_PLAT_LOG_TAG, "Failed to allocate OpenThread message"); ESP_LOGE(OT_PLAT_LOG_TAG, "Failed to allocate OpenThread message");
ExitNow(error = ESP_ERR_NO_MEM); ExitNow(error = ESP_ERR_NO_MEM);
@@ -359,22 +368,28 @@ exit:
void esp_openthread_netif_glue_deinit(void) void esp_openthread_netif_glue_deinit(void)
{ {
otInstance *instance = esp_openthread_get_instance(); otInstance *instance = esp_openthread_get_instance();
otIp6SetAddressCallback(instance, NULL, NULL); if (s_openthread_netif) {
otIp6SetReceiveCallback(instance, NULL, NULL); esp_netif_action_stop(s_openthread_netif, OPENTHREAD_EVENT, OPENTHREAD_EVENT_STOP, NULL);
if (s_packet_queue) { s_openthread_netif = NULL;
vQueueDelete(s_packet_queue);
s_packet_queue = NULL;
} }
unregister_openthread_event_handlers();
esp_openthread_platform_workflow_unregister(netif_glue_workflow);
if (s_openthread_netif_glue.event_fd >= 0) { if (s_openthread_netif_glue.event_fd >= 0) {
close(s_openthread_netif_glue.event_fd); close(s_openthread_netif_glue.event_fd);
s_openthread_netif_glue.event_fd = -1; s_openthread_netif_glue.event_fd = -1;
} }
if (esp_event_post(OPENTHREAD_EVENT, OPENTHREAD_EVENT_STOP, NULL, 0, 0) != ESP_OK) { otIp6SetReceiveCallback(instance, NULL, NULL);
ESP_LOGE(OT_PLAT_LOG_TAG, "Failed to stop OpenThread netif"); otIp6SetAddressCallback(instance, NULL, NULL);
if (s_packet_queue) {
otMessage *msg = NULL;
while (xQueueReceive(s_packet_queue, &msg, 0) == pdTRUE) {
if (msg) {
otMessageFree(msg);
}
}
vQueueDelete(s_packet_queue);
s_packet_queue = NULL;
} }
s_openthread_netif = NULL;
unregister_openthread_event_handlers();
esp_openthread_platform_workflow_unregister(netif_glue_workflow);
} }
void esp_openthread_netif_glue_update(esp_openthread_mainloop_context_t *mainloop) void esp_openthread_netif_glue_update(esp_openthread_mainloop_context_t *mainloop)
@@ -33,15 +33,18 @@ static esp_openthread_platform_workflow_t *s_workflow_list = NULL;
esp_err_t esp_openthread_platform_workflow_register(esp_openthread_update_func update_func, esp_err_t esp_openthread_platform_workflow_register(esp_openthread_update_func update_func,
esp_openthread_process_func process_func, const char *name) esp_openthread_process_func process_func, const char *name)
{ {
uint8_t name_len = strnlen(name, WORKFLOW_MAX_NAMELEN - 1); assert(name != NULL);
size_t name_len = strlen(name);
ESP_RETURN_ON_FALSE(name_len > 0 && name_len < WORKFLOW_MAX_NAMELEN, ESP_ERR_INVALID_ARG, OT_PLAT_LOG_TAG,
"Workflow name '%s' is invalid", name);
esp_openthread_platform_workflow_t *current_workflow = s_workflow_list; esp_openthread_platform_workflow_t *current_workflow = s_workflow_list;
esp_openthread_platform_workflow_t *before_workflow = NULL; esp_openthread_platform_workflow_t *before_workflow = NULL;
esp_openthread_platform_workflow_t *add_workflow = esp_openthread_platform_workflow_t *add_workflow =
static_cast<esp_openthread_platform_workflow_t *>(calloc(1, sizeof(esp_openthread_platform_workflow_t))); static_cast<esp_openthread_platform_workflow_t *>(calloc(1, sizeof(esp_openthread_platform_workflow_t)));
ESP_RETURN_ON_FALSE(add_workflow != NULL, ESP_ERR_NO_MEM, OT_PLAT_LOG_TAG, ESP_RETURN_ON_FALSE(add_workflow != NULL, ESP_ERR_NO_MEM, OT_PLAT_LOG_TAG,
"Failed to alloc memory for esp_openthread_workflow"); "Failed to alloc memory for esp_openthread_workflow");
strncpy(add_workflow->name, name, name_len); memcpy(add_workflow->name, name, name_len + 1);
add_workflow->name[name_len] = '\0';
add_workflow->update_func = update_func; add_workflow->update_func = update_func;
add_workflow->process_func = process_func; add_workflow->process_func = process_func;
add_workflow->next = NULL; add_workflow->next = NULL;
@@ -67,6 +70,9 @@ esp_err_t esp_openthread_platform_workflow_register(esp_openthread_update_func u
void esp_openthread_platform_workflow_unregister(const char *name) void esp_openthread_platform_workflow_unregister(const char *name)
{ {
if (name == NULL) {
return;
}
esp_openthread_platform_workflow_t *current_workflow = s_workflow_list; esp_openthread_platform_workflow_t *current_workflow = s_workflow_list;
esp_openthread_platform_workflow_t *before_workflow = NULL; esp_openthread_platform_workflow_t *before_workflow = NULL;
while (current_workflow) { while (current_workflow) {
@@ -206,13 +212,17 @@ void esp_openthread_platform_update(esp_openthread_mainloop_context_t *mainloop)
esp_err_t esp_openthread_platform_process(otInstance *instance, const esp_openthread_mainloop_context_t *mainloop) esp_err_t esp_openthread_platform_process(otInstance *instance, const esp_openthread_mainloop_context_t *mainloop)
{ {
esp_err_t error = ESP_OK;
esp_openthread_platform_workflow_t *current_workflow = s_workflow_list; esp_openthread_platform_workflow_t *current_workflow = s_workflow_list;
while (current_workflow) { while (current_workflow) {
ESP_RETURN_ON_ERROR(current_workflow->process_func(instance, mainloop), OT_PLAT_LOG_TAG, "process %s failed", esp_err_t ret = current_workflow->process_func(instance, mainloop);
current_workflow->name); if (ret != ESP_OK) {
ESP_LOGE(OT_PLAT_LOG_TAG, "process %s failed", current_workflow->name);
error = ret;
}
current_workflow = current_workflow->next; current_workflow = current_workflow->next;
} }
return ESP_OK; return error;
} }
uint32_t esp_openthread_get_alloc_caps(void) uint32_t esp_openthread_get_alloc_caps(void)
@@ -63,9 +63,13 @@ esp_err_t IRAM_ATTR esp_openthread_task_queue_post(esp_openthread_task_t task, v
BaseType_t task_woken = pdFALSE; BaseType_t task_woken = pdFALSE;
if (!xPortCanYield()) { if (!xPortCanYield()) {
ESP_RETURN_ON_FALSE_ISR(s_task_queue != NULL && s_task_queue_event_fd >= 0, ESP_ERR_INVALID_STATE, OT_PLAT_LOG_TAG,
"OpenThread task queue not initialized");
ESP_RETURN_ON_FALSE_ISR(xQueueSendFromISR(s_task_queue, &task_storage, &task_woken), ESP_FAIL, OT_PLAT_LOG_TAG, ESP_RETURN_ON_FALSE_ISR(xQueueSendFromISR(s_task_queue, &task_storage, &task_woken), ESP_FAIL, OT_PLAT_LOG_TAG,
"Failed to post task to OpenThread task queue"); "Failed to post task to OpenThread task queue");
} else { } else {
ESP_RETURN_ON_FALSE(s_task_queue != NULL && s_task_queue_event_fd >= 0, ESP_ERR_INVALID_STATE, OT_PLAT_LOG_TAG,
"OpenThread task queue not initialized");
ESP_RETURN_ON_FALSE(xQueueSend(s_task_queue, &task_storage, OT_TASK_QUEUE_SENDING_WAIT_TIME), ESP_FAIL, OT_PLAT_LOG_TAG, ESP_RETURN_ON_FALSE(xQueueSend(s_task_queue, &task_storage, OT_TASK_QUEUE_SENDING_WAIT_TIME), ESP_FAIL, OT_PLAT_LOG_TAG,
"Failed to post task to OpenThread task queue"); "Failed to post task to OpenThread task queue");
} }