task_wdt.c 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598
  1. /*
  2. * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. */
  6. #include <stdint.h>
  7. #include <stdbool.h>
  8. #include <stdio.h>
  9. #include <sys/queue.h>
  10. #include "sdkconfig.h"
  11. #include "freertos/FreeRTOS.h"
  12. #include "freertos/task.h"
  13. #include "hal/wdt_hal.h"
  14. #include "esp_err.h"
  15. #include "esp_attr.h"
  16. #include "esp_check.h"
  17. #include "esp_log.h"
  18. #include "esp_intr_alloc.h"
  19. #include "esp_debug_helpers.h"
  20. #include "esp_freertos_hooks.h"
  21. #include "esp_task_wdt.h"
  22. #include "esp_private/periph_ctrl.h"
  23. #include "esp_private/system_internal.h"
  24. #include "esp_private/crosscore_int.h"
  25. // --------------------------------------------------- Definitions -----------------------------------------------------
  26. // ----------------------- Macros --------------------------
  27. // HAL related variables and constants
  28. #define TWDT_INSTANCE WDT_MWDT0
  29. #define TWDT_TICKS_PER_US MWDT0_TICKS_PER_US
  30. #define TWDT_PRESCALER MWDT0_TICK_PRESCALER // Tick period of 500us if WDT source clock is 80MHz
  31. // ---------------------- Typedefs -------------------------
  32. /**
  33. * @brief Structure used for each subscribed task
  34. */
  35. typedef struct twdt_entry twdt_entry_t;
  36. struct twdt_entry {
  37. SLIST_ENTRY(twdt_entry) slist_entry;
  38. TaskHandle_t task_handle; // NULL if user entry
  39. const char *user_name; // NULL if task entry
  40. bool has_reset;
  41. };
  42. // Structure used to hold run time configuration of the TWDT
  43. typedef struct twdt_obj twdt_obj_t;
  44. struct twdt_obj {
  45. wdt_hal_context_t hal;
  46. SLIST_HEAD(entry_list_head, twdt_entry) entries_slist;
  47. uint32_t idle_core_mask; // Current core's who's idle tasks are subscribed
  48. bool panic; // Flag to trigger panic when TWDT times out
  49. intr_handle_t intr_handle;
  50. };
  51. // ----------------------- Objects -------------------------
  52. static const char *TAG = "task_wdt";
  53. static portMUX_TYPE spinlock = portMUX_INITIALIZER_UNLOCKED;
  54. static twdt_obj_t *p_twdt_obj = NULL;
  55. #if CONFIG_FREERTOS_SMP
  56. #define CORE_USER_NAME_LEN 8 // Long enough for "CPU XXX"
  57. static esp_task_wdt_user_handle_t core_user_handles[portNUM_PROCESSORS] = {NULL};
  58. static char core_user_names[portNUM_PROCESSORS][CORE_USER_NAME_LEN];
  59. #endif
  60. // ----------------------------------------------------- Private -------------------------------------------------------
  61. // ---------------------- Callbacks ------------------------
  62. /**
  63. * @brief User ISR callback placeholder
  64. *
  65. * This function is called by task_wdt_isr function (ISR for when TWDT times out). It can be redefined in user code to
  66. * handle TWDT events.
  67. *
  68. * @note It has the same limitations as the interrupt function. Do not use ESP_LOGI functions inside.
  69. */
  70. void __attribute__((weak)) esp_task_wdt_isr_user_handler(void)
  71. {
  72. }
  73. /**
  74. * @brief Idle hook callback
  75. *
  76. * Idle hook callback called by the idle tasks to feed the TWDT
  77. *
  78. * @return Whether the idle tasks should continue idling
  79. */
  80. static bool idle_hook_cb(void)
  81. {
  82. #if CONFIG_FREERTOS_SMP
  83. esp_task_wdt_reset_user(core_user_handles[xPortGetCoreID()]);
  84. #else
  85. esp_task_wdt_reset();
  86. #endif
  87. return true;
  88. }
  89. // ----------------------- Helpers -------------------------
  90. /**
  91. * @brief Reset hardware timer and reset flags of each entry
  92. */
  93. static void reset_hw_timer(void)
  94. {
  95. // All tasks have reset; time to reset the hardware timer.
  96. wdt_hal_write_protect_disable(&p_twdt_obj->hal);
  97. wdt_hal_feed(&p_twdt_obj->hal);
  98. wdt_hal_write_protect_enable(&p_twdt_obj->hal);
  99. //Clear the has_reset flag in each entry
  100. twdt_entry_t *entry;
  101. SLIST_FOREACH(entry, &p_twdt_obj->entries_slist, slist_entry) {
  102. entry->has_reset = false;
  103. }
  104. }
  105. /**
  106. * @brief Checks whether a user entry exists and if all other entries have been reset
  107. *
  108. * @param[in] user_entry User entry
  109. * @param[out] all_reset Whether all entries have been reset
  110. * @return Whether the user entry exists
  111. */
  112. static bool find_entry_and_check_all_reset(twdt_entry_t *user_entry, bool *all_reset)
  113. {
  114. bool found_user_entry = false;
  115. bool found_non_reset = false;
  116. twdt_entry_t *entry;
  117. SLIST_FOREACH(entry, &p_twdt_obj->entries_slist, slist_entry) {
  118. if (entry == user_entry) {
  119. found_user_entry = true;
  120. } else if (entry->has_reset == false) {
  121. found_non_reset = true;
  122. }
  123. }
  124. *all_reset = !found_non_reset;
  125. return found_user_entry;
  126. }
  127. /**
  128. * @brief Find whether a task entry exists, and checks if all other entries have been reset
  129. *
  130. * @param[in] handle Task handle
  131. * @param[out] all_reset Whether all entries have been reset
  132. * @return Task entry, or NULL if not found
  133. */
  134. static twdt_entry_t *find_entry_from_task_handle_and_check_all_reset(TaskHandle_t handle, bool *all_reset)
  135. {
  136. twdt_entry_t *target = NULL;
  137. bool found_non_reset = false;
  138. twdt_entry_t *entry;
  139. SLIST_FOREACH(entry, &p_twdt_obj->entries_slist, slist_entry) {
  140. if (entry->task_handle == handle) {
  141. target = entry;
  142. } else if (entry->has_reset == false) {
  143. found_non_reset = true;
  144. }
  145. }
  146. *all_reset = !found_non_reset;
  147. return target;
  148. }
  149. /**
  150. * @brief Create a task/user entry and add it to the task WDT
  151. *
  152. * @param[in] is_task Whether the entry is a task entry or user entry
  153. * @param[in] entry_data Data associated with the entry (either a task handle or user entry name)
  154. * @param[out] entry_ret Pointer to created entry
  155. * @return ESP_OK if entry was added, failure otherwise
  156. */
  157. static esp_err_t add_entry(bool is_task, void *entry_data, twdt_entry_t **entry_ret)
  158. {
  159. esp_err_t ret;
  160. // Allocate entry object
  161. twdt_entry_t *entry = calloc(1, sizeof(twdt_entry_t));
  162. if (entry == NULL) {
  163. return ESP_ERR_NO_MEM;
  164. }
  165. if (is_task) {
  166. entry->task_handle = (TaskHandle_t)entry_data;
  167. } else {
  168. entry->user_name = (const char *)entry_data;
  169. }
  170. portENTER_CRITICAL(&spinlock);
  171. // Check TWDT state
  172. ESP_GOTO_ON_FALSE_ISR((p_twdt_obj != NULL), ESP_ERR_INVALID_STATE, state_err, TAG, "task watchdog was never initialized");
  173. // Check if the task is an entry, and if all entries have been reset
  174. bool all_reset;
  175. if (is_task) {
  176. twdt_entry_t *entry_found = find_entry_from_task_handle_and_check_all_reset(entry->task_handle, &all_reset);
  177. ESP_GOTO_ON_FALSE_ISR((entry_found == NULL), ESP_ERR_INVALID_ARG, state_err, TAG, "task is already subscribed");
  178. } else {
  179. bool entry_found = find_entry_and_check_all_reset(entry, &all_reset);
  180. ESP_GOTO_ON_FALSE_ISR(!entry_found, ESP_ERR_INVALID_ARG, state_err, TAG, "user is already subscribed");
  181. }
  182. // Add entry to list
  183. SLIST_INSERT_HEAD(&p_twdt_obj->entries_slist, entry, slist_entry);
  184. if (all_reset) { //Reset hardware timer if all other tasks in list have reset in
  185. reset_hw_timer();
  186. }
  187. portEXIT_CRITICAL(&spinlock);
  188. *entry_ret = entry;
  189. return ESP_OK;
  190. state_err:
  191. portEXIT_CRITICAL(&spinlock);
  192. free(entry);
  193. return ret;
  194. }
  195. /**
  196. * @brief Delete a task/user entry
  197. *
  198. * @param[in] is_task Whether the entry is a task entry or user entry
  199. * @param[in] entry_data Data associated with the entry (either a task handle or user entry name)
  200. * @return ESP_OK if entry was deleted, failure otherwise
  201. */
  202. static esp_err_t delete_entry(bool is_task, void *entry_data)
  203. {
  204. esp_err_t ret;
  205. portENTER_CRITICAL(&spinlock);
  206. // Check TWDT state
  207. ESP_GOTO_ON_FALSE_ISR((p_twdt_obj != NULL), ESP_ERR_INVALID_STATE, err, TAG, "task watchdog was never initialized");
  208. // Find entry for task
  209. bool all_reset;
  210. twdt_entry_t *entry;
  211. if (is_task) {
  212. entry = find_entry_from_task_handle_and_check_all_reset((TaskHandle_t)entry_data, &all_reset);
  213. ESP_GOTO_ON_FALSE_ISR((entry != NULL), ESP_ERR_NOT_FOUND, err, TAG, "task not found");
  214. } else {
  215. entry = (twdt_entry_t *)entry_data;
  216. bool entry_found = find_entry_and_check_all_reset(entry, &all_reset);
  217. ESP_GOTO_ON_FALSE_ISR(entry_found, ESP_ERR_NOT_FOUND, err, TAG, "user not found");
  218. }
  219. // Remove entry
  220. SLIST_REMOVE(&p_twdt_obj->entries_slist, entry, twdt_entry, slist_entry);
  221. // Reset hardware timer if all remaining tasks have reset
  222. if (all_reset) {
  223. reset_hw_timer();
  224. }
  225. portEXIT_CRITICAL(&spinlock);
  226. free(entry);
  227. return ESP_OK;
  228. err:
  229. portEXIT_CRITICAL(&spinlock);
  230. return ret;
  231. }
  232. /**
  233. * @brief Unsubscribe the idle tasks of one or more cores
  234. *
  235. * @param core_mask
  236. */
  237. static void unsubscribe_idle(uint32_t core_mask)
  238. {
  239. int core_num = 0;
  240. while (core_mask != 0) {
  241. if (core_mask & 0x1) {
  242. #if CONFIG_FREERTOS_SMP
  243. assert(core_user_handles[core_num]);
  244. ESP_ERROR_CHECK(esp_register_freertos_idle_hook_for_cpu(idle_hook_cb, core_num));
  245. ESP_ERROR_CHECK(esp_task_wdt_delete_user(core_user_handles[core_num]));
  246. core_user_handles[core_num] = NULL;
  247. #else
  248. TaskHandle_t idle_task_handle = xTaskGetIdleTaskHandleForCPU(core_num);
  249. assert(idle_task_handle);
  250. esp_deregister_freertos_idle_hook_for_cpu(idle_hook_cb, core_num);
  251. ESP_ERROR_CHECK(esp_task_wdt_delete(idle_task_handle));
  252. #endif
  253. }
  254. core_mask >>= 1;
  255. core_num++;
  256. }
  257. }
  258. /**
  259. * @brief Subscribes the idle tasks of one or more cores
  260. *
  261. * @param core_mask Bit mask of cores to subscribe
  262. */
  263. static void subscribe_idle(uint32_t core_mask)
  264. {
  265. int core_num = 0;
  266. while (core_mask != 0) {
  267. if (core_mask & 0x1) {
  268. #if CONFIG_FREERTOS_SMP
  269. snprintf(core_user_names[core_num], CORE_USER_NAME_LEN, "CPU %d", (uint8_t)core_num);
  270. ESP_ERROR_CHECK(esp_task_wdt_add_user((const char *)core_user_names[core_num], &core_user_handles[core_num]));
  271. ESP_ERROR_CHECK(esp_register_freertos_idle_hook_for_cpu(idle_hook_cb, core_num));
  272. #else
  273. TaskHandle_t idle_task_handle = xTaskGetIdleTaskHandleForCPU(core_num);
  274. assert(idle_task_handle);
  275. ESP_ERROR_CHECK(esp_task_wdt_add(idle_task_handle));
  276. ESP_ERROR_CHECK(esp_register_freertos_idle_hook_for_cpu(idle_hook_cb, core_num));
  277. #endif
  278. }
  279. core_mask >>= 1;
  280. core_num++;
  281. }
  282. }
  283. /**
  284. * @brief TWDT timeout ISR function
  285. *
  286. * The ISR checks which entries have not been reset, prints some debugging information, and triggers a panic if
  287. * configured to do so.
  288. *
  289. * @param arg ISR argument
  290. */
  291. static void task_wdt_isr(void *arg)
  292. {
  293. portENTER_CRITICAL_ISR(&spinlock);
  294. // Reset hardware timer so that 2nd stage timeout is not reached (will trigger system reset)
  295. wdt_hal_write_protect_disable(&p_twdt_obj->hal);
  296. wdt_hal_handle_intr(&p_twdt_obj->hal); // Feeds WDT and clears acknowledges interrupt
  297. wdt_hal_write_protect_enable(&p_twdt_obj->hal);
  298. // If there are no entries, there's nothing to do.
  299. if (SLIST_EMPTY(&p_twdt_obj->entries_slist)) {
  300. portEXIT_CRITICAL_ISR(&spinlock);
  301. return;
  302. }
  303. // Find what entries triggered the TWDT timeout (i.e., which entries have not been reset)
  304. /*
  305. Note: We are currently in a critical section, thus under normal circumstances, logging should not be allowed.
  306. However, TWDT timeouts count as fatal errors, thus reporting the fatal error is considered more important than
  307. minimizing interrupt latency. Thus we allow logging in critical sections in this narrow case.
  308. */
  309. ESP_EARLY_LOGE(TAG, "Task watchdog got triggered. The following tasks/users did not reset the watchdog in time:");
  310. twdt_entry_t *entry;
  311. SLIST_FOREACH(entry, &p_twdt_obj->entries_slist, slist_entry) {
  312. if (!entry->has_reset) {
  313. if (entry->task_handle) {
  314. #if CONFIG_FREERTOS_SMP
  315. #if configNUM_CORES > 1
  316. // Log the task's name and its affinity
  317. ESP_EARLY_LOGE(TAG, " - %s (0x%x)", pcTaskGetName(entry->task_handle), vTaskCoreAffinityGet(entry->task_handle));
  318. #else // configNUM_CORES > 1
  319. // Log the task's name
  320. ESP_EARLY_LOGE(TAG, " - %s", pcTaskGetName(entry->task_handle));
  321. #endif // configNUM_CORES > 1
  322. #else // CONFIG_FREERTOS_SMP
  323. BaseType_t task_affinity = xTaskGetAffinity(entry->task_handle);
  324. const char *cpu;
  325. if (task_affinity == 0) {
  326. cpu = DRAM_STR("CPU 0");
  327. } else if (task_affinity == 1) {
  328. cpu = DRAM_STR("CPU 1");
  329. } else {
  330. cpu = DRAM_STR("CPU 0/1");
  331. }
  332. ESP_EARLY_LOGE(TAG, " - %s (%s)", pcTaskGetName(entry->task_handle), cpu);
  333. #endif // CONFIG_FREERTOS_SMP
  334. } else {
  335. ESP_EARLY_LOGE(TAG, " - %s", entry->user_name);
  336. }
  337. }
  338. }
  339. ESP_EARLY_LOGE(TAG, "%s", DRAM_STR("Tasks currently running:"));
  340. for (int x = 0; x < portNUM_PROCESSORS; x++) {
  341. ESP_EARLY_LOGE(TAG, "CPU %d: %s", x, pcTaskGetName(xTaskGetCurrentTaskHandleForCPU(x)));
  342. }
  343. portEXIT_CRITICAL_ISR(&spinlock);
  344. // Run user ISR handler
  345. esp_task_wdt_isr_user_handler();
  346. // Trigger configured timeout behavior (e.g., panic or print backtrace)
  347. if (p_twdt_obj->panic) {
  348. ESP_EARLY_LOGE(TAG, "Aborting.");
  349. esp_reset_reason_set_hint(ESP_RST_TASK_WDT);
  350. abort();
  351. } else { // Print
  352. #if !CONFIG_IDF_TARGET_ESP32C3 && !CONFIG_IDF_TARGET_ESP32H2 && !CONFIG_IDF_TARGET_ESP32C2 // TODO: ESP32-C3 IDF-2986
  353. int current_core = xPortGetCoreID();
  354. // Print backtrace of current core
  355. ESP_EARLY_LOGE(TAG, "Print CPU %d (current core) backtrace", current_core);
  356. esp_backtrace_print(100);
  357. #if !CONFIG_FREERTOS_UNICORE
  358. // Print backtrace of other core
  359. ESP_EARLY_LOGE(TAG, "Print CPU %d backtrace", !current_core);
  360. esp_crosscore_int_send_print_backtrace(!current_core);
  361. #endif
  362. #endif
  363. }
  364. }
  365. // ----------------------------------------------------- Public --------------------------------------------------------
  366. esp_err_t esp_task_wdt_init(const esp_task_wdt_config_t *config)
  367. {
  368. ESP_RETURN_ON_FALSE((config != NULL && config->idle_core_mask < (1 << portNUM_PROCESSORS)), ESP_ERR_INVALID_ARG, TAG, "Invalid arguments");
  369. ESP_RETURN_ON_FALSE(p_twdt_obj == NULL, ESP_ERR_INVALID_STATE, TAG, "TWDT already initialized");
  370. esp_err_t ret;
  371. twdt_obj_t *obj = NULL;
  372. if (p_twdt_obj == NULL) {
  373. // Allocate and initialize TWDT driver object
  374. obj = calloc(1, sizeof(twdt_obj_t));
  375. ESP_GOTO_ON_FALSE((obj != NULL), ESP_ERR_NO_MEM, err, TAG, "insufficient memory");
  376. SLIST_INIT(&obj->entries_slist);
  377. obj->panic = config->trigger_panic;
  378. ESP_ERROR_CHECK(esp_intr_alloc(ETS_TG0_WDT_LEVEL_INTR_SOURCE, 0, task_wdt_isr, NULL, &obj->intr_handle));
  379. portENTER_CRITICAL(&spinlock);
  380. // Configure hardware timer
  381. periph_module_enable(PERIPH_TIMG0_MODULE);
  382. wdt_hal_init(&obj->hal, TWDT_INSTANCE, TWDT_PRESCALER, true);
  383. // Assign the driver object
  384. p_twdt_obj = obj;
  385. portEXIT_CRITICAL(&spinlock);
  386. }
  387. portENTER_CRITICAL(&spinlock);
  388. wdt_hal_write_protect_disable(&p_twdt_obj->hal);
  389. // Configure 1st stage timeout and behavior
  390. wdt_hal_config_stage(&p_twdt_obj->hal, WDT_STAGE0, config->timeout_ms * (1000 / TWDT_TICKS_PER_US), WDT_STAGE_ACTION_INT);
  391. // Configure 2nd stage timeout and behavior
  392. wdt_hal_config_stage(&p_twdt_obj->hal, WDT_STAGE1, config->timeout_ms * (2 * 1000 / TWDT_TICKS_PER_US), WDT_STAGE_ACTION_RESET_SYSTEM);
  393. // Enable the WDT
  394. wdt_hal_enable(&p_twdt_obj->hal);
  395. wdt_hal_write_protect_enable(&p_twdt_obj->hal);
  396. // Update which core's idle tasks are subscribed
  397. uint32_t old_core_mask = p_twdt_obj->idle_core_mask;
  398. p_twdt_obj->idle_core_mask = config->idle_core_mask;
  399. portEXIT_CRITICAL(&spinlock);
  400. if (old_core_mask) {
  401. // Unsubscribe all previously watched core idle tasks
  402. unsubscribe_idle(old_core_mask);
  403. }
  404. if (config->idle_core_mask) {
  405. // Subscribe the new cores idle tasks
  406. subscribe_idle(config->idle_core_mask);
  407. }
  408. ret = ESP_OK;
  409. err:
  410. return ret;
  411. }
  412. esp_err_t esp_task_wdt_deinit(void)
  413. {
  414. ESP_RETURN_ON_FALSE(p_twdt_obj != NULL, ESP_ERR_INVALID_STATE, TAG, "TWDT was never initialized");
  415. esp_err_t ret;
  416. // Unsubscribe all previously watched core idle tasks
  417. unsubscribe_idle(p_twdt_obj->idle_core_mask);
  418. portENTER_CRITICAL(&spinlock);
  419. // Check TWDT state
  420. ESP_GOTO_ON_FALSE_ISR(SLIST_EMPTY(&p_twdt_obj->entries_slist), ESP_ERR_INVALID_STATE, err, TAG, "Tasks/users still subscribed");
  421. // Disable hardware timer and the interrupt
  422. wdt_hal_write_protect_disable(&p_twdt_obj->hal);
  423. wdt_hal_disable(&p_twdt_obj->hal);
  424. wdt_hal_write_protect_enable(&p_twdt_obj->hal);
  425. wdt_hal_deinit(&p_twdt_obj->hal);
  426. esp_intr_disable(p_twdt_obj->intr_handle);
  427. // Unassign driver object
  428. twdt_obj_t *obj = p_twdt_obj;
  429. p_twdt_obj = NULL;
  430. portEXIT_CRITICAL(&spinlock);
  431. // Free driver resources
  432. ESP_ERROR_CHECK(esp_intr_free(obj->intr_handle)); // Deregister interrupt
  433. free(obj); // Free p_twdt_obj
  434. return ESP_OK;
  435. err:
  436. portEXIT_CRITICAL(&spinlock);
  437. subscribe_idle(p_twdt_obj->idle_core_mask); // Resubscribe idle tasks
  438. return ret;
  439. }
  440. esp_err_t esp_task_wdt_add(TaskHandle_t task_handle)
  441. {
  442. ESP_RETURN_ON_FALSE(p_twdt_obj != NULL, ESP_ERR_INVALID_STATE, TAG, "TWDT was never initialized");
  443. esp_err_t ret;
  444. if (task_handle == NULL) { // Get handle of current task if none is provided
  445. task_handle = xTaskGetCurrentTaskHandle();
  446. }
  447. twdt_entry_t *entry;
  448. ret = add_entry(true, (void *)task_handle, &entry);
  449. (void) entry; // Returned entry pointer not used
  450. return ret;
  451. }
  452. esp_err_t esp_task_wdt_add_user(const char *user_name, esp_task_wdt_user_handle_t *user_handle_ret)
  453. {
  454. ESP_RETURN_ON_FALSE((user_name != NULL && user_handle_ret != NULL), ESP_ERR_INVALID_ARG, TAG, "Invalid arguments");
  455. ESP_RETURN_ON_FALSE(p_twdt_obj != NULL, ESP_ERR_INVALID_STATE, TAG, "TWDT was never initialized");
  456. esp_err_t ret;
  457. twdt_entry_t *entry;
  458. ret = add_entry(false, (void *)user_name, &entry);
  459. if (ret == ESP_OK) {
  460. *user_handle_ret = (esp_task_wdt_user_handle_t)entry;
  461. }
  462. return ret;
  463. }
  464. esp_err_t esp_task_wdt_reset(void)
  465. {
  466. ESP_RETURN_ON_FALSE(p_twdt_obj != NULL, ESP_ERR_INVALID_STATE, TAG, "TWDT was never initialized");
  467. esp_err_t ret;
  468. TaskHandle_t handle = xTaskGetCurrentTaskHandle();
  469. portENTER_CRITICAL(&spinlock);
  470. // Find entry from task handle
  471. bool all_reset;
  472. twdt_entry_t *entry;
  473. entry = find_entry_from_task_handle_and_check_all_reset(handle, &all_reset);
  474. ESP_GOTO_ON_FALSE_ISR((entry != NULL), ESP_ERR_NOT_FOUND, err, TAG, "task not found");
  475. // Mark entry as reset and issue timer reset if all entries have been reset
  476. entry->has_reset = true; // Reset the task if it's on the task list
  477. if (all_reset) { // Reset if all other tasks in list have reset in
  478. reset_hw_timer();
  479. }
  480. ret = ESP_OK;
  481. err:
  482. portEXIT_CRITICAL(&spinlock);
  483. return ret;
  484. }
  485. esp_err_t esp_task_wdt_reset_user(esp_task_wdt_user_handle_t user_handle)
  486. {
  487. ESP_RETURN_ON_FALSE(user_handle != NULL, ESP_ERR_INVALID_ARG, TAG, "Invalid arguments");
  488. ESP_RETURN_ON_FALSE(p_twdt_obj != NULL, ESP_ERR_INVALID_STATE, TAG, "TWDT was never initialized");
  489. esp_err_t ret;
  490. portENTER_CRITICAL(&spinlock);
  491. // Check if entry exists
  492. bool all_reset;
  493. twdt_entry_t *entry = (twdt_entry_t *)user_handle;
  494. bool entry_found = find_entry_and_check_all_reset(entry, &all_reset);
  495. ESP_GOTO_ON_FALSE_ISR(entry_found, ESP_ERR_NOT_FOUND, err, TAG, "user handle not found");
  496. // Mark entry as reset and issue timer reset if all entries have been reset
  497. entry->has_reset = true; // Reset the task if it's on the task list
  498. if (all_reset) { // Reset if all other tasks in list have reset in
  499. reset_hw_timer();
  500. }
  501. ret = ESP_OK;
  502. err:
  503. portEXIT_CRITICAL(&spinlock);
  504. return ret;
  505. }
  506. esp_err_t esp_task_wdt_delete(TaskHandle_t task_handle)
  507. {
  508. ESP_RETURN_ON_FALSE(p_twdt_obj != NULL, ESP_ERR_INVALID_STATE, TAG, "TWDT was never initialized");
  509. esp_err_t ret;
  510. if (task_handle == NULL) {
  511. task_handle = xTaskGetCurrentTaskHandle();
  512. }
  513. ret = delete_entry(true, (void *)task_handle);
  514. return ret;
  515. }
  516. esp_err_t esp_task_wdt_delete_user(esp_task_wdt_user_handle_t user_handle)
  517. {
  518. ESP_RETURN_ON_FALSE(user_handle != NULL, ESP_ERR_INVALID_ARG, TAG, "Invalid arguments");
  519. ESP_RETURN_ON_FALSE(p_twdt_obj != NULL, ESP_ERR_INVALID_STATE, TAG, "TWDT was never initialized");
  520. return delete_entry(false, (void *)user_handle);
  521. }
  522. esp_err_t esp_task_wdt_status(TaskHandle_t task_handle)
  523. {
  524. ESP_RETURN_ON_FALSE(p_twdt_obj != NULL, ESP_ERR_INVALID_STATE, TAG, "TWDT was never initialized");
  525. esp_err_t ret;
  526. if (task_handle == NULL) {
  527. task_handle = xTaskGetCurrentTaskHandle();
  528. }
  529. portENTER_CRITICAL(&spinlock);
  530. // Find entry for task
  531. bool all_reset;
  532. twdt_entry_t *entry;
  533. entry = find_entry_from_task_handle_and_check_all_reset(task_handle, &all_reset);
  534. (void) all_reset; // Unused
  535. ret = (entry != NULL) ? ESP_OK : ESP_ERR_NOT_FOUND;
  536. portEXIT_CRITICAL(&spinlock);
  537. return ret;
  538. }