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