blob: c041d49dfd798163ffd851b38cfa0249ecd359c7 [file] [log] [blame]
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -07001/*
Travis Geiselbrecht671cb792009-06-28 11:27:48 -07002 * Copyright (c) 2008-2009 Travis Geiselbrecht
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -07003 *
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files
6 * (the "Software"), to deal in the Software without restriction,
7 * including without limitation the rights to use, copy, modify, merge,
8 * publish, distribute, sublicense, and/or sell copies of the Software,
9 * and to permit persons to whom the Software is furnished to do so,
10 * subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 */
23#include <debug.h>
24#include <list.h>
25#include <kernel/thread.h>
26#include <kernel/timer.h>
27#include <platform/timer.h>
28#include <platform.h>
29
30static struct list_node timer_queue;
31
Travis Geiselbrecht671cb792009-06-28 11:27:48 -070032static enum handler_return timer_tick(void *arg, time_t now);
33
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -070034void timer_initialize(timer_t *timer)
35{
36 timer->magic = TIMER_MAGIC;
37 list_clear_node(&timer->node);
38 timer->scheduled_time = 0;
39 timer->periodic_time = 0;
40 timer->callback = 0;
41 timer->arg = 0;
42}
43
44static void insert_timer_in_queue(timer_t *timer)
45{
46 timer_t *entry;
47
Travis Geiselbrecht671cb792009-06-28 11:27:48 -070048// TRACEF("timer %p, scheduled %d, periodic %d\n", timer, timer->scheduled_time, timer->periodic_time);
49
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -070050 list_for_every_entry(&timer_queue, entry, timer_t, node) {
Travis Geiselbrecht671cb792009-06-28 11:27:48 -070051 if (TIME_GT(entry->scheduled_time, timer->scheduled_time)) {
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -070052 list_add_before(&entry->node, &timer->node);
53 return;
54 }
55 }
56
57 /* walked off the end of the list */
58 list_add_tail(&timer_queue, &timer->node);
59}
60
Travis Geiselbrecht671cb792009-06-28 11:27:48 -070061static void timer_set(timer_t *timer, time_t delay, time_t period, timer_callback callback, void *arg)
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -070062{
63 time_t now;
64
Travis Geiselbrecht671cb792009-06-28 11:27:48 -070065// TRACEF("timer %p, delay %d, period %d, callback %p, arg %p, now %d\n", timer, delay, period, callback, arg);
Travis Geiselbrecht887061f2008-09-05 01:47:07 -070066
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -070067 DEBUG_ASSERT(timer->magic == TIMER_MAGIC);
68
69 if (list_in_list(&timer->node)) {
70 panic("timer %p already in list\n", timer);
71 }
72
73 now = current_time();
74 timer->scheduled_time = now + delay;
Travis Geiselbrecht671cb792009-06-28 11:27:48 -070075 timer->periodic_time = period;
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -070076 timer->callback = callback;
77 timer->arg = arg;
78
Travis Geiselbrecht887061f2008-09-05 01:47:07 -070079// TRACEF("scheduled time %u\n", timer->scheduled_time);
80
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -070081 enter_critical_section();
82
83 insert_timer_in_queue(timer);
84
Travis Geiselbrecht671cb792009-06-28 11:27:48 -070085#if PLATFORM_HAS_DYNAMIC_TIMER
86 if (list_peek_head_type(&timer_queue, timer_t, node) == timer) {
87 /* we just modified the head of the timer queue */
88// TRACEF("setting new timer for %u msecs\n", (uint)delay);
89 platform_set_oneshot_timer(timer_tick, NULL, delay);
90 }
91#endif
92
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -070093 exit_critical_section();
94}
95
Travis Geiselbrecht671cb792009-06-28 11:27:48 -070096void timer_set_oneshot(timer_t *timer, time_t delay, timer_callback callback, void *arg)
97{
98 if (delay == 0)
99 delay = 1;
100 timer_set(timer, delay, 0, callback, arg);
101}
102
103void timer_set_periodic(timer_t *timer, time_t period, timer_callback callback, void *arg)
104{
105 if (period == 0)
106 period = 1;
107 timer_set(timer, period, period, callback, arg);
108}
109
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700110void timer_cancel(timer_t *timer)
111{
112 DEBUG_ASSERT(timer->magic == TIMER_MAGIC);
113
114 enter_critical_section();
115
Travis Geiselbrecht671cb792009-06-28 11:27:48 -0700116#if PLATFORM_HAS_DYNAMIC_TIMER
117 timer_t *oldhead = list_peek_head_type(&timer_queue, timer_t, node);
118#endif
119
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700120 if (list_in_list(&timer->node))
121 list_delete(&timer->node);
122
Travis Geiselbrecht671cb792009-06-28 11:27:48 -0700123 /* to keep it from being reinserted into the queue if called from
124 * periodic timer callback.
125 */
126 timer->periodic_time = 0;
127 timer->callback = NULL;
128 timer->arg = NULL;
129
130#if PLATFORM_HAS_DYNAMIC_TIMER
131 /* see if we've just modified the head of the timer queue */
132 timer_t *newhead = list_peek_head_type(&timer_queue, timer_t, node);
133 if (newhead == NULL) {
134// TRACEF("clearing old hw timer, nothing in the queue\n");
135 platform_stop_timer();
136 } else if (newhead != oldhead) {
137 time_t delay;
138 time_t now = current_time();
139
140 if (TIME_LT(newhead->scheduled_time, now))
141 delay = 0;
142 else
143 delay = newhead->scheduled_time - now;
144
145// TRACEF("setting new timer to %d\n", delay);
146 platform_set_oneshot_timer(timer_tick, NULL, delay);
147 }
148#endif
149
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700150 exit_critical_section();
151}
152
153/* called at interrupt time to process any pending timers */
154static enum handler_return timer_tick(void *arg, time_t now)
155{
156 timer_t *timer;
157 enum handler_return ret = INT_NO_RESCHEDULE;
158
159#if THREAD_STATS
160 thread_stats.timer_ints++;
161#endif
162
Travis Geiselbrecht671cb792009-06-28 11:27:48 -0700163// TRACEF("now %d\n", now);
164
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700165 for (;;) {
166 /* see if there's an event to process */
167 timer = list_peek_head_type(&timer_queue, timer_t, node);
Travis Geiselbrecht671cb792009-06-28 11:27:48 -0700168 if (likely(!timer || TIME_LT(now, timer->scheduled_time)))
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700169 break;
170
171 /* process it */
172 DEBUG_ASSERT(timer->magic == TIMER_MAGIC);
173 list_delete(&timer->node);
174// timer = list_remove_head_type(&timer_queue, timer_t, node);
175// ASSERT(timer);
176
Travis Geiselbrecht671cb792009-06-28 11:27:48 -0700177// TRACEF("dequeued timer %p, scheduled %d periodic %d\n", timer, timer->scheduled_time, timer->periodic_time);
178
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700179#if THREAD_STATS
180 thread_stats.timers++;
181#endif
182
Travis Geiselbrecht671cb792009-06-28 11:27:48 -0700183 bool periodic = timer->periodic_time > 0;
184
185// TRACEF("timer %p firing callback %p, arg %p\n", timer, timer->callback, timer->arg);
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700186 if (timer->callback(timer, now, timer->arg) == INT_RESCHEDULE)
187 ret = INT_RESCHEDULE;
Travis Geiselbrecht671cb792009-06-28 11:27:48 -0700188
189 /* if it was a periodic timer and it hasn't been requeued
190 * by the callback put it back in the list
191 */
192 if (periodic && !list_in_list(&timer->node) && timer->periodic_time > 0) {
193// TRACEF("periodic timer, period %u\n", (uint)timer->periodic_time);
194 timer->scheduled_time = now + timer->periodic_time;
195 insert_timer_in_queue(timer);
196 }
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700197 }
198
Travis Geiselbrecht671cb792009-06-28 11:27:48 -0700199#if PLATFORM_HAS_DYNAMIC_TIMER
200 /* reset the timer to the next event */
201 timer = list_peek_head_type(&timer_queue, timer_t, node);
202 if (timer) {
203 /* has to be the case or it would have fired already */
204 ASSERT(TIME_GT(timer->scheduled_time, now));
205
206 time_t delay = timer->scheduled_time - now;
207
208// TRACEF("setting new timer for %u msecs for event %p\n", (uint)delay, timer);
209 platform_set_oneshot_timer(timer_tick, NULL, delay);
210 }
211#else
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700212 /* let the scheduler have a shot to do quantum expiration, etc */
Travis Geiselbrecht671cb792009-06-28 11:27:48 -0700213 /* in case of dynamic timer, the scheduler will set up a periodic timer */
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700214 if (thread_timer_tick() == INT_RESCHEDULE)
215 ret = INT_RESCHEDULE;
Travis Geiselbrecht671cb792009-06-28 11:27:48 -0700216#endif
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700217
Travis Geiselbrecht671cb792009-06-28 11:27:48 -0700218 // XXX fix this, should return ret
Travis Geiselbrecht1d0df692008-09-01 02:26:09 -0700219 return INT_RESCHEDULE;
220}
221
222void timer_init(void)
223{
224 list_initialize(&timer_queue);
225
226 /* register for a periodic timer tick */
227 platform_set_periodic_timer(timer_tick, NULL, 10); /* 10ms */
228}
229
230