1 //==========================================================================
5 //==========================================================================
6 //####BSDCOPYRIGHTBEGIN####
8 // -------------------------------------------
10 // Portions of this software may have been derived from OpenBSD,
11 // FreeBSD or other sources, and are covered by the appropriate
12 // copyright disclaimers included herein.
14 // Portions created by Red Hat are
15 // Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
17 // -------------------------------------------
19 //####BSDCOPYRIGHTEND####
20 //==========================================================================
22 //==========================================================================
28 //==========================================================================
29 //####BSDCOPYRIGHTBEGIN####
31 // -------------------------------------------
33 // Portions of this software may have been derived from OpenBSD or other sources,
34 // and are covered by the appropriate copyright disclaimers included herein.
36 // -------------------------------------------
38 //####BSDCOPYRIGHTEND####
39 //==========================================================================
40 //#####DESCRIPTIONBEGIN####
42 // Author(s): gthomas, hmt
43 // Contributors: gthomas, hmt
45 // Description: Simple timeout functions
46 //####DESCRIPTIONEND####
48 #include <sys/param.h>
49 #include <pkgconf/net.h>
50 #include <cyg/kernel/kapi.h>
51 #include <cyg/infra/cyg_ass.h>
55 void alarm_timeout_init(void);
60 static timeout_entry _timeouts[NTIMEOUTS];
61 static timeout_entry *timeouts = (timeout_entry *)NULL;
62 static cyg_handle_t timeout_alarm_handle;
63 static cyg_alarm timeout_alarm;
64 static cyg_int32 last_delta;
65 static cyg_tick_count_t last_set_time;
67 #define STACK_SIZE CYGNUM_HAL_STACK_SIZE_TYPICAL
68 static char alarm_stack[STACK_SIZE];
69 static cyg_thread alarm_thread_data;
70 static cyg_handle_t alarm_thread_handle;
72 static cyg_flag_t alarm_flag;
74 // ------------------------------------------------------------------------
75 // This routine exists so that this module can synchronize:
76 extern cyg_uint32 cyg_splinternal(void);
83 for (f = timeouts; f; f = f->next) {
84 diag_printf("%p: delta: %d, fun: %p, param: %p\n", f, f->delta, f->fun, f->arg);
87 #endif // TIMEOUT_DEBUG
89 // ------------------------------------------------------------------------
91 // Called from the thread, this runs the alarm callbacks.
92 // Locking is already in place when this is called.
97 timeout_entry *e, *e_next;
99 CYG_ASSERT( 0 < last_delta, "last_delta underflow" );
101 min_delta = last_delta; // local copy
102 last_delta = -1; // flag recursive call underway
106 e_next = e->next; // Because this can change during processing
109 if ( !(e->delta >= min_delta)) {
110 diag_printf("Bad delta in timeout: %p, delta: %d, min: %d, last: %ld\n", e, e->delta, min_delta, last_set_time);
114 // Note: this _can_ happen if timeouts are scheduled before the clock starts!
115 // CYG_ASSERT( e->delta >= min_delta, "e->delta underflow" );
116 e->delta -= min_delta;
117 if (e->delta <= 0) { // Defensive
118 // Time for this item to 'fire'
119 timeout_fun *fun = e->fun;
121 // Call it *after* cleansing the record
122 // diag_printf("%s(%p, %p, %p)\n", __FUNCTION__, e, e->fun, e->arg);
123 e->flags &= ~CALLOUT_PENDING;
126 e->next->prev = e->prev;
129 e->prev->next = e->next;
139 // Now scan for a new timeout *after* running all the callbacks
140 // (because they can add timeouts themselves)
141 min_delta = 0x7FFFFFFF; // Maxint
142 for (e = timeouts; e; e = e->next )
144 if (e->delta < min_delta)
145 min_delta = e->delta;
147 CYG_ASSERT( 0 < min_delta, "min_delta underflow" );
149 if (min_delta != 0x7FFFFFFF) {
150 // Still something to do, schedule it
151 last_set_time = cyg_current_time();
152 cyg_alarm_initialize(timeout_alarm_handle, last_set_time+min_delta, 0);
153 last_delta = min_delta;
155 last_delta = 0; // flag no activity
158 diag_printf("Timeout list after %s\n", __FUNCTION__);
163 // ------------------------------------------------------------------------
164 // ALARM EVENT FUNCTION
165 // This is the DSR for the alarm firing:
167 do_alarm(cyg_handle_t alarm, cyg_addrword_t data)
169 cyg_flag_setbits( &alarm_flag, 1 );
172 void ecos_synch_eth_drv_dsr(void)
174 cyg_flag_setbits( &alarm_flag, 2 );
177 // ------------------------------------------------------------------------
178 // HANDLER THREAD ENTRY ROUTINE
179 // This waits on the DSR to tell it to run:
181 alarm_thread(cyg_addrword_t param)
183 // This is from the logical ethernet dev; it calls those delivery
184 // functions who need attention.
185 extern void eth_drv_run_deliveries( void );
187 // This is from the logical ethernet dev; it tickles somehow
188 // all ethernet devices in case one is wedged.
189 extern void eth_drv_tickle_devices( void );
194 #ifdef CYGPKG_NET_FAST_THREAD_TICKLE_DEVS
195 cyg_tick_count_t later = cyg_current_time();
196 later += CYGNUM_NET_FAST_THREAD_TICKLE_DEVS_DELAY;
197 x = cyg_flag_timed_wait(
200 CYG_FLAG_WAITMODE_OR | CYG_FLAG_WAITMODE_CLR,
206 CYG_FLAG_WAITMODE_OR | CYG_FLAG_WAITMODE_CLR );
208 CYG_ASSERT( 3 & x, "Lost my bits" );
209 #endif // CYGPKG_NET_FAST_THREAD_TICKLE_DEVS
210 CYG_ASSERT( !((~3) & x), "Extra bits" );
212 spl = cyg_splinternal();
214 CYG_ASSERT( 0 == spl, "spl nonzero" );
217 eth_drv_run_deliveries();
218 #ifdef CYGPKG_NET_FAST_THREAD_TICKLE_DEVS
219 // This is in the else clause for "do we deliver" because the
220 // network stack might have continuous timing events anyway - so
221 // the timeout would not occur, x would be 1 every time.
222 else // Tickle the devices...
223 eth_drv_tickle_devices();
224 #endif // CYGPKG_NET_FAST_THREAD_TICKLE_DEVS
233 // ------------------------------------------------------------------------
234 // INITIALIZATION FUNCTION
236 cyg_alarm_timeout_init( void )
238 // Init the alarm object, attached to the real time clock
240 cyg_clock_to_counter(cyg_real_time_clock(), &h);
241 cyg_alarm_create(h, do_alarm, 0, &timeout_alarm_handle, &timeout_alarm);
242 // Init the flag of waking up
243 cyg_flag_init( &alarm_flag );
244 // Create alarm background thread to run the callbacks
246 CYGPKG_NET_FAST_THREAD_PRIORITY, // Priority
247 alarm_thread, // entry
248 0, // entry parameter
249 "Network alarm support", // Name
250 &alarm_stack[0], // Stack
252 &alarm_thread_handle, // Handle
253 &alarm_thread_data // Thread data structure
255 cyg_thread_resume(alarm_thread_handle); // Start it
258 // ------------------------------------------------------------------------
259 // EXPORTED API: SET A TIMEOUT
260 // This can be called from anywhere, including recursively from the timeout
261 // functions themselves.
263 timeout(timeout_fun *fun, void *arg, cyg_int32 delta)
269 // this needs to be atomic - recursive calls from the alarm
270 // handler thread itself are allowed:
271 int spl = cyg_splinternal();
273 stamp = 0; // Assume no slots available
274 for (e = _timeouts, i = 0; i < NTIMEOUTS; i++, e++) {
275 if ((e->flags & CALLOUT_PENDING) == 0) {
278 e->flags = CALLOUT_LOCAL;
279 callout_reset(e, delta, fun, arg);
280 stamp = (cyg_uint32)e;
288 // ------------------------------------------------------------------------
289 // EXPORTED API: CANCEL A TIMEOUT
290 // This can be called from anywhere, including recursively from the timeout
291 // functions themselves.
293 untimeout(timeout_fun *fun, void * arg)
297 int spl = cyg_splinternal();
299 for (e = _timeouts, i = 0; i < NTIMEOUTS; i++, e++) {
300 if (e->delta && (e->fun == fun) && (e->arg == arg)) {
309 callout_init(struct callout *c)
311 bzero(c, sizeof(*c));
315 callout_reset(struct callout *c, int delta, timeout_fun *f, void *p)
317 int spl = cyg_splinternal();
319 CYG_ASSERT( 0 < delta, "delta is right now, or even sooner!" );
321 // Renormalize delta wrt the existing set alarm, if there is one
322 if (last_delta > 0) {
325 int _time = cyg_current_time();
326 #endif // TIMEOUT_DEBUG
327 // There is an active alarm
328 if (last_set_time != 0) {
329 // Adjust the delta to be absolute, relative to the alarm
330 delta += (cyg_int32)(cyg_current_time() - last_set_time);
332 // We don't know exactly when the alarm will fire, so just
333 // schedule this event for the first time, or sometime after
334 ; // Leaving the value alone won't be "too wrong"
337 diag_printf("delta changed from %d to %d, now: %d, then: %d, last_delta: %d\n",
338 _delta, delta, _time, (int)last_set_time, last_delta);
342 // So recorded_delta is set to either:
343 // alarm is active: delta + NOW - THEN
344 // alarm is inactive: delta
346 // Add this callout/timeout to the list of things to do
347 if (c->flags & CALLOUT_PENDING) {
350 c->prev = (timeout_entry *)NULL;
352 if (c->next != (timeout_entry *)NULL) {
356 c->flags |= CALLOUT_PENDING | CALLOUT_ACTIVE;
362 diag_printf("%s(%p, %d, %p, %p)\n", __FUNCTION__, c, delta, f, p);
366 if ((0 == last_delta || // alarm was inactive OR
367 delta < last_delta) ) { // alarm was active but later than we need
369 // (if last_delta is -1, this call is recursive from the handler so
370 // also do nothing in that case)
372 // Here, we know the new item added is sooner than that which was
373 // most recently set, if any, so we can just go and set it up.
374 if ( 0 == last_delta )
375 last_set_time = cyg_current_time();
377 // So we use, to set the alarm either:
378 // alarm is active: (delta + NOW - THEN) + THEN
379 // alarm is inactive: delta + NOW
380 // and in either case it is true that
381 // (recorded_delta + last_set_time) == (delta + NOW)
382 cyg_alarm_initialize(timeout_alarm_handle, last_set_time+delta, 0);
384 if ((int)last_set_time == 0) {
385 diag_printf("delta: %d, time: %ld, last_delta: %d\n", delta, last_set_time, last_delta);
390 // Otherwise, the alarm is active, AND it is set to fire sooner than we
391 // require, so when it does, that will sort out calling the item we
394 #ifdef CYGPKG_INFRA_DEBUG
395 // Do some more checking akin to that in the alarm handler:
396 if ( last_delta != -1 ) { // not a recursive call
397 cyg_tick_count_t now = cyg_current_time();
400 CYG_ASSERT( last_delta >= 0, "Bad last delta" );
402 for (e = timeouts; e; e = e->next) {
404 CYG_ASSERT( e->delta >= last_delta, "e->delta underflow" );
405 // the following triggers if the "next" timeout has not just
406 // passed, but passed by 1000 ticks - which with the normal
407 // 1 tick = 10ms means 10 seconds - a long time.
408 CYG_ASSERT( last_set_time + e->delta + 1000 > now,
409 "Recorded alarm not in the future! Starved network thread?" );
410 if ( e->delta < delta )
413 CYG_ASSERT( 0 == e->fun, "Function recorded for 0 delta" );
416 if (delta < last_delta) {
417 diag_printf("Failed to pick smallest delta - picked: %d, last: %d\n", delta, last_delta);
418 for (e = timeouts; e; e = e->next) {
419 diag_printf(" timeout: %p at %d\n", e->fun, e->delta);
422 CYG_ASSERT( delta >= last_delta, "We didn't pick the smallest delta!" );
429 callout_stop(struct callout *c)
431 int spl = cyg_splinternal();
434 diag_printf("%s(%p) = %x\n", __FUNCTION__, c, c->flags);
436 if ((c->flags & CALLOUT_PENDING) == 0) {
437 c->flags &= ~CALLOUT_ACTIVE;
441 c->flags &= ~(CALLOUT_PENDING | CALLOUT_ACTIVE);
443 c->next->prev = c->prev;
446 c->prev->next = c->next;
454 callout_active(struct callout *c)
456 return ((c->flags & CALLOUT_ACTIVE) != 0);
460 callout_deactivate(struct callout *c)
462 c->flags &= ~CALLOUT_ACTIVE;
466 callout_pending(struct callout *c)
468 return ((c->flags & CALLOUT_PENDING) != 0);
472 // ------------------------------------------------------------------------