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