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