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 
get_response_string(struct at_client * at,struct net_buf * buf,char stop_byte,enum at_state state)122 static int get_response_string(struct at_client *at, struct net_buf *buf,
123 			       char stop_byte, enum at_state state)
124 {
125 	int cmd_len = 0;
126 	uint8_t pos = at->pos;
127 	const char *str = (char *)buf->data;
128 
129 	while (cmd_len < buf->len && at->pos != at->buf_max_len) {
130 		if (*str != stop_byte) {
131 			at->buf[at->pos++] = *str;
132 			cmd_len++;
133 			str++;
134 			pos = at->pos;
135 		} else {
136 			cmd_len++;
137 			at->buf[at->pos] = '\0';
138 			at->pos = 0U;
139 			at->state = state;
140 			break;
141 		}
142 	}
143 	net_buf_pull(buf, cmd_len);
144 
145 	if (pos == at->buf_max_len) {
146 		return -ENOBUFS;
147 	}
148 
149 	return 0;
150 }
151 
reset_buffer(struct at_client * at)152 static void reset_buffer(struct at_client *at)
153 {
154 	(void)memset(at->buf, 0, at->buf_max_len);
155 	at->pos = 0U;
156 }
157 
at_state_start(struct at_client * at,struct net_buf * buf)158 static int at_state_start(struct at_client *at, struct net_buf *buf)
159 {
160 	int err;
161 
162 	err = at_check_byte(buf, '\r');
163 	if (err < 0) {
164 		return err;
165 	}
166 	at->state = AT_STATE_START_CR;
167 
168 	return 0;
169 }
170 
at_state_start_cr(struct at_client * at,struct net_buf * buf)171 static int at_state_start_cr(struct at_client *at, struct net_buf *buf)
172 {
173 	int err;
174 
175 	err = at_check_byte(buf, '\n');
176 	if (err < 0) {
177 		return err;
178 	}
179 	at->state = AT_STATE_START_LF;
180 
181 	return 0;
182 }
183 
at_state_start_lf(struct at_client * at,struct net_buf * buf)184 static int at_state_start_lf(struct at_client *at, struct net_buf *buf)
185 {
186 	reset_buffer(at);
187 	if (at_check_byte(buf, '+') == 0) {
188 		at->state = AT_STATE_GET_CMD_STRING;
189 		return 0;
190 	} else if (isalpha(*buf->data) != 0) {
191 		at->state = AT_STATE_GET_RESULT_STRING;
192 		return 0;
193 	}
194 
195 	return -ENODATA;
196 }
197 
at_state_get_cmd_string(struct at_client * at,struct net_buf * buf)198 static int at_state_get_cmd_string(struct at_client *at, struct net_buf *buf)
199 {
200 	return get_response_string(at, buf, ':', AT_STATE_PROCESS_CMD);
201 }
202 
is_cmer(struct at_client * at)203 static bool is_cmer(struct at_client *at)
204 {
205 	if (strncmp(at->buf, "CME ERROR", 9) == 0) {
206 		return true;
207 	}
208 
209 	return false;
210 }
211 
at_state_process_cmd(struct at_client * at,struct net_buf * buf)212 static int at_state_process_cmd(struct at_client *at, struct net_buf *buf)
213 {
214 	if (is_cmer(at)) {
215 		at->state = AT_STATE_PROCESS_AG_NW_ERR;
216 		return 0;
217 	}
218 
219 	if (at->resp) {
220 		at->resp(at, buf);
221 		at->resp = NULL;
222 		return 0;
223 	}
224 	at->state = AT_STATE_UNSOLICITED_CMD;
225 	return 0;
226 }
227 
at_state_get_result_string(struct at_client * at,struct net_buf * buf)228 static int at_state_get_result_string(struct at_client *at, struct net_buf *buf)
229 {
230 	return get_response_string(at, buf, '\r', AT_STATE_PROCESS_RESULT);
231 }
232 
is_ring(struct at_client * at)233 static bool is_ring(struct at_client *at)
234 {
235 	if (strncmp(at->buf, "RING", 4) == 0) {
236 		return true;
237 	}
238 
239 	return false;
240 }
241 
at_state_process_result(struct at_client * at,struct net_buf * buf)242 static int at_state_process_result(struct at_client *at, struct net_buf *buf)
243 {
244 	enum at_cme cme_err;
245 	enum at_result result;
246 
247 	if (is_ring(at)) {
248 		at->state = AT_STATE_UNSOLICITED_CMD;
249 		return 0;
250 	}
251 
252 	if (at_parse_result(at->buf, buf, &result) == 0) {
253 		if (at->finish) {
254 			/* cme_err is 0 - Is invalid until result is
255 			 * AT_RESULT_CME_ERROR
256 			 */
257 			cme_err = 0;
258 			at->finish(at, result, cme_err);
259 		}
260 	}
261 
262 	/* Reset the state to process unsolicited response */
263 	at->cmd_state = AT_CMD_START;
264 	at->state = AT_STATE_START;
265 
266 	return 0;
267 }
268 
cme_handle(struct at_client * at)269 int cme_handle(struct at_client *at)
270 {
271 	enum at_cme cme_err;
272 	uint32_t val;
273 
274 	if (!at_get_number(at, &val) && val <= CME_ERROR_NETWORK_NOT_ALLOWED) {
275 		cme_err = val;
276 	} else {
277 		cme_err = CME_ERROR_UNKNOWN;
278 	}
279 
280 	if (at->finish) {
281 		at->finish(at, AT_RESULT_CME_ERROR, cme_err);
282 	}
283 
284 	return 0;
285 }
286 
at_state_process_ag_nw_err(struct at_client * at,struct net_buf * buf)287 static int at_state_process_ag_nw_err(struct at_client *at, struct net_buf *buf)
288 {
289 	at->cmd_state = AT_CMD_GET_VALUE;
290 	return at_parse_cmd_input(at, buf, NULL, cme_handle,
291 				  AT_CMD_TYPE_NORMAL);
292 }
293 
at_state_unsolicited_cmd(struct at_client * at,struct net_buf * buf)294 static int at_state_unsolicited_cmd(struct at_client *at, struct net_buf *buf)
295 {
296 	if (at->unsolicited) {
297 		return at->unsolicited(at, buf);
298 	}
299 
300 	return -ENODATA;
301 }
302 
303 /* The order of handler function should match the enum at_state */
304 static handle_parse_input_t parser_cb[] = {
305 	at_state_start, /* AT_STATE_START */
306 	at_state_start_cr, /* AT_STATE_START_CR */
307 	at_state_start_lf, /* AT_STATE_START_LF */
308 	at_state_get_cmd_string, /* AT_STATE_GET_CMD_STRING */
309 	at_state_process_cmd, /* AT_STATE_PROCESS_CMD */
310 	at_state_get_result_string, /* AT_STATE_GET_RESULT_STRING */
311 	at_state_process_result, /* AT_STATE_PROCESS_RESULT */
312 	at_state_process_ag_nw_err, /* AT_STATE_PROCESS_AG_NW_ERR */
313 	at_state_unsolicited_cmd /* AT_STATE_UNSOLICITED_CMD */
314 };
315 
at_parse_input(struct at_client * at,struct net_buf * buf)316 int at_parse_input(struct at_client *at, struct net_buf *buf)
317 {
318 	int ret;
319 
320 	while (buf->len) {
321 		if (at->state < AT_STATE_START || at->state >= AT_STATE_END) {
322 			return -EINVAL;
323 		}
324 		ret = parser_cb[at->state](at, buf);
325 		if (ret < 0) {
326 			/* Reset the state in case of error */
327 			at->cmd_state = AT_CMD_START;
328 			at->state = AT_STATE_START;
329 			return ret;
330 		}
331 	}
332 
333 	return 0;
334 }
335 
at_cmd_start(struct at_client * at,struct net_buf * buf,const char * prefix,parse_val_t func,enum at_cmd_type type)336 static int at_cmd_start(struct at_client *at, struct net_buf *buf,
337 			const char *prefix, parse_val_t func,
338 			enum at_cmd_type type)
339 {
340 	if (!str_has_prefix(at->buf, prefix)) {
341 		if (type == AT_CMD_TYPE_NORMAL) {
342 			at->state = AT_STATE_UNSOLICITED_CMD;
343 		}
344 		return -ENODATA;
345 	}
346 
347 	if (type == AT_CMD_TYPE_OTHER) {
348 		/* Skip for Other type such as ..RING.. which does not have
349 		 * values to get processed.
350 		 */
351 		at->cmd_state = AT_CMD_PROCESS_VALUE;
352 	} else {
353 		at->cmd_state = AT_CMD_GET_VALUE;
354 	}
355 
356 	return 0;
357 }
358 
at_cmd_get_value(struct at_client * at,struct net_buf * buf,const char * prefix,parse_val_t func,enum at_cmd_type type)359 static int at_cmd_get_value(struct at_client *at, struct net_buf *buf,
360 			    const char *prefix, parse_val_t func,
361 			    enum at_cmd_type type)
362 {
363 	/* Reset buffer before getting the values */
364 	reset_buffer(at);
365 	return get_cmd_value(at, buf, '\r', AT_CMD_PROCESS_VALUE);
366 }
367 
at_cmd_process_value(struct at_client * at,struct net_buf * buf,const char * prefix,parse_val_t func,enum at_cmd_type type)368 static int at_cmd_process_value(struct at_client *at, struct net_buf *buf,
369 				const char *prefix, parse_val_t func,
370 				enum at_cmd_type type)
371 {
372 	int ret;
373 
374 	ret = func(at);
375 	at->cmd_state = AT_CMD_STATE_END_LF;
376 
377 	return ret;
378 }
379 
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)380 static int at_cmd_state_end_lf(struct at_client *at, struct net_buf *buf,
381 			       const char *prefix, parse_val_t func,
382 			       enum at_cmd_type type)
383 {
384 	int err;
385 
386 	err = at_check_byte(buf, '\n');
387 	if (err < 0) {
388 		return err;
389 	}
390 
391 	at->cmd_state = AT_CMD_START;
392 	at->state = AT_STATE_START;
393 	return 0;
394 }
395 
396 /* The order of handler function should match the enum at_cmd_state */
397 static handle_cmd_input_t cmd_parser_cb[] = {
398 	at_cmd_start, /* AT_CMD_START */
399 	at_cmd_get_value, /* AT_CMD_GET_VALUE */
400 	at_cmd_process_value, /* AT_CMD_PROCESS_VALUE */
401 	at_cmd_state_end_lf /* AT_CMD_STATE_END_LF */
402 };
403 
at_parse_cmd_input(struct at_client * at,struct net_buf * buf,const char * prefix,parse_val_t func,enum at_cmd_type type)404 int at_parse_cmd_input(struct at_client *at, struct net_buf *buf,
405 		       const char *prefix, parse_val_t func,
406 		       enum at_cmd_type type)
407 {
408 	int ret;
409 
410 	while (buf->len) {
411 		if (at->cmd_state < AT_CMD_START ||
412 		    at->cmd_state >= AT_CMD_STATE_END) {
413 			return -EINVAL;
414 		}
415 		ret = cmd_parser_cb[at->cmd_state](at, buf, prefix, func, type);
416 		if (ret < 0) {
417 			return ret;
418 		}
419 		/* Check for main state, the end of cmd parsing and return. */
420 		if (at->state == AT_STATE_START) {
421 			return 0;
422 		}
423 	}
424 
425 	return 0;
426 }
427 
at_has_next_list(struct at_client * at)428 int at_has_next_list(struct at_client *at)
429 {
430 	return at->buf[at->pos] != '\0';
431 }
432 
at_open_list(struct at_client * at)433 int at_open_list(struct at_client *at)
434 {
435 	skip_space(at);
436 
437 	/* The list shall start with '(' open parenthesis */
438 	if (at->buf[at->pos] != '(') {
439 		return -ENODATA;
440 	}
441 	at->pos++;
442 
443 	return 0;
444 }
445 
at_close_list(struct at_client * at)446 int at_close_list(struct at_client *at)
447 {
448 	skip_space(at);
449 
450 	if (at->buf[at->pos] != ')') {
451 		return -ENODATA;
452 	}
453 	at->pos++;
454 
455 	next_list(at);
456 
457 	return 0;
458 }
459 
at_list_get_string(struct at_client * at,char * name,uint8_t len)460 int at_list_get_string(struct at_client *at, char *name, uint8_t len)
461 {
462 	int i = 0;
463 
464 	skip_space(at);
465 
466 	if (at->buf[at->pos] != '"') {
467 		return -ENODATA;
468 	}
469 	at->pos++;
470 
471 	while (at->buf[at->pos] != '\0' && at->buf[at->pos] != '"') {
472 		if (i == len) {
473 			return -ENODATA;
474 		}
475 		name[i++] = at->buf[at->pos++];
476 	}
477 
478 	if (i == len) {
479 		return -ENODATA;
480 	}
481 
482 	name[i] = '\0';
483 
484 	if (at->buf[at->pos] != '"') {
485 		return -ENODATA;
486 	}
487 	at->pos++;
488 
489 	skip_space(at);
490 	next_list(at);
491 
492 	return 0;
493 }
494 
at_list_get_range(struct at_client * at,uint32_t * min,uint32_t * max)495 int at_list_get_range(struct at_client *at, uint32_t *min, uint32_t *max)
496 {
497 	uint32_t low, high;
498 	int ret;
499 
500 	ret = at_get_number(at, &low);
501 	if (ret < 0) {
502 		return ret;
503 	}
504 
505 	if (at->buf[at->pos] == '-') {
506 		at->pos++;
507 		goto out;
508 	}
509 
510 	if (isdigit((unsigned char)at->buf[at->pos]) == 0) {
511 		return -ENODATA;
512 	}
513 out:
514 	ret = at_get_number(at, &high);
515 	if (ret < 0) {
516 		return ret;
517 	}
518 
519 	*min = low;
520 	*max = high;
521 
522 	next_list(at);
523 
524 	return 0;
525 }
526 
at_register_unsolicited(struct at_client * at,at_resp_cb_t unsolicited)527 void at_register_unsolicited(struct at_client *at, at_resp_cb_t unsolicited)
528 {
529 	at->unsolicited = unsolicited;
530 }
531 
at_register(struct at_client * at,at_resp_cb_t resp,at_finish_cb_t finish)532 void at_register(struct at_client *at, at_resp_cb_t resp, at_finish_cb_t finish)
533 {
534 	at->resp = resp;
535 	at->finish = finish;
536 	at->state = AT_STATE_START;
537 }
538