1 /*
2  * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <stdint.h>
8 #include <stdbool.h>
9 #include <stdio.h>
10 #include "sdkconfig.h"
11 #include "hal/wdt_hal.h"
12 #include "esp_err.h"
13 #include "esp_attr.h"
14 #include "esp_check.h"
15 #include "esp_log.h"
16 #include "esp_debug_helpers.h"
17 #include "esp_timer.h"
18 #include "esp_private/esp_task_wdt_impl.h"
19 
20 /**
21  * Context for the software implementation of the Task WatchDog Timer.
22  * This will be passed as a parameter to public functions below. */
23 typedef struct {
24     esp_timer_handle_t sw_timer;
25     uint32_t period_ms;
26 } twdt_ctx_soft_t;
27 
28 /**
29  * Declare the initial context as static. It will be passed to the
30  * task_wdt implementation as the implementation context in the
31  * init function. */
32 static twdt_ctx_soft_t init_context;
33 
34 static const char *TAG = "task_wdt_impl_soft";
35 
36 
esp_task_wdt_impl_timer_allocate(const esp_task_wdt_config_t * config,twdt_isr_callback callback,twdt_ctx_t * obj)37 esp_err_t esp_task_wdt_impl_timer_allocate(const esp_task_wdt_config_t *config,
38                                            twdt_isr_callback callback,
39                                            twdt_ctx_t *obj)
40 {
41     twdt_ctx_soft_t *ctx = &init_context;
42     const esp_timer_create_args_t timer_args = {
43         .callback = callback,
44         .arg = NULL,
45         .dispatch_method = ESP_TIMER_ISR,
46         .name = "Task software watchdog",
47         .skip_unhandled_events = true
48     };
49 
50     /* Software Task timer. As we don't have a spare hardware watchdog timer, we will use esp_timer to simulate one */
51     esp_err_t ret = esp_timer_create(&timer_args, &ctx->sw_timer);
52     ESP_GOTO_ON_FALSE((ret == ESP_OK), ret, reterr, TAG, "could not start periodic timer");
53 
54     /* Configure it as a periodic timer, so that we check the Tasks everytime it is triggered.
55      * No need to start the timer here, it will be started later with `esp_task_wdt_impl_timer_restart` */
56     ctx->period_ms = config->timeout_ms;
57 
58     /* Return our context to the caller */
59     *obj = (twdt_ctx_t) ctx;
60 
61 reterr:
62     return ret;
63 }
64 
esp_task_wdt_impl_timer_reconfigure(twdt_ctx_t obj,const esp_task_wdt_config_t * config)65 esp_err_t esp_task_wdt_impl_timer_reconfigure(twdt_ctx_t obj, const esp_task_wdt_config_t *config)
66 {
67     esp_err_t ret = ESP_OK;
68     twdt_ctx_soft_t* ctx = (twdt_ctx_soft_t*) obj;
69 
70     if (config == NULL || ctx == NULL) {
71         ret = ESP_ERR_INVALID_STATE;
72     }
73 
74     if (ret == ESP_OK) {
75         /* The timer is stopped, we only need to update the period in our context, next time we start the
76          * timer with `esp_task_wdt_impl_timer_restart`, we will pass the context's period to the
77          * underlying esp_timer instance. */
78         ctx->period_ms = config->timeout_ms;
79     }
80 
81     return ret;
82 }
83 
84 
esp_task_wdt_impl_timer_free(twdt_ctx_t obj)85 void esp_task_wdt_impl_timer_free(twdt_ctx_t obj)
86 {
87     const twdt_ctx_soft_t* ctx = (twdt_ctx_soft_t*) obj;
88 
89     if (ctx != NULL && ctx->sw_timer != NULL) {
90         ESP_ERROR_CHECK(esp_timer_delete(ctx->sw_timer));
91     }
92 }
93 
94 
esp_task_wdt_impl_timer_feed(twdt_ctx_t obj)95 esp_err_t esp_task_wdt_impl_timer_feed(twdt_ctx_t obj)
96 {
97     esp_err_t ret = ESP_OK;
98     const twdt_ctx_soft_t* ctx = (twdt_ctx_soft_t*) obj;
99 
100     if (ctx == NULL) {
101         ret = ESP_ERR_INVALID_STATE;
102     }
103 
104     if (ret == ESP_OK) {
105         /* Feed the periodic timer by restarting it, specifying the same period */
106         ret = esp_timer_restart(ctx->sw_timer, ctx->period_ms * 1000);
107     }
108 
109     return ret;
110 }
111 
112 
esp_task_wdt_impl_timeout_triggered(twdt_ctx_t obj)113 void esp_task_wdt_impl_timeout_triggered(twdt_ctx_t obj)
114 {
115     (void) obj;
116 }
117 
118 
esp_task_wdt_impl_timer_stop(twdt_ctx_t obj)119 esp_err_t esp_task_wdt_impl_timer_stop(twdt_ctx_t obj)
120 {
121     esp_err_t ret = ESP_OK;
122     const twdt_ctx_soft_t* ctx = (twdt_ctx_soft_t*) obj;
123 
124     if (ctx == NULL || ctx->sw_timer == NULL) {
125         ret = ESP_ERR_INVALID_STATE;
126     }
127 
128     if (ret == ESP_OK) {
129         ret = esp_timer_stop(ctx->sw_timer);
130     }
131 
132     return ret;
133 }
134 
135 
esp_task_wdt_impl_timer_restart(twdt_ctx_t obj)136 esp_err_t esp_task_wdt_impl_timer_restart(twdt_ctx_t obj)
137 {
138     esp_err_t ret = ESP_OK;
139     twdt_ctx_soft_t* ctx = (twdt_ctx_soft_t*) obj;
140 
141     if (ctx == NULL || ctx->sw_timer == NULL) {
142         ret = ESP_ERR_INVALID_STATE;
143     }
144 
145     if (ret == ESP_OK) {
146         esp_timer_start_periodic(ctx->sw_timer, ctx->period_ms * 1000);
147     }
148 
149     return ret;
150 }
151