1/* 2 * Copyright (C) 2013, 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#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 175 setitimer(ITIMER_REAL, &tv, 0); 176 setitimer(ITIMER_REAL, 0, &tv); 177 178 if (tv.it_interval.tv_usec == 0) 179 tv.it_interval.tv_usec = 1; 180 181 g_granularity = tv.it_interval.tv_usec; 182 signal(SIGALRM, alarm_handler); 183} 184 185int clock_gettime( 186 clockid_t clock_id, /* clock ID (always CLOCK_REALTIME) */ 187 struct timespec * tp /* where to store current time */ 188) 189{ 190 struct timeval tv; 191 int n; 192 193 194 n = gettimeofday(&tv, NULL); 195 TIMEVAL_TO_TIMESPEC(&tv, tp); 196 197 return n; 198} 199 200 201int timer_create( 202 clockid_t clock_id, /* clock ID (always CLOCK_REALTIME) */ 203 struct sigevent * evp, /* user event handler */ 204 timer_t * pTimer /* ptr to return value */ 205) 206{ 207 struct event *event; 208 209 if (clock_id != CLOCK_REALTIME) { 210 TIMERDBG("timer_create can only support clock id CLOCK_REALTIME"); 211 exit(1); 212 } 213 214 if (evp != NULL) { 215 if (evp->sigev_notify != SIGEV_SIGNAL || evp->sigev_signo != SIGALRM) { 216 TIMERDBG("timer_create can only support signalled alarms using SIGALRM"); 217 exit(1); 218 } 219 } 220 221 event = event_freelist; 222 if (event == NULL) { 223 print_event_queue(); 224 } 225 assert(event != NULL); 226 227 event->flags = TFLAG_NONE; 228 229 event_freelist = event->next; 230 event->next = NULL; 231 232 check_event_queue(); 233 234 *pTimer = (timer_t) event; 235 236 return 0; 237} 238 239int timer_delete( 240 timer_t timerid /* timer ID */ 241) 242{ 243 struct event *event = (struct event *) timerid; 244 245 if (event->flags & TFLAG_DELETED) { 246 TIMERDBG("Cannot delete a deleted event"); 247 return 1; 248 } 249 250 timer_cancel(timerid); 251 252 event->flags |= TFLAG_DELETED; 253 254 event->next = event_freelist; 255 event_freelist = event; 256 257 return 0; 258} 259 260int timer_connect 261( 262 timer_t timerid, /* timer ID */ 263 void (*routine)(timer_t, int), /* user routine */ 264 int arg /* user argument */ 265) 266{ 267 struct event *event = (struct event *) timerid; 268 269 assert(routine != NULL); 270 event->func = routine; 271 event->arg = arg; 272 273 return 0; 274} 275 276/* 277 * Please Call this function only from the call back functions of the alarm_handler. 278 * This is just a hack 279 */ 280int timer_change_settime 281( 282 timer_t timerid, /* timer ID */ 283 const struct itimerspec * value /* time to be set */ 284) 285{ 286 struct event *event = (struct event *) timerid; 287 288 TIMESPEC_TO_TIMEVAL(&event->it_interval, &value->it_interval); 289 TIMESPEC_TO_TIMEVAL(&event->it_value, &value->it_value); 290 291 return 1; 292} 293 294int timer_settime 295( 296 timer_t timerid, /* timer ID */ 297 int flags, /* absolute or relative */ 298 const struct itimerspec * value, /* time to be set */ 299 struct itimerspec * ovalue /* previous time set (NULL=no result) */ 300) 301{ 302 struct itimerval itimer; 303 struct event *event = (struct event *) timerid; 304 struct event **ppevent; 305 306 TIMESPEC_TO_TIMEVAL(&event->it_interval, &value->it_interval); 307 TIMESPEC_TO_TIMEVAL(&event->it_value, &value->it_value); 308 309 /* if .it_value is zero, the timer is disarmed */ 310 if (!timerisset(&event->it_value)) { 311 timer_cancel(timerid); 312 return 0; 313 } 314 315 block_timer(); 316 317#ifdef TIMER_PROFILE 318 event->expected_ms = (event->it_value.tv_sec * MS_PER_SEC) + (event->it_value.tv_usec / 319 US_PER_MS); 320 event->start = uclock(); 321#endif 322 if (event->next) { 323 TIMERDBG("calling timer_settime with a timer that is already on the queue."); 324 } 325 326 327 /* We always want to make sure that the event at the head of the 328 * queue has a timeout greater than the itimer granularity. 329 * Otherwise we end up with the situation that the time remaining 330 * on an itimer is greater than the time at the head of the queue 331 * in the first place. 332 */ 333 timerroundup(&event->it_value, g_granularity); 334 335 timerclear(&itimer.it_value); 336 getitimer(ITIMER_REAL, &itimer); 337 if (timerisset(&itimer.it_value)) { 338 /* reset the top timer to have an interval equal to the remaining interval 339 * when the timer was cancelled. 340 */ 341 if (event_queue) { 342 /* CSTYLED */ 343 if (timercmp(&(itimer.it_value), &(event_queue->it_value), >)) { 344 /* it is an error if the amount of time remaining is more than the 345 * amount of time requested by the top event. 346 */ 347 TIMERDBG("timer_settime: TIMER ERROR!"); 348 349 } else { 350 /* some portion of the top event has already expired. 351 * Reset the interval of the top event to remaining 352 * time left in that interval. 353 */ 354 event_queue->it_value = itimer.it_value; 355 356 /* if we were the earliest timer before now, we are still the 357 * earliest timer now. we do not need to reorder the list. 358 */ 359 } 360 } 361 } 362 363 /* Now, march down the list, decrementing the new timer by the 364 * current it_value of each event on the queue. 365 */ 366 ppevent = &event_queue; 367 while (*ppevent) { 368 /* CSTYLED */ 369 if (timercmp(&(event->it_value), &((*ppevent)->it_value), <)) { 370 /* if the proposed event will trigger sooner than the next event 371 * in the queue, we will insert the new event just before the next one. 372 * we also need to adjust the delta value to the next event. 373 */ 374 timersub(&((*ppevent)->it_value), &(event->it_value), 375 &((*ppevent)->it_value)); 376 break; 377 } 378 /* subtract the interval of the next event from the proposed interval. */ 379 timersub(&(event->it_value), &((*ppevent)->it_value), &(event->it_value)); 380 381 ppevent = &((*ppevent)->next); 382 } 383 384 /* we have found our proper place in the queue, */ 385 /* link our new event into the pending event queue. */ 386 event->next = *ppevent; 387 *ppevent = event; 388 389 check_event_queue(); 390 391 /* if our new event ended up at the front of the queue, reissue the timer. */ 392 if (event == event_queue) { 393 timerroundup(&event_queue->it_value, g_granularity); 394 timerclear(&itimer.it_interval); 395 itimer.it_value = event_queue->it_value; 396 397 /* we want to be sure to never turn off the timer completely, */ 398 /* so if the next interval is zero, set it to some small value. */ 399 if (!timerisset(&(itimer.it_value))) 400 itimer.it_value = (struct timeval) { 0, 1 }; 401 402 assert(!timerisset(&itimer.it_interval)); 403 assert(itimer.it_value.tv_sec > 0 || itimer.it_value.tv_usec >= g_granularity); 404 assert(event_queue->it_value.tv_sec > 0 || event_queue->it_value.tv_usec >= 405 g_granularity); 406 setitimer(ITIMER_REAL, &itimer, NULL); 407 check_timer(); 408 } 409 410 event->flags &= ~TFLAG_CANCELLED; 411 412 unblock_timer(); 413 414 return 0; 415} 416 417static void check_timer() 418{ 419 struct itimerval itimer; 420 421 getitimer(ITIMER_REAL, &itimer); 422 if (timerisset(&itimer.it_interval)) { 423 TIMERDBG("ERROR timer interval is set."); 424 } 425 /* CSTYLED */ 426 if (timercmp(&(itimer.it_value), &(event_queue->it_value), >)) { 427 TIMERDBG("ERROR timer expires later than top event."); 428 } 429} 430 431 432static void check_event_queue() 433{ 434 struct timeval sum; 435 struct event *event; 436 int i = 0; 437 438#ifdef notdef 439 int nfree = 0; 440 struct event *p; 441 for (p = event_freelist; p; p = p->next) 442 nfree++; 443 printf("%d free events\n", nfree); 444#endif 445 446 timerclear(&sum); 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!"); 450 print_event_queue(); 451 exit(1); 452 } 453 i++; 454 } 455} 456 457#if THIS_FINDS_USE 458/* The original upnp version has this unused function, so I left it in 459 * to maintain the resemblance. 460 */ 461static int count_queue(struct event *event_queue) 462{ 463 struct event *event; 464 int i = 0; 465 for (event = event_queue; event; event = event->next) 466 i++; 467 return i; 468} 469#endif 470 471static void print_event_queue() 472{ 473 struct event *event; 474 int i = 0; 475 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"); 483 break; 484 } 485 } 486} 487 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. */ 491static void alarm_handler(int i) 492{ 493 struct event *event, **ppevent; 494 struct itimerval itimer; 495 struct timeval small_interval = { 0, g_granularity/2 }; 496#ifdef TIMER_PROFILE 497 uint junk; 498 uclock_t end; 499 uint actual; 500#endif 501 502 block_timer(); 503 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'}. */ 506 /* */ 507 if (!event_queue) { 508 unblock_timer(); 509 return; 510 } 511 512 do { 513 /* remove the top event. */ 514 event = event_queue; 515 event_queue = event_queue->next; 516 event->next = NULL; 517 518#ifdef TIMER_PROFILE 519 end = uclock(); 520 actual = ((end-event->start)/((uclock_t)UCLOCKS_PER_SEC/1000)); 521 if (actual < 0) 522 junk = end; 523 TIMERDBG("expected %d ms actual %d ms", event->expected_ms, 524 ((end-event->start)/((uclock_t)UCLOCKS_PER_SEC/1000))); 525#endif 526 527 /* call the event callback function */ 528 (*(event->func))((timer_t) event, (int)event->arg); 529 530 /* If the event has been cancelled, do NOT put it back on the queue. */ 531 if (!(event->flags & TFLAG_CANCELLED)) { 532 533 /* if the event is a recurring event, reset the timer and 534 * find its correct place in the sorted list of events. 535 */ 536 if (timerisset(&event->it_interval)) { 537 /* event is recurring... */ 538 event->it_value = event->it_interval; 539#ifdef TIMER_PROFILE 540 event->expected_ms = (event->it_value.tv_sec * MS_PER_SEC) + 541 (event->it_value.tv_usec / US_PER_MS); 542 event->start = uclock(); 543#endif 544 timerroundup(&event->it_value, g_granularity); 545 546 /* Now, march down the list, decrementing the new timer by the */ 547 /* current delta of each event on the queue. */ 548 ppevent = &event_queue; 549 while (*ppevent) { 550 if (timercmp(&(event->it_value), &((*ppevent)->it_value), 551 /* CSTYLED */ 552 <)) { 553 /* if the proposed event will trigger sooner than 554 * the next event 555 * in the queue, we will insert the new event just 556 * before the next one. 557 * we also need to adjust the delta value to the 558 * next event. 559 */ 560 timersub(&((*ppevent)->it_value), 561 &(event->it_value), 562 &((*ppevent)->it_value)); 563 break; 564 } 565 timersub(&(event->it_value), &((*ppevent)->it_value), 566 &(event->it_value)); 567 ppevent = &((*ppevent)->next); 568 } 569 570 /* we have found our proper place in the queue, */ 571 /* link our new event into the pending event queue. */ 572 event->next = *ppevent; 573 *ppevent = event; 574 } else { 575 /* there is no interval, so recycle the event structure. 576 * timer_delete((timer_t) event); 577 */ 578 } 579 } 580 581 check_event_queue(); 582 583 /* CSTYLED */ 584 } while (event_queue && timercmp(&event_queue->it_value, &small_interval, <)); 585 586 /* re-issue the timer... */ 587 if (event_queue) { 588 timerroundup(&event_queue->it_value, g_granularity); 589 590 timerclear(&itimer.it_interval); 591 itimer.it_value = event_queue->it_value; 592 /* we want to be sure to never turn off the timer completely, */ 593 /* so if the next interval is zero, set it to some small value. */ 594 if (!timerisset(&(itimer.it_value))) 595 itimer.it_value = (struct timeval) { 0, 1 }; 596 597 setitimer(ITIMER_REAL, &itimer, NULL); 598 check_timer(); 599 } else { 600 TIMERDBG("There are no events in the queue - timer not reset."); 601 } 602 603 unblock_timer(); 604} 605 606static int block_count = 0; 607 608void block_timer() 609{ 610 sigset_t set; 611 612 if (block_count++ == 0) { 613 sigemptyset(&set); 614 sigaddset(&set, SIGALRM); 615 sigprocmask(SIG_BLOCK, &set, NULL); 616 } 617} 618 619void unblock_timer() 620{ 621 sigset_t set; 622 623 if (--block_count == 0) { 624 sigemptyset(&set); 625 sigaddset(&set, SIGALRM); 626 sigprocmask(SIG_UNBLOCK, &set, NULL); 627 } 628} 629 630void timer_cancel_all() 631{ 632 struct itimerval timeroff = { { 0, 0 }, { 0, 0} }; 633 struct event *event; 634 struct event **ppevent; 635 636 setitimer(ITIMER_REAL, &timeroff, NULL); 637 638 ppevent = &event_queue; 639 while (*ppevent) { 640 event = *ppevent; 641 *ppevent = event->next; 642 event->next = NULL; 643 } 644} 645 646 647void timer_cancel(timer_t timerid) 648{ 649 struct itimerval itimer; 650 struct itimerval timeroff = { { 0, 0 }, { 0, 0} }; 651 struct event *event = (struct event *) timerid; 652 struct event **ppevent; 653 654 if (event->flags & TFLAG_CANCELLED) { 655 TIMERDBG("Cannot cancel a cancelled event"); 656 return; 657 } 658 659 block_timer(); 660 661 ppevent = &event_queue; 662 while (*ppevent) { 663 if (*ppevent == event) { 664 665 /* RACE CONDITION - if the alarm goes off while we are in 666 * this loop, and if the timer we want to cancel is the 667 * next to expire, the alarm will end up firing 668 * after this routine is complete, causing it to go off early. 669 */ 670 671 /* If the cancelled timer is the next to expire, 672 * we need to do something special to clean up correctly. 673 */ 674 if (event == event_queue && event->next != NULL) { 675 timerclear(&itimer.it_value); 676 getitimer(ITIMER_REAL, &itimer); 677 678 /* subtract the time that has already passed while waiting for this 679 * timer... 680 */ 681 timersub(&(event->it_value), &(itimer.it_value), 682 &(event->it_value)); 683 684 /* and add any remainder to the next timer in the list */ 685 timeradd(&(event->next->it_value), &(event->it_value), 686 &(event->next->it_value)); 687 } 688 689 *ppevent = event->next; 690 event->next = NULL; 691 692 if (event_queue) { 693 timerroundup(&event_queue->it_value, g_granularity); 694 timerclear(&itimer.it_interval); 695 itimer.it_value = event_queue->it_value; 696 697 /* We want to be sure to never turn off the timer 698 * completely if there are more events on the queue, 699 * so if the next interval is zero, set it to some 700 * small value. 701 */ 702 703 if (!timerisset(&(itimer.it_value))) 704 itimer.it_value = (struct timeval) { 0, 1 }; 705 706 assert(itimer.it_value.tv_sec > 0 || itimer.it_value.tv_usec >= 707 g_granularity); 708 assert(event_queue->it_value.tv_sec > 0 || 709 event_queue->it_value.tv_usec >= 710 g_granularity); 711 setitimer(ITIMER_REAL, &itimer, NULL); 712 check_timer(); 713 } else { 714 setitimer(ITIMER_REAL, &timeroff, NULL); 715 } 716 break; 717 } 718 ppevent = &((*ppevent)->next); 719 } 720 721 event->flags |= TFLAG_CANCELLED; 722 723 unblock_timer(); 724} 725 726/* 727* timer related headers 728*/ 729#include "bcmtimer.h" 730 731/* 732* locally used global variables and constants 733*/ 734 735/* 736* Initialize internal resources used in the timer module. It must be called 737* before any other timer function calls. The param 'timer_entries' is used 738* to pre-allocate fixed number of timer entries. 739*/ 740int bcm_timer_module_init(int timer_entries, bcm_timer_module_id *module_id) 741{ 742 init_event_queue(timer_entries); 743 *module_id = (bcm_timer_module_id)event_freelist; 744 return 0; 745} 746 747/* 748* Cleanup internal resources used by this timer module. It deletes all 749* pending timer entries from the backend timer system as well. 750*/ 751int bcm_timer_module_cleanup(bcm_timer_module_id module_id) 752{ 753 module_id = 0; 754 return 0; 755} 756 757/* Enable/Disable timer module */ 758int bcm_timer_module_enable(bcm_timer_module_id module_id, int enable) 759{ 760 if (enable) 761 unblock_timer(); 762 else 763 block_timer(); 764 return 0; 765} 766 767int bcm_timer_create(bcm_timer_module_id module_id, bcm_timer_id *timer_id) 768{ 769 module_id = 0; 770 return timer_create(CLOCK_REALTIME, NULL, (timer_t *)timer_id); 771} 772 773int bcm_timer_delete(bcm_timer_id timer_id) 774{ 775 return timer_delete((timer_t)timer_id); 776} 777 778int bcm_timer_gettime(bcm_timer_id timer_id, struct itimerspec *timer_spec) 779{ 780 return -1; 781} 782 783int bcm_timer_settime(bcm_timer_id timer_id, const struct itimerspec *timer_spec) 784{ 785 return timer_settime((timer_t)timer_id, 0, timer_spec, NULL); 786} 787 788int bcm_timer_connect(bcm_timer_id timer_id, bcm_timer_cb func, int data) 789{ 790 return timer_connect((timer_t)timer_id, (void *)func, data); 791} 792 793int bcm_timer_cancel(bcm_timer_id timer_id) 794{ 795 timer_cancel((timer_t)timer_id); 796 return 0; 797} 798int bcm_timer_change_expirytime(bcm_timer_id timer_id, const struct itimerspec *timer_spec) 799{ 800 timer_change_settime((timer_t)timer_id, timer_spec); 801 return 1; 802} 803