]> git.karo-electronics.de Git - karo-tx-redboot.git/blob - packages/io/fileio/v2_0/src/select.cxx
03782e149d6d79ba6aec717926197efc737df546
[karo-tx-redboot.git] / packages / io / fileio / v2_0 / src / select.cxx
1 //==========================================================================
2 //
3 //      select.cxx
4 //
5 //      Fileio select() support
6 //
7 //==========================================================================
8 //####ECOSGPLCOPYRIGHTBEGIN####
9 // -------------------------------------------
10 // This file is part of eCos, the Embedded Configurable Operating System.
11 // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
12 // Copyright (C) 2002 Nick Garnett
13 //
14 // eCos is free software; you can redistribute it and/or modify it under
15 // the terms of the GNU General Public License as published by the Free
16 // Software Foundation; either version 2 or (at your option) any later version.
17 //
18 // eCos is distributed in the hope that it will be useful, but WITHOUT ANY
19 // WARRANTY; without even the implied warranty of MERCHANTABILITY or
20 // FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
21 // for more details.
22 //
23 // You should have received a copy of the GNU General Public License along
24 // with eCos; if not, write to the Free Software Foundation, Inc.,
25 // 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
26 //
27 // As a special exception, if other files instantiate templates or use macros
28 // or inline functions from this file, or you compile this file and link it
29 // with other works to produce a work based on this file, this file does not
30 // by itself cause the resulting work to be covered by the GNU General Public
31 // License. However the source code for this file must still be made available
32 // in accordance with section (3) of the GNU General Public License.
33 //
34 // This exception does not invalidate any other reasons why a work based on
35 // this file might be covered by the GNU General Public License.
36 //
37 // Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
38 // at http://sources.redhat.com/ecos/ecos-license/
39 // -------------------------------------------
40 //####ECOSGPLCOPYRIGHTEND####
41 //==========================================================================
42 //#####DESCRIPTIONBEGIN####
43 //
44 // Author(s):           nickg
45 // Contributors:        nickg
46 // Date:                2000-05-25
47 // Purpose:             Fileio select() support
48 // Description:         Support for select().
49 //                      
50 //              
51 //              
52 //
53 //####DESCRIPTIONEND####
54 //
55 //==========================================================================
56
57 #include <pkgconf/hal.h>
58 #include <pkgconf/kernel.h>
59 #include <pkgconf/io_fileio.h>
60
61 #include <cyg/kernel/ktypes.h>         // base kernel types
62 #include <cyg/infra/cyg_trac.h>        // tracing macros
63 #include <cyg/infra/cyg_ass.h>         // assertion macros
64
65 #include <stdarg.h>                    // for fcntl()
66
67 #include "fio.h"                       // Private header
68
69 #include <sys/select.h>                // select header
70
71 #include <cyg/kernel/sched.hxx>        // scheduler definitions
72 #include <cyg/kernel/thread.hxx>       // thread definitions
73 #include <cyg/kernel/flag.hxx>         // flag definitions
74 #include <cyg/kernel/clock.hxx>        // clock definitions
75
76 #include <cyg/kernel/sched.inl>
77 #include <cyg/kernel/thread.inl>
78 #include <cyg/kernel/clock.inl>
79
80 //==========================================================================
81 // File object locking
82
83 #define LOCK_FILE( fp ) cyg_file_lock( fp )
84
85 #define UNLOCK_FILE( fp ) cyg_file_unlock( fp )
86
87 // Get a flag based on the thread's unique ID. Note: In a system with a large
88 // number of threads, the same flag may be used by more than one thread.
89 #define SELECT_WAIT_FLAG_GET() (1 << (Cyg_Thread::self()->get_unique_id() \
90                                 & (sizeof (Cyg_FlagValue) * NBBY - 1)))
91
92 //==========================================================================
93 // Local variables
94
95 static volatile cyg_uint32 selwake_count = 0;
96
97 // A flag is used to block a thread until data from the device is available. This
98 // prevents all threads from waking up at the same time and polling for changes.
99 // Each thread is allocated a flag bit via the SELECT_WAIT_FLAG_GET() macro when 
100 // the thread registers for selection via cyg_selrecord (). The flag is stored in
101 // the driver's select info block. Only those threads specified via the flags in 
102 // the select info are woken up by cyg_selwakeup (). 
103 // If there are more than 32 threads in the system, then there is a chance that
104 // cyg_selwakeup () may wake up more than one thread. Each thread then polls for
105 // changes.
106 static Cyg_Flag select_flag CYGBLD_ATTRIB_INIT_PRI(CYG_INIT_IO_FS);
107
108 //==========================================================================
109 // Timeval to ticks conversion support
110
111 // Converters from sec and us to ticks
112 static struct Cyg_Clock::converter us_converter, sec_converter;
113
114 static cyg_bool converters_initialized = false;
115
116 externC cyg_tick_count cyg_timeval_to_ticks( const struct timeval *tv )
117 {
118     if( !converters_initialized )
119     {
120         // Create the converters we need.
121         Cyg_Clock::real_time_clock->get_other_to_clock_converter( 1000, &us_converter );
122         Cyg_Clock::real_time_clock->get_other_to_clock_converter( 1000000000, &sec_converter );
123
124         converters_initialized = true;
125     }
126     
127     // Short circuit zero timeval
128     if( tv->tv_sec == 0 && tv->tv_usec == 0 )
129     {
130         return 0;
131     }
132         
133     // Convert the seconds field to ticks.
134     cyg_tick_count ticks = Cyg_Clock::convert( tv->tv_sec, &sec_converter );
135
136     // Convert the nanoseconds. This will round down to nearest whole tick.
137     ticks += Cyg_Clock::convert( (cyg_tick_count)tv->tv_usec, &us_converter );
138
139     return ticks;
140 }
141
142 //==========================================================================
143 // Select API function
144
145 static int
146 cyg_pselect(int nfd, fd_set *in, fd_set *out, fd_set *ex,
147            struct timeval *tv, const sigset_t *mask)
148 {
149     FILEIO_ENTRY();
150
151     int error = ENOERR;
152     int fd, mode, num;
153     cyg_file *fp;
154     fd_set in_res, out_res, ex_res;  // Result sets
155     fd_set *selection[3], *result[3];
156     cyg_tick_count ticks;
157     int mode_type[] = {CYG_FREAD, CYG_FWRITE, 0};
158     cyg_uint32 wake_count;
159     sigset_t oldmask;
160
161     Cyg_FlagValue myFlag = SELECT_WAIT_FLAG_GET ();
162     int    maxFdIndex = __howmany(nfd, __NFDBITS); // size of fd sets
163
164     // Make sure the nfd < FD_SETSIZE, a value greater than FD_SETSIZE 
165     // would break the results sets
166     if(nfd > FD_SETSIZE)
167     {
168         FILEIO_RETURN(EINVAL);
169     }
170
171     FD_ZERO(&in_res);
172     FD_ZERO(&out_res);
173     FD_ZERO(&ex_res);
174
175     // Set up sets
176     selection[0] = in;   result[0] = &in_res;
177     selection[1] = out;  result[1] = &out_res;
178     selection[2] = ex;   result[2] = &ex_res;
179
180     // Compute end time
181     if (tv)
182         ticks = cyg_timeval_to_ticks( tv );
183     else ticks = 0;
184
185     // Scan sets for possible I/O until something found, timeout or error.
186     while (!error)
187     {
188         wake_count = selwake_count;
189
190         num = 0;  // Total file descriptors "ready"
191         for (mode = 0;  !error && mode < 3;  mode++)
192         {
193             if (selection[mode]) 
194             {
195                 fd_mask *fds_bits = selection[mode]->fds_bits;
196                 int      index, fdbase;
197                 for(index = 0, fdbase = 0; !error && index < maxFdIndex; index++, fdbase += __NFDBITS)
198                 {
199                     fd_mask mask = fds_bits[index];
200                     for(fd = fdbase; mask != 0; fd++, mask >>= 1)
201                     {
202                         if(mask & 1)
203                         {
204                             fp = cyg_fp_get( fd );
205                             if( fp == NULL )
206                             {
207                                 error = EBADF;
208                                 break;
209                             }
210
211                             if ((*fp->f_ops->fo_select)(fp, mode_type[mode], 0))
212                             {
213                                 FD_SET(fd, result[mode]);
214                                 num++;
215                             }
216                             cyg_fp_free( fp );
217                         }
218                     }
219                 }
220             }
221         }
222
223         if (error)
224             break;
225         
226         if (num)
227         {
228             // Found something, update user's sets
229             if (in)  FD_COPY( &in_res, in );
230             if (out) FD_COPY( &out_res, out );
231             if (ex)  FD_COPY( &ex_res, ex );
232             CYG_FILEIO_DELIVER_SIGNALS( mask );
233             FILEIO_RETURN_VALUE(num);
234         }
235
236         Cyg_Scheduler::lock();
237
238         // Switch to the supplied signal mask. This will permit delivery
239         // of any signals that might terminate this select operation.
240         
241         CYG_FILEIO_SIGMASK_SET( mask, &oldmask );
242     
243         do
244         {
245
246             // We need to see if any signals have been posted while we
247             // were testing all those files. The handlers will not
248             // have run because we have ASRs inhibited but the signal
249             // will have been set pending.
250
251             if( CYG_FILEIO_SIGPENDING() )
252             {
253                 // There are pending signals so we need to terminate
254                 // the select operation and return EINTR. Handlers for
255                 // the pending signals will be called just before we
256                 // return.
257
258                 error = EINTR;
259                 break;
260             }
261             
262             if( wake_count == selwake_count )
263             {
264                 // Nothing found, see if we want to wait
265                 if (tv)
266                 {
267                     // Special case of "poll"
268                     if (ticks == 0)
269                     {
270                         error = EAGAIN;
271                         break;
272                     }
273
274                     ticks += Cyg_Clock::real_time_clock->current_value();
275                 
276                     if( !select_flag.wait (myFlag, Cyg_Flag::OR, ticks) )
277                     {
278                         // A non-standard wakeup, if the current time is equal to
279                         // or past the timeout, return zero. Otherwise return
280                         // EINTR, since we have been released.
281
282                         if( Cyg_Clock::real_time_clock->current_value() >= ticks )
283                         {
284                             error = EAGAIN;
285                             break;
286                         }
287                         else error = EINTR;
288                     }
289
290                     ticks -= Cyg_Clock::real_time_clock->current_value();
291                 }
292                 else
293                 {
294                     // Wait forever (until something happens)
295                     if( !select_flag.wait (myFlag, Cyg_Flag::OR) )
296                         error = EINTR;
297                 }
298             }
299
300         } while(0);
301
302         CYG_FILEIO_SIGMASK_SET( &oldmask, NULL );
303         
304         Cyg_Scheduler::unlock();
305         
306     } // while(!error)
307
308     // If the error code is EAGAIN, this means that a timeout has
309     // happened. We return zero in that case, rather than a proper
310     // error code.
311     // If the error code is EINTR, then a signal may be pending
312     // delivery. Call back into the POSIX package to handle it.
313     
314     if( error == EAGAIN )
315         FILEIO_RETURN_VALUE(0);
316     else if( error == EINTR )
317         CYG_FILEIO_DELIVER_SIGNALS( mask );
318
319     FILEIO_RETURN(error);
320 }
321
322 // -------------------------------------------------------------------------
323 // Select API function
324
325 __externC int
326 select(int nfd, fd_set *in, fd_set *out, fd_set *ex, struct timeval *tv)
327 {
328         return cyg_pselect(nfd, in, out, ex, tv, NULL);
329 }
330
331 // -------------------------------------------------------------------------
332 // Pselect API function
333 //
334 // This is derived from the POSIX-200X specification.
335
336 __externC int
337 pselect(int nfd, fd_set *in, fd_set *out, fd_set *ex,
338         const struct timespec *ts, const sigset_t *sigmask)
339 {
340         struct timeval tv;
341
342 #ifndef CYGPKG_POSIX_SIGNALS
343         CYG_ASSERT( sigmask == NULL,
344                     "pselect called with non-null sigmask without POSIX signal support"
345                     );
346 #endif
347
348         if (ts != NULL)
349         {
350             tv.tv_sec = ts->tv_sec;
351             tv.tv_usec = ts->tv_nsec/1000;
352         }
353
354         return cyg_pselect(nfd, in, out, ex, ts ? &tv : NULL, sigmask);
355 }
356
357 //==========================================================================
358 // Select support functions.
359
360 // -------------------------------------------------------------------------
361 // cyg_selinit() is used to initialize a selinfo structure
362
363 void cyg_selinit( struct CYG_SELINFO_TAG *sip )
364 {
365     sip->si_info = 0;
366     sip->si_waitFlag = 0;
367 }
368
369 // -------------------------------------------------------------------------
370 // cyg_selrecord() is called when a client device needs to register
371 // the current thread for selection. Save the flag that identifies the thread. 
372 void cyg_selrecord( CYG_ADDRWORD info, struct CYG_SELINFO_TAG *sip )
373 {
374     sip->si_info = info;
375     Cyg_Scheduler::lock();
376     sip->si_waitFlag |= SELECT_WAIT_FLAG_GET ();
377     Cyg_Scheduler::unlock();    
378 }
379
380 // -------------------------------------------------------------------------
381 // cyg_selwakeup() is called when the client device matches the select
382 // criterion, and needs to wake up a thread.
383 void cyg_selwakeup( struct CYG_SELINFO_TAG *sip )
384 {
385     // We don't actually use the si_info field of selinfo at present.
386     Cyg_Scheduler::lock();
387  
388     if( sip->si_waitFlag != 0 )
389     {
390         // If the flag is still present, this selection has not fired before. 
391         // Only wake up the threads waiting on the flags specified in si_waitFlag. 
392         // There is no need to wake threads that are not waiting for this data.
393         select_flag.setbits (sip->si_waitFlag); 
394         sip->si_waitFlag = 0;     // clear all flags
395         select_flag.maskbits (sip->si_waitFlag); 
396         selwake_count++;
397     }
398     Cyg_Scheduler::unlock();    
399 }
400
401 // -------------------------------------------------------------------------
402 // EOF select.cxx