1 /****************************************************************************
2 * fs/vfs/fs_select.c
3 *
4 * Copyright (C) 2008-2009, 2012-2013 Gregory Nutt. All rights reserved.
5 * Author: Gregory Nutt <gnutt@nuttx.org>
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * 3. Neither the name NuttX nor the names of its contributors may be
18 * used to endorse or promote products derived from this software
19 * without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
28 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
33 *
34 ****************************************************************************/
35
36 /****************************************************************************
37 * Included Files
38 ****************************************************************************/
39
40 #include "vfs_config.h"
41
42 #include "sys/select.h"
43
44 #include "string.h"
45 #include "unistd.h"
46 #include "poll.h"
47 #include "assert.h"
48 #include "errno.h"
49
50 #include "stdlib.h"
51
52 #include "los_signal.h"
53 #include "los_syscall.h"
54
55 #ifndef CONFIG_DISABLE_POLL
56
57
58 /****************************************************************************
59 * Pre-processor Definitions
60 ****************************************************************************/
61
62 #define POLL_IN_SET (POLLIN | POLLRDNORM | POLLRDBAND | POLLHUP | POLLERR)
63 #define POLL_OUT_SET (POLLOUT | POLLWRBAND | POLLWRNORM | POLLERR)
64 #define POLL_EX_SET (POLLPRI)
65
66 /* pollfd count in stack, optimization in order to avoid small memory allocation */
67
68 #define POLL_STACK_CNT 5
69
70 /****************************************************************************
71 * Private Functions
72 ****************************************************************************/
73
74 /****************************************************************************
75 * Public Functions
76 ****************************************************************************/
77 extern unsigned int sleep(unsigned int seconds);
78
79 /****************************************************************************
80 * Name: select
81 *
82 * Description:
83 * select() allows a program to monitor multiple file descriptors, waiting
84 * until one or more of the file descriptors become "ready" for some class
85 * of I/O operation (e.g., input possible). A file descriptor is
86 * considered ready if it is possible to perform the corresponding I/O
87 * operation (e.g., read(2)) without blocking.
88 *
89 * NOTE: poll() is the fundamental API for performing such monitoring
90 * operation under NuttX. select() is provided for compatibility and
91 * is simply a layer of added logic on top of poll(). As such, select()
92 * is more wasteful of resources and poll() is the recommended API to be
93 * used.
94 *
95 * Input Parameters:
96 * nfds - the maximum fd number (+1) of any descriptor in any of the
97 * three sets.
98 * readfds - the set of descriptions to monitor for read-ready events
99 * writefds - the set of descriptions to monitor for write-ready events
100 * exceptfds - the set of descriptions to monitor for error events
101 * timeout - Return at this time if none of these events of interest
102 * occur.
103 *
104 * Returned Value:
105 * 0: Timer expired
106 * >0: The number of bits set in the three sets of descriptors
107 * -1: An error occurred (errno will be set appropriately)
108 *
109 ****************************************************************************/
110
do_select(int nfds,fd_set * readfds,fd_set * writefds,fd_set * exceptfds,struct timeval * timeout,PollFun poll)111 int do_select(int nfds, fd_set *readfds, fd_set *writefds,
112 fd_set *exceptfds, struct timeval *timeout, PollFun poll)
113 {
114 struct pollfd *pollset = NULL;
115 struct pollfd pfd[POLL_STACK_CNT];
116 int pfd_alloc_flag = 0;
117 int fd;
118 int npfds;
119 int msec;
120 int ndx;
121 int ret;
122
123 if (nfds < 0)
124 {
125 set_errno(EINVAL);
126 return VFS_ERROR;
127 }
128
129 /* How many pollfd structures do we need to allocate? */
130
131 /* Initialize the descriptor list for poll() */
132
133 for (fd = 0, npfds = 0; fd < nfds; fd++)
134 {
135 /* Check if any monitor operation is requested on this fd */
136
137 if ((readfds && FD_ISSET(fd, readfds)) ||
138 (writefds && FD_ISSET(fd, writefds)) ||
139 (exceptfds && FD_ISSET(fd, exceptfds)))
140 {
141 /* Yes.. increment the count of pollfds structures needed */
142
143 npfds++;
144 }
145 }
146
147 /* Allocate the descriptor list for poll() */
148
149 if (npfds != 0)
150 {
151 /* use stack variable in order to avoid small memory allocation. */
152
153 if (npfds <= POLL_STACK_CNT)
154 {
155 pollset = pfd;
156 (void)memset_s(pollset, npfds * sizeof(struct pollfd), 0, npfds * sizeof(struct pollfd));
157 }
158 else
159 {
160 pollset = (struct pollfd *)zalloc(npfds * sizeof(struct pollfd));
161 if (pollset == NULL)
162 {
163 set_errno(ENOMEM);
164 return VFS_ERROR;
165 }
166 pfd_alloc_flag = 1;
167 }
168 }
169 else
170 {
171 /* If the readfds, writefds, and exceptfds arguments are all null pointers and
172 * the timeout argument is not a null pointer, the select() function shall block for
173 * the time specified. If the readfds, writefds, and exceptfds arguments are all
174 * null pointers and the timeout argument is a null pointer, this is NOT permitted
175 * as LiteOS doesn't support Signal machanism, so select() can't come back anymore.
176 */
177
178 if (timeout != NULL)
179 {
180 /* 1000000 : Convert seconds to microseconds. */
181
182 if ((long long)timeout->tv_sec * 1000000 > 0xffffffff)
183 {
184 (void)sleep(timeout->tv_sec);
185 }
186 else
187 {
188 (void)usleep(timeout->tv_sec * 1000000 + timeout->tv_usec);
189 }
190 return OK;
191 }
192 else
193 {
194 set_errno(EINVAL);
195 return VFS_ERROR;
196 }
197 }
198
199 /* Initialize the descriptor list for poll() */
200
201 for (fd = 0, ndx = 0; fd < nfds; fd++)
202 {
203 int incr = 0;
204
205 /* The readfs set holds the set of FDs that the caller can be assured
206 * of reading from without blocking. Note that POLLHUP is included as
207 * a read-able condition. POLLHUP will be reported at the end-of-file
208 * or when a connection is lost. In either case, the read() can then
209 * be performed without blocking.
210 */
211
212 if (readfds && FD_ISSET(fd, readfds))
213 {
214 pollset[ndx].fd = fd;
215 pollset[ndx].events |= (POLLIN | POLLRDNORM);
216 incr = 1;
217 }
218
219 /* The writefds set holds the set of FDs that the caller can be assured
220 * of writing to without blocking.
221 */
222
223 if (writefds && FD_ISSET(fd, writefds))
224 {
225 pollset[ndx].fd = fd;
226 pollset[ndx].events |= (POLLOUT | POLLWRNORM);
227 incr = 1;
228 }
229
230 /* The exceptfds set holds the set of FDs that are watched for exceptions */
231
232 if (exceptfds && FD_ISSET(fd, exceptfds))
233 {
234 pollset[ndx].fd = fd;
235 incr = 1;
236 }
237
238 ndx += incr;
239 }
240
241 DEBUGASSERT(ndx == npfds);
242
243 /* Convert the timeout to milliseconds */
244
245 if (timeout)
246 {
247 /* Calculate the timeout in milliseconds */
248
249 msec = timeout->tv_sec * 1000 + timeout->tv_usec / 1000;
250 }
251 else
252 {
253 /* Any negative value of msec means no timeout */
254
255 msec = -1;
256 }
257
258 /* Then let poll do all of the real work. */
259
260 ret = poll(pollset, npfds, msec);
261
262 /* Now set up the return values */
263
264 if (readfds)
265 {
266 (void)memset_s(readfds, sizeof(fd_set), 0, sizeof(fd_set));
267 }
268
269 if (writefds)
270 {
271 (void)memset_s(writefds, sizeof(fd_set), 0, sizeof(fd_set));
272 }
273
274 if (exceptfds)
275 {
276 (void)memset_s(exceptfds, sizeof(fd_set), 0, sizeof(fd_set));
277 }
278
279 /* Convert the poll descriptor list back into selects 3 bitsets */
280
281 if (ret > 0)
282 {
283 ret = 0;
284 for (ndx = 0; ndx < npfds; ndx++)
285 {
286 /* Check for read conditions. Note that POLLHUP is included as a
287 * read condition. POLLHUP will be reported when no more data will
288 * be available (such as when a connection is lost). In either
289 * case, the read() can then be performed without blocking.
290 */
291
292 if (readfds)
293 {
294 if (pollset[ndx].revents & POLL_IN_SET)
295 {
296 FD_SET(pollset[ndx].fd, readfds);
297 ret++;
298 }
299 }
300
301 /* Check for write conditions */
302
303 if (writefds)
304 {
305 if (pollset[ndx].revents & POLL_OUT_SET)
306 {
307 FD_SET(pollset[ndx].fd, writefds);
308 ret++;
309 }
310 }
311
312 /* Check for exceptions */
313
314 if (exceptfds)
315 {
316 if (pollset[ndx].revents & POLL_EX_SET)
317 {
318 FD_SET(pollset[ndx].fd, exceptfds);
319 ret++;
320 }
321 }
322 }
323 }
324
325 if (pfd_alloc_flag)
326 {
327 free(pollset);
328 }
329 return ret;
330 }
331
select(int nfds,fd_set * readfds,fd_set * writefds,fd_set * exceptfds,struct timeval * timeout)332 int select(int nfds, fd_set *readfds, fd_set *writefds,
333 fd_set *exceptfds, struct timeval *timeout)
334 {
335 return do_select(nfds, readfds, writefds, exceptfds, timeout, poll);
336 }
337
338 #endif /* CONFIG_DISABLE_POLL */
339