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