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