2 * Copyright 2004, Broadcom Corporation
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.
10 * Low resolution timer interface linux specific implementation.
20 #define TIMERDBG(fmt, args...) printf("%s: " fmt "\n" , __FUNCTION__ , ## args)
22 #define TIMERDBG(fmt, args...)
27 * POSIX timer support for Linux. Taken from linux_timer.c in upnp
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.
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.
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)
58 #define MS_PER_SEC 1000
59 #define US_PER_SEC 1000000
60 #define US_PER_MS 1000
61 #define UCLOCKS_PER_SEC 1000000
63 typedef void (*event_callback_t
)(timer_t
, int);
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; \
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; \
79 #define ROUNDUP(x,y) ((((x)+(y)-1)/(y))*(y))
81 #define timerroundup(t,g) \
83 if (!timerisset(t)) (t)->tv_usec=1; \
84 if ((t)->tv_sec == 0) (t)->tv_usec=ROUNDUP((t)->tv_usec, g); \
87 typedef long uclock_t
;
90 #define TFLAG_CANCELLED (1<<0)
91 #define TFLAG_DELETED (1<<1)
94 struct timeval it_interval
;
95 struct timeval it_value
;
96 event_callback_t func
;
106 void timer_cancel(timer_t timerid
);
108 static void alarm_handler(int i
);
109 static void check_event_queue();
110 static void print_event_queue();
111 static void check_timer();
113 static int count_queue(struct event
*);
117 void unblock_timer();
119 static struct event
*event_queue
= NULL
;
120 static struct event
*event_freelist
;
121 static uint g_granularity
;
122 static int g_maxevents
= 0;
128 gettimeofday(&tv
, NULL
);
129 return ((tv
.tv_sec
* US_PER_SEC
) + tv
.tv_usec
);
133 void init_event_queue(int n
)
139 event_freelist
= (struct event
*) malloc(n
* sizeof(struct event
));
140 memset(event_freelist
, 0, n
* sizeof(struct event
));
142 for (i
= 0; i
< (n
-1); i
++)
143 event_freelist
[i
].next
= &event_freelist
[i
+1];
145 event_freelist
[i
].next
= NULL
;
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
;
155 signal(SIGALRM
, alarm_handler
);
160 clockid_t clock_id
, /* clock ID (always CLOCK_REALTIME) */
161 struct timespec
* tp
/* where to store current time */
168 n
= gettimeofday(&tv
, NULL
);
169 TIMEVAL_TO_TIMESPEC(&tv
, tp
);
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 */
183 if (clock_id
!= CLOCK_REALTIME
) {
184 TIMERDBG("timer_create can only support clock id CLOCK_REALTIME");
189 if (evp
->sigev_notify
!= SIGEV_SIGNAL
|| evp
->sigev_signo
!= SIGALRM
) {
190 TIMERDBG("timer_create can only support signalled alarms using SIGALRM");
195 event
= event_freelist
;
199 assert(event
!= NULL
);
201 event
->flags
= TFLAG_NONE
;
203 event_freelist
= event
->next
;
208 *pTimer
= (timer_t
) event
;
214 timer_t timerid
/* timer ID */
217 struct event
*event
= (struct event
*) timerid
;
219 if (event
->flags
& TFLAG_DELETED
) {
220 TIMERDBG("Cannot delete a deleted event");
224 timer_cancel(timerid
);
226 event
->flags
|= TFLAG_DELETED
;
228 event
->next
= event_freelist
;
229 event_freelist
= event
;
236 timer_t timerid
, /* timer ID */
237 void (*routine
)(timer_t
, int), /* user routine */
238 int arg
/* user argument */
241 struct event
*event
= (struct event
*) timerid
;
243 assert(routine
!= NULL
);
244 event
->func
= routine
;
253 timer_t timerid
, /* timer ID */
254 int flags
, /* absolute or relative */
255 const struct itimerspec
* value
, /* time to be set */
256 struct itimerspec
* ovalue
/* previous time set (NULL=no result) */
259 struct itimerval itimer
;
260 struct event
*event
= (struct event
*) timerid
;
261 struct event
**ppevent
;
263 TIMESPEC_TO_TIMEVAL(&event
->it_interval
, &value
->it_interval
);
264 TIMESPEC_TO_TIMEVAL(&event
->it_value
, &value
->it_value
);
266 /* if .it_value is zero, the timer is disarmed */
267 if (!timerisset(&event
->it_value
)) {
268 timer_cancel(timerid
);
275 event
->expected_ms
= (event
->it_value
.tv_sec
* MS_PER_SEC
) + (event
->it_value
.tv_usec
/ US_PER_MS
);
276 event
->start
= uclock();
279 TIMERDBG("calling timer_settime with a timer that is already on the queue.");
283 /* We always want to make sure that the event at the head of the
284 queue has a timeout greater than the itimer granularity.
285 Otherwise we end up with the situation that the time remaining
286 on an itimer is greater than the time at the head of the queue
287 in the first place. */
288 timerroundup(&event
->it_value
, g_granularity
);
290 timerclear(&itimer
.it_value
);
291 getitimer(ITIMER_REAL
, &itimer
);
292 if (timerisset(&itimer
.it_value
)) {
293 // reset the top timer to have an interval equal to the remaining interval
294 // when the timer was cancelled.
296 if (timercmp(&(itimer
.it_value
), &(event_queue
->it_value
), >)) {
297 // it is an error if the amount of time remaining is more than the amount of time
298 // requested by the top event.
300 TIMERDBG("timer_settime: TIMER ERROR!");
303 // some portion of the top event has already expired.
304 // Reset the interval of the top event to remaining
305 // time left in that interval.
307 event_queue
->it_value
= itimer
.it_value
;
309 // if we were the earliest timer before now, we are still the earliest timer now.
310 // we do not need to reorder the list.
315 // Now, march down the list, decrementing the new timer by the
316 // current it_value of each event on the queue.
317 ppevent
= &event_queue
;
319 if ( timercmp(&(event
->it_value
), &((*ppevent
)->it_value
), <) ) {
320 // if the proposed event will trigger sooner than the next event
321 // in the queue, we will insert the new event just before the next one.
323 // we also need to adjust the delta value to the next event.
324 timersub(&((*ppevent
)->it_value
), &(event
->it_value
), &((*ppevent
)->it_value
));
327 // subtract the interval of the next event from the proposed interval.
328 timersub(&(event
->it_value
), &((*ppevent
)->it_value
), &(event
->it_value
));
330 ppevent
= &((*ppevent
)->next
);
333 // we have found our proper place in the queue,
334 // link our new event into the pending event queue.
335 event
->next
= *ppevent
;
340 // if our new event ended up at the front of the queue, reissue the timer.
341 if (event
== event_queue
) {
342 timerroundup(&event_queue
->it_value
, g_granularity
);
343 timerclear(&itimer
.it_interval
);
344 itimer
.it_value
= event_queue
->it_value
;
346 // we want to be sure to never turn off the timer completely,
347 // so if the next interval is zero, set it to some small value.
348 if (!timerisset(&(itimer
.it_value
)))
349 itimer
.it_value
= (struct timeval
) { 0, 1 };
351 assert(!timerisset(&itimer
.it_interval
));
352 assert(itimer
.it_value
.tv_sec
> 0 || itimer
.it_value
.tv_usec
>= g_granularity
);
353 assert(event_queue
->it_value
.tv_sec
> 0 || event_queue
->it_value
.tv_usec
>= g_granularity
);
354 setitimer(ITIMER_REAL
, &itimer
, NULL
);
358 event
->flags
&= ~TFLAG_CANCELLED
;
365 static void check_timer()
367 struct itimerval itimer
;
369 getitimer(ITIMER_REAL
, &itimer
);
370 if (timerisset(&itimer
.it_interval
)) {
371 TIMERDBG("ERROR timer interval is set.");
373 if (timercmp(&(itimer
.it_value
), &(event_queue
->it_value
), >)) {
374 TIMERDBG("ERROR timer expires later than top event.");
379 static void check_event_queue()
388 for (p
= event_freelist
; p
; p
= p
->next
)
390 printf("%d free events\n", nfree
);
394 for (event
= event_queue
; event
; event
= event
->next
) {
395 if (i
> g_maxevents
) {
396 TIMERDBG("timer queue looks like it loops back on itself!");
405 /* The original upnp version has this unused function, so I left it in
406 to maintain the resemblance. */
407 static int count_queue(struct event
*event_queue
)
411 for (event
= event_queue
; event
; event
= event
->next
)
417 static void print_event_queue()
422 for (event
= event_queue
; event
; event
= event
->next
) {
423 printf("#%d (0x%x)->0x%x: \t%d sec %d usec\t%p\n",
424 i
++, (unsigned int) event
, (unsigned int) event
->next
, (int) event
->it_value
.tv_sec
, (int) event
->it_value
.tv_usec
, event
->func
);
425 if (i
> g_maxevents
) {
426 printf("...(giving up)\n");
432 // The top element of the event queue must have expired.
433 // Remove that element, run its function, and reset the timer.
434 // if there is no interval, recycle the event structure.
435 static void alarm_handler(int i
)
437 struct event
*event
, **ppevent
;
438 struct itimerval itimer
;
439 struct timeval small_interval
= { 0, g_granularity
/2 };
448 // Loop through the event queue and remove the first event plus any
449 // subsequent events that will expire very soon thereafter (within 'small_interval'}.
452 // remove the top event.
454 event_queue
= event_queue
->next
;
459 actual
= ((end
-event
->start
)/((uclock_t
)UCLOCKS_PER_SEC
/1000));
462 TIMERDBG("expected %d ms actual %d ms", event
->expected_ms
, ((end
-event
->start
)/((uclock_t
)UCLOCKS_PER_SEC
/1000)));
465 // call the event callback function
466 (*(event
->func
))((timer_t
) event
, (int)event
->arg
);
468 /* If the event has been cancelled, do NOT put it back on the queue. */
469 if ( !(event
->flags
& TFLAG_CANCELLED
) ) {
471 // if the event is a recurring event, reset the timer and
472 // find its correct place in the sorted list of events.
474 if (timerisset(&event
->it_interval
)) {
475 // event is recurring...
477 event
->it_value
= event
->it_interval
;
479 event
->expected_ms
= (event
->it_value
.tv_sec
* MS_PER_SEC
) + (event
->it_value
.tv_usec
/ US_PER_MS
);
480 event
->start
= uclock();
482 timerroundup(&event
->it_value
, g_granularity
);
484 // Now, march down the list, decrementing the new timer by the
485 // current delta of each event on the queue.
486 ppevent
= &event_queue
;
488 if ( timercmp(&(event
->it_value
), &((*ppevent
)->it_value
), <) ) {
489 // if the proposed event will trigger sooner than the next event
490 // in the queue, we will insert the new event just before the next one.
492 // we also need to adjust the delta value to the next event.
493 timersub(&((*ppevent
)->it_value
), &(event
->it_value
), &((*ppevent
)->it_value
));
496 timersub(&(event
->it_value
), &((*ppevent
)->it_value
), &(event
->it_value
));
497 ppevent
= &((*ppevent
)->next
);
500 // we have found our proper place in the queue,
501 // link our new event into the pending event queue.
502 event
->next
= *ppevent
;
505 // there is no interval, so recycle the event structure.
506 //timer_delete((timer_t) event);
512 } while (event_queue
&& timercmp(&event_queue
->it_value
, &small_interval
, <));
514 // re-issue the timer...
516 timerroundup(&event_queue
->it_value
, g_granularity
);
518 timerclear(&itimer
.it_interval
);
519 itimer
.it_value
= event_queue
->it_value
;
520 // we want to be sure to never turn off the timer completely,
521 // so if the next interval is zero, set it to some small value.
522 if (!timerisset(&(itimer
.it_value
)))
523 itimer
.it_value
= (struct timeval
) { 0, 1 };
525 setitimer(ITIMER_REAL
, &itimer
, NULL
);
528 TIMERDBG("There are no events in the queue - timer not reset.");
534 static int block_count
= 0;
540 if (block_count
++ == 0) {
542 sigaddset(&set
, SIGALRM
);
543 sigprocmask(SIG_BLOCK
, &set
, NULL
);
551 if (--block_count
== 0) {
553 sigaddset(&set
, SIGALRM
);
554 sigprocmask(SIG_UNBLOCK
, &set
, NULL
);
558 void timer_cancel_all()
560 struct itimerval timeroff
= { { 0, 0 }, { 0, 0} };
562 struct event
**ppevent
;
564 setitimer(ITIMER_REAL
, &timeroff
, NULL
);
566 ppevent
= &event_queue
;
569 *ppevent
= event
->next
;
576 void timer_cancel(timer_t timerid
)
578 struct itimerval itimer
;
579 struct itimerval timeroff
= { { 0, 0 }, { 0, 0} };
580 struct event
*event
= (struct event
*) timerid
;
581 struct event
**ppevent
;
583 if (event
->flags
& TFLAG_CANCELLED
) {
584 TIMERDBG("Cannot cancel a cancelled event");
590 ppevent
= &event_queue
;
592 if ( *ppevent
== event
) {
594 /* RACE CONDITION - if the alarm goes off while we are in
595 this loop, and if the timer we want to cancel is the
596 next to expire, the alarm will end up firing
597 after this routine is complete, causing it to go off early. */
599 /* If the cancelled timer is the next to expire,
600 we need to do something special to clean up correctly. */
601 if (event
== event_queue
&& event
->next
!= NULL
) {
602 timerclear(&itimer
.it_value
);
603 getitimer(ITIMER_REAL
, &itimer
);
605 /* subtract the time that has already passed while waiting for this timer... */
606 timersub(&(event
->it_value
), &(itimer
.it_value
), &(event
->it_value
));
608 /* and add any remainder to the next timer in the list */
609 timeradd(&(event
->next
->it_value
), &(event
->it_value
), &(event
->next
->it_value
));
612 *ppevent
= event
->next
;
616 timerroundup(&event_queue
->it_value
, g_granularity
);
617 timerclear(&itimer
.it_interval
);
618 itimer
.it_value
= event_queue
->it_value
;
620 /* We want to be sure to never turn off the timer
621 completely if there are more events on the queue,
622 so if the next interval is zero, set it to some
625 if (!timerisset(&(itimer
.it_value
)))
626 itimer
.it_value
= (struct timeval
) { 0, 1 };
628 assert(itimer
.it_value
.tv_sec
> 0 || itimer
.it_value
.tv_usec
>= g_granularity
);
629 assert(event_queue
->it_value
.tv_sec
> 0 || event_queue
->it_value
.tv_usec
>= g_granularity
);
630 setitimer(ITIMER_REAL
, &itimer
, NULL
);
633 setitimer(ITIMER_REAL
, &timeroff
, NULL
);
637 ppevent
= &((*ppevent
)->next
);
640 event
->flags
|= TFLAG_CANCELLED
;
646 * timer related headers
648 #include "bcmtimer.h"
651 * locally used global variables and constants
655 * Initialize internal resources used in the timer module. It must be called
656 * before any other timer function calls. The param 'timer_entries' is used
657 * to pre-allocate fixed number of timer entries.
659 int bcm_timer_module_init(int timer_entries
, bcm_timer_module_id
*module_id
)
661 init_event_queue(timer_entries
);
662 *module_id
= (bcm_timer_module_id
)event_freelist
;
667 * Cleanup internal resources used by this timer module. It deletes all
668 * pending timer entries from the backend timer system as well.
670 int bcm_timer_module_cleanup(bcm_timer_module_id module_id
)
676 int bcm_timer_create(bcm_timer_module_id module_id
, bcm_timer_id
*timer_id
)
679 return timer_create(CLOCK_REALTIME
, NULL
, (timer_t
*)timer_id
);
682 int bcm_timer_delete(bcm_timer_id timer_id
)
684 return timer_delete((timer_t
)timer_id
);
687 int bcm_timer_gettime(bcm_timer_id timer_id
, struct itimerspec
*timer_spec
)
692 int bcm_timer_settime(bcm_timer_id timer_id
, const struct itimerspec
*timer_spec
)
694 return timer_settime((timer_t
)timer_id
, 0, timer_spec
, NULL
);
697 int bcm_timer_connect(bcm_timer_id timer_id
, bcm_timer_cb func
, int data
)
699 return timer_connect((timer_t
)timer_id
, (void *)func
, data
);
702 int bcm_timer_cancel(bcm_timer_id timer_id
)
704 timer_cancel((timer_t
)timer_id
);