1 /*
2 * Copyright (c) 2020 Laird Connectivity
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT swir_hl7800
8
9 #include <zephyr/logging/log.h>
10 #include <zephyr/logging/log_ctrl.h>
11 #define LOG_MODULE_NAME modem_hl7800
12 LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_MODEM_LOG_LEVEL);
13
14 #include <zephyr/types.h>
15 #include <stddef.h>
16 #include <stdlib.h>
17 #include <ctype.h>
18 #include <errno.h>
19 #include <zephyr/kernel.h>
20 #include <zephyr/drivers/gpio.h>
21 #include <zephyr/device.h>
22 #include <zephyr/init.h>
23
24 #include <zephyr/pm/device.h>
25 #include <zephyr/drivers/uart.h>
26 #include <zephyr/sys/util.h>
27
28 #include <zephyr/net/net_context.h>
29 #include <zephyr/net/net_if.h>
30 #include <zephyr/net/net_offload.h>
31 #include <zephyr/net/net_pkt.h>
32 #include <zephyr/net/dns_resolve.h>
33 #include <zephyr/net/offloaded_netdev.h>
34 #if defined(CONFIG_NET_IPV6)
35 #include "ipv6.h"
36 #endif
37 #if defined(CONFIG_NET_IPV4)
38 #include "ipv4.h"
39 #endif
40 #if defined(CONFIG_NET_UDP)
41 #include "udp_internal.h"
42 #endif
43
44 #ifdef CONFIG_MODEM_HL7800_FW_UPDATE
45 #include <zephyr/fs/fs.h>
46 #endif
47
48 #include "modem_receiver.h"
49 #include <zephyr/drivers/modem/hl7800.h>
50
51 #define PREFIXED_SWITCH_CASE_RETURN_STRING(prefix, val) \
52 case prefix##_##val: { \
53 return #val; \
54 }
55
56 /* Uncomment the #define below to enable a hexdump of all incoming
57 * data from the modem receiver
58 */
59 /* #define HL7800_ENABLE_VERBOSE_MODEM_RECV_HEXDUMP 1 */
60
61 #define HL7800_LOG_UNHANDLED_RX_MSGS 1
62
63 /* Uncomment the #define(s) below to enable extra debugging */
64 /* #define HL7800_RX_LOCK_LOG 1 */
65 /* #define HL7800_TX_LOCK_LOG 1 */
66 /* #define HL7800_IO_LOG 1 */
67
68 #define HL7800_RX_LOCK_DBG_LOG(fmt, ...) \
69 do { \
70 if (IS_ENABLED(HL7800_RX_LOCK_LOG)) { \
71 LOG_DBG(fmt, ##__VA_ARGS__); \
72 } \
73 } while (false)
74
75 #define HL7800_TX_LOCK_DBG_LOG(fmt, ...) \
76 do { \
77 if (IS_ENABLED(HL7800_TX_LOCK_LOG)) { \
78 LOG_DBG(fmt, ##__VA_ARGS__); \
79 } \
80 } while (false)
81
82 #define HL7800_IO_DBG_LOG(fmt, ...) \
83 do { \
84 if (IS_ENABLED(HL7800_IO_LOG)) { \
85 LOG_WRN(fmt, ##__VA_ARGS__); \
86 } \
87 } while (false)
88
89 #if ((LOG_LEVEL == LOG_LEVEL_DBG) && \
90 defined(CONFIG_MODEM_HL7800_LOW_POWER_MODE))
91 #define PRINT_AWAKE_MSG LOG_WRN("awake")
92 #define PRINT_NOT_AWAKE_MSG LOG_WRN("NOT awake")
93 #else
94 #define PRINT_AWAKE_MSG
95 #define PRINT_NOT_AWAKE_MSG
96 #endif
97
98 enum tcp_notif {
99 HL7800_TCP_NET_ERR,
100 HL7800_TCP_NO_SOCKS,
101 HL7800_TCP_MEM,
102 HL7800_TCP_DNS,
103 HL7800_TCP_DISCON,
104 HL7800_TCP_CONN,
105 HL7800_TCP_ERR,
106 HL7800_TCP_CLIENT_REQ,
107 HL7800_TCP_DATA_SND,
108 HL7800_TCP_ID,
109 HL7800_TCP_RUNNING,
110 HL7800_TCP_ALL_USED,
111 HL7800_TCP_TIMEOUT,
112 HL7800_TCP_SSL_CONN,
113 HL7800_TCP_SSL_INIT
114 };
115
116 enum udp_notif {
117 HL7800_UDP_NET_ERR = 0,
118 HL7800_UDP_NO_SOCKS = 1,
119 HL7800_UDP_MEM = 2,
120 HL7800_UDP_DNS = 3,
121 HL7800_UDP_CONN = 5,
122 HL7800_UDP_ERR = 6,
123 HL7800_UDP_DATA_SND = 8, /* this matches TCP_DATA_SND */
124 HL7800_UDP_ID = 9,
125 HL7800_UDP_RUNNING = 10,
126 HL7800_UDP_ALL_USED = 11
127 };
128
129 enum socket_state {
130 SOCK_IDLE,
131 SOCK_RX,
132 SOCK_TX,
133 SOCK_CONNECTED,
134 };
135
136 enum hl7800_lpm {
137 HL7800_LPM_NONE,
138 HL7800_LPM_EDRX,
139 HL7800_LPM_PSM,
140 };
141
142 /* pin settings */
143 enum mdm_control_pins {
144 MDM_RESET = 0,
145 MDM_WAKE,
146 MDM_PWR_ON,
147 MDM_FAST_SHUTD,
148 MDM_VGPIO,
149 MDM_UART_DSR,
150 MDM_UART_CTS,
151 MDM_GPIO6,
152 MAX_MDM_CONTROL_PINS,
153 };
154
155 enum net_operator_status { NO_OPERATOR, REGISTERED };
156
157 enum device_service_indications {
158 WDSI_PKG_DOWNLOADED = 3,
159 };
160
161 #ifdef CONFIG_MODEM_HL7800_FW_UPDATE
162 enum XMODEM_CONTROL_CHARACTERS {
163 XM_SOH = 0x01,
164 XM_SOH_1K = 0x02,
165 XM_EOT = 0x04,
166 XM_ACK = 0x06, /* 'R' */
167 XM_NACK = 0x15, /* 'N' */
168 XM_ETB = 0x17,
169 XM_CAN = 0x18,
170 XM_C = 0x43
171 };
172
173 #define XMODEM_DATA_SIZE 1024
174 #define XMODEM_PACKET_SIZE (XMODEM_DATA_SIZE + 4)
175 #define XMODEM_PAD_VALUE 26
176
177 struct xmodem_packet {
178 uint8_t preamble;
179 uint8_t id;
180 uint8_t id_complement;
181 uint8_t data[XMODEM_DATA_SIZE];
182 uint8_t crc;
183 };
184 #endif
185
186 #define MDM_UART_DEV DEVICE_DT_GET(DT_INST_BUS(0))
187
188 #define MDM_SEND_OK_ENABLED 0
189 #define MDM_SEND_OK_DISABLED 1
190
191 #define MDM_CMD_SEND_TIMEOUT K_SECONDS(6)
192 #define MDM_IP_SEND_RX_TIMEOUT K_SECONDS(62)
193 #define MDM_SOCK_NOTIF_DELAY K_MSEC(150)
194 #define MDM_CMD_CONN_TIMEOUT K_SECONDS(31)
195
196 #define MDM_MAX_DATA_LENGTH 1500
197 #define MDM_MTU 1500
198 #define MDM_MAX_RESP_SIZE 128
199 #define MDM_IP_INFO_RESP_SIZE 256
200 #define MDM_EID_LENGTH 33
201 #define MDM_CCID_RESP_MAX_SIZE (MDM_HL7800_ICCID_MAX_SIZE + MDM_EID_LENGTH)
202
203 #define MDM_HANDLER_MATCH_MAX_LEN 100
204
205 #define MDM_MAX_SOCKETS 6
206
207 /* Special value used to indicate that a socket is being created
208 * and that its actual ID hasn't been assigned yet.
209 */
210 #define MDM_CREATE_SOCKET_ID (MDM_MAX_SOCKETS + 1)
211 #define MDM_INVALID_SOCKET_ID -1
212
213 #define BUF_ALLOC_TIMEOUT K_SECONDS(1)
214
215 #define SIZE_OF_NUL 1
216
217 #define SIZE_WITHOUT_NUL(v) (sizeof(v) - SIZE_OF_NUL)
218
219 #define CMD_HANDLER(cmd_, cb_) \
220 { \
221 .cmd = cmd_, .cmd_len = (uint16_t)sizeof(cmd_) - 1, \
222 .func = on_cmd_##cb_ \
223 }
224
225 #define MDM_MANUFACTURER_LENGTH 16
226 #define MDM_MODEL_LENGTH 7
227 #define MDM_SN_RESPONSE_LENGTH (MDM_HL7800_SERIAL_NUMBER_SIZE + 7)
228 #define MDM_NETWORK_STATUS_LENGTH 45
229
230 #define MDM_TOP_BAND_SIZE 4
231 #define MDM_MIDDLE_BAND_SIZE 8
232 #define MDM_BOTTOM_BAND_SIZE 8
233 #define MDM_TOP_BAND_START_POSITION 2
234 #define MDM_MIDDLE_BAND_START_POSITION 6
235 #define MDM_BOTTOM_BAND_START_POSITION 14
236 #define MDM_BAND_BITMAP_STR_LENGTH_MAX \
237 (MDM_TOP_BAND_SIZE + MDM_MIDDLE_BAND_SIZE + MDM_BOTTOM_BAND_SIZE)
238 #define MDM_BAND_BITMAP_STR_LENGTH_MIN 1
239
240 #define MDM_DEFAULT_AT_CMD_RETRIES 3
241 #define MDM_WAKEUP_TIME K_SECONDS(12)
242 #define MDM_BOOT_TIME K_SECONDS(12)
243 #define MDM_WAKE_TO_CHECK_CTS_DELAY_MS K_MSEC(20)
244
245 #define MDM_WAIT_FOR_DATA_TIME K_MSEC(50)
246 #define MDM_RESET_LOW_TIME K_MSEC(50)
247 #define MDM_RESET_HIGH_TIME K_MSEC(10)
248 #define MDM_WAIT_FOR_DATA_RETRIES 3
249
250 #define RSSI_UNKNOWN -999
251
252 #define DNS_WORK_DELAY_SECS 1
253 #define IFACE_WORK_DELAY K_MSEC(500)
254 #define SOCKET_CLEANUP_WORK_DELAY K_MSEC(100)
255 #define WAIT_FOR_KSUP_RETRIES 5
256
257 #define CGCONTRDP_RESPONSE_NUM_DELIMS 7
258 #define COPS_RESPONSE_NUM_DELIMS 2
259 #define KCELLMEAS_RESPONSE_NUM_DELIMS 4
260
261 #define PROFILE_LINE_1 \
262 "E1 Q0 V1 X4 &C1 &D1 &R1 &S0 +IFC=2,2 &K3 +IPR=115200 +FCLASS0\r\n"
263 #define PROFILE_LINE_2 \
264 "S00:255 S01:255 S03:255 S04:255 S05:255 S07:255 S08:255 S10:255\r\n"
265
266 #define ADDRESS_FAMILY_IP "IP"
267 #define ADDRESS_FAMILY_IPV4 "IPV4"
268 #if defined(CONFIG_MODEM_HL7800_ADDRESS_FAMILY_IPV4V6)
269 #define MODEM_HL7800_ADDRESS_FAMILY "IPV4V6"
270 #elif defined(CONFIG_MODEM_HL7800_ADDRESS_FAMILY_IPV4)
271 #define MODEM_HL7800_ADDRESS_FAMILY ADDRESS_FAMILY_IPV4
272 #else
273 #define MODEM_HL7800_ADDRESS_FAMILY "IPV6"
274 #endif
275 #define MDM_HL7800_SOCKET_AF_IPV4 0
276 #define MDM_HL7800_SOCKET_AF_IPV6 1
277
278 #define SET_RAT_M1_CMD_LEGACY "AT+KSRAT=0"
279 #define SET_RAT_NB1_CMD_LEGACY "AT+KSRAT=1"
280 #define SET_RAT_M1_CMD "AT+KSRAT=0,1"
281 #define SET_RAT_NB1_CMD "AT+KSRAT=1,1"
282 #define NEW_RAT_CMD_MIN_VERSION "HL7800.4.5.4.0"
283 #define HL7800_VERSION_FORMAT "HL7800.%zu.%zu.%zu.%zu"
284
285 #define MAX_PROFILE_LINE_LENGTH \
286 MAX(sizeof(PROFILE_LINE_1), sizeof(PROFILE_LINE_2))
287
288 #define IPV6_ADDR_FORMAT "####:####:####:####:####:####:####:####"
289 #define HL7800_IPV6_ADDR_LEN \
290 sizeof("a01.a02.a03.a04.a05.a06.a07.a08.a09.a10.a11.a12.a13.a14.a15.a16")
291
292 #define MDM_ADDR_FAM_MAX_LEN sizeof("IPV4V6")
293
294 /* The ? can be a + or - */
295 static const char TIME_STRING_FORMAT[] = "\"yy/MM/dd,hh:mm:ss?zz\"";
296 #define TIME_STRING_DIGIT_STRLEN 2
297 #define TIME_STRING_SEPARATOR_STRLEN 1
298 #define TIME_STRING_PLUS_MINUS_INDEX (6 * 3)
299 #define TIME_STRING_FIRST_SEPARATOR_INDEX 0
300 #define TIME_STRING_FIRST_DIGIT_INDEX 1
301 #define TIME_STRING_TO_TM_STRUCT_YEAR_OFFSET (2000 - 1900)
302
303 /* Time structure min, max */
304 #define TM_YEAR_RANGE 0, 99
305 #define TM_MONTH_RANGE_PLUS_1 1, 12
306 #define TM_DAY_RANGE 1, 31
307 #define TM_HOUR_RANGE 0, 23
308 #define TM_MIN_RANGE 0, 59
309 #define TM_SEC_RANGE 0, 60 /* leap second */
310 #define QUARTER_HOUR_RANGE 0, 96
311 #define SECONDS_PER_QUARTER_HOUR (15 * 60)
312
313 #define SEND_AT_CMD_ONCE_EXPECT_OK(c) \
314 do { \
315 ret = send_at_cmd(NULL, (c), MDM_CMD_SEND_TIMEOUT, 0, false); \
316 if (ret < 0) { \
317 LOG_ERR("%s result:%d", (c), ret); \
318 goto error; \
319 } \
320 } while (false)
321
322 #define SEND_AT_CMD_IGNORE_ERROR(c) \
323 do { \
324 ret = send_at_cmd(NULL, (c), MDM_CMD_SEND_TIMEOUT, 0, false); \
325 if (ret < 0) { \
326 LOG_ERR("%s result:%d", (c), ret); \
327 } \
328 } while (false)
329
330 #define SEND_AT_CMD_EXPECT_OK(c) \
331 do { \
332 ret = send_at_cmd(NULL, (c), MDM_CMD_SEND_TIMEOUT, \
333 MDM_DEFAULT_AT_CMD_RETRIES, false); \
334 if (ret < 0) { \
335 LOG_ERR("%s result:%d", (c), ret); \
336 goto error; \
337 } \
338 } while (false)
339
340 /* Complex has "no_id_resp" set to true because the sending command
341 * is the command used to process the response
342 */
343 #define SEND_COMPLEX_AT_CMD(c) \
344 do { \
345 ret = send_at_cmd(NULL, (c), MDM_CMD_SEND_TIMEOUT, \
346 MDM_DEFAULT_AT_CMD_RETRIES, true); \
347 if (ret < 0) { \
348 LOG_ERR("%s result:%d", (c), ret); \
349 goto error; \
350 } \
351 } while (false)
352
353 NET_BUF_POOL_DEFINE(mdm_recv_pool, CONFIG_MODEM_HL7800_RECV_BUF_CNT,
354 CONFIG_MODEM_HL7800_RECV_BUF_SIZE, 0, NULL);
355
356 static uint8_t mdm_recv_buf[MDM_MAX_DATA_LENGTH];
357
358 static K_SEM_DEFINE(hl7800_RX_lock_sem, 1, 1);
359 static K_SEM_DEFINE(hl7800_TX_lock_sem, 1, 1);
360 static K_SEM_DEFINE(cb_lock, 1, 1);
361
362 /* RX thread structures */
363 K_THREAD_STACK_DEFINE(hl7800_rx_stack, CONFIG_MODEM_HL7800_RX_STACK_SIZE);
364 struct k_thread hl7800_rx_thread;
365 #define RX_THREAD_PRIORITY K_PRIO_COOP(7)
366
367 /* RX thread work queue */
368 K_THREAD_STACK_DEFINE(hl7800_workq_stack,
369 CONFIG_MODEM_HL7800_RX_WORKQ_STACK_SIZE);
370 static struct k_work_q hl7800_workq;
371 #define WORKQ_PRIORITY K_PRIO_COOP(7)
372
373 static const char EOF_PATTERN[] = "--EOF--Pattern--";
374 static const char CONNECT_STRING[] = "CONNECT";
375 static const char OK_STRING[] = "OK";
376
377 struct hl7800_socket {
378 struct net_context *context;
379 sa_family_t family;
380 enum net_sock_type type;
381 enum net_ip_protocol ip_proto;
382 struct sockaddr src;
383 struct sockaddr dst;
384
385 bool created;
386 bool reconfig;
387 int socket_id;
388 int rx_size;
389 int error;
390 enum socket_state state;
391
392 /** semaphore */
393 struct k_sem sock_send_sem;
394
395 /** socket callbacks */
396 struct k_work recv_cb_work;
397 struct k_work rx_data_work;
398 struct k_work_delayable notif_work;
399 net_context_recv_cb_t recv_cb;
400 struct net_pkt *recv_pkt;
401 void *recv_user_data;
402 };
403
404 struct stale_socket {
405 int reserved; /* first word of queue data item reserved for the kernel */
406 enum net_sock_type type;
407 uint8_t id;
408 bool allocated;
409 };
410
411 #define NO_ID_RESP_CMD_MAX_LENGTH 32
412
413 struct hl7800_config {
414 struct gpio_dt_spec gpio[MAX_MDM_CONTROL_PINS];
415 };
416
417 struct hl7800_iface_ctx {
418 struct net_if *iface;
419 uint8_t mac_addr[6];
420 struct in_addr ipv4Addr, subnet, gateway, dns_v4;
421 #ifdef CONFIG_NET_IPV6
422 struct in6_addr ipv6Addr, dns_v6;
423 char dns_v6_string[HL7800_IPV6_ADDR_LEN];
424 #endif
425 bool restarting;
426 bool initialized;
427 bool wait_for_KSUP;
428 uint32_t wait_for_KSUP_tries;
429 bool reconfig_IP_connection;
430 char dns_v4_string[NET_IPV4_ADDR_LEN];
431 char no_id_resp_cmd[NO_ID_RESP_CMD_MAX_LENGTH];
432 bool search_no_id_resp;
433
434 /* GPIO PORT devices */
435 struct gpio_callback mdm_vgpio_cb;
436 struct gpio_callback mdm_uart_dsr_cb;
437 struct gpio_callback mdm_gpio6_cb;
438 struct gpio_callback mdm_uart_cts_cb;
439 int vgpio_state;
440 int dsr_state;
441 int gpio6_state;
442 int cts_state;
443 int last_cts_state;
444 int last_cts_time;
445
446 /* RX specific attributes */
447 struct mdm_receiver_context mdm_ctx;
448
449 /* socket data */
450 struct hl7800_socket sockets[MDM_MAX_SOCKETS];
451 int last_socket_id;
452 int last_error;
453 struct stale_socket stale_sockets[MDM_MAX_SOCKETS];
454 struct k_queue stale_socket_queue;
455
456 /* semaphores */
457 struct k_sem response_sem;
458 struct k_sem mdm_awake;
459
460 /* work */
461 struct k_work_delayable rssi_query_work;
462 struct k_work_delayable iface_status_work;
463 struct k_work_delayable dns_work;
464 struct k_work mdm_vgpio_work;
465 struct k_work_delayable mdm_reset_work;
466 struct k_work_delayable allow_sleep_work;
467 struct k_work_delayable delete_untracked_socket_work;
468 struct k_work mdm_pwr_off_work;
469
470 #ifdef CONFIG_MODEM_HL7800_FW_UPDATE
471 /* firmware update */
472 enum mdm_hl7800_fota_state fw_update_state;
473 struct fs_file_t fw_update_file;
474 struct xmodem_packet fw_packet;
475 uint32_t fw_packet_count;
476 int file_pos;
477 struct k_work finish_fw_update_work;
478 bool fw_updated;
479 #endif
480
481 /* modem info */
482 /* NOTE: make sure length is +1 for null char */
483 char mdm_manufacturer[MDM_MANUFACTURER_LENGTH];
484 char mdm_model[MDM_MODEL_LENGTH];
485 char mdm_revision[MDM_HL7800_REVISION_MAX_SIZE];
486 char mdm_imei[MDM_HL7800_IMEI_SIZE];
487 char mdm_sn[MDM_HL7800_SERIAL_NUMBER_SIZE];
488 char mdm_network_status[MDM_NETWORK_STATUS_LENGTH];
489 char mdm_iccid[MDM_HL7800_ICCID_MAX_SIZE];
490 enum mdm_hl7800_startup_state mdm_startup_state;
491 enum mdm_hl7800_radio_mode mdm_rat;
492 char mdm_active_bands_string[MDM_HL7800_LTE_BAND_STR_SIZE];
493 char mdm_bands_string[MDM_HL7800_LTE_BAND_STR_SIZE];
494 char mdm_imsi[MDM_HL7800_IMSI_MAX_STR_SIZE];
495 int mdm_rssi;
496 uint16_t mdm_bands_top;
497 uint32_t mdm_bands_middle;
498 uint32_t mdm_bands_bottom;
499 int32_t mdm_sinr;
500 bool mdm_echo_is_on;
501 struct mdm_hl7800_apn mdm_apn;
502 bool mdm_startup_reporting_on;
503 int device_services_ind;
504 bool new_rat_cmd_support;
505 uint8_t operator_index;
506 enum mdm_hl7800_functionality functionality;
507 char mdm_pdp_addr_fam[MDM_ADDR_FAM_MAX_LEN];
508
509 /* modem state */
510 bool busy;
511 bool socket_cmd;
512 bool allow_sleep;
513 enum mdm_hl7800_sleep desired_sleep_level;
514 enum mdm_hl7800_sleep sleep_state;
515 enum hl7800_lpm low_power_mode;
516 enum mdm_hl7800_network_state network_state;
517 bool network_dropped;
518 bool dns_ready;
519 enum net_operator_status operator_status;
520 struct tm local_time;
521 int32_t local_time_offset;
522 bool local_time_valid;
523 bool configured;
524 bool off;
525 void (*wake_up_callback)(int state);
526 void (*gpio6_callback)(int state);
527 void (*cts_callback)(int state);
528
529 #ifdef CONFIG_MODEM_HL7800_GPS
530 struct k_work_delayable gps_work;
531 uint32_t gps_query_location_rate_seconds;
532 #endif
533 };
534
535 struct cmd_handler {
536 const char *cmd;
537 uint16_t cmd_len;
538 bool (*func)(struct net_buf **buf, uint16_t len);
539 };
540
541 static sys_slist_t hl7800_event_callback_list =
542 SYS_SLIST_STATIC_INIT(&hl7800_event_callback_list);
543
544 const static struct hl7800_config hl7800_cfg = {
545 .gpio = {
546 GPIO_DT_SPEC_INST_GET(0, mdm_reset_gpios),
547 GPIO_DT_SPEC_INST_GET(0, mdm_wake_gpios),
548 GPIO_DT_SPEC_INST_GET(0, mdm_pwr_on_gpios),
549 GPIO_DT_SPEC_INST_GET(0, mdm_fast_shutd_gpios),
550 GPIO_DT_SPEC_INST_GET(0, mdm_vgpio_gpios),
551 GPIO_DT_SPEC_INST_GET(0, mdm_uart_dsr_gpios),
552 GPIO_DT_SPEC_INST_GET(0, mdm_uart_cts_gpios),
553 GPIO_DT_SPEC_INST_GET(0, mdm_gpio6_gpios),
554 },
555 };
556 static struct hl7800_iface_ctx iface_ctx;
557
558 static size_t hl7800_read_rx(struct net_buf **buf);
559 static char *get_network_state_string(enum mdm_hl7800_network_state state);
560 static char *get_startup_state_string(enum mdm_hl7800_startup_state state);
561 static char *get_sleep_state_string(enum mdm_hl7800_sleep state);
562 static void set_network_state(enum mdm_hl7800_network_state state);
563 static void set_startup_state(enum mdm_hl7800_startup_state state);
564 static void set_sleep_state(enum mdm_hl7800_sleep state);
565 static void generate_network_state_event(void);
566 static void generate_startup_state_event(void);
567 static void generate_sleep_state_event(void);
568 static int modem_boot_handler(char *reason);
569 static void mdm_vgpio_work_cb(struct k_work *item);
570 static void mdm_reset_work_callback(struct k_work *item);
571 static void mdm_power_off_work_callback(struct k_work *item);
572 static int write_apn(char *access_point_name);
573 #ifdef CONFIG_MODEM_HL7800_LOW_POWER_MODE
574 static void mark_sockets_for_reconfig(void);
575 #endif
576 static void hl7800_build_mac(struct hl7800_iface_ctx *ictx);
577 static void rssi_query(void);
578
579 #ifdef CONFIG_MODEM_HL7800_LOW_POWER_MODE
580 static void initialize_sleep_level(void);
581 static int set_sleep_level(void);
582 #endif
583
584 #ifdef CONFIG_MODEM_HL7800_FW_UPDATE
585 static char *get_fota_state_string(enum mdm_hl7800_fota_state state);
586 static void set_fota_state(enum mdm_hl7800_fota_state state);
587 static void generate_fota_state_event(void);
588 static void generate_fota_count_event(void);
589 #endif
590
alloc_stale_socket(void)591 static struct stale_socket *alloc_stale_socket(void)
592 {
593 struct stale_socket *sock = NULL;
594
595 for (int i = 0; i < MDM_MAX_SOCKETS; i++) {
596 if (!iface_ctx.stale_sockets[i].allocated) {
597 sock = &iface_ctx.stale_sockets[i];
598 sock->allocated = true;
599 break;
600 }
601 }
602
603 return sock;
604 }
605
free_stale_socket(struct stale_socket * sock)606 static void free_stale_socket(struct stale_socket *sock)
607 {
608 if (sock != NULL) {
609 sock->allocated = false;
610 }
611 }
612
queue_stale_socket(enum net_sock_type type,uint8_t id)613 static int queue_stale_socket(enum net_sock_type type, uint8_t id)
614 {
615 int ret = 0;
616 struct stale_socket *sock = NULL;
617
618 sock = alloc_stale_socket();
619 if (sock != NULL) {
620 LOG_DBG("Queueing stale socket %d", id);
621 sock->type = type;
622 sock->id = id;
623 k_queue_append(&iface_ctx.stale_socket_queue, (void *)sock);
624 } else {
625 LOG_ERR("Could not alloc stale socket");
626 ret = -ENOMEM;
627 }
628
629 return ret;
630 }
631
dequeue_stale_socket(void)632 static struct stale_socket *dequeue_stale_socket(void)
633 {
634 struct stale_socket *sock = NULL;
635
636 sock = (struct stale_socket *)k_queue_get(&iface_ctx.stale_socket_queue, K_NO_WAIT);
637
638 return sock;
639 }
640
641 static bool convert_time_string_to_struct(struct tm *tm, int32_t *offset,
642 char *time_string);
643 static int modem_reset_and_configure(void);
644
read_pin(int default_state,const struct gpio_dt_spec * spec)645 static int read_pin(int default_state, const struct gpio_dt_spec *spec)
646 {
647 int state = gpio_pin_get_raw(spec->port, spec->pin);
648
649 if (state < 0) {
650 LOG_ERR("Unable to read port: %s pin: %d status: %d",
651 spec->port->name, spec->pin, state);
652 state = default_state;
653 }
654
655 return state;
656 }
657
658 #ifdef CONFIG_MODEM_HL7800_LOW_POWER_MODE
is_cmd_ready(void)659 static bool is_cmd_ready(void)
660 {
661 iface_ctx.vgpio_state = read_pin(0, &hl7800_cfg.gpio[MDM_VGPIO]);
662
663 iface_ctx.gpio6_state = read_pin(0, &hl7800_cfg.gpio[MDM_GPIO6]);
664
665 iface_ctx.cts_state = read_pin(1, &hl7800_cfg.gpio[MDM_UART_CTS]);
666
667 return iface_ctx.vgpio_state && iface_ctx.gpio6_state && !iface_ctx.cts_state;
668 }
669 #endif
670
671 /**
672 * The definition of awake is that the HL7800
673 * is ready to receive AT commands successfully
674 */
check_hl7800_awake(void)675 static void check_hl7800_awake(void)
676 {
677 #ifdef CONFIG_MODEM_HL7800_LOW_POWER_MODE
678 bool is_cmd_rdy = is_cmd_ready();
679
680 if (is_cmd_rdy && (iface_ctx.sleep_state != HL7800_SLEEP_AWAKE) &&
681 !iface_ctx.allow_sleep && !iface_ctx.wait_for_KSUP) {
682 PRINT_AWAKE_MSG;
683 set_sleep_state(HL7800_SLEEP_AWAKE);
684 k_sem_give(&iface_ctx.mdm_awake);
685 } else if (!is_cmd_rdy && iface_ctx.sleep_state == HL7800_SLEEP_AWAKE &&
686 iface_ctx.allow_sleep) {
687 PRINT_NOT_AWAKE_MSG;
688
689 if (iface_ctx.desired_sleep_level == HL7800_SLEEP_HIBERNATE ||
690 iface_ctx.desired_sleep_level == HL7800_SLEEP_LITE_HIBERNATE) {
691 /* If the device is sleeping (not ready to receive commands)
692 * then the device may send +KSUP when waking up.
693 * We should wait for it.
694 */
695 iface_ctx.wait_for_KSUP = true;
696 iface_ctx.wait_for_KSUP_tries = 0;
697
698 set_sleep_state(iface_ctx.desired_sleep_level);
699
700 } else if (iface_ctx.desired_sleep_level == HL7800_SLEEP_SLEEP) {
701 set_sleep_state(HL7800_SLEEP_SLEEP);
702 }
703 }
704 #endif
705 }
706
hl7800_RX_lock(void)707 static int hl7800_RX_lock(void)
708 {
709 HL7800_RX_LOCK_DBG_LOG("Locking RX [%p]...", k_current_get());
710 int rc = k_sem_take(&hl7800_RX_lock_sem, K_FOREVER);
711
712 if (rc != 0) {
713 LOG_ERR("Unable to lock hl7800 (%d)", rc);
714 } else {
715 HL7800_RX_LOCK_DBG_LOG("Locked RX [%p]", k_current_get());
716 }
717
718 return rc;
719 }
720
hl7800_RX_unlock(void)721 static void hl7800_RX_unlock(void)
722 {
723 HL7800_RX_LOCK_DBG_LOG("UNLocking RX [%p]...", k_current_get());
724 k_sem_give(&hl7800_RX_lock_sem);
725 HL7800_RX_LOCK_DBG_LOG("UNLocked RX [%p]", k_current_get());
726 }
727
hl7800_RX_locked(void)728 static bool hl7800_RX_locked(void)
729 {
730 if (k_sem_count_get(&hl7800_RX_lock_sem) == 0) {
731 return true;
732 } else {
733 return false;
734 }
735 }
736
hl7800_TX_lock(void)737 static int hl7800_TX_lock(void)
738 {
739 HL7800_TX_LOCK_DBG_LOG("Locking TX [%p]...", k_current_get());
740 int rc = k_sem_take(&hl7800_TX_lock_sem, K_FOREVER);
741
742 if (rc != 0) {
743 LOG_ERR("Unable to lock hl7800 (%d)", rc);
744 } else {
745 HL7800_TX_LOCK_DBG_LOG("Locked TX [%p]", k_current_get());
746 }
747
748 return rc;
749 }
750
hl7800_TX_unlock(void)751 static void hl7800_TX_unlock(void)
752 {
753 HL7800_TX_LOCK_DBG_LOG("UNLocking TX [%p]...", k_current_get());
754 k_sem_give(&hl7800_TX_lock_sem);
755 HL7800_TX_LOCK_DBG_LOG("UNLocked TX [%p]", k_current_get());
756 }
757
hl7800_TX_locked(void)758 static bool hl7800_TX_locked(void)
759 {
760 if (k_sem_count_get(&hl7800_TX_lock_sem) == 0) {
761 return true;
762 } else {
763 return false;
764 }
765 }
766
hl7800_lock(void)767 static void hl7800_lock(void)
768 {
769 hl7800_TX_lock();
770 hl7800_RX_lock();
771 }
772
hl7800_unlock(void)773 static void hl7800_unlock(void)
774 {
775 hl7800_RX_unlock();
776 hl7800_TX_unlock();
777 }
778
socket_get(void)779 static struct hl7800_socket *socket_get(void)
780 {
781 int i;
782 struct hl7800_socket *sock = NULL;
783
784 for (i = 0; i < MDM_MAX_SOCKETS; i++) {
785 if (!iface_ctx.sockets[i].context) {
786 sock = &iface_ctx.sockets[i];
787 break;
788 }
789 }
790
791 return sock;
792 }
793
socket_from_id(int socket_id)794 static struct hl7800_socket *socket_from_id(int socket_id)
795 {
796 int i;
797 struct hl7800_socket *sock = NULL;
798
799 if (socket_id < 1) {
800 return NULL;
801 }
802
803 for (i = 0; i < MDM_MAX_SOCKETS; i++) {
804 if (iface_ctx.sockets[i].socket_id == socket_id) {
805 sock = &iface_ctx.sockets[i];
806 break;
807 }
808 }
809
810 return sock;
811 }
812
set_busy(bool busy)813 static inline void set_busy(bool busy)
814 {
815 iface_ctx.busy = busy;
816 }
817
socket_put(struct hl7800_socket * sock)818 static void socket_put(struct hl7800_socket *sock)
819 {
820 if (!sock) {
821 return;
822 }
823
824 sock->context = NULL;
825 sock->socket_id = MDM_INVALID_SOCKET_ID;
826 sock->created = false;
827 sock->reconfig = false;
828 sock->error = 0;
829 sock->rx_size = 0;
830 sock->state = SOCK_IDLE;
831 (void)memset(&sock->src, 0, sizeof(struct sockaddr));
832 (void)memset(&sock->dst, 0, sizeof(struct sockaddr));
833 }
834
hl7800_sprint_ip_addr(const struct sockaddr * addr)835 char *hl7800_sprint_ip_addr(const struct sockaddr *addr)
836 {
837 static char buf[NET_IPV6_ADDR_LEN];
838
839 #if defined(CONFIG_NET_IPV6)
840 if (addr->sa_family == AF_INET6) {
841 return net_addr_ntop(AF_INET6, &net_sin6(addr)->sin6_addr, buf,
842 sizeof(buf));
843 } else
844 #endif
845 #if defined(CONFIG_NET_IPV4)
846 if (addr->sa_family == AF_INET) {
847 return net_addr_ntop(AF_INET, &net_sin(addr)->sin_addr, buf,
848 sizeof(buf));
849 } else
850 #endif
851 {
852 LOG_ERR("Unknown IP address family:%d", addr->sa_family);
853 return NULL;
854 }
855 }
856
mdm_hl7800_register_wake_test_point_callback(void (* func)(int state))857 void mdm_hl7800_register_wake_test_point_callback(void (*func)(int state))
858 {
859 iface_ctx.wake_up_callback = func;
860 }
861
mdm_hl7800_register_gpio6_callback(void (* func)(int state))862 void mdm_hl7800_register_gpio6_callback(void (*func)(int state))
863 {
864 iface_ctx.gpio6_callback = func;
865 }
866
mdm_hl7800_register_cts_callback(void (* func)(int state))867 void mdm_hl7800_register_cts_callback(void (*func)(int state))
868 {
869 iface_ctx.cts_callback = func;
870 }
871
modem_assert_reset(bool assert)872 static void modem_assert_reset(bool assert)
873 {
874 if (assert) {
875 HL7800_IO_DBG_LOG("MDM_RESET -> ASSERTED");
876 gpio_pin_set_dt(&hl7800_cfg.gpio[MDM_RESET], 1);
877 } else {
878 HL7800_IO_DBG_LOG("MDM_RESET -> NOT_ASSERTED");
879 gpio_pin_set_dt(&hl7800_cfg.gpio[MDM_RESET], 0);
880 }
881 }
882
modem_assert_wake(bool assert)883 static void modem_assert_wake(bool assert)
884 {
885 int state;
886
887 if (assert) {
888 HL7800_IO_DBG_LOG("MDM_WAKE_PIN -> ASSERTED");
889 state = 1;
890 } else {
891 HL7800_IO_DBG_LOG("MDM_WAKE_PIN -> NOT_ASSERTED");
892 state = 0;
893 }
894
895 gpio_pin_set_dt(&hl7800_cfg.gpio[MDM_WAKE], state);
896
897 if (iface_ctx.wake_up_callback != NULL) {
898 iface_ctx.wake_up_callback(state);
899 }
900 }
901
modem_assert_pwr_on(bool assert)902 static void modem_assert_pwr_on(bool assert)
903 {
904 if (assert) {
905 HL7800_IO_DBG_LOG("MDM_PWR_ON -> ASSERTED");
906 gpio_pin_set_dt(&hl7800_cfg.gpio[MDM_PWR_ON], 1);
907 } else {
908 HL7800_IO_DBG_LOG("MDM_PWR_ON -> NOT_ASSERTED");
909 gpio_pin_set_dt(&hl7800_cfg.gpio[MDM_PWR_ON], 0);
910 }
911 }
912
modem_assert_fast_shutd(bool assert)913 static void modem_assert_fast_shutd(bool assert)
914 {
915 if (assert) {
916 HL7800_IO_DBG_LOG("MDM_FAST_SHUTD -> ASSERTED");
917 gpio_pin_set_dt(&hl7800_cfg.gpio[MDM_FAST_SHUTD], 1);
918 } else {
919 HL7800_IO_DBG_LOG("MDM_FAST_SHUTD -> NOT_ASSERTED");
920 gpio_pin_set_dt(&hl7800_cfg.gpio[MDM_FAST_SHUTD], 0);
921 }
922 }
923
allow_sleep_work_callback(struct k_work * item)924 static void allow_sleep_work_callback(struct k_work *item)
925 {
926 ARG_UNUSED(item);
927 if (!iface_ctx.busy) {
928 LOG_DBG("Allow sleep");
929 iface_ctx.allow_sleep = true;
930 set_sleep_state(iface_ctx.desired_sleep_level);
931 modem_assert_wake(false);
932 } else {
933 k_work_reschedule_for_queue(&hl7800_workq, &iface_ctx.allow_sleep_work,
934 K_MSEC(CONFIG_MODEM_HL7800_ALLOW_SLEEP_DELAY_MS));
935 }
936 }
937
allow_sleep(bool allow)938 static void allow_sleep(bool allow)
939 {
940 #ifdef CONFIG_MODEM_HL7800_LOW_POWER_MODE
941 if (allow) {
942 if (!iface_ctx.restarting && !iface_ctx.busy) {
943 k_work_reschedule_for_queue(
944 &hl7800_workq, &iface_ctx.allow_sleep_work,
945 K_MSEC(CONFIG_MODEM_HL7800_ALLOW_SLEEP_DELAY_MS));
946 } else {
947 k_work_cancel_delayable(&iface_ctx.allow_sleep_work);
948 }
949 } else {
950 LOG_DBG("Keep awake");
951 k_work_cancel_delayable(&iface_ctx.allow_sleep_work);
952 iface_ctx.allow_sleep = false;
953 modem_assert_wake(true);
954 }
955 #endif
956 }
957
event_handler(enum mdm_hl7800_event event,void * event_data)958 static void event_handler(enum mdm_hl7800_event event, void *event_data)
959 {
960 sys_snode_t *node;
961 struct mdm_hl7800_callback_agent *agent;
962 int ret;
963
964 ret = k_sem_take(&cb_lock, K_FOREVER);
965 if (ret == 0) {
966 SYS_SLIST_FOR_EACH_NODE(&hl7800_event_callback_list, node) {
967 agent = CONTAINER_OF(node, struct mdm_hl7800_callback_agent, node);
968 if (agent->event_callback != NULL) {
969 agent->event_callback(event, event_data);
970 }
971 }
972 k_sem_give(&cb_lock);
973 }
974 }
975
mdm_hl7800_get_signal_quality(int * rsrp,int * sinr)976 void mdm_hl7800_get_signal_quality(int *rsrp, int *sinr)
977 {
978 if (CONFIG_MODEM_HL7800_RSSI_RATE_SECONDS == 0) {
979 rssi_query();
980 }
981
982 *rsrp = iface_ctx.mdm_rssi;
983 *sinr = iface_ctx.mdm_sinr;
984 }
985
mdm_hl7800_wakeup(bool wakeup)986 void mdm_hl7800_wakeup(bool wakeup)
987 {
988 allow_sleep(!wakeup);
989 }
990
991 /* Send an AT command with a series of response handlers */
send_at_cmd(struct hl7800_socket * sock,const uint8_t * data,k_timeout_t timeout,int retries,bool no_id_resp)992 static int send_at_cmd(struct hl7800_socket *sock, const uint8_t *data,
993 k_timeout_t timeout, int retries, bool no_id_resp)
994 {
995 int ret = 0;
996
997 iface_ctx.last_error = 0;
998
999 do {
1000 if (!sock) {
1001 k_sem_reset(&iface_ctx.response_sem);
1002 iface_ctx.last_socket_id = 0;
1003 iface_ctx.socket_cmd = false;
1004 } else {
1005 sock->error = 0;
1006 iface_ctx.socket_cmd = true;
1007 k_sem_reset(&sock->sock_send_sem);
1008 iface_ctx.last_socket_id = sock->socket_id;
1009 }
1010 if (no_id_resp) {
1011 strncpy(iface_ctx.no_id_resp_cmd, data,
1012 sizeof(iface_ctx.no_id_resp_cmd) - 1);
1013 iface_ctx.search_no_id_resp = true;
1014 }
1015
1016 LOG_DBG("OUT: [%s]", (char *)data);
1017 mdm_receiver_send(&iface_ctx.mdm_ctx, data, strlen(data));
1018 mdm_receiver_send(&iface_ctx.mdm_ctx, "\r", 1);
1019
1020 if (K_TIMEOUT_EQ(timeout, K_NO_WAIT)) {
1021 goto done;
1022 }
1023
1024 if (!sock) {
1025 ret = k_sem_take(&iface_ctx.response_sem, timeout);
1026 } else {
1027 ret = k_sem_take(&sock->sock_send_sem, timeout);
1028 }
1029
1030 if (ret == 0) {
1031 if (sock) {
1032 ret = sock->error;
1033 } else {
1034 ret = iface_ctx.last_error;
1035 }
1036 } else if (ret == -EAGAIN) {
1037 ret = -ETIMEDOUT;
1038 }
1039
1040 retries--;
1041 if (retries < 0) {
1042 retries = 0;
1043 }
1044 } while (ret != 0 && retries > 0);
1045 done:
1046 iface_ctx.search_no_id_resp = false;
1047 return ret;
1048 }
1049
wakeup_hl7800(void)1050 static int wakeup_hl7800(void)
1051 {
1052 set_busy(true);
1053 #ifdef CONFIG_MODEM_HL7800_LOW_POWER_MODE
1054 int ret;
1055
1056 allow_sleep(false);
1057
1058 /* If modem is in sleep mode (not hibernate),
1059 * then it can respond in ~10 ms.
1060 */
1061 if (iface_ctx.desired_sleep_level == HL7800_SLEEP_SLEEP) {
1062 k_sleep(MDM_WAKE_TO_CHECK_CTS_DELAY_MS);
1063 }
1064
1065 if (!is_cmd_ready()) {
1066 LOG_DBG("Waiting to wakeup");
1067 ret = k_sem_take(&iface_ctx.mdm_awake, MDM_WAKEUP_TIME);
1068 if (ret) {
1069 LOG_DBG("Err waiting for wakeup: %d", ret);
1070 }
1071 }
1072 #endif
1073 return 0;
1074 }
1075
mdm_hl7800_send_at_cmd(const uint8_t * data)1076 int32_t mdm_hl7800_send_at_cmd(const uint8_t *data)
1077 {
1078 int ret;
1079
1080 if (!data) {
1081 return -EINVAL;
1082 }
1083
1084 hl7800_lock();
1085 wakeup_hl7800();
1086 iface_ctx.last_socket_id = 0;
1087 ret = send_at_cmd(NULL, data, MDM_CMD_SEND_TIMEOUT, 0, false);
1088 set_busy(false);
1089 allow_sleep(true);
1090 hl7800_unlock();
1091 return ret;
1092 }
1093
1094 /* The access point name (and username and password) are stored in the modem's
1095 * non-volatile memory.
1096 */
mdm_hl7800_update_apn(char * access_point_name)1097 int32_t mdm_hl7800_update_apn(char *access_point_name)
1098 {
1099 int ret = -EINVAL;
1100
1101 hl7800_lock();
1102 wakeup_hl7800();
1103 iface_ctx.last_socket_id = 0;
1104 ret = write_apn(access_point_name);
1105 set_busy(false);
1106 allow_sleep(true);
1107 hl7800_unlock();
1108
1109 if (ret >= 0) {
1110 /* After a reset the APN will be re-read from the modem
1111 * and an event will be generated.
1112 */
1113 k_work_reschedule_for_queue(&hl7800_workq, &iface_ctx.mdm_reset_work,
1114 K_NO_WAIT);
1115 }
1116 return ret;
1117 }
1118
mdm_hl7800_valid_rat(uint8_t value)1119 bool mdm_hl7800_valid_rat(uint8_t value)
1120 {
1121 if ((value == MDM_RAT_CAT_M1) || (value == MDM_RAT_CAT_NB1)) {
1122 return true;
1123 }
1124 return false;
1125 }
1126
mdm_hl7800_update_rat(enum mdm_hl7800_radio_mode value)1127 int32_t mdm_hl7800_update_rat(enum mdm_hl7800_radio_mode value)
1128 {
1129 int ret = -EINVAL;
1130
1131 if (value == iface_ctx.mdm_rat) {
1132 /* The set command will fail (in the modem)
1133 * if the RAT isn't different.
1134 */
1135 return 0;
1136 } else if (!mdm_hl7800_valid_rat(value)) {
1137 return ret;
1138 }
1139
1140 hl7800_lock();
1141 wakeup_hl7800();
1142 iface_ctx.last_socket_id = 0;
1143
1144 if (value == MDM_RAT_CAT_M1) {
1145 if (iface_ctx.new_rat_cmd_support) {
1146 SEND_AT_CMD_ONCE_EXPECT_OK(SET_RAT_M1_CMD);
1147 } else {
1148 SEND_AT_CMD_ONCE_EXPECT_OK(SET_RAT_M1_CMD_LEGACY);
1149 }
1150 } else { /* MDM_RAT_CAT_NB1 */
1151 if (iface_ctx.new_rat_cmd_support) {
1152 SEND_AT_CMD_ONCE_EXPECT_OK(SET_RAT_NB1_CMD);
1153 } else {
1154 SEND_AT_CMD_ONCE_EXPECT_OK(SET_RAT_NB1_CMD_LEGACY);
1155 }
1156 }
1157
1158 error:
1159
1160 set_busy(false);
1161 allow_sleep(true);
1162 hl7800_unlock();
1163
1164 /* Changing the RAT causes the modem to reset.
1165 * A reset and reconfigure ensures the modem configuration and
1166 * state are valid.
1167 */
1168 if (ret >= 0) {
1169 k_work_reschedule_for_queue(&hl7800_workq, &iface_ctx.mdm_reset_work, K_NO_WAIT);
1170 }
1171
1172 return ret;
1173 }
1174
mdm_hl7800_get_local_time(struct tm * tm,int32_t * offset)1175 int32_t mdm_hl7800_get_local_time(struct tm *tm, int32_t *offset)
1176 {
1177 int ret;
1178
1179 iface_ctx.local_time_valid = false;
1180
1181 hl7800_lock();
1182 wakeup_hl7800();
1183 iface_ctx.last_socket_id = 0;
1184 ret = send_at_cmd(NULL, "AT+CCLK?", MDM_CMD_SEND_TIMEOUT, 0, false);
1185 set_busy(false);
1186 allow_sleep(true);
1187 if (iface_ctx.local_time_valid) {
1188 memcpy(tm, &iface_ctx.local_time, sizeof(struct tm));
1189 memcpy(offset, &iface_ctx.local_time_offset, sizeof(*offset));
1190 } else {
1191 ret = -EIO;
1192 }
1193 hl7800_unlock();
1194 return ret;
1195 }
1196
mdm_hl7800_get_operator_index(void)1197 int32_t mdm_hl7800_get_operator_index(void)
1198 {
1199 int ret;
1200
1201 hl7800_lock();
1202 wakeup_hl7800();
1203 iface_ctx.last_socket_id = 0;
1204 ret = send_at_cmd(NULL, "AT+KCARRIERCFG?", MDM_CMD_SEND_TIMEOUT, 0,
1205 false);
1206 set_busy(false);
1207 allow_sleep(true);
1208 hl7800_unlock();
1209 if (ret < 0) {
1210 return ret;
1211 } else {
1212 return iface_ctx.operator_index;
1213 }
1214 }
1215
mdm_hl7800_get_functionality(void)1216 int32_t mdm_hl7800_get_functionality(void)
1217 {
1218 int ret;
1219
1220 hl7800_lock();
1221 wakeup_hl7800();
1222 iface_ctx.last_socket_id = 0;
1223 ret = send_at_cmd(NULL, "AT+CFUN?", MDM_CMD_SEND_TIMEOUT, 0, false);
1224 set_busy(false);
1225 allow_sleep(true);
1226 hl7800_unlock();
1227
1228 if (ret < 0) {
1229 return ret;
1230 } else {
1231 return iface_ctx.functionality;
1232 }
1233 }
1234
mdm_hl7800_set_functionality(enum mdm_hl7800_functionality mode)1235 int32_t mdm_hl7800_set_functionality(enum mdm_hl7800_functionality mode)
1236 {
1237 int ret;
1238 char buf[sizeof("AT+CFUN=###,0")] = { 0 };
1239
1240 hl7800_lock();
1241 wakeup_hl7800();
1242 snprintk(buf, sizeof(buf), "AT+CFUN=%u,0", mode);
1243 iface_ctx.last_socket_id = 0;
1244 ret = send_at_cmd(NULL, buf, MDM_CMD_SEND_TIMEOUT,
1245 MDM_DEFAULT_AT_CMD_RETRIES, false);
1246 set_busy(false);
1247 allow_sleep(true);
1248 hl7800_unlock();
1249
1250 return ret;
1251 }
1252
1253 #ifdef CONFIG_MODEM_HL7800_GPS
mdm_hl7800_set_gps_rate(uint32_t rate)1254 int32_t mdm_hl7800_set_gps_rate(uint32_t rate)
1255 {
1256 int ret = -1;
1257
1258 hl7800_lock();
1259 wakeup_hl7800();
1260 iface_ctx.gps_query_location_rate_seconds = rate;
1261
1262 /* Stopping first allows changing the rate between two non-zero values.
1263 * Ignore error if GNSS isn't running.
1264 */
1265 SEND_AT_CMD_IGNORE_ERROR("AT+GNSSSTOP");
1266
1267 if (rate == 0) {
1268 SEND_AT_CMD_EXPECT_OK("AT+CFUN=1,0");
1269 } else {
1270 /* Navigation doesn't work when LTE is on. */
1271 SEND_AT_CMD_EXPECT_OK("AT+CFUN=4,0");
1272
1273 SEND_AT_CMD_EXPECT_OK("AT+GNSSCONF=1,1");
1274
1275 if (IS_ENABLED(CONFIG_MODEM_HL7800_USE_GLONASS)) {
1276 SEND_AT_CMD_EXPECT_OK("AT+GNSSCONF=10,1");
1277 }
1278 /* Enable all NMEA sentences */
1279 SEND_AT_CMD_EXPECT_OK("AT+GNSSNMEA=0,1000,0,1FF");
1280 /* Enable GPS */
1281 SEND_AT_CMD_EXPECT_OK("AT+GNSSSTART=0");
1282 }
1283
1284 error:
1285 if (rate && ret == 0) {
1286 k_work_reschedule_for_queue(&hl7800_workq, &iface_ctx.gps_work,
1287 K_SECONDS(iface_ctx.gps_query_location_rate_seconds));
1288 } else {
1289 k_work_cancel_delayable(&iface_ctx.gps_work);
1290 }
1291 LOG_DBG("GPS status: %d rate: %u", ret, rate);
1292
1293 set_busy(false);
1294 allow_sleep(true);
1295 hl7800_unlock();
1296 return ret;
1297 }
1298 #endif /* CONFIG_MODEM_HL7800_GPS */
1299
1300 #ifdef CONFIG_MODEM_HL7800_POLTE
mdm_hl7800_polte_register(void)1301 int32_t mdm_hl7800_polte_register(void)
1302 {
1303 int ret = -1;
1304
1305 hl7800_lock();
1306 wakeup_hl7800();
1307 /* register for events */
1308 SEND_AT_CMD_EXPECT_OK("AT%POLTEEV=\"REGISTER\",1");
1309 SEND_AT_CMD_EXPECT_OK("AT%POLTEEV=\"LOCATION\",1");
1310 /* register with polte.io */
1311 SEND_AT_CMD_EXPECT_OK("AT%POLTECMD=\"REGISTER\"");
1312 error:
1313 LOG_DBG("PoLTE register status: %d", ret);
1314 set_busy(false);
1315 allow_sleep(true);
1316 hl7800_unlock();
1317 return ret;
1318 }
1319
mdm_hl7800_polte_enable(char * user,char * password)1320 int32_t mdm_hl7800_polte_enable(char *user, char *password)
1321 {
1322 int ret = -1;
1323 char buf[sizeof(MDM_HL7800_SET_POLTE_USER_AND_PASSWORD_FMT_STR) +
1324 MDM_HL7800_MAX_POLTE_USER_ID_SIZE + MDM_HL7800_MAX_POLTE_PASSWORD_SIZE] = { 0 };
1325
1326 hl7800_lock();
1327 wakeup_hl7800();
1328
1329 /* register for events */
1330 SEND_AT_CMD_EXPECT_OK("AT%POLTEEV=\"REGISTER\",1");
1331 SEND_AT_CMD_EXPECT_OK("AT%POLTEEV=\"LOCATION\",1");
1332 /* restore user and password (not saved in NV by modem) */
1333 snprintk(buf, sizeof(buf), MDM_HL7800_SET_POLTE_USER_AND_PASSWORD_FMT_STR, user, password);
1334 ret = send_at_cmd(NULL, buf, MDM_CMD_SEND_TIMEOUT, MDM_DEFAULT_AT_CMD_RETRIES, false);
1335
1336 error:
1337 LOG_DBG("PoLTE register status: %d", ret);
1338 set_busy(false);
1339 allow_sleep(true);
1340 hl7800_unlock();
1341 return ret;
1342 }
1343
mdm_hl7800_polte_locate(void)1344 int32_t mdm_hl7800_polte_locate(void)
1345 {
1346 int ret = -1;
1347
1348 hl7800_lock();
1349 wakeup_hl7800();
1350 SEND_AT_CMD_EXPECT_OK("AT%POLTECMD=\"LOCATE\",2,1");
1351 error:
1352 LOG_DBG("PoLTE locate status: %d", ret);
1353 set_busy(false);
1354 allow_sleep(true);
1355 hl7800_unlock();
1356 return ret;
1357 }
1358
1359 #endif /* CONFIG_MODEM_HL7800_POLTE */
1360
1361 /**
1362 * @brief Perform a site survey.
1363 *
1364 */
mdm_hl7800_perform_site_survey(void)1365 int32_t mdm_hl7800_perform_site_survey(void)
1366 {
1367 int ret;
1368
1369 hl7800_lock();
1370 wakeup_hl7800();
1371 ret = send_at_cmd(NULL, "at%meas=\"97\"", MDM_CMD_SEND_TIMEOUT, 0, false);
1372 set_busy(false);
1373 allow_sleep(true);
1374 hl7800_unlock();
1375 return ret;
1376 }
1377
mdm_hl7800_generate_status_events(void)1378 void mdm_hl7800_generate_status_events(void)
1379 {
1380 hl7800_lock();
1381 generate_startup_state_event();
1382 generate_network_state_event();
1383 generate_sleep_state_event();
1384 #ifdef CONFIG_MODEM_HL7800_FW_UPDATE
1385 generate_fota_state_event();
1386 #endif
1387 event_handler(HL7800_EVENT_RSSI, &iface_ctx.mdm_rssi);
1388 event_handler(HL7800_EVENT_SINR, &iface_ctx.mdm_sinr);
1389 event_handler(HL7800_EVENT_APN_UPDATE, &iface_ctx.mdm_apn);
1390 event_handler(HL7800_EVENT_RAT, &iface_ctx.mdm_rat);
1391 event_handler(HL7800_EVENT_BANDS, iface_ctx.mdm_bands_string);
1392 event_handler(HL7800_EVENT_ACTIVE_BANDS, iface_ctx.mdm_active_bands_string);
1393 event_handler(HL7800_EVENT_REVISION, iface_ctx.mdm_revision);
1394 hl7800_unlock();
1395 }
1396
mdm_hl7800_log_filter_set(uint32_t level)1397 uint32_t mdm_hl7800_log_filter_set(uint32_t level)
1398 {
1399 uint32_t new_log_level = 0;
1400
1401 #ifdef CONFIG_LOG_RUNTIME_FILTERING
1402 new_log_level =
1403 log_filter_set(NULL, Z_LOG_LOCAL_DOMAIN_ID,
1404 log_source_id_get(STRINGIFY(LOG_MODULE_NAME)),
1405 level);
1406 #endif
1407
1408 return new_log_level;
1409 }
1410
send_data(struct hl7800_socket * sock,struct net_pkt * pkt)1411 static int send_data(struct hl7800_socket *sock, struct net_pkt *pkt)
1412 {
1413 int ret;
1414 struct net_buf *frag;
1415 char dst_addr[NET_IPV6_ADDR_LEN];
1416 char buf[sizeof("AT+KUDPSND=##,\"" IPV6_ADDR_FORMAT "\",#####,####")];
1417 size_t send_len, actual_send_len;
1418
1419 send_len = 0, actual_send_len = 0;
1420
1421 if (!sock) {
1422 return -EINVAL;
1423 }
1424
1425 sock->error = 0;
1426 sock->state = SOCK_TX;
1427
1428 frag = pkt->frags;
1429 send_len = net_buf_frags_len(frag);
1430 /* start sending data */
1431 k_sem_reset(&sock->sock_send_sem);
1432 if (sock->type == SOCK_STREAM) {
1433 snprintk(buf, sizeof(buf), "AT+KTCPSND=%d,%zu", sock->socket_id,
1434 send_len);
1435 } else {
1436 if (!net_addr_ntop(sock->family, &net_sin(&sock->dst)->sin_addr,
1437 dst_addr, sizeof(dst_addr))) {
1438 LOG_ERR("Invalid dst addr");
1439 return -EINVAL;
1440 }
1441 snprintk(buf, sizeof(buf), "AT+KUDPSND=%d,\"%s\",%u,%zu",
1442 sock->socket_id, dst_addr,
1443 net_sin(&sock->dst)->sin_port, send_len);
1444 }
1445 send_at_cmd(sock, buf, K_NO_WAIT, 0, false);
1446
1447 /* wait for CONNECT or error */
1448 ret = k_sem_take(&sock->sock_send_sem, MDM_IP_SEND_RX_TIMEOUT);
1449 if (ret) {
1450 LOG_ERR("Err waiting for CONNECT (%d)", ret);
1451 goto done;
1452 }
1453 /* check for error */
1454 if (sock->error != 0) {
1455 ret = sock->error;
1456 LOG_ERR("AT+K**PSND (%d)", ret);
1457 goto done;
1458 }
1459
1460 /* Loop through packet data and send */
1461 while (frag) {
1462 actual_send_len += frag->len;
1463 mdm_receiver_send(&iface_ctx.mdm_ctx, frag->data, frag->len);
1464 frag = frag->frags;
1465 }
1466 if (actual_send_len != send_len) {
1467 LOG_WRN("AT+K**PSND act: %zd exp: %zd", actual_send_len,
1468 send_len);
1469 }
1470 LOG_DBG("Sent %zu bytes", actual_send_len);
1471
1472 /* Send EOF pattern to terminate data */
1473 k_sem_reset(&sock->sock_send_sem);
1474 mdm_receiver_send(&iface_ctx.mdm_ctx, EOF_PATTERN, strlen(EOF_PATTERN));
1475 ret = k_sem_take(&sock->sock_send_sem, MDM_IP_SEND_RX_TIMEOUT);
1476 if (ret == 0) {
1477 ret = sock->error;
1478 } else if (ret == -EAGAIN) {
1479 ret = -ETIMEDOUT;
1480 }
1481 done:
1482 if (sock->type == SOCK_STREAM) {
1483 if (sock->error == 0) {
1484 sock->state = SOCK_CONNECTED;
1485 }
1486 } else {
1487 sock->state = SOCK_IDLE;
1488 }
1489
1490 return ret;
1491 }
1492
1493 /*** NET_BUF HELPERS ***/
1494
is_crlf(uint8_t c)1495 static bool is_crlf(uint8_t c)
1496 {
1497 if (c == '\n' || c == '\r') {
1498 return true;
1499 } else {
1500 return false;
1501 }
1502 }
1503
net_buf_skipcrlf(struct net_buf ** buf)1504 static void net_buf_skipcrlf(struct net_buf **buf)
1505 {
1506 /* chop off any /n or /r */
1507 while (*buf && is_crlf(*(*buf)->data)) {
1508 net_buf_pull_u8(*buf);
1509 if (!(*buf)->len) {
1510 *buf = net_buf_frag_del(NULL, *buf);
1511 }
1512 }
1513 }
1514
net_buf_findcrlf(struct net_buf * buf,struct net_buf ** frag)1515 static uint16_t net_buf_findcrlf(struct net_buf *buf, struct net_buf **frag)
1516 {
1517 uint16_t len = 0U, pos = 0U;
1518
1519 while (buf && !is_crlf(*(buf->data + pos))) {
1520 if (pos + 1 >= buf->len) {
1521 len += buf->len;
1522 buf = buf->frags;
1523 pos = 0U;
1524 } else {
1525 pos++;
1526 }
1527 }
1528
1529 if (buf && is_crlf(*(buf->data + pos))) {
1530 len += pos;
1531 *frag = buf;
1532 return len;
1533 }
1534
1535 return 0;
1536 }
1537
net_buf_get_u8(struct net_buf ** buf)1538 static uint8_t net_buf_get_u8(struct net_buf **buf)
1539 {
1540 uint8_t val = net_buf_pull_u8(*buf);
1541
1542 if (!(*buf)->len) {
1543 *buf = net_buf_frag_del(NULL, *buf);
1544 }
1545 return val;
1546 }
1547
net_buf_remove(struct net_buf ** buf,uint32_t len)1548 static uint32_t net_buf_remove(struct net_buf **buf, uint32_t len)
1549 {
1550 uint32_t to_remove;
1551 uint32_t removed = 0;
1552
1553 while (*buf && len > 0) {
1554 to_remove = (*buf)->len;
1555 if (to_remove > len) {
1556 to_remove = len;
1557 }
1558 net_buf_pull(*buf, to_remove);
1559 removed += to_remove;
1560 len -= to_remove;
1561 if (!(*buf)->len) {
1562 *buf = net_buf_frag_del(NULL, *buf);
1563 }
1564 }
1565 return removed;
1566 }
1567
1568 /*** UDP / TCP Helper Function ***/
1569
1570 /* Setup IP header data to be used by some network applications.
1571 * While much is dummy data, some fields such as dst, port and family are
1572 * important.
1573 * Return the IP + protocol header length.
1574 */
pkt_setup_ip_data(struct net_pkt * pkt,struct hl7800_socket * sock)1575 static int pkt_setup_ip_data(struct net_pkt *pkt, struct hl7800_socket *sock)
1576 {
1577 int hdr_len = 0;
1578 uint16_t src_port = 0U, dst_port = 0U;
1579 #if defined(CONFIG_NET_TCP)
1580 struct net_tcp_hdr *tcp;
1581 #endif
1582
1583 #if defined(CONFIG_NET_IPV6)
1584 if (net_pkt_family(pkt) == AF_INET6) {
1585 if (net_ipv6_create(
1586 pkt,
1587 &((struct sockaddr_in6 *)&sock->dst)->sin6_addr,
1588 &((struct sockaddr_in6 *)&sock->src)->sin6_addr)) {
1589 return -1;
1590 }
1591 src_port = ntohs(net_sin6(&sock->src)->sin6_port);
1592 dst_port = ntohs(net_sin6(&sock->dst)->sin6_port);
1593
1594 hdr_len = sizeof(struct net_ipv6_hdr);
1595 }
1596 #endif
1597 #if defined(CONFIG_NET_IPV4)
1598 if (net_pkt_family(pkt) == AF_INET) {
1599 if (net_ipv4_create(
1600 pkt, &((struct sockaddr_in *)&sock->dst)->sin_addr,
1601 &((struct sockaddr_in *)&sock->src)->sin_addr)) {
1602 return -1;
1603 }
1604 src_port = ntohs(net_sin(&sock->src)->sin_port);
1605 dst_port = ntohs(net_sin(&sock->dst)->sin_port);
1606
1607 hdr_len = sizeof(struct net_ipv4_hdr);
1608 }
1609 #endif
1610
1611 #if defined(CONFIG_NET_UDP)
1612 if (sock->ip_proto == IPPROTO_UDP) {
1613 if (net_udp_create(pkt, dst_port, src_port)) {
1614 return -1;
1615 }
1616
1617 hdr_len += NET_UDPH_LEN;
1618 }
1619 #endif
1620 #if defined(CONFIG_NET_TCP)
1621 if (sock->ip_proto == IPPROTO_TCP) {
1622 NET_PKT_DATA_ACCESS_DEFINE(tcp_access, struct net_tcp_hdr);
1623
1624 tcp = (struct net_tcp_hdr *)net_pkt_get_data(pkt, &tcp_access);
1625 if (!tcp) {
1626 return -1;
1627 }
1628
1629 (void)memset(tcp, 0, NET_TCPH_LEN);
1630
1631 /* Setup TCP header */
1632 tcp->src_port = dst_port;
1633 tcp->dst_port = src_port;
1634
1635 if (net_pkt_set_data(pkt, &tcp_access)) {
1636 return -1;
1637 }
1638
1639 hdr_len += NET_TCPH_LEN;
1640 }
1641 #endif /* CONFIG_NET_TCP */
1642
1643 return hdr_len;
1644 }
1645
1646 /*** MODEM RESPONSE HANDLERS ***/
1647
wait_for_modem_data(struct net_buf ** buf,uint32_t current_len,uint32_t expected_len)1648 static uint32_t wait_for_modem_data(struct net_buf **buf, uint32_t current_len,
1649 uint32_t expected_len)
1650 {
1651 uint32_t waitForDataTries = 0;
1652
1653 while ((current_len < expected_len) &&
1654 (waitForDataTries < MDM_WAIT_FOR_DATA_RETRIES)) {
1655 LOG_DBG("cur:%d, exp:%d", current_len, expected_len);
1656 k_sleep(MDM_WAIT_FOR_DATA_TIME);
1657 current_len += hl7800_read_rx(buf);
1658 waitForDataTries++;
1659 }
1660
1661 return current_len;
1662 }
1663
wait_for_modem_data_and_newline(struct net_buf ** buf,uint32_t current_len,uint32_t expected_len)1664 static uint32_t wait_for_modem_data_and_newline(struct net_buf **buf,
1665 uint32_t current_len,
1666 uint32_t expected_len)
1667 {
1668 return wait_for_modem_data(buf, current_len,
1669 (expected_len + strlen("\r\n")));
1670 }
1671
1672 /* Handler: AT+CGMI */
on_cmd_atcmdinfo_manufacturer(struct net_buf ** buf,uint16_t len)1673 static bool on_cmd_atcmdinfo_manufacturer(struct net_buf **buf, uint16_t len)
1674 {
1675 struct net_buf *frag = NULL;
1676 size_t out_len;
1677 int len_no_null = MDM_MANUFACTURER_LENGTH - 1;
1678
1679 /* make sure revision data is received
1680 * waiting for: Sierra Wireless\r\n
1681 */
1682 wait_for_modem_data_and_newline(buf, net_buf_frags_len(*buf),
1683 MDM_MANUFACTURER_LENGTH);
1684
1685 frag = NULL;
1686 len = net_buf_findcrlf(*buf, &frag);
1687 if (!frag) {
1688 LOG_ERR("Unable to find mfg end");
1689 goto done;
1690 }
1691 if (len < len_no_null) {
1692 LOG_WRN("mfg too short (len:%d)", len);
1693 } else if (len > len_no_null) {
1694 LOG_WRN("mfg too long (len:%d)", len);
1695 len = MDM_MANUFACTURER_LENGTH;
1696 }
1697
1698 out_len = net_buf_linearize(iface_ctx.mdm_manufacturer,
1699 sizeof(iface_ctx.mdm_manufacturer) - 1, *buf, 0,
1700 len);
1701 iface_ctx.mdm_manufacturer[out_len] = 0;
1702 LOG_INF("Manufacturer: %s", iface_ctx.mdm_manufacturer);
1703 done:
1704 return true;
1705 }
1706
1707 /* Handler: AT+CGMM */
on_cmd_atcmdinfo_model(struct net_buf ** buf,uint16_t len)1708 static bool on_cmd_atcmdinfo_model(struct net_buf **buf, uint16_t len)
1709 {
1710 struct net_buf *frag = NULL;
1711 size_t out_len;
1712 int len_no_null = MDM_MODEL_LENGTH - 1;
1713
1714 /* make sure model data is received
1715 * waiting for: HL7800\r\n
1716 */
1717 wait_for_modem_data_and_newline(buf, net_buf_frags_len(*buf),
1718 MDM_MODEL_LENGTH);
1719
1720 frag = NULL;
1721 len = net_buf_findcrlf(*buf, &frag);
1722 if (!frag) {
1723 LOG_ERR("Unable to find model end");
1724 goto done;
1725 }
1726 if (len < len_no_null) {
1727 LOG_WRN("model too short (len:%d)", len);
1728 } else if (len > len_no_null) {
1729 LOG_WRN("model too long (len:%d)", len);
1730 len = MDM_MODEL_LENGTH;
1731 }
1732
1733 out_len = net_buf_linearize(iface_ctx.mdm_model, sizeof(iface_ctx.mdm_model) - 1,
1734 *buf, 0, len);
1735 iface_ctx.mdm_model[out_len] = 0;
1736 LOG_INF("Model: %s", iface_ctx.mdm_model);
1737 done:
1738 return true;
1739 }
1740
1741 /* Handler: AT+CGMR */
on_cmd_atcmdinfo_revision(struct net_buf ** buf,uint16_t len)1742 static bool on_cmd_atcmdinfo_revision(struct net_buf **buf, uint16_t len)
1743 {
1744 struct net_buf *frag = NULL;
1745 size_t out_len;
1746
1747 /* make sure revision data is received
1748 * waiting for something like: AHL7800.1.2.3.1.20171211\r\n
1749 */
1750 wait_for_modem_data_and_newline(buf, net_buf_frags_len(*buf),
1751 MDM_HL7800_REVISION_MAX_SIZE);
1752
1753 frag = NULL;
1754 len = net_buf_findcrlf(*buf, &frag);
1755 if (!frag) {
1756 LOG_ERR("Unable to find rev end");
1757 goto done;
1758 }
1759 if (len == 0) {
1760 LOG_WRN("revision not found");
1761 } else if (len > MDM_HL7800_REVISION_MAX_STRLEN) {
1762 LOG_WRN("revision too long (len:%d)", len);
1763 len = MDM_HL7800_REVISION_MAX_STRLEN;
1764 }
1765
1766 out_len = net_buf_linearize(
1767 iface_ctx.mdm_revision, sizeof(iface_ctx.mdm_revision) - 1, *buf, 0, len);
1768 iface_ctx.mdm_revision[out_len] = 0;
1769 LOG_INF("Revision: %s", iface_ctx.mdm_revision);
1770 event_handler(HL7800_EVENT_REVISION, iface_ctx.mdm_revision);
1771 done:
1772 return true;
1773 }
1774
1775 /* Handler: AT+CGSN */
on_cmd_atcmdinfo_imei(struct net_buf ** buf,uint16_t len)1776 static bool on_cmd_atcmdinfo_imei(struct net_buf **buf, uint16_t len)
1777 {
1778 struct net_buf *frag = NULL;
1779 size_t out_len;
1780
1781 /* make sure IMEI data is received
1782 * waiting for: ###############\r\n
1783 */
1784 wait_for_modem_data_and_newline(buf, net_buf_frags_len(*buf),
1785 MDM_HL7800_IMEI_SIZE);
1786
1787 frag = NULL;
1788 len = net_buf_findcrlf(*buf, &frag);
1789 if (!frag) {
1790 LOG_ERR("Unable to find IMEI end");
1791 goto done;
1792 }
1793 if (len < MDM_HL7800_IMEI_STRLEN) {
1794 LOG_WRN("IMEI too short (len:%d)", len);
1795 } else if (len > MDM_HL7800_IMEI_STRLEN) {
1796 LOG_WRN("IMEI too long (len:%d)", len);
1797 len = MDM_HL7800_IMEI_STRLEN;
1798 }
1799
1800 out_len = net_buf_linearize(iface_ctx.mdm_imei, sizeof(iface_ctx.mdm_imei) - 1,
1801 *buf, 0, len);
1802 iface_ctx.mdm_imei[out_len] = 0;
1803
1804 LOG_INF("IMEI: %s", iface_ctx.mdm_imei);
1805 done:
1806 return true;
1807 }
1808
1809 /* Handler: +CCID: <ICCID>,<EID>
1810 * NOTE: EID will only be present for eSIM
1811 */
on_cmd_atcmdinfo_iccid(struct net_buf ** buf,uint16_t len)1812 static bool on_cmd_atcmdinfo_iccid(struct net_buf **buf, uint16_t len)
1813 {
1814 char value[MDM_CCID_RESP_MAX_SIZE];
1815 char *delim;
1816 int iccid_len;
1817 size_t out_len;
1818
1819 out_len = net_buf_linearize(value, sizeof(value), *buf, 0, len);
1820 value[out_len] = 0;
1821
1822 LOG_DBG("+CCID: %s", value);
1823
1824 if (len > MDM_HL7800_ICCID_MAX_STRLEN) {
1825 delim = strchr(value, ',');
1826 if (!delim) {
1827 LOG_ERR("Could not process +CCID");
1828 return true;
1829 }
1830 /* Replace ',' with null so value contains two null terminated strings */
1831 *delim = 0;
1832 LOG_INF("EID: %s", delim + 1);
1833 }
1834
1835 iccid_len = strlen(value);
1836 strncpy(iface_ctx.mdm_iccid, value, sizeof(iface_ctx.mdm_iccid));
1837 len = MIN(iccid_len, sizeof(iface_ctx.mdm_iccid) - 1);
1838 iface_ctx.mdm_iccid[len] = '\0';
1839
1840 if (iccid_len > len) {
1841 LOG_WRN("ICCID too long: %d (max %d)", iccid_len, len);
1842 }
1843
1844 LOG_INF("ICCID: %s", iface_ctx.mdm_iccid);
1845
1846 return true;
1847 }
1848
on_cmd_atcmdinfo_imsi(struct net_buf ** buf,uint16_t len)1849 static bool on_cmd_atcmdinfo_imsi(struct net_buf **buf, uint16_t len)
1850 {
1851 struct net_buf *frag = NULL;
1852 size_t out_len;
1853
1854 /* The handler for the IMSI is based on the command.
1855 * waiting for: <IMSI>\r\n
1856 */
1857 wait_for_modem_data_and_newline(buf, net_buf_frags_len(*buf),
1858 MDM_HL7800_IMSI_MIN_STR_SIZE);
1859
1860 frag = NULL;
1861 len = net_buf_findcrlf(*buf, &frag);
1862 if (!frag) {
1863 LOG_ERR("Unable to find IMSI end");
1864 goto done;
1865 }
1866 if (len > MDM_HL7800_IMSI_MAX_STRLEN) {
1867 LOG_WRN("IMSI too long (len:%d)", len);
1868 len = MDM_HL7800_IMSI_MAX_STRLEN;
1869 }
1870
1871 out_len = net_buf_linearize(iface_ctx.mdm_imsi, MDM_HL7800_IMSI_MAX_STR_SIZE,
1872 *buf, 0, len);
1873 iface_ctx.mdm_imsi[out_len] = 0;
1874
1875 if (strstr(iface_ctx.mdm_imsi, "ERROR") != NULL) {
1876 LOG_ERR("Unable to read IMSI");
1877 memset(iface_ctx.mdm_imsi, 0, sizeof(iface_ctx.mdm_imsi));
1878 }
1879
1880 LOG_INF("IMSI: %s", iface_ctx.mdm_imsi);
1881 done:
1882 return true;
1883 }
1884
dns_work_cb(struct k_work * work)1885 static void dns_work_cb(struct k_work *work)
1886 {
1887 #if defined(CONFIG_DNS_RESOLVER) && !defined(CONFIG_DNS_SERVER_IP_ADDRESSES)
1888 int ret;
1889 struct dns_resolve_context *dnsCtx;
1890 struct sockaddr temp_addr;
1891 bool valid_address = false;
1892 bool retry = false;
1893 static const char *const dns_servers_str[] = {
1894 #ifdef CONFIG_NET_IPV6
1895 iface_ctx.dns_v6_string,
1896 #endif
1897 #ifdef CONFIG_NET_IPV4
1898 iface_ctx.dns_v4_string,
1899 #endif
1900 NULL};
1901
1902 #ifdef CONFIG_NET_IPV6
1903 valid_address =
1904 net_ipaddr_parse(iface_ctx.dns_v6_string, strlen(iface_ctx.dns_v6_string),
1905 &temp_addr);
1906 if (!valid_address && IS_ENABLED(CONFIG_NET_IPV4)) {
1907 /* IPv6 DNS string is not valid, replace it with IPv4 address and recheck */
1908 strncpy(iface_ctx.dns_v6_string, iface_ctx.dns_v4_string,
1909 sizeof(iface_ctx.dns_v6_string) - 1);
1910 valid_address = net_ipaddr_parse(iface_ctx.dns_v6_string,
1911 strlen(iface_ctx.dns_v6_string), &temp_addr);
1912 }
1913 #else
1914 valid_address =
1915 net_ipaddr_parse(iface_ctx.dns_v4_string, strlen(iface_ctx.dns_v4_string),
1916 &temp_addr);
1917 #endif
1918
1919 if (!valid_address) {
1920 LOG_WRN("No valid DNS address!");
1921 } else if (iface_ctx.iface && net_if_is_up(iface_ctx.iface) && !iface_ctx.dns_ready) {
1922 /* set new DNS addr in DNS resolver */
1923 LOG_DBG("Refresh DNS resolver");
1924 dnsCtx = dns_resolve_get_default();
1925 if (dnsCtx->state == DNS_RESOLVE_CONTEXT_INACTIVE) {
1926 LOG_DBG("Initializing DNS resolver");
1927 ret = dns_resolve_init(dnsCtx, (const char **)dns_servers_str, NULL);
1928 if (ret < 0) {
1929 LOG_ERR("dns_resolve_init fail (%d)", ret);
1930 retry = true;
1931 }
1932 } else {
1933 LOG_DBG("Reconfiguring DNS resolver");
1934 ret = dns_resolve_reconfigure(dnsCtx, (const char **)dns_servers_str, NULL);
1935 if (ret < 0) {
1936 LOG_ERR("dns_resolve_reconfigure fail (%d)", ret);
1937 retry = true;
1938 }
1939 }
1940 if (!retry) {
1941 LOG_DBG("DNS ready");
1942 iface_ctx.dns_ready = true;
1943 } else {
1944 LOG_DBG("DNS not ready, schedule a retry");
1945 k_work_reschedule_for_queue(&hl7800_workq, &iface_ctx.dns_work,
1946 K_SECONDS(DNS_WORK_DELAY_SECS * 2));
1947 }
1948 }
1949 #endif
1950 }
1951
mdm_hl7800_get_iccid(void)1952 char *mdm_hl7800_get_iccid(void)
1953 {
1954 return iface_ctx.mdm_iccid;
1955 }
1956
mdm_hl7800_get_sn(void)1957 char *mdm_hl7800_get_sn(void)
1958 {
1959 return iface_ctx.mdm_sn;
1960 }
1961
mdm_hl7800_get_imei(void)1962 char *mdm_hl7800_get_imei(void)
1963 {
1964 return iface_ctx.mdm_imei;
1965 }
1966
mdm_hl7800_get_fw_version(void)1967 char *mdm_hl7800_get_fw_version(void)
1968 {
1969 return iface_ctx.mdm_revision;
1970 }
1971
mdm_hl7800_get_imsi(void)1972 char *mdm_hl7800_get_imsi(void)
1973 {
1974 return iface_ctx.mdm_imsi;
1975 }
1976
1977 /* Convert HL7800 IPv6 address string in format
1978 * a01.a02.a03.a04.a05.a06.a07.a08.a09.a10.a11.a12.a13.a14.a15.a16 to
1979 * an IPv6 address.
1980 */
hl7800_net_addr6_pton(const char * src,struct in6_addr * dst)1981 static int hl7800_net_addr6_pton(const char *src, struct in6_addr *dst)
1982 {
1983 int num_sections = 8;
1984 int i, len;
1985 uint16_t ipv6_section;
1986
1987 len = strlen(src);
1988 for (i = 0; i < len; i++) {
1989 if (!(src[i] >= '0' && src[i] <= '9') && src[i] != '.') {
1990 return -EINVAL;
1991 }
1992 }
1993
1994 for (i = 0; i < num_sections; i++) {
1995 if (!src || *src == '\0') {
1996 return -EINVAL;
1997 }
1998
1999 ipv6_section = (uint16_t)strtol(src, NULL, 10);
2000 src = strchr(src, '.');
2001 if (!src) {
2002 return -EINVAL;
2003 }
2004 src++;
2005 if (*src == '\0') {
2006 return -EINVAL;
2007 }
2008 ipv6_section = (ipv6_section << 8) | (uint16_t)strtol(src, NULL, 10);
2009 UNALIGNED_PUT(htons(ipv6_section), &dst->s6_addr16[i]);
2010
2011 src = strchr(src, '.');
2012 if (src) {
2013 src++;
2014 } else {
2015 if (i < num_sections - 1) {
2016 return -EINVAL;
2017 }
2018 }
2019 }
2020
2021 return 0;
2022 }
2023
2024 /* Handler: +CGCONTRDP: <cid>,<bearer_id>,<apn>,<local_addr and subnet_mask>,
2025 * <gw_addr>,<DNS_prim_addr>,<DNS_sec_addr>
2026 */
on_cmd_atcmdinfo_ipaddr(struct net_buf ** buf,uint16_t len)2027 static bool on_cmd_atcmdinfo_ipaddr(struct net_buf **buf, uint16_t len)
2028 {
2029 int ret;
2030 int num_delims = CGCONTRDP_RESPONSE_NUM_DELIMS;
2031 char *delims[CGCONTRDP_RESPONSE_NUM_DELIMS];
2032 size_t out_len;
2033 char value[MDM_IP_INFO_RESP_SIZE];
2034 char *search_start, *addr_start, *sm_start;
2035 struct in_addr new_ipv4_addr;
2036 struct in6_addr new_ipv6_addr;
2037 bool is_ipv4;
2038 int addr_len;
2039 char temp_addr_str[HL7800_IPV6_ADDR_LEN];
2040 k_timeout_t delay;
2041
2042 out_len = net_buf_linearize(value, sizeof(value), *buf, 0, len);
2043 value[out_len] = 0;
2044 search_start = value;
2045 LOG_DBG("IP info: %s", value);
2046
2047 /* find all delimiters (,) */
2048 for (int i = 0; i < num_delims; i++) {
2049 delims[i] = strchr(search_start, ',');
2050 if (!delims[i]) {
2051 LOG_ERR("Could not find delim %d, val: %s", i,
2052 value);
2053 return true;
2054 }
2055 /* Start next search after current delim location */
2056 search_start = delims[i] + 1;
2057 }
2058
2059 /* determine if IPv4 or IPv6 by checking length of ip address plus
2060 * gateway string.
2061 */
2062 is_ipv4 = false;
2063 addr_len = delims[3] - delims[2];
2064 LOG_DBG("IP string len: %d", addr_len);
2065 if (addr_len <= (NET_IPV4_ADDR_LEN * 2)) {
2066 is_ipv4 = true;
2067 }
2068
2069 /* Find start of subnet mask */
2070 addr_start = delims[2] + 1;
2071 if (is_ipv4) {
2072 num_delims = 4;
2073 } else {
2074 num_delims = 16;
2075 }
2076 search_start = addr_start;
2077 sm_start = addr_start;
2078 for (int i = 0; i < num_delims; i++) {
2079 sm_start = strchr(search_start, '.');
2080 if (!sm_start) {
2081 LOG_ERR("Could not find submask start");
2082 return true;
2083 }
2084 /* Start next search after current delim location */
2085 search_start = sm_start + 1;
2086 }
2087
2088 /* get new IP addr */
2089 addr_len = sm_start - addr_start;
2090 strncpy(temp_addr_str, addr_start, addr_len);
2091 temp_addr_str[addr_len] = 0;
2092 LOG_DBG("IP addr: %s", temp_addr_str);
2093 if (is_ipv4) {
2094 ret = net_addr_pton(AF_INET, temp_addr_str, &new_ipv4_addr);
2095 } else {
2096 ret = hl7800_net_addr6_pton(temp_addr_str, &new_ipv6_addr);
2097 }
2098 if (ret < 0) {
2099 LOG_ERR("Invalid IP addr");
2100 return true;
2101 }
2102
2103 if (is_ipv4) {
2104 /* move past the '.' */
2105 sm_start += 1;
2106 /* store new subnet mask */
2107 addr_len = delims[3] - sm_start;
2108 strncpy(temp_addr_str, sm_start, addr_len);
2109 temp_addr_str[addr_len] = 0;
2110 ret = net_addr_pton(AF_INET, temp_addr_str, &iface_ctx.subnet);
2111 if (ret < 0) {
2112 LOG_ERR("Invalid subnet");
2113 return true;
2114 }
2115
2116 /* store new gateway */
2117 addr_start = delims[3] + 1;
2118 addr_len = delims[4] - addr_start;
2119 strncpy(temp_addr_str, addr_start, addr_len);
2120 temp_addr_str[addr_len] = 0;
2121 ret = net_addr_pton(AF_INET, temp_addr_str, &iface_ctx.gateway);
2122 if (ret < 0) {
2123 LOG_ERR("Invalid gateway");
2124 return true;
2125 }
2126 }
2127
2128 /* store new dns */
2129 addr_start = delims[4] + 1;
2130 addr_len = delims[5] - addr_start;
2131 strncpy(temp_addr_str, addr_start, addr_len);
2132 temp_addr_str[addr_len] = 0;
2133 if (is_ipv4) {
2134 ret = strncmp(temp_addr_str, iface_ctx.dns_v4_string, addr_len);
2135 if (ret != 0) {
2136 iface_ctx.dns_ready = false;
2137 }
2138 strncpy(iface_ctx.dns_v4_string, addr_start, addr_len);
2139 iface_ctx.dns_v4_string[addr_len] = 0;
2140 ret = net_addr_pton(AF_INET, iface_ctx.dns_v4_string, &iface_ctx.dns_v4);
2141 LOG_DBG("IPv4 DNS addr: %s", iface_ctx.dns_v4_string);
2142 }
2143 #ifdef CONFIG_NET_IPV6
2144 else {
2145 ret = strncmp(temp_addr_str, iface_ctx.dns_v6_string, addr_len);
2146 if (ret != 0) {
2147 iface_ctx.dns_ready = false;
2148 }
2149 /* store HL7800 formatted IPv6 DNS string temporarily */
2150 strncpy(iface_ctx.dns_v6_string, addr_start, addr_len);
2151
2152 ret = hl7800_net_addr6_pton(iface_ctx.dns_v6_string, &iface_ctx.dns_v6);
2153 net_addr_ntop(AF_INET6, &iface_ctx.dns_v6, iface_ctx.dns_v6_string,
2154 sizeof(iface_ctx.dns_v6_string));
2155 LOG_DBG("IPv6 DNS addr: %s", iface_ctx.dns_v6_string);
2156 }
2157 #endif
2158 if (ret < 0) {
2159 LOG_ERR("Invalid dns");
2160 return true;
2161 }
2162
2163 if (iface_ctx.iface) {
2164 if (is_ipv4) {
2165 #ifdef CONFIG_NET_IPV4
2166 /* remove the current IPv4 addr before adding a new one.
2167 * We dont care if it is successful or not.
2168 */
2169 net_if_ipv4_addr_rm(iface_ctx.iface, &iface_ctx.ipv4Addr);
2170
2171 if (!net_if_ipv4_addr_add(iface_ctx.iface, &new_ipv4_addr,
2172 NET_ADDR_DHCP, 0)) {
2173 LOG_ERR("Cannot set iface IPv4 addr");
2174 }
2175
2176 net_if_ipv4_set_netmask(iface_ctx.iface, &iface_ctx.subnet);
2177 net_if_ipv4_set_gw(iface_ctx.iface, &iface_ctx.gateway);
2178 #endif
2179 /* store the new IP addr */
2180 net_ipaddr_copy(&iface_ctx.ipv4Addr, &new_ipv4_addr);
2181 } else {
2182 #if CONFIG_NET_IPV6
2183 net_if_ipv6_addr_rm(iface_ctx.iface, &iface_ctx.ipv6Addr);
2184 if (!net_if_ipv6_addr_add(iface_ctx.iface, &new_ipv6_addr,
2185 NET_ADDR_AUTOCONF, 0)) {
2186 LOG_ERR("Cannot set iface IPv6 addr");
2187 }
2188 #endif
2189 }
2190
2191 /* start DNS update work */
2192 delay = K_NO_WAIT;
2193 if (!iface_ctx.initialized) {
2194 /* Delay this in case the network
2195 * stack is still starting up
2196 */
2197 delay = K_SECONDS(DNS_WORK_DELAY_SECS);
2198 }
2199 k_work_reschedule_for_queue(&hl7800_workq, &iface_ctx.dns_work,
2200 delay);
2201 } else {
2202 LOG_ERR("iface NULL");
2203 }
2204
2205 return true;
2206 }
2207
2208 /* Handler1: +COPS: <mode>[,<format>,<oper>[,<AcT>]]
2209 *
2210 * Handler2:
2211 * +COPS: [list of supported (<stat>, long alphanumeric <oper>, short
2212 * alphanumeric <oper>, numeric <oper>[,< AcT>])s][,,
2213 * (list of supported <mode>s),(list of supported <format>s)]
2214 */
on_cmd_atcmdinfo_operator_status(struct net_buf ** buf,uint16_t len)2215 static bool on_cmd_atcmdinfo_operator_status(struct net_buf **buf, uint16_t len)
2216 {
2217 size_t out_len;
2218 char value[MDM_MAX_RESP_SIZE];
2219 int num_delims = COPS_RESPONSE_NUM_DELIMS;
2220 char *delims[COPS_RESPONSE_NUM_DELIMS];
2221 char *search_start;
2222 int i;
2223
2224 out_len = net_buf_linearize(value, sizeof(value), *buf, 0, len);
2225 value[out_len] = 0;
2226
2227 /* For AT+COPS=?, result is most likely longer than size of log string */
2228 if (strchr(value, '(') != NULL) {
2229 LOG_HEXDUMP_DBG(value, out_len, "Operator: ");
2230 goto done;
2231 } else {
2232 LOG_INF("Operator: %s", value);
2233 }
2234
2235 /* Process AT+COPS? */
2236 if (len == 1) {
2237 /* only mode was returned, there is no operator info */
2238 iface_ctx.operator_status = NO_OPERATOR;
2239 goto done;
2240 }
2241
2242 search_start = value;
2243
2244 /* find all delimiters (,) */
2245 for (i = 0; i < num_delims; i++) {
2246 delims[i] = strchr(search_start, ',');
2247 if (!delims[i]) {
2248 LOG_ERR("Could not find delim %d, val: %s", i, value);
2249 goto done;
2250 }
2251 /* Start next search after current delim location */
2252 search_start = delims[i] + 1;
2253 }
2254
2255 /* we found both delimiters, that means we have an operator */
2256 iface_ctx.operator_status = REGISTERED;
2257 done:
2258 return true;
2259 }
2260
2261 /* Handler: +KGSN: T5640400011101 */
on_cmd_atcmdinfo_serial_number(struct net_buf ** buf,uint16_t len)2262 static bool on_cmd_atcmdinfo_serial_number(struct net_buf **buf, uint16_t len)
2263 {
2264 struct net_buf *frag = NULL;
2265 char value[MDM_SN_RESPONSE_LENGTH];
2266 size_t out_len;
2267 int sn_len;
2268 char *val_start;
2269
2270 /* make sure SN# data is received.
2271 * we are waiting for: +KGSN: ##############\r\n
2272 */
2273 wait_for_modem_data(buf, net_buf_frags_len(*buf),
2274 MDM_SN_RESPONSE_LENGTH);
2275
2276 frag = NULL;
2277 len = net_buf_findcrlf(*buf, &frag);
2278 if (!frag) {
2279 LOG_ERR("Unable to find sn end");
2280 goto done;
2281 }
2282
2283 /* get msg data */
2284 out_len = net_buf_linearize(value, sizeof(value), *buf, 0, len);
2285 value[out_len] = 0;
2286
2287 /* find ':' */
2288 val_start = strchr(value, ':');
2289 if (!val_start) {
2290 LOG_ERR("Unable to find sn ':'");
2291 goto done;
2292 }
2293 /* Remove ": " chars */
2294 val_start += 2;
2295
2296 sn_len = len - (val_start - value);
2297 if (sn_len < MDM_HL7800_SERIAL_NUMBER_STRLEN) {
2298 LOG_WRN("sn too short (len:%d)", sn_len);
2299 } else if (sn_len > MDM_HL7800_SERIAL_NUMBER_STRLEN) {
2300 LOG_WRN("sn too long (len:%d)", sn_len);
2301 sn_len = MDM_HL7800_SERIAL_NUMBER_STRLEN;
2302 }
2303
2304 strncpy(iface_ctx.mdm_sn, val_start, sn_len);
2305 iface_ctx.mdm_sn[sn_len] = 0;
2306 LOG_INF("Serial #: %s", iface_ctx.mdm_sn);
2307 done:
2308 return true;
2309 }
2310
2311 /* Handler: +KSRAT: # */
on_cmd_radio_tech_status(struct net_buf ** buf,uint16_t len)2312 static bool on_cmd_radio_tech_status(struct net_buf **buf, uint16_t len)
2313 {
2314 size_t out_len;
2315 char value[MDM_MAX_RESP_SIZE];
2316
2317 out_len = net_buf_linearize(value, sizeof(value), *buf, 0, len);
2318 value[out_len] = 0;
2319 iface_ctx.mdm_rat = strtol(value, NULL, 10);
2320 LOG_INF("+KSRAT: %d", iface_ctx.mdm_rat);
2321 event_handler(HL7800_EVENT_RAT, &iface_ctx.mdm_rat);
2322
2323 return true;
2324 }
2325
2326 /* Handler: +KBNDCFG: #,####################### */
on_cmd_radio_band_configuration(struct net_buf ** buf,uint16_t len)2327 static bool on_cmd_radio_band_configuration(struct net_buf **buf, uint16_t len)
2328 {
2329 size_t out_len;
2330 char value[MDM_MAX_RESP_SIZE];
2331 char n_tmp[sizeof("#########")];
2332
2333 out_len = net_buf_linearize(value, sizeof(value), *buf, 0, len);
2334 value[out_len] = 0;
2335
2336 if (value[0] != (iface_ctx.mdm_rat == MDM_RAT_CAT_M1 ? '0' : '1')) {
2337 /* Invalid RAT */
2338 return true;
2339 } else if (strlen(value) < sizeof("#,###################")) {
2340 /* String size too short */
2341 return true;
2342 }
2343
2344 memcpy(iface_ctx.mdm_bands_string, &value[MDM_TOP_BAND_START_POSITION],
2345 MDM_HL7800_LTE_BAND_STRLEN);
2346
2347 memcpy(n_tmp, &value[MDM_TOP_BAND_START_POSITION], MDM_TOP_BAND_SIZE);
2348 n_tmp[MDM_TOP_BAND_SIZE] = 0;
2349 iface_ctx.mdm_bands_top = strtoul(n_tmp, NULL, 16);
2350
2351 memcpy(n_tmp, &value[MDM_MIDDLE_BAND_START_POSITION],
2352 MDM_MIDDLE_BAND_SIZE);
2353 n_tmp[MDM_MIDDLE_BAND_SIZE] = 0;
2354 iface_ctx.mdm_bands_middle = strtoul(n_tmp, NULL, 16);
2355
2356 memcpy(n_tmp, &value[MDM_BOTTOM_BAND_START_POSITION],
2357 MDM_BOTTOM_BAND_SIZE);
2358 n_tmp[MDM_BOTTOM_BAND_SIZE] = 0;
2359 iface_ctx.mdm_bands_bottom = strtoul(n_tmp, NULL, 16);
2360
2361 LOG_INF("Current band configuration: %04x %08x %08x",
2362 iface_ctx.mdm_bands_top, iface_ctx.mdm_bands_middle,
2363 iface_ctx.mdm_bands_bottom);
2364
2365 event_handler(HL7800_EVENT_BANDS, iface_ctx.mdm_bands_string);
2366
2367 return true;
2368 }
2369
2370 /* Handler: +KBND: #,####################### */
on_cmd_radio_active_bands(struct net_buf ** buf,uint16_t len)2371 static bool on_cmd_radio_active_bands(struct net_buf **buf, uint16_t len)
2372 {
2373 size_t out_len;
2374 char value[MDM_MAX_RESP_SIZE];
2375
2376 out_len = net_buf_linearize(value, sizeof(value), *buf, 0, len);
2377 value[out_len] = 0;
2378
2379 if (strlen(value) < sizeof("#,###################")) {
2380 /* String size too short */
2381 return true;
2382 }
2383
2384 memcpy(iface_ctx.mdm_active_bands_string,
2385 &value[MDM_TOP_BAND_START_POSITION], MDM_HL7800_LTE_BAND_STRLEN);
2386 event_handler(HL7800_EVENT_ACTIVE_BANDS, iface_ctx.mdm_active_bands_string);
2387
2388 return true;
2389 }
2390
get_startup_state_string(enum mdm_hl7800_startup_state state)2391 static char *get_startup_state_string(enum mdm_hl7800_startup_state state)
2392 {
2393 /* clang-format off */
2394 switch (state) {
2395 PREFIXED_SWITCH_CASE_RETURN_STRING(HL7800_STARTUP_STATE, READY);
2396 PREFIXED_SWITCH_CASE_RETURN_STRING(HL7800_STARTUP_STATE, WAITING_FOR_ACCESS_CODE);
2397 PREFIXED_SWITCH_CASE_RETURN_STRING(HL7800_STARTUP_STATE, SIM_NOT_PRESENT);
2398 PREFIXED_SWITCH_CASE_RETURN_STRING(HL7800_STARTUP_STATE, SIMLOCK);
2399 PREFIXED_SWITCH_CASE_RETURN_STRING(HL7800_STARTUP_STATE, UNRECOVERABLE_ERROR);
2400 PREFIXED_SWITCH_CASE_RETURN_STRING(HL7800_STARTUP_STATE, UNKNOWN);
2401 PREFIXED_SWITCH_CASE_RETURN_STRING(HL7800_STARTUP_STATE, INACTIVE_SIM);
2402 default:
2403 return "UNKNOWN";
2404 }
2405 /* clang-format on */
2406 }
2407
set_startup_state(enum mdm_hl7800_startup_state state)2408 static void set_startup_state(enum mdm_hl7800_startup_state state)
2409 {
2410 iface_ctx.mdm_startup_state = state;
2411 generate_startup_state_event();
2412 }
2413
generate_startup_state_event(void)2414 static void generate_startup_state_event(void)
2415 {
2416 struct mdm_hl7800_compound_event event;
2417
2418 event.code = iface_ctx.mdm_startup_state;
2419 event.string = get_startup_state_string(iface_ctx.mdm_startup_state);
2420 LOG_INF("Startup State: %s", event.string);
2421 event_handler(HL7800_EVENT_STARTUP_STATE_CHANGE, &event);
2422 }
2423
mdm_hl7800_set_desired_sleep_level(enum mdm_hl7800_sleep level)2424 int mdm_hl7800_set_desired_sleep_level(enum mdm_hl7800_sleep level)
2425 {
2426 int r = -EPERM;
2427
2428 #if CONFIG_MODEM_HL7800_LOW_POWER_MODE
2429 switch (level) {
2430 case HL7800_SLEEP_AWAKE:
2431 case HL7800_SLEEP_HIBERNATE:
2432 case HL7800_SLEEP_LITE_HIBERNATE:
2433 case HL7800_SLEEP_SLEEP:
2434 iface_ctx.desired_sleep_level = level;
2435 r = 0;
2436 break;
2437 default:
2438 r = -EINVAL;
2439 }
2440
2441 if (r == 0) {
2442 hl7800_lock();
2443 wakeup_hl7800();
2444 r = set_sleep_level();
2445 set_busy(false);
2446 allow_sleep(true);
2447 hl7800_unlock();
2448 }
2449 #endif
2450
2451 return r;
2452 }
2453
initialize_sleep_level(void)2454 static void initialize_sleep_level(void)
2455 {
2456 if (iface_ctx.desired_sleep_level == HL7800_SLEEP_UNINITIALIZED) {
2457 if (IS_ENABLED(CONFIG_MODEM_HL7800_SLEEP_LEVEL_HIBERNATE)) {
2458 iface_ctx.desired_sleep_level = HL7800_SLEEP_HIBERNATE;
2459 } else if (IS_ENABLED(CONFIG_MODEM_HL7800_SLEEP_LEVEL_LITE_HIBERNATE)) {
2460 iface_ctx.desired_sleep_level = HL7800_SLEEP_LITE_HIBERNATE;
2461 } else if (IS_ENABLED(CONFIG_MODEM_HL7800_SLEEP_LEVEL_SLEEP)) {
2462 iface_ctx.desired_sleep_level = HL7800_SLEEP_SLEEP;
2463 } else {
2464 iface_ctx.desired_sleep_level = HL7800_SLEEP_AWAKE;
2465 }
2466 }
2467 }
2468
2469 #ifdef CONFIG_MODEM_HL7800_LOW_POWER_MODE
set_sleep_level(void)2470 static int set_sleep_level(void)
2471 {
2472 char cmd[sizeof("AT+KSLEEP=#,#,##")];
2473 static const char SLEEP_CMD_FMT[] = "AT+KSLEEP=%d,%d,%d";
2474 int delay = CONFIG_MODEM_HL7800_SLEEP_DELAY_AFTER_REBOOT;
2475 int ret = 0;
2476
2477 /* AT+KSLEEP= <management>[,<level>[,<delay to sleep after reboot>]]
2478 * management 1 means the HL7800 decides when it enters sleep mode
2479 */
2480 switch (iface_ctx.desired_sleep_level) {
2481 case HL7800_SLEEP_HIBERNATE:
2482 snprintk(cmd, sizeof(cmd), SLEEP_CMD_FMT, 1, 2, delay);
2483 break;
2484 case HL7800_SLEEP_LITE_HIBERNATE:
2485 snprintk(cmd, sizeof(cmd), SLEEP_CMD_FMT, 1, 1, delay);
2486 break;
2487 case HL7800_SLEEP_SLEEP:
2488 snprintk(cmd, sizeof(cmd), SLEEP_CMD_FMT, 1, 0, delay);
2489 break;
2490 default:
2491 /* don't sleep */
2492 snprintk(cmd, sizeof(cmd), SLEEP_CMD_FMT, 2, 0, delay);
2493 break;
2494 }
2495
2496 SEND_AT_CMD_EXPECT_OK(cmd);
2497
2498 error:
2499 return ret;
2500 }
2501
2502 #endif /* CONFIG_MODEM_HL7800_LOW_POWER_MODE */
2503
get_sleep_state_string(enum mdm_hl7800_sleep state)2504 static char *get_sleep_state_string(enum mdm_hl7800_sleep state)
2505 {
2506 /* clang-format off */
2507 switch (state) {
2508 PREFIXED_SWITCH_CASE_RETURN_STRING(HL7800_SLEEP, UNINITIALIZED);
2509 PREFIXED_SWITCH_CASE_RETURN_STRING(HL7800_SLEEP, HIBERNATE);
2510 PREFIXED_SWITCH_CASE_RETURN_STRING(HL7800_SLEEP, LITE_HIBERNATE);
2511 PREFIXED_SWITCH_CASE_RETURN_STRING(HL7800_SLEEP, SLEEP);
2512 PREFIXED_SWITCH_CASE_RETURN_STRING(HL7800_SLEEP, AWAKE);
2513 default:
2514 return "UNKNOWN";
2515 }
2516 /* clang-format on */
2517 }
2518
set_sleep_state(enum mdm_hl7800_sleep state)2519 static void set_sleep_state(enum mdm_hl7800_sleep state)
2520 {
2521 iface_ctx.sleep_state = state;
2522 if (iface_ctx.sleep_state != HL7800_SLEEP_AWAKE) {
2523 k_sem_reset(&iface_ctx.mdm_awake);
2524 }
2525 generate_sleep_state_event();
2526 }
2527
generate_sleep_state_event(void)2528 static void generate_sleep_state_event(void)
2529 {
2530 struct mdm_hl7800_compound_event event;
2531
2532 event.code = iface_ctx.sleep_state;
2533 event.string = get_sleep_state_string(iface_ctx.sleep_state);
2534 LOG_INF("Sleep State: %s", event.string);
2535 event_handler(HL7800_EVENT_SLEEP_STATE_CHANGE, &event);
2536 }
2537
2538 #ifdef CONFIG_MODEM_HL7800_FW_UPDATE
get_fota_state_string(enum mdm_hl7800_fota_state state)2539 static char *get_fota_state_string(enum mdm_hl7800_fota_state state)
2540 {
2541 /* clang-format off */
2542 switch (state) {
2543 PREFIXED_SWITCH_CASE_RETURN_STRING(HL7800_FOTA, IDLE);
2544 PREFIXED_SWITCH_CASE_RETURN_STRING(HL7800_FOTA, START);
2545 PREFIXED_SWITCH_CASE_RETURN_STRING(HL7800_FOTA, WIP);
2546 PREFIXED_SWITCH_CASE_RETURN_STRING(HL7800_FOTA, PAD);
2547 PREFIXED_SWITCH_CASE_RETURN_STRING(HL7800_FOTA, SEND_EOT);
2548 PREFIXED_SWITCH_CASE_RETURN_STRING(HL7800_FOTA, FILE_ERROR);
2549 PREFIXED_SWITCH_CASE_RETURN_STRING(HL7800_FOTA, INSTALL);
2550 PREFIXED_SWITCH_CASE_RETURN_STRING(HL7800_FOTA, REBOOT_AND_RECONFIGURE);
2551 PREFIXED_SWITCH_CASE_RETURN_STRING(HL7800_FOTA, COMPLETE);
2552 default:
2553 return "UNKNOWN";
2554 }
2555 /* clang-format on */
2556 }
2557
set_fota_state(enum mdm_hl7800_fota_state state)2558 static void set_fota_state(enum mdm_hl7800_fota_state state)
2559 {
2560 LOG_INF("FOTA state: %s->%s",
2561 get_fota_state_string(iface_ctx.fw_update_state),
2562 get_fota_state_string(state));
2563 iface_ctx.fw_update_state = state;
2564 generate_fota_state_event();
2565 }
2566
generate_fota_state_event(void)2567 static void generate_fota_state_event(void)
2568 {
2569 struct mdm_hl7800_compound_event event;
2570
2571 event.code = iface_ctx.fw_update_state;
2572 event.string = get_fota_state_string(iface_ctx.fw_update_state);
2573 event_handler(HL7800_EVENT_FOTA_STATE, &event);
2574 }
2575
generate_fota_count_event(void)2576 static void generate_fota_count_event(void)
2577 {
2578 uint32_t count = iface_ctx.fw_packet_count * XMODEM_DATA_SIZE;
2579
2580 event_handler(HL7800_EVENT_FOTA_COUNT, &count);
2581 }
2582 #endif
2583
2584 /* Handler: +KSUP: # */
on_cmd_startup_report(struct net_buf ** buf,uint16_t len)2585 static bool on_cmd_startup_report(struct net_buf **buf, uint16_t len)
2586 {
2587 size_t out_len;
2588 char value[MDM_MAX_RESP_SIZE];
2589
2590 memset(value, 0, sizeof(value));
2591 out_len = net_buf_linearize(value, sizeof(value), *buf, 0, len);
2592 if (out_len > 0) {
2593 set_startup_state(strtol(value, NULL, 10));
2594 } else {
2595 set_startup_state(HL7800_STARTUP_STATE_UNKNOWN);
2596 }
2597
2598 #ifdef CONFIG_MODEM_HL7800_FW_UPDATE
2599 if (iface_ctx.fw_updated) {
2600 iface_ctx.fw_updated = false;
2601 set_fota_state(HL7800_FOTA_REBOOT_AND_RECONFIGURE);
2602 /* issue reset after a firmware update to reconfigure modem state */
2603 k_work_reschedule_for_queue(&hl7800_workq, &iface_ctx.mdm_reset_work,
2604 K_NO_WAIT);
2605 } else
2606 #endif
2607 {
2608 PRINT_AWAKE_MSG;
2609 iface_ctx.wait_for_KSUP = false;
2610 iface_ctx.mdm_startup_reporting_on = true;
2611 iface_ctx.reconfig_IP_connection = true;
2612 #ifdef CONFIG_MODEM_HL7800_LOW_POWER_MODE
2613 mark_sockets_for_reconfig();
2614 #endif
2615 set_sleep_state(HL7800_SLEEP_AWAKE);
2616 k_sem_give(&iface_ctx.mdm_awake);
2617 }
2618
2619 return true;
2620 }
2621
profile_handler(struct net_buf ** buf,uint16_t len,bool active_profile)2622 static bool profile_handler(struct net_buf **buf, uint16_t len,
2623 bool active_profile)
2624 {
2625 uint32_t size;
2626 int echo_state = -1;
2627 struct net_buf *frag = NULL;
2628 uint16_t line_length;
2629 char line[MAX_PROFILE_LINE_LENGTH];
2630 size_t output_length;
2631
2632 /* Prepare net buffer for this parser. */
2633 net_buf_remove(buf, len);
2634 net_buf_skipcrlf(buf);
2635
2636 size = wait_for_modem_data(buf, net_buf_frags_len(*buf),
2637 sizeof(PROFILE_LINE_1));
2638 net_buf_skipcrlf(buf); /* remove any \r\n that are in the front */
2639
2640 /* Parse configuration data to determine if echo is on/off. */
2641 line_length = net_buf_findcrlf(*buf, &frag);
2642 if (line_length) {
2643 memset(line, 0, sizeof(line));
2644 output_length = net_buf_linearize(line, SIZE_WITHOUT_NUL(line),
2645 *buf, 0, line_length);
2646 LOG_DBG("length: %u: %s", line_length, line);
2647
2648 /* Echo on off is the first thing on the line: E0, E1 */
2649 if (output_length >= SIZE_WITHOUT_NUL("E?")) {
2650 echo_state = (line[1] == '1') ? 1 : 0;
2651 }
2652 }
2653 LOG_DBG("echo: %d", echo_state);
2654 net_buf_remove(buf, line_length);
2655 net_buf_skipcrlf(buf);
2656
2657 if (active_profile) {
2658 iface_ctx.mdm_echo_is_on = (echo_state != 0);
2659 }
2660
2661 /* Discard next line. This waits for the longest possible response even
2662 * though most registers won't have the value 0xFF. */
2663 size = wait_for_modem_data(buf, net_buf_frags_len(*buf),
2664 sizeof(PROFILE_LINE_2));
2665 net_buf_skipcrlf(buf);
2666 len = net_buf_findcrlf(*buf, &frag);
2667 net_buf_remove(buf, len);
2668 net_buf_skipcrlf(buf);
2669
2670 return false;
2671 }
2672
on_cmd_atcmdinfo_active_profile(struct net_buf ** buf,uint16_t len)2673 static bool on_cmd_atcmdinfo_active_profile(struct net_buf **buf, uint16_t len)
2674 {
2675 return profile_handler(buf, len, true);
2676 }
2677
on_cmd_atcmdinfo_stored_profile0(struct net_buf ** buf,uint16_t len)2678 static bool on_cmd_atcmdinfo_stored_profile0(struct net_buf **buf, uint16_t len)
2679 {
2680 return profile_handler(buf, len, false);
2681 }
2682
on_cmd_atcmdinfo_stored_profile1(struct net_buf ** buf,uint16_t len)2683 static bool on_cmd_atcmdinfo_stored_profile1(struct net_buf **buf, uint16_t len)
2684 {
2685 return profile_handler(buf, len, false);
2686 }
2687
2688 /* +WPPP: 1,1,"username","password" */
on_cmd_atcmdinfo_pdp_authentication_cfg(struct net_buf ** buf,uint16_t len)2689 static bool on_cmd_atcmdinfo_pdp_authentication_cfg(struct net_buf **buf,
2690 uint16_t len)
2691 {
2692 struct net_buf *frag = NULL;
2693 uint16_t line_length;
2694 char line[MDM_HL7800_APN_CMD_MAX_SIZE];
2695 size_t output_length;
2696 size_t i;
2697 char *p;
2698
2699 wait_for_modem_data_and_newline(buf, net_buf_frags_len(*buf),
2700 MDM_HL7800_APN_CMD_MAX_SIZE);
2701
2702 line_length = net_buf_findcrlf(*buf, &frag);
2703 if (line_length) {
2704 memset(line, 0, sizeof(line));
2705 output_length = net_buf_linearize(line, SIZE_WITHOUT_NUL(line),
2706 *buf, 0, line_length);
2707 LOG_DBG("length: %u: %s", line_length, line);
2708 if (output_length > 0) {
2709 memset(iface_ctx.mdm_apn.username, 0,
2710 sizeof(iface_ctx.mdm_apn.username));
2711 memset(iface_ctx.mdm_apn.password, 0,
2712 sizeof(iface_ctx.mdm_apn.password));
2713
2714 i = 0;
2715 p = strchr(line, '"');
2716 if (p != NULL) {
2717 p += 1;
2718 i = 0;
2719 while ((p != NULL) && (*p != '"') &&
2720 (i <
2721 MDM_HL7800_APN_USERNAME_MAX_STRLEN)) {
2722 iface_ctx.mdm_apn.username[i++] = *p++;
2723 }
2724 } else {
2725 LOG_WRN("Issue parsing APN username");
2726 goto done;
2727 }
2728 LOG_INF("APN Username: %s",
2729 iface_ctx.mdm_apn.username);
2730
2731 p = strchr(p + 1, '"');
2732 if (p != NULL) {
2733 p += 1;
2734 i = 0;
2735 while ((p != NULL) && (*p != '"') &&
2736 (i <
2737 MDM_HL7800_APN_PASSWORD_MAX_STRLEN)) {
2738 iface_ctx.mdm_apn.password[i++] = *p++;
2739 }
2740 }
2741 LOG_INF("APN Password: %s",
2742 iface_ctx.mdm_apn.password);
2743 }
2744 }
2745 done:
2746 net_buf_remove(buf, line_length);
2747 net_buf_skipcrlf(buf);
2748
2749 return false;
2750 }
2751
2752 /* Only context 1 is used. Other contexts are unhandled.
2753 *
2754 * +CGDCONT: 1,"IP","access point name",,0,0,0,0,0,,0,,,,,
2755 */
on_cmd_atcmdinfo_pdp_context(struct net_buf ** buf,uint16_t len)2756 static bool on_cmd_atcmdinfo_pdp_context(struct net_buf **buf, uint16_t len)
2757 {
2758 struct net_buf *frag = NULL;
2759 uint16_t line_length;
2760 char line[MDM_HL7800_APN_CMD_MAX_SIZE];
2761 size_t output_length;
2762 char *p;
2763 size_t i;
2764
2765 wait_for_modem_data_and_newline(buf, net_buf_frags_len(*buf),
2766 MDM_HL7800_APN_CMD_MAX_SIZE);
2767
2768 line_length = net_buf_findcrlf(*buf, &frag);
2769 if (line_length) {
2770 memset(line, 0, sizeof(line));
2771 output_length = net_buf_linearize(line, SIZE_WITHOUT_NUL(line),
2772 *buf, 0, line_length);
2773 LOG_DBG("length: %u: %s", line_length, line);
2774 if (output_length > 0) {
2775 memset(iface_ctx.mdm_apn.value, 0, sizeof(iface_ctx.mdm_apn.value));
2776 memset(iface_ctx.mdm_pdp_addr_fam, 0, MDM_ADDR_FAM_MAX_LEN);
2777
2778 /* Address family after first , */
2779 p = strchr(line, ',');
2780 if (p == NULL) {
2781 LOG_WRN("Issue parsing APN response");
2782 goto done;
2783 }
2784 p += 2;
2785 i = 0;
2786 while ((p != NULL) && (*p != '"') && (i < MDM_ADDR_FAM_MAX_LEN)) {
2787 iface_ctx.mdm_pdp_addr_fam[i++] = *p++;
2788 }
2789
2790 if (strcmp(iface_ctx.mdm_pdp_addr_fam, ADDRESS_FAMILY_IP) == 0) {
2791 snprintk(iface_ctx.mdm_pdp_addr_fam,
2792 sizeof(iface_ctx.mdm_pdp_addr_fam), "%s",
2793 ADDRESS_FAMILY_IPV4);
2794 }
2795
2796 LOG_DBG("PDP address family: %s", iface_ctx.mdm_pdp_addr_fam);
2797
2798 /* APN after second , " */
2799 p = strchr(p, ',');
2800 if (p == NULL) {
2801 LOG_WRN("Issue parsing APN response");
2802 goto done;
2803 }
2804 p++;
2805 if (*p == ',') {
2806 /* APN is blank */
2807 goto done;
2808 }
2809 if (*p == '"') {
2810 p++;
2811 i = 0;
2812 while ((p != NULL) && (*p != '"') &&
2813 (i < MDM_HL7800_APN_MAX_STRLEN)) {
2814 iface_ctx.mdm_apn.value[i++] = *p++;
2815 }
2816 }
2817
2818 LOG_INF("APN: %s", iface_ctx.mdm_apn.value);
2819 }
2820 }
2821 done:
2822 net_buf_remove(buf, line_length);
2823 net_buf_skipcrlf(buf);
2824
2825 return false;
2826 }
2827
hl7800_query_rssi(void)2828 static int hl7800_query_rssi(void)
2829 {
2830 int ret;
2831
2832 ret = send_at_cmd(NULL, "AT+KCELLMEAS=0", MDM_CMD_SEND_TIMEOUT, 1,
2833 false);
2834 if (ret < 0) {
2835 LOG_ERR("AT+KCELLMEAS ret:%d", ret);
2836 }
2837 return ret;
2838 }
2839
hl7800_start_rssi_work(void)2840 static void hl7800_start_rssi_work(void)
2841 {
2842 /* Rate is not checked here to allow one reading
2843 * when going from network down->up
2844 */
2845 k_work_reschedule_for_queue(&hl7800_workq, &iface_ctx.rssi_query_work,
2846 K_NO_WAIT);
2847 }
2848
hl7800_stop_rssi_work(void)2849 static void hl7800_stop_rssi_work(void)
2850 {
2851 int rc;
2852
2853 rc = k_work_cancel_delayable(&iface_ctx.rssi_query_work);
2854 if (rc != 0) {
2855 LOG_ERR("Could not cancel RSSI work [%d]", rc);
2856 }
2857 }
2858
rssi_query(void)2859 static void rssi_query(void)
2860 {
2861 hl7800_lock();
2862 wakeup_hl7800();
2863 hl7800_query_rssi();
2864 set_busy(false);
2865 allow_sleep(true);
2866 hl7800_unlock();
2867 }
2868
hl7800_rssi_query_work(struct k_work * work)2869 static void hl7800_rssi_query_work(struct k_work *work)
2870 {
2871 rssi_query();
2872
2873 /* re-start RSSI query work */
2874 if (CONFIG_MODEM_HL7800_RSSI_RATE_SECONDS > 0) {
2875 k_work_reschedule_for_queue(&hl7800_workq, &iface_ctx.rssi_query_work,
2876 K_SECONDS(CONFIG_MODEM_HL7800_RSSI_RATE_SECONDS));
2877 }
2878 }
2879
2880 #ifdef CONFIG_MODEM_HL7800_GPS
2881 /* Unsolicited notification
2882 * Handler: +GNSSEV: <eventType>,<eventStatus>
2883 */
on_cmd_gps_event(struct net_buf ** buf,uint16_t len)2884 static bool on_cmd_gps_event(struct net_buf **buf, uint16_t len)
2885 {
2886 size_t out_len;
2887 char value[MDM_MAX_RESP_SIZE];
2888 char *start = NULL;
2889 char *end = NULL;
2890 int8_t event = -1;
2891 int8_t status = -1;
2892
2893 memset(value, 0, sizeof(value));
2894 out_len = net_buf_linearize(value, sizeof(value), *buf, 0, len);
2895 if (out_len > 0) {
2896 start = value;
2897 event = strtol(start, &end, 10);
2898 if (end == strchr(value, ',')) {
2899 start = end + 1;
2900 status = strtol(start, &end, 10);
2901 }
2902 }
2903
2904 LOG_INF("GPS event: %d status: %d", event, status);
2905
2906 if (event == HL7800_GNSS_EVENT_POSITION) {
2907 event_handler(HL7800_EVENT_GPS_POSITION_STATUS, &status);
2908 }
2909
2910 return true;
2911 }
2912
gps_work_callback(struct k_work * work)2913 static void gps_work_callback(struct k_work *work)
2914 {
2915 ARG_UNUSED(work);
2916 int r;
2917
2918 hl7800_lock();
2919 wakeup_hl7800();
2920 r = send_at_cmd(NULL, "AT+GNSSLOC?", MDM_CMD_SEND_TIMEOUT, 1, false);
2921 set_busy(false);
2922 allow_sleep(true);
2923 hl7800_unlock();
2924
2925 LOG_DBG("GPS location request status: %d", r);
2926
2927 if (iface_ctx.gps_query_location_rate_seconds) {
2928 k_work_reschedule_for_queue(&hl7800_workq, &iface_ctx.gps_work,
2929 K_SECONDS(iface_ctx.gps_query_location_rate_seconds));
2930 }
2931 }
2932
2933 /* The AT+GNSSLOC? command returns 1 of 2 things:
2934 *
2935 * +GNSSLOC:
2936 * Latitude: "49 Deg 10 Min 21.49 Sec N"
2937 * Longitude: "123 Deg 4 Min 14.76 Sec W"
2938 * GpsTime: "yyyy mm dd hh:mm:ss"
2939 * FixType: "2D" or "3D"
2940 * HEPE: "8.485 m" (Horizontal Estimated Position Error)
2941 * Altitude: "-1 m"
2942 * AltUnc: "3.0 m"
2943 * Direction: "0.0 deg"
2944 * HorSpeed: "0.0 m/s"
2945 * VerSpeed: "0.0 m/s"
2946 * OK
2947 *
2948 * OR
2949 *
2950 * +GNSSLOC:
2951 * FIX NOT AVAILABLE
2952 * OK
2953 *
2954 * Since each response is on its own line, the command handler is used
2955 * to handle each one as an individual response.
2956 */
gps_handler(struct net_buf ** buf,uint16_t len,enum mdm_hl7800_gps_string_types str_type)2957 static bool gps_handler(struct net_buf **buf, uint16_t len,
2958 enum mdm_hl7800_gps_string_types str_type)
2959 {
2960 struct mdm_hl7800_compound_event event;
2961 char gps_str[MDM_HL7800_MAX_GPS_STR_SIZE];
2962 size_t gps_len = sizeof(gps_str) - 1;
2963 struct net_buf *frag = NULL;
2964 size_t out_len;
2965
2966 wait_for_modem_data_and_newline(buf, net_buf_frags_len(*buf), sizeof(gps_str));
2967
2968 frag = NULL;
2969 len = net_buf_findcrlf(*buf, &frag);
2970 if (!frag) {
2971 LOG_ERR("Unable to find end");
2972 goto done;
2973 }
2974
2975 if (len > gps_len) {
2976 LOG_WRN("GPS string too long (len:%d)", len);
2977 len = gps_len;
2978 }
2979
2980 out_len = net_buf_linearize(gps_str, gps_len, *buf, 0, len);
2981 gps_str[out_len] = 0;
2982
2983 event.code = str_type;
2984 event.string = gps_str;
2985 event_handler(HL7800_EVENT_GPS, &event);
2986 done:
2987 return true;
2988 }
2989
on_cmd_latitude(struct net_buf ** buf,uint16_t len)2990 static bool on_cmd_latitude(struct net_buf **buf, uint16_t len)
2991 {
2992 return gps_handler(buf, len, HL7800_GPS_STR_LATITUDE);
2993 }
2994
on_cmd_longitude(struct net_buf ** buf,uint16_t len)2995 static bool on_cmd_longitude(struct net_buf **buf, uint16_t len)
2996 {
2997 return gps_handler(buf, len, HL7800_GPS_STR_LONGITUDE);
2998 }
2999
on_cmd_gps_time(struct net_buf ** buf,uint16_t len)3000 static bool on_cmd_gps_time(struct net_buf **buf, uint16_t len)
3001 {
3002 return gps_handler(buf, len, HL7800_GPS_STR_GPS_TIME);
3003 }
3004
on_cmd_fix_type(struct net_buf ** buf,uint16_t len)3005 static bool on_cmd_fix_type(struct net_buf **buf, uint16_t len)
3006 {
3007 return gps_handler(buf, len, HL7800_GPS_STR_FIX_TYPE);
3008 }
3009
on_cmd_hepe(struct net_buf ** buf,uint16_t len)3010 static bool on_cmd_hepe(struct net_buf **buf, uint16_t len)
3011 {
3012 return gps_handler(buf, len, HL7800_GPS_STR_HEPE);
3013 }
3014
on_cmd_altitude(struct net_buf ** buf,uint16_t len)3015 static bool on_cmd_altitude(struct net_buf **buf, uint16_t len)
3016 {
3017 return gps_handler(buf, len, HL7800_GPS_STR_ALTITUDE);
3018 }
3019
on_cmd_alt_unc(struct net_buf ** buf,uint16_t len)3020 static bool on_cmd_alt_unc(struct net_buf **buf, uint16_t len)
3021 {
3022 return gps_handler(buf, len, HL7800_GPS_STR_ALT_UNC);
3023 }
3024
on_cmd_direction(struct net_buf ** buf,uint16_t len)3025 static bool on_cmd_direction(struct net_buf **buf, uint16_t len)
3026 {
3027 return gps_handler(buf, len, HL7800_GPS_STR_DIRECTION);
3028 }
3029
on_cmd_hor_speed(struct net_buf ** buf,uint16_t len)3030 static bool on_cmd_hor_speed(struct net_buf **buf, uint16_t len)
3031 {
3032 return gps_handler(buf, len, HL7800_GPS_STR_HOR_SPEED);
3033 }
3034
on_cmd_ver_speed(struct net_buf ** buf,uint16_t len)3035 static bool on_cmd_ver_speed(struct net_buf **buf, uint16_t len)
3036 {
3037 return gps_handler(buf, len, HL7800_GPS_STR_VER_SPEED);
3038 }
3039 #endif /* CONFIG_MODEM_HL7800_GPS */
3040
3041 #ifdef CONFIG_MODEM_HL7800_POLTE
3042 /* Handler: %POLTEEVU: "REGISTER",0, <mqttAuthUser>, <mqttAuthPassword> */
on_cmd_polte_registration(struct net_buf ** buf,uint16_t len)3043 static bool on_cmd_polte_registration(struct net_buf **buf, uint16_t len)
3044 {
3045 char rsp[MDM_MAX_RESP_SIZE] = { 0 };
3046 size_t rsp_len = sizeof(rsp) - 1;
3047 char *rsp_end = rsp + rsp_len;
3048 struct mdm_hl7800_polte_registration_event_data data;
3049 struct net_buf *frag = NULL;
3050 size_t out_len;
3051 char *location;
3052 bool parsed;
3053
3054 memset(&data, 0, sizeof(data));
3055
3056 wait_for_modem_data_and_newline(buf, net_buf_frags_len(*buf), sizeof(rsp));
3057
3058 location = rsp;
3059 parsed = false;
3060 frag = NULL;
3061 len = net_buf_findcrlf(*buf, &frag);
3062 do {
3063 if (!frag) {
3064 LOG_ERR("Unable to find end");
3065 break;
3066 }
3067
3068 if (len > rsp_len) {
3069 LOG_WRN("string too long (len:%d)", len);
3070 len = rsp_len;
3071 }
3072
3073 out_len = net_buf_linearize(rsp, rsp_len, *buf, 0, len);
3074 rsp[out_len] = 0;
3075
3076 /* Command handler looks for string up to the user field */
3077 location = strstr(location, "\"");
3078 if (location != NULL && location < rsp_end) {
3079 location += 1;
3080 if (location >= rsp_end) {
3081 break;
3082 }
3083 data.user = location;
3084 } else {
3085 break;
3086 }
3087
3088 /* Find end of user field and null terminate string */
3089 location = strstr(location, "\"");
3090 if (location != NULL && location < rsp_end) {
3091 *location = 0;
3092 location += 1;
3093 if (location >= rsp_end) {
3094 break;
3095 }
3096 } else {
3097 break;
3098 }
3099
3100 location = strstr(location, ",\"");
3101 if (location != NULL && location < rsp_end) {
3102 location += 2;
3103 if (location >= rsp_end) {
3104 break;
3105 }
3106 data.password = location;
3107
3108 } else {
3109 break;
3110 }
3111
3112 location = strstr(location, "\"");
3113 if (location != NULL && location < rsp_end) {
3114 *location = 0;
3115 } else {
3116 break;
3117 }
3118 parsed = true;
3119 } while (false);
3120
3121 if (parsed && data.user && data.password) {
3122 data.status = 0;
3123 } else {
3124 data.status = -1;
3125 LOG_ERR("Unable to parse PoLTE registration");
3126 }
3127
3128 event_handler(HL7800_EVENT_POLTE_REGISTRATION, &data);
3129
3130 return true;
3131 }
3132
3133 /* Handler: %POLTECMD: "LOCATE",<res> */
on_cmd_polte_locate_cmd_rsp(struct net_buf ** buf,uint16_t len)3134 static bool on_cmd_polte_locate_cmd_rsp(struct net_buf **buf, uint16_t len)
3135 {
3136 char rsp[sizeof("99")] = { 0 };
3137 size_t rsp_len = sizeof(rsp) - 1;
3138 size_t out_len;
3139 struct net_buf *frag = NULL;
3140 struct mdm_hl7800_polte_location_data data;
3141
3142 memset(&data, 0, sizeof(data));
3143
3144 wait_for_modem_data_and_newline(buf, net_buf_frags_len(*buf), sizeof(rsp));
3145
3146 data.status = -1;
3147 frag = NULL;
3148 len = net_buf_findcrlf(*buf, &frag);
3149 do {
3150 if (!frag) {
3151 LOG_ERR("Unable to find end");
3152 break;
3153 }
3154
3155 if (len > rsp_len) {
3156 LOG_WRN("string too long (len:%d)", len);
3157 len = rsp_len;
3158 }
3159
3160 out_len = net_buf_linearize(rsp, rsp_len, *buf, 0, len);
3161 rsp[out_len] = 0;
3162
3163 data.status = (uint32_t)strtoul(rsp, NULL, 10);
3164 } while (false);
3165
3166 event_handler(HL7800_EVENT_POLTE_LOCATE_STATUS, &data);
3167
3168 return true;
3169 }
3170
3171 /* Handler:
3172 * %POLTEEVU: "LOCATION",<stat>[,<latitude>,<longitude>,<time>,<confidence>]
3173 */
on_cmd_polte_location(struct net_buf ** buf,uint16_t len)3174 static bool on_cmd_polte_location(struct net_buf **buf, uint16_t len)
3175 {
3176 char rsp[MDM_MAX_RESP_SIZE] = { 0 };
3177 size_t rsp_len = sizeof(rsp) - 1;
3178 char *rsp_end = rsp + rsp_len;
3179 struct net_buf *frag = NULL;
3180 size_t out_len = 0;
3181 char *start;
3182 char *end;
3183 bool parsed;
3184 struct mdm_hl7800_polte_location_data data;
3185 static const char POLTE_LOC_DELIMITER[] = "\",\"";
3186
3187 memset(&data, 0, sizeof(data));
3188
3189 wait_for_modem_data_and_newline(buf, net_buf_frags_len(*buf), sizeof(rsp));
3190
3191 parsed = false;
3192 frag = NULL;
3193 len = net_buf_findcrlf(*buf, &frag);
3194 do {
3195 if (!frag) {
3196 LOG_ERR("Unable to find end");
3197 break;
3198 }
3199
3200 if (len > rsp_len) {
3201 LOG_WRN("string too long (len:%d)", len);
3202 len = rsp_len;
3203 }
3204
3205 out_len = net_buf_linearize(rsp, rsp_len, *buf, 0, len);
3206 rsp[out_len] = 0;
3207
3208 data.status = -1;
3209 start = rsp;
3210 end = "";
3211 /* Comma isn't present when there is an error. */
3212 start = strstr(start, ",");
3213 if (start != NULL && start < rsp_end) {
3214 *start = ' ';
3215 start += 1;
3216 }
3217 data.status = (uint32_t)strtoul(rsp, &end, 10);
3218 if (data.status != 0) {
3219 LOG_WRN("Response not received from PoLTE server: %d", data.status);
3220 data.status = MDM_HL7800_POLTE_SERVER_ERROR;
3221 parsed = true;
3222 break;
3223 } else if (start >= rsp_end) {
3224 break;
3225 }
3226
3227 start = strstr(start, "\"") + 1;
3228 end = strstr(start, POLTE_LOC_DELIMITER);
3229 if (start > rsp && start < rsp_end && end < rsp_end && end > start) {
3230 memcpy(data.latitude, start, MIN(end - start, sizeof(data.latitude) - 1));
3231 } else {
3232 break;
3233 }
3234
3235 start = end + strlen(POLTE_LOC_DELIMITER);
3236 end = strstr(start, POLTE_LOC_DELIMITER);
3237 if (start > rsp && start < rsp_end && end < rsp_end && end > start) {
3238 memcpy(data.longitude, start, MIN(end - start, sizeof(data.longitude) - 1));
3239 } else {
3240 break;
3241 }
3242
3243 start = end + strlen(POLTE_LOC_DELIMITER);
3244 end = strstr(start, POLTE_LOC_DELIMITER);
3245 if (start > rsp && start < rsp_end && end < rsp_end && end > start) {
3246 data.timestamp = (uint32_t)strtoul(start, NULL, 10);
3247 } else {
3248 break;
3249 }
3250
3251 start = end + strlen(POLTE_LOC_DELIMITER);
3252 end = strstr(start, "\"");
3253 if (start > rsp && start < rsp_end && end < rsp_end && end > start) {
3254 memcpy(data.confidence_in_meters, start,
3255 MIN(end - start, sizeof(data.confidence_in_meters) - 1));
3256 } else {
3257 break;
3258 }
3259
3260 parsed = true;
3261 } while (false);
3262
3263 if (!parsed) {
3264 LOG_HEXDUMP_ERR(rsp, out_len, "Unable to parse PoLTE location");
3265 } else {
3266 LOG_HEXDUMP_DBG(rsp, out_len, "PoLTE Location");
3267 }
3268
3269 event_handler(HL7800_EVENT_POLTE, &data);
3270
3271 return true;
3272 }
3273 #endif /* CONFIG_MODEM_HL7800_POLTE */
3274
notify_all_tcp_sockets_closed(void)3275 static void notify_all_tcp_sockets_closed(void)
3276 {
3277 int i;
3278 struct hl7800_socket *sock = NULL;
3279
3280 for (i = 0; i < MDM_MAX_SOCKETS; i++) {
3281 sock = &iface_ctx.sockets[i];
3282 if ((sock->context != NULL) && (sock->type == SOCK_STREAM)) {
3283 LOG_DBG("Sock %d closed", sock->socket_id);
3284 /* signal RX callback with null packet */
3285 if (sock->recv_cb) {
3286 sock->recv_cb(sock->context, sock->recv_pkt,
3287 NULL, NULL, 0,
3288 sock->recv_user_data);
3289 }
3290 }
3291 }
3292 }
3293
iface_status_work_cb(struct k_work * work)3294 static void iface_status_work_cb(struct k_work *work)
3295 {
3296 int ret;
3297 hl7800_lock();
3298 enum mdm_hl7800_network_state state;
3299
3300 if (iface_ctx.off) {
3301 goto done;
3302 } else if (!iface_ctx.initialized && iface_ctx.restarting) {
3303 LOG_DBG("Wait for driver init, process network state later");
3304 /* we are not ready to process this yet, try again later */
3305 k_work_reschedule_for_queue(&hl7800_workq,
3306 &iface_ctx.iface_status_work,
3307 IFACE_WORK_DELAY);
3308 goto done;
3309 } else if (iface_ctx.wait_for_KSUP &&
3310 iface_ctx.wait_for_KSUP_tries < WAIT_FOR_KSUP_RETRIES) {
3311 LOG_DBG("Wait for +KSUP before updating network state");
3312 iface_ctx.wait_for_KSUP_tries++;
3313 /* we have not received +KSUP yet, lets wait more time to receive +KSUP */
3314 k_work_reschedule_for_queue(&hl7800_workq,
3315 &iface_ctx.iface_status_work,
3316 IFACE_WORK_DELAY);
3317 goto done;
3318 } else if (iface_ctx.wait_for_KSUP &&
3319 iface_ctx.wait_for_KSUP_tries >= WAIT_FOR_KSUP_RETRIES) {
3320 /* give up waiting for KSUP */
3321 LOG_DBG("Give up waiting for");
3322 iface_ctx.wait_for_KSUP = false;
3323 check_hl7800_awake();
3324 }
3325
3326 wakeup_hl7800();
3327
3328 LOG_DBG("Updating network state...");
3329
3330 state = iface_ctx.network_state;
3331 /* Ensure we bring the network interface down and then re-check the current state */
3332 if (iface_ctx.network_dropped) {
3333 iface_ctx.network_dropped = false;
3334 state = HL7800_OUT_OF_COVERAGE;
3335 k_work_reschedule_for_queue(&hl7800_workq, &iface_ctx.iface_status_work,
3336 IFACE_WORK_DELAY);
3337 }
3338
3339 /* Query operator selection */
3340 ret = send_at_cmd(NULL, "AT+COPS?", MDM_CMD_SEND_TIMEOUT, 0, false);
3341 if (ret < 0) {
3342 LOG_ERR("AT+COPS ret:%d", ret);
3343 }
3344
3345 /* bring iface up/down */
3346 switch (state) {
3347 case HL7800_HOME_NETWORK:
3348 case HL7800_ROAMING:
3349 if (iface_ctx.iface) {
3350 LOG_DBG("HL7800 iface UP");
3351 net_if_carrier_on(iface_ctx.iface);
3352 }
3353 break;
3354 case HL7800_OUT_OF_COVERAGE:
3355 default:
3356 if (iface_ctx.iface && (iface_ctx.low_power_mode != HL7800_LPM_PSM)) {
3357 LOG_DBG("HL7800 iface DOWN");
3358 iface_ctx.dns_ready = false;
3359 net_if_carrier_off(iface_ctx.iface);
3360 }
3361 break;
3362 }
3363
3364 if ((iface_ctx.iface && !net_if_is_up(iface_ctx.iface)) ||
3365 (iface_ctx.low_power_mode == HL7800_LPM_PSM && state == HL7800_OUT_OF_COVERAGE)) {
3366 hl7800_stop_rssi_work();
3367 notify_all_tcp_sockets_closed();
3368 } else if (iface_ctx.iface && net_if_is_up(iface_ctx.iface)) {
3369 hl7800_start_rssi_work();
3370 /* get IP address info */
3371 (void)send_at_cmd(NULL, "AT+CGCONTRDP=1", MDM_CMD_SEND_TIMEOUT,
3372 CONFIG_MODEM_HL7800_GET_IP_ADDR_INFO_ATTEMPTS, false);
3373 /* get active bands */
3374 SEND_AT_CMD_IGNORE_ERROR("AT+KBND?");
3375 }
3376 LOG_DBG("Network state updated");
3377 set_busy(false);
3378 allow_sleep(true);
3379 done:
3380 hl7800_unlock();
3381 }
3382
get_network_state_string(enum mdm_hl7800_network_state state)3383 static char *get_network_state_string(enum mdm_hl7800_network_state state)
3384 {
3385 switch (state) {
3386 PREFIXED_SWITCH_CASE_RETURN_STRING(HL7800, NOT_REGISTERED);
3387 PREFIXED_SWITCH_CASE_RETURN_STRING(HL7800, HOME_NETWORK);
3388 PREFIXED_SWITCH_CASE_RETURN_STRING(HL7800, SEARCHING);
3389 PREFIXED_SWITCH_CASE_RETURN_STRING(HL7800, REGISTRATION_DENIED);
3390 PREFIXED_SWITCH_CASE_RETURN_STRING(HL7800, OUT_OF_COVERAGE);
3391 PREFIXED_SWITCH_CASE_RETURN_STRING(HL7800, ROAMING);
3392 PREFIXED_SWITCH_CASE_RETURN_STRING(HL7800, EMERGENCY);
3393 PREFIXED_SWITCH_CASE_RETURN_STRING(HL7800, UNABLE_TO_CONFIGURE);
3394 default:
3395 return "UNKNOWN";
3396 }
3397 }
3398
set_network_state(enum mdm_hl7800_network_state state)3399 static void set_network_state(enum mdm_hl7800_network_state state)
3400 {
3401 iface_ctx.network_state = state;
3402 generate_network_state_event();
3403 }
3404
generate_network_state_event(void)3405 static void generate_network_state_event(void)
3406 {
3407 struct mdm_hl7800_compound_event event;
3408
3409 event.code = iface_ctx.network_state;
3410 event.string = get_network_state_string(iface_ctx.network_state);
3411 LOG_INF("Network State: %d %s", iface_ctx.network_state, event.string);
3412 event_handler(HL7800_EVENT_NETWORK_STATE_CHANGE, &event);
3413 }
3414
3415 /* Handler: +CEREG: <n>,<stat>[,[<lac>],[<ci>],[<AcT>]
3416 * [,[<cause_type>],[<reject_cause>] [,[<Active-Time>],[<Periodic-TAU>]]]]
3417 */
on_cmd_network_report_query(struct net_buf ** buf,uint16_t len)3418 static bool on_cmd_network_report_query(struct net_buf **buf, uint16_t len)
3419 {
3420 size_t out_len;
3421 char value[MDM_MAX_RESP_SIZE];
3422 char *pos;
3423 int l;
3424 char val[MDM_MAX_RESP_SIZE];
3425
3426 out_len = net_buf_linearize(value, sizeof(value), *buf, 0, len);
3427 pos = strchr(value, ',');
3428 if (pos) {
3429 l = (value + out_len) - pos;
3430 strncpy(val, pos + 1, l);
3431 val[l] = 0;
3432 set_network_state(strtol(val, NULL, 0));
3433
3434 /* start work to adjust iface */
3435 k_work_reschedule_for_queue(&hl7800_workq,
3436 &iface_ctx.iface_status_work,
3437 IFACE_WORK_DELAY);
3438 }
3439
3440 return true;
3441 }
3442
on_cmd_operator_index_query(struct net_buf ** buf,uint16_t len)3443 static bool on_cmd_operator_index_query(struct net_buf **buf, uint16_t len)
3444 {
3445 struct net_buf *frag = NULL;
3446 char carrier[MDM_HL7800_OPERATOR_INDEX_SIZE];
3447 size_t out_len;
3448
3449 wait_for_modem_data_and_newline(buf, net_buf_frags_len(*buf),
3450 MDM_HL7800_OPERATOR_INDEX_SIZE);
3451
3452 frag = NULL;
3453 len = net_buf_findcrlf(*buf, &frag);
3454 if (!frag) {
3455 LOG_ERR("Unable to find end of operator index response");
3456 goto done;
3457 }
3458
3459 out_len = net_buf_linearize(carrier, MDM_HL7800_OPERATOR_INDEX_STRLEN,
3460 *buf, 0, len);
3461 carrier[out_len] = 0;
3462 iface_ctx.operator_index = (uint8_t)strtol(carrier, NULL, 10);
3463
3464 LOG_INF("Operator Index: %u", iface_ctx.operator_index);
3465 done:
3466 return true;
3467 }
3468
on_cmd_modem_functionality(struct net_buf ** buf,uint16_t len)3469 static bool on_cmd_modem_functionality(struct net_buf **buf, uint16_t len)
3470 {
3471 struct net_buf *frag;
3472 size_t out_len;
3473 char rsp[MDM_HL7800_MODEM_FUNCTIONALITY_SIZE];
3474
3475 wait_for_modem_data_and_newline(buf, net_buf_frags_len(*buf),
3476 MDM_HL7800_MODEM_FUNCTIONALITY_SIZE);
3477
3478 frag = NULL;
3479 len = net_buf_findcrlf(*buf, &frag);
3480 if (!frag) {
3481 LOG_ERR("Unable to find end of response");
3482 goto done;
3483 }
3484
3485 out_len = net_buf_linearize(rsp, MDM_HL7800_MODEM_FUNCTIONALITY_STRLEN,
3486 *buf, 0, len);
3487 rsp[out_len] = 0;
3488 iface_ctx.functionality = strtol(rsp, NULL, 10);
3489
3490 LOG_INF("Modem Functionality: %u", iface_ctx.functionality);
3491 done:
3492 return true;
3493 }
3494
3495 /* There can be multiple responses from a single command.
3496 * %MEAS: EARFCN=5826, CellID=420, RSRP=-99, RSRQ=-15
3497 * %MEAS: EARFCN=6400, CellID=201, RSRP=-93, RSRQ=-21
3498 */
on_cmd_survey_status(struct net_buf ** buf,uint16_t len)3499 static bool on_cmd_survey_status(struct net_buf **buf, uint16_t len)
3500 {
3501 struct net_buf *frag = NULL;
3502 char response[sizeof("EARFCN=XXXXXXXXXXX, CellID=XXXXXXXXXXX, RSRP=-XXX, RSRQ=-XXX")];
3503 char *key;
3504 size_t out_len;
3505 char *value;
3506 struct mdm_hl7800_site_survey site_survey;
3507
3508 wait_for_modem_data_and_newline(buf, net_buf_frags_len(*buf),
3509 sizeof(response));
3510
3511 frag = NULL;
3512 len = net_buf_findcrlf(*buf, &frag);
3513 if (!frag) {
3514 LOG_ERR("Unable to find end");
3515 goto done;
3516 }
3517
3518 out_len = net_buf_linearize(response, sizeof(response), *buf, 0, len);
3519 LOG_HEXDUMP_DBG(response, out_len, "Site Survey");
3520
3521 key = "EARFCN=";
3522 value = strstr(response, key);
3523 if (value == NULL) {
3524 goto done;
3525 } else {
3526 value += strlen(key);
3527 site_survey.earfcn = strtoul(value, NULL, 10);
3528 }
3529
3530 key = "CellID=";
3531 value = strstr(response, key);
3532 if (value == NULL) {
3533 goto done;
3534 } else {
3535 value += strlen(key);
3536 site_survey.cell_id = strtoul(value, NULL, 10);
3537 }
3538
3539 key = "RSRP=";
3540 value = strstr(response, key);
3541 if (value == NULL) {
3542 goto done;
3543 } else {
3544 value += strlen(key);
3545 site_survey.rsrp = strtol(value, NULL, 10);
3546 }
3547
3548 key = "RSRQ=";
3549 value = strstr(response, key);
3550 if (value == NULL) {
3551 goto done;
3552 } else {
3553 value += strlen(key);
3554 site_survey.rsrq = strtol(value, NULL, 10);
3555 }
3556
3557 event_handler(HL7800_EVENT_SITE_SURVEY, &site_survey);
3558
3559 done:
3560 return true;
3561 }
3562
3563 /* Handler: +CCLK: "yy/MM/dd,hh:mm:ss±zz" */
on_cmd_rtc_query(struct net_buf ** buf,uint16_t len)3564 static bool on_cmd_rtc_query(struct net_buf **buf, uint16_t len)
3565 {
3566 struct net_buf *frag = NULL;
3567 size_t str_len = sizeof(TIME_STRING_FORMAT) - 1;
3568 char rtc_string[sizeof(TIME_STRING_FORMAT)];
3569
3570 memset(rtc_string, 0, sizeof(rtc_string));
3571 iface_ctx.local_time_valid = false;
3572
3573 wait_for_modem_data_and_newline(buf, net_buf_frags_len(*buf),
3574 sizeof(TIME_STRING_FORMAT));
3575
3576 frag = NULL;
3577 len = net_buf_findcrlf(*buf, &frag);
3578 if (!frag) {
3579 goto done;
3580 }
3581 if (len != str_len) {
3582 LOG_WRN("Unexpected length for RTC string %d (expected:%zu)",
3583 len, str_len);
3584 } else {
3585 net_buf_linearize(rtc_string, str_len, *buf, 0, str_len);
3586 LOG_INF("RTC string: '%s'", rtc_string);
3587 iface_ctx.local_time_valid = convert_time_string_to_struct(
3588 &iface_ctx.local_time, &iface_ctx.local_time_offset, rtc_string);
3589 }
3590 done:
3591 return true;
3592 }
3593
valid_time_string(const char * time_string)3594 static bool valid_time_string(const char *time_string)
3595 {
3596 size_t offset, i;
3597
3598 /* Ensure the all the expected delimiters are present */
3599 offset = TIME_STRING_DIGIT_STRLEN + TIME_STRING_SEPARATOR_STRLEN;
3600 i = TIME_STRING_FIRST_SEPARATOR_INDEX;
3601
3602 for (; i < TIME_STRING_PLUS_MINUS_INDEX; i += offset) {
3603 if (time_string[i] != TIME_STRING_FORMAT[i]) {
3604 return false;
3605 }
3606 }
3607 /* The last character is the offset from UTC and can be either
3608 * positive or negative. The last " is also handled here.
3609 */
3610 if ((time_string[i] == '+' || time_string[i] == '-') &&
3611 (time_string[i + offset] == '"')) {
3612 return true;
3613 }
3614 return false;
3615 }
3616
get_next_time_string_digit(int * failure_cnt,char ** pp,int min,int max)3617 int get_next_time_string_digit(int *failure_cnt, char **pp, int min, int max)
3618 {
3619 char digits[TIME_STRING_DIGIT_STRLEN + SIZE_OF_NUL];
3620 int result;
3621
3622 memset(digits, 0, sizeof(digits));
3623 memcpy(digits, *pp, TIME_STRING_DIGIT_STRLEN);
3624 *pp += TIME_STRING_DIGIT_STRLEN + TIME_STRING_SEPARATOR_STRLEN;
3625 result = strtol(digits, NULL, 10);
3626 if (result > max) {
3627 *failure_cnt += 1;
3628 return max;
3629 } else if (result < min) {
3630 *failure_cnt += 1;
3631 return min;
3632 } else {
3633 return result;
3634 }
3635 }
3636
convert_time_string_to_struct(struct tm * tm,int32_t * offset,char * time_string)3637 static bool convert_time_string_to_struct(struct tm *tm, int32_t *offset,
3638 char *time_string)
3639 {
3640 int fc = 0;
3641 char *ptr = time_string;
3642
3643 if (!valid_time_string(ptr)) {
3644 return false;
3645 }
3646 ptr = &ptr[TIME_STRING_FIRST_DIGIT_INDEX];
3647 tm->tm_year = TIME_STRING_TO_TM_STRUCT_YEAR_OFFSET +
3648 get_next_time_string_digit(&fc, &ptr, TM_YEAR_RANGE);
3649 tm->tm_mon =
3650 get_next_time_string_digit(&fc, &ptr, TM_MONTH_RANGE_PLUS_1) -
3651 1;
3652 tm->tm_mday = get_next_time_string_digit(&fc, &ptr, TM_DAY_RANGE);
3653 tm->tm_hour = get_next_time_string_digit(&fc, &ptr, TM_HOUR_RANGE);
3654 tm->tm_min = get_next_time_string_digit(&fc, &ptr, TM_MIN_RANGE);
3655 tm->tm_sec = get_next_time_string_digit(&fc, &ptr, TM_SEC_RANGE);
3656 tm->tm_isdst = 0;
3657 *offset = (int32_t)get_next_time_string_digit(&fc, &ptr,
3658 QUARTER_HOUR_RANGE) *
3659 SECONDS_PER_QUARTER_HOUR;
3660 if (time_string[TIME_STRING_PLUS_MINUS_INDEX] == '-') {
3661 *offset *= -1;
3662 }
3663 return (fc == 0);
3664 }
3665
3666 /* Handler: +CEREG: <stat>[,[<lac>],[<ci>],[<AcT>]
3667 * [,[<cause_type>],[<reject_cause>] [,[<Active-Time>],[<Periodic-TAU>]]]]
3668 */
on_cmd_network_report(struct net_buf ** buf,uint16_t len)3669 static bool on_cmd_network_report(struct net_buf **buf, uint16_t len)
3670 {
3671 size_t out_len;
3672 char *pos;
3673 int l;
3674 char val[MDM_MAX_RESP_SIZE];
3675
3676 out_len = net_buf_linearize(iface_ctx.mdm_network_status,
3677 sizeof(iface_ctx.mdm_network_status) - 1, *buf,
3678 0, len);
3679 iface_ctx.mdm_network_status[out_len] = 0;
3680 LOG_DBG("Network status: %s", iface_ctx.mdm_network_status);
3681 pos = strchr(iface_ctx.mdm_network_status, ',');
3682 if (pos) {
3683 l = pos - iface_ctx.mdm_network_status;
3684 strncpy(val, iface_ctx.mdm_network_status, l);
3685 val[l] = 0;
3686 set_network_state(strtol(val, NULL, 0));
3687 } else {
3688 set_network_state(strtol(iface_ctx.mdm_network_status, NULL, 0));
3689 }
3690
3691 /* keep HL7800 awake because we want to process the network state soon */
3692 set_busy(true);
3693 allow_sleep(false);
3694 /* start work to adjust iface */
3695 k_work_reschedule_for_queue(&hl7800_workq, &iface_ctx.iface_status_work,
3696 IFACE_WORK_DELAY);
3697
3698 return true;
3699 }
3700
3701 /* Handler: +KCELLMEAS: <RSRP>,<Downlink Path Loss>,<PUSCH Tx Power>,
3702 * <PUCCH Tx Power>,<SiNR>
3703 */
on_cmd_atcmdinfo_rssi(struct net_buf ** buf,uint16_t len)3704 static bool on_cmd_atcmdinfo_rssi(struct net_buf **buf, uint16_t len)
3705 {
3706 /* number of ',' delimiters in this response */
3707 int num_delims = KCELLMEAS_RESPONSE_NUM_DELIMS;
3708 char *delims[KCELLMEAS_RESPONSE_NUM_DELIMS];
3709 size_t out_len;
3710 char value[MDM_MAX_RESP_SIZE];
3711 char *search_start;
3712 int i;
3713
3714 out_len = net_buf_linearize(value, len, *buf, 0, len);
3715 value[out_len] = 0;
3716 search_start = value;
3717
3718 /* find all delimiters */
3719 for (i = 0; i < num_delims; i++) {
3720 delims[i] = strchr(search_start, ',');
3721 if (!delims[i]) {
3722 LOG_ERR("Could not find delim %d, val: %s", i,
3723 value);
3724 goto done;
3725 }
3726 /* Start next search after current delim location */
3727 search_start = delims[i] + 1;
3728 }
3729 /* the first value in the message is the RSRP */
3730 iface_ctx.mdm_rssi = strtol(value, NULL, 10);
3731 /* the 4th ',' (last in the msg) is the start of the SINR */
3732 iface_ctx.mdm_sinr = strtol(delims[3] + 1, NULL, 10);
3733 if ((delims[1] - delims[0]) == 1) {
3734 /* there is no value between the first and second
3735 * delimiter, signal is unknown
3736 */
3737 LOG_INF("RSSI (RSRP): UNKNOWN");
3738 } else {
3739 LOG_INF("RSSI (RSRP): %d SINR: %d", iface_ctx.mdm_rssi,
3740 iface_ctx.mdm_sinr);
3741 event_handler(HL7800_EVENT_RSSI, &iface_ctx.mdm_rssi);
3742 event_handler(HL7800_EVENT_SINR, &iface_ctx.mdm_sinr);
3743 }
3744 done:
3745 return true;
3746 }
3747
3748 /* Handle the "OK" response from an AT command or a socket call */
on_cmd_sockok(struct net_buf ** buf,uint16_t len)3749 static bool on_cmd_sockok(struct net_buf **buf, uint16_t len)
3750 {
3751 struct hl7800_socket *sock = NULL;
3752
3753 sock = socket_from_id(iface_ctx.last_socket_id);
3754 if (!sock || !iface_ctx.socket_cmd) {
3755 iface_ctx.last_error = 0;
3756 k_sem_give(&iface_ctx.response_sem);
3757 } else {
3758 sock->error = 0;
3759 k_sem_give(&sock->sock_send_sem);
3760 }
3761 return true;
3762 }
3763
3764 /* Handler: +KTCP_IND/+KUDP_IND */
on_cmd_sock_ind(struct net_buf ** buf,uint16_t len,const char * const type)3765 static bool on_cmd_sock_ind(struct net_buf **buf, uint16_t len, const char *const type)
3766 {
3767 struct hl7800_socket *sock = NULL;
3768 char *delim;
3769 char value[MDM_MAX_RESP_SIZE];
3770 size_t out_len;
3771 int id;
3772
3773 iface_ctx.last_error = 0;
3774
3775 out_len = net_buf_linearize(value, sizeof(value), *buf, 0, len);
3776 value[out_len] = 0;
3777
3778 /* find ',' because this is the format we expect */
3779 delim = strchr(value, ',');
3780 if (!delim) {
3781 LOG_ERR("%s could not find ','", type);
3782 goto done;
3783 }
3784
3785 id = strtol(value, NULL, 10);
3786 LOG_DBG("%s ID: %d", type, id);
3787 sock = socket_from_id(id);
3788 if (sock) {
3789 sock->error = 0;
3790 k_sem_give(&sock->sock_send_sem);
3791 }
3792
3793 done:
3794 return true;
3795 }
3796
on_cmd_ktcp_ind(struct net_buf ** buf,uint16_t len)3797 static bool on_cmd_ktcp_ind(struct net_buf **buf, uint16_t len)
3798 {
3799 return on_cmd_sock_ind(buf, len, "+KTCP_IND");
3800 }
3801
on_cmd_kudp_ind(struct net_buf ** buf,uint16_t len)3802 static bool on_cmd_kudp_ind(struct net_buf **buf, uint16_t len)
3803 {
3804 return on_cmd_sock_ind(buf, len, "+KUDP_IND");
3805 }
3806
3807
3808 /* Handler: ERROR */
on_cmd_sockerror(struct net_buf ** buf,uint16_t len)3809 static bool on_cmd_sockerror(struct net_buf **buf, uint16_t len)
3810 {
3811 struct hl7800_socket *sock = NULL;
3812 char string[MDM_MAX_RESP_SIZE];
3813
3814 if (len > 0) {
3815 memset(string, 0, sizeof(string));
3816 net_buf_linearize(string, sizeof(string), *buf, 0, len);
3817 LOG_ERR("'%s'", string);
3818 }
3819
3820 sock = socket_from_id(iface_ctx.last_socket_id);
3821 if (!sock) {
3822 iface_ctx.last_error = -EIO;
3823 k_sem_give(&iface_ctx.response_sem);
3824 } else {
3825 sock->error = -EIO;
3826 k_sem_give(&sock->sock_send_sem);
3827 }
3828
3829 return true;
3830 }
3831
3832 /* Handler: CME/CMS Error */
on_cmd_sock_error_code(struct net_buf ** buf,uint16_t len)3833 static bool on_cmd_sock_error_code(struct net_buf **buf, uint16_t len)
3834 {
3835 struct hl7800_socket *sock = NULL;
3836 char value[MDM_MAX_RESP_SIZE];
3837 size_t out_len;
3838
3839 out_len = net_buf_linearize(value, sizeof(value), *buf, 0, len);
3840 value[out_len] = 0;
3841
3842 LOG_ERR("Error code: %s", value);
3843
3844 sock = socket_from_id(iface_ctx.last_socket_id);
3845 if (!sock) {
3846 iface_ctx.last_error = -EIO;
3847 k_sem_give(&iface_ctx.response_sem);
3848 } else {
3849 sock->error = -EIO;
3850 k_sem_give(&sock->sock_send_sem);
3851 }
3852
3853 return true;
3854 }
3855
sock_notif_cb_work(struct k_work * work)3856 static void sock_notif_cb_work(struct k_work *work)
3857 {
3858 struct hl7800_socket *sock = NULL;
3859 struct k_work_delayable *dwork;
3860
3861 dwork = k_work_delayable_from_work(work);
3862 sock = CONTAINER_OF(dwork, struct hl7800_socket, notif_work);
3863
3864 hl7800_lock();
3865 /* send null packet */
3866 if (sock->recv_pkt != NULL) {
3867 /* we are in the middle of RX,
3868 * requeue this and try again
3869 */
3870 k_work_reschedule_for_queue(&hl7800_workq, &sock->notif_work,
3871 MDM_SOCK_NOTIF_DELAY);
3872 } else {
3873 if (sock->type == SOCK_STREAM) {
3874 LOG_DBG("Sock %d trigger NULL packet", sock->socket_id);
3875 k_work_submit_to_queue(&hl7800_workq, &sock->recv_cb_work);
3876 }
3877 }
3878 hl7800_unlock();
3879 }
3880
3881 /* Handler: +KTCP_NOTIF/+KUDP_NOTIF */
on_cmd_sock_notif(struct net_buf ** buf,uint16_t len)3882 static bool on_cmd_sock_notif(struct net_buf **buf, uint16_t len)
3883 {
3884 struct hl7800_socket *sock = NULL;
3885 char *delim;
3886 char value[MDM_MAX_RESP_SIZE];
3887 size_t out_len;
3888 uint8_t notif_val;
3889 bool err = false;
3890 bool trigger_sem = true;
3891 int id;
3892
3893 out_len = net_buf_linearize(value, sizeof(value), *buf, 0, len);
3894 value[out_len] = 0;
3895
3896 /* find ',' because this is the format we expect */
3897 delim = strchr(value, ',');
3898 if (!delim) {
3899 LOG_ERR("+K**P_NOTIF could not find ','");
3900 goto done;
3901 }
3902
3903 id = strtol(value, NULL, 10);
3904 notif_val = strtol(delim + 1, NULL, 10);
3905 if (notif_val == HL7800_TCP_DISCON) {
3906 LOG_DBG("+K**P_NOTIF: %d,%d", id, notif_val);
3907 } else {
3908 LOG_WRN("+K**P_NOTIF: %d,%d", id, notif_val);
3909 }
3910 sock = socket_from_id(id);
3911 if (!sock) {
3912 goto done;
3913 }
3914
3915 switch (notif_val) {
3916 case HL7800_TCP_DATA_SND:
3917 err = false;
3918 sock->error = 0;
3919 break;
3920 case HL7800_TCP_DISCON:
3921 trigger_sem = false;
3922 err = true;
3923 sock->error = -ENOTCONN;
3924 break;
3925 default:
3926 iface_ctx.network_dropped = true;
3927 err = true;
3928 sock->error = -EIO;
3929 break;
3930 }
3931
3932 if (err) {
3933 /* Send NULL packet to callback to notify upper stack layers
3934 * that the peer closed the connection or there was an error.
3935 * This is so an app will not get stuck in recv() forever.
3936 * Let's do the callback processing in a different work queue
3937 * so RX is not delayed.
3938 */
3939 k_work_reschedule_for_queue(&hl7800_workq, &sock->notif_work, MDM_SOCK_NOTIF_DELAY);
3940 if (trigger_sem) {
3941 k_sem_give(&sock->sock_send_sem);
3942 }
3943
3944 if (iface_ctx.network_dropped) {
3945 k_work_reschedule_for_queue(&hl7800_workq, &iface_ctx.iface_status_work,
3946 IFACE_WORK_DELAY);
3947 }
3948 }
3949 done:
3950 return true;
3951 }
3952
delete_socket(struct hl7800_socket * sock,enum net_sock_type type,uint8_t id)3953 static int delete_socket(struct hl7800_socket *sock, enum net_sock_type type, uint8_t id)
3954 {
3955 char cmd[sizeof("AT+KUDPCLOSE=###")];
3956
3957 if (type == SOCK_STREAM) {
3958 snprintk(cmd, sizeof(cmd), "AT+KTCPDEL=%d", id);
3959 } else if (type == SOCK_DGRAM) {
3960 snprintk(cmd, sizeof(cmd), "AT+KUDPCLOSE=%d", id);
3961 }
3962
3963 return send_at_cmd(sock, cmd, MDM_CMD_SEND_TIMEOUT, 0, false);
3964 }
3965
delete_untracked_socket_work_cb(struct k_work * item)3966 static void delete_untracked_socket_work_cb(struct k_work *item)
3967 {
3968 struct stale_socket *sock = NULL;
3969
3970 hl7800_lock();
3971 wakeup_hl7800();
3972 do {
3973 sock = dequeue_stale_socket();
3974 if (sock != NULL) {
3975 LOG_DBG("Delete untracked socket [%d]", sock->id);
3976 delete_socket(NULL, sock->type, sock->id);
3977 free_stale_socket(sock);
3978 }
3979 } while (sock != NULL);
3980
3981 set_busy(false);
3982 allow_sleep(true);
3983 hl7800_unlock();
3984 }
3985
on_cmd_sockcreate(enum net_sock_type type,struct net_buf ** buf,uint16_t len)3986 static bool on_cmd_sockcreate(enum net_sock_type type, struct net_buf **buf, uint16_t len)
3987 {
3988 size_t out_len;
3989 char value[MDM_MAX_RESP_SIZE];
3990 struct hl7800_socket *sock = NULL;
3991
3992 out_len = net_buf_linearize(value, sizeof(value), *buf, 0, len);
3993 value[out_len] = 0;
3994 iface_ctx.last_socket_id = strtol(value, NULL, 10);
3995 if (type == SOCK_STREAM) {
3996 LOG_DBG("+KTCPCFG: %d", iface_ctx.last_socket_id);
3997 } else if (type == SOCK_DGRAM) {
3998 LOG_DBG("+KUDPCFG: %d", iface_ctx.last_socket_id);
3999 }
4000
4001 /* check if the socket has been created already */
4002 sock = socket_from_id(iface_ctx.last_socket_id);
4003 if (!sock) {
4004 LOG_DBG("look up new socket by creation id");
4005 sock = socket_from_id(MDM_CREATE_SOCKET_ID);
4006 if (!sock || sock->type != type) {
4007 if (queue_stale_socket(type, iface_ctx.last_socket_id) == 0) {
4008 /* delay some time before socket cleanup in case there
4009 * are multiple sockets to cleanup
4010 */
4011 k_work_reschedule_for_queue(&hl7800_workq,
4012 &iface_ctx.delete_untracked_socket_work,
4013 SOCKET_CLEANUP_WORK_DELAY);
4014 }
4015 goto done;
4016 }
4017 }
4018
4019 sock->socket_id = iface_ctx.last_socket_id;
4020 sock->created = true;
4021 sock->reconfig = false;
4022 /* don't give back semaphore -- OK to follow */
4023 done:
4024 return true;
4025 }
4026
4027 /* Handler: +KTCPCFG: <session_id> */
on_cmd_sock_tcp_create(struct net_buf ** buf,uint16_t len)4028 static bool on_cmd_sock_tcp_create(struct net_buf **buf, uint16_t len)
4029 {
4030 return on_cmd_sockcreate(SOCK_STREAM, buf, len);
4031 }
4032
4033 /* Handler: +KUDPCFG: <session_id> */
on_cmd_sock_udp_create(struct net_buf ** buf,uint16_t len)4034 static bool on_cmd_sock_udp_create(struct net_buf **buf, uint16_t len)
4035 {
4036 return on_cmd_sockcreate(SOCK_DGRAM, buf, len);
4037 }
4038
sockreadrecv_cb_work(struct k_work * work)4039 static void sockreadrecv_cb_work(struct k_work *work)
4040 {
4041 struct hl7800_socket *sock = NULL;
4042 struct net_pkt *pkt;
4043
4044 sock = CONTAINER_OF(work, struct hl7800_socket, recv_cb_work);
4045
4046 LOG_DBG("Sock %d RX CB (size: %zd)", sock->socket_id,
4047 (sock->recv_pkt != NULL) ? net_pkt_get_len(sock->recv_pkt) : 0);
4048 /* return data */
4049 pkt = sock->recv_pkt;
4050 sock->recv_pkt = NULL;
4051 if (sock->recv_cb) {
4052 sock->recv_cb(sock->context, pkt, NULL, NULL, 0,
4053 sock->recv_user_data);
4054 } else {
4055 net_pkt_unref(pkt);
4056 }
4057 }
4058
sock_read(struct net_buf ** buf)4059 static void sock_read(struct net_buf **buf)
4060 {
4061 struct hl7800_socket *sock = NULL;
4062 struct net_buf *frag;
4063 uint8_t c = 0U;
4064 int i, hdr_len;
4065 char ok_resp[sizeof(OK_STRING)];
4066 char eof[sizeof(EOF_PATTERN)];
4067 size_t out_len;
4068
4069 sock = socket_from_id(iface_ctx.last_socket_id);
4070 if (!sock) {
4071 LOG_ERR("Socket not found! (%d)", iface_ctx.last_socket_id);
4072 goto exit;
4073 }
4074
4075 if (sock->error != 0) {
4076 /* cancel notif work and restart */
4077 k_work_reschedule_for_queue(&hl7800_workq, &sock->notif_work,
4078 MDM_SOCK_NOTIF_DELAY);
4079 }
4080
4081 LOG_DBG("Socket %d RX %u bytes", sock->socket_id, sock->rx_size);
4082
4083 /* remove ending \r\n from last CONNECT */
4084 if (net_buf_frags_len(*buf) < 2) {
4085 /* wait for \n to be RXd. \r was already RXd. */
4086 wait_for_modem_data(buf, 0, 1);
4087 }
4088 /* remove \r\n */
4089 net_buf_remove(buf, 2);
4090 if (!*buf) {
4091 wait_for_modem_data(buf, 0, sock->rx_size);
4092 }
4093
4094 LOG_DBG("Processing RX, buf len: %zd", net_buf_frags_len(*buf));
4095
4096 /* allocate an RX pkt */
4097 sock->recv_pkt = net_pkt_rx_alloc_with_buffer(
4098 net_context_get_iface(sock->context), sock->rx_size,
4099 sock->family, sock->ip_proto, BUF_ALLOC_TIMEOUT);
4100 if (!sock->recv_pkt) {
4101 LOG_ERR("Failed net_pkt_get_reserve_rx!");
4102 goto done;
4103 }
4104
4105 /* set pkt data */
4106 net_pkt_set_context(sock->recv_pkt, sock->context);
4107
4108 /* add IP / protocol headers */
4109 hdr_len = pkt_setup_ip_data(sock->recv_pkt, sock);
4110
4111 /* receive data */
4112 for (i = 0; i < sock->rx_size; i++) {
4113 /* pull data from buf and advance to the next frag if needed */
4114 c = net_buf_get_u8(buf);
4115 /* write data to packet */
4116 if (net_pkt_write_u8(sock->recv_pkt, c)) {
4117 LOG_ERR("Unable to add data! Aborting! Bytes RXd:%d",
4118 i);
4119 goto rx_err;
4120 }
4121
4122 if (!*buf && i < sock->rx_size) {
4123 LOG_DBG("RX more data, bytes RXd:%d", i + 1);
4124 /* wait for at least one more byte */
4125 wait_for_modem_data(buf, 0, 1);
4126 if (!*buf) {
4127 LOG_ERR("No data in buf!");
4128 break;
4129 }
4130 }
4131 }
4132
4133 LOG_DBG("Got all data, get EOF and OK (buf len:%zd)",
4134 net_buf_frags_len(*buf));
4135
4136 if (!*buf || (net_buf_frags_len(*buf) < strlen(EOF_PATTERN))) {
4137 wait_for_modem_data(buf, net_buf_frags_len(*buf),
4138 strlen(EOF_PATTERN));
4139 if (!*buf) {
4140 LOG_WRN("No EOF present");
4141 goto all_rx_data;
4142 }
4143 }
4144
4145 out_len = net_buf_linearize(eof, sizeof(eof), *buf, 0,
4146 strlen(EOF_PATTERN));
4147 eof[out_len] = 0;
4148 /* remove EOF pattern from buffer */
4149 net_buf_remove(buf, strlen(EOF_PATTERN));
4150 if (strcmp(eof, EOF_PATTERN)) {
4151 LOG_WRN("Could not find EOF [%s]", eof);
4152 }
4153
4154 /* Make sure we have \r\nOK\r\n length in the buffer */
4155 if (!*buf || (net_buf_frags_len(*buf) < strlen(OK_STRING) + 4)) {
4156 wait_for_modem_data(buf, net_buf_frags_len(*buf),
4157 strlen(OK_STRING) + 4);
4158 if (!*buf) {
4159 LOG_WRN("No OK present");
4160 goto all_rx_data;
4161 }
4162 }
4163
4164 frag = NULL;
4165 (void)net_buf_findcrlf(*buf, &frag);
4166 if (!frag) {
4167 LOG_WRN("Unable to find OK start");
4168 goto all_rx_data;
4169 }
4170 /* remove \r\n before OK */
4171 net_buf_skipcrlf(buf);
4172
4173 out_len = net_buf_linearize(ok_resp, sizeof(ok_resp), *buf, 0,
4174 strlen(OK_STRING));
4175 ok_resp[out_len] = 0;
4176 /* remove the message from the buffer */
4177 net_buf_remove(buf, strlen(OK_STRING));
4178 if (strcmp(ok_resp, OK_STRING)) {
4179 LOG_WRN("Could not find OK [%s]", ok_resp);
4180 }
4181
4182 /* remove \r\n after OK */
4183 net_buf_skipcrlf(buf);
4184
4185 all_rx_data:
4186 net_pkt_cursor_init(sock->recv_pkt);
4187 net_pkt_set_overwrite(sock->recv_pkt, true);
4188
4189 if (hdr_len > 0) {
4190 net_pkt_skip(sock->recv_pkt, hdr_len);
4191 }
4192
4193 /* Let's do the callback processing in a different work queue in
4194 * case the app takes a long time.
4195 */
4196 k_work_submit_to_queue(&hl7800_workq, &sock->recv_cb_work);
4197 LOG_DBG("Sock %d RX done", sock->socket_id);
4198 goto done;
4199 rx_err:
4200 net_pkt_unref(sock->recv_pkt);
4201 sock->recv_pkt = NULL;
4202 done:
4203 if (sock->type == SOCK_STREAM) {
4204 if (sock->error == 0) {
4205 sock->state = SOCK_CONNECTED;
4206 }
4207 } else {
4208 sock->state = SOCK_IDLE;
4209 }
4210 exit:
4211 set_busy(false);
4212 allow_sleep(true);
4213 hl7800_TX_unlock();
4214 }
4215
on_cmd_connect(struct net_buf ** buf,uint16_t len)4216 static bool on_cmd_connect(struct net_buf **buf, uint16_t len)
4217 {
4218 bool remove_data_from_buffer = true;
4219 struct hl7800_socket *sock = NULL;
4220
4221 sock = socket_from_id(iface_ctx.last_socket_id);
4222 if (!sock) {
4223 LOG_ERR("Sock (%d) not found", iface_ctx.last_socket_id);
4224 goto done;
4225 }
4226
4227 if (sock->state == SOCK_RX) {
4228 remove_data_from_buffer = false;
4229 sock_read(buf);
4230 } else {
4231 k_sem_give(&sock->sock_send_sem);
4232 }
4233
4234 done:
4235 return remove_data_from_buffer;
4236 }
4237
start_socket_rx(struct hl7800_socket * sock,uint16_t rx_size)4238 static int start_socket_rx(struct hl7800_socket *sock, uint16_t rx_size)
4239 {
4240 char sendbuf[sizeof("AT+KTCPRCV=+#########,#####")];
4241
4242 if ((sock->socket_id <= 0) || (sock->rx_size <= 0)) {
4243 LOG_WRN("Cannot start socket RX, ID: %d rx size: %d",
4244 sock->socket_id, sock->rx_size);
4245 return -1;
4246 }
4247
4248 LOG_DBG("Start socket RX ID:%d size:%d", sock->socket_id, rx_size);
4249 sock->state = SOCK_RX;
4250 if (sock->type == SOCK_DGRAM) {
4251 #if defined(CONFIG_NET_IPV4)
4252 if (rx_size > (net_if_get_mtu(iface_ctx.iface) - NET_IPV4UDPH_LEN)) {
4253 sock->rx_size =
4254 net_if_get_mtu(iface_ctx.iface) - NET_IPV4UDPH_LEN;
4255 }
4256 #endif
4257 #if defined(CONFIG_NET_IPV6)
4258 if (rx_size > (net_if_get_mtu(iface_ctx.iface) - NET_IPV6UDPH_LEN)) {
4259 sock->rx_size =
4260 net_if_get_mtu(iface_ctx.iface) - NET_IPV6UDPH_LEN;
4261 }
4262 #endif
4263 snprintk(sendbuf, sizeof(sendbuf), "AT+KUDPRCV=%d,%u",
4264 sock->socket_id, rx_size);
4265 } else {
4266 #if defined(CONFIG_NET_IPV4)
4267 if (rx_size > (net_if_get_mtu(iface_ctx.iface) - NET_IPV4TCPH_LEN)) {
4268 sock->rx_size =
4269 net_if_get_mtu(iface_ctx.iface) - NET_IPV4TCPH_LEN;
4270 }
4271 #endif
4272 #if defined(CONFIG_NET_IPV6)
4273 if (rx_size > (net_if_get_mtu(iface_ctx.iface) - NET_IPV6TCPH_LEN)) {
4274 sock->rx_size =
4275 net_if_get_mtu(iface_ctx.iface) - NET_IPV6TCPH_LEN;
4276 }
4277 #endif
4278 snprintk(sendbuf, sizeof(sendbuf), "AT+KTCPRCV=%d,%u",
4279 sock->socket_id, sock->rx_size);
4280 }
4281
4282 /* Send AT+K**PRCV, The modem
4283 * will respond with "CONNECT" and the data requested
4284 * and then "OK" or "ERROR".
4285 * The rest of the data processing will be handled
4286 * once CONNECT is RXd.
4287 */
4288 send_at_cmd(sock, sendbuf, K_NO_WAIT, 0, false);
4289 return 0;
4290 }
4291
sock_rx_data_cb_work(struct k_work * work)4292 static void sock_rx_data_cb_work(struct k_work *work)
4293 {
4294 struct hl7800_socket *sock = NULL;
4295 int rc;
4296
4297 sock = CONTAINER_OF(work, struct hl7800_socket, rx_data_work);
4298
4299 hl7800_lock();
4300 wakeup_hl7800();
4301
4302 /* start RX */
4303 rc = start_socket_rx(sock, sock->rx_size);
4304
4305 /* Only unlock the RX because we just locked it above.
4306 * At the end of socket RX, the TX will be unlocked.
4307 */
4308 hl7800_RX_unlock();
4309 if (rc < 0) {
4310 /* we didn't start socket RX so unlock TX now. */
4311 hl7800_TX_unlock();
4312 }
4313 }
4314
4315 /* Handler: +KTCP_DATA/+KUDP_DATA: <socket_id>,<left_bytes> */
on_cmd_sockdataind(struct net_buf ** buf,uint16_t len)4316 static bool on_cmd_sockdataind(struct net_buf **buf, uint16_t len)
4317 {
4318 int socket_id, left_bytes, rc;
4319 size_t out_len;
4320 char *delim;
4321 char value[sizeof("##,####")];
4322 struct hl7800_socket *sock = NULL;
4323 bool unlock = false;
4324 bool defer_rx = false;
4325
4326 if (!hl7800_TX_locked()) {
4327 hl7800_TX_lock();
4328 unlock = true;
4329 } else {
4330 defer_rx = true;
4331 }
4332
4333 out_len = net_buf_linearize(value, sizeof(value) - 1, *buf, 0, len);
4334 value[out_len] = 0;
4335
4336 /* First comma separator marks the end of socket_id */
4337 delim = strchr(value, ',');
4338 if (!delim) {
4339 LOG_ERR("Missing comma");
4340 goto error;
4341 }
4342
4343 /* replace comma with null */
4344 *delim++ = '\0';
4345 socket_id = strtol(value, NULL, 0);
4346
4347 /* second param is for left_bytes */
4348 left_bytes = strtol(delim, NULL, 0);
4349
4350 sock = socket_from_id(socket_id);
4351 if (!sock) {
4352 LOG_ERR("Unable to find socket_id:%d", socket_id);
4353 goto error;
4354 }
4355
4356 sock->rx_size = left_bytes;
4357 if (defer_rx) {
4358 LOG_DBG("Defer socket RX -> ID: %d bytes: %u", socket_id,
4359 left_bytes);
4360 k_work_submit_to_queue(&hl7800_workq, &sock->rx_data_work);
4361 } else {
4362 if (left_bytes > 0) {
4363 wakeup_hl7800();
4364 rc = start_socket_rx(sock, left_bytes);
4365 if (rc < 0) {
4366 goto error;
4367 }
4368 goto done;
4369 }
4370 }
4371 error:
4372 if (unlock) {
4373 hl7800_TX_unlock();
4374 }
4375 done:
4376 return true;
4377 }
4378
4379 /* Handler: +WDSI: ## */
on_cmd_device_service_ind(struct net_buf ** buf,uint16_t len)4380 static bool on_cmd_device_service_ind(struct net_buf **buf, uint16_t len)
4381 {
4382 char value[MDM_MAX_RESP_SIZE];
4383 size_t out_len;
4384
4385 memset(value, 0, sizeof(value));
4386 out_len = net_buf_linearize(value, sizeof(value), *buf, 0, len);
4387 if (out_len > 0) {
4388 iface_ctx.device_services_ind = strtol(value, NULL, 10);
4389 }
4390 LOG_INF("+WDSI: %d", iface_ctx.device_services_ind);
4391
4392 #ifdef CONFIG_MODEM_HL7800_FW_UPDATE
4393 if (iface_ctx.device_services_ind == WDSI_PKG_DOWNLOADED) {
4394 k_work_submit_to_queue(&hl7800_workq,
4395 &iface_ctx.finish_fw_update_work);
4396 }
4397 #endif
4398
4399 return true;
4400 }
4401
read_rx_allocator(k_timeout_t timeout,void * user_data)4402 static inline struct net_buf *read_rx_allocator(k_timeout_t timeout,
4403 void *user_data)
4404 {
4405 return net_buf_alloc((struct net_buf_pool *)user_data, timeout);
4406 }
4407
hl7800_read_rx(struct net_buf ** buf)4408 static size_t hl7800_read_rx(struct net_buf **buf)
4409 {
4410 uint8_t uart_buffer[CONFIG_MODEM_HL7800_RECV_BUF_SIZE];
4411 size_t bytes_read, total_read;
4412 int ret;
4413 uint16_t rx_len;
4414
4415 bytes_read = 0, total_read = 0;
4416
4417 /* read all of the data from mdm_receiver */
4418 while (true) {
4419 ret = mdm_receiver_recv(&iface_ctx.mdm_ctx, uart_buffer,
4420 sizeof(uart_buffer), &bytes_read);
4421 if (ret < 0 || bytes_read == 0) {
4422 /* mdm_receiver buffer is empty */
4423 break;
4424 }
4425
4426 if (IS_ENABLED(HL7800_ENABLE_VERBOSE_MODEM_RECV_HEXDUMP)) {
4427 LOG_HEXDUMP_DBG((const uint8_t *)&uart_buffer,
4428 bytes_read, "HL7800 RX");
4429 }
4430 /* make sure we have storage */
4431 if (!*buf) {
4432 *buf = net_buf_alloc(&mdm_recv_pool, BUF_ALLOC_TIMEOUT);
4433 if (!*buf) {
4434 LOG_ERR("Can't allocate RX data! "
4435 "Skipping data!");
4436 break;
4437 }
4438 }
4439
4440 rx_len =
4441 net_buf_append_bytes(*buf, bytes_read, uart_buffer,
4442 BUF_ALLOC_TIMEOUT,
4443 read_rx_allocator, &mdm_recv_pool);
4444 if (rx_len < bytes_read) {
4445 LOG_ERR("Data was lost! read %u of %zu!", rx_len,
4446 bytes_read);
4447 }
4448 total_read += bytes_read;
4449 }
4450
4451 return total_read;
4452 }
4453
4454 #ifdef CONFIG_MODEM_HL7800_FW_UPDATE
finish_fw_update_work_callback(struct k_work * item)4455 static void finish_fw_update_work_callback(struct k_work *item)
4456 {
4457 ARG_UNUSED(item);
4458
4459 send_at_cmd(NULL, "AT+WDSR=4", MDM_CMD_SEND_TIMEOUT, 0, false);
4460 iface_ctx.fw_updated = true;
4461 set_fota_state(HL7800_FOTA_INSTALL);
4462 hl7800_unlock();
4463 }
4464
calc_fw_update_crc(uint8_t * ptr,int count)4465 static uint8_t calc_fw_update_crc(uint8_t *ptr, int count)
4466 {
4467 uint8_t crc = 0;
4468 unsigned char l;
4469 uint16_t i = 0;
4470
4471 while (i < count) {
4472 l = *ptr;
4473 crc += l;
4474 ++ptr;
4475 ++i;
4476 }
4477
4478 return crc;
4479 }
4480
send_fw_update_packet(struct xmodem_packet * pkt)4481 static int send_fw_update_packet(struct xmodem_packet *pkt)
4482 {
4483 generate_fota_count_event();
4484 LOG_DBG("Send FW update packet %d,%d", pkt->id, iface_ctx.fw_packet_count);
4485 return mdm_receiver_send(&iface_ctx.mdm_ctx, (const uint8_t *)pkt,
4486 XMODEM_PACKET_SIZE);
4487 }
4488
prepare_and_send_fw_packet(void)4489 static int prepare_and_send_fw_packet(void)
4490 {
4491 int ret = 0;
4492 int read_res;
4493
4494 iface_ctx.fw_packet.id_complement = 0xFF - iface_ctx.fw_packet.id;
4495
4496 ret = fs_seek(&iface_ctx.fw_update_file, iface_ctx.file_pos, FS_SEEK_SET);
4497 if (ret < 0) {
4498 set_fota_state(HL7800_FOTA_FILE_ERROR);
4499 LOG_ERR("Could not seek to offset %d of file", iface_ctx.file_pos);
4500 return ret;
4501 }
4502
4503 read_res = fs_read(&iface_ctx.fw_update_file, iface_ctx.fw_packet.data,
4504 XMODEM_DATA_SIZE);
4505 if (read_res < 0) {
4506 set_fota_state(HL7800_FOTA_FILE_ERROR);
4507 LOG_ERR("Failed to read fw update file [%d]", read_res);
4508 return ret;
4509 } else if (read_res < XMODEM_DATA_SIZE) {
4510 set_fota_state(HL7800_FOTA_PAD);
4511 fs_close(&iface_ctx.fw_update_file);
4512 /* pad rest of data */
4513 for (int i = read_res; i < XMODEM_DATA_SIZE; i++) {
4514 iface_ctx.fw_packet.data[i] = XMODEM_PAD_VALUE;
4515 }
4516 }
4517
4518 iface_ctx.fw_packet.crc =
4519 calc_fw_update_crc(iface_ctx.fw_packet.data, XMODEM_DATA_SIZE);
4520
4521 send_fw_update_packet(&iface_ctx.fw_packet);
4522
4523 iface_ctx.file_pos += read_res;
4524 iface_ctx.fw_packet_count++;
4525 iface_ctx.fw_packet.id++;
4526
4527 return ret;
4528 }
4529
process_fw_update_rx(struct net_buf ** rx_buf)4530 static void process_fw_update_rx(struct net_buf **rx_buf)
4531 {
4532 static uint8_t xm_msg;
4533 uint8_t eot = XM_EOT;
4534
4535 xm_msg = net_buf_get_u8(rx_buf);
4536
4537 if (xm_msg == XM_NACK) {
4538 if (iface_ctx.fw_update_state == HL7800_FOTA_START) {
4539 /* send first FW update packet */
4540 set_fota_state(HL7800_FOTA_WIP);
4541 iface_ctx.file_pos = 0;
4542 iface_ctx.fw_packet_count = 1;
4543 iface_ctx.fw_packet.id = 1;
4544 iface_ctx.fw_packet.preamble = XM_SOH_1K;
4545
4546 prepare_and_send_fw_packet();
4547 } else if (iface_ctx.fw_update_state == HL7800_FOTA_WIP) {
4548 LOG_DBG("RX FW update NACK");
4549 /* resend last packet */
4550 send_fw_update_packet(&iface_ctx.fw_packet);
4551 }
4552 } else if (xm_msg == XM_ACK) {
4553 LOG_DBG("RX FW update ACK");
4554 if (iface_ctx.fw_update_state == HL7800_FOTA_WIP) {
4555 /* send next FW update packet */
4556 prepare_and_send_fw_packet();
4557 } else if (iface_ctx.fw_update_state == HL7800_FOTA_PAD) {
4558 set_fota_state(HL7800_FOTA_SEND_EOT);
4559 mdm_receiver_send(&iface_ctx.mdm_ctx, &eot, sizeof(eot));
4560 }
4561 } else {
4562 LOG_WRN("RX unhandled FW update value: %02x", xm_msg);
4563 }
4564 }
4565
4566 #endif /* CONFIG_MODEM_HL7800_FW_UPDATE */
4567
4568 /* RX thread */
hl7800_rx(void * p1,void * p2,void * p3)4569 static void hl7800_rx(void *p1, void *p2, void *p3)
4570 {
4571 ARG_UNUSED(p1);
4572 ARG_UNUSED(p2);
4573 ARG_UNUSED(p3);
4574
4575 struct net_buf *rx_buf = NULL;
4576 struct net_buf *frag = NULL;
4577 int i, cmp_res;
4578 uint16_t len;
4579 size_t out_len;
4580 bool cmd_handled = false;
4581 static char rx_msg[MDM_HANDLER_MATCH_MAX_LEN];
4582 bool unlock = false;
4583 bool remove_line_from_buf = true;
4584 #ifdef HL7800_LOG_UNHANDLED_RX_MSGS
4585 char msg[MDM_MAX_RESP_SIZE];
4586 #endif
4587
4588 static const struct cmd_handler handlers[] = {
4589 /* MODEM Information */
4590 CMD_HANDLER("AT+CGMI", atcmdinfo_manufacturer),
4591 CMD_HANDLER("AT+CGMM", atcmdinfo_model),
4592 CMD_HANDLER("AT+CGMR", atcmdinfo_revision),
4593 CMD_HANDLER("AT+CGSN", atcmdinfo_imei),
4594 CMD_HANDLER("AT+KGSN=3", atcmdinfo_serial_number),
4595 CMD_HANDLER("+KCELLMEAS: ", atcmdinfo_rssi),
4596 CMD_HANDLER("+CGCONTRDP: ", atcmdinfo_ipaddr),
4597 CMD_HANDLER("+COPS: ", atcmdinfo_operator_status),
4598 CMD_HANDLER("+KSRAT: ", radio_tech_status),
4599 CMD_HANDLER("+KBNDCFG: ", radio_band_configuration),
4600 CMD_HANDLER("+KBND: ", radio_active_bands),
4601 CMD_HANDLER("+CCID: ", atcmdinfo_iccid),
4602 CMD_HANDLER("ACTIVE PROFILE:", atcmdinfo_active_profile),
4603 CMD_HANDLER("STORED PROFILE 0:", atcmdinfo_stored_profile0),
4604 CMD_HANDLER("STORED PROFILE 1:", atcmdinfo_stored_profile1),
4605 CMD_HANDLER("+WPPP: 1,1,", atcmdinfo_pdp_authentication_cfg),
4606 CMD_HANDLER("+CGDCONT: 1", atcmdinfo_pdp_context),
4607 CMD_HANDLER("AT+CEREG?", network_report_query),
4608 CMD_HANDLER("+KCARRIERCFG: ", operator_index_query),
4609 CMD_HANDLER("AT+CIMI", atcmdinfo_imsi),
4610 CMD_HANDLER("+CFUN: ", modem_functionality),
4611 CMD_HANDLER("%MEAS: ", survey_status),
4612 CMD_HANDLER("+CCLK: ", rtc_query),
4613
4614 /* UNSOLICITED modem information */
4615 /* mobile startup report */
4616 CMD_HANDLER("+KSUP: ", startup_report),
4617 /* network status */
4618 CMD_HANDLER("+CEREG: ", network_report),
4619
4620 /* SOLICITED CMD AND SOCKET RESPONSES */
4621 CMD_HANDLER("OK", sockok),
4622 CMD_HANDLER("ERROR", sockerror),
4623
4624 /* SOLICITED SOCKET RESPONSES */
4625 CMD_HANDLER("+CME ERROR: ", sock_error_code),
4626 CMD_HANDLER("+CMS ERROR: ", sock_error_code),
4627 CMD_HANDLER("+CEER: ", sockerror),
4628 CMD_HANDLER("+KTCPCFG: ", sock_tcp_create),
4629 CMD_HANDLER("+KUDPCFG: ", sock_udp_create),
4630 CMD_HANDLER(CONNECT_STRING, connect),
4631 CMD_HANDLER("NO CARRIER", sockerror),
4632
4633 /* UNSOLICITED SOCKET RESPONSES */
4634 CMD_HANDLER("+KTCP_IND: ", ktcp_ind),
4635 CMD_HANDLER("+KUDP_IND: ", kudp_ind),
4636 CMD_HANDLER("+KTCP_NOTIF: ", sock_notif),
4637 CMD_HANDLER("+KUDP_NOTIF: ", sock_notif),
4638 CMD_HANDLER("+KTCP_DATA: ", sockdataind),
4639 CMD_HANDLER("+KUDP_DATA: ", sockdataind),
4640
4641 /* FIRMWARE UPDATE RESPONSES */
4642 CMD_HANDLER("+WDSI: ", device_service_ind),
4643
4644 #ifdef CONFIG_MODEM_HL7800_GPS
4645 CMD_HANDLER("+GNSSEV: ", gps_event),
4646 CMD_HANDLER("Latitude: ", latitude),
4647 CMD_HANDLER("Longitude: ", longitude),
4648 CMD_HANDLER("GpsTime: ", gps_time),
4649 CMD_HANDLER("FixType: ", fix_type),
4650 CMD_HANDLER("HEPE: ", hepe),
4651 CMD_HANDLER("Altitude: ", altitude),
4652 CMD_HANDLER("AltUnc: ", alt_unc),
4653 CMD_HANDLER("Direction: ", direction),
4654 CMD_HANDLER("HorSpeed: ", hor_speed),
4655 CMD_HANDLER("VerSpeed: ", ver_speed),
4656 #endif
4657
4658 #ifdef CONFIG_MODEM_HL7800_POLTE
4659 CMD_HANDLER("%POLTEEVU: \"REGISTER\",0,", polte_registration),
4660 CMD_HANDLER("%POLTECMD: \"LOCATE\",", polte_locate_cmd_rsp),
4661 CMD_HANDLER("%POLTEEVU: \"LOCATION\",", polte_location),
4662 #endif
4663 };
4664
4665 while (true) {
4666 /* wait for incoming data */
4667 (void)k_sem_take(&iface_ctx.mdm_ctx.rx_sem, K_FOREVER);
4668
4669 hl7800_read_rx(&rx_buf);
4670 /* If an external module hasn't locked the command processor,
4671 * then do so now.
4672 */
4673 if (!hl7800_RX_locked()) {
4674 hl7800_RX_lock();
4675 unlock = true;
4676 } else {
4677 unlock = false;
4678 }
4679
4680 while (rx_buf) {
4681 remove_line_from_buf = true;
4682 cmd_handled = false;
4683
4684 #ifdef CONFIG_MODEM_HL7800_FW_UPDATE
4685 if ((iface_ctx.fw_update_state == HL7800_FOTA_START) ||
4686 (iface_ctx.fw_update_state == HL7800_FOTA_WIP) ||
4687 (iface_ctx.fw_update_state == HL7800_FOTA_PAD)) {
4688 process_fw_update_rx(&rx_buf);
4689 if (!rx_buf) {
4690 break;
4691 }
4692 }
4693 #endif
4694
4695 net_buf_skipcrlf(&rx_buf);
4696 if (!rx_buf) {
4697 break;
4698 }
4699
4700 frag = NULL;
4701 len = net_buf_findcrlf(rx_buf, &frag);
4702 if (!frag) {
4703 break;
4704 }
4705
4706 out_len = net_buf_linearize(rx_msg, sizeof(rx_msg),
4707 rx_buf, 0, len);
4708
4709 /* look for matching data handlers */
4710 i = -1;
4711 for (i = 0; i < ARRAY_SIZE(handlers); i++) {
4712 if (iface_ctx.search_no_id_resp) {
4713 cmp_res = strncmp(iface_ctx.no_id_resp_cmd,
4714 handlers[i].cmd,
4715 handlers[i].cmd_len);
4716 } else {
4717 cmp_res =
4718 strncmp(rx_msg, handlers[i].cmd,
4719 handlers[i].cmd_len);
4720 }
4721
4722 if (cmp_res == 0) {
4723 /* found a matching handler */
4724
4725 /* skip cmd_len */
4726 if (!iface_ctx.search_no_id_resp) {
4727 rx_buf = net_buf_skip(
4728 rx_buf,
4729 handlers[i].cmd_len);
4730 }
4731
4732 /* locate next cr/lf */
4733 frag = NULL;
4734 len = net_buf_findcrlf(rx_buf, &frag);
4735 if (!frag) {
4736 break;
4737 }
4738
4739 LOG_DBG("HANDLE %s (len:%u)",
4740 handlers[i].cmd, len);
4741 /* call handler */
4742 if (handlers[i].func) {
4743 remove_line_from_buf =
4744 handlers[i].func(
4745 &rx_buf, len);
4746 }
4747 cmd_handled = true;
4748 iface_ctx.search_no_id_resp = false;
4749 frag = NULL;
4750 /* make sure buf still has data */
4751 if (!rx_buf) {
4752 break;
4753 }
4754
4755 /* We've handled the current line
4756 * and need to exit the "search for
4757 * handler loop". Let's skip any
4758 * "extra" data and look for the next
4759 * CR/LF, leaving us ready for the
4760 * next handler search.
4761 */
4762 len = net_buf_findcrlf(rx_buf, &frag);
4763 break;
4764 }
4765 }
4766
4767 /* Handle unhandled commands */
4768 if (IS_ENABLED(HL7800_LOG_UNHANDLED_RX_MSGS) &&
4769 !cmd_handled && frag && len > 1) {
4770 out_len = net_buf_linearize(msg, sizeof(msg),
4771 rx_buf, 0, len);
4772 msg[out_len] = 0;
4773 LOG_HEXDUMP_DBG((const uint8_t *)&msg, len,
4774 "UNHANDLED RX");
4775 }
4776 if (remove_line_from_buf && frag && rx_buf) {
4777 /* clear out processed line (buffers) */
4778 net_buf_remove(&rx_buf, len);
4779 }
4780 }
4781
4782 if (unlock) {
4783 hl7800_RX_unlock();
4784 }
4785
4786 /* give up time if we have a solid stream of data */
4787 k_yield();
4788 }
4789 }
4790
shutdown_uart(void)4791 static void shutdown_uart(void)
4792 {
4793 #ifdef CONFIG_PM_DEVICE
4794 int rc;
4795 enum pm_device_state state;
4796
4797 rc = pm_device_state_get(iface_ctx.mdm_ctx.uart_dev, &state);
4798 if (rc) {
4799 LOG_ERR("Error getting UART power state (%d)", rc);
4800 }
4801 if (state != PM_DEVICE_STATE_SUSPENDED) {
4802 HL7800_IO_DBG_LOG("Power OFF the UART");
4803 uart_irq_rx_disable(iface_ctx.mdm_ctx.uart_dev);
4804 rc = pm_device_action_run(iface_ctx.mdm_ctx.uart_dev, PM_DEVICE_ACTION_SUSPEND);
4805 if (rc) {
4806 LOG_ERR("Error disabling UART peripheral (%d)", rc);
4807 uart_irq_rx_enable(iface_ctx.mdm_ctx.uart_dev);
4808 }
4809 }
4810 #endif
4811 }
4812
power_on_uart(void)4813 static void power_on_uart(void)
4814 {
4815 #ifdef CONFIG_PM_DEVICE
4816 int rc;
4817 enum pm_device_state state;
4818
4819 rc = pm_device_state_get(iface_ctx.mdm_ctx.uart_dev, &state);
4820 if (rc) {
4821 LOG_ERR("Error getting UART power state (%d)", rc);
4822 }
4823 if (state != PM_DEVICE_STATE_ACTIVE) {
4824 HL7800_IO_DBG_LOG("Power ON the UART");
4825 rc = pm_device_action_run(iface_ctx.mdm_ctx.uart_dev, PM_DEVICE_ACTION_RESUME);
4826 if (rc) {
4827 LOG_ERR("Error enabling UART peripheral (%d)", rc);
4828 uart_irq_rx_disable(iface_ctx.mdm_ctx.uart_dev);
4829 } else {
4830 uart_irq_rx_enable(iface_ctx.mdm_ctx.uart_dev);
4831 }
4832 }
4833 #endif
4834 }
4835
4836 /* Make sure all IO voltages are removed for proper reset. */
prepare_io_for_reset(void)4837 static void prepare_io_for_reset(void)
4838 {
4839 HL7800_IO_DBG_LOG("Preparing IO for reset/sleep");
4840 shutdown_uart();
4841 modem_assert_wake(false);
4842 modem_assert_pwr_on(false);
4843 modem_assert_fast_shutd(false);
4844 iface_ctx.wait_for_KSUP = true;
4845 iface_ctx.wait_for_KSUP_tries = 0;
4846 }
4847
mdm_vgpio_work_cb(struct k_work * item)4848 static void mdm_vgpio_work_cb(struct k_work *item)
4849 {
4850 ARG_UNUSED(item);
4851
4852 hl7800_lock();
4853 if (!iface_ctx.vgpio_state) {
4854 if (iface_ctx.desired_sleep_level == HL7800_SLEEP_HIBERNATE ||
4855 iface_ctx.desired_sleep_level == HL7800_SLEEP_LITE_HIBERNATE) {
4856 if (iface_ctx.sleep_state != iface_ctx.desired_sleep_level) {
4857 set_sleep_state(iface_ctx.desired_sleep_level);
4858 }
4859 }
4860 if (iface_ctx.iface && iface_ctx.initialized &&
4861 iface_ctx.low_power_mode != HL7800_LPM_PSM) {
4862 net_if_carrier_off(iface_ctx.iface);
4863 }
4864 }
4865 hl7800_unlock();
4866 }
4867
mdm_vgpio_callback_isr(const struct device * port,struct gpio_callback * cb,uint32_t pins)4868 void mdm_vgpio_callback_isr(const struct device *port, struct gpio_callback *cb, uint32_t pins)
4869 {
4870 ARG_UNUSED(port);
4871 ARG_UNUSED(cb);
4872 ARG_UNUSED(pins);
4873
4874 iface_ctx.vgpio_state = read_pin(1, &hl7800_cfg.gpio[MDM_VGPIO]);
4875 HL7800_IO_DBG_LOG("VGPIO:%d", iface_ctx.vgpio_state);
4876 if (!iface_ctx.vgpio_state) {
4877 prepare_io_for_reset();
4878 if (!iface_ctx.restarting && iface_ctx.initialized) {
4879 iface_ctx.reconfig_IP_connection = true;
4880 }
4881 } else {
4882 if (iface_ctx.off) {
4883 return;
4884 }
4885 /* The peripheral must be enabled in ISR context
4886 * because the driver may be
4887 * waiting for +KSUP or waiting to send commands.
4888 * This can occur, for example, during a modem reset.
4889 */
4890 power_on_uart();
4891 /* Keep the modem awake to see if it has anything to send to us. */
4892 allow_sleep(false);
4893 /* Allow the modem to go back to sleep if it was the one who
4894 * sourced the transition.
4895 */
4896 allow_sleep(true);
4897 }
4898 check_hl7800_awake();
4899
4900 /* When the network state changes a semaphore must be taken.
4901 * This can't be done in interrupt context because the wait time != 0.
4902 */
4903 k_work_submit_to_queue(&hl7800_workq, &iface_ctx.mdm_vgpio_work);
4904 }
4905
mdm_uart_dsr_callback_isr(const struct device * port,struct gpio_callback * cb,uint32_t pins)4906 void mdm_uart_dsr_callback_isr(const struct device *port, struct gpio_callback *cb, uint32_t pins)
4907 {
4908 ARG_UNUSED(port);
4909 ARG_UNUSED(cb);
4910 ARG_UNUSED(pins);
4911
4912 iface_ctx.dsr_state = read_pin(1, &hl7800_cfg.gpio[MDM_UART_DSR]);
4913 HL7800_IO_DBG_LOG("MDM_UART_DSR:%d", iface_ctx.dsr_state);
4914 }
4915
4916 #ifdef CONFIG_MODEM_HL7800_LOW_POWER_MODE
mark_sockets_for_reconfig(void)4917 static void mark_sockets_for_reconfig(void)
4918 {
4919 int i;
4920 struct hl7800_socket *sock = NULL;
4921
4922 for (i = 0; i < MDM_MAX_SOCKETS; i++) {
4923 sock = &iface_ctx.sockets[i];
4924 if ((sock->context != NULL) && (sock->created)) {
4925 /* mark socket as possibly needing re-configuration */
4926 sock->reconfig = true;
4927 }
4928 }
4929 }
4930 #endif
4931
mdm_gpio6_callback_isr(const struct device * port,struct gpio_callback * cb,uint32_t pins)4932 void mdm_gpio6_callback_isr(const struct device *port, struct gpio_callback *cb, uint32_t pins)
4933 {
4934 ARG_UNUSED(port);
4935 ARG_UNUSED(cb);
4936 ARG_UNUSED(pins);
4937
4938 iface_ctx.gpio6_state = read_pin(1, &hl7800_cfg.gpio[MDM_GPIO6]);
4939 HL7800_IO_DBG_LOG("MDM_GPIO6:%d", iface_ctx.gpio6_state);
4940 #ifdef CONFIG_MODEM_HL7800_LOW_POWER_MODE
4941 if (!iface_ctx.gpio6_state) {
4942 /* HL7800 is not awake, shut down UART to save power */
4943 shutdown_uart();
4944 iface_ctx.wait_for_KSUP = true;
4945 iface_ctx.wait_for_KSUP_tries = 0;
4946 iface_ctx.reconfig_IP_connection = true;
4947 mark_sockets_for_reconfig();
4948 } else {
4949 if (iface_ctx.off) {
4950 return;
4951 } else if (iface_ctx.vgpio_state) {
4952 power_on_uart();
4953 /* Keep the modem awake to see if it has anything to send to us. */
4954 allow_sleep(false);
4955 /* Allow the modem to go back to sleep if it was the one who
4956 * sourced the transition.
4957 */
4958 allow_sleep(true);
4959 }
4960 }
4961 check_hl7800_awake();
4962
4963 if ((iface_ctx.gpio6_callback != NULL) &&
4964 ((iface_ctx.desired_sleep_level == HL7800_SLEEP_HIBERNATE) ||
4965 (iface_ctx.desired_sleep_level == HL7800_SLEEP_LITE_HIBERNATE))) {
4966 iface_ctx.gpio6_callback(iface_ctx.gpio6_state);
4967 }
4968 #endif
4969 }
4970
mdm_uart_cts_callback_isr(const struct device * port,struct gpio_callback * cb,uint32_t pins)4971 void mdm_uart_cts_callback_isr(const struct device *port, struct gpio_callback *cb, uint32_t pins)
4972 {
4973 ARG_UNUSED(port);
4974 ARG_UNUSED(cb);
4975 ARG_UNUSED(pins);
4976 uint64_t now;
4977 uint64_t elapsed;
4978 int resample_state;
4979
4980 iface_ctx.cts_state = read_pin(0, &hl7800_cfg.gpio[MDM_UART_CTS]);
4981
4982 /* Debounce the CTS signal */
4983 now = k_ticks_to_us_floor64(k_uptime_ticks());
4984 elapsed = now - iface_ctx.last_cts_time;
4985 if (iface_ctx.last_cts_time <= 0) {
4986 /* This is the first transition we have seen, continue */
4987 } else if (elapsed <= CONFIG_MODEM_HL7800_CTS_FILTER_US) {
4988 /* CTS changed too quickly, ignore this transition */
4989 iface_ctx.last_cts_time = now;
4990 return;
4991 }
4992 iface_ctx.last_cts_time = now;
4993 k_busy_wait(CONFIG_MODEM_HL7800_CTS_FILTER_US);
4994 resample_state = read_pin(0, &hl7800_cfg.gpio[MDM_UART_CTS]);
4995 if (iface_ctx.cts_state != resample_state) {
4996 /* CTS changed while we were debouncing, ignore it */
4997 iface_ctx.cts_state = resample_state;
4998 return;
4999 }
5000 iface_ctx.cts_state = resample_state;
5001 if (iface_ctx.cts_state != iface_ctx.last_cts_state) {
5002 iface_ctx.last_cts_state = iface_ctx.cts_state;
5003 } else {
5004 return;
5005 }
5006
5007 HL7800_IO_DBG_LOG("MDM_UART_CTS:%d(%llu)", iface_ctx.cts_state, elapsed);
5008
5009 #ifdef CONFIG_MODEM_HL7800_LOW_POWER_MODE
5010 if (iface_ctx.cts_state && iface_ctx.allow_sleep) {
5011 /* HL7800 cannot receive UART data, shut down UART to save power.
5012 * This is critical for proper low power operation. If the UART is disabled
5013 * after VGPIO is low, the UART will not suspend properly.
5014 */
5015 shutdown_uart();
5016 } else {
5017 if (iface_ctx.off) {
5018 return;
5019 }
5020 if (iface_ctx.vgpio_state && iface_ctx.gpio6_state) {
5021 power_on_uart();
5022 /* Wake up the modem to see if it has anything to send to us. */
5023 allow_sleep(false);
5024 /* Allow the modem to go back to sleep if it was the one who
5025 * sourced the CTS transition.
5026 */
5027 allow_sleep(true);
5028 }
5029 }
5030 #endif
5031
5032 if ((iface_ctx.cts_callback != NULL) &&
5033 (iface_ctx.desired_sleep_level == HL7800_SLEEP_SLEEP)) {
5034 iface_ctx.cts_callback(iface_ctx.cts_state);
5035 }
5036
5037 check_hl7800_awake();
5038 }
5039
modem_reset(void)5040 static void modem_reset(void)
5041 {
5042 prepare_io_for_reset();
5043
5044 LOG_INF("Modem Reset");
5045 /* Hard reset the modem */
5046 modem_assert_reset(true);
5047 /* >20 milliseconds required for reset low */
5048 k_sleep(MDM_RESET_LOW_TIME);
5049
5050 iface_ctx.mdm_startup_reporting_on = false;
5051 set_sleep_state(HL7800_SLEEP_UNINITIALIZED);
5052 check_hl7800_awake();
5053 set_network_state(HL7800_NOT_REGISTERED);
5054 set_startup_state(HL7800_STARTUP_STATE_UNKNOWN);
5055 #ifdef CONFIG_MODEM_HL7800_FW_UPDATE
5056 if (iface_ctx.fw_update_state == HL7800_FOTA_REBOOT_AND_RECONFIGURE) {
5057 set_fota_state(HL7800_FOTA_COMPLETE);
5058 } else {
5059 set_fota_state(HL7800_FOTA_IDLE);
5060 }
5061 #endif
5062 k_sem_reset(&iface_ctx.mdm_awake);
5063 iface_ctx.off = true;
5064 }
5065
modem_run(void)5066 static void modem_run(void)
5067 {
5068 LOG_INF("Modem Run");
5069 iface_ctx.off = false;
5070 modem_assert_reset(false);
5071 allow_sleep(false);
5072 k_sleep(MDM_RESET_HIGH_TIME);
5073 }
5074
modem_boot_handler(char * reason)5075 static int modem_boot_handler(char *reason)
5076 {
5077 int ret;
5078
5079 LOG_DBG("%s", reason);
5080 ret = k_sem_take(&iface_ctx.mdm_awake, MDM_BOOT_TIME);
5081 if (ret) {
5082 LOG_WRN("Err waiting for boot: %d, DSR: %u", ret, iface_ctx.dsr_state);
5083 } else {
5084 LOG_INF("Modem booted!");
5085 }
5086
5087 /* Turn OFF EPS network registration status reporting because
5088 * it isn't needed until after initialization is complete.
5089 */
5090 SEND_AT_CMD_EXPECT_OK("AT+CEREG=0");
5091
5092 /* Determine if echo is on/off by reading the profile
5093 * note: It wasn't clear how to read the
5094 * active profile so all 3 are read.
5095 */
5096 iface_ctx.mdm_echo_is_on = true;
5097 SEND_AT_CMD_EXPECT_OK("AT&V");
5098
5099 if (iface_ctx.mdm_echo_is_on) {
5100 /* Turn OFF echo (after boot/reset) because a profile
5101 * hasn't been saved yet
5102 */
5103 SEND_AT_CMD_EXPECT_OK("ATE0");
5104
5105 /* Save profile 0 */
5106 SEND_AT_CMD_EXPECT_OK("AT&W");
5107
5108 /* Reread profiles so echo state can be checked again. */
5109 SEND_AT_CMD_EXPECT_OK("AT&V");
5110 }
5111
5112 __ASSERT(!iface_ctx.mdm_echo_is_on, "Echo should be off");
5113
5114 return 0;
5115
5116 error:
5117 return ret;
5118 }
5119
5120 /**
5121 * @brief compares two version strings with any delimiter
5122 *
5123 * @param v1: version string 1
5124 * @param v2: version string 2
5125 *
5126 * @retval 0 if equal, < 0 if v1 < v2, > 0 if v1 > v2.
5127 */
compare_versions(char * v1,const char * v2)5128 static int compare_versions(char *v1, const char *v2)
5129 {
5130 int result = 0;
5131 char *tail1;
5132 char *tail2;
5133 unsigned long ver1, ver2;
5134
5135 /* loop through each level of the version string */
5136 while (result == 0) {
5137 /* extract leading version numbers */
5138 ver1 = strtoul(v1, &tail1, 10);
5139 ver2 = strtoul(v2, &tail2, 10);
5140
5141 /* if numbers differ, then set the result */
5142 if (ver1 < ver2) {
5143 result = -1;
5144 } else if (ver1 > ver2) {
5145 result = 1;
5146 } else {
5147 /* if numbers are the same, go to next level */
5148 v1 = tail1;
5149 v2 = tail2;
5150 /* if we reach the end of both, then they are identical */
5151 if (*v1 == '\0' && *v2 == '\0') {
5152 break;
5153 /* if we reach the end of one only, it is the smaller */
5154 } else if (*v1 == '\0') {
5155 result = -1;
5156 } else if (*v2 == '\0') {
5157 result = 1;
5158 /* not at end ... so far they match so keep going */
5159 } else {
5160 v1++;
5161 v2++;
5162 }
5163 }
5164 }
5165 return result;
5166 }
5167
setup_gprs_connection(char * access_point_name)5168 static int setup_gprs_connection(char *access_point_name)
5169 {
5170 char cmd_string[sizeof("AT+KCNXCFG=1,\"GPRS\",\"\",,,"
5171 "\"IPV4V6\"") +
5172 MDM_HL7800_APN_MAX_SIZE];
5173 int cmd_max_len = sizeof(cmd_string) - 1;
5174
5175 memset(cmd_string, 0, cmd_max_len);
5176 strncat(cmd_string, "AT+KCNXCFG=1,\"GPRS\",\"", cmd_max_len);
5177 strncat(cmd_string, access_point_name, cmd_max_len);
5178 strncat(cmd_string, "\",,,\"", cmd_max_len);
5179 strncat(cmd_string, MODEM_HL7800_ADDRESS_FAMILY "\"", cmd_max_len);
5180 return send_at_cmd(NULL, cmd_string, MDM_CMD_SEND_TIMEOUT, 0, false);
5181 }
5182
set_bands(const char * bands,bool full_reboot)5183 static int set_bands(const char *bands, bool full_reboot)
5184 {
5185 int ret;
5186 char cmd[sizeof("AT+KBNDCFG=#,####################")];
5187
5188 snprintk(cmd, sizeof(cmd), "AT+KBNDCFG=%d,%s", iface_ctx.mdm_rat, bands);
5189 ret = send_at_cmd(NULL, cmd, MDM_CMD_SEND_TIMEOUT, MDM_DEFAULT_AT_CMD_RETRIES, false);
5190 if (ret < 0) {
5191 return ret;
5192 }
5193
5194 if (!full_reboot) {
5195 ret = send_at_cmd(NULL, "AT+CFUN=1,1", MDM_CMD_SEND_TIMEOUT,
5196 MDM_DEFAULT_AT_CMD_RETRIES, false);
5197 if (ret < 0) {
5198 return ret;
5199 }
5200
5201 ret = modem_boot_handler("LTE bands were just set");
5202 } else {
5203 k_work_reschedule_for_queue(&hl7800_workq, &iface_ctx.mdm_reset_work, K_NO_WAIT);
5204 }
5205 return ret;
5206 }
5207
mdm_hl7800_set_bands(const char * bands)5208 int32_t mdm_hl7800_set_bands(const char *bands)
5209 {
5210 int ret, i;
5211 char temp_bands[MDM_BAND_BITMAP_STR_LENGTH_MAX + 1];
5212 int num_leading_zeros;
5213
5214 if ((bands == NULL) || (strlen(bands) > MDM_BAND_BITMAP_STR_LENGTH_MAX) ||
5215 (strlen(bands) < MDM_BAND_BITMAP_STR_LENGTH_MIN)) {
5216 return -EINVAL;
5217 }
5218
5219 if (strlen(bands) < MDM_BAND_BITMAP_STR_LENGTH_MAX) {
5220 num_leading_zeros = MDM_BAND_BITMAP_STR_LENGTH_MAX - strlen(bands);
5221 for (i = 0; i < num_leading_zeros; i++) {
5222 temp_bands[i] = '0';
5223 if (i == (num_leading_zeros - 1)) {
5224 strncpy(temp_bands + (i + 1), bands, sizeof(temp_bands) - (i + 1));
5225 }
5226 }
5227 } else {
5228 memcpy(temp_bands, bands, sizeof(temp_bands));
5229 }
5230
5231 /* no need to set bands if settings match */
5232 if (strncmp(temp_bands, iface_ctx.mdm_bands_string, sizeof(temp_bands)) == 0) {
5233 return 0;
5234 }
5235
5236 hl7800_lock();
5237
5238 ret = set_bands(temp_bands, true);
5239
5240 hl7800_unlock();
5241
5242 return ret;
5243 }
5244
modem_reset_and_configure(void)5245 static int modem_reset_and_configure(void)
5246 {
5247 int ret = 0;
5248 bool sleep = false;
5249 bool config_apn = false;
5250 char *apn;
5251 #ifdef CONFIG_MODEM_HL7800_EDRX
5252 int edrx_act_type;
5253 char set_edrx_msg[sizeof("AT+CEDRXS=2,4,\"0000\"")];
5254 #endif
5255 #if CONFIG_MODEM_HL7800_CONFIGURE_BANDS
5256 uint16_t bands_top = 0;
5257 uint32_t bands_middle = 0, bands_bottom = 0;
5258 char new_bands[MDM_BAND_BITMAP_STR_LENGTH_MAX + 1];
5259 #endif
5260 #if CONFIG_MODEM_HL7800_PSM
5261 const char TURN_ON_PSM[] =
5262 "AT+CPSMS=1,,,\"" CONFIG_MODEM_HL7800_PSM_PERIODIC_TAU
5263 "\",\"" CONFIG_MODEM_HL7800_PSM_ACTIVE_TIME "\"";
5264 #endif
5265
5266 set_busy(true);
5267 iface_ctx.restarting = true;
5268 iface_ctx.dns_ready = false;
5269 if (iface_ctx.iface) {
5270 net_if_carrier_off(iface_ctx.iface);
5271 }
5272
5273 hl7800_stop_rssi_work();
5274 initialize_sleep_level();
5275
5276 reboot:
5277 modem_reset();
5278 modem_run();
5279 ret = modem_boot_handler("Initialization");
5280 if (!iface_ctx.mdm_startup_reporting_on) {
5281 /* Turn on mobile start-up reporting for next reset.
5282 * It will indicate if SIM is present.
5283 * Its value is saved in non-volatile memory on the HL7800.
5284 */
5285 SEND_AT_CMD_EXPECT_OK("AT+KSREP=1");
5286 goto reboot;
5287 } else if (ret < 0) {
5288 goto error;
5289 }
5290
5291 /* turn on numeric error codes */
5292 SEND_AT_CMD_EXPECT_OK("AT+CMEE=1");
5293
5294 /* modem revision */
5295 SEND_COMPLEX_AT_CMD("AT+CGMR");
5296
5297 /* determine RAT command support */
5298 ret = compare_versions(iface_ctx.mdm_revision, NEW_RAT_CMD_MIN_VERSION);
5299 if (ret < 0) {
5300 iface_ctx.new_rat_cmd_support = false;
5301 } else {
5302 iface_ctx.new_rat_cmd_support = true;
5303 }
5304
5305 /* Query current Radio Access Technology (RAT) */
5306 SEND_AT_CMD_EXPECT_OK("AT+KSRAT?");
5307
5308 /* If CONFIG_MODEM_HL7800_RAT_M1 or CONFIG_MODEM_HL7800_RAT_NB1, then
5309 * set the radio mode. This is only done here if the driver has not been
5310 * initialized (!iface_ctx.configured) yet because the public API also
5311 * allows the RAT to be changed (and will reset the modem).
5312 */
5313 #ifndef CONFIG_MODEM_HL7800_RAT_NO_CHANGE
5314 if (!iface_ctx.configured) {
5315 #if CONFIG_MODEM_HL7800_RAT_M1
5316 if (iface_ctx.mdm_rat != MDM_RAT_CAT_M1) {
5317 if (iface_ctx.new_rat_cmd_support) {
5318 SEND_AT_CMD_ONCE_EXPECT_OK(SET_RAT_M1_CMD);
5319 } else {
5320 SEND_AT_CMD_ONCE_EXPECT_OK(
5321 SET_RAT_M1_CMD_LEGACY);
5322 }
5323 if (ret >= 0) {
5324 goto reboot;
5325 }
5326 }
5327 #elif CONFIG_MODEM_HL7800_RAT_NB1
5328 if (iface_ctx.mdm_rat != MDM_RAT_CAT_NB1) {
5329 if (iface_ctx.new_rat_cmd_support) {
5330 SEND_AT_CMD_ONCE_EXPECT_OK(SET_RAT_NB1_CMD);
5331 } else {
5332 SEND_AT_CMD_ONCE_EXPECT_OK(
5333 SET_RAT_NB1_CMD_LEGACY);
5334 }
5335
5336 if (ret >= 0) {
5337 goto reboot;
5338 }
5339 }
5340 #endif
5341 }
5342 #endif
5343
5344 SEND_AT_CMD_EXPECT_OK("AT+KBNDCFG?");
5345 /* Configure LTE bands */
5346 #if CONFIG_MODEM_HL7800_CONFIGURE_BANDS
5347 #if CONFIG_MODEM_HL7800_BAND_1
5348 bands_bottom |= 1 << 0;
5349 #endif
5350 #if CONFIG_MODEM_HL7800_BAND_2
5351 bands_bottom |= 1 << 1;
5352 #endif
5353 #if CONFIG_MODEM_HL7800_BAND_3
5354 bands_bottom |= 1 << 2;
5355 #endif
5356 #if CONFIG_MODEM_HL7800_BAND_4
5357 bands_bottom |= 1 << 3;
5358 #endif
5359 #if CONFIG_MODEM_HL7800_BAND_5
5360 bands_bottom |= 1 << 4;
5361 #endif
5362 #if CONFIG_MODEM_HL7800_BAND_8
5363 bands_bottom |= 1 << 7;
5364 #endif
5365 #if CONFIG_MODEM_HL7800_BAND_9
5366 bands_bottom |= 1 << 8;
5367 #endif
5368 #if CONFIG_MODEM_HL7800_BAND_10
5369 bands_bottom |= 1 << 9;
5370 #endif
5371 #if CONFIG_MODEM_HL7800_BAND_12
5372 bands_bottom |= 1 << 11;
5373 #endif
5374 #if CONFIG_MODEM_HL7800_BAND_13
5375 bands_bottom |= 1 << 12;
5376 #endif
5377 #if CONFIG_MODEM_HL7800_BAND_14
5378 bands_bottom |= 1 << 13;
5379 #endif
5380 #if CONFIG_MODEM_HL7800_BAND_17
5381 bands_bottom |= 1 << 16;
5382 #endif
5383 #if CONFIG_MODEM_HL7800_BAND_18
5384 bands_bottom |= 1 << 17;
5385 #endif
5386 #if CONFIG_MODEM_HL7800_BAND_19
5387 bands_bottom |= 1 << 18;
5388 #endif
5389 #if CONFIG_MODEM_HL7800_BAND_20
5390 bands_bottom |= 1 << 19;
5391 #endif
5392 #if CONFIG_MODEM_HL7800_BAND_25
5393 bands_bottom |= 1 << 24;
5394 #endif
5395 #if CONFIG_MODEM_HL7800_BAND_26
5396 bands_bottom |= 1 << 25;
5397 #endif
5398 #if CONFIG_MODEM_HL7800_BAND_27
5399 bands_bottom |= 1 << 26;
5400 #endif
5401 #if CONFIG_MODEM_HL7800_BAND_28
5402 bands_bottom |= 1 << 27;
5403 #endif
5404 #if CONFIG_MODEM_HL7800_BAND_66
5405 bands_top |= 1 << 1;
5406 #endif
5407
5408 /* Check if bands are configured correctly */
5409 if (iface_ctx.mdm_bands_top != bands_top ||
5410 iface_ctx.mdm_bands_middle != bands_middle ||
5411 iface_ctx.mdm_bands_bottom != bands_bottom) {
5412 if (iface_ctx.mdm_bands_top != bands_top) {
5413 LOG_INF("Top band mismatch, want %04x got %04x",
5414 bands_top, iface_ctx.mdm_bands_top);
5415 }
5416 if (iface_ctx.mdm_bands_middle != bands_middle) {
5417 LOG_INF("Middle band mismatch, want %08x got %08x",
5418 bands_middle, iface_ctx.mdm_bands_middle);
5419 }
5420 if (iface_ctx.mdm_bands_bottom != bands_bottom) {
5421 LOG_INF("Bottom band mismatch, want %08x got %08x",
5422 bands_bottom, iface_ctx.mdm_bands_bottom);
5423 }
5424
5425 snprintk(new_bands, sizeof(new_bands),
5426 "%0" STRINGIFY(MDM_TOP_BAND_SIZE) "x%0" STRINGIFY(
5427 MDM_MIDDLE_BAND_SIZE) "x%0" STRINGIFY(MDM_BOTTOM_BAND_SIZE) "x",
5428 bands_top, bands_middle, bands_bottom);
5429
5430 ret = set_bands(new_bands, false);
5431 if (ret < 0) {
5432 goto error;
5433 }
5434 }
5435 #endif
5436
5437 /**
5438 * Disable the radio until all config is done.
5439 * This ensures all settings are applied during this session instead of on the next reboot.
5440 */
5441 SEND_AT_CMD_EXPECT_OK("AT+CFUN=4,0");
5442
5443 iface_ctx.low_power_mode = HL7800_LPM_NONE;
5444 #ifdef CONFIG_MODEM_HL7800_LOW_POWER_MODE
5445 /* enable GPIO6 low power monitoring */
5446 SEND_AT_CMD_EXPECT_OK("AT+KHWIOCFG=3,1,6");
5447
5448 ret = set_sleep_level();
5449 if (ret < 0) {
5450 goto error;
5451 }
5452
5453 #if CONFIG_MODEM_HL7800_PSM
5454 iface_ctx.low_power_mode = HL7800_LPM_PSM;
5455 /* Turn off eDRX */
5456 SEND_AT_CMD_EXPECT_OK("AT+CEDRXS=0");
5457
5458 SEND_AT_CMD_EXPECT_OK(TURN_ON_PSM);
5459 #elif CONFIG_MODEM_HL7800_EDRX
5460 iface_ctx.low_power_mode = HL7800_LPM_EDRX;
5461 /* Turn off PSM */
5462 SEND_AT_CMD_EXPECT_OK("AT+CPSMS=0");
5463
5464 /* turn on eDRX */
5465 if (iface_ctx.mdm_rat == MDM_RAT_CAT_NB1) {
5466 edrx_act_type = 5;
5467 } else {
5468 edrx_act_type = 4;
5469 }
5470 snprintk(set_edrx_msg, sizeof(set_edrx_msg), "AT+CEDRXS=1,%d,\"%s\"",
5471 edrx_act_type, CONFIG_MODEM_HL7800_EDRX_VALUE);
5472 SEND_AT_CMD_EXPECT_OK(set_edrx_msg);
5473 #endif
5474 sleep = true;
5475 #else
5476 /* Turn off sleep mode */
5477 SEND_AT_CMD_EXPECT_OK("AT+KSLEEP=2");
5478
5479 /* Turn off PSM */
5480 SEND_AT_CMD_EXPECT_OK("AT+CPSMS=0");
5481
5482 /* Turn off eDRX */
5483 SEND_AT_CMD_EXPECT_OK("AT+CEDRXS=0");
5484 #endif
5485
5486 /* modem manufacturer */
5487 SEND_COMPLEX_AT_CMD("AT+CGMI");
5488
5489 /* modem model */
5490 SEND_COMPLEX_AT_CMD("AT+CGMM");
5491
5492 /* query modem IMEI */
5493 SEND_COMPLEX_AT_CMD("AT+CGSN");
5494
5495 /* query modem serial number */
5496 SEND_COMPLEX_AT_CMD("AT+KGSN=3");
5497
5498 if (iface_ctx.mdm_startup_state != HL7800_STARTUP_STATE_SIM_NOT_PRESENT) {
5499 /* query SIM ICCID */
5500 SEND_AT_CMD_IGNORE_ERROR("AT+CCID?");
5501
5502 /* query SIM IMSI */
5503 (void)send_at_cmd(NULL, "AT+CIMI", MDM_CMD_SEND_TIMEOUT, MDM_DEFAULT_AT_CMD_RETRIES,
5504 true);
5505 }
5506
5507 /* Query PDP context to get APN */
5508 SEND_AT_CMD_EXPECT_OK("AT+CGDCONT?");
5509 apn = iface_ctx.mdm_apn.value;
5510 if (strcmp(iface_ctx.mdm_pdp_addr_fam, MODEM_HL7800_ADDRESS_FAMILY)) {
5511 config_apn = true;
5512 }
5513
5514 /* Query PDP authentication context to get APN username/password.
5515 * Temporary Workaround - Ignore error
5516 * On some modules this is returning an error and the response data.
5517 */
5518 SEND_AT_CMD_IGNORE_ERROR("AT+WPPP?");
5519
5520 #if CONFIG_MODEM_HL7800_SET_APN_NAME_ON_STARTUP
5521 if (!iface_ctx.configured) {
5522 if (strncmp(iface_ctx.mdm_apn.value, CONFIG_MODEM_HL7800_APN_NAME,
5523 MDM_HL7800_APN_MAX_STRLEN) != 0) {
5524 apn = CONFIG_MODEM_HL7800_APN_NAME;
5525 config_apn = true;
5526 }
5527 }
5528 #endif
5529
5530 if (config_apn) {
5531 /* set PDP context address family along with current APN */
5532 ret = write_apn(apn);
5533 if (ret < 0) {
5534 goto error;
5535 }
5536 SEND_AT_CMD_EXPECT_OK("AT+CGDCONT?");
5537 }
5538
5539 ret = setup_gprs_connection(iface_ctx.mdm_apn.value);
5540 if (ret < 0) {
5541 goto error;
5542 }
5543
5544 /* query the network status in case we already registered */
5545 SEND_COMPLEX_AT_CMD("AT+CEREG?");
5546
5547 /* Turn on EPS network registration status reporting */
5548 SEND_AT_CMD_EXPECT_OK("AT+CEREG=5");
5549
5550 /* query all socket configs to cleanup any sockets that are not
5551 * tracked by the driver
5552 */
5553 SEND_AT_CMD_EXPECT_OK("AT+KTCPCFG?");
5554 SEND_AT_CMD_EXPECT_OK("AT+KUDPCFG?");
5555
5556 /* Enabled the LTE radio */
5557 #if !defined(CONFIG_MODEM_HL7800_BOOT_IN_AIRPLANE_MODE)
5558 SEND_AT_CMD_EXPECT_OK("AT+CFUN=1,0");
5559 #endif
5560
5561 /* The modem has been initialized and now the network interface can be
5562 * started in the CEREG message handler.
5563 */
5564 LOG_INF("Modem ready!");
5565 iface_ctx.restarting = false;
5566 iface_ctx.configured = true;
5567 set_busy(false);
5568 allow_sleep(sleep);
5569 /* trigger APN update event */
5570 event_handler(HL7800_EVENT_APN_UPDATE, &iface_ctx.mdm_apn);
5571
5572 #ifdef CONFIG_MODEM_HL7800_BOOT_DELAY
5573 if (!iface_ctx.initialized) {
5574 if (iface_ctx.iface != NULL) {
5575 hl7800_build_mac(&iface_ctx);
5576 net_if_set_link_addr(iface_ctx.iface, iface_ctx.mac_addr,
5577 sizeof(iface_ctx.mac_addr),
5578 NET_LINK_ETHERNET);
5579 iface_ctx.initialized = true;
5580 }
5581 }
5582 #endif
5583
5584 return 0;
5585
5586 error:
5587 LOG_ERR("Unable to configure modem");
5588 iface_ctx.configured = false;
5589 set_network_state(HL7800_UNABLE_TO_CONFIGURE);
5590 /* Kernel will fault with non-zero return value.
5591 * Allow other parts of application to run when modem cannot be configured.
5592 */
5593 return 0;
5594 }
5595
write_apn(char * access_point_name)5596 static int write_apn(char *access_point_name)
5597 {
5598 char cmd_string[MDM_HL7800_APN_CMD_MAX_SIZE];
5599
5600 /* PDP Context */
5601 memset(cmd_string, 0, MDM_HL7800_APN_CMD_MAX_SIZE);
5602 if (strcmp(MODEM_HL7800_ADDRESS_FAMILY, ADDRESS_FAMILY_IPV4)) {
5603 strncat(cmd_string, "AT+CGDCONT=1,\"" MODEM_HL7800_ADDRESS_FAMILY "\",\"",
5604 MDM_HL7800_APN_CMD_MAX_STRLEN);
5605 } else {
5606 strncat(cmd_string, "AT+CGDCONT=1,\"" ADDRESS_FAMILY_IP "\",\"",
5607 MDM_HL7800_APN_CMD_MAX_STRLEN);
5608 }
5609 strncat(cmd_string, access_point_name, MDM_HL7800_APN_CMD_MAX_STRLEN);
5610 strncat(cmd_string, "\"", MDM_HL7800_APN_CMD_MAX_STRLEN);
5611 return send_at_cmd(NULL, cmd_string, MDM_CMD_SEND_TIMEOUT, 0, false);
5612 }
5613
mdm_reset_work_callback(struct k_work * item)5614 static void mdm_reset_work_callback(struct k_work *item)
5615 {
5616 ARG_UNUSED(item);
5617
5618 mdm_hl7800_reset();
5619 }
5620
mdm_hl7800_reset(void)5621 int32_t mdm_hl7800_reset(void)
5622 {
5623 int ret;
5624
5625 hl7800_lock();
5626
5627 ret = modem_reset_and_configure();
5628
5629 hl7800_unlock();
5630
5631 return ret;
5632 }
5633
mdm_power_off_work_callback(struct k_work * item)5634 static void mdm_power_off_work_callback(struct k_work *item)
5635 {
5636 ARG_UNUSED(item);
5637 int ret;
5638 #if defined(CONFIG_DNS_RESOLVER)
5639 struct dns_resolve_context *dns_ctx;
5640
5641 LOG_DBG("Shutdown DNS resolver");
5642 dns_ctx = dns_resolve_get_default();
5643 (void)dns_resolve_close(dns_ctx);
5644 #endif
5645
5646 hl7800_lock();
5647
5648 notify_all_tcp_sockets_closed();
5649
5650 ret = send_at_cmd(NULL, "AT+CPOF", MDM_CMD_SEND_TIMEOUT, 1, false);
5651 if (ret) {
5652 LOG_ERR("AT+CPOF ret:%d", ret);
5653 return;
5654 }
5655 prepare_io_for_reset();
5656 iface_ctx.dns_ready = false;
5657 iface_ctx.configured = false;
5658 iface_ctx.off = true;
5659 set_busy(false);
5660 /* bring the iface down */
5661 if (iface_ctx.iface) {
5662 net_if_carrier_off(iface_ctx.iface);
5663 }
5664 LOG_INF("Modem powered off");
5665
5666 hl7800_unlock();
5667 }
5668
hl7800_power_off(void)5669 static int hl7800_power_off(void)
5670 {
5671 LOG_INF("Powering off modem");
5672 wakeup_hl7800();
5673 hl7800_stop_rssi_work();
5674 k_work_cancel_delayable(&iface_ctx.iface_status_work);
5675 k_work_cancel_delayable(&iface_ctx.dns_work);
5676 k_work_cancel_delayable(&iface_ctx.mdm_reset_work);
5677 k_work_cancel_delayable(&iface_ctx.allow_sleep_work);
5678 k_work_cancel_delayable(&iface_ctx.delete_untracked_socket_work);
5679 (void)k_work_submit_to_queue(&hl7800_workq, &iface_ctx.mdm_pwr_off_work);
5680
5681 return 0;
5682 }
5683
mdm_hl7800_power_off(void)5684 int32_t mdm_hl7800_power_off(void)
5685 {
5686 int rc;
5687
5688 hl7800_lock();
5689 rc = hl7800_power_off();
5690 hl7800_unlock();
5691
5692 return rc;
5693 }
5694
mdm_hl7800_register_event_callback(struct mdm_hl7800_callback_agent * agent)5695 int mdm_hl7800_register_event_callback(struct mdm_hl7800_callback_agent *agent)
5696 {
5697 int ret;
5698
5699 ret = k_sem_take(&cb_lock, K_NO_WAIT);
5700 if (ret < 0) {
5701 return ret;
5702 }
5703 if (!agent->event_callback) {
5704 LOG_WRN("event_callback is NULL");
5705 }
5706 sys_slist_append(&hl7800_event_callback_list, &agent->node);
5707 k_sem_give(&cb_lock);
5708
5709 return ret;
5710 }
5711
mdm_hl7800_unregister_event_callback(struct mdm_hl7800_callback_agent * agent)5712 int mdm_hl7800_unregister_event_callback(struct mdm_hl7800_callback_agent *agent)
5713 {
5714 int ret;
5715
5716 ret = k_sem_take(&cb_lock, K_NO_WAIT);
5717 if (ret < 0) {
5718 return ret;
5719 }
5720 ret = (int)sys_slist_find_and_remove(&hl7800_event_callback_list, &agent->node);
5721 if (ret) {
5722 ret = 0;
5723 } else {
5724 ret = -ENOENT;
5725 }
5726 k_sem_give(&cb_lock);
5727
5728 return ret;
5729 }
5730
5731 /*** OFFLOAD FUNCTIONS ***/
5732
connect_TCP_socket(struct hl7800_socket * sock)5733 static int connect_TCP_socket(struct hl7800_socket *sock)
5734 {
5735 int ret;
5736 char cmd_con[sizeof("AT+KTCPCNX=##")];
5737
5738 snprintk(cmd_con, sizeof(cmd_con), "AT+KTCPCNX=%d", sock->socket_id);
5739 ret = send_at_cmd(sock, cmd_con, MDM_CMD_SEND_TIMEOUT, 0, false);
5740 if (ret < 0) {
5741 LOG_ERR("AT+KTCPCNX ret:%d", ret);
5742 ret = -EIO;
5743 goto done;
5744 }
5745 /* Now wait for +KTCP_IND or +KTCP_NOTIF to ensure
5746 * the connection succeeded or failed.
5747 */
5748 ret = k_sem_take(&sock->sock_send_sem, MDM_CMD_CONN_TIMEOUT);
5749 if (ret == 0) {
5750 ret = sock->error;
5751 } else if (ret == -EAGAIN) {
5752 ret = -ETIMEDOUT;
5753 }
5754 if (ret < 0) {
5755 LOG_ERR("+KTCP_IND/NOTIF ret:%d", ret);
5756 goto done;
5757 } else {
5758 sock->state = SOCK_CONNECTED;
5759 net_context_set_state(sock->context, NET_CONTEXT_CONNECTED);
5760 }
5761 done:
5762 return ret;
5763 }
5764
configure_TCP_socket(struct hl7800_socket * sock)5765 static int configure_TCP_socket(struct hl7800_socket *sock)
5766 {
5767 int ret;
5768 char cmd_cfg[sizeof("AT+KTCPCFG=#,#,\"" IPV6_ADDR_FORMAT "\",#####,,,,#,,#")];
5769 int dst_port = -1;
5770 int af;
5771 bool restore_on_boot = false;
5772
5773 #ifdef CONFIG_MODEM_HL7800_LOW_POWER_MODE
5774 restore_on_boot = true;
5775 #endif
5776
5777 if (sock->dst.sa_family == AF_INET6) {
5778 af = MDM_HL7800_SOCKET_AF_IPV6;
5779 dst_port = net_sin6(&sock->dst)->sin6_port;
5780 } else if (sock->dst.sa_family == AF_INET) {
5781 af = MDM_HL7800_SOCKET_AF_IPV4;
5782 dst_port = net_sin(&sock->dst)->sin_port;
5783 } else {
5784 return -EINVAL;
5785 }
5786
5787 sock->socket_id = MDM_CREATE_SOCKET_ID;
5788
5789 snprintk(cmd_cfg, sizeof(cmd_cfg), "AT+KTCPCFG=%d,%d,\"%s\",%u,,,,%d,,%d", 1, 0,
5790 hl7800_sprint_ip_addr(&sock->dst), dst_port, af, restore_on_boot);
5791 ret = send_at_cmd(sock, cmd_cfg, MDM_CMD_SEND_TIMEOUT, 0, false);
5792 if (ret < 0) {
5793 LOG_ERR("AT+KTCPCFG ret:%d", ret);
5794 ret = -EIO;
5795 goto done;
5796 }
5797
5798 done:
5799 return ret;
5800 }
5801
configure_UDP_socket(struct hl7800_socket * sock)5802 static int configure_UDP_socket(struct hl7800_socket *sock)
5803 {
5804 int ret = 0;
5805 char cmd[sizeof("AT+KUDPCFG=1,0,,,,,0,#")];
5806 int af;
5807 bool restore_on_boot = false;
5808
5809 #ifdef CONFIG_MODEM_HL7800_LOW_POWER_MODE
5810 restore_on_boot = true;
5811 #endif
5812
5813 sock->socket_id = MDM_CREATE_SOCKET_ID;
5814
5815 if (sock->family == AF_INET) {
5816 af = MDM_HL7800_SOCKET_AF_IPV4;
5817 } else if (sock->family == AF_INET6) {
5818 af = MDM_HL7800_SOCKET_AF_IPV6;
5819 } else {
5820 return -EINVAL;
5821 }
5822
5823 snprintk(cmd, sizeof(cmd), "AT+KUDPCFG=1,0,,,,,%d,%d", af, restore_on_boot);
5824 ret = send_at_cmd(sock, cmd, MDM_CMD_SEND_TIMEOUT, 0, false);
5825 if (ret < 0) {
5826 LOG_ERR("AT+KUDPCFG ret:%d", ret);
5827 goto done;
5828 }
5829
5830 /* Now wait for +KUDP_IND or +KUDP_NOTIF to ensure
5831 * the socket was created.
5832 */
5833 ret = k_sem_take(&sock->sock_send_sem, MDM_CMD_CONN_TIMEOUT);
5834 if (ret == 0) {
5835 ret = sock->error;
5836 } else if (ret == -EAGAIN) {
5837 ret = -ETIMEDOUT;
5838 }
5839 if (ret < 0) {
5840 LOG_ERR("+KUDP_IND/NOTIF ret:%d", ret);
5841 goto done;
5842 }
5843 done:
5844 return ret;
5845 }
5846
reconfigure_IP_connection(void)5847 static int reconfigure_IP_connection(void)
5848 {
5849 int ret = 0;
5850
5851 if (iface_ctx.reconfig_IP_connection) {
5852 iface_ctx.reconfig_IP_connection = false;
5853
5854 /* reconfigure GPRS connection so sockets can be used */
5855 ret = setup_gprs_connection(iface_ctx.mdm_apn.value);
5856 if (ret < 0) {
5857 LOG_ERR("AT+KCNXCFG= ret:%d", ret);
5858 goto done;
5859 }
5860
5861 /* query all TCP socket configs */
5862 ret = send_at_cmd(NULL, "AT+KTCPCFG?", MDM_CMD_SEND_TIMEOUT, 0,
5863 false);
5864
5865 /* query all UDP socket configs */
5866 ret = send_at_cmd(NULL, "AT+KUDPCFG?", MDM_CMD_SEND_TIMEOUT, 0,
5867 false);
5868
5869 /* TODO: to make this better, wait for +KUDP_IND or timeout */
5870 k_sleep(K_SECONDS(1));
5871 }
5872
5873 done:
5874 return ret;
5875 }
5876
offload_get(sa_family_t family,enum net_sock_type type,enum net_ip_protocol ip_proto,struct net_context ** context)5877 static int offload_get(sa_family_t family, enum net_sock_type type,
5878 enum net_ip_protocol ip_proto,
5879 struct net_context **context)
5880 {
5881 int ret = 0;
5882 struct hl7800_socket *sock = NULL;
5883
5884 hl7800_lock();
5885 /* new socket */
5886 sock = socket_get();
5887 if (!sock) {
5888 ret = -ENOMEM;
5889 goto done;
5890 }
5891
5892 (*context)->offload_context = sock;
5893 /* set the context iface index to our iface */
5894 (*context)->iface = net_if_get_by_iface(iface_ctx.iface);
5895 sock->family = family;
5896 sock->type = type;
5897 sock->ip_proto = ip_proto;
5898 sock->context = *context;
5899 sock->reconfig = false;
5900 sock->created = false;
5901 sock->socket_id = MDM_CREATE_SOCKET_ID;
5902
5903 /* If UDP, create UDP socket now.
5904 * TCP socket needs to be created later once the
5905 * connection IP address is known.
5906 */
5907 if (type == SOCK_DGRAM) {
5908 wakeup_hl7800();
5909
5910 /* reconfig IP connection if necessary */
5911 (void)reconfigure_IP_connection();
5912
5913 if (!sock->created) {
5914 ret = configure_UDP_socket(sock);
5915 if (ret < 0) {
5916 socket_put(sock);
5917 goto done;
5918 }
5919 }
5920 }
5921 done:
5922 set_busy(false);
5923 allow_sleep(true);
5924 hl7800_unlock();
5925 return ret;
5926 }
5927
offload_bind(struct net_context * context,const struct sockaddr * addr,socklen_t addr_len)5928 static int offload_bind(struct net_context *context,
5929 const struct sockaddr *addr, socklen_t addr_len)
5930 {
5931 struct hl7800_socket *sock = NULL;
5932
5933 if (!context) {
5934 return -EINVAL;
5935 }
5936
5937 sock = (struct hl7800_socket *)context->offload_context;
5938 if (!sock) {
5939 LOG_ERR("Can't locate socket for net_ctx:%p!", context);
5940 return -EINVAL;
5941 }
5942
5943 /* save bind address information */
5944 sock->src.sa_family = addr->sa_family;
5945 #if defined(CONFIG_NET_IPV6)
5946 if (addr->sa_family == AF_INET6) {
5947 net_ipaddr_copy(&net_sin6(&sock->src)->sin6_addr,
5948 &net_sin6(addr)->sin6_addr);
5949 net_sin6(&sock->src)->sin6_port = net_sin6(addr)->sin6_port;
5950 } else
5951 #endif
5952 #if defined(CONFIG_NET_IPV4)
5953 if (addr->sa_family == AF_INET) {
5954 net_ipaddr_copy(&net_sin(&sock->src)->sin_addr,
5955 &net_sin(addr)->sin_addr);
5956 net_sin(&sock->src)->sin_port = net_sin(addr)->sin_port;
5957 } else
5958 #endif
5959 {
5960 return -EPFNOSUPPORT;
5961 }
5962
5963 return 0;
5964 }
5965
offload_listen(struct net_context * context,int backlog)5966 static int offload_listen(struct net_context *context, int backlog)
5967 {
5968 /* NOT IMPLEMENTED */
5969 return -ENOTSUP;
5970 }
5971
offload_connect(struct net_context * context,const struct sockaddr * addr,socklen_t addr_len,net_context_connect_cb_t cb,int32_t timeout,void * user_data)5972 static int offload_connect(struct net_context *context,
5973 const struct sockaddr *addr, socklen_t addr_len,
5974 net_context_connect_cb_t cb, int32_t timeout,
5975 void *user_data)
5976 {
5977 int ret = 0;
5978 int dst_port = -1;
5979 struct hl7800_socket *sock;
5980
5981 if (!context || !addr) {
5982 return -EINVAL;
5983 }
5984
5985 sock = (struct hl7800_socket *)context->offload_context;
5986 if (!sock) {
5987 LOG_ERR("Can't locate socket for net_ctx:%p!", context);
5988 return -EINVAL;
5989 }
5990
5991 if (sock->socket_id < 1) {
5992 LOG_ERR("Invalid socket_id(%d) for net_ctx:%p!",
5993 sock->socket_id, context);
5994 return -EINVAL;
5995 }
5996
5997 sock->dst.sa_family = addr->sa_family;
5998
5999 #if defined(CONFIG_NET_IPV6)
6000 if (addr->sa_family == AF_INET6) {
6001 net_ipaddr_copy(&net_sin6(&sock->dst)->sin6_addr,
6002 &net_sin6(addr)->sin6_addr);
6003 dst_port = ntohs(net_sin6(addr)->sin6_port);
6004 net_sin6(&sock->dst)->sin6_port = dst_port;
6005 } else
6006 #endif
6007 #if defined(CONFIG_NET_IPV4)
6008 if (addr->sa_family == AF_INET) {
6009 net_ipaddr_copy(&net_sin(&sock->dst)->sin_addr,
6010 &net_sin(addr)->sin_addr);
6011 dst_port = ntohs(net_sin(addr)->sin_port);
6012 net_sin(&sock->dst)->sin_port = dst_port;
6013 } else
6014 #endif
6015 {
6016 return -EINVAL;
6017 }
6018
6019 if (dst_port < 0) {
6020 LOG_ERR("Invalid port: %d", dst_port);
6021 return -EINVAL;
6022 }
6023
6024 hl7800_lock();
6025
6026 if (sock->type == SOCK_STREAM) {
6027 wakeup_hl7800();
6028
6029 reconfigure_IP_connection();
6030
6031 /* Configure/create TCP connection */
6032 if (!sock->created) {
6033 ret = configure_TCP_socket(sock);
6034 if (ret < 0) {
6035 goto done;
6036 }
6037 }
6038
6039 /* Connect to TCP */
6040 ret = connect_TCP_socket(sock);
6041 if (ret < 0) {
6042 goto done;
6043 }
6044 }
6045
6046 done:
6047 set_busy(false);
6048 allow_sleep(true);
6049 hl7800_unlock();
6050
6051 if (cb) {
6052 cb(context, ret, user_data);
6053 }
6054
6055 return ret;
6056 }
6057
offload_accept(struct net_context * context,net_tcp_accept_cb_t cb,int32_t timeout,void * user_data)6058 static int offload_accept(struct net_context *context, net_tcp_accept_cb_t cb,
6059 int32_t timeout, void *user_data)
6060 {
6061 /* NOT IMPLEMENTED */
6062 return -ENOTSUP;
6063 }
6064
offload_sendto(struct net_pkt * pkt,const struct sockaddr * dst_addr,socklen_t addr_len,net_context_send_cb_t cb,int32_t timeout,void * user_data)6065 static int offload_sendto(struct net_pkt *pkt, const struct sockaddr *dst_addr,
6066 socklen_t addr_len, net_context_send_cb_t cb,
6067 int32_t timeout, void *user_data)
6068 {
6069 struct net_context *context = net_pkt_context(pkt);
6070 struct hl7800_socket *sock;
6071 int ret, dst_port = 0;
6072
6073 if (!context) {
6074 return -EINVAL;
6075 }
6076
6077 sock = (struct hl7800_socket *)context->offload_context;
6078 if (!sock) {
6079 LOG_ERR("Can't locate socket for net_ctx:%p!", context);
6080 return -EINVAL;
6081 }
6082
6083 #if defined(CONFIG_NET_IPV6)
6084 if (dst_addr->sa_family == AF_INET6) {
6085 net_ipaddr_copy(&net_sin6(&sock->dst)->sin6_addr,
6086 &net_sin6(dst_addr)->sin6_addr);
6087 dst_port = ntohs(net_sin6(dst_addr)->sin6_port);
6088 net_sin6(&sock->dst)->sin6_port = dst_port;
6089 } else
6090 #endif
6091 #if defined(CONFIG_NET_IPV4)
6092 if (dst_addr->sa_family == AF_INET) {
6093 net_ipaddr_copy(&net_sin(&sock->dst)->sin_addr,
6094 &net_sin(dst_addr)->sin_addr);
6095 dst_port = ntohs(net_sin(dst_addr)->sin_port);
6096 net_sin(&sock->dst)->sin_port = dst_port;
6097 } else
6098 #endif
6099 {
6100 return -EINVAL;
6101 }
6102
6103 hl7800_lock();
6104
6105 wakeup_hl7800();
6106
6107 reconfigure_IP_connection();
6108
6109 ret = send_data(sock, pkt);
6110
6111 set_busy(false);
6112 allow_sleep(true);
6113 hl7800_unlock();
6114
6115 if (ret >= 0) {
6116 net_pkt_unref(pkt);
6117 }
6118
6119 if (cb) {
6120 cb(context, ret, user_data);
6121 }
6122
6123 return ret;
6124 }
6125
offload_send(struct net_pkt * pkt,net_context_send_cb_t cb,int32_t timeout,void * user_data)6126 static int offload_send(struct net_pkt *pkt, net_context_send_cb_t cb,
6127 int32_t timeout, void *user_data)
6128 {
6129 struct net_context *context = net_pkt_context(pkt);
6130 socklen_t addr_len;
6131
6132 addr_len = 0;
6133 #if defined(CONFIG_NET_IPV6)
6134 if (net_pkt_family(pkt) == AF_INET6) {
6135 addr_len = sizeof(struct sockaddr_in6);
6136 } else
6137 #endif /* CONFIG_NET_IPV6 */
6138 #if defined(CONFIG_NET_IPV4)
6139 if (net_pkt_family(pkt) == AF_INET) {
6140 addr_len = sizeof(struct sockaddr_in);
6141 } else
6142 #endif /* CONFIG_NET_IPV4 */
6143 {
6144 return -EPFNOSUPPORT;
6145 }
6146
6147 return offload_sendto(pkt, &context->remote, addr_len, cb, timeout,
6148 user_data);
6149 }
6150
offload_recv(struct net_context * context,net_context_recv_cb_t cb,int32_t timeout,void * user_data)6151 static int offload_recv(struct net_context *context, net_context_recv_cb_t cb,
6152 int32_t timeout, void *user_data)
6153 {
6154 struct hl7800_socket *sock;
6155
6156 if (!context) {
6157 return -EINVAL;
6158 }
6159
6160 sock = (struct hl7800_socket *)context->offload_context;
6161 if (!sock) {
6162 LOG_ERR("Can't locate socket for net_ctx:%p!", context);
6163 return -EINVAL;
6164 }
6165
6166 sock->recv_cb = cb;
6167 sock->recv_user_data = user_data;
6168
6169 return 0;
6170 }
6171
offload_put(struct net_context * context)6172 static int offload_put(struct net_context *context)
6173 {
6174 struct hl7800_socket *sock;
6175 char cmd[sizeof("AT+KTCPCLOSE=##")];
6176
6177 if (!context) {
6178 return -EINVAL;
6179 }
6180
6181 sock = (struct hl7800_socket *)context->offload_context;
6182 if (!sock) {
6183 /* socket was already closed? Exit quietly here. */
6184 return 0;
6185 }
6186
6187 /* cancel notif work if queued */
6188 k_work_cancel_delayable(&sock->notif_work);
6189
6190 hl7800_lock();
6191
6192 /* close connection */
6193 if (sock->type == SOCK_STREAM) {
6194 snprintk(cmd, sizeof(cmd), "AT+KTCPCLOSE=%d",
6195 sock->socket_id);
6196 } else {
6197 snprintk(cmd, sizeof(cmd), "AT+KUDPCLOSE=%d",
6198 sock->socket_id);
6199 }
6200
6201 wakeup_hl7800();
6202
6203 if ((sock->type == SOCK_DGRAM) || (sock->error != -ENOTCONN)) {
6204 send_at_cmd(sock, cmd, MDM_CMD_SEND_TIMEOUT, 0, false);
6205 }
6206
6207 if (sock->type == SOCK_STREAM) {
6208 /* delete session */
6209 delete_socket(sock, sock->type, sock->socket_id);
6210 }
6211 set_busy(false);
6212 allow_sleep(true);
6213
6214 socket_put(sock);
6215 net_context_unref(context);
6216 if (sock->type == SOCK_STREAM) {
6217 /* TCP contexts are referenced twice,
6218 * once for the app and once for the stack.
6219 * Since TCP stack is not used for offload,
6220 * unref a second time.
6221 */
6222 net_context_unref(context);
6223 }
6224
6225 hl7800_unlock();
6226
6227 return 0;
6228 }
6229
6230 static struct net_offload offload_funcs = {
6231 .get = offload_get,
6232 .bind = offload_bind,
6233 .listen = offload_listen,
6234 .connect = offload_connect,
6235 .accept = offload_accept,
6236 .send = offload_send,
6237 .sendto = offload_sendto,
6238 .recv = offload_recv,
6239 .put = offload_put,
6240 };
6241
6242 /* Use the last 6 digits of the IMEI as the mac address */
hl7800_build_mac(struct hl7800_iface_ctx * ictx)6243 static void hl7800_build_mac(struct hl7800_iface_ctx *ictx)
6244 {
6245 ictx->mac_addr[0] = ictx->mdm_imei[MDM_HL7800_IMEI_STRLEN - 6];
6246 ictx->mac_addr[1] = ictx->mdm_imei[MDM_HL7800_IMEI_STRLEN - 5];
6247 ictx->mac_addr[2] = ictx->mdm_imei[MDM_HL7800_IMEI_STRLEN - 4];
6248 ictx->mac_addr[3] = ictx->mdm_imei[MDM_HL7800_IMEI_STRLEN - 3];
6249 ictx->mac_addr[4] = ictx->mdm_imei[MDM_HL7800_IMEI_STRLEN - 2];
6250 ictx->mac_addr[5] = ictx->mdm_imei[MDM_HL7800_IMEI_STRLEN - 1];
6251 }
6252
6253 #ifdef CONFIG_MODEM_HL7800_FW_UPDATE
mdm_hl7800_update_fw(char * file_path)6254 int32_t mdm_hl7800_update_fw(char *file_path)
6255 {
6256 int ret = 0;
6257 struct fs_dirent file_info;
6258 char cmd1[sizeof("AT+WDSD=24643584")];
6259
6260 /* get file info */
6261 ret = fs_stat(file_path, &file_info);
6262 if (ret >= 0) {
6263 LOG_DBG("file '%s' size %zu", file_info.name, file_info.size);
6264 } else {
6265 LOG_ERR("Failed to get file [%s] info: %d", file_path, ret);
6266 goto err;
6267 }
6268
6269 ret = fs_open(&iface_ctx.fw_update_file, file_path, FS_O_READ);
6270 if (ret < 0) {
6271 LOG_ERR("%s open err: %d", file_path, ret);
6272 goto err;
6273 }
6274
6275 /* turn on device service indications */
6276 ret = send_at_cmd(NULL, "AT+WDSI=2", MDM_CMD_SEND_TIMEOUT, 0, false);
6277 if (ret < 0) {
6278 goto err;
6279 }
6280
6281 notify_all_tcp_sockets_closed();
6282 hl7800_stop_rssi_work();
6283 k_work_cancel_delayable(&iface_ctx.iface_status_work);
6284 k_work_cancel_delayable(&iface_ctx.dns_work);
6285 k_work_cancel_delayable(&iface_ctx.mdm_reset_work);
6286 k_work_cancel_delayable(&iface_ctx.allow_sleep_work);
6287 k_work_cancel_delayable(&iface_ctx.delete_untracked_socket_work);
6288 iface_ctx.dns_ready = false;
6289 if (iface_ctx.iface) {
6290 LOG_DBG("HL7800 iface DOWN");
6291 net_if_carrier_off(iface_ctx.iface);
6292 }
6293
6294 /* HL7800 will stay locked for the duration of the FW update */
6295 hl7800_lock();
6296
6297 /* start firmware update process */
6298 LOG_INF("Initiate FW update, total packets: %zd",
6299 ((file_info.size / XMODEM_DATA_SIZE) + 1));
6300 set_fota_state(HL7800_FOTA_START);
6301 (void)snprintk(cmd1, sizeof(cmd1), "AT+WDSD=%zd", file_info.size);
6302 (void)send_at_cmd(NULL, cmd1, K_NO_WAIT, 0, false);
6303
6304 err:
6305 return ret;
6306 }
6307 #endif
6308
hl7800_init(const struct device * dev)6309 static int hl7800_init(const struct device *dev)
6310 {
6311 int i, ret = 0;
6312 struct k_work_queue_config cfg = {
6313 .name = "hl7800_workq",
6314 };
6315 ARG_UNUSED(dev);
6316
6317 LOG_DBG("HL7800 Init");
6318
6319 /* The UART starts in the on state and CTS is set low by the HL7800 */
6320 iface_ctx.cts_state = iface_ctx.last_cts_state = 0;
6321
6322 /* Prevent the network interface from starting until
6323 * the modem has been initialized
6324 * because the modem may not have a valid SIM card.
6325 */
6326 iface_ctx.iface = net_if_get_default();
6327 if (iface_ctx.iface == NULL) {
6328 return -EIO;
6329 }
6330
6331 net_if_carrier_off(iface_ctx.iface);
6332
6333 /* init sockets */
6334 for (i = 0; i < MDM_MAX_SOCKETS; i++) {
6335 iface_ctx.sockets[i].socket_id = MDM_INVALID_SOCKET_ID;
6336 k_work_init(&iface_ctx.sockets[i].recv_cb_work,
6337 sockreadrecv_cb_work);
6338 k_work_init(&iface_ctx.sockets[i].rx_data_work,
6339 sock_rx_data_cb_work);
6340 k_work_init_delayable(&iface_ctx.sockets[i].notif_work,
6341 sock_notif_cb_work);
6342 k_sem_init(&iface_ctx.sockets[i].sock_send_sem, 0, 1);
6343 }
6344 iface_ctx.last_socket_id = 0;
6345 k_sem_init(&iface_ctx.response_sem, 0, 1);
6346 k_sem_init(&iface_ctx.mdm_awake, 0, 1);
6347
6348 /* initialize the work queue */
6349 k_work_queue_start(&hl7800_workq, hl7800_workq_stack,
6350 K_THREAD_STACK_SIZEOF(hl7800_workq_stack),
6351 WORKQ_PRIORITY, &cfg);
6352
6353 /* init work tasks */
6354 k_work_init_delayable(&iface_ctx.rssi_query_work, hl7800_rssi_query_work);
6355 k_work_init_delayable(&iface_ctx.iface_status_work, iface_status_work_cb);
6356 k_work_init_delayable(&iface_ctx.dns_work, dns_work_cb);
6357 k_work_init(&iface_ctx.mdm_vgpio_work, mdm_vgpio_work_cb);
6358 k_work_init_delayable(&iface_ctx.mdm_reset_work, mdm_reset_work_callback);
6359 k_work_init_delayable(&iface_ctx.allow_sleep_work,
6360 allow_sleep_work_callback);
6361 k_work_init_delayable(&iface_ctx.delete_untracked_socket_work,
6362 delete_untracked_socket_work_cb);
6363 k_work_init(&iface_ctx.mdm_pwr_off_work, mdm_power_off_work_callback);
6364
6365 #ifdef CONFIG_MODEM_HL7800_GPS
6366 k_work_init_delayable(&iface_ctx.gps_work, gps_work_callback);
6367 #endif
6368
6369 #ifdef CONFIG_MODEM_HL7800_FW_UPDATE
6370 k_work_init(&iface_ctx.finish_fw_update_work,
6371 finish_fw_update_work_callback);
6372 iface_ctx.fw_updated = false;
6373 #endif
6374
6375 /* setup port devices and pin directions */
6376 for (i = 0; i < MAX_MDM_CONTROL_PINS; i++) {
6377 if (!gpio_is_ready_dt(&hl7800_cfg.gpio[i])) {
6378 LOG_ERR("gpio port (%s) not ready!",
6379 hl7800_cfg.gpio[i].port->name);
6380 return -ENODEV;
6381 }
6382 }
6383
6384 ret = gpio_pin_configure_dt(&hl7800_cfg.gpio[MDM_RESET], GPIO_OUTPUT);
6385 if (ret) {
6386 LOG_ERR("Error configuring IO MDM_RESET %d err: %d!",
6387 hl7800_cfg.gpio[MDM_RESET].pin, ret);
6388 return ret;
6389 }
6390
6391 ret = gpio_pin_configure_dt(&hl7800_cfg.gpio[MDM_WAKE], GPIO_OUTPUT);
6392 if (ret) {
6393 LOG_ERR("Error configuring IO MDM_WAKE %d err: %d!",
6394 hl7800_cfg.gpio[MDM_WAKE].pin, ret);
6395 return ret;
6396 }
6397
6398 ret = gpio_pin_configure_dt(&hl7800_cfg.gpio[MDM_PWR_ON], GPIO_OUTPUT);
6399 if (ret) {
6400 LOG_ERR("Error configuring IO MDM_PWR_ON %d err: %d!",
6401 hl7800_cfg.gpio[MDM_PWR_ON].pin, ret);
6402 return ret;
6403 }
6404
6405 ret = gpio_pin_configure_dt(&hl7800_cfg.gpio[MDM_FAST_SHUTD], GPIO_OUTPUT);
6406 if (ret) {
6407 LOG_ERR("Error configuring IO MDM_FAST_SHUTD %d err: %d!",
6408 hl7800_cfg.gpio[MDM_FAST_SHUTD].pin, ret);
6409 return ret;
6410 }
6411
6412 ret = gpio_pin_configure_dt(&hl7800_cfg.gpio[MDM_VGPIO], GPIO_INPUT);
6413 if (ret) {
6414 LOG_ERR("Error configuring IO MDM_VGPIO %d err: %d!",
6415 hl7800_cfg.gpio[MDM_VGPIO].pin, ret);
6416 return ret;
6417 }
6418
6419 ret = gpio_pin_configure_dt(&hl7800_cfg.gpio[MDM_UART_DSR], GPIO_INPUT);
6420 if (ret) {
6421 LOG_ERR("Error configuring IO MDM_UART_DSR %d err: %d!",
6422 hl7800_cfg.gpio[MDM_UART_DSR].pin, ret);
6423 return ret;
6424 }
6425
6426 ret = gpio_pin_configure_dt(&hl7800_cfg.gpio[MDM_UART_CTS], GPIO_INPUT);
6427 if (ret) {
6428 LOG_ERR("Error configuring IO MDM_UART_CTS %d err: %d!",
6429 hl7800_cfg.gpio[MDM_UART_CTS].pin, ret);
6430 return ret;
6431 }
6432
6433 ret = gpio_pin_configure_dt(&hl7800_cfg.gpio[MDM_GPIO6], GPIO_INPUT);
6434 if (ret) {
6435 LOG_ERR("Error configuring IO MDM_GPIO6 %d err: %d!",
6436 hl7800_cfg.gpio[MDM_GPIO6].pin, ret);
6437 return ret;
6438 }
6439
6440 modem_assert_wake(false);
6441 modem_assert_pwr_on(false);
6442 modem_assert_fast_shutd(false);
6443
6444 /* Allow modem to run so we are in a known state.
6445 * This allows HL7800 VGPIO to be high, which is good because the UART
6446 * IO are already configured.
6447 */
6448 modem_run();
6449
6450 /* setup input pin callbacks */
6451 /* VGPIO */
6452 gpio_init_callback(&iface_ctx.mdm_vgpio_cb, mdm_vgpio_callback_isr,
6453 BIT(hl7800_cfg.gpio[MDM_VGPIO].pin));
6454 ret = gpio_add_callback(hl7800_cfg.gpio[MDM_VGPIO].port,
6455 &iface_ctx.mdm_vgpio_cb);
6456 if (ret) {
6457 LOG_ERR("Cannot setup vgpio callback! (%d)", ret);
6458 return ret;
6459 }
6460 ret = gpio_pin_interrupt_configure_dt(&hl7800_cfg.gpio[MDM_VGPIO], GPIO_INT_EDGE_BOTH);
6461 if (ret) {
6462 LOG_ERR("Error config vgpio interrupt! (%d)", ret);
6463 return ret;
6464 }
6465
6466 /* UART DSR */
6467 gpio_init_callback(&iface_ctx.mdm_uart_dsr_cb, mdm_uart_dsr_callback_isr,
6468 BIT(hl7800_cfg.gpio[MDM_UART_DSR].pin));
6469 ret = gpio_add_callback(hl7800_cfg.gpio[MDM_UART_DSR].port,
6470 &iface_ctx.mdm_uart_dsr_cb);
6471 if (ret) {
6472 LOG_ERR("Cannot setup uart dsr callback! (%d)", ret);
6473 return ret;
6474 }
6475 ret = gpio_pin_interrupt_configure_dt(&hl7800_cfg.gpio[MDM_UART_DSR], GPIO_INT_EDGE_BOTH);
6476 if (ret) {
6477 LOG_ERR("Error config uart dsr interrupt! (%d)", ret);
6478 return ret;
6479 }
6480
6481 /* GPIO6 */
6482 gpio_init_callback(&iface_ctx.mdm_gpio6_cb, mdm_gpio6_callback_isr,
6483 BIT(hl7800_cfg.gpio[MDM_GPIO6].pin));
6484 ret = gpio_add_callback(hl7800_cfg.gpio[MDM_GPIO6].port,
6485 &iface_ctx.mdm_gpio6_cb);
6486 if (ret) {
6487 LOG_ERR("Cannot setup gpio6 callback! (%d)", ret);
6488 return ret;
6489 }
6490 ret = gpio_pin_interrupt_configure_dt(&hl7800_cfg.gpio[MDM_GPIO6], GPIO_INT_EDGE_BOTH);
6491 if (ret) {
6492 LOG_ERR("Error config gpio6 interrupt! (%d)", ret);
6493 return ret;
6494 }
6495
6496 /* UART CTS */
6497 gpio_init_callback(&iface_ctx.mdm_uart_cts_cb, mdm_uart_cts_callback_isr,
6498 BIT(hl7800_cfg.gpio[MDM_UART_CTS].pin));
6499 ret = gpio_add_callback(hl7800_cfg.gpio[MDM_UART_CTS].port,
6500 &iface_ctx.mdm_uart_cts_cb);
6501 if (ret) {
6502 LOG_ERR("Cannot setup uart cts callback! (%d)", ret);
6503 return ret;
6504 }
6505 ret = gpio_pin_interrupt_configure_dt(&hl7800_cfg.gpio[MDM_UART_CTS], GPIO_INT_EDGE_BOTH);
6506 if (ret) {
6507 LOG_ERR("Error config uart cts interrupt! (%d)", ret);
6508 return ret;
6509 }
6510
6511 /* Set modem data storage */
6512 iface_ctx.mdm_ctx.data_manufacturer = iface_ctx.mdm_manufacturer;
6513 iface_ctx.mdm_ctx.data_model = iface_ctx.mdm_model;
6514 iface_ctx.mdm_ctx.data_revision = iface_ctx.mdm_revision;
6515 #ifdef CONFIG_MODEM_SIM_NUMBERS
6516 iface_ctx.mdm_ctx.data_imei = iface_ctx.mdm_imei;
6517 #endif
6518 iface_ctx.mdm_ctx.data_rssi = &iface_ctx.mdm_rssi;
6519
6520 ret = mdm_receiver_register(&iface_ctx.mdm_ctx, MDM_UART_DEV,
6521 mdm_recv_buf, sizeof(mdm_recv_buf));
6522 if (ret < 0) {
6523 LOG_ERR("Error registering modem receiver (%d)!", ret);
6524 return ret;
6525 }
6526
6527 k_queue_init(&iface_ctx.stale_socket_queue);
6528
6529 /* start RX thread */
6530 k_thread_name_set(
6531 k_thread_create(&hl7800_rx_thread, hl7800_rx_stack,
6532 K_THREAD_STACK_SIZEOF(hl7800_rx_stack),
6533 hl7800_rx, NULL, NULL, NULL,
6534 RX_THREAD_PRIORITY, 0, K_NO_WAIT),
6535 "hl7800 rx");
6536
6537 #ifdef CONFIG_MODEM_HL7800_BOOT_DELAY
6538 modem_reset();
6539 #else
6540 ret = modem_reset_and_configure();
6541 #endif
6542
6543 return ret;
6544 }
6545
offload_iface_init(struct net_if * iface)6546 static void offload_iface_init(struct net_if *iface)
6547 {
6548 const struct device *dev = net_if_get_device(iface);
6549 struct hl7800_iface_ctx *ctx = dev->data;
6550
6551 iface->if_dev->offload = &offload_funcs;
6552 ctx->iface = iface;
6553
6554 if (!IS_ENABLED(CONFIG_MODEM_HL7800_BOOT_DELAY)) {
6555 hl7800_build_mac(&iface_ctx);
6556 net_if_set_link_addr(iface, iface_ctx.mac_addr, sizeof(iface_ctx.mac_addr),
6557 NET_LINK_ETHERNET);
6558 iface_ctx.initialized = true;
6559 }
6560 }
6561
6562 static struct offloaded_if_api api_funcs = {
6563 .iface_api.init = offload_iface_init,
6564 };
6565
6566 NET_DEVICE_DT_INST_OFFLOAD_DEFINE(0, hl7800_init, NULL, &iface_ctx,
6567 &hl7800_cfg, CONFIG_MODEM_HL7800_INIT_PRIORITY,
6568 &api_funcs, MDM_MTU);
6569