1/*
2 * Copyright (C) 2010, 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,v 1.2 2008/11/12 06:54:21 Exp $
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	setitimer(ITIMER_REAL, &tv, 0);
175	setitimer(ITIMER_REAL, 0, &tv);
176	g_granularity = tv.it_interval.tv_usec;
177
178	signal(SIGALRM, alarm_handler);
179}
180
181int timer_connect
182(
183	timer_t     timerid, /* timer ID */
184	void (*routine)(timer_t, int), /* user routine */
185	int         arg      /* user argument */
186)
187{
188	struct event *event = (struct event *) timerid;
189
190	assert(routine != NULL);
191	event->func = routine;
192	event->arg = arg;
193
194	return 0;
195}
196
197/*
198 * Please Call this function only from the call back functions of the alarm_handler.
199 * This is just a hack
200 */
201int timer_change_settime
202(
203	timer_t                   timerid, /* timer ID */
204	const struct itimerspec * value   /* time to be set */
205)
206{
207	struct event *event = (struct event *) timerid;
208
209	TIMESPEC_TO_TIMEVAL(&event->it_interval, &value->it_interval);
210	TIMESPEC_TO_TIMEVAL(&event->it_value, &value->it_value);
211
212	return 1;
213}
214
215static void check_timer()
216{
217	struct itimerval itimer;
218
219	getitimer(ITIMER_REAL, &itimer);
220	if (timerisset(&itimer.it_interval)) {
221		TIMERDBG("ERROR timer interval is set.");
222	}
223	/* CSTYLED */
224	if (timercmp(&(itimer.it_value), &(event_queue->it_value), >)) {
225		TIMERDBG("ERROR timer expires later than top event.");
226	}
227}
228
229
230static void check_event_queue()
231{
232	struct timeval sum;
233	struct event *event;
234	int i = 0;
235
236#ifdef notdef
237	int nfree = 0;
238	struct event *p;
239	for (p = event_freelist; p; p = p->next)
240	nfree++;
241	printf("%d free events\n", nfree);
242#endif
243
244	timerclear(&sum);
245	for (event = event_queue; event; event = event->next) {
246		if (i > g_maxevents) {
247			TIMERDBG("timer queue looks like it loops back on itself!");
248			print_event_queue();
249			exit(1);
250		}
251		i++;
252	}
253}
254
255#if THIS_FINDS_USE
256/* The original upnp version has this unused function, so I left it in
257 * to maintain the resemblance.
258 */
259static int count_queue(struct event *event_queue)
260{
261	struct event *event;
262	int i = 0;
263	for (event = event_queue; event; event = event->next)
264		i++;
265	return i;
266}
267#endif
268
269static void print_event_queue()
270{
271	struct event *event;
272	int i = 0;
273
274	for (event = event_queue; event; event = event->next) {
275		printf("#%d (0x%x)->0x%x: \t%d sec %d usec\t%p\n",
276		       i++, (unsigned int) event, (unsigned int) event->next, (int)
277		       event->it_value.tv_sec,
278		       (int) event->it_value.tv_usec, event->func);
279		if (i > g_maxevents) {
280			printf("...(giving up)\n");
281			break;
282		}
283	}
284}
285
286/* The top element of the event queue must have expired. */
287/* Remove that element, run its function, and reset the timer. */
288/* if there is no interval, recycle the event structure. */
289static void alarm_handler(int i)
290{
291	struct event *event, **ppevent;
292	struct itimerval itimer;
293	struct timeval small_interval = { 0, g_granularity/2 };
294#ifdef TIMER_PROFILE
295	uint junk;
296	uclock_t end;
297	uint actual;
298#endif
299
300	block_timer();
301
302	/* Loop through the event queue and remove the first event plus any */
303	/* subsequent events that will expire very soon thereafter (within 'small_interval'}. */
304	/* */
305	do {
306		/* remove the top event. */
307		event = event_queue;
308		event_queue = event_queue->next;
309		event->next = NULL;
310
311#ifdef TIMER_PROFILE
312		end = uclock();
313		actual = ((end-event->start)/((uclock_t)UCLOCKS_PER_SEC/1000));
314		if (actual < 0)
315			junk = end;
316		TIMERDBG("expected %d ms actual %d ms", event->expected_ms,
317		         ((end-event->start)/((uclock_t)UCLOCKS_PER_SEC/1000)));
318#endif
319
320		/* call the event callback function */
321		(*(event->func))((timer_t) event, (int)event->arg);
322
323		/* If the event has been cancelled, do NOT put it back on the queue. */
324		if (!(event->flags & TFLAG_CANCELLED)) {
325
326			/* if the event is a recurring event, reset the timer and
327			 * find its correct place in the sorted list of events.
328			 */
329			if (timerisset(&event->it_interval)) {
330				/* event is recurring... */
331				event->it_value = event->it_interval;
332#ifdef TIMER_PROFILE
333				event->expected_ms = (event->it_value.tv_sec * MS_PER_SEC) +
334				        (event->it_value.tv_usec / US_PER_MS);
335				event->start = uclock();
336#endif
337				timerroundup(&event->it_value, g_granularity);
338
339				/* Now, march down the list, decrementing the new timer by the */
340				/* current delta of each event on the queue. */
341				ppevent = &event_queue;
342				while (*ppevent) {
343					if (timercmp(&(event->it_value), &((*ppevent)->it_value),
344						     /* CSTYLED */
345						     <)) {
346						/* if the proposed event will trigger sooner than
347						 * the next event
348						 * in the queue, we will insert the new event just
349						 * before the next one.
350						 * we also need to adjust the delta value to the
351						 * next event.
352						 */
353						timersub(&((*ppevent)->it_value),
354						         &(event->it_value),
355						         &((*ppevent)->it_value));
356						break;
357					}
358					timersub(&(event->it_value), &((*ppevent)->it_value),
359					         &(event->it_value));
360					ppevent = &((*ppevent)->next);
361				}
362
363				/* we have found our proper place in the queue, */
364				/* link our new event into the pending event queue. */
365				event->next = *ppevent;
366				*ppevent = event;
367			} else {
368				/* there is no interval, so recycle the event structure.
369				 * timer_delete((timer_t) event);
370				 */
371			}
372		}
373
374		check_event_queue();
375
376		/* CSTYLED */
377	} while (event_queue && timercmp(&event_queue->it_value, &small_interval, <));
378
379	/* re-issue the timer... */
380	if (event_queue) {
381		timerroundup(&event_queue->it_value, g_granularity);
382
383		timerclear(&itimer.it_interval);
384		itimer.it_value = event_queue->it_value;
385		/* we want to be sure to never turn off the timer completely, */
386		/* so if the next interval is zero, set it to some small value. */
387		if (!timerisset(&(itimer.it_value)))
388			itimer.it_value = (struct timeval) { 0, 1 };
389
390		setitimer(ITIMER_REAL, &itimer, NULL);
391		check_timer();
392	} else {
393		TIMERDBG("There are no events in the queue - timer not reset.");
394	}
395
396	unblock_timer();
397}
398
399static int block_count = 0;
400
401void block_timer()
402{
403	sigset_t set;
404
405	if (block_count++ == 0) {
406		sigemptyset(&set);
407		sigaddset(&set, SIGALRM);
408		sigprocmask(SIG_BLOCK, &set, NULL);
409	}
410}
411
412void unblock_timer()
413{
414	sigset_t set;
415
416	if (--block_count == 0) {
417		sigemptyset(&set);
418		sigaddset(&set, SIGALRM);
419		sigprocmask(SIG_UNBLOCK, &set, NULL);
420	}
421}
422
423void timer_cancel_all()
424{
425	struct itimerval timeroff = { { 0, 0 }, { 0, 0} };
426	struct event *event;
427	struct event **ppevent;
428
429	setitimer(ITIMER_REAL, &timeroff, NULL);
430
431	ppevent = &event_queue;
432	while (*ppevent) {
433		event = *ppevent;
434		*ppevent = event->next;
435		event->next = NULL;
436	}
437}
438
439
440void timer_cancel(timer_t timerid)
441{
442	struct itimerval itimer;
443	struct itimerval timeroff = { { 0, 0 }, { 0, 0} };
444	struct event *event = (struct event *) timerid;
445	struct event **ppevent;
446
447	if (event->flags & TFLAG_CANCELLED) {
448		TIMERDBG("Cannot cancel a cancelled event");
449		return;
450	}
451
452	block_timer();
453
454	ppevent = &event_queue;
455	while (*ppevent) {
456		if (*ppevent == event) {
457
458			/* RACE CONDITION - if the alarm goes off while we are in
459			 * this loop, and if the timer we want to cancel is the
460			 * next to expire, the alarm will end up firing
461			 * after this routine is complete, causing it to go off early.
462			 */
463
464			/* If the cancelled timer is the next to expire,
465			 * we need to do something special to clean up correctly.
466			 */
467			if (event == event_queue && event->next != NULL) {
468				timerclear(&itimer.it_value);
469				getitimer(ITIMER_REAL, &itimer);
470
471				/* subtract the time that has already passed while waiting for this
472				 * timer...
473				 */
474				timersub(&(event->it_value), &(itimer.it_value),
475				         &(event->it_value));
476
477				/* and add any remainder to the next timer in the list */
478				timeradd(&(event->next->it_value), &(event->it_value),
479				         &(event->next->it_value));
480			}
481
482			*ppevent = event->next;
483			event->next = NULL;
484
485			if (event_queue) {
486				timerroundup(&event_queue->it_value, g_granularity);
487				timerclear(&itimer.it_interval);
488				itimer.it_value = event_queue->it_value;
489
490				/* We want to be sure to never turn off the timer
491				 * completely if there are more events on the queue,
492				 * so if the next interval is zero, set it to some
493				 * small value.
494				 */
495
496				if (!timerisset(&(itimer.it_value)))
497					itimer.it_value = (struct timeval) { 0, 1 };
498
499				assert(itimer.it_value.tv_sec > 0 || itimer.it_value.tv_usec >=
500				       g_granularity);
501				assert(event_queue->it_value.tv_sec > 0 ||
502				       event_queue->it_value.tv_usec >=
503				       g_granularity);
504				setitimer(ITIMER_REAL, &itimer, NULL);
505				check_timer();
506			} else {
507				setitimer(ITIMER_REAL, &timeroff, NULL);
508			}
509			break;
510		}
511		ppevent = &((*ppevent)->next);
512	}
513
514	event->flags |= TFLAG_CANCELLED;
515
516	unblock_timer();
517}
518
519/*
520* timer related headers
521*/
522#include "bcmtimer.h"
523
524/*
525* locally used global variables and constants
526*/
527
528/*
529* Initialize internal resources used in the timer module. It must be called
530* before any other timer function calls. The param 'timer_entries' is used
531* to pre-allocate fixed number of timer entries.
532*/
533int bcm_timer_module_init(int timer_entries, bcm_timer_module_id *module_id)
534{
535	init_event_queue(timer_entries);
536	*module_id = (bcm_timer_module_id)event_freelist;
537	return 0;
538}
539
540/*
541* Cleanup internal resources used by this timer module. It deletes all
542* pending timer entries from the backend timer system as well.
543*/
544int bcm_timer_module_cleanup(bcm_timer_module_id module_id)
545{
546	module_id = 0;
547	return 0;
548}
549
550/* Enable/Disable timer module */
551int bcm_timer_module_enable(bcm_timer_module_id module_id, int enable)
552{
553	if (enable)
554		unblock_timer();
555	else
556		block_timer();
557	return 0;
558}
559
560int bcm_timer_create(bcm_timer_module_id module_id, bcm_timer_id *timer_id)
561{
562	module_id = 0;
563	return timer_create(CLOCK_REALTIME, NULL, (timer_t *)timer_id);
564}
565
566int bcm_timer_delete(bcm_timer_id timer_id)
567{
568	return timer_delete((timer_t)timer_id);
569}
570
571int bcm_timer_gettime(bcm_timer_id timer_id, struct itimerspec *timer_spec)
572{
573	return -1;
574}
575
576int bcm_timer_settime(bcm_timer_id timer_id, const struct itimerspec *timer_spec)
577{
578	return timer_settime((timer_t)timer_id, 0, timer_spec, NULL);
579}
580
581int bcm_timer_connect(bcm_timer_id timer_id, bcm_timer_cb func, int data)
582{
583	return timer_connect((timer_t)timer_id, (void *)func, data);
584}
585
586int bcm_timer_cancel(bcm_timer_id timer_id)
587{
588	timer_cancel((timer_t)timer_id);
589	return 0;
590}
591int bcm_timer_change_expirytime(bcm_timer_id timer_id, const struct itimerspec *timer_spec)
592{
593	timer_change_settime((timer_t)timer_id, timer_spec);
594	return 1;
595}
596