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