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