diff options
Diffstat (limited to 'firmware/kernel/timeout.c')
-rw-r--r-- | firmware/kernel/timeout.c | 97 |
1 files changed, 97 insertions, 0 deletions
diff --git a/firmware/kernel/timeout.c b/firmware/kernel/timeout.c new file mode 100644 index 0000000000..8039e56ffb --- /dev/null +++ b/firmware/kernel/timeout.c | |||
@@ -0,0 +1,97 @@ | |||
1 | |||
2 | /**************************************************************************** | ||
3 | * Tick-based interval timers/one-shots - be mindful this is not really | ||
4 | * intended for continuous timers but for events that need to run for a short | ||
5 | * time and be cancelled without further software intervention. | ||
6 | ****************************************************************************/ | ||
7 | |||
8 | #include "config.h" | ||
9 | #include "system.h" /* TIME_AFTER */ | ||
10 | #include "kernel.h" | ||
11 | #include "timeout.h" | ||
12 | #include "general.h" | ||
13 | |||
14 | /* list of active timeout events */ | ||
15 | static struct timeout *tmo_list[MAX_NUM_TIMEOUTS+1]; | ||
16 | |||
17 | /* timeout tick task - calls event handlers when they expire | ||
18 | * Event handlers may alter expiration, callback and data during operation. | ||
19 | */ | ||
20 | static void timeout_tick(void) | ||
21 | { | ||
22 | unsigned long tick = current_tick; | ||
23 | struct timeout **p = tmo_list; | ||
24 | struct timeout *curr; | ||
25 | |||
26 | for(curr = *p; curr != NULL; curr = *(++p)) | ||
27 | { | ||
28 | int ticks; | ||
29 | |||
30 | if(TIME_BEFORE(tick, curr->expires)) | ||
31 | continue; | ||
32 | |||
33 | /* this event has expired - call callback */ | ||
34 | ticks = curr->callback(curr); | ||
35 | if(ticks > 0) | ||
36 | { | ||
37 | curr->expires = tick + ticks; /* reload */ | ||
38 | } | ||
39 | else | ||
40 | { | ||
41 | timeout_cancel(curr); /* cancel */ | ||
42 | } | ||
43 | } | ||
44 | } | ||
45 | |||
46 | /* Cancels a timeout callback - can be called from the ISR */ | ||
47 | void timeout_cancel(struct timeout *tmo) | ||
48 | { | ||
49 | int oldlevel = disable_irq_save(); | ||
50 | int rc = remove_array_ptr((void **)tmo_list, tmo); | ||
51 | |||
52 | if(rc >= 0 && *tmo_list == NULL) | ||
53 | { | ||
54 | tick_remove_task(timeout_tick); /* Last one - remove task */ | ||
55 | } | ||
56 | |||
57 | restore_irq(oldlevel); | ||
58 | } | ||
59 | |||
60 | /* Adds a timeout callback - calling with an active timeout resets the | ||
61 | interval - can be called from the ISR */ | ||
62 | void timeout_register(struct timeout *tmo, timeout_cb_type callback, | ||
63 | int ticks, intptr_t data) | ||
64 | { | ||
65 | int oldlevel; | ||
66 | void **arr, **p; | ||
67 | |||
68 | if(tmo == NULL) | ||
69 | return; | ||
70 | |||
71 | oldlevel = disable_irq_save(); | ||
72 | |||
73 | /* See if this one is already registered */ | ||
74 | arr = (void **)tmo_list; | ||
75 | p = find_array_ptr(arr, tmo); | ||
76 | |||
77 | if(p - arr < MAX_NUM_TIMEOUTS) | ||
78 | { | ||
79 | /* Vacancy */ | ||
80 | if(*p == NULL) | ||
81 | { | ||
82 | /* Not present */ | ||
83 | if(*tmo_list == NULL) | ||
84 | { | ||
85 | tick_add_task(timeout_tick); /* First one - add task */ | ||
86 | } | ||
87 | |||
88 | *p = tmo; | ||
89 | } | ||
90 | |||
91 | tmo->callback = callback; | ||
92 | tmo->data = data; | ||
93 | tmo->expires = current_tick + ticks; | ||
94 | } | ||
95 | |||
96 | restore_irq(oldlevel); | ||
97 | } | ||