/****************************************************************************** * * Copyright 2004-2012 Broadcom Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ******************************************************************************/ /****************************************************************************** * * BTA AG AT command interpreter. * ******************************************************************************/ #define LOG_TAG "bta_ag_at" #include #include "bt_target.h" // Must be first to define build configuration: #include "bta/ag/bta_ag_at.h" #include "bta/ag/bta_ag_int.h" #include "bta/include/utl.h" #include "osi/include/allocator.h" #include "osi/include/log.h" /***************************************************************************** * Constants ****************************************************************************/ /****************************************************************************** * * Function bta_ag_at_init * * Description Initialize the AT command parser control block. * * * Returns void * *****************************************************************************/ void bta_ag_at_init(tBTA_AG_AT_CB* p_cb) { p_cb->p_cmd_buf = nullptr; p_cb->cmd_pos = 0; } /****************************************************************************** * * Function bta_ag_at_reinit * * Description Re-initialize the AT command parser control block. This * function resets the AT command parser state and frees * any GKI buffer. * * * Returns void * *****************************************************************************/ void bta_ag_at_reinit(tBTA_AG_AT_CB* p_cb) { osi_free_and_reset((void**)&p_cb->p_cmd_buf); p_cb->cmd_pos = 0; } /****************************************************************************** * * Function bta_ag_process_at * * Description Parse AT commands. This function will take the input * character string and parse it for AT commands according to * the AT command table passed in the control block. * * * Returns void * *****************************************************************************/ void bta_ag_process_at(tBTA_AG_AT_CB* p_cb, char* p_end) { uint16_t idx; uint8_t arg_type; char* p_arg; int16_t int_arg = 0; /* loop through at command table looking for match */ for (idx = 0; p_cb->p_at_tbl[idx].p_cmd[0] != 0; idx++) { if (!utl_strucmp(p_cb->p_at_tbl[idx].p_cmd, p_cb->p_cmd_buf)) { break; } } /* if there is a match; verify argument type */ if (p_cb->p_at_tbl[idx].p_cmd[0] != 0) { /* start of argument is p + strlen matching command */ p_arg = p_cb->p_cmd_buf + strlen(p_cb->p_at_tbl[idx].p_cmd); if (p_arg > p_end) { (*p_cb->p_err_cback)((tBTA_AG_SCB*)p_cb->p_user, false, nullptr); android_errorWriteLog(0x534e4554, "112860487"); return; } /* if no argument */ if (p_arg[0] == 0) { arg_type = BTA_AG_AT_NONE; } /* else if arg is '?' and it is last character */ else if (p_arg[0] == '?' && p_arg[1] == 0) { /* we have a read */ arg_type = BTA_AG_AT_READ; } /* else if arg is '=' */ else if (p_arg[0] == '=' && p_arg[1] != 0) { if (p_arg[1] == '?' && p_arg[2] == 0) { /* we have a test */ arg_type = BTA_AG_AT_TEST; } else { /* we have a set */ arg_type = BTA_AG_AT_SET; /* skip past '=' */ p_arg++; } } else /* else it is freeform argument */ { arg_type = BTA_AG_AT_FREE; } /* if arguments match command capabilities */ if ((arg_type & p_cb->p_at_tbl[idx].arg_type) != 0) { /* if it's a set integer check max, min range */ if (arg_type == BTA_AG_AT_SET && p_cb->p_at_tbl[idx].fmt == BTA_AG_AT_INT) { int_arg = utl_str2int(p_arg); if (int_arg < (int16_t)p_cb->p_at_tbl[idx].min || int_arg > (int16_t)p_cb->p_at_tbl[idx].max) { /* arg out of range; error */ LOG_WARN("arg out of range"); (*p_cb->p_err_cback)((tBTA_AG_SCB*)p_cb->p_user, false, nullptr); } else { (*p_cb->p_cmd_cback)((tBTA_AG_SCB*)p_cb->p_user, p_cb->p_at_tbl[idx].command_id, arg_type, p_arg, p_end, int_arg); } } else { (*p_cb->p_cmd_cback)((tBTA_AG_SCB*)p_cb->p_user, p_cb->p_at_tbl[idx].command_id, arg_type, p_arg, p_end, int_arg); } } else { /* else error */ LOG_WARN("Incoming arg type 0x%x does not match cmd arg type 0x%x", arg_type, p_cb->p_at_tbl[idx].arg_type); (*p_cb->p_err_cback)((tBTA_AG_SCB*)p_cb->p_user, false, nullptr); } } else { /* else no match call error callback */ LOG_WARN("Unmatched command index %d", idx); (*p_cb->p_err_cback)((tBTA_AG_SCB*)p_cb->p_user, true, p_cb->p_cmd_buf); } } /****************************************************************************** * * Function bta_ag_at_parse * * Description Parse AT commands. This function will take the input * character string and parse it for AT commands according to * the AT command table passed in the control block. * * * Returns void * *****************************************************************************/ void bta_ag_at_parse(tBTA_AG_AT_CB* p_cb, char* p_buf, uint16_t len) { int i = 0; char* p_save; if (p_cb->p_cmd_buf == nullptr) { p_cb->p_cmd_buf = (char*)osi_malloc(p_cb->cmd_max_len); p_cb->cmd_pos = 0; } for (i = 0; i < len;) { while (p_cb->cmd_pos < p_cb->cmd_max_len - 1 && i < len) { /* Skip null characters between AT commands. */ if ((p_cb->cmd_pos == 0) && (p_buf[i] == 0)) { i++; continue; } p_cb->p_cmd_buf[p_cb->cmd_pos] = p_buf[i++]; if (p_cb->p_cmd_buf[p_cb->cmd_pos] == '\r' || p_cb->p_cmd_buf[p_cb->cmd_pos] == '\n') { p_cb->p_cmd_buf[p_cb->cmd_pos] = 0; if ((p_cb->cmd_pos > 2) && (p_cb->p_cmd_buf[0] == 'A' || p_cb->p_cmd_buf[0] == 'a') && (p_cb->p_cmd_buf[1] == 'T' || p_cb->p_cmd_buf[1] == 't')) { p_save = p_cb->p_cmd_buf; char* p_end = p_cb->p_cmd_buf + p_cb->cmd_pos; p_cb->p_cmd_buf += 2; bta_ag_process_at(p_cb, p_end); p_cb->p_cmd_buf = p_save; } p_cb->cmd_pos = 0; } else if (p_cb->p_cmd_buf[p_cb->cmd_pos] == 0x1A || p_cb->p_cmd_buf[p_cb->cmd_pos] == 0x1B) { p_cb->p_cmd_buf[++p_cb->cmd_pos] = 0; (*p_cb->p_err_cback)((tBTA_AG_SCB*)p_cb->p_user, true, p_cb->p_cmd_buf); p_cb->cmd_pos = 0; } else { ++p_cb->cmd_pos; } } if (i < len) p_cb->cmd_pos = 0; } }