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 <kernel.h>
14 #include <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,
23 struct modem_socket *sock)
24 {
25 uint16_t total = 0U;
26
27 k_sem_take(&cfg->sem_lock, K_FOREVER);
28
29 if (!sock || !sock->packet_count) {
30 goto exit;
31 }
32
33 total = sock->packet_sizes[0];
34
35 exit:
36 k_sem_give(&cfg->sem_lock);
37 return total;
38 }
39
modem_socket_packet_get_total(struct modem_socket * sock)40 static uint16_t modem_socket_packet_get_total(struct modem_socket *sock)
41 {
42 int i;
43 uint16_t total = 0U;
44
45 if (!sock || !sock->packet_count) {
46 return 0U;
47 }
48
49 for (i = 0; i < sock->packet_count; i++) {
50 total += sock->packet_sizes[i];
51 }
52
53 return total;
54 }
55
modem_socket_packet_drop_first(struct modem_socket * sock)56 static int modem_socket_packet_drop_first(struct modem_socket *sock)
57 {
58 int i;
59
60 if (!sock || !sock->packet_count) {
61 return -EINVAL;
62 }
63
64 sock->packet_count--;
65 for (i = 0; i < sock->packet_count; i++) {
66 sock->packet_sizes[i] =
67 sock->packet_sizes[i + 1];
68 }
69
70 sock->packet_sizes[sock->packet_count] = 0U;
71 return 0;
72 }
73
modem_socket_packet_size_update(struct modem_socket_config * cfg,struct modem_socket * sock,int new_total)74 int modem_socket_packet_size_update(struct modem_socket_config *cfg,
75 struct modem_socket *sock, int new_total)
76 {
77 uint16_t old_total = 0U;
78
79 if (!sock) {
80 return -EINVAL;
81 }
82
83 k_sem_take(&cfg->sem_lock, K_FOREVER);
84
85 if (new_total < 0) {
86 new_total += modem_socket_packet_get_total(sock);
87 }
88
89 if (new_total <= 0) {
90 /* reset outstanding value here */
91 sock->packet_count = 0U;
92 sock->packet_sizes[0] = 0U;
93 k_sem_give(&cfg->sem_lock);
94 return 0;
95 }
96
97 old_total = modem_socket_packet_get_total(sock);
98 if (new_total == old_total) {
99 goto data_ready;
100 }
101
102 /* remove sent packets */
103 if (new_total < old_total) {
104 /* remove packets that are not included in new_size */
105 while (old_total > new_total && sock->packet_count > 0) {
106 /* handle partial read */
107 if (old_total - new_total < sock->packet_sizes[0]) {
108 sock->packet_sizes[0] -= old_total - new_total;
109 break;
110 }
111
112 old_total -= sock->packet_sizes[0];
113 modem_socket_packet_drop_first(sock);
114 }
115
116 goto data_ready;
117 }
118
119 /* new packet to add */
120 if (sock->packet_count >= CONFIG_MODEM_SOCKET_PACKET_COUNT) {
121 k_sem_give(&cfg->sem_lock);
122 return -ENOMEM;
123 }
124
125 if (new_total - old_total > 0) {
126 sock->packet_sizes[sock->packet_count] = new_total - old_total;
127 sock->packet_count++;
128 } else {
129 k_sem_give(&cfg->sem_lock);
130 return -EINVAL;
131 }
132
133 data_ready:
134 k_sem_give(&cfg->sem_lock);
135 return new_total;
136 }
137
138 /*
139 * Socket Support Functions
140 */
141
modem_socket_get(struct modem_socket_config * cfg,int family,int type,int proto)142 int modem_socket_get(struct modem_socket_config *cfg,
143 int family, int type, int proto)
144 {
145 int i;
146
147 k_sem_take(&cfg->sem_lock, K_FOREVER);
148
149 for (i = 0; i < cfg->sockets_len; i++) {
150 if (cfg->sockets[i].id < cfg->base_socket_num) {
151 break;
152 }
153 }
154
155 if (i >= cfg->sockets_len) {
156 k_sem_give(&cfg->sem_lock);
157 return -ENOMEM;
158 }
159
160 /* FIXME: 4 fds max now due to POSIX_OS conflict */
161 cfg->sockets[i].sock_fd = z_reserve_fd();
162 if (cfg->sockets[i].sock_fd < 0) {
163 k_sem_give(&cfg->sem_lock);
164 return -errno;
165 }
166
167 cfg->sockets[i].family = family;
168 cfg->sockets[i].type = type;
169 cfg->sockets[i].ip_proto = proto;
170 /* socket # needs assigning */
171 cfg->sockets[i].id = cfg->sockets_len + 1;
172 z_finalize_fd(cfg->sockets[i].sock_fd, &cfg->sockets[i],
173 (const struct fd_op_vtable *)cfg->vtable);
174
175 k_sem_give(&cfg->sem_lock);
176 return cfg->sockets[i].sock_fd;
177 }
178
modem_socket_from_fd(struct modem_socket_config * cfg,int sock_fd)179 struct modem_socket *modem_socket_from_fd(struct modem_socket_config *cfg,
180 int sock_fd)
181 {
182 int i;
183
184 k_sem_take(&cfg->sem_lock, K_FOREVER);
185
186 for (i = 0; i < cfg->sockets_len; i++) {
187 if (cfg->sockets[i].sock_fd == sock_fd) {
188 k_sem_give(&cfg->sem_lock);
189 return &cfg->sockets[i];
190 }
191 }
192
193 k_sem_give(&cfg->sem_lock);
194
195 return NULL;
196 }
197
modem_socket_from_id(struct modem_socket_config * cfg,int id)198 struct modem_socket *modem_socket_from_id(struct modem_socket_config *cfg,
199 int id)
200 {
201 int i;
202
203 if (id < cfg->base_socket_num) {
204 return NULL;
205 }
206
207 k_sem_take(&cfg->sem_lock, K_FOREVER);
208
209 for (i = 0; i < cfg->sockets_len; i++) {
210 if (cfg->sockets[i].id == id) {
211 k_sem_give(&cfg->sem_lock);
212 return &cfg->sockets[i];
213 }
214 }
215
216 k_sem_give(&cfg->sem_lock);
217
218 return NULL;
219 }
220
modem_socket_from_newid(struct modem_socket_config * cfg)221 struct modem_socket *modem_socket_from_newid(struct modem_socket_config *cfg)
222 {
223 return modem_socket_from_id(cfg, cfg->sockets_len + 1);
224 }
225
modem_socket_put(struct modem_socket_config * cfg,int sock_fd)226 void modem_socket_put(struct modem_socket_config *cfg, int sock_fd)
227 {
228 struct modem_socket *sock = modem_socket_from_fd(cfg, sock_fd);
229
230 if (!sock) {
231 return;
232 }
233
234 k_sem_take(&cfg->sem_lock, K_FOREVER);
235
236 sock->id = cfg->base_socket_num - 1;
237 sock->sock_fd = -1;
238 sock->is_waiting = false;
239 sock->is_polled = false;
240 sock->is_connected = false;
241 (void)memset(&sock->src, 0, sizeof(struct sockaddr));
242 (void)memset(&sock->dst, 0, sizeof(struct sockaddr));
243 memset(&sock->packet_sizes, 0, sizeof(sock->packet_sizes));
244 sock->packet_count = 0;
245 k_sem_reset(&sock->sem_data_ready);
246
247 k_sem_give(&cfg->sem_lock);
248 }
249
250 /*
251 * Generic Poll Function
252 */
253
254 /*
255 * FIXME: The design here makes the poll function non-reentrant. If two
256 * different threads poll on two different sockets we'll end up with unexpected
257 * behavior - the higher priority thread will be unblocked, regardless on which
258 * socket it polled. I think we could live with such limitation though in the
259 * initial implementation, but this should be improved in the future.
260 */
modem_socket_poll(struct modem_socket_config * cfg,struct zsock_pollfd * fds,int nfds,int msecs)261 int modem_socket_poll(struct modem_socket_config *cfg,
262 struct zsock_pollfd *fds, int nfds, int msecs)
263 {
264 struct modem_socket *sock;
265 int ret, i;
266 uint8_t found_count = 0;
267
268 if (!cfg) {
269 return -EINVAL;
270 }
271
272 k_sem_reset(&cfg->sem_poll);
273
274 for (i = 0; i < nfds; i++) {
275 sock = modem_socket_from_fd(cfg, fds[i].fd);
276 if (sock) {
277 /*
278 * Handle user check for POLLOUT events:
279 * we consider the socket to always be writable.
280 */
281 if (fds[i].events & ZSOCK_POLLOUT) {
282 found_count++;
283 break;
284 } else if (fds[i].events & ZSOCK_POLLIN) {
285 sock->is_polled = true;
286
287 /*
288 * Handle check done after data reception on
289 * the socket. In this case that was received
290 * but as the socket wasn't polled, no sem_poll
291 * semaphore was given at that time. Therefore
292 * if there is a polled socket with data,
293 * increment found_count to escape the
294 * k_sem_take().
295 */
296 if (sock->packet_sizes[0] > 0U) {
297 found_count++;
298 break;
299 }
300 }
301 }
302 }
303
304 /* Avoid waiting on semaphore if we have already found an event */
305 ret = 0;
306 if (!found_count) {
307 ret = k_sem_take(&cfg->sem_poll, K_MSEC(msecs));
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) &&
326 (sock->packet_sizes[0] > 0U)) {
327 fds[i].revents |= ZSOCK_POLLIN;
328 found_count++;
329 }
330
331 sock->is_polled = false;
332 }
333
334 /* EBUSY, EAGAIN and ETIMEDOUT aren't true errors */
335 if (ret < 0 && ret != -EBUSY && ret != -EAGAIN && ret != -ETIMEDOUT) {
336 errno = ret;
337 return -1;
338 }
339
340 errno = 0;
341 return found_count;
342 }
343
modem_socket_wait_data(struct modem_socket_config * cfg,struct modem_socket * sock)344 void modem_socket_wait_data(struct modem_socket_config *cfg,
345 struct modem_socket *sock)
346 {
347 k_sem_take(&cfg->sem_lock, K_FOREVER);
348 sock->is_waiting = true;
349 k_sem_give(&cfg->sem_lock);
350
351 k_sem_take(&sock->sem_data_ready, K_FOREVER);
352 }
353
modem_socket_data_ready(struct modem_socket_config * cfg,struct modem_socket * sock)354 void modem_socket_data_ready(struct modem_socket_config *cfg,
355 struct modem_socket *sock)
356 {
357 k_sem_take(&cfg->sem_lock, K_FOREVER);
358
359 if (sock->is_waiting) {
360 /* unblock sockets waiting on recv() */
361 sock->is_waiting = false;
362 k_sem_give(&sock->sem_data_ready);
363 }
364
365 if (sock->is_polled) {
366 /* unblock poll() */
367 k_sem_give(&cfg->sem_poll);
368 }
369
370 k_sem_give(&cfg->sem_lock);
371 }
372
modem_socket_init(struct modem_socket_config * cfg,const struct socket_op_vtable * vtable)373 int modem_socket_init(struct modem_socket_config *cfg,
374 const struct socket_op_vtable *vtable)
375 {
376 int i;
377
378 k_sem_init(&cfg->sem_poll, 0, 1);
379 k_sem_init(&cfg->sem_lock, 1, 1);
380 for (i = 0; i < cfg->sockets_len; i++) {
381 k_sem_init(&cfg->sockets[i].sem_data_ready, 0, 1);
382 cfg->sockets[i].id = cfg->base_socket_num - 1;
383 }
384
385 cfg->vtable = vtable;
386
387 return 0;
388 }
389