1 /**
2  * @file at.c
3  * Generic AT command handling library implementation
4  */
5 
6 /*
7  * Copyright (c) 2015-2016 Intel Corporation
8  *
9  * SPDX-License-Identifier: Apache-2.0
10  */
11 
12 #include <errno.h>
13 #include <ctype.h>
14 #include <string.h>
15 #include <stdarg.h>
16 #include <zephyr/net_buf.h>
17 
18 #include "at.h"
19 
next_list(struct at_client * at)20 static void next_list(struct at_client *at)
21 {
22 	if (at->buf[at->pos] == ',') {
23 		at->pos++;
24 	}
25 }
26 
at_check_byte(struct net_buf * buf,char check_byte)27 int at_check_byte(struct net_buf *buf, char check_byte)
28 {
29 	const unsigned char *str = buf->data;
30 
31 	if (*str != check_byte) {
32 		return -EINVAL;
33 	}
34 	net_buf_pull(buf, 1);
35 
36 	return 0;
37 }
38 
skip_space(struct at_client * at)39 static void skip_space(struct at_client *at)
40 {
41 	while (at->buf[at->pos] == ' ') {
42 		at->pos++;
43 	}
44 }
45 
at_get_number(struct at_client * at,uint32_t * val)46 int at_get_number(struct at_client *at, uint32_t *val)
47 {
48 	uint32_t i;
49 
50 	skip_space(at);
51 
52 	for (i = 0U, *val = 0U;
53 	     isdigit((unsigned char)at->buf[at->pos]) != 0;
54 	     at->pos++, i++) {
55 		*val = *val * 10U + at->buf[at->pos] - '0';
56 	}
57 
58 	if (i == 0U) {
59 		return -ENODATA;
60 	}
61 
62 	next_list(at);
63 	return 0;
64 }
65 
str_has_prefix(const char * str,const char * prefix)66 static bool str_has_prefix(const char *str, const char *prefix)
67 {
68 	if (strncmp(str, prefix, strlen(prefix)) != 0) {
69 		return false;
70 	}
71 
72 	return true;
73 }
74 
at_parse_result(const char * str,struct net_buf * buf,enum at_result * result)75 static int at_parse_result(const char *str, struct net_buf *buf,
76 			   enum at_result *result)
77 {
78 	/* Map the result and check for end lf */
79 	if ((!strncmp(str, "OK", 2)) && (at_check_byte(buf, '\n') == 0)) {
80 		*result = AT_RESULT_OK;
81 		return 0;
82 	}
83 
84 	if ((!strncmp(str, "ERROR", 5)) && (at_check_byte(buf, '\n')) == 0) {
85 		*result = AT_RESULT_ERROR;
86 		return 0;
87 	}
88 
89 	return -ENOMSG;
90 }
91 
get_cmd_value(struct at_client * at,struct net_buf * buf,char stop_byte,enum at_cmd_state cmd_state)92 static int get_cmd_value(struct at_client *at, struct net_buf *buf,
93 			 char stop_byte, enum at_cmd_state cmd_state)
94 {
95 	int cmd_len = 0;
96 	uint8_t pos = at->pos;
97 	const char *str = (char *)buf->data;
98 
99 	while (cmd_len < buf->len && at->pos != at->buf_max_len) {
100 		if (*str != stop_byte) {
101 			at->buf[at->pos++] = *str;
102 			cmd_len++;
103 			str++;
104 			pos = at->pos;
105 		} else {
106 			cmd_len++;
107 			at->buf[at->pos] = '\0';
108 			at->pos = 0U;
109 			at->cmd_state = cmd_state;
110 			break;
111 		}
112 	}
113 	net_buf_pull(buf, cmd_len);
114 
115 	if (pos == at->buf_max_len) {
116 		return -ENOBUFS;
117 	}
118 
119 	return 0;
120 }
121 
is_stop_byte(char target,char * stop_string)122 static bool is_stop_byte(char target, char *stop_string)
123 {
124 	return (strchr(stop_string, target) != NULL);
125 }
126 
is_vgm_or_vgs(struct at_client * at)127 static bool is_vgm_or_vgs(struct at_client *at)
128 {
129 	if (!strcmp(at->buf, "VGM")) {
130 		return true;
131 	}
132 
133 	if (!strcmp(at->buf, "VGS")) {
134 		return true;
135 	}
136 	return false;
137 }
138 
get_response_string(struct at_client * at,struct net_buf * buf,char * stop_string,enum at_state state)139 static int get_response_string(struct at_client *at, struct net_buf *buf, char *stop_string,
140 			       enum at_state state)
141 {
142 	int cmd_len = 0;
143 	uint8_t pos = at->pos;
144 	const char *str = (char *)buf->data;
145 
146 	while (cmd_len < buf->len && at->pos != at->buf_max_len) {
147 		if (!is_stop_byte(*str, stop_string)) {
148 			at->buf[at->pos++] = *str;
149 			cmd_len++;
150 			str++;
151 			pos = at->pos;
152 		} else {
153 			char stop_byte = at->buf[at->pos];
154 
155 			cmd_len++;
156 			at->buf[at->pos] = '\0';
157 			at->pos = 0U;
158 			at->state = state;
159 			if ((stop_byte == '=') && !is_vgm_or_vgs(at)) {
160 				return -EINVAL;
161 			}
162 			break;
163 		}
164 	}
165 	net_buf_pull(buf, cmd_len);
166 
167 	if (pos == at->buf_max_len) {
168 		return -ENOBUFS;
169 	}
170 
171 	return 0;
172 }
173 
reset_buffer(struct at_client * at)174 static void reset_buffer(struct at_client *at)
175 {
176 	(void)memset(at->buf, 0, at->buf_max_len);
177 	at->pos = 0U;
178 }
179 
at_state_start(struct at_client * at,struct net_buf * buf)180 static int at_state_start(struct at_client *at, struct net_buf *buf)
181 {
182 	int err;
183 
184 	err = at_check_byte(buf, '\r');
185 	if (err < 0) {
186 		return err;
187 	}
188 	at->state = AT_STATE_START_CR;
189 
190 	return 0;
191 }
192 
at_state_start_cr(struct at_client * at,struct net_buf * buf)193 static int at_state_start_cr(struct at_client *at, struct net_buf *buf)
194 {
195 	int err;
196 
197 	err = at_check_byte(buf, '\n');
198 	if (err < 0) {
199 		return err;
200 	}
201 	at->state = AT_STATE_START_LF;
202 
203 	return 0;
204 }
205 
at_state_start_lf(struct at_client * at,struct net_buf * buf)206 static int at_state_start_lf(struct at_client *at, struct net_buf *buf)
207 {
208 	reset_buffer(at);
209 	if (at_check_byte(buf, '+') == 0) {
210 		at->state = AT_STATE_GET_CMD_STRING;
211 		return 0;
212 	} else if (isalpha(*buf->data) != 0) {
213 		at->state = AT_STATE_GET_RESULT_STRING;
214 		return 0;
215 	}
216 
217 	return -ENODATA;
218 }
219 
at_state_get_cmd_string(struct at_client * at,struct net_buf * buf)220 static int at_state_get_cmd_string(struct at_client *at, struct net_buf *buf)
221 {
222 	return get_response_string(at, buf, ":=", AT_STATE_PROCESS_CMD);
223 }
224 
is_cmer(struct at_client * at)225 static bool is_cmer(struct at_client *at)
226 {
227 	if (strncmp(at->buf, "CME ERROR", 9) == 0) {
228 		return true;
229 	}
230 
231 	return false;
232 }
233 
at_state_process_cmd(struct at_client * at,struct net_buf * buf)234 static int at_state_process_cmd(struct at_client *at, struct net_buf *buf)
235 {
236 	if (is_cmer(at)) {
237 		at->state = AT_STATE_PROCESS_AG_NW_ERR;
238 		return 0;
239 	}
240 
241 	if (at->resp) {
242 		at->resp(at, buf);
243 		at->resp = NULL;
244 		return 0;
245 	}
246 	at->state = AT_STATE_UNSOLICITED_CMD;
247 	return 0;
248 }
249 
at_state_get_result_string(struct at_client * at,struct net_buf * buf)250 static int at_state_get_result_string(struct at_client *at, struct net_buf *buf)
251 {
252 	return get_response_string(at, buf, "\r", AT_STATE_PROCESS_RESULT);
253 }
254 
is_ring(struct at_client * at)255 static bool is_ring(struct at_client *at)
256 {
257 	if (strncmp(at->buf, "RING", 4) == 0) {
258 		return true;
259 	}
260 
261 	return false;
262 }
263 
at_state_process_result(struct at_client * at,struct net_buf * buf)264 static int at_state_process_result(struct at_client *at, struct net_buf *buf)
265 {
266 	enum at_cme cme_err;
267 	enum at_result result;
268 
269 	if (is_ring(at)) {
270 		at->state = AT_STATE_UNSOLICITED_CMD;
271 		return 0;
272 	}
273 
274 	if (at_parse_result(at->buf, buf, &result) == 0) {
275 		if (at->finish) {
276 			/* cme_err is 0 - Is invalid until result is
277 			 * AT_RESULT_CME_ERROR
278 			 */
279 			cme_err = 0;
280 			at->finish(at, result, cme_err);
281 		}
282 	}
283 
284 	/* Reset the state to process unsolicited response */
285 	at->cmd_state = AT_CMD_START;
286 	at->state = AT_STATE_START;
287 
288 	return 0;
289 }
290 
cme_handle(struct at_client * at)291 int cme_handle(struct at_client *at)
292 {
293 	enum at_cme cme_err;
294 	uint32_t val;
295 
296 	if (!at_get_number(at, &val) && val <= CME_ERROR_NETWORK_NOT_ALLOWED) {
297 		cme_err = val;
298 	} else {
299 		cme_err = CME_ERROR_UNKNOWN;
300 	}
301 
302 	if (at->finish) {
303 		at->finish(at, AT_RESULT_CME_ERROR, cme_err);
304 	}
305 
306 	return 0;
307 }
308 
at_state_process_ag_nw_err(struct at_client * at,struct net_buf * buf)309 static int at_state_process_ag_nw_err(struct at_client *at, struct net_buf *buf)
310 {
311 	at->cmd_state = AT_CMD_GET_VALUE;
312 	return at_parse_cmd_input(at, buf, NULL, cme_handle,
313 				  AT_CMD_TYPE_NORMAL);
314 }
315 
at_state_unsolicited_cmd(struct at_client * at,struct net_buf * buf)316 static int at_state_unsolicited_cmd(struct at_client *at, struct net_buf *buf)
317 {
318 	if (at->unsolicited) {
319 		return at->unsolicited(at, buf);
320 	}
321 
322 	return -ENODATA;
323 }
324 
325 /* The order of handler function should match the enum at_state */
326 static handle_parse_input_t parser_cb[] = {
327 	at_state_start, /* AT_STATE_START */
328 	at_state_start_cr, /* AT_STATE_START_CR */
329 	at_state_start_lf, /* AT_STATE_START_LF */
330 	at_state_get_cmd_string, /* AT_STATE_GET_CMD_STRING */
331 	at_state_process_cmd, /* AT_STATE_PROCESS_CMD */
332 	at_state_get_result_string, /* AT_STATE_GET_RESULT_STRING */
333 	at_state_process_result, /* AT_STATE_PROCESS_RESULT */
334 	at_state_process_ag_nw_err, /* AT_STATE_PROCESS_AG_NW_ERR */
335 	at_state_unsolicited_cmd /* AT_STATE_UNSOLICITED_CMD */
336 };
337 
at_parse_input(struct at_client * at,struct net_buf * buf)338 int at_parse_input(struct at_client *at, struct net_buf *buf)
339 {
340 	int ret;
341 
342 	while (buf->len) {
343 		if (at->state < AT_STATE_START || at->state >= AT_STATE_END) {
344 			return -EINVAL;
345 		}
346 		ret = parser_cb[at->state](at, buf);
347 		if (ret < 0) {
348 			/* Reset the state in case of error */
349 			at->cmd_state = AT_CMD_START;
350 			at->state = AT_STATE_START;
351 			return ret;
352 		}
353 	}
354 
355 	return 0;
356 }
357 
at_cmd_start(struct at_client * at,struct net_buf * buf,const char * prefix,parse_val_t func,enum at_cmd_type type)358 static int at_cmd_start(struct at_client *at, struct net_buf *buf,
359 			const char *prefix, parse_val_t func,
360 			enum at_cmd_type type)
361 {
362 	if (!str_has_prefix(at->buf, prefix)) {
363 		if (type == AT_CMD_TYPE_NORMAL) {
364 			at->state = AT_STATE_UNSOLICITED_CMD;
365 		}
366 		return -ENODATA;
367 	}
368 
369 	if (type == AT_CMD_TYPE_OTHER) {
370 		/* Skip for Other type such as ..RING.. which does not have
371 		 * values to get processed.
372 		 */
373 		at->cmd_state = AT_CMD_PROCESS_VALUE;
374 	} else {
375 		at->cmd_state = AT_CMD_GET_VALUE;
376 	}
377 
378 	return 0;
379 }
380 
at_cmd_get_value(struct at_client * at,struct net_buf * buf,const char * prefix,parse_val_t func,enum at_cmd_type type)381 static int at_cmd_get_value(struct at_client *at, struct net_buf *buf,
382 			    const char *prefix, parse_val_t func,
383 			    enum at_cmd_type type)
384 {
385 	/* Reset buffer before getting the values */
386 	reset_buffer(at);
387 	return get_cmd_value(at, buf, '\r', AT_CMD_PROCESS_VALUE);
388 }
389 
at_cmd_process_value(struct at_client * at,struct net_buf * buf,const char * prefix,parse_val_t func,enum at_cmd_type type)390 static int at_cmd_process_value(struct at_client *at, struct net_buf *buf,
391 				const char *prefix, parse_val_t func,
392 				enum at_cmd_type type)
393 {
394 	int ret;
395 
396 	ret = func(at);
397 	at->cmd_state = AT_CMD_STATE_END_LF;
398 
399 	return ret;
400 }
401 
at_cmd_state_end_lf(struct at_client * at,struct net_buf * buf,const char * prefix,parse_val_t func,enum at_cmd_type type)402 static int at_cmd_state_end_lf(struct at_client *at, struct net_buf *buf,
403 			       const char *prefix, parse_val_t func,
404 			       enum at_cmd_type type)
405 {
406 	int err;
407 
408 	err = at_check_byte(buf, '\n');
409 	if (err < 0) {
410 		return err;
411 	}
412 
413 	at->cmd_state = AT_CMD_START;
414 	at->state = AT_STATE_START;
415 	return 0;
416 }
417 
418 /* The order of handler function should match the enum at_cmd_state */
419 static handle_cmd_input_t cmd_parser_cb[] = {
420 	at_cmd_start, /* AT_CMD_START */
421 	at_cmd_get_value, /* AT_CMD_GET_VALUE */
422 	at_cmd_process_value, /* AT_CMD_PROCESS_VALUE */
423 	at_cmd_state_end_lf /* AT_CMD_STATE_END_LF */
424 };
425 
at_parse_cmd_input(struct at_client * at,struct net_buf * buf,const char * prefix,parse_val_t func,enum at_cmd_type type)426 int at_parse_cmd_input(struct at_client *at, struct net_buf *buf,
427 		       const char *prefix, parse_val_t func,
428 		       enum at_cmd_type type)
429 {
430 	int ret;
431 
432 	while (buf->len) {
433 		if (at->cmd_state < AT_CMD_START ||
434 		    at->cmd_state >= AT_CMD_STATE_END) {
435 			return -EINVAL;
436 		}
437 		ret = cmd_parser_cb[at->cmd_state](at, buf, prefix, func, type);
438 		if (ret < 0) {
439 			return ret;
440 		}
441 		/* Check for main state, the end of cmd parsing and return. */
442 		if (at->state == AT_STATE_START) {
443 			return 0;
444 		}
445 	}
446 
447 	return 0;
448 }
449 
at_has_next_list(struct at_client * at)450 int at_has_next_list(struct at_client *at)
451 {
452 	return at->buf[at->pos] != '\0' && at->buf[at->pos] != ')';
453 }
454 
at_open_list(struct at_client * at)455 int at_open_list(struct at_client *at)
456 {
457 	skip_space(at);
458 
459 	/* The list shall start with '(' open parenthesis */
460 	if (at->buf[at->pos] != '(') {
461 		return -ENODATA;
462 	}
463 	at->pos++;
464 
465 	return 0;
466 }
467 
at_close_list(struct at_client * at)468 int at_close_list(struct at_client *at)
469 {
470 	skip_space(at);
471 
472 	if (at->buf[at->pos] != ')') {
473 		return -ENODATA;
474 	}
475 	at->pos++;
476 
477 	next_list(at);
478 
479 	return 0;
480 }
481 
at_list_get_string(struct at_client * at,char * name,uint8_t len)482 int at_list_get_string(struct at_client *at, char *name, uint8_t len)
483 {
484 	int i = 0;
485 
486 	skip_space(at);
487 
488 	if (at->buf[at->pos] != '"') {
489 		return -ENODATA;
490 	}
491 	at->pos++;
492 
493 	while (at->buf[at->pos] != '\0' && at->buf[at->pos] != '"') {
494 		if (i == len) {
495 			return -ENODATA;
496 		}
497 		name[i++] = at->buf[at->pos++];
498 	}
499 
500 	if (i == len) {
501 		return -ENODATA;
502 	}
503 
504 	name[i] = '\0';
505 
506 	if (at->buf[at->pos] != '"') {
507 		return -ENODATA;
508 	}
509 	at->pos++;
510 
511 	skip_space(at);
512 	next_list(at);
513 
514 	return 0;
515 }
516 
at_list_get_range(struct at_client * at,uint32_t * min,uint32_t * max)517 int at_list_get_range(struct at_client *at, uint32_t *min, uint32_t *max)
518 {
519 	uint32_t low, high;
520 	int ret;
521 
522 	ret = at_get_number(at, &low);
523 	if (ret < 0) {
524 		return ret;
525 	}
526 
527 	if (at->buf[at->pos] == '-') {
528 		at->pos++;
529 		goto out;
530 	}
531 
532 	if (isdigit((unsigned char)at->buf[at->pos]) == 0) {
533 		return -ENODATA;
534 	}
535 out:
536 	ret = at_get_number(at, &high);
537 	if (ret < 0) {
538 		return ret;
539 	}
540 
541 	*min = low;
542 	*max = high;
543 
544 	next_list(at);
545 
546 	return 0;
547 }
548 
at_register_unsolicited(struct at_client * at,at_resp_cb_t unsolicited)549 void at_register_unsolicited(struct at_client *at, at_resp_cb_t unsolicited)
550 {
551 	at->unsolicited = unsolicited;
552 }
553 
at_register(struct at_client * at,at_resp_cb_t resp,at_finish_cb_t finish)554 void at_register(struct at_client *at, at_resp_cb_t resp, at_finish_cb_t finish)
555 {
556 	at->resp = resp;
557 	at->finish = finish;
558 	at->state = AT_STATE_START;
559 }
560 
at_get_string(struct at_client * at)561 char *at_get_string(struct at_client *at)
562 {
563 	uint8_t pos = at->pos;
564 	char *string;
565 
566 	skip_space(at);
567 
568 	if (at->buf[at->pos] != '"') {
569 		at->pos = pos;
570 		return NULL;
571 	}
572 	at->pos++;
573 	string = &at->buf[at->pos];
574 
575 	while (at->buf[at->pos] != '\0' && at->buf[at->pos] != '"') {
576 		at->pos++;
577 	}
578 
579 	if (at->buf[at->pos] != '"') {
580 		at->pos = pos;
581 		return NULL;
582 	}
583 
584 	at->buf[at->pos] = '\0';
585 	at->pos++;
586 
587 	skip_space(at);
588 	next_list(at);
589 
590 	return string;
591 }
592 
at_get_raw_string(struct at_client * at,size_t * string_len)593 char *at_get_raw_string(struct at_client *at, size_t *string_len)
594 {
595 	char *string;
596 
597 	skip_space(at);
598 
599 	string = &at->buf[at->pos];
600 
601 	while (at->buf[at->pos] != '\0' &&
602 		   at->buf[at->pos] != ',' &&
603 		   at->buf[at->pos] != ')') {
604 		at->pos++;
605 	}
606 
607 	if (string_len) {
608 		*string_len = &at->buf[at->pos] - string;
609 	}
610 
611 	skip_space(at);
612 	next_list(at);
613 
614 	return string;
615 }
616