1 /*
2 * SPDX-FileCopyrightText: 2017-2022 Espressif Systems (Shanghai) CO LTD
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6 #include <zephyr/kernel.h>
7 #include <sys/param.h>
8 #include <string.h>
9 #include "soc/soc.h"
10 #include "esp_types.h"
11 #include "esp_attr.h"
12 #include "esp_err.h"
13 #include "esp_task.h"
14 #include "esp_log.h"
15 #include "esp_ipc.h"
16 #include "esp_timer.h"
17 #include "esp_timer_impl.h"
18
19 #include "esp_private/startup_internal.h"
20 #include "esp_private/esp_timer_private.h"
21 #include "esp_private/system_internal.h"
22
23 #if CONFIG_IDF_TARGET_ESP32
24 #include "esp32/rtc.h"
25 #elif CONFIG_IDF_TARGET_ESP32S2
26 #include "esp32s2/rtc.h"
27 #elif CONFIG_IDF_TARGET_ESP32S3
28 #include "esp32s3/rtc.h"
29 #elif CONFIG_IDF_TARGET_ESP32C3
30 #include "esp32c3/rtc.h"
31 #elif CONFIG_IDF_TARGET_ESP32C2
32 #include "esp32c2/rtc.h"
33 #elif CONFIG_IDF_TARGET_ESP32C6
34 #include "esp32c6/rtc.h"
35 #elif CONFIG_IDF_TARGET_ESP32H2
36 #include "esp32h2/rtc.h"
37 #endif
38
39 #include "sdkconfig.h"
40
41 #if defined(CONFIG_SOC_SERIES_ESP32C2) || \
42 defined(CONFIG_SOC_SERIES_ESP32C3) || \
43 defined(CONFIG_SOC_SERIES_ESP32C6)
44 #include <zephyr/drivers/interrupt_controller/intc_esp32c3.h>
45 #define ISR_HANDLER isr_handler_t
46 #else
47 #include <zephyr/drivers/interrupt_controller/intc_esp32.h>
48 #define ISR_HANDLER intr_handler_t
49 #endif
50
51 #define LOG_MODULE_NAME esp_timer
52 #include <zephyr/logging/log.h>
53 LOG_MODULE_REGISTER(LOG_MODULE_NAME);
54
55 #ifdef CONFIG_ESP_TIMER_PROFILING
56 #define WITH_PROFILING 1
57 #endif
58
59 #ifndef NDEBUG
60 // Enable built-in checks in queue.h in debug builds
61 #define INVARIANTS
62 #endif
63 #include "sys/queue.h"
64
65 #define EVENT_ID_DELETE_TIMER 0xF0DE1E1E
66
67 #define TIMER_EVENT_QUEUE_SIZE 16
68
69 typedef enum {
70 FL_ISR_DISPATCH_METHOD = (1 << 0), //!< 0=Callback is called from timer task, 1=Callback is called from timer ISR
71 FL_SKIP_UNHANDLED_EVENTS = (1 << 1), //!< 0=NOT skip unhandled events for periodic timers, 1=Skip unhandled events for periodic timers
72 } flags_t;
73
74 struct esp_timer {
75 uint64_t alarm;
76 uint64_t period:56;
77 flags_t flags:8;
78 union {
79 esp_timer_cb_t callback;
80 uint32_t event_id;
81 };
82 void* arg;
83 #if WITH_PROFILING
84 const char* name;
85 size_t times_triggered;
86 size_t times_armed;
87 size_t times_skipped;
88 uint64_t total_callback_run_time;
89 #endif // WITH_PROFILING
90 LIST_ENTRY(esp_timer) list_entry;
91 };
92
93 static bool init_status = false;
94 static inline bool is_initialized(void);
95 static esp_err_t timer_insert(esp_timer_handle_t timer, bool without_update_alarm);
96 static esp_err_t timer_remove(esp_timer_handle_t timer);
97 static bool timer_armed(esp_timer_handle_t timer);
98 static void timer_list_lock(esp_timer_dispatch_t timer_type);
99 static void timer_list_unlock(esp_timer_dispatch_t timer_type);
100
101 #if WITH_PROFILING
102 static void timer_insert_inactive(esp_timer_handle_t timer);
103 static void timer_remove_inactive(esp_timer_handle_t timer);
104 #endif // WITH_PROFILING
105
106 __attribute__((unused)) static const char* TAG = "esp_timer";
107
108 // lists of currently armed timers for two dispatch methods: ISR and TASK
109 static LIST_HEAD(esp_timer_list, esp_timer) s_timers[ESP_TIMER_MAX] = {
110 [0 ... (ESP_TIMER_MAX - 1)] = LIST_HEAD_INITIALIZER(s_timers)
111 };
112 #if WITH_PROFILING
113 // lists of unarmed timers for two dispatch methods: ISR and TASK,
114 // used only to be able to dump statistics about all the timers
115 static LIST_HEAD(esp_inactive_timer_list, esp_timer) s_inactive_timers[ESP_TIMER_MAX] = {
116 [0 ... (ESP_TIMER_MAX - 1)] = LIST_HEAD_INITIALIZER(s_timers)
117 };
118 #endif
119
120 K_KERNEL_STACK_MEMBER(timer_task_stack, CONFIG_ESP32_TIMER_TASK_STACK_SIZE);
121 // task used to dispatch timer callbacks
122 static struct k_thread s_timer_task;
123 // counting semaphore used to notify the timer task from ISR
124 static struct k_sem s_timer_semaphore;
125 // lock protecting s_timers, s_inactive_timers
126 static unsigned int s_timer_lock[ESP_TIMER_MAX] = {
127 [0 ... (ESP_TIMER_MAX - 1)] = 0
128 };
129
130 #ifdef CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD
131 // For ISR dispatch method, a callback function of the timer may require a context switch
132 static volatile BaseType_t s_isr_dispatch_need_yield = pdFALSE;
133 #endif // CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD
134
esp_timer_create(const esp_timer_create_args_t * args,esp_timer_handle_t * out_handle)135 esp_err_t esp_timer_create(const esp_timer_create_args_t* args,
136 esp_timer_handle_t* out_handle)
137 {
138 if (!is_initialized()) {
139 return ESP_ERR_INVALID_STATE;
140 }
141 if (args == NULL || args->callback == NULL || out_handle == NULL ||
142 args->dispatch_method < 0 || args->dispatch_method >= ESP_TIMER_MAX) {
143 return ESP_ERR_INVALID_ARG;
144 }
145 esp_timer_handle_t result = (esp_timer_handle_t) k_calloc(1, sizeof(*result));
146 if (result == NULL) {
147 return ESP_ERR_NO_MEM;
148 }
149 result->callback = args->callback;
150 result->arg = args->arg;
151 result->flags = (args->dispatch_method ? FL_ISR_DISPATCH_METHOD : 0) |
152 (args->skip_unhandled_events ? FL_SKIP_UNHANDLED_EVENTS : 0);
153 #if WITH_PROFILING
154 result->name = args->name;
155 esp_timer_dispatch_t dispatch_method = result->flags & FL_ISR_DISPATCH_METHOD;
156 timer_list_lock(dispatch_method);
157 timer_insert_inactive(result);
158 timer_list_unlock(dispatch_method);
159 #endif
160 *out_handle = result;
161 return ESP_OK;
162 }
163
164 /*
165 * We have placed this function in IRAM to ensure consistency with the esp_timer API.
166 * esp_timer_start_once, esp_timer_start_periodic and esp_timer_stop are in IRAM.
167 * But actually in IDF esp_timer_restart is used only in one place, which requires keeping
168 * in IRAM when PM_SLP_IRAM_OPT = y and ESP_TASK_WDT USE ESP_TIMER = y.
169 */
esp_timer_restart(esp_timer_handle_t timer,uint64_t timeout_us)170 esp_err_t IRAM_ATTR esp_timer_restart(esp_timer_handle_t timer, uint64_t timeout_us)
171 {
172 esp_err_t ret = ESP_OK;
173
174 if (timer == NULL) {
175 return ESP_ERR_INVALID_ARG;
176 }
177
178 if (!is_initialized()) {
179 return ESP_ERR_INVALID_STATE;
180 }
181
182 esp_timer_dispatch_t dispatch_method = timer->flags & FL_ISR_DISPATCH_METHOD;
183 timer_list_lock(dispatch_method);
184
185 if (!timer_armed(timer)) {
186 timer_list_unlock(dispatch_method);
187 return ESP_ERR_INVALID_STATE;
188 }
189
190 const int64_t now = esp_timer_impl_get_time();
191 const uint64_t period = timer->period;
192
193 /* We need to remove the timer to the list of timers and reinsert it at
194 * the right position. In fact, the timers are sorted by their alarm value
195 * (earliest first) */
196 ret = timer_remove(timer);
197
198 if (ret == ESP_OK) {
199 /* Two cases here:
200 * - if the alarm was a periodic one, i.e. `period` is not 0, the given timeout_us becomes the new period
201 * - if the alarm was a one-shot one, i.e. `period` is 0, it remains non-periodic. */
202 if (period != 0) {
203 /* Remove function got rid of the alarm and period fields, restore them */
204 const uint64_t new_period = MAX(timeout_us, esp_timer_impl_get_min_period_us());
205 timer->alarm = now + new_period;
206 timer->period = new_period;
207 } else {
208 /* The new one-shot alarm shall be triggered timeout_us after the current time */
209 timer->alarm = now + timeout_us;
210 timer->period = 0;
211 }
212 ret = timer_insert(timer, false);
213 }
214
215 timer_list_unlock(dispatch_method);
216
217 return ret;
218 }
219
esp_timer_start_once(esp_timer_handle_t timer,uint64_t timeout_us)220 esp_err_t IRAM_ATTR esp_timer_start_once(esp_timer_handle_t timer, uint64_t timeout_us)
221 {
222 if (timer == NULL) {
223 return ESP_ERR_INVALID_ARG;
224 }
225 if (!is_initialized()) {
226 return ESP_ERR_INVALID_STATE;
227 }
228 int64_t alarm = esp_timer_get_time() + timeout_us;
229 esp_timer_dispatch_t dispatch_method = timer->flags & FL_ISR_DISPATCH_METHOD;
230 esp_err_t err;
231
232 timer_list_lock(dispatch_method);
233
234 /* Check if the timer is armed once the list is locked.
235 * Otherwise another task may arm the timer inbetween the check
236 * and us locking the list, resulting in us inserting the
237 * timer to s_timers a second time. This will create a loop
238 * in s_timers. */
239 if (timer_armed(timer)) {
240 err = ESP_ERR_INVALID_STATE;
241 } else {
242 timer->alarm = alarm;
243 timer->period = 0;
244 #if WITH_PROFILING
245 timer->times_armed++;
246 #endif
247 err = timer_insert(timer, false);
248 }
249
250 timer_list_unlock(dispatch_method);
251 return err;
252 }
253
esp_timer_start_periodic(esp_timer_handle_t timer,uint64_t period_us)254 esp_err_t IRAM_ATTR esp_timer_start_periodic(esp_timer_handle_t timer, uint64_t period_us)
255 {
256 if (timer == NULL) {
257 return ESP_ERR_INVALID_ARG;
258 }
259 if (!is_initialized()) {
260 return ESP_ERR_INVALID_STATE;
261 }
262 period_us = MAX(period_us, esp_timer_impl_get_min_period_us());
263 int64_t alarm = esp_timer_get_time() + period_us;
264 esp_timer_dispatch_t dispatch_method = timer->flags & FL_ISR_DISPATCH_METHOD;
265 esp_err_t err;
266 timer_list_lock(dispatch_method);
267
268 /* Check if the timer is armed once the list is locked to avoid a data race */
269 if (timer_armed(timer)) {
270 err = ESP_ERR_INVALID_STATE;
271 } else {
272 timer->alarm = alarm;
273 timer->period = period_us;
274 #if WITH_PROFILING
275 timer->times_armed++;
276 timer->times_skipped = 0;
277 #endif
278 err = timer_insert(timer, false);
279 }
280
281 timer_list_unlock(dispatch_method);
282 return err;
283 }
284
esp_timer_stop(esp_timer_handle_t timer)285 esp_err_t IRAM_ATTR esp_timer_stop(esp_timer_handle_t timer)
286 {
287 if (!is_initialized()) {
288 return ESP_ERR_INVALID_STATE;
289 }
290 esp_timer_dispatch_t dispatch_method = timer->flags & FL_ISR_DISPATCH_METHOD;
291 esp_err_t err;
292
293 timer_list_lock(dispatch_method);
294
295 /* Check if the timer is armed once the list is locked to avoid a data race */
296 if (!timer_armed(timer)) {
297 err = ESP_ERR_INVALID_STATE;
298 } else {
299 err = timer_remove(timer);
300 }
301 timer_list_unlock(dispatch_method);
302 return err;
303 }
304
esp_timer_delete(esp_timer_handle_t timer)305 esp_err_t esp_timer_delete(esp_timer_handle_t timer)
306 {
307 if (timer == NULL) {
308 return ESP_ERR_INVALID_ARG;
309 }
310 int64_t alarm = esp_timer_get_time();
311 esp_err_t err;
312 timer_list_lock(ESP_TIMER_TASK);
313
314 /* Check if the timer is armed once the list is locked to avoid a data race */
315 if (timer_armed(timer)) {
316 err = ESP_ERR_INVALID_STATE;
317 } else {
318 // A case for the timer with ESP_TIMER_ISR:
319 // This ISR timer was removed from the ISR list in esp_timer_stop() or in timer_process_alarm() -> LIST_REMOVE(it, list_entry)
320 // and here this timer will be added to another the TASK list, see below.
321 // We do this because we want to free memory of the timer in a task context instead of an isr context.
322 timer->flags &= ~FL_ISR_DISPATCH_METHOD;
323 timer->event_id = EVENT_ID_DELETE_TIMER;
324 timer->alarm = alarm;
325 timer->period = 0;
326 err = timer_insert(timer, false);
327 }
328 timer_list_unlock(ESP_TIMER_TASK);
329 return err;
330 }
331
timer_insert(esp_timer_handle_t timer,bool without_update_alarm)332 static IRAM_ATTR esp_err_t timer_insert(esp_timer_handle_t timer, bool without_update_alarm)
333 {
334 #if WITH_PROFILING
335 timer_remove_inactive(timer);
336 #endif
337 esp_timer_handle_t it, last = NULL;
338 esp_timer_dispatch_t dispatch_method = timer->flags & FL_ISR_DISPATCH_METHOD;
339 if (LIST_FIRST(&s_timers[dispatch_method]) == NULL) {
340 LIST_INSERT_HEAD(&s_timers[dispatch_method], timer, list_entry);
341 } else {
342 LIST_FOREACH(it, &s_timers[dispatch_method], list_entry) {
343 if (timer->alarm < it->alarm) {
344 LIST_INSERT_BEFORE(it, timer, list_entry);
345 break;
346 }
347 last = it;
348 }
349 if (it == NULL) {
350 __ASSERT_NO_MSG(last);
351 LIST_INSERT_AFTER(last, timer, list_entry);
352 }
353 }
354 if (without_update_alarm == false && timer == LIST_FIRST(&s_timers[dispatch_method])) {
355 esp_timer_impl_set_alarm_id(timer->alarm, dispatch_method);
356 }
357 return ESP_OK;
358 }
359
timer_remove(esp_timer_handle_t timer)360 static IRAM_ATTR esp_err_t timer_remove(esp_timer_handle_t timer)
361 {
362 esp_timer_dispatch_t dispatch_method = timer->flags & FL_ISR_DISPATCH_METHOD;
363 esp_timer_handle_t first_timer = LIST_FIRST(&s_timers[dispatch_method]);
364 LIST_REMOVE(timer, list_entry);
365 timer->alarm = 0;
366 timer->period = 0;
367 if (timer == first_timer) { // if this timer was the first in the list.
368 uint64_t next_timestamp = UINT64_MAX;
369 first_timer = LIST_FIRST(&s_timers[dispatch_method]);
370 if (first_timer) { // if after removing the timer from the list, this list is not empty.
371 next_timestamp = first_timer->alarm;
372 }
373 esp_timer_impl_set_alarm_id(next_timestamp, dispatch_method);
374 }
375 #if WITH_PROFILING
376 timer_insert_inactive(timer);
377 #endif
378 return ESP_OK;
379 }
380
381 #if WITH_PROFILING
382
timer_insert_inactive(esp_timer_handle_t timer)383 static IRAM_ATTR void timer_insert_inactive(esp_timer_handle_t timer)
384 {
385 esp_timer_dispatch_t dispatch_method = timer->flags & FL_ISR_DISPATCH_METHOD;
386 esp_timer_handle_t head = LIST_FIRST(&s_inactive_timers[dispatch_method]);
387 if (head == NULL) {
388 LIST_INSERT_HEAD(&s_inactive_timers[dispatch_method], timer, list_entry);
389 } else {
390 /* Insert as head element as this is the fastest thing to do.
391 * Removal is O(1) anyway.
392 */
393 LIST_INSERT_BEFORE(head, timer, list_entry);
394 }
395 }
396
timer_remove_inactive(esp_timer_handle_t timer)397 static IRAM_ATTR void timer_remove_inactive(esp_timer_handle_t timer)
398 {
399 LIST_REMOVE(timer, list_entry);
400 }
401
402 #endif // WITH_PROFILING
403
timer_armed(esp_timer_handle_t timer)404 static IRAM_ATTR bool timer_armed(esp_timer_handle_t timer)
405 {
406 return timer->alarm > 0;
407 }
408
timer_list_lock(esp_timer_dispatch_t timer_type)409 static IRAM_ATTR void timer_list_lock(esp_timer_dispatch_t timer_type)
410 {
411 unsigned key = irq_lock();
412
413 /* ensure lock is not already held */
414 __ASSERT_NO_MSG(s_timer_lock[timer_type] == 0);
415 s_timer_lock[timer_type] = key;
416 }
417
timer_list_unlock(esp_timer_dispatch_t timer_type)418 static IRAM_ATTR void timer_list_unlock(esp_timer_dispatch_t timer_type)
419 {
420 unsigned int key = s_timer_lock[timer_type];
421 s_timer_lock[timer_type] = 0;
422
423 irq_unlock(key);
424 }
425
426 #ifdef CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD
timer_process_alarm(esp_timer_dispatch_t dispatch_method)427 static IRAM_ATTR bool timer_process_alarm(esp_timer_dispatch_t dispatch_method)
428 #else
429 static bool timer_process_alarm(esp_timer_dispatch_t dispatch_method)
430 #endif
431 {
432 timer_list_lock(dispatch_method);
433 bool processed = false;
434 esp_timer_handle_t it;
435 while (1) {
436 it = LIST_FIRST(&s_timers[dispatch_method]);
437 int64_t now = esp_timer_impl_get_time();
438 if (it == NULL || it->alarm > now) {
439 break;
440 }
441 processed = true;
442 LIST_REMOVE(it, list_entry);
443 if (it->event_id == EVENT_ID_DELETE_TIMER) {
444 // It is handled only by ESP_TIMER_TASK (see esp_timer_delete()).
445 // All the ESP_TIMER_ISR timers which should be deleted are moved by esp_timer_delete() to the ESP_TIMER_TASK list.
446 // We want to free memory of the timer in a task context instead of an isr context.
447 k_free(it);
448 it = NULL;
449 } else {
450 if (it->period > 0) {
451 int skipped = (now - it->alarm) / it->period;
452 if ((it->flags & FL_SKIP_UNHANDLED_EVENTS) && (skipped > 1)) {
453 it->alarm = now + it->period;
454 #if WITH_PROFILING
455 it->times_skipped += skipped;
456 #endif
457 } else {
458 it->alarm += it->period;
459 }
460 timer_insert(it, true);
461 } else {
462 it->alarm = 0;
463 #if WITH_PROFILING
464 timer_insert_inactive(it);
465 #endif
466 }
467 #if WITH_PROFILING
468 uint64_t callback_start = now;
469 #endif
470 esp_timer_cb_t callback = it->callback;
471 void* arg = it->arg;
472 timer_list_unlock(dispatch_method);
473 (*callback)(arg);
474 timer_list_lock(dispatch_method);
475 #if WITH_PROFILING
476 it->times_triggered++;
477 it->total_callback_run_time += esp_timer_impl_get_time() - callback_start;
478 #endif
479 }
480 } // while(1)
481 if (it) {
482 if (dispatch_method == ESP_TIMER_TASK || (dispatch_method != ESP_TIMER_TASK && processed == true)) {
483 esp_timer_impl_set_alarm_id(it->alarm, dispatch_method);
484 }
485 } else {
486 if (processed) {
487 esp_timer_impl_set_alarm_id(UINT64_MAX, dispatch_method);
488 }
489 }
490 timer_list_unlock(dispatch_method);
491 return processed;
492 }
493
timer_task(void * arg)494 static void timer_task(void* arg)
495 {
496 while (true){
497 k_sem_take(&s_timer_semaphore, K_FOREVER);
498 // all deferred events are processed at a time
499 timer_process_alarm(ESP_TIMER_TASK);
500 }
501 }
502
503 #ifdef CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD
esp_timer_isr_dispatch_need_yield(void)504 IRAM_ATTR void esp_timer_isr_dispatch_need_yield(void)
505 {
506 __ASSERT_NO_MSG(k_is_in_isr();
507 s_isr_dispatch_need_yield = pdTRUE;
508 }
509 #endif
510
511 static void IRAM_ATTR timer_alarm_handler(void* arg)
512 {
513 bool isr_timers_processed = false;
514
515 #ifdef CONFIG_ESP_TIMER_SUPPORTS_ISR_DISPATCH_METHOD
516 esp_timer_impl_try_to_set_next_alarm();
517 // process timers with ISR dispatch method
518 isr_timers_processed = timer_process_alarm(ESP_TIMER_ISR);
519 s_isr_dispatch_need_yield = pdFALSE;
520 #endif
521
522 if (isr_timers_processed == false) {
523 k_sem_give(&s_timer_semaphore);
524 }
525 }
526
527 static IRAM_ATTR inline bool is_initialized(void)
528 {
529 return init_status;
530 }
531
532 esp_err_t esp_timer_early_init(void)
533 {
534 esp_timer_impl_early_init();
535 #if CONFIG_ESP_TIME_FUNCS_USE_ESP_TIMER
536 esp_timer_impl_init_system_time();
537 #endif
538 return ESP_OK;
539 }
540
541 static esp_err_t init_timer_task(void)
542 {
543 esp_err_t err = ESP_OK;
544 if (is_initialized()) {
545 ESP_EARLY_LOGE(TAG, "Task is already initialized");
546 err = ESP_ERR_INVALID_STATE;
547 } else {
548 int ret = k_sem_init(&s_timer_semaphore, 0, TIMER_EVENT_QUEUE_SIZE);
549
550 if (ret) {
551 ESP_EARLY_LOGE(TAG, "Not enough memory to run esp_timer");
552 return ESP_ERR_NO_MEM;
553 }
554
555 k_tid_t tid = k_thread_create(&s_timer_task, timer_task_stack,
556 CONFIG_ESP32_TIMER_TASK_STACK_SIZE, (k_thread_entry_t)timer_task, NULL, NULL, NULL,
557 CONFIG_ESP32_TIMER_TASK_PRIO, K_INHERIT_PERMS, K_NO_WAIT);
558
559 if (!tid) {
560 ESP_EARLY_LOGE(TAG, "Not enough memory to create timer task");
561 err = ESP_ERR_NO_MEM;
562 }
563
564 k_thread_name_set(tid, "esp_timer");
565 }
566 return err;
567 }
568
569 static void deinit_timer_task(void)
570 {
571 k_thread_abort(&s_timer_task);
572 }
573
574 esp_err_t esp_timer_init(void)
575 {
576 esp_err_t err = ESP_OK;
577 err = init_timer_task();
578 if (err == ESP_OK) {
579 err = esp_timer_impl_init((ISR_HANDLER)&timer_alarm_handler);
580 if (err != ESP_OK) {
581 ESP_EARLY_LOGE(TAG, "ISR init failed");
582 deinit_timer_task();
583 }
584 init_status = true;
585 }
586 return err;
587 }
588
589 #if defined(CONFIG_WIFI_ESP32) || defined(CONFIG_BT_ESP32)
590 SYS_INIT(esp_timer_init, POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT);
591 #endif
592
593 esp_err_t esp_timer_deinit(void)
594 {
595 if (!is_initialized()) {
596 return ESP_ERR_INVALID_STATE;
597 }
598
599 /* Check if there are any active timers */
600 for (esp_timer_dispatch_t dispatch_method = ESP_TIMER_TASK; dispatch_method < ESP_TIMER_MAX; ++dispatch_method) {
601 if (!LIST_EMPTY(&s_timers[dispatch_method])) {
602 return ESP_ERR_INVALID_STATE;
603 }
604 }
605
606 /* We can only check if there are any timers which are not deleted if
607 * profiling is enabled.
608 */
609 #if WITH_PROFILING
610 for (esp_timer_dispatch_t dispatch_method = ESP_TIMER_TASK; dispatch_method < ESP_TIMER_MAX; ++dispatch_method) {
611 if (!LIST_EMPTY(&s_inactive_timers[dispatch_method])) {
612 return ESP_ERR_INVALID_STATE;
613 }
614 }
615 #endif
616
617 esp_timer_impl_deinit();
618 deinit_timer_task();
619 init_status = false;
620 return ESP_OK;
621 }
622
623 static void print_timer_info(esp_timer_handle_t t, char** dst, size_t* dst_size)
624 {
625 #if WITH_PROFILING
626 size_t cb;
627 // name is optional, might be missed.
628 if (t->name) {
629 cb = snprintf(*dst, *dst_size, "%-20.20s ", t->name);
630 } else {
631 cb = snprintf(*dst, *dst_size, "timer@%-10p ", t);
632 }
633 cb += snprintf(*dst + cb, *dst_size + cb, "%-10lld %-12lld %-12d %-12d %-12d %-12lld\n",
634 (uint64_t)t->period, t->alarm, t->times_armed,
635 t->times_triggered, t->times_skipped, t->total_callback_run_time);
636 /* keep this in sync with the format string, used in esp_timer_dump */
637 #define TIMER_INFO_LINE_LEN 90
638 #else
639 size_t cb = snprintf(*dst, *dst_size, "timer@%-14p %-10lld %-12lld\n", t, (uint64_t)t->period, t->alarm);
640 #define TIMER_INFO_LINE_LEN 46
641 #endif
642 *dst += cb;
643 *dst_size -= cb;
644 }
645
646
647 esp_err_t esp_timer_dump(FILE* stream)
648 {
649 /* Since timer lock is a critical section, we don't want to print directly
650 * to stdout, since that may cause a deadlock if stdout is interrupt-driven
651 * (via the UART driver). Allocate sufficiently large chunk of memory first,
652 * print to it, then dump this memory to stdout.
653 */
654
655 esp_timer_handle_t it;
656
657 /* First count the number of timers */
658 size_t timer_count = 0;
659 for (esp_timer_dispatch_t dispatch_method = ESP_TIMER_TASK; dispatch_method < ESP_TIMER_MAX; ++dispatch_method) {
660 timer_list_lock(dispatch_method);
661 LIST_FOREACH(it, &s_timers[dispatch_method], list_entry) {
662 ++timer_count;
663 }
664 #if WITH_PROFILING
665 LIST_FOREACH(it, &s_inactive_timers[dispatch_method], list_entry) {
666 ++timer_count;
667 }
668 #endif
669 timer_list_unlock(dispatch_method);
670 }
671
672 /* Allocate the memory for this number of timers. Since we have unlocked,
673 * we may find that there are more timers. There's no bulletproof solution
674 * for this (can't allocate from a critical section), but we allocate
675 * slightly more and the output will be truncated if that is not enough.
676 */
677 size_t buf_size = TIMER_INFO_LINE_LEN * (timer_count + 3);
678 char* print_buf = k_calloc(1, buf_size + 1);
679 if (print_buf == NULL) {
680 return ESP_ERR_NO_MEM;
681 }
682
683 /* Print to the buffer */
684 char* pos = print_buf;
685 for (esp_timer_dispatch_t dispatch_method = ESP_TIMER_TASK; dispatch_method < ESP_TIMER_MAX; ++dispatch_method) {
686 timer_list_lock(dispatch_method);
687 LIST_FOREACH(it, &s_timers[dispatch_method], list_entry) {
688 print_timer_info(it, &pos, &buf_size);
689 }
690 #if WITH_PROFILING
691 LIST_FOREACH(it, &s_inactive_timers[dispatch_method], list_entry) {
692 print_timer_info(it, &pos, &buf_size);
693 }
694 #endif
695 timer_list_unlock(dispatch_method);
696 }
697
698 if (stream != NULL) {
699 fprintf(stream, "Timer stats:\n");
700 #if WITH_PROFILING
701 fprintf(stream, "%-20s %-10s %-12s %-12s %-12s %-12s %-12s\n",
702 "Name", "Period", "Alarm", "Times_armed", "Times_trigg", "Times_skip", "Cb_exec_time");
703 #else
704 fprintf(stream, "%-20s %-10s %-12s\n", "Name", "Period", "Alarm");
705 #endif
706
707 /* Print the buffer */
708 fputs(print_buf, stream);
709 }
710
711 k_free(print_buf);
712 return ESP_OK;
713 }
714
715 int64_t IRAM_ATTR esp_timer_get_next_alarm(void)
716 {
717 int64_t next_alarm = INT64_MAX;
718 for (esp_timer_dispatch_t dispatch_method = ESP_TIMER_TASK; dispatch_method < ESP_TIMER_MAX; ++dispatch_method) {
719 timer_list_lock(dispatch_method);
720 esp_timer_handle_t it = LIST_FIRST(&s_timers[dispatch_method]);
721 if (it) {
722 if (next_alarm > it->alarm) {
723 next_alarm = it->alarm;
724 }
725 }
726 timer_list_unlock(dispatch_method);
727 }
728 return next_alarm;
729 }
730
731 int64_t IRAM_ATTR esp_timer_get_next_alarm_for_wake_up(void)
732 {
733 int64_t next_alarm = INT64_MAX;
734 for (esp_timer_dispatch_t dispatch_method = ESP_TIMER_TASK; dispatch_method < ESP_TIMER_MAX; ++dispatch_method) {
735 timer_list_lock(dispatch_method);
736 esp_timer_handle_t it = NULL;
737 LIST_FOREACH(it, &s_timers[dispatch_method], list_entry) {
738 // timers with the SKIP_UNHANDLED_EVENTS flag do not want to wake up CPU from a sleep mode.
739 if ((it->flags & FL_SKIP_UNHANDLED_EVENTS) == 0) {
740 if (next_alarm > it->alarm) {
741 next_alarm = it->alarm;
742 }
743 break;
744 }
745 }
746 timer_list_unlock(dispatch_method);
747 }
748 return next_alarm;
749 }
750
751 esp_err_t IRAM_ATTR esp_timer_get_period(esp_timer_handle_t timer, uint64_t *period)
752 {
753 if (timer == NULL || period == NULL) {
754 return ESP_ERR_INVALID_ARG;
755 }
756
757 esp_timer_dispatch_t dispatch_method = timer->flags & FL_ISR_DISPATCH_METHOD;
758
759 timer_list_lock(dispatch_method);
760 *period = timer->period;
761 timer_list_unlock(dispatch_method);
762
763 return ESP_OK;
764 }
765
766 esp_err_t IRAM_ATTR esp_timer_get_expiry_time(esp_timer_handle_t timer, uint64_t *expiry)
767 {
768 if (timer == NULL || expiry == NULL) {
769 return ESP_ERR_INVALID_ARG;
770 }
771
772 if (timer->period > 0) {
773 /* Return error for periodic timers */
774 return ESP_ERR_NOT_SUPPORTED;
775 }
776
777 esp_timer_dispatch_t dispatch_method = timer->flags & FL_ISR_DISPATCH_METHOD;
778
779 timer_list_lock(dispatch_method);
780 *expiry = timer->alarm;
781 timer_list_unlock(dispatch_method);
782
783 return ESP_OK;
784 }
785
786 bool IRAM_ATTR esp_timer_is_active(esp_timer_handle_t timer)
787 {
788 return timer_armed(timer);
789 }
790