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