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