blob: ebe8d9fd06a1a8d3a9a6e296dadae8f1395b1733 [file] [log] [blame]
Lukas Gehreke53dea672021-08-27 10:21:14 +02001/*
2 * Copyright (C) 2021 metraTec GmbH
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7#define DT_DRV_COMPAT simcom_sim7080
8
Gerard Marull-Paretasfb60aab2022-05-06 10:25:46 +02009#include <zephyr/logging/log.h>
Lukas Gehreke53dea672021-08-27 10:21:14 +020010LOG_MODULE_REGISTER(modem_simcom_sim7080, CONFIG_MODEM_LOG_LEVEL);
11
Gerard Marull-Paretasfb60aab2022-05-06 10:25:46 +020012#include <zephyr/drivers/modem/simcom-sim7080.h>
Lukas Gehreke53dea672021-08-27 10:21:14 +020013#include "simcom-sim7080.h"
14
15#define SMS_TP_UDHI_HEADER 0x40
16
17static struct k_thread modem_rx_thread;
18static struct k_work_q modem_workq;
19static struct sim7080_data mdata;
20static struct modem_context mctx;
21static const struct socket_op_vtable offload_socket_fd_op_vtable;
22
23static struct zsock_addrinfo dns_result;
24static struct sockaddr dns_result_addr;
25static char dns_result_canonname[DNS_MAX_NAME_SIZE + 1];
26
27static struct sim7080_gnss_data gnss_data;
28
29static K_KERNEL_STACK_DEFINE(modem_rx_stack, CONFIG_MODEM_SIMCOM_SIM7080_RX_STACK_SIZE);
30static K_KERNEL_STACK_DEFINE(modem_workq_stack, CONFIG_MODEM_SIMCOM_SIM7080_RX_WORKQ_STACK_SIZE);
31NET_BUF_POOL_DEFINE(mdm_recv_pool, MDM_RECV_MAX_BUF, MDM_RECV_BUF_SIZE, 0, NULL);
32
Marcin Niestroj41c618d2022-05-13 19:20:16 +020033/* pin settings */
34static const struct gpio_dt_spec power_gpio = GPIO_DT_SPEC_INST_GET(0, mdm_power_gpios);
35
Lukas Gehreke53dea672021-08-27 10:21:14 +020036static void socket_close(struct modem_socket *sock);
37const struct socket_dns_offload offload_dns_ops;
38
39static inline uint32_t hash32(char *str, int len)
40{
41#define HASH_MULTIPLIER 37
42 uint32_t h = 0;
43 int i;
44
45 for (i = 0; i < len; ++i) {
46 h = (h * HASH_MULTIPLIER) + str[i];
47 }
48
49 return h;
50}
51
52static inline uint8_t *modem_get_mac(const struct device *dev)
53{
54 struct sim7080_data *data = dev->data;
55 uint32_t hash_value;
56
57 data->mac_addr[0] = 0x00;
58 data->mac_addr[1] = 0x10;
59
60 /* use IMEI for mac_addr */
61 hash_value = hash32(mdata.mdm_imei, strlen(mdata.mdm_imei));
62
63 UNALIGNED_PUT(hash_value, (uint32_t *)(data->mac_addr + 2));
64
65 return data->mac_addr;
66}
67
Robert Lubosfa8ba732022-03-30 14:16:42 +020068static int offload_socket(int family, int type, int proto);
69
Lukas Gehreke53dea672021-08-27 10:21:14 +020070/* Setup the Modem NET Interface. */
71static void modem_net_iface_init(struct net_if *iface)
72{
73 const struct device *dev = net_if_get_device(iface);
74 struct sim7080_data *data = dev->data;
75
76 net_if_set_link_addr(iface, modem_get_mac(dev), sizeof(data->mac_addr), NET_LINK_ETHERNET);
77
78 data->netif = iface;
79
80 socket_offload_dns_register(&offload_dns_ops);
Robert Lubosfa8ba732022-03-30 14:16:42 +020081
82 net_if_socket_offload_set(iface, offload_socket);
Lukas Gehreke53dea672021-08-27 10:21:14 +020083}
84
85/**
86 * Changes the operating state of the sim7080.
87 *
88 * @param state The new state.
89 */
90static void change_state(enum sim7080_state state)
91{
92 LOG_DBG("Changing state to (%d)", state);
93 mdata.state = state;
94}
95
96/**
97 * Get the current operating state of the sim7080.
98 *
99 * @return The current state.
100 */
101static enum sim7080_state get_state(void)
102{
103 return mdata.state;
104}
105
106/*
107 * Parses the +CAOPEN command and gives back the
108 * connect semaphore.
109 */
110MODEM_CMD_DEFINE(on_cmd_caopen)
111{
112 int result = atoi(argv[1]);
113
114 LOG_INF("+CAOPEN: %d", result);
115 modem_cmd_handler_set_error(data, result);
116 return 0;
117}
118
119/*
120 * Unlock the tx ready semaphore if '> ' is received.
121 */
122MODEM_CMD_DIRECT_DEFINE(on_cmd_tx_ready)
123{
124 k_sem_give(&mdata.sem_tx_ready);
125 return len;
126}
127
128/*
129 * Connects an modem socket. Protocol can either be TCP or UDP.
130 */
131static int offload_connect(void *obj, const struct sockaddr *addr, socklen_t addrlen)
132{
133 struct modem_socket *sock = (struct modem_socket *)obj;
134 uint16_t dst_port = 0;
135 char *protocol;
136 struct modem_cmd cmd[] = { MODEM_CMD("+CAOPEN: ", on_cmd_caopen, 2U, ",") };
137 char buf[sizeof("AT+CAOPEN: #,#,#####,#xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:xxxx#,####")];
138 char ip_str[NET_IPV6_ADDR_LEN];
139 int ret;
140
141 /* Modem is not attached to the network. */
142 if (get_state() != SIM7080_STATE_NETWORKING) {
143 return -EAGAIN;
144 }
145
146 if (sock->id < mdata.socket_config.base_socket_num - 1) {
147 LOG_ERR("Invalid socket id %d from fd %d", sock->id, sock->sock_fd);
148 errno = EINVAL;
149 return -1;
150 }
151
152 if (sock->is_connected == true) {
153 LOG_ERR("Socket is already connected! id: %d, fd: %d", sock->id, sock->sock_fd);
154 errno = EISCONN;
155 return -1;
156 }
157
158 /* get the destination port */
159 if (addr->sa_family == AF_INET6) {
160 dst_port = ntohs(net_sin6(addr)->sin6_port);
161 } else if (addr->sa_family == AF_INET) {
162 dst_port = ntohs(net_sin(addr)->sin_port);
163 }
164
165 /* Get protocol */
166 protocol = (sock->type == SOCK_STREAM) ? "TCP" : "UDP";
167
168 ret = modem_context_sprint_ip_addr(addr, ip_str, sizeof(ip_str));
169 if (ret != 0) {
170 LOG_ERR("Failed to format IP!");
171 errno = ENOMEM;
172 return -1;
173 }
174
175 ret = snprintk(buf, sizeof(buf), "AT+CAOPEN=%d,%d,\"%s\",\"%s\",%d", 0, sock->sock_fd,
176 protocol, ip_str, dst_port);
177 if (ret < 0) {
178 LOG_ERR("Failed to build connect command. ID: %d, FD: %d", sock->id, sock->sock_fd);
179 errno = ENOMEM;
180 return -1;
181 }
182
183 ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler, cmd, ARRAY_SIZE(cmd), buf,
184 &mdata.sem_response, MDM_CONNECT_TIMEOUT);
185 if (ret < 0) {
Krzysztof Chruscinski041f0e52022-06-20 07:43:37 +0200186 LOG_ERR("%s ret: %d", buf, ret);
Lukas Gehreke53dea672021-08-27 10:21:14 +0200187 socket_close(sock);
188 goto error;
189 }
190
191 ret = modem_cmd_handler_get_error(&mdata.cmd_handler_data);
192 if (ret != 0) {
193 LOG_ERR("Closing the socket!");
194 socket_close(sock);
195 goto error;
196 }
197
198 sock->is_connected = true;
199 errno = 0;
200 return 0;
201error:
202 errno = -ret;
203 return -1;
204}
205
206/*
207 * Send data over a given socket.
208 *
209 * First we signal the module that we want to send data over a socket.
210 * This is done by sending AT+CASEND=<sockfd>,<nbytes>\r\n.
211 * If The module is ready to send data it will send back
Nazar Kazakovf483b1b2022-03-16 21:07:43 +0000212 * an UNTERMINATED prompt '> '. After that data can be sent to the modem.
Lukas Gehreke53dea672021-08-27 10:21:14 +0200213 * As terminating byte a STRG+Z (0x1A) is sent. The module will
214 * then send a OK or ERROR.
215 */
216static ssize_t offload_sendto(void *obj, const void *buf, size_t len, int flags,
217 const struct sockaddr *dest_addr, socklen_t addrlen)
218{
219 int ret;
220 struct modem_socket *sock = (struct modem_socket *)obj;
221 char send_buf[sizeof("AT+CASEND=#,####")] = { 0 };
222 char ctrlz = 0x1A;
223
224 /* Modem is not attached to the network. */
225 if (get_state() != SIM7080_STATE_NETWORKING) {
226 LOG_ERR("Modem currently not attached to the network!");
227 return -EAGAIN;
228 }
229
230 /* Do some sanity checks. */
231 if (!buf || len == 0) {
232 errno = EINVAL;
233 return -1;
234 }
235
236 /* Socket has to be connected. */
237 if (!sock->is_connected) {
238 errno = ENOTCONN;
239 return -1;
240 }
241
242 /* Only send up to MTU bytes. */
243 if (len > MDM_MAX_DATA_LENGTH) {
244 len = MDM_MAX_DATA_LENGTH;
245 }
246
247 ret = snprintk(send_buf, sizeof(send_buf), "AT+CASEND=%d,%ld", sock->sock_fd, (long)len);
248 if (ret < 0) {
249 LOG_ERR("Failed to build send command!!");
250 errno = ENOMEM;
251 return -1;
252 }
253
254 /* Make sure only one send can be done at a time. */
255 k_sem_take(&mdata.cmd_handler_data.sem_tx_lock, K_FOREVER);
256 k_sem_reset(&mdata.sem_tx_ready);
257
258 /* Send CASEND */
259 mdata.current_sock_written = len;
260 ret = modem_cmd_send_nolock(&mctx.iface, &mctx.cmd_handler, NULL, 0U, send_buf, NULL,
261 K_NO_WAIT);
262 if (ret < 0) {
263 LOG_ERR("Failed to send CASEND!!");
264 goto exit;
265 }
266
267 /* Wait for '> ' */
268 ret = k_sem_take(&mdata.sem_tx_ready, K_SECONDS(2));
269 if (ret < 0) {
270 LOG_ERR("Timeout while waiting for tx");
271 goto exit;
272 }
273
274 /* Send data */
275 mctx.iface.write(&mctx.iface, buf, len);
276 mctx.iface.write(&mctx.iface, &ctrlz, 1);
277
278 /* Wait for the OK */
279 k_sem_reset(&mdata.sem_response);
280 ret = k_sem_take(&mdata.sem_response, MDM_CMD_TIMEOUT);
281 if (ret < 0) {
282 LOG_ERR("Timeout waiting for OK");
283 }
284
285exit:
286 k_sem_give(&mdata.cmd_handler_data.sem_tx_lock);
287 /* Data was successfully sent */
288
289 if (ret < 0) {
290 errno = -ret;
291 return -1;
292 }
293
294 errno = 0;
295 return mdata.current_sock_written;
296}
297
298/*
299 * Read data from a given socket.
300 *
301 * The response has the form +CARECV: <length>,data\r\nOK\r\n
302 */
303static int sockread_common(int sockfd, struct modem_cmd_handler_data *data, int socket_data_length,
304 uint16_t len)
305{
306 struct modem_socket *sock;
307 struct socket_read_data *sock_data;
308 int ret, packet_size;
309
310 if (!len) {
311 LOG_ERR("Invalid length, aborting");
312 return -EAGAIN;
313 }
314
315 if (!data->rx_buf) {
316 LOG_ERR("Incorrect format! Ignoring data!");
317 return -EINVAL;
318 }
319
320 if (socket_data_length <= 0) {
321 LOG_ERR("Length error (%d)", socket_data_length);
322 return -EAGAIN;
323 }
324
325 if (net_buf_frags_len(data->rx_buf) < socket_data_length) {
326 LOG_DBG("Not enough data -- wait!");
327 return -EAGAIN;
328 }
329
330 sock = modem_socket_from_fd(&mdata.socket_config, sockfd);
331 if (!sock) {
332 LOG_ERR("Socket not found! (%d)", sockfd);
333 ret = -EINVAL;
334 goto exit;
335 }
336
337 sock_data = (struct socket_read_data *)sock->data;
338 if (!sock_data) {
339 LOG_ERR("Socket data not found! (%d)", sockfd);
340 ret = -EINVAL;
341 goto exit;
342 }
343
344 ret = net_buf_linearize(sock_data->recv_buf, sock_data->recv_buf_len, data->rx_buf, 0,
345 (uint16_t)socket_data_length);
346 data->rx_buf = net_buf_skip(data->rx_buf, ret);
347 sock_data->recv_read_len = ret;
348 if (ret != socket_data_length) {
349 LOG_ERR("Total copied data is different then received data!"
350 " copied:%d vs. received:%d",
351 ret, socket_data_length);
352 ret = -EINVAL;
353 goto exit;
354 }
355
356exit:
357 /* Indication only sets length to a dummy value. */
358 packet_size = modem_socket_next_packet_size(&mdata.socket_config, sock);
359 modem_socket_packet_size_update(&mdata.socket_config, sock, -packet_size);
360 return ret;
361}
362
363/*
364 * Handler for carecv response.
365 */
366MODEM_CMD_DEFINE(on_cmd_carecv)
367{
368 return sockread_common(mdata.current_sock_fd, data, atoi(argv[0]), len);
369}
370
371/*
372 * Read data from a given socket.
373 */
374static ssize_t offload_recvfrom(void *obj, void *buf, size_t max_len, int flags,
375 struct sockaddr *src_addr, socklen_t *addrlen)
376{
377 struct modem_socket *sock = (struct modem_socket *)obj;
378 char sendbuf[sizeof("AT+CARECV=##,####")];
379 int ret, packet_size;
380 struct socket_read_data sock_data;
381
382 struct modem_cmd data_cmd[] = { MODEM_CMD("+CARECV: ", on_cmd_carecv, 1U, ",") };
383
384 /* Modem is not attached to the network. */
385 if (get_state() != SIM7080_STATE_NETWORKING) {
386 LOG_ERR("Modem currently not attached to the network!");
387 return -EAGAIN;
388 }
389
390 if (!buf || max_len == 0) {
391 errno = EINVAL;
392 return -1;
393 }
394
395 if (flags & ZSOCK_MSG_PEEK) {
396 errno = ENOTSUP;
397 return -1;
398 }
399
400 packet_size = modem_socket_next_packet_size(&mdata.socket_config, sock);
401 if (!packet_size) {
402 if (flags & ZSOCK_MSG_DONTWAIT) {
403 errno = EAGAIN;
404 return -1;
405 }
406
407 modem_socket_wait_data(&mdata.socket_config, sock);
408 packet_size = modem_socket_next_packet_size(&mdata.socket_config, sock);
409 }
410
411 max_len = (max_len > MDM_MAX_DATA_LENGTH) ? MDM_MAX_DATA_LENGTH : max_len;
412 snprintk(sendbuf, sizeof(sendbuf), "AT+CARECV=%d,%zd", sock->sock_fd, max_len);
413
414 memset(&sock_data, 0, sizeof(sock_data));
415 sock_data.recv_buf = buf;
416 sock_data.recv_buf_len = max_len;
417 sock_data.recv_addr = src_addr;
418 sock->data = &sock_data;
419 mdata.current_sock_fd = sock->sock_fd;
420
421 ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler, data_cmd, ARRAY_SIZE(data_cmd),
422 sendbuf, &mdata.sem_response, MDM_CMD_TIMEOUT);
423 if (ret < 0) {
424 errno = -ret;
425 ret = -1;
426 goto exit;
427 }
428
429 /* HACK: use dst address as src */
430 if (src_addr && addrlen) {
431 *addrlen = sizeof(sock->dst);
432 memcpy(src_addr, &sock->dst, *addrlen);
433 }
434
435 errno = 0;
436 ret = sock_data.recv_read_len;
437
438exit:
439 /* clear socket data */
440 mdata.current_sock_fd = -1;
441 sock->data = NULL;
442 return ret;
443}
444
445/*
446 * Sends messages to the modem.
447 */
448static ssize_t offload_sendmsg(void *obj, const struct msghdr *msg, int flags)
449{
Marcin Niestrojdf5d1f22022-05-24 12:35:38 +0200450 struct modem_socket *sock = obj;
Lukas Gehreke53dea672021-08-27 10:21:14 +0200451 ssize_t sent = 0;
452 const char *buf;
453 size_t len;
454 int ret;
455
456 /* Modem is not attached to the network. */
457 if (get_state() != SIM7080_STATE_NETWORKING) {
458 LOG_ERR("Modem currently not attached to the network!");
459 return -EAGAIN;
460 }
461
Marcin Niestrojdf5d1f22022-05-24 12:35:38 +0200462 if (sock->type == SOCK_DGRAM) {
463 /*
464 * Current implementation only handles single contiguous fragment at a time, so
465 * prevent sending multiple datagrams.
466 */
467 if (msghdr_non_empty_iov_count(msg) > 1) {
468 errno = EMSGSIZE;
469 return -1;
470 }
471 }
472
Lukas Gehreke53dea672021-08-27 10:21:14 +0200473 for (int i = 0; i < msg->msg_iovlen; i++) {
474 buf = msg->msg_iov[i].iov_base;
475 len = msg->msg_iov[i].iov_len;
476
477 while (len > 0) {
478 ret = offload_sendto(obj, buf, len, flags, msg->msg_name, msg->msg_namelen);
479 if (ret < 0) {
480 if (ret == -EAGAIN) {
481 k_sleep(K_SECONDS(1));
482 } else {
Marcin Niestroj032b9082022-05-24 12:23:04 +0200483 return ret;
Lukas Gehreke53dea672021-08-27 10:21:14 +0200484 }
485 } else {
486 sent += ret;
487 buf += ret;
488 len -= ret;
489 }
490 }
491 }
492
493 return sent;
494}
495
496/*
497 * Closes a given socket.
498 */
499static void socket_close(struct modem_socket *sock)
500{
501 char buf[sizeof("AT+CACLOSE=##")];
502 int ret;
503
504 snprintk(buf, sizeof(buf), "AT+CACLOSE=%d", sock->sock_fd);
505
506 ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler, NULL, 0U, buf, &mdata.sem_response,
507 MDM_CMD_TIMEOUT);
508 if (ret < 0) {
Krzysztof Chruscinski041f0e52022-06-20 07:43:37 +0200509 LOG_ERR("%s ret: %d", buf, ret);
Lukas Gehreke53dea672021-08-27 10:21:14 +0200510 }
511
512 modem_socket_put(&mdata.socket_config, sock->sock_fd);
513}
514
515/*
516 * Offloads read by reading from a given socket.
517 */
518static ssize_t offload_read(void *obj, void *buffer, size_t count)
519{
520 return offload_recvfrom(obj, buffer, count, 0, NULL, 0);
521}
522
523/*
524 * Offloads write by writing to a given socket.
525 */
526static ssize_t offload_write(void *obj, const void *buffer, size_t count)
527{
528 return offload_sendto(obj, buffer, count, 0, NULL, 0);
529}
530
531/*
532 * Offloads close by terminating the connection and freeing the socket.
533 */
534static int offload_close(void *obj)
535{
536 struct modem_socket *sock = (struct modem_socket *)obj;
537
538 /* Modem is not attached to the network. */
539 if (get_state() != SIM7080_STATE_NETWORKING) {
540 LOG_ERR("Modem currently not attached to the network!");
541 return -EAGAIN;
542 }
543
544 /* Make sure we assigned an id */
545 if (sock->id < mdata.socket_config.base_socket_num) {
546 return 0;
547 }
548
549 /* Close the socket only if it is connected. */
550 if (sock->is_connected) {
551 socket_close(sock);
552 }
553
554 return 0;
555}
556
557/*
558 * Polls a given socket.
559 */
560static int offload_poll(struct zsock_pollfd *fds, int nfds, int msecs)
561{
562 int i;
563 void *obj;
564
565 /* Modem is not attached to the network. */
566 if (get_state() != SIM7080_STATE_NETWORKING) {
567 LOG_ERR("Modem currently not attached to the network!");
568 return -EAGAIN;
569 }
570
571 /* Only accept modem sockets. */
572 for (i = 0; i < nfds; i++) {
573 if (fds[i].fd < 0) {
574 continue;
575 }
576
577 /* If vtable matches, then it's modem socket. */
578 obj = z_get_fd_obj(fds[i].fd,
579 (const struct fd_op_vtable *)&offload_socket_fd_op_vtable,
580 EINVAL);
581 if (obj == NULL) {
582 return -1;
583 }
584 }
585
586 return modem_socket_poll(&mdata.socket_config, fds, nfds, msecs);
587}
588
589/*
590 * Offloads ioctl. Only supported ioctl is poll_offload.
591 */
592static int offload_ioctl(void *obj, unsigned int request, va_list args)
593{
594 switch (request) {
595 case ZFD_IOCTL_POLL_PREPARE:
596 return -EXDEV;
597
598 case ZFD_IOCTL_POLL_UPDATE:
599 return -EOPNOTSUPP;
600
601 case ZFD_IOCTL_POLL_OFFLOAD: {
602 /* Poll on the given socket. */
603 struct zsock_pollfd *fds;
604 int nfds, timeout;
605
606 fds = va_arg(args, struct zsock_pollfd *);
607 nfds = va_arg(args, int);
608 timeout = va_arg(args, int);
609
610 return offload_poll(fds, nfds, timeout);
611 }
612
613 default:
614 errno = EINVAL;
615 return -1;
616 }
617}
618
619static const struct socket_op_vtable offload_socket_fd_op_vtable = {
620 .fd_vtable = {
621 .read = offload_read,
622 .write = offload_write,
623 .close = offload_close,
624 .ioctl = offload_ioctl,
625 },
626 .bind = NULL,
627 .connect = offload_connect,
628 .sendto = offload_sendto,
629 .recvfrom = offload_recvfrom,
630 .listen = NULL,
631 .accept = NULL,
632 .sendmsg = offload_sendmsg,
633 .getsockopt = NULL,
634 .setsockopt = NULL,
635};
636
637/*
638 * Parses the dns response from the modem.
639 *
640 * Response on success:
641 * +CDNSGIP: 1,<domain name>,<IPv4>[,<IPv6>]
642 *
643 * Response on failure:
644 * +CDNSGIP: 0,<err>
645 */
646MODEM_CMD_DEFINE(on_cmd_cdnsgip)
647{
648 int state;
649 char ips[256];
650 size_t out_len;
651 int ret = -1;
652
653 state = atoi(argv[0]);
654 if (state == 0) {
Krzysztof Chruscinski041f0e52022-06-20 07:43:37 +0200655 LOG_ERR("DNS lookup failed with error %s", argv[1]);
Lukas Gehreke53dea672021-08-27 10:21:14 +0200656 goto exit;
657 }
658
659 /* Offset to skip the leading " */
660 out_len = net_buf_linearize(ips, sizeof(ips) - 1, data->rx_buf, 1, len);
661 ips[out_len] = '\0';
662
663 /* find trailing " */
664 char *ipv4 = strstr(ips, "\"");
665
666 if (!ipv4) {
667 LOG_ERR("Malformed DNS response!!");
668 goto exit;
669 }
670
671 *ipv4 = '\0';
672 net_addr_pton(dns_result.ai_family, ips,
673 &((struct sockaddr_in *)&dns_result_addr)->sin_addr);
674 ret = 0;
675
676exit:
677 k_sem_give(&mdata.sem_dns);
678 return ret;
679}
680
681/*
682 * Perform a dns lookup.
683 */
684static int offload_getaddrinfo(const char *node, const char *service,
685 const struct zsock_addrinfo *hints, struct zsock_addrinfo **res)
686{
687 struct modem_cmd cmd[] = { MODEM_CMD("+CDNSGIP: ", on_cmd_cdnsgip, 2U, ",") };
688 char sendbuf[sizeof("AT+CDNSGIP=\"\",##,#####") + 128];
689 uint32_t port = 0;
690 int ret;
691
692 /* Modem is not attached to the network. */
693 if (get_state() != SIM7080_STATE_NETWORKING) {
694 LOG_ERR("Modem currently not attached to the network!");
695 return DNS_EAI_AGAIN;
696 }
697
698 /* init result */
699 (void)memset(&dns_result, 0, sizeof(dns_result));
700 (void)memset(&dns_result_addr, 0, sizeof(dns_result_addr));
701
702 /* Currently only support IPv4. */
703 dns_result.ai_family = AF_INET;
704 dns_result_addr.sa_family = AF_INET;
705 dns_result.ai_addr = &dns_result_addr;
706 dns_result.ai_addrlen = sizeof(dns_result_addr);
707 dns_result.ai_canonname = dns_result_canonname;
708 dns_result_canonname[0] = '\0';
709
710 if (service) {
711 port = atoi(service);
712 if (port < 1 || port > USHRT_MAX)
713 return DNS_EAI_SERVICE;
714 }
715
716 if (port > 0U) {
717 if (dns_result.ai_family == AF_INET)
718 net_sin(&dns_result_addr)->sin_port = htons(port);
719 }
720
721 /* Check if node is an IP address */
722 if (net_addr_pton(dns_result.ai_family, node,
723 &((struct sockaddr_in *)&dns_result_addr)->sin_addr) == 0) {
724 *res = &dns_result;
725 return 0;
726 }
727
728 /* user flagged node as numeric host, but we failed net_addr_pton */
729 if (hints && hints->ai_flags & AI_NUMERICHOST) {
730 return DNS_EAI_NONAME;
731 }
732
733 snprintk(sendbuf, sizeof(sendbuf), "AT+CDNSGIP=\"%s\",10,20000", node);
734 ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler, cmd, ARRAY_SIZE(cmd), sendbuf,
735 &mdata.sem_dns, MDM_DNS_TIMEOUT);
736 if (ret < 0) {
737 return ret;
738 }
739
740 *res = (struct zsock_addrinfo *)&dns_result;
741 return 0;
742}
743
744/*
745 * Free addrinfo structure.
746 */
747static void offload_freeaddrinfo(struct zsock_addrinfo *res)
748{
749 /* No need to free static memory. */
750 res = NULL;
751}
752
753/*
754 * DNS vtable.
755 */
756const struct socket_dns_offload offload_dns_ops = {
757 .getaddrinfo = offload_getaddrinfo,
758 .freeaddrinfo = offload_freeaddrinfo,
759};
760
761static struct net_if_api api_funcs = {
762 .init = modem_net_iface_init,
763};
764
765static bool offload_is_supported(int family, int type, int proto)
766{
Marcin Niestroj61dfdf12022-04-07 20:14:46 +0200767 if (family != AF_INET &&
768 family != AF_INET6) {
769 return false;
770 }
771
772 if (type != SOCK_DGRAM &&
773 type != SOCK_STREAM) {
774 return false;
775 }
776
777 if (proto != IPPROTO_TCP &&
778 proto != IPPROTO_UDP) {
779 return false;
780 }
781
Lukas Gehreke53dea672021-08-27 10:21:14 +0200782 return true;
783}
784
785static int offload_socket(int family, int type, int proto)
786{
787 int ret;
788
789 ret = modem_socket_get(&mdata.socket_config, family, type, proto);
790 if (ret < 0) {
791 errno = -ret;
792 return -1;
793 }
794
795 errno = 0;
796 return ret;
797}
798
799/*
800 * Process all messages received from the modem.
801 */
802static void modem_rx(void)
803{
804 while (true) {
805 /* Wait for incoming data */
806 k_sem_take(&mdata.iface_data.rx_sem, K_FOREVER);
807
808 mctx.cmd_handler.process(&mctx.cmd_handler, &mctx.iface);
809 }
810}
811
812MODEM_CMD_DEFINE(on_cmd_ok)
813{
814 modem_cmd_handler_set_error(data, 0);
815 k_sem_give(&mdata.sem_response);
816 return 0;
817}
818
819MODEM_CMD_DEFINE(on_cmd_error)
820{
821 modem_cmd_handler_set_error(data, -EIO);
822 k_sem_give(&mdata.sem_response);
823 return 0;
824}
825
826MODEM_CMD_DEFINE(on_cmd_exterror)
827{
828 modem_cmd_handler_set_error(data, -EIO);
829 k_sem_give(&mdata.sem_response);
830 return 0;
831}
832
833/*
834 * Handles pdp context urc.
835 *
836 * The urc has the form +APP PDP: <index>,<state>.
837 * State can either be ACTIVE for activation or
838 * DEACTIVE if disabled.
839 */
840MODEM_CMD_DEFINE(on_urc_app_pdp)
841{
842 mdata.pdp_active = strcmp(argv[1], "ACTIVE") == 0;
843 LOG_INF("PDP context: %u", mdata.pdp_active);
844 k_sem_give(&mdata.sem_response);
845 return 0;
846}
847
848MODEM_CMD_DEFINE(on_urc_sms)
849{
Krzysztof Chruscinski041f0e52022-06-20 07:43:37 +0200850 LOG_INF("SMS: %s", argv[0]);
Lukas Gehreke53dea672021-08-27 10:21:14 +0200851 return 0;
852}
853
854/*
855 * Handles socket data notification.
856 *
857 * The sim modem sends and unsolicited +CADATAIND: <cid>
858 * if data can be read from a socket.
859 */
860MODEM_CMD_DEFINE(on_urc_cadataind)
861{
862 struct modem_socket *sock;
863 int sock_fd;
864
865 sock_fd = atoi(argv[0]);
866
867 sock = modem_socket_from_fd(&mdata.socket_config, sock_fd);
868 if (!sock) {
869 return 0;
870 }
871
872 /* Modem does not tell packet size. Set dummy for receive. */
873 modem_socket_packet_size_update(&mdata.socket_config, sock, 1);
874
875 LOG_INF("Data available on socket: %d", sock_fd);
876 modem_socket_data_ready(&mdata.socket_config, sock);
877
878 return 0;
879}
880
881/*
882 * Handles the castate response.
883 *
884 * +CASTATE: <cid>,<state>
885 *
886 * Cid is the connection id (socket fd) and
887 * state can be:
888 * 0 - Closed by remote server or error
889 * 1 - Connected to remote server
890 * 2 - Listening
891 */
892MODEM_CMD_DEFINE(on_urc_castate)
893{
894 struct modem_socket *sock;
895 int sockfd, state;
896
897 sockfd = atoi(argv[0]);
898 state = atoi(argv[1]);
899
900 sock = modem_socket_from_fd(&mdata.socket_config, sockfd);
901 if (!sock) {
902 return 0;
903 }
904
905 /* Only continue if socket was closed. */
906 if (state != 0) {
907 return 0;
908 }
909
910 LOG_INF("Socket close indication for socket: %d", sockfd);
911
912 sock->is_connected = false;
913 LOG_INF("Socket closed: %d", sockfd);
914
915 return 0;
916}
917
918/**
919 * Handles the ftpget urc.
920 *
921 * +FTPGET: <mode>,<error>
922 *
923 * Mode can be 1 for opening a session and
924 * reporting that data is available or 2 for
925 * reading data. This urc handler will only handle
926 * mode 1 because 2 will not occur as urc.
927 *
928 * Error can be either:
929 * - 1 for data available/opened session.
930 * - 0 If transfer is finished.
931 * - >0 for some error.
932 */
933MODEM_CMD_DEFINE(on_urc_ftpget)
934{
935 int error = atoi(argv[0]);
936
937 LOG_INF("+FTPGET: 1,%d", error);
938
939 /* Transfer finished. */
940 if (error == 0) {
941 mdata.ftp.state = SIM7080_FTP_CONNECTION_STATE_FINISHED;
942 } else if (error == 1) {
943 mdata.ftp.state = SIM7080_FTP_CONNECTION_STATE_CONNECTED;
944 } else {
945 mdata.ftp.state = SIM7080_FTP_CONNECTION_STATE_ERROR;
946 }
947
948 k_sem_give(&mdata.sem_ftp);
949
950 return 0;
951}
952
953/*
Nazar Kazakov9713f0d2022-02-24 12:00:55 +0000954 * Read manufacturer identification.
Lukas Gehreke53dea672021-08-27 10:21:14 +0200955 */
956MODEM_CMD_DEFINE(on_cmd_cgmi)
957{
958 size_t out_len = net_buf_linearize(
959 mdata.mdm_manufacturer, sizeof(mdata.mdm_manufacturer) - 1, data->rx_buf, 0, len);
960 mdata.mdm_manufacturer[out_len] = '\0';
Krzysztof Chruscinski041f0e52022-06-20 07:43:37 +0200961 LOG_INF("Manufacturer: %s", mdata.mdm_manufacturer);
Lukas Gehreke53dea672021-08-27 10:21:14 +0200962 return 0;
963}
964
965/*
966 * Read model identification.
967 */
968MODEM_CMD_DEFINE(on_cmd_cgmm)
969{
970 size_t out_len = net_buf_linearize(mdata.mdm_model, sizeof(mdata.mdm_model) - 1,
971 data->rx_buf, 0, len);
972 mdata.mdm_model[out_len] = '\0';
Krzysztof Chruscinski041f0e52022-06-20 07:43:37 +0200973 LOG_INF("Model: %s", mdata.mdm_model);
Lukas Gehreke53dea672021-08-27 10:21:14 +0200974 return 0;
975}
976
977/*
978 * Read software release.
979 *
980 * Response will be in format RESPONSE: <revision>.
981 */
982MODEM_CMD_DEFINE(on_cmd_cgmr)
983{
984 size_t out_len;
985 char *p;
986
987 out_len = net_buf_linearize(mdata.mdm_revision, sizeof(mdata.mdm_revision) - 1,
988 data->rx_buf, 0, len);
989 mdata.mdm_revision[out_len] = '\0';
990
991 /* The module prepends a Revision: */
992 p = strchr(mdata.mdm_revision, ':');
993 if (p) {
994 out_len = strlen(p + 1);
995 memmove(mdata.mdm_revision, p + 1, out_len + 1);
996 }
997
Krzysztof Chruscinski041f0e52022-06-20 07:43:37 +0200998 LOG_INF("Revision: %s", mdata.mdm_revision);
Lukas Gehreke53dea672021-08-27 10:21:14 +0200999 return 0;
1000}
1001
1002/*
1003 * Read serial number identification.
1004 */
1005MODEM_CMD_DEFINE(on_cmd_cgsn)
1006{
1007 size_t out_len =
1008 net_buf_linearize(mdata.mdm_imei, sizeof(mdata.mdm_imei) - 1, data->rx_buf, 0, len);
1009 mdata.mdm_imei[out_len] = '\0';
Krzysztof Chruscinski041f0e52022-06-20 07:43:37 +02001010 LOG_INF("IMEI: %s", mdata.mdm_imei);
Lukas Gehreke53dea672021-08-27 10:21:14 +02001011 return 0;
1012}
1013
1014#if defined(CONFIG_MODEM_SIM_NUMBERS)
1015/*
1016 * Read international mobile subscriber identity.
1017 */
1018MODEM_CMD_DEFINE(on_cmd_cimi)
1019{
1020 size_t out_len =
1021 net_buf_linearize(mdata.mdm_imsi, sizeof(mdata.mdm_imsi) - 1, data->rx_buf, 0, len);
1022 mdata.mdm_imsi[out_len] = '\0';
1023
1024 /* Log the received information. */
Krzysztof Chruscinski041f0e52022-06-20 07:43:37 +02001025 LOG_INF("IMSI: %s", mdata.mdm_imsi);
Lukas Gehreke53dea672021-08-27 10:21:14 +02001026 return 0;
1027}
1028
1029/*
1030 * Read iccid.
1031 */
1032MODEM_CMD_DEFINE(on_cmd_ccid)
1033{
1034 size_t out_len = net_buf_linearize(mdata.mdm_iccid, sizeof(mdata.mdm_iccid) - 1,
1035 data->rx_buf, 0, len);
1036 mdata.mdm_iccid[out_len] = '\0';
1037
1038 /* Log the received information. */
Krzysztof Chruscinski041f0e52022-06-20 07:43:37 +02001039 LOG_INF("ICCID: %s", mdata.mdm_iccid);
Lukas Gehreke53dea672021-08-27 10:21:14 +02001040 return 0;
1041}
1042#endif /* defined(CONFIG_MODEM_SIM_NUMBERS) */
1043
1044/*
1045 * Parses the non urc C(E)REG and updates registration status.
1046 */
1047MODEM_CMD_DEFINE(on_cmd_cereg)
1048{
1049 mdata.mdm_registration = atoi(argv[1]);
1050 LOG_INF("CREG: %u", mdata.mdm_registration);
1051 return 0;
1052}
1053
1054MODEM_CMD_DEFINE(on_cmd_cpin)
1055{
1056 mdata.cpin_ready = strcmp(argv[0], "READY") == 0;
1057 LOG_INF("CPIN: %d", mdata.cpin_ready);
1058 return 0;
1059}
1060
1061MODEM_CMD_DEFINE(on_cmd_cgatt)
1062{
1063 mdata.mdm_cgatt = atoi(argv[0]);
1064 LOG_INF("CGATT: %d", mdata.mdm_cgatt);
1065 return 0;
1066}
1067
1068/*
1069 * Handler for RSSI query.
1070 *
1071 * +CSQ: <rssi>,<ber>
1072 * rssi: 0,-115dBm; 1,-111dBm; 2...30,-110...-54dBm; 31,-52dBm or greater.
1073 * 99, ukn
1074 * ber: Not used.
1075 */
1076MODEM_CMD_DEFINE(on_cmd_csq)
1077{
1078 int rssi = atoi(argv[0]);
1079
1080 if (rssi == 0) {
1081 mdata.mdm_rssi = -115;
1082 } else if (rssi == 1) {
1083 mdata.mdm_rssi = -111;
1084 } else if (rssi > 1 && rssi < 31) {
1085 mdata.mdm_rssi = -114 + 2 * rssi;
1086 } else if (rssi == 31) {
1087 mdata.mdm_rssi = -52;
1088 } else {
1089 mdata.mdm_rssi = -1000;
1090 }
1091
1092 LOG_INF("RSSI: %d", mdata.mdm_rssi);
1093 return 0;
1094}
1095
1096/*
1097 * Queries modem RSSI.
1098 *
1099 * If a work queue parameter is provided query work will
1100 * be scheduled. Otherwise rssi is queried once.
1101 */
1102static void modem_rssi_query_work(struct k_work *work)
1103{
1104 struct modem_cmd cmd[] = { MODEM_CMD("+CSQ: ", on_cmd_csq, 2U, ",") };
1105 static char *send_cmd = "AT+CSQ";
1106 int ret;
1107
1108 ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler, cmd, ARRAY_SIZE(cmd), send_cmd,
1109 &mdata.sem_response, MDM_CMD_TIMEOUT);
1110 if (ret < 0) {
1111 LOG_ERR("AT+CSQ ret:%d", ret);
1112 }
1113
1114 if (work) {
1115 k_work_reschedule_for_queue(&modem_workq, &mdata.rssi_query_work,
1116 K_SECONDS(RSSI_TIMEOUT_SECS));
1117 }
1118}
1119
1120/*
1121 * Possible responses by the sim7080.
1122 */
1123static const struct modem_cmd response_cmds[] = {
1124 MODEM_CMD("OK", on_cmd_ok, 0U, ""),
1125 MODEM_CMD("ERROR", on_cmd_error, 0U, ""),
1126 MODEM_CMD("+CME ERROR: ", on_cmd_exterror, 1U, ""),
1127 MODEM_CMD_DIRECT(">", on_cmd_tx_ready),
1128};
1129
1130/*
1131 * Possible unsolicited commands.
1132 */
1133static const struct modem_cmd unsolicited_cmds[] = {
1134 MODEM_CMD("+APP PDP: ", on_urc_app_pdp, 2U, ","),
1135 MODEM_CMD("SMS ", on_urc_sms, 1U, ""),
1136 MODEM_CMD("+CADATAIND: ", on_urc_cadataind, 1U, ""),
1137 MODEM_CMD("+CASTATE: ", on_urc_castate, 2U, ","),
1138 MODEM_CMD("+FTPGET: 1,", on_urc_ftpget, 1U, ""),
1139};
1140
1141/*
1142 * Activates the pdp context
1143 */
1144static int modem_pdp_activate(void)
1145{
1146 int counter;
1147 int ret = 0;
1148#if defined(CONFIG_MODEM_SIMCOM_SIM7080_RAT_GSM)
1149 const char *buf = "AT+CREG?";
1150 struct modem_cmd cmds[] = { MODEM_CMD("+CREG: ", on_cmd_cereg, 2U, ",") };
1151#else
1152 const char *buf = "AT+CEREG?";
1153 struct modem_cmd cmds[] = { MODEM_CMD("+CEREG: ", on_cmd_cereg, 2U, ",") };
1154#endif /* defined(CONFIG_MODEM_SIMCOM_SIM7080_RAT_GSM) */
1155
1156 struct modem_cmd cgatt_cmd[] = { MODEM_CMD("+CGATT: ", on_cmd_cgatt, 1U, "") };
1157
1158 counter = 0;
1159 while (counter++ < MDM_MAX_CGATT_WAITS && mdata.mdm_cgatt != 1) {
1160 ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler, cgatt_cmd,
1161 ARRAY_SIZE(cgatt_cmd), "AT+CGATT?", &mdata.sem_response,
1162 MDM_CMD_TIMEOUT);
1163 if (ret < 0) {
1164 LOG_ERR("Failed to query cgatt!!");
1165 return -1;
1166 }
1167
1168 k_sleep(K_SECONDS(1));
1169 }
1170
1171 if (counter >= MDM_MAX_CGATT_WAITS) {
1172 LOG_WRN("Network attach failed!!");
1173 return -1;
1174 }
1175
1176 if (!mdata.cpin_ready || mdata.mdm_cgatt != 1) {
1177 LOG_ERR("Fatal: Modem is not attached to GPRS network!!");
1178 return -1;
1179 }
1180
1181 LOG_INF("Waiting for network");
1182
1183 /* Wait until the module is registered to the network.
1184 * Registration will be set by urc.
1185 */
1186 counter = 0;
1187 while (counter++ < MDM_MAX_CEREG_WAITS && mdata.mdm_registration != 1 &&
1188 mdata.mdm_registration != 5) {
1189 ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler, cmds, ARRAY_SIZE(cmds), buf,
1190 &mdata.sem_response, MDM_CMD_TIMEOUT);
1191 if (ret < 0) {
1192 LOG_ERR("Failed to query registration!!");
1193 return -1;
1194 }
1195
1196 k_sleep(K_SECONDS(1));
1197 }
1198
1199 if (counter >= MDM_MAX_CEREG_WAITS) {
1200 LOG_WRN("Network registration failed!");
1201 ret = -1;
1202 goto error;
1203 }
1204
1205 /* Set dual stack mode (IPv4/IPv6) */
1206 ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler, NULL, 0, "AT+CNCFG=0,0",
1207 &mdata.sem_response, MDM_CMD_TIMEOUT);
1208 if (ret < 0) {
1209 LOG_ERR("Could not configure pdp context!");
1210 goto error;
1211 }
1212
1213 /*
1214 * Now activate the pdp context and wait for confirmation.
1215 */
1216 ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler, NULL, 0, "AT+CNACT=0,1",
1217 &mdata.sem_response, MDM_CMD_TIMEOUT);
1218 if (ret < 0) {
1219 LOG_ERR("Could not activate PDP context.");
1220 goto error;
1221 }
1222
1223 ret = k_sem_take(&mdata.sem_response, MDM_PDP_TIMEOUT);
1224 if (ret < 0 || mdata.pdp_active == false) {
1225 LOG_ERR("Failed to activate PDP context.");
1226 ret = -1;
1227 goto error;
1228 }
1229
1230 LOG_INF("Network active.");
1231
1232error:
1233 return ret;
1234}
1235
1236/*
1237 * Toggles the modems power pin.
1238 */
1239static void modem_pwrkey(void)
1240{
1241 /* Power pin should be high for 1.5 seconds. */
Marcin Niestroj41c618d2022-05-13 19:20:16 +02001242 gpio_pin_set_dt(&power_gpio, 1);
Lukas Gehreke53dea672021-08-27 10:21:14 +02001243 k_sleep(K_MSEC(1500));
Marcin Niestroj41c618d2022-05-13 19:20:16 +02001244 gpio_pin_set_dt(&power_gpio, 0);
Lukas Gehreke53dea672021-08-27 10:21:14 +02001245 k_sleep(K_SECONDS(5));
1246}
1247
1248/*
1249 * Commands to be sent at setup.
1250 */
1251static const struct setup_cmd setup_cmds[] = {
1252 SETUP_CMD_NOHANDLE("ATH"),
1253 SETUP_CMD("AT+CGMI", "", on_cmd_cgmi, 0U, ""),
1254 SETUP_CMD("AT+CGMM", "", on_cmd_cgmm, 0U, ""),
1255 SETUP_CMD("AT+CGMR", "", on_cmd_cgmr, 0U, ""),
1256 SETUP_CMD("AT+CGSN", "", on_cmd_cgsn, 0U, ""),
1257#if defined(CONFIG_MODEM_SIM_NUMBERS)
1258 SETUP_CMD("AT+CIMI", "", on_cmd_cimi, 0U, ""),
1259 SETUP_CMD("AT+CCID", "", on_cmd_ccid, 0U, ""),
1260#endif /* defined(CONFIG_MODEM_SIM_NUMBERS) */
1261#if defined(CONFIG_MODEM_SIMCOM_SIM7080_RAT_NB1)
1262 SETUP_CMD_NOHANDLE("AT+CNMP=38"),
1263 SETUP_CMD_NOHANDLE("AT+CMNB=2"),
1264 SETUP_CMD_NOHANDLE("AT+CBANDCFG=\"NB-IOT\"," MDM_LTE_BANDS),
1265#endif /* defined(CONFIG_MODEM_SIMCOM_SIM7080_RAT_NB1) */
1266#if defined(CONFIG_MODEM_SIMCOM_SIM7080_RAT_M1)
1267 SETUP_CMD_NOHANDLE("AT+CNMP=38"),
1268 SETUP_CMD_NOHANDLE("AT+CMNB=1"),
1269 SETUP_CMD_NOHANDLE("AT+CBANDCFG=\"CAT-M\"," MDM_LTE_BANDS),
1270#endif /* defined(CONFIG_MODEM_SIMCOM_SIM7080_RAT_M1) */
1271#if defined(CONFIG_MODEM_SIMCOM_SIM7080_RAT_GSM)
1272 SETUP_CMD_NOHANDLE("AT+CNMP=13"),
1273#endif /* defined(CONFIG_MODEM_SIMCOM_SIM7080_RAT_GSM) */
1274 SETUP_CMD("AT+CPIN?", "+CPIN: ", on_cmd_cpin, 1U, ""),
1275};
1276
1277/**
1278 * Performs the autobaud sequence until modem answers or limit is reached.
1279 *
1280 * @return On successful boot 0 is returned. Otherwise <0 is returned.
1281 */
1282static int modem_autobaud(void)
1283{
1284 int boot_tries = 0;
1285 int counter = 0;
1286 int ret;
1287
1288 while (boot_tries++ <= MDM_BOOT_TRIES) {
1289 modem_pwrkey();
1290
1291 /*
1292 * The sim7080 has a autobaud function.
1293 * On startup multiple AT's are sent until
1294 * a OK is received.
1295 */
1296 counter = 0;
1297 while (counter < MDM_MAX_AUTOBAUD) {
1298 ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler, NULL, 0U, "AT",
1299 &mdata.sem_response, K_MSEC(500));
1300
1301 /* OK was received. */
1302 if (ret == 0) {
1303 /* Disable echo */
1304 return modem_cmd_send(&mctx.iface, &mctx.cmd_handler, NULL, 0U,
1305 "ATE0", &mdata.sem_response, K_SECONDS(2));
1306 }
1307
1308 counter++;
1309 }
1310 }
1311
1312 return -1;
1313}
1314
1315/**
1316 * Get the next parameter from the gnss phrase.
1317 *
1318 * @param src The source string supported on first call.
Nazar Kazakov9713f0d2022-02-24 12:00:55 +00001319 * @param delim The delimiter of the parameter list.
Lukas Gehreke53dea672021-08-27 10:21:14 +02001320 * @param saveptr Pointer for subsequent parses.
1321 * @return On success a pointer to the parameter. On failure
1322 * or end of string NULL is returned.
1323 *
1324 * This function is used instead of strtok because strtok would
1325 * skip empty parameters, which is not desired. The modem may
1326 * omit parameters which could lead to a incorrect parse.
1327 */
1328static char *gnss_get_next_param(char *src, const char *delim, char **saveptr)
1329{
1330 char *start, *del;
1331
1332 if (src) {
1333 start = src;
1334 } else {
1335 start = *saveptr;
1336 }
1337
1338 /* Illegal start string. */
1339 if (!start) {
1340 return NULL;
1341 }
1342
1343 /* End of string reached. */
1344 if (*start == '\0' || *start == '\r') {
1345 return NULL;
1346 }
1347
1348 del = strstr(start, delim);
1349 if (!del) {
1350 return NULL;
1351 }
1352
1353 *del = '\0';
1354 *saveptr = del + 1;
1355
1356 if (del == start) {
1357 return NULL;
1358 }
1359
1360 return start;
1361}
1362
1363static void gnss_skip_param(char **saveptr)
1364{
1365 gnss_get_next_param(NULL, ",", saveptr);
1366}
1367
1368/**
1369 * Splits float parameters of the CGNSINF response on '.'
1370 *
1371 * @param src Null terminated string containing the float.
1372 * @param f1 Resulting number part of the float.
1373 * @param f2 Resulting fraction part of the float.
1374 * @return 0 if parsing was successful. Otherwise <0 is returned.
1375 *
1376 * If the number part of the float is negative f1 and f2 will be
1377 * negative too.
1378 */
1379static int gnss_split_on_dot(const char *src, int32_t *f1, int32_t *f2)
1380{
1381 char *dot = strchr(src, '.');
1382
1383 if (!dot) {
1384 return -1;
1385 }
1386
1387 *dot = '\0';
1388
1389 *f1 = (int32_t)strtol(src, NULL, 10);
1390 *f2 = (int32_t)strtol(dot + 1, NULL, 10);
1391
1392 if (*f1 < 0) {
1393 *f2 = -*f2;
1394 }
1395
1396 return 0;
1397}
1398
1399/**
1400 * Parses cgnsinf response into the gnss_data structure.
1401 *
1402 * @param gps_buf Null terminated buffer containing the response.
1403 * @return 0 on successful parse. Otherwise <0 is returned.
1404 */
1405static int parse_cgnsinf(char *gps_buf)
1406{
1407 char *saveptr;
1408 int ret;
1409 int32_t number, fraction;
1410
1411 char *run_status = gnss_get_next_param(gps_buf, ",", &saveptr);
1412
1413 if (run_status == NULL) {
1414 goto error;
1415 } else if (*run_status != '1') {
1416 goto error;
1417 }
1418
1419 char *fix_status = gnss_get_next_param(NULL, ",", &saveptr);
1420
1421 if (fix_status == NULL) {
1422 goto error;
1423 } else if (*fix_status != '1') {
1424 goto error;
1425 }
1426
1427 char *utc = gnss_get_next_param(NULL, ",", &saveptr);
1428
1429 if (utc == NULL) {
1430 goto error;
1431 }
1432
1433 char *lat = gnss_get_next_param(NULL, ",", &saveptr);
1434
1435 if (lat == NULL) {
1436 goto error;
1437 }
1438
1439 char *lon = gnss_get_next_param(NULL, ",", &saveptr);
1440
1441 if (lon == NULL) {
1442 goto error;
1443 }
1444
1445 char *alt = gnss_get_next_param(NULL, ",", &saveptr);
1446 char *speed = gnss_get_next_param(NULL, ",", &saveptr);
1447 char *course = gnss_get_next_param(NULL, ",", &saveptr);
1448
1449 /* discard fix mode and reserved*/
1450 gnss_skip_param(&saveptr);
1451 gnss_skip_param(&saveptr);
1452
1453 char *hdop = gnss_get_next_param(NULL, ",", &saveptr);
1454
1455 if (hdop == NULL) {
1456 goto error;
1457 }
1458
1459 gnss_data.run_status = 1;
1460 gnss_data.fix_status = 1;
1461
1462 strncpy(gnss_data.utc, utc, sizeof(gnss_data.utc));
1463
1464 ret = gnss_split_on_dot(lat, &number, &fraction);
1465 if (ret != 0) {
1466 goto error;
1467 }
1468 gnss_data.lat = number * 10000000 + fraction * 10;
1469
1470 ret = gnss_split_on_dot(lon, &number, &fraction);
1471 if (ret != 0) {
1472 goto error;
1473 }
1474 gnss_data.lon = number * 10000000 + fraction * 10;
1475
1476 if (alt) {
1477 ret = gnss_split_on_dot(alt, &number, &fraction);
1478 if (ret != 0) {
1479 goto error;
1480 }
1481 gnss_data.alt = number * 1000 + fraction;
1482 } else {
1483 gnss_data.alt = 0;
1484 }
1485
1486 ret = gnss_split_on_dot(hdop, &number, &fraction);
1487 if (ret != 0) {
1488 goto error;
1489 }
1490 gnss_data.hdop = number * 100 + fraction * 10;
1491
1492 if (course) {
1493 ret = gnss_split_on_dot(course, &number, &fraction);
1494 if (ret != 0) {
1495 goto error;
1496 }
1497 gnss_data.cog = number * 100 + fraction * 10;
1498 } else {
1499 gnss_data.cog = 0;
1500 }
1501
1502 if (speed) {
1503 ret = gnss_split_on_dot(speed, &number, &fraction);
1504 if (ret != 0) {
1505 goto error;
1506 }
1507 gnss_data.kmh = number * 10 + fraction / 10;
1508 } else {
1509 gnss_data.kmh = 0;
1510 }
1511
1512 return 0;
1513error:
1514 memset(&gnss_data, 0, sizeof(gnss_data));
1515 return -1;
1516}
1517
1518/*
1519 * Parses the +CGNSINF Gnss response.
1520 *
1521 * The CGNSINF command has the following parameters but
1522 * not all parameters are set by the module:
1523 *
1524 * +CGNSINF: <GNSS run status>,<Fix status>,<UTC date & Time>,
1525 * <Latitude>,<Longitude>,<MSL Altitude>,<Speed Over Ground>,
1526 * <Course Over Ground>,<Fix Mode>,<Reserved1>,<HDOP>,<PDOP>,
1527 * <VDOP>,<Reserved2>,<GNSS Satellites in View>,<Reserved3>,
1528 * <HPA>,<VPA>
1529 *
1530 */
1531MODEM_CMD_DEFINE(on_cmd_cgnsinf)
1532{
1533 char gps_buf[MDM_GNSS_PARSER_MAX_LEN];
1534 size_t out_len = net_buf_linearize(gps_buf, sizeof(gps_buf) - 1, data->rx_buf, 0, len);
1535
1536 gps_buf[out_len] = '\0';
1537 return parse_cgnsinf(gps_buf);
1538}
1539
1540int mdm_sim7080_query_gnss(struct sim7080_gnss_data *data)
1541{
1542 int ret;
1543 struct modem_cmd cmds[] = { MODEM_CMD("+CGNSINF: ", on_cmd_cgnsinf, 0U, NULL) };
1544
1545 if (get_state() != SIM7080_STATE_GNSS) {
1546 LOG_ERR("GNSS functionality is not enabled!!");
1547 return -1;
1548 }
1549
1550 ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler, cmds, ARRAY_SIZE(cmds), "AT+CGNSINF",
1551 &mdata.sem_response, K_SECONDS(2));
1552 if (ret < 0) {
1553 return ret;
1554 }
1555
1556 if (!gnss_data.run_status || !gnss_data.fix_status) {
1557 return -EAGAIN;
1558 }
1559
1560 if (data) {
1561 memcpy(data, &gnss_data, sizeof(gnss_data));
1562 }
1563
1564 memset(&gnss_data, 0, sizeof(gnss_data));
1565
1566 return ret;
1567}
1568
1569int mdm_sim7080_start_gnss(void)
1570{
1571 int ret;
1572
1573 change_state(SIM7080_STATE_INIT);
1574 k_work_cancel_delayable(&mdata.rssi_query_work);
1575
1576 ret = modem_autobaud();
1577 if (ret < 0) {
1578 LOG_ERR("Failed to start modem!!");
1579 return -1;
1580 }
1581
1582 ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler, NULL, 0U, "AT+CGNSCOLD",
1583 &mdata.sem_response, K_SECONDS(2));
1584 if (ret < 0) {
1585 return -1;
1586 }
1587
1588 change_state(SIM7080_STATE_GNSS);
1589 return 0;
1590}
1591
1592/**
1593 * Parse the +FTPGET response.
1594 *
1595 * +FTPGET: <mode>,<len>
1596 *
1597 * Mode is hard set to 2.
1598 *
1599 * Length is the number of bytes following (the ftp data).
1600 */
1601MODEM_CMD_DEFINE(on_cmd_ftpget)
1602{
1603 int nbytes = atoi(argv[0]);
1604 int bytes_to_skip;
1605 size_t out_len;
1606
1607 if (nbytes == 0) {
1608 mdata.ftp.nread = 0;
1609 return 0;
1610 }
1611
1612 /* Skip length parameter and trailing \r\n */
1613 bytes_to_skip = strlen(argv[0]) + 2;
1614
1615 /* Wait until data is ready.
1616 * >= to ensure buffer is not empty after skip.
1617 */
1618 if (net_buf_frags_len(data->rx_buf) <= nbytes + bytes_to_skip) {
1619 return -EAGAIN;
1620 }
1621
1622 out_len = net_buf_linearize(mdata.ftp.read_buffer, mdata.ftp.nread, data->rx_buf,
1623 bytes_to_skip, nbytes);
1624 if (out_len != nbytes) {
1625 LOG_WRN("FTP read size differs!");
1626 }
1627 data->rx_buf = net_buf_skip(data->rx_buf, nbytes + bytes_to_skip);
1628
1629 mdata.ftp.nread = nbytes;
1630
1631 return 0;
1632}
1633
1634int mdm_sim7080_ftp_get_read(char *dst, size_t *size)
1635{
1636 int ret;
1637 char buffer[sizeof("AT+FTPGET=#,######")];
1638 struct modem_cmd cmds[] = { MODEM_CMD("+FTPGET: 2,", on_cmd_ftpget, 1U, "") };
1639
1640 /* Some error occurred. */
1641 if (mdata.ftp.state == SIM7080_FTP_CONNECTION_STATE_ERROR ||
1642 mdata.ftp.state == SIM7080_FTP_CONNECTION_STATE_INITIAL) {
1643 return SIM7080_FTP_RC_ERROR;
1644 }
1645
1646 /* Setup buffer. */
1647 mdata.ftp.read_buffer = dst;
1648 mdata.ftp.nread = *size;
1649
1650 /* Read ftp data. */
1651 ret = snprintk(buffer, sizeof(buffer), "AT+FTPGET=2,%zu", *size);
1652 if (ret < 0) {
1653 *size = 0;
1654 return SIM7080_FTP_RC_ERROR;
1655 }
1656
1657 /* Wait for data from the server. */
1658 k_sem_take(&mdata.sem_ftp, K_MSEC(200));
1659
1660 if (mdata.ftp.state == SIM7080_FTP_CONNECTION_STATE_FINISHED) {
1661 *size = 0;
1662 return SIM7080_FTP_RC_FINISHED;
1663 } else if (mdata.ftp.state == SIM7080_FTP_CONNECTION_STATE_ERROR) {
1664 *size = 0;
1665 return SIM7080_FTP_RC_ERROR;
1666 }
1667
1668 ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler, cmds, ARRAY_SIZE(cmds), buffer,
1669 &mdata.sem_response, MDM_CMD_TIMEOUT);
1670 if (ret < 0) {
1671 *size = 0;
1672 return SIM7080_FTP_RC_ERROR;
1673 }
1674
1675 /* Set read size. */
1676 *size = mdata.ftp.nread;
1677
1678 return SIM7080_FTP_RC_OK;
1679}
1680
1681int mdm_sim7080_ftp_get_start(const char *server, const char *user, const char *passwd,
1682 const char *file, const char *path)
1683{
1684 int ret;
1685 char buffer[256];
1686
1687 /* Start network. */
1688 ret = mdm_sim7080_start_network();
1689 if (ret < 0) {
1690 LOG_ERR("Failed to start network for FTP!");
1691 return -1;
1692 }
1693
1694 /* Set connection id for ftp. */
1695 ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler, NULL, 0U, "AT+FTPCID=0",
1696 &mdata.sem_response, MDM_CMD_TIMEOUT);
1697 if (ret < 0) {
1698 LOG_WRN("Failed to set FTP Cid!");
1699 return -1;
1700 }
1701
1702 /* Set ftp server. */
1703 ret = snprintk(buffer, sizeof(buffer), "AT+FTPSERV=\"%s\"", server);
1704 if (ret < 0) {
1705 LOG_WRN("Failed to build command!");
1706 return -1;
1707 }
1708
1709 ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler, NULL, 0U, buffer, &mdata.sem_response,
1710 MDM_CMD_TIMEOUT);
1711 if (ret < 0) {
1712 LOG_WRN("Failed to set FTP Cid!");
1713 return -1;
1714 }
1715
1716 /* Set ftp user. */
1717 ret = snprintk(buffer, sizeof(buffer), "AT+FTPUN=\"%s\"", user);
1718 if (ret < 0) {
1719 LOG_WRN("Failed to build command!");
1720 return -1;
1721 }
1722
1723 ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler, NULL, 0U, buffer, &mdata.sem_response,
1724 MDM_CMD_TIMEOUT);
1725 if (ret < 0) {
1726 LOG_WRN("Failed to set ftp user!");
1727 return -1;
1728 }
1729
1730 /* Set ftp password. */
1731 ret = snprintk(buffer, sizeof(buffer), "AT+FTPPW=\"%s\"", passwd);
1732 if (ret < 0) {
1733 LOG_WRN("Failed to build command!");
1734 return -1;
1735 }
1736
1737 ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler, NULL, 0U, buffer, &mdata.sem_response,
1738 MDM_CMD_TIMEOUT);
1739 if (ret < 0) {
1740 LOG_WRN("Failed to set ftp password!");
1741 return -1;
1742 }
1743
1744 /* Set ftp filename. */
1745 ret = snprintk(buffer, sizeof(buffer), "AT+FTPGETNAME=\"%s\"", file);
1746 if (ret < 0) {
1747 LOG_WRN("Failed to build command!");
1748 return -1;
1749 }
1750
1751 ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler, NULL, 0U, buffer, &mdata.sem_response,
1752 MDM_CMD_TIMEOUT);
1753 if (ret < 0) {
1754 LOG_WRN("Failed to set ftp filename!");
1755 return -1;
1756 }
1757
1758 /* Set ftp filename. */
1759 ret = snprintk(buffer, sizeof(buffer), "AT+FTPGETNAME=\"%s\"", file);
1760 if (ret < 0) {
1761 LOG_WRN("Failed to build command!");
1762 return -1;
1763 }
1764
1765 ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler, NULL, 0U, buffer, &mdata.sem_response,
1766 MDM_CMD_TIMEOUT);
1767 if (ret < 0) {
1768 LOG_WRN("Failed to set ftp filename!");
1769 return -1;
1770 }
1771
1772 /* Set ftp path. */
1773 ret = snprintk(buffer, sizeof(buffer), "AT+FTPGETPATH=\"%s\"", path);
1774 if (ret < 0) {
1775 LOG_WRN("Failed to build command!");
1776 return -1;
1777 }
1778
1779 ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler, NULL, 0U, buffer, &mdata.sem_response,
1780 MDM_CMD_TIMEOUT);
1781 if (ret < 0) {
1782 LOG_WRN("Failed to set ftp path!");
1783 return -1;
1784 }
1785
1786 /* Initialize ftp variables. */
1787 mdata.ftp.read_buffer = NULL;
1788 mdata.ftp.nread = 0;
1789 mdata.ftp.state = SIM7080_FTP_CONNECTION_STATE_INITIAL;
1790
1791 /* Start the ftp session. */
1792 ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler, NULL, 0U, "AT+FTPGET=1",
1793 &mdata.sem_ftp, MDM_CMD_TIMEOUT);
1794 if (ret < 0) {
1795 LOG_WRN("Failed to start session!");
1796 return -1;
1797 }
1798
1799 if (mdata.ftp.state != SIM7080_FTP_CONNECTION_STATE_CONNECTED) {
1800 LOG_WRN("Session state is not connected!");
1801 return -1;
1802 }
1803
1804 return 0;
1805}
1806
1807/**
1808 * Decode readable hex to "real" hex.
1809 */
1810static uint8_t mdm_pdu_decode_ascii(char byte)
1811{
1812 if ((byte >= '0') && (byte <= '9'))
1813 return byte - '0';
1814 else if ((byte >= 'A') && (byte <= 'F'))
1815 return byte - 'A' + 10;
1816 else if ((byte >= 'a') && (byte <= 'f'))
1817 return byte - 'a' + 10;
1818 else
1819 return 255;
1820}
1821
1822/**
1823 * Reads "byte" from pdu.
1824 *
1825 * @param pdu pdu to read from.
1826 * @param index index of "byte".
1827 *
1828 * Sim module "encodes" one pdu byte as two human readable bytes
1829 * this functions squashes these two bytes into one.
1830 */
1831static uint8_t mdm_pdu_read_byte(const char *pdu, size_t index)
1832{
1833 return (mdm_pdu_decode_ascii(pdu[index * 2]) << 4 |
1834 mdm_pdu_decode_ascii(pdu[index * 2 + 1]));
1835}
1836
1837/**
1838 * Decodes time from pdu.
1839 *
1840 * @param pdu pdu to read from.
1841 * @param index index of "byte".
1842 */
1843static uint8_t mdm_pdu_read_time(const char *pdu, size_t index)
1844{
1845 return (mdm_pdu_decode_ascii(pdu[index * 2]) +
1846 mdm_pdu_decode_ascii(pdu[index * 2 + 1]) * 10);
1847}
1848
1849/**
1850 * Decode a sms from pdu mode.
1851 */
1852static int mdm_decode_pdu(const char *pdu, size_t pdu_len, struct sim7080_sms *target_buf)
1853{
1854 size_t index;
1855
1856 /*
1857 * GSM_03.38 to Unicode conversion table
1858 */
1859 const short enc7_basic[128] = {
1860 '@', 0xA3, '$', 0xA5, 0xE8, 0xE9, 0xF9, 0xEC, 0xF2, 0xE7,
1861 '\n', 0xD8, 0xF8, '\r', 0xC5, 0xF8, 0x0394, '_', 0x03A6, 0x0393,
1862 0x039B, 0x03A9, 0x03A0, 0x03A8, 0x03A3, 0x0398, 0x039E, '\x1b', 0xC6, 0xE6,
1863 0xDF, 0xC9, ' ', '!', '\"', '#', 0xA4, '%', '&', '\'',
1864 '(', ')', '*', '+', ',', '-', '.', '/', '0', '1',
1865 '2', '3', '4', '5', '6', '7', '8', '9', ':', ';',
1866 '<', '=', '>', '?', 0xA1, 'A', 'B', 'C', 'D', 'E',
1867 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
1868 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y',
1869 'Z', 0xC4, 0xD6, 0xD1, 0xDC, 0xA7, 0xBF, 'a', 'b', 'c',
1870 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
1871 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w',
1872 'x', 'y', 'z', 0xE4, 0xF6, 0xF1, 0xFC, 0xE0
1873 };
1874
1875 /* two bytes in pdu are on real byte */
1876 pdu_len = (pdu_len / 2);
1877
1878 /* first byte of pdu is length of trailing SMSC information
1879 * skip it by setting index to SMSC length + 1.
1880 */
1881 index = mdm_pdu_read_byte(pdu, 0) + 1;
1882
1883 if (index >= pdu_len) {
1884 return -1;
1885 }
1886
1887 /* read first octet */
1888 target_buf->first_octet = mdm_pdu_read_byte(pdu, index++);
1889
1890 if (index >= pdu_len) {
1891 return -1;
1892 }
1893
1894 /* pdu_index now points to the address field.
1895 * first byte of addr field is the addr length -> skip it.
1896 * address type is not included in addr len -> add +1.
1897 * address is coded in semi octets
1898 * + addr_len/2 if even
1899 * + addr_len/2 + 1 if odd
1900 */
1901 uint8_t addr_len = mdm_pdu_read_byte(pdu, index);
1902
1903 index += ((addr_len % 2) == 0) ? (addr_len / 2) + 2 : (addr_len / 2) + 3;
1904
1905 if (index >= pdu_len) {
1906 return -1;
1907 }
1908
Nazar Kazakov9713f0d2022-02-24 12:00:55 +00001909 /* read protocol identifier */
Lukas Gehreke53dea672021-08-27 10:21:14 +02001910 target_buf->tp_pid = mdm_pdu_read_byte(pdu, index++);
1911
1912 if (index >= pdu_len) {
1913 return -1;
1914 }
1915
1916 /* read coding scheme */
1917 uint8_t tp_dcs = mdm_pdu_read_byte(pdu, index++);
1918
1919 /* parse date and time */
1920 if ((index + 7) >= pdu_len) {
1921 return -1;
1922 }
1923
1924 target_buf->time.year = mdm_pdu_read_time(pdu, index++);
1925 target_buf->time.month = mdm_pdu_read_time(pdu, index++);
1926 target_buf->time.day = mdm_pdu_read_time(pdu, index++);
1927 target_buf->time.hour = mdm_pdu_read_time(pdu, index++);
1928 target_buf->time.minute = mdm_pdu_read_time(pdu, index++);
1929 target_buf->time.second = mdm_pdu_read_time(pdu, index++);
1930 target_buf->time.timezone = mdm_pdu_read_time(pdu, index++);
1931
1932 /* Read user data length */
1933 uint8_t tp_udl = mdm_pdu_read_byte(pdu, index++);
1934
1935 /* Discard header */
1936 uint8_t header_skip = 0;
1937
1938 if (target_buf->first_octet & SMS_TP_UDHI_HEADER) {
1939 uint8_t tp_udhl = mdm_pdu_read_byte(pdu, index);
1940
1941 index += tp_udhl + 1;
1942 header_skip = tp_udhl + 1;
1943
1944 if (index >= pdu_len) {
1945 return -1;
1946 }
1947 }
1948
1949 /* Read data according to type set in TP-DCS */
1950 if (tp_dcs == 0x00) {
1951 /* 7 bit GSM coding */
1952 uint8_t fill_level = 0;
1953 uint16_t buf = 0;
1954
1955 if (target_buf->first_octet & SMS_TP_UDHI_HEADER) {
1956 /* Initial fill because septets are aligned to
1957 * septet boundary after header
1958 */
1959 uint8_t fill_bits = 7 - ((header_skip * 8) % 7);
1960
1961 if (fill_bits == 7) {
1962 fill_bits = 0;
1963 }
1964
1965 buf = mdm_pdu_read_byte(pdu, index++);
1966
1967 fill_level = 8 - fill_bits;
1968 }
1969
1970 uint16_t data_index = 0;
1971
1972 for (unsigned int idx = 0; idx < tp_udl; idx++) {
1973 if (fill_level < 7) {
1974 uint8_t octet = mdm_pdu_read_byte(pdu, index++);
1975
1976 buf &= ((1 << fill_level) - 1);
1977 buf |= (octet << fill_level);
1978 fill_level += 8;
1979 }
1980
1981 /*
1982 * Convert 7-bit encoded data to Unicode and
1983 * then to UTF-8
1984 */
1985 short letter = enc7_basic[buf & 0x007f];
1986
1987 if (letter < 0x0080) {
1988 target_buf->data[data_index++] = letter & 0x007f;
1989 } else if (letter < 0x0800) {
1990 target_buf->data[data_index++] = 0xc0 | ((letter & 0x07c0) >> 6);
1991 target_buf->data[data_index++] = 0x80 | ((letter & 0x003f) >> 0);
1992 }
1993 buf >>= 7;
1994 fill_level -= 7;
1995 }
1996 target_buf->data_len = data_index;
1997 } else if (tp_dcs == 0x04) {
1998 /* 8 bit binary coding */
1999 for (int idx = 0; idx < tp_udl - header_skip; idx++) {
2000 target_buf->data[idx] = mdm_pdu_read_byte(pdu, index++);
2001 }
2002 target_buf->data_len = tp_udl;
2003 } else if (tp_dcs == 0x08) {
2004 /* Unicode (16 bit per character) */
2005 for (int idx = 0; idx < tp_udl - header_skip; idx++) {
2006 target_buf->data[idx] = mdm_pdu_read_byte(pdu, index++);
2007 }
2008 target_buf->data_len = tp_udl;
2009 } else {
2010 return -1;
2011 }
2012
2013 return 0;
2014}
2015
2016/**
2017 * Check if given char sequence is crlf.
2018 *
2019 * @param c The char sequence.
2020 * @param len Total length of the fragment.
2021 * @return @c true if char sequence is crlf.
2022 * Otherwise @c false is returned.
2023 */
2024static bool is_crlf(uint8_t *c, uint8_t len)
2025{
2026 /* crlf does not fit. */
2027 if (len < 2) {
2028 return false;
2029 }
2030
2031 return c[0] == '\r' && c[1] == '\n';
2032}
2033
2034/**
2035 * Find terminating crlf in a netbuffer.
2036 *
2037 * @param buf The netbuffer.
2038 * @param skip Bytes to skip before search.
2039 * @return Length of the returned fragment or 0 if not found.
2040 */
2041static size_t net_buf_find_crlf(struct net_buf *buf, size_t skip)
2042{
2043 size_t len = 0, pos = 0;
2044 struct net_buf *frag = buf;
2045
2046 /* Skip to the start. */
2047 while (frag && skip >= frag->len) {
2048 skip -= frag->len;
2049 frag = frag->frags;
2050 }
2051
2052 /* Need to wait for more data. */
2053 if (!frag) {
2054 return 0;
2055 }
2056
2057 pos = skip;
2058
2059 while (frag && !is_crlf(frag->data + pos, frag->len - pos)) {
2060 if (pos + 1 >= frag->len) {
2061 len += frag->len;
2062 frag = frag->frags;
2063 pos = 0U;
2064 } else {
2065 pos++;
2066 }
2067 }
2068
2069 if (frag && is_crlf(frag->data + pos, frag->len - pos)) {
2070 len += pos;
2071 return len - skip;
2072 }
2073
2074 return 0;
2075}
2076
2077/**
2078 * Parses list sms and add them to buffer.
2079 * Format is:
2080 *
2081 * +CMGL: <index>,<stat>,,<length><CR><LF><pdu><CR><LF>
2082 * +CMGL: <index>,<stat>,,<length><CR><LF><pdu><CR><LF>
2083 * ...
2084 * OK
2085 */
2086MODEM_CMD_DEFINE(on_cmd_cmgl)
2087{
2088 int sms_index, sms_stat, ret;
2089 char pdu_buffer[256];
2090 size_t out_len, sms_len, param_len;
2091 struct sim7080_sms *sms;
2092
2093 sms_index = atoi(argv[0]);
2094 sms_stat = atoi(argv[1]);
2095
2096 /* Get the length of the "length" parameter.
2097 * The last parameter will be stuck in the netbuffer.
2098 * It is not the actual length of the trailing pdu so
2099 * we have to search the next crlf.
2100 */
2101 param_len = net_buf_find_crlf(data->rx_buf, 0);
2102 if (param_len == 0) {
2103 LOG_INF("No <CR><LF>");
2104 return -EAGAIN;
2105 }
2106
2107 /* Get actual trailing pdu len. +2 to skip crlf. */
2108 sms_len = net_buf_find_crlf(data->rx_buf, param_len + 2);
2109 if (sms_len == 0) {
2110 return -EAGAIN;
2111 }
2112
2113 /* Skip to start of pdu. */
2114 data->rx_buf = net_buf_skip(data->rx_buf, param_len + 2);
2115
2116 out_len = net_buf_linearize(pdu_buffer, sizeof(pdu_buffer) - 1, data->rx_buf, 0, sms_len);
2117 pdu_buffer[out_len] = '\0';
2118
2119 data->rx_buf = net_buf_skip(data->rx_buf, sms_len);
2120
2121 /* No buffer specified. */
2122 if (!mdata.sms_buffer) {
2123 return 0;
2124 }
2125
2126 /* No space left in buffer. */
2127 if (mdata.sms_buffer_pos >= mdata.sms_buffer->nsms) {
2128 return 0;
2129 }
2130
2131 sms = &mdata.sms_buffer->sms[mdata.sms_buffer_pos];
2132
2133 ret = mdm_decode_pdu(pdu_buffer, out_len, sms);
2134 if (ret < 0) {
2135 return 0;
2136 }
2137
2138 sms->stat = sms_stat;
2139 sms->index = sms_index;
2140 sms->data[sms->data_len] = '\0';
2141
2142 mdata.sms_buffer_pos++;
2143
2144 return 0;
2145}
2146
2147int mdm_sim7080_read_sms(struct sim7080_sms_buffer *buffer)
2148{
2149 int ret;
2150 struct modem_cmd cmds[] = { MODEM_CMD("+CMGL: ", on_cmd_cmgl, 4U, ",\r") };
2151
2152 mdata.sms_buffer = buffer;
2153 mdata.sms_buffer_pos = 0;
2154
2155 ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler, cmds, ARRAY_SIZE(cmds), "AT+CMGL=4",
2156 &mdata.sem_response, K_SECONDS(20));
2157 if (ret < 0) {
2158 return -1;
2159 }
2160
2161 return mdata.sms_buffer_pos;
2162}
2163
2164int mdm_sim7080_delete_sms(uint16_t index)
2165{
2166 int ret;
2167 char buf[sizeof("AT+CMGD=#####")] = { 0 };
2168
2169 ret = snprintk(buf, sizeof(buf), "AT+CMGD=%u", index);
2170 if (ret < 0) {
2171 return -1;
2172 }
2173
2174 ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler, NULL, 0, buf, &mdata.sem_response,
2175 K_SECONDS(5));
2176 if (ret < 0) {
2177 return -1;
2178 }
2179
2180 return 0;
2181}
2182
2183/*
2184 * Does the modem setup by starting it and
2185 * bringing the modem to a PDP active state.
2186 */
2187static int modem_setup(void)
2188{
2189 int ret = 0;
2190 int counter = 0;
2191
2192 k_work_cancel_delayable(&mdata.rssi_query_work);
2193
2194 ret = modem_autobaud();
2195 if (ret < 0) {
2196 LOG_ERR("Booting modem failed!!");
2197 goto error;
2198 }
2199
2200 ret = modem_cmd_handler_setup_cmds(&mctx.iface, &mctx.cmd_handler, setup_cmds,
2201 ARRAY_SIZE(setup_cmds), &mdata.sem_response,
2202 MDM_REGISTRATION_TIMEOUT);
2203 if (ret < 0) {
2204 LOG_ERR("Failed to send init commands!");
2205 goto error;
2206 }
2207
2208 k_sleep(K_SECONDS(3));
2209
2210 /* Wait for acceptable rssi values. */
2211 modem_rssi_query_work(NULL);
2212 k_sleep(MDM_WAIT_FOR_RSSI_DELAY);
2213
2214 counter = 0;
2215 while (counter++ < MDM_WAIT_FOR_RSSI_COUNT &&
2216 (mdata.mdm_rssi >= 0 || mdata.mdm_rssi <= -1000)) {
2217 modem_rssi_query_work(NULL);
2218 k_sleep(MDM_WAIT_FOR_RSSI_DELAY);
2219 }
2220
2221 if (mdata.mdm_rssi >= 0 || mdata.mdm_rssi <= -1000) {
2222 LOG_ERR("Network not reachable!!");
2223 ret = -ENETUNREACH;
2224 goto error;
2225 }
2226
2227 ret = modem_pdp_activate();
2228 if (ret < 0) {
2229 goto error;
2230 }
2231
2232 k_work_reschedule_for_queue(&modem_workq, &mdata.rssi_query_work,
2233 K_SECONDS(RSSI_TIMEOUT_SECS));
2234
2235 change_state(SIM7080_STATE_NETWORKING);
2236
2237error:
2238 return ret;
2239}
2240
2241int mdm_sim7080_start_network(void)
2242{
2243 change_state(SIM7080_STATE_INIT);
2244 return modem_setup();
2245}
2246
2247int mdm_sim7080_power_on(void)
2248{
2249 return modem_autobaud();
2250}
2251
2252int mdm_sim7080_power_off(void)
2253{
2254 int tries = 5;
2255 int autobaud_tries;
2256 int ret = 0;
2257
2258 k_work_cancel_delayable(&mdata.rssi_query_work);
2259
2260 /* Check if module is already off. */
2261 ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler, NULL, 0U, "AT", &mdata.sem_response,
2262 K_MSEC(1000));
2263 if (ret < 0) {
2264 change_state(SIM7080_STATE_OFF);
2265 return 0;
2266 }
2267
2268 while (tries--) {
2269 modem_pwrkey();
2270
2271 autobaud_tries = 5;
2272
2273 while (autobaud_tries--) {
2274 ret = modem_cmd_send(&mctx.iface, &mctx.cmd_handler, NULL, 0U, "AT",
2275 &mdata.sem_response, K_MSEC(500));
2276 if (ret == 0) {
2277 break;
2278 }
2279 }
2280
2281 if (ret < 0) {
2282 change_state(SIM7080_STATE_OFF);
2283 return 0;
2284 }
2285 }
2286
2287 return -1;
2288}
2289
2290const char *mdm_sim7080_get_manufacturer(void)
2291{
2292 return mdata.mdm_manufacturer;
2293}
2294
2295const char *mdm_sim7080_get_model(void)
2296{
2297 return mdata.mdm_model;
2298}
2299
2300const char *mdm_sim7080_get_revision(void)
2301{
2302 return mdata.mdm_revision;
2303}
2304
2305const char *mdm_sim7080_get_imei(void)
2306{
2307 return mdata.mdm_imei;
2308}
2309
2310/*
2311 * Initializes modem handlers and context.
2312 * After successful init this function calls
2313 * modem_setup.
2314 */
2315static int modem_init(const struct device *dev)
2316{
2317 int ret;
2318
2319 ARG_UNUSED(dev);
2320
2321 k_sem_init(&mdata.sem_response, 0, 1);
2322 k_sem_init(&mdata.sem_tx_ready, 0, 1);
2323 k_sem_init(&mdata.sem_dns, 0, 1);
2324 k_sem_init(&mdata.sem_ftp, 0, 1);
2325 k_work_queue_start(&modem_workq, modem_workq_stack,
2326 K_KERNEL_STACK_SIZEOF(modem_workq_stack), K_PRIO_COOP(7), NULL);
2327
2328 /* Assume the modem is not registered to the network. */
2329 mdata.mdm_registration = 0;
2330 mdata.cpin_ready = false;
2331 mdata.pdp_active = false;
2332
2333 mdata.sms_buffer = NULL;
2334 mdata.sms_buffer_pos = 0;
2335
2336 /* Socket config. */
2337 mdata.socket_config.sockets = &mdata.sockets[0];
2338 mdata.socket_config.sockets_len = ARRAY_SIZE(mdata.sockets);
2339 mdata.socket_config.base_socket_num = MDM_BASE_SOCKET_NUM;
2340 ret = modem_socket_init(&mdata.socket_config, &offload_socket_fd_op_vtable);
2341 if (ret < 0) {
2342 goto error;
2343 }
2344
2345 change_state(SIM7080_STATE_INIT);
2346
2347 /* Command handler. */
2348 mdata.cmd_handler_data.cmds[CMD_RESP] = response_cmds;
2349 mdata.cmd_handler_data.cmds_len[CMD_RESP] = ARRAY_SIZE(response_cmds);
2350 mdata.cmd_handler_data.cmds[CMD_UNSOL] = unsolicited_cmds;
2351 mdata.cmd_handler_data.cmds_len[CMD_UNSOL] = ARRAY_SIZE(unsolicited_cmds);
2352 mdata.cmd_handler_data.match_buf = &mdata.cmd_match_buf[0];
2353 mdata.cmd_handler_data.match_buf_len = sizeof(mdata.cmd_match_buf);
2354 mdata.cmd_handler_data.buf_pool = &mdm_recv_pool;
2355 mdata.cmd_handler_data.alloc_timeout = BUF_ALLOC_TIMEOUT;
2356 mdata.cmd_handler_data.eol = "\r\n";
2357 ret = modem_cmd_handler_init(&mctx.cmd_handler, &mdata.cmd_handler_data);
2358 if (ret < 0) {
2359 goto error;
2360 }
2361
2362 /* Uart handler. */
2363 mdata.iface_data.rx_rb_buf = &mdata.iface_rb_buf[0];
2364 mdata.iface_data.rx_rb_buf_len = sizeof(mdata.iface_rb_buf);
2365 ret = modem_iface_uart_init(&mctx.iface, &mdata.iface_data, MDM_UART_DEV);
2366 if (ret < 0) {
2367 goto error;
2368 }
2369
2370 mdata.current_sock_fd = -1;
2371 mdata.current_sock_written = 0;
2372
2373 mdata.ftp.read_buffer = NULL;
2374 mdata.ftp.nread = 0;
2375 mdata.ftp.state = SIM7080_FTP_CONNECTION_STATE_INITIAL;
2376
2377 /* Modem data storage. */
2378 mctx.data_manufacturer = mdata.mdm_manufacturer;
2379 mctx.data_model = mdata.mdm_model;
2380 mctx.data_revision = mdata.mdm_revision;
2381 mctx.data_imei = mdata.mdm_imei;
2382#if defined(CONFIG_MODEM_SIM_NUMBERS)
2383 mctx.data_imsi = mdata.mdm_imsi;
2384 mctx.data_iccid = mdata.mdm_iccid;
2385#endif /* #if defined(CONFIG_MODEM_SIM_NUMBERS) */
2386 mctx.data_rssi = &mdata.mdm_rssi;
2387
Marcin Niestroj41c618d2022-05-13 19:20:16 +02002388 ret = gpio_pin_configure_dt(&power_gpio, GPIO_OUTPUT_LOW);
2389 if (ret < 0) {
2390 LOG_ERR("Failed to configure %s pin", "power");
2391 goto error;
2392 }
2393
Lukas Gehreke53dea672021-08-27 10:21:14 +02002394 mctx.driver_data = &mdata;
2395
2396 memset(&gnss_data, 0, sizeof(gnss_data));
2397
2398 ret = modem_context_register(&mctx);
2399 if (ret < 0) {
2400 LOG_ERR("Error registering modem context: %d", ret);
2401 goto error;
2402 }
2403
2404 k_thread_create(&modem_rx_thread, modem_rx_stack, K_KERNEL_STACK_SIZEOF(modem_rx_stack),
2405 (k_thread_entry_t)modem_rx, NULL, NULL, NULL, K_PRIO_COOP(7), 0, K_NO_WAIT);
2406
2407 /* Init RSSI query */
2408 k_work_init_delayable(&mdata.rssi_query_work, modem_rssi_query_work);
2409
2410 return modem_setup();
2411error:
2412 return ret;
2413}
2414
2415/* Register device with the networking stack. */
2416NET_DEVICE_DT_INST_OFFLOAD_DEFINE(0, modem_init, NULL, &mdata, NULL,
2417 CONFIG_MODEM_SIMCOM_SIM7080_INIT_PRIORITY, &api_funcs,
2418 MDM_MAX_DATA_LENGTH);
2419
Robert Lubos79469882022-03-30 14:55:35 +02002420NET_SOCKET_OFFLOAD_REGISTER(simcom_sim7080, CONFIG_NET_SOCKETS_OFFLOAD_PRIORITY,
2421 AF_UNSPEC, offload_is_supported, offload_socket);