]> granicus.if.org Git - esp-idf/blobdiff - components/esp32/task_wdt.c
Merge branch 'feature/esp-wrover-kit-v4_1' into 'master'
[esp-idf] / components / esp32 / task_wdt.c
index bb45b6a4afbc2e18dbd5b45941c2367072a9f3d3..fb10aac05e66f0dae4e3fe38d3e1db66721a8529 100644 (file)
@@ -3,7 +3,7 @@
 // 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
@@ -12,8 +12,6 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-
-
 #include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include "esp_err.h"
 #include "esp_intr.h"
 #include "esp_intr_alloc.h"
-#include "esp_ipc.h"
 #include "esp_attr.h"
 #include "esp_freertos_hooks.h"
 #include "soc/timer_group_struct.h"
 #include "soc/timer_group_reg.h"
 #include "esp_log.h"
 #include "driver/timer.h"
-
+#include "driver/periph_ctrl.h"
 #include "esp_task_wdt.h"
+#include "esp_system_internal.h"
 
-//Assertion macro that exits from critical section and returns 'ret'
-#define ASSERT_EXIT_CRIT_RET_ERR(cond, ret)  ({                             \
+//Assertion macro where, if 'cond' is false, will exit the critical section and return 'ret'
+#define ASSERT_EXIT_CRIT_RETURN(cond, ret)  ({                              \
             if(!(cond)){                                                    \
-                portEXIT_CRITICAL(&taskwdt_spinlock);                       \
+                portEXIT_CRITICAL(&twdt_spinlock);                          \
                 return ret;                                                 \
             }                                                               \
 })
 
