1 /*
2 * Copyright (c) 2018 Linaro Limited
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/kernel.h>
8 #include <zephyr/syscall_handler.h>
9 #include <zephyr/sys/math_extras.h>
10 #include <zephyr/net/socket.h>
11 #include "sockets_internal.h"
12
13 /* Get size, in elements, of an array within a struct. */
14 #define STRUCT_MEMBER_ARRAY_SIZE(type, field) ARRAY_SIZE(((type *)0)->field)
15
16 /* Returns results in word_idx and bit_mask "output" params */
17 #define FD_SET_CALC_OFFSETS(set, word_idx, bit_mask) { \
18 unsigned int b_idx = fd % (sizeof(set->bitset[0]) * 8); \
19 word_idx = fd / (sizeof(set->bitset[0]) * 8); \
20 bit_mask = 1 << b_idx; \
21 }
22
ZSOCK_FD_ZERO(zsock_fd_set * set)23 void ZSOCK_FD_ZERO(zsock_fd_set *set)
24 {
25 int i;
26
27 for (i = 0; i < ARRAY_SIZE(set->bitset); i++) {
28 set->bitset[i] = 0U;
29 }
30 }
31
ZSOCK_FD_ISSET(int fd,zsock_fd_set * set)32 int ZSOCK_FD_ISSET(int fd, zsock_fd_set *set)
33 {
34 uint32_t word_idx, bit_mask;
35
36 if (fd < 0 || fd >= ZSOCK_FD_SETSIZE) {
37 return 0;
38 }
39
40 FD_SET_CALC_OFFSETS(set, word_idx, bit_mask);
41
42 return (set->bitset[word_idx] & bit_mask) != 0U;
43 }
44
ZSOCK_FD_CLR(int fd,zsock_fd_set * set)45 void ZSOCK_FD_CLR(int fd, zsock_fd_set *set)
46 {
47 uint32_t word_idx, bit_mask;
48
49 if (fd < 0 || fd >= ZSOCK_FD_SETSIZE) {
50 return;
51 }
52
53 FD_SET_CALC_OFFSETS(set, word_idx, bit_mask);
54
55 set->bitset[word_idx] &= ~bit_mask;
56 }
57
ZSOCK_FD_SET(int fd,zsock_fd_set * set)58 void ZSOCK_FD_SET(int fd, zsock_fd_set *set)
59 {
60 uint32_t word_idx, bit_mask;
61
62 if (fd < 0 || fd >= ZSOCK_FD_SETSIZE) {
63 return;
64 }
65
66 FD_SET_CALC_OFFSETS(set, word_idx, bit_mask);
67
68 set->bitset[word_idx] |= bit_mask;
69 }
70
z_impl_zsock_select(int nfds,zsock_fd_set * readfds,zsock_fd_set * writefds,zsock_fd_set * exceptfds,struct zsock_timeval * timeout)71 int z_impl_zsock_select(int nfds, zsock_fd_set *readfds, zsock_fd_set *writefds,
72 zsock_fd_set *exceptfds, struct zsock_timeval *timeout)
73 {
74 struct zsock_pollfd pfds[CONFIG_NET_SOCKETS_POLL_MAX];
75 k_timeout_t poll_timeout;
76 int i, res;
77 int num_pfds = 0;
78 int num_selects = 0;
79 int fd_no = 0;
80
81 for (i = 0; i < STRUCT_MEMBER_ARRAY_SIZE(zsock_fd_set, bitset); i++) {
82 uint32_t bit_mask = 1U;
83 uint32_t read_mask = 0U, write_mask = 0U, except_mask = 0U;
84 uint32_t ored_mask;
85
86 if (readfds != NULL) {
87 read_mask = readfds->bitset[i];
88 }
89
90 if (writefds != NULL) {
91 write_mask = writefds->bitset[i];
92 }
93
94 if (exceptfds != NULL) {
95 except_mask = exceptfds->bitset[i];
96 }
97
98 ored_mask = read_mask | write_mask | except_mask;
99 if (ored_mask == 0U) {
100 fd_no += sizeof(ored_mask) * 8;
101 continue;
102 }
103
104 do {
105 if (ored_mask & bit_mask) {
106 int events = 0;
107
108 if (num_pfds >= ARRAY_SIZE(pfds)) {
109 errno = ENOMEM;
110 return -1;
111 }
112
113 if (read_mask & bit_mask) {
114 events |= ZSOCK_POLLIN;
115 }
116
117 if (write_mask & bit_mask) {
118 events |= ZSOCK_POLLOUT;
119 }
120
121 if (except_mask & bit_mask) {
122 events |= ZSOCK_POLLPRI;
123 }
124
125 pfds[num_pfds].fd = fd_no;
126 pfds[num_pfds++].events = events;
127 }
128
129 bit_mask <<= 1;
130 fd_no++;
131 } while (bit_mask != 0U);
132 }
133
134 if (timeout == NULL) {
135 poll_timeout = K_FOREVER;
136 } else {
137 poll_timeout =
138 K_USEC(timeout->tv_sec * 1000000UL + timeout->tv_usec);
139 }
140
141 res = zsock_poll_internal(pfds, num_pfds, poll_timeout);
142 if (res == -1) {
143 return -1;
144 }
145
146 if (readfds != NULL) {
147 ZSOCK_FD_ZERO(readfds);
148 }
149
150 if (writefds != NULL) {
151 ZSOCK_FD_ZERO(writefds);
152 }
153
154 if (exceptfds != NULL) {
155 ZSOCK_FD_ZERO(exceptfds);
156 }
157
158 for (i = 0; i < num_pfds && res > 0; i++) {
159 short revents = pfds[i].revents;
160 int fd = pfds[i].fd;
161
162 if (revents == 0) {
163 continue;
164 }
165
166 /* POSIX: "EBADF: One or more of the file descriptor sets
167 * specified a file descriptor that is not a valid open
168 * file descriptor."
169 * So, unlike poll(), a single invalid fd aborts the entire
170 * select().
171 */
172 if (revents & ZSOCK_POLLNVAL) {
173 errno = EBADF;
174 return -1;
175 }
176
177 if (revents & ZSOCK_POLLIN) {
178 if (readfds != NULL) {
179 ZSOCK_FD_SET(fd, readfds);
180 num_selects++;
181 }
182 }
183
184 if (revents & ZSOCK_POLLOUT) {
185 if (writefds != NULL) {
186 ZSOCK_FD_SET(fd, writefds);
187 num_selects++;
188 }
189 }
190
191 /* It's unclear if HUP/ERR belong here. At least not ignore
192 * them. Zephyr doesn't use HUP and barely use ERR so far.
193 */
194 if (revents & (ZSOCK_POLLPRI | ZSOCK_POLLHUP | ZSOCK_POLLERR)) {
195 if (exceptfds != NULL) {
196 ZSOCK_FD_SET(fd, exceptfds);
197 num_selects++;
198 }
199 }
200
201 res--;
202 }
203
204 return num_selects;
205 }
206
207 #ifdef CONFIG_USERSPACE
z_vrfy_zsock_select(int nfds,zsock_fd_set * readfds,zsock_fd_set * writefds,zsock_fd_set * exceptfds,struct zsock_timeval * timeout)208 static int z_vrfy_zsock_select(int nfds, zsock_fd_set *readfds,
209 zsock_fd_set *writefds,
210 zsock_fd_set *exceptfds,
211 struct zsock_timeval *timeout)
212 {
213 zsock_fd_set *readfds_copy = NULL, *writefds_copy = NULL,
214 *exceptfds_copy = NULL;
215 struct zsock_timeval *timeval = NULL;
216 int ret = -1;
217
218 if (readfds) {
219 readfds_copy = z_user_alloc_from_copy((void *)readfds,
220 sizeof(zsock_fd_set));
221 if (!readfds_copy) {
222 errno = ENOMEM;
223 goto out;
224 }
225 }
226
227 if (writefds) {
228 writefds_copy = z_user_alloc_from_copy((void *)writefds,
229 sizeof(zsock_fd_set));
230 if (!writefds_copy) {
231 errno = ENOMEM;
232 goto out;
233 }
234 }
235
236 if (exceptfds) {
237 exceptfds_copy = z_user_alloc_from_copy((void *)exceptfds,
238 sizeof(zsock_fd_set));
239 if (!exceptfds_copy) {
240 errno = ENOMEM;
241 goto out;
242 }
243 }
244
245 if (timeout) {
246 timeval = z_user_alloc_from_copy((void *)timeout,
247 sizeof(struct zsock_timeval));
248 if (!timeval) {
249 errno = ENOMEM;
250 goto out;
251 }
252 }
253
254 ret = z_impl_zsock_select(nfds, readfds_copy, writefds_copy,
255 exceptfds_copy, timeval);
256
257 if (ret >= 0) {
258 if (readfds_copy) {
259 z_user_to_copy((void *)readfds, readfds_copy,
260 sizeof(zsock_fd_set));
261 }
262
263 if (writefds_copy) {
264 z_user_to_copy((void *)writefds, writefds_copy,
265 sizeof(zsock_fd_set));
266 }
267
268 if (exceptfds_copy) {
269 z_user_to_copy((void *)exceptfds, exceptfds_copy,
270 sizeof(zsock_fd_set));
271 }
272 }
273
274 out:
275 k_free(timeval);
276 k_free(readfds_copy);
277 k_free(writefds_copy);
278 k_free(exceptfds_copy);
279
280 return ret;
281 }
282 #include <syscalls/zsock_select_mrsh.c>
283 #endif
284