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