2 * Copyright (c) <2014>, Olivier Matz <zer0@droids-corp.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * * Neither the name of the University of California, Berkeley nor the
14 * names of its contributors may be used to endorse or promote products
15 * derived from this software without specific prior written permission.
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
18 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
19 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
20 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
21 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
22 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
24 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 /* Inspired from Intel DPDK rte_timer library */
31 * Copyright (c) <2010>, Intel Corporation
32 * All rights reserved.
34 * Redistribution and use in source and binary forms, with or without
35 * modification, are permitted provided that the following conditions
38 * - Redistributions of source code must retain the above copyright
39 * notice, this list of conditions and the following disclaimer.
41 * - Redistributions in binary form must reproduce the above copyright
42 * notice, this list of conditions and the following disclaimer in
43 * the documentation and/or other materials provided with the
46 * - Neither the name of Intel Corporation nor the names of its
47 * contributors may be used to endorse or promote products derived
48 * from this software without specific prior written permission.
50 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
51 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
52 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
53 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
54 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
55 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
56 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
57 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
58 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
59 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
60 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
61 * OF THE POSSIBILITY OF SUCH DAMAGE.
70 #include <aversive/pgmspace.h>
71 #include <aversive/queue.h>
75 /* allow to browse a list while modifying the current element */
76 #define LIST_FOREACH_SAFE(cur, next, head, field) \
77 for ((cur) = LIST_FIRST((head)), \
78 (next) = ((cur) ? LIST_NEXT((cur), field) : NULL); \
81 (next) = ((cur) ? LIST_NEXT((cur), field) : NULL))
84 /* called with irq locked */
85 #define CALLOUT_STAT_ADD(cm, field, x) do { \
86 cm->stats.field += x; \
89 #define CALLOUT_STAT_ADD(cm, field, x) do { } while(0)
93 #define callout_dprintf_P(fmt, ...) printf_P(PSTR("%s(): " fmt), __FUNCTION__, \
96 #define callout_dprintf_P(...) do { } while (0)
99 /* Initialize a callout manager */
101 callout_mgr_init(struct callout_mgr *cm, get_time_t *get_time)
103 memset(cm, 0, sizeof(*cm));
104 cm->get_time = get_time;
105 LIST_INIT(&cm->sched_list);
108 /* Initialize the timer handle tim for use */
110 callout_init(struct callout *tim, callout_cb_t f, void *arg, uint8_t priority)
112 memset(tim, 0, sizeof(*tim));
115 tim->priority = priority;
119 * Add a timer in the scheduled list (timer must not already be in a list). The
120 * timers are sorted in the list according the expire time (the closer timers
123 * called with irq locked
126 callout_add_in_sched_list(struct callout_mgr *cm, struct callout *tim)
128 struct callout *t, *prev_t;
130 callout_dprintf_P("cm=%p tim=%p\r\n", cm, tim);
131 tim->state = CALLOUT_STATE_SCHEDULED;
134 if (LIST_EMPTY(&cm->sched_list)) {
135 LIST_INSERT_HEAD(&cm->sched_list, tim, next);
139 /* 'tim' expires before first entry */
140 t = LIST_FIRST(&cm->sched_list);
141 if ((int16_t)(tim->expire - t->expire) <= 0) {
142 LIST_INSERT_HEAD(&cm->sched_list, tim, next);
146 /* find an element that will expire after 'tim' */
147 LIST_FOREACH(t, &cm->sched_list, next) {
148 if ((int16_t)(tim->expire - t->expire) <= 0) {
149 LIST_INSERT_BEFORE(t, tim, next);
155 /* not found, insert at the end of the list */
156 LIST_INSERT_AFTER(prev_t, tim, next);
160 * Add a timer in the local expired list (timer must not already be in a
161 * list). The timers are sorted in the list according to the priority (high
164 * called with irq locked
167 callout_add_in_expired_list(struct callout_mgr *cm,
168 struct callout_list *expired_list, struct callout *tim)
170 struct callout *t, *prev_t;
172 (void)cm; /* avoid warning if debug is disabled */
174 callout_dprintf_P("cm=%p tim=%p\r\n", cm, tim);
175 tim->state = CALLOUT_STATE_EXPIRED;
178 if (LIST_EMPTY(expired_list)) {
179 LIST_INSERT_HEAD(expired_list, tim, next);
183 /* 'tim' has a higher prio */
184 t = LIST_FIRST(expired_list);
185 if (tim->priority >= t->priority) {
186 LIST_INSERT_HEAD(expired_list, tim, next);
190 /* find an element that will expire after 'tim' */
191 LIST_FOREACH(t, expired_list, next) {
192 if (tim->priority >= t->priority) {
193 LIST_INSERT_BEFORE(t, tim, next);
199 /* not found, insert at the end of the list */
200 LIST_INSERT_AFTER(prev_t, tim, next);
204 * del from list (timer must be in a list)
207 callout_del(struct callout_mgr *cm, struct callout *tim)
209 (void)cm; /* avoid warning if debug is disabled */
210 callout_dprintf_P("cm=%p tim=%p\r\n", cm, tim);
211 LIST_REMOVE(tim, next);
214 /* Reset and start the timer associated with the timer handle tim */
216 __callout_schedule(struct callout_mgr *cm, struct callout *tim,
221 callout_dprintf_P("cm=%p tim=%p expire=%d\r\n",
225 CALLOUT_STAT_ADD(cm, schedule, 1);
227 /* remove it from list */
228 if (tim->state != CALLOUT_STATE_STOPPED) {
230 if (tim->state == CALLOUT_STATE_SCHEDULED)
231 CALLOUT_STAT_ADD(cm, cur_scheduled, -1);
232 else if (tim->state == CALLOUT_STATE_EXPIRED)
233 CALLOUT_STAT_ADD(cm, cur_expired, -1);
234 if (tim->state == CALLOUT_STATE_RUNNING)
235 CALLOUT_STAT_ADD(cm, cur_running, -1);
237 callout_del(cm, tim);
240 tim->expire = expire;
241 CALLOUT_STAT_ADD(cm, cur_scheduled, 1);
242 callout_add_in_sched_list(cm, tim);
248 /* Reset and start the timer associated with the timer handle tim */
250 callout_schedule(struct callout_mgr *cm, struct callout *tim,
253 return __callout_schedule(cm, tim, cm->get_time() + ticks);
256 /* Reset and start the timer associated with the timer handle tim */
258 callout_reschedule(struct callout_mgr *cm, struct callout *tim,
261 return __callout_schedule(cm, tim, tim->expire + ticks);
264 /* Stop the timer associated with the timer handle tim */
266 callout_stop(struct callout_mgr *cm, struct callout *tim)
270 callout_dprintf_P("cm=%p tim=%p\r\n", cm, tim);
273 if (tim->state != CALLOUT_STATE_STOPPED) {
276 if (tim->state == CALLOUT_STATE_SCHEDULED)
277 CALLOUT_STAT_ADD(cm, cur_scheduled, -1);
278 else if (tim->state == CALLOUT_STATE_EXPIRED)
279 CALLOUT_STAT_ADD(cm, cur_expired, -1);
280 if (tim->state == CALLOUT_STATE_RUNNING)
281 CALLOUT_STAT_ADD(cm, cur_running, -1);
282 CALLOUT_STAT_ADD(cm, stop, 1);
284 /* remove it from list */
285 callout_del(cm, tim);
286 tim->state = CALLOUT_STATE_STOPPED;
291 /* must be called periodically, run all timer that expired */
292 void callout_manage(struct callout_mgr *cm)
294 struct callout_list expired_list;
295 struct callout_list reschedule_list;
296 struct callout *tim, *tim_next;
301 CALLOUT_STAT_ADD(cm, manage, 1);
302 callout_dprintf_P("cm=%p\r\n", cm);
304 /* maximize the number of self-recursions */
305 if (cm->nb_recursion >= CALLOUT_MAX_RECURSION) {
306 CALLOUT_STAT_ADD(cm, max_recursion, 1);
312 LIST_INIT(&expired_list);
313 LIST_INIT(&reschedule_list);
314 cur_time = cm->get_time();
315 old_prio = cm->cur_priority;
317 /* move all expired timers in a local list */
318 LIST_FOREACH_SAFE(tim, tim_next, &cm->sched_list, next) {
320 diff = cur_time - tim->expire;
322 /* check the expiration time (tasks are sorted) */
326 callout_dprintf_P("cm=%p diff=%d\r\n", cm, diff);
328 /* check the priority, if it's too low, inc stats */
329 if (tim->priority <= cm->cur_priority) {
331 CALLOUT_STAT_ADD(cm, delayed, 1);
333 /* reschedule to avoid an overflow */
334 CALLOUT_STAT_ADD(cm, hard_delayed, 1);
335 LIST_REMOVE(tim, next);
336 tim->expire = cur_time;
337 LIST_INSERT_HEAD(&reschedule_list, tim, next);
342 LIST_REMOVE(tim, next);
343 callout_add_in_expired_list(cm, &expired_list, tim);
344 CALLOUT_STAT_ADD(cm, cur_scheduled, -1);
345 CALLOUT_STAT_ADD(cm, cur_expired, 1);
348 /* reschedule hard_delayed timers, this does not happen usually */
349 while (!LIST_EMPTY(&reschedule_list)) {
350 tim = LIST_FIRST(&reschedule_list);
351 LIST_REMOVE(tim, next);
352 callout_add_in_sched_list(cm, tim);
355 /* for each timer of 'expired' list, execute callback */
356 while (!LIST_EMPTY(&expired_list)) {
357 tim = LIST_FIRST(&expired_list);
358 LIST_REMOVE(tim, next);
360 /* execute callback function */
361 CALLOUT_STAT_ADD(cm, cur_expired, -1);
362 CALLOUT_STAT_ADD(cm, cur_running, 1);
363 tim->state = CALLOUT_STATE_RUNNING;
364 cm->cur_priority = tim->priority;
366 tim->f(cm, tim, tim->arg);
370 cm->cur_priority = old_prio;
375 /* set the current priority level */
376 uint8_t callout_mgr_set_prio(struct callout_mgr *cm, uint8_t new_prio)
380 old_prio = cm->cur_priority;
381 if (new_prio <= old_prio)
384 cm->cur_priority = new_prio;
388 /* restore the current priority level */
389 void callout_mgr_restore_prio(struct callout_mgr *cm, uint8_t old_prio)
391 cm->cur_priority = old_prio;
394 /* dump statistics about timers */
395 void callout_dump_stats(struct callout_mgr *cm)
398 printf_P(PSTR("Timer statistics:\r\n"));
399 printf_P(PSTR(" schedule = %"PRIu32"\r\n"), cm->stats.schedule);
400 printf_P(PSTR(" stop = %"PRIu32"\r\n"), cm->stats.stop);
401 printf_P(PSTR(" manage = %"PRIu32"\r\n"), cm->stats.manage);
402 printf_P(PSTR(" max_recursion = %"PRIu32"\r\n"), cm->stats.max_recursion);
403 printf_P(PSTR(" delayed = %"PRIu32"\r\n"), cm->stats.delayed);
404 printf_P(PSTR(" hard_delayed = %"PRIu32"\r\n"), cm->stats.hard_delayed);
406 printf_P(PSTR(" cur_scheduled = %u\r\n"), cm->stats.cur_scheduled);
407 printf_P(PSTR(" cur_expired = %u\r\n"), cm->stats.cur_expired);
408 printf_P(PSTR(" cur_running = %u\r\n"), cm->stats.cur_running);
410 printf_P(PSTR("No timer statistics, CALLOUT_STATS is disabled\r\n"));