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