summaryrefslogtreecommitdiffstats
path: root/bta/ag/bta_ag_sco.c
diff options
context:
space:
mode:
Diffstat (limited to 'bta/ag/bta_ag_sco.c')
-rw-r--r--bta/ag/bta_ag_sco.c1662
1 files changed, 1662 insertions, 0 deletions
diff --git a/bta/ag/bta_ag_sco.c b/bta/ag/bta_ag_sco.c
new file mode 100644
index 0000000..0c811b4
--- /dev/null
+++ b/bta/ag/bta_ag_sco.c
@@ -0,0 +1,1662 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2004-2012 Broadcom Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ ******************************************************************************/
+
+/******************************************************************************
+ *
+ * This file contains functions for managing the SCO connection used in AG.
+ *
+ ******************************************************************************/
+
+#include "bta_api.h"
+#include "bta_ag_api.h"
+#include "bta_ag_co.h"
+#if (BTM_SCO_HCI_INCLUDED == TRUE )
+#include "bta_dm_co.h"
+#endif
+#include "bta_ag_int.h"
+#include "btm_api.h"
+#include "gki.h"
+
+#ifndef BTA_AG_SCO_DEBUG
+#define BTA_AG_SCO_DEBUG FALSE
+#endif
+
+#ifndef BTA_AG_CODEC_NEGO_TIMEOUT
+#define BTA_AG_CODEC_NEGO_TIMEOUT 3000
+#endif
+
+#if BTA_AG_SCO_DEBUG == TRUE
+static char *bta_ag_sco_evt_str(UINT8 event);
+static char *bta_ag_sco_state_str(UINT8 state);
+#endif
+
+#define BTA_AG_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)
+
+/* sco events */
+enum
+{
+ BTA_AG_SCO_LISTEN_E, /* listen request */
+ BTA_AG_SCO_OPEN_E, /* open request */
+ BTA_AG_SCO_XFER_E, /* transfer request */
+#if (BTM_WBS_INCLUDED == TRUE )
+ BTA_AG_SCO_CN_DONE_E, /* codec negotiation done */
+ BTA_AG_SCO_REOPEN_E, /* Retry with other codec when failed */
+#endif
+ BTA_AG_SCO_CLOSE_E, /* close request */
+ BTA_AG_SCO_SHUTDOWN_E, /* shutdown request */
+ BTA_AG_SCO_CONN_OPEN_E, /* sco open */
+ BTA_AG_SCO_CONN_CLOSE_E, /* sco closed */
+ BTA_AG_SCO_CI_DATA_E /* SCO data ready */
+};
+
+#if (BTM_WBS_INCLUDED == TRUE )
+#define BTA_AG_NUM_CODECS 2
+static const tBTM_ESCO_PARAMS bta_ag_esco_params[BTA_AG_NUM_CODECS] =
+{
+ /* CVSD */
+ {
+ BTM_64KBITS_RATE, /* TX Bandwidth (64 kbits/sec) */
+ BTM_64KBITS_RATE, /* RX Bandwidth (64 kbits/sec) */
+ 0x000a, /* 10 ms (HS/HF can use EV3, 2-EV3, 3-EV3) */
+ BTM_VOICE_SETTING_CVSD, /* Inp Linear, Air CVSD, 2s Comp, 16bit */
+ (BTM_SCO_PKT_TYPES_MASK_HV1 + /* Packet Types */
+ BTM_SCO_PKT_TYPES_MASK_HV2 +
+ BTM_SCO_PKT_TYPES_MASK_HV3 +
+ BTM_SCO_PKT_TYPES_MASK_EV3 +
+ BTM_SCO_PKT_TYPES_MASK_EV4 +
+ BTM_SCO_PKT_TYPES_MASK_EV5 +
+ BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 +
+ BTM_SCO_PKT_TYPES_MASK_NO_3_EV5),
+ BTM_ESCO_RETRANS_POWER /* Retransmission effort */
+ },
+ /* mSBC */
+ {
+ BTM_64KBITS_RATE, /* TX Bandwidth (64 kbits/sec), 8000 */
+ BTM_64KBITS_RATE, /* RX Bandwidth (64 kbits/sec), 8000 */
+ 13, /* 13 ms */
+ BTM_VOICE_SETTING_TRANS, /* Inp Linear, Transparent, 2s Comp, 16bit */
+ (BTM_SCO_PKT_TYPES_MASK_EV3 | /* Packet Types : EV3 + 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),
+ BTM_ESCO_RETRANS_QUALITY /* Retransmission effort */
+ }
+};
+#else
+static const tBTM_ESCO_PARAMS bta_ag_esco_params =
+{
+ BTM_64KBITS_RATE, /* TX Bandwidth (64 kbits/sec) */
+ BTM_64KBITS_RATE, /* RX Bandwidth (64 kbits/sec) */
+ 0x000a, /* 10 ms (HS/HF can use EV3, 2-EV3, 3-EV3) */
+ 0x0060, /* Inp Linear, Air CVSD, 2s Comp, 16bit */
+ (BTM_SCO_PKT_TYPES_MASK_HV1 + /* Packet Types */
+ BTM_SCO_PKT_TYPES_MASK_HV2 +
+ BTM_SCO_PKT_TYPES_MASK_HV3 +
+ BTM_SCO_PKT_TYPES_MASK_EV3 +
+ BTM_SCO_PKT_TYPES_MASK_EV4 +
+ BTM_SCO_PKT_TYPES_MASK_EV5 +
+ BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 +
+ BTM_SCO_PKT_TYPES_MASK_NO_3_EV5),
+ BTM_ESCO_RETRANS_POWER /* Retransmission effort */
+};
+#endif
+
+/*******************************************************************************
+**
+** Function bta_ag_sco_conn_cback
+**
+** Description BTM SCO connection callback.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+static void bta_ag_sco_conn_cback(UINT16 sco_idx)
+{
+ UINT16 handle;
+ BT_HDR *p_buf;
+ tBTA_AG_SCB *p_scb;
+
+ /* match callback to scb; first check current sco scb */
+ if (bta_ag_cb.sco.p_curr_scb != NULL && bta_ag_cb.sco.p_curr_scb->in_use)
+ {
+ handle = bta_ag_scb_to_idx(bta_ag_cb.sco.p_curr_scb);
+ }
+ /* then check for scb connected to this peer */
+ else
+ {
+ /* Check if SLC is up */
+ handle = bta_ag_idx_by_bdaddr(BTM_ReadScoBdAddr(sco_idx));
+ p_scb = bta_ag_scb_by_idx(handle);
+ if(p_scb && !p_scb->svc_conn)
+ handle = 0;
+ }
+
+ if (handle != 0)
+ {
+ if ((p_buf = (BT_HDR *) GKI_getbuf(sizeof(BT_HDR))) != NULL)
+ {
+ p_buf->event = BTA_AG_SCO_OPEN_EVT;
+ p_buf->layer_specific = handle;
+ bta_sys_sendmsg(p_buf);
+ }
+ }
+ /* no match found; disconnect sco, init sco variables */
+ else
+ {
+ bta_ag_cb.sco.p_curr_scb = NULL;
+ bta_ag_cb.sco.state = BTA_AG_SCO_SHUTDOWN_ST;
+ BTM_RemoveSco(sco_idx);
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_ag_sco_disc_cback
+**
+** Description BTM SCO disconnection callback.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+static void bta_ag_sco_disc_cback(UINT16 sco_idx)
+{
+ BT_HDR *p_buf;
+ UINT16 handle = 0;
+
+ APPL_TRACE_DEBUG3 ("bta_ag_sco_disc_cback(): sco_idx: 0x%x p_cur_scb: 0x%08x sco.state: %d", sco_idx, bta_ag_cb.sco.p_curr_scb, bta_ag_cb.sco.state);
+
+ APPL_TRACE_DEBUG4 ("bta_ag_sco_disc_cback(): scb[0] addr: 0x%08x in_use: %u sco_idx: 0x%x sco state: %u",
+ &bta_ag_cb.scb[0], bta_ag_cb.scb[0].in_use, bta_ag_cb.scb[0].sco_idx, bta_ag_cb.scb[0].state);
+ APPL_TRACE_DEBUG4 ("bta_ag_sco_disc_cback(): scb[1] addr: 0x%08x in_use: %u sco_idx: 0x%x sco state: %u",
+ &bta_ag_cb.scb[1], bta_ag_cb.scb[1].in_use, bta_ag_cb.scb[1].sco_idx, bta_ag_cb.scb[1].state);
+
+ /* match callback to scb */
+ if (bta_ag_cb.sco.p_curr_scb != NULL && bta_ag_cb.sco.p_curr_scb->in_use)
+ {
+ /* We only care about callbacks for the active SCO */
+ if (bta_ag_cb.sco.p_curr_scb->sco_idx != sco_idx)
+ {
+ if (bta_ag_cb.sco.p_curr_scb->sco_idx != 0xFFFF)
+ return;
+ }
+ handle = bta_ag_scb_to_idx(bta_ag_cb.sco.p_curr_scb);
+ }
+
+ if (handle != 0)
+ {
+#if (BTM_SCO_HCI_INCLUDED == TRUE )
+ tBTM_STATUS status = BTM_ConfigScoPath(BTM_SCO_ROUTE_PCM, NULL, NULL, TRUE);
+ APPL_TRACE_DEBUG1("bta_ag_sco_disc_cback sco close config status = %d", status);
+ /* SCO clean up here */
+ bta_dm_sco_co_close();
+#endif
+
+#if (BTM_WBS_INCLUDED == TRUE )
+ /* Restore settings */
+ if(bta_ag_cb.sco.p_curr_scb->inuse_codec == BTA_AG_CODEC_MSBC)
+ {
+ BTM_SetWBSCodec (BTM_SCO_CODEC_NONE);
+ BTM_WriteVoiceSettings (BTM_VOICE_SETTING_CVSD);
+
+ /* If SCO open was initiated by AG and failed for mSBC, try CVSD again. */
+ if (bta_ag_sco_is_opening (bta_ag_cb.sco.p_curr_scb))
+ {
+ bta_ag_cb.sco.p_curr_scb->codec_fallback = TRUE;
+ APPL_TRACE_DEBUG0("Fallback to CVSD");
+ }
+ }
+
+ bta_ag_cb.sco.p_curr_scb->inuse_codec = BTA_AG_CODEC_NONE;
+#endif
+
+ if ((p_buf = (BT_HDR *) GKI_getbuf(sizeof(BT_HDR))) != NULL)
+ {
+ p_buf->event = BTA_AG_SCO_CLOSE_EVT;
+ p_buf->layer_specific = handle;
+ bta_sys_sendmsg(p_buf);
+ }
+ }
+ /* no match found */
+ else
+ {
+ APPL_TRACE_DEBUG0("no scb for ag_sco_disc_cback");
+
+ /* sco could be closed after scb dealloc'ed */
+ if (bta_ag_cb.sco.p_curr_scb != NULL)
+ {
+ bta_ag_cb.sco.p_curr_scb->sco_idx = BTM_INVALID_SCO_INDEX;
+ bta_ag_cb.sco.p_curr_scb = NULL;
+ bta_ag_cb.sco.state = BTA_AG_SCO_SHUTDOWN_ST;
+ }
+ }
+}
+#if (BTM_SCO_HCI_INCLUDED == TRUE )
+/*******************************************************************************
+**
+** Function bta_ag_sco_read_cback
+**
+** Description Callback function is the callback function for incoming
+** SCO data over HCI.
+**
+** Returns void
+**
+*******************************************************************************/
+static void bta_ag_sco_read_cback (UINT16 sco_inx, BT_HDR *p_data, tBTM_SCO_DATA_FLAG status)
+{
+ if (status != BTM_SCO_DATA_CORRECT)
+ {
+ APPL_TRACE_DEBUG1("bta_ag_sco_read_cback: status(%d)", status);
+ }
+
+ /* Callout function must free the data. */
+ bta_dm_sco_co_in_data (p_data, status);
+}
+#endif
+/*******************************************************************************
+**
+** Function bta_ag_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_ag_remove_sco(tBTA_AG_SCB *p_scb, BOOLEAN only_active)
+{
+ BOOLEAN removed_started = FALSE;
+ tBTM_STATUS status;
+
+ if (p_scb->sco_idx != BTM_INVALID_SCO_INDEX)
+ {
+ if (!only_active || p_scb->sco_idx == bta_ag_cb.sco.cur_idx)
+ {
+ status = BTM_RemoveSco(p_scb->sco_idx);
+
+ APPL_TRACE_DEBUG2("ag remove sco: inx 0x%04x, status:0x%x", p_scb->sco_idx, status);
+
+ if (status == BTM_CMD_STARTED)
+ {
+ /* Sco is connected; set current control block */
+ bta_ag_cb.sco.p_curr_scb = p_scb;
+
+ removed_started = TRUE;
+ }
+ /* If no connection reset the sco handle */
+ else if ( (status == BTM_SUCCESS) || (status == BTM_UNKNOWN_ADDR) )
+ {
+ p_scb->sco_idx = BTM_INVALID_SCO_INDEX;
+ }
+ }
+ }
+ return removed_started;
+}
+
+
+/*******************************************************************************
+**
+** Function bta_ag_esco_connreq_cback
+**
+** Description BTM eSCO connection requests and eSCO change requests
+** Only the connection requests are processed by BTA.
+**
+** Returns void
+**
+*******************************************************************************/
+static void bta_ag_esco_connreq_cback(tBTM_ESCO_EVT event, tBTM_ESCO_EVT_DATA *p_data)
+{
+ tBTA_AG_SCB *p_scb;
+ UINT16 handle;
+ UINT16 sco_inx = p_data->conn_evt.sco_inx;
+
+ /* Only process connection requests */
+ if (event == BTM_ESCO_CONN_REQ_EVT)
+ {
+ if ((handle = bta_ag_idx_by_bdaddr(BTM_ReadScoBdAddr(sco_inx))) != 0 &&
+ ((p_scb = bta_ag_scb_by_idx(handle)) != NULL) && p_scb->svc_conn)
+ {
+ p_scb->sco_idx = sco_inx;
+
+ /* If no other SCO active, allow this one */
+ if (!bta_ag_cb.sco.p_curr_scb)
+ {
+ APPL_TRACE_EVENT1("bta_ag_esco_connreq_cback: Accept Conn Request (sco_inx 0x%04x)", sco_inx);
+ bta_ag_sco_conn_rsp(p_scb, &p_data->conn_evt);
+
+ bta_ag_cb.sco.state = BTA_AG_SCO_OPENING_ST;
+ bta_ag_cb.sco.p_curr_scb = p_scb;
+ bta_ag_cb.sco.cur_idx = p_scb->sco_idx;
+ }
+ else /* Begin a transfer: Close current SCO before responding */
+ {
+ APPL_TRACE_DEBUG0("bta_ag_esco_connreq_cback: Begin XFER");
+ bta_ag_cb.sco.p_xfer_scb = p_scb;
+ bta_ag_cb.sco.conn_data = p_data->conn_evt;
+ bta_ag_cb.sco.state = BTA_AG_SCO_OPEN_XFER_ST;
+
+ if (!bta_ag_remove_sco(bta_ag_cb.sco.p_curr_scb, TRUE))
+ {
+ APPL_TRACE_ERROR1("bta_ag_esco_connreq_cback: Nothing to remove so accept Conn Request (sco_inx 0x%04x)", sco_inx);
+ bta_ag_cb.sco.p_xfer_scb = NULL;
+ bta_ag_cb.sco.state = BTA_AG_SCO_LISTEN_ST;
+
+ bta_ag_sco_conn_rsp(p_scb, &p_data->conn_evt);
+ }
+ }
+ }
+ /* If error occurred send reject response immediately */
+ else
+ {
+ APPL_TRACE_WARNING0("no scb for bta_ag_esco_connreq_cback or no resources");
+ BTM_EScoConnRsp(p_data->conn_evt.sco_inx, HCI_ERR_HOST_REJECT_RESOURCES, NULL);
+ }
+ }
+ /* Received a change in the esco link */
+ else if (event == BTM_ESCO_CHG_EVT)
+ {
+ APPL_TRACE_EVENT5("eSCO change event (inx %d): rtrans %d, rxlen %d, txlen %d, txint %d",
+ p_data->chg_evt.sco_inx,
+ p_data->chg_evt.retrans_window, p_data->chg_evt.rx_pkt_len,
+ p_data->chg_evt.tx_pkt_len, p_data->chg_evt.tx_interval);
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_ag_cback_sco
+**
+** Description Call application callback function with SCO event.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+static void bta_ag_cback_sco(tBTA_AG_SCB *p_scb, UINT8 event)
+{
+ tBTA_AG_HDR sco;
+
+ sco.handle = bta_ag_scb_to_idx(p_scb);
+ sco.app_id = p_scb->app_id;
+
+ /* call close cback */
+ (*bta_ag_cb.p_cback)(event, (tBTA_AG *) &sco);
+}
+
+/*******************************************************************************
+**
+** Function bta_ag_create_sco
+**
+** Description
+**
+**
+** Returns void
+**
+*******************************************************************************/
+static void bta_ag_create_sco(tBTA_AG_SCB *p_scb, BOOLEAN is_orig)
+{
+ tBTM_STATUS status;
+ UINT8 *p_bd_addr = NULL;
+ tBTM_ESCO_PARAMS params;
+#if (BTM_WBS_INCLUDED == TRUE )
+ tBTA_AG_PEER_CODEC esco_codec = BTM_SCO_CODEC_CVSD;
+ int codec_index = 0;
+#endif
+#if (BTM_SCO_HCI_INCLUDED == TRUE )
+ tBTM_SCO_ROUTE_TYPE sco_route;
+ tBTA_CODEC_INFO codec_info = {BTA_SCO_CODEC_PCM};
+ UINT32 pcm_sample_rate;
+#endif
+
+ /* Make sure this sco handle is not already in use */
+ if (p_scb->sco_idx != BTM_INVALID_SCO_INDEX)
+ {
+ APPL_TRACE_WARNING1("bta_ag_create_sco: Index 0x%04x Already In Use!",
+ p_scb->sco_idx);
+ return;
+ }
+
+#if (BTM_WBS_INCLUDED == TRUE )
+ if ((p_scb->sco_codec == BTM_SCO_CODEC_MSBC) &&
+ !p_scb->codec_fallback &&
+ !p_scb->retry_with_sco_only)
+ esco_codec = BTM_SCO_CODEC_MSBC;
+
+ if (p_scb->codec_fallback)
+ {
+ p_scb->codec_fallback = FALSE;
+
+ /* Force AG to send +BCS for the next audio connection. */
+ p_scb->codec_updated = TRUE;
+ }
+
+ if (esco_codec == BTM_SCO_CODEC_MSBC)
+ codec_index = esco_codec - 1;
+
+ params = bta_ag_esco_params[codec_index];
+#else
+ params = bta_ag_esco_params;
+#endif
+
+ if(bta_ag_cb.sco.param_updated) /* If we do not use the default parameters */
+ params = bta_ag_cb.sco.params;
+
+ if(!bta_ag_cb.sco.param_updated)
+ {
+#if (BTM_WBS_INCLUDED == TRUE)
+ if (!codec_index) /* For non-WBS */
+#endif
+ {
+ /* Use the application packet types (5 slot EV packets not allowed) */
+ params.packet_types = p_bta_ag_cfg->sco_pkt_types |
+ BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 |
+ BTM_SCO_PKT_TYPES_MASK_NO_3_EV5;
+ }
+ }
+
+ /* if initiating set current scb and peer bd addr */
+ if (is_orig)
+ {
+ /* Attempt to use eSCO if remote host supports HFP >= 1.5 */
+ /* Need to find out from SIG if HSP can use eSCO; for now use SCO */
+ if (p_scb->conn_service == BTA_AG_HFP && p_scb->peer_version >= HFP_VERSION_1_5 && !p_scb->retry_with_sco_only)
+ {
+
+ BTM_SetEScoMode(BTM_LINK_TYPE_ESCO, &params);
+ /* 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_AG_NO_EDR_ESCO))
+ {
+#if (BTM_WBS_INCLUDED == TRUE )
+ if (esco_codec != BTA_AG_CODEC_MSBC)
+ {
+ p_scb->retry_with_sco_only = TRUE;
+ APPL_TRACE_API0("Setting retry_with_sco_only to TRUE");
+ }
+ else /* Do not use SCO when using mSBC */
+ {
+ p_scb->retry_with_sco_only = FALSE;
+ APPL_TRACE_API0("Setting retry_with_sco_only to FALSE");
+ }
+#else
+ p_scb->retry_with_sco_only = TRUE;
+ APPL_TRACE_API0("Setting retry_with_sco_only to TRUE");
+#endif
+ }
+ }
+ else
+ {
+ if(p_scb->retry_with_sco_only)
+ APPL_TRACE_API0("retrying with SCO only");
+ p_scb->retry_with_sco_only = FALSE;
+
+ BTM_SetEScoMode(BTM_LINK_TYPE_SCO, &params);
+ }
+
+ bta_ag_cb.sco.p_curr_scb = p_scb;
+
+ /* tell sys to stop av if any */
+ bta_sys_sco_use(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr);
+
+ /* Allow any platform specific pre-SCO set up to take place */
+ bta_ag_co_audio_state(bta_ag_scb_to_idx(p_scb), p_scb->app_id, BTA_AG_CO_AUD_STATE_SETUP);
+
+#if (BTM_SCO_HCI_INCLUDED == TRUE )
+#if (BTM_WBS_INCLUDED == TRUE)
+ if (esco_codec == BTA_AG_CODEC_MSBC)
+ pcm_sample_rate = BTA_DM_SCO_SAMP_RATE_16K;
+ else
+#endif
+ pcm_sample_rate = BTA_DM_SCO_SAMP_RATE_8K;
+
+ sco_route = bta_dm_sco_co_init(pcm_sample_rate, pcm_sample_rate, &codec_info, p_scb->app_id);
+#endif
+
+#if (BTM_WBS_INCLUDED == TRUE )
+ if (esco_codec == BTA_AG_CODEC_MSBC)
+ {
+ /* Enable mSBC codec in fw */
+ BTM_SetWBSCodec (esco_codec);
+ }
+
+ /* Specify PCM input for SBC codec in fw */
+ BTM_ConfigI2SPCM (esco_codec, (UINT8)HCI_BRCM_I2SPCM_IS_DEFAULT_ROLE, (UINT8)HCI_BRCM_I2SPCM_SAMPLE_DEFAULT, (UINT8)HCI_BRCM_I2SPCM_CLOCK_DEFAULT);
+
+ /* This setting may not be necessary */
+ /* To be verified with stable 2049 boards */
+ if (esco_codec == BTA_AG_CODEC_MSBC)
+ BTM_WriteVoiceSettings (BTM_VOICE_SETTING_TRANS);
+ else
+ BTM_WriteVoiceSettings (BTM_VOICE_SETTING_CVSD);
+
+ /* save the current codec because sco_codec can be updated while SCO is open. */
+ p_scb->inuse_codec = esco_codec;
+#endif
+
+#if (BTM_SCO_HCI_INCLUDED == TRUE )
+ /* initialize SCO setup, no voice setting for AG, data rate <==> sample rate */
+ BTM_ConfigScoPath(sco_route, bta_ag_sco_read_cback, NULL, TRUE);
+#endif
+ bta_ag_cb.sco.cur_idx = p_scb->sco_idx;
+ }
+ else
+ p_scb->retry_with_sco_only = FALSE;
+
+ p_bd_addr = p_scb->peer_addr;
+
+ status = BTM_CreateSco(p_bd_addr, is_orig, params.packet_types,
+ &p_scb->sco_idx, bta_ag_sco_conn_cback,
+ bta_ag_sco_disc_cback);
+ if (status == BTM_CMD_STARTED)
+ {
+ if (!is_orig)
+ {
+ BTM_RegForEScoEvts(p_scb->sco_idx, bta_ag_esco_connreq_cback);
+ }
+ else /* Initiating the connection, set the current sco handle */
+ {
+ bta_ag_cb.sco.cur_idx = p_scb->sco_idx;
+ }
+ }
+
+ APPL_TRACE_API4("ag create sco: orig %d, inx 0x%04x, status 0x%x, pkt types 0x%04x",
+ is_orig, p_scb->sco_idx, status, params.packet_types);
+}
+
+#if (BTM_WBS_INCLUDED == TRUE )
+/*******************************************************************************
+**
+** Function bta_ag_cn_timer_cback
+**
+** Description
+**
+**
+** Returns void
+**
+*******************************************************************************/
+static void bta_ag_cn_timer_cback (TIMER_LIST_ENT *p_tle)
+{
+ tBTA_AG_SCB *p_scb;
+
+ if (p_tle)
+ {
+ p_scb = (tBTA_AG_SCB *)p_tle->param;
+
+ if (p_scb)
+ {
+ /* Announce that codec negotiation failed. */
+ bta_ag_sco_codec_nego(p_scb, FALSE);
+
+ /* call app callback */
+ bta_ag_cback_sco(p_scb, BTA_AG_AUDIO_CLOSE_EVT);
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function bta_ag_codec_negotiate
+**
+** Description Initiate codec negotiation by sending AT command.
+** If not necessary, skip negotiation.
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_ag_codec_negotiate(tBTA_AG_SCB *p_scb)
+{
+ bta_ag_cb.sco.p_curr_scb = p_scb;
+
+ if (p_scb->codec_updated || p_scb->codec_fallback)
+ {
+ /* Change the power mode to Active until sco open is completed. */
+ bta_sys_busy(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr);
+
+ /* Send +BCS to the peer */
+ bta_ag_send_bcs(p_scb, NULL);
+
+ /* Start timer to handle timeout */
+ p_scb->cn_timer.p_cback = (TIMER_CBACK*)&bta_ag_cn_timer_cback;
+ p_scb->cn_timer.param = (INT32)p_scb;
+ bta_sys_start_timer(&p_scb->cn_timer, 0, BTA_AG_CODEC_NEGO_TIMEOUT);
+ }
+ else
+ {
+ /* use same codec type as previous SCO connection, skip codec negotiation */
+ bta_ag_sco_codec_nego(p_scb, TRUE);
+ }
+}
+#endif
+
+/*******************************************************************************
+**
+** Function bta_ag_sco_event
+**
+** Description
+**
+**
+** Returns void
+**
+*******************************************************************************/
+static void bta_ag_sco_event(tBTA_AG_SCB *p_scb, UINT8 event)
+{
+ tBTA_AG_SCO_CB *p_sco = &bta_ag_cb.sco;
+#if (BTM_WBS_INCLUDED == TRUE )
+ tBTA_AG_SCB *p_cn_scb = NULL; /* For codec negotiation */
+#endif
+#if (BTM_SCO_HCI_INCLUDED == TRUE )
+ BT_HDR *p_buf;
+#endif
+#if BTA_AG_SCO_DEBUG == TRUE
+ UINT8 in_state = p_sco->state;
+
+ APPL_TRACE_EVENT5("BTA ag sco evt (hdl 0x%04x): State %d (%s), Event %d (%s)",
+ p_scb->sco_idx,
+ p_sco->state, bta_ag_sco_state_str(p_sco->state),
+ event, bta_ag_sco_evt_str(event));
+#else
+ APPL_TRACE_EVENT3("BTA ag sco evt (hdl 0x%04x): State %d, Event %d",
+ p_scb->sco_idx, p_sco->state, event);
+#endif
+
+#if (BTM_SCO_HCI_INCLUDED == TRUE )
+ if (event == BTA_AG_SCO_CI_DATA_E)
+ {
+ while (TRUE)
+ {
+ bta_dm_sco_co_out_data(&p_buf);
+ if (p_buf)
+ {
+ if (p_sco->state == BTA_AG_SCO_OPEN_ST)
+ BTM_WriteScoData(p_sco->p_curr_scb->sco_idx, p_buf);
+ else
+ GKI_freebuf(p_buf);
+ }
+ else
+ break;
+ }
+
+ return;
+ }
+#endif
+
+ switch (p_sco->state)
+ {
+ case BTA_AG_SCO_SHUTDOWN_ST:
+ switch (event)
+ {
+ case BTA_AG_SCO_LISTEN_E:
+ /* create sco listen connection */
+ bta_ag_create_sco(p_scb, FALSE);
+ p_sco->state = BTA_AG_SCO_LISTEN_ST;
+ break;
+
+ default:
+ APPL_TRACE_WARNING1("BTA_AG_SCO_SHUTDOWN_ST: Ignoring event %d", event);
+ break;
+ }
+ break;
+
+ case BTA_AG_SCO_LISTEN_ST:
+ switch (event)
+ {
+ case BTA_AG_SCO_LISTEN_E:
+ /* create sco listen connection (Additional channel) */
+ bta_ag_create_sco(p_scb, FALSE);
+ break;
+
+ case BTA_AG_SCO_OPEN_E:
+ /* remove listening connection */
+ bta_ag_remove_sco(p_scb, FALSE);
+
+#if (BTM_WBS_INCLUDED == TRUE )
+ /* start codec negotiation */
+ p_sco->state = BTA_AG_SCO_CODEC_ST;
+ p_cn_scb = p_scb;
+#else
+ /* create sco connection to peer */
+ bta_ag_create_sco(p_scb, TRUE);
+ p_sco->state = BTA_AG_SCO_OPENING_ST;
+#endif
+ break;
+
+ case BTA_AG_SCO_SHUTDOWN_E:
+ /* remove listening connection */
+ bta_ag_remove_sco(p_scb, FALSE);
+
+ if (p_scb == p_sco->p_curr_scb)
+ p_sco->p_curr_scb = NULL;
+
+ /* If last SCO instance then finish shutting down */
+ if (!bta_ag_other_scb_open(p_scb))
+ {
+ p_sco->state = BTA_AG_SCO_SHUTDOWN_ST;
+ }
+ break;
+
+ case BTA_AG_SCO_CLOSE_E:
+ /* remove listening connection */
+ /* Ignore the event. We need to keep listening SCO for the active SLC */
+ APPL_TRACE_WARNING1("BTA_AG_SCO_LISTEN_ST: Ignoring event %d", event);
+ break;
+
+ case BTA_AG_SCO_CONN_CLOSE_E:
+ /* sco failed; create sco listen connection */
+ bta_ag_create_sco(p_scb, FALSE);
+ p_sco->state = BTA_AG_SCO_LISTEN_ST;
+ break;
+
+ default:
+ APPL_TRACE_WARNING1("BTA_AG_SCO_LISTEN_ST: Ignoring event %d", event);
+ break;
+ }
+ break;
+
+#if (BTM_WBS_INCLUDED == TRUE )
+ case BTA_AG_SCO_CODEC_ST:
+ switch (event)
+ {
+ case BTA_AG_SCO_LISTEN_E:
+ /* create sco listen connection (Additional channel) */
+ bta_ag_create_sco(p_scb, FALSE);
+ break;
+
+ case BTA_AG_SCO_CN_DONE_E:
+ /* create sco connection to peer */
+ bta_ag_create_sco(p_scb, TRUE);
+ p_sco->state = BTA_AG_SCO_OPENING_ST;
+ break;
+
+ case BTA_AG_SCO_XFER_E:
+ /* save xfer scb */
+ p_sco->p_xfer_scb = p_scb;
+ p_sco->state = BTA_AG_SCO_CLOSE_XFER_ST;
+ break;
+
+ case BTA_AG_SCO_SHUTDOWN_E:
+ /* remove listening connection */
+ bta_ag_remove_sco(p_scb, FALSE);
+
+ if (p_scb == p_sco->p_curr_scb)
+ p_sco->p_curr_scb = NULL;
+
+ /* If last SCO instance then finish shutting down */
+ if (!bta_ag_other_scb_open(p_scb))
+ {
+ p_sco->state = BTA_AG_SCO_SHUTDOWN_ST;
+ }
+ break;
+
+ case BTA_AG_SCO_CLOSE_E:
+ /* sco open is not started yet. just go back to listening */
+ p_sco->state = BTA_AG_SCO_LISTEN_ST;
+ break;
+
+ case BTA_AG_SCO_CONN_CLOSE_E:
+ /* sco failed; create sco listen connection */
+ bta_ag_create_sco(p_scb, FALSE);
+ p_sco->state = BTA_AG_SCO_LISTEN_ST;
+ break;
+
+ default:
+ APPL_TRACE_WARNING1("BTA_AG_SCO_CODEC_ST: Ignoring event %d", event);
+ break;
+ }
+ break;
+#endif
+
+ case BTA_AG_SCO_OPENING_ST:
+ switch (event)
+ {
+ case BTA_AG_SCO_LISTEN_E:
+ /* second headset has now joined */
+ /* create sco listen connection (Additional channel) */
+ if (p_scb != p_sco->p_curr_scb)
+ {
+ bta_ag_create_sco(p_scb, FALSE);
+ }
+ break;
+
+#if (BTM_WBS_INCLUDED == TRUE)
+ case BTA_AG_SCO_REOPEN_E:
+ /* start codec negotiation */
+ p_sco->state = BTA_AG_SCO_CODEC_ST;
+ p_cn_scb = p_scb;
+ break;
+#endif
+
+ case BTA_AG_SCO_XFER_E:
+ /* save xfer scb */
+ p_sco->p_xfer_scb = p_scb;
+ p_sco->state = BTA_AG_SCO_CLOSE_XFER_ST;
+ break;
+
+ case BTA_AG_SCO_CLOSE_E:
+ p_sco->state = BTA_AG_SCO_OPEN_CL_ST;
+ break;
+
+ case BTA_AG_SCO_SHUTDOWN_E:
+ /* If not opening scb, just close it */
+ if (p_scb != p_sco->p_curr_scb)
+ {
+ /* remove listening connection */
+ bta_ag_remove_sco(p_scb, FALSE);
+ }
+ else
+ p_sco->state = BTA_AG_SCO_SHUTTING_ST;
+
+ break;
+
+ case BTA_AG_SCO_CONN_OPEN_E:
+ p_sco->state = BTA_AG_SCO_OPEN_ST;
+ break;
+
+ case BTA_AG_SCO_CONN_CLOSE_E:
+ /* sco failed; create sco listen connection */
+ bta_ag_create_sco(p_scb, FALSE);
+ p_sco->state = BTA_AG_SCO_LISTEN_ST;
+ break;
+
+ default:
+ APPL_TRACE_WARNING1("BTA_AG_SCO_OPENING_ST: Ignoring event %d", event);
+ break;
+ }
+ break;
+
+ case BTA_AG_SCO_OPEN_CL_ST:
+ switch (event)
+ {
+ case BTA_AG_SCO_XFER_E:
+ /* save xfer scb */
+ p_sco->p_xfer_scb = p_scb;
+
+ p_sco->state = BTA_AG_SCO_CLOSE_XFER_ST;
+ break;
+
+ case BTA_AG_SCO_OPEN_E:
+ p_sco->state = BTA_AG_SCO_OPENING_ST;
+ break;
+
+ case BTA_AG_SCO_SHUTDOWN_E:
+ /* If not opening scb, just close it */
+ if (p_scb != p_sco->p_curr_scb)
+ {
+ /* remove listening connection */
+ bta_ag_remove_sco(p_scb, FALSE);
+ }
+ else
+ p_sco->state = BTA_AG_SCO_SHUTTING_ST;
+
+ break;
+
+ case BTA_AG_SCO_CONN_OPEN_E:
+ /* close sco connection */
+ bta_ag_remove_sco(p_scb, TRUE);
+
+ p_sco->state = BTA_AG_SCO_CLOSING_ST;
+ break;
+
+ case BTA_AG_SCO_CONN_CLOSE_E:
+ /* sco failed; create sco listen connection */
+
+ p_sco->state = BTA_AG_SCO_LISTEN_ST;
+ break;
+
+ default:
+ APPL_TRACE_WARNING1("BTA_AG_SCO_OPEN_CL_ST: Ignoring event %d", event);
+ break;
+ }
+ break;
+
+ case BTA_AG_SCO_OPEN_XFER_ST:
+ switch (event)
+ {
+ case BTA_AG_SCO_CLOSE_E:
+ /* close sco connection */
+ bta_ag_remove_sco(p_scb, TRUE);
+
+ p_sco->state = BTA_AG_SCO_CLOSING_ST;
+ break;
+
+ case BTA_AG_SCO_SHUTDOWN_E:
+ /* remove all connection */
+ bta_ag_remove_sco(p_scb, FALSE);
+ p_sco->state = BTA_AG_SCO_SHUTTING_ST;
+
+ break;
+
+ case BTA_AG_SCO_CONN_CLOSE_E:
+ /* closed sco; place in listen mode and
+ accept the transferred connection */
+ bta_ag_create_sco(p_scb, FALSE); /* Back into listen mode */
+
+ /* Accept sco connection with xfer scb */
+ bta_ag_sco_conn_rsp(p_sco->p_xfer_scb, &p_sco->conn_data);
+ p_sco->state = BTA_AG_SCO_OPENING_ST;
+ p_sco->p_curr_scb = p_sco->p_xfer_scb;
+ p_sco->cur_idx = p_sco->p_xfer_scb->sco_idx;
+ p_sco->p_xfer_scb = NULL;
+ break;
+
+ default:
+ APPL_TRACE_WARNING1("BTA_AG_SCO_OPEN_XFER_ST: Ignoring event %d", event);
+ break;
+ }
+ break;
+
+ case BTA_AG_SCO_OPEN_ST:
+ switch (event)
+ {
+ case BTA_AG_SCO_LISTEN_E:
+ /* second headset has now joined */
+ /* create sco listen connection (Additional channel) */
+ if (p_scb != p_sco->p_curr_scb)
+ {
+ bta_ag_create_sco(p_scb, FALSE);
+ }
+ break;
+
+ case BTA_AG_SCO_XFER_E:
+ /* close current sco connection */
+ bta_ag_remove_sco(p_sco->p_curr_scb, TRUE);
+
+ /* save xfer scb */
+ p_sco->p_xfer_scb = p_scb;
+
+ p_sco->state = BTA_AG_SCO_CLOSE_XFER_ST;
+ break;
+
+ case BTA_AG_SCO_CLOSE_E:
+ /* close sco connection if active */
+ if (bta_ag_remove_sco(p_scb, TRUE))
+ {
+ p_sco->state = BTA_AG_SCO_CLOSING_ST;
+ }
+ break;
+
+ case BTA_AG_SCO_SHUTDOWN_E:
+ /* remove all listening connections */
+ bta_ag_remove_sco(p_scb, FALSE);
+
+ /* If SCO was active on this scb, close it */
+ if (p_scb == p_sco->p_curr_scb)
+ {
+ p_sco->state = BTA_AG_SCO_SHUTTING_ST;
+ }
+ break;
+
+ case BTA_AG_SCO_CONN_CLOSE_E:
+ /* peer closed sco; create sco listen connection */
+ bta_ag_create_sco(p_scb, FALSE);
+ p_sco->state = BTA_AG_SCO_LISTEN_ST;
+ break;
+
+ default:
+ APPL_TRACE_WARNING1("BTA_AG_SCO_OPEN_ST: Ignoring event %d", event);
+ break;
+ }
+ break;
+
+ case BTA_AG_SCO_CLOSING_ST:
+ switch (event)
+ {
+ case BTA_AG_SCO_LISTEN_E:
+ /* create sco listen connection (Additional channel) */
+ if (p_scb != p_sco->p_curr_scb)
+ {
+ bta_ag_create_sco(p_scb, FALSE);
+ }
+ break;
+
+ case BTA_AG_SCO_OPEN_E:
+ p_sco->state = BTA_AG_SCO_CLOSE_OP_ST;
+ break;
+
+ case BTA_AG_SCO_XFER_E:
+ /* save xfer scb */
+ p_sco->p_xfer_scb = p_scb;
+
+ p_sco->state = BTA_AG_SCO_CLOSE_XFER_ST;
+ break;
+
+ case BTA_AG_SCO_SHUTDOWN_E:
+ /* If not closing scb, just close it */
+ if (p_scb != p_sco->p_curr_scb)
+ {
+ /* remove listening connection */
+ bta_ag_remove_sco(p_scb, FALSE);
+ }
+ else
+ p_sco->state = BTA_AG_SCO_SHUTTING_ST;
+
+ break;
+
+ case BTA_AG_SCO_CONN_CLOSE_E:
+ /* peer closed sco; create sco listen connection */
+ bta_ag_create_sco(p_scb, FALSE);
+
+ p_sco->state = BTA_AG_SCO_LISTEN_ST;
+ break;
+
+ default:
+ APPL_TRACE_WARNING1("BTA_AG_SCO_CLOSING_ST: Ignoring event %d", event);
+ break;
+ }
+ break;
+
+ case BTA_AG_SCO_CLOSE_OP_ST:
+ switch (event)
+ {
+ case BTA_AG_SCO_CLOSE_E:
+ p_sco->state = BTA_AG_SCO_CLOSING_ST;
+ break;
+
+ case BTA_AG_SCO_SHUTDOWN_E:
+ p_sco->state = BTA_AG_SCO_SHUTTING_ST;
+ break;
+
+ case BTA_AG_SCO_CONN_CLOSE_E:
+#if (BTM_WBS_INCLUDED == TRUE )
+ /* start codec negotiation */
+ p_sco->state = BTA_AG_SCO_CODEC_ST;
+ p_cn_scb = p_scb;
+#else
+ /* open sco connection */
+ bta_ag_create_sco(p_scb, TRUE);
+ p_sco->state = BTA_AG_SCO_OPENING_ST;
+#endif
+ break;
+
+ case BTA_AG_SCO_LISTEN_E:
+ /* create sco listen connection (Additional channel) */
+ if (p_scb != p_sco->p_curr_scb)
+ {
+ bta_ag_create_sco(p_scb, FALSE);
+ }
+ break;
+
+ default:
+ APPL_TRACE_WARNING1("BTA_AG_SCO_CLOSE_OP_ST: Ignoring event %d", event);
+ break;
+ }
+ break;
+
+ case BTA_AG_SCO_CLOSE_XFER_ST:
+ switch (event)
+ {
+ case BTA_AG_SCO_CONN_OPEN_E:
+ /* close sco connection so headset can be transferred
+ Probably entered this state from "opening state" */
+ bta_ag_remove_sco(p_scb, TRUE);
+ break;
+
+ case BTA_AG_SCO_CLOSE_E:
+ /* clear xfer scb */
+ p_sco->p_xfer_scb = NULL;
+
+ p_sco->state = BTA_AG_SCO_CLOSING_ST;
+ break;
+
+ case BTA_AG_SCO_SHUTDOWN_E:
+ /* clear xfer scb */
+ p_sco->p_xfer_scb = NULL;
+
+ p_sco->state = BTA_AG_SCO_SHUTTING_ST;
+ break;
+
+ case BTA_AG_SCO_CONN_CLOSE_E:
+ /* closed sco; place old sco in listen mode,
+ take current sco out of listen, and
+ create originating sco for current */
+ bta_ag_create_sco(p_scb, FALSE);
+ bta_ag_remove_sco(p_sco->p_xfer_scb, FALSE);
+
+#if (BTM_WBS_INCLUDED == TRUE )
+ /* start codec negotiation */
+ p_sco->state = BTA_AG_SCO_CODEC_ST;
+ p_cn_scb = p_sco->p_xfer_scb;
+ p_sco->p_xfer_scb = NULL;
+#else
+ /* create sco connection to peer */
+ bta_ag_create_sco(p_sco->p_xfer_scb, TRUE);
+ p_sco->p_xfer_scb = NULL;
+ p_sco->state = BTA_AG_SCO_OPENING_ST;
+#endif
+ break;
+
+ default:
+ APPL_TRACE_WARNING1("BTA_AG_SCO_CLOSE_XFER_ST: Ignoring event %d", event);
+ break;
+ }
+ break;
+
+ case BTA_AG_SCO_SHUTTING_ST:
+ switch (event)
+ {
+ case BTA_AG_SCO_CONN_OPEN_E:
+ /* close sco connection; wait for conn close event */
+ bta_ag_remove_sco(p_scb, TRUE);
+ break;
+
+ case BTA_AG_SCO_CONN_CLOSE_E:
+ /* If last SCO instance then finish shutting down */
+ if (!bta_ag_other_scb_open(p_scb))
+ {
+ p_sco->state = BTA_AG_SCO_SHUTDOWN_ST;
+ }
+ else /* Other instance is still listening */
+ {
+ p_sco->state = BTA_AG_SCO_LISTEN_ST;
+ }
+
+ if (p_scb == p_sco->p_curr_scb)
+ {
+ p_sco->p_curr_scb->sco_idx = BTM_INVALID_SCO_INDEX;
+ p_sco->p_curr_scb = NULL;
+ }
+ break;
+
+ case BTA_AG_SCO_LISTEN_E:
+ /* create sco listen connection (Additional channel) */
+ if (p_scb != p_sco->p_curr_scb)
+ {
+ bta_ag_create_sco(p_scb, FALSE);
+ }
+ break;
+
+ case BTA_AG_SCO_SHUTDOWN_E:
+ if (!bta_ag_other_scb_open(p_scb))
+ {
+ p_sco->state = BTA_AG_SCO_SHUTDOWN_ST;
+ }
+ else /* Other instance is still listening */
+ {
+ p_sco->state = BTA_AG_SCO_LISTEN_ST;
+ }
+
+ if (p_scb == p_sco->p_curr_scb)
+ {
+ p_sco->p_curr_scb->sco_idx = BTM_INVALID_SCO_INDEX;
+ p_sco->p_curr_scb = NULL;
+ }
+ break;
+
+ default:
+ APPL_TRACE_WARNING1("BTA_AG_SCO_SHUTTING_ST: Ignoring event %d", event);
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+#if BTA_AG_SCO_DEBUG == TRUE
+ if (p_sco->state != in_state)
+ {
+ APPL_TRACE_EVENT3("BTA AG SCO State Change: [%s] -> [%s] after Event [%s]",
+ bta_ag_sco_state_str(in_state),
+ bta_ag_sco_state_str(p_sco->state),
+ bta_ag_sco_evt_str(event));
+ }
+#endif
+
+#if (BTM_WBS_INCLUDED == TRUE )
+ if (p_cn_scb)
+ {
+ bta_ag_codec_negotiate(p_cn_scb);
+ }
+#endif
+}
+
+/*******************************************************************************
+**
+** Function bta_ag_sco_is_open
+**
+** Description Check if sco is open for this scb.
+**
+**
+** Returns TRUE if sco open for this scb, FALSE otherwise.
+**
+*******************************************************************************/
+BOOLEAN bta_ag_sco_is_open(tBTA_AG_SCB *p_scb)
+{
+ return ((bta_ag_cb.sco.state == BTA_AG_SCO_OPEN_ST) &&
+ (bta_ag_cb.sco.p_curr_scb == p_scb));
+}
+
+/*******************************************************************************
+**
+** Function bta_ag_sco_is_opening
+**
+** Description Check if sco is in Opening state.
+**
+**
+** Returns TRUE if sco is in Opening state for this scb, FALSE otherwise.
+**
+*******************************************************************************/
+BOOLEAN bta_ag_sco_is_opening(tBTA_AG_SCB *p_scb)
+{
+#if (BTM_WBS_INCLUDED == TRUE )
+ return (((bta_ag_cb.sco.state == BTA_AG_SCO_OPENING_ST) ||
+ (bta_ag_cb.sco.state == BTA_AG_SCO_OPENING_ST)) &&
+ (bta_ag_cb.sco.p_curr_scb == p_scb));
+#else
+ return ((bta_ag_cb.sco.state == BTA_AG_SCO_OPENING_ST) &&
+ (bta_ag_cb.sco.p_curr_scb == p_scb));
+#endif
+}
+
+/*******************************************************************************
+**
+** Function bta_ag_sco_listen
+**
+** Description
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_ag_sco_listen(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
+{
+ bta_ag_sco_event(p_scb, BTA_AG_SCO_LISTEN_E);
+}
+
+/*******************************************************************************
+**
+** Function bta_ag_sco_open
+**
+** Description
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_ag_sco_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
+{
+ UINT8 event;
+
+ /* if another scb using sco, this is a transfer */
+ if (bta_ag_cb.sco.p_curr_scb != NULL && bta_ag_cb.sco.p_curr_scb != p_scb)
+ {
+ event = BTA_AG_SCO_XFER_E;
+ }
+ /* else it is an open */
+ else
+ {
+ event = BTA_AG_SCO_OPEN_E;
+ }
+
+ bta_ag_sco_event(p_scb, event);
+}
+
+/*******************************************************************************
+**
+** Function bta_ag_sco_close
+**
+** Description
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_ag_sco_close(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
+{
+ /* if scb is in use */
+#if (BTM_WBS_INCLUDED == TRUE )
+ /* sco_idx is not allocated in SCO_CODEC_ST, we still need to move to listening state. */
+ if ((p_scb->sco_idx != BTM_INVALID_SCO_INDEX) || (bta_ag_cb.sco.state == BTA_AG_SCO_CODEC_ST))
+#else
+ if (p_scb->sco_idx != BTM_INVALID_SCO_INDEX)
+#endif
+ {
+ APPL_TRACE_DEBUG1("bta_ag_sco_close: sco_inx = %d", p_scb->sco_idx);
+ bta_ag_sco_event(p_scb, BTA_AG_SCO_CLOSE_E);
+ }
+}
+
+#if (BTM_WBS_INCLUDED == TRUE )
+
+/*******************************************************************************
+**
+** Function bta_ag_sco_codec_nego
+**
+** Description
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_ag_sco_codec_nego(tBTA_AG_SCB *p_scb, BOOLEAN result)
+{
+ if(result == TRUE)
+ {
+ /* Subsequent sco connection will skip codec negotiation */
+ p_scb->codec_updated = FALSE;
+
+ bta_ag_sco_event(p_scb, BTA_AG_SCO_CN_DONE_E);
+ }
+ else /* codec negotiation failed */
+ bta_ag_sco_event(p_scb, BTA_AG_SCO_CLOSE_E);
+}
+#endif
+
+/*******************************************************************************
+**
+** Function bta_ag_sco_shutdown
+**
+** Description
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_ag_sco_shutdown(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
+{
+ bta_ag_sco_event(p_scb, BTA_AG_SCO_SHUTDOWN_E);
+}
+
+/*******************************************************************************
+**
+** Function bta_ag_sco_conn_open
+**
+** Description
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_ag_sco_conn_open(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
+{
+ bta_ag_sco_event(p_scb, BTA_AG_SCO_CONN_OPEN_E);
+
+ bta_sys_sco_open(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr);
+
+ bta_ag_co_audio_state(bta_ag_scb_to_idx(p_scb), p_scb->app_id, BTA_AG_CO_AUD_STATE_ON);
+
+#if (BTM_SCO_HCI_INCLUDED == TRUE )
+ /* open SCO codec if SCO is routed through transport */
+ bta_dm_sco_co_open(bta_ag_scb_to_idx(p_scb), BTA_SCO_OUT_PKT_SIZE, BTA_AG_CI_SCO_DATA_EVT);
+#endif
+
+ /* call app callback */
+ bta_ag_cback_sco(p_scb, BTA_AG_AUDIO_OPEN_EVT);
+
+ p_scb->retry_with_sco_only = FALSE;
+}
+
+/*******************************************************************************
+**
+** Function bta_ag_sco_conn_close
+**
+** Description
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_ag_sco_conn_close(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
+{
+ UINT16 handle = bta_ag_scb_to_idx(p_scb);
+
+ /* clear current scb */
+ bta_ag_cb.sco.p_curr_scb = NULL;
+ p_scb->sco_idx = BTM_INVALID_SCO_INDEX;
+
+#if (BTM_WBS_INCLUDED == TRUE)
+ /* codec_fallback is set when AG is initiator and connection failed for mSBC. */
+ if (p_scb->codec_fallback && p_scb->svc_conn)
+ {
+ bta_ag_sco_event(p_scb, BTA_AG_SCO_REOPEN_E);
+ }
+ else if (p_scb->retry_with_sco_only && p_scb->svc_conn)
+ {
+ /* retry_with_sco_only is set when AG is initiator and connection failed for eSCO */
+ bta_ag_create_sco(p_scb, TRUE);
+ }
+#else
+ /* retry_with_sco_only, will be set only when AG is initiator
+ ** and AG is first trying to establish an eSCO connection */
+ if (p_scb->retry_with_sco_only && p_scb->svc_conn)
+ {
+ bta_ag_create_sco(p_scb, TRUE);
+ }
+#endif
+ else
+ {
+ /* Indicate if the closing of audio is because of transfer */
+ if (bta_ag_cb.sco.p_xfer_scb)
+ bta_ag_co_audio_state(handle, p_scb->app_id, BTA_AG_CO_AUD_STATE_OFF_XFER);
+ else
+ bta_ag_co_audio_state(handle, p_scb->app_id, BTA_AG_CO_AUD_STATE_OFF);
+
+ bta_ag_sco_event(p_scb, BTA_AG_SCO_CONN_CLOSE_E);
+
+ bta_sys_sco_close(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr);
+
+ /* if av got suspended by this call, let it resume. */
+ /* In case call stays alive regardless of sco, av should not be affected. */
+ if(((p_scb->call_ind == BTA_AG_CALL_INACTIVE) && (p_scb->callsetup_ind == BTA_AG_CALLSETUP_NONE))
+ || (p_scb->post_sco == BTA_AG_POST_SCO_CALL_END))
+ {
+ bta_sys_sco_unuse(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr);
+ }
+
+ /* call app callback */
+ bta_ag_cback_sco(p_scb, BTA_AG_AUDIO_CLOSE_EVT);
+ }
+ p_scb->retry_with_sco_only = FALSE;
+}
+
+/*******************************************************************************
+**
+** Function bta_ag_sco_conn_rsp
+**
+** Description Process the SCO connection request
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_ag_sco_conn_rsp(tBTA_AG_SCB *p_scb, tBTM_ESCO_CONN_REQ_EVT_DATA *p_data)
+{
+ tBTM_ESCO_PARAMS resp;
+ UINT8 hci_status = HCI_SUCCESS;
+#if (BTM_SCO_HCI_INCLUDED == TRUE )
+ tBTA_CODEC_INFO codec_info = {BTA_SCO_CODEC_PCM};
+ UINT32 pcm_sample_rate;
+#endif
+
+ if (bta_ag_cb.sco.state == BTA_AG_SCO_LISTEN_ST ||
+ bta_ag_cb.sco.state == BTA_AG_SCO_CLOSE_XFER_ST ||
+ bta_ag_cb.sco.state == BTA_AG_SCO_OPEN_XFER_ST)
+ {
+ /* If script overrided sco parameter by BTA_CMD_SET_ESCO_PARAM */
+ if (bta_ag_cb.sco.param_updated)
+ {
+ resp = bta_ag_cb.sco.params;
+ }
+ else
+ {
+ resp.rx_bw = BTM_64KBITS_RATE;
+ resp.tx_bw = BTM_64KBITS_RATE;
+ resp.max_latency = 10;
+ resp.voice_contfmt = 0x60;
+ resp.retrans_effort = BTM_ESCO_RETRANS_POWER;
+
+ if (p_data->link_type == BTM_LINK_TYPE_SCO)
+ {
+ resp.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);
+ }
+ else /* Allow controller to use all types available except 5-slot EDR */
+ {
+ resp.packet_types = (BTM_SCO_LINK_ALL_PKT_MASK |
+ BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 |
+ BTM_SCO_PKT_TYPES_MASK_NO_3_EV5);
+ }
+ }
+
+ /* tell sys to stop av if any */
+ bta_sys_sco_use(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr);
+
+ /* Allow any platform specific pre-SCO set up to take place */
+ bta_ag_co_audio_state(bta_ag_scb_to_idx(p_scb), p_scb->app_id, BTA_AG_CO_AUD_STATE_SETUP);
+
+#if (BTM_WBS_INCLUDED == TRUE )
+ /* When HS initiated SCO, it cannot be WBS. */
+ BTM_ConfigI2SPCM (BTM_SCO_CODEC_CVSD, (UINT8)HCI_BRCM_I2SPCM_IS_DEFAULT_ROLE, (UINT8)HCI_BRCM_I2SPCM_SAMPLE_DEFAULT, (UINT8)HCI_BRCM_I2SPCM_CLOCK_DEFAULT);
+#endif
+
+#if (BTM_SCO_HCI_INCLUDED == TRUE )
+ pcm_sample_rate = BTA_DM_SCO_SAMP_RATE_8K;
+
+ /* initialize SCO setup, no voice setting for AG, data rate <==> sample rate */
+ BTM_ConfigScoPath(bta_dm_sco_co_init(pcm_sample_rate, pcm_sample_rate, &codec_info, p_scb->app_id),
+ bta_ag_sco_read_cback, NULL, TRUE);
+#endif
+ }
+ else
+ hci_status = HCI_ERR_HOST_REJECT_DEVICE;
+
+#if (BTM_WBS_INCLUDED == TRUE )
+ /* If SCO open was initiated from HS, it must be CVSD */
+ p_scb->inuse_codec = BTA_AG_CODEC_NONE;
+#endif
+
+ BTM_EScoConnRsp(p_data->sco_inx, hci_status, &resp);
+}
+
+/*******************************************************************************
+**
+** Function bta_ag_ci_sco_data
+**
+** Description Process the SCO data ready callin event
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_ag_ci_sco_data(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data)
+{
+#if (BTM_SCO_HCI_INCLUDED == TRUE )
+ bta_ag_sco_event(p_scb, BTA_AG_SCO_CI_DATA_E);
+#endif
+}
+
+/*******************************************************************************
+**
+** Function bta_ag_set_esco_param
+**
+** Description Update esco parameters from script wrapper.
+**
+**
+** Returns void
+**
+*******************************************************************************/
+void bta_ag_set_esco_param(BOOLEAN set_reset, tBTM_ESCO_PARAMS *param)
+{
+ if(set_reset == FALSE) /* reset the parameters to default */
+ {
+ bta_ag_cb.sco.param_updated = FALSE;
+ APPL_TRACE_DEBUG0("bta_ag_set_esco_param : Resetting ESCO parameters to default");
+ }
+ else
+ {
+ bta_ag_cb.sco.param_updated = TRUE;
+ bta_ag_cb.sco.params = *param;
+ APPL_TRACE_DEBUG0("bta_ag_set_esco_param : Setting ESCO parameters");
+ }
+}
+
+/*******************************************************************************
+** Debugging functions
+*******************************************************************************/
+
+#if BTA_AG_SCO_DEBUG == TRUE
+static char *bta_ag_sco_evt_str(UINT8 event)
+{
+ switch (event)
+ {
+ case BTA_AG_SCO_LISTEN_E:
+ return "Listen Request";
+ case BTA_AG_SCO_OPEN_E:
+ return "Open Request";
+ case BTA_AG_SCO_XFER_E:
+ return "Transfer Request";
+#if (BTM_WBS_INCLUDED == TRUE )
+ case BTA_AG_SCO_CN_DONE_E:
+ return "Codec Negotiation Done";
+ case BTA_AG_SCO_REOPEN_E:
+ return "Reopen Request";
+#endif
+ case BTA_AG_SCO_CLOSE_E:
+ return "Close Request";
+ case BTA_AG_SCO_SHUTDOWN_E:
+ return "Shutdown Request";
+ case BTA_AG_SCO_CONN_OPEN_E:
+ return "Opened";
+ case BTA_AG_SCO_CONN_CLOSE_E:
+ return "Closed";
+ case BTA_AG_SCO_CI_DATA_E :
+ return "Sco Data";
+ default:
+ return "Unknown SCO Event";
+ }
+}
+
+static char *bta_ag_sco_state_str(UINT8 state)
+{
+ switch (state)
+ {
+ case BTA_AG_SCO_SHUTDOWN_ST:
+ return "Shutdown";
+ case BTA_AG_SCO_LISTEN_ST:
+ return "Listening";
+#if (BTM_WBS_INCLUDED == TRUE )
+ case BTA_AG_SCO_CODEC_ST:
+ return "Codec Negotiation";
+#endif
+ case BTA_AG_SCO_OPENING_ST:
+ return "Opening";
+ case BTA_AG_SCO_OPEN_CL_ST:
+ return "Open while closing";
+ case BTA_AG_SCO_OPEN_XFER_ST:
+ return "Opening while Transferring";
+ case BTA_AG_SCO_OPEN_ST:
+ return "Open";
+ case BTA_AG_SCO_CLOSING_ST:
+ return "Closing";
+ case BTA_AG_SCO_CLOSE_OP_ST:
+ return "Close while Opening";
+ case BTA_AG_SCO_CLOSE_XFER_ST:
+ return "Close while Transferring";
+ case BTA_AG_SCO_SHUTTING_ST:
+ return "Shutting Down";
+ default:
+ return "Unknown SCO State";
+ }
+}
+
+#endif