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