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