0402e443784e70d7ae73fdbad1a95fce1cf56fa5
[openwrt.git] / package / openwrt / libshared / linux_timer.c
1 /*
2 * Copyright 2004, Broadcom Corporation
3 * All Rights Reserved.
4 *
5 * THIS SOFTWARE IS OFFERED "AS IS", AND BROADCOM GRANTS NO WARRANTIES OF ANY
6 * KIND, EXPRESS OR IMPLIED, BY STATUTE, COMMUNICATION OR OTHERWISE. BROADCOM
7 * SPECIFICALLY DISCLAIMS ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS
8 * FOR A SPECIFIC PURPOSE OR NONINFRINGEMENT CONCERNING THIS SOFTWARE.
9 *
10 * Low resolution timer interface linux specific implementation.
11 *
12 * $Id$
13 */
14
15 /*
16 * debug facilities
17 */
18 #define TIMER_DEBUG 0
19 #if TIMER_DEBUG
20 #define TIMERDBG(fmt, args...) printf("%s: " fmt "\n" , __FUNCTION__ , ## args)
21 #else
22 #define TIMERDBG(fmt, args...)
23 #endif
24
25
26 /*
27 * POSIX timer support for Linux. Taken from linux_timer.c in upnp
28 */
29
30 #define __USE_GNU
31
32
33 #include <stdlib.h> // for malloc, free, etc.
34 #include <string.h> // for memset, strncasecmp, etc.
35 #include <assert.h> // for assert, of course.
36 #include <signal.h> // for sigemptyset, etc.
37 #include <stdio.h> // for printf, etc.
38 #include <sys/time.h>
39 #include <time.h>
40
41 /* define TIMER_PROFILE to enable code which guages how accurate the timer functions are.
42 For each expiring timer the code will print the expected time interval and the actual time interval.
43 #define TIMER_PROFILE
44 */
45 #undef TIMER_PROFILE
46
47 /*
48 timer_cancel( ) - cancel a timer
49 timer_connect( ) - connect a user routine to the timer signal
50 timer_create( ) - allocate a timer using the specified clock for a timing base (POSIX)
51 timer_delete( ) - remove a previously created timer (POSIX)
52 timer_gettime( ) - get the remaining time before expiration and the reload value (POSIX)
53 timer_getoverrun( ) - return the timer expiration overrun (POSIX)
54 timer_settime( ) - set the time until the next expiration and arm timer (POSIX)
55 nanosleep( ) - suspend the current task until the time interval elapses (POSIX)
56 */
57
58 #define MS_PER_SEC 1000
59 #define US_PER_SEC 1000000
60 #define US_PER_MS 1000
61 #define UCLOCKS_PER_SEC 1000000
62
63 typedef void (*event_callback_t)(timer_t, int);
64
65 #ifndef TIMESPEC_TO_TIMEVAL
66 # define TIMESPEC_TO_TIMEVAL(tv, ts) { \
67 (tv)->tv_sec = (ts)->tv_sec; \
68 (tv)->tv_usec = (ts)->tv_nsec / 1000; \
69 }
70 #endif
71
72 #ifndef TIMEVAL_TO_TIMESPEC
73 # define TIMEVAL_TO_TIMESPEC(tv, ts) { \
74 (ts)->tv_sec = (tv)->tv_sec; \
75 (ts)->tv_nsec = (tv)->tv_usec * 1000; \
76 }
77 #endif
78
79 #define ROUNDUP(x,y) ((((x)+(y)-1)/(y))*(y))
80
81 #define timerroundup(t,g) \
82 do { \
83 if (!timerisset(t)) (t)->tv_usec=1; \
84 if ((t)->tv_sec == 0) (t)->tv_usec=ROUNDUP((t)->tv_usec, g); \
85 } while (0)
86
87 typedef long uclock_t;
88
89 #define TFLAG_NONE 0
90 #define TFLAG_CANCELLED (1<<0)
91 #define TFLAG_DELETED (1<<1)
92
93 struct event {
94 struct timeval it_interval;
95 struct timeval it_value;
96 event_callback_t func;
97 int arg;
98 unsigned short flags;
99 struct event *next;
100 #ifdef TIMER_PROFILE
101 uint expected_ms;
102 uclock_t start;
103 #endif
104 };
105
106 void timer_cancel(timer_t timerid);
107
108 static void alarm_handler(int i);
109 static void check_event_queue();
110 static void print_event_queue();
111 static void check_timer();
112 #if THIS_FINDS_USE
113 static int count_queue(struct event *);
114 #endif
115 static int timer_change_settime(timer_t timer_id, const struct itimerspec *timer_spec);
116 void block_timer();
117 void unblock_timer();
118
119 static struct event *event_queue = NULL;
120 static struct event *event_freelist;
121 static uint g_granularity;
122 static int g_maxevents = 0;
123
124 uclock_t uclock()
125 {
126 struct timeval tv;
127
128 gettimeofday(&tv, NULL);
129 return ((tv.tv_sec * US_PER_SEC) + tv.tv_usec);
130 }
131
132
133 void init_event_queue(int n)
134 {
135 int i;
136 struct itimerval tv;
137
138 g_maxevents = n;
139 event_freelist = (struct event *) malloc(n * sizeof(struct event));
140 memset(event_freelist, 0, n * sizeof(struct event));
141
142 for (i = 0; i < (n-1); i++)
143 event_freelist[i].next = &event_freelist[i+1];
144
145 event_freelist[i].next = NULL;
146
147 tv.it_interval.tv_sec = 0;
148 tv.it_interval.tv_usec = 1;
149 tv.it_value.tv_sec = 0;
150 tv.it_value.tv_usec = 0;
151 setitimer (ITIMER_REAL, &tv, 0);
152 setitimer (ITIMER_REAL, 0, &tv);
153 g_granularity = tv.it_interval.tv_usec;
154
155 signal(SIGALRM, alarm_handler);
156 }
157
158
159 int clock_gettime(
160 clockid_t clock_id, /* clock ID (always CLOCK_REALTIME) */
161 struct timespec * tp /* where to store current time */
162 )
163 {
164 struct timeval tv;
165 int n;
166
167
168 n = gettimeofday(&tv, NULL);
169 TIMEVAL_TO_TIMESPEC(&tv, tp);
170
171 return n;
172 }
173
174
175 int timer_create(
176 clockid_t clock_id, /* clock ID (always CLOCK_REALTIME) */
177 struct sigevent * evp, /* user event handler */
178 timer_t * pTimer /* ptr to return value */
179 )
180 {
181 struct event *event;
182
183 if (clock_id != CLOCK_REALTIME) {
184 TIMERDBG("timer_create can only support clock id CLOCK_REALTIME");
185 exit(1);
186 }
187
188 if (evp != NULL) {
189 if (evp->sigev_notify != SIGEV_SIGNAL || evp->sigev_signo != SIGALRM) {
190 TIMERDBG("timer_create can only support signalled alarms using SIGALRM");
191 exit(1);
192 }
193 }
194
195 event = event_freelist;
196 if (event == NULL) {
197 print_event_queue();
198 }
199 assert(event != NULL);
200
201 event->flags = TFLAG_NONE;
202
203 event_freelist = event->next;
204 event->next = NULL;
205
206 check_event_queue();
207
208 *pTimer = (timer_t) event;
209
210 return 0;
211 }
212
213 int timer_delete(
214 timer_t timerid /* timer ID */
215 )
216 {
217 struct event *event = (struct event *) timerid;
218
219 if (event->flags & TFLAG_DELETED) {
220 TIMERDBG("Cannot delete a deleted event");
221 return 1;
222 }
223
224 timer_cancel(timerid);
225
226 event->flags |= TFLAG_DELETED;
227
228 event->next = event_freelist;
229 event_freelist = event;
230
231 return 0;
232 }
233
234 int timer_connect
235 (
236 timer_t timerid, /* timer ID */
237 void (*routine)(timer_t, int), /* user routine */
238 int arg /* user argument */
239 )
240 {
241 struct event *event = (struct event *) timerid;
242
243 assert(routine != NULL);
244 event->func = routine;
245 event->arg = arg;
246
247 return 0;
248 }
249
250 /*
251 * Please Call this function only from the call back functions of the alarm_handler.
252 * This is just a hack
253 */
254 int timer_change_settime
255 (
256 timer_t timerid, /* timer ID */
257 const struct itimerspec * value /* time to be set */
258 )
259 {
260 struct event *event = (struct event *) timerid;
261
262 TIMESPEC_TO_TIMEVAL(&event->it_interval, &value->it_interval);
263 TIMESPEC_TO_TIMEVAL(&event->it_value, &value->it_value);
264
265 return 1;
266 }
267
268 int timer_settime
269 (
270 timer_t timerid, /* timer ID */
271 int flags, /* absolute or relative */
272 const struct itimerspec * value, /* time to be set */
273 struct itimerspec * ovalue /* previous time set (NULL=no result) */
274 )
275 {
276 struct itimerval itimer;
277 struct event *event = (struct event *) timerid;
278 struct event **ppevent;
279
280 TIMESPEC_TO_TIMEVAL(&event->it_interval, &value->it_interval);
281 TIMESPEC_TO_TIMEVAL(&event->it_value, &value->it_value);
282
283 /* if .it_value is zero, the timer is disarmed */
284 if (!timerisset(&event->it_value)) {
285 timer_cancel(timerid);
286 return 0;
287 }
288
289 block_timer();
290
291 #ifdef TIMER_PROFILE
292 event->expected_ms = (event->it_value.tv_sec * MS_PER_SEC) + (event->it_value.tv_usec / US_PER_MS);
293 event->start = uclock();
294 #endif
295 if (event->next) {
296 TIMERDBG("calling timer_settime with a timer that is already on the queue.");
297 }
298
299
300 /* We always want to make sure that the event at the head of the
301 queue has a timeout greater than the itimer granularity.
302 Otherwise we end up with the situation that the time remaining
303 on an itimer is greater than the time at the head of the queue
304 in the first place. */
305 timerroundup(&event->it_value, g_granularity);
306
307 timerclear(&itimer.it_value);
308 getitimer(ITIMER_REAL, &itimer);
309 if (timerisset(&itimer.it_value)) {
310 // reset the top timer to have an interval equal to the remaining interval
311 // when the timer was cancelled.
312 if (event_queue) {
313 if (timercmp(&(itimer.it_value), &(event_queue->it_value), >)) {
314 // it is an error if the amount of time remaining is more than the amount of time
315 // requested by the top event.
316 //
317 TIMERDBG("timer_settime: TIMER ERROR!");
318
319 } else {
320 // some portion of the top event has already expired.
321 // Reset the interval of the top event to remaining
322 // time left in that interval.
323 //
324 event_queue->it_value = itimer.it_value;
325
326 // if we were the earliest timer before now, we are still the earliest timer now.
327 // we do not need to reorder the list.
328 }
329 }
330 }
331
332 // Now, march down the list, decrementing the new timer by the
333 // current it_value of each event on the queue.
334 ppevent = &event_queue;
335 while (*ppevent) {
336 if ( timercmp(&(event->it_value), &((*ppevent)->it_value), <) ) {
337 // if the proposed event will trigger sooner than the next event
338 // in the queue, we will insert the new event just before the next one.
339 //
340 // we also need to adjust the delta value to the next event.
341 timersub(&((*ppevent)->it_value), &(event->it_value), &((*ppevent)->it_value));
342 break;
343 }
344 // subtract the interval of the next event from the proposed interval.
345 timersub(&(event->it_value), &((*ppevent)->it_value), &(event->it_value));
346
347 ppevent = &((*ppevent)->next);
348 }
349
350 // we have found our proper place in the queue,
351 // link our new event into the pending event queue.
352 event->next = *ppevent;
353 *ppevent = event;
354
355 check_event_queue();
356
357 // if our new event ended up at the front of the queue, reissue the timer.
358 if (event == event_queue) {
359 timerroundup(&event_queue->it_value, g_granularity);
360 timerclear(&itimer.it_interval);
361 itimer.it_value = event_queue->it_value;
362
363 // we want to be sure to never turn off the timer completely,
364 // so if the next interval is zero, set it to some small value.
365 if (!timerisset(&(itimer.it_value)))
366 itimer.it_value = (struct timeval) { 0, 1 };
367
368 assert(!timerisset(&itimer.it_interval));
369 assert(itimer.it_value.tv_sec > 0 || itimer.it_value.tv_usec >= g_granularity);
370 assert(event_queue->it_value.tv_sec > 0 || event_queue->it_value.tv_usec >= g_granularity);
371 setitimer(ITIMER_REAL, &itimer, NULL);
372 check_timer();
373 }
374
375 event->flags &= ~TFLAG_CANCELLED;
376
377 unblock_timer();
378
379 return 0;
380 }
381
382 static void check_timer()
383 {
384 struct itimerval itimer;
385
386 getitimer(ITIMER_REAL, &itimer);
387 if (timerisset(&itimer.it_interval)) {
388 TIMERDBG("ERROR timer interval is set.");
389 }
390 if (timercmp(&(itimer.it_value), &(event_queue->it_value), >)) {
391 TIMERDBG("ERROR timer expires later than top event.");
392 }
393 }
394
395
396 static void check_event_queue()
397 {
398 struct timeval sum;
399 struct event *event;
400 int i = 0;
401
402 #ifdef notdef
403 int nfree = 0;
404 struct event *p;
405 for (p = event_freelist; p; p = p->next)
406 nfree++;
407 printf("%d free events\n", nfree);
408 #endif
409
410 timerclear(&sum);
411 for (event = event_queue; event; event = event->next) {
412 if (i > g_maxevents) {
413 TIMERDBG("timer queue looks like it loops back on itself!");
414 print_event_queue();
415 exit(1);
416 }
417 i++;
418 }
419 }
420
421 #if THIS_FINDS_USE
422 /* The original upnp version has this unused function, so I left it in
423 to maintain the resemblance. */
424 static int count_queue(struct event *event_queue)
425 {
426 struct event *event;
427 int i = 0;
428 for (event = event_queue; event; event = event->next)
429 i++;
430 return i;
431 }
432 #endif
433
434 static void print_event_queue()
435 {
436 struct event *event;
437 int i = 0;
438
439 for (event = event_queue; event; event = event->next) {
440 printf("#%d (0x%x)->0x%x: \t%d sec %d usec\t%p\n",
441 i++, (unsigned int) event, (unsigned int) event->next, (int) event->it_value.tv_sec, (int) event->it_value.tv_usec, event->func);
442 if (i > g_maxevents) {
443 printf("...(giving up)\n");
444 break;
445 }
446 }
447 }
448
449 // The top element of the event queue must have expired.
450 // Remove that element, run its function, and reset the timer.
451 // if there is no interval, recycle the event structure.
452 static void alarm_handler(int i)
453 {
454 struct event *event, **ppevent;
455 struct itimerval itimer;
456 struct timeval small_interval = { 0, g_granularity/2 };
457 #ifdef TIMER_PROFILE
458 uint junk;
459 uclock_t end;
460 uint actual;
461 #endif
462
463 block_timer();
464
465 // Loop through the event queue and remove the first event plus any
466 // subsequent events that will expire very soon thereafter (within 'small_interval'}.
467 //
468 do {
469 // remove the top event.
470 event = event_queue;
471 event_queue = event_queue->next;
472 event->next = NULL;
473
474 #ifdef TIMER_PROFILE
475 end = uclock();
476 actual = ((end-event->start)/((uclock_t)UCLOCKS_PER_SEC/1000));
477 if (actual < 0)
478 junk = end;
479 TIMERDBG("expected %d ms actual %d ms", event->expected_ms, ((end-event->start)/((uclock_t)UCLOCKS_PER_SEC/1000)));
480 #endif
481
482 // call the event callback function
483 (*(event->func))((timer_t) event, (int)event->arg);
484
485 /* If the event has been cancelled, do NOT put it back on the queue. */
486 if ( !(event->flags & TFLAG_CANCELLED) ) {
487
488 // if the event is a recurring event, reset the timer and
489 // find its correct place in the sorted list of events.
490 //
491 if (timerisset(&event->it_interval)) {
492 // event is recurring...
493 //
494 event->it_value = event->it_interval;
495 #ifdef TIMER_PROFILE
496 event->expected_ms = (event->it_value.tv_sec * MS_PER_SEC) + (event->it_value.tv_usec / US_PER_MS);
497 event->start = uclock();
498 #endif
499 timerroundup(&event->it_value, g_granularity);
500
501 // Now, march down the list, decrementing the new timer by the
502 // current delta of each event on the queue.
503 ppevent = &event_queue;
504 while (*ppevent) {
505 if ( timercmp(&(event->it_value), &((*ppevent)->it_value), <) ) {
506 // if the proposed event will trigger sooner than the next event
507 // in the queue, we will insert the new event just before the next one.
508 //
509 // we also need to adjust the delta value to the next event.
510 timersub(&((*ppevent)->it_value), &(event->it_value), &((*ppevent)->it_value));
511 break;
512 }
513 timersub(&(event->it_value), &((*ppevent)->it_value), &(event->it_value));
514 ppevent = &((*ppevent)->next);
515 }
516
517 // we have found our proper place in the queue,
518 // link our new event into the pending event queue.
519 event->next = *ppevent;
520 *ppevent = event;
521 } else {
522 // there is no interval, so recycle the event structure.
523 //timer_delete((timer_t) event);
524 }
525 }
526
527 check_event_queue();
528
529 } while (event_queue && timercmp(&event_queue->it_value, &small_interval, <));
530
531 // re-issue the timer...
532 if (event_queue) {
533 timerroundup(&event_queue->it_value, g_granularity);
534
535 timerclear(&itimer.it_interval);
536 itimer.it_value = event_queue->it_value;
537 // we want to be sure to never turn off the timer completely,
538 // so if the next interval is zero, set it to some small value.
539 if (!timerisset(&(itimer.it_value)))
540 itimer.it_value = (struct timeval) { 0, 1 };
541
542 setitimer(ITIMER_REAL, &itimer, NULL);
543 check_timer();
544 } else {
545 TIMERDBG("There are no events in the queue - timer not reset.");
546 }
547
548 unblock_timer();
549 }
550
551 static int block_count = 0;
552
553 void block_timer()
554 {
555 sigset_t set;
556
557 if (block_count++ == 0) {
558 sigemptyset(&set);
559 sigaddset(&set, SIGALRM);
560 sigprocmask(SIG_BLOCK, &set, NULL);
561 }
562 }
563
564 void unblock_timer()
565 {
566 sigset_t set;
567
568 if (--block_count == 0) {
569 sigemptyset(&set);
570 sigaddset(&set, SIGALRM);
571 sigprocmask(SIG_UNBLOCK, &set, NULL);
572 }
573 }
574
575 void timer_cancel_all()
576 {
577 struct itimerval timeroff = { { 0, 0 }, { 0, 0} };
578 struct event *event;
579 struct event **ppevent;
580
581 setitimer(ITIMER_REAL, &timeroff, NULL);
582
583 ppevent = &event_queue;
584 while (*ppevent) {
585 event = *ppevent;
586 *ppevent = event->next;
587 event->next = NULL;
588 }
589 }
590
591
592
593 void timer_cancel(timer_t timerid)
594 {
595 struct itimerval itimer;
596 struct itimerval timeroff = { { 0, 0 }, { 0, 0} };
597 struct event *event = (struct event *) timerid;
598 struct event **ppevent;
599
600 if (event->flags & TFLAG_CANCELLED) {
601 TIMERDBG("Cannot cancel a cancelled event");
602 return;
603 }
604
605 block_timer();
606
607 ppevent = &event_queue;
608 while (*ppevent) {
609 if ( *ppevent == event ) {
610
611 /* RACE CONDITION - if the alarm goes off while we are in
612 this loop, and if the timer we want to cancel is the
613 next to expire, the alarm will end up firing
614 after this routine is complete, causing it to go off early. */
615
616 /* If the cancelled timer is the next to expire,
617 we need to do something special to clean up correctly. */
618 if (event == event_queue && event->next != NULL) {
619 timerclear(&itimer.it_value);
620 getitimer(ITIMER_REAL, &itimer);
621
622 /* subtract the time that has already passed while waiting for this timer... */
623 timersub(&(event->it_value), &(itimer.it_value), &(event->it_value));
624
625 /* and add any remainder to the next timer in the list */
626 timeradd(&(event->next->it_value), &(event->it_value), &(event->next->it_value));
627 }
628
629 *ppevent = event->next;
630 event->next = NULL;
631
632 if (event_queue) {
633 timerroundup(&event_queue->it_value, g_granularity);
634 timerclear(&itimer.it_interval);
635 itimer.it_value = event_queue->it_value;
636
637 /* We want to be sure to never turn off the timer
638 completely if there are more events on the queue,
639 so if the next interval is zero, set it to some
640 small value. */
641
642 if (!timerisset(&(itimer.it_value)))
643 itimer.it_value = (struct timeval) { 0, 1 };
644
645 assert(itimer.it_value.tv_sec > 0 || itimer.it_value.tv_usec >= g_granularity);
646 assert(event_queue->it_value.tv_sec > 0 || event_queue->it_value.tv_usec >= g_granularity);
647 setitimer(ITIMER_REAL, &itimer, NULL);
648 check_timer();
649 } else {
650 setitimer(ITIMER_REAL, &timeroff, NULL);
651 }
652 break;
653 }
654 ppevent = &((*ppevent)->next);
655 }
656
657 event->flags |= TFLAG_CANCELLED;
658
659 unblock_timer();
660 }
661
662 /*
663 * timer related headers
664 */
665 #include "bcmtimer.h"
666
667 /*
668 * locally used global variables and constants
669 */
670
671 /*
672 * Initialize internal resources used in the timer module. It must be called
673 * before any other timer function calls. The param 'timer_entries' is used
674 * to pre-allocate fixed number of timer entries.
675 */
676 int bcm_timer_module_init(int timer_entries, bcm_timer_module_id *module_id)
677 {
678 init_event_queue(timer_entries);
679 *module_id = (bcm_timer_module_id)event_freelist;
680 return 0;
681 }
682
683 /*
684 * Cleanup internal resources used by this timer module. It deletes all
685 * pending timer entries from the backend timer system as well.
686 */
687 int bcm_timer_module_cleanup(bcm_timer_module_id module_id)
688 {
689 module_id = 0;
690 return 0;
691 }
692
693 /* Enable/Disable timer module */
694 int bcm_timer_module_enable(bcm_timer_module_id module_id, int enable)
695 {
696 if (enable)
697 unblock_timer();
698 else
699 block_timer();
700 return 0;
701 }
702
703 int bcm_timer_create(bcm_timer_module_id module_id, bcm_timer_id *timer_id)
704 {
705 module_id = 0;
706 return timer_create(CLOCK_REALTIME, NULL, (timer_t *)timer_id);
707 }
708
709 int bcm_timer_delete(bcm_timer_id timer_id)
710 {
711 return timer_delete((timer_t)timer_id);
712 }
713
714 int bcm_timer_gettime(bcm_timer_id timer_id, struct itimerspec *timer_spec)
715 {
716 return -1;
717 }
718
719 int bcm_timer_settime(bcm_timer_id timer_id, const struct itimerspec *timer_spec)
720 {
721 return timer_settime((timer_t)timer_id, 0, timer_spec, NULL);
722 }
723
724 int bcm_timer_connect(bcm_timer_id timer_id, bcm_timer_cb func, int data)
725 {
726 return timer_connect((timer_t)timer_id, (void *)func, data);
727 }
728
729 int bcm_timer_cancel(bcm_timer_id timer_id)
730 {
731 timer_cancel((timer_t)timer_id);
732 return 0;
733 }
734 int bcm_timer_change_expirytime(bcm_timer_id timer_id, const struct itimerspec *timer_spec)
735 {
736 timer_change_settime((timer_t)timer_id, timer_spec);
737 return 1;
738 }
This page took 0.074088 seconds and 3 git commands to generate.