]> git.kernelconcepts.de Git - karo-tx-redboot.git/blob - packages/io/fileio/v2_0/src/select.cxx
unified MX27, MX25, MX37 trees
[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 #include <sys/time.h>
71
72 #include <cyg/kernel/sched.hxx>        // scheduler definitions
73 #include <cyg/kernel/thread.hxx>       // thread definitions
74 #include <cyg/kernel/flag.hxx>         // flag definitions
75 #include <cyg/kernel/clock.hxx>        // clock definitions
76
77 #include <cyg/kernel/sched.inl>
78 #include <cyg/kernel/thread.inl>
79 #include <cyg/kernel/clock.inl>
80
81 //==========================================================================
82 // File object locking
83
84 #define LOCK_FILE( fp ) cyg_file_lock( fp )
85
86 #define UNLOCK_FILE( fp ) cyg_file_unlock( fp )
87
88 // Get a flag based on the thread's unique ID. Note: In a system with a large
89 // number of threads, the same flag may be used by more than one thread.
90 #define SELECT_WAIT_FLAG_GET() (1 << (Cyg_Thread::self()->get_unique_id() \
91                                 & (sizeof (Cyg_FlagValue) * NBBY - 1)))
92
93 //==========================================================================
94 // Local variables
95
96 static volatile cyg_uint32 selwake_count = 0;
97
98 // A flag is used to block a thread until data from the device is available. This
99 // prevents all threads from waking up at the same time and polling for changes.
100 // Each thread is allocated a flag bit via the SELECT_WAIT_FLAG_GET() macro when 
101 // the thread registers for selection via cyg_selrecord (). The flag is stored in
102 // the driver's select info block. Only those threads specified via the flags in 
103 // the select info are woken up by cyg_selwakeup (). 
104 // If there are more than 32 threads in the system, then there is a chance that
105 // cyg_selwakeup () may wake up more than one thread. Each thread then polls for
106 // changes.
107 static Cyg_Flag select_flag CYGBLD_ATTRIB_INIT_PRI(CYG_INIT_IO_FS);
108
109 //==========================================================================
110 // Timeval to ticks conversion support
111
112 // Converters from sec and us to ticks
113 static struct Cyg_Clock::converter us_converter, sec_converter;
114
115 static cyg_bool converters_initialized = false;
116
117 externC cyg_tick_count cyg_timeval_to_ticks( const struct timeval *tv )
118 {
119     if( !converters_initialized )
120     {
121         // Create the converters we need.
122         Cyg_Clock::real_time_clock->get_other_to_clock_converter( 1000, &us_converter );
123         Cyg_Clock::real_time_clock->get_other_to_clock_converter( 1000000000, &sec_converter );
124
125         converters_initialized = true;
126     }
127     
128     // Short circuit zero timeval
129     if( tv->tv_sec == 0 && tv->tv_usec == 0 )
130     {
131         return 0;
132     }
133         
134     // Convert the seconds field to ticks.
135     cyg_tick_count ticks = Cyg_Clock::convert( tv->tv_sec, &sec_converter );
136
137     // Convert the nanoseconds. This will round down to nearest whole tick.
138     ticks += Cyg_Clock::convert( (cyg_tick_count)tv->tv_usec, &us_converter );
139
140     return ticks;
141 }
142
143 //==========================================================================
144 // Select API function
145
146 static int
147 cyg_pselect(int nfd, fd_set *in, fd_set *out, fd_set *ex,
148            struct timeval *tv, const sigset_t *mask)
149 {
150     FILEIO_ENTRY();
151
152     int error = ENOERR;
153     int fd, mode, num;
154     cyg_file *fp;
155     fd_set in_res, out_res, ex_res;  // Result sets
156     fd_set *selection[3], *result[3];
157     cyg_tick_count ticks;
158     int mode_type[] = {CYG_FREAD, CYG_FWRITE, 0};
159     cyg_uint32 wake_count;
160     sigset_t oldmask;
161
162     Cyg_FlagValue myFlag = SELECT_WAIT_FLAG_GET ();
163     int    maxFdIndex = __howmany(nfd, __NFDBITS); // size of fd sets
164
165     // Make sure the nfd < FD_SETSIZE, a value greater than FD_SETSIZE 
166     // would break the results sets
167     if(nfd > FD_SETSIZE)
168     {
169         FILEIO_RETURN(EINVAL);
170     }
171
172     FD_ZERO(&in_res);
173     FD_ZERO(&out_res);
174     FD_ZERO(&ex_res);
175
176     // Set up sets
177     selection[0] = in;   result[0] = &in_res;
178     selection[1] = out;  result[1] = &out_res;
179     selection[2] = ex;   result[2] = &ex_res;
180
181     // Compute end time
182     if (tv)
183         ticks = cyg_timeval_to_ticks( tv );
184     else ticks = 0;
185
186     // Scan sets for possible I/O until something found, timeout or error.
187     while (!error)
188     {
189         wake_count = selwake_count;
190
191         num = 0;  // Total file descriptors "ready"
192         for (mode = 0;  !error && mode < 3;  mode++)
193         {
194             if (selection[mode]) 
195             {
196                 fd_mask *fds_bits = selection[mode]->fds_bits;
197                 int      index, fdbase;
198                 for(index = 0, fdbase = 0; !error && index < maxFdIndex; index++, fdbase += __NFDBITS)
199                 {
200                     fd_mask mask = fds_bits[index];
201                     for(fd = fdbase; mask != 0; fd++, mask >>= 1)
202                     {
203                         if(mask & 1)
204                         {
205                             fp = cyg_fp_get( fd );
206                             if( fp == NULL )
207                             {
208                                 error = EBADF;
209                                 break;
210                             }
211
212                             if ((*fp->f_ops->fo_select)(fp, mode_type[mode], 0))
213                             {
214                                 FD_SET(fd, result[mode]);
215                                 num++;
216                             }
217                             cyg_fp_free( fp );
218                         }
219                     }
220                 }
221             }
222         }
223
224         if (error)
225             break;
226         
227         if (num)
228         {
229             // Found something, update user's sets
230             if (in)  FD_COPY( &in_res, in );
231             if (out) FD_COPY( &out_res, out );
232             if (ex)  FD_COPY( &ex_res, ex );
233             CYG_FILEIO_DELIVER_SIGNALS( mask );
234             FILEIO_RETURN_VALUE(num);
235         }
236
237         Cyg_Scheduler::lock();
238
239         // Switch to the supplied signal mask. This will permit delivery
240         // of any signals that might terminate this select operation.
241         
242         CYG_FILEIO_SIGMASK_SET( mask, &oldmask );
243     
244         do
245         {
246
247             // We need to see if any signals have been posted while we
248             // were testing all those files. The handlers will not
249             // have run because we have ASRs inhibited but the signal
250             // will have been set pending.
251
252             if( CYG_FILEIO_SIGPENDING() )
253             {
254                 // There are pending signals so we need to terminate
255                 // the select operation and return EINTR. Handlers for
256                 // the pending signals will be called just before we
257                 // return.
258
259                 error = EINTR;
260                 break;
261             }
262             
263             if( wake_count == selwake_count )
264             {
265                 // Nothing found, see if we want to wait
266                 if (tv)
267                 {
268                     // Special case of "poll"
269                     if (ticks == 0)
270                     {
271                         error = EAGAIN;
272                         break;
273                     }
274
275                     ticks += Cyg_Clock::real_time_clock->current_value();
276                 
277                     if( !select_flag.wait (myFlag, Cyg_Flag::OR, ticks) )
278                     {
279                         // A non-standard wakeup, if the current time is equal to
280                         // or past the timeout, return zero. Otherwise return
281                         // EINTR, since we have been released.
282
283                         if( Cyg_Clock::real_time_clock->current_value() >= ticks )
284                         {
285                             error = EAGAIN;
286                             break;
287                         }
288                         else error = EINTR;
289                     }
290
291                     ticks -= Cyg_Clock::real_time_clock->current_value();
292                 }
293                 else
294                 {
295                     // Wait forever (until something happens)
296                     if( !select_flag.wait (myFlag, Cyg_Flag::OR) )
297                         error = EINTR;
298                 }
299             }
300
301         } while(0);
302
303         CYG_FILEIO_SIGMASK_SET( &oldmask, NULL );
304         
305         Cyg_Scheduler::unlock();
306         
307     } // while(!error)
308
309     // If the error code is EAGAIN, this means that a timeout has
310     // happened. We return zero in that case, rather than a proper
311     // error code.
312     // If the error code is EINTR, then a signal may be pending
313     // delivery. Call back into the POSIX package to handle it.
314     
315     if( error == EAGAIN )
316         FILEIO_RETURN_VALUE(0);
317     else if( error == EINTR )
318         CYG_FILEIO_DELIVER_SIGNALS( mask );
319
320     FILEIO_RETURN(error);
321 }
322
323 // -------------------------------------------------------------------------
324 // Select API function
325
326 __externC int
327 select(int nfd, fd_set *in, fd_set *out, fd_set *ex, struct timeval *tv)
328 {
329         return cyg_pselect(nfd, in, out, ex, tv, NULL);
330 }
331
332 // -------------------------------------------------------------------------
333 // Pselect API function
334 //
335 // This is derived from the POSIX-200X specification.
336
337 __externC int
338 pselect(int nfd, fd_set *in, fd_set *out, fd_set *ex,
339         const struct timespec *ts, const sigset_t *sigmask)
340 {
341         struct timeval tv;
342
343 #ifndef CYGPKG_POSIX_SIGNALS
344         CYG_ASSERT( sigmask == NULL,
345                     "pselect called with non-null sigmask without POSIX signal support"
346                     );
347 #endif
348
349         if (ts != NULL)
350         {
351             tv.tv_sec = ts->tv_sec;
352             tv.tv_usec = ts->tv_nsec/1000;
353         }
354
355         return cyg_pselect(nfd, in, out, ex, ts ? &tv : NULL, sigmask);
356 }
357
358 //==========================================================================
359 // Select support functions.
360
361 // -------------------------------------------------------------------------
362 // cyg_selinit() is used to initialize a selinfo structure
363
364 void cyg_selinit( struct CYG_SELINFO_TAG *sip )
365 {
366     sip->si_info = 0;
367     sip->si_waitFlag = 0;
368 }
369
370 // -------------------------------------------------------------------------
371 // cyg_selrecord() is called when a client device needs to register
372 // the current thread for selection. Save the flag that identifies the thread. 
373 void cyg_selrecord( CYG_ADDRWORD info, struct CYG_SELINFO_TAG *sip )
374 {
375     sip->si_info = info;
376     Cyg_Scheduler::lock();
377     sip->si_waitFlag |= SELECT_WAIT_FLAG_GET ();
378     Cyg_Scheduler::unlock();    
379 }
380
381 // -------------------------------------------------------------------------
382 // cyg_selwakeup() is called when the client device matches the select
383 // criterion, and needs to wake up a thread.
384 void cyg_selwakeup( struct CYG_SELINFO_TAG *sip )
385 {
386     // We don't actually use the si_info field of selinfo at present.
387     Cyg_Scheduler::lock();
388  
389     if( sip->si_waitFlag != 0 )
390     {
391         // If the flag is still present, this selection has not fired before. 
392         // Only wake up the threads waiting on the flags specified in si_waitFlag. 
393         // There is no need to wake threads that are not waiting for this data.
394         select_flag.setbits (sip->si_waitFlag); 
395         sip->si_waitFlag = 0;     // clear all flags
396         select_flag.maskbits (sip->si_waitFlag); 
397         selwake_count++;
398     }
399     Cyg_Scheduler::unlock();    
400 }
401
402 // -------------------------------------------------------------------------
403 // EOF select.cxx