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