1 // Copyright 2017 Espressif Systems (Shanghai) PTE LTD
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include <zephyr.h>
16 #include <sys/param.h>
17 #include <string.h>
18 #include "soc/soc.h"
19 #include "esp_types.h"
20 #include "esp_attr.h"
21 #include "esp_err.h"
22 #include "esp32/rom/ets_sys.h"
23 #include "esp_task.h"
24 #include "esp_log.h"
25 #include "freertos/FreeRTOS.h"
26 #include "freertos/task.h"
27 #include "freertos/semphr.h"
28 // #include "soc/spinlock.h"
29 #include "esp_timer.h"
30 #include "esp_timer_impl.h"
31 #include "sdkconfig.h"
32 
33 #include "esp_private/startup_internal.h"
34 #include "esp_private/esp_timer_private.h"
35 #include "esp_private/system_internal.h"
36 #define LOG_MODULE_NAME esp_timer
37 #include <logging/log.h>
38 LOG_MODULE_REGISTER(LOG_MODULE_NAME);
39 
40 
41 #if CONFIG_IDF_TARGET_ESP32
42 #include "esp32/rtc.h"
43 #elif CONFIG_IDF_TARGET_ESP32S2
44 #include "esp32s2/rtc.h"
45 #elif CONFIG_IDF_TARGET_ESP32S3
46 #include "esp32s3/rtc.h"
47 #elif CONFIG_IDF_TARGET_ESP32C3
48 #include "esp32c3/rtc.h"
49 #endif
50 
51 #ifdef CONFIG_ESP_TIMER_PROFILING
52 #define WITH_PROFILING 1
53 #endif
54 
55 #ifndef NDEBUG
56 // Enable built-in checks in queue.h in debug builds
57 #define INVARIANTS
58 #endif
59 #include "sys/queue.h"
60 
61 #define EVENT_ID_DELETE_TIMER   0xF0DE1E1E
62 
63 #define TIMER_EVENT_QUEUE_SIZE      16
64 typedef enum {
65     FL_DISPATCH_METHOD       = (1 << 0),  //!< 0=Callback is called from timer task, 1=Callback is called from timer ISR
66     FL_SKIP_UNHANDLED_EVENTS = (1 << 1),  //!< 0=NOT skip unhandled events for periodic timers, 1=Skip unhandled events for periodic timers
67 } flags_t;
68 
69 struct esp_timer {
70     uint64_t alarm;
71     uint64_t period:56;
72     flags_t flags:8;
73     union {
74         esp_timer_cb_t callback;
75         uint32_t event_id;
76     };
77     void* arg;
78 #if WITH_PROFILING
79     const char* name;
80     size_t times_triggered;
81     size_t times_armed;
82     size_t times_skipped;
83     uint64_t total_callback_run_time;
84 #endif // WITH_PROFILING
85     LIST_ENTRY(esp_timer) list_entry;
86 };
87 
88 K_KERNEL_STACK_MEMBER(timer_task_stack, 4096);
89 static bool init_status = false;
90 
91 static bool is_initialized(void);
92 static esp_err_t timer_insert(esp_timer_handle_t timer);
93 static esp_err_t timer_remove(esp_timer_handle_t timer);
94 static bool timer_armed(esp_timer_handle_t timer);
95 static void timer_list_lock(void);
96 static void timer_list_unlock(void);
97 
98 #if WITH_PROFILING
99 static void timer_insert_inactive(esp_timer_handle_t timer);
100 static void timer_remove_inactive(esp_timer_handle_t timer);
101 #endif // WITH_PROFILING
102 
103 // list of currently armed timers
104 static LIST_HEAD(esp_timer_list, esp_timer) s_timers =
105         LIST_HEAD_INITIALIZER(s_timers);
106 #if WITH_PROFILING
107 // list of unarmed timers, used only to be able to dump statistics about
108 // all the timers
109 static LIST_HEAD(esp_inactive_timer_list, esp_timer) s_inactive_timers =
110         LIST_HEAD_INITIALIZER(s_timers);
111 #endif
112 // task used to dispatch timer callbacks
113 static struct k_thread s_timer_task;
114 // counting semaphore used to notify the timer task from ISR
115 static struct k_sem s_timer_semaphore;
116 
117 #if CONFIG_SPIRAM_USE_MALLOC
118 // memory for s_timer_semaphore
119 static StaticQueue_t s_timer_semaphore_memory;
120 #endif
121 
122 // lock protecting s_timers, s_inactive_timers
123 static unsigned int s_timer_lock;
124 
esp_timer_create(const esp_timer_create_args_t * args,esp_timer_handle_t * out_handle)125 esp_err_t esp_timer_create(const esp_timer_create_args_t* args,
126                            esp_timer_handle_t* out_handle)
127 {
128     if (!is_initialized()) {
129         return ESP_ERR_INVALID_STATE;
130     }
131     if (args == NULL || args->callback == NULL || out_handle == NULL) {
132         return ESP_ERR_INVALID_ARG;
133     }
134     esp_timer_handle_t result = (esp_timer_handle_t) k_calloc(1, sizeof(*result));
135     if (result == NULL) {
136         return ESP_ERR_NO_MEM;
137     }
138     result->callback = args->callback;
139     result->arg = args->arg;
140     result->flags = (args->dispatch_method ? FL_DISPATCH_METHOD : 0) |
141                     (args->skip_unhandled_events ? FL_SKIP_UNHANDLED_EVENTS : 0);
142 #if WITH_PROFILING
143     result->name = args->name;
144     timer_insert_inactive(result);
145 #endif
146     *out_handle = result;
147     return ESP_OK;
148 }
149 
esp_timer_start_once(esp_timer_handle_t timer,uint64_t timeout_us)150 esp_err_t IRAM_ATTR esp_timer_start_once(esp_timer_handle_t timer, uint64_t timeout_us)
151 {
152     if (timer == NULL) {
153         return ESP_ERR_INVALID_ARG;
154     }
155     if (!is_initialized() || timer_armed(timer)) {
156         return ESP_ERR_INVALID_STATE;
157     }
158     timer_list_lock();
159     timer->alarm = esp_timer_get_time() + timeout_us;
160     timer->period = 0;
161 #if WITH_PROFILING
162     timer->times_armed++;
163 #endif
164     esp_err_t err = timer_insert(timer);
165     timer_list_unlock();
166     return err;
167 }
168 
esp_timer_start_periodic(esp_timer_handle_t timer,uint64_t period_us)169 esp_err_t IRAM_ATTR esp_timer_start_periodic(esp_timer_handle_t timer, uint64_t period_us)
170 {
171     if (timer == NULL) {
172         return ESP_ERR_INVALID_ARG;
173     }
174     if (!is_initialized() || timer_armed(timer)) {
175         return ESP_ERR_INVALID_STATE;
176     }
177     timer_list_lock();
178     period_us = MAX(period_us, esp_timer_impl_get_min_period_us());
179     timer->alarm = esp_timer_get_time() + period_us;
180     timer->period = period_us;
181 #if WITH_PROFILING
182     timer->times_armed++;
183     timer->times_skipped = 0;
184 #endif
185     esp_err_t err = timer_insert(timer);
186     timer_list_unlock();
187     return err;
188 }
189 
esp_timer_stop(esp_timer_handle_t timer)190 esp_err_t IRAM_ATTR esp_timer_stop(esp_timer_handle_t timer)
191 {
192     if (timer == NULL) {
193         return ESP_ERR_INVALID_ARG;
194     }
195     if (!is_initialized() || !timer_armed(timer)) {
196         return ESP_ERR_INVALID_STATE;
197     }
198     return timer_remove(timer);
199 }
200 
esp_timer_delete(esp_timer_handle_t timer)201 esp_err_t esp_timer_delete(esp_timer_handle_t timer)
202 {
203     if (timer == NULL) {
204         return ESP_ERR_INVALID_ARG;
205     }
206     if (timer_armed(timer)) {
207         return ESP_ERR_INVALID_STATE;
208     }
209     timer_list_lock();
210     timer->event_id = EVENT_ID_DELETE_TIMER;
211     timer->alarm = esp_timer_get_time();
212     timer->period = 0;
213     timer_insert(timer);
214     timer_list_unlock();
215     return ESP_OK;
216 }
217 
timer_insert(esp_timer_handle_t timer)218 static IRAM_ATTR esp_err_t timer_insert(esp_timer_handle_t timer)
219 {
220 #if WITH_PROFILING
221     timer_remove_inactive(timer);
222 #endif
223     esp_timer_handle_t it, last = NULL;
224     if (LIST_FIRST(&s_timers) == NULL) {
225         LIST_INSERT_HEAD(&s_timers, timer, list_entry);
226     } else {
227         LIST_FOREACH(it, &s_timers, list_entry) {
228             if (timer->alarm < it->alarm) {
229                 LIST_INSERT_BEFORE(it, timer, list_entry);
230                 break;
231             }
232             last = it;
233         }
234         if (it == NULL) {
235             assert(last);
236             LIST_INSERT_AFTER(last, timer, list_entry);
237         }
238     }
239     if (timer == LIST_FIRST(&s_timers)) {
240         esp_timer_impl_set_alarm(timer->alarm);
241     }
242     return ESP_OK;
243 }
244 
timer_remove(esp_timer_handle_t timer)245 static IRAM_ATTR esp_err_t timer_remove(esp_timer_handle_t timer)
246 {
247     timer_list_lock();
248     LIST_REMOVE(timer, list_entry);
249     timer->alarm = 0;
250     timer->period = 0;
251 #if WITH_PROFILING
252     timer_insert_inactive(timer);
253 #endif
254     timer_list_unlock();
255     return ESP_OK;
256 }
257 
258 #if WITH_PROFILING
259 
timer_insert_inactive(esp_timer_handle_t timer)260 static IRAM_ATTR void timer_insert_inactive(esp_timer_handle_t timer)
261 {
262     /* May be locked or not, depending on where this is called from.
263      * Lock recursively.
264      */
265     timer_list_lock();
266     esp_timer_handle_t head = LIST_FIRST(&s_inactive_timers);
267     if (head == NULL) {
268         LIST_INSERT_HEAD(&s_inactive_timers, timer, list_entry);
269     } else {
270         /* Insert as head element as this is the fastest thing to do.
271          * Removal is O(1) anyway.
272          */
273         LIST_INSERT_BEFORE(head, timer, list_entry);
274     }
275     timer_list_unlock();
276 }
277 
timer_remove_inactive(esp_timer_handle_t timer)278 static IRAM_ATTR void timer_remove_inactive(esp_timer_handle_t timer)
279 {
280     timer_list_lock();
281     LIST_REMOVE(timer, list_entry);
282     timer_list_unlock();
283 }
284 
285 #endif // WITH_PROFILING
286 
timer_armed(esp_timer_handle_t timer)287 static IRAM_ATTR bool timer_armed(esp_timer_handle_t timer)
288 {
289     return timer->alarm > 0;
290 }
291 
timer_list_lock(void)292 static IRAM_ATTR void timer_list_lock(void)
293 {
294     s_timer_lock = irq_lock();
295 }
296 
timer_list_unlock(void)297 static IRAM_ATTR void timer_list_unlock(void)
298 {
299     irq_unlock(s_timer_lock);
300 }
301 
timer_process_alarm(esp_timer_dispatch_t dispatch_method)302 static void timer_process_alarm(esp_timer_dispatch_t dispatch_method)
303 {
304     /* unused, provision to allow running callbacks from ISR */
305     (void) dispatch_method;
306 
307     timer_list_lock();
308     esp_timer_handle_t it;
309     while (1) {
310         it = LIST_FIRST(&s_timers);
311         int64_t now = esp_timer_impl_get_time();
312         if (it == NULL || it->alarm > now) {
313             break;
314         }
315         LIST_REMOVE(it, list_entry);
316         if (it->event_id == EVENT_ID_DELETE_TIMER) {
317             k_free(it);
318             it = NULL;
319         } else {
320             if (it->period > 0) {
321                 int skipped = (now - it->alarm) / it->period;
322                 if ((it->flags & FL_SKIP_UNHANDLED_EVENTS) && (skipped > 1)) {
323                     it->alarm = now + it->period;
324 #if WITH_PROFILING
325                     it->times_skipped += skipped;
326 #endif
327                 } else {
328                     it->alarm += it->period;
329                 }
330                 timer_insert(it);
331             } else {
332                 it->alarm = 0;
333 #if WITH_PROFILING
334                 timer_insert_inactive(it);
335 #endif
336             }
337 #if WITH_PROFILING
338             uint64_t callback_start = now;
339 #endif
340             esp_timer_cb_t callback = it->callback;
341             void* arg = it->arg;
342             timer_list_unlock();
343             (*callback)(arg);
344             timer_list_lock();
345 #if WITH_PROFILING
346             it->times_triggered++;
347             it->total_callback_run_time += esp_timer_impl_get_time() - callback_start;
348 #endif
349         }
350     }
351     if (it) {
352         esp_timer_impl_set_alarm(it->alarm);
353     }
354     timer_list_unlock();
355 }
356 
timer_task(void * arg)357 static void timer_task(void* arg)
358 {
359     while (true){
360         k_sem_take(&s_timer_semaphore, K_FOREVER);
361         // all deferred events are processed at a time
362         timer_process_alarm(ESP_TIMER_TASK);
363     }
364 }
365 
timer_alarm_handler(void * arg)366 static void IRAM_ATTR timer_alarm_handler(void* arg)
367 {
368     k_sem_give(&s_timer_semaphore);
369 }
370 
is_initialized(void)371 static IRAM_ATTR inline bool is_initialized(void)
372 {
373     return init_status;
374 }
375 
esp_timer_init(void)376 esp_err_t esp_timer_init(void)
377 {
378     esp_err_t err;
379     if (is_initialized()) {
380         return ESP_ERR_INVALID_STATE;
381     }
382 
383     int ret = k_sem_init(&s_timer_semaphore, 0, TIMER_EVENT_QUEUE_SIZE);
384     if (ret != 0)
385     {
386         goto out;
387     }
388 
389     k_tid_t tid = k_thread_create(&s_timer_task, timer_task_stack,
390                 4096, (k_thread_entry_t)timer_task, NULL, NULL, NULL,
391                 3, K_INHERIT_PERMS, K_NO_WAIT);
392 
393     if (!tid)
394     {
395         goto out;
396     }
397 
398     k_thread_name_set(tid, "esp_timer");
399 
400     err = esp_timer_impl_init(&timer_alarm_handler);
401     if (err != ESP_OK) {
402         goto out;
403     }
404 
405 #if CONFIG_ESP_TIME_FUNCS_USE_ESP_TIMER
406     // [refactor-todo] this logic, "esp_rtc_get_time_us() - g_startup_time", is also
407     // the weak definition of esp_system_get_time; find a way to remove this duplication.
408     esp_timer_private_advance(esp_rtc_get_time_us() - g_startup_time);
409 #endif
410 
411 	init_status = true;
412 
413 	return ESP_OK;
414 
415 out:
416 	LOG_ERR("could not start esp timer");
417 	k_free(&s_timer_task);
418 	init_status = false;
419 
420     return ESP_ERR_NO_MEM;
421 }
422 
esp_timer_deinit(void)423 esp_err_t esp_timer_deinit(void)
424 {
425     if (!is_initialized()) {
426         return ESP_ERR_INVALID_STATE;
427     }
428 
429     /* Check if there are any active timers */
430     if (!LIST_EMPTY(&s_timers)) {
431         return ESP_ERR_INVALID_STATE;
432     }
433 
434     /* We can only check if there are any timers which are not deleted if
435      * profiling is enabled.
436      */
437 #if WITH_PROFILING
438     if (!LIST_EMPTY(&s_inactive_timers)) {
439         return ESP_ERR_INVALID_STATE;
440     }
441 #endif
442 
443     esp_timer_impl_deinit();
444 
445     k_free(&s_timer_task);
446     init_status = false;
447     return ESP_OK;
448 }
449 
print_timer_info(esp_timer_handle_t t,char ** dst,size_t * dst_size)450 static void print_timer_info(esp_timer_handle_t t, char** dst, size_t* dst_size)
451 {
452 #if WITH_PROFILING
453     size_t cb;
454     // name is optional, might be missed.
455     if (t->name) {
456         cb = snprintf(*dst, *dst_size, "%-12s  ", t->name);
457     } else {
458         cb = snprintf(*dst, *dst_size, "timer@%p  ", t);
459     }
460     cb += snprintf(*dst + cb, *dst_size + cb, "%12lld  %12lld  %9d  %9d  %6d  %12lld\n",
461                     (uint64_t)t->period, t->alarm, t->times_armed,
462                     t->times_triggered, t->times_skipped, t->total_callback_run_time);
463     /* keep this in sync with the format string, used in esp_timer_dump */
464 #define TIMER_INFO_LINE_LEN 90
465 #else
466     size_t cb = snprintf(*dst, *dst_size, "timer@%p  %12lld  %12lld\n", t, (uint64_t)t->period, t->alarm);
467 #define TIMER_INFO_LINE_LEN 46
468 #endif
469     *dst += cb;
470     *dst_size -= cb;
471 }
472 
473 
esp_timer_dump(FILE * stream)474 esp_err_t esp_timer_dump(FILE* stream)
475 {
476     /* Since timer lock is a critical section, we don't want to print directly
477      * to stdout, since that may cause a deadlock if stdout is interrupt-driven
478      * (via the UART driver). Allocate sufficiently large chunk of memory first,
479      * print to it, then dump this memory to stdout.
480      */
481 
482     esp_timer_handle_t it;
483 
484     /* First count the number of timers */
485     size_t timer_count = 0;
486     timer_list_lock();
487     LIST_FOREACH(it, &s_timers, list_entry) {
488         ++timer_count;
489     }
490 #if WITH_PROFILING
491     LIST_FOREACH(it, &s_inactive_timers, list_entry) {
492         ++timer_count;
493     }
494 #endif
495     timer_list_unlock();
496 
497     /* Allocate the memory for this number of timers. Since we have unlocked,
498      * we may find that there are more timers. There's no bulletproof solution
499      * for this (can't allocate from a critical section), but we allocate
500      * slightly more and the output will be truncated if that is not enough.
501      */
502     size_t buf_size = TIMER_INFO_LINE_LEN * (timer_count + 3);
503     char* print_buf = k_calloc(1, buf_size + 1);
504     if (print_buf == NULL) {
505         return ESP_ERR_NO_MEM;
506     }
507 
508     /* Print to the buffer */
509     timer_list_lock();
510     char* pos = print_buf;
511     LIST_FOREACH(it, &s_timers, list_entry) {
512         print_timer_info(it, &pos, &buf_size);
513     }
514 #if WITH_PROFILING
515     LIST_FOREACH(it, &s_inactive_timers, list_entry) {
516         print_timer_info(it, &pos, &buf_size);
517     }
518 #endif
519     timer_list_unlock();
520 
521     /* Print the buffer */
522     fputs(print_buf, stream);
523 
524     k_free(print_buf);
525     return ESP_OK;
526 }
527 
esp_timer_get_next_alarm(void)528 int64_t IRAM_ATTR esp_timer_get_next_alarm(void)
529 {
530     int64_t next_alarm = INT64_MAX;
531     timer_list_lock();
532     esp_timer_handle_t it = LIST_FIRST(&s_timers);
533     if (it) {
534         next_alarm = it->alarm;
535     }
536     timer_list_unlock();
537     return next_alarm;
538 }
539 
540 // Provides strong definition for system time functions relied upon
541 // by core components.
542 #if CONFIG_ESP_TIME_FUNCS_USE_ESP_TIMER
esp_system_get_time(void)543 int64_t IRAM_ATTR esp_system_get_time(void)
544 {
545     return esp_timer_get_time();
546 }
547 
esp_system_get_time_resolution(void)548 uint32_t IRAM_ATTR esp_system_get_time_resolution(void)
549 {
550     return 1000;
551 }
552 #endif
553