1 /*
2 * Copyright (c) 2022 Trackunit Corporation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #undef _POSIX_C_SOURCE
8 #define _POSIX_C_SOURCE 200809L
9
10 #include <zephyr/logging/log.h>
11 LOG_MODULE_REGISTER(modem_chat, CONFIG_MODEM_MODULES_LOG_LEVEL);
12
13 #include <zephyr/kernel.h>
14 #include <string.h>
15
16 #include <zephyr/modem/chat.h>
17
18 #include "modem_workqueue.h"
19
20 const struct modem_chat_match modem_chat_any_match = MODEM_CHAT_MATCH("", "", NULL);
21 const struct modem_chat_match modem_chat_empty_matches[0];
22 const struct modem_chat_script_chat modem_chat_empty_script_chats[0];
23
24 #define MODEM_CHAT_MATCHES_INDEX_RESPONSE (0)
25 #define MODEM_CHAT_MATCHES_INDEX_ABORT (1)
26 #define MODEM_CHAT_MATCHES_INDEX_UNSOL (2)
27
28 #define MODEM_CHAT_SCRIPT_STATE_RUNNING_BIT (0)
29
30 #if defined(CONFIG_LOG) && (CONFIG_MODEM_MODULES_LOG_LEVEL == LOG_LEVEL_DBG)
31
32 static char log_buffer[CONFIG_MODEM_CHAT_LOG_BUFFER_SIZE];
33
modem_chat_log_received_command(struct modem_chat * chat)34 static void modem_chat_log_received_command(struct modem_chat *chat)
35 {
36 uint16_t log_buffer_pos = 0;
37 uint16_t argv_len;
38
39 for (uint16_t i = 0; i < chat->argc; i++) {
40 argv_len = (uint16_t)strlen(chat->argv[i]);
41
42 /* Validate argument fits in log buffer including termination */
43 if (sizeof(log_buffer) < (log_buffer_pos + argv_len + 1)) {
44 LOG_WRN("log buffer overrun");
45 break;
46 }
47
48 /* Copy argument and append space */
49 memcpy(&log_buffer[log_buffer_pos], chat->argv[i], argv_len);
50 log_buffer_pos += argv_len;
51 log_buffer[log_buffer_pos] = ' ';
52 log_buffer_pos++;
53 }
54
55 /* Terminate line after last argument, overwriting trailing space */
56 log_buffer_pos = log_buffer_pos == 0 ? log_buffer_pos : log_buffer_pos - 1;
57 log_buffer[log_buffer_pos] = '\0';
58
59 LOG_DBG("%s", log_buffer);
60 }
61
62 #else
63
modem_chat_log_received_command(struct modem_chat * chat)64 static void modem_chat_log_received_command(struct modem_chat *chat)
65 {
66 }
67
68 #endif
69
modem_chat_script_stop(struct modem_chat * chat,enum modem_chat_script_result result)70 static void modem_chat_script_stop(struct modem_chat *chat, enum modem_chat_script_result result)
71 {
72 if ((chat == NULL) || (chat->script == NULL)) {
73 return;
74 }
75
76 /* Handle result */
77 if (result == MODEM_CHAT_SCRIPT_RESULT_SUCCESS) {
78 LOG_DBG("%s: complete", chat->script->name);
79 } else if (result == MODEM_CHAT_SCRIPT_RESULT_ABORT) {
80 LOG_WRN("%s: aborted", chat->script->name);
81 } else {
82 LOG_WRN("%s: timed out", chat->script->name);
83 }
84
85 /* Call back with result */
86 if (chat->script->callback != NULL) {
87 chat->script->callback(chat, result, chat->user_data);
88 }
89
90 /* Clear parse_match in case it is stored in the script being stopped */
91 if ((chat->parse_match != NULL) &&
92 ((chat->parse_match_type == MODEM_CHAT_MATCHES_INDEX_ABORT) ||
93 (chat->parse_match_type == MODEM_CHAT_MATCHES_INDEX_RESPONSE))) {
94 chat->parse_match = NULL;
95 chat->parse_match_len = 0;
96 }
97
98 /* Clear reference to script */
99 chat->script = NULL;
100
101 /* Clear response and abort commands */
102 chat->matches[MODEM_CHAT_MATCHES_INDEX_ABORT] = NULL;
103 chat->matches_size[MODEM_CHAT_MATCHES_INDEX_ABORT] = 0;
104 chat->matches[MODEM_CHAT_MATCHES_INDEX_RESPONSE] = NULL;
105 chat->matches_size[MODEM_CHAT_MATCHES_INDEX_RESPONSE] = 0;
106
107 /* Cancel work */
108 k_work_cancel_delayable(&chat->script_timeout_work);
109 k_work_cancel(&chat->script_send_work);
110 k_work_cancel_delayable(&chat->script_send_timeout_work);
111
112 /* Clear script running state */
113 atomic_clear_bit(&chat->script_state, MODEM_CHAT_SCRIPT_STATE_RUNNING_BIT);
114
115 /* Store result of script for script stoppted indication */
116 chat->script_result = result;
117
118 /* Indicate script stopped */
119 k_sem_give(&chat->script_stopped_sem);
120 }
121
modem_chat_set_script_send_state(struct modem_chat * chat,enum modem_chat_script_send_state state)122 static void modem_chat_set_script_send_state(struct modem_chat *chat,
123 enum modem_chat_script_send_state state)
124 {
125 chat->script_send_pos = 0;
126 chat->script_send_state = state;
127 }
128
modem_chat_script_send(struct modem_chat * chat)129 static void modem_chat_script_send(struct modem_chat *chat)
130 {
131 modem_chat_set_script_send_state(chat, MODEM_CHAT_SCRIPT_SEND_STATE_REQUEST);
132 modem_work_submit(&chat->script_send_work);
133 }
134
modem_chat_script_set_response_matches(struct modem_chat * chat)135 static void modem_chat_script_set_response_matches(struct modem_chat *chat)
136 {
137 const struct modem_chat_script_chat *script_chat =
138 &chat->script->script_chats[chat->script_chat_it];
139
140 chat->matches[MODEM_CHAT_MATCHES_INDEX_RESPONSE] = script_chat->response_matches;
141 chat->matches_size[MODEM_CHAT_MATCHES_INDEX_RESPONSE] = script_chat->response_matches_size;
142 }
143
modem_chat_script_clear_response_matches(struct modem_chat * chat)144 static void modem_chat_script_clear_response_matches(struct modem_chat *chat)
145 {
146 chat->matches[MODEM_CHAT_MATCHES_INDEX_RESPONSE] = NULL;
147 chat->matches_size[MODEM_CHAT_MATCHES_INDEX_RESPONSE] = 0;
148 }
149
modem_chat_script_chat_has_request(struct modem_chat * chat)150 static bool modem_chat_script_chat_has_request(struct modem_chat *chat)
151 {
152 const struct modem_chat_script_chat *script_chat =
153 &chat->script->script_chats[chat->script_chat_it];
154
155 return script_chat->request_size > 0;
156 }
157
modem_chat_script_chat_has_matches(struct modem_chat * chat)158 static bool modem_chat_script_chat_has_matches(struct modem_chat *chat)
159 {
160 const struct modem_chat_script_chat *script_chat =
161 &chat->script->script_chats[chat->script_chat_it];
162
163 return script_chat->response_matches_size > 0;
164 }
165
modem_chat_script_chat_get_send_timeout(struct modem_chat * chat)166 static uint16_t modem_chat_script_chat_get_send_timeout(struct modem_chat *chat)
167 {
168 const struct modem_chat_script_chat *script_chat =
169 &chat->script->script_chats[chat->script_chat_it];
170
171 return script_chat->timeout;
172 }
173
modem_chat_script_chat_has_send_timeout(struct modem_chat * chat)174 static bool modem_chat_script_chat_has_send_timeout(struct modem_chat *chat)
175 {
176 return modem_chat_script_chat_get_send_timeout(chat) > 0;
177 }
178
modem_chat_script_chat_schedule_send_timeout(struct modem_chat * chat)179 static void modem_chat_script_chat_schedule_send_timeout(struct modem_chat *chat)
180 {
181 uint16_t timeout = modem_chat_script_chat_get_send_timeout(chat);
182
183 modem_work_schedule(&chat->script_send_timeout_work, K_MSEC(timeout));
184 }
185
modem_chat_script_next(struct modem_chat * chat,bool initial)186 static void modem_chat_script_next(struct modem_chat *chat, bool initial)
187 {
188 const struct modem_chat_script_chat *script_chat;
189
190 /* Advance iterator if not initial */
191 if (initial == true) {
192 /* Reset iterator */
193 chat->script_chat_it = 0;
194 } else {
195 /* Advance iterator */
196 chat->script_chat_it++;
197 }
198
199 /* Check if end of script reached */
200 if (chat->script_chat_it == chat->script->script_chats_size) {
201 modem_chat_script_stop(chat, MODEM_CHAT_SCRIPT_RESULT_SUCCESS);
202
203 return;
204 }
205
206 LOG_DBG("%s: step: %u", chat->script->name, chat->script_chat_it);
207
208 script_chat = &chat->script->script_chats[chat->script_chat_it];
209
210 /* Continue script */
211 if (modem_chat_script_chat_has_request(chat)) {
212 LOG_DBG("sending: %.*s", script_chat->request_size, script_chat->request);
213 modem_chat_script_clear_response_matches(chat);
214 modem_chat_script_send(chat);
215 } else if (modem_chat_script_chat_has_matches(chat)) {
216 modem_chat_script_set_response_matches(chat);
217 } else {
218 modem_chat_script_chat_schedule_send_timeout(chat);
219 }
220 }
221
modem_chat_script_start(struct modem_chat * chat,const struct modem_chat_script * script)222 static void modem_chat_script_start(struct modem_chat *chat, const struct modem_chat_script *script)
223 {
224 /* Save script */
225 chat->script = script;
226
227 /* Set abort matches */
228 chat->matches[MODEM_CHAT_MATCHES_INDEX_ABORT] = script->abort_matches;
229 chat->matches_size[MODEM_CHAT_MATCHES_INDEX_ABORT] = script->abort_matches_size;
230
231 LOG_DBG("running script: %s", chat->script->name);
232
233 /* Set first script command */
234 modem_chat_script_next(chat, true);
235
236 /* Start timeout work if script started */
237 if (chat->script != NULL) {
238 modem_work_schedule(&chat->script_timeout_work, K_SECONDS(chat->script->timeout));
239 }
240 }
241
modem_chat_script_run_handler(struct k_work * item)242 static void modem_chat_script_run_handler(struct k_work *item)
243 {
244 struct modem_chat *chat = CONTAINER_OF(item, struct modem_chat, script_run_work);
245
246 /* Start script */
247 modem_chat_script_start(chat, chat->pending_script);
248 }
249
modem_chat_script_timeout_handler(struct k_work * item)250 static void modem_chat_script_timeout_handler(struct k_work *item)
251 {
252 struct k_work_delayable *dwork = k_work_delayable_from_work(item);
253 struct modem_chat *chat = CONTAINER_OF(dwork, struct modem_chat, script_timeout_work);
254
255 /* Abort script */
256 modem_chat_script_stop(chat, MODEM_CHAT_SCRIPT_RESULT_TIMEOUT);
257 }
258
modem_chat_script_abort_handler(struct k_work * item)259 static void modem_chat_script_abort_handler(struct k_work *item)
260 {
261 struct modem_chat *chat = CONTAINER_OF(item, struct modem_chat, script_abort_work);
262
263 /* Validate script is currently running */
264 if (chat->script == NULL) {
265 return;
266 }
267
268 /* Abort script */
269 modem_chat_script_stop(chat, MODEM_CHAT_SCRIPT_RESULT_ABORT);
270 }
271
272 /* Returns true when request part has been sent */
modem_chat_send_script_request_part(struct modem_chat * chat)273 static bool modem_chat_send_script_request_part(struct modem_chat *chat)
274 {
275 const struct modem_chat_script_chat *script_chat =
276 &chat->script->script_chats[chat->script_chat_it];
277
278 uint8_t *request_part;
279 uint16_t request_size;
280 uint16_t request_part_size;
281 int ret;
282
283 switch (chat->script_send_state) {
284 case MODEM_CHAT_SCRIPT_SEND_STATE_REQUEST:
285 request_part = (uint8_t *)(&script_chat->request[chat->script_send_pos]);
286 request_size = script_chat->request_size;
287 break;
288
289 case MODEM_CHAT_SCRIPT_SEND_STATE_DELIMITER:
290 request_part = (uint8_t *)(&chat->delimiter[chat->script_send_pos]);
291 request_size = chat->delimiter_size;
292 break;
293
294 default:
295 return false;
296 }
297
298 request_part_size = request_size - chat->script_send_pos;
299 ret = modem_pipe_transmit(chat->pipe, request_part, request_part_size);
300 if (ret < 1) {
301 if (ret < 0) {
302 LOG_ERR("Failed to %s %u bytes. (%d)", "transmit", request_part_size, ret);
303 }
304 return false;
305 }
306
307 chat->script_send_pos += (uint16_t)ret;
308
309 /* Return true if all data was sent */
310 return request_size <= chat->script_send_pos;
311 }
312
modem_chat_script_send_handler(struct k_work * item)313 static void modem_chat_script_send_handler(struct k_work *item)
314 {
315 struct modem_chat *chat = CONTAINER_OF(item, struct modem_chat, script_send_work);
316
317 if (chat->script == NULL) {
318 return;
319 }
320
321 switch (chat->script_send_state) {
322 case MODEM_CHAT_SCRIPT_SEND_STATE_IDLE:
323 return;
324
325 case MODEM_CHAT_SCRIPT_SEND_STATE_REQUEST:
326 if (!modem_chat_send_script_request_part(chat)) {
327 return;
328 }
329
330 modem_chat_set_script_send_state(chat, MODEM_CHAT_SCRIPT_SEND_STATE_DELIMITER);
331 __fallthrough;
332
333 case MODEM_CHAT_SCRIPT_SEND_STATE_DELIMITER:
334 if (!modem_chat_send_script_request_part(chat)) {
335 return;
336 }
337
338 modem_chat_set_script_send_state(chat, MODEM_CHAT_SCRIPT_SEND_STATE_IDLE);
339 break;
340 }
341
342 if (modem_chat_script_chat_has_matches(chat)) {
343 modem_chat_script_set_response_matches(chat);
344 } else if (modem_chat_script_chat_has_send_timeout(chat)) {
345 modem_chat_script_chat_schedule_send_timeout(chat);
346 } else {
347 modem_chat_script_next(chat, false);
348 }
349 }
350
modem_chat_script_send_timeout_handler(struct k_work * item)351 static void modem_chat_script_send_timeout_handler(struct k_work *item)
352 {
353 struct k_work_delayable *dwork = k_work_delayable_from_work(item);
354 struct modem_chat *chat = CONTAINER_OF(dwork, struct modem_chat, script_send_timeout_work);
355
356 /* Validate script is currently running */
357 if (chat->script == NULL) {
358 return;
359 }
360
361 modem_chat_script_next(chat, false);
362 }
363
364 #if CONFIG_MODEM_STATS
get_receive_buf_length(struct modem_chat * chat)365 static uint32_t get_receive_buf_length(struct modem_chat *chat)
366 {
367 return chat->receive_buf_len;
368 }
369
advertise_receive_buf_stats(struct modem_chat * chat)370 static void advertise_receive_buf_stats(struct modem_chat *chat)
371 {
372 uint32_t length;
373
374 length = get_receive_buf_length(chat);
375 modem_stats_buffer_advertise_length(&chat->receive_buf_stats, length);
376 }
377 #endif
378
modem_chat_parse_reset(struct modem_chat * chat)379 static void modem_chat_parse_reset(struct modem_chat *chat)
380 {
381 #if CONFIG_MODEM_STATS
382 advertise_receive_buf_stats(chat);
383 #endif
384
385 /* Reset parameters used for parsing */
386 chat->receive_buf_len = 0;
387 chat->delimiter_match_len = 0;
388 chat->argc = 0;
389 chat->parse_match = NULL;
390 }
391
392 /* Exact match is stored at end of receive buffer */
modem_chat_parse_save_match(struct modem_chat * chat)393 static void modem_chat_parse_save_match(struct modem_chat *chat)
394 {
395 uint8_t *argv;
396
397 /* Store length of match including NULL to avoid overwriting it if buffer overruns */
398 chat->parse_match_len = chat->receive_buf_len + 1;
399
400 /* Copy match to end of receive buffer */
401 argv = &chat->receive_buf[chat->receive_buf_size - chat->parse_match_len];
402
403 /* Copy match to end of receive buffer (excluding NULL) */
404 memcpy(argv, &chat->receive_buf[0], chat->parse_match_len - 1);
405
406 /* Save match */
407 chat->argv[chat->argc] = argv;
408
409 /* Terminate match */
410 chat->receive_buf[chat->receive_buf_size - 1] = '\0';
411
412 /* Increment argument count */
413 chat->argc++;
414 }
415
modem_chat_match_matches_received(struct modem_chat * chat,const struct modem_chat_match * match)416 static bool modem_chat_match_matches_received(struct modem_chat *chat,
417 const struct modem_chat_match *match)
418 {
419 for (uint16_t i = 0; i < match->match_size; i++) {
420 if ((match->match[i] == chat->receive_buf[i]) ||
421 (match->wildcards == true && match->match[i] == '?')) {
422 continue;
423 }
424
425 return false;
426 }
427
428 return true;
429 }
430
modem_chat_parse_find_match(struct modem_chat * chat)431 static bool modem_chat_parse_find_match(struct modem_chat *chat)
432 {
433 /* Find in all matches types */
434 for (uint16_t i = 0; i < ARRAY_SIZE(chat->matches); i++) {
435 /* Find in all matches of matches type */
436 for (uint16_t u = 0; u < chat->matches_size[i]; u++) {
437 /* Validate match size matches received data length */
438 if (chat->matches[i][u].match_size != chat->receive_buf_len) {
439 continue;
440 }
441
442 /* Validate match */
443 if (modem_chat_match_matches_received(chat, &chat->matches[i][u]) ==
444 false) {
445 continue;
446 }
447
448 /* Complete match found */
449 chat->parse_match = &chat->matches[i][u];
450 chat->parse_match_type = i;
451 return true;
452 }
453 }
454
455 return false;
456 }
457
modem_chat_parse_is_separator(struct modem_chat * chat)458 static bool modem_chat_parse_is_separator(struct modem_chat *chat)
459 {
460 for (uint16_t i = 0; i < chat->parse_match->separators_size; i++) {
461 if ((chat->parse_match->separators[i]) ==
462 (chat->receive_buf[chat->receive_buf_len - 1])) {
463 return true;
464 }
465 }
466
467 return false;
468 }
469
modem_chat_parse_end_del_start(struct modem_chat * chat)470 static bool modem_chat_parse_end_del_start(struct modem_chat *chat)
471 {
472 for (uint8_t i = 0; i < chat->delimiter_size; i++) {
473 if (chat->receive_buf[chat->receive_buf_len - 1] == chat->delimiter[i]) {
474 return true;
475 }
476 }
477
478 return false;
479 }
480
modem_chat_parse_end_del_complete(struct modem_chat * chat)481 static bool modem_chat_parse_end_del_complete(struct modem_chat *chat)
482 {
483 /* Validate length of end delimiter */
484 if (chat->receive_buf_len < chat->delimiter_size) {
485 return false;
486 }
487
488 /* Compare end delimiter with receive buffer content */
489 return (memcmp(&chat->receive_buf[chat->receive_buf_len - chat->delimiter_size],
490 chat->delimiter, chat->delimiter_size) == 0)
491 ? true
492 : false;
493 }
494
modem_chat_on_command_received_unsol(struct modem_chat * chat)495 static void modem_chat_on_command_received_unsol(struct modem_chat *chat)
496 {
497 /* Callback */
498 if (chat->parse_match->callback != NULL) {
499 chat->parse_match->callback(chat, (char **)chat->argv, chat->argc, chat->user_data);
500 }
501 }
502
modem_chat_on_command_received_abort(struct modem_chat * chat)503 static void modem_chat_on_command_received_abort(struct modem_chat *chat)
504 {
505 /* Callback */
506 if (chat->parse_match->callback != NULL) {
507 chat->parse_match->callback(chat, (char **)chat->argv, chat->argc, chat->user_data);
508 }
509
510 /* Abort script */
511 modem_chat_script_stop(chat, MODEM_CHAT_SCRIPT_RESULT_ABORT);
512 }
513
modem_chat_on_command_received_resp(struct modem_chat * chat)514 static void modem_chat_on_command_received_resp(struct modem_chat *chat)
515 {
516 /* Callback */
517 if (chat->parse_match->callback != NULL) {
518 chat->parse_match->callback(chat, (char **)chat->argv, chat->argc, chat->user_data);
519 }
520
521 /* Validate response command is not partial */
522 if (chat->parse_match->partial) {
523 return;
524 }
525
526 /* Advance script */
527 modem_chat_script_next(chat, false);
528 }
529
modem_chat_parse_find_catch_all_match(struct modem_chat * chat)530 static bool modem_chat_parse_find_catch_all_match(struct modem_chat *chat)
531 {
532 /* Find in all matches types */
533 for (uint16_t i = 0; i < ARRAY_SIZE(chat->matches); i++) {
534 /* Find in all matches of matches type */
535 for (uint16_t u = 0; u < chat->matches_size[i]; u++) {
536 /* Validate match config is matching previous bytes */
537 if (chat->matches[i][u].match_size == 0) {
538 chat->parse_match = &chat->matches[i][u];
539 chat->parse_match_type = i;
540 return true;
541 }
542 }
543 }
544
545 return false;
546 }
547
modem_chat_on_command_received(struct modem_chat * chat)548 static void modem_chat_on_command_received(struct modem_chat *chat)
549 {
550 modem_chat_log_received_command(chat);
551
552 switch (chat->parse_match_type) {
553 case MODEM_CHAT_MATCHES_INDEX_UNSOL:
554 modem_chat_on_command_received_unsol(chat);
555 break;
556
557 case MODEM_CHAT_MATCHES_INDEX_ABORT:
558 modem_chat_on_command_received_abort(chat);
559 break;
560
561 case MODEM_CHAT_MATCHES_INDEX_RESPONSE:
562 modem_chat_on_command_received_resp(chat);
563 break;
564 }
565 }
566
modem_chat_on_unknown_command_received(struct modem_chat * chat)567 static void modem_chat_on_unknown_command_received(struct modem_chat *chat)
568 {
569 /* Terminate received command */
570 chat->receive_buf[chat->receive_buf_len - chat->delimiter_size] = '\0';
571
572 /* Try to find catch all match */
573 if (modem_chat_parse_find_catch_all_match(chat) == false) {
574 LOG_DBG("%s", chat->receive_buf);
575 return;
576 }
577
578 /* Parse command */
579 chat->argv[0] = "";
580 chat->argv[1] = chat->receive_buf;
581 chat->argc = 2;
582
583 modem_chat_on_command_received(chat);
584 }
585
modem_chat_process_byte(struct modem_chat * chat,uint8_t byte)586 static void modem_chat_process_byte(struct modem_chat *chat, uint8_t byte)
587 {
588 /* Validate receive buffer not overrun */
589 if (chat->receive_buf_size == chat->receive_buf_len) {
590 LOG_WRN("receive buffer overrun");
591 modem_chat_parse_reset(chat);
592 return;
593 }
594
595 /* Validate argv buffer not overrun */
596 if (chat->argc == chat->argv_size) {
597 LOG_WRN("argv buffer overrun");
598 modem_chat_parse_reset(chat);
599 return;
600 }
601
602 /* Copy byte to receive buffer */
603 chat->receive_buf[chat->receive_buf_len] = byte;
604 chat->receive_buf_len++;
605
606 /* Validate end delimiter not complete */
607 if (modem_chat_parse_end_del_complete(chat) == true) {
608 /* Filter out empty lines */
609 if (chat->receive_buf_len == chat->delimiter_size) {
610 /* Reset parser */
611 modem_chat_parse_reset(chat);
612 return;
613 }
614
615 /* Check if match exists */
616 if (chat->parse_match == NULL) {
617 /* Handle unknown command */
618 modem_chat_on_unknown_command_received(chat);
619
620 /* Reset parser */
621 modem_chat_parse_reset(chat);
622 return;
623 }
624
625 /* Check if trailing argument exists */
626 if (chat->parse_arg_len > 0) {
627 chat->argv[chat->argc] =
628 &chat->receive_buf[chat->receive_buf_len - chat->delimiter_size -
629 chat->parse_arg_len];
630 chat->receive_buf[chat->receive_buf_len - chat->delimiter_size] = '\0';
631 chat->argc++;
632 }
633
634 /* Handle received command */
635 modem_chat_on_command_received(chat);
636
637 /* Reset parser */
638 modem_chat_parse_reset(chat);
639 return;
640 }
641
642 /* Validate end delimiter not started */
643 if (modem_chat_parse_end_del_start(chat) == true) {
644 return;
645 }
646
647 /* Find matching command if missing */
648 if (chat->parse_match == NULL) {
649 /* Find matching command */
650 if (modem_chat_parse_find_match(chat) == false) {
651 return;
652 }
653
654 /* Save match */
655 modem_chat_parse_save_match(chat);
656
657 /* Prepare argument parser */
658 chat->parse_arg_len = 0;
659 return;
660 }
661
662 /* Check if separator reached */
663 if (modem_chat_parse_is_separator(chat) == true) {
664 /* Check if argument is empty */
665 if (chat->parse_arg_len == 0) {
666 /* Save empty argument */
667 chat->argv[chat->argc] = "";
668 } else {
669 /* Save pointer to start of argument */
670 chat->argv[chat->argc] =
671 &chat->receive_buf[chat->receive_buf_len - chat->parse_arg_len - 1];
672
673 /* Replace separator with string terminator */
674 chat->receive_buf[chat->receive_buf_len - 1] = '\0';
675 }
676
677 /* Increment argument count */
678 chat->argc++;
679
680 /* Reset parse argument length */
681 chat->parse_arg_len = 0;
682 return;
683 }
684
685 /* Increment argument length */
686 chat->parse_arg_len++;
687 }
688
modem_chat_discard_byte(struct modem_chat * chat,uint8_t byte)689 static bool modem_chat_discard_byte(struct modem_chat *chat, uint8_t byte)
690 {
691 for (uint8_t i = 0; i < chat->filter_size; i++) {
692 if (byte == chat->filter[i]) {
693 return true;
694 }
695 }
696
697 return false;
698 }
699
700 /* Process chunk of received bytes */
modem_chat_process_bytes(struct modem_chat * chat)701 static void modem_chat_process_bytes(struct modem_chat *chat)
702 {
703 for (uint16_t i = 0; i < chat->work_buf_len; i++) {
704 if (modem_chat_discard_byte(chat, chat->work_buf[i])) {
705 continue;
706 }
707
708 modem_chat_process_byte(chat, chat->work_buf[i]);
709 }
710 }
711
712 #if CONFIG_MODEM_STATS
get_work_buf_length(struct modem_chat * chat)713 static uint32_t get_work_buf_length(struct modem_chat *chat)
714 {
715 return chat->work_buf_len;
716 }
717
advertise_work_buf_stats(struct modem_chat * chat)718 static void advertise_work_buf_stats(struct modem_chat *chat)
719 {
720 uint32_t length;
721
722 length = get_work_buf_length(chat);
723 modem_stats_buffer_advertise_length(&chat->work_buf_stats, length);
724 }
725 #endif
726
modem_chat_process_handler(struct k_work * item)727 static void modem_chat_process_handler(struct k_work *item)
728 {
729 struct modem_chat *chat = CONTAINER_OF(item, struct modem_chat, receive_work);
730 int ret;
731
732 /* Fill work buffer */
733 ret = modem_pipe_receive(chat->pipe, chat->work_buf, sizeof(chat->work_buf));
734 if (ret < 1) {
735 return;
736 }
737
738 /* Save received data length */
739 chat->work_buf_len = (size_t)ret;
740
741 #if CONFIG_MODEM_STATS
742 advertise_work_buf_stats(chat);
743 #endif
744
745 /* Process data */
746 modem_chat_process_bytes(chat);
747 modem_work_submit(&chat->receive_work);
748 }
749
modem_chat_pipe_callback(struct modem_pipe * pipe,enum modem_pipe_event event,void * user_data)750 static void modem_chat_pipe_callback(struct modem_pipe *pipe, enum modem_pipe_event event,
751 void *user_data)
752 {
753 struct modem_chat *chat = (struct modem_chat *)user_data;
754
755 switch (event) {
756 case MODEM_PIPE_EVENT_RECEIVE_READY:
757 modem_work_submit(&chat->receive_work);
758 break;
759
760 case MODEM_PIPE_EVENT_TRANSMIT_IDLE:
761 modem_work_submit(&chat->script_send_work);
762 break;
763
764 default:
765 break;
766 }
767 }
768
modem_chat_validate_array(const void * array,size_t size)769 static bool modem_chat_validate_array(const void *array, size_t size)
770 {
771 return ((array == NULL) && (size == 0)) ||
772 ((array != NULL) && (size > 0));
773 }
774
775 #if CONFIG_MODEM_STATS
get_receive_buf_size(struct modem_chat * chat)776 static uint32_t get_receive_buf_size(struct modem_chat *chat)
777 {
778 return chat->receive_buf_size;
779 }
780
get_work_buf_size(struct modem_chat * chat)781 static uint32_t get_work_buf_size(struct modem_chat *chat)
782 {
783 return sizeof(chat->work_buf);
784 }
785
init_buf_stats(struct modem_chat * chat)786 static void init_buf_stats(struct modem_chat *chat)
787 {
788 uint32_t size;
789
790 size = get_receive_buf_size(chat);
791 modem_stats_buffer_init(&chat->receive_buf_stats, "chat_rx", size);
792 size = get_work_buf_size(chat);
793 modem_stats_buffer_init(&chat->work_buf_stats, "chat_work", size);
794 }
795 #endif
796
modem_chat_init(struct modem_chat * chat,const struct modem_chat_config * config)797 int modem_chat_init(struct modem_chat *chat, const struct modem_chat_config *config)
798 {
799 __ASSERT_NO_MSG(chat != NULL);
800 __ASSERT_NO_MSG(config != NULL);
801 __ASSERT_NO_MSG(config->receive_buf != NULL);
802 __ASSERT_NO_MSG(config->receive_buf_size > 0);
803 __ASSERT_NO_MSG(config->argv != NULL);
804 __ASSERT_NO_MSG(config->argv_size > 0);
805 __ASSERT_NO_MSG(config->delimiter != NULL);
806 __ASSERT_NO_MSG(config->delimiter_size > 0);
807 __ASSERT_NO_MSG(!((config->filter == NULL) && (config->filter_size > 0)));
808 __ASSERT_NO_MSG(!((config->unsol_matches == NULL) && (config->unsol_matches_size > 0)));
809
810 memset(chat, 0x00, sizeof(*chat));
811 chat->pipe = NULL;
812 chat->user_data = config->user_data;
813 chat->receive_buf = config->receive_buf;
814 chat->receive_buf_size = config->receive_buf_size;
815 chat->argv = config->argv;
816 chat->argv_size = config->argv_size;
817 chat->delimiter = config->delimiter;
818 chat->delimiter_size = config->delimiter_size;
819 chat->filter = config->filter;
820 chat->filter_size = config->filter_size;
821 chat->matches[MODEM_CHAT_MATCHES_INDEX_UNSOL] = config->unsol_matches;
822 chat->matches_size[MODEM_CHAT_MATCHES_INDEX_UNSOL] = config->unsol_matches_size;
823 atomic_set(&chat->script_state, 0);
824 k_sem_init(&chat->script_stopped_sem, 0, 1);
825 k_work_init(&chat->receive_work, modem_chat_process_handler);
826 k_work_init(&chat->script_run_work, modem_chat_script_run_handler);
827 k_work_init_delayable(&chat->script_timeout_work, modem_chat_script_timeout_handler);
828 k_work_init(&chat->script_abort_work, modem_chat_script_abort_handler);
829 k_work_init(&chat->script_send_work, modem_chat_script_send_handler);
830 k_work_init_delayable(&chat->script_send_timeout_work,
831 modem_chat_script_send_timeout_handler);
832
833 #if CONFIG_MODEM_STATS
834 init_buf_stats(chat);
835 #endif
836
837 return 0;
838 }
839
modem_chat_attach(struct modem_chat * chat,struct modem_pipe * pipe)840 int modem_chat_attach(struct modem_chat *chat, struct modem_pipe *pipe)
841 {
842 chat->pipe = pipe;
843 modem_chat_parse_reset(chat);
844 modem_pipe_attach(chat->pipe, modem_chat_pipe_callback, chat);
845 return 0;
846 }
847
modem_chat_is_running(struct modem_chat * chat)848 bool modem_chat_is_running(struct modem_chat *chat)
849 {
850 return atomic_test_bit(&chat->script_state, MODEM_CHAT_SCRIPT_STATE_RUNNING_BIT);
851 }
852
modem_chat_run_script_async(struct modem_chat * chat,const struct modem_chat_script * script)853 int modem_chat_run_script_async(struct modem_chat *chat, const struct modem_chat_script *script)
854 {
855 bool script_is_running;
856
857 if (chat->pipe == NULL) {
858 return -EPERM;
859 }
860
861 /* Validate script */
862 if (script->script_chats == NULL ||
863 (script->script_chats_size == 0
864 && script->script_chats != modem_chat_empty_script_chats) ||
865 (script->abort_matches_size == 0
866 && script->abort_matches != NULL
867 && script->abort_matches != modem_chat_empty_matches)) {
868 return -EINVAL;
869 }
870
871 /* Validate script commands */
872 for (uint16_t i = 0; i < script->script_chats_size; i++) {
873 if ((script->script_chats[i].request_size == 0) &&
874 (script->script_chats[i].response_matches_size == 0) &&
875 (script->script_chats[i].timeout == 0)) {
876 return -EINVAL;
877 }
878 }
879
880 script_is_running =
881 atomic_test_and_set_bit(&chat->script_state, MODEM_CHAT_SCRIPT_STATE_RUNNING_BIT);
882
883 if (script_is_running == true) {
884 return -EBUSY;
885 }
886
887 k_sem_reset(&chat->script_stopped_sem);
888
889 chat->pending_script = script;
890 modem_work_submit(&chat->script_run_work);
891 return 0;
892 }
893
modem_chat_run_script(struct modem_chat * chat,const struct modem_chat_script * script)894 int modem_chat_run_script(struct modem_chat *chat, const struct modem_chat_script *script)
895 {
896 int ret;
897
898 ret = modem_chat_run_script_async(chat, script);
899 if (ret < 0) {
900 return ret;
901 }
902
903 ret = k_sem_take(&chat->script_stopped_sem, K_FOREVER);
904 if (ret < 0) {
905 return ret;
906 }
907
908 return (chat->script_result == MODEM_CHAT_SCRIPT_RESULT_SUCCESS) ? 0 : -EAGAIN;
909 }
910
modem_chat_script_abort(struct modem_chat * chat)911 void modem_chat_script_abort(struct modem_chat *chat)
912 {
913 modem_work_submit(&chat->script_abort_work);
914 }
915
modem_chat_release(struct modem_chat * chat)916 void modem_chat_release(struct modem_chat *chat)
917 {
918 struct k_work_sync sync;
919
920 if (chat->pipe) {
921 modem_pipe_release(chat->pipe);
922 }
923
924 k_work_cancel_sync(&chat->script_run_work, &sync);
925 k_work_cancel_sync(&chat->script_abort_work, &sync);
926 k_work_cancel_sync(&chat->receive_work, &sync);
927 k_work_cancel_sync(&chat->script_send_work, &sync);
928
929 chat->pipe = NULL;
930 chat->receive_buf_len = 0;
931 chat->work_buf_len = 0;
932 chat->argc = 0;
933 chat->script = NULL;
934 chat->script_chat_it = 0;
935 atomic_set(&chat->script_state, 0);
936 chat->script_result = MODEM_CHAT_SCRIPT_RESULT_ABORT;
937 k_sem_reset(&chat->script_stopped_sem);
938 chat->script_send_state = MODEM_CHAT_SCRIPT_SEND_STATE_IDLE;
939 chat->script_send_pos = 0;
940 chat->parse_match = NULL;
941 chat->parse_match_len = 0;
942 chat->parse_arg_len = 0;
943 chat->matches[MODEM_CHAT_MATCHES_INDEX_ABORT] = NULL;
944 chat->matches_size[MODEM_CHAT_MATCHES_INDEX_ABORT] = 0;
945 chat->matches[MODEM_CHAT_MATCHES_INDEX_RESPONSE] = NULL;
946 chat->matches_size[MODEM_CHAT_MATCHES_INDEX_RESPONSE] = 0;
947 }
948
modem_chat_match_init(struct modem_chat_match * chat_match)949 void modem_chat_match_init(struct modem_chat_match *chat_match)
950 {
951 memset(chat_match, 0, sizeof(struct modem_chat_match));
952 }
953
modem_chat_match_set_match(struct modem_chat_match * chat_match,const char * match)954 int modem_chat_match_set_match(struct modem_chat_match *chat_match, const char *match)
955 {
956 size_t size;
957
958 size = strnlen(match, UINT8_MAX + 1);
959
960 if (size == (UINT8_MAX + 1)) {
961 return -ENOMEM;
962 }
963
964 chat_match->match = match;
965 chat_match->match_size = (uint8_t)size;
966 return 0;
967 }
968
modem_chat_match_set_separators(struct modem_chat_match * chat_match,const char * separators)969 int modem_chat_match_set_separators(struct modem_chat_match *chat_match, const char *separators)
970 {
971 size_t size;
972
973 size = strnlen(separators, UINT8_MAX + 1);
974
975 if (size == (UINT8_MAX + 1)) {
976 return -ENOMEM;
977 }
978
979 chat_match->separators = separators;
980 chat_match->separators_size = (uint8_t)size;
981 return 0;
982 }
983
modem_chat_match_set_callback(struct modem_chat_match * match,modem_chat_match_callback callback)984 void modem_chat_match_set_callback(struct modem_chat_match *match,
985 modem_chat_match_callback callback)
986 {
987 match->callback = callback;
988 }
989
modem_chat_match_set_partial(struct modem_chat_match * match,bool partial)990 void modem_chat_match_set_partial(struct modem_chat_match *match, bool partial)
991 {
992 match->partial = partial;
993 }
994
modem_chat_match_enable_wildcards(struct modem_chat_match * match,bool enable)995 void modem_chat_match_enable_wildcards(struct modem_chat_match *match, bool enable)
996 {
997 match->wildcards = enable;
998 }
999
modem_chat_script_chat_init(struct modem_chat_script_chat * script_chat)1000 void modem_chat_script_chat_init(struct modem_chat_script_chat *script_chat)
1001 {
1002 memset(script_chat, 0, sizeof(struct modem_chat_script_chat));
1003 }
1004
modem_chat_script_chat_set_request(struct modem_chat_script_chat * script_chat,const char * request)1005 int modem_chat_script_chat_set_request(struct modem_chat_script_chat *script_chat,
1006 const char *request)
1007 {
1008 size_t size;
1009
1010 size = strnlen(request, UINT16_MAX + 1);
1011
1012 if (size == (UINT16_MAX + 1)) {
1013 return -ENOMEM;
1014 }
1015
1016 script_chat->request = request;
1017 script_chat->request_size = (uint16_t)size;
1018 return 0;
1019 }
1020
modem_chat_script_chat_set_response_matches(struct modem_chat_script_chat * script_chat,const struct modem_chat_match * response_matches,uint16_t response_matches_size)1021 int modem_chat_script_chat_set_response_matches(struct modem_chat_script_chat *script_chat,
1022 const struct modem_chat_match *response_matches,
1023 uint16_t response_matches_size)
1024 {
1025 if (!modem_chat_validate_array(response_matches, response_matches_size)) {
1026 return -EINVAL;
1027 }
1028
1029 script_chat->response_matches = response_matches;
1030 script_chat->response_matches_size = response_matches_size;
1031 return 0;
1032 }
1033
modem_chat_script_chat_set_timeout(struct modem_chat_script_chat * script_chat,uint16_t timeout)1034 void modem_chat_script_chat_set_timeout(struct modem_chat_script_chat *script_chat,
1035 uint16_t timeout)
1036 {
1037 script_chat->timeout = timeout;
1038 }
1039
modem_chat_script_init(struct modem_chat_script * script)1040 void modem_chat_script_init(struct modem_chat_script *script)
1041 {
1042 memset(script, 0, sizeof(struct modem_chat_script));
1043 script->name = "";
1044 }
1045
modem_chat_script_set_name(struct modem_chat_script * script,const char * name)1046 void modem_chat_script_set_name(struct modem_chat_script *script, const char *name)
1047 {
1048 script->name = name;
1049 }
1050
modem_chat_script_set_script_chats(struct modem_chat_script * script,const struct modem_chat_script_chat * script_chats,uint16_t script_chats_size)1051 int modem_chat_script_set_script_chats(struct modem_chat_script *script,
1052 const struct modem_chat_script_chat *script_chats,
1053 uint16_t script_chats_size)
1054 {
1055 if (!modem_chat_validate_array(script_chats, script_chats_size)) {
1056 return -EINVAL;
1057 }
1058
1059 script->script_chats = script_chats;
1060 script->script_chats_size = script_chats_size;
1061 return 0;
1062 }
1063
modem_chat_script_set_abort_matches(struct modem_chat_script * script,const struct modem_chat_match * abort_matches,uint16_t abort_matches_size)1064 int modem_chat_script_set_abort_matches(struct modem_chat_script *script,
1065 const struct modem_chat_match *abort_matches,
1066 uint16_t abort_matches_size)
1067 {
1068 if (!modem_chat_validate_array(abort_matches, abort_matches_size)) {
1069 return -EINVAL;
1070 }
1071
1072 script->abort_matches = abort_matches;
1073 script->abort_matches_size = abort_matches_size;
1074 return 0;
1075 }
1076
modem_chat_script_set_callback(struct modem_chat_script * script,modem_chat_script_callback callback)1077 void modem_chat_script_set_callback(struct modem_chat_script *script,
1078 modem_chat_script_callback callback)
1079 {
1080 script->callback = callback;
1081 }
1082
modem_chat_script_set_timeout(struct modem_chat_script * script,uint32_t timeout_s)1083 void modem_chat_script_set_timeout(struct modem_chat_script *script, uint32_t timeout_s)
1084 {
1085 script->timeout = timeout_s;
1086 }
1087