Bluetooth: Add support for HFP Client role.
Implementation changes in BTA and BTIF layer to support
HFP Client role.
Change-Id: I9f939c18e8f989a50f298d0b313c5a0959c030a7
diff --git a/bta/hf_client/bta_hf_client_act.c b/bta/hf_client/bta_hf_client_act.c
new file mode 100644
index 0000000..854260a
--- /dev/null
+++ b/bta/hf_client/bta_hf_client_act.c
@@ -0,0 +1,768 @@
+/******************************************************************************
+ *
+ * Copyright (c) 2014 The Android Open Source Project
+ * Copyright (C) 2003-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 action functions for the handsfree client.
+ *
+ ******************************************************************************/
+
+#include "bta_api.h"
+#include "bd.h"
+#include "bta_hf_client_api.h"
+#include "bta_hf_client_int.h"
+#include "bta_dm_int.h"
+#include "l2c_api.h"
+#include "port_api.h"
+#include "bta_sys.h"
+#include "utl.h"
+#include "bt_utils.h"
+#include <string.h>
+
+/*****************************************************************************
+** Constants
+*****************************************************************************/
+
+/* maximum length of data to read from RFCOMM */
+#define BTA_HF_CLIENT_RFC_READ_MAX 512
+
+/*******************************************************************************
+**
+** Function bta_hf_client_register
+**
+** Description This function initializes values of the scb and sets up
+** the SDP record for the services.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_hf_client_register(tBTA_HF_CLIENT_DATA *p_data)
+{
+ tBTA_HF_CLIENT evt;
+ tBTA_UTL_COD cod;
+
+ memset(&evt, 0, sizeof(evt));
+
+ /* initialize control block */
+ bta_hf_client_scb_init();
+
+ bta_hf_client_cb.scb.serv_sec_mask = p_data->api_register.sec_mask;
+ bta_hf_client_cb.scb.features = p_data->api_register.features;
+
+ /* initialize AT control block */
+ bta_hf_client_at_init();
+
+ /* create SDP records */
+ bta_hf_client_create_record(p_data);
+
+ /* Set the Audio service class bit */
+ cod.service = BTM_COD_SERVICE_AUDIO;
+ utl_set_device_class(&cod, BTA_UTL_SET_COD_SERVICE_CLASS);
+
+ /* start RFCOMM server */
+ bta_hf_client_start_server();
+
+ /* call app callback with register event */
+ evt.reg.status = BTA_HF_CLIENT_SUCCESS;
+ (*bta_hf_client_cb.p_cback)(BTA_HF_CLIENT_REGISTER_EVT, &evt);
+}
+
+/*******************************************************************************
+**
+** Function bta_hf_client_deregister
+**
+** Description This function removes the sdp records, closes the RFCOMM
+** servers, and deallocates the service control block.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_hf_client_deregister(tBTA_HF_CLIENT_DATA *p_data)
+{
+ bta_hf_client_cb.scb.deregister = TRUE;
+
+ /* remove sdp record */
+ bta_hf_client_del_record(p_data);
+
+ /* remove rfcomm server */
+ bta_hf_client_close_server();
+
+ /* disable */
+ bta_hf_client_scb_disable();
+}
+
+/*******************************************************************************
+**
+** Function bta_hf_client_start_dereg
+**
+** Description Start a deregister event.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_hf_client_start_dereg(tBTA_HF_CLIENT_DATA *p_data)
+{
+ bta_hf_client_cb.scb.deregister = TRUE;
+
+ /* remove sdp record */
+ bta_hf_client_del_record(p_data);
+}
+
+/*******************************************************************************
+**
+** Function bta_hf_client_start_close
+**
+** Description Start the process of closing SCO and RFCOMM connection.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_hf_client_start_close(tBTA_HF_CLIENT_DATA *p_data)
+{
+ /* Take the link out of sniff and set L2C idle time to 0 */
+ bta_dm_pm_active(bta_hf_client_cb.scb.peer_addr);
+ L2CA_SetIdleTimeoutByBdAddr(bta_hf_client_cb.scb.peer_addr, 0);
+
+ /* if SCO is open close SCO and wait on RFCOMM close */
+ if (bta_hf_client_cb.scb.sco_state == BTA_HF_CLIENT_SCO_OPEN_ST)
+ {
+ bta_hf_client_cb.scb.sco_close_rfc = TRUE;
+ }
+ else
+ {
+ bta_hf_client_rfc_do_close(p_data);
+ }
+
+ /* always do SCO shutdown to handle all SCO corner cases */
+ bta_hf_client_sco_shutdown(NULL);
+}
+
+/*******************************************************************************
+**
+** Function bta_hf_client_start_open
+**
+** Description This starts an HF Client open.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_hf_client_start_open(tBTA_HF_CLIENT_DATA *p_data)
+{
+ BD_ADDR pending_bd_addr;
+
+ /* store parameters */
+ if (p_data)
+ {
+ bdcpy(bta_hf_client_cb.scb.peer_addr, p_data->api_open.bd_addr);
+ bta_hf_client_cb.scb.cli_sec_mask = p_data->api_open.sec_mask;
+ }
+
+ /* Check if RFCOMM has any incoming connection to avoid collision. */
+ if (PORT_IsOpening (pending_bd_addr))
+ {
+ /* Let the incoming connection goes through. */
+ /* Issue collision for now. */
+ /* We will decide what to do when we find incoming connection later.*/
+ bta_hf_client_collision_cback (0, BTA_ID_HS, 0, bta_hf_client_cb.scb.peer_addr);
+ return;
+ }
+
+ /* close server */
+ bta_hf_client_close_server();
+
+ /* set role */
+ bta_hf_client_cb.scb.role = BTA_HF_CLIENT_INT;
+
+ /* do service search */
+ bta_hf_client_do_disc();
+}
+
+/*******************************************************************************
+**
+** Function bta_hf_client_cback_open
+**
+** Description Send open callback event to application.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+static void bta_hf_client_cback_open(tBTA_HF_CLIENT_DATA *p_data, tBTA_HF_CLIENT_STATUS status)
+{
+ tBTA_HF_CLIENT evt;
+
+ memset(&evt, 0, sizeof(evt));
+
+ /* call app callback with open event */
+ evt.open.status = status;
+ if(p_data)
+ {
+ /* if p_data is provided then we need to pick the bd address from the open api structure */
+ bdcpy(evt.open.bd_addr, p_data->api_open.bd_addr);
+ }
+ else
+ {
+ bdcpy(evt.open.bd_addr, bta_hf_client_cb.scb.peer_addr);
+ }
+
+ (*bta_hf_client_cb.p_cback)(BTA_HF_CLIENT_OPEN_EVT, &evt);
+}
+
+/*******************************************************************************
+**
+** Function bta_hf_client_rfc_open
+**
+** Description Handle RFCOMM channel open.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_hf_client_rfc_open(tBTA_HF_CLIENT_DATA *p_data)
+{
+ UNUSED(p_data);
+
+ bta_sys_conn_open(BTA_ID_HS, 1, bta_hf_client_cb.scb.peer_addr);
+
+ bta_hf_client_cback_open(NULL, BTA_HF_CLIENT_SUCCESS);
+
+ /* start SLC procedure */
+ bta_hf_client_slc_seq(FALSE);
+}
+
+/*******************************************************************************
+**
+** Function bta_hf_client_rfc_acp_open
+**
+** Description Handle RFCOMM channel open when accepting connection.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_hf_client_rfc_acp_open(tBTA_HF_CLIENT_DATA *p_data)
+{
+ UINT16 lcid;
+ int i;
+ BD_ADDR dev_addr;
+ int status;
+
+ /* set role */
+ bta_hf_client_cb.scb.role = BTA_HF_CLIENT_ACP;
+
+ APPL_TRACE_DEBUG2 ("bta_hf_client_rfc_acp_open: serv_handle = %d rfc.port_handle = %d",
+ bta_hf_client_cb.scb.serv_handle, p_data->rfc.port_handle);
+
+ /* get bd addr of peer */
+ if (PORT_SUCCESS != (status=PORT_CheckConnection(p_data->rfc.port_handle, dev_addr, &lcid)))
+ {
+ APPL_TRACE_DEBUG1 ("bta_hf_client_rfc_acp_open error PORT_CheckConnection returned status %d", status);
+ }
+
+ /* Collision Handling */
+ if (bta_hf_client_cb.scb.colli_tmr_on)
+ {
+ /* stop collision timer */
+ bta_hf_client_cb.scb.colli_tmr_on = FALSE;
+ bta_sys_stop_timer (&bta_hf_client_cb.scb.colli_timer);
+
+ if (bdcmp (dev_addr, bta_hf_client_cb.scb.peer_addr) == 0)
+ {
+ /* If incoming and outgoing device are same, nothing more to do. */
+ /* Outgoing conn will be aborted because we have successful incoming conn. */
+ }
+ else
+ {
+ /* Resume outgoing connection. */
+ bta_hf_client_resume_open ();
+ }
+ }
+
+ bdcpy (bta_hf_client_cb.scb.peer_addr, dev_addr);
+ bta_hf_client_cb.scb.conn_handle = p_data->rfc.port_handle;
+
+ /* do service discovery to get features */
+ bta_hf_client_do_disc();
+
+ /* continue with open processing */
+ bta_hf_client_rfc_open(p_data);
+}
+
+/*******************************************************************************
+**
+** Function bta_hf_client_rfc_fail
+**
+** Description RFCOMM connection failed.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_hf_client_rfc_fail(tBTA_HF_CLIENT_DATA *p_data)
+{
+ UNUSED(p_data);
+
+ /* reinitialize stuff */
+ bta_hf_client_cb.scb.conn_handle = 0;
+ bta_hf_client_cb.scb.peer_features = 0;
+ bta_hf_client_cb.scb.chld_features = 0;
+ bta_hf_client_cb.scb.role = BTA_HF_CLIENT_ACP;
+ bta_hf_client_cb.scb.svc_conn = FALSE;
+ bta_hf_client_cb.scb.send_at_reply = FALSE;
+ bta_hf_client_cb.scb.negotiated_codec = BTM_SCO_CODEC_CVSD;
+
+ bta_hf_client_at_reset();
+
+ /* reopen server */
+ bta_hf_client_start_server();
+
+ /* call open cback w. failure */
+ bta_hf_client_cback_open(NULL, BTA_HF_CLIENT_FAIL_RFCOMM);
+}
+
+/*******************************************************************************
+**
+** Function bta_hf_client_disc_fail
+**
+** Description This function handles a discovery failure.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_hf_client_disc_fail(tBTA_HF_CLIENT_DATA *p_data)
+{
+ UNUSED(p_data);
+
+ /* reopen server */
+ bta_hf_client_start_server();
+
+ /* reinitialize stuff */
+
+ /* call open cback w. failure */
+ bta_hf_client_cback_open(NULL, BTA_HF_CLIENT_FAIL_SDP);
+}
+
+/*******************************************************************************
+**
+** Function bta_hf_client_open_fail
+**
+** Description open connection failed.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_hf_client_open_fail(tBTA_HF_CLIENT_DATA *p_data)
+{
+ /* call open cback w. failure */
+ bta_hf_client_cback_open(p_data, BTA_HF_CLIENT_FAIL_RESOURCES);
+}
+
+/*******************************************************************************
+**
+** Function bta_hf_client_rfc_close
+**
+** Description RFCOMM connection closed.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_hf_client_rfc_close(tBTA_HF_CLIENT_DATA *p_data)
+{
+ int i, num_active_conn = 0;
+ UNUSED(p_data);
+
+ /* reinitialize stuff */
+ bta_hf_client_cb.scb.peer_features = 0;
+ bta_hf_client_cb.scb.chld_features = 0;
+ bta_hf_client_cb.scb.role = BTA_HF_CLIENT_ACP;
+ bta_hf_client_cb.scb.svc_conn = FALSE;
+ bta_hf_client_cb.scb.send_at_reply = FALSE;
+ bta_hf_client_cb.scb.negotiated_codec = BTM_SCO_CODEC_CVSD;
+
+ bta_hf_client_at_reset();
+
+ bta_sys_conn_close(BTA_ID_HS, 1, bta_hf_client_cb.scb.peer_addr);
+
+ /* call close cback */
+ (*bta_hf_client_cb.p_cback)(BTA_HF_CLIENT_CLOSE_EVT, NULL);
+
+ /* if not deregistering reopen server */
+ if (bta_hf_client_cb.scb.deregister == FALSE)
+ {
+ /* Clear peer bd_addr so instance can be reused */
+ bdcpy(bta_hf_client_cb.scb.peer_addr, bd_addr_null);
+
+ /* start server as it might got closed on open*/
+ bta_hf_client_start_server();
+
+ bta_hf_client_cb.scb.conn_handle = 0;
+
+ /* Make sure SCO is shutdown */
+ bta_hf_client_sco_shutdown(NULL);
+
+ bta_sys_sco_unuse(BTA_ID_HS, 1, bta_hf_client_cb.scb.peer_addr);
+ }
+ /* else close port and deallocate scb */
+ else
+ {
+ bta_hf_client_close_server();
+ bta_hf_client_scb_disable();
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_hf_client_disc_int_res
+**
+** Description This function handles a discovery result when initiator.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_hf_client_disc_int_res(tBTA_HF_CLIENT_DATA *p_data)
+{
+ UINT16 event = BTA_HF_CLIENT_DISC_FAIL_EVT;
+
+ APPL_TRACE_DEBUG1 ("bta_hf_client_disc_int_res: Status: %d", p_data->disc_result.status);
+
+ /* if found service */
+ if (p_data->disc_result.status == SDP_SUCCESS ||
+ p_data->disc_result.status == SDP_DB_FULL)
+ {
+ /* get attributes */
+ if (bta_hf_client_sdp_find_attr())
+ {
+ event = BTA_HF_CLIENT_DISC_OK_EVT;
+ }
+ }
+
+ /* free discovery db */
+ bta_hf_client_free_db(p_data);
+
+ /* send ourselves sdp ok/fail event */
+ bta_hf_client_sm_execute(event, p_data);
+}
+
+/*******************************************************************************
+**
+** Function bta_hf_client_disc_acp_res
+**
+** Description This function handles a discovery result when acceptor.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_hf_client_disc_acp_res(tBTA_HF_CLIENT_DATA *p_data)
+{
+ /* if found service */
+ if (p_data->disc_result.status == SDP_SUCCESS ||
+ p_data->disc_result.status == SDP_DB_FULL)
+ {
+ /* get attributes */
+ bta_hf_client_sdp_find_attr();
+ }
+
+ /* free discovery db */
+ bta_hf_client_free_db(p_data);
+}
+
+/*******************************************************************************
+**
+** Function bta_hf_client_rfc_data
+**
+** Description Read and process data from RFCOMM.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_hf_client_rfc_data(tBTA_HF_CLIENT_DATA *p_data)
+{
+ UINT16 len;
+ char buf[BTA_HF_CLIENT_RFC_READ_MAX];
+ UNUSED(p_data);
+
+ memset(buf, 0, sizeof(buf));
+
+ /* read data from rfcomm; if bad status, we're done */
+ while (PORT_ReadData(bta_hf_client_cb.scb.conn_handle, buf, BTA_HF_CLIENT_RFC_READ_MAX, &len) == PORT_SUCCESS)
+ {
+ /* if no data, we're done */
+ if (len == 0)
+ {
+ break;
+ }
+
+ bta_hf_client_at_parse(buf, len);
+
+ /* no more data to read, we're done */
+ if (len < BTA_HF_CLIENT_RFC_READ_MAX)
+ {
+ break;
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_hf_client_svc_conn_open
+**
+** Description Service level connection opened
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_hf_client_svc_conn_open(tBTA_HF_CLIENT_DATA *p_data)
+{
+ tBTA_HF_CLIENT evt;
+ UNUSED(p_data);
+
+ memset(&evt, 0, sizeof(evt));
+
+ if (!bta_hf_client_cb.scb.svc_conn)
+ {
+ /* set state variable */
+ bta_hf_client_cb.scb.svc_conn = TRUE;
+
+ /* call callback */
+ evt.conn.peer_feat = bta_hf_client_cb.scb.peer_features;
+ evt.conn.chld_feat = bta_hf_client_cb.scb.chld_features;
+
+ (*bta_hf_client_cb.p_cback)(BTA_HF_CLIENT_CONN_EVT, &evt);
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_hf_client_cback_ind
+**
+** Description Send indicator callback event to application.
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_hf_client_ind(tBTA_HF_CLIENT_IND_TYPE type, UINT16 value)
+{
+ tBTA_HF_CLIENT evt;
+
+ memset(&evt, 0, sizeof(evt));
+
+ evt.ind.type = type;
+ evt.ind.value = value;
+
+ (*bta_hf_client_cb.p_cback)(BTA_HF_CLIENT_IND_EVT, &evt);
+}
+
+/*******************************************************************************
+**
+** Function bta_hf_client_evt_val
+**
+** Description Send event to application.
+** This is a generic helper for events with common data.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_hf_client_evt_val(tBTA_HF_CLIENT_EVT type, UINT16 value)
+{
+ tBTA_HF_CLIENT evt;
+
+ memset(&evt, 0, sizeof(evt));
+
+ evt.val.value = value;
+
+ (*bta_hf_client_cb.p_cback)(type, &evt);
+}
+
+/*******************************************************************************
+**
+** Function bta_hf_client_operator_name
+**
+** Description Send operator name event to application.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_hf_client_operator_name(char *name)
+{
+ tBTA_HF_CLIENT evt;
+
+ memset(&evt, 0, sizeof(evt));
+
+ strlcpy(evt.operator.name, name, BTA_HF_CLIENT_OPERATOR_NAME_LEN + 1);
+ evt.operator.name[BTA_HF_CLIENT_OPERATOR_NAME_LEN] = '\0';
+
+ (*bta_hf_client_cb.p_cback)(BTA_HF_CLIENT_OPERATOR_NAME_EVT, &evt);
+}
+
+
+/*******************************************************************************
+**
+** Function bta_hf_client_clip
+**
+** Description Send CLIP event to application.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_hf_client_clip(char *number)
+{
+ tBTA_HF_CLIENT evt;
+
+ memset(&evt, 0, sizeof(evt));
+
+ strlcpy(evt.number.number, number, BTA_HF_CLIENT_NUMBER_LEN + 1);
+ evt.number.number[BTA_HF_CLIENT_NUMBER_LEN] = '\0';
+
+ (*bta_hf_client_cb.p_cback)(BTA_HF_CLIENT_CLIP_EVT, &evt);
+}
+
+/*******************************************************************************
+**
+** Function bta_hf_client_ccwa
+**
+** Description Send CLIP event to application.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_hf_client_ccwa(char *number)
+{
+ tBTA_HF_CLIENT evt;
+
+ memset(&evt, 0, sizeof(evt));
+
+ strlcpy(evt.number.number, number, BTA_HF_CLIENT_NUMBER_LEN + 1);
+ evt.number.number[BTA_HF_CLIENT_NUMBER_LEN] = '\0';
+
+ (*bta_hf_client_cb.p_cback)(BTA_HF_CLIENT_CCWA_EVT, &evt);
+}
+
+/*******************************************************************************
+**
+** Function bta_hf_client_at_result
+**
+** Description Send AT result event to application.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_hf_client_at_result(tBTA_HF_CLIENT_AT_RESULT_TYPE type, UINT16 cme)
+{
+ tBTA_HF_CLIENT evt;
+
+ memset(&evt, 0, sizeof(evt));
+
+ evt.result.type = type;
+ evt.result.cme = cme;
+
+ (*bta_hf_client_cb.p_cback)(BTA_HF_CLIENT_AT_RESULT_EVT, &evt);
+}
+
+/*******************************************************************************
+**
+** Function bta_hf_client_clcc
+**
+** Description Send clcc event to application.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_hf_client_clcc(UINT32 idx, BOOLEAN incoming, UINT8 status, BOOLEAN mpty, char *number)
+{
+ tBTA_HF_CLIENT evt;
+
+ memset(&evt, 0, sizeof(evt));
+
+ evt.clcc.idx = idx;
+ evt.clcc.inc = incoming;
+ evt.clcc.status = status;
+ evt.clcc.mpty = mpty;
+
+ if (number)
+ {
+ evt.clcc.number_present = TRUE;
+ strlcpy(evt.clcc.number, number, BTA_HF_CLIENT_NUMBER_LEN + 1);
+ evt.clcc.number[BTA_HF_CLIENT_NUMBER_LEN] = '\0';
+ }
+
+ (*bta_hf_client_cb.p_cback)(BTA_HF_CLIENT_CLCC_EVT, &evt);
+}
+
+/*******************************************************************************
+**
+** Function bta_hf_client_cnum
+**
+** Description Send cnum event to application.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_hf_client_cnum(char *number, UINT16 service)
+{
+ tBTA_HF_CLIENT evt;
+
+ memset(&evt, 0, sizeof(evt));
+
+ evt.cnum.service = service;
+ strlcpy(evt.cnum.number, number, BTA_HF_CLIENT_NUMBER_LEN + 1);
+ evt.cnum.number[BTA_HF_CLIENT_NUMBER_LEN] = '\0';
+
+ (*bta_hf_client_cb.p_cback)(BTA_HF_CLIENT_CNUM_EVT, &evt);
+}
+
+/*******************************************************************************
+**
+** Function bta_hf_client_binp
+**
+** Description Send BINP event to application.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_hf_client_binp(char *number)
+{
+ tBTA_HF_CLIENT evt;
+
+ memset(&evt, 0, sizeof(evt));
+
+ strlcpy(evt.number.number, number, BTA_HF_CLIENT_NUMBER_LEN + 1);
+ evt.number.number[BTA_HF_CLIENT_NUMBER_LEN] = '\0';
+
+ (*bta_hf_client_cb.p_cback)(BTA_HF_CLIENT_BINP_EVT, &evt);
+}
diff --git a/bta/hf_client/bta_hf_client_at.c b/bta/hf_client/bta_hf_client_at.c
new file mode 100644
index 0000000..4f061ba
--- /dev/null
+++ b/bta/hf_client/bta_hf_client_at.c
@@ -0,0 +1,1816 @@
+/******************************************************************************
+ *
+ * Copyright (c) 2014 The Android Open Source Project
+ * Copyright (C) 2003-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.
+ *
+ ******************************************************************************/
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include "bta_hf_client_api.h"
+#include "bta_hf_client_int.h"
+#include "port_api.h"
+
+/* Uncomment to enable AT traffic dumping */
+/* #define BTA_HF_CLIENT_AT_DUMP 1 */
+
+/* minimum length of AT event */
+#define BTA_HF_CLIENT_AT_EVENT_MIN_LEN 3
+
+/* timeout for AT response */
+#define BTA_HF_CLIENT_AT_TIMEOUT 29989
+
+/* timeout for AT hold timer */
+#define BTA_HF_CLIENT_AT_HOLD_TIMEOUT 41
+
+/******************************************************************************
+**
+** DATA TYPES AND CONTAINERS
+**
+*******************************************************************************/
+/* BRSF: store received values here */
+extern tBTA_HF_CLIENT_CB bta_hf_client_cb;
+
+/******************************************************************************
+** SUPPORTED EVENT MESSAGES
+*******************************************************************************/
+
+/* CIND: supported indicator names */
+#define BTA_HF_CLIENT_INDICATOR_BATTERYCHG "battchg"
+#define BTA_HF_CLIENT_INDICATOR_SIGNAL "signal"
+#define BTA_HF_CLIENT_INDICATOR_SERVICE "service"
+#define BTA_HF_CLIENT_INDICATOR_CALL "call"
+#define BTA_HF_CLIENT_INDICATOR_ROAM "roam"
+#define BTA_HF_CLIENT_INDICATOR_CALLSETUP "callsetup"
+#define BTA_HF_CLIENT_INDICATOR_CALLHELD "callheld"
+
+/* CIND: represents each indicators boundaries */
+typedef struct
+{
+ char* name;
+ UINT8 min;
+ UINT8 max;
+ UINT8 namelen;
+} tBTA_HF_CLIENT_INDICATOR;
+
+#define BTA_HF_CLIENT_AT_SUPPORTED_INDICATOR_COUNT 7
+
+/* CIND: storage room for indicators value range and their statuses */
+static const tBTA_HF_CLIENT_INDICATOR bta_hf_client_indicators[BTA_HF_CLIENT_AT_SUPPORTED_INDICATOR_COUNT] =
+{
+ /* name | min | max | name length - used by parser */
+ {BTA_HF_CLIENT_INDICATOR_BATTERYCHG, 0, 5, sizeof(BTA_HF_CLIENT_INDICATOR_BATTERYCHG)},
+ {BTA_HF_CLIENT_INDICATOR_SIGNAL, 0, 5, sizeof(BTA_HF_CLIENT_INDICATOR_SIGNAL)},
+ {BTA_HF_CLIENT_INDICATOR_SERVICE, 0, 1, sizeof(BTA_HF_CLIENT_INDICATOR_SERVICE)},
+ {BTA_HF_CLIENT_INDICATOR_CALL, 0, 1, sizeof(BTA_HF_CLIENT_INDICATOR_CALL)},
+ {BTA_HF_CLIENT_INDICATOR_ROAM, 0, 1, sizeof(BTA_HF_CLIENT_INDICATOR_ROAM)},
+ {BTA_HF_CLIENT_INDICATOR_CALLSETUP, 0, 3, sizeof(BTA_HF_CLIENT_INDICATOR_CALLSETUP)},
+ {BTA_HF_CLIENT_INDICATOR_CALLHELD, 0, 2, sizeof(BTA_HF_CLIENT_INDICATOR_CALLHELD)}
+};
+
+/* +VGM/+VGS - gain min/max values */
+#define BTA_HF_CLIENT_VGS_MIN 0
+#define BTA_HF_CLIENT_VGS_MAX 15
+#define BTA_HF_CLIENT_VGM_MIN 0
+#define BTA_HF_CLIENT_VGM_MAX 15
+
+UINT32 service_index = 0;
+BOOLEAN service_availability = TRUE;
+/* helper functions for handling AT commands queueing */
+
+static void bta_hf_client_clear_queued_at(void)
+{
+ tBTA_HF_CLIENT_AT_QCMD *cur = bta_hf_client_cb.scb.at_cb.queued_cmd;
+ tBTA_HF_CLIENT_AT_QCMD *next;
+
+ while (cur != NULL) {
+ next = cur->next;
+ GKI_freebuf(cur);
+ cur = next;
+ }
+
+ bta_hf_client_cb.scb.at_cb.queued_cmd = NULL;
+}
+
+static void bta_hf_client_queue_at(tBTA_HF_CLIENT_AT_CMD cmd, const char *buf, UINT16 buf_len)
+{
+ tBTA_HF_CLIENT_AT_QCMD *new_cmd;
+
+ APPL_TRACE_DEBUG1("%s", __FUNCTION__);
+
+ if ((new_cmd = (tBTA_HF_CLIENT_AT_QCMD *) GKI_getbuf(sizeof(tBTA_HF_CLIENT_AT_QCMD))) != NULL)
+ {
+ new_cmd->cmd = cmd;
+ new_cmd->buf_len = buf_len;
+ new_cmd->next = NULL;
+ memcpy(new_cmd->buf, buf, buf_len);
+
+ if (bta_hf_client_cb.scb.at_cb.queued_cmd != NULL)
+ {
+ tBTA_HF_CLIENT_AT_QCMD *qcmd = bta_hf_client_cb.scb.at_cb.queued_cmd;
+
+ while (qcmd->next != NULL)
+ qcmd = qcmd->next;
+
+ qcmd->next = new_cmd;
+ }
+ else
+ {
+ bta_hf_client_cb.scb.at_cb.queued_cmd = new_cmd;
+ }
+ }
+}
+
+static void bta_hf_client_at_resp_timer_cback (TIMER_LIST_ENT *p_tle)
+{
+ if (p_tle)
+ {
+ bta_hf_client_cb.scb.at_cb.resp_timer_on = FALSE;
+
+ APPL_TRACE_ERROR0("HFPClient: AT response timeout, disconnecting");
+
+ bta_hf_client_sm_execute(BTA_HF_CLIENT_API_CLOSE_EVT, NULL);
+ }
+}
+
+static void bta_hf_client_stop_at_resp_timer(void)
+{
+ if (bta_hf_client_cb.scb.at_cb.resp_timer_on)
+ {
+ bta_hf_client_cb.scb.at_cb.resp_timer_on = FALSE;
+ bta_sys_stop_timer (&bta_hf_client_cb.scb.at_cb.resp_timer);
+ }
+}
+
+static void bta_hf_client_start_at_resp_timer(void)
+{
+ if (bta_hf_client_cb.scb.at_cb.resp_timer_on)
+ {
+ bta_sys_stop_timer (&bta_hf_client_cb.scb.at_cb.resp_timer);
+ }
+
+ bta_hf_client_cb.scb.at_cb.resp_timer.p_cback = (TIMER_CBACK*)&bta_hf_client_at_resp_timer_cback;
+ bta_sys_start_timer(&bta_hf_client_cb.scb.at_cb.resp_timer, 0, BTA_HF_CLIENT_AT_TIMEOUT);
+ bta_hf_client_cb.scb.at_cb.resp_timer_on = TRUE;
+}
+
+static void bta_hf_client_send_at(tBTA_HF_CLIENT_AT_CMD cmd, char *buf, UINT16 buf_len)
+{
+ if ((bta_hf_client_cb.scb.at_cb.current_cmd == BTA_HF_CLIENT_AT_NONE ||
+ bta_hf_client_cb.scb.svc_conn == FALSE) &&
+ bta_hf_client_cb.scb.at_cb.hold_timer_on == FALSE)
+ {
+ UINT16 len;
+
+#ifdef BTA_HF_CLIENT_AT_DUMP
+ APPL_TRACE_DEBUG3("%s %.*s", __FUNCTION__, buf_len - 1, buf);
+#endif
+
+ bta_hf_client_cb.scb.at_cb.current_cmd = cmd;
+ PORT_WriteData(bta_hf_client_cb.scb.conn_handle, buf, buf_len, &len);
+
+ bta_hf_client_start_at_resp_timer();
+
+ return;
+ }
+
+ bta_hf_client_queue_at(cmd, buf, buf_len);
+}
+
+static void bta_hf_client_send_queued_at(void)
+{
+ tBTA_HF_CLIENT_AT_QCMD *cur = bta_hf_client_cb.scb.at_cb.queued_cmd;
+ tBTA_HF_CLIENT_AT_QCMD *next;
+
+ APPL_TRACE_DEBUG1("%s", __FUNCTION__);
+
+ if (cur != NULL)
+ {
+ next = cur->next;
+
+ bta_hf_client_send_at(cur->cmd, cur->buf, cur->buf_len);
+
+ GKI_freebuf(cur);
+
+ bta_hf_client_cb.scb.at_cb.queued_cmd = next;
+ }
+}
+
+static void bta_hf_client_at_hold_timer_cback(TIMER_LIST_ENT *p_tle)
+{
+ APPL_TRACE_DEBUG1("%s", __FUNCTION__);
+
+ if (p_tle)
+ {
+ bta_hf_client_cb.scb.at_cb.hold_timer_on = FALSE;
+ bta_hf_client_send_queued_at();
+ }
+}
+
+static void bta_hf_client_stop_at_hold_timer(void)
+{
+ APPL_TRACE_DEBUG1("%s", __FUNCTION__);
+
+ if (bta_hf_client_cb.scb.at_cb.hold_timer_on)
+ {
+ bta_hf_client_cb.scb.at_cb.hold_timer_on = FALSE;
+ bta_sys_stop_timer (&bta_hf_client_cb.scb.at_cb.hold_timer);
+ }
+}
+
+static void bta_hf_client_start_at_hold_timer(void)
+{
+ TIMER_LIST_ENT *timer = &bta_hf_client_cb.scb.at_cb.hold_timer;
+
+ APPL_TRACE_DEBUG1("%s", __FUNCTION__);
+
+ if (bta_hf_client_cb.scb.at_cb.hold_timer_on)
+ {
+ bta_sys_stop_timer (timer);
+ }
+
+ timer->p_cback = (TIMER_CBACK*)&bta_hf_client_at_hold_timer_cback;
+ bta_sys_start_timer(timer, 0, BTA_HF_CLIENT_AT_HOLD_TIMEOUT);
+ bta_hf_client_cb.scb.at_cb.hold_timer_on = TRUE;
+}
+
+/******************************************************************************
+**
+** COMMON AT EVENT HANDLING FUNCTIONS
+**
+** Receives data (strings, ints, etc.) from the parser and processes this data.
+** No buffer parsing is being done here.
+*******************************************************************************/
+
+static void bta_hf_client_handle_ok()
+{
+ APPL_TRACE_DEBUG1("%s", __FUNCTION__);
+
+ bta_hf_client_stop_at_resp_timer();
+
+ if (!bta_hf_client_cb.scb.svc_conn)
+ {
+ bta_hf_client_slc_seq(FALSE);
+ return;
+ }
+
+ switch(bta_hf_client_cb.scb.at_cb.current_cmd)
+ {
+ case BTA_HF_CLIENT_AT_BIA:
+ case BTA_HF_CLIENT_AT_BCC:
+ break;
+ case BTA_HF_CLIENT_AT_BCS:
+ bta_hf_client_start_at_hold_timer();
+ bta_hf_client_cb.scb.at_cb.current_cmd = BTA_HF_CLIENT_AT_NONE;
+ return;
+ case BTA_HF_CLIENT_AT_CLIP: //last cmd is post slc seq
+ if (bta_hf_client_cb.scb.send_at_reply == FALSE)
+ {
+ bta_hf_client_cb.scb.send_at_reply = TRUE;
+ }
+ break;
+ case BTA_HF_CLIENT_AT_NONE:
+ bta_hf_client_stop_at_hold_timer();
+ break;
+ default:
+ if (bta_hf_client_cb.scb.send_at_reply)
+ {
+ bta_hf_client_at_result(BTA_HF_CLIENT_AT_RESULT_OK, 0);
+ }
+ break;
+ }
+
+ bta_hf_client_cb.scb.at_cb.current_cmd = BTA_HF_CLIENT_AT_NONE;
+
+ bta_hf_client_send_queued_at();
+}
+
+static void bta_hf_client_handle_error(tBTA_HF_CLIENT_AT_RESULT_TYPE type, UINT16 cme)
+{
+ APPL_TRACE_DEBUG3("%s %u %u", __FUNCTION__, type, cme);
+
+ bta_hf_client_stop_at_resp_timer();
+
+ if (!bta_hf_client_cb.scb.svc_conn)
+ {
+ bta_hf_client_slc_seq(TRUE);
+ return;
+ }
+
+ switch(bta_hf_client_cb.scb.at_cb.current_cmd)
+ {
+ case BTA_HF_CLIENT_AT_BIA:
+ break;
+ case BTA_HF_CLIENT_AT_BCC:
+ case BTA_HF_CLIENT_AT_BCS:
+ bta_hf_client_cback_sco(BTA_HF_CLIENT_AUDIO_CLOSE_EVT);
+ break;
+ case BTA_HF_CLIENT_AT_CLIP: //last cmd is post slc seq
+ if (bta_hf_client_cb.scb.send_at_reply == FALSE)
+ {
+ bta_hf_client_cb.scb.send_at_reply = TRUE;
+ }
+ break;
+ default:
+ if (bta_hf_client_cb.scb.send_at_reply)
+ {
+ bta_hf_client_at_result(type, cme);
+ }
+ break;
+ }
+
+ bta_hf_client_cb.scb.at_cb.current_cmd = BTA_HF_CLIENT_AT_NONE;
+
+ bta_hf_client_send_queued_at();
+}
+
+static void bta_hf_client_handle_ring()
+{
+ APPL_TRACE_DEBUG1("%s", __FUNCTION__);
+ bta_hf_client_evt_val(BTA_HF_CLIENT_RING_INDICATION,0);
+}
+
+static void bta_hf_client_handle_brsf(UINT32 value)
+{
+ APPL_TRACE_DEBUG2("%s 0x%x", __FUNCTION__, value);
+ bta_hf_client_cb.scb.peer_features = value;
+}
+
+/* handles a single indicator descriptor - registers it for value changing events */
+static void bta_hf_client_handle_cind_list_item(char *name, UINT32 min, UINT32 max, UINT32 index)
+{
+
+ UINT8 i = 0;
+
+ APPL_TRACE_DEBUG5("%s %lu.%s <%lu:%lu>", __FUNCTION__, index, name, min, max);
+
+ /* look for a matching indicator on list of supported ones */
+ for(i = 0; i < BTA_HF_CLIENT_AT_SUPPORTED_INDICATOR_COUNT; i++)
+ {
+ if (strcmp(name,BTA_HF_CLIENT_INDICATOR_SERVICE) == 0)
+ {
+ service_index = index;
+ }
+ /* look for a match - search one sign further than indicators name to check for string end */
+ /* It will distinguish 'callheld' which could be matched by strncmp as 'call'. */
+ if (strncmp(name, bta_hf_client_indicators[i].name, bta_hf_client_indicators[i].namelen) != 0)
+ continue;
+
+ /* index - enumerates value position in the incoming sequence */
+ /* if name matches one of the known indicators, add its incoming position */
+ /* to lookup table for easy value->indicator matching later, when only values come */
+ bta_hf_client_cb.scb.at_cb.indicator_lookup[index] = i;
+
+ return;
+ }
+}
+
+static void bta_hf_client_handle_cind_value(UINT32 index, UINT32 value)
+{
+ APPL_TRACE_DEBUG3("%s index: %u value: %u", __FUNCTION__, index, value);
+
+ if (index >= BTA_HF_CLIENT_AT_INDICATOR_COUNT)
+ {
+ return;
+ }
+
+ if (service_index == index)
+ {
+ if (value == 0)
+ {
+ service_availability = FALSE;
+ }
+ else
+ {
+ service_availability = TRUE;
+ }
+ }
+ if (bta_hf_client_cb.scb.at_cb.indicator_lookup[index] == -1)
+ {
+ return;
+ }
+
+ /* get the real array index from lookup table */
+ index = bta_hf_client_cb.scb.at_cb.indicator_lookup[index];
+
+ /* Ignore out of range values */
+ if(value > bta_hf_client_indicators[index].max ||
+ value < bta_hf_client_indicators[index].min)
+ {
+ return;
+ }
+
+ /* tBTA_HF_CLIENT_IND_TYPE match index in bta_hf_client_indicators */
+ bta_hf_client_ind(index, value);
+}
+
+static void bta_hf_client_handle_chld(UINT32 mask)
+{
+ APPL_TRACE_DEBUG2("%s 0x%x", __FUNCTION__, mask);
+
+ bta_hf_client_cb.scb.chld_features |= mask;
+}
+
+static void bta_hf_client_handle_ciev(UINT32 index, UINT32 value)
+{
+ INT8 realind = -1;
+
+ APPL_TRACE_DEBUG3("%s index: %u value: %u", __FUNCTION__, index, value);
+
+ if(index == 0 || index >= BTA_HF_CLIENT_AT_INDICATOR_COUNT)
+ {
+ return;
+ }
+
+ realind = bta_hf_client_cb.scb.at_cb.indicator_lookup[index - 1];
+
+ if(realind >= 0 && realind < BTA_HF_CLIENT_AT_SUPPORTED_INDICATOR_COUNT)
+ {
+ /* get the real in-array index from lookup table by index it comes at */
+ /* if there is no bug it should automatically be correctly calculated */
+ if(value > bta_hf_client_indicators[realind].max || value < bta_hf_client_indicators[realind].min)
+ {
+ return;
+ }
+
+ /* tBTA_HF_CLIENT_IND_TYPE match index in bta_hf_client_indicators */
+ bta_hf_client_ind(realind, value);
+ }
+}
+
+static void bta_hf_client_handle_bcs(UINT32 codec)
+{
+ APPL_TRACE_DEBUG2("%s %u", __FUNCTION__, codec);
+
+ if (codec == BTM_SCO_CODEC_CVSD ||
+ (codec == BTM_SCO_CODEC_MSBC && bta_hf_client_cb.msbc_enabled == TRUE))
+ {
+ bta_hf_client_cb.scb.negotiated_codec = codec;
+ bta_hf_client_send_at_bcs(codec);
+ }
+ else
+ {
+ bta_hf_client_cb.scb.negotiated_codec = BTM_SCO_CODEC_CVSD;
+ bta_hf_client_send_at_bac();
+ }
+}
+
+static void bta_hf_client_handle_bsir(UINT32 provided)
+{
+ APPL_TRACE_DEBUG2("%s %u", __FUNCTION__, provided);
+
+ bta_hf_client_evt_val(BTA_HF_CLIENT_BSIR_EVT, provided);
+}
+
+static void bta_hf_client_handle_cmeerror(UINT32 code)
+{
+ bta_hf_client_handle_error(BTA_HF_CLIENT_AT_RESULT_CME, code);
+}
+
+static void bta_hf_client_handle_vgm(UINT32 value)
+{
+ APPL_TRACE_DEBUG2("%s %lu", __FUNCTION__, value);
+
+ if(value <= BTA_HF_CLIENT_VGM_MAX)
+ {
+ bta_hf_client_evt_val(BTA_HF_CLIENT_MIC_EVT, value);
+ }
+}
+
+static void bta_hf_client_handle_vgs(UINT32 value)
+{
+ APPL_TRACE_DEBUG2("%s %lu", __FUNCTION__, value);
+
+ if(value <= BTA_HF_CLIENT_VGS_MAX)
+ {
+ bta_hf_client_evt_val(BTA_HF_CLIENT_SPK_EVT, value);
+ }
+}
+
+static void bta_hf_client_handle_bvra(UINT32 value)
+{
+ APPL_TRACE_DEBUG2("%s %lu", __FUNCTION__, value);
+
+ if (value > 1)
+ {
+ return;
+ }
+
+ bta_hf_client_evt_val(BTA_HF_CLIENT_VOICE_REC_EVT, value);
+}
+
+static void bta_hf_client_handle_clip(char *numstr, UINT32 type)
+{
+ APPL_TRACE_DEBUG3("%s %u %s", __FUNCTION__, type, numstr);
+
+ bta_hf_client_clip(numstr);
+}
+
+static void bta_hf_client_handle_ccwa(char *numstr, UINT32 type)
+{
+ APPL_TRACE_DEBUG3("%s %u %s", __FUNCTION__, type, numstr);
+
+ bta_hf_client_ccwa(numstr);
+}
+
+static void bta_hf_client_handle_cops(char *opstr, UINT32 mode)
+{
+ APPL_TRACE_DEBUG3("%s %u %s", __FUNCTION__, mode, opstr);
+
+ bta_hf_client_operator_name(opstr);
+}
+
+static void bta_hf_client_handle_binp(char *numstr)
+{
+ APPL_TRACE_DEBUG2("%s %s", __FUNCTION__, numstr);
+
+ bta_hf_client_binp(numstr);
+}
+
+static void bta_hf_client_handle_clcc(UINT16 idx, UINT16 dir, UINT16 status, UINT16 mode, UINT16 mpty, char *numstr, UINT16 type)
+{
+ APPL_TRACE_DEBUG6("%s idx: %u dir: %u status: %u mode: %u mpty: %u",
+ __FUNCTION__, idx, dir, status, mode, mpty);
+
+ if (numstr)
+ {
+ APPL_TRACE_DEBUG3("%s number: %s type: %u", __FUNCTION__, numstr, type);
+ }
+
+ bta_hf_client_clcc(idx, dir, status, mpty, numstr);
+}
+
+static void bta_hf_client_handle_cnum( char *numstr, UINT16 type, UINT16 service)
+{
+ APPL_TRACE_DEBUG4("%s number: %s type: %u service: %u", __FUNCTION__, numstr, type, service);
+
+ /* TODO: should number be modified according to type? */
+ bta_hf_client_cnum(numstr, service);
+}
+
+static void bta_hf_client_handle_btrh( UINT16 code)
+{
+ APPL_TRACE_DEBUG2("%s %lu", __FUNCTION__, code);
+
+ bta_hf_client_evt_val(BTA_HF_CLIENT_BTRH_EVT, code);
+}
+
+/******************************************************************************
+**
+** COMMON AT EVENTS PARSING FUNCTIONS
+**
+*******************************************************************************/
+
+/* Check if prefix match and skip spaces if any */
+#define AT_CHECK_EVENT(buf, event) \
+ if (strncmp("\r\n"event, buf,sizeof("\r\n"event) - 1) != 0) return buf; \
+ buf += sizeof("\r\n"event) - 1; \
+ while (*buf == ' ') buf++;
+
+/* check for <cr><lf> and forward buffer if match */
+#define AT_CHECK_RN(buf) \
+ if (strncmp("\r\n", buf, sizeof("\r\n") - 1) != 0) { \
+ APPL_TRACE_DEBUG1("%s missing end <cr><lf>", __FUNCTION__); \
+ return NULL;} \
+ buf += sizeof("\r\n") - 1;
+
+/* skip rest of AT string up to <cr> */
+#define AT_SKIP_REST(buf) while(*buf != '\r') buf++;
+
+static char *bta_hf_client_parse_ok(char *buffer)
+{
+ AT_CHECK_EVENT(buffer, "OK");
+ AT_CHECK_RN(buffer);
+
+ bta_hf_client_handle_ok();
+
+ return buffer;
+}
+
+static char *bta_hf_client_parse_error(char *buffer)
+{
+ AT_CHECK_EVENT(buffer, "ERROR");
+ AT_CHECK_RN(buffer);
+
+ bta_hf_client_handle_error(BTA_HF_CLIENT_AT_RESULT_ERROR, 0);
+
+ return buffer;
+}
+
+static char *bta_hf_client_parse_ring(char *buffer)
+{
+ AT_CHECK_EVENT(buffer, "RING");
+ AT_CHECK_RN(buffer);
+
+ bta_hf_client_handle_ring();
+
+ return buffer;
+}
+
+/* generic uint32 parser */
+static char *bta_hf_client_parse_uint32(char *buffer, void (*handler_callback)(UINT32))
+{
+ UINT32 value;
+ int res;
+ int offset;
+
+ res = sscanf(buffer, "%u%n", &value, &offset);
+ if (res < 1)
+ {
+ return NULL;
+ }
+
+ buffer += offset;
+
+ AT_CHECK_RN(buffer);
+
+ handler_callback(value);
+ return buffer;
+}
+
+static char *bta_hf_client_parse_brsf(char *buffer)
+{
+ AT_CHECK_EVENT(buffer, "+BRSF:");
+
+ return bta_hf_client_parse_uint32(buffer, bta_hf_client_handle_brsf);
+}
+
+static char *bta_hf_client_parse_cind_values(char *buffer)
+{
+ /* value and its position */
+ UINT16 index = 0;
+ UINT32 value = 0;
+
+ int offset;
+ int res;
+
+ while((res = sscanf(buffer, "%u%n", &value, &offset)) > 0)
+ {
+ /* decides if its valid index and value, if yes stores it */
+ bta_hf_client_handle_cind_value(index, value);
+
+ buffer += offset;
+
+ /* check if more values are present */
+ if (*buffer != ',')
+ {
+ break;
+ }
+
+ index++;
+ buffer++;
+ }
+
+ if (res > 0)
+ {
+ AT_CHECK_RN(buffer);
+ return buffer;
+ }
+
+ return NULL;
+}
+
+static char *bta_hf_client_parse_cind_list(char *buffer)
+{
+ int offset;
+ char name[129];
+ UINT32 min, max;
+ UINT32 index = 0;
+ int res;
+
+ while ((res = sscanf(buffer, "(\"%128[^\"]\",(%u%*[-,]%u))%n", name, &min, &max, &offset)) > 2)
+ {
+ bta_hf_client_handle_cind_list_item(name, min, max, index);
+ buffer += offset;
+ index++;
+
+ if (*buffer != ',')
+ {
+ break;
+ }
+
+ buffer++;
+ }
+
+ if (res > 2)
+ {
+ AT_CHECK_RN(buffer);
+ return buffer;
+ }
+
+ return NULL;
+}
+
+static char *bta_hf_client_parse_cind(char *buffer)
+{
+ AT_CHECK_EVENT(buffer, "+CIND:");
+
+ if(*buffer == '(')
+ return bta_hf_client_parse_cind_list(buffer);
+
+ return bta_hf_client_parse_cind_values(buffer);
+}
+
+static char *bta_hf_client_parse_chld(char *buffer)
+{
+ AT_CHECK_EVENT(buffer, "+CHLD:");
+
+ if (*buffer != '(')
+ {
+ return NULL;
+ }
+
+ buffer++;
+
+ while(*buffer != '\0')
+ {
+ if(strncmp("0",buffer, 1) == 0)
+ {
+ bta_hf_client_handle_chld(BTA_HF_CLIENT_CHLD_REL);
+ buffer++;
+ }
+ else if(strncmp("1x",buffer, 2) == 0)
+ {
+ bta_hf_client_handle_chld(BTA_HF_CLIENT_CHLD_REL_X);
+ buffer += 2;
+ }
+ else if(strncmp("1",buffer, 1) == 0)
+ {
+ bta_hf_client_handle_chld(BTA_HF_CLIENT_CHLD_REL_ACC);
+ buffer++;
+ }
+ else if(strncmp("2x",buffer, 2) == 0)
+ {
+ bta_hf_client_handle_chld(BTA_HF_CLIENT_CHLD_PRIV_X);
+ buffer += 2;
+ }
+ else if(strncmp("2",buffer, 1) == 0)
+ {
+ bta_hf_client_handle_chld(BTA_HF_CLIENT_CHLD_HOLD_ACC);
+ buffer++;
+ }
+ else if(strncmp("3",buffer, 1) == 0)
+ {
+ bta_hf_client_handle_chld(BTA_HF_CLIENT_CHLD_MERGE);
+ buffer++;
+ }
+ else if(strncmp("4",buffer, 1) == 0)
+ {
+ bta_hf_client_handle_chld(BTA_HF_CLIENT_CHLD_MERGE_DETACH);
+ buffer++;
+ }
+ else
+ {
+ return NULL;
+ }
+
+ if (*buffer == ',')
+ {
+ buffer++;
+ continue;
+ }
+
+ if (*buffer == ')')
+ {
+ buffer++;
+ break;
+ }
+
+ return NULL;
+ }
+
+ AT_CHECK_RN(buffer);
+
+ return buffer;
+}
+
+static char *bta_hf_client_parse_ciev(char *buffer)
+{
+ UINT32 index, value;
+ int res;
+ int offset;
+
+ AT_CHECK_EVENT(buffer, "+CIEV:");
+
+ res = sscanf(buffer, "%u,%u%n", &index, &value, &offset);
+ if(res < 2)
+ {
+ return NULL;
+ }
+
+ buffer += offset;
+
+ AT_CHECK_RN(buffer);
+
+ bta_hf_client_handle_ciev(index, value);
+ return buffer;
+}
+
+static char *bta_hf_client_parse_bcs(char *buffer)
+{
+ AT_CHECK_EVENT(buffer, "+BCS:");
+
+ return bta_hf_client_parse_uint32(buffer, bta_hf_client_handle_bcs);
+}
+
+static char *bta_hf_client_parse_bsir(char *buffer)
+{
+ AT_CHECK_EVENT(buffer, "+BSIR:");
+
+ return bta_hf_client_parse_uint32(buffer, bta_hf_client_handle_bsir);
+}
+
+static char *bta_hf_client_parse_cmeerror(char *buffer)
+{
+ AT_CHECK_EVENT(buffer, "+CME ERROR:");
+
+ return bta_hf_client_parse_uint32(buffer, bta_hf_client_handle_cmeerror);
+}
+
+static char *bta_hf_client_parse_vgm(char *buffer)
+{
+ AT_CHECK_EVENT(buffer, "+VGM:");
+
+ return bta_hf_client_parse_uint32(buffer, bta_hf_client_handle_vgm);
+}
+
+static char *bta_hf_client_parse_vgme(char *buffer)
+{
+ AT_CHECK_EVENT(buffer, "+VGM=");
+
+ return bta_hf_client_parse_uint32(buffer, bta_hf_client_handle_vgm);
+}
+
+static char *bta_hf_client_parse_vgs(char *buffer)
+{
+ AT_CHECK_EVENT(buffer, "+VGS:");
+
+ return bta_hf_client_parse_uint32(buffer, bta_hf_client_handle_vgs);
+}
+
+static char *bta_hf_client_parse_vgse(char *buffer)
+{
+ AT_CHECK_EVENT(buffer, "+VGS=");
+
+ return bta_hf_client_parse_uint32(buffer, bta_hf_client_handle_vgs);
+}
+
+static char *bta_hf_client_parse_bvra(char *buffer)
+{
+ AT_CHECK_EVENT(buffer, "+BVRA:");
+
+ return bta_hf_client_parse_uint32(buffer, bta_hf_client_handle_bvra);
+}
+
+static char *bta_hf_client_parse_clip(char *buffer)
+{
+ /* spec forces 32 chars, plus \0 here */
+ char number[33];
+ UINT32 type = 0;
+ int res;
+ int offset;
+
+ AT_CHECK_EVENT(buffer, "+CLIP:");
+
+ /* there might be something more after %lu but HFP doesn't care */
+ res = sscanf(buffer, "\"%32[^\"]\",%u%n", number, &type, &offset);
+ if(res < 2)
+ {
+ return NULL;
+ }
+
+ buffer += offset;
+
+ AT_SKIP_REST(buffer);
+
+ AT_CHECK_RN(buffer);
+
+ bta_hf_client_handle_clip(number, type);
+ return buffer;
+}
+
+/* in HFP context there is no difference between ccwa and clip */
+static char *bta_hf_client_parse_ccwa(char *buffer)
+{
+ /* ac to spec 32 chars max, plus \0 here */
+ char number[33];
+ UINT32 type = 0;
+ int res ;
+ int offset;
+
+ AT_CHECK_EVENT(buffer, "+CCWA:");
+
+ /* there might be something more after %lu but HFP doesn't care */
+ res = sscanf(buffer, "\"%32[^\"]\",%u%n", number, &type, &offset);
+ if(res < 2)
+ {
+ return NULL;
+ }
+
+ buffer += offset;
+
+ AT_SKIP_REST(buffer);
+
+ AT_CHECK_RN(buffer);
+
+ bta_hf_client_handle_ccwa(number, type);
+ return buffer;
+}
+
+static char *bta_hf_client_parse_cops(char *buffer)
+{
+ UINT8 mode;
+ /* spec forces 16 chars max, plus \0 here */
+ char opstr[17];
+ int res;
+ int offset;
+
+ AT_CHECK_EVENT(buffer, "+COPS:");
+
+ /* TODO: Not sure if operator string actually can contain escaped " char inside */
+ res = sscanf(buffer, "%hhi,0,\"%16[^\"]\"%n", &mode, opstr, &offset);
+ if(res < 2)
+ {
+ return NULL;
+ }
+
+ buffer += offset;
+
+ AT_SKIP_REST(buffer);
+
+ AT_CHECK_RN(buffer);
+
+ bta_hf_client_handle_cops(opstr, mode);
+ return buffer;
+}
+
+static char *bta_hf_client_parse_binp(char *buffer)
+{
+ /* HFP only supports phone number as BINP data */
+ /* phone number is 32 chars plus one for \0*/
+ char numstr[33];
+ int res;
+ int offset;
+
+ AT_CHECK_EVENT(buffer, "+BINP:");
+
+ res = sscanf(buffer, "\"%32[^\"]\"\r\n%n", numstr, &offset);
+ if(res < 1)
+ {
+ return NULL;
+ }
+
+ buffer += offset;
+
+ /* some phones might sent type as well, just skip it */
+ AT_SKIP_REST(buffer);
+
+ AT_CHECK_RN(buffer);
+
+ bta_hf_client_handle_binp(numstr);
+ return buffer;
+}
+
+static char *bta_hf_client_parse_clcc(char *buffer)
+{
+ UINT16 idx, dir, status, mode, mpty;
+ char numstr[33]; /* spec forces 32 chars, plus one for \0*/
+ UINT16 type;
+ int res;
+ int offset;
+
+ AT_CHECK_EVENT(buffer, "+CLCC:");
+
+ res = sscanf(buffer, "%hu,%hu,%hu,%hu,%hu%n",
+ &idx, &dir, &status, &mode, &mpty, &offset);
+ if (res < 5)
+ {
+ return NULL;
+ }
+
+ buffer += offset;
+
+ /* check optional part */
+ if (*buffer == ',')
+ {
+ int res2;
+
+ res2 = sscanf(buffer, ",\"%32[^\"]\",%hu%n", numstr, &type, &offset);
+ if (res2 < 0)
+ {
+ return NULL;
+ }
+
+ if (res2 == 0)
+ {
+ res2 = sscanf(buffer, ",\"\",%hu%n", &type, &offset);
+ if (res < 0)
+ {
+ return NULL;
+ }
+
+ /* numstr is not matched in second attempt, correct this */
+ res2++;
+ numstr[0] = '\0';
+ }
+
+ if (res2 < 2)
+ {
+ return NULL;
+ }
+
+ res += res2;
+ buffer += offset;
+ }
+
+ AT_CHECK_RN(buffer);
+
+ if(res > 6)
+ {
+ /* we also have last two optional parameters */
+ bta_hf_client_handle_clcc(idx, dir, status, mode, mpty, numstr, type);
+ }
+ else
+ {
+ /* we didn't get the last two parameters */
+ bta_hf_client_handle_clcc(idx, dir, status, mode, mpty, NULL, 0);
+ }
+
+ return buffer;
+}
+
+static char *bta_hf_client_parse_cnum(char *buffer)
+{
+ char numstr[33]; /* spec forces 32 chars, plus one for \0*/
+ UINT16 type;
+ UINT16 service = 0; /* 0 in case this optional parameter is not being sent */
+ int res;
+ int offset;
+
+ AT_CHECK_EVENT(buffer, "+CNUM:");
+
+ res = sscanf(buffer, ",\"%32[^\"]\",%hu,,%hu%n", numstr, &type, &service, &offset);
+ if(res < 0)
+ {
+ return NULL;
+ }
+
+ if (res == 0)
+ {
+ res = sscanf(buffer, ",\"\",%hu,,%hu%n", &type, &service, &offset);
+ if (res < 0)
+ {
+ return NULL;
+ }
+
+ /* numstr is not matched in second attempt, correct this */
+ res++;
+ numstr[0] = '\0';
+ }
+
+ if (res < 3)
+ {
+ return NULL;
+ }
+
+ buffer += offset;
+
+ AT_CHECK_RN(buffer);
+
+ /* service is optional */
+ if(res == 2)
+ {
+ bta_hf_client_handle_cnum(numstr, type, service);
+ return buffer;
+ }
+
+ if (service != 4 && service != 5)
+ {
+ return NULL;
+ }
+
+ bta_hf_client_handle_cnum(numstr, type, service);
+ return buffer;
+}
+
+static char *bta_hf_client_parse_btrh(char *buffer)
+{
+ UINT16 code = 0;
+ int res;
+ int offset;
+
+ AT_CHECK_EVENT(buffer, "+BTRH:");
+
+ res = sscanf(buffer, "%hu%n", &code, &offset);
+ if(res < 1)
+ {
+ return NULL;
+ }
+
+ buffer += offset;
+
+ AT_CHECK_RN(buffer);
+
+ bta_hf_client_handle_btrh(code);
+ return buffer;
+}
+
+static char *bta_hf_client_parse_busy(char *buffer)
+{
+ AT_CHECK_EVENT(buffer, "BUSY");
+ AT_CHECK_RN(buffer);
+
+ bta_hf_client_handle_error(BTA_HF_CLIENT_AT_RESULT_BUSY, 0);
+
+ return buffer;
+}
+
+static char *bta_hf_client_parse_delayed(char *buffer)
+{
+ AT_CHECK_EVENT(buffer, "DELAYED");
+ AT_CHECK_RN(buffer);
+
+ bta_hf_client_handle_error(BTA_HF_CLIENT_AT_RESULT_DELAY, 0);
+
+ return buffer;
+}
+
+static char *bta_hf_client_parse_no_carrier(char *buffer)
+{
+ AT_CHECK_EVENT(buffer, "NO CARRIER");
+ AT_CHECK_RN(buffer);
+
+ bta_hf_client_handle_error(BTA_HF_CLIENT_AT_RESULT_NO_CARRIER, 0);
+
+ return buffer;
+}
+
+static char *bta_hf_client_parse_no_answer(char *buffer)
+{
+ AT_CHECK_EVENT(buffer, "NO ANSWER");
+ AT_CHECK_RN(buffer);
+
+ bta_hf_client_handle_error(BTA_HF_CLIENT_AT_RESULT_NO_ANSWER, 0);
+
+ return buffer;
+}
+
+static char *bta_hf_client_parse_blacklisted(char *buffer)
+{
+ AT_CHECK_EVENT(buffer, "BLACKLISTED");
+ AT_CHECK_RN(buffer);
+
+ bta_hf_client_handle_error(BTA_HF_CLIENT_AT_RESULT_BLACKLISTED, 0);
+
+ return buffer;
+}
+
+static char *bta_hf_client_skip_unknown(char *buffer)
+{
+ char *start;
+ char *tmp;
+
+ tmp = strstr(buffer, "\r\n");
+ if (tmp == NULL)
+ {
+ return NULL;
+ }
+
+ buffer += 2;
+ start = buffer;
+
+ tmp = strstr(buffer, "\r\n");
+ if (tmp == NULL)
+ {
+ return NULL;
+ }
+
+ buffer = tmp + 2;
+
+ APPL_TRACE_DEBUG3("%s %.*s", __FUNCTION__, buffer - start - 2, start);
+
+ return buffer;
+}
+
+
+/******************************************************************************
+** SUPPORTED EVENT MESSAGES
+*******************************************************************************/
+
+/* returned values are as follow:
+ * != NULL && != buf : match and parsed ok
+ * == NULL : match but parse failed
+ * != NULL && == buf : no match
+ */
+typedef char* (*tBTA_HF_CLIENT_PARSER_CALLBACK)(char*);
+
+static const tBTA_HF_CLIENT_PARSER_CALLBACK bta_hf_client_parser_cb[] =
+{
+ bta_hf_client_parse_ok,
+ bta_hf_client_parse_error,
+ bta_hf_client_parse_ring,
+ bta_hf_client_parse_brsf,
+ bta_hf_client_parse_cind,
+ bta_hf_client_parse_ciev,
+ bta_hf_client_parse_chld,
+ bta_hf_client_parse_bcs,
+ bta_hf_client_parse_bsir,
+ bta_hf_client_parse_cmeerror,
+ bta_hf_client_parse_vgm,
+ bta_hf_client_parse_vgme,
+ bta_hf_client_parse_vgs,
+ bta_hf_client_parse_vgse,
+ bta_hf_client_parse_bvra,
+ bta_hf_client_parse_clip,
+ bta_hf_client_parse_ccwa,
+ bta_hf_client_parse_cops,
+ bta_hf_client_parse_binp,
+ bta_hf_client_parse_clcc,
+ bta_hf_client_parse_cnum,
+ bta_hf_client_parse_btrh,
+ bta_hf_client_parse_busy,
+ bta_hf_client_parse_delayed,
+ bta_hf_client_parse_no_carrier,
+ bta_hf_client_parse_no_answer,
+ bta_hf_client_parse_blacklisted,
+ bta_hf_client_skip_unknown
+};
+
+/* calculate supported event list length */
+static const UINT16 bta_hf_client_psraser_cb_count =
+ sizeof(bta_hf_client_parser_cb) / sizeof(bta_hf_client_parser_cb[0]);
+
+#ifdef BTA_HF_CLIENT_AT_DUMP
+static void bta_hf_client_dump_at(void)
+{
+ char dump[(4 * BTA_HF_CLIENT_AT_PARSER_MAX_LEN) + 1];
+ char *p1, *p2;
+
+ p1 = bta_hf_client_cb.scb.at_cb.buf;
+ p2 = dump;
+
+ while (*p1 != '\0')
+ {
+ if (*p1 == '\r')
+ {
+ strlcpy(p2, "<cr>", 4);
+ p2 += 4;
+ }
+ else if (*p1 == '\n')
+ {
+ strlcpy(p2, "<lf>", 4);
+ p2 += 4;
+ }
+ else
+ {
+ *p2 = *p1;
+ p2++;
+ }
+ p1++;
+ }
+
+ *p2 = '\0';
+
+ APPL_TRACE_DEBUG2("%s %s", __FUNCTION__, dump);
+}
+#endif
+
+static void bta_hf_client_at_parse_start(void)
+{
+ char *buf = bta_hf_client_cb.scb.at_cb.buf;
+
+ APPL_TRACE_DEBUG1("%s", __FUNCTION__);
+
+#ifdef BTA_HF_CLIENT_AT_DUMP
+ bta_hf_client_dump_at();
+#endif
+
+ while(*buf != '\0')
+ {
+ int i;
+ char *tmp = NULL;
+
+ for(i = 0; i < bta_hf_client_psraser_cb_count; i++)
+ {
+ tmp = bta_hf_client_parser_cb[i](buf);
+ if (tmp == NULL)
+ {
+ APPL_TRACE_ERROR0("HFPCient: AT event/reply parsing failed, skipping");
+ tmp = bta_hf_client_skip_unknown(buf);
+ break;
+ }
+
+ /* matched or unknown skipped, if unknown failed tmp is NULL so
+ this is also handled */
+ if (tmp != buf)
+ {
+ buf = tmp;
+ break;
+ }
+ }
+
+ /* could not skip unknown (received garbage?)... disconnect */
+ if (tmp == NULL)
+ {
+ APPL_TRACE_ERROR0("HFPCient: could not skip unknown AT event, disconnecting");
+ bta_hf_client_at_reset();
+ bta_hf_client_sm_execute(BTA_HF_CLIENT_API_CLOSE_EVT, NULL);
+ return;
+ }
+
+ buf = tmp;
+ }
+}
+
+static BOOLEAN bta_hf_client_check_at_complete(void)
+{
+ BOOLEAN ret = FALSE;
+ tBTA_HF_CLIENT_AT_CB *at_cb = &bta_hf_client_cb.scb.at_cb;
+
+ if (at_cb->offset >= BTA_HF_CLIENT_AT_EVENT_MIN_LEN)
+ {
+ if (at_cb->buf[at_cb->offset - 2] == '\r' && at_cb->buf[at_cb->offset - 1] == '\n')
+ {
+ ret = TRUE;
+ }
+ }
+
+ APPL_TRACE_DEBUG2("%s %d", __FUNCTION__, ret);
+
+ return ret;
+}
+
+static void bta_hf_client_at_clear_buf(void)
+{
+ memset(bta_hf_client_cb.scb.at_cb.buf, 0, sizeof(bta_hf_client_cb.scb.at_cb.buf));
+ bta_hf_client_cb.scb.at_cb.offset = 0;
+}
+
+/******************************************************************************
+**
+** MAIN PARSING FUNCTION
+**
+**
+*******************************************************************************/
+void bta_hf_client_at_parse(char *buf, unsigned int len)
+{
+ APPL_TRACE_DEBUG3("%s offset: %u len: %u", __FUNCTION__, bta_hf_client_cb.scb.at_cb.offset, len);
+
+ if (len + bta_hf_client_cb.scb.at_cb.offset > BTA_HF_CLIENT_AT_PARSER_MAX_LEN)
+ {
+ char tmp_buff[BTA_HF_CLIENT_AT_PARSER_MAX_LEN];
+ unsigned int tmp = bta_hf_client_cb.scb.at_cb.offset;
+ unsigned int space_left = BTA_HF_CLIENT_AT_PARSER_MAX_LEN - bta_hf_client_cb.scb.at_cb.offset;
+
+ APPL_TRACE_DEBUG1("%s overrun, trying to recover", __FUNCTION__);
+
+ /* fill up parser buffer */
+ memcpy(bta_hf_client_cb.scb.at_cb.buf + bta_hf_client_cb.scb.at_cb.offset, buf, space_left);
+ len -= space_left;
+ buf += space_left;
+ bta_hf_client_cb.scb.at_cb.offset += space_left;
+
+ /* find end of last complete command before proceeding */
+ while(bta_hf_client_check_at_complete() == FALSE)
+ {
+ if (bta_hf_client_cb.scb.at_cb.offset == 0) {
+ APPL_TRACE_ERROR0("HFPClient: AT parser buffer overrun, disconnecting");
+
+ bta_hf_client_at_reset();
+ bta_hf_client_sm_execute(BTA_HF_CLIENT_API_CLOSE_EVT, NULL);
+ return;
+ }
+
+ bta_hf_client_cb.scb.at_cb.offset--;
+ }
+
+ /* cut buffer to complete AT event and keep cut data */
+ tmp += space_left - bta_hf_client_cb.scb.at_cb.offset;
+ memcpy(tmp_buff, bta_hf_client_cb.scb.at_cb.buf + bta_hf_client_cb.scb.at_cb.offset, tmp);
+ bta_hf_client_cb.scb.at_cb.buf[bta_hf_client_cb.scb.at_cb.offset] = '\0';
+
+ /* parse */
+ bta_hf_client_at_parse_start();
+ bta_hf_client_at_clear_buf();
+
+ /* recover cut data */
+ memcpy(bta_hf_client_cb.scb.at_cb.buf, tmp_buff, tmp);
+ bta_hf_client_cb.scb.at_cb.offset += tmp;
+ }
+
+ memcpy(bta_hf_client_cb.scb.at_cb.buf + bta_hf_client_cb.scb.at_cb.offset, buf, len);
+ bta_hf_client_cb.scb.at_cb.offset += len;
+
+ /* If last event is complete, parsing can be started */
+ if (bta_hf_client_check_at_complete() == TRUE)
+ {
+ bta_hf_client_at_parse_start();
+ bta_hf_client_at_clear_buf();
+ }
+}
+
+void bta_hf_client_send_at_brsf(void)
+{
+ char buf[BTA_HF_CLIENT_AT_MAX_LEN];
+ int at_len;
+
+ APPL_TRACE_DEBUG1("%s", __FUNCTION__);
+
+ at_len = snprintf(buf, sizeof(buf), "AT+BRSF=%u\r", bta_hf_client_cb.scb.features);
+
+ bta_hf_client_send_at(BTA_HF_CLIENT_AT_BRSF , buf, at_len);
+}
+
+void bta_hf_client_send_at_bac(void)
+{
+ char *buf;
+
+ APPL_TRACE_DEBUG1("%s", __FUNCTION__);
+
+ if (bta_hf_client_cb.msbc_enabled)
+ {
+ buf = "AT+BAC=1,2\r";
+ }
+ else
+ {
+ buf = "AT+BAC=1\r";
+ }
+
+ bta_hf_client_send_at(BTA_HF_CLIENT_AT_BAC, buf, strlen(buf));
+}
+
+void bta_hf_client_send_at_bcs(UINT32 codec)
+{
+ char buf[BTA_HF_CLIENT_AT_MAX_LEN];
+ int at_len;
+
+ APPL_TRACE_DEBUG1("%s", __FUNCTION__);
+
+ at_len = snprintf(buf, sizeof(buf), "AT+BCS=%u\r", codec);
+
+ bta_hf_client_send_at(BTA_HF_CLIENT_AT_BCS, buf, at_len);
+}
+
+void bta_hf_client_send_at_cind(BOOLEAN status)
+{
+ char *buf;
+ tBTA_HF_CLIENT_AT_CMD cmd;
+
+ APPL_TRACE_DEBUG1("%s", __FUNCTION__);
+
+ if (status)
+ {
+ buf = "AT+CIND?\r";
+ cmd = BTA_HF_CLIENT_AT_CIND_STATUS;
+ }
+ else
+ {
+ buf = "AT+CIND=?\r";
+ cmd = BTA_HF_CLIENT_AT_CIND;
+ }
+
+ bta_hf_client_send_at(cmd, buf, strlen(buf));
+}
+
+void bta_hf_client_send_at_cmer(BOOLEAN activate)
+{
+ char *buf;
+
+ APPL_TRACE_DEBUG1("%s", __FUNCTION__);
+
+ if (activate)
+ buf = "AT+CMER=3,0,0,1\r";
+ else
+ buf = "AT+CMER=3,0,0,0\r";
+
+ bta_hf_client_send_at(BTA_HF_CLIENT_AT_CMER, buf, strlen(buf));
+}
+
+void bta_hf_client_send_at_chld(char cmd, UINT32 idx)
+{
+ char buf[BTA_HF_CLIENT_AT_MAX_LEN];
+ int at_len;
+
+ APPL_TRACE_DEBUG1("%s", __FUNCTION__);
+
+ if (idx > 0)
+ at_len = snprintf(buf, sizeof(buf), "AT+CHLD=%c%u\r", cmd, idx);
+ else
+ at_len = snprintf(buf, sizeof(buf), "AT+CHLD=%c\r", cmd);
+
+ bta_hf_client_send_at(BTA_HF_CLIENT_AT_CHLD, buf, at_len);
+}
+
+void bta_hf_client_send_at_clip(BOOLEAN activate)
+{
+ char *buf;
+
+ APPL_TRACE_DEBUG1("%s", __FUNCTION__);
+
+ if (activate)
+ buf = "AT+CLIP=1\r";
+ else
+ buf = "AT+CLIP=0\r";
+
+ bta_hf_client_send_at(BTA_HF_CLIENT_AT_CLIP, buf, strlen(buf));
+}
+
+void bta_hf_client_send_at_ccwa(BOOLEAN activate)
+{
+ char *buf;
+
+ APPL_TRACE_DEBUG1("%s", __FUNCTION__);
+
+ if (activate)
+ buf = "AT+CCWA=1\r";
+ else
+ buf = "AT+CCWA=0\r";
+
+ bta_hf_client_send_at(BTA_HF_CLIENT_AT_CCWA, buf, strlen(buf));
+}
+
+
+void bta_hf_client_send_at_cmee(BOOLEAN activate)
+{
+ char *buf;
+
+ APPL_TRACE_DEBUG1("%s", __FUNCTION__);
+
+ if (activate)
+ buf = "AT+CMEE=1\r";
+ else
+ buf = "AT+CMEE=0\r";
+
+ bta_hf_client_send_at(BTA_HF_CLIENT_AT_CMEE, buf, strlen(buf));
+}
+
+void bta_hf_client_send_at_cops(BOOLEAN query)
+{
+ char *buf;
+
+ APPL_TRACE_DEBUG1("%s", __FUNCTION__);
+
+ if (!service_availability)
+ {
+ APPL_TRACE_DEBUG0("Skip AT+COPS no service");
+ return;
+ }
+ if (query)
+ buf = "AT+COPS?\r";
+ else
+ buf = "AT+COPS=3,0\r";
+
+ bta_hf_client_send_at(BTA_HF_CLIENT_AT_COPS, buf, strlen(buf));
+}
+
+void bta_hf_client_send_at_clcc(void)
+{
+ char *buf;
+
+ APPL_TRACE_DEBUG1("%s", __FUNCTION__);
+
+ buf = "AT+CLCC\r";
+
+ bta_hf_client_send_at(BTA_HF_CLIENT_AT_CLCC, buf, strlen(buf));
+}
+
+void bta_hf_client_send_at_bvra(BOOLEAN enable)
+{
+ char *buf;
+
+ APPL_TRACE_DEBUG1("%s", __FUNCTION__);
+
+ if (enable)
+ buf = "AT+BVRA=1\r";
+ else
+ buf = "AT+BVRA=0\r";
+
+ bta_hf_client_send_at(BTA_HF_CLIENT_AT_BVRA, buf, strlen(buf));
+}
+
+void bta_hf_client_send_at_vgs(UINT32 volume)
+{
+ char buf[BTA_HF_CLIENT_AT_MAX_LEN];
+ int at_len;
+
+ APPL_TRACE_DEBUG1("%s", __FUNCTION__);
+
+ at_len = snprintf(buf, sizeof(buf), "AT+VGS=%u\r", volume);
+
+ bta_hf_client_send_at(BTA_HF_CLIENT_AT_VGS, buf, at_len);
+}
+
+void bta_hf_client_send_at_vgm(UINT32 volume)
+{
+ char buf[BTA_HF_CLIENT_AT_MAX_LEN];
+ int at_len;
+
+ APPL_TRACE_DEBUG1("%s", __FUNCTION__);
+
+ at_len = snprintf(buf, sizeof(buf), "AT+VGM=%u\r", volume);
+
+ bta_hf_client_send_at(BTA_HF_CLIENT_AT_VGM, buf, at_len);
+}
+
+void bta_hf_client_send_at_atd(char *number, UINT32 memory)
+{
+ char buf[BTA_HF_CLIENT_AT_MAX_LEN];
+ int at_len;
+
+ APPL_TRACE_DEBUG1("%s", __FUNCTION__);
+
+ if (number[0] != '\0')
+ {
+ at_len = snprintf(buf, sizeof(buf), "ATD%s;\r", number);
+ }
+ else
+ {
+ at_len = snprintf(buf, sizeof(buf), "ATD>%u;\r", memory);
+ }
+
+ bta_hf_client_send_at(BTA_HF_CLIENT_AT_ATD, buf, at_len);
+}
+
+void bta_hf_client_send_at_bldn(void)
+{
+ char *buf;
+
+ APPL_TRACE_DEBUG1("%s", __FUNCTION__);
+
+ buf = "AT+BLDN\r";
+
+ bta_hf_client_send_at(BTA_HF_CLIENT_AT_BLDN, buf, strlen(buf));
+}
+
+void bta_hf_client_send_at_ata(void)
+{
+ char *buf;
+
+ APPL_TRACE_DEBUG1("%s", __FUNCTION__);
+
+ buf = "ATA\r";
+
+ bta_hf_client_send_at(BTA_HF_CLIENT_AT_ATA, buf, strlen(buf));
+}
+
+void bta_hf_client_send_at_chup(void)
+{
+ char *buf;
+
+ APPL_TRACE_DEBUG1("%s", __FUNCTION__);
+
+ buf = "AT+CHUP\r";
+
+ bta_hf_client_send_at(BTA_HF_CLIENT_AT_CHUP, buf, strlen(buf));
+}
+
+void bta_hf_client_send_at_btrh(BOOLEAN query, UINT32 val)
+{
+ char buf[BTA_HF_CLIENT_AT_MAX_LEN];
+ int at_len;
+
+ APPL_TRACE_DEBUG1("%s", __FUNCTION__);
+
+ if (query == TRUE)
+ {
+ at_len = snprintf(buf, sizeof(buf), "AT+BTRH?\r");
+ }
+ else
+ {
+ at_len = snprintf(buf, sizeof(buf), "AT+BTRH=%u\r", val);
+ }
+
+ bta_hf_client_send_at(BTA_HF_CLIENT_AT_BTRH, buf, at_len);
+}
+
+void bta_hf_client_send_at_vts(char code)
+{
+ char buf[BTA_HF_CLIENT_AT_MAX_LEN];
+ int at_len;
+
+ APPL_TRACE_DEBUG1("%s", __FUNCTION__);
+
+ at_len = snprintf(buf, sizeof(buf), "AT+VTS=%c\r", code);
+
+ bta_hf_client_send_at(BTA_HF_CLIENT_AT_VTS, buf, at_len);
+}
+
+void bta_hf_client_send_at_bcc(void)
+{
+ char *buf;
+
+ APPL_TRACE_DEBUG1("%s", __FUNCTION__);
+
+ buf = "AT+BCC\r";
+
+ bta_hf_client_send_at(BTA_HF_CLIENT_AT_BCC, buf, strlen(buf));
+}
+
+void bta_hf_client_send_at_cnum(void)
+{
+ char *buf;
+
+ APPL_TRACE_DEBUG1("%s", __FUNCTION__);
+
+ if (!service_availability)
+ {
+ APPL_TRACE_DEBUG0("Skip AT+CNUM no Service");
+ return;
+ }
+ buf = "AT+CNUM\r";
+
+ bta_hf_client_send_at(BTA_HF_CLIENT_AT_CNUM, buf, strlen(buf));
+}
+
+void bta_hf_client_send_at_nrec(void)
+{
+ char *buf;
+
+ APPL_TRACE_DEBUG1("%s", __FUNCTION__);
+
+ if (!(bta_hf_client_cb.scb.peer_features & BTA_HF_CLIENT_PEER_FEAT_ECNR))
+ {
+ APPL_TRACE_DEBUG0("Remote does not support NREC.");
+ return;
+ }
+
+ buf = "AT+NREC=0\r";
+
+ bta_hf_client_send_at(BTA_HF_CLIENT_AT_NREC, buf, strlen(buf));
+}
+
+void bta_hf_client_send_at_binp(UINT32 action)
+{
+ char buf[BTA_HF_CLIENT_AT_MAX_LEN];
+ int at_len;
+
+ APPL_TRACE_DEBUG1("%s", __FUNCTION__);
+
+ at_len = snprintf(buf, sizeof(buf), "AT+BINP=%u\r", action);
+
+ bta_hf_client_send_at(BTA_HF_CLIENT_AT_BINP, buf, at_len);
+}
+
+void bta_hf_client_send_at_bia(void)
+{
+ char buf[BTA_HF_CLIENT_AT_MAX_LEN];
+ int at_len;
+ int i;
+
+ APPL_TRACE_DEBUG1("%s", __FUNCTION__);
+ if (bta_hf_client_cb.scb.peer_version < HFP_VERSION_1_6)
+ {
+ APPL_TRACE_DEBUG0("Remote does not Support AT+BIA");
+ return;
+ }
+
+ at_len = snprintf(buf, sizeof(buf), "AT+BIA=");
+
+ for(i = 0; i < BTA_HF_CLIENT_AT_INDICATOR_COUNT; i++)
+ {
+ int sup = bta_hf_client_cb.scb.at_cb.indicator_lookup[i] == -1 ? 0 : 1;
+
+ at_len += snprintf(buf + at_len, sizeof(buf) - at_len, "%u,", sup);
+ }
+
+ buf[at_len - 1] = '\r';
+
+ bta_hf_client_send_at(BTA_HF_CLIENT_AT_BIA, buf, at_len);
+}
+
+void bta_hf_client_at_init(void)
+{
+ memset(&bta_hf_client_cb.scb.at_cb, 0, sizeof(tBTA_HF_CLIENT_AT_CB));
+ bta_hf_client_at_reset();
+}
+
+void bta_hf_client_at_reset(void)
+{
+ int i;
+
+ bta_hf_client_stop_at_resp_timer();
+ bta_hf_client_stop_at_hold_timer();
+
+ bta_hf_client_clear_queued_at();
+
+ bta_hf_client_at_clear_buf();
+
+ for (i = 0; i < BTA_HF_CLIENT_AT_INDICATOR_COUNT; i++)
+ {
+ bta_hf_client_cb.scb.at_cb.indicator_lookup[i] = -1;
+ }
+
+ bta_hf_client_cb.scb.at_cb.current_cmd = BTA_HF_CLIENT_AT_NONE;
+}
diff --git a/bta/hf_client/bta_hf_client_at.h b/bta/hf_client/bta_hf_client_at.h
new file mode 100644
index 0000000..917c605
--- /dev/null
+++ b/bta/hf_client/bta_hf_client_at.h
@@ -0,0 +1,117 @@
+/******************************************************************************
+ *
+ * Copyright (c) 2014 The Android Open Source Project
+ * Copyright (C) 2003-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.
+ *
+ ******************************************************************************/
+
+/*****************************************************************************
+** Data types
+*****************************************************************************/
+
+/* ASCII character string of arguments to the AT command */
+#define BTA_HF_CLIENT_AT_MAX_LEN 512
+
+/* AT command table element */
+typedef struct
+{
+ const char *p_cmd; /* AT command string */
+ UINT8 arg_type; /* allowable argument type syntax */
+ UINT8 fmt; /* whether arg is int or string */
+ UINT8 min; /* minimum value for int arg */
+ INT16 max; /* maximum value for int arg */
+} tBTA_AG_AT_CMD;
+
+/* callback function executed when command is parsed */
+typedef void (tBTA_AG_AT_CMD_CBACK)(void *p_user, UINT16 cmd, UINT8 arg_type,
+ char *p_arg, INT16 int_arg);
+
+/* callback function executed to send "ERROR" result code */
+typedef void (tBTA_AG_AT_ERR_CBACK)(void *p_user, BOOLEAN unknown, char *p_arg);
+
+enum
+{
+ BTA_HF_CLIENT_AT_NONE,
+ BTA_HF_CLIENT_AT_BRSF,
+ BTA_HF_CLIENT_AT_BAC,
+ BTA_HF_CLIENT_AT_CIND,
+ BTA_HF_CLIENT_AT_CIND_STATUS,
+ BTA_HF_CLIENT_AT_CMER,
+ BTA_HF_CLIENT_AT_CHLD,
+ BTA_HF_CLIENT_AT_CMEE,
+ BTA_HF_CLIENT_AT_BIA,
+ BTA_HF_CLIENT_AT_CLIP,
+ BTA_HF_CLIENT_AT_CCWA,
+ BTA_HF_CLIENT_AT_COPS,
+ BTA_HF_CLIENT_AT_CLCC,
+ BTA_HF_CLIENT_AT_BVRA,
+ BTA_HF_CLIENT_AT_VGS,
+ BTA_HF_CLIENT_AT_VGM,
+ BTA_HF_CLIENT_AT_ATD,
+ BTA_HF_CLIENT_AT_BLDN,
+ BTA_HF_CLIENT_AT_ATA,
+ BTA_HF_CLIENT_AT_CHUP,
+ BTA_HF_CLIENT_AT_BTRH,
+ BTA_HF_CLIENT_AT_VTS,
+ BTA_HF_CLIENT_AT_BCC,
+ BTA_HF_CLIENT_AT_BCS,
+ BTA_HF_CLIENT_AT_CNUM,
+ BTA_HF_CLIENT_AT_NREC,
+ BTA_HF_CLIENT_AT_BINP,
+};
+
+typedef UINT8 tBTA_HF_CLIENT_AT_CMD;
+
+/* Maximum combined buffer for received AT events string */
+#define BTA_HF_CLIENT_AT_PARSER_MAX_LEN 4096
+
+/* This structure holds prepared AT command queued for sending */
+struct queued_at_cmd{
+ tBTA_HF_CLIENT_AT_CMD cmd;
+ char buf[BTA_HF_CLIENT_AT_MAX_LEN];
+ UINT16 buf_len;
+ struct queued_at_cmd *next;
+};
+typedef struct queued_at_cmd tBTA_HF_CLIENT_AT_QCMD;
+
+/* Maximum number of indicators */
+#define BTA_HF_CLIENT_AT_INDICATOR_COUNT 20
+
+/* AT command parsing control block */
+typedef struct
+{
+ char buf[BTA_HF_CLIENT_AT_PARSER_MAX_LEN + 1]; /* extra byte to always have \0 at the end */
+ unsigned int offset;
+ tBTA_HF_CLIENT_AT_CMD current_cmd;
+ tBTA_HF_CLIENT_AT_QCMD *queued_cmd;
+
+ TIMER_LIST_ENT resp_timer; /* AT response timer */
+ BOOLEAN resp_timer_on; /* TRUE if AT response timer is active */
+
+ TIMER_LIST_ENT hold_timer; /* AT hold timer */
+ BOOLEAN hold_timer_on; /* TRUE if AT hold timer is active */
+
+ /* CIND: lookup table to store the sequence of incoming indicators and their values
+ so when their values come later, we know which value in sequence match certain indicator */
+ int indicator_lookup[BTA_HF_CLIENT_AT_INDICATOR_COUNT];
+
+} tBTA_HF_CLIENT_AT_CB;
+
+/*****************************************************************************
+** Functions
+*****************************************************************************/
+
+void bta_hf_client_at_init(void);
+void bta_hf_client_at_reset(void);
diff --git a/bta/hf_client/bta_hf_client_cmd.c b/bta/hf_client/bta_hf_client_cmd.c
new file mode 100644
index 0000000..f71fce7
--- /dev/null
+++ b/bta/hf_client/bta_hf_client_cmd.c
@@ -0,0 +1,88 @@
+/******************************************************************************
+ *
+ * Copyright (c) 2014 The Android Open Source Project
+ * Copyright (C) 2003-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.
+ *
+ ******************************************************************************/
+
+#include "bta_hf_client_int.h"
+#include "stdio.h"
+
+void bta_hf_client_send_at_cmd(tBTA_HF_CLIENT_DATA *p_data)
+{
+ tBTA_HF_CLIENT_DATA_VAL *p_val = (tBTA_HF_CLIENT_DATA_VAL *)p_data;
+ char buf[BTA_HF_CLIENT_AT_MAX_LEN];
+
+ switch(p_val->uint8_val)
+ {
+ case BTA_HF_CLIENT_AT_CMD_VTS:
+ bta_hf_client_send_at_vts((char)p_val->uint32_val1);
+ break;
+ case BTA_HF_CLIENT_AT_CMD_BTRH:
+ bta_hf_client_send_at_btrh(FALSE, p_val->uint32_val1);
+ break;
+ case BTA_HF_CLIENT_AT_CMD_CHUP:
+ bta_hf_client_send_at_chup();
+ break;
+ case BTA_HF_CLIENT_AT_CMD_CHLD:
+ /* expects ascii code for command */
+ bta_hf_client_send_at_chld('0' + p_val->uint32_val1, p_val->uint32_val2);
+ break;
+ case BTA_HF_CLIENT_AT_CMD_BCC:
+ bta_hf_client_send_at_bcc();
+ break;
+ case BTA_HF_CLIENT_AT_CMD_CNUM:
+ bta_hf_client_send_at_cnum();
+ break;
+ case BTA_HF_CLIENT_AT_CMD_ATA:
+ bta_hf_client_send_at_ata();
+ break;
+ case BTA_HF_CLIENT_AT_CMD_COPS:
+ bta_hf_client_send_at_cops(TRUE);
+ break;
+ case BTA_HF_CLIENT_AT_CMD_ATD:
+ bta_hf_client_send_at_atd(p_val->str, p_val->uint32_val1);
+ break;
+ case BTA_HF_CLIENT_AT_CMD_VGM:
+ bta_hf_client_send_at_vgm(p_val->uint32_val1);
+ break;
+ case BTA_HF_CLIENT_AT_CMD_VGS:
+ bta_hf_client_send_at_vgs(p_val->uint32_val1);
+ break;
+ case BTA_HF_CLIENT_AT_CMD_BVRA:
+ bta_hf_client_send_at_bvra(p_val->uint32_val1 == 0 ? FALSE : TRUE);
+ break;
+ case BTA_HF_CLIENT_AT_CMD_CLCC:
+ bta_hf_client_send_at_clcc();
+ break;
+ case BTA_HF_CLIENT_AT_CMD_BINP:
+ bta_hf_client_send_at_binp(p_val->uint32_val1);
+ break;
+ case BTA_HF_CLIENT_AT_CMD_BLDN:
+ bta_hf_client_send_at_bldn();
+ break;
+ case BTA_HF_CLIENT_AT_CMD_NREC:
+ bta_hf_client_send_at_nrec();
+ break;
+ default:
+ APPL_TRACE_ERROR0("Default case");
+ snprintf(buf,BTA_HF_CLIENT_AT_MAX_LEN,
+ "Cmd %d 1st arg %u 2nd arg %u string arg %s",
+ p_val->uint8_val, p_val->uint32_val1,
+ p_val->uint32_val2, p_val->str);
+ APPL_TRACE_ERROR1("%s ", buf);
+ break;
+ }
+}
diff --git a/bta/hf_client/bta_hf_client_int.h b/bta/hf_client/bta_hf_client_int.h
new file mode 100644
index 0000000..fbd5082
--- /dev/null
+++ b/bta/hf_client/bta_hf_client_int.h
@@ -0,0 +1,294 @@
+/******************************************************************************
+ *
+ * Copyright (c) 2014 The Android Open Source Project
+ * Copyright (C) 2003-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.
+ *
+ ******************************************************************************/
+
+#include "bta_sys.h"
+#include "bta_api.h"
+#include "bta_hf_client_api.h"
+#include "bta_hf_client_at.h"
+
+/*****************************************************************************
+** Constants
+*****************************************************************************/
+#define HFP_VERSION_1_1 0x0101
+#define HFP_VERSION_1_5 0x0105
+#define HFP_VERSION_1_6 0x0106
+
+/* RFCOMM MTU SIZE */
+#define BTA_HF_CLIENT_MTU 256
+
+/* profile role for connection */
+#define BTA_HF_CLIENT_ACP 0 /* accepted connection */
+#define BTA_HF_CLIENT_INT 1 /* initiating connection */
+
+/* Timer to wait for retry in case of collision */
+#ifndef BTA_HF_CLIENT_COLLISION_TIMER
+#define BTA_HF_CLIENT_COLLISION_TIMER 2411
+#endif
+
+enum
+{
+ /* these events are handled by the state machine */
+ BTA_HF_CLIENT_API_REGISTER_EVT = BTA_SYS_EVT_START(BTA_ID_HS),
+ BTA_HF_CLIENT_API_DEREGISTER_EVT,
+ BTA_HF_CLIENT_API_OPEN_EVT,
+ BTA_HF_CLIENT_API_CLOSE_EVT,
+ BTA_HF_CLIENT_API_AUDIO_OPEN_EVT,
+ BTA_HF_CLIENT_API_AUDIO_CLOSE_EVT,
+ BTA_HF_CLIENT_RFC_OPEN_EVT,
+ BTA_HF_CLIENT_RFC_CLOSE_EVT,
+ BTA_HF_CLIENT_RFC_SRV_CLOSE_EVT,
+ BTA_HF_CLIENT_RFC_DATA_EVT,
+ BTA_HF_CLIENT_DISC_ACP_RES_EVT,
+ BTA_HF_CLIENT_DISC_INT_RES_EVT,
+ BTA_HF_CLIENT_DISC_OK_EVT,
+ BTA_HF_CLIENT_DISC_FAIL_EVT,
+ BTA_HF_CLIENT_SCO_OPEN_EVT,
+ BTA_HF_CLIENT_SCO_CLOSE_EVT,
+ BTA_HF_CLIENT_SEND_AT_CMD_EVT,
+ BTA_HF_CLIENT_MAX_EVT,
+
+ /* these events are handled outside of the state machine */
+ BTA_HF_CLIENT_API_ENABLE_EVT,
+ BTA_HF_CLIENT_API_DISABLE_EVT
+};
+
+/*****************************************************************************
+** Data types
+*****************************************************************************/
+
+/* data type for BTA_HF_CLIENT_API_ENABLE_EVT */
+typedef struct
+{
+ BT_HDR hdr;
+ tBTA_HF_CLIENT_CBACK *p_cback;
+} tBTA_HF_CLIENT_API_ENABLE;
+
+/* data type for BTA_HF_CLIENT_API_REGISTER_EVT */
+typedef struct
+{
+ BT_HDR hdr;
+ tBTA_HF_CLIENT_CBACK *p_cback;
+ tBTA_SEC sec_mask;
+ tBTA_HF_CLIENT_FEAT features;
+ char name[BTA_SERVICE_NAME_LEN+1];
+} tBTA_HF_CLIENT_API_REGISTER;
+
+/* data type for BTA_HF_CLIENT_API_OPEN_EVT */
+typedef struct
+{
+ BT_HDR hdr;
+ BD_ADDR bd_addr;
+ tBTA_SEC sec_mask;
+} tBTA_HF_CLIENT_API_OPEN;
+
+/* data type for BTA_HF_CLIENT_DISC_RESULT_EVT */
+typedef struct
+{
+ BT_HDR hdr;
+ UINT16 status;
+} tBTA_HF_CLIENT_DISC_RESULT;
+
+/* data type for RFCOMM events */
+typedef struct
+{
+ BT_HDR hdr;
+ UINT16 port_handle;
+} tBTA_HF_CLIENT_RFC;
+
+/* generic purpose data type for other events */
+typedef struct
+{
+ BT_HDR hdr;
+ BOOLEAN bool_val;
+ UINT8 uint8_val;
+ UINT32 uint32_val1;
+ UINT32 uint32_val2;
+ char str[BTA_HF_CLIENT_NUMBER_LEN + 1];
+} tBTA_HF_CLIENT_DATA_VAL;
+
+/* union of all event datatypes */
+typedef union
+{
+ BT_HDR hdr;
+ tBTA_HF_CLIENT_API_ENABLE api_enable;
+ tBTA_HF_CLIENT_API_REGISTER api_register;
+ tBTA_HF_CLIENT_API_OPEN api_open;
+ tBTA_HF_CLIENT_DISC_RESULT disc_result;
+ tBTA_HF_CLIENT_RFC rfc;
+ tBTA_HF_CLIENT_DATA_VAL val;
+
+} tBTA_HF_CLIENT_DATA;
+
+/* type for each service control block */
+typedef struct
+{
+ UINT16 serv_handle; /* RFCOMM server handle */
+ BD_ADDR peer_addr; /* peer bd address */
+ tSDP_DISCOVERY_DB *p_disc_db; /* pointer to discovery database */
+ UINT16 conn_handle; /* RFCOMM handle of connected service */
+ tBTA_SEC serv_sec_mask; /* server security mask */
+ tBTA_SEC cli_sec_mask; /* client security mask */
+ tBTA_HF_CLIENT_FEAT features; /* features registered by application */
+ tBTA_HF_CLIENT_PEER_FEAT peer_features; /* peer device features */
+ tBTA_HF_CLIENT_CHLD_FEAT chld_features; /* call handling features */
+ UINT16 peer_version; /* profile version of peer device */
+ UINT8 peer_scn; /* peer scn */
+ UINT8 role; /* initiator/acceptor role */
+ UINT16 sco_idx; /* SCO handle */
+ UINT8 sco_state; /* SCO state variable */
+ BOOLEAN sco_close_rfc; /* TRUE if also close RFCOMM after SCO */
+ BOOLEAN retry_with_sco_only;
+ BOOLEAN deregister; /* TRUE if service shutting down */
+ BOOLEAN svc_conn; /* set to TRUE when service level connection is up */
+ BOOLEAN send_at_reply; /* set to TRUE to notify framework about AT results */
+ tBTA_HF_CLIENT_AT_CB at_cb; /* AT Parser control block */
+ UINT8 state; /* state machine state */
+ tBTM_SCO_CODEC_TYPE negotiated_codec; /* negotiated codec */
+ TIMER_LIST_ENT colli_timer; /* Collision timer */
+ BOOLEAN colli_tmr_on; /* TRUE if collision timer is active */
+} tBTA_HF_CLIENT_SCB;
+
+/* sco states */
+enum
+{
+ BTA_HF_CLIENT_SCO_SHUTDOWN_ST, /* no listening, no connection */
+ BTA_HF_CLIENT_SCO_LISTEN_ST, /* listening */
+ BTA_HF_CLIENT_SCO_OPENING_ST, /* connection opening */
+ BTA_HF_CLIENT_SCO_OPEN_CL_ST, /* opening connection being closed */
+ BTA_HF_CLIENT_SCO_OPEN_ST, /* open */
+ BTA_HF_CLIENT_SCO_CLOSING_ST, /* closing */
+ BTA_HF_CLIENT_SCO_CLOSE_OP_ST, /* closing sco being opened */
+ BTA_HF_CLIENT_SCO_SHUTTING_ST /* sco shutting down */
+};
+
+/* type for AG control block */
+typedef struct
+{
+ tBTA_HF_CLIENT_SCB scb; /* service control block */
+ UINT32 sdp_handle;
+ UINT8 scn;
+ tBTA_HF_CLIENT_CBACK *p_cback; /* application callback */
+ BOOLEAN msbc_enabled;
+} tBTA_HF_CLIENT_CB;
+
+/*****************************************************************************
+** Global data
+*****************************************************************************/
+
+/* control block declaration */
+extern tBTA_HF_CLIENT_CB bta_hf_client_cb;
+
+/*****************************************************************************
+** Function prototypes
+*****************************************************************************/
+
+/* main functions */
+extern void bta_hf_client_scb_init(void);
+extern void bta_hf_client_scb_disable(void);
+extern BOOLEAN bta_hf_client_hdl_event(BT_HDR *p_msg);
+extern void bta_hf_client_sm_execute(UINT16 event,
+ tBTA_HF_CLIENT_DATA *p_data);
+extern void bta_hf_client_slc_seq(BOOLEAN error);
+extern void bta_hf_client_collision_cback (tBTA_SYS_CONN_STATUS status, UINT8 id,
+ UINT8 app_id, BD_ADDR peer_addr);
+extern void bta_hf_client_resume_open ();
+
+/* SDP functions */
+extern BOOLEAN bta_hf_client_add_record(char *p_service_name,
+ UINT8 scn, tBTA_HF_CLIENT_FEAT features,
+ UINT32 sdp_handle);
+extern void bta_hf_client_create_record(tBTA_HF_CLIENT_DATA *p_data);
+extern void bta_hf_client_del_record(tBTA_HF_CLIENT_DATA *p_data);
+extern BOOLEAN bta_hf_client_sdp_find_attr(void);
+extern void bta_hf_client_do_disc(void);
+extern void bta_hf_client_free_db(tBTA_HF_CLIENT_DATA *p_data);
+
+/* RFCOMM functions */
+extern void bta_hf_client_setup_port(UINT16 handle);
+extern void bta_hf_client_start_server(void);
+extern void bta_hf_client_close_server(void);
+extern void bta_hf_client_rfc_do_open(tBTA_HF_CLIENT_DATA *p_data);
+extern void bta_hf_client_rfc_do_close(tBTA_HF_CLIENT_DATA *p_data);
+
+/* SCO functions */
+extern void bta_hf_client_sco_listen(tBTA_HF_CLIENT_DATA *p_data);
+extern void bta_hf_client_sco_shutdown(tBTA_HF_CLIENT_DATA *p_data);
+extern void bta_hf_client_sco_conn_open(tBTA_HF_CLIENT_DATA *p_data);
+extern void bta_hf_client_sco_conn_close(tBTA_HF_CLIENT_DATA *p_data);
+extern void bta_hf_client_sco_open(tBTA_HF_CLIENT_DATA *p_data);
+extern void bta_hf_client_sco_close(tBTA_HF_CLIENT_DATA *p_data);
+extern void bta_hf_client_cback_sco(UINT8 event);
+
+/* AT command functions */
+extern void bta_hf_client_at_parse(char *buf, unsigned int len);
+extern void bta_hf_client_send_at_brsf(void);
+extern void bta_hf_client_send_at_bac(void);
+extern void bta_hf_client_send_at_cind(BOOLEAN status);
+extern void bta_hf_client_send_at_cmer(BOOLEAN activate);
+extern void bta_hf_client_send_at_chld(char cmd, UINT32 idx);
+extern void bta_hf_client_send_at_clip(BOOLEAN activate);
+extern void bta_hf_client_send_at_ccwa(BOOLEAN activate);
+extern void bta_hf_client_send_at_cmee(BOOLEAN activate);
+extern void bta_hf_client_send_at_cops(BOOLEAN query);
+extern void bta_hf_client_send_at_clcc(void);
+extern void bta_hf_client_send_at_bvra(BOOLEAN enable);
+extern void bta_hf_client_send_at_vgs(UINT32 volume);
+extern void bta_hf_client_send_at_vgm(UINT32 volume);
+extern void bta_hf_client_send_at_atd(char *number, UINT32 memory);
+extern void bta_hf_client_send_at_bldn(void);
+extern void bta_hf_client_send_at_ata(void);
+extern void bta_hf_client_send_at_chup(void);
+extern void bta_hf_client_send_at_btrh(BOOLEAN query, UINT32 val);
+extern void bta_hf_client_send_at_vts(char code);
+extern void bta_hf_client_send_at_bcc(void);
+extern void bta_hf_client_send_at_bcs(UINT32 codec);
+extern void bta_hf_client_send_at_cnum(void);
+extern void bta_hf_client_send_at_nrec(void);
+extern void bta_hf_client_send_at_binp(UINT32 action);
+extern void bta_hf_client_send_at_bia(void);
+
+/* Action functions */
+extern void bta_hf_client_register(tBTA_HF_CLIENT_DATA *p_data);
+extern void bta_hf_client_deregister(tBTA_HF_CLIENT_DATA *p_data);
+extern void bta_hf_client_start_dereg(tBTA_HF_CLIENT_DATA *p_data);
+extern void bta_hf_client_start_close(tBTA_HF_CLIENT_DATA *p_data);
+extern void bta_hf_client_start_open(tBTA_HF_CLIENT_DATA *p_data);
+extern void bta_hf_client_rfc_acp_open(tBTA_HF_CLIENT_DATA *p_data);
+extern void bta_hf_client_rfc_open(tBTA_HF_CLIENT_DATA *p_data);
+extern void bta_hf_client_rfc_fail(tBTA_HF_CLIENT_DATA *p_data);
+extern void bta_hf_client_disc_fail(tBTA_HF_CLIENT_DATA *p_data);
+extern void bta_hf_client_open_fail(tBTA_HF_CLIENT_DATA *p_data);
+extern void bta_hf_client_rfc_close(tBTA_HF_CLIENT_DATA *p_data);
+extern void bta_hf_client_disc_acp_res(tBTA_HF_CLIENT_DATA *p_data);
+extern void bta_hf_client_rfc_data(tBTA_HF_CLIENT_DATA *p_data);
+extern void bta_hf_client_disc_int_res(tBTA_HF_CLIENT_DATA *p_data);
+extern void bta_hf_client_svc_conn_open(tBTA_HF_CLIENT_DATA *p_data);
+extern void bta_hf_client_ind(tBTA_HF_CLIENT_IND_TYPE type, UINT16 value);
+extern void bta_hf_client_evt_val(tBTA_HF_CLIENT_EVT type, UINT16 value);
+extern void bta_hf_client_operator_name(char *name);
+extern void bta_hf_client_clip(char *number);
+extern void bta_hf_client_ccwa(char *number);
+extern void bta_hf_client_at_result(tBTA_HF_CLIENT_AT_RESULT_TYPE type, UINT16 cme);
+extern void bta_hf_client_clcc(UINT32 idx, BOOLEAN incoming, UINT8 status, BOOLEAN mpty, char *number);
+extern void bta_hf_client_cnum(char *number, UINT16 service);
+extern void bta_hf_client_binp(char *number);
+
+/* Commands handling functions */
+extern void bta_hf_client_dial(tBTA_HF_CLIENT_DATA *p_data);
+extern void bta_hf_client_send_at_cmd(tBTA_HF_CLIENT_DATA *p_data);
diff --git a/bta/hf_client/bta_hf_client_rfc.c b/bta/hf_client/bta_hf_client_rfc.c
new file mode 100644
index 0000000..b71f919
--- /dev/null
+++ b/bta/hf_client/bta_hf_client_rfc.c
@@ -0,0 +1,268 @@
+/******************************************************************************
+ *
+ * Copyright (c) 2014 The Android Open Source Project
+ * 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 the audio gateway functions controlling the RFCOMM
+ * connections.
+ *
+ ******************************************************************************/
+
+#include <string.h>
+#include "bta_api.h"
+#include "bta_hf_client_int.h"
+#include "port_api.h"
+#include "bd.h"
+#include "bt_utils.h"
+
+/*******************************************************************************
+**
+** Function bta_hf_client_port_cback
+**
+** Description RFCOMM Port callback
+**
+**
+** Returns void
+**
+*******************************************************************************/
+static void bta_hf_client_port_cback(UINT32 code, UINT16 port_handle)
+{
+ BT_HDR *p_buf;
+ UNUSED(code);
+
+ /* ignore port events for port handles other than connected handle */
+ if (port_handle != bta_hf_client_cb.scb.conn_handle)
+ {
+ APPL_TRACE_DEBUG2("bta_hf_client_port_cback ignoring handle:%d conn_handle = %d",
+ port_handle, bta_hf_client_cb.scb.conn_handle);
+ return;
+ }
+
+ if ((p_buf = (BT_HDR *) GKI_getbuf(sizeof(BT_HDR))) != NULL)
+ {
+ p_buf->event = BTA_HF_CLIENT_RFC_DATA_EVT;
+ bta_sys_sendmsg(p_buf);
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_hf_client_mgmt_cback
+**
+** Description RFCOMM management callback
+**
+**
+** Returns void
+**
+*******************************************************************************/
+static void bta_hf_client_mgmt_cback(UINT32 code, UINT16 port_handle)
+{
+ tBTA_HF_CLIENT_RFC *p_buf;
+ UINT16 event;
+
+ APPL_TRACE_DEBUG4("bta_hf_client_mgmt_cback : code = %d, port_handle = %d, conn_handle = %d, serv_handle = %d",
+ code, port_handle, bta_hf_client_cb.scb.conn_handle, bta_hf_client_cb.scb.serv_handle);
+
+ /* ignore close event for port handles other than connected handle */
+ if ((code != PORT_SUCCESS) && (port_handle != bta_hf_client_cb.scb.conn_handle))
+ {
+ APPL_TRACE_DEBUG1("bta_hf_client_mgmt_cback ignoring handle:%d", port_handle);
+ return;
+ }
+
+ if (code == PORT_SUCCESS)
+ {
+ if ((bta_hf_client_cb.scb.conn_handle && (port_handle == bta_hf_client_cb.scb.conn_handle)) || /* outgoing connection */
+ (port_handle == bta_hf_client_cb.scb.serv_handle)) /* incoming connection */
+ {
+ event = BTA_HF_CLIENT_RFC_OPEN_EVT;
+ }
+ else
+ {
+ APPL_TRACE_ERROR1 ("bta_hf_client_mgmt_cback: PORT_SUCCESS, ignoring handle = %d", port_handle);
+ return;
+ }
+ }
+ /* distinguish server close events */
+ else if (port_handle == bta_hf_client_cb.scb.conn_handle)
+ {
+ event = BTA_HF_CLIENT_RFC_CLOSE_EVT;
+ }
+ else
+ {
+ event = BTA_HF_CLIENT_RFC_SRV_CLOSE_EVT;
+ }
+
+ if ((p_buf = (tBTA_HF_CLIENT_RFC *) GKI_getbuf(sizeof(tBTA_HF_CLIENT_RFC))) != NULL)
+ {
+ p_buf->hdr.event = event;
+ p_buf->port_handle = port_handle;
+ bta_sys_sendmsg(p_buf);
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_hf_client_setup_port
+**
+** Description Setup RFCOMM port for use by HF Client.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_hf_client_setup_port(UINT16 handle)
+{
+ PORT_SetEventMask(handle, PORT_EV_RXCHAR);
+ PORT_SetEventCallback(handle, bta_hf_client_port_cback);
+}
+
+/*******************************************************************************
+**
+** Function bta_hf_client_start_server
+**
+** Description Setup RFCOMM server for use by HF Client.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_hf_client_start_server(void)
+{
+ int i;
+ int port_status;
+
+ if (bta_hf_client_cb.scb.serv_handle > 0)
+ {
+ APPL_TRACE_DEBUG2("%s already started, handle: %d", __FUNCTION__, bta_hf_client_cb.scb.serv_handle);
+ return;
+ }
+
+ BTM_SetSecurityLevel(FALSE, "", BTM_SEC_SERVICE_HF_HANDSFREE, bta_hf_client_cb.scb.serv_sec_mask,
+ BT_PSM_RFCOMM, BTM_SEC_PROTO_RFCOMM, bta_hf_client_cb.scn);
+
+ port_status = RFCOMM_CreateConnection(UUID_SERVCLASS_HF_HANDSFREE, bta_hf_client_cb.scn,
+ TRUE, BTA_HF_CLIENT_MTU, (UINT8 *) bd_addr_any, &(bta_hf_client_cb.scb.serv_handle),
+ bta_hf_client_mgmt_cback);
+
+ if (port_status == PORT_SUCCESS)
+ {
+ bta_hf_client_setup_port(bta_hf_client_cb.scb.serv_handle);
+ }
+ else
+ {
+ /* TODO: can we handle this better? */
+ APPL_TRACE_DEBUG1("bta_hf_client_start_server: RFCOMM_CreateConnection returned error:%d", port_status);
+ }
+
+ APPL_TRACE_DEBUG1("bta_hf_client_start_server handle: %d", bta_hf_client_cb.scb.serv_handle);
+}
+
+/*******************************************************************************
+**
+** Function bta_hf_client_close_server
+**
+** Description Close RFCOMM server port for use by HF Client.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_hf_client_close_server(void)
+{
+ APPL_TRACE_DEBUG2("%s %d", __FUNCTION__, bta_hf_client_cb.scb.serv_handle);
+
+ if (bta_hf_client_cb.scb.serv_handle == 0)
+ {
+ APPL_TRACE_DEBUG1("%s already stopped", __FUNCTION__);
+ return;
+ }
+
+ RFCOMM_RemoveServer(bta_hf_client_cb.scb.serv_handle);
+ bta_hf_client_cb.scb.serv_handle = 0;
+}
+
+/*******************************************************************************
+**
+** Function bta_hf_client_rfc_do_open
+**
+** Description Open an RFCOMM connection to the peer device.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_hf_client_rfc_do_open(tBTA_HF_CLIENT_DATA *p_data)
+{
+ BTM_SetSecurityLevel(TRUE, "", BTM_SEC_SERVICE_HF_HANDSFREE,
+ bta_hf_client_cb.scb.cli_sec_mask, BT_PSM_RFCOMM,
+ BTM_SEC_PROTO_RFCOMM, bta_hf_client_cb.scb.peer_scn);
+
+ if (RFCOMM_CreateConnection(UUID_SERVCLASS_HF_HANDSFREE, bta_hf_client_cb.scb.peer_scn,
+ FALSE, BTA_HF_CLIENT_MTU, bta_hf_client_cb.scb.peer_addr, &(bta_hf_client_cb.scb.conn_handle),
+ bta_hf_client_mgmt_cback) == PORT_SUCCESS)
+ {
+ bta_hf_client_setup_port(bta_hf_client_cb.scb.conn_handle);
+ APPL_TRACE_DEBUG1("bta_hf_client_rfc_do_open : conn_handle = %d", bta_hf_client_cb.scb.conn_handle);
+ }
+ /* RFCOMM create connection failed; send ourselves RFCOMM close event */
+ else
+ {
+ bta_hf_client_sm_execute(BTA_HF_CLIENT_RFC_CLOSE_EVT, p_data);
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_hf_client_rfc_do_close
+**
+** Description Close RFCOMM connection.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_hf_client_rfc_do_close(tBTA_HF_CLIENT_DATA *p_data)
+{
+ tBTA_HF_CLIENT_RFC *p_buf;
+ UNUSED(p_data);
+
+ if (bta_hf_client_cb.scb.conn_handle)
+ {
+ RFCOMM_RemoveConnection(bta_hf_client_cb.scb.conn_handle);
+ }
+ else
+ {
+ /* Close API was called while HF Client is in Opening state. */
+ /* Need to trigger the state machine to send callback to the app */
+ /* and move back to INIT state. */
+ if ((p_buf = (tBTA_HF_CLIENT_RFC *) GKI_getbuf(sizeof(tBTA_HF_CLIENT_RFC))) != NULL)
+ {
+ p_buf->hdr.event = BTA_HF_CLIENT_RFC_CLOSE_EVT;
+ bta_sys_sendmsg(p_buf);
+ }
+
+ /* Cancel SDP if it had been started. */
+ if(bta_hf_client_cb.scb.p_disc_db)
+ {
+ (void)SDP_CancelServiceSearch (bta_hf_client_cb.scb.p_disc_db);
+ bta_hf_client_free_db(NULL);
+ }
+ }
+}
diff --git a/bta/hf_client/bta_hf_client_sco.c b/bta/hf_client/bta_hf_client_sco.c
new file mode 100644
index 0000000..224dcc6
--- /dev/null
+++ b/bta/hf_client/bta_hf_client_sco.c
@@ -0,0 +1,734 @@
+/******************************************************************************
+ *
+ * Copyright (c) 2014 The Android Open Source Project
+ * 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.
+ *
+ ******************************************************************************/
+
+#include "bta_hf_client_int.h"
+#include <bt_trace.h>
+#include <bd.h>
+#include <string.h>
+#include "bt_utils.h"
+
+#define BTA_HF_CLIENT_NO_EDR_ESCO (BTM_SCO_PKT_TYPES_MASK_NO_2_EV3 | \
+ BTM_SCO_PKT_TYPES_MASK_NO_3_EV3 | \
+ BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 | \
+ BTM_SCO_PKT_TYPES_MASK_NO_3_EV5)
+
+static const tBTM_ESCO_PARAMS bta_hf_client_esco_params[] = {
+ /* SCO CVSD */
+ {
+ .rx_bw = BTM_64KBITS_RATE,
+ .tx_bw = BTM_64KBITS_RATE,
+ .max_latency = 10,
+ .voice_contfmt = BTM_VOICE_SETTING_CVSD,
+ .packet_types = (BTM_SCO_LINK_ONLY_MASK |
+ BTM_SCO_PKT_TYPES_MASK_NO_2_EV3 |
+ BTM_SCO_PKT_TYPES_MASK_NO_3_EV3 |
+ BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 |
+ BTM_SCO_PKT_TYPES_MASK_NO_3_EV5),
+ .retrans_effort = BTM_ESCO_RETRANS_POWER,
+ },
+ /* ESCO CVSD */
+ {
+ .rx_bw = BTM_64KBITS_RATE,
+ .tx_bw = BTM_64KBITS_RATE,
+ .max_latency = 10,
+ .voice_contfmt = BTM_VOICE_SETTING_CVSD,
+ /* Allow controller to use all types available except 5-slot EDR */
+ .packet_types = (BTM_SCO_LINK_ALL_PKT_MASK |
+ BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 |
+ BTM_SCO_PKT_TYPES_MASK_NO_3_EV5),
+ .retrans_effort = BTM_ESCO_RETRANS_POWER,
+ },
+ /* ESCO mSBC */
+ {
+ .rx_bw = BTM_64KBITS_RATE,
+ .tx_bw = BTM_64KBITS_RATE,
+ .max_latency = 13,
+ .voice_contfmt = BTM_VOICE_SETTING_TRANS,
+ /* Packet Types : EV3 + 2-EV3 */
+ .packet_types = (BTM_SCO_PKT_TYPES_MASK_EV3 |
+ BTM_SCO_PKT_TYPES_MASK_NO_3_EV3 |
+ BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 |
+ BTM_SCO_PKT_TYPES_MASK_NO_3_EV5),
+ .retrans_effort = BTM_ESCO_RETRANS_QUALITY,
+ }
+};
+
+enum
+{
+ BTA_HF_CLIENT_SCO_LISTEN_E,
+ BTA_HF_CLIENT_SCO_OPEN_E, /* open request */
+ BTA_HF_CLIENT_SCO_CLOSE_E, /* close request */
+ BTA_HF_CLIENT_SCO_SHUTDOWN_E, /* shutdown request */
+ BTA_HF_CLIENT_SCO_CONN_OPEN_E, /* sco opened */
+ BTA_HF_CLIENT_SCO_CONN_CLOSE_E, /* sco closed */
+};
+
+/*******************************************************************************
+**
+** Function bta_hf_client_remove_sco
+**
+** Description Removes the specified SCO from the system.
+** If only_active is TRUE, then SCO is only removed if connected
+**
+** Returns BOOLEAN - TRUE if Sco removal was started
+**
+*******************************************************************************/
+static BOOLEAN bta_hf_client_sco_remove(BOOLEAN only_active)
+{
+ BOOLEAN removed_started = FALSE;
+ tBTM_STATUS status;
+
+ APPL_TRACE_DEBUG2("%s %d", __FUNCTION__, only_active);
+
+ if (bta_hf_client_cb.scb.sco_idx != BTM_INVALID_SCO_INDEX)
+ {
+ status = BTM_RemoveSco(bta_hf_client_cb.scb.sco_idx);
+
+ APPL_TRACE_DEBUG3("%s idx 0x%04x, status:0x%x", __FUNCTION__, bta_hf_client_cb.scb.sco_idx, status);
+
+ if (status == BTM_CMD_STARTED)
+ {
+ removed_started = TRUE;
+ }
+ /* If no connection reset the sco handle */
+ else if ( (status == BTM_SUCCESS) || (status == BTM_UNKNOWN_ADDR) )
+ {
+ bta_hf_client_cb.scb.sco_idx = BTM_INVALID_SCO_INDEX;
+ }
+ }
+ return removed_started;
+}
+
+/*******************************************************************************
+**
+** Function bta_hf_client_cback_sco
+**
+** Description Call application callback function with SCO event.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_hf_client_cback_sco(UINT8 event)
+{
+ tBTA_HF_CLIENT evt;
+
+ memset(&evt, 0, sizeof(evt));
+
+ /* call app cback */
+ (*bta_hf_client_cb.p_cback)(event, (tBTA_HF_CLIENT *) &evt);
+}
+
+/*******************************************************************************
+**
+** Function bta_hf_client_sco_conn_rsp
+**
+** Description Process the SCO connection request
+**
+**
+** Returns void
+**
+*******************************************************************************/
+static void bta_hf_client_sco_conn_rsp(tBTM_ESCO_CONN_REQ_EVT_DATA *p_data)
+{
+ tBTM_ESCO_PARAMS resp;
+ UINT8 hci_status = HCI_SUCCESS;
+
+ APPL_TRACE_DEBUG1("%s", __FUNCTION__);
+
+ if (bta_hf_client_cb.scb.sco_state == BTA_HF_CLIENT_SCO_LISTEN_ST)
+ {
+ if (p_data->link_type == BTM_LINK_TYPE_SCO)
+ {
+ resp = bta_hf_client_esco_params[0];
+ }
+ else
+ {
+ resp = bta_hf_client_esco_params[bta_hf_client_cb.scb.negotiated_codec];
+ }
+
+ /* tell sys to stop av if any */
+ bta_sys_sco_use(BTA_ID_HS, 1, bta_hf_client_cb.scb.peer_addr);
+ }
+ else
+ {
+ hci_status = HCI_ERR_HOST_REJECT_DEVICE;
+ }
+
+ BTM_EScoConnRsp(p_data->sco_inx, hci_status, &resp);
+}
+
+/*******************************************************************************
+**
+** Function bta_hf_client_sco_connreq_cback
+**
+** Description BTM eSCO connection requests and eSCO change requests
+** Only the connection requests are processed by BTA.
+**
+** Returns void
+**
+*******************************************************************************/
+static void bta_hf_client_esco_connreq_cback(tBTM_ESCO_EVT event, tBTM_ESCO_EVT_DATA *p_data)
+{
+ APPL_TRACE_DEBUG2("%s %d", __FUNCTION__, event);
+
+ if (event != BTM_ESCO_CONN_REQ_EVT)
+ {
+ return;
+ }
+
+ /* TODO check remote bdaddr, should allow connect only from device with
+ * active SLC */
+
+ bta_hf_client_cb.scb.sco_idx = p_data->conn_evt.sco_inx;
+
+ bta_hf_client_sco_conn_rsp(&p_data->conn_evt);
+
+ bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_OPENING_ST;
+}
+
+/*******************************************************************************
+**
+** Function bta_hf_client_sco_conn_cback
+**
+** Description BTM SCO connection callback.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+static void bta_hf_client_sco_conn_cback(UINT16 sco_idx)
+{
+ BT_HDR *p_buf;
+ UINT8 *rem_bd;
+
+ APPL_TRACE_DEBUG2("%s %d", __FUNCTION__, sco_idx);
+
+ rem_bd = BTM_ReadScoBdAddr(sco_idx);
+
+ if (rem_bd && bdcmp(bta_hf_client_cb.scb.peer_addr, rem_bd) == 0 &&
+ bta_hf_client_cb.scb.svc_conn && bta_hf_client_cb.scb.sco_idx == sco_idx)
+ {
+ if ((p_buf = (BT_HDR *) GKI_getbuf(sizeof(BT_HDR))) != NULL)
+ {
+ p_buf->event = BTA_HF_CLIENT_SCO_OPEN_EVT;
+ p_buf->layer_specific = bta_hf_client_cb.scb.conn_handle;
+ bta_sys_sendmsg(p_buf);
+ }
+ }
+ /* no match found; disconnect sco, init sco variables */
+ else
+ {
+ bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_SHUTDOWN_ST;
+ BTM_RemoveSco(sco_idx);
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_hf_client_sco_disc_cback
+**
+** Description BTM SCO disconnection callback.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+static void bta_hf_client_sco_disc_cback(UINT16 sco_idx)
+{
+ BT_HDR *p_buf;
+
+ APPL_TRACE_DEBUG2("%s %d", __FUNCTION__, sco_idx);
+
+ if (bta_hf_client_cb.scb.sco_idx == sco_idx)
+ {
+ if ((p_buf = (BT_HDR *) GKI_getbuf(sizeof(BT_HDR))) != NULL)
+ {
+ p_buf->event = BTA_HF_CLIENT_SCO_CLOSE_EVT;
+ p_buf->layer_specific = bta_hf_client_cb.scb.conn_handle;;
+ bta_sys_sendmsg(p_buf);
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_hf_client_create_sco
+**
+** Description
+**
+**
+** Returns void
+**
+*******************************************************************************/
+static void bta_hf_client_sco_create(BOOLEAN is_orig)
+{
+ tBTM_STATUS status;
+ UINT8 *p_bd_addr = NULL;
+ tBTM_ESCO_PARAMS params;
+
+ APPL_TRACE_DEBUG2("%s %d", __FUNCTION__, is_orig);
+
+ /* Make sure this sco handle is not already in use */
+ if (bta_hf_client_cb.scb.sco_idx != BTM_INVALID_SCO_INDEX)
+ {
+ APPL_TRACE_WARNING2("%s: Index 0x%04x already in use", __FUNCTION__,
+ bta_hf_client_cb.scb.sco_idx);
+ return;
+ }
+
+ params = bta_hf_client_esco_params[1];
+
+ /* if initiating set current scb and peer bd addr */
+ if (is_orig)
+ {
+ /* Attempt to use eSCO if remote host supports HFP >= 1.5 */
+ if (bta_hf_client_cb.scb.peer_version >= HFP_VERSION_1_5 && !bta_hf_client_cb.scb.retry_with_sco_only)
+ {
+ BTM_SetEScoMode(BTM_LINK_TYPE_ESCO, ¶ms);
+ /* If ESCO or EDR ESCO, retry with SCO only in case of failure */
+ if((params.packet_types & BTM_ESCO_LINK_ONLY_MASK)
+ ||!((params.packet_types & ~(BTM_ESCO_LINK_ONLY_MASK | BTM_SCO_LINK_ONLY_MASK)) ^ BTA_HF_CLIENT_NO_EDR_ESCO))
+ {
+ bta_hf_client_cb.scb.retry_with_sco_only = TRUE;
+ APPL_TRACE_API0("Setting retry_with_sco_only to TRUE");
+ }
+ }
+ else
+ {
+ if(bta_hf_client_cb.scb.retry_with_sco_only)
+ APPL_TRACE_API0("retrying with SCO only");
+ bta_hf_client_cb.scb.retry_with_sco_only = FALSE;
+
+ BTM_SetEScoMode(BTM_LINK_TYPE_SCO, ¶ms);
+ }
+
+ /* tell sys to stop av if any */
+ bta_sys_sco_use(BTA_ID_HS, 1, bta_hf_client_cb.scb.peer_addr);
+ }
+ else
+ {
+ bta_hf_client_cb.scb.retry_with_sco_only = FALSE;
+ }
+
+ p_bd_addr = bta_hf_client_cb.scb.peer_addr;
+
+ status = BTM_CreateSco(p_bd_addr, is_orig, params.packet_types,
+ &bta_hf_client_cb.scb.sco_idx, bta_hf_client_sco_conn_cback,
+ bta_hf_client_sco_disc_cback);
+ if (status == BTM_CMD_STARTED && !is_orig)
+ {
+ if(!BTM_RegForEScoEvts(bta_hf_client_cb.scb.sco_idx, bta_hf_client_esco_connreq_cback))
+ APPL_TRACE_DEBUG1("%s SCO registration success", __FUNCTION__);
+ }
+
+ APPL_TRACE_API5("%s: orig %d, inx 0x%04x, status 0x%x, pkt types 0x%04x",
+ __FUNCTION__, is_orig, bta_hf_client_cb.scb.sco_idx,
+ status, params.packet_types);
+}
+
+
+/*******************************************************************************
+**
+** Function bta_hf_client_sco_event
+**
+** Description Handle SCO events
+**
+**
+** Returns void
+**
+*******************************************************************************/
+static void bta_hf_client_sco_event(UINT8 event)
+{
+ APPL_TRACE_DEBUG3("%s state: %d event: %d", __FUNCTION__,
+ bta_hf_client_cb.scb.sco_state, event);
+
+ switch (bta_hf_client_cb.scb.sco_state)
+ {
+ case BTA_HF_CLIENT_SCO_SHUTDOWN_ST:
+ switch (event)
+ {
+ case BTA_HF_CLIENT_SCO_LISTEN_E:
+ /* create sco listen connection */
+ bta_hf_client_sco_create(FALSE);
+ bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_LISTEN_ST;
+ break;
+
+ default:
+ APPL_TRACE_WARNING1("BTA_HF_CLIENT_SCO_SHUTDOWN_ST: Ignoring event %d", event);
+ break;
+ }
+ break;
+
+ case BTA_HF_CLIENT_SCO_LISTEN_ST:
+ switch (event)
+ {
+ case BTA_HF_CLIENT_SCO_LISTEN_E:
+ /* create sco listen connection (Additional channel) */
+ bta_hf_client_sco_create(FALSE);
+ break;
+
+ case BTA_HF_CLIENT_SCO_OPEN_E:
+ /* remove listening connection */
+ bta_hf_client_sco_remove(FALSE);
+
+ /* create sco connection to peer */
+ bta_hf_client_sco_create(TRUE);
+ bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_OPENING_ST;
+ break;
+
+ case BTA_HF_CLIENT_SCO_SHUTDOWN_E:
+ /* remove listening connection */
+ bta_hf_client_sco_remove(FALSE);
+
+ bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_SHUTDOWN_ST;
+ break;
+
+ case BTA_HF_CLIENT_SCO_CLOSE_E:
+ /* remove listening connection */
+ /* Ignore the event. We need to keep listening SCO for the active SLC */
+ APPL_TRACE_WARNING1("BTA_HF_CLIENT_SCO_LISTEN_ST: Ignoring event %d", event);
+ break;
+
+ case BTA_HF_CLIENT_SCO_CONN_CLOSE_E:
+ /* sco failed; create sco listen connection */
+ bta_hf_client_sco_create(FALSE);
+ bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_LISTEN_ST;
+ break;
+
+ default:
+ APPL_TRACE_WARNING1("BTA_HF_CLIENT_SCO_LISTEN_ST: Ignoring event %d", event);
+ break;
+ }
+ break;
+
+ case BTA_HF_CLIENT_SCO_OPENING_ST:
+ switch (event)
+ {
+ case BTA_HF_CLIENT_SCO_CLOSE_E:
+ bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_OPEN_CL_ST;
+ break;
+
+ case BTA_HF_CLIENT_SCO_SHUTDOWN_E:
+ bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_SHUTTING_ST;
+ break;
+
+ case BTA_HF_CLIENT_SCO_CONN_OPEN_E:
+ bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_OPEN_ST;
+ break;
+
+ case BTA_HF_CLIENT_SCO_CONN_CLOSE_E:
+ /* sco failed; create sco listen connection */
+ bta_hf_client_sco_create(FALSE);
+ bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_LISTEN_ST;
+ break;
+
+ default:
+ APPL_TRACE_WARNING1("BTA_HF_CLIENT_SCO_OPENING_ST: Ignoring event %d", event);
+ break;
+ }
+ break;
+
+ case BTA_HF_CLIENT_SCO_OPEN_CL_ST:
+ switch (event)
+ {
+ case BTA_HF_CLIENT_SCO_OPEN_E:
+ bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_OPENING_ST;
+ break;
+
+ case BTA_HF_CLIENT_SCO_SHUTDOWN_E:
+ bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_SHUTTING_ST;
+ break;
+
+ case BTA_HF_CLIENT_SCO_CONN_OPEN_E:
+ /* close sco connection */
+ bta_hf_client_sco_remove(TRUE);
+
+ bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_CLOSING_ST;
+ break;
+
+ case BTA_HF_CLIENT_SCO_CONN_CLOSE_E:
+ /* sco failed; create sco listen connection */
+
+ bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_LISTEN_ST;
+ break;
+
+ default:
+ APPL_TRACE_WARNING1("BTA_HF_CLIENT_SCO_OPEN_CL_ST: Ignoring event %d", event);
+ break;
+ }
+ break;
+
+ case BTA_HF_CLIENT_SCO_OPEN_ST:
+ switch (event)
+ {
+ case BTA_HF_CLIENT_SCO_CLOSE_E:
+ /* close sco connection if active */
+ if (bta_hf_client_sco_remove(TRUE))
+ {
+ bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_CLOSING_ST;
+ }
+ break;
+
+ case BTA_HF_CLIENT_SCO_SHUTDOWN_E:
+ /* remove all listening connections */
+ bta_hf_client_sco_remove(FALSE);
+
+ bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_SHUTTING_ST;
+ break;
+
+ case BTA_HF_CLIENT_SCO_CONN_CLOSE_E:
+ /* peer closed sco; create sco listen connection */
+ bta_hf_client_sco_create(FALSE);
+ bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_LISTEN_ST;
+ break;
+
+ default:
+ APPL_TRACE_WARNING1("BTA_HF_CLIENT_SCO_OPEN_ST: Ignoring event %d", event);
+ break;
+ }
+ break;
+
+ case BTA_HF_CLIENT_SCO_CLOSING_ST:
+ switch (event)
+ {
+ case BTA_HF_CLIENT_SCO_OPEN_E:
+ bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_CLOSE_OP_ST;
+ break;
+
+ case BTA_HF_CLIENT_SCO_SHUTDOWN_E:
+ bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_SHUTTING_ST;
+ break;
+
+ case BTA_HF_CLIENT_SCO_CONN_CLOSE_E:
+ /* peer closed sco; create sco listen connection */
+ bta_hf_client_sco_create(FALSE);
+
+ bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_LISTEN_ST;
+ break;
+
+ default:
+ APPL_TRACE_WARNING1("BTA_HF_CLIENT_SCO_CLOSING_ST: Ignoring event %d", event);
+ break;
+ }
+ break;
+
+ case BTA_HF_CLIENT_SCO_CLOSE_OP_ST:
+ switch (event)
+ {
+ case BTA_HF_CLIENT_SCO_CLOSE_E:
+ bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_CLOSING_ST;
+ break;
+
+ case BTA_HF_CLIENT_SCO_SHUTDOWN_E:
+ bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_SHUTTING_ST;
+ break;
+
+ case BTA_HF_CLIENT_SCO_CONN_CLOSE_E:
+ /* open sco connection */
+ bta_hf_client_sco_create(TRUE);
+ bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_OPENING_ST;
+ break;
+
+ default:
+ APPL_TRACE_WARNING1("BTA_HF_CLIENT_SCO_CLOSE_OP_ST: Ignoring event %d", event);
+ break;
+ }
+ break;
+
+ case BTA_HF_CLIENT_SCO_SHUTTING_ST:
+ switch (event)
+ {
+ case BTA_HF_CLIENT_SCO_CONN_OPEN_E:
+ /* close sco connection; wait for conn close event */
+ bta_hf_client_sco_remove(TRUE);
+ break;
+
+ case BTA_HF_CLIENT_SCO_CONN_CLOSE_E:
+ bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_SHUTDOWN_ST;
+ break;
+
+ case BTA_HF_CLIENT_SCO_SHUTDOWN_E:
+ bta_hf_client_cb.scb.sco_state = BTA_HF_CLIENT_SCO_SHUTDOWN_ST;
+ break;
+
+ default:
+ APPL_TRACE_WARNING1("BTA_HF_CLIENT_SCO_SHUTTING_ST: Ignoring event %d", event);
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_hf_client_sco_listen
+**
+** Description Initialize SCO listener
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_hf_client_sco_listen(tBTA_HF_CLIENT_DATA *p_data)
+{
+ UNUSED(p_data);
+
+ APPL_TRACE_DEBUG1("%s", __FUNCTION__);
+
+ bta_hf_client_sco_event(BTA_HF_CLIENT_SCO_LISTEN_E);
+}
+
+/*******************************************************************************
+**
+** Function bta_hf_client_sco_shutdown
+**
+** Description
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_hf_client_sco_shutdown(tBTA_HF_CLIENT_DATA *p_data)
+{
+ UNUSED(p_data);
+
+ APPL_TRACE_DEBUG1("%s", __FUNCTION__);
+
+ bta_hf_client_sco_event(BTA_HF_CLIENT_SCO_SHUTDOWN_E);
+}
+
+/*******************************************************************************
+**
+** Function bta_hf_client_sco_conn_open
+**
+** Description
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_hf_client_sco_conn_open(tBTA_HF_CLIENT_DATA *p_data)
+{
+ UNUSED(p_data);
+
+ APPL_TRACE_DEBUG1("%s", __FUNCTION__);
+
+ bta_hf_client_sco_event(BTA_HF_CLIENT_SCO_CONN_OPEN_E);
+
+ bta_sys_sco_open(BTA_ID_HS, 1, bta_hf_client_cb.scb.peer_addr);
+
+ if (bta_hf_client_cb.scb.negotiated_codec == BTM_SCO_CODEC_MSBC)
+ {
+ bta_hf_client_cback_sco(BTA_HF_CLIENT_AUDIO_MSBC_OPEN_EVT);
+ }
+ else
+ {
+ bta_hf_client_cback_sco(BTA_HF_CLIENT_AUDIO_OPEN_EVT);
+ }
+
+ bta_hf_client_cb.scb.retry_with_sco_only = FALSE;
+}
+
+/*******************************************************************************
+**
+** Function bta_hf_client_sco_conn_close
+**
+** Description
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_hf_client_sco_conn_close(tBTA_HF_CLIENT_DATA *p_data)
+{
+ APPL_TRACE_DEBUG1("%s", __FUNCTION__);
+
+ /* clear current scb */
+ bta_hf_client_cb.scb.sco_idx = BTM_INVALID_SCO_INDEX;
+
+ /* retry_with_sco_only, will be set only when initiator
+ ** and HFClient is first trying to establish an eSCO connection */
+ if (bta_hf_client_cb.scb.retry_with_sco_only && bta_hf_client_cb.scb.svc_conn)
+ {
+ bta_hf_client_sco_create(TRUE);
+ }
+ else
+ {
+ bta_hf_client_sco_event(BTA_HF_CLIENT_SCO_CONN_CLOSE_E);
+
+ bta_sys_sco_close(BTA_ID_HS, 1, bta_hf_client_cb.scb.peer_addr);
+
+ bta_sys_sco_unuse(BTA_ID_HS, 1, bta_hf_client_cb.scb.peer_addr);
+
+ /* call app callback */
+ bta_hf_client_cback_sco(BTA_HF_CLIENT_AUDIO_CLOSE_EVT);
+
+ if (bta_hf_client_cb.scb.sco_close_rfc == TRUE)
+ {
+ bta_hf_client_cb.scb.sco_close_rfc = FALSE;
+ bta_hf_client_rfc_do_close(p_data);
+ }
+ }
+ bta_hf_client_cb.scb.retry_with_sco_only = FALSE;
+}
+
+/*******************************************************************************
+**
+** Function bta_hf_client_sco_open
+**
+** Description
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_hf_client_sco_open(tBTA_HF_CLIENT_DATA *p_data)
+{
+ UNUSED(p_data);
+
+ APPL_TRACE_DEBUG1("%s", __FUNCTION__);
+
+ bta_hf_client_sco_event(BTA_HF_CLIENT_SCO_OPEN_E);
+}
+
+/*******************************************************************************
+**
+** Function bta_hf_client_sco_close
+**
+** Description
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_hf_client_sco_close(tBTA_HF_CLIENT_DATA *p_data)
+{
+ UNUSED(p_data);
+
+ APPL_TRACE_DEBUG2("%s 0x%x", __FUNCTION__, bta_hf_client_cb.scb.sco_idx);
+
+ if (bta_hf_client_cb.scb.sco_idx != BTM_INVALID_SCO_INDEX)
+ {
+ bta_hf_client_sco_event(BTA_HF_CLIENT_SCO_CLOSE_E);
+ }
+}
diff --git a/bta/hf_client/bta_hf_client_sdp.c b/bta/hf_client/bta_hf_client_sdp.c
new file mode 100644
index 0000000..f967fbf
--- /dev/null
+++ b/bta/hf_client/bta_hf_client_sdp.c
@@ -0,0 +1,383 @@
+/******************************************************************************
+ *
+ * Copyright (c) 2014 The Android Open Source Project
+ * Copyright (C) 2003-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 the audio gateway functions performing SDP
+ * operations.
+ *
+ ******************************************************************************/
+
+#include <string.h>
+#include "bta_api.h"
+#include "bta_sys.h"
+#include "bt_utils.h"
+#include "bta_hf_client_api.h"
+#include "bta_hf_client_int.h"
+
+/* Number of protocol elements in protocol element list. */
+#define BTA_HF_CLIENT_NUM_PROTO_ELEMS 2
+
+/* Number of elements in service class id list. */
+#define BTA_HF_CLIENT_NUM_SVC_ELEMS 2
+
+/*******************************************************************************
+**
+** Function bta_hf_client_sdp_cback
+**
+** Description SDP callback function.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+static void bta_hf_client_sdp_cback(UINT16 status)
+{
+ tBTA_HF_CLIENT_DISC_RESULT *p_buf;
+ UINT16 event;
+
+ APPL_TRACE_DEBUG1("bta_hf_client_sdp_cback status:0x%x", status);
+
+ /* set event according to int/acp */
+ if (bta_hf_client_cb.scb.role == BTA_HF_CLIENT_ACP)
+ {
+ event = BTA_HF_CLIENT_DISC_ACP_RES_EVT;
+ }
+ else
+ {
+ event = BTA_HF_CLIENT_DISC_INT_RES_EVT;
+ }
+
+ if ((p_buf = (tBTA_HF_CLIENT_DISC_RESULT *) GKI_getbuf(sizeof(tBTA_HF_CLIENT_DISC_RESULT))) != NULL)
+ {
+ p_buf->hdr.event = event;
+ p_buf->status = status;
+ bta_sys_sendmsg(p_buf);
+ }
+}
+
+/******************************************************************************
+**
+** Function bta_hf_client_add_record
+**
+** Description This function is called by a server application to add
+** HFP Client information to an SDP record. Prior to
+** calling this function the application must call
+** SDP_CreateRecord() to create an SDP record.
+**
+** Returns TRUE if function execution succeeded,
+** FALSE if function execution failed.
+**
+******************************************************************************/
+BOOLEAN bta_hf_client_add_record(char *p_service_name, UINT8 scn,
+ tBTA_HF_CLIENT_FEAT features, UINT32 sdp_handle)
+{
+ tSDP_PROTOCOL_ELEM proto_elem_list[BTA_HF_CLIENT_NUM_PROTO_ELEMS];
+ UINT16 svc_class_id_list[BTA_HF_CLIENT_NUM_SVC_ELEMS];
+ UINT16 browse_list[] = {UUID_SERVCLASS_PUBLIC_BROWSE_GROUP};
+ UINT16 version;
+ UINT16 profile_uuid;
+ BOOLEAN result = TRUE;
+ BOOLEAN codec_supported = FALSE;
+ UINT8 buf[2];
+ UINT16 sdp_features = 0;
+
+ APPL_TRACE_DEBUG0("bta_hf_client_add_record");
+
+ memset( proto_elem_list, 0 , BTA_HF_CLIENT_NUM_PROTO_ELEMS*sizeof(tSDP_PROTOCOL_ELEM));
+
+ /* add the protocol element sequence */
+ proto_elem_list[0].protocol_uuid = UUID_PROTOCOL_L2CAP;
+ proto_elem_list[0].num_params = 0;
+ proto_elem_list[1].protocol_uuid = UUID_PROTOCOL_RFCOMM;
+ proto_elem_list[1].num_params = 1;
+ proto_elem_list[1].params[0] = scn;
+ result &= SDP_AddProtocolList(sdp_handle, BTA_HF_CLIENT_NUM_PROTO_ELEMS, proto_elem_list);
+
+ /* add service class id list */
+ svc_class_id_list[0] = UUID_SERVCLASS_HF_HANDSFREE;
+ svc_class_id_list[1] = UUID_SERVCLASS_GENERIC_AUDIO;
+ result &= SDP_AddServiceClassIdList(sdp_handle, BTA_HF_CLIENT_NUM_SVC_ELEMS, svc_class_id_list);
+
+ /* add profile descriptor list */
+ profile_uuid = UUID_SERVCLASS_HF_HANDSFREE;
+ version = HFP_VERSION_1_6;
+
+ result &= SDP_AddProfileDescriptorList(sdp_handle, profile_uuid, version);
+
+ /* add service name */
+ if (p_service_name != NULL && p_service_name[0] != 0)
+ {
+ result &= SDP_AddAttribute(sdp_handle, ATTR_ID_SERVICE_NAME, TEXT_STR_DESC_TYPE,
+ (UINT32)(strlen(p_service_name)+1), (UINT8 *) p_service_name);
+ }
+
+ /* add features */
+ if (features & BTA_HF_CLIENT_FEAT_ECNR)
+ sdp_features |= BTA_HF_CLIENT_FEAT_ECNR;
+
+ if (features & BTA_HF_CLIENT_FEAT_3WAY)
+ sdp_features |= BTA_HF_CLIENT_FEAT_3WAY;
+
+ if (features & BTA_HF_CLIENT_FEAT_CLI)
+ sdp_features |= BTA_HF_CLIENT_FEAT_CLI;
+
+ if (features & BTA_HF_CLIENT_FEAT_VREC)
+ sdp_features |= BTA_HF_CLIENT_FEAT_VREC;
+
+ if (features & BTA_HF_CLIENT_FEAT_VOL)
+ sdp_features |= BTA_HF_CLIENT_FEAT_VOL;
+
+ /* Codec bit position is different in SDP (bit 5) and in BRSF (bit 7) */
+ if (features & BTA_HF_CLIENT_FEAT_CODEC)
+ sdp_features |= 0x0020;
+
+ UINT16_TO_BE_FIELD(buf, sdp_features);
+ result &= SDP_AddAttribute(sdp_handle, ATTR_ID_SUPPORTED_FEATURES, UINT_DESC_TYPE, 2, buf);
+
+ /* add browse group list */
+ result &= SDP_AddUuidSequence(sdp_handle, ATTR_ID_BROWSE_GROUP_LIST, 1, browse_list);
+
+ return result;
+}
+
+/*******************************************************************************
+**
+** Function bta_hf_client_create_record
+**
+** Description Create SDP record for registered service.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_hf_client_create_record(tBTA_HF_CLIENT_DATA *p_data)
+{
+ /* add sdp record if not already registered */
+ if (bta_hf_client_cb.sdp_handle == 0)
+ {
+ bta_hf_client_cb.sdp_handle = SDP_CreateRecord();
+ bta_hf_client_cb.scn = BTM_AllocateSCN();
+ bta_hf_client_add_record(p_data->api_register.name,
+ bta_hf_client_cb.scn,
+ p_data->api_register.features,
+ bta_hf_client_cb.sdp_handle);
+
+ bta_sys_add_uuid(UUID_SERVCLASS_HF_HANDSFREE);
+ }
+
+}
+
+/*******************************************************************************
+**
+** Function bta_hf_client_del_record
+**
+** Description Delete SDP record for registered service.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_hf_client_del_record(tBTA_HF_CLIENT_DATA *p_data)
+{
+ UNUSED(p_data);
+
+ APPL_TRACE_DEBUG0("bta_hf_client_del_record");
+
+ if (bta_hf_client_cb.sdp_handle != 0)
+ {
+ SDP_DeleteRecord(bta_hf_client_cb.sdp_handle);
+ bta_hf_client_cb.sdp_handle = 0;
+ BTM_FreeSCN(bta_hf_client_cb.scn);
+ BTM_SecClrService(BTM_SEC_SERVICE_HF_HANDSFREE);
+ bta_sys_remove_uuid(UUID_SERVCLASS_HF_HANDSFREE);
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_hf_client_sdp_find_attr
+**
+** Description Process SDP discovery results to find requested attribute
+**
+**
+** Returns TRUE if results found, FALSE otherwise.
+**
+*******************************************************************************/
+BOOLEAN bta_hf_client_sdp_find_attr(void)
+{
+ tSDP_DISC_REC *p_rec = NULL;
+ tSDP_DISC_ATTR *p_attr;
+ tSDP_PROTOCOL_ELEM pe;
+ BOOLEAN result = FALSE;
+
+ bta_hf_client_cb.scb.peer_version = HFP_VERSION_1_1; /* Default version */
+
+ /* loop through all records we found */
+ while (TRUE)
+ {
+ /* get next record; if none found, we're done */
+ if ((p_rec = SDP_FindServiceInDb(bta_hf_client_cb.scb.p_disc_db, UUID_SERVCLASS_AG_HANDSFREE, p_rec)) == NULL)
+ {
+ break;
+ }
+
+ /* get scn from proto desc list if initiator */
+ if (bta_hf_client_cb.scb.role == BTA_HF_CLIENT_INT)
+ {
+ if (SDP_FindProtocolListElemInRec(p_rec, UUID_PROTOCOL_RFCOMM, &pe))
+ {
+ bta_hf_client_cb.scb.peer_scn = (UINT8) pe.params[0];
+ }
+ else
+ {
+ continue;
+ }
+ }
+
+ /* get profile version (if failure, version parameter is not updated) */
+ SDP_FindProfileVersionInRec(p_rec, UUID_SERVCLASS_HF_HANDSFREE, &bta_hf_client_cb.scb.peer_version);
+
+ /* get features */
+ if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_SUPPORTED_FEATURES)) != NULL)
+ {
+ /* Found attribute. Get value. */
+ /* There might be race condition between SDP and BRSF. */
+ /* Do not update if we already received BRSF. */
+ if (bta_hf_client_cb.scb.peer_features == 0)
+ {
+ bta_hf_client_cb.scb.peer_features = p_attr->attr_value.v.u16;
+
+ /* SDP and BRSF WBS bit are different, correct it if set */
+ if (bta_hf_client_cb.scb.peer_features & 0x0020)
+ {
+ bta_hf_client_cb.scb.peer_features &= ~0x0020;
+ bta_hf_client_cb.scb.peer_features |= BTA_HF_CLIENT_PEER_CODEC;
+ }
+
+ /* get network for ability to reject calls */
+ if ((p_attr = SDP_FindAttributeInRec(p_rec, ATTR_ID_NETWORK)) != NULL)
+ {
+ if (p_attr->attr_value.v.u16 == 0x01)
+ {
+ bta_hf_client_cb.scb.peer_features |= BTA_HF_CLIENT_PEER_REJECT;
+ }
+ }
+ }
+ }
+
+ /* found what we needed */
+ result = TRUE;
+ break;
+ }
+
+ APPL_TRACE_DEBUG3("%s peer_version=0x%x peer_features=0x%x",
+ __FUNCTION__, bta_hf_client_cb.scb.peer_version,
+ bta_hf_client_cb.scb.peer_features);
+
+ return result;
+}
+
+/*******************************************************************************
+**
+** Function bta_hf_client_do_disc
+**
+** Description Do service discovery.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_hf_client_do_disc(void)
+{
+ tSDP_UUID uuid_list[2];
+ UINT16 num_uuid = 1;
+ UINT16 attr_list[4];
+ UINT8 num_attr;
+ BOOLEAN db_inited = FALSE;
+
+ /* initiator; get proto list and features */
+ if (bta_hf_client_cb.scb.role == BTA_HF_CLIENT_INT)
+ {
+ attr_list[0] = ATTR_ID_SERVICE_CLASS_ID_LIST;
+ attr_list[1] = ATTR_ID_PROTOCOL_DESC_LIST;
+ attr_list[2] = ATTR_ID_BT_PROFILE_DESC_LIST;
+ attr_list[3] = ATTR_ID_SUPPORTED_FEATURES;
+ num_attr = 4;
+ uuid_list[0].uu.uuid16 = UUID_SERVCLASS_AG_HANDSFREE;
+ }
+ /* acceptor; get features */
+ else
+ {
+ attr_list[0] = ATTR_ID_SERVICE_CLASS_ID_LIST;
+ attr_list[1] = ATTR_ID_BT_PROFILE_DESC_LIST;
+ attr_list[2] = ATTR_ID_SUPPORTED_FEATURES;
+ num_attr = 3;
+ uuid_list[0].uu.uuid16 = UUID_SERVCLASS_AG_HANDSFREE;
+ }
+
+ /* allocate buffer for sdp database */
+ bta_hf_client_cb.scb.p_disc_db = (tSDP_DISCOVERY_DB *) GKI_getbuf(GKI_MAX_BUF_SIZE);
+
+ if (bta_hf_client_cb.scb.p_disc_db)
+ {
+ /* set up service discovery database; attr happens to be attr_list len */
+ uuid_list[0].len = LEN_UUID_16;
+ uuid_list[1].len = LEN_UUID_16;
+ db_inited = SDP_InitDiscoveryDb(bta_hf_client_cb.scb.p_disc_db, GKI_MAX_BUF_SIZE, num_uuid,
+ uuid_list, num_attr, attr_list);
+ }
+
+ if (db_inited)
+ {
+ /*Service discovery not initiated */
+ db_inited = SDP_ServiceSearchAttributeRequest(bta_hf_client_cb.scb.peer_addr,
+ bta_hf_client_cb.scb.p_disc_db, bta_hf_client_sdp_cback);
+ }
+
+ if (!db_inited)
+ {
+ /*free discover db */
+ bta_hf_client_free_db(NULL);
+ /* sent failed event */
+ bta_hf_client_sm_execute(BTA_HF_CLIENT_DISC_FAIL_EVT, NULL);
+ }
+
+}
+
+/*******************************************************************************
+**
+** Function bta_hf_client_free_db
+**
+** Description Free discovery database.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_hf_client_free_db(tBTA_HF_CLIENT_DATA *p_data)
+{
+ UNUSED(p_data);
+
+ if (bta_hf_client_cb.scb.p_disc_db != NULL)
+ {
+ GKI_freebuf(bta_hf_client_cb.scb.p_disc_db);
+ bta_hf_client_cb.scb.p_disc_db = NULL;
+ }
+}