1 /******************************************************************************
2  *
3  *  Copyright 2004-2012 Broadcom Corporation
4  *
5  *  Licensed under the Apache License, Version 2.0 (the "License");
6  *  you may not use this file except in compliance with the License.
7  *  You may obtain a copy of the License at:
8  *
9  *  http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  *
17  ******************************************************************************/
18 
19 /******************************************************************************
20  *
21  *  BTA AG AT command interpreter.
22  *
23  ******************************************************************************/
24 
25 #include <cstring>
26 
27 #include "bt_common.h"
28 #include "bta_ag_at.h"
29 #include "utl.h"
30 
31 /*****************************************************************************
32  *  Constants
33  ****************************************************************************/
34 
35 /******************************************************************************
36  *
37  * Function         bta_ag_at_init
38  *
39  * Description      Initialize the AT command parser control block.
40  *
41  *
42  * Returns          void
43  *
44  *****************************************************************************/
bta_ag_at_init(tBTA_AG_AT_CB * p_cb)45 void bta_ag_at_init(tBTA_AG_AT_CB* p_cb) {
46   p_cb->p_cmd_buf = nullptr;
47   p_cb->cmd_pos = 0;
48 }
49 
50 /******************************************************************************
51  *
52  * Function         bta_ag_at_reinit
53  *
54  * Description      Re-initialize the AT command parser control block.  This
55  *                  function resets the AT command parser state and frees
56  *                  any GKI buffer.
57  *
58  *
59  * Returns          void
60  *
61  *****************************************************************************/
bta_ag_at_reinit(tBTA_AG_AT_CB * p_cb)62 void bta_ag_at_reinit(tBTA_AG_AT_CB* p_cb) {
63   osi_free_and_reset((void**)&p_cb->p_cmd_buf);
64   p_cb->cmd_pos = 0;
65 }
66 
67 /******************************************************************************
68  *
69  * Function         bta_ag_process_at
70  *
71  * Description      Parse AT commands.  This function will take the input
72  *                  character string and parse it for AT commands according to
73  *                  the AT command table passed in the control block.
74  *
75  *
76  * Returns          void
77  *
78  *****************************************************************************/
bta_ag_process_at(tBTA_AG_AT_CB * p_cb)79 void bta_ag_process_at(tBTA_AG_AT_CB* p_cb) {
80   uint16_t idx;
81   uint8_t arg_type;
82   char* p_arg;
83   int16_t int_arg = 0;
84   /* loop through at command table looking for match */
85   for (idx = 0; p_cb->p_at_tbl[idx].p_cmd[0] != 0; idx++) {
86     if (!utl_strucmp(p_cb->p_at_tbl[idx].p_cmd, p_cb->p_cmd_buf)) {
87       break;
88     }
89   }
90 
91   /* if there is a match; verify argument type */
92   if (p_cb->p_at_tbl[idx].p_cmd[0] != 0) {
93     /* start of argument is p + strlen matching command */
94     p_arg = p_cb->p_cmd_buf + strlen(p_cb->p_at_tbl[idx].p_cmd);
95 
96     /* if no argument */
97     if (p_arg[0] == 0) {
98       arg_type = BTA_AG_AT_NONE;
99     }
100     /* else if arg is '?' and it is last character */
101     else if (p_arg[0] == '?' && p_arg[1] == 0) {
102       /* we have a read */
103       arg_type = BTA_AG_AT_READ;
104     }
105     /* else if arg is '=' */
106     else if (p_arg[0] == '=' && p_arg[1] != 0) {
107       if (p_arg[1] == '?' && p_arg[2] == 0) {
108         /* we have a test */
109         arg_type = BTA_AG_AT_TEST;
110       } else {
111         /* we have a set */
112         arg_type = BTA_AG_AT_SET;
113 
114         /* skip past '=' */
115         p_arg++;
116       }
117     } else
118     /* else it is freeform argument */
119     {
120       arg_type = BTA_AG_AT_FREE;
121     }
122 
123     /* if arguments match command capabilities */
124     if ((arg_type & p_cb->p_at_tbl[idx].arg_type) != 0) {
125       /* if it's a set integer check max, min range */
126       if (arg_type == BTA_AG_AT_SET &&
127           p_cb->p_at_tbl[idx].fmt == BTA_AG_AT_INT) {
128         int_arg = utl_str2int(p_arg);
129         if (int_arg < (int16_t)p_cb->p_at_tbl[idx].min ||
130             int_arg > (int16_t)p_cb->p_at_tbl[idx].max) {
131           /* arg out of range; error */
132           (*p_cb->p_err_cback)((tBTA_AG_SCB*)p_cb->p_user, false, nullptr);
133         } else {
134           (*p_cb->p_cmd_cback)((tBTA_AG_SCB*)p_cb->p_user,
135                                p_cb->p_at_tbl[idx].command_id, arg_type, p_arg,
136                                int_arg);
137         }
138       } else {
139         (*p_cb->p_cmd_cback)((tBTA_AG_SCB*)p_cb->p_user,
140                              p_cb->p_at_tbl[idx].command_id, arg_type, p_arg,
141                              int_arg);
142       }
143     }
144     /* else error */
145     else {
146       (*p_cb->p_err_cback)((tBTA_AG_SCB*)p_cb->p_user, false, nullptr);
147     }
148   }
149   /* else no match call error callback */
150   else {
151     (*p_cb->p_err_cback)((tBTA_AG_SCB*)p_cb->p_user, true, p_cb->p_cmd_buf);
152   }
153 }
154 
155 /******************************************************************************
156  *
157  * Function         bta_ag_at_parse
158  *
159  * Description      Parse AT commands.  This function will take the input
160  *                  character string and parse it for AT commands according to
161  *                  the AT command table passed in the control block.
162  *
163  *
164  * Returns          void
165  *
166  *****************************************************************************/
bta_ag_at_parse(tBTA_AG_AT_CB * p_cb,char * p_buf,uint16_t len)167 void bta_ag_at_parse(tBTA_AG_AT_CB* p_cb, char* p_buf, uint16_t len) {
168   int i = 0;
169   char* p_save;
170 
171   if (p_cb->p_cmd_buf == nullptr) {
172     p_cb->p_cmd_buf = (char*)osi_malloc(p_cb->cmd_max_len);
173     p_cb->cmd_pos = 0;
174   }
175 
176   for (i = 0; i < len;) {
177     while (p_cb->cmd_pos < p_cb->cmd_max_len - 1 && i < len) {
178       /* Skip null characters between AT commands. */
179       if ((p_cb->cmd_pos == 0) && (p_buf[i] == 0)) {
180         i++;
181         continue;
182       }
183 
184       p_cb->p_cmd_buf[p_cb->cmd_pos] = p_buf[i++];
185       if (p_cb->p_cmd_buf[p_cb->cmd_pos] == '\r' ||
186           p_cb->p_cmd_buf[p_cb->cmd_pos] == '\n') {
187         p_cb->p_cmd_buf[p_cb->cmd_pos] = 0;
188         if ((p_cb->cmd_pos > 2) &&
189             (p_cb->p_cmd_buf[0] == 'A' || p_cb->p_cmd_buf[0] == 'a') &&
190             (p_cb->p_cmd_buf[1] == 'T' || p_cb->p_cmd_buf[1] == 't')) {
191           p_save = p_cb->p_cmd_buf;
192           p_cb->p_cmd_buf += 2;
193           bta_ag_process_at(p_cb);
194           p_cb->p_cmd_buf = p_save;
195         }
196 
197         p_cb->cmd_pos = 0;
198 
199       } else if (p_cb->p_cmd_buf[p_cb->cmd_pos] == 0x1A ||
200                  p_cb->p_cmd_buf[p_cb->cmd_pos] == 0x1B) {
201         p_cb->p_cmd_buf[++p_cb->cmd_pos] = 0;
202         (*p_cb->p_err_cback)((tBTA_AG_SCB*)p_cb->p_user, true, p_cb->p_cmd_buf);
203         p_cb->cmd_pos = 0;
204       } else {
205         ++p_cb->cmd_pos;
206       }
207     }
208 
209     if (i < len) p_cb->cmd_pos = 0;
210   }
211 }
212