1/* 2 * Copyright (C) 2010, Broadcom Corporation. All Rights Reserved. 3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 11 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 13 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 * 16 * Low resolution timer interface linux specific implementation. 17 * 18 * $Id: bsd_timer.c,v 1.2 2008/11/12 06:54:21 Exp $ 19 */ 20 21/* 22* debug facilities 23*/ 24#define TIMER_DEBUG 0 /* Turn off the debug */ 25#if TIMER_DEBUG 26#define TIMERDBG(fmt, args...) printf("%s: " fmt "\n", __FUNCTION__ , ## args) 27#else 28#define TIMERDBG(fmt, args...) 29#endif 30 31 32/* 33 * POSIX timer support for Linux. Taken from linux_timer.c in upnp 34 */ 35 36#define __USE_GNU 37 38 39#include <stdlib.h> // for malloc, free, etc. 40#include <string.h> // for memset, strncasecmp, etc. 41#include <assert.h> // for assert, of course. 42#include <signal.h> // for sigemptyset, etc. 43#include <stdio.h> // for printf, etc. 44#include <sys/time.h> 45#include <time.h> 46 47/* define TIMER_PROFILE to enable code which guages how accurate the timer functions are. 48 * For each expiring timer the code will print the expected time interval and the actual time 49 * interval. 50 * #define TIMER_PROFILE 51 */ 52#undef TIMER_PROFILE 53 54/* 55timer_cancel( ) - cancel a timer 56timer_connect( ) - connect a user routine to the timer signal 57timer_create( ) - allocate a timer using the specified clock for a timing base (POSIX) 58timer_delete( ) - remove a previously created timer (POSIX) 59timer_gettime( ) - get the remaining time before expiration and the reload value (POSIX) 60timer_getoverrun( ) - return the timer expiration overrun (POSIX) 61timer_settime( ) - set the time until the next expiration and arm timer (POSIX) 62nanosleep( ) - suspend the current task until the time interval elapses (POSIX) 63*/ 64 65#define MS_PER_SEC 1000 /* 1000ms per second */ 66#define US_PER_SEC 1000000 /* 1000000us per second */ 67#define US_PER_MS 1000 /* 1000us per ms */ 68#define UCLOCKS_PER_SEC 1000000 /* Clock ticks per second */ 69 70typedef void (*event_callback_t)(timer_t, int); 71 72#ifdef BCMQT 73uint htclkratio = 50; 74static void TIMESPEC_TO_TIMEVAL(struct timeval *tv, const struct timespec *ts) 75{ 76 uint ms = (ts->tv_sec * 1000 + ts->tv_nsec / 1000000) * htclkratio; 77 tv->tv_sec = ms / 1000; 78 tv->tv_usec = (ms % 1000) * 1000; 79} 80static void TIMEVAL_TO_TIMESPEC(const struct timeval *tv, struct timespec *ts) 81{ 82 uint ms = (tv->tv_sec * 1000 + tv->tv_usec / 1000) / htclkratio; 83 ts->tv_sec = ms / 1000; 84 ts->tv_nsec = (ms % 1000) * 1000000; 85} 86#else /* BCMQT */ 87#ifndef TIMESPEC_TO_TIMEVAL 88# define TIMESPEC_TO_TIMEVAL(tv, ts) { \ 89 (tv)->tv_sec = (ts)->tv_sec; \ 90 (tv)->tv_usec = (ts)->tv_nsec / 1000; \ 91} 92#endif 93 94#ifndef TIMEVAL_TO_TIMESPEC 95# define TIMEVAL_TO_TIMESPEC(tv, ts) { \ 96 (ts)->tv_sec = (tv)->tv_sec; \ 97 (ts)->tv_nsec = (tv)->tv_usec * 1000; \ 98} 99#endif 100#endif /* !BCMQT */ 101 102#define ROUNDUP(x, y) ((((x)+(y)-1)/(y))*(y)) 103 104#define timerroundup(t, g) \ 105 do { \ 106 if (!timerisset(t)) (t)->tv_usec = 1; \ 107 if ((t)->tv_sec == 0) (t)->tv_usec = ROUNDUP((t)->tv_usec, g); \ 108 } while (0) 109 110typedef long uclock_t; 111 112#define TFLAG_NONE 0 113#define TFLAG_CANCELLED (1<<0) 114#define TFLAG_DELETED (1<<1) 115 116struct event { 117 struct timeval it_interval; 118 struct timeval it_value; 119 event_callback_t func; 120 int arg; 121 unsigned short flags; 122 struct event *next; 123#ifdef TIMER_PROFILE 124 uint expected_ms; 125 uclock_t start; 126#endif 127}; 128 129void timer_cancel(timer_t timerid); 130 131static void alarm_handler(int i); 132static void check_event_queue(); 133static void print_event_queue(); 134static void check_timer(); 135#if THIS_FINDS_USE 136static int count_queue(struct event *); 137#endif 138static int timer_change_settime(timer_t timer_id, const struct itimerspec *timer_spec); 139void block_timer(); 140void unblock_timer(); 141 142static struct event *event_queue = NULL; 143static struct event *event_freelist; 144static uint g_granularity; 145static int g_maxevents = 0; 146 147uclock_t uclock() 148{ 149 struct timeval tv; 150 151 gettimeofday(&tv, NULL); 152 return ((tv.tv_sec * US_PER_SEC) + tv.tv_usec); 153} 154 155 156void init_event_queue(int n) 157{ 158 int i; 159 struct itimerval tv; 160 161 g_maxevents = n; 162 event_freelist = (struct event *) malloc(n * sizeof(struct event)); 163 memset(event_freelist, 0, n * sizeof(struct event)); 164 165 for (i = 0; i < (n-1); i++) 166 event_freelist[i].next = &event_freelist[i+1]; 167 168 event_freelist[i].next = NULL; 169 170 tv.it_interval.tv_sec = 0; 171 tv.it_interval.tv_usec = 1; 172 tv.it_value.tv_sec = 0; 173 tv.it_value.tv_usec = 0; 174 setitimer(ITIMER_REAL, &tv, 0); 175 setitimer(ITIMER_REAL, 0, &tv); 176 g_granularity = tv.it_interval.tv_usec; 177 178 signal(SIGALRM, alarm_handler); 179} 180 181int timer_connect 182( 183 timer_t timerid, /* timer ID */ 184 void (*routine)(timer_t, int), /* user routine */ 185 int arg /* user argument */ 186) 187{ 188 struct event *event = (struct event *) timerid; 189 190 assert(routine != NULL); 191 event->func = routine; 192 event->arg = arg; 193 194 return 0; 195} 196 197/* 198 * Please Call this function only from the call back functions of the alarm_handler. 199 * This is just a hack 200 */ 201int timer_change_settime 202( 203 timer_t timerid, /* timer ID */ 204 const struct itimerspec * value /* time to be set */ 205) 206{ 207 struct event *event = (struct event *) timerid; 208 209 TIMESPEC_TO_TIMEVAL(&event->it_interval, &value->it_interval); 210 TIMESPEC_TO_TIMEVAL(&event->it_value, &value->it_value); 211 212 return 1; 213} 214 215static void check_timer() 216{ 217 struct itimerval itimer; 218 219 getitimer(ITIMER_REAL, &itimer); 220 if (timerisset(&itimer.it_interval)) { 221 TIMERDBG("ERROR timer interval is set."); 222 } 223 /* CSTYLED */ 224 if (timercmp(&(itimer.it_value), &(event_queue->it_value), >)) { 225 TIMERDBG("ERROR timer expires later than top event."); 226 } 227} 228 229 230static void check_event_queue() 231{ 232 struct timeval sum; 233 struct event *event; 234 int i = 0; 235 236#ifdef notdef 237 int nfree = 0; 238 struct event *p; 239 for (p = event_freelist; p; p = p->next) 240 nfree++; 241 printf("%d free events\n", nfree); 242#endif 243 244 timerclear(&sum); 245 for (event = event_queue; event; event = event->next) { 246 if (i > g_maxevents) { 247 TIMERDBG("timer queue looks like it loops back on itself!"); 248 print_event_queue(); 249 exit(1); 250 } 251 i++; 252 } 253} 254 255#if THIS_FINDS_USE 256/* The original upnp version has this unused function, so I left it in 257 * to maintain the resemblance. 258 */ 259static int count_queue(struct event *event_queue) 260{ 261 struct event *event; 262 int i = 0; 263 for (event = event_queue; event; event = event->next) 264 i++; 265 return i; 266} 267#endif 268 269static void print_event_queue() 270{ 271 struct event *event; 272 int i = 0; 273 274 for (event = event_queue; event; event = event->next) { 275 printf("#%d (0x%x)->0x%x: \t%d sec %d usec\t%p\n", 276 i++, (unsigned int) event, (unsigned int) event->next, (int) 277 event->it_value.tv_sec, 278 (int) event->it_value.tv_usec, event->func); 279 if (i > g_maxevents) { 280 printf("...(giving up)\n"); 281 break; 282 } 283 } 284} 285 286/* The top element of the event queue must have expired. */ 287/* Remove that element, run its function, and reset the timer. */ 288/* if there is no interval, recycle the event structure. */ 289static void alarm_handler(int i) 290{ 291 struct event *event, **ppevent; 292 struct itimerval itimer; 293 struct timeval small_interval = { 0, g_granularity/2 }; 294#ifdef TIMER_PROFILE 295 uint junk; 296 uclock_t end; 297 uint actual; 298#endif 299 300 block_timer(); 301 302 /* Loop through the event queue and remove the first event plus any */ 303 /* subsequent events that will expire very soon thereafter (within 'small_interval'}. */ 304 /* */ 305 do { 306 /* remove the top event. */ 307 event = event_queue; 308 event_queue = event_queue->next; 309 event->next = NULL; 310 311#ifdef TIMER_PROFILE 312 end = uclock(); 313 actual = ((end-event->start)/((uclock_t)UCLOCKS_PER_SEC/1000)); 314 if (actual < 0) 315 junk = end; 316 TIMERDBG("expected %d ms actual %d ms", event->expected_ms, 317 ((end-event->start)/((uclock_t)UCLOCKS_PER_SEC/1000))); 318#endif 319 320 /* call the event callback function */ 321 (*(event->func))((timer_t) event, (int)event->arg); 322 323 /* If the event has been cancelled, do NOT put it back on the queue. */ 324 if (!(event->flags & TFLAG_CANCELLED)) { 325 326 /* if the event is a recurring event, reset the timer and 327 * find its correct place in the sorted list of events. 328 */ 329 if (timerisset(&event->it_interval)) { 330 /* event is recurring... */ 331 event->it_value = event->it_interval; 332#ifdef TIMER_PROFILE 333 event->expected_ms = (event->it_value.tv_sec * MS_PER_SEC) + 334 (event->it_value.tv_usec / US_PER_MS); 335 event->start = uclock(); 336#endif 337 timerroundup(&event->it_value, g_granularity); 338 339 /* Now, march down the list, decrementing the new timer by the */ 340 /* current delta of each event on the queue. */ 341 ppevent = &event_queue; 342 while (*ppevent) { 343 if (timercmp(&(event->it_value), &((*ppevent)->it_value), 344 /* CSTYLED */ 345 <)) { 346 /* if the proposed event will trigger sooner than 347 * the next event 348 * in the queue, we will insert the new event just 349 * before the next one. 350 * we also need to adjust the delta value to the 351 * next event. 352 */ 353 timersub(&((*ppevent)->it_value), 354 &(event->it_value), 355 &((*ppevent)->it_value)); 356 break; 357 } 358 timersub(&(event->it_value), &((*ppevent)->it_value), 359 &(event->it_value)); 360 ppevent = &((*ppevent)->next); 361 } 362 363 /* we have found our proper place in the queue, */ 364 /* link our new event into the pending event queue. */ 365 event->next = *ppevent; 366 *ppevent = event; 367 } else { 368 /* there is no interval, so recycle the event structure. 369 * timer_delete((timer_t) event); 370 */ 371 } 372 } 373 374 check_event_queue(); 375 376 /* CSTYLED */ 377 } while (event_queue && timercmp(&event_queue->it_value, &small_interval, <)); 378 379 /* re-issue the timer... */ 380 if (event_queue) { 381 timerroundup(&event_queue->it_value, g_granularity); 382 383 timerclear(&itimer.it_interval); 384 itimer.it_value = event_queue->it_value; 385 /* we want to be sure to never turn off the timer completely, */ 386 /* so if the next interval is zero, set it to some small value. */ 387 if (!timerisset(&(itimer.it_value))) 388 itimer.it_value = (struct timeval) { 0, 1 }; 389 390 setitimer(ITIMER_REAL, &itimer, NULL); 391 check_timer(); 392 } else { 393 TIMERDBG("There are no events in the queue - timer not reset."); 394 } 395 396 unblock_timer(); 397} 398 399static int block_count = 0; 400 401void block_timer() 402{ 403 sigset_t set; 404 405 if (block_count++ == 0) { 406 sigemptyset(&set); 407 sigaddset(&set, SIGALRM); 408 sigprocmask(SIG_BLOCK, &set, NULL); 409 } 410} 411 412void unblock_timer() 413{ 414 sigset_t set; 415 416 if (--block_count == 0) { 417 sigemptyset(&set); 418 sigaddset(&set, SIGALRM); 419 sigprocmask(SIG_UNBLOCK, &set, NULL); 420 } 421} 422 423void timer_cancel_all() 424{ 425 struct itimerval timeroff = { { 0, 0 }, { 0, 0} }; 426 struct event *event; 427 struct event **ppevent; 428 429 setitimer(ITIMER_REAL, &timeroff, NULL); 430 431 ppevent = &event_queue; 432 while (*ppevent) { 433 event = *ppevent; 434 *ppevent = event->next; 435 event->next = NULL; 436 } 437} 438 439 440void timer_cancel(timer_t timerid) 441{ 442 struct itimerval itimer; 443 struct itimerval timeroff = { { 0, 0 }, { 0, 0} }; 444 struct event *event = (struct event *) timerid; 445 struct event **ppevent; 446 447 if (event->flags & TFLAG_CANCELLED) { 448 TIMERDBG("Cannot cancel a cancelled event"); 449 return; 450 } 451 452 block_timer(); 453 454 ppevent = &event_queue; 455 while (*ppevent) { 456 if (*ppevent == event) { 457 458 /* RACE CONDITION - if the alarm goes off while we are in 459 * this loop, and if the timer we want to cancel is the 460 * next to expire, the alarm will end up firing 461 * after this routine is complete, causing it to go off early. 462 */ 463 464 /* If the cancelled timer is the next to expire, 465 * we need to do something special to clean up correctly. 466 */ 467 if (event == event_queue && event->next != NULL) { 468 timerclear(&itimer.it_value); 469 getitimer(ITIMER_REAL, &itimer); 470 471 /* subtract the time that has already passed while waiting for this 472 * timer... 473 */ 474 timersub(&(event->it_value), &(itimer.it_value), 475 &(event->it_value)); 476 477 /* and add any remainder to the next timer in the list */ 478 timeradd(&(event->next->it_value), &(event->it_value), 479 &(event->next->it_value)); 480 } 481 482 *ppevent = event->next; 483 event->next = NULL; 484 485 if (event_queue) { 486 timerroundup(&event_queue->it_value, g_granularity); 487 timerclear(&itimer.it_interval); 488 itimer.it_value = event_queue->it_value; 489 490 /* We want to be sure to never turn off the timer 491 * completely if there are more events on the queue, 492 * so if the next interval is zero, set it to some 493 * small value. 494 */ 495 496 if (!timerisset(&(itimer.it_value))) 497 itimer.it_value = (struct timeval) { 0, 1 }; 498 499 assert(itimer.it_value.tv_sec > 0 || itimer.it_value.tv_usec >= 500 g_granularity); 501 assert(event_queue->it_value.tv_sec > 0 || 502 event_queue->it_value.tv_usec >= 503 g_granularity); 504 setitimer(ITIMER_REAL, &itimer, NULL); 505 check_timer(); 506 } else { 507 setitimer(ITIMER_REAL, &timeroff, NULL); 508 } 509 break; 510 } 511 ppevent = &((*ppevent)->next); 512 } 513 514 event->flags |= TFLAG_CANCELLED; 515 516 unblock_timer(); 517} 518 519/* 520* timer related headers 521*/ 522#include "bcmtimer.h" 523 524/* 525* locally used global variables and constants 526*/ 527 528/* 529* Initialize internal resources used in the timer module. It must be called 530* before any other timer function calls. The param 'timer_entries' is used 531* to pre-allocate fixed number of timer entries. 532*/ 533int bcm_timer_module_init(int timer_entries, bcm_timer_module_id *module_id) 534{ 535 init_event_queue(timer_entries); 536 *module_id = (bcm_timer_module_id)event_freelist; 537 return 0; 538} 539 540/* 541* Cleanup internal resources used by this timer module. It deletes all 542* pending timer entries from the backend timer system as well. 543*/ 544int bcm_timer_module_cleanup(bcm_timer_module_id module_id) 545{ 546 module_id = 0; 547 return 0; 548} 549 550/* Enable/Disable timer module */ 551int bcm_timer_module_enable(bcm_timer_module_id module_id, int enable) 552{ 553 if (enable) 554 unblock_timer(); 555 else 556 block_timer(); 557 return 0; 558} 559 560int bcm_timer_create(bcm_timer_module_id module_id, bcm_timer_id *timer_id) 561{ 562 module_id = 0; 563 return timer_create(CLOCK_REALTIME, NULL, (timer_t *)timer_id); 564} 565 566int bcm_timer_delete(bcm_timer_id timer_id) 567{ 568 return timer_delete((timer_t)timer_id); 569} 570 571int bcm_timer_gettime(bcm_timer_id timer_id, struct itimerspec *timer_spec) 572{ 573 return -1; 574} 575 576int bcm_timer_settime(bcm_timer_id timer_id, const struct itimerspec *timer_spec) 577{ 578 return timer_settime((timer_t)timer_id, 0, timer_spec, NULL); 579} 580 581int bcm_timer_connect(bcm_timer_id timer_id, bcm_timer_cb func, int data) 582{ 583 return timer_connect((timer_t)timer_id, (void *)func, data); 584} 585 586int bcm_timer_cancel(bcm_timer_id timer_id) 587{ 588 timer_cancel((timer_t)timer_id); 589 return 0; 590} 591int bcm_timer_change_expirytime(bcm_timer_id timer_id, const struct itimerspec *timer_spec) 592{ 593 timer_change_settime((timer_t)timer_id, timer_spec); 594 return 1; 595} 596