You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1496 lines
48 KiB
1496 lines
48 KiB
/****************************************************************************** |
|
* |
|
* 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 <stddef.h> |
|
|
|
#include "bt_common.h" |
|
#include "bta_ag_api.h" |
|
#include "bta_ag_co.h" |
|
#include "bta_ag_int.h" |
|
#include "bta_api.h" |
|
#if (BTM_SCO_HCI_INCLUDED == TRUE) |
|
#include "bta_dm_co.h" |
|
#endif |
|
#include "btm_api.h" |
|
#include "device/include/controller.h" |
|
#include "device/include/esco_parameters.h" |
|
#include "osi/include/osi.h" |
|
#include "utl.h" |
|
|
|
#if defined(MTK_INTEROP_EXTENSION) && (MTK_INTEROP_EXTENSION == TRUE) |
|
#include "mediatek/include/interop_mtk.h" |
|
#endif |
|
|
|
#ifndef BTA_AG_SCO_DEBUG |
|
#define BTA_AG_SCO_DEBUG FALSE |
|
#endif |
|
|
|
/* Codec negotiation timeout */ |
|
#ifndef BTA_AG_CODEC_NEGOTIATION_TIMEOUT_MS |
|
/** M: Change the codec negotiation timeout from 3s to 15s @{ */ |
|
#define BTA_AG_CODEC_NEGOTIATION_TIMEOUT_MS (15 * 1000) /* 15 seconds */ |
|
/** @} */ |
|
#endif |
|
|
|
#if (BTA_AG_SCO_DEBUG == TRUE) |
|
static char* bta_ag_sco_evt_str(uint8_t event); |
|
static char* bta_ag_sco_state_str(uint8_t state); |
|
#endif |
|
|
|
static bool sco_allowed = true; |
|
|
|
/** M: Modify for SCO retry @{ */ |
|
static bool retry_with_sco_only = false; /* indicator to try with SCO only when eSCO fails */ |
|
/** @} */ |
|
|
|
#define BTA_AG_NO_EDR_ESCO \ |
|
(ESCO_PKT_TYPES_MASK_NO_2_EV3 | ESCO_PKT_TYPES_MASK_NO_3_EV3 | \ |
|
ESCO_PKT_TYPES_MASK_NO_2_EV5 | ESCO_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 */ |
|
BTA_AG_SCO_CN_DONE_E, /* codec negotiation done */ |
|
BTA_AG_SCO_REOPEN_E, /* Retry with other codec when failed */ |
|
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 */ |
|
}; |
|
|
|
static void bta_ag_create_pending_sco(tBTA_AG_SCB* p_scb, bool is_local); |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_ag_sco_conn_cback |
|
* |
|
* Description BTM SCO connection callback. |
|
* |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
static void bta_ag_sco_conn_cback(uint16_t sco_idx) { |
|
uint16_t handle; |
|
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) { |
|
BT_HDR* p_buf = (BT_HDR*)osi_malloc(sizeof(BT_HDR)); |
|
p_buf->event = BTA_AG_SCO_OPEN_EVT; |
|
p_buf->layer_specific = handle; |
|
bta_sys_sendmsg(p_buf); |
|
} else { |
|
/* no match found; disconnect sco, init sco variables */ |
|
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_t sco_idx) { |
|
uint16_t handle = 0; |
|
|
|
APPL_TRACE_DEBUG( |
|
"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_DEBUG( |
|
"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_DEBUG( |
|
"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(ESCO_DATA_PATH_PCM, NULL, NULL, true); |
|
APPL_TRACE_DEBUG("%s: sco close config status = %d", __func__, status); |
|
/* SCO clean up here */ |
|
bta_dm_sco_co_close(); |
|
#endif |
|
|
|
/* Restore settings */ |
|
if (bta_ag_cb.sco.p_curr_scb->inuse_codec == BTA_AG_CODEC_MSBC) { |
|
/* Bypass vendor specific and voice settings if enhanced eSCO supported */ |
|
/** M: Change feature For enhanced setup synchronous connection. @{ */ |
|
// Always to set the voice setting. |
|
/*if (!(controller_get_interface() |
|
->supports_enhanced_setup_synchronous_connection())) {*/ |
|
BTM_WriteVoiceSettings(BTM_VOICE_SETTING_CVSD); |
|
/*}*/ |
|
/** @} */ |
|
|
|
/* If SCO open was initiated by AG and failed for mSBC T2, try mSBC T1 |
|
* 'Safe setting' first. If T1 also fails, try CVSD */ |
|
if (bta_ag_sco_is_opening(bta_ag_cb.sco.p_curr_scb)) { |
|
bta_ag_cb.sco.p_curr_scb->state = BTA_AG_SCO_CODEC_ST; |
|
if (bta_ag_cb.sco.p_curr_scb->codec_msbc_settings == |
|
BTA_AG_SCO_MSBC_SETTINGS_T2) { |
|
APPL_TRACE_WARNING( |
|
"%s: eSCO/SCO failed to open, falling back to mSBC T1 settings", |
|
__func__); |
|
bta_ag_cb.sco.p_curr_scb->codec_msbc_settings = |
|
BTA_AG_SCO_MSBC_SETTINGS_T1; |
|
} else { |
|
APPL_TRACE_WARNING( |
|
"%s: eSCO/SCO failed to open, falling back to CVSD", __func__); |
|
bta_ag_cb.sco.p_curr_scb->codec_fallback = true; |
|
} |
|
} |
|
} else if (bta_ag_sco_is_opening(bta_ag_cb.sco.p_curr_scb)) { |
|
APPL_TRACE_ERROR("%s: eSCO/SCO failed to open, no more fall back", |
|
__func__); |
|
} |
|
|
|
bta_ag_cb.sco.p_curr_scb->inuse_codec = BTA_AG_CODEC_NONE; |
|
|
|
BT_HDR* p_buf = (BT_HDR*)osi_malloc(sizeof(BT_HDR)); |
|
p_buf->event = BTA_AG_SCO_CLOSE_EVT; |
|
p_buf->layer_specific = handle; |
|
bta_sys_sendmsg(p_buf); |
|
} else { |
|
/* no match found */ |
|
APPL_TRACE_DEBUG("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_t sco_inx, BT_HDR* p_data, |
|
tBTM_SCO_DATA_FLAG status) { |
|
if (status != BTM_SCO_DATA_CORRECT) { |
|
APPL_TRACE_DEBUG("%s: status %d", __func__, 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 bool - true if SCO removal was started |
|
* |
|
******************************************************************************/ |
|
static bool bta_ag_remove_sco(tBTA_AG_SCB* p_scb, bool only_active) { |
|
if (p_scb->sco_idx != BTM_INVALID_SCO_INDEX) { |
|
if (!only_active || p_scb->sco_idx == bta_ag_cb.sco.cur_idx) { |
|
tBTM_STATUS status = BTM_RemoveSco(p_scb->sco_idx); |
|
APPL_TRACE_DEBUG("%s: SCO index 0x%04x, status %d", __func__, |
|
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; |
|
return true; |
|
} else if ((status == BTM_SUCCESS) || (status == BTM_UNKNOWN_ADDR)) { |
|
/* If no connection reset the SCO handle */ |
|
p_scb->sco_idx = BTM_INVALID_SCO_INDEX; |
|
} |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* 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_t handle; |
|
uint16_t 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_EVENT("%s: Accept Conn Request (sco_inx 0x%04x)", __func__, |
|
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_DEBUG("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_ERROR( |
|
"%s: Nothing to remove,so accept Conn Request(sco_inx 0x%04x)", |
|
__func__, 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); |
|
} |
|
} |
|
} else { |
|
/* If error occurred send reject response immediately */ |
|
APPL_TRACE_WARNING( |
|
"no scb for bta_ag_esco_connreq_cback or no resources"); |
|
BTM_EScoConnRsp(p_data->conn_evt.sco_inx, HCI_ERR_HOST_REJECT_RESOURCES, |
|
(enh_esco_params_t*)NULL); |
|
} |
|
} else if (event == BTM_ESCO_CHG_EVT) { |
|
/* Received a change in the esco link */ |
|
APPL_TRACE_EVENT( |
|
"%s: eSCO change event (inx %d): rtrans %d, " |
|
"rxlen %d, txlen %d, txint %d", |
|
__func__, 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_t 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 Create a SCO connection for a given control block |
|
* p_scb : Pointer to the target AG control block |
|
* is_orig : Whether to initiate or listen for SCO connection |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
static void bta_ag_create_sco(tBTA_AG_SCB* p_scb, bool is_orig) { |
|
APPL_TRACE_DEBUG( |
|
"%s: BEFORE codec_updated=%d, codec_fallback=%d, " |
|
"sco_codec=%d, peer_codec=%d, msbc_settings=%d", |
|
__func__, p_scb->codec_updated, p_scb->codec_fallback, p_scb->sco_codec, |
|
p_scb->peer_codecs, p_scb->codec_msbc_settings); |
|
tBTA_AG_PEER_CODEC esco_codec = BTA_AG_CODEC_CVSD; |
|
|
|
/* Make sure this SCO handle is not already in use */ |
|
if (p_scb->sco_idx != BTM_INVALID_SCO_INDEX) { |
|
APPL_TRACE_ERROR("%s: Index 0x%04x already in use!", __func__, |
|
p_scb->sco_idx); |
|
return; |
|
} |
|
|
|
/** M: Modify for SCO retry @{ */ |
|
if ((p_scb->sco_codec == BTA_AG_CODEC_MSBC) && !p_scb->codec_fallback && !retry_with_sco_only) |
|
/** @} */ |
|
esco_codec = BTA_AG_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; |
|
/* Reset mSBC settings to T2 for the next audio connection */ |
|
p_scb->codec_msbc_settings = BTA_AG_SCO_MSBC_SETTINGS_T2; |
|
} |
|
|
|
esco_codec_t codec_index = ESCO_CODEC_CVSD; |
|
/* If WBS included, use CVSD by default, index is 0 for CVSD by |
|
* initialization. If eSCO codec is mSBC, index is T2 or T1 */ |
|
if (esco_codec == BTA_AG_CODEC_MSBC) { |
|
if (p_scb->codec_msbc_settings == BTA_AG_SCO_MSBC_SETTINGS_T2) { |
|
codec_index = ESCO_CODEC_MSBC_T2; |
|
} else { |
|
codec_index = ESCO_CODEC_MSBC_T1; |
|
} |
|
} |
|
|
|
/* Initialize eSCO parameters */ |
|
enh_esco_params_t params = esco_parameters_for_codec(codec_index); |
|
/* For CVSD */ |
|
if (esco_codec == BTM_SCO_CODEC_CVSD) { |
|
/* Use the applicable packet types |
|
(3-EV3 not allowed due to errata 2363) */ |
|
params.packet_types = |
|
p_bta_ag_cfg->sco_pkt_types | ESCO_PKT_TYPES_MASK_NO_3_EV3; |
|
if ((!(p_scb->features & BTA_AG_FEAT_ESCO)) || |
|
(!(p_scb->peer_features & BTA_AG_PEER_FEAT_ESCO))) { |
|
params.max_latency_ms = 10; |
|
params.retransmission_effort = ESCO_RETRANSMISSION_POWER; |
|
} |
|
/** M: Modify for SCO retry @{ */ |
|
if(retry_with_sco_only == false) { |
|
retry_with_sco_only = true; |
|
} else { |
|
params.retransmission_effort = ESCO_RETRANSMISSION_OFF; |
|
retry_with_sco_only = false; |
|
} |
|
} else { |
|
retry_with_sco_only = false; |
|
} |
|
/** @} */ |
|
|
|
/* If initiating, setup parameters to start SCO/eSCO connection */ |
|
if (is_orig) { |
|
bta_ag_cb.sco.is_local = true; |
|
/* Set eSCO Mode */ |
|
BTM_SetEScoMode(¶ms); |
|
bta_ag_cb.sco.p_curr_scb = p_scb; |
|
/* save the current codec as sco_codec can be updated while SCO is open. */ |
|
p_scb->inuse_codec = esco_codec; |
|
|
|
/* tell sys to stop av if any */ |
|
bta_sys_sco_use(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); |
|
|
|
/* Send pending commands to create SCO connection to peer */ |
|
bta_ag_create_pending_sco(p_scb, bta_ag_cb.sco.is_local); |
|
} else { |
|
/** M: Modify for SCO retry @{ */ |
|
retry_with_sco_only = false; |
|
/** @} */ |
|
/* Not initiating, go to listen mode */ |
|
tBTM_STATUS status = BTM_CreateSco( |
|
&p_scb->peer_addr, false, params.packet_types, &p_scb->sco_idx, |
|
bta_ag_sco_conn_cback, bta_ag_sco_disc_cback); |
|
if (status == BTM_CMD_STARTED) |
|
BTM_RegForEScoEvts(p_scb->sco_idx, bta_ag_esco_connreq_cback); |
|
|
|
APPL_TRACE_API("%s: orig %d, inx 0x%04x, status 0x%x, pkt types 0x%04x", |
|
__func__, is_orig, p_scb->sco_idx, status, |
|
params.packet_types); |
|
} |
|
APPL_TRACE_DEBUG( |
|
"%s: AFTER codec_updated=%d, codec_fallback=%d, " |
|
"sco_codec=%d, peer_codec=%d, msbc_settings=%d", |
|
__func__, p_scb->codec_updated, p_scb->codec_fallback, p_scb->sco_codec, |
|
p_scb->peer_codecs, p_scb->codec_msbc_settings); |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_ag_create_pending_sco |
|
* |
|
* Description This Function is called after the pre-SCO vendor setup is |
|
* done for the BTA to continue and send the HCI Commands for |
|
* creating/accepting SCO connection with peer based on the |
|
* is_local parameter. |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
static void bta_ag_create_pending_sco(tBTA_AG_SCB* p_scb, bool is_local) { |
|
tBTA_AG_PEER_CODEC esco_codec = p_scb->inuse_codec; |
|
enh_esco_params_t params; |
|
bta_ag_cb.sco.p_curr_scb = p_scb; |
|
bta_ag_cb.sco.cur_idx = p_scb->sco_idx; |
|
|
|
/* Local device requested SCO connection to peer */ |
|
if (is_local) { |
|
if (esco_codec == BTA_AG_CODEC_MSBC) { |
|
if (p_scb->codec_msbc_settings == BTA_AG_SCO_MSBC_SETTINGS_T2) { |
|
params = esco_parameters_for_codec(ESCO_CODEC_MSBC_T2); |
|
} else |
|
params = esco_parameters_for_codec(ESCO_CODEC_MSBC_T1); |
|
} else { |
|
params = esco_parameters_for_codec(ESCO_CODEC_CVSD); |
|
if ((!(p_scb->features & BTA_AG_FEAT_ESCO)) || |
|
(!(p_scb->peer_features & BTA_AG_PEER_FEAT_ESCO))) { |
|
params.max_latency_ms = 10; |
|
params.retransmission_effort = ESCO_RETRANSMISSION_POWER; |
|
} |
|
} |
|
|
|
/* Bypass voice settings if enhanced SCO setup command is supported */ |
|
/** M: Change feature: always set the voice settings. @{ */ |
|
/*if (!(controller_get_interface() |
|
->supports_enhanced_setup_synchronous_connection())) {*/ |
|
if (esco_codec == BTA_AG_CODEC_MSBC) |
|
BTM_WriteVoiceSettings(BTM_VOICE_SETTING_TRANS); |
|
else |
|
BTM_WriteVoiceSettings(BTM_VOICE_SETTING_CVSD); |
|
/*}*/ |
|
/** @} */ |
|
#if (BTM_SCO_HCI_INCLUDED == TRUE) |
|
/* initialize SCO setup, no voice setting for AG, data rate <==> sample |
|
* rate */ |
|
BTM_ConfigScoPath(params.input_data_path, bta_ag_sco_read_cback, NULL, |
|
TRUE); |
|
#endif |
|
|
|
tBTM_STATUS status = BTM_CreateSco( |
|
&p_scb->peer_addr, true, params.packet_types, &p_scb->sco_idx, |
|
bta_ag_sco_conn_cback, bta_ag_sco_disc_cback); |
|
if (status == BTM_CMD_STARTED) { |
|
/* Initiating the connection, set the current sco handle */ |
|
bta_ag_cb.sco.cur_idx = p_scb->sco_idx; |
|
} |
|
} else { |
|
/* Local device accepted SCO connection from peer */ |
|
params = esco_parameters_for_codec(ESCO_CODEC_CVSD); |
|
if ((!(p_scb->features & BTA_AG_FEAT_ESCO)) || |
|
(!(p_scb->peer_features & BTA_AG_PEER_FEAT_ESCO))) { |
|
params.max_latency_ms = 10; |
|
params.retransmission_effort = ESCO_RETRANSMISSION_POWER; |
|
} |
|
|
|
BTM_EScoConnRsp(p_scb->sco_idx, HCI_SUCCESS, ¶ms); |
|
} |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_ag_codec_negotiation_timer_cback |
|
* |
|
* Description |
|
* |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
static void bta_ag_codec_negotiation_timer_cback(void* data) { |
|
APPL_TRACE_DEBUG("%s", __func__); |
|
tBTA_AG_SCB* p_scb = (tBTA_AG_SCB*)data; |
|
|
|
/* 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) { |
|
APPL_TRACE_DEBUG("%s", __func__); |
|
bta_ag_cb.sco.p_curr_scb = p_scb; |
|
|
|
if ((p_scb->codec_updated || p_scb->codec_fallback) && |
|
(p_scb->peer_features & BTA_AG_PEER_FEAT_CODEC)) { |
|
/* 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); |
|
|
|
/** M: Force to use SCO. @{ */ |
|
#if defined(MTK_INTEROP_EXTENSION) && (MTK_INTEROP_EXTENSION == TRUE) |
|
uint8_t lmp_ver = 0; |
|
uint16_t lmp_subver = 0; |
|
uint16_t mfct_set = 0; |
|
BTM_ReadRemoteVersion(p_scb->peer_addr, &lmp_ver, |
|
&mfct_set, &lmp_subver); |
|
//Realtek Semiconductor Corporation(93) |
|
//Airoha Technology Corp. (148) |
|
if (mfct_set == 93 || mfct_set == 148) |
|
{ |
|
APPL_TRACE_DEBUG("need use eSco"); |
|
} |
|
|
|
if ((interop_mtk_match_addr_name(INTEROP_MTK_HFP_FORCE_TO_USE_SCO, |
|
&(p_scb->peer_addr)) ||interop_mtk_match_addr_name( |
|
INTEROP_MTK_HFP_FORCE_TO_USE_CVSD, &(p_scb->peer_addr))) |
|
&& (mfct_set != 93 && mfct_set != 148)) { |
|
p_scb->sco_codec = BTA_AG_CODEC_CVSD; |
|
} |
|
#endif |
|
/** @} */ |
|
|
|
/* Send +BCS to the peer */ |
|
bta_ag_send_bcs(p_scb, NULL); |
|
|
|
/* Start timer to handle timeout */ |
|
alarm_set_on_mloop(p_scb->codec_negotiation_timer, |
|
BTA_AG_CODEC_NEGOTIATION_TIMEOUT_MS, |
|
bta_ag_codec_negotiation_timer_cback, p_scb); |
|
} else { |
|
/* use same codec type as previous SCO connection, skip codec negotiation */ |
|
APPL_TRACE_DEBUG( |
|
"use same codec type as previous SCO connection,skip codec " |
|
"negotiation"); |
|
bta_ag_sco_codec_nego(p_scb, true); |
|
} |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_ag_sco_event |
|
* |
|
* Description |
|
* |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
static void bta_ag_sco_event(tBTA_AG_SCB* p_scb, uint8_t event) { |
|
tBTA_AG_SCO_CB* p_sco = &bta_ag_cb.sco; |
|
#if (BTM_SCO_HCI_INCLUDED == TRUE) |
|
BT_HDR* p_buf; |
|
#endif |
|
|
|
#if (BTA_AG_SCO_DEBUG == TRUE) |
|
uint8_t in_state = p_sco->state; |
|
|
|
if (event != BTA_AG_SCO_CI_DATA_E) { |
|
APPL_TRACE_EVENT("%s: SCO Index 0x%04x, State %d (%s), Event %d (%s)", |
|
__func__, p_scb->sco_idx, p_sco->state, |
|
bta_ag_sco_state_str(p_sco->state), event, |
|
bta_ag_sco_evt_str(event)); |
|
} |
|
#else |
|
if (event != BTA_AG_SCO_CI_DATA_E) { |
|
APPL_TRACE_EVENT("%s: SCO Index 0x%04x, State %d, Event %d", __func__, |
|
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 |
|
osi_free(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_WARNING("%s: BTA_AG_SCO_SHUTDOWN_ST: Ignoring event %d", |
|
__func__, 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); |
|
|
|
/* start codec negotiation */ |
|
p_sco->state = BTA_AG_SCO_CODEC_ST; |
|
bta_ag_codec_negotiate(p_scb); |
|
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. Keep listening SCO for the active SLC |
|
*/ |
|
APPL_TRACE_WARNING("%s: BTA_AG_SCO_LISTEN_ST: Ignoring event %d", |
|
__func__, 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_WARNING("%s: BTA_AG_SCO_LISTEN_ST: Ignoring event %d", |
|
__func__, event); |
|
break; |
|
} |
|
break; |
|
|
|
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_WARNING("%s: BTA_AG_SCO_CODEC_ST: Ignoring event %d", |
|
__func__, event); |
|
break; |
|
} |
|
break; |
|
|
|
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; |
|
|
|
case BTA_AG_SCO_REOPEN_E: |
|
/* start codec negotiation */ |
|
p_sco->state = BTA_AG_SCO_CODEC_ST; |
|
bta_ag_codec_negotiate(p_scb); |
|
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_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 |
|
/** M: Bug Fix for ag sco state machine @{ */ |
|
//p_sco->state = BTA_AG_SCO_SHUTTING_ST; |
|
p_sco->state = BTA_AG_SCO_SHUTDOWN_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_WARNING("%s: BTA_AG_SCO_OPENING_ST: Ignoring event %d", |
|
__func__, 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_WARNING("%s: BTA_AG_SCO_OPEN_CL_ST: Ignoring event %d", |
|
__func__, 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_WARNING("%s: BTA_AG_SCO_OPEN_XFER_ST: Ignoring event %d", |
|
__func__, 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_WARNING("%s: BTA_AG_SCO_OPEN_ST: Ignoring event %d", |
|
__func__, 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_WARNING("%s: BTA_AG_SCO_CLOSING_ST: Ignoring event %d", |
|
__func__, 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: |
|
/* start codec negotiation */ |
|
p_sco->state = BTA_AG_SCO_CODEC_ST; |
|
bta_ag_codec_negotiate(p_scb); |
|
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_WARNING("%s: BTA_AG_SCO_CLOSE_OP_ST: Ignoring event %d", |
|
__func__, 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); |
|
|
|
/* start codec negotiation */ |
|
p_sco->state = BTA_AG_SCO_CODEC_ST; |
|
tBTA_AG_SCB* p_cn_scb = p_sco->p_xfer_scb; |
|
p_sco->p_xfer_scb = NULL; |
|
bta_ag_codec_negotiate(p_cn_scb); |
|
break; |
|
} |
|
|
|
default: |
|
APPL_TRACE_WARNING("%s: BTA_AG_SCO_CLOSE_XFER_ST: Ignoring event %d", |
|
__func__, 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 SCO closed for other HS which is not being disconnected, |
|
then create listen sco connection for it as scb still open */ |
|
if (bta_ag_scb_open(p_scb)) { |
|
bta_ag_create_sco(p_scb, false); |
|
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_WARNING("%s: BTA_AG_SCO_SHUTTING_ST: Ignoring event %d", |
|
__func__, event); |
|
break; |
|
} |
|
break; |
|
|
|
default: |
|
break; |
|
} |
|
#if (BTA_AG_SCO_DEBUG == TRUE) |
|
if (p_sco->state != in_state) { |
|
APPL_TRACE_EVENT("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 |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* 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. |
|
* |
|
******************************************************************************/ |
|
bool 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. |
|
* |
|
******************************************************************************/ |
|
bool bta_ag_sco_is_opening(tBTA_AG_SCB* p_scb) { |
|
return ((bta_ag_cb.sco.state == BTA_AG_SCO_OPENING_ST) && |
|
(bta_ag_cb.sco.p_curr_scb == p_scb)); |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_ag_sco_listen |
|
* |
|
* Description |
|
* |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
void bta_ag_sco_listen(tBTA_AG_SCB* p_scb, UNUSED_ATTR 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, UNUSED_ATTR tBTA_AG_DATA* p_data) { |
|
uint8_t event; |
|
|
|
if (!sco_allowed) { |
|
APPL_TRACE_DEBUG("%s not opening sco, by policy", __func__); |
|
return; |
|
} |
|
|
|
/* 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, UNUSED_ATTR tBTA_AG_DATA* p_data) { |
|
/* if scb is in use */ |
|
/* sco_idx is not allocated in SCO_CODEC_ST, still need to move to listen |
|
* state. */ |
|
if ((p_scb->sco_idx != BTM_INVALID_SCO_INDEX) || |
|
(bta_ag_cb.sco.state == BTA_AG_SCO_CODEC_ST)) |
|
{ |
|
APPL_TRACE_DEBUG("bta_ag_sco_close: sco_inx = %d", p_scb->sco_idx); |
|
bta_ag_sco_event(p_scb, BTA_AG_SCO_CLOSE_E); |
|
} |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_ag_sco_codec_nego |
|
* |
|
* Description Handles result of eSCO codec negotiation |
|
* |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
void bta_ag_sco_codec_nego(tBTA_AG_SCB* p_scb, bool result) { |
|
if (result == true) { |
|
/* Subsequent SCO connection will skip codec negotiation */ |
|
APPL_TRACE_DEBUG("%s: Succeeded for index 0x%04x", __func__, |
|
p_scb->sco_idx); |
|
p_scb->codec_updated = false; |
|
bta_ag_sco_event(p_scb, BTA_AG_SCO_CN_DONE_E); |
|
} else { |
|
/* codec negotiation failed */ |
|
APPL_TRACE_ERROR("%s: Failed for index 0x%04x", __func__, p_scb->sco_idx); |
|
bta_ag_sco_event(p_scb, BTA_AG_SCO_CLOSE_E); |
|
} |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_ag_sco_shutdown |
|
* |
|
* Description |
|
* |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
void bta_ag_sco_shutdown(tBTA_AG_SCB* p_scb, UNUSED_ATTR 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, |
|
UNUSED_ATTR 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); |
|
|
|
#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); |
|
|
|
/** M: Modify for SCO retry @{ */ |
|
retry_with_sco_only = false; |
|
/** @} */ |
|
/* reset to mSBC T2 settings as the preferred */ |
|
p_scb->codec_msbc_settings = BTA_AG_SCO_MSBC_SETTINGS_T2; |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_ag_sco_conn_close |
|
* |
|
* Description |
|
* |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
void bta_ag_sco_conn_close(tBTA_AG_SCB* p_scb, |
|
UNUSED_ATTR tBTA_AG_DATA* p_data) { |
|
/* clear current scb */ |
|
bta_ag_cb.sco.p_curr_scb = NULL; |
|
p_scb->sco_idx = BTM_INVALID_SCO_INDEX; |
|
|
|
/* codec_fallback is set when AG is initiator and connection failed for mSBC. |
|
* OR if codec is msbc and T2 settings failed, then retry Safe T1 settings */ |
|
if (p_scb->svc_conn && |
|
(p_scb->codec_fallback || |
|
(p_scb->sco_codec == BTM_SCO_CODEC_MSBC && |
|
p_scb->codec_msbc_settings == BTA_AG_SCO_MSBC_SETTINGS_T1))) { |
|
bta_ag_sco_event(p_scb, BTA_AG_SCO_REOPEN_E); |
|
/** M: Modify for SCO retry @{ */ |
|
} else if (p_scb->svc_conn && retry_with_sco_only) { |
|
bta_ag_create_sco(p_scb, TRUE); |
|
/** @} */ |
|
} else { |
|
/* Indicate if the closing of audio is because of transfer */ |
|
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->codec_msbc_settings = BTA_AG_SCO_MSBC_SETTINGS_T2; |
|
} |
|
/** M: Modify for SCO retry @{ */ |
|
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) { |
|
bta_ag_cb.sco.is_local = false; |
|
|
|
APPL_TRACE_DEBUG("%s: eSCO %d, state %d", __func__, |
|
controller_get_interface() |
|
->supports_enhanced_setup_synchronous_connection(), |
|
bta_ag_cb.sco.state); |
|
|
|
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) { |
|
/* tell sys to stop av if any */ |
|
bta_sys_sco_use(BTA_ID_AG, p_scb->app_id, p_scb->peer_addr); |
|
/* When HS initiated SCO, it cannot be WBS. */ |
|
#if (BTM_SCO_HCI_INCLUDED == TRUE) |
|
/* Configure the transport being used */ |
|
BTM_ConfigScoPath(resp.input_data_path, bta_ag_sco_read_cback, NULL, TRUE); |
|
#endif |
|
} |
|
|
|
/* If SCO open was initiated from HS, it must be CVSD */ |
|
p_scb->inuse_codec = BTA_AG_CODEC_NONE; |
|
/* Send pending commands to create SCO connection to peer */ |
|
bta_ag_create_pending_sco(p_scb, bta_ag_cb.sco.is_local); |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function bta_ag_ci_sco_data |
|
* |
|
* Description Process the SCO data ready callin event |
|
* |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
void bta_ag_ci_sco_data(UNUSED_ATTR tBTA_AG_SCB* p_scb, |
|
UNUSED_ATTR tBTA_AG_DATA* p_data) { |
|
#if (BTM_SCO_HCI_INCLUDED == TRUE) |
|
bta_ag_sco_event(p_scb, BTA_AG_SCO_CI_DATA_E); |
|
#endif |
|
} |
|
|
|
void bta_ag_set_sco_allowed(tBTA_AG_DATA* p_data) { |
|
sco_allowed = ((tBTA_AG_API_SET_SCO_ALLOWED*)p_data)->value; |
|
APPL_TRACE_DEBUG(sco_allowed ? "sco now allowed" : "sco now not allowed"); |
|
} |
|
|
|
/******************************************************************************* |
|
* Debugging functions |
|
******************************************************************************/ |
|
|
|
#if (BTA_AG_SCO_DEBUG == TRUE) |
|
static char* bta_ag_sco_evt_str(uint8_t 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"; |
|
case BTA_AG_SCO_CN_DONE_E: |
|
return "Codec Negotiation Done"; |
|
case BTA_AG_SCO_REOPEN_E: |
|
return "Reopen Request"; |
|
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_t state) { |
|
switch (state) { |
|
case BTA_AG_SCO_SHUTDOWN_ST: |
|
return "Shutdown"; |
|
case BTA_AG_SCO_LISTEN_ST: |
|
return "Listening"; |
|
case BTA_AG_SCO_CODEC_ST: |
|
return "Codec Negotiation"; |
|
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 /* (BTA_AG_SCO_DEBUG) */
|
|
|