1 /** @file
2 * @brief Modem socket / packet size handler
3 *
4 * Generic modem socket and packet size implementation for modem context
5 */
6
7 /*
8 * Copyright (c) 2019-2020 Foundries.io
9 *
10 * SPDX-License-Identifier: Apache-2.0
11 */
12
13 #include <zephyr/kernel.h>
14 #include <zephyr/sys/fdtable.h>
15
16 #include "modem_socket.h"
17
18 /*
19 * Packet Size Support Functions
20 */
21
modem_socket_next_packet_size(struct modem_socket_config * cfg,struct modem_socket * sock)22 uint16_t modem_socket_next_packet_size(struct modem_socket_config *cfg, struct modem_socket *sock)
23 {
24 uint16_t total = 0U;
25
26 k_sem_take(&cfg->sem_lock, K_FOREVER);
27
28 if (!sock || !sock->packet_count) {
29 goto exit;
30 }
31
32 total = sock->packet_sizes[0];
33
34 exit:
35 k_sem_give(&cfg->sem_lock);
36 return total;
37 }
38
modem_socket_packet_get_total(struct modem_socket * sock)39 static uint16_t modem_socket_packet_get_total(struct modem_socket *sock)
40 {
41 int i;
42 uint16_t total = 0U;
43
44 if (!sock || !sock->packet_count) {
45 return 0U;
46 }
47
48 for (i = 0; i < sock->packet_count; i++) {
49 total += sock->packet_sizes[i];
50 }
51
52 return total;
53 }
54
modem_socket_packet_drop_first(struct modem_socket * sock)55 static int modem_socket_packet_drop_first(struct modem_socket *sock)
56 {
57 int i;
58
59 if (!sock || !sock->packet_count) {
60 return -EINVAL;
61 }
62
63 sock->packet_count--;
64 for (i = 0; i < sock->packet_count; i++) {
65 sock->packet_sizes[i] = sock->packet_sizes[i + 1];
66 }
67
68 sock->packet_sizes[sock->packet_count] = 0U;
69 return 0;
70 }
71
modem_socket_packet_size_update(struct modem_socket_config * cfg,struct modem_socket * sock,int new_total)72 int modem_socket_packet_size_update(struct modem_socket_config *cfg, struct modem_socket *sock,
73 int new_total)
74 {
75 uint16_t old_total = 0U;
76
77 if (!sock) {
78 return -EINVAL;
79 }
80
81 k_sem_take(&cfg->sem_lock, K_FOREVER);
82
83 if (new_total < 0) {
84 new_total += modem_socket_packet_get_total(sock);
85 }
86
87 if (new_total <= 0) {
88 /* reset outstanding value here */
89 sock->packet_count = 0U;
90 sock->packet_sizes[0] = 0U;
91 k_poll_signal_reset(&sock->sig_data_ready);
92 k_sem_give(&cfg->sem_lock);
93 return 0;
94 }
95
96 old_total = modem_socket_packet_get_total(sock);
97 if (new_total == old_total) {
98 goto data_ready;
99 }
100
101 /* remove sent packets */
102 if (new_total < old_total) {
103 /* remove packets that are not included in new_size */
104 while (old_total > new_total && sock->packet_count > 0) {
105 /* handle partial read */
106 if (old_total - new_total < sock->packet_sizes[0]) {
107 sock->packet_sizes[0] -= old_total - new_total;
108 break;
109 }
110
111 old_total -= sock->packet_sizes[0];
112 modem_socket_packet_drop_first(sock);
113 }
114
115 goto data_ready;
116 }
117
118 /* new packet to add */
119 if (sock->packet_count >= CONFIG_MODEM_SOCKET_PACKET_COUNT) {
120 k_sem_give(&cfg->sem_lock);
121 return -ENOMEM;
122 }
123
124 if (new_total - old_total > 0) {
125 sock->packet_sizes[sock->packet_count] = new_total - old_total;
126 sock->packet_count++;
127 } else {
128 k_sem_give(&cfg->sem_lock);
129 return -EINVAL;
130 }
131
132 data_ready:
133 if (sock->packet_sizes[0]) {
134 k_poll_signal_raise(&sock->sig_data_ready, 0);
135 } else {
136 k_poll_signal_reset(&sock->sig_data_ready);
137 }
138 k_sem_give(&cfg->sem_lock);
139 return new_total;
140 }
141
142 /*
143 * Socket Support Functions
144 */
145
146 /*
147 * This function reserves a file descriptor from the fdtable, make sure to update the
148 * POSIX_FDS_MAX Kconfig option to support at minimum the required amount of sockets
149 */
modem_socket_get(struct modem_socket_config * cfg,int family,int type,int proto)150 int modem_socket_get(struct modem_socket_config *cfg, int family, int type, int proto)
151 {
152 int i;
153
154 k_sem_take(&cfg->sem_lock, K_FOREVER);
155
156 for (i = 0; i < cfg->sockets_len; i++) {
157 if (cfg->sockets[i].id < cfg->base_socket_id) {
158 break;
159 }
160 }
161
162 if (i >= cfg->sockets_len) {
163 k_sem_give(&cfg->sem_lock);
164 return -ENOMEM;
165 }
166
167 cfg->sockets[i].sock_fd = z_reserve_fd();
168 if (cfg->sockets[i].sock_fd < 0) {
169 k_sem_give(&cfg->sem_lock);
170 return -errno;
171 }
172
173 cfg->sockets[i].family = family;
174 cfg->sockets[i].type = type;
175 cfg->sockets[i].ip_proto = proto;
176 cfg->sockets[i].id = (cfg->assign_id) ? (i + cfg->base_socket_id) :
177 (cfg->base_socket_id + cfg->sockets_len);
178 z_finalize_fd(cfg->sockets[i].sock_fd, &cfg->sockets[i],
179 (const struct fd_op_vtable *)cfg->vtable);
180
181 k_sem_give(&cfg->sem_lock);
182 return cfg->sockets[i].sock_fd;
183 }
184
modem_socket_from_fd(struct modem_socket_config * cfg,int sock_fd)185 struct modem_socket *modem_socket_from_fd(struct modem_socket_config *cfg, int sock_fd)
186 {
187 int i;
188
189 k_sem_take(&cfg->sem_lock, K_FOREVER);
190
191 for (i = 0; i < cfg->sockets_len; i++) {
192 if (cfg->sockets[i].sock_fd == sock_fd) {
193 k_sem_give(&cfg->sem_lock);
194 return &cfg->sockets[i];
195 }
196 }
197
198 k_sem_give(&cfg->sem_lock);
199
200 return NULL;
201 }
202
modem_socket_from_id(struct modem_socket_config * cfg,int id)203 struct modem_socket *modem_socket_from_id(struct modem_socket_config *cfg, int id)
204 {
205 int i;
206
207 if (id < cfg->base_socket_id) {
208 return NULL;
209 }
210
211 k_sem_take(&cfg->sem_lock, K_FOREVER);
212
213 for (i = 0; i < cfg->sockets_len; i++) {
214 if (cfg->sockets[i].id == id) {
215 k_sem_give(&cfg->sem_lock);
216 return &cfg->sockets[i];
217 }
218 }
219
220 k_sem_give(&cfg->sem_lock);
221
222 return NULL;
223 }
224
modem_socket_from_newid(struct modem_socket_config * cfg)225 struct modem_socket *modem_socket_from_newid(struct modem_socket_config *cfg)
226 {
227 return modem_socket_from_id(cfg, cfg->base_socket_id + cfg->sockets_len);
228 }
229
modem_socket_put(struct modem_socket_config * cfg,int sock_fd)230 void modem_socket_put(struct modem_socket_config *cfg, int sock_fd)
231 {
232 struct modem_socket *sock = modem_socket_from_fd(cfg, sock_fd);
233
234 if (!sock) {
235 return;
236 }
237
238 k_sem_take(&cfg->sem_lock, K_FOREVER);
239
240 sock->id = cfg->base_socket_id - 1;
241 sock->sock_fd = -1;
242 sock->is_waiting = false;
243 sock->is_connected = false;
244 (void)memset(&sock->src, 0, sizeof(struct sockaddr));
245 (void)memset(&sock->dst, 0, sizeof(struct sockaddr));
246 memset(&sock->packet_sizes, 0, sizeof(sock->packet_sizes));
247 sock->packet_count = 0;
248 k_sem_reset(&sock->sem_data_ready);
249 k_poll_signal_reset(&sock->sig_data_ready);
250
251 k_sem_give(&cfg->sem_lock);
252 }
253
254 /*
255 * Generic Poll Function
256 */
257
258 /*
259 * FIXME: The design here makes the poll function non-reentrant for same sockets.
260 * If two different threads poll on two identical sockets we'll end up with unexpected
261 * behavior - the higher priority thread will be unblocked, regardless on which
262 * socket it polled. I think we could live with such limitation though in the
263 * initial implementation, but this should be improved in the future.
264 */
modem_socket_poll(struct modem_socket_config * cfg,struct zsock_pollfd * fds,int nfds,int msecs)265 int modem_socket_poll(struct modem_socket_config *cfg, struct zsock_pollfd *fds, int nfds,
266 int msecs)
267 {
268 struct modem_socket *sock;
269 int ret, i;
270 uint8_t found_count = 0;
271
272 if (!cfg || nfds > CONFIG_NET_SOCKETS_POLL_MAX) {
273 return -EINVAL;
274 }
275 struct k_poll_event events[nfds];
276 int eventcount = 0;
277
278 for (i = 0; i < nfds; i++) {
279 sock = modem_socket_from_fd(cfg, fds[i].fd);
280 if (sock) {
281 /*
282 * Handle user check for POLLOUT events:
283 * we consider the socket to always be writable.
284 */
285 if (fds[i].events & ZSOCK_POLLOUT) {
286 found_count++;
287 break;
288 } else if (fds[i].events & ZSOCK_POLLIN) {
289 k_poll_event_init(&events[eventcount++], K_POLL_TYPE_SIGNAL,
290 K_POLL_MODE_NOTIFY_ONLY, &sock->sig_data_ready);
291 if (sock->packet_sizes[0] > 0U) {
292 found_count++;
293 break;
294 }
295 }
296 }
297 }
298
299 /* Avoid waiting on semaphore if we have already found an event */
300 ret = 0;
301 if (!found_count) {
302 k_timeout_t timeout = K_FOREVER;
303
304 if (msecs >= 0) {
305 timeout = K_MSEC(msecs);
306 }
307 ret = k_poll(events, eventcount, timeout);
308 }
309 /* Reset counter as we reiterate on all polled sockets */
310 found_count = 0;
311
312 for (i = 0; i < nfds; i++) {
313 sock = modem_socket_from_fd(cfg, fds[i].fd);
314 if (!sock) {
315 continue;
316 }
317
318 /*
319 * Handle user check for ZSOCK_POLLOUT events:
320 * we consider the socket to always be writable.
321 */
322 if (fds[i].events & ZSOCK_POLLOUT) {
323 fds[i].revents |= ZSOCK_POLLOUT;
324 found_count++;
325 } else if ((fds[i].events & ZSOCK_POLLIN) && (sock->packet_sizes[0] > 0U)) {
326 fds[i].revents |= ZSOCK_POLLIN;
327 found_count++;
328 }
329 }
330
331 /* EBUSY, EAGAIN and ETIMEDOUT aren't true errors */
332 if (ret < 0 && ret != -EBUSY && ret != -EAGAIN && ret != -ETIMEDOUT) {
333 errno = ret;
334 return -1;
335 }
336
337 errno = 0;
338 return found_count;
339 }
340
modem_socket_poll_prepare(struct modem_socket_config * cfg,struct modem_socket * sock,struct zsock_pollfd * pfd,struct k_poll_event ** pev,struct k_poll_event * pev_end)341 int modem_socket_poll_prepare(struct modem_socket_config *cfg, struct modem_socket *sock,
342 struct zsock_pollfd *pfd, struct k_poll_event **pev,
343 struct k_poll_event *pev_end)
344 {
345 if (pfd->events & ZSOCK_POLLIN) {
346 if (*pev == pev_end) {
347 errno = ENOMEM;
348 return -1;
349 }
350
351 k_poll_event_init(*pev, K_POLL_TYPE_SIGNAL, K_POLL_MODE_NOTIFY_ONLY,
352 &sock->sig_data_ready);
353 (*pev)++;
354 }
355
356 if (pfd->events & ZSOCK_POLLOUT) {
357 if (*pev == pev_end) {
358 errno = ENOMEM;
359 return -1;
360 }
361 /* Not Implemented */
362 errno = ENOTSUP;
363 return -1;
364 }
365
366 return 0;
367 }
368
modem_socket_poll_update(struct modem_socket * sock,struct zsock_pollfd * pfd,struct k_poll_event ** pev)369 int modem_socket_poll_update(struct modem_socket *sock, struct zsock_pollfd *pfd,
370 struct k_poll_event **pev)
371 {
372 ARG_UNUSED(sock);
373
374 if (pfd->events & ZSOCK_POLLIN) {
375 if ((*pev)->state != K_POLL_STATE_NOT_READY) {
376 pfd->revents |= ZSOCK_POLLIN;
377 }
378 (*pev)++;
379 }
380
381 if (pfd->events & ZSOCK_POLLOUT) {
382 /* Not implemented, but the modem socket is always ready to transmit,
383 * so set the revents
384 */
385 pfd->revents |= ZSOCK_POLLOUT;
386 (*pev)++;
387 }
388
389 return 0;
390 }
391
modem_socket_wait_data(struct modem_socket_config * cfg,struct modem_socket * sock)392 void modem_socket_wait_data(struct modem_socket_config *cfg, struct modem_socket *sock)
393 {
394 k_sem_take(&cfg->sem_lock, K_FOREVER);
395 sock->is_waiting = true;
396 k_sem_give(&cfg->sem_lock);
397
398 k_sem_take(&sock->sem_data_ready, K_FOREVER);
399 }
400
modem_socket_data_ready(struct modem_socket_config * cfg,struct modem_socket * sock)401 void modem_socket_data_ready(struct modem_socket_config *cfg, struct modem_socket *sock)
402 {
403 k_sem_take(&cfg->sem_lock, K_FOREVER);
404
405 if (sock->is_waiting) {
406 /* unblock sockets waiting on recv() */
407 sock->is_waiting = false;
408 k_sem_give(&sock->sem_data_ready);
409 }
410
411 k_sem_give(&cfg->sem_lock);
412 }
413
modem_socket_init(struct modem_socket_config * cfg,struct modem_socket * sockets,size_t sockets_len,int base_socket_id,bool assign_id,const struct socket_op_vtable * vtable)414 int modem_socket_init(struct modem_socket_config *cfg, struct modem_socket *sockets,
415 size_t sockets_len, int base_socket_id, bool assign_id,
416 const struct socket_op_vtable *vtable)
417 {
418 /* Verify arguments */
419 if (cfg == NULL || sockets == NULL || sockets_len < 1 || vtable == NULL) {
420 return -EINVAL;
421 }
422
423 /* Initialize config */
424 cfg->sockets = sockets;
425 cfg->sockets_len = sockets_len;
426 cfg->base_socket_id = base_socket_id;
427 cfg->assign_id = assign_id;
428 k_sem_init(&cfg->sem_lock, 1, 1);
429 cfg->vtable = vtable;
430
431 /* Initialize associated sockets */
432 for (int i = 0; i < cfg->sockets_len; i++) {
433 /* Clear entire socket structure */
434 memset(&cfg->sockets[i], 0, sizeof(cfg->sockets[i]));
435
436 /* Initialize socket members */
437 k_sem_init(&cfg->sockets[i].sem_data_ready, 0, 1);
438 k_poll_signal_init(&cfg->sockets[i].sig_data_ready);
439 cfg->sockets[i].id = -1;
440 }
441 return 0;
442 }
443
modem_socket_is_allocated(const struct modem_socket_config * cfg,const struct modem_socket * sock)444 bool modem_socket_is_allocated(const struct modem_socket_config *cfg,
445 const struct modem_socket *sock)
446 {
447 /* Socket is allocated with a reserved id value if id is not dynamically assigned */
448 if (cfg->assign_id == false && sock->id == (cfg->base_socket_id + cfg->sockets_len)) {
449 return true;
450 }
451
452 /* Socket must have been allocated if id is assigned */
453 return modem_socket_id_is_assigned(cfg, sock);
454 }
455
modem_socket_id_is_assigned(const struct modem_socket_config * cfg,const struct modem_socket * sock)456 bool modem_socket_id_is_assigned(const struct modem_socket_config *cfg,
457 const struct modem_socket *sock)
458 {
459 /* Verify socket is assigned to a valid value */
460 if ((cfg->base_socket_id <= sock->id) &&
461 (sock->id < (cfg->base_socket_id + cfg->sockets_len))) {
462 return true;
463 }
464 return false;
465 }
466
modem_socket_id_assign(const struct modem_socket_config * cfg,struct modem_socket * sock,int id)467 int modem_socket_id_assign(const struct modem_socket_config *cfg,
468 struct modem_socket *sock, int id)
469 {
470 /* Verify dynamically assigning id is disabled */
471 if (cfg->assign_id) {
472 return -EPERM;
473 }
474
475 /* Verify id is currently not assigned */
476 if (modem_socket_id_is_assigned(cfg, sock)) {
477 return -EPERM;
478 }
479
480 /* Verify id is valid */
481 if (id < cfg->base_socket_id || (cfg->base_socket_id + cfg->sockets_len) <= id) {
482 return -EINVAL;
483 }
484
485 /* Assign id */
486 sock->id = id;
487 return 0;
488 }
489