2 * Copyright (C) 2008, 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.
12 * $Id: linux_timer.c,v 1.14 2008/07/17 09:22:40 Exp $
20 #define TIMER_DEBUG 0 /* Turn off the debug */
22 #define TIMERDBG(fmt, args...) printf("%s: " fmt "\n", __FUNCTION__ , ## args)
24 #define TIMERDBG(fmt, args...)
29 * POSIX timer support for Linux. Taken from linux_timer.c in upnp
35 #include <stdlib.h> // for malloc, free, etc.
36 #include <string.h> // for memset, strncasecmp, etc.
37 #include <assert.h> // for assert, of course.
38 #include <signal.h> // for sigemptyset, etc.
39 #include <stdio.h> // for printf, etc.
43 /* define TIMER_PROFILE to enable code which guages how accurate the timer functions are.
44 * For each expiring timer the code will print the expected time interval and the actual time
46 * #define TIMER_PROFILE
51 timer_cancel( ) - cancel a timer
52 timer_connect( ) - connect a user routine to the timer signal
53 timer_create( ) - allocate a timer using the specified clock for a timing base (POSIX)
54 timer_delete( ) - remove a previously created timer (POSIX)
55 timer_gettime( ) - get the remaining time before expiration and the reload value (POSIX)
56 timer_getoverrun( ) - return the timer expiration overrun (POSIX)
57 timer_settime( ) - set the time until the next expiration and arm timer (POSIX)
58 nanosleep( ) - suspend the current task until the time interval elapses (POSIX)
61 #define MS_PER_SEC 1000 /* 1000ms per second */
62 #define US_PER_SEC 1000000 /* 1000000us per second */
63 #define US_PER_MS 1000 /* 1000us per ms */
64 #define UCLOCKS_PER_SEC 1000000 /* Clock ticks per second */
66 typedef void (*event_callback_t
)(timer_t
, int);
70 static void TIMESPEC_TO_TIMEVAL(struct timeval
*tv
, const struct timespec
*ts
)
72 uint ms
= (ts
->tv_sec
* 1000 + ts
->tv_nsec
/ 1000000) * htclkratio
;
73 tv
->tv_sec
= ms
/ 1000;
74 tv
->tv_usec
= (ms
% 1000) * 1000;
76 static void TIMEVAL_TO_TIMESPEC(const struct timeval
*tv
, struct timespec
*ts
)
78 uint ms
= (tv
->tv_sec
* 1000 + tv
->tv_usec
/ 1000) / htclkratio
;
79 ts
->tv_sec
= ms
/ 1000;
80 ts
->tv_nsec
= (ms
% 1000) * 1000000;
83 #ifndef TIMESPEC_TO_TIMEVAL
84 # define TIMESPEC_TO_TIMEVAL(tv, ts) { \
85 (tv)->tv_sec = (ts)->tv_sec; \
86 (tv)->tv_usec = (ts)->tv_nsec / 1000; \
90 #ifndef TIMEVAL_TO_TIMESPEC
91 # define TIMEVAL_TO_TIMESPEC(tv, ts) { \
92 (ts)->tv_sec = (tv)->tv_sec; \
93 (ts)->tv_nsec = (tv)->tv_usec * 1000; \
98 #define ROUNDUP(x, y) ((((x)+(y)-1)/(y))*(y))
100 #define timerroundup(t, g) \
102 if (!timerisset(t)) (t)->tv_usec = 1; \
103 if ((t)->tv_sec == 0) (t)->tv_usec = ROUNDUP((t)->tv_usec, g); \
106 typedef long uclock_t
;
109 #define TFLAG_CANCELLED (1<<0)
110 #define TFLAG_DELETED (1<<1)
111 #define TFLAG_QUEUED (1<<2)
114 struct timeval it_interval
;
115 struct timeval it_value
;
116 event_callback_t func
;
118 unsigned short flags
;
126 void timer_cancel(timer_t timerid
);
128 static void alarm_handler(int i
);
129 static void check_event_queue();
130 static void print_event_queue();
131 static void check_timer();
133 static int count_queue(struct event
*);
135 static int timer_change_settime(timer_t timer_id
, const struct itimerspec
*timer_spec
);
137 void unblock_timer();
139 static struct event
*event_queue
= NULL
;
140 static struct event
*event_freelist
;
141 static uint g_granularity
;
142 static int g_maxevents
= 0;
149 gettimeofday(&tv
, NULL
);
150 return ((tv
.tv_sec
* US_PER_SEC
) + tv
.tv_usec
);
154 void init_event_queue(int n
)
160 event_freelist
= (struct event
*) malloc(n
* sizeof(struct event
));
161 memset(event_freelist
, 0, n
* sizeof(struct event
));
163 for (i
= 0; i
< (n
-1); i
++)
164 event_freelist
[i
].next
= &event_freelist
[i
+1];
166 event_freelist
[i
].next
= NULL
;
168 tv
.it_interval
.tv_sec
= 0;
169 tv
.it_interval
.tv_usec
= 1;
170 tv
.it_value
.tv_sec
= 0;
171 tv
.it_value
.tv_usec
= 1;
173 signal(SIGALRM
, alarm_handler
);
175 setitimer(ITIMER_REAL
, &tv
, 0);
176 setitimer(ITIMER_REAL
, 0, &tv
);
177 g_granularity
= tv
.it_interval
.tv_usec
;
183 clockid_t clock_id
, /* clock ID (always CLOCK_REALTIME) */
184 struct timespec
* tp
/* where to store current time */
191 n
= gettimeofday(&tv
, NULL
);
192 TIMEVAL_TO_TIMESPEC(&tv
, tp
);
200 clockid_t clock_id
, /* clock ID (always CLOCK_REALTIME) */
201 struct sigevent
* evp
, /* user event handler */
202 timer_t
* pTimer
/* ptr to return value */
207 if (clock_id
!= CLOCK_REALTIME
) {
208 TIMERDBG("timer_create can only support clock id CLOCK_REALTIME");
213 if (evp
->sigev_notify
!= SIGEV_SIGNAL
|| evp
->sigev_signo
!= SIGALRM
) {
214 TIMERDBG("timer_create can only support signalled alarms using SIGALRM");
219 event
= event_freelist
;
223 assert(event
!= NULL
);
225 event
->flags
= TFLAG_NONE
;
227 event_freelist
= event
->next
;
229 event
->flags
&= ~TFLAG_QUEUED
;
233 *pTimer
= (timer_t
) event
;
239 timer_t timerid
/* timer ID */
242 struct event
*event
= (struct event
*) timerid
;
244 if (event
->flags
& TFLAG_DELETED
) {
245 TIMERDBG("Cannot delete a deleted event");
249 timer_cancel(timerid
);
251 event
->flags
|= TFLAG_DELETED
;
253 event
->next
= event_freelist
;
254 event_freelist
= event
;
261 timer_t timerid
, /* timer ID */
262 void (*routine
)(timer_t
, int), /* user routine */
263 int arg
/* user argument */
266 struct event
*event
= (struct event
*) timerid
;
268 assert(routine
!= NULL
);
269 event
->func
= routine
;
276 * Please Call this function only from the call back functions of the alarm_handler.
277 * This is just a hack
279 int timer_change_settime
281 timer_t timerid
, /* timer ID */
282 const struct itimerspec
* value
/* time to be set */
285 struct event
*event
= (struct event
*) timerid
;
287 TIMESPEC_TO_TIMEVAL(&event
->it_interval
, &value
->it_interval
);
288 TIMESPEC_TO_TIMEVAL(&event
->it_value
, &value
->it_value
);
295 timer_t timerid
, /* timer ID */
296 int flags
, /* absolute or relative */
297 const struct itimerspec
* value
, /* time to be set */
298 struct itimerspec
* ovalue
/* previous time set (NULL=no result) */
301 struct itimerval itimer
;
302 struct event
*event
= (struct event
*) timerid
;
303 struct event
**ppevent
;
305 TIMESPEC_TO_TIMEVAL(&event
->it_interval
, &value
->it_interval
);
306 TIMESPEC_TO_TIMEVAL(&event
->it_value
, &value
->it_value
);
308 /* if .it_value is zero, the timer is disarmed */
309 if (!timerisset(&event
->it_value
)) {
310 timer_cancel(timerid
);
317 event
->expected_ms
= (event
->it_value
.tv_sec
* MS_PER_SEC
) + (event
->it_value
.tv_usec
/
319 event
->start
= uclock();
322 TIMERDBG("calling timer_settime with a timer that is already on the queue.");
326 /* We always want to make sure that the event at the head of the
327 * queue has a timeout greater than the itimer granularity.
328 * Otherwise we end up with the situation that the time remaining
329 * on an itimer is greater than the time at the head of the queue
330 * in the first place.
332 timerroundup(&event
->it_value
, g_granularity
);
334 timerclear(&itimer
.it_value
);
335 getitimer(ITIMER_REAL
, &itimer
);
336 if (timerisset(&itimer
.it_value
)) {
337 /* reset the top timer to have an interval equal to the remaining interval
338 * when the timer was cancelled.
342 if (timercmp(&(itimer
.it_value
), &(event_queue
->it_value
), >)) {
343 /* it is an error if the amount of time remaining is more than the
344 * amount of time requested by the top event.
346 TIMERDBG("timer_settime: TIMER ERROR!");
349 /* some portion of the top event has already expired.
350 * Reset the interval of the top event to remaining
351 * time left in that interval.
353 event_queue
->it_value
= itimer
.it_value
;
355 /* if we were the earliest timer before now, we are still the
356 * earliest timer now. we do not need to reorder the list.
362 /* Now, march down the list, decrementing the new timer by the
363 * current it_value of each event on the queue.
365 ppevent
= &event_queue
;
368 if (timercmp(&(event
->it_value
), &((*ppevent
)->it_value
), <)) {
369 /* if the proposed event will trigger sooner than the next event
370 * in the queue, we will insert the new event just before the next one.
371 * we also need to adjust the delta value to the next event.
373 timersub(&((*ppevent
)->it_value
), &(event
->it_value
),
374 &((*ppevent
)->it_value
));
377 /* subtract the interval of the next event from the proposed interval. */
378 timersub(&(event
->it_value
), &((*ppevent
)->it_value
), &(event
->it_value
));
380 ppevent
= &((*ppevent
)->next
);
383 /* we have found our proper place in the queue, */
384 /* link our new event into the pending event queue. */
385 event
->next
= *ppevent
;
390 /* if our new event ended up at the front of the queue, reissue the timer. */
391 if (event
== event_queue
) {
392 timerroundup(&event_queue
->it_value
, g_granularity
);
393 timerclear(&itimer
.it_interval
);
394 itimer
.it_value
= event_queue
->it_value
;
396 /* we want to be sure to never turn off the timer completely, */
397 /* so if the next interval is zero, set it to some small value. */
398 if (!timerisset(&(itimer
.it_value
)))
399 itimer
.it_value
= (struct timeval
) { 0, 1 };
401 assert(!timerisset(&itimer
.it_interval
));
402 assert(itimer
.it_value
.tv_sec
> 0 || itimer
.it_value
.tv_usec
>= g_granularity
);
403 assert(event_queue
->it_value
.tv_sec
> 0 || event_queue
->it_value
.tv_usec
>=
405 setitimer(ITIMER_REAL
, &itimer
, NULL
);
409 event
->flags
&= ~TFLAG_CANCELLED
;
410 event
->flags
|= TFLAG_QUEUED
;
417 static void check_timer()
419 struct itimerval itimer
;
421 getitimer(ITIMER_REAL
, &itimer
);
422 if (timerisset(&itimer
.it_interval
)) {
423 TIMERDBG("ERROR timer interval is set.");
426 if (timercmp(&(itimer
.it_value
), &(event_queue
->it_value
), >)) {
427 TIMERDBG("ERROR timer expires later than top event.");
432 static void check_event_queue()
441 for (p
= event_freelist
; p
; p
= p
->next
)
443 printf("%d free events\n", nfree
);
447 for (event
= event_queue
; event
; event
= event
->next
) {
448 if (i
> g_maxevents
) {
449 TIMERDBG("timer queue looks like it loops back on itself!");
458 /* The original upnp version has this unused function, so I left it in
459 * to maintain the resemblance.
461 static int count_queue(struct event
*event_queue
)
465 for (event
= event_queue
; event
; event
= event
->next
)
471 static void print_event_queue()
476 for (event
= event_queue
; event
; event
= event
->next
) {
477 printf("#%d (0x%x)->0x%x: \t%d sec %d usec\t%p\n",
478 i
++, (unsigned int) event
, (unsigned int) event
->next
, (int)
479 event
->it_value
.tv_sec
,
480 (int) event
->it_value
.tv_usec
, event
->func
);
481 if (i
> g_maxevents
) {
482 printf("...(giving up)\n");
488 /* The top element of the event queue must have expired. */
489 /* Remove that element, run its function, and reset the timer. */
490 /* if there is no interval, recycle the event structure. */
491 static void alarm_handler(int i
)
493 struct event
*event
, **ppevent
;
494 struct itimerval itimer
;
495 struct timeval small_interval
= { 0, g_granularity
/2 };
504 /* Loop through the event queue and remove the first event plus any */
505 /* subsequent events that will expire very soon thereafter (within 'small_interval'}. */
508 /* remove the top event. */
510 event_queue
= event_queue
->next
;
515 actual
= ((end
-event
->start
)/((uclock_t
)UCLOCKS_PER_SEC
/1000));
518 TIMERDBG("expected %d ms actual %d ms", event
->expected_ms
,
519 ((end
-event
->start
)/((uclock_t
)UCLOCKS_PER_SEC
/1000)));
522 /* call the event callback function */
523 (*(event
->func
))((timer_t
) event
, (int)event
->arg
);
525 /* If the event has been cancelled, do NOT put it back on the queue. */
526 /* Check for TFLAG_QUEUED is to avoid pathologic case, when after
527 * dequeueing event handler deletes its own timer and allocates new one
528 * which (at least in some cases) gets the same pointer and thus its
529 * 'flags' will be rewritten, most notably TFLAG_CANCELLED, and, to
530 * complete the disaster, it will be queued. alarm_handler tries to
531 * enqueue 'event' (which is on the same memory position as newly
532 * allocated timer), which results in queueing the same pointer once
533 * more. And this way, loop in event queue is created. */
534 if (!(event
->flags
& TFLAG_CANCELLED
) && !(event
->flags
& TFLAG_QUEUED
)) {
536 /* if the event is a recurring event, reset the timer and
537 * find its correct place in the sorted list of events.
539 if (timerisset(&event
->it_interval
)) {
540 /* event is recurring... */
541 event
->it_value
= event
->it_interval
;
543 event
->expected_ms
= (event
->it_value
.tv_sec
* MS_PER_SEC
) +
544 (event
->it_value
.tv_usec
/ US_PER_MS
);
545 event
->start
= uclock();
547 timerroundup(&event
->it_value
, g_granularity
);
549 /* Now, march down the list, decrementing the new timer by the */
550 /* current delta of each event on the queue. */
551 ppevent
= &event_queue
;
553 if (timercmp(&(event
->it_value
), &((*ppevent
)->it_value
),
556 /* if the proposed event will trigger sooner than
558 * in the queue, we will insert the new event just
559 * before the next one.
560 * we also need to adjust the delta value to the
563 timersub(&((*ppevent
)->it_value
),
565 &((*ppevent
)->it_value
));
568 timersub(&(event
->it_value
), &((*ppevent
)->it_value
),
570 ppevent
= &((*ppevent
)->next
);
573 /* we have found our proper place in the queue, */
574 /* link our new event into the pending event queue. */
575 event
->next
= *ppevent
;
577 event
->flags
|= TFLAG_QUEUED
;
579 /* there is no interval, so recycle the event structure.
580 * timer_delete((timer_t) event);
588 } while (event_queue
&& timercmp(&event_queue
->it_value
, &small_interval
, <));
590 /* re-issue the timer... */
592 timerroundup(&event_queue
->it_value
, g_granularity
);
594 timerclear(&itimer
.it_interval
);
595 itimer
.it_value
= event_queue
->it_value
;
596 /* we want to be sure to never turn off the timer completely, */
597 /* so if the next interval is zero, set it to some small value. */
598 if (!timerisset(&(itimer
.it_value
)))
599 itimer
.it_value
= (struct timeval
) { 0, 1 };
601 setitimer(ITIMER_REAL
, &itimer
, NULL
);
604 TIMERDBG("There are no events in the queue - timer not reset.");
610 static int block_count
= 0;
616 if (block_count
++ == 0) {
618 sigaddset(&set
, SIGALRM
);
619 sigprocmask(SIG_BLOCK
, &set
, NULL
);
627 if (--block_count
== 0) {
629 sigaddset(&set
, SIGALRM
);
630 sigprocmask(SIG_UNBLOCK
, &set
, NULL
);
634 void timer_cancel_all()
636 struct itimerval timeroff
= { { 0, 0 }, { 0, 0} };
638 struct event
**ppevent
;
640 setitimer(ITIMER_REAL
, &timeroff
, NULL
);
642 ppevent
= &event_queue
;
645 *ppevent
= event
->next
;
651 void timer_cancel(timer_t timerid
)
653 struct itimerval itimer
;
654 struct itimerval timeroff
= { { 0, 0 }, { 0, 0} };
655 struct event
*event
= (struct event
*) timerid
;
656 struct event
**ppevent
;
658 if (event
->flags
& TFLAG_CANCELLED
) {
659 TIMERDBG("Cannot cancel a cancelled event");
665 ppevent
= &event_queue
;
667 if (*ppevent
== event
) {
669 /* RACE CONDITION - if the alarm goes off while we are in
670 * this loop, and if the timer we want to cancel is the
671 * next to expire, the alarm will end up firing
672 * after this routine is complete, causing it to go off early.
675 /* If the cancelled timer is the next to expire,
676 * we need to do something special to clean up correctly.
678 if (event
== event_queue
&& event
->next
!= NULL
) {
679 timerclear(&itimer
.it_value
);
680 getitimer(ITIMER_REAL
, &itimer
);
682 /* subtract the time that has already passed while waiting for this
685 timersub(&(event
->it_value
), &(itimer
.it_value
),
688 /* and add any remainder to the next timer in the list */
689 timeradd(&(event
->next
->it_value
), &(event
->it_value
),
690 &(event
->next
->it_value
));
693 *ppevent
= event
->next
;
697 timerroundup(&event_queue
->it_value
, g_granularity
);
698 timerclear(&itimer
.it_interval
);
699 itimer
.it_value
= event_queue
->it_value
;
701 /* We want to be sure to never turn off the timer
702 * completely if there are more events on the queue,
703 * so if the next interval is zero, set it to some
707 if (!timerisset(&(itimer
.it_value
)))
708 itimer
.it_value
= (struct timeval
) { 0, 1 };
710 assert(itimer
.it_value
.tv_sec
> 0 || itimer
.it_value
.tv_usec
>=
712 assert(event_queue
->it_value
.tv_sec
> 0 ||
713 event_queue
->it_value
.tv_usec
>=
715 setitimer(ITIMER_REAL
, &itimer
, NULL
);
718 setitimer(ITIMER_REAL
, &timeroff
, NULL
);
722 ppevent
= &((*ppevent
)->next
);
725 event
->flags
|= TFLAG_CANCELLED
;
731 * timer related headers
733 #include "bcmtimer.h"
736 * locally used global variables and constants
740 * Initialize internal resources used in the timer module. It must be called
741 * before any other timer function calls. The param 'timer_entries' is used
742 * to pre-allocate fixed number of timer entries.
744 int bcm_timer_module_init(int timer_entries
, bcm_timer_module_id
*module_id
)
746 init_event_queue(timer_entries
);
747 *module_id
= (bcm_timer_module_id
)event_freelist
;
752 * Cleanup internal resources used by this timer module. It deletes all
753 * pending timer entries from the backend timer system as well.
755 int bcm_timer_module_cleanup(bcm_timer_module_id module_id
)
761 /* Enable/Disable timer module */
762 int bcm_timer_module_enable(bcm_timer_module_id module_id
, int enable
)
771 int bcm_timer_create(bcm_timer_module_id module_id
, bcm_timer_id
*timer_id
)
774 return timer_create(CLOCK_REALTIME
, NULL
, (timer_t
*)timer_id
);
777 int bcm_timer_delete(bcm_timer_id timer_id
)
779 return timer_delete((timer_t
)timer_id
);
782 int bcm_timer_gettime(bcm_timer_id timer_id
, struct itimerspec
*timer_spec
)
787 int bcm_timer_settime(bcm_timer_id timer_id
, const struct itimerspec
*timer_spec
)
789 return timer_settime((timer_t
)timer_id
, 0, timer_spec
, NULL
);
792 int bcm_timer_connect(bcm_timer_id timer_id
, bcm_timer_cb func
, int data
)
794 return timer_connect((timer_t
)timer_id
, (void *)func
, data
);
797 int bcm_timer_cancel(bcm_timer_id timer_id
)
799 timer_cancel((timer_t
)timer_id
);
802 int bcm_timer_change_expirytime(bcm_timer_id timer_id
, const struct itimerspec
*timer_spec
)
804 timer_change_settime((timer_t
)timer_id
, timer_spec
);