summaryrefslogtreecommitdiffstats
path: root/bta/ag/bta_ag_cmd.c
diff options
context:
space:
mode:
Diffstat (limited to 'bta/ag/bta_ag_cmd.c')
-rw-r--r--bta/ag/bta_ag_cmd.c1836
1 files changed, 1836 insertions, 0 deletions
diff --git a/bta/ag/bta_ag_cmd.c b/bta/ag/bta_ag_cmd.c
new file mode 100644
index 0000000..d552eed
--- /dev/null
+++ b/bta/ag/bta_ag_cmd.c
@@ -0,0 +1,1836 @@
+/******************************************************************************
+ *
+ * Copyright (C) 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.
+ *
+ ******************************************************************************/
+
+/******************************************************************************
+ *
+ * This file contains functions for processing AT commands and results.
+ *
+ ******************************************************************************/
+
+#include "bta_api.h"
+#include "bta_sys.h"
+#include "bta_ag_api.h"
+#include "bta_ag_int.h"
+#include "bta_ag_at.h"
+#include "port_api.h"
+#include "utl.h"
+#include <stdio.h>
+#include <string.h>
+
+/*****************************************************************************
+** Constants
+*****************************************************************************/
+
+/* ring timeout */
+#define BTA_AG_RING_TOUT 10000
+
+#define BTA_AG_CMD_MAX_VAL 32767 /* Maximum value is signed 16-bit value */
+
+
+
+/* clip type constants */
+#define BTA_AG_CLIP_TYPE_MIN 128
+#define BTA_AG_CLIP_TYPE_MAX 175
+#define BTA_AG_CLIP_TYPE_DEFAULT 129
+#define BTA_AG_CLIP_TYPE_VOIP 255
+
+#if defined(BTA_AG_MULTI_RESULT_INCLUDED) && (BTA_AG_MULTI_RESULT_INCLUDED == TRUE)
+#define BTA_AG_AT_MULTI_LEN 2
+#define AT_SET_RES_CB(res_cb, c, p, i) {res_cb.code = c; res_cb.p_arg = p; res_cb.int_arg = i;}
+
+/* type for AT result code block */
+typedef struct
+{
+ UINT8 code;
+ char *p_arg;
+ INT16 int_arg;
+} tBTA_AG_RESULT_CB;
+
+/* type for multiple AT result codes block */
+typedef struct
+{
+ UINT8 num_result;
+ tBTA_AG_RESULT_CB res_cb[BTA_AG_AT_MULTI_LEN];
+} tBTA_AG_MULTI_RESULT_CB;
+#endif
+
+/* enumeration of HSP AT commands matches HSP command interpreter table */
+enum
+{
+ BTA_AG_HS_CMD_CKPD,
+ BTA_AG_HS_CMD_VGS,
+ BTA_AG_HS_CMD_VGM
+};
+
+/* enumeration of HFP AT commands matches HFP command interpreter table */
+enum
+{
+ BTA_AG_HF_CMD_A,
+ BTA_AG_HF_CMD_D,
+ BTA_AG_HF_CMD_VGS,
+ BTA_AG_HF_CMD_VGM,
+ BTA_AG_HF_CMD_CCWA,
+ BTA_AG_HF_CMD_CHLD,
+ BTA_AG_HF_CMD_CHUP,
+ BTA_AG_HF_CMD_CIND,
+ BTA_AG_HF_CMD_CLIP,
+ BTA_AG_HF_CMD_CMER,
+ BTA_AG_HF_CMD_VTS,
+ BTA_AG_HF_CMD_BINP,
+ BTA_AG_HF_CMD_BLDN,
+ BTA_AG_HF_CMD_BVRA,
+ BTA_AG_HF_CMD_BRSF,
+ BTA_AG_HF_CMD_NREC,
+ BTA_AG_HF_CMD_CNUM,
+ BTA_AG_HF_CMD_BTRH,
+ BTA_AG_HF_CMD_CLCC,
+ BTA_AG_HF_CMD_COPS,
+ BTA_AG_HF_CMD_CMEE,
+ BTA_AG_HF_CMD_BIA,
+ BTA_AG_HF_CMD_CBC,
+ BTA_AG_HF_CMD_BCC,
+ BTA_AG_HF_CMD_BCS,
+ BTA_AG_HF_CMD_BAC
+};
+
+/* AT command interpreter table for HSP */
+const tBTA_AG_AT_CMD bta_ag_hsp_cmd[] =
+{
+ {"+CKPD", BTA_AG_AT_SET, BTA_AG_AT_INT, 200, 200},
+ {"+VGS", BTA_AG_AT_SET, BTA_AG_AT_INT, 0, 15},
+ {"+VGM", BTA_AG_AT_SET, BTA_AG_AT_INT, 0, 15},
+ {"", BTA_AG_AT_NONE, BTA_AG_AT_STR, 0, 0}
+};
+
+/* AT command interpreter table for HFP */
+const tBTA_AG_AT_CMD bta_ag_hfp_cmd[] =
+{
+ {"A", BTA_AG_AT_NONE, BTA_AG_AT_STR, 0, 0},
+ {"D", (BTA_AG_AT_NONE | BTA_AG_AT_FREE), BTA_AG_AT_STR, 0, 0},
+ {"+VGS", BTA_AG_AT_SET, BTA_AG_AT_INT, 0, 15},
+ {"+VGM", BTA_AG_AT_SET, BTA_AG_AT_INT, 0, 15},
+ {"+CCWA", BTA_AG_AT_SET, BTA_AG_AT_INT, 0, 1},
+ /* Consider CHLD as str to take care of indexes for ECC */
+ {"+CHLD", (BTA_AG_AT_SET | BTA_AG_AT_TEST), BTA_AG_AT_STR, 0, 4},
+ {"+CHUP", BTA_AG_AT_NONE, BTA_AG_AT_STR, 0, 0},
+ {"+CIND", (BTA_AG_AT_READ | BTA_AG_AT_TEST), BTA_AG_AT_STR, 0, 0},
+ {"+CLIP", BTA_AG_AT_SET, BTA_AG_AT_INT, 0, 1},
+ {"+CMER", BTA_AG_AT_SET, BTA_AG_AT_STR, 0, 0},
+ {"+VTS", BTA_AG_AT_SET, BTA_AG_AT_STR, 0, 0},
+ {"+BINP", BTA_AG_AT_SET, BTA_AG_AT_INT, 1, 1},
+ {"+BLDN", BTA_AG_AT_NONE, BTA_AG_AT_STR, 0, 0},
+ {"+BVRA", BTA_AG_AT_SET, BTA_AG_AT_INT, 0, 1},
+ {"+BRSF", BTA_AG_AT_SET, BTA_AG_AT_INT, 0, BTA_AG_CMD_MAX_VAL},
+ {"+NREC", BTA_AG_AT_SET, BTA_AG_AT_INT, 0, 0},
+ {"+CNUM", BTA_AG_AT_NONE, BTA_AG_AT_STR, 0, 0},
+ {"+BTRH", (BTA_AG_AT_READ | BTA_AG_AT_SET), BTA_AG_AT_INT, 0, 2},
+ {"+CLCC", BTA_AG_AT_NONE, BTA_AG_AT_STR, 0, 0},
+ {"+COPS", (BTA_AG_AT_READ | BTA_AG_AT_SET), BTA_AG_AT_STR, 0, 0},
+ {"+CMEE", BTA_AG_AT_SET, BTA_AG_AT_INT, 0, 1},
+ {"+BIA", BTA_AG_AT_SET, BTA_AG_AT_STR, 0, 20},
+ {"+CBC", BTA_AG_AT_SET, BTA_AG_AT_INT, 0, 100},
+ {"+BCC", BTA_AG_AT_NONE, BTA_AG_AT_STR, 0, 0},
+ {"+BCS", BTA_AG_AT_SET, BTA_AG_AT_INT, 0, BTA_AG_CMD_MAX_VAL},
+ {"+BAC", BTA_AG_AT_SET, BTA_AG_AT_STR, 0, 0},
+ {"", BTA_AG_AT_NONE, BTA_AG_AT_STR, 0, 0}
+};
+
+/* AT result code table element */
+typedef struct
+{
+ const char *p_res; /* AT result string */
+ UINT8 fmt; /* whether argument is int or string */
+} tBTA_AG_RESULT;
+
+/* AT result code argument types */
+enum
+{
+ BTA_AG_RES_FMT_NONE, /* no argument */
+ BTA_AG_RES_FMT_INT, /* integer argument */
+ BTA_AG_RES_FMT_STR /* string argument */
+};
+
+/* enumeration of AT result codes, matches constant table */
+enum
+{
+ BTA_AG_RES_OK,
+ BTA_AG_RES_ERROR,
+ BTA_AG_RES_RING,
+ BTA_AG_RES_VGS,
+ BTA_AG_RES_VGM,
+ BTA_AG_RES_CCWA,
+ BTA_AG_RES_CHLD,
+ BTA_AG_RES_CIND,
+ BTA_AG_RES_CLIP,
+ BTA_AG_RES_CIEV,
+ BTA_AG_RES_BINP,
+ BTA_AG_RES_BVRA,
+ BTA_AG_RES_BRSF,
+ BTA_AG_RES_BSIR,
+ BTA_AG_RES_CNUM,
+ BTA_AG_RES_BTRH,
+ BTA_AG_RES_CLCC,
+ BTA_AG_RES_COPS,
+ BTA_AG_RES_CMEE,
+ BTA_AG_RES_BCS,
+ BTA_AG_RES_UNAT
+};
+
+#if defined(BTA_HSP_RESULT_REPLACE_COLON) && (BTA_HSP_RESULT_REPLACE_COLON == TRUE)
+#define COLON_IDX_4_VGSVGM 4
+#endif
+/* AT result code constant table (Indexed by result code) */
+const tBTA_AG_RESULT bta_ag_result_tbl[] =
+{
+ {"OK", BTA_AG_RES_FMT_NONE},
+ {"ERROR", BTA_AG_RES_FMT_NONE},
+ {"RING", BTA_AG_RES_FMT_NONE},
+ {"+VGS: ", BTA_AG_RES_FMT_INT},
+ {"+VGM: ", BTA_AG_RES_FMT_INT},
+ {"+CCWA: ", BTA_AG_RES_FMT_STR},
+ {"+CHLD: ", BTA_AG_RES_FMT_STR},
+ {"+CIND: ", BTA_AG_RES_FMT_STR},
+ {"+CLIP: ", BTA_AG_RES_FMT_STR},
+ {"+CIEV: ", BTA_AG_RES_FMT_STR},
+ {"+BINP: ", BTA_AG_RES_FMT_STR},
+ {"+BVRA: ", BTA_AG_RES_FMT_INT},
+ {"+BRSF: ", BTA_AG_RES_FMT_INT},
+ {"+BSIR: ", BTA_AG_RES_FMT_INT},
+ {"+CNUM: ", BTA_AG_RES_FMT_STR},
+ {"+BTRH: ", BTA_AG_RES_FMT_INT},
+ {"+CLCC: ", BTA_AG_RES_FMT_STR},
+ {"+COPS: ", BTA_AG_RES_FMT_STR},
+ {"+CME ERROR: ", BTA_AG_RES_FMT_INT},
+ {"+BCS: ", BTA_AG_RES_FMT_INT},
+ {"", BTA_AG_RES_FMT_STR}
+};
+
+const tBTA_AG_AT_CMD *bta_ag_at_tbl[BTA_AG_NUM_IDX] =
+{
+ bta_ag_hsp_cmd,
+ bta_ag_hfp_cmd
+};
+
+/* callback event lookup table for HSP */
+const tBTA_AG_EVT bta_ag_hsp_cb_evt[] =
+{
+ BTA_AG_AT_CKPD_EVT, /* BTA_AG_HS_CMD_CKPD */
+ BTA_AG_SPK_EVT, /* BTA_AG_HS_CMD_VGS */
+ BTA_AG_MIC_EVT /* BTA_AG_HS_CMD_VGM */
+};
+
+/* callback event lookup table for HFP (Indexed by command) */
+const tBTA_AG_EVT bta_ag_hfp_cb_evt[] =
+{
+ BTA_AG_AT_A_EVT, /* BTA_AG_HF_CMD_A */
+ BTA_AG_AT_D_EVT, /* BTA_AG_HF_CMD_D */
+ BTA_AG_SPK_EVT, /* BTA_AG_HF_CMD_VGS */
+ BTA_AG_MIC_EVT, /* BTA_AG_HF_CMD_VGM */
+ 0, /* BTA_AG_HF_CMD_CCWA */
+ BTA_AG_AT_CHLD_EVT, /* BTA_AG_HF_CMD_CHLD */
+ BTA_AG_AT_CHUP_EVT, /* BTA_AG_HF_CMD_CHUP */
+ BTA_AG_AT_CIND_EVT, /* BTA_AG_HF_CMD_CIND */
+ 0, /* BTA_AG_HF_CMD_CLIP */
+ 0, /* BTA_AG_HF_CMD_CMER */
+ BTA_AG_AT_VTS_EVT, /* BTA_AG_HF_CMD_VTS */
+ BTA_AG_AT_BINP_EVT, /* BTA_AG_HF_CMD_BINP */
+ BTA_AG_AT_BLDN_EVT, /* BTA_AG_HF_CMD_BLDN */
+ BTA_AG_AT_BVRA_EVT, /* BTA_AG_HF_CMD_BVRA */
+ 0, /* BTA_AG_HF_CMD_BRSF */
+ BTA_AG_AT_NREC_EVT, /* BTA_AG_HF_CMD_NREC */
+ BTA_AG_AT_CNUM_EVT, /* BTA_AG_HF_CMD_CNUM */
+ BTA_AG_AT_BTRH_EVT, /* BTA_AG_HF_CMD_BTRH */
+ BTA_AG_AT_CLCC_EVT, /* BTA_AG_HF_CMD_CLCC */
+ BTA_AG_AT_COPS_EVT, /* BTA_AG_HF_CMD_COPS */
+ 0, /* BTA_AG_HF_CMD_CMEE */
+ 0, /* BTA_AG_HF_CMD_BIA */
+ BTA_AG_AT_CBC_EVT, /* BTA_AG_HF_CMD_CBC */
+ 0, /* BTA_AG_HF_CMD_BCC */
+ BTA_AG_AT_BCS_EVT, /* BTA_AG_HF_CMD_BCS */
+ BTA_AG_AT_BAC_EVT /* BTA_AG_HF_CMD_BAC */
+};
+
+/* translation of API result code values to internal values */
+const UINT8 bta_ag_trans_result[] =
+{
+ BTA_AG_RES_VGS, /* BTA_AG_SPK_RES */
+ BTA_AG_RES_VGM, /* BTA_AG_MIC_RES */
+ BTA_AG_RES_BSIR, /* BTA_AG_INBAND_RING_RES */
+ BTA_AG_RES_CIND, /* BTA_AG_CIND_RES */
+ BTA_AG_RES_BINP, /* BTA_AG_BINP_RES */
+ BTA_AG_RES_CIEV, /* BTA_AG_IND_RES */
+ BTA_AG_RES_BVRA, /* BTA_AG_BVRA_RES */
+ BTA_AG_RES_CNUM, /* BTA_AG_CNUM_RES */
+ BTA_AG_RES_BTRH, /* BTA_AG_BTRH_RES */
+ BTA_AG_RES_CLCC, /* BTA_AG_CLCC_RES */
+ BTA_AG_RES_COPS, /* BTA_AG_COPS_RES */
+ 0, /* BTA_AG_IN_CALL_RES */
+ 0, /* BTA_AG_IN_CALL_CONN_RES */
+ BTA_AG_RES_CCWA, /* BTA_AG_CALL_WAIT_RES */
+ 0, /* BTA_AG_OUT_CALL_ORIG_RES */
+ 0, /* BTA_AG_OUT_CALL_ALERT_RES */
+ 0, /* BTA_AG_OUT_CALL_CONN_RES */
+ 0, /* BTA_AG_CALL_CANCEL_RES */
+ 0, /* BTA_AG_END_CALL_RES */
+ 0, /* BTA_AG_IN_CALL_HELD_RES */
+ BTA_AG_RES_UNAT /* BTA_AG_UNAT_RES */
+};
+
+/* callsetup indicator value lookup table */
+const UINT8 bta_ag_callsetup_ind_tbl[] =
+{
+ 0, /* BTA_AG_SPK_RES */
+ 0, /* BTA_AG_MIC_RES */
+ 0, /* BTA_AG_INBAND_RING_RES */
+ 0, /* BTA_AG_CIND_RES */
+ 0, /* BTA_AG_BINP_RES */
+ 0, /* BTA_AG_IND_RES */
+ 0, /* BTA_AG_BVRA_RES */
+ 0, /* BTA_AG_CNUM_RES */
+ 0, /* BTA_AG_BTRH_RES */
+ 0, /* BTA_AG_CLCC_RES */
+ 0, /* BTA_AG_COPS_RES */
+ BTA_AG_CALLSETUP_INCOMING, /* BTA_AG_IN_CALL_RES */
+ BTA_AG_CALLSETUP_NONE, /* BTA_AG_IN_CALL_CONN_RES */
+ BTA_AG_CALLSETUP_INCOMING, /* BTA_AG_CALL_WAIT_RES */
+ BTA_AG_CALLSETUP_OUTGOING, /* BTA_AG_OUT_CALL_ORIG_RES */
+ BTA_AG_CALLSETUP_ALERTING, /* BTA_AG_OUT_CALL_ALERT_RES */
+ BTA_AG_CALLSETUP_NONE, /* BTA_AG_OUT_CALL_CONN_RES */
+ BTA_AG_CALLSETUP_NONE, /* BTA_AG_CALL_CANCEL_RES */
+ BTA_AG_CALLSETUP_NONE, /* BTA_AG_END_CALL_RES */
+ BTA_AG_CALLSETUP_NONE /* BTA_AG_IN_CALL_HELD_RES */
+};
+
+/*******************************************************************************
+**
+** Function bta_ag_send_result
+**
+** Description Send an AT result code.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+static void bta_ag_send_result(tBTA_AG_SCB *p_scb, UINT8 code, char *p_arg,
+ INT16 int_arg)
+{
+ char buf[BTA_AG_AT_MAX_LEN + 16];
+ char *p = buf;
+ UINT16 len;
+
+#if defined(BTA_AG_RESULT_DEBUG) && (BTA_AG_RESULT_DEBUG == TRUE)
+ memset(buf, NULL, sizeof(buf));
+#endif
+ /* init with \r\n */
+ *p++ = '\r';
+ *p++ = '\n';
+
+ /* copy result code string */
+ BCM_STRCPY_S(p, sizeof(buf), bta_ag_result_tbl[code].p_res);
+#if defined(BTA_HSP_RESULT_REPLACE_COLON) && (BTA_HSP_RESULT_REPLACE_COLON == TRUE)
+ if(p_scb->conn_service == BTA_AG_HSP)
+ {
+ /* If HSP then ":"symbol should be changed as "=" for HSP compatibility */
+ switch(code)
+ {
+ case BTA_AG_RES_VGS:
+ case BTA_AG_RES_VGM:
+ if(*(p+COLON_IDX_4_VGSVGM) == ':')
+ {
+ #if defined(BTA_AG_RESULT_DEBUG) && (BTA_AG_RESULT_DEBUG == TRUE)
+ APPL_TRACE_DEBUG0("[HSP] ':'symbol is changed as '=' for HSP compatibility");
+ #endif
+ *(p+COLON_IDX_4_VGSVGM) = '=';
+ }
+ break;
+ }
+ }
+#endif
+ p += strlen(bta_ag_result_tbl[code].p_res);
+
+ /* copy argument if any */
+ if (bta_ag_result_tbl[code].fmt == BTA_AG_RES_FMT_INT)
+ {
+ p += utl_itoa((UINT16) int_arg, p);
+ }
+ else if (bta_ag_result_tbl[code].fmt == BTA_AG_RES_FMT_STR)
+ {
+ BCM_STRCPY_S(p, sizeof(buf), p_arg);
+ p += strlen(p_arg);
+ }
+
+ /* finish with \r\n */
+ *p++ = '\r';
+ *p++ = '\n';
+
+#if defined(BTA_AG_RESULT_DEBUG) && (BTA_AG_RESULT_DEBUG == TRUE)
+ APPL_TRACE_DEBUG1("bta_ag_send_result: %s", buf);
+#endif
+
+ /* send to RFCOMM */
+ PORT_WriteData(p_scb->conn_handle, buf, (UINT16) (p - buf), &len);
+}
+
+#if defined(BTA_AG_MULTI_RESULT_INCLUDED) && (BTA_AG_MULTI_RESULT_INCLUDED == TRUE)
+/*******************************************************************************
+**
+** Function bta_ag_send_multi_result
+**
+** Description Send multiple AT result codes.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+static void bta_ag_send_multi_result(tBTA_AG_SCB *p_scb, tBTA_AG_MULTI_RESULT_CB *m_res_cb)
+{
+ char buf[BTA_AG_AT_MAX_LEN * BTA_AG_AT_MULTI_LEN + 16];
+ char *p = buf;
+ UINT16 len;
+ UINT8 res_idx = 0;
+
+ if((!m_res_cb) || (m_res_cb->num_result == 0) || (m_res_cb->num_result > BTA_AG_AT_MULTI_LEN))
+ {
+ APPL_TRACE_DEBUG0("m_res_cb is NULL or num_result is out of range.");
+ return;
+ }
+
+#if defined(BTA_AG_RESULT_DEBUG) && (BTA_AG_RESULT_DEBUG == TRUE)
+ memset(buf, NULL, sizeof(buf));
+#endif
+
+ while(res_idx < m_res_cb->num_result)
+ {
+ /* init with \r\n */
+ *p++ = '\r';
+ *p++ = '\n';
+
+ /* copy result code string */
+ BCM_STRCPY_S(p, sizeof(buf), bta_ag_result_tbl[m_res_cb->res_cb[res_idx].code].p_res);
+ p += strlen(bta_ag_result_tbl[m_res_cb->res_cb[res_idx].code].p_res);
+
+ /* copy argument if any */
+ if (bta_ag_result_tbl[m_res_cb->res_cb[res_idx].code].fmt == BTA_AG_RES_FMT_INT)
+ {
+ p += utl_itoa((UINT16) m_res_cb->res_cb[res_idx].int_arg, p);
+ }
+ else if (bta_ag_result_tbl[m_res_cb->res_cb[res_idx].code].fmt == BTA_AG_RES_FMT_STR)
+ {
+ BCM_STRCPY_S(p, sizeof(buf), m_res_cb->res_cb[res_idx].p_arg);
+ p += strlen(m_res_cb->res_cb[res_idx].p_arg);
+ }
+
+ /* finish with \r\n */
+ *p++ = '\r';
+ *p++ = '\n';
+
+ res_idx++;
+ }
+
+#if defined(BTA_AG_RESULT_DEBUG) && (BTA_AG_RESULT_DEBUG == TRUE)
+ APPL_TRACE_DEBUG1("send_result: %s", buf);
+#endif
+
+ /* send to RFCOMM */
+ PORT_WriteData(p_scb->conn_handle, buf, (UINT16) (p - buf), &len);
+}
+#endif
+
+/*******************************************************************************
+**
+** Function bta_ag_send_ok
+**
+** Description Send an OK result code.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+static void bta_ag_send_ok(tBTA_AG_SCB *p_scb)
+{
+ bta_ag_send_result(p_scb, BTA_AG_RES_OK, NULL, 0);
+}
+
+/*******************************************************************************
+**
+** Function bta_ag_send_error
+**
+** Description Send an ERROR result code.
+** errcode - used to send verbose errocode
+**
+**
+** Returns void
+**
+*******************************************************************************/
+static void bta_ag_send_error(tBTA_AG_SCB *p_scb, INT16 errcode)
+{
+ /* If HFP and extended audio gateway error codes are enabled */
+ if (p_scb->conn_service == BTA_AG_HFP && p_scb->cmee_enabled)
+ bta_ag_send_result(p_scb, BTA_AG_RES_CMEE, NULL, errcode);
+ else
+ bta_ag_send_result(p_scb, BTA_AG_RES_ERROR, NULL, 0);
+}
+
+/*******************************************************************************
+**
+** Function bta_ag_send_ind
+**
+** Description Send an indicator CIEV result code.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+static void bta_ag_send_ind(tBTA_AG_SCB *p_scb, UINT16 id, UINT16 value, BOOLEAN on_demand)
+{
+ char str[12];
+ char *p = str;
+
+ /* If the indicator is masked out, just return */
+ /* Mandatory indicators can not be masked out. */
+ if ((p_scb->bia_masked_out & ((UINT32)1 << id)) &&
+ ((id != BTA_AG_IND_CALL) && (id != BTA_AG_IND_CALLSETUP) && (id != BTA_AG_IND_CALLHELD)))
+ return;
+
+ /* Ensure we do not send duplicate indicators if not requested by app */
+ /* If it was requested by app, transmit CIEV even if it is duplicate. */
+ if (id == BTA_AG_IND_CALL)
+ {
+ if ((value == p_scb->call_ind) && (on_demand == FALSE))
+ return;
+
+ p_scb->call_ind = (UINT8)value;
+ }
+
+ if ((id == BTA_AG_IND_CALLSETUP) && (on_demand == FALSE))
+ {
+ if (value == p_scb->callsetup_ind)
+ return;
+
+ p_scb->callsetup_ind = (UINT8)value;
+ }
+
+ if ((id == BTA_AG_IND_SERVICE) && (on_demand == FALSE))
+ {
+ if (value == p_scb->service_ind)
+ return;
+
+ p_scb->service_ind = (UINT8)value;
+ }
+ if ((id == BTA_AG_IND_SIGNAL) && (on_demand == FALSE))
+ {
+ if (value == p_scb->signal_ind)
+ return;
+
+ p_scb->signal_ind = (UINT8)value;
+ }
+ if ((id == BTA_AG_IND_ROAM) && (on_demand == FALSE))
+ {
+ if (value == p_scb->roam_ind)
+ return;
+
+ p_scb->roam_ind = (UINT8)value;
+ }
+ if ((id == BTA_AG_IND_BATTCHG) && (on_demand == FALSE))
+ {
+ if (value == p_scb->battchg_ind)
+ return;
+
+ p_scb->battchg_ind = (UINT8)value;
+ }
+
+ if ((id == BTA_AG_IND_CALLHELD) && (on_demand == FALSE))
+ {
+ /* call swap could result in sending callheld=1 multiple times */
+ if ((value != 1) && (value == p_scb->callheld_ind))
+ return;
+
+ p_scb->callheld_ind = (UINT8)value;
+ }
+
+ if (p_scb->cmer_enabled)
+ {
+ p += utl_itoa(id, p);
+ *p++ = ',';
+ utl_itoa(value, p);
+ bta_ag_send_result(p_scb, BTA_AG_RES_CIEV, str, 0);
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_ag_parse_cmer
+**
+** Description Parse AT+CMER parameter string.
+**
+**
+** Returns TRUE if parsed ok, FALSE otherwise.
+**
+*******************************************************************************/
+static BOOLEAN bta_ag_parse_cmer(char *p_s, BOOLEAN *p_enabled)
+{
+ INT16 n[4] = {-1, -1, -1, -1};
+ int i;
+ char *p;
+
+ for (i = 0; i < 4; i++)
+ {
+ /* skip to comma delimiter */
+ for (p = p_s; *p != ',' && *p != 0; p++);
+
+ /* get integer value */
+ *p = 0;
+ n[i] = utl_str2int(p_s);
+ p_s = p + 1;
+ if (p_s == 0)
+ {
+ break;
+ }
+ }
+
+ /* process values */
+ if (n[0] < 0 || n[3] < 0)
+ {
+ return FALSE;
+ }
+
+ if ((n[0] == 3) && ((n[3] == 1) || (n[3] == 0)))
+ {
+ *p_enabled = (BOOLEAN) n[3];
+ }
+
+ return TRUE;
+}
+
+/*******************************************************************************
+**
+** Function bta_ag_parse_chld
+**
+** Description Parse AT+CHLD parameter string.
+**
+**
+** Returns Returns idx (1-7), or 0 if ECC not enabled or idx doesn't exist
+**
+*******************************************************************************/
+static UINT8 bta_ag_parse_chld(tBTA_AG_SCB *p_scb, char *p_s)
+{
+ UINT8 retval = 0;
+ INT16 idx = -1;
+
+ if (p_s[1] != 0)
+ {
+ /* p_idxstr++; point to beginning of call number */
+ idx = utl_str2int(&p_s[1]);
+ if (idx != -1 && idx < 255)
+ retval = (UINT8)idx;
+ }
+
+ return (retval);
+}
+
+#if (BTM_WBS_INCLUDED == TRUE )
+/*******************************************************************************
+**
+** Function bta_ag_parse_bac
+**
+** Description Parse AT+BAC parameter string.
+**
+** Returns Returns bitmap of supported codecs.
+**
+*******************************************************************************/
+static tBTA_AG_PEER_CODEC bta_ag_parse_bac(tBTA_AG_SCB *p_scb, char *p_s)
+{
+ tBTA_AG_PEER_CODEC retval = BTA_AG_CODEC_NONE;
+ UINT16 uuid_codec;
+ BOOLEAN cont = FALSE; /* Continue processing */
+ char *p;
+
+ while(p_s)
+ {
+ /* skip to comma delimiter */
+ for(p = p_s; *p != ',' && *p != 0; p++);
+
+ /* get integre value */
+ if (*p != 0)
+ {
+ *p = 0;
+ cont = TRUE;
+ }
+ else
+ cont = FALSE;
+
+ uuid_codec = utl_str2int(p_s);
+ switch(uuid_codec)
+ {
+ case UUID_CODEC_CVSD: retval |= BTA_AG_CODEC_CVSD; break;
+ case UUID_CODEC_MSBC: retval |= BTA_AG_CODEC_MSBC; break;
+ default:
+ APPL_TRACE_ERROR1("Unknown Codec UUID(%d) received", uuid_codec);
+ return BTA_AG_CODEC_NONE;
+ }
+
+ if (cont)
+ p_s = p + 1;
+ else
+ break;
+ }
+
+ return (retval);
+}
+#endif
+
+/*******************************************************************************
+**
+** Function bta_ag_process_unat_res
+**
+** Description Process the unat response data and remove extra carriage return
+** and line feed
+**
+**
+** Returns void
+**
+*******************************************************************************/
+
+static void bta_ag_process_unat_res(char *unat_result)
+{
+ UINT8 str_leng;
+ UINT8 i = 0;
+ UINT8 j = 0;
+ UINT8 pairs_of_nl_cr;
+ char trim_data[BTA_AG_AT_MAX_LEN];
+
+
+
+ str_leng = strlen(unat_result);
+
+ /* If no extra CR and LF, just return */
+ if(str_leng < 4)
+ return;
+
+ /* Remove the carriage return and left feed */
+ while(unat_result[0] =='\r' && unat_result[1] =='\n'
+ && unat_result[str_leng-2] =='\r' && unat_result[str_leng-1] =='\n')
+ {
+ pairs_of_nl_cr = 1;
+ for (i=0;i<(str_leng-4*pairs_of_nl_cr);i++)
+ {
+ trim_data[j++] = unat_result[i+pairs_of_nl_cr*2];
+ }
+ /* Add EOF */
+ trim_data[j] = '\0';
+ str_leng = str_leng - 4;
+ BCM_STRNCPY_S(unat_result, BTA_AG_AT_MAX_LEN+1, trim_data,str_leng+1);
+ i=0;
+ j=0;
+
+ if(str_leng <4)
+ return;
+
+
+ }
+ return;
+}
+
+
+/*******************************************************************************
+**
+** Function bta_ag_inband_enabled
+**
+** Description Determine whether in-band ring can be used.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+BOOLEAN bta_ag_inband_enabled(tBTA_AG_SCB *p_scb)
+{
+ /* if feature is enabled and no other scbs connected */
+ if (p_scb->inband_enabled && !bta_ag_other_scb_open(p_scb))
+ {
+ return TRUE;
+ }
+ else
+ {
+ return FALSE;
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_ag_send_call_inds
+**
+** Description Send call and callsetup indicators.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_ag_send_call_inds(tBTA_AG_SCB *p_scb, tBTA_AG_RES result)
+{
+ UINT8 call = p_scb->call_ind;
+ UINT8 callsetup;
+
+ /* set new call and callsetup values based on BTA_AgResult */
+ callsetup = bta_ag_callsetup_ind_tbl[result];
+
+ if (result == BTA_AG_END_CALL_RES)
+ {
+ call = BTA_AG_CALL_INACTIVE;
+ }
+ else if (result == BTA_AG_IN_CALL_CONN_RES || result == BTA_AG_OUT_CALL_CONN_RES
+ || result == BTA_AG_IN_CALL_HELD_RES)
+ {
+ call = BTA_AG_CALL_ACTIVE;
+ }
+ else
+ {
+ call = p_scb->call_ind;
+ }
+
+ /* Send indicator function tracks if the values have actually changed */
+ bta_ag_send_ind(p_scb, BTA_AG_IND_CALL, call, FALSE);
+ bta_ag_send_ind(p_scb, BTA_AG_IND_CALLSETUP, callsetup, FALSE);
+}
+
+/*******************************************************************************
+**
+** Function bta_ag_at_hsp_cback
+**
+** Description AT command processing callback for HSP.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_ag_at_hsp_cback(tBTA_AG_SCB *p_scb, UINT16 cmd, UINT8 arg_type,
+ char *p_arg, INT16 int_arg)
+{
+ tBTA_AG_VAL val;
+
+ APPL_TRACE_DEBUG4("AT cmd:%d arg_type:%d arg:%d arg:%s", cmd, arg_type,
+ int_arg, p_arg);
+
+ /* send OK */
+ bta_ag_send_ok(p_scb);
+
+ val.hdr.handle = bta_ag_scb_to_idx(p_scb);
+ val.hdr.app_id = p_scb->app_id;
+ val.num = (UINT16) int_arg;
+ BCM_STRNCPY_S(val.str, sizeof(val.str), p_arg, BTA_AG_AT_MAX_LEN);
+ val.str[BTA_AG_AT_MAX_LEN] = 0;
+
+ /* call callback with event */
+ (*bta_ag_cb.p_cback)(bta_ag_hsp_cb_evt[cmd], (tBTA_AG *) &val);
+}
+
+/*******************************************************************************
+**
+** Function bta_ag_at_hfp_cback
+**
+** Description AT command processing callback for HFP.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_ag_at_hfp_cback(tBTA_AG_SCB *p_scb, UINT16 cmd, UINT8 arg_type,
+ char *p_arg, INT16 int_arg)
+{
+ tBTA_AG_VAL val;
+ tBTA_AG_EVT event;
+ tBTA_AG_SCB *ag_scb;
+ UINT32 i, ind_id;
+ UINT32 bia_masked_out;
+#if (BTM_WBS_INCLUDED == TRUE )
+ tBTA_AG_PEER_CODEC codec_type, codec_sent;
+#endif
+
+ APPL_TRACE_DEBUG4("HFP AT cmd:%d arg_type:%d arg:%d arg:%s", cmd, arg_type,
+ int_arg, p_arg);
+
+ val.hdr.handle = bta_ag_scb_to_idx(p_scb);
+ val.hdr.app_id = p_scb->app_id;
+ val.num = int_arg;
+ BCM_STRNCPY_S(val.str, sizeof(val.str), p_arg, BTA_AG_AT_MAX_LEN);
+ val.str[BTA_AG_AT_MAX_LEN] = 0;
+
+ event = bta_ag_hfp_cb_evt[cmd];
+
+ switch (cmd)
+ {
+ case BTA_AG_HF_CMD_A:
+ case BTA_AG_HF_CMD_VGS:
+ case BTA_AG_HF_CMD_VGM:
+ case BTA_AG_HF_CMD_CHUP:
+ case BTA_AG_HF_CMD_CBC:
+ /* send OK */
+ bta_ag_send_ok(p_scb);
+ break;
+
+ case BTA_AG_HF_CMD_BLDN:
+ /* Do not send OK, App will send error or OK depending on
+ ** last dial number enabled or not */
+ break;
+
+ case BTA_AG_HF_CMD_D:
+ /* Do not send OK for Dial cmds
+ ** Let application decide whether to send OK or ERROR*/
+
+ /* if mem dial cmd, make sure string contains only digits */
+ if(p_arg[0] == '>')
+ {
+ if(!utl_isintstr(p_arg+1))
+ {
+ event = 0;
+ bta_ag_send_error(p_scb, BTA_AG_ERR_INV_CHAR_IN_DSTR);
+ }
+ }
+ else if (p_arg[0] == 'V') /* ATDV : Dial VoIP Call */
+ {
+ /* We do not check string. Code will be added later if needed. */
+ if(!((p_scb->peer_features & BTA_AG_PEER_FEAT_VOIP) && (p_scb->features & BTA_AG_FEAT_VOIP)))
+ {
+ event = 0;
+ bta_ag_send_error(p_scb, BTA_AG_ERR_OP_NOT_SUPPORTED);
+ }
+ }
+ /* If dial cmd, make sure string contains only dial digits
+ ** Dial digits are 0-9, A-C, *, #, + */
+ else
+ {
+ if(!utl_isdialstr(p_arg))
+ {
+ event = 0;
+ bta_ag_send_error(p_scb, BTA_AG_ERR_INV_CHAR_IN_DSTR);
+ }
+ }
+ break;
+
+ case BTA_AG_HF_CMD_CCWA:
+ /* store setting */
+ p_scb->ccwa_enabled = (BOOLEAN) int_arg;
+
+ /* send OK */
+ bta_ag_send_ok(p_scb);
+ break;
+
+ case BTA_AG_HF_CMD_CHLD:
+ if (arg_type == BTA_AG_AT_TEST)
+ {
+ /* don't call callback */
+ event = 0;
+
+ /* send CHLD string */
+ /* Form string based on supported 1.5 feature */
+ if ((p_scb->peer_version >= HFP_VERSION_1_5) &&
+ (p_scb->features & BTA_AG_FEAT_ECC) &&
+ (p_scb->peer_features & BTA_AG_PEER_FEAT_ECC))
+ bta_ag_send_result(p_scb, BTA_AG_RES_CHLD, p_bta_ag_cfg->chld_val_ecc, 0);
+ else
+ bta_ag_send_result(p_scb, BTA_AG_RES_CHLD, p_bta_ag_cfg->chld_val, 0);
+
+ /* send OK */
+ bta_ag_send_ok(p_scb);
+
+ /* if service level conn. not already open, now it's open */
+ bta_ag_svc_conn_open(p_scb, NULL);
+
+ }
+ else
+ {
+ val.idx = bta_ag_parse_chld(p_scb, val.str);
+
+ if(val.idx && !((p_scb->features & BTA_AG_FEAT_ECC) && (p_scb->peer_features & BTA_AG_PEER_FEAT_ECC)))
+ {
+ /* we do not support ECC, but HF is sending us a CHLD with call index*/
+ event = 0;
+ bta_ag_send_error(p_scb, BTA_AG_ERR_OP_NOT_SUPPORTED);
+
+ }
+ else
+ {
+
+ /* If it is swap between calls, set call held indicator to 3(out of valid 0-2)
+ ** Application will set it back to 1
+ ** callheld indicator will be sent across to the peer. */
+ if(val.str[0] == '2')
+ {
+ for (i = 0, ag_scb = &bta_ag_cb.scb[0]; i < BTA_AG_NUM_SCB; i++, ag_scb++)
+ {
+ if (ag_scb->in_use)
+ {
+ if((ag_scb->call_ind == BTA_AG_CALL_ACTIVE)
+ && (ag_scb->callsetup_ind == BTA_AG_CALLSETUP_NONE))
+ ag_scb->callheld_ind = BTA_AG_CALLHELD_NOACTIVE + 1;
+ }
+ }
+ }
+ }
+
+ /* Do not send OK. Let app decide after parsing the val str */
+ /* bta_ag_send_ok(p_scb); */
+ }
+ break;
+
+ case BTA_AG_HF_CMD_CIND:
+ if (arg_type == BTA_AG_AT_TEST)
+ {
+ /* don't call callback */
+ event = 0;
+
+ /* send CIND string, send OK */
+ bta_ag_send_result(p_scb, BTA_AG_RES_CIND, p_bta_ag_cfg->cind_info, 0);
+ bta_ag_send_ok(p_scb);
+ }
+ break;
+
+ case BTA_AG_HF_CMD_CLIP:
+ /* store setting, send OK */
+ p_scb->clip_enabled = (BOOLEAN) int_arg;
+ bta_ag_send_ok(p_scb);
+ break;
+
+ case BTA_AG_HF_CMD_CMER:
+ /* if parsed ok store setting, send OK */
+ if (bta_ag_parse_cmer(p_arg, &p_scb->cmer_enabled))
+ {
+ bta_ag_send_ok(p_scb);
+
+ /* if service level conn. not already open and our features and
+ ** peer features do not have 3-way, service level conn. now open
+ */
+ if (!p_scb->svc_conn &&
+ !((p_scb->features & BTA_AG_FEAT_3WAY) && (p_scb->peer_features & BTA_AG_PEER_FEAT_3WAY)))
+ {
+ bta_ag_svc_conn_open(p_scb, NULL);
+ }
+ }
+ else
+ {
+ bta_ag_send_error(p_scb, BTA_AG_ERR_INV_CHAR_IN_TSTR);
+ }
+ break;
+
+ case BTA_AG_HF_CMD_VTS:
+ /* check argument */
+ if (strlen(p_arg) == 1)
+ {
+ bta_ag_send_ok(p_scb);
+ }
+ else
+ {
+ event = 0;
+ bta_ag_send_error(p_scb, BTA_AG_ERR_INV_CHAR_IN_TSTR);
+ }
+ break;
+
+ case BTA_AG_HF_CMD_BINP:
+ /* if feature not set don't call callback, send ERROR */
+ if (!(p_scb->features & BTA_AG_FEAT_VTAG))
+ {
+ event = 0;
+ bta_ag_send_error(p_scb, BTA_AG_ERR_OP_NOT_SUPPORTED);
+ }
+ break;
+
+ case BTA_AG_HF_CMD_BVRA:
+ /* if feature not supported don't call callback, send ERROR. App will send OK */
+ if (!(p_scb->features & BTA_AG_FEAT_VREC))
+ {
+ event = 0;
+ bta_ag_send_error(p_scb, BTA_AG_ERR_OP_NOT_SUPPORTED);
+ }
+ break;
+
+ case BTA_AG_HF_CMD_BRSF:
+ /* store peer features */
+ p_scb->peer_features = (UINT16) int_arg;
+
+ /* send BRSF, send OK */
+ bta_ag_send_result(p_scb, BTA_AG_RES_BRSF, NULL,
+ (INT16) (p_scb->features & BTA_AG_BSRF_FEAT_SPEC));
+ bta_ag_send_ok(p_scb);
+ break;
+
+ case BTA_AG_HF_CMD_NREC:
+ /* if feature send OK, else don't call callback, send ERROR */
+ if (p_scb->features & BTA_AG_FEAT_ECNR)
+ {
+ bta_ag_send_ok(p_scb);
+ }
+ else
+ {
+ event = 0;
+ bta_ag_send_error(p_scb, BTA_AG_ERR_OP_NOT_SUPPORTED);
+ }
+ break;
+
+ case BTA_AG_HF_CMD_BTRH:
+ /* if feature send BTRH, send OK:, else don't call callback, send ERROR */
+ if (p_scb->features & BTA_AG_FEAT_BTRH)
+ {
+ /* If set command; send response and notify app */
+ if (arg_type == BTA_AG_AT_SET)
+ {
+ for (i = 0, ag_scb = &bta_ag_cb.scb[0]; i < BTA_AG_NUM_SCB; i++, ag_scb++)
+ {
+ if (ag_scb->in_use)
+ {
+ bta_ag_send_result(ag_scb, BTA_AG_RES_BTRH, NULL, int_arg);
+ }
+ }
+ bta_ag_send_ok(p_scb);
+ }
+ else /* Read Command */
+ {
+ val.num = BTA_AG_BTRH_READ;
+ }
+ }
+ else
+ {
+ event = 0;
+ bta_ag_send_error(p_scb, BTA_AG_ERR_OP_NOT_SUPPORTED);
+ }
+ break;
+
+ case BTA_AG_HF_CMD_COPS:
+ if (arg_type == BTA_AG_AT_SET)
+ {
+ /* don't call callback */
+ event = 0;
+
+ /* send OK */
+ bta_ag_send_ok(p_scb);
+ }
+ break;
+
+ case BTA_AG_HF_CMD_CMEE:
+ if (p_scb->features & BTA_AG_FEAT_EXTERR)
+ {
+ /* store setting */
+ p_scb->cmee_enabled = (BOOLEAN) int_arg;
+
+ /* send OK */
+ bta_ag_send_ok(p_scb);
+ }
+ else
+ {
+ bta_ag_send_error(p_scb, BTA_AG_ERR_OP_NOT_SUPPORTED);
+ }
+ /* don't call callback */
+ event = 0;
+ break;
+
+ case BTA_AG_HF_CMD_BIA:
+ /* don't call callback */
+ event = 0;
+
+ bia_masked_out = p_scb->bia_masked_out;
+
+ /* Parse the indicator mask */
+ for (i = 0, ind_id = 1; (val.str[i] != 0) && (ind_id <= 20); i++, ind_id++)
+ {
+ if (val.str[i] == ',')
+ continue;
+
+ if (val.str[i] == '0')
+ bia_masked_out |= ((UINT32)1 << ind_id);
+ else if (val.str[i] == '1')
+ bia_masked_out &= ~((UINT32)1 << ind_id);
+ else
+ break;
+
+ i++;
+ if ( (val.str[i] != 0) && (val.str[i] != ',') )
+ break;
+ }
+ if (val.str[i] == 0)
+ {
+ p_scb->bia_masked_out = bia_masked_out;
+ bta_ag_send_ok (p_scb);
+ }
+ else
+ bta_ag_send_error (p_scb, BTA_AG_ERR_INVALID_INDEX);
+ break;
+
+ case BTA_AG_HF_CMD_CNUM:
+ break;
+ case BTA_AG_HF_CMD_CLCC:
+ if(!(p_scb->features & BTA_AG_FEAT_ECS))
+ {
+ event = 0;
+ bta_ag_send_error(p_scb, BTA_AG_ERR_OP_NOT_SUPPORTED);
+ }
+ break;
+
+#if (BTM_WBS_INCLUDED == TRUE )
+ case BTA_AG_HF_CMD_BAC:
+ bta_ag_send_ok(p_scb);
+
+ /* store available codecs from the peer */
+ if((p_scb->peer_features & BTA_AG_PEER_FEAT_CODEC) && (p_scb->features & BTA_AG_FEAT_CODEC))
+ {
+ p_scb->peer_codecs = bta_ag_parse_bac(p_scb, p_arg);
+ p_scb->codec_updated = TRUE;
+
+ if (p_scb->peer_codecs & BTA_AG_CODEC_MSBC)
+ {
+ p_scb->sco_codec = UUID_CODEC_MSBC;
+ APPL_TRACE_DEBUG0("Received AT+BAC, updating sco codec to MSBC");
+ }
+ else
+ {
+ p_scb->sco_codec = UUID_CODEC_CVSD;
+ APPL_TRACE_DEBUG0("Received AT+BAC, updating sco codec to CVSD");
+ }
+
+ /* Received BAC while in codec negotiation. */
+ if ((bta_ag_cb.sco.state == BTA_AG_SCO_CODEC_ST) && (bta_ag_cb.sco.p_curr_scb == p_scb))
+ {
+ bta_ag_codec_negotiate (p_scb);
+ }
+ }
+ else
+ {
+ p_scb->peer_codecs = BTA_AG_CODEC_NONE;
+ APPL_TRACE_ERROR0("Unexpected CMD:AT+BAC, Codec Negotiation is not supported");
+ }
+ break;
+
+ case BTA_AG_HF_CMD_BCS:
+ /* stop cn timer */
+ bta_sys_stop_timer(&p_scb->cn_timer);
+
+ switch(int_arg)
+ {
+ case UUID_CODEC_CVSD: codec_type = BTA_AG_CODEC_CVSD; break;
+ case UUID_CODEC_MSBC: codec_type = BTA_AG_CODEC_MSBC; break;
+ default:
+ APPL_TRACE_ERROR1("Unknown codec_uuid %d", int_arg);
+ codec_type = 0xFFFF;
+ break;
+ }
+
+ if (p_scb->codec_fallback)
+ codec_sent = BTA_AG_CODEC_CVSD;
+ else
+ codec_sent = p_scb->sco_codec;
+
+ if(codec_type == codec_sent)
+ bta_ag_sco_codec_nego(p_scb, TRUE);
+ else
+ bta_ag_sco_codec_nego(p_scb, FALSE);
+
+ bta_ag_send_ok(p_scb);
+ break;
+
+ case BTA_AG_HF_CMD_BCC:
+ bta_ag_send_ok(p_scb);
+ bta_ag_sco_open(p_scb, NULL);
+ break;
+#endif
+
+ default:
+ break;
+ }
+
+ /* call callback */
+ if (event != 0)
+ {
+ (*bta_ag_cb.p_cback)(event, (tBTA_AG *) &val);
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_ag_at_err_cback
+**
+** Description AT command parser error callback.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_ag_at_err_cback(tBTA_AG_SCB *p_scb, BOOLEAN unknown, char *p_arg)
+{
+ tBTA_AG_VAL val;
+
+ if(unknown && (!strlen(p_arg)))
+ {
+ APPL_TRACE_DEBUG0("Empty AT cmd string received");
+ bta_ag_send_ok(p_scb);
+ return;
+ }
+
+ /* if unknown AT command and configured to pass these to app */
+ if (unknown && (p_scb->features & BTA_AG_FEAT_UNAT))
+ {
+ val.hdr.handle = bta_ag_scb_to_idx(p_scb);
+ val.hdr.app_id = p_scb->app_id;
+ val.num = 0;
+ BCM_STRNCPY_S(val.str, sizeof(val.str), p_arg, BTA_AG_AT_MAX_LEN);
+ val.str[BTA_AG_AT_MAX_LEN] = 0;
+ (*bta_ag_cb.p_cback)(BTA_AG_AT_UNAT_EVT, (tBTA_AG *) &val);
+ }
+ else
+ {
+ bta_ag_send_error(p_scb, BTA_AG_ERR_OP_NOT_SUPPORTED);
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_ag_hsp_result
+**
+** Description Handle API result for HSP connections.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_ag_hsp_result(tBTA_AG_SCB *p_scb, tBTA_AG_API_RESULT *p_result)
+{
+ UINT8 code = bta_ag_trans_result[p_result->result];
+
+ APPL_TRACE_DEBUG1("bta_ag_hsp_result : res = %d", p_result->result);
+
+ switch(p_result->result)
+ {
+ case BTA_AG_SPK_RES:
+ case BTA_AG_MIC_RES:
+ bta_ag_send_result(p_scb, code, NULL, p_result->data.num);
+ break;
+
+ case BTA_AG_IN_CALL_RES:
+ /* tell sys to stop av if any */
+ bta_sys_sco_use(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr);
+
+ /* if sco already opened or no inband ring send ring now */
+ if (bta_ag_sco_is_open(p_scb) || !bta_ag_inband_enabled(p_scb) ||
+ (p_scb->features & BTA_AG_FEAT_NOSCO))
+ {
+ bta_ag_send_ring(p_scb, (tBTA_AG_DATA *) p_result);
+ }
+ /* else open sco, send ring after sco opened */
+ else
+ {
+ /* HSPv1.2: AG shall not send RING if using in-band ring tone. */
+ if (p_scb->hsp_version >= HSP_VERSION_1_2)
+ p_scb->post_sco = BTA_AG_POST_SCO_NONE;
+ else
+ p_scb->post_sco = BTA_AG_POST_SCO_RING;
+
+ bta_ag_sco_open(p_scb, (tBTA_AG_DATA *) p_result);
+ }
+ break;
+
+ case BTA_AG_IN_CALL_CONN_RES:
+ case BTA_AG_OUT_CALL_ORIG_RES:
+ /* if incoming call connected stop ring timer */
+ if (p_result->result == BTA_AG_IN_CALL_CONN_RES)
+ {
+ bta_sys_stop_timer(&p_scb->act_timer);
+ }
+
+ if (!(p_scb->features & BTA_AG_FEAT_NOSCO))
+ {
+ /* if audio connected to this scb open sco */
+ if (p_result->data.audio_handle == bta_ag_scb_to_idx(p_scb))
+ {
+ bta_ag_sco_open(p_scb, (tBTA_AG_DATA *) p_result);
+ }
+ /* else if no audio at call close sco */
+ else if (p_result->data.audio_handle == BTA_AG_HANDLE_NONE)
+ {
+ bta_ag_sco_close(p_scb, (tBTA_AG_DATA *) p_result);
+ }
+ }
+ break;
+
+ case BTA_AG_END_CALL_RES:
+ /* stop ring timer */
+ bta_sys_stop_timer(&p_scb->act_timer);
+
+ /* close sco */
+ if ((bta_ag_sco_is_open(p_scb) || bta_ag_sco_is_opening(p_scb)) && !(p_scb->features & BTA_AG_FEAT_NOSCO))
+ {
+ bta_ag_sco_close(p_scb, (tBTA_AG_DATA *) p_result);
+ }
+ else
+ {
+ /* if av got suspended by this call, let it resume. */
+ bta_sys_sco_unuse(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr);
+ }
+ break;
+
+ case BTA_AG_INBAND_RING_RES:
+ p_scb->inband_enabled = p_result->data.state;
+ APPL_TRACE_DEBUG1("inband_enabled set to %d", p_scb->inband_enabled);
+ break;
+
+ case BTA_AG_UNAT_RES:
+ if (p_result->data.ok_flag != BTA_AG_OK_ERROR)
+ {
+ if (p_result->data.str[0] != 0)
+ {
+ bta_ag_send_result(p_scb, code, p_result->data.str, 0);
+ }
+
+ if (p_result->data.ok_flag == BTA_AG_OK_DONE)
+ bta_ag_send_ok(p_scb);
+ }
+ else
+ {
+ bta_ag_send_error(p_scb, BTA_AG_ERR_INV_CHAR_IN_TSTR);
+ }
+ break;
+
+ default:
+ /* ignore all others */
+ break;
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_ag_hfp_result
+**
+** Description Handle API result for HFP connections.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_ag_hfp_result(tBTA_AG_SCB *p_scb, tBTA_AG_API_RESULT *p_result)
+{
+ UINT8 code = bta_ag_trans_result[p_result->result];
+
+ APPL_TRACE_DEBUG1("bta_ag_hfp_result : res = %d", p_result->result);
+
+ switch(p_result->result)
+ {
+ case BTA_AG_SPK_RES:
+ case BTA_AG_MIC_RES:
+ bta_ag_send_result(p_scb, code, NULL, p_result->data.num);
+ break;
+
+ case BTA_AG_IN_CALL_RES:
+ /* tell sys to stop av if any */
+ bta_sys_sco_use(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr);
+
+ /* store caller id string.
+ * append type info at the end.
+ * make sure a valid type info is passed.
+ * otherwise add 129 as default type */
+ if ((p_result->data.num < BTA_AG_CLIP_TYPE_MIN) || (p_result->data.num > BTA_AG_CLIP_TYPE_MAX))
+ {
+ if (p_result->data.num != BTA_AG_CLIP_TYPE_VOIP)
+ p_result->data.num = BTA_AG_CLIP_TYPE_DEFAULT;
+ }
+
+ APPL_TRACE_DEBUG1("CLIP type :%d", p_result->data.num);
+ p_scb->clip[0] = 0;
+ if (p_result->data.str[0] != 0)
+ sprintf(p_scb->clip,"%s,%d", p_result->data.str, p_result->data.num);
+
+ /* send callsetup indicator */
+ if (p_scb->post_sco == BTA_AG_POST_SCO_CALL_END)
+ {
+ /* Need to sent 2 callsetup IND's(Call End and Incoming call) after SCO close. */
+ p_scb->post_sco = BTA_AG_POST_SCO_CALL_END_INCALL;
+ }
+ else
+ {
+ bta_ag_send_call_inds(p_scb, p_result->result);
+
+ /* if sco already opened or no inband ring send ring now */
+ if (bta_ag_sco_is_open(p_scb) || !bta_ag_inband_enabled(p_scb) ||
+ (p_scb->features & BTA_AG_FEAT_NOSCO))
+ {
+ bta_ag_send_ring(p_scb, (tBTA_AG_DATA *) p_result);
+ }
+ /* else open sco, send ring after sco opened */
+ else
+ {
+ p_scb->post_sco = BTA_AG_POST_SCO_RING;
+ bta_ag_sco_open(p_scb, (tBTA_AG_DATA *) p_result);
+ }
+ }
+ break;
+
+ case BTA_AG_IN_CALL_CONN_RES:
+ /* stop ring timer */
+ bta_sys_stop_timer(&p_scb->act_timer);
+
+ /* if sco not opened and we need to open it, open sco first
+ ** then send indicators
+ */
+ if (p_result->data.audio_handle == bta_ag_scb_to_idx(p_scb) &&
+ !bta_ag_sco_is_open(p_scb) && !(p_scb->features & BTA_AG_FEAT_NOSCO))
+ {
+ p_scb->post_sco = BTA_AG_POST_SCO_CALL_CONN;
+ bta_ag_sco_open(p_scb, (tBTA_AG_DATA *) p_result);
+ }
+ /* else if sco open and we need to close it, close sco first
+ ** then send indicators
+ */
+ else if (p_result->data.audio_handle == BTA_AG_HANDLE_NONE &&
+ bta_ag_sco_is_open(p_scb) && !(p_scb->features & BTA_AG_FEAT_NOSCO))
+ {
+ p_scb->post_sco = BTA_AG_POST_SCO_CALL_CONN;
+ bta_ag_sco_close(p_scb, (tBTA_AG_DATA *) p_result);
+ }
+ /* else send indicators now */
+ else
+ {
+ bta_ag_send_call_inds(p_scb, p_result->result);
+ }
+ break;
+
+ case BTA_AG_IN_CALL_HELD_RES:
+ /* stop ring timer */
+ bta_sys_stop_timer(&p_scb->act_timer);
+
+ bta_ag_send_call_inds(p_scb, p_result->result);
+
+ break;
+
+ case BTA_AG_OUT_CALL_ORIG_RES:
+ /* if sco open and we need to close it, close sco first
+ ** then send indicators; else send indicators now
+ */
+ if (p_result->data.audio_handle == BTA_AG_HANDLE_NONE &&
+ bta_ag_sco_is_open(p_scb) && !(p_scb->features & BTA_AG_FEAT_NOSCO))
+ {
+ p_scb->post_sco = BTA_AG_POST_SCO_CALL_ORIG;
+ bta_ag_sco_close(p_scb, (tBTA_AG_DATA *) p_result);
+ }
+ else
+ {
+ bta_ag_send_call_inds(p_scb, p_result->result);
+ if (p_result->data.audio_handle == bta_ag_scb_to_idx(p_scb) &&
+ !(p_scb->features & BTA_AG_FEAT_NOSCO))
+ {
+ bta_ag_sco_open(p_scb, (tBTA_AG_DATA *) p_result);
+ }
+ }
+ break;
+
+ case BTA_AG_OUT_CALL_ALERT_RES:
+ /* send indicators */
+ bta_ag_send_call_inds(p_scb, p_result->result);
+ if (p_result->data.audio_handle == bta_ag_scb_to_idx(p_scb) &&
+ !(p_scb->features & BTA_AG_FEAT_NOSCO))
+ {
+ bta_ag_sco_open(p_scb, (tBTA_AG_DATA *) p_result);
+ }
+ break;
+
+ case BTA_AG_OUT_CALL_CONN_RES:
+ /* send indicators */
+ bta_ag_send_call_inds(p_scb, p_result->result);
+
+ /* open or close sco */
+ if (!(p_scb->features & BTA_AG_FEAT_NOSCO))
+ {
+ if (p_result->data.audio_handle == bta_ag_scb_to_idx(p_scb))
+ {
+ bta_ag_sco_open(p_scb, (tBTA_AG_DATA *) p_result);
+ }
+ else if (p_result->data.audio_handle == BTA_AG_HANDLE_NONE)
+ {
+ bta_ag_sco_close(p_scb, (tBTA_AG_DATA *) p_result);
+ }
+ }
+ break;
+
+ case BTA_AG_CALL_CANCEL_RES:
+ /* send indicators */
+ bta_ag_send_call_inds(p_scb, p_result->result);
+ break;
+
+ case BTA_AG_END_CALL_RES:
+ /* stop ring timer */
+ bta_sys_stop_timer(&p_scb->act_timer);
+
+ /* if sco open, close sco then send indicator values */
+ if ((bta_ag_sco_is_open(p_scb) || bta_ag_sco_is_opening(p_scb)) && !(p_scb->features & BTA_AG_FEAT_NOSCO))
+ {
+ p_scb->post_sco = BTA_AG_POST_SCO_CALL_END;
+ bta_ag_sco_close(p_scb, (tBTA_AG_DATA *) p_result);
+ }
+ else if (p_scb->post_sco == BTA_AG_POST_SCO_CALL_END_INCALL)
+ {
+ /* sco closing for outgoing call because of incoming call */
+ /* Send only callsetup end indicator after sco close */
+ p_scb->post_sco = BTA_AG_POST_SCO_CALL_END;
+ }
+ else
+ {
+ bta_ag_send_call_inds(p_scb, p_result->result);
+
+ /* if av got suspended by this call, let it resume. */
+ bta_sys_sco_unuse(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr);
+ }
+ break;
+
+ case BTA_AG_INBAND_RING_RES:
+ p_scb->inband_enabled = p_result->data.state;
+ APPL_TRACE_DEBUG1("inband_enabled set to %d", p_scb->inband_enabled);
+ bta_ag_send_result(p_scb, code, NULL, p_result->data.state);
+ break;
+
+ case BTA_AG_CIND_RES:
+ /* store local values */
+ p_scb->call_ind = p_result->data.str[0] - '0';
+ p_scb->callsetup_ind = p_result->data.str[2] - '0';
+ p_scb->service_ind = p_result->data.str[4] - '0';
+ p_scb->signal_ind = p_result->data.str[6] - '0';
+ p_scb->roam_ind = p_result->data.str[8] - '0';
+ p_scb->battchg_ind = p_result->data.str[10] - '0';
+ APPL_TRACE_DEBUG2("cind call:%d callsetup:%d", p_scb->call_ind, p_scb->callsetup_ind);
+
+ bta_ag_send_result(p_scb, code, p_result->data.str, 0);
+ bta_ag_send_ok(p_scb);
+ break;
+
+ case BTA_AG_BINP_RES:
+ case BTA_AG_CNUM_RES:
+ case BTA_AG_CLCC_RES:
+ case BTA_AG_COPS_RES:
+ if (p_result->data.ok_flag != BTA_AG_OK_ERROR)
+ {
+ if (p_result->data.str[0] != 0)
+ {
+ bta_ag_send_result(p_scb, code, p_result->data.str, 0);
+ }
+
+ if (p_result->data.ok_flag == BTA_AG_OK_DONE)
+ bta_ag_send_ok(p_scb);
+ }
+ else
+ {
+ bta_ag_send_error(p_scb, p_result->data.errcode);
+ }
+ break;
+
+
+ case BTA_AG_UNAT_RES:
+ if (p_result->data.ok_flag != BTA_AG_OK_ERROR)
+ {
+ if (p_result->data.str[0] != 0)
+ {
+ bta_ag_process_unat_res(p_result->data.str);
+ APPL_TRACE_DEBUG1("BTA_AG_RES :%s",p_result->data.str);
+ bta_ag_send_result(p_scb, code, p_result->data.str, 0);
+ }
+
+ if (p_result->data.ok_flag == BTA_AG_OK_DONE)
+ bta_ag_send_ok(p_scb);
+ }
+ else
+ {
+ bta_ag_send_error(p_scb, p_result->data.errcode);
+ }
+ break;
+
+ case BTA_AG_CALL_WAIT_RES:
+ if (p_scb->ccwa_enabled)
+ {
+ bta_ag_send_result(p_scb, code, p_result->data.str, 0);
+ }
+ bta_ag_send_call_inds(p_scb, p_result->result);
+ break;
+
+ case BTA_AG_IND_RES:
+ bta_ag_send_ind(p_scb, p_result->data.ind.id, p_result->data.ind.value, FALSE);
+ break;
+
+ case BTA_AG_BVRA_RES:
+ bta_ag_send_result(p_scb, code, NULL, p_result->data.state);
+ break;
+
+ case BTA_AG_BTRH_RES:
+ if (p_result->data.ok_flag != BTA_AG_OK_ERROR)
+ {
+ /* Don't respond to read if not in response & hold state */
+ if (p_result->data.num != BTA_AG_BTRH_NO_RESP)
+ {
+ bta_ag_send_result(p_scb, code, NULL, p_result->data.num);
+ }
+
+ /* In case of a response to a read request we need to send OK */
+ if (p_result->data.ok_flag == BTA_AG_OK_DONE)
+ bta_ag_send_ok(p_scb);
+ }
+ else
+ {
+ bta_ag_send_error(p_scb, p_result->data.errcode);
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function bta_ag_result
+**
+** Description Handle API result.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_ag_result(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
+{
+ if (p_scb->conn_service == BTA_AG_HSP)
+ {
+ bta_ag_hsp_result(p_scb, &p_data->api_result);
+ }
+ else
+ {
+ bta_ag_hfp_result(p_scb, &p_data->api_result);
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_ag_setcodec
+**
+** Description Handle API SetCodec
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_ag_setcodec(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
+{
+#if (BTM_WBS_INCLUDED == TRUE )
+ tBTA_AG_PEER_CODEC codec_type = p_data->api_setcodec.codec;
+
+ /* Check if the requested codec type is valid */
+ if((codec_type != BTA_AG_CODEC_NONE) &&
+ (codec_type != BTA_AG_CODEC_CVSD) &&
+ (codec_type != BTA_AG_CODEC_MSBC))
+ {
+ APPL_TRACE_ERROR1("bta_ag_setcodec error: unsupported codec type %d", codec_type);
+ return;
+ }
+
+ if((p_scb->peer_codecs & codec_type) || (codec_type == BTA_AG_CODEC_NONE) || (codec_type == BTA_AG_CODEC_CVSD))
+ {
+ p_scb->sco_codec = codec_type;
+ p_scb->codec_updated = TRUE;
+ APPL_TRACE_DEBUG1("bta_ag_setcodec: Updated codec type %d", codec_type);
+ }
+ else
+ {
+ APPL_TRACE_ERROR1("bta_ag_setcodec error: unsupported codec type %d", codec_type);
+ }
+#endif
+}
+
+
+#if (BTM_WBS_INCLUDED == TRUE )
+/*******************************************************************************
+**
+** Function bta_ag_send_bcs
+**
+** Description Send +BCS AT command to peer.
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_ag_send_bcs(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
+{
+ UINT16 codec_uuid;
+
+ if (p_scb->codec_fallback)
+ {
+ codec_uuid = UUID_CODEC_CVSD;
+ }
+ else
+ {
+ switch(p_scb->sco_codec)
+ {
+ case BTA_AG_CODEC_NONE: codec_uuid = UUID_CODEC_CVSD; break;
+ case BTA_AG_CODEC_CVSD: codec_uuid = UUID_CODEC_CVSD; break;
+ case BTA_AG_CODEC_MSBC: codec_uuid = UUID_CODEC_MSBC; break;
+ default:
+ APPL_TRACE_ERROR1("bta_ag_send_bcs: unknown codec %d, use CVSD", p_scb->sco_codec);
+ codec_uuid = UUID_CODEC_CVSD;
+ break;
+ }
+ }
+
+ /* send +BCS */
+ bta_ag_send_result(p_scb, BTA_AG_RES_BCS, NULL, codec_uuid);
+
+}
+#endif
+
+/*******************************************************************************
+**
+** Function bta_ag_send_ring
+**
+** Description Send RING result code to peer.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_ag_send_ring(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
+{
+#if defined(BTA_AG_MULTI_RESULT_INCLUDED) && (BTA_AG_MULTI_RESULT_INCLUDED == TRUE)
+ tBTA_AG_MULTI_RESULT_CB m_res_cb;
+
+ if (p_scb->conn_service == BTA_AG_HFP && p_scb->clip_enabled && p_scb->clip[0] != 0)
+ {
+ memset(&m_res_cb, NULL, sizeof(tBTA_AG_MULTI_RESULT_CB));
+
+ m_res_cb.num_result = 2;
+ AT_SET_RES_CB(m_res_cb.res_cb[0], BTA_AG_RES_RING, NULL, 0)
+ AT_SET_RES_CB(m_res_cb.res_cb[1], BTA_AG_RES_CLIP, p_scb->clip, 0)
+
+ bta_ag_send_multi_result(p_scb, &m_res_cb);
+ }
+ else
+ {
+ /* send RING ONLY */
+ bta_ag_send_result(p_scb, BTA_AG_RES_RING, NULL, 0);
+ }
+#else
+ /* send RING */
+ bta_ag_send_result(p_scb, BTA_AG_RES_RING, NULL, 0);
+
+ /* if HFP and clip enabled and clip data send CLIP */
+ if (p_scb->conn_service == BTA_AG_HFP && p_scb->clip_enabled && p_scb->clip[0] != 0)
+ {
+ bta_ag_send_result(p_scb, BTA_AG_RES_CLIP, p_scb->clip, 0);
+ }
+#endif
+
+ /* restart ring timer */
+ bta_sys_start_timer(&p_scb->act_timer, BTA_AG_RING_TOUT_EVT, BTA_AG_RING_TOUT);
+}
+
+