-typedef struct wdt_task_t wdt_task_t;
-struct wdt_task_t {
+//Empty define used in ASSERT_EXIT_CRIT_RETURN macro when returning in void
+#define VOID_RETURN
+
+//Structure used for each subscribed task
+typedef struct twdt_task_t twdt_task_t;
+struct twdt_task_t {
     TaskHandle_t task_handle;
-    bool fed;
-    wdt_task_t *next;
+    bool has_reset;
+    twdt_task_t *next;
 };
 
-typedef struct task_wdt_config_t task_wdt_config_t;
-struct task_wdt_config_t {
-    wdt_task_t *list;
-    uint32_t timeout;
-    bool panic;
-    bool idle_enable[portNUM_PROCESSORS];
-    TaskHandle_t idle_handles[portNUM_PROCESSORS];
+//Structure used to hold run time configuration of the TWDT
+typedef struct twdt_config_t twdt_config_t;
+struct twdt_config_t {
+    twdt_task_t *list;      //Linked list of subscribed tasks
+    uint32_t timeout;       //Timeout period of TWDT
+    bool panic;             //Flag to trigger panic when TWDT times out
     intr_handle_t intr_handle;
 };
 
-static task_wdt_config_t *wdt_config = NULL;
-static portMUX_TYPE taskwdt_spinlock = portMUX_INITIALIZER_UNLOCKED;
+static twdt_config_t *twdt_config = NULL;
+static portMUX_TYPE twdt_spinlock = portMUX_INITIALIZER_UNLOCKED;
 
 /*
- * Internal function that checks the list for target task. Returns the list item
- * if found and returns null if not found. Also checks if the all the other
- * tasks have checked in. Called within critical.
+ * Idle hook callback for Idle Tasks to reset the TWDT. This callback will only
+ * be registered to the Idle Hook of a particular core when the corresponding
+ * Idle Task subscribes to the TWDT.
  */
-static wdt_task_t *check_list(TaskHandle_t handle, bool *checked)
+static bool idle_hook_cb(void)
 {
-    wdt_task_t *target = NULL;
-    *checked = true;
-    for(wdt_task_t *task = wdt_config->list; task != NULL; task = task->next){
+    esp_task_wdt_reset();
+    return true;
+}
+
+/*
+ * Internal function that looks for the target task in the TWDT task list.
+ * Returns the list item if found and returns null if not found. Also checks if
+ * all the other tasks have reset. Should be called within critical.
+ */
+static twdt_task_t *find_task_in_twdt_list(TaskHandle_t handle, bool *all_reset)
+{
+    twdt_task_t *target = NULL;
+    *all_reset = true;
+    for(twdt_task_t *task = twdt_config->list; task != NULL; task = task->next){
         if(task->task_handle == handle){
             target = task;   //Get pointer to target task list member
         }else{
-            if(task->fed == false){     //If a task has yet to check in
-                *checked = false;
+            if(task->has_reset == false){     //If a task has yet to reset
+                *all_reset = false;
             }
         }
     }
@@ -89,50 +101,51 @@ static wdt_task_t *check_list(TaskHandle_t handle, bool *checked)
 }
 
 /*
- * Resets the wdt and fed flags of each task on the list. Called within critical
+ * Resets the hardware timer and has_reset flags of each task on the list.
+ * Called within critical
  */
-static void feed_wdt()
+static void reset_hw_timer()
 {
-    //All tasks have checked in; time to feed the hw watchdog.
+    //All tasks have reset; time to reset the hardware timer.
     TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
     TIMERG0.wdt_feed=1;
     TIMERG0.wdt_wprotect=0;
-    //Reset fed flags on all tasks in list
-    for (wdt_task_t *task = wdt_config->list; task != NULL; task = task->next){
-        task->fed=false;
+    //Clear all has_reset flags in list
+    for (twdt_task_t *task = twdt_config->list; task != NULL; task = task->next){
+        task->has_reset=false;
     }
 }
 
 /*
- * ISR for when wdt expires. Checks for which tasks have not fed. Trigger panic
- * if configured to do so in initialization
+ * ISR for when TWDT times out. Checks for which tasks have not reset. Also
+ * triggers panic if configured to do so
  */
 static void task_wdt_isr(void *arg)
 {
-    portENTER_CRITICAL(&taskwdt_spinlock);
-    wdt_task_t *wdttask;
+    portENTER_CRITICAL(&twdt_spinlock);
+    twdt_task_t *twdttask;
     const char *cpu;
-    //Feed the watchdog so we don't reset
+    //Reset hardware timer so that 2nd stage timeout is not reached (will trigger system reset)
     TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;
     TIMERG0.wdt_feed=1;
     TIMERG0.wdt_wprotect=0;
-    //Ack interrupt
+    //Acknowledge interrupt
     TIMERG0.int_clr_timers.wdt=1;
     //We are taking a spinlock while doing I/O (ets_printf) here. Normally, that is a pretty
     //bad thing, possibly (temporarily) hanging up the 2nd core and stopping FreeRTOS. In this case,
     //something bad already happened and reporting this is considered more important
     //than the badness caused by a spinlock here.
-    if (!wdt_config->list) {       //No tasks on list
-        portEXIT_CRITICAL(&taskwdt_spinlock);
-        return;
-    }
-    //Watchdog got triggered because at least one task did not report in.
-    ets_printf("Task watchdog got triggered. The following tasks did not feed the watchdog in time:\n");
-    for (wdttask=wdt_config->list; wdttask!=NULL; wdttask=wdttask->next) {
-        if (!wdttask->fed) {
-            cpu=xTaskGetAffinity(wdttask->task_handle)==0?DRAM_STR("CPU 0"):DRAM_STR("CPU 1");
-            if (xTaskGetAffinity(wdttask->task_handle)==tskNO_AFFINITY) cpu=DRAM_STR("CPU 0/1");
-            ets_printf(" - %s (%s)\n", pcTaskGetTaskName(wdttask->task_handle), cpu);
+
+    //Return immediately if no tasks have been added to task list
+    ASSERT_EXIT_CRIT_RETURN((twdt_config->list != NULL), VOID_RETURN);
+
+    //Watchdog got triggered because at least one task did not reset in time.
+    ets_printf("Task watchdog got triggered. The following tasks did not reset the watchdog in time:\n");
+    for (twdttask=twdt_config->list; twdttask!=NULL; twdttask=twdttask->next) {
+        if (!twdttask->has_reset) {
+            cpu=xTaskGetAffinity(twdttask->task_handle)==0?DRAM_STR("CPU 0"):DRAM_STR("CPU 1");
+            if (xTaskGetAffinity(twdttask->task_handle)==tskNO_AFFINITY) cpu=DRAM_STR("CPU 0/1");
+            ets_printf(" - %s (%s)\n", pcTaskGetTaskName(twdttask->task_handle), cpu);
         }
     }
     ets_printf(DRAM_STR("Tasks currently running:\n"));
@@ -140,57 +153,39 @@ static void task_wdt_isr(void *arg)
         ets_printf("CPU %d: %s\n", x, pcTaskGetTaskName(xTaskGetCurrentTaskHandleForCPU(x)));
     }
 
-    if (wdt_config->panic){
+    if (twdt_config->panic){     //Trigger Panic if configured to do so
         ets_printf("Aborting.\n");
-        portEXIT_CRITICAL(&taskwdt_spinlock);
+        portEXIT_CRITICAL(&twdt_spinlock);
+        esp_reset_reason_set_hint(ESP_RST_TASK_WDT);
         abort();
     }
 
-    portEXIT_CRITICAL(&taskwdt_spinlock);
+    portEXIT_CRITICAL(&twdt_spinlock);
 }
 
 /*
- * Idle hook for Idle Task to feed the wdt. Will only call feed if the idle task
- * of that core has been added to wdt task list.
- */
-static bool idle_hook(void)
-{
-    uint32_t core_id = xPortGetCoreID();
-    if (wdt_config->idle_enable[core_id]){
-        esp_task_wdt_feed();
-    }
-    return true;
-}
-
-/*
- * Initializes the task watchdog timer by allocating memory for the config data
+ * Initializes the TWDT by allocating memory for the config data
  * structure, obtaining the idle task handles/registering idle hooks, and
  * setting the hardware timer registers. If reconfiguring, it will just modify
  * wdt_config and reset the hardware timer.
  */
 esp_err_t esp_task_wdt_init(uint32_t timeout, bool panic)
 {
-    portENTER_CRITICAL(&taskwdt_spinlock);
-    if(wdt_config == NULL){        //wdt not initialized yet
-       //Allocate memory for wdt_config
-        wdt_config = calloc(1, sizeof(task_wdt_config_t));
-        ASSERT_EXIT_CRIT_RET_ERR((wdt_config != NULL), ESP_ERR_NO_MEM);
-
-        wdt_config->list = NULL;
-        wdt_config->timeout = timeout;
-        wdt_config->panic = panic;
-
-        //Get idle task handles
-        for(int i = 0; i <  portNUM_PROCESSORS; i++){
-            wdt_config->idle_handles[i] = xTaskGetIdleTaskHandleForCPU( i );
-            wdt_config->idle_enable[i] = false;
-        }
+    portENTER_CRITICAL(&twdt_spinlock);
+    if(twdt_config == NULL){        //TWDT not initialized yet
+        //Allocate memory for wdt_config
+        twdt_config = calloc(1, sizeof(twdt_config_t));
+        ASSERT_EXIT_CRIT_RETURN((twdt_config != NULL), ESP_ERR_NO_MEM);
+
+        twdt_config->list = NULL;
+        twdt_config->timeout = timeout;
+        twdt_config->panic = panic;
 
-        //Register Idle Hook and Interrupts
-        esp_register_freertos_idle_hook(idle_hook);
-        ESP_ERROR_CHECK(esp_intr_alloc(ETS_TG0_WDT_LEVEL_INTR_SOURCE, 0, task_wdt_isr, NULL, &wdt_config->intr_handle))
+        //Register Interrupt and ISR
+        ESP_ERROR_CHECK(esp_intr_alloc(ETS_TG0_WDT_LEVEL_INTR_SOURCE, 0, task_wdt_isr, NULL, &twdt_config->intr_handle))
 
         //Configure hardware timer
+        periph_module_enable(PERIPH_TIMG0_MODULE);
         TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;               //Disable write protection
         TIMERG0.wdt_config0.sys_reset_length=7;                 //3.2uS
         TIMERG0.wdt_config0.cpu_reset_length=7;                 //3.2uS
@@ -198,185 +193,225 @@ esp_err_t esp_task_wdt_init(uint32_t timeout, bool panic)
         TIMERG0.wdt_config0.stg0=TIMG_WDT_STG_SEL_INT;          //1st stage timeout: interrupt
         TIMERG0.wdt_config0.stg1=TIMG_WDT_STG_SEL_RESET_SYSTEM; //2nd stage timeout: reset system
         TIMERG0.wdt_config1.clk_prescale=80*500;                //Prescaler: wdt counts in ticks of 0.5mS
-        TIMERG0.wdt_config2=wdt_config->timeout*2;      //Set timeout before interrupt
-        TIMERG0.wdt_config3=wdt_config->timeout*4;      //Set timeout before reset
+        TIMERG0.wdt_config2=twdt_config->timeout*2000;      //Set timeout before interrupt
+        TIMERG0.wdt_config3=twdt_config->timeout*4000;      //Set timeout before reset
         TIMERG0.wdt_config0.en=1;
         TIMERG0.wdt_feed=1;
         TIMERG0.wdt_wprotect=0;                         //Enable write protection
 
-    }else{      //wdt_config previously initialized
+    }else{      //twdt_config previously initialized
         //Reconfigure task wdt
-        wdt_config->panic = panic;
-        wdt_config->timeout = timeout;
+        twdt_config->panic = panic;
+        twdt_config->timeout = timeout;
 
         //Reconfigure hardware timer
         TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;   //Disable write protection
         TIMERG0.wdt_config0.en=0;                   //Disable timer
-        TIMERG0.wdt_config2=wdt_config->timeout*2;           //Set timeout before interrupt
-        TIMERG0.wdt_config3=wdt_config->timeout*4;           //Set timeout before reset
+        TIMERG0.wdt_config2=twdt_config->timeout*2000;           //Set timeout before interrupt
+        TIMERG0.wdt_config3=twdt_config->timeout*4000;           //Set timeout before reset
         TIMERG0.wdt_config0.en=1;                   //Renable timer
         TIMERG0.wdt_feed=1;                         //Reset timer
         TIMERG0.wdt_wprotect=0;                     //Enable write protection
     }
-    portEXIT_CRITICAL(&taskwdt_spinlock);
+    portEXIT_CRITICAL(&twdt_spinlock);
     return ESP_OK;
 }
 
 esp_err_t esp_task_wdt_deinit()
 {
-    portENTER_CRITICAL(&taskwdt_spinlock);
-    //wdt must already be initialized
-    ASSERT_EXIT_CRIT_RET_ERR((wdt_config != NULL), ESP_ERR_INVALID_STATE);
+    portENTER_CRITICAL(&twdt_spinlock);
+    //TWDT must already be initialized
+    ASSERT_EXIT_CRIT_RETURN((twdt_config != NULL), ESP_ERR_NOT_FOUND);
     //Task list must be empty
-    ASSERT_EXIT_CRIT_RET_ERR((wdt_config->list == NULL), ESP_ERR_INVALID_STATE);
+    ASSERT_EXIT_CRIT_RETURN((twdt_config->list == NULL), ESP_ERR_INVALID_STATE);
 
     //Disable hardware timer
     TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE;   //Disable write protection
     TIMERG0.wdt_config0.en=0;                   //Disable timer
     TIMERG0.wdt_wprotect=0;                     //Enable write protection
 
-    esp_deregister_freertos_idle_hook(idle_hook);   //deregister idle hook
-    ESP_ERROR_CHECK(esp_intr_free(wdt_config->intr_handle))  //Unregister interrupt
-    free(wdt_config);                      //Free wdt_config
-    wdt_config = NULL;
-    portEXIT_CRITICAL(&taskwdt_spinlock);
+    ESP_ERROR_CHECK(esp_intr_free(twdt_config->intr_handle))  //Unregister interrupt
+    free(twdt_config);                      //Free twdt_config
+    twdt_config = NULL;
+    portEXIT_CRITICAL(&twdt_spinlock);
     return ESP_OK;
 }
 
 esp_err_t esp_task_wdt_add(TaskHandle_t handle)
 {
-    portENTER_CRITICAL(&taskwdt_spinlock);      //Nested critical in Legacy (called from feed)
-#ifdef CONFIG_TASK_WDT_LEGACY_BEHAVIOR
-    wdt_task_t *target_task;
-#else
-    //Return error if wdt has not been initialized
-    ASSERT_EXIT_CRIT_RET_ERR((wdt_config != NULL), ESP_ERR_INVALID_STATE);
-
-    wdt_task_t *target_task;
-    bool all_fed;
+    portENTER_CRITICAL(&twdt_spinlock);
+    //TWDT must already be initialized
+    ASSERT_EXIT_CRIT_RETURN((twdt_config != NULL), ESP_ERR_INVALID_STATE);
+
+    twdt_task_t *target_task;
+    bool all_reset;
     if (handle == NULL){    //Get handle of current task if none is provided
         handle = xTaskGetCurrentTaskHandle();
     }
-
-    //Check if tasks exists in task list, and if all other tasks have checked in
-    target_task = check_list(handle, &all_fed);
-    //Return error if task has already been added
-    ASSERT_EXIT_CRIT_RET_ERR((target_task == NULL), ESP_ERR_INVALID_ARG);
-#endif      //CONFIG_TASK_WDT_LEGACY_BEHAVIOR
-    //Add task to wdt list
-    target_task = calloc(1,sizeof(wdt_task_t));
-    //Return error if calloc failed
-    ASSERT_EXIT_CRIT_RET_ERR((target_task != NULL), ESP_ERR_NO_MEM);
-
+    //Check if tasks exists in task list, and if all other tasks have reset
+    target_task = find_task_in_twdt_list(handle, &all_reset);
+    //task cannot be already subscribed
+    ASSERT_EXIT_CRIT_RETURN((target_task == NULL), ESP_ERR_INVALID_ARG);
+
+    //Add target task to TWDT task list
+    target_task = calloc(1,sizeof(twdt_task_t));
+    ASSERT_EXIT_CRIT_RETURN((target_task != NULL), ESP_ERR_NO_MEM);
     target_task->task_handle = handle;
-    target_task->fed = true;
+    target_task->has_reset = true;
     target_task->next = NULL;
-    if (wdt_config->list == NULL) {    //Adding to empty list
-        wdt_config->list = target_task;
+    if (twdt_config->list == NULL) {    //Adding to empty list
+        twdt_config->list = target_task;
     } else {    //Adding to tail of list
-        wdt_task_t *task;
-        for (task = wdt_config->list; task->next != NULL; task = task->next){
-            ;   //point task to current tail of wdt task list
+        twdt_task_t *task;
+        for (task = twdt_config->list; task->next != NULL; task = task->next){
+            ;   //point task to current tail of TWDT task list
         }
         task->next = target_task;
     }
 
-    //If idle task, set idle_enable flag
+    //If idle task, register the idle hook callback to appropriate core
     for(int i = 0; i < portNUM_PROCESSORS; i++){
-        if(handle == wdt_config->idle_handles[i]){
-            wdt_config->idle_enable[i] = true;
+        if(handle == xTaskGetIdleTaskHandleForCPU(i)){
+            ESP_ERROR_CHECK(esp_register_freertos_idle_hook_for_cpu(idle_hook_cb, i))
             break;
         }
     }
 
-#ifndef CONFIG_TASK_WDT_LEGACY_BEHAVIOR
-    if(all_fed){     //Reset if all other tasks in list have checked in
-        feed_wdt();
+    if(all_reset){     //Reset hardware timer if all other tasks in list have reset in
+        reset_hw_timer();
     }
-#endif
-    portEXIT_CRITICAL(&taskwdt_spinlock);              //Nested critical if Legacy
+
+    portEXIT_CRITICAL(&twdt_spinlock);       //Nested critical if Legacy
     return ESP_OK;
 }
 
-esp_err_t esp_task_wdt_feed()
+esp_err_t esp_task_wdt_reset()
 {
-    portENTER_CRITICAL(&taskwdt_spinlock);
-    //Return error if wdt has not been initialized
-    ASSERT_EXIT_CRIT_RET_ERR((wdt_config != NULL), ESP_ERR_INVALID_STATE);
+    portENTER_CRITICAL(&twdt_spinlock);
+    //TWDT must already be initialized
+    ASSERT_EXIT_CRIT_RETURN((twdt_config != NULL), ESP_ERR_INVALID_STATE);
 
     TaskHandle_t handle = xTaskGetCurrentTaskHandle();
-    wdt_task_t *target_task;
-    bool all_fed;
-
-    //Check if tasks exists in task list, and if all other tasks have checked in
-    target_task = check_list(handle, &all_fed);
-
-    if(target_task == NULL){
-#ifdef CONFIG_TASK_WDT_LEGACY_BEHAVIOR
-        //Add task to wdt task list if it doesn't exist, return error if failed to add
-        ASSERT_EXIT_CRIT_RET_ERR((esp_task_wdt_add(handle) == ESP_OK), ESP_ERR_NO_MEM);
-#else
-        portEXIT_CRITICAL(&taskwdt_spinlock);
-        return ESP_ERR_INVALID_STATE;                  //Return error if task does not exist
-#endif      //CONFIG_TASK_WDT_LEGACY_BEHAVIOR
-    }else{
-        target_task->fed = true;   //Feed the task
-    }
+    twdt_task_t *target_task;
+    bool all_reset;
 
-    if(all_fed){     //Reset if all other tasks in list have checked in
-        feed_wdt();
+    //Check if task exists in task list, and if all other tasks have reset
+    target_task = find_task_in_twdt_list(handle, &all_reset);
+    //Return error if trying to reset task that is not on the task list
+    ASSERT_EXIT_CRIT_RETURN((target_task != NULL), ESP_ERR_NOT_FOUND);
+
+    target_task->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
+        reset_hw_timer();
     }
 
-    portEXIT_CRITICAL(&taskwdt_spinlock);
+    portEXIT_CRITICAL(&twdt_spinlock);
     return ESP_OK;
 }
 
-#ifdef CONFIG_TASK_WDT_LEGACY_BEHAVIOR
-esp_err_t esp_task_wdt_delete()
-{
-    TaskHandle_t handle = xTaskGetCurrentTaskHandle();
-#else
 esp_err_t esp_task_wdt_delete(TaskHandle_t handle)
 {
     if(handle == NULL){
         handle = xTaskGetCurrentTaskHandle();
     }
-#endif      //CONFIG_TASK_WDT_LEGACY_BEHAVIOR
-    portENTER_CRITICAL(&taskwdt_spinlock);
-    //Return error if wdt has not been initialized
-    ASSERT_EXIT_CRIT_RET_ERR((wdt_config != NULL), ESP_ERR_INVALID_STATE);
-    wdt_task_t *target_task;
-    bool all_fed;
-
-    target_task = check_list(handle, &all_fed);
+    portENTER_CRITICAL(&twdt_spinlock);
+    //Return error if twdt has not been initialized
+    ASSERT_EXIT_CRIT_RETURN((twdt_config != NULL), ESP_ERR_NOT_FOUND);
 
+    twdt_task_t *target_task;
+    bool all_reset;
+    target_task = find_task_in_twdt_list(handle, &all_reset);
     //Task doesn't exist on list. Return error
-    ASSERT_EXIT_CRIT_RET_ERR((target_task != NULL), ESP_ERR_INVALID_ARG);
+    ASSERT_EXIT_CRIT_RETURN((target_task != NULL), ESP_ERR_INVALID_ARG);
 
-    if(target_task == wdt_config->list){     //target_task is head of list. Delete
-        wdt_config->list = target_task->next;
+    if(target_task == twdt_config->list){     //target_task is head of list. Delete
+        twdt_config->list = target_task->next;
         free(target_task);
     }else{                                      //target_task not head of list. Delete
-        wdt_task_t *prev;
-        for (prev = wdt_config->list; prev->next != target_task; prev = prev->next){
+        twdt_task_t *prev;
+        for (prev = twdt_config->list; prev->next != target_task; prev = prev->next){
             ;   //point prev to task preceding target_task
         }
         prev->next = target_task->next;
         free(target_task);
     }
 
-    //If idle task, disable idle_enable flag
+    //If idle task, deregister idle hook callback form appropriate core
     for(int i = 0; i < portNUM_PROCESSORS; i++){
-        if(handle == wdt_config->idle_handles[i]){
-            wdt_config->idle_enable[i] = false;
+        if(handle == xTaskGetIdleTaskHandleForCPU(i)){
+            esp_deregister_freertos_idle_hook_for_cpu(idle_hook_cb, i);
+            break;
         }
     }
 
-    if(all_fed){     //Reset timer if all remaining tasks have checked in
-        feed_wdt();
+    if(all_reset){     //Reset hardware timer if all remaining tasks have reset
+        reset_hw_timer();
     }
 
-    portEXIT_CRITICAL(&taskwdt_spinlock);
+    portEXIT_CRITICAL(&twdt_spinlock);
     return ESP_OK;
 }
 
+esp_err_t esp_task_wdt_status(TaskHandle_t handle)
+{
+    if(handle == NULL){
+        handle = xTaskGetCurrentTaskHandle();
+    }
+
+    portENTER_CRITICAL(&twdt_spinlock);
+    //Return if TWDT is not initialized
+    ASSERT_EXIT_CRIT_RETURN((twdt_config != NULL), ESP_ERR_INVALID_STATE);
+
+    twdt_task_t *task;
+    for(task = twdt_config->list; task!=NULL; task=task->next){
+        //Return ESP_OK if task is found
+        ASSERT_EXIT_CRIT_RETURN((task->task_handle != handle), ESP_OK);
+    }
+
+    //Task could not be found
+    portEXIT_CRITICAL(&twdt_spinlock);
+    return ESP_ERR_NOT_FOUND;
+}
+
+void esp_task_wdt_feed()
+{
+    portENTER_CRITICAL(&twdt_spinlock);
+    //Return immediately if TWDT has not been initialized
+    ASSERT_EXIT_CRIT_RETURN((twdt_config != NULL), VOID_RETURN);
+
+    //Check if task is on list
+    TaskHandle_t handle = xTaskGetCurrentTaskHandle();
+    bool all_reset;
+    twdt_task_t *target_task = find_task_in_twdt_list(handle, &all_reset);
+
+    //reset the task if it's on the list, then return
+    if(target_task != NULL){
+        target_task->has_reset = true;
+        if(all_reset){
+            reset_hw_timer();       //Reset hardware timer if all other tasks have reset
+        }
+        portEXIT_CRITICAL(&twdt_spinlock);
+        return;
+    }
+
+    //Add task if it's has not on list
+    target_task = calloc(1, sizeof(twdt_task_t));
+    ASSERT_EXIT_CRIT_RETURN((target_task != NULL), VOID_RETURN);   //If calloc failed
+    target_task->task_handle = handle;
+    target_task->has_reset = true;
+    target_task->next = NULL;
+
+    if (twdt_config->list == NULL) {    //Adding to empty list
+        twdt_config->list = target_task;
+    } else {    //Adding to tail of list
+        twdt_task_t *task;
+        for (task = twdt_config->list; task->next != NULL; task = task->next){
+            ;   //point task to current tail of wdt task list
+        }
+        task->next = target_task;
+    }
+
+    portEXIT_CRITICAL(&twdt_spinlock);
+}
+
+