/****************************************************************************** * * 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 #include "bta_ag_int.h" #include "bta/bta_api.h" #include "bta/bta_ag_api.h" #include "bta/bta_ag_co.h" #include "bta/bta_hfp_defs.h" #if (BTM_SCO_HCI_INCLUDED == TRUE ) #include "bta/bta_dm_co.h" #include "hci/hci_audio.h" #endif #include "bta/utl.h" #include "stack/btm_api.h" #include "common/bt_trace.h" #include "osi/allocator.h" #if (BTA_AG_INCLUDED == TRUE) #ifndef BTA_AG_CODEC_NEGO_TIMEOUT #define BTA_AG_CODEC_NEGO_TIMEOUT 3000 #endif static char *bta_ag_sco_evt_str(UINT8 event); static char *bta_ag_sco_state_str(UINT8 state); #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 3 #define BTA_AG_ESCO_SETTING_IDX_CVSD 0 /* eSCO setting for CVSD */ #define BTA_AG_ESCO_SETTING_IDX_T1 1 /* eSCO setting for mSBC T1 */ #define BTA_AG_ESCO_SETTING_IDX_T2 2 /* eSCO setting for mSBC T2 */ 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 T1 */ { BTM_64KBITS_RATE, /* TX Bandwidth (64 kbits/sec), 8000 */ BTM_64KBITS_RATE, /* RX Bandwidth (64 kbits/sec), 8000 */ 8, /* 8 ms */ BTM_VOICE_SETTING_TRANS, /* Inp Linear, Transparent, 2s Comp, 16bit */ (BTM_SCO_PKT_TYPES_MASK_EV3 | /* Packet Types : EV3 + 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 | BTM_SCO_PKT_TYPES_MASK_NO_2_EV3 ), BTM_ESCO_RETRANS_QUALITY /* Retransmission effort */ }, /* mSBC T2*/ { 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 /* WBS not included, CVSD by default */ 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 = &bta_ag_cb.scb[0]; tBTM_ESCO_DATA sco_data; APPL_TRACE_DEBUG("%s %d", __FUNCTION__, sco_idx); /* 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) { BTM_ReadEScoLinkParms(sco_idx, &sco_data); p_scb->link_type = sco_data.link_type; p_scb->tx_interval = sco_data.tx_interval; p_scb->retrans_window = sco_data.retrans_window; p_scb->air_mode = sco_data.air_mode; if (sco_data.air_mode == BTM_SCO_AIR_MODE_CVSD) { p_scb->out_pkt_len = sco_data.tx_pkt_len * 2; p_scb->in_pkt_len = sco_data.rx_pkt_len * 2; } else { p_scb->out_pkt_len = sco_data.tx_pkt_len; p_scb->in_pkt_len = sco_data.rx_pkt_len; } if ((p_buf = (BT_HDR *) osi_malloc(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_DEBUG ("bta_ag_sco_disc_cback(): sco_idx: 0x%x p_cur_scb: 0x%08x sco.state: %d", (unsigned int)sco_idx, (unsigned int)bta_ag_cb.sco.p_curr_scb, (unsigned int)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", (unsigned int) &bta_ag_cb.scb[0], (unsigned int)bta_ag_cb.scb[0].in_use, (unsigned int)bta_ag_cb.scb[0].sco_idx, (unsigned int)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", (unsigned int) &bta_ag_cb.scb[1], (unsigned int) bta_ag_cb.scb[1].in_use, (unsigned int) bta_ag_cb.scb[1].sco_idx, (unsigned int) 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_DEBUG("bta_ag_sco_disc_cback sco close config status = %d", status); /* SCO clean up here */ bta_ag_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) { /* set_sco_codec(BTM_SCO_CODEC_NONE); we should get a close */ BTM_WriteVoiceSettings (BTM_VOICE_SETTING_CVSD); /* If SCO open was initiated by AG and failed for mSBC, then attempt mSBC with T1 settings i.e. 'Safe Settings'. If this fails, then switch to CVSD */ if (bta_ag_sco_is_opening (bta_ag_cb.sco.p_curr_scb)) { if (bta_ag_cb.sco.p_curr_scb->codec_msbc_settings == BTA_AG_SCO_MSBC_SETTINGS_T2) { APPL_TRACE_DEBUG("Fallback to mSBC T1 settings"); bta_ag_cb.sco.p_curr_scb->codec_msbc_settings = BTA_AG_SCO_MSBC_SETTINGS_T1; } else { APPL_TRACE_DEBUG("Fallback to CVSD settings"); bta_ag_cb.sco.p_curr_scb->codec_fallback = TRUE; } } } bta_ag_cb.sco.p_curr_scb->inuse_codec = BTA_AG_CODEC_NONE; #endif if ((p_buf = (BT_HDR *) osi_malloc(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_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 sco_inx, BT_HDR *p_data, tBTM_SCO_DATA_FLAG status) { if (status != BTM_SCO_DATA_CORRECT) { APPL_TRACE_WARNING("bta_ag_sco_read_cback: status(%d)", status); } /* Callout function must free the data. */ bta_ag_sco_co_in_data(p_data, status); osi_free(p_data); } #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_DEBUG("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_EVENT("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_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("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_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, NULL); } } /* Received a change in the esco link */ else if (event == BTM_ESCO_CHG_EVT) { APPL_TRACE_EVENT("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 Create a sco connection and is is_orig is TRUE means AG originate ** this connection, if FALSE it's peer device originate the connection. ** ** ** 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_HFP_CODEC_INFO codec_info = {BTA_HFP_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_WARNING("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 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 == BTM_SCO_CODEC_MSBC) { if (p_scb->codec_msbc_settings == BTA_AG_SCO_MSBC_SETTINGS_T2) { codec_index = BTA_AG_ESCO_SETTING_IDX_T2; } else { codec_index = BTA_AG_ESCO_SETTING_IDX_T1; } } params = bta_ag_esco_params[codec_index]; #else /* When WBS is not included, use CVSD by default */ 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 (esco_codec == BTM_SCO_CODEC_CVSD) /* For CVSD */ #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, ¶ms); /* If ESCO or EDR ESCO, retry with SCO only in case of failure */ if((params.packet_types & BTM_ESCO_LINK_ONLY_MASK) ||!((params.packet_types & ~(BTM_ESCO_LINK_ONLY_MASK | BTM_SCO_LINK_ONLY_MASK)) ^ BTA_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_API("Setting retry_with_sco_only to TRUE"); } else /* Do not use SCO when using mSBC */ { p_scb->retry_with_sco_only = FALSE; APPL_TRACE_API("Setting retry_with_sco_only to FALSE"); } #else p_scb->retry_with_sco_only = TRUE; APPL_TRACE_API("Setting retry_with_sco_only to TRUE"); #endif } } else { if(p_scb->retry_with_sco_only){ APPL_TRACE_API("retrying with SCO only"); } p_scb->retry_with_sco_only = FALSE; BTM_SetEScoMode(BTM_LINK_TYPE_SCO, ¶ms); } 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); #if (BTM_SCO_HCI_INCLUDED == TRUE) #if (BTM_WBS_INCLUDED == TRUE) /* Allow any platform specific pre-SCO set up to take place */ bta_ag_sco_audio_state(bta_ag_scb_to_idx(p_scb), p_scb->app_id, SCO_STATE_SETUP, esco_codec); /* 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; #else /* Allow any platform specific pre-SCO set up to take place */ bta_ag_sco_audio_state(bta_ag_scb_to_idx(p_scb), p_scb->app_id, SCO_STATE_SETUP); #endif #endif #if (BTM_SCO_HCI_INCLUDED == TRUE) #if (BTM_WBS_INCLUDED == TRUE) if (esco_codec == BTA_AG_CODEC_MSBC) pcm_sample_rate = BTA_HFP_SCO_SAMP_RATE_16K; #endif pcm_sample_rate = BTA_HFP_SCO_SAMP_RATE_8K; sco_route = bta_ag_sco_co_init(pcm_sample_rate, pcm_sample_rate, &codec_info, p_scb->app_id); #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_API("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_attempt_msbc_safe_settings ** ** Description Checks if ESCO connection needs to be attempted using mSBC T1(safe) settings ** ** ** Returns TRUE if T1 settings has to be used, FALSE otherwise ** *******************************************************************************/ BOOLEAN bta_ag_attempt_msbc_safe_settings(tBTA_AG_SCB *p_scb) { if (p_scb->svc_conn && p_scb->sco_codec == BTM_SCO_CODEC_MSBC && p_scb->codec_msbc_settings == BTA_AG_SCO_MSBC_SETTINGS_T1) return TRUE; else return FALSE; } /******************************************************************************* ** ** 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 || bta_ag_attempt_msbc_safe_settings(p_scb)) && (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); /* 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 */ APPL_TRACE_DEBUG("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 AG Sco State Machine ** ** ** 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 UINT8 in_state = p_sco->state; APPL_TRACE_EVENT("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)); #if (BTM_SCO_HCI_INCLUDED == TRUE) BT_HDR *p_buf; if (event == BTA_AG_SCO_CI_DATA_E) { UINT16 pkt_offset = 1 + HCI_SCO_PREAMBLE_SIZE; UINT16 len_to_send = 0; while (TRUE) { p_buf = osi_calloc(sizeof(BT_HDR) + pkt_offset + p_scb->out_pkt_len); if (!p_buf) { APPL_TRACE_WARNING("%s, no mem", __FUNCTION__); break; } p_buf->offset = pkt_offset; len_to_send = bta_ag_sco_co_out_data(p_buf->data + pkt_offset); p_buf->len = len_to_send; if (len_to_send == p_scb->out_pkt_len) { if (p_sco->state == BTA_AG_SCO_OPEN_ST) { tBTM_STATUS write_stat = BTM_WriteScoData(p_sco->p_curr_scb->sco_idx, p_buf); if (write_stat != BTM_SUCCESS) { break; } else { osi_free(p_buf); } } else { osi_free(p_buf); break; } } } return; } #endif /* State Machine Start */ 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("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_WARNING("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_WARNING("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_WARNING("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_WARNING("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_WARNING("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_WARNING("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_WARNING("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_WARNING("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_WARNING("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_WARNING("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 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("BTA_AG_SCO_SHUTTING_ST: Ignoring event %d", event); break; } break; default: break; } 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)); } #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) { UNUSED(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; UNUSED(p_data); /* 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) { UNUSED(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_DEBUG("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) { UNUSED(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) { UNUSED(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) #if (BTM_WBS_INCLUDED == TRUE) bta_ag_sco_audio_state(bta_ag_scb_to_idx(p_scb), p_scb->app_id, SCO_STATE_ON, p_scb->inuse_codec); #else bta_ag_sco_audio_state(bta_ag_scb_to_idx(p_scb), p_scb->app_id, SCO_STATE_ON); #endif /* open SCO codec if SCO is routed through transport */ bta_ag_sco_co_open(bta_ag_scb_to_idx(p_scb), p_scb->air_mode, BTA_HFP_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; #if (BTM_WBS_INCLUDED == TRUE) /* reset to mSBC T2 settings as the preferred */ p_scb->codec_msbc_settings = BTA_AG_SCO_MSBC_SETTINGS_T2; #endif } /******************************************************************************* ** ** Function bta_ag_sco_conn_close ** ** Description This function is called when a SCO connection is closed ** ** ** Returns void ** *******************************************************************************/ void bta_ag_sco_conn_close(tBTA_AG_SCB *p_scb, tBTA_AG_DATA *p_data) { #if (BTM_SCO_HCI_INCLUDED == TRUE) UINT16 handle = bta_ag_scb_to_idx(p_scb); #endif UNUSED(p_data); /* 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. */ /* OR if codec is msbc and T2 settings failed, then retry Safe T1 settings */ if ((p_scb->codec_fallback && p_scb->svc_conn) || bta_ag_attempt_msbc_safe_settings(p_scb)) { 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 { #if (BTM_SCO_HCI_INCLUDED == TRUE) sco_state_t sco_state = bta_ag_cb.sco.p_xfer_scb ? SCO_STATE_OFF_TRANSFER : SCO_STATE_OFF; #if (BTM_WBS_INCLUDED == TRUE) /* Indicate if the closing of audio is because of transfer */ bta_ag_sco_audio_state(handle, p_scb->app_id, sco_state, p_scb->inuse_codec); #else /* Indicate if the closing of audio is because of transfer */ bta_ag_sco_audio_state(handle, p_scb->app_id, sco_state); #endif #endif 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); #if (BTM_WBS_INCLUDED == TRUE) p_scb->codec_msbc_settings = BTA_AG_SCO_MSBC_SETTINGS_T2; #endif } 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_HFP_CODEC_INFO codec_info = {BTA_HFP_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); #if (BTM_SCO_HCI_INCLUDED == TRUE) #if (BTM_WBS_INCLUDED == TRUE) /* When HS initiated SCO, it cannot be WBS. */ /* Allow any platform specific pre-SCO set up to take place */ bta_ag_sco_audio_state(bta_ag_scb_to_idx(p_scb), p_scb->app_id, SCO_STATE_SETUP, BTA_AG_CODEC_CVSD); #else /* Allow any platform specific pre-SCO set up to take place */ bta_ag_sco_audio_state(bta_ag_scb_to_idx(p_scb), p_scb->app_id, SCO_STATE_SETUP); #endif pcm_sample_rate = BTA_HFP_SCO_SAMP_RATE_8K; /* initialize SCO setup, no voice setting for AG, data rate <==> sample rate */ BTM_ConfigScoPath(bta_ag_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) { UNUSED(p_scb); UNUSED(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_DEBUG("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_DEBUG("bta_ag_set_esco_param : Setting ESCO parameters"); } } /******************************************************************************* ** Debugging functions *******************************************************************************/ 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 //#if (BTA_AG_INCLUDED == TRUE)