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