]> git.karo-electronics.de Git - karo-tx-redboot.git/blob - packages/net/bsd_tcpip/v2_0/src/ecos/timeout.c
c38752be96520aa7e8217a6339a1901f7b381946
[karo-tx-redboot.git] / packages / net / bsd_tcpip / v2_0 / src / ecos / timeout.c
1 //==========================================================================
2 //
3 //      src/ecos/timeout.c
4 //
5 //==========================================================================
6 //####BSDCOPYRIGHTBEGIN####
7 //
8 // -------------------------------------------
9 //
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.
13 //
14 // Portions created by Red Hat are
15 // Copyright (C) 2002 Red Hat, Inc. All Rights Reserved.
16 //
17 // -------------------------------------------
18 //
19 //####BSDCOPYRIGHTEND####
20 //==========================================================================
21
22 //==========================================================================
23 //
24 //        lib/timeout.c
25 //
26 //        timeout support
27 //
28 //==========================================================================
29 //####BSDCOPYRIGHTBEGIN####
30 //
31 // -------------------------------------------
32 //
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.
35 //
36 // -------------------------------------------
37 //
38 //####BSDCOPYRIGHTEND####
39 //==========================================================================
40 //#####DESCRIPTIONBEGIN####
41 //
42 // Author(s):     gthomas, hmt
43 // Contributors:  gthomas, hmt
44 // Date:          1999-02-05
45 // Description:   Simple timeout functions
46 //####DESCRIPTIONEND####
47
48 #include <sys/param.h>
49 #include <pkgconf/net.h>
50 #include <cyg/kernel/kapi.h>
51 #include <cyg/infra/cyg_ass.h>
52
53 // Timeout support
54
55 void alarm_timeout_init(void);
56
57 #ifndef NTIMEOUTS
58 #define NTIMEOUTS 8
59 #endif
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;
66
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;
71
72 static cyg_flag_t alarm_flag;  
73
74 // ------------------------------------------------------------------------
75 // This routine exists so that this module can synchronize:
76 extern cyg_uint32 cyg_splinternal(void);
77
78 #ifdef TIMEOUT_DEBUG
79 static void
80 _show_timeouts(void)
81 {
82     timeout_entry *f;
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);
85     }
86 }
87 #endif // TIMEOUT_DEBUG
88
89 // ------------------------------------------------------------------------
90 // CALLBACK FUNCTION
91 // Called from the thread, this runs the alarm callbacks.
92 // Locking is already in place when this is called.
93 static void
94 do_timeout(void)
95 {
96     cyg_int32 min_delta;
97     timeout_entry *e, *e_next;
98
99     CYG_ASSERT( 0 < last_delta, "last_delta underflow" );
100
101     min_delta = last_delta; // local copy
102     last_delta = -1; // flag recursive call underway
103
104     e = timeouts;
105     while (e) {
106         e_next = e->next;  // Because this can change during processing
107         if (e->delta) {
108 #ifdef TIMEOUT_DEBUG
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);
111                     _show_timeouts();
112                 }                
113 #endif
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;
120                 void *arg = e->arg;
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;
124                 e->delta = 0;
125                 if (e->next) {
126                     e->next->prev = e->prev;
127                 }
128                 if (e->prev) {
129                     e->prev->next = e->next;
130                 } else {
131                     timeouts = e->next;
132                 }
133                 (*fun)(arg);
134             }
135         }
136         e = e_next;
137     }
138
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 )
143         if (e->delta)
144             if (e->delta < min_delta)
145                 min_delta = e->delta;
146
147     CYG_ASSERT( 0 < min_delta, "min_delta underflow" );
148
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;
154     } else {
155         last_delta = 0; // flag no activity
156     }
157 #ifdef TIMEOUT_DEBUG
158     diag_printf("Timeout list after %s\n", __FUNCTION__);
159     _show_timeouts();
160 #endif
161 }
162
163 // ------------------------------------------------------------------------
164 // ALARM EVENT FUNCTION
165 // This is the DSR for the alarm firing:
166 static void
167 do_alarm(cyg_handle_t alarm, cyg_addrword_t data)
168 {
169     cyg_flag_setbits( &alarm_flag, 1 ); 
170 }
171
172 void ecos_synch_eth_drv_dsr(void)
173 {
174     cyg_flag_setbits( &alarm_flag, 2 ); 
175 }
176
177 // ------------------------------------------------------------------------
178 // HANDLER THREAD ENTRY ROUTINE
179 // This waits on the DSR to tell it to run:
180 static void
181 alarm_thread(cyg_addrword_t param)
182 {
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 );
186
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 );
190
191     while ( 1 ) {
192         int spl;
193         int x;
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(
198             &alarm_flag,
199             -1,
200             CYG_FLAG_WAITMODE_OR | CYG_FLAG_WAITMODE_CLR,
201             later );
202 #else
203         x = cyg_flag_wait(
204             &alarm_flag,
205             -1,
206             CYG_FLAG_WAITMODE_OR | CYG_FLAG_WAITMODE_CLR );
207
208         CYG_ASSERT( 3 & x, "Lost my bits" );
209 #endif // CYGPKG_NET_FAST_THREAD_TICKLE_DEVS
210         CYG_ASSERT( !((~3) & x), "Extra bits" );
211
212         spl = cyg_splinternal();
213
214         CYG_ASSERT( 0 == spl, "spl nonzero" );
215
216         if ( 2 & x )
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
225
226         if ( 1 & x )
227             do_timeout();
228
229         cyg_splx(spl);
230     }
231 }
232
233 // ------------------------------------------------------------------------
234 // INITIALIZATION FUNCTION
235 void
236 cyg_alarm_timeout_init( void )
237 {
238     // Init the alarm object, attached to the real time clock
239     cyg_handle_t h;
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
245     cyg_thread_create(
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
251         STACK_SIZE,                     // Size
252         &alarm_thread_handle,           // Handle
253         &alarm_thread_data              // Thread data structure
254         );
255     cyg_thread_resume(alarm_thread_handle);    // Start it
256 }
257
258 // ------------------------------------------------------------------------
259 // EXPORTED API: SET A TIMEOUT
260 // This can be called from anywhere, including recursively from the timeout
261 // functions themselves.
262 cyg_uint32
263 timeout(timeout_fun *fun, void *arg, cyg_int32 delta)
264 {
265     int i;
266     timeout_entry *e;
267     cyg_uint32 stamp;
268
269     // this needs to be atomic - recursive calls from the alarm
270     // handler thread itself are allowed:
271     int spl = cyg_splinternal();
272
273     stamp = 0;  // Assume no slots available
274     for (e = _timeouts, i = 0;  i < NTIMEOUTS;  i++, e++) {
275         if ((e->flags & CALLOUT_PENDING) == 0) {
276             // Free entry
277             callout_init(e);
278             e->flags = CALLOUT_LOCAL;
279             callout_reset(e, delta, fun, arg);
280             stamp = (cyg_uint32)e;
281             break;
282         }
283     }
284     cyg_splx(spl);
285     return stamp;
286 }
287
288 // ------------------------------------------------------------------------
289 // EXPORTED API: CANCEL A TIMEOUT
290 // This can be called from anywhere, including recursively from the timeout
291 // functions themselves.
292 void
293 untimeout(timeout_fun *fun, void * arg)
294 {
295     int i;
296     timeout_entry *e;
297     int spl = cyg_splinternal();
298
299     for (e = _timeouts, i = 0; i < NTIMEOUTS; i++, e++) {
300         if (e->delta && (e->fun == fun) && (e->arg == arg)) {
301             callout_stop(e);
302             break;
303         }
304     }
305     cyg_splx(spl);
306 }
307
308 void 
309 callout_init(struct callout *c) 
310 {
311     bzero(c, sizeof(*c));
312 }
313
314 void 
315 callout_reset(struct callout *c, int delta, timeout_fun *f, void *p) 
316 {
317     int spl = cyg_splinternal();
318
319     CYG_ASSERT( 0 < delta, "delta is right now, or even sooner!" );
320
321     // Renormalize delta wrt the existing set alarm, if there is one
322     if (last_delta > 0) {
323 #ifdef TIMEOUT_DEBUG
324         int _delta = delta;    
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);
331         } else {
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"
335         }
336 #ifdef TIMEOUT_DEBUG
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);
339         _show_timeouts();
340 #endif
341     }
342     // So recorded_delta is set to either:
343     // alarm is active:   delta + NOW - THEN
344     // alarm is inactive: delta
345
346     // Add this callout/timeout to the list of things to do
347     if (c->flags & CALLOUT_PENDING) {
348         callout_stop(c);
349     }
350     c->prev = (timeout_entry *)NULL;    
351     c->next = timeouts;
352     if (c->next != (timeout_entry *)NULL) {
353         c->next->prev = c;
354     }
355     timeouts = c;
356     c->flags |= CALLOUT_PENDING | CALLOUT_ACTIVE;
357     c->fun = f;
358     c->arg = p;
359     c->delta = delta;
360
361 #ifdef TIMEOUT_DEBUG
362     diag_printf("%s(%p, %d, %p, %p)\n", __FUNCTION__, c, delta, f, p);
363     _show_timeouts();
364 #endif
365
366     if ((0 == last_delta ||      // alarm was inactive  OR
367          delta < last_delta) ) { // alarm was active but later than we need
368
369         // (if last_delta is -1, this call is recursive from the handler so
370         //  also do nothing in that case)
371
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();
376         
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);
383 #ifdef TIMEOUT_DEBUG
384         if ((int)last_set_time == 0) {
385             diag_printf("delta: %d, time: %ld, last_delta: %d\n", delta, last_set_time, last_delta);
386         }
387 #endif
388         last_delta = delta;
389     }
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
392     // just added.
393
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();
398         timeout_entry *e;
399
400         CYG_ASSERT( last_delta >= 0, "Bad last delta" );
401         delta = 0x7fffffff;
402         for (e = timeouts;  e;  e = e->next) {
403             if (e->delta) {
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 )
411                     delta = e->delta;
412             } else {
413                 CYG_ASSERT( 0 == e->fun, "Function recorded for 0 delta" );
414             }
415         }
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);
420             }
421         }
422         CYG_ASSERT( delta >= last_delta, "We didn't pick the smallest delta!" );
423     }
424 #endif
425     cyg_splx(spl);
426 }
427
428 void 
429 callout_stop(struct callout *c) 
430 {
431     int spl = cyg_splinternal();
432
433 #ifdef TIMEOUT_DEBUG
434     diag_printf("%s(%p) = %x\n", __FUNCTION__, c, c->flags);
435 #endif
436     if ((c->flags & CALLOUT_PENDING) == 0) {
437         c->flags &= ~CALLOUT_ACTIVE;
438         cyg_splx(spl);
439         return;
440     }
441     c->flags &= ~(CALLOUT_PENDING | CALLOUT_ACTIVE);
442     if (c->next) {
443         c->next->prev = c->prev;
444     }
445     if (c->prev) {
446         c->prev->next = c->next;
447     } else {
448         timeouts = c->next;
449     }
450     cyg_splx(spl);
451 }
452
453 int  
454 callout_active(struct callout *c) 
455 {
456     return ((c->flags & CALLOUT_ACTIVE) != 0);
457 }
458
459 void 
460 callout_deactivate(struct callout *c) 
461 {
462     c->flags &= ~CALLOUT_ACTIVE;
463 }
464
465 int  
466 callout_pending(struct callout *c) 
467 {
468     return ((c->flags & CALLOUT_PENDING) != 0);
469 }
470
471
472 // ------------------------------------------------------------------------
473
474 // EOF timeout.c