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