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