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