1 /*
2  * Copyright (c) 2006-2021, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author            Notes
8  * 2016-12-28     Bernard           first version
9  * 2024-04-08     TroyMitchell      Add all function comments
10  */
11 
12 #include <rtthread.h>
13 #include <poll.h>
14 #include <sys/select.h>
15 
16 /**
17  * @brief   Initialize the file descriptor set to have zero bits for all file descriptors.
18  * @param   set     Pointer to the file descriptor set to be initialized.
19  * @param   nfds    The maximum file descriptor in the set plus one.
20  * @note    The actual size of the 'fd_set' is determined based on the parameter 'nfds'.
21  */
fdszero(fd_set * set,int nfds)22 static void fdszero(fd_set *set, int nfds)
23 {
24     fd_mask *m;
25     int n;
26 
27     /*
28       The 'sizeof(fd_set)' of the system space may differ from user space,
29       so the actual size of the 'fd_set' is determined here with the parameter 'nfds'
30     */
31     m = (fd_mask *)set;
32     for (n = 0; n < nfds; n += (sizeof(fd_mask) * 8))
33     {
34         rt_memset(m, 0, sizeof(fd_mask));
35         m ++;
36     }
37 }
38 
39 /**
40  * @brief   Synchronous I/O multiplexing: multiplex input/output over a set of file descriptors.
41  * @param   nfds        The highest-numbered file descriptor in any of the three sets, plus 1.
42  * @param   readfds     A pointer to a set of file descriptors to be checked for read readiness.
43  * @param   writefds    A pointer to a set of file descriptors to be checked for write readiness.
44  * @param   exceptfds   A pointer to a set of file descriptors to be checked for exceptions.
45  * @param   timeout     The maximum time to wait for any of the specified file descriptors to become ready.
46  * @return  Upon successful completion, the total number of file descriptors in all the sets that are ready for the requested operation is returned; otherwise, -1 is returned on error.
47  */
select(int nfds,fd_set * readfds,fd_set * writefds,fd_set * exceptfds,struct timeval * timeout)48 int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)
49 {
50     int fd;
51     int npfds;
52     int msec;
53     int ndx;
54     int ret;
55     struct pollfd *pollset = RT_NULL;
56 
57     /* How many pollfd structures do we need to allocate? */
58     for (fd = 0, npfds = 0; fd < nfds; fd++)
59     {
60         /* Check if any monitor operation is requested on this fd */
61         if ((readfds   && FD_ISSET(fd, readfds))  ||
62             (writefds  && FD_ISSET(fd, writefds)) ||
63             (exceptfds && FD_ISSET(fd, exceptfds)))
64         {
65             npfds++;
66         }
67     }
68 
69     /* Allocate the descriptor list for poll() */
70     if (npfds > 0)
71     {
72         pollset = (struct pollfd *)rt_calloc(npfds, sizeof(struct pollfd));
73         if (!pollset)
74         {
75             return -1;
76         }
77     }
78 
79     /* Initialize the descriptor list for poll() */
80     for (fd = 0, ndx = 0; fd < nfds; fd++)
81     {
82         int incr = 0;
83 
84         /* The readfs set holds the set of FDs that the caller can be assured
85          * of reading from without blocking.  Note that POLLHUP is included as
86          * a read-able condition.  POLLHUP will be reported at the end-of-file
87          * or when a connection is lost.  In either case, the read() can then
88          * be performed without blocking.
89          */
90 
91         if (readfds && FD_ISSET(fd, readfds))
92         {
93             pollset[ndx].fd         = fd;
94             pollset[ndx].events |= POLLIN;
95             incr = 1;
96         }
97 
98         if (writefds && FD_ISSET(fd, writefds))
99         {
100             pollset[ndx].fd      = fd;
101             pollset[ndx].events |= POLLOUT;
102             incr = 1;
103         }
104 
105         if (exceptfds && FD_ISSET(fd, exceptfds))
106         {
107             pollset[ndx].fd = fd;
108             incr = 1;
109         }
110 
111         ndx += incr;
112     }
113 
114     RT_ASSERT(ndx == npfds);
115 
116     /* Convert the timeout to milliseconds */
117     if (timeout)
118     {
119         msec = (int)timeout->tv_sec * 1000 + (int)timeout->tv_usec / 1000;
120     }
121     else
122     {
123         msec = -1;
124     }
125 
126     /* Then let poll do all of the real work. */
127 
128     ret = poll(pollset, npfds, msec);
129 
130     /* Now set up the return values */
131     if (readfds)
132     {
133         fdszero(readfds, nfds);
134     }
135 
136     if (writefds)
137     {
138         fdszero(writefds, nfds);
139     }
140 
141     if (exceptfds)
142     {
143         fdszero(exceptfds, nfds);
144     }
145 
146     /* Convert the poll descriptor list back into selects 3 bitsets */
147 
148     if (ret > 0)
149     {
150         ret = 0;
151         for (ndx = 0; ndx < npfds; ndx++)
152         {
153             /* Check for read conditions.  Note that POLLHUP is included as a
154              * read condition.  POLLHUP will be reported when no more data will
155              * be available (such as when a connection is lost).  In either
156              * case, the read() can then be performed without blocking.
157              */
158 
159             if (readfds)
160             {
161                 if (pollset[ndx].revents & (POLLIN | POLLHUP))
162                 {
163                     FD_SET(pollset[ndx].fd, readfds);
164                     ret++;
165                 }
166             }
167 
168             /* Check for write conditions */
169             if (writefds)
170             {
171                 if (pollset[ndx].revents & POLLOUT)
172                 {
173                     FD_SET(pollset[ndx].fd, writefds);
174                     ret++;
175                 }
176             }
177 
178             /* Check for exceptions */
179             if (exceptfds)
180             {
181                 if (pollset[ndx].revents & POLLERR)
182                 {
183                     FD_SET(pollset[ndx].fd, exceptfds);
184                     ret++;
185                 }
186             }
187         }
188     }
189 
190     if (pollset) rt_free(pollset);
191 
192     return ret;
193 }
194