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 #define LOG_TAG "bta_ag_at"
25 
26 #include "bta/ag/bta_ag_at.h"
27 
28 #include <bluetooth/log.h>
29 #include <com_android_bluetooth_flags.h>
30 
31 #include <cstdint>
32 #include <cstdlib>
33 #include <cstring>
34 
35 #include "bta/ag/bta_ag_int.h"
36 #include "bta/include/utl.h"
37 #include "osi/include/allocator.h"
38 
39 using namespace bluetooth;
40 
41 /*****************************************************************************
42  *  Constants
43  ****************************************************************************/
44 
45 /******************************************************************************
46  *
47  * Function         bta_ag_at_init
48  *
49  * Description      Initialize the AT command parser control block.
50  *
51  *
52  * Returns          void
53  *
54  *****************************************************************************/
bta_ag_at_init(tBTA_AG_AT_CB * p_cb)55 void bta_ag_at_init(tBTA_AG_AT_CB* p_cb) {
56   p_cb->p_cmd_buf = nullptr;
57   p_cb->cmd_pos = 0;
58 }
59 
60 /******************************************************************************
61  *
62  * Function         bta_ag_at_reinit
63  *
64  * Description      Re-initialize the AT command parser control block.  This
65  *                  function resets the AT command parser state and frees
66  *                  any GKI buffer.
67  *
68  *
69  * Returns          void
70  *
71  *****************************************************************************/
bta_ag_at_reinit(tBTA_AG_AT_CB * p_cb)72 void bta_ag_at_reinit(tBTA_AG_AT_CB* p_cb) {
73   osi_free_and_reset((void**)&p_cb->p_cmd_buf);
74   p_cb->cmd_pos = 0;
75 }
76 
77 /******************************************************************************
78  *
79  * Function         bta_ag_process_at
80  *
81  * Description      Parse AT commands.  This function will take the input
82  *                  character string and parse it for AT commands according to
83  *                  the AT command table passed in the control block.
84  *
85  *
86  * Returns          void
87  *
88  *****************************************************************************/
bta_ag_process_at(tBTA_AG_AT_CB * p_cb,char * p_end)89 static void bta_ag_process_at(tBTA_AG_AT_CB* p_cb, char* p_end) {
90   uint16_t idx;
91   uint8_t arg_type;
92   char* p_arg;
93   int16_t int_arg = 0;
94   /* loop through at command table looking for match */
95   for (idx = 0; p_cb->p_at_tbl[idx].p_cmd[0] != 0; idx++) {
96     if (!utl_strucmp(p_cb->p_at_tbl[idx].p_cmd, p_cb->p_cmd_buf)) {
97       break;
98     }
99   }
100 
101   /* if there is a match; verify argument type */
102   if (p_cb->p_at_tbl[idx].p_cmd[0] != 0) {
103     /* start of argument is p + strlen matching command */
104     p_arg = p_cb->p_cmd_buf + strlen(p_cb->p_at_tbl[idx].p_cmd);
105     if (p_arg > p_end) {
106       (*p_cb->p_err_cback)((tBTA_AG_SCB*)p_cb->p_user, false, nullptr);
107       return;
108     }
109 
110     /* if no argument */
111     if (p_arg[0] == 0) {
112       arg_type = BTA_AG_AT_NONE;
113     } else if (p_arg[0] == '?' && p_arg[1] == 0) {
114       /* else if arg is '?' and it is last character */
115       /* we have a read */
116       arg_type = BTA_AG_AT_READ;
117     } else if (p_arg[0] == '=' && p_arg[1] != 0) {
118       /* else if arg is '=' */
119       if (p_arg[1] == '?' && p_arg[2] == 0) {
120         /* we have a test */
121         arg_type = BTA_AG_AT_TEST;
122       } else {
123         /* we have a set */
124         arg_type = BTA_AG_AT_SET;
125 
126         /* skip past '=' */
127         p_arg++;
128       }
129     } else
130     /* else it is freeform argument */
131     {
132       arg_type = BTA_AG_AT_FREE;
133     }
134 
135     /* if arguments match command capabilities */
136     if ((arg_type & p_cb->p_at_tbl[idx].arg_type) != 0) {
137       /* if it's a set integer check max, min range */
138       if (arg_type == BTA_AG_AT_SET && p_cb->p_at_tbl[idx].fmt == BTA_AG_AT_INT) {
139         if (com::android::bluetooth::flags::bta_ag_cmd_brsf_allow_uint32()) {
140           if (p_cb->p_at_tbl[idx].command_id == BTA_AG_LOCAL_EVT_BRSF) {
141             // Per HFP v1.9 BRSF could be 32-bit integer and we should ignore
142             // all reserved bits rather than responding ERROR.
143             long long int_arg_ll = std::atoll(p_arg);
144             if (int_arg_ll >= (1ll << 32) || int_arg_ll < 0) {
145               int_arg_ll = -1;
146             }
147 
148             // Ignore reserved bits. 0xfff because there are 12 defined bits.
149             if (int_arg_ll > 0 && (int_arg_ll & (~0xfffll))) {
150               log::warn("BRSF: reserved bit is set: 0x{:x}", int_arg_ll);
151               int_arg_ll &= 0xfffll;
152             }
153 
154             int_arg = static_cast<int16_t>(int_arg_ll);
155           } else {
156             int_arg = utl_str2int(p_arg);
157           }
158         } else {
159           int_arg = utl_str2int(p_arg);
160         }
161         if (int_arg < (int16_t)p_cb->p_at_tbl[idx].min ||
162             int_arg > (int16_t)p_cb->p_at_tbl[idx].max) {
163           /* arg out of range; error */
164           log::warn("arg out of range");
165           (*p_cb->p_err_cback)((tBTA_AG_SCB*)p_cb->p_user, false, nullptr);
166         } else {
167           (*p_cb->p_cmd_cback)((tBTA_AG_SCB*)p_cb->p_user, p_cb->p_at_tbl[idx].command_id, arg_type,
168                                p_arg, p_end, int_arg);
169         }
170       } else {
171         (*p_cb->p_cmd_cback)((tBTA_AG_SCB*)p_cb->p_user, p_cb->p_at_tbl[idx].command_id, arg_type,
172                              p_arg, p_end, int_arg);
173       }
174     } else {
175       /* else error */
176       log::warn("Incoming arg type 0x{:x} does not match cmd arg type 0x{:x}", arg_type,
177                 p_cb->p_at_tbl[idx].arg_type);
178       (*p_cb->p_err_cback)((tBTA_AG_SCB*)p_cb->p_user, false, nullptr);
179     }
180   } else {
181     /* else no match call error callback */
182     log::warn("Unmatched command index {}", idx);
183     (*p_cb->p_err_cback)((tBTA_AG_SCB*)p_cb->p_user, true, p_cb->p_cmd_buf);
184   }
185 }
186 
187 /******************************************************************************
188  *
189  * Function         bta_ag_at_parse
190  *
191  * Description      Parse AT commands.  This function will take the input
192  *                  character string and parse it for AT commands according to
193  *                  the AT command table passed in the control block.
194  *
195  *
196  * Returns          void
197  *
198  *****************************************************************************/
bta_ag_at_parse(tBTA_AG_AT_CB * p_cb,char * p_buf,uint16_t len)199 void bta_ag_at_parse(tBTA_AG_AT_CB* p_cb, char* p_buf, uint16_t len) {
200   int i = 0;
201   char* p_save;
202 
203   if (p_cb->p_cmd_buf == nullptr) {
204     p_cb->p_cmd_buf = (char*)osi_malloc(p_cb->cmd_max_len);
205     p_cb->cmd_pos = 0;
206   }
207 
208   for (i = 0; i < len;) {
209     while (p_cb->cmd_pos < p_cb->cmd_max_len - 1 && i < len) {
210       /* Skip null characters between AT commands. */
211       if ((p_cb->cmd_pos == 0) && (p_buf[i] == 0)) {
212         i++;
213         continue;
214       }
215 
216       p_cb->p_cmd_buf[p_cb->cmd_pos] = p_buf[i++];
217       if (p_cb->p_cmd_buf[p_cb->cmd_pos] == '\r' || p_cb->p_cmd_buf[p_cb->cmd_pos] == '\n') {
218         p_cb->p_cmd_buf[p_cb->cmd_pos] = 0;
219         if ((p_cb->cmd_pos > 2) && (p_cb->p_cmd_buf[0] == 'A' || p_cb->p_cmd_buf[0] == 'a') &&
220             (p_cb->p_cmd_buf[1] == 'T' || p_cb->p_cmd_buf[1] == 't')) {
221           p_save = p_cb->p_cmd_buf;
222           char* p_end = p_cb->p_cmd_buf + p_cb->cmd_pos;
223           p_cb->p_cmd_buf += 2;
224           bta_ag_process_at(p_cb, p_end);
225           p_cb->p_cmd_buf = p_save;
226         }
227 
228         p_cb->cmd_pos = 0;
229 
230       } else if (p_cb->p_cmd_buf[p_cb->cmd_pos] == 0x1A || p_cb->p_cmd_buf[p_cb->cmd_pos] == 0x1B) {
231         p_cb->p_cmd_buf[++p_cb->cmd_pos] = 0;
232         (*p_cb->p_err_cback)((tBTA_AG_SCB*)p_cb->p_user, true, p_cb->p_cmd_buf);
233         p_cb->cmd_pos = 0;
234       } else {
235         ++p_cb->cmd_pos;
236       }
237     }
238 
239     if (i < len) {
240       p_cb->cmd_pos = 0;
241     }
242   }
243 }
244