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: bsd_timer.c 327734 2012-04-16 17:25:20Z $
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