summaryrefslogtreecommitdiffstats
path: root/bsd_eth_drivers/libbsdport/rtems_callout.c
blob: ce9a477e3c09dc81aab8022a8dbaffd622da173d (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
#include <rtems.h>
#include <rtems/error.h>
#include <string.h>

#include <rtems/rtems_bsdnet.h>
#include <rtems/rtems_bsdnet_internal.h>

#include "mutex.h"
#include "callout.h"

#define STATIC static

/* Implementation modelled after
 *
 * "Redesign the BSD Callout and Timer Facilities"
 * Adam M. Costello, George Varghese
 * Dpt. of Computer Science, Washington University, 1995.
 */

#include <assert.h>

/* rely on networking semaphore for now */
#define LIST_KEY_DECL(k) 
#define LIST_LOCK(k)   do {} while (0)
#define LIST_UNLOCK(k) do {} while (0)

#define WHEELBITS 5

#define WHEELMASK ((1<<(WHEELBITS))-1)

#define CALLOUT_EVENT	RTEMS_EVENT_1
#define KILL_EVENT		RTEMS_EVENT_2


typedef void (*timeout_t)(void*);

STATIC volatile callout_time_t hard_ticks = 0;
STATIC volatile callout_time_t soft_ticks = 0;

STATIC struct callout *c_wheel[1<<WHEELBITS] = {0};

static inline void
c_enq(struct callout **where, struct callout *c)
{
	assert( c->c_pprev == 0 && c->c_next == 0 );
	if ( (c->c_next = *where) )
		(*where)->c_pprev = &c->c_next;
	c->c_pprev = where;
	*where     = c;
}

static inline void
c_deq(struct callout *c)
{
struct callout *n;
	assert( c->c_pprev );
	if ( (n = *c->c_pprev = c->c_next) )
		n->c_pprev = c->c_pprev;
	c->c_next = 0;
	c->c_pprev = 0;
}

static inline void
softclock()
{
struct callout        *c, *n;
rtems_interrupt_level  k1;
callout_time_t         st,ht;
LIST_KEY_DECL(k);

	/* I believe this is free of a race condition (softclock
	 * and hardclock both update volatile 'soft_ticks' variable):
	 *  a) 'hardclock' runs at IRQ level and is atomic
	 *  b) inside while loop 'soft_ticks' is != 'hard_ticks'
	 *  c) hardclock only modifies soft_ticks if 'soft_ticks'=='hard_ticks'
	 *     hence this could only happen just after the update of 'soft_ticks'
	 *     at the end of the while loop completes.
	 */

	while ( 1 ) {
	/* Must atomically read 'soft_ticks' and 'hard_ticks' -- otherwise,
	 * hardclock might update both but we get one old and one new value
	 */
	rtems_interrupt_disable(k1);
		st = soft_ticks;
		ht = hard_ticks;
	rtems_interrupt_enable(k1);
		if ( st == ht )
			break; /* caught up */

		/* at this point, we know that st != ht and therefore,
		 * hardclock will only increment hard_ticks but leave
		 * soft_ticks alone.
		 */

		st++;

		LIST_LOCK(k);
		for ( c = c_wheel[ st & WHEELMASK ]; c; c=n ) {
			n = c->c_next;
			if ( c->c_time <= 0 ) {
				/* this one expired */
				c_deq(c);
				if ( c->c_func )
					c->c_func(c->c_arg);
			} else {
				c->c_time--;
			}
		}
		LIST_UNLOCK(k);
		soft_ticks = st;
		/* here, soft_ticks could have caught up and
		 * a hardclock occurring here could also
		 * update soft_ticks.
		 */
	}
}

static inline void
hardclock(rtems_id tid)
{
	if ( hard_ticks++ == soft_ticks && !c_wheel[hard_ticks & WHEELMASK] ) {
		/* nothing to do */
		soft_ticks++;
	} else {
		rtems_event_send(tid, CALLOUT_EVENT);
	}
}

static void
calloutTick(rtems_id myself, void *arg)
{
rtems_id tid = (rtems_id)arg;

	hardclock(tid);

	rtems_timer_fire_after(myself, 1, calloutTick, arg);
}

static void
calloutTask(void *arg)
{
rtems_event_set   ev;
rtems_status_code sc;
rtems_id          ticker = 0;
rtems_id          me;

	sc = rtems_timer_create(rtems_build_name('b','s','d','c'), &ticker);
	if ( RTEMS_SUCCESSFUL != sc ) {
		rtems_error(sc, "Creation of timer failed\n");
		goto bail;
	}
	rtems_task_ident(RTEMS_SELF, RTEMS_LOCAL, &me);

	rtems_timer_fire_after(ticker, 1, calloutTick, (void*)me);

	while ( 1 ) {
		sc = rtems_bsdnet_event_receive (CALLOUT_EVENT | KILL_EVENT, RTEMS_EVENT_ANY | RTEMS_WAIT, RTEMS_NO_TIMEOUT, &ev);
		if ( RTEMS_SUCCESSFUL != sc ) {
			rtems_error(sc, "calloutTask: unable to receive event; terminating\n");
			break;
		}
		if ( ev & KILL_EVENT ) {
			break;
		}
		softclock();
	}
bail:
	rtems_timer_delete(ticker);
	rtems_task_delete(RTEMS_SELF);
}


/* We cannot stop a callout that's in progress */

void
callout_stop(struct callout *c)
{
LIST_KEY_DECL(k);

	if ( !c->c_pprev )
		return;	/* not currently on a list */

	LIST_LOCK(k);
		/* remove from list */
		c_deq(c);
	LIST_UNLOCK(k);
}


void
callout_reset(struct callout *c, int ticks, timeout_t fn, void *arg)
{
LIST_KEY_DECL(k);
int                 i;

	if ( ticks <= 0 )
		ticks = 1;

	callout_stop(c);

	c->c_func = fn;
	c->c_arg  = arg;

	LIST_LOCK(k);
	i         = (hard_ticks + ticks) & WHEELMASK;
	c->c_time = ticks >> WHEELBITS;

	/* enqueue */
	c_enq(&c_wheel[i], c);

	LIST_UNLOCK(k);
}

static rtems_id callout_tid = 0;

void
callout_init(struct callout *c, int mpsafe)
{
	/* non thread-safe lazy init in case nobody cared to do it ... */
	if ( !callout_tid )
		rtems_callout_initialize();
	memset(c,0,sizeof(*c));	
}

void
callout_init_mtx(struct callout *c, struct mtx *m, unsigned flags)
{
	if ( m->mtx_id )
		rtems_panic("callout_init_mtx: using mutex not supported\n");
	callout_init(c,0);
	c->c_mtx = m;
}

rtems_id
rtems_callout_initialize()
{
	if ( !callout_tid )
		callout_tid=rtems_bsdnet_newproc ("cout", 4096, calloutTask, NULL);
	return callout_tid;
}

#ifdef DEBUG
void
_cexpModuleInitialize(void*u)
{
	rtems_bsdnet_initialize_network();
	rtems_callout_initialize();
}
#endif