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