| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808 |
- /*
- * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
- *
- * SPDX-License-Identifier: Apache-2.0
- */
- #include <stdint.h>
- #include <stdbool.h>
- #include <stdio.h>
- #include <sys/queue.h>
- #include "sdkconfig.h"
- #include "freertos/FreeRTOS.h"
- #include "freertos/task.h"
- #include "esp_private/freertos_debug.h"
- #include "esp_err.h"
- #include "esp_attr.h"
- #include "esp_check.h"
- #include "esp_log.h"
- #include "esp_debug_helpers.h"
- #include "esp_freertos_hooks.h"
- #include "esp_task_wdt.h"
- #include "esp_private/system_internal.h"
- #include "esp_private/crosscore_int.h"
- #include "esp_private/esp_task_wdt.h"
- #include "esp_private/esp_task_wdt_impl.h"
- #if CONFIG_IDF_TARGET_ARCH_RISCV
- #include "riscv/rvruntime-frames.h"
- #endif //CONFIG_IDF_TARGET_ARCH_RISCV
- #if CONFIG_ESP_SYSTEM_USE_EH_FRAME
- #include "esp_private/eh_frame_parser.h"
- #endif // CONFIG_ESP_SYSTEM_USE_EH_FRAME
- #if CONFIG_IDF_TARGET_ARCH_RISCV && !CONFIG_ESP_SYSTEM_USE_EH_FRAME
- /* Function used to print all the registers pointed by the given frame .*/
- extern void panic_print_registers(const void *frame, int core);
- #endif // CONFIG_IDF_TARGET_ARCH_RISCV && !CONFIG_ESP_SYSTEM_USE_EH_FRAME
- /* We will use this function in order to simulate an `abort()` occurring in
- * a different context than the one it's called from. */
- extern void xt_unhandled_exception(void *frame);
- /* Forward declaration of the idle hook callback */
- static bool idle_hook_cb(void);
- /* Global flag set to make the `panic` mechanism think a real `abort()` was
- * called. This is used in the ISR handler, in case we have to panic when
- * a task doesn't feed its timer. */
- extern bool g_panic_abort;
- /* Global flag marking whether the current ISR is a Task Watchdog ISR. */
- bool g_twdt_isr = false;
- // --------------------------------------------------- Definitions -----------------------------------------------------
- // ---------------------- Typedefs -------------------------
- /**
- * @brief Structure used for each subscribed task
- */
- typedef struct twdt_entry twdt_entry_t;
- struct twdt_entry {
- SLIST_ENTRY(twdt_entry) slist_entry;
- TaskHandle_t task_handle; // NULL if user entry
- const char *user_name; // NULL if task entry
- bool has_reset;
- };
- // Structure used to hold run time configuration of the TWDT
- typedef struct twdt_obj twdt_obj_t;
- struct twdt_obj {
- twdt_ctx_t impl_ctx;
- SLIST_HEAD(entry_list_head, twdt_entry) entries_slist;
- uint32_t idle_core_mask; // Current core's who's idle tasks are subscribed
- bool panic; // Flag to trigger panic when TWDT times out
- bool waiting_for_task; // Flag to start the timer as soon as a task is added
- };
- // ----------------------- Objects -------------------------
- static const char *TAG = "task_wdt";
- static portMUX_TYPE spinlock = portMUX_INITIALIZER_UNLOCKED;
- static twdt_obj_t *p_twdt_obj = NULL;
- #if CONFIG_FREERTOS_SMP
- #define CORE_USER_NAME_LEN 8 // Long enough for "CPU XXX"
- static esp_task_wdt_user_handle_t core_user_handles[portNUM_PROCESSORS] = {NULL};
- static char core_user_names[portNUM_PROCESSORS][CORE_USER_NAME_LEN];
- #endif
- // ----------------------------------------------------- Private -------------------------------------------------------
- // ----------------------- Helpers -------------------------
- /**
- * @brief Reset the timer and reset flags of each entry
- * When entering this function, the spinlock has already been taken, no need to take it back.
- */
- static void task_wdt_timer_feed(void)
- {
- esp_task_wdt_impl_timer_feed(p_twdt_obj->impl_ctx);
- /* Clear the has_reset flag in each entry */
- twdt_entry_t *entry;
- SLIST_FOREACH(entry, &p_twdt_obj->entries_slist, slist_entry) {
- entry->has_reset = false;
- }
- }
- /**
- * @brief Checks whether a user entry exists and if all other entries have been reset
- *
- * @param[in] user_entry User entry
- * @param[out] all_reset Whether all entries have been reset
- * @return Whether the user entry exists
- */
- static bool find_entry_and_check_all_reset(twdt_entry_t *user_entry, bool *all_reset)
- {
- bool found_user_entry = false;
- bool found_non_reset = false;
- twdt_entry_t *entry;
- SLIST_FOREACH(entry, &p_twdt_obj->entries_slist, slist_entry) {
- if (entry == user_entry) {
- found_user_entry = true;
- } else if (entry->has_reset == false) {
- found_non_reset = true;
- }
- }
- *all_reset = !found_non_reset;
- return found_user_entry;
- }
- /**
- * @brief Find whether a task entry exists, and checks if all other entries have been reset
- *
- * @param[in] handle Task handle
- * @param[out] all_reset Whether all entries have been reset
- * @return Task entry, or NULL if not found
- */
- static twdt_entry_t *find_entry_from_task_handle_and_check_all_reset(TaskHandle_t handle, bool *all_reset)
- {
- twdt_entry_t *target = NULL;
- bool found_non_reset = false;
- twdt_entry_t *entry;
- SLIST_FOREACH(entry, &p_twdt_obj->entries_slist, slist_entry) {
- if (entry->task_handle == handle) {
- target = entry;
- } else if (entry->has_reset == false) {
- found_non_reset = true;
- }
- }
- *all_reset = !found_non_reset;
- return target;
- }
- /**
- * @brief Create a task/user entry and add it to the task WDT
- *
- * @param[in] is_task Whether the entry is a task entry or user entry
- * @param[in] entry_data Data associated with the entry (either a task handle or user entry name)
- * @param[out] entry_ret Pointer to created entry
- * @return ESP_OK if entry was added, failure otherwise
- */
- static esp_err_t add_entry(bool is_task, void *entry_data, twdt_entry_t **entry_ret)
- {
- esp_err_t ret;
- // Allocate entry object
- twdt_entry_t *entry = calloc(1, sizeof(twdt_entry_t));
- if (entry == NULL) {
- return ESP_ERR_NO_MEM;
- }
- if (is_task) {
- entry->task_handle = (TaskHandle_t)entry_data;
- } else {
- entry->user_name = (const char *)entry_data;
- }
- portENTER_CRITICAL(&spinlock);
- // Check TWDT state
- ESP_GOTO_ON_FALSE_ISR((p_twdt_obj != NULL), ESP_ERR_INVALID_STATE, state_err, TAG, "task watchdog was never initialized");
- // Check if the task is an entry, and if all entries have been reset
- bool all_reset;
- if (is_task) {
- twdt_entry_t *entry_found = find_entry_from_task_handle_and_check_all_reset(entry->task_handle, &all_reset);
- ESP_GOTO_ON_FALSE_ISR((entry_found == NULL), ESP_ERR_INVALID_ARG, state_err, TAG, "task is already subscribed");
- } else {
- bool entry_found = find_entry_and_check_all_reset(entry, &all_reset);
- ESP_GOTO_ON_FALSE_ISR(!entry_found, ESP_ERR_INVALID_ARG, state_err, TAG, "user is already subscribed");
- }
- // Add entry to list
- SLIST_INSERT_HEAD(&p_twdt_obj->entries_slist, entry, slist_entry);
- // Start the timer if it has not been started yet and was waiting on a task to registered
- if (p_twdt_obj->waiting_for_task) {
- esp_task_wdt_impl_timer_restart(p_twdt_obj->impl_ctx);
- p_twdt_obj->waiting_for_task = false;
- }
- if (all_reset) { //Reset hardware timer if all other tasks in list have reset in
- task_wdt_timer_feed();
- }
- portEXIT_CRITICAL(&spinlock);
- *entry_ret = entry;
- return ESP_OK;
- state_err:
- portEXIT_CRITICAL(&spinlock);
- free(entry);
- return ret;
- }
- /**
- * @brief Delete a task/user entry
- *
- * @param[in] is_task Whether the entry is a task entry or user entry
- * @param[in] entry_data Data associated with the entry (either a task handle or user entry name)
- * @return ESP_OK if entry was deleted, failure otherwise
- */
- static esp_err_t delete_entry(bool is_task, void *entry_data)
- {
- esp_err_t ret;
- portENTER_CRITICAL(&spinlock);
- // Check TWDT state
- ESP_GOTO_ON_FALSE_ISR((p_twdt_obj != NULL), ESP_ERR_INVALID_STATE, err, TAG, "task watchdog was never initialized");
- // Find entry for task
- bool all_reset;
- twdt_entry_t *entry;
- if (is_task) {
- entry = find_entry_from_task_handle_and_check_all_reset((TaskHandle_t)entry_data, &all_reset);
- ESP_GOTO_ON_FALSE_ISR((entry != NULL), ESP_ERR_NOT_FOUND, err, TAG, "task not found");
- } else {
- entry = (twdt_entry_t *)entry_data;
- bool entry_found = find_entry_and_check_all_reset(entry, &all_reset);
- ESP_GOTO_ON_FALSE_ISR(entry_found, ESP_ERR_NOT_FOUND, err, TAG, "user not found");
- }
- // Remove entry
- SLIST_REMOVE(&p_twdt_obj->entries_slist, entry, twdt_entry, slist_entry);
- /* Stop the timer if we don't have any more tasks/objects to watch */
- if (SLIST_EMPTY(&p_twdt_obj->entries_slist)) {
- p_twdt_obj->waiting_for_task = true;
- esp_task_wdt_impl_timer_stop(p_twdt_obj->impl_ctx);
- } else {
- p_twdt_obj->waiting_for_task = false;
- }
- /* Reset hardware timer if all remaining tasks have reset and if the list of tasks is not empty */
- if (!p_twdt_obj->waiting_for_task && all_reset) {
- task_wdt_timer_feed();
- }
- portEXIT_CRITICAL(&spinlock);
- free(entry);
- return ESP_OK;
- err:
- portEXIT_CRITICAL(&spinlock);
- return ret;
- }
- /**
- * @brief Unsubscribe the idle tasks of one or more cores
- *
- * @param core_mask
- */
- static void unsubscribe_idle(uint32_t core_mask)
- {
- int core_num = 0;
- while (core_mask != 0) {
- if (core_mask & 0x1) {
- #if CONFIG_FREERTOS_SMP
- assert(core_user_handles[core_num]);
- esp_deregister_freertos_idle_hook_for_cpu(idle_hook_cb, core_num);
- ESP_ERROR_CHECK(esp_task_wdt_delete_user(core_user_handles[core_num]));
- core_user_handles[core_num] = NULL;
- #else // CONFIG_FREERTOS_SMP
- TaskHandle_t idle_task_handle = xTaskGetIdleTaskHandleForCore(core_num);
- assert(idle_task_handle);
- esp_deregister_freertos_idle_hook_for_cpu(idle_hook_cb, core_num);
- ESP_ERROR_CHECK(esp_task_wdt_delete(idle_task_handle));
- #endif // CONFIG_FREERTOS_SMP
- }
- core_mask >>= 1;
- core_num++;
- }
- }
- /**
- * @brief Subscribes the idle tasks of one or more cores
- *
- * @param core_mask Bit mask of cores to subscribe
- */
- static void subscribe_idle(uint32_t core_mask)
- {
- int core_num = 0;
- while (core_mask != 0) {
- if (core_mask & 0x1) {
- #if CONFIG_FREERTOS_SMP
- snprintf(core_user_names[core_num], CORE_USER_NAME_LEN, "CPU %d", (uint8_t)core_num);
- ESP_ERROR_CHECK(esp_task_wdt_add_user((const char *)core_user_names[core_num], &core_user_handles[core_num]));
- ESP_ERROR_CHECK(esp_register_freertos_idle_hook_for_cpu(idle_hook_cb, core_num));
- #else // CONFIG_FREERTOS_SMP
- TaskHandle_t idle_task_handle = xTaskGetIdleTaskHandleForCore(core_num);
- assert(idle_task_handle);
- ESP_ERROR_CHECK(esp_task_wdt_add(idle_task_handle));
- ESP_ERROR_CHECK(esp_register_freertos_idle_hook_for_cpu(idle_hook_cb, core_num));
- #endif // CONFIG_FREERTOS_SMP
- }
- core_mask >>= 1;
- core_num++;
- }
- }
- /**
- * The behavior of the Task Watchdog depends on the configuration from the `menuconfig`.
- * It can be summarized as follow, regardless of the target:
- * +------------------------+--------------------------------+--------------------------+
- * | \ Panic configuration | | |
- * | +------------------+ | Panic Enabled | Panic Disabled |
- * | TWDT triggered on \ | | |
- * +------------------------+--------------------------------+--------------------------+
- * | | - Current core backtrace | - Current core backtrace |
- * | Both Cores | - Crosscore TWDT abort | - Crosscore backtrace |
- * | | - Wait for other core to abort | |
- * +------------------------+--------------------------------+--------------------------+
- * | Other Core | - Crosscore TWDT abort | - Crosscore backtrace |
- * +------------------------+--------------------------------+--------------------------+
- * | Current Core | - Abort from current CPU | - Current core backtrace |
- * +------------------------+--------------------------------+--------------------------+
- *
- */
- /**
- * Function simulating an abort coming from the interrupted task of the current
- * core.
- * It is called either by the function right below or by a crosscore interrupt,
- * in the case where the other core (than the main one) has to abort because one
- * of his tasks didn't reset the TWDT on time.
- */
- void task_wdt_timeout_abort(bool current_core)
- {
- TaskSnapshot_t snapshot = { 0 };
- BaseType_t ret = pdTRUE;
- ESP_EARLY_LOGE(TAG, "Aborting.");
- esp_reset_reason_set_hint(ESP_RST_TASK_WDT);
- ret = vTaskGetSnapshot(xTaskGetCurrentTaskHandle(), &snapshot);
- assert(ret == pdTRUE);
- g_panic_abort = true;
- /* We should set this flag as late as possible, as this function may
- * be called after a crosscore interrupt. Indeed, a higher interrupt may occur
- * after calling the crosscore interrupt, if its handler fails, this flag
- * shall not be set.
- * This flag will tell the coredump component (if activated) that yes, we are in
- * an ISR context, but it is intended, it is not because an ISR encountered an
- * exception. If we don't set such flag, later tested by coredump, the latter would
- * switch the execution frame/context we are giving it to the interrupt stack.
- * For details about this behavior in the TODO task: IDF-5694
- */
- g_twdt_isr = true;
- void *frame = (void *) snapshot.pxTopOfStack;
- #if CONFIG_ESP_SYSTEM_USE_EH_FRAME | CONFIG_IDF_TARGET_ARCH_XTENSA
- if (current_core) {
- ESP_EARLY_LOGE(TAG, "Print CPU %d (current core) backtrace", xPortGetCoreID());
- } else {
- ESP_EARLY_LOGE(TAG, "Print CPU %d backtrace", xPortGetCoreID());
- }
- #endif
- xt_unhandled_exception(frame);
- }
- static void task_wdt_timeout_handling(int cores_fail, bool panic)
- {
- const int current_core = xPortGetCoreID();
- if (panic) {
- #if !CONFIG_FREERTOS_UNICORE
- const int other_core = !current_core;
- if ((cores_fail & BIT(0)) && (cores_fail & BIT(1))) {
- /* In the case where both CPUs have failing tasks, print the current CPU backtrace and then let the
- * other core fail. */
- ESP_EARLY_LOGE(TAG, "Print CPU %d (current core) backtrace", current_core);
- esp_backtrace_print(100);
- /* TODO: the interrupt we send should have the highest priority */
- esp_crosscore_int_send_twdt_abort(other_core);
- /* We are going to abort, on the other core, we have nothing to
- * do anymore here, just wait until we crash */
- while (1) {}
- } else if (cores_fail & BIT(other_core)) {
- /* If only the other core is failing, we can tell it to abort. */
- esp_crosscore_int_send_twdt_abort(other_core);
- while (1) {}
- }
- #endif // !CONFIG_FREERTOS_UNICORE
- /* Current core is failing, abort right now */
- task_wdt_timeout_abort(true);
- } else {
- /* Print backtrace of the core that failed to reset the watchdog */
- if (cores_fail & BIT(current_core)) {
- ESP_EARLY_LOGE(TAG, "Print CPU %d (current core) backtrace", current_core);
- esp_backtrace_print(100);
- }
- #if !CONFIG_FREERTOS_UNICORE
- const int other_core = !current_core;
- if (cores_fail & BIT(other_core)) {
- ESP_EARLY_LOGE(TAG, "Print CPU %d backtrace", other_core);
- esp_crosscore_int_send_print_backtrace(other_core);
- }
- #endif // !CONFIG_FREERTOS_UNICORE
- }
- }
- // ---------------------- Callbacks ------------------------
- /**
- * @brief Idle hook callback
- *
- * Idle hook callback called by the idle tasks to feed the TWDT
- *
- * @return Whether the idle tasks should continue idling
- */
- static bool idle_hook_cb(void)
- {
- #if CONFIG_FREERTOS_SMP
- esp_task_wdt_reset_user(core_user_handles[xPortGetCoreID()]);
- #else // CONFIG_FREERTOS_SMP
- esp_task_wdt_reset();
- #endif // CONFIG_FREERTOS_SMP
- return true;
- }
- /**
- * @brief TWDT timeout ISR function
- *
- * The ISR checks which entries have not been reset, prints some debugging information, and triggers a panic if
- * configured to do so.
- *
- * @param arg ISR argument
- */
- static void task_wdt_isr(void *arg)
- {
- portENTER_CRITICAL_ISR(&spinlock);
- esp_task_wdt_impl_timeout_triggered(p_twdt_obj->impl_ctx);
- // If there are no entries, there's nothing to do.
- if (SLIST_EMPTY(&p_twdt_obj->entries_slist)) {
- portEXIT_CRITICAL_ISR(&spinlock);
- return;
- }
- // Find what entries triggered the TWDT timeout (i.e., which entries have not been reset)
- /*
- Note: We are currently in a critical section, thus under normal circumstances, logging should not be allowed.
- However, TWDT timeouts count as fatal errors, thus reporting the fatal error is considered more important than
- minimizing interrupt latency. Thus we allow logging in critical sections in this narrow case.
- */
- ESP_EARLY_LOGE(TAG, "Task watchdog got triggered. The following tasks/users did not reset the watchdog in time:");
- twdt_entry_t *entry;
- /* Keep a bitmap of CPU cores having tasks that have not reset TWDT.
- * Bit 0 represents core 0, bit 1 represents core 1, and so on. */
- int cpus_fail = 0;
- bool panic = p_twdt_obj->panic;
- SLIST_FOREACH(entry, &p_twdt_obj->entries_slist, slist_entry) {
- if (!entry->has_reset) {
- if (entry->task_handle) {
- #if CONFIG_FREERTOS_SMP
- #if configNUM_CORES > 1
- // Log the task's name and its affinity
- const UBaseType_t affinity = vTaskCoreAffinityGet(entry->task_handle);
- ESP_EARLY_LOGE(TAG, " - %s (0x%x)", pcTaskGetName(entry->task_handle), affinity);
- cpus_fail |= affinity;
- #else // configNUM_CORES > 1
- // Log the task's name
- ESP_EARLY_LOGE(TAG, " - %s", pcTaskGetName(entry->task_handle));
- cpus_fail |= BIT(0);
- #endif // configNUM_CORES > 1
- #else // CONFIG_FREERTOS_SMP
- BaseType_t task_affinity = xTaskGetCoreID(entry->task_handle);
- const char *cpu;
- if (task_affinity == 0) {
- cpu = DRAM_STR("CPU 0");
- cpus_fail |= BIT(0);
- } else if (task_affinity == 1) {
- cpu = DRAM_STR("CPU 1");
- cpus_fail |= BIT(1);
- } else {
- cpu = DRAM_STR("CPU 0/1");
- cpus_fail |= BIT(1) | BIT(0);
- }
- ESP_EARLY_LOGE(TAG, " - %s (%s)", pcTaskGetName(entry->task_handle), cpu);
- #endif // CONFIG_FREERTOS_SMP
- } else {
- /* User entry, we cannot predict on which core it is scheduled to run,
- * so let's mark all cores as failing */
- #if configNUM_CORES > 1
- cpus_fail = BIT(1) | BIT(0);
- #else // configNUM_CORES > 1
- cpus_fail = BIT(0);
- #endif // configNUM_CORES > 1
- ESP_EARLY_LOGE(TAG, " - %s", entry->user_name);
- }
- }
- }
- ESP_EARLY_LOGE(TAG, "%s", DRAM_STR("Tasks currently running:"));
- for (int x = 0; x < portNUM_PROCESSORS; x++) {
- ESP_EARLY_LOGE(TAG, "CPU %d: %s", x, pcTaskGetName(xTaskGetCurrentTaskHandleForCore(x)));
- }
- portEXIT_CRITICAL_ISR(&spinlock);
- /* Run user ISR handler.
- * This function has been declared as weak, thus, it may be possible that it was not defines.
- * to check this, we can directly test its address. In any case, the linker will get rid of
- * this `if` when linking, this means that if the function was not defined, the whole `if`
- * block will be discarded (zero runtime overhead), else only the function call will be kept.
- */
- if (esp_task_wdt_isr_user_handler != NULL) {
- esp_task_wdt_isr_user_handler();
- }
- // Trigger configured timeout behavior (e.g., panic or print backtrace)
- assert(cpus_fail != 0);
- task_wdt_timeout_handling(cpus_fail, panic);
- }
- // ----------------------------------------------------- Public --------------------------------------------------------
- esp_err_t esp_task_wdt_init(const esp_task_wdt_config_t *config)
- {
- ESP_RETURN_ON_FALSE((config != NULL && config->idle_core_mask < (1 << portNUM_PROCESSORS)), ESP_ERR_INVALID_ARG, TAG, "Invalid arguments");
- ESP_RETURN_ON_FALSE(p_twdt_obj == NULL, ESP_ERR_INVALID_STATE, TAG, "TWDT already initialized");
- esp_err_t ret = ESP_OK;
- twdt_obj_t *obj = NULL;
- /* Allocate and initialize the global object */
- obj = calloc(1, sizeof(twdt_obj_t));
- ESP_GOTO_ON_FALSE((obj != NULL), ESP_ERR_NO_MEM, err, TAG, "insufficient memory");
- SLIST_INIT(&obj->entries_slist);
- obj->panic = config->trigger_panic;
- /* Allocate the timer itself, NOT STARTED */
- ret = esp_task_wdt_impl_timer_allocate(config, task_wdt_isr, &obj->impl_ctx);
- if (ret != ESP_OK) {
- goto err;
- }
- /* No error so far, we can assign it to the driver object */
- p_twdt_obj = obj;
- /* Update which core's idle tasks are subscribed */
- p_twdt_obj->idle_core_mask = config->idle_core_mask;
- if (config->idle_core_mask) {
- /* Subscribe the new cores idle tasks */
- subscribe_idle(config->idle_core_mask);
- }
- /* Start the timer only if we are watching some tasks */
- if (!SLIST_EMPTY(&p_twdt_obj->entries_slist)) {
- p_twdt_obj->waiting_for_task = false;
- esp_task_wdt_impl_timer_restart(p_twdt_obj->impl_ctx);
- } else {
- p_twdt_obj->waiting_for_task = true;
- }
- return ESP_OK;
- err:
- free(obj);
- return ret;
- }
- esp_err_t esp_task_wdt_reconfigure(const esp_task_wdt_config_t *config)
- {
- ESP_RETURN_ON_FALSE((config != NULL && config->idle_core_mask < (1 << portNUM_PROCESSORS)), ESP_ERR_INVALID_ARG, TAG, "Invalid arguments");
- ESP_RETURN_ON_FALSE(p_twdt_obj != NULL, ESP_ERR_INVALID_STATE, TAG, "TWDT not initialized yet");
- uint32_t old_core_mask = 0;
- esp_err_t ret = ESP_OK;
- /* Stop the timer to make sure we don't get into the ISR while reconfiguring the TWDT */
- portENTER_CRITICAL(&spinlock);
- ret = esp_task_wdt_impl_timer_stop(p_twdt_obj->impl_ctx);
- if (ret != ESP_OK) {
- goto err;
- }
- /* We can start reconfiguring the tasks */
- p_twdt_obj->panic = config->trigger_panic;
- /* Reconfigure the timer underneath (without restarting it) */
- ret = esp_task_wdt_impl_timer_reconfigure(p_twdt_obj->impl_ctx, config);
- if (ret != ESP_OK) {
- goto err;
- }
- old_core_mask = p_twdt_obj->idle_core_mask;
- /* If the new mask is different than the old one, we have to subscribe the new idle tasks */
- if (old_core_mask != config->idle_core_mask) {
- p_twdt_obj->idle_core_mask = config->idle_core_mask;
- /* Unsubscribe all previously watched core idle tasks */
- unsubscribe_idle(old_core_mask);
- if (config->idle_core_mask) {
- /* Subscribe the new cores idle tasks */
- subscribe_idle(config->idle_core_mask);
- }
- }
- /* Start the timer only if we are watching some tasks */
- if (!SLIST_EMPTY(&p_twdt_obj->entries_slist)) {
- esp_task_wdt_impl_timer_restart(p_twdt_obj->impl_ctx);
- }
- portEXIT_CRITICAL(&spinlock);
- err:
- return ESP_OK;
- }
- esp_err_t esp_task_wdt_stop(void)
- {
- esp_err_t ret = ESP_OK;
- /* If the timer has not been initialized, do not attempt to stop it */
- if (p_twdt_obj == NULL) {
- ret = ESP_ERR_INVALID_STATE;
- }
- if (ret == ESP_OK) {
- portENTER_CRITICAL(&spinlock);
- ret = esp_task_wdt_impl_timer_stop(p_twdt_obj->impl_ctx);
- portEXIT_CRITICAL(&spinlock);
- }
- return ret;
- }
- esp_err_t esp_task_wdt_restart(void)
- {
- esp_err_t ret = ESP_OK;
- /* If the timer has not been initialized, do not attempt to stop it */
- if (p_twdt_obj == NULL) {
- ret = ESP_ERR_INVALID_STATE;
- }
- if (ret == ESP_OK) {
- portENTER_CRITICAL(&spinlock);
- ret = esp_task_wdt_impl_timer_restart(p_twdt_obj->impl_ctx);
- portEXIT_CRITICAL(&spinlock);
- }
- return ret;
- }
- esp_err_t esp_task_wdt_deinit(void)
- {
- esp_err_t ret;
- ESP_RETURN_ON_FALSE(p_twdt_obj != NULL, ESP_ERR_INVALID_STATE, TAG, "TWDT was never initialized");
- // Unsubscribe all previously watched core idle tasks
- unsubscribe_idle(p_twdt_obj->idle_core_mask);
- // Check TWDT state
- ESP_GOTO_ON_FALSE_ISR(SLIST_EMPTY(&p_twdt_obj->entries_slist), ESP_ERR_INVALID_STATE, err, TAG, "Tasks/users still subscribed");
- // Disable the timer
- esp_task_wdt_impl_timer_stop(p_twdt_obj->impl_ctx);
- // Free driver resources
- esp_task_wdt_impl_timer_free(p_twdt_obj->impl_ctx);
- // Free the global object
- free(p_twdt_obj);
- p_twdt_obj = NULL;
- return ESP_OK;
- err:
- subscribe_idle(p_twdt_obj->idle_core_mask); // Resubscribe idle tasks
- return ret;
- }
- esp_err_t esp_task_wdt_add(TaskHandle_t task_handle)
- {
- ESP_RETURN_ON_FALSE(p_twdt_obj != NULL, ESP_ERR_INVALID_STATE, TAG, "TWDT was never initialized");
- esp_err_t ret;
- if (task_handle == NULL) { // Get handle of current task if none is provided
- task_handle = xTaskGetCurrentTaskHandle();
- }
- twdt_entry_t *entry;
- ret = add_entry(true, (void *)task_handle, &entry);
- (void) entry; // Returned entry pointer not used
- return ret;
- }
- esp_err_t esp_task_wdt_add_user(const char *user_name, esp_task_wdt_user_handle_t *user_handle_ret)
- {
- ESP_RETURN_ON_FALSE((user_name != NULL && user_handle_ret != NULL), ESP_ERR_INVALID_ARG, TAG, "Invalid arguments");
- ESP_RETURN_ON_FALSE(p_twdt_obj != NULL, ESP_ERR_INVALID_STATE, TAG, "TWDT was never initialized");
- esp_err_t ret;
- twdt_entry_t *entry;
- ret = add_entry(false, (void *)user_name, &entry);
- if (ret == ESP_OK) {
- *user_handle_ret = (esp_task_wdt_user_handle_t)entry;
- }
- return ret;
- }
- esp_err_t esp_task_wdt_reset(void)
- {
- ESP_RETURN_ON_FALSE(p_twdt_obj != NULL, ESP_ERR_INVALID_STATE, TAG, "TWDT was never initialized");
- esp_err_t ret;
- TaskHandle_t handle = xTaskGetCurrentTaskHandle();
- portENTER_CRITICAL(&spinlock);
- // Find entry from task handle
- bool all_reset;
- twdt_entry_t *entry;
- entry = find_entry_from_task_handle_and_check_all_reset(handle, &all_reset);
- ESP_GOTO_ON_FALSE_ISR((entry != NULL), ESP_ERR_NOT_FOUND, err, TAG, "task not found");
- // Mark entry as reset and issue timer reset if all entries have been reset
- entry->has_reset = true; // Reset the task if it's on the task list
- if (all_reset) { // Reset if all other tasks in list have reset in
- task_wdt_timer_feed();
- }
- ret = ESP_OK;
- err:
- portEXIT_CRITICAL(&spinlock);
- return ret;
- }
- esp_err_t esp_task_wdt_reset_user(esp_task_wdt_user_handle_t user_handle)
- {
- ESP_RETURN_ON_FALSE(user_handle != NULL, ESP_ERR_INVALID_ARG, TAG, "Invalid arguments");
- ESP_RETURN_ON_FALSE(p_twdt_obj != NULL, ESP_ERR_INVALID_STATE, TAG, "TWDT was never initialized");
- esp_err_t ret;
- portENTER_CRITICAL(&spinlock);
- // Check if entry exists
- bool all_reset;
- twdt_entry_t *entry = (twdt_entry_t *)user_handle;
- bool entry_found = find_entry_and_check_all_reset(entry, &all_reset);
- ESP_GOTO_ON_FALSE_ISR(entry_found, ESP_ERR_NOT_FOUND, err, TAG, "user handle not found");
- // Mark entry as reset and issue timer reset if all entries have been reset
- entry->has_reset = true; // Reset the task if it's on the task list
- if (all_reset) { // Reset if all other tasks in list have reset in
- task_wdt_timer_feed();
- }
- ret = ESP_OK;
- err:
- portEXIT_CRITICAL(&spinlock);
- return ret;
- }
- esp_err_t esp_task_wdt_delete(TaskHandle_t task_handle)
- {
- ESP_RETURN_ON_FALSE(p_twdt_obj != NULL, ESP_ERR_INVALID_STATE, TAG, "TWDT was never initialized");
- esp_err_t ret;
- if (task_handle == NULL) {
- task_handle = xTaskGetCurrentTaskHandle();
- }
- ret = delete_entry(true, (void *)task_handle);
- return ret;
- }
- esp_err_t esp_task_wdt_delete_user(esp_task_wdt_user_handle_t user_handle)
- {
- ESP_RETURN_ON_FALSE(user_handle != NULL, ESP_ERR_INVALID_ARG, TAG, "Invalid arguments");
- ESP_RETURN_ON_FALSE(p_twdt_obj != NULL, ESP_ERR_INVALID_STATE, TAG, "TWDT was never initialized");
- return delete_entry(false, (void *)user_handle);
- }
- esp_err_t esp_task_wdt_status(TaskHandle_t task_handle)
- {
- ESP_RETURN_ON_FALSE(p_twdt_obj != NULL, ESP_ERR_INVALID_STATE, TAG, "TWDT was never initialized");
- esp_err_t ret;
- if (task_handle == NULL) {
- task_handle = xTaskGetCurrentTaskHandle();
- }
- portENTER_CRITICAL(&spinlock);
- // Find entry for task
- bool all_reset;
- twdt_entry_t *entry;
- entry = find_entry_from_task_handle_and_check_all_reset(task_handle, &all_reset);
- (void) all_reset; // Unused
- ret = (entry != NULL) ? ESP_OK : ESP_ERR_NOT_FOUND;
- portEXIT_CRITICAL(&spinlock);
- return ret;
- }
|