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.
1904 lines
67 KiB
1904 lines
67 KiB
/****************************************************************************** |
|
* |
|
* Copyright (C) 2000-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 that handle SCO connections. This includes |
|
* operations such as connect, disconnect, change supported packet types. |
|
* |
|
******************************************************************************/ |
|
|
|
#include <string.h> |
|
#include "bt_common.h" |
|
#include "bt_target.h" |
|
#include "bt_types.h" |
|
#include "bt_utils.h" |
|
#include "btm_api.h" |
|
#include "btm_int.h" |
|
#include "btu.h" |
|
#include "device/include/controller.h" |
|
#include "device/include/esco_parameters.h" |
|
#include "hcidefs.h" |
|
#include "hcimsgs.h" |
|
#include "osi/include/osi.h" |
|
|
|
#if defined(MTK_INTEROP_EXTENSION) && (MTK_INTEROP_EXTENSION == TRUE) |
|
#include "mediatek/include/interop_mtk.h" |
|
#endif |
|
|
|
#if (BTM_SCO_INCLUDED == TRUE) |
|
|
|
/******************************************************************************/ |
|
/* L O C A L D A T A D E F I N I T I O N S */ |
|
/******************************************************************************/ |
|
|
|
#define SCO_ST_UNUSED 0 |
|
#define SCO_ST_LISTENING 1 |
|
#define SCO_ST_W4_CONN_RSP 2 |
|
#define SCO_ST_CONNECTING 3 |
|
#define SCO_ST_CONNECTED 4 |
|
#define SCO_ST_DISCONNECTING 5 |
|
#define SCO_ST_PEND_UNPARK 6 |
|
#define SCO_ST_PEND_ROLECHANGE 7 |
|
#define SCO_ST_PEND_MODECHANGE 8 |
|
|
|
/******************************************************************************/ |
|
/* L O C A L F U N C T I O N P R O T O T Y P E S */ |
|
/******************************************************************************/ |
|
|
|
static uint16_t btm_sco_voice_settings_to_legacy(enh_esco_params_t* p_parms); |
|
|
|
/******************************************************************************* |
|
* |
|
* Function btm_sco_flush_sco_data |
|
* |
|
* Description This function is called to flush the SCO data for this |
|
* channel. |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
#if (BTM_SCO_HCI_INCLUDED == TRUE && BTM_MAX_SCO_LINKS > 0) |
|
void btm_sco_flush_sco_data(uint16_t sco_inx) { |
|
tSCO_CONN* p; |
|
BT_HDR* p_buf; |
|
|
|
if (sco_inx < BTM_MAX_SCO_LINKS) { |
|
p = &btm_cb.sco_cb.sco_db[sco_inx]; |
|
while ((p_buf = (BT_HDR*)fixed_queue_try_dequeue(p->xmit_data_q)) != NULL) |
|
osi_free(p_buf); |
|
} |
|
} |
|
} |
|
#else |
|
void btm_sco_flush_sco_data(UNUSED_ATTR uint16_t sco_inx) {} |
|
#endif |
|
/******************************************************************************* |
|
* |
|
* Function btm_sco_init |
|
* |
|
* Description This function is called at BTM startup to initialize |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
void btm_sco_init(void) { |
|
#if (BTM_SCO_HCI_INCLUDED == TRUE) |
|
for (int i = 0; i < BTM_MAX_SCO_LINKS; i++) |
|
btm_cb.sco_cb.sco_db[i].xmit_data_q = fixed_queue_new(SIZE_MAX); |
|
#endif |
|
/* Initialize nonzero defaults */ |
|
btm_cb.sco_cb.sco_disc_reason = BTM_INVALID_SCO_DISC_REASON; |
|
btm_cb.sco_cb.def_esco_parms = esco_parameters_for_codec(ESCO_CODEC_CVSD); |
|
btm_cb.sco_cb.def_esco_parms.max_latency_ms = 12; |
|
btm_cb.sco_cb.sco_route = ESCO_DATA_PATH_PCM; |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function btm_esco_conn_rsp |
|
* |
|
* Description This function is called upon receipt of an (e)SCO connection |
|
* request event (BTM_ESCO_CONN_REQ_EVT) to accept or reject |
|
* the request. Parameters used to negotiate eSCO links. |
|
* If p_parms is NULL, then default values are used. |
|
* If the link type of the incoming request is SCO, then only |
|
* the tx_bw, max_latency, content format, and packet_types are |
|
* valid. The hci_status parameter should be |
|
* ([0x0] to accept, [0x0d..0x0f] to reject) |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
static void btm_esco_conn_rsp(uint16_t sco_inx, uint8_t hci_status, |
|
const RawAddress& bda, |
|
enh_esco_params_t* p_parms) { |
|
#if (BTM_MAX_SCO_LINKS > 0) |
|
tSCO_CONN* p_sco = NULL; |
|
|
|
if (sco_inx < BTM_MAX_SCO_LINKS) p_sco = &btm_cb.sco_cb.sco_db[sco_inx]; |
|
|
|
/* Reject the connect request if refused by caller or wrong state */ |
|
if (hci_status != HCI_SUCCESS || p_sco == NULL) { |
|
if (p_sco) { |
|
p_sco->state = (p_sco->state == SCO_ST_W4_CONN_RSP) ? SCO_ST_LISTENING |
|
: SCO_ST_UNUSED; |
|
} |
|
if (!btm_cb.sco_cb.esco_supported) { |
|
btsnd_hcic_reject_conn(bda, hci_status); |
|
} else { |
|
btsnd_hcic_reject_esco_conn(bda, hci_status); |
|
} |
|
|
|
/** M: Bug Fix for create sco collision @{ */ |
|
if (p_sco != NULL) |
|
p_sco->is_collision = false; |
|
/** @} */ |
|
} else /* Connection is being accepted */ |
|
{ |
|
p_sco->state = SCO_ST_CONNECTING; |
|
enh_esco_params_t* p_setup = &p_sco->esco.setup; |
|
/* If parameters not specified use the default */ |
|
if (p_parms) { |
|
*p_setup = *p_parms; |
|
} else { |
|
/* Use the last setup passed thru BTM_SetEscoMode (or defaults) */ |
|
*p_setup = btm_cb.sco_cb.def_esco_parms; |
|
} |
|
|
|
uint16_t temp_packet_types = |
|
(p_setup->packet_types & BTM_SCO_SUPPORTED_PKTS_MASK & |
|
btm_cb.btm_sco_pkt_types_supported); |
|
|
|
/* Make sure at least one eSCO packet type is sent, else might confuse peer |
|
*/ |
|
/* Taking this out to confirm with BQB tests |
|
** Real application would like to include this though, as many devices |
|
** do not retry with SCO only if an eSCO connection fails. |
|
if (!(temp_packet_types & BTM_ESCO_LINK_ONLY_MASK)) |
|
{ |
|
temp_packet_types |= BTM_SCO_PKT_TYPES_MASK_EV3; |
|
} |
|
*/ |
|
/* If SCO request, remove eSCO packet types (conformance) */ |
|
if (p_sco->esco.data.link_type == BTM_LINK_TYPE_SCO) { |
|
temp_packet_types &= BTM_SCO_LINK_ONLY_MASK; |
|
temp_packet_types |= BTM_SCO_EXCEPTION_PKTS_MASK; |
|
} else { |
|
/* OR in any exception packet types */ |
|
temp_packet_types |= |
|
((p_setup->packet_types & BTM_SCO_EXCEPTION_PKTS_MASK) | |
|
(btm_cb.btm_sco_pkt_types_supported & BTM_SCO_EXCEPTION_PKTS_MASK)); |
|
} |
|
|
|
/** M: Change feature For enhanced setup synchronous connection. @{ */ |
|
// MTK platform uses differenct command by codec type. |
|
/* Use Enhanced Synchronous commands if supported */ |
|
if (p_setup && (p_setup->transmit_coding_format.coding_format == ESCO_CODING_FORMAT_MSBC |
|
&& p_setup->receive_coding_format.coding_format == ESCO_CODING_FORMAT_MSBC)) { |
|
/*if (controller_get_interface() |
|
->supports_enhanced_setup_synchronous_connection()) {*/ |
|
/* Use the saved SCO routing */ |
|
/** @} */ |
|
p_setup->input_data_path = p_setup->output_data_path = |
|
btm_cb.sco_cb.sco_route; |
|
|
|
BTM_TRACE_DEBUG( |
|
"%s: txbw 0x%x, rxbw 0x%x, lat 0x%x, retrans 0x%02x, " |
|
"pkt 0x%04x, path %u", |
|
__func__, p_setup->transmit_bandwidth, p_setup->receive_bandwidth, |
|
p_setup->max_latency_ms, p_setup->retransmission_effort, |
|
p_setup->packet_types, p_setup->input_data_path); |
|
|
|
btsnd_hcic_enhanced_accept_synchronous_connection(bda, p_setup); |
|
|
|
} else { |
|
/* Use legacy command if enhanced SCO setup is not supported */ |
|
uint16_t voice_content_format = btm_sco_voice_settings_to_legacy(p_setup); |
|
btsnd_hcic_accept_esco_conn( |
|
bda, p_setup->transmit_bandwidth, p_setup->receive_bandwidth, |
|
p_setup->max_latency_ms, voice_content_format, |
|
p_setup->retransmission_effort, p_setup->packet_types); |
|
} |
|
} |
|
#endif |
|
} |
|
|
|
#if (BTM_SCO_HCI_INCLUDED == TRUE) |
|
/******************************************************************************* |
|
* |
|
* Function btm_sco_check_send_pkts |
|
* |
|
* Description This function is called to check if it can send packets |
|
* to the Host Controller. |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
void btm_sco_check_send_pkts(uint16_t sco_inx) { |
|
tSCO_CB* p_cb = &btm_cb.sco_cb; |
|
tSCO_CONN* p_ccb = &p_cb->sco_db[sco_inx]; |
|
|
|
/* If there is data to send, send it now */ |
|
BT_HDR* p_buf; |
|
while ((p_buf = (BT_HDR*)fixed_queue_try_dequeue(p_ccb->xmit_data_q)) != |
|
NULL) { |
|
#if (BTM_SCO_HCI_DEBUG == TRUE) |
|
BTM_TRACE_DEBUG("btm: [%d] buf in xmit_data_q", |
|
fixed_queue_length(p_ccb->xmit_data_q) + 1); |
|
#endif |
|
|
|
HCI_SCO_DATA_TO_LOWER(p_buf); |
|
} |
|
} |
|
#endif /* BTM_SCO_HCI_INCLUDED == TRUE */ |
|
|
|
/******************************************************************************* |
|
* |
|
* Function btm_route_sco_data |
|
* |
|
* Description Route received SCO data. |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
void btm_route_sco_data(BT_HDR* p_msg) { |
|
#if (BTM_SCO_HCI_INCLUDED == TRUE) |
|
uint16_t sco_inx, handle; |
|
uint8_t* p = (uint8_t*)(p_msg + 1) + p_msg->offset; |
|
uint8_t pkt_size = 0; |
|
uint8_t pkt_status = 0; |
|
|
|
/* Extract Packet_Status_Flag and handle */ |
|
STREAM_TO_UINT16(handle, p); |
|
pkt_status = HCID_GET_EVENT(handle); |
|
handle = HCID_GET_HANDLE(handle); |
|
|
|
STREAM_TO_UINT8(pkt_size, p); |
|
|
|
sco_inx = btm_find_scb_by_handle(handle); |
|
if (sco_inx != BTM_MAX_SCO_LINKS) { |
|
/* send data callback */ |
|
if (!btm_cb.sco_cb.p_data_cb) |
|
/* if no data callback registered, just free the buffer */ |
|
osi_free(p_msg); |
|
else { |
|
(*btm_cb.sco_cb.p_data_cb)(sco_inx, p_msg, |
|
(tBTM_SCO_DATA_FLAG)pkt_status); |
|
} |
|
} else /* no mapping handle SCO connection is active, free the buffer */ |
|
{ |
|
osi_free(p_msg); |
|
} |
|
#else |
|
osi_free(p_msg); |
|
#endif |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function BTM_WriteScoData |
|
* |
|
* Description This function write SCO data to a specified instance. The |
|
* data to be written p_buf needs to carry an offset of |
|
* HCI_SCO_PREAMBLE_SIZE bytes, and the data length can not |
|
* exceed BTM_SCO_DATA_SIZE_MAX bytes, whose default value is |
|
* set to 60 and is configurable. Data longer than the maximum |
|
* bytes will be truncated. |
|
* |
|
* Returns BTM_SUCCESS: data write is successful |
|
* BTM_ILLEGAL_VALUE: SCO data contains illegal offset value. |
|
* BTM_SCO_BAD_LENGTH: SCO data length exceeds the max SCO |
|
* packet size. |
|
* BTM_NO_RESOURCES: no resources. |
|
* BTM_UNKNOWN_ADDR: unknown SCO connection handle, or SCO is |
|
* not routed via HCI. |
|
* |
|
* |
|
******************************************************************************/ |
|
#if (BTM_SCO_HCI_INCLUDED == TRUE && BTM_MAX_SCO_LINKS > 0) |
|
tBTM_STATUS BTM_WriteScoData(uint16_t sco_inx, BT_HDR* p_buf) { |
|
tSCO_CONN* p_ccb = &btm_cb.sco_cb.sco_db[sco_inx]; |
|
uint8_t* p; |
|
tBTM_STATUS status = BTM_SUCCESS; |
|
|
|
if (sco_inx < BTM_MAX_SCO_LINKS && btm_cb.sco_cb.p_data_cb && |
|
p_ccb->state == SCO_ST_CONNECTED) { |
|
/* Ensure we have enough space in the buffer for the SCO and HCI headers */ |
|
if (p_buf->offset < HCI_SCO_PREAMBLE_SIZE) { |
|
BTM_TRACE_ERROR("BTM SCO - cannot send buffer, offset: %d", |
|
p_buf->offset); |
|
osi_free(p_buf); |
|
status = BTM_ILLEGAL_VALUE; |
|
} else /* write HCI header */ |
|
{ |
|
/* Step back 3 bytes to add the headers */ |
|
p_buf->offset -= HCI_SCO_PREAMBLE_SIZE; |
|
/* Set the pointer to the beginning of the data */ |
|
p = (uint8_t*)(p_buf + 1) + p_buf->offset; |
|
/* add HCI handle */ |
|
UINT16_TO_STREAM(p, p_ccb->hci_handle); |
|
/* only sent the first BTM_SCO_DATA_SIZE_MAX bytes data if more than max, |
|
and set warning status */ |
|
if (p_buf->len > BTM_SCO_DATA_SIZE_MAX) { |
|
p_buf->len = BTM_SCO_DATA_SIZE_MAX; |
|
status = BTM_SCO_BAD_LENGTH; |
|
} |
|
|
|
UINT8_TO_STREAM(p, (uint8_t)p_buf->len); |
|
p_buf->len += HCI_SCO_PREAMBLE_SIZE; |
|
|
|
fixed_queue_enqueue(p_ccb->xmit_data_q, p_buf); |
|
|
|
btm_sco_check_send_pkts(sco_inx); |
|
} |
|
} else { |
|
osi_free(p_buf); |
|
|
|
BTM_TRACE_ERROR("%s:invalid sco index: %d at state [%d]", __func__, sco_inx, |
|
btm_cb.sco_cb.sco_db[sco_inx].state); |
|
status = BTM_UNKNOWN_ADDR; |
|
} |
|
|
|
return status; |
|
} |
|
#else |
|
tBTM_STATUS BTM_WriteScoData(UNUSED_ATTR uint16_t sco_inx, |
|
UNUSED_ATTR BT_HDR* p_buf) { |
|
return (BTM_NO_RESOURCES); |
|
} |
|
#endif |
|
|
|
#if (BTM_MAX_SCO_LINKS > 0) |
|
/******************************************************************************* |
|
* |
|
* Function btm_send_connect_request |
|
* |
|
* Description This function is called to respond to SCO connect |
|
* indications |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
static tBTM_STATUS btm_send_connect_request(uint16_t acl_handle, |
|
enh_esco_params_t* p_setup) { |
|
tACL_CONN* p_acl; |
|
|
|
/* Send connect request depending on version of spec */ |
|
if (!btm_cb.sco_cb.esco_supported) { |
|
btsnd_hcic_add_SCO_conn(acl_handle, BTM_ESCO_2_SCO(p_setup->packet_types)); |
|
} else { |
|
uint16_t temp_packet_types = |
|
(p_setup->packet_types & BTM_SCO_SUPPORTED_PKTS_MASK & |
|
btm_cb.btm_sco_pkt_types_supported); |
|
|
|
/* OR in any exception packet types */ |
|
temp_packet_types |= |
|
((p_setup->packet_types & BTM_SCO_EXCEPTION_PKTS_MASK) | |
|
(btm_cb.btm_sco_pkt_types_supported & BTM_SCO_EXCEPTION_PKTS_MASK)); |
|
|
|
/* Finally, remove EDR eSCO if the remote device doesn't support it */ |
|
/* UPF25: Only SCO was brought up in this case */ |
|
btm_handle_to_acl_index(acl_handle); |
|
uint8_t acl_index = btm_handle_to_acl_index(acl_handle); |
|
if (acl_index < MAX_L2CAP_LINKS) { |
|
p_acl = &btm_cb.acl_db[acl_index]; |
|
if (!HCI_EDR_ESCO_2MPS_SUPPORTED(p_acl->peer_lmp_feature_pages[0])) { |
|
BTM_TRACE_WARNING("BTM Remote does not support 2-EDR eSCO"); |
|
temp_packet_types |= |
|
(ESCO_PKT_TYPES_MASK_NO_2_EV3 | ESCO_PKT_TYPES_MASK_NO_2_EV5); |
|
} |
|
if (!HCI_EDR_ESCO_3MPS_SUPPORTED(p_acl->peer_lmp_feature_pages[0])) { |
|
BTM_TRACE_WARNING("BTM Remote does not support 3-EDR eSCO"); |
|
temp_packet_types |= |
|
(ESCO_PKT_TYPES_MASK_NO_3_EV3 | ESCO_PKT_TYPES_MASK_NO_3_EV5); |
|
} |
|
|
|
/* Check to see if BR/EDR Secure Connections is being used |
|
** If so, we cannot use SCO-only packet types (HFP 1.7) |
|
*/ |
|
if (BTM_BothEndsSupportSecureConnections(p_acl->remote_addr)) { |
|
temp_packet_types &= ~(BTM_SCO_PKT_TYPE_MASK); |
|
BTM_TRACE_DEBUG("%s: SCO Conn: pkt_types after removing SCO (0x%04x)", |
|
__func__, temp_packet_types); |
|
|
|
/* Return error if no packet types left */ |
|
if (temp_packet_types == 0) { |
|
BTM_TRACE_ERROR("%s: SCO Conn (BR/EDR SC): No packet types available", |
|
__func__); |
|
return (BTM_WRONG_MODE); |
|
} |
|
} else { |
|
BTM_TRACE_DEBUG( |
|
"%s: SCO Conn(BR/EDR SC):local or peer does not support BR/EDR SC", |
|
__func__); |
|
} |
|
} |
|
|
|
/* Save the previous types in case command fails */ |
|
uint16_t saved_packet_types = p_setup->packet_types; |
|
p_setup->packet_types = temp_packet_types; |
|
|
|
/** 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(btm_cb.acl_db[acl_index].remote_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, |
|
&(btm_cb.acl_db[acl_index].remote_addr)) |
|
&& (mfct_set != 93 && mfct_set != 148)) { |
|
*p_setup = esco_parameters_for_codec(ESCO_CODEC_CVSD); |
|
p_setup->packet_types = BTM_SCO_LINK_ONLY_MASK | BTM_SCO_EXCEPTION_PKTS_MASK; |
|
p_setup->retransmission_effort = ESCO_RETRANSMISSION_OFF; |
|
p_setup->max_latency_ms = 10; |
|
} |
|
#endif |
|
/** @} */ |
|
|
|
/** M: Change feature For enhanced setup synchronous connection. @{ */ |
|
// MTK platform uses differenct command by codec type. |
|
/* Use Enhanced Synchronous commands if supported */ |
|
if (p_setup && (p_setup->transmit_coding_format.coding_format == ESCO_CODING_FORMAT_MSBC |
|
&& p_setup->receive_coding_format.coding_format == ESCO_CODING_FORMAT_MSBC)/*FALSE*/) { |
|
/*if (controller_get_interface() |
|
->supports_enhanced_setup_synchronous_connection()) {*/ |
|
/* Use the saved SCO routing */ |
|
/** @} */ |
|
p_setup->input_data_path = p_setup->output_data_path = |
|
btm_cb.sco_cb.sco_route; |
|
|
|
BTM_TRACE_DEBUG( |
|
"%s: txbw 0x%x, rxbw 0x%x, " |
|
"lat 0x%x, retrans 0x%02x, pkt 0x%04x, path %u", |
|
__func__, p_setup->transmit_bandwidth, p_setup->receive_bandwidth, |
|
p_setup->max_latency_ms, p_setup->retransmission_effort, |
|
p_setup->packet_types, p_setup->input_data_path); |
|
|
|
btsnd_hcic_enhanced_set_up_synchronous_connection(acl_handle, p_setup); |
|
p_setup->packet_types = saved_packet_types; |
|
} else { /* Use older command */ |
|
uint16_t voice_content_format = btm_sco_voice_settings_to_legacy(p_setup); |
|
|
|
/** M: Modify for SCO retry @{ */ |
|
if (p_setup->retransmission_effort == ESCO_RETRANSMISSION_OFF) { |
|
p_setup->packet_types = BTM_SCO_LINK_ONLY_MASK | BTM_SCO_EXCEPTION_PKTS_MASK; |
|
} |
|
/** @} */ |
|
BTM_TRACE_API( |
|
"%s: txbw 0x%x, rxbw 0x%x, " |
|
"lat 0x%x, voice 0x%x, retrans 0x%02x, pkt 0x%04x", |
|
__func__, p_setup->transmit_bandwidth, p_setup->receive_bandwidth, |
|
p_setup->max_latency_ms, voice_content_format, |
|
p_setup->retransmission_effort, p_setup->packet_types); |
|
|
|
btsnd_hcic_setup_esco_conn( |
|
acl_handle, p_setup->transmit_bandwidth, p_setup->receive_bandwidth, |
|
p_setup->max_latency_ms, voice_content_format, |
|
p_setup->retransmission_effort, p_setup->packet_types); |
|
} |
|
} |
|
|
|
return (BTM_CMD_STARTED); |
|
} |
|
#endif |
|
|
|
/******************************************************************************* |
|
* |
|
* Function btm_set_sco_ind_cback |
|
* |
|
* Description This function is called to register for TCS SCO connect |
|
* indications. |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
void btm_set_sco_ind_cback(tBTM_SCO_IND_CBACK* sco_ind_cb) { |
|
btm_cb.sco_cb.app_sco_ind_cb = sco_ind_cb; |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function btm_accept_sco_link |
|
* |
|
* Description This function is called to respond to TCS SCO connect |
|
* indications |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
void btm_accept_sco_link(uint16_t sco_inx, enh_esco_params_t* p_setup, |
|
tBTM_SCO_CB* p_conn_cb, tBTM_SCO_CB* p_disc_cb) { |
|
#if (BTM_MAX_SCO_LINKS > 0) |
|
tSCO_CONN* p_sco; |
|
|
|
if (sco_inx >= BTM_MAX_SCO_LINKS) { |
|
BTM_TRACE_ERROR("btm_accept_sco_link: Invalid sco_inx(%d)", sco_inx); |
|
return; |
|
} |
|
|
|
/* Link role is ignored in for this message */ |
|
p_sco = &btm_cb.sco_cb.sco_db[sco_inx]; |
|
p_sco->p_conn_cb = p_conn_cb; |
|
p_sco->p_disc_cb = p_disc_cb; |
|
p_sco->esco.data.link_type = |
|
BTM_LINK_TYPE_ESCO; /* Accept with all supported types */ |
|
|
|
BTM_TRACE_DEBUG("TCS accept SCO: Packet Types 0x%04x", p_setup->packet_types); |
|
|
|
btm_esco_conn_rsp(sco_inx, HCI_SUCCESS, p_sco->esco.data.bd_addr, p_setup); |
|
#else |
|
btm_reject_sco_link(sco_inx); |
|
#endif |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function btm_reject_sco_link |
|
* |
|
* Description This function is called to respond to SCO connect |
|
* indications |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
void btm_reject_sco_link(uint16_t sco_inx) { |
|
btm_esco_conn_rsp(sco_inx, HCI_ERR_HOST_REJECT_RESOURCES, |
|
btm_cb.sco_cb.sco_db[sco_inx].esco.data.bd_addr, NULL); |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function BTM_CreateSco |
|
* |
|
* Description This function is called to create an SCO connection. If the |
|
* "is_orig" flag is true, the connection will be originated, |
|
* otherwise BTM will wait for the other side to connect. |
|
* |
|
* NOTE: If BTM_IGNORE_SCO_PKT_TYPE is passed in the pkt_types |
|
* parameter the default packet types is used. |
|
* |
|
* Returns BTM_UNKNOWN_ADDR if the ACL connection is not up |
|
* BTM_BUSY if another SCO being set up to |
|
* the same BD address |
|
* BTM_NO_RESOURCES if the max SCO limit has been reached |
|
* BTM_CMD_STARTED if the connection establishment is started. |
|
* In this case, "*p_sco_inx" is filled in |
|
* with the sco index used for the connection. |
|
* |
|
******************************************************************************/ |
|
tBTM_STATUS BTM_CreateSco(const RawAddress* remote_bda, bool is_orig, |
|
uint16_t pkt_types, uint16_t* p_sco_inx, |
|
tBTM_SCO_CB* p_conn_cb, tBTM_SCO_CB* p_disc_cb) { |
|
#if (BTM_MAX_SCO_LINKS > 0) |
|
enh_esco_params_t* p_setup; |
|
tSCO_CONN* p = &btm_cb.sco_cb.sco_db[0]; |
|
uint16_t xx; |
|
uint16_t acl_handle = 0; |
|
tACL_CONN* p_acl; |
|
|
|
#if (BTM_SCO_WAKE_PARKED_LINK == TRUE) |
|
tBTM_PM_PWR_MD pm; |
|
tBTM_PM_STATE state; |
|
#else |
|
uint8_t mode; |
|
#endif // BTM_SCO_WAKE_PARKED_LINK |
|
|
|
*p_sco_inx = BTM_INVALID_SCO_INDEX; |
|
|
|
/* If originating, ensure that there is an ACL connection to the BD Address */ |
|
|
|
if (is_orig) { |
|
if (!remote_bda) { |
|
BTM_TRACE_ERROR("%s: remote_bda is null", __func__); |
|
return BTM_ILLEGAL_VALUE; |
|
} |
|
acl_handle = BTM_GetHCIConnHandle(*remote_bda, BT_TRANSPORT_BR_EDR); |
|
if (acl_handle == 0xFFFF) { |
|
VLOG(2) << __func__ << ": cannot find ACL handle for remote device " |
|
<< remote_bda; |
|
return BTM_UNKNOWN_ADDR; |
|
} |
|
} |
|
|
|
if (remote_bda) { |
|
/* If any SCO is being established to the remote BD address, refuse this */ |
|
for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) { |
|
if (((p->state == SCO_ST_CONNECTING) || (p->state == SCO_ST_LISTENING) || |
|
(p->state == SCO_ST_PEND_UNPARK)) && |
|
(p->esco.data.bd_addr == *remote_bda)) { |
|
return BTM_BUSY; |
|
} |
|
} |
|
} else { |
|
/* Support only 1 wildcard BD address at a time */ |
|
for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) { |
|
if ((p->state == SCO_ST_LISTENING) && (!p->rem_bd_known)) return BTM_BUSY; |
|
} |
|
} |
|
|
|
/* Try to find an unused control block, and kick off the SCO establishment */ |
|
for (xx = 0, p = &btm_cb.sco_cb.sco_db[0]; xx < BTM_MAX_SCO_LINKS; |
|
xx++, p++) { |
|
if (p->state == SCO_ST_UNUSED) { |
|
if (remote_bda) { |
|
if (is_orig) { |
|
/* can not create SCO link if in park mode */ |
|
#if (BTM_SCO_WAKE_PARKED_LINK == TRUE) |
|
if ((btm_read_power_mode_state(p->esco.data.bd_addr, &state) == |
|
BTM_SUCCESS)) { |
|
if (state == BTM_PM_ST_SNIFF || state == BTM_PM_ST_PARK || |
|
state == BTM_PM_ST_PENDING) { |
|
BTM_TRACE_DEBUG("%s In sniff, park or pend mode: %d", __func__, |
|
state); |
|
memset((void*)&pm, 0, sizeof(pm)); |
|
pm.mode = BTM_PM_MD_ACTIVE; |
|
BTM_SetPowerMode(BTM_PM_SET_ONLY_ID, *remote_bda, &pm); |
|
p->state = SCO_ST_PEND_UNPARK; |
|
} |
|
} |
|
#else // BTM_SCO_WAKE_PARKED_LINK |
|
if ((BTM_ReadPowerMode(*remote_bda, &mode) == BTM_SUCCESS) && |
|
(mode == BTM_PM_MD_PARK)) |
|
return (BTM_WRONG_MODE); |
|
#endif // BTM_SCO_WAKE_PARKED_LINK |
|
} |
|
p->esco.data.bd_addr = *remote_bda; |
|
p->rem_bd_known = true; |
|
} else |
|
p->rem_bd_known = false; |
|
|
|
p_setup = &p->esco.setup; |
|
*p_setup = btm_cb.sco_cb.def_esco_parms; |
|
|
|
/* Determine the packet types */ |
|
p_setup->packet_types = pkt_types & BTM_SCO_SUPPORTED_PKTS_MASK & |
|
btm_cb.btm_sco_pkt_types_supported; |
|
/* OR in any exception packet types */ |
|
if (controller_get_interface()->get_bt_version()->hci_version >= |
|
HCI_PROTO_VERSION_2_0) { |
|
p_setup->packet_types |= |
|
(pkt_types & BTM_SCO_EXCEPTION_PKTS_MASK) | |
|
(btm_cb.btm_sco_pkt_types_supported & BTM_SCO_EXCEPTION_PKTS_MASK); |
|
} |
|
|
|
p->p_conn_cb = p_conn_cb; |
|
p->p_disc_cb = p_disc_cb; |
|
p->hci_handle = BTM_INVALID_HCI_HANDLE; |
|
p->is_orig = is_orig; |
|
|
|
if (p->state != SCO_ST_PEND_UNPARK) { |
|
if (is_orig) { |
|
/* If role change is in progress, do not proceed with SCO setup |
|
* Wait till role change is complete */ |
|
p_acl = btm_bda_to_acl(*remote_bda, BT_TRANSPORT_BR_EDR); |
|
if (p_acl && p_acl->switch_role_state != BTM_ACL_SWKEY_STATE_IDLE) { |
|
BTM_TRACE_API("Role Change is in progress for ACL handle 0x%04x", |
|
acl_handle); |
|
p->state = SCO_ST_PEND_ROLECHANGE; |
|
} |
|
} |
|
} |
|
|
|
if (p->state != SCO_ST_PEND_UNPARK && |
|
p->state != SCO_ST_PEND_ROLECHANGE) { |
|
if (is_orig) { |
|
BTM_TRACE_API("%s:(e)SCO Link for ACL handle 0x%04x", __func__, |
|
acl_handle); |
|
|
|
if ((btm_send_connect_request(acl_handle, p_setup)) != |
|
BTM_CMD_STARTED) |
|
return (BTM_NO_RESOURCES); |
|
|
|
p->state = SCO_ST_CONNECTING; |
|
} else |
|
p->state = SCO_ST_LISTENING; |
|
} |
|
|
|
*p_sco_inx = xx; |
|
|
|
return BTM_CMD_STARTED; |
|
} |
|
} |
|
|
|
#endif |
|
/* If here, all SCO blocks in use */ |
|
return BTM_NO_RESOURCES; |
|
} |
|
|
|
#if (BTM_SCO_WAKE_PARKED_LINK == TRUE) |
|
/******************************************************************************* |
|
* |
|
* Function btm_sco_chk_pend_unpark |
|
* |
|
* Description This function is called by BTIF when there is a mode change |
|
* event to see if there are SCO commands waiting for the |
|
* unpark. |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
void btm_sco_chk_pend_unpark(uint8_t hci_status, uint16_t hci_handle) { |
|
#if (BTM_MAX_SCO_LINKS > 0) |
|
uint16_t xx; |
|
uint16_t acl_handle; |
|
tSCO_CONN* p = &btm_cb.sco_cb.sco_db[0]; |
|
|
|
for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) { |
|
if ((p->state == SCO_ST_PEND_UNPARK) && |
|
((acl_handle = BTM_GetHCIConnHandle( |
|
p->esco.data.bd_addr, BT_TRANSPORT_BR_EDR)) == hci_handle)) |
|
|
|
{ |
|
BTM_TRACE_API( |
|
"%s:(e)SCO Link for ACL " |
|
"handle 0x%04x, hci_status 0x%02x", |
|
__func__, acl_handle, hci_status); |
|
|
|
if ((btm_send_connect_request(acl_handle, &p->esco.setup)) == |
|
BTM_CMD_STARTED) |
|
p->state = SCO_ST_CONNECTING; |
|
} |
|
} |
|
#endif // BTM_MAX_SCO_LINKS |
|
} |
|
#endif // BTM_SCO_WAKE_PARKED_LINK |
|
|
|
/******************************************************************************* |
|
* |
|
* Function btm_sco_chk_pend_rolechange |
|
* |
|
* Description This function is called by BTIF when there is a role change |
|
* event to see if there are SCO commands waiting for the role |
|
* change. |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
void btm_sco_chk_pend_rolechange(uint16_t hci_handle) { |
|
#if (BTM_MAX_SCO_LINKS > 0) |
|
uint16_t xx; |
|
uint16_t acl_handle; |
|
tSCO_CONN* p = &btm_cb.sco_cb.sco_db[0]; |
|
|
|
for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) { |
|
if ((p->state == SCO_ST_PEND_ROLECHANGE) && |
|
((acl_handle = BTM_GetHCIConnHandle( |
|
p->esco.data.bd_addr, BT_TRANSPORT_BR_EDR)) == hci_handle)) |
|
|
|
{ |
|
BTM_TRACE_API( |
|
"btm_sco_chk_pend_rolechange -> (e)SCO Link for ACL handle 0x%04x", |
|
acl_handle); |
|
|
|
if ((btm_send_connect_request(acl_handle, &p->esco.setup)) == |
|
BTM_CMD_STARTED) |
|
p->state = SCO_ST_CONNECTING; |
|
} |
|
} |
|
#endif |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function btm_sco_disc_chk_pend_for_modechange |
|
* |
|
* Description This function is called by btm when there is a mode change |
|
* event to see if there are SCO disconnect commands waiting |
|
* for the mode change. |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
void btm_sco_disc_chk_pend_for_modechange(uint16_t hci_handle) { |
|
#if (BTM_MAX_SCO_LINKS > 0) |
|
tSCO_CONN* p = &btm_cb.sco_cb.sco_db[0]; |
|
|
|
BTM_TRACE_DEBUG("%s: hci_handle 0x%04x, p->state 0x%02x", __func__, |
|
hci_handle, p->state); |
|
|
|
for (uint16_t xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) { |
|
if ((p->state == SCO_ST_PEND_MODECHANGE) && |
|
(BTM_GetHCIConnHandle(p->esco.data.bd_addr, BT_TRANSPORT_BR_EDR)) == |
|
hci_handle) |
|
|
|
{ |
|
BTM_TRACE_DEBUG("%s: SCO Link handle 0x%04x", __func__, p->hci_handle); |
|
BTM_RemoveSco(xx); |
|
} |
|
} |
|
#endif |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function btm_sco_conn_req |
|
* |
|
* Description This function is called by BTIF when an SCO connection |
|
* request is received from a remote. |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
void btm_sco_conn_req(const RawAddress& bda, DEV_CLASS dev_class, |
|
uint8_t link_type) { |
|
#if (BTM_MAX_SCO_LINKS > 0) |
|
tSCO_CB* p_sco = &btm_cb.sco_cb; |
|
tSCO_CONN* p = &p_sco->sco_db[0]; |
|
uint16_t xx; |
|
tBTM_ESCO_CONN_REQ_EVT_DATA evt_data; |
|
|
|
for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) { |
|
/* |
|
* If the sco state is in the SCO_ST_CONNECTING state, we still need |
|
* to return accept sco to avoid race conditon for sco creation |
|
*/ |
|
int rem_bd_matches = p->rem_bd_known && p->esco.data.bd_addr == bda; |
|
|
|
/** M: Bug Fix for create sco collision @{ */ |
|
if ((p->state == SCO_ST_CONNECTING) && rem_bd_matches) |
|
p->is_collision = true; |
|
/** @} */ |
|
|
|
if (((p->state == SCO_ST_CONNECTING) && rem_bd_matches) || |
|
((p->state == SCO_ST_LISTENING) && |
|
(rem_bd_matches || !p->rem_bd_known))) { |
|
/* If this guy was a wildcard, he is not one any more */ |
|
p->rem_bd_known = true; |
|
p->esco.data.link_type = link_type; |
|
p->state = SCO_ST_W4_CONN_RSP; |
|
p->esco.data.bd_addr = bda; |
|
|
|
/* If no callback, auto-accept the connection if packet types match */ |
|
if (!p->esco.p_esco_cback) { |
|
/* If requesting eSCO reject if default parameters are SCO only */ |
|
if ((link_type == BTM_LINK_TYPE_ESCO && |
|
!(p_sco->def_esco_parms.packet_types & BTM_ESCO_LINK_ONLY_MASK) && |
|
((p_sco->def_esco_parms.packet_types & |
|
BTM_SCO_EXCEPTION_PKTS_MASK) == BTM_SCO_EXCEPTION_PKTS_MASK)) |
|
|
|
/* Reject request if SCO is desired but no SCO packets delected */ |
|
|| |
|
(link_type == BTM_LINK_TYPE_SCO && |
|
!(p_sco->def_esco_parms.packet_types & BTM_SCO_LINK_ONLY_MASK))) { |
|
btm_esco_conn_rsp(xx, HCI_ERR_HOST_REJECT_RESOURCES, bda, NULL); |
|
} else /* Accept the request */ |
|
{ |
|
btm_esco_conn_rsp(xx, HCI_SUCCESS, bda, NULL); |
|
} |
|
} else /* Notify upper layer of connect indication */ |
|
{ |
|
evt_data.bd_addr = bda; |
|
memcpy(evt_data.dev_class, dev_class, DEV_CLASS_LEN); |
|
evt_data.link_type = link_type; |
|
evt_data.sco_inx = xx; |
|
tBTM_ESCO_EVT_DATA btm_esco_evt_data; |
|
btm_esco_evt_data.conn_evt = evt_data; |
|
p->esco.p_esco_cback(BTM_ESCO_CONN_REQ_EVT, &btm_esco_evt_data); |
|
} |
|
|
|
return; |
|
} |
|
} |
|
|
|
/* TCS usage */ |
|
if (btm_cb.sco_cb.app_sco_ind_cb) { |
|
/* Now, try to find an unused control block */ |
|
for (xx = 0, p = &btm_cb.sco_cb.sco_db[0]; xx < BTM_MAX_SCO_LINKS; |
|
xx++, p++) { |
|
if (p->state == SCO_ST_UNUSED) { |
|
p->is_orig = false; |
|
p->state = SCO_ST_LISTENING; |
|
|
|
p->esco.data.link_type = link_type; |
|
p->esco.data.bd_addr = bda; |
|
p->rem_bd_known = true; |
|
break; |
|
} |
|
} |
|
if (xx < BTM_MAX_SCO_LINKS) { |
|
btm_cb.sco_cb.app_sco_ind_cb(xx); |
|
return; |
|
} |
|
} |
|
|
|
#endif |
|
/* If here, no one wants the SCO connection. Reject it */ |
|
BTM_TRACE_WARNING( |
|
"btm_sco_conn_req: No one wants this SCO connection; rejecting it"); |
|
btm_esco_conn_rsp(BTM_MAX_SCO_LINKS, HCI_ERR_HOST_REJECT_RESOURCES, bda, |
|
NULL); |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function btm_sco_connected |
|
* |
|
* Description This function is called by BTIF when an (e)SCO connection |
|
* is connected. |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
void btm_sco_connected(uint8_t hci_status, const RawAddress* bda, |
|
uint16_t hci_handle, tBTM_ESCO_DATA* p_esco_data) { |
|
#if (BTM_MAX_SCO_LINKS > 0) |
|
tSCO_CONN* p = &btm_cb.sco_cb.sco_db[0]; |
|
uint16_t xx; |
|
bool spt = false; |
|
tBTM_CHG_ESCO_PARAMS parms; |
|
#endif |
|
|
|
btm_cb.sco_cb.sco_disc_reason = hci_status; |
|
|
|
#if (BTM_MAX_SCO_LINKS > 0) |
|
for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) { |
|
/** M: Bug Fix for create sco collision @{ */ |
|
if (p->is_collision && (!bda) && hci_status != HCI_SUCCESS) { |
|
p->is_collision = false; |
|
return; |
|
} |
|
/** @} */ |
|
if (((p->state == SCO_ST_CONNECTING) || (p->state == SCO_ST_LISTENING) || |
|
(p->state == SCO_ST_W4_CONN_RSP)) && |
|
(p->rem_bd_known) && (!bda || p->esco.data.bd_addr == *bda)) { |
|
if (hci_status != HCI_SUCCESS) { |
|
/* Report the error if originator, otherwise remain in Listen mode */ |
|
if (p->is_orig) { |
|
/* If role switch is pending, we need try again after role switch is |
|
* complete */ |
|
if (hci_status == HCI_ERR_ROLE_SWITCH_PENDING) { |
|
BTM_TRACE_API("Role Change pending for HCI handle 0x%04x", |
|
hci_handle); |
|
p->state = SCO_ST_PEND_ROLECHANGE; |
|
} |
|
/* avoid calling disconnect callback because of sco creation race */ |
|
else if (hci_status != HCI_ERR_LMP_ERR_TRANS_COLLISION) { |
|
p->state = SCO_ST_UNUSED; |
|
(*p->p_disc_cb)(xx); |
|
} |
|
} else { |
|
/* Notify the upper layer that incoming sco connection has failed. */ |
|
if (p->state == SCO_ST_CONNECTING) { |
|
p->state = SCO_ST_UNUSED; |
|
(*p->p_disc_cb)(xx); |
|
} else |
|
p->state = SCO_ST_LISTENING; |
|
} |
|
/** M: Bug Fix for create sco collision @{ */ |
|
p->is_collision = false; |
|
/** @} */ |
|
|
|
return; |
|
} |
|
|
|
if (p->state == SCO_ST_LISTENING) spt = true; |
|
|
|
p->state = SCO_ST_CONNECTED; |
|
p->hci_handle = hci_handle; |
|
/** M: Bug Fix for create sco collision @{ */ |
|
p->is_collision = false; |
|
/** @} */ |
|
|
|
if (!btm_cb.sco_cb.esco_supported) { |
|
p->esco.data.link_type = BTM_LINK_TYPE_SCO; |
|
if (spt) { |
|
parms.packet_types = p->esco.setup.packet_types; |
|
/* Keep the other parameters the same for SCO */ |
|
parms.max_latency_ms = p->esco.setup.max_latency_ms; |
|
parms.retransmission_effort = p->esco.setup.retransmission_effort; |
|
|
|
BTM_ChangeEScoLinkParms(xx, &parms); |
|
} |
|
} else { |
|
if (p_esco_data) p->esco.data = *p_esco_data; |
|
} |
|
|
|
(*p->p_conn_cb)(xx); |
|
|
|
return; |
|
} |
|
} |
|
#endif |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function btm_find_scb_by_handle |
|
* |
|
* Description Look through all active SCO connection for a match based on |
|
* the HCI handle. |
|
* |
|
* Returns index to matched SCO connection CB, or BTM_MAX_SCO_LINKS if |
|
* no match. |
|
* |
|
******************************************************************************/ |
|
uint16_t btm_find_scb_by_handle(uint16_t handle) { |
|
int xx; |
|
tSCO_CONN* p = &btm_cb.sco_cb.sco_db[0]; |
|
|
|
for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) { |
|
if ((p->state == SCO_ST_CONNECTED) && (p->hci_handle == handle)) { |
|
return (xx); |
|
} |
|
} |
|
|
|
/* If here, no match found */ |
|
return (xx); |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function BTM_RemoveSco |
|
* |
|
* Description This function is called to remove a specific SCO connection. |
|
* |
|
* Returns status of the operation |
|
* |
|
******************************************************************************/ |
|
tBTM_STATUS BTM_RemoveSco(uint16_t sco_inx) { |
|
#if (BTM_MAX_SCO_LINKS > 0) |
|
tSCO_CONN* p = &btm_cb.sco_cb.sco_db[sco_inx]; |
|
uint16_t tempstate; |
|
tBTM_PM_STATE state = BTM_PM_ST_INVALID; |
|
|
|
BTM_TRACE_DEBUG("%s", __func__); |
|
|
|
/* Validity check */ |
|
if ((sco_inx >= BTM_MAX_SCO_LINKS) || (p->state == SCO_ST_UNUSED)) |
|
return (BTM_UNKNOWN_ADDR); |
|
|
|
/* If no HCI handle, simply drop the connection and return */ |
|
if (p->hci_handle == BTM_INVALID_HCI_HANDLE || |
|
p->state == SCO_ST_PEND_UNPARK) { |
|
p->hci_handle = BTM_INVALID_HCI_HANDLE; |
|
p->state = SCO_ST_UNUSED; |
|
p->esco.p_esco_cback = NULL; /* Deregister the eSCO event callback */ |
|
return (BTM_SUCCESS); |
|
} |
|
|
|
if ((btm_read_power_mode_state(p->esco.data.bd_addr, &state) == |
|
BTM_SUCCESS) && |
|
state == BTM_PM_ST_PENDING) { |
|
BTM_TRACE_DEBUG("%s: BTM_PM_ST_PENDING for ACL mapped with SCO Link 0x%04x", |
|
__func__, p->hci_handle); |
|
p->state = SCO_ST_PEND_MODECHANGE; |
|
return (BTM_CMD_STARTED); |
|
} |
|
|
|
tempstate = p->state; |
|
p->state = SCO_ST_DISCONNECTING; |
|
|
|
btsnd_hcic_disconnect(p->hci_handle, HCI_ERR_PEER_USER); |
|
|
|
return (BTM_CMD_STARTED); |
|
#else |
|
return (BTM_NO_RESOURCES); |
|
#endif |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function btm_remove_sco_links |
|
* |
|
* Description This function is called to remove all sco links for an ACL |
|
* link. |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
void btm_remove_sco_links(const RawAddress& bda) { |
|
#if (BTM_MAX_SCO_LINKS > 0) |
|
tSCO_CONN* p = &btm_cb.sco_cb.sco_db[0]; |
|
uint16_t xx; |
|
|
|
for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) { |
|
if (p->rem_bd_known && p->esco.data.bd_addr == bda) { |
|
BTM_RemoveSco(xx); |
|
} |
|
} |
|
#endif |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function btm_sco_removed |
|
* |
|
* Description This function is called by BTIF when an SCO connection |
|
* is removed. |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
void btm_sco_removed(uint16_t hci_handle, uint8_t reason) { |
|
#if (BTM_MAX_SCO_LINKS > 0) |
|
tSCO_CONN* p = &btm_cb.sco_cb.sco_db[0]; |
|
uint16_t xx; |
|
#endif |
|
|
|
btm_cb.sco_cb.sco_disc_reason = reason; |
|
|
|
#if (BTM_MAX_SCO_LINKS > 0) |
|
p = &btm_cb.sco_cb.sco_db[0]; |
|
for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) { |
|
if ((p->state != SCO_ST_UNUSED) && (p->state != SCO_ST_LISTENING) && |
|
(p->hci_handle == hci_handle)) { |
|
btm_sco_flush_sco_data(xx); |
|
|
|
p->state = SCO_ST_UNUSED; |
|
p->hci_handle = BTM_INVALID_HCI_HANDLE; |
|
p->rem_bd_known = false; |
|
p->esco.p_esco_cback = NULL; /* Deregister eSCO callback */ |
|
(*p->p_disc_cb)(xx); |
|
|
|
return; |
|
} |
|
} |
|
#endif |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function btm_sco_acl_removed |
|
* |
|
* Description This function is called when an ACL connection is |
|
* removed. If the BD address is NULL, it is assumed that |
|
* the local device is down, and all SCO links are removed. |
|
* If a specific BD address is passed, only SCO connections |
|
* to that BD address are removed. |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
void btm_sco_acl_removed(const RawAddress* bda) { |
|
#if (BTM_MAX_SCO_LINKS > 0) |
|
tSCO_CONN* p = &btm_cb.sco_cb.sco_db[0]; |
|
uint16_t xx; |
|
|
|
for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) { |
|
if (p->state != SCO_ST_UNUSED) { |
|
if ((!bda) || (p->esco.data.bd_addr == *bda && p->rem_bd_known)) { |
|
btm_sco_flush_sco_data(xx); |
|
|
|
p->state = SCO_ST_UNUSED; |
|
p->esco.p_esco_cback = NULL; /* Deregister eSCO callback */ |
|
(*p->p_disc_cb)(xx); |
|
} |
|
} |
|
} |
|
#endif |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function BTM_SetScoPacketTypes |
|
* |
|
* Description This function is called to set the packet types used for |
|
* a specific SCO connection, |
|
* |
|
* Parameters pkt_types - One or more of the following |
|
* BTM_SCO_PKT_TYPES_MASK_HV1 |
|
* 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_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_LINK_ALL_MASK - enables all supported types |
|
* |
|
* Returns status of the operation |
|
* |
|
******************************************************************************/ |
|
tBTM_STATUS BTM_SetScoPacketTypes(uint16_t sco_inx, uint16_t pkt_types) { |
|
#if (BTM_MAX_SCO_LINKS > 0) |
|
tBTM_CHG_ESCO_PARAMS parms; |
|
tSCO_CONN* p; |
|
|
|
/* Validity check */ |
|
if (sco_inx >= BTM_MAX_SCO_LINKS) return (BTM_UNKNOWN_ADDR); |
|
|
|
p = &btm_cb.sco_cb.sco_db[sco_inx]; |
|
parms.packet_types = pkt_types; |
|
|
|
/* Keep the other parameters the same for SCO */ |
|
parms.max_latency_ms = p->esco.setup.max_latency_ms; |
|
parms.retransmission_effort = p->esco.setup.retransmission_effort; |
|
|
|
return (BTM_ChangeEScoLinkParms(sco_inx, &parms)); |
|
#else |
|
return (BTM_UNKNOWN_ADDR); |
|
#endif |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function BTM_ReadScoPacketTypes |
|
* |
|
* Description This function is read the packet types used for a specific |
|
* SCO connection. |
|
* |
|
* Returns Packet types supported for the connection |
|
* One or more of the following (bitmask): |
|
* BTM_SCO_PKT_TYPES_MASK_HV1 |
|
* 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_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 |
|
* |
|
******************************************************************************/ |
|
uint16_t BTM_ReadScoPacketTypes(uint16_t sco_inx) { |
|
#if (BTM_MAX_SCO_LINKS > 0) |
|
tSCO_CONN* p = &btm_cb.sco_cb.sco_db[sco_inx]; |
|
|
|
/* Validity check */ |
|
if ((sco_inx < BTM_MAX_SCO_LINKS) && (p->state == SCO_ST_CONNECTED)) |
|
return (p->esco.setup.packet_types); |
|
else |
|
return (0); |
|
#else |
|
return (0); |
|
#endif |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function BTM_ReadScoDiscReason |
|
* |
|
* Description This function is returns the reason why an (e)SCO connection |
|
* has been removed. It contains the value until read, or until |
|
* another (e)SCO connection has disconnected. |
|
* |
|
* Returns HCI reason or BTM_INVALID_SCO_DISC_REASON if not set. |
|
* |
|
******************************************************************************/ |
|
uint16_t BTM_ReadScoDiscReason(void) { |
|
uint16_t res = btm_cb.sco_cb.sco_disc_reason; |
|
btm_cb.sco_cb.sco_disc_reason = BTM_INVALID_SCO_DISC_REASON; |
|
return (res); |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function BTM_ReadDeviceScoPacketTypes |
|
* |
|
* Description This function is read the SCO packet types that |
|
* the device supports. |
|
* |
|
* Returns Packet types supported by the device. |
|
* One or more of the following (bitmask): |
|
* BTM_SCO_PKT_TYPES_MASK_HV1 |
|
* 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_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 |
|
* |
|
******************************************************************************/ |
|
uint16_t BTM_ReadDeviceScoPacketTypes(void) { |
|
return (btm_cb.btm_sco_pkt_types_supported); |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function BTM_ReadScoHandle |
|
* |
|
* Description This function is used to read the HCI handle used for a |
|
* specific SCO connection, |
|
* |
|
* Returns handle for the connection, or 0xFFFF if invalid SCO index. |
|
* |
|
******************************************************************************/ |
|
uint16_t BTM_ReadScoHandle(uint16_t sco_inx) { |
|
#if (BTM_MAX_SCO_LINKS > 0) |
|
tSCO_CONN* p = &btm_cb.sco_cb.sco_db[sco_inx]; |
|
|
|
/* Validity check */ |
|
if ((sco_inx < BTM_MAX_SCO_LINKS) && (p->state == SCO_ST_CONNECTED)) |
|
return (p->hci_handle); |
|
else |
|
return (BTM_INVALID_HCI_HANDLE); |
|
#else |
|
return (BTM_INVALID_HCI_HANDLE); |
|
#endif |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function BTM_ReadScoBdAddr |
|
* |
|
* Description This function is read the remote BD Address for a specific |
|
* SCO connection, |
|
* |
|
* Returns pointer to BD address or NULL if not known |
|
* |
|
******************************************************************************/ |
|
const RawAddress* BTM_ReadScoBdAddr(uint16_t sco_inx) { |
|
#if (BTM_MAX_SCO_LINKS > 0) |
|
tSCO_CONN* p = &btm_cb.sco_cb.sco_db[sco_inx]; |
|
|
|
/* Validity check */ |
|
if ((sco_inx < BTM_MAX_SCO_LINKS) && (p->rem_bd_known)) |
|
return &(p->esco.data.bd_addr); |
|
else |
|
return (NULL); |
|
#else |
|
return (NULL); |
|
#endif |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function BTM_SetEScoMode |
|
* |
|
* Description This function sets up the negotiated parameters for SCO or |
|
* eSCO, and sets as the default mode used for outgoing calls |
|
* to BTM_CreateSco. It does not change any currently active |
|
* (e)SCO links. |
|
* Note: Incoming (e)SCO connections will always use packet |
|
* types supported by the controller. If eSCO is not |
|
* desired the feature should be disabled in the |
|
* controller's feature mask. |
|
* |
|
* Returns BTM_SUCCESS if the successful. |
|
* BTM_BUSY if there are one or more active (e)SCO links. |
|
* |
|
******************************************************************************/ |
|
tBTM_STATUS BTM_SetEScoMode(enh_esco_params_t* p_parms) { |
|
enh_esco_params_t* p_def = &btm_cb.sco_cb.def_esco_parms; |
|
|
|
if (btm_cb.sco_cb.esco_supported) { |
|
*p_def = *p_parms; |
|
} else { |
|
/* Load defaults for SCO only */ |
|
*p_def = esco_parameters_for_codec(ESCO_CODEC_CVSD); |
|
p_def->packet_types &= BTM_SCO_LINK_ONLY_MASK; |
|
p_def->retransmission_effort = ESCO_RETRANSMISSION_OFF; |
|
p_def->max_latency_ms = 12; |
|
BTM_TRACE_WARNING("%s: eSCO not supported", __func__); |
|
} |
|
|
|
BTM_TRACE_API( |
|
"%s: txbw 0x%08x, rxbw 0x%08x, max_lat 0x%04x, " |
|
"pkt 0x%04x, rtx effort 0x%02x", |
|
__func__, p_def->transmit_bandwidth, p_def->receive_bandwidth, |
|
p_def->max_latency_ms, p_def->packet_types, p_def->retransmission_effort); |
|
|
|
return BTM_SUCCESS; |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function BTM_RegForEScoEvts |
|
* |
|
* Description This function registers a SCO event callback with the |
|
* specified instance. It should be used to received |
|
* connection indication events and change of link parameter |
|
* events. |
|
* |
|
* Returns BTM_SUCCESS if the successful. |
|
* BTM_ILLEGAL_VALUE if there is an illegal sco_inx |
|
* BTM_MODE_UNSUPPORTED if controller version is not BT1.2 or |
|
* later or does not support eSCO. |
|
* |
|
******************************************************************************/ |
|
tBTM_STATUS BTM_RegForEScoEvts(uint16_t sco_inx, |
|
tBTM_ESCO_CBACK* p_esco_cback) { |
|
#if (BTM_MAX_SCO_LINKS > 0) |
|
if (!btm_cb.sco_cb.esco_supported) { |
|
btm_cb.sco_cb.sco_db[sco_inx].esco.p_esco_cback = NULL; |
|
return (BTM_MODE_UNSUPPORTED); |
|
} |
|
|
|
if (sco_inx < BTM_MAX_SCO_LINKS && |
|
btm_cb.sco_cb.sco_db[sco_inx].state != SCO_ST_UNUSED) { |
|
btm_cb.sco_cb.sco_db[sco_inx].esco.p_esco_cback = p_esco_cback; |
|
return (BTM_SUCCESS); |
|
} |
|
return (BTM_ILLEGAL_VALUE); |
|
#else |
|
return (BTM_MODE_UNSUPPORTED); |
|
#endif |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function BTM_ReadEScoLinkParms |
|
* |
|
* Description This function returns the current eSCO link parameters for |
|
* the specified handle. This can be called anytime a |
|
* connection is active, but is typically called after |
|
* receiving the SCO opened callback. |
|
* |
|
* Note: If called over a 1.1 controller, only the packet types |
|
* field has meaning. |
|
* |
|
* Returns BTM_SUCCESS if returned data is valid connection. |
|
* BTM_WRONG_MODE if no connection with a peer device or bad |
|
* sco_inx. |
|
* |
|
******************************************************************************/ |
|
tBTM_STATUS BTM_ReadEScoLinkParms(uint16_t sco_inx, tBTM_ESCO_DATA* p_parms) { |
|
#if (BTM_MAX_SCO_LINKS > 0) |
|
uint8_t index; |
|
|
|
BTM_TRACE_API("%s: -> sco_inx 0x%04x", __func__, sco_inx); |
|
|
|
if (sco_inx < BTM_MAX_SCO_LINKS && |
|
btm_cb.sco_cb.sco_db[sco_inx].state >= SCO_ST_CONNECTED) { |
|
*p_parms = btm_cb.sco_cb.sco_db[sco_inx].esco.data; |
|
return (BTM_SUCCESS); |
|
} |
|
|
|
if (sco_inx == BTM_FIRST_ACTIVE_SCO_INDEX) { |
|
for (index = 0; index < BTM_MAX_SCO_LINKS; index++) { |
|
if (btm_cb.sco_cb.sco_db[index].state >= SCO_ST_CONNECTED) { |
|
BTM_TRACE_API("%s: the first active SCO index is %d", __func__, index); |
|
*p_parms = btm_cb.sco_cb.sco_db[index].esco.data; |
|
return (BTM_SUCCESS); |
|
} |
|
} |
|
} |
|
|
|
#endif |
|
|
|
BTM_TRACE_API("BTM_ReadEScoLinkParms cannot find the SCO index!"); |
|
memset(p_parms, 0, sizeof(tBTM_ESCO_DATA)); |
|
return (BTM_WRONG_MODE); |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function BTM_ChangeEScoLinkParms |
|
* |
|
* Description This function requests renegotiation of the parameters on |
|
* the current eSCO Link. If any of the changes are accepted |
|
* by the controllers, the BTM_ESCO_CHG_EVT event is sent in |
|
* the tBTM_ESCO_CBACK function with the current settings of |
|
* the link. The callback is registered through the call to |
|
* BTM_SetEScoMode. |
|
* |
|
* Note: If called over a SCO link (including 1.1 controller), |
|
* a change packet type request is sent out instead. |
|
* |
|
* Returns BTM_CMD_STARTED if command is successfully initiated. |
|
* BTM_NO_RESOURCES - not enough resources to initiate command. |
|
* BTM_WRONG_MODE if no connection with a peer device or bad |
|
* sco_inx. |
|
* |
|
******************************************************************************/ |
|
tBTM_STATUS BTM_ChangeEScoLinkParms(uint16_t sco_inx, |
|
tBTM_CHG_ESCO_PARAMS* p_parms) { |
|
#if (BTM_MAX_SCO_LINKS > 0) |
|
|
|
/* Make sure sco handle is valid and on an active link */ |
|
if (sco_inx >= BTM_MAX_SCO_LINKS || |
|
btm_cb.sco_cb.sco_db[sco_inx].state != SCO_ST_CONNECTED) |
|
return (BTM_WRONG_MODE); |
|
|
|
tSCO_CONN* p_sco = &btm_cb.sco_cb.sco_db[sco_inx]; |
|
enh_esco_params_t* p_setup = &p_sco->esco.setup; |
|
|
|
/* Save the previous types in case command fails */ |
|
uint16_t saved_packet_types = p_setup->packet_types; |
|
|
|
/* If SCO connection OR eSCO not supported just send change packet types */ |
|
if (p_sco->esco.data.link_type == BTM_LINK_TYPE_SCO || |
|
!btm_cb.sco_cb.esco_supported) { |
|
p_setup->packet_types = |
|
p_parms->packet_types & |
|
(btm_cb.btm_sco_pkt_types_supported & BTM_SCO_LINK_ONLY_MASK); |
|
|
|
BTM_TRACE_API("%s: SCO Link for handle 0x%04x, pkt 0x%04x", __func__, |
|
p_sco->hci_handle, p_setup->packet_types); |
|
|
|
BTM_TRACE_API("%s: SCO Link for handle 0x%04x, pkt 0x%04x", __func__, |
|
p_sco->hci_handle, p_setup->packet_types); |
|
|
|
btsnd_hcic_change_conn_type(p_sco->hci_handle, |
|
BTM_ESCO_2_SCO(p_setup->packet_types)); |
|
} else /* eSCO is supported and the link type is eSCO */ |
|
{ |
|
uint16_t temp_packet_types = |
|
(p_parms->packet_types & BTM_SCO_SUPPORTED_PKTS_MASK & |
|
btm_cb.btm_sco_pkt_types_supported); |
|
|
|
/* OR in any exception packet types */ |
|
temp_packet_types |= |
|
((p_parms->packet_types & BTM_SCO_EXCEPTION_PKTS_MASK) | |
|
(btm_cb.btm_sco_pkt_types_supported & BTM_SCO_EXCEPTION_PKTS_MASK)); |
|
p_setup->packet_types = temp_packet_types; |
|
|
|
BTM_TRACE_API("%s -> eSCO Link for handle 0x%04x", __func__, |
|
p_sco->hci_handle); |
|
BTM_TRACE_API( |
|
" txbw 0x%x, rxbw 0x%x, lat 0x%x, retrans 0x%02x, pkt 0x%04x", |
|
p_setup->transmit_bandwidth, p_setup->receive_bandwidth, |
|
p_parms->max_latency_ms, p_parms->retransmission_effort, |
|
temp_packet_types); |
|
|
|
/** M: Change feature For enhanced setup synchronous connection. @{ */ |
|
// MTK platform uses differenct command by codec type. |
|
/* Use Enhanced Synchronous commands if supported */ |
|
if (p_setup && (p_setup->transmit_coding_format.coding_format == ESCO_CODING_FORMAT_MSBC |
|
&& p_setup->receive_coding_format.coding_format == ESCO_CODING_FORMAT_MSBC)) { |
|
/*if (controller_get_interface() |
|
->supports_enhanced_setup_synchronous_connection()) {*/ |
|
/* Use the saved SCO routing */ |
|
/** @} */ |
|
p_setup->input_data_path = p_setup->output_data_path = |
|
btm_cb.sco_cb.sco_route; |
|
|
|
btsnd_hcic_enhanced_set_up_synchronous_connection(p_sco->hci_handle, |
|
p_setup); |
|
p_setup->packet_types = saved_packet_types; |
|
} else { /* Use older command */ |
|
uint16_t voice_content_format = btm_sco_voice_settings_to_legacy(p_setup); |
|
/* When changing an existing link, only change latency, retrans, and |
|
* pkts */ |
|
btsnd_hcic_setup_esco_conn(p_sco->hci_handle, p_setup->transmit_bandwidth, |
|
p_setup->receive_bandwidth, |
|
p_parms->max_latency_ms, voice_content_format, |
|
p_parms->retransmission_effort, |
|
p_setup->packet_types); |
|
} |
|
|
|
BTM_TRACE_API( |
|
"%s: txbw 0x%x, rxbw 0x%x, lat 0x%x, retrans 0x%02x, pkt 0x%04x", |
|
__func__, p_setup->transmit_bandwidth, p_setup->receive_bandwidth, |
|
p_parms->max_latency_ms, p_parms->retransmission_effort, |
|
temp_packet_types); |
|
} |
|
|
|
return (BTM_CMD_STARTED); |
|
#else |
|
return (BTM_WRONG_MODE); |
|
#endif |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function BTM_EScoConnRsp |
|
* |
|
* Description This function is called upon receipt of an (e)SCO connection |
|
* request event (BTM_ESCO_CONN_REQ_EVT) to accept or reject |
|
* the request. Parameters used to negotiate eSCO links. |
|
* If p_parms is NULL, then values set through BTM_SetEScoMode |
|
* are used. |
|
* If the link type of the incoming request is SCO, then only |
|
* the tx_bw, max_latency, content format, and packet_types are |
|
* valid. The hci_status parameter should be |
|
* ([0x0] to accept, [0x0d..0x0f] to reject) |
|
* |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
void BTM_EScoConnRsp(uint16_t sco_inx, uint8_t hci_status, |
|
enh_esco_params_t* p_parms) { |
|
#if (BTM_MAX_SCO_LINKS > 0) |
|
if (sco_inx < BTM_MAX_SCO_LINKS && |
|
btm_cb.sco_cb.sco_db[sco_inx].state == SCO_ST_W4_CONN_RSP) { |
|
btm_esco_conn_rsp(sco_inx, hci_status, |
|
btm_cb.sco_cb.sco_db[sco_inx].esco.data.bd_addr, p_parms); |
|
} |
|
#endif |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function btm_read_def_esco_mode |
|
* |
|
* Description This function copies the current default esco settings into |
|
* the return buffer. |
|
* |
|
* Returns tBTM_SCO_TYPE |
|
* |
|
******************************************************************************/ |
|
void btm_read_def_esco_mode(enh_esco_params_t* p_parms) { |
|
#if (BTM_MAX_SCO_LINKS > 0) |
|
*p_parms = btm_cb.sco_cb.def_esco_parms; |
|
#endif |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function btm_esco_proc_conn_chg |
|
* |
|
* Description This function is called by BTIF when an SCO connection |
|
* is changed. |
|
* |
|
* Returns void |
|
* |
|
******************************************************************************/ |
|
void btm_esco_proc_conn_chg(uint8_t status, uint16_t handle, |
|
uint8_t tx_interval, uint8_t retrans_window, |
|
uint16_t rx_pkt_len, uint16_t tx_pkt_len) { |
|
#if (BTM_MAX_SCO_LINKS > 0) |
|
tSCO_CONN* p = &btm_cb.sco_cb.sco_db[0]; |
|
tBTM_CHG_ESCO_EVT_DATA data; |
|
uint16_t xx; |
|
|
|
BTM_TRACE_EVENT("btm_esco_proc_conn_chg -> handle 0x%04x, status 0x%02x", |
|
handle, status); |
|
|
|
for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) { |
|
if (p->state == SCO_ST_CONNECTED && handle == p->hci_handle) { |
|
/* If upper layer wants notification */ |
|
if (p->esco.p_esco_cback) { |
|
data.bd_addr = p->esco.data.bd_addr; |
|
data.hci_status = status; |
|
data.sco_inx = xx; |
|
data.rx_pkt_len = p->esco.data.rx_pkt_len = rx_pkt_len; |
|
data.tx_pkt_len = p->esco.data.tx_pkt_len = tx_pkt_len; |
|
data.tx_interval = p->esco.data.tx_interval = tx_interval; |
|
data.retrans_window = p->esco.data.retrans_window = retrans_window; |
|
|
|
tBTM_ESCO_EVT_DATA btm_esco_evt_data; |
|
btm_esco_evt_data.chg_evt = data; |
|
(*p->esco.p_esco_cback)(BTM_ESCO_CHG_EVT, &btm_esco_evt_data); |
|
} |
|
return; |
|
} |
|
} |
|
#endif |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function btm_is_sco_active |
|
* |
|
* Description This function is called to see if a SCO handle is already in |
|
* use. |
|
* |
|
* Returns bool |
|
* |
|
******************************************************************************/ |
|
bool btm_is_sco_active(uint16_t handle) { |
|
#if (BTM_MAX_SCO_LINKS > 0) |
|
uint16_t xx; |
|
tSCO_CONN* p = &btm_cb.sco_cb.sco_db[0]; |
|
|
|
for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) { |
|
if (handle == p->hci_handle && p->state == SCO_ST_CONNECTED) return (true); |
|
} |
|
#endif |
|
return (false); |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function BTM_GetNumScoLinks |
|
* |
|
* Description This function returns the number of active sco links. |
|
* |
|
* Returns uint8_t |
|
* |
|
******************************************************************************/ |
|
uint8_t BTM_GetNumScoLinks(void) { |
|
#if (BTM_MAX_SCO_LINKS > 0) |
|
tSCO_CONN* p = &btm_cb.sco_cb.sco_db[0]; |
|
uint16_t xx; |
|
uint8_t num_scos = 0; |
|
|
|
for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) { |
|
switch (p->state) { |
|
case SCO_ST_W4_CONN_RSP: |
|
case SCO_ST_CONNECTING: |
|
case SCO_ST_CONNECTED: |
|
case SCO_ST_DISCONNECTING: |
|
case SCO_ST_PEND_UNPARK: |
|
num_scos++; |
|
} |
|
} |
|
return (num_scos); |
|
#else |
|
return (0); |
|
#endif |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function btm_is_sco_active_by_bdaddr |
|
* |
|
* Description This function is called to see if a SCO connection is active |
|
* for a bd address. |
|
* |
|
* Returns bool |
|
* |
|
******************************************************************************/ |
|
bool btm_is_sco_active_by_bdaddr(const RawAddress& remote_bda) { |
|
#if (BTM_MAX_SCO_LINKS > 0) |
|
uint8_t xx; |
|
tSCO_CONN* p = &btm_cb.sco_cb.sco_db[0]; |
|
|
|
/* If any SCO is being established to the remote BD address, refuse this */ |
|
for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) { |
|
if (p->esco.data.bd_addr == remote_bda && p->state == SCO_ST_CONNECTED) { |
|
return (true); |
|
} |
|
} |
|
#endif |
|
return (false); |
|
} |
|
|
|
/******************************************************************************* |
|
* |
|
* Function btm_sco_voice_settings_2_legacy |
|
* |
|
* Description This function is called to convert the Enhanced eSCO |
|
* parameters into voice setting parameter mask used |
|
* for legacy setup synchronous connection HCI commands |
|
* |
|
* Returns UINT16 - 16-bit mask for voice settings |
|
* |
|
* HCI_INP_CODING_LINEAR 0x0000 (0000000000) |
|
* HCI_INP_CODING_U_LAW 0x0100 (0100000000) |
|
* HCI_INP_CODING_A_LAW 0x0200 (1000000000) |
|
* HCI_INP_CODING_MASK 0x0300 (1100000000) |
|
* |
|
* HCI_INP_DATA_FMT_1S_COMPLEMENT 0x0000 (0000000000) |
|
* HCI_INP_DATA_FMT_2S_COMPLEMENT 0x0040 (0001000000) |
|
* HCI_INP_DATA_FMT_SIGN_MAGNITUDE 0x0080 (0010000000) |
|
* HCI_INP_DATA_FMT_UNSIGNED 0x00c0 (0011000000) |
|
* HCI_INP_DATA_FMT_MASK 0x00c0 (0011000000) |
|
* |
|
* HCI_INP_SAMPLE_SIZE_8BIT 0x0000 (0000000000) |
|
* HCI_INP_SAMPLE_SIZE_16BIT 0x0020 (0000100000) |
|
* HCI_INP_SAMPLE_SIZE_MASK 0x0020 (0000100000) |
|
* |
|
* HCI_INP_LINEAR_PCM_BIT_POS_MASK 0x001c (0000011100) |
|
* HCI_INP_LINEAR_PCM_BIT_POS_OFFS 2 |
|
* |
|
* HCI_AIR_CODING_FORMAT_CVSD 0x0000 (0000000000) |
|
* HCI_AIR_CODING_FORMAT_U_LAW 0x0001 (0000000001) |
|
* HCI_AIR_CODING_FORMAT_A_LAW 0x0002 (0000000010) |
|
* HCI_AIR_CODING_FORMAT_TRANSPNT 0x0003 (0000000011) |
|
* HCI_AIR_CODING_FORMAT_MASK 0x0003 (0000000011) |
|
* |
|
* default (0001100000) |
|
* HCI_DEFAULT_VOICE_SETTINGS (HCI_INP_CODING_LINEAR \ |
|
* | HCI_INP_DATA_FMT_2S_COMPLEMENT \ |
|
* | HCI_INP_SAMPLE_SIZE_16BIT \ |
|
* | HCI_AIR_CODING_FORMAT_CVSD) |
|
* |
|
******************************************************************************/ |
|
static uint16_t btm_sco_voice_settings_to_legacy(enh_esco_params_t* p_params) { |
|
uint16_t voice_settings = 0; |
|
|
|
/* Convert Input Coding Format: If no uLaw or aLAW then Linear will be used |
|
* (0) */ |
|
if (p_params->input_coding_format.coding_format == ESCO_CODING_FORMAT_ULAW) |
|
voice_settings |= HCI_INP_CODING_U_LAW; |
|
else if (p_params->input_coding_format.coding_format == |
|
ESCO_CODING_FORMAT_ALAW) |
|
voice_settings |= HCI_INP_CODING_A_LAW; |
|
/* else default value of '0 is good 'Linear' */ |
|
|
|
/* Convert Input Data Format. Use 2's Compliment as the default */ |
|
switch (p_params->input_pcm_data_format) { |
|
case ESCO_PCM_DATA_FORMAT_1_COMP: |
|
/* voice_settings |= HCI_INP_DATA_FMT_1S_COMPLEMENT; value is '0' |
|
* already */ |
|
break; |
|
|
|
case ESCO_PCM_DATA_FORMAT_SIGN: |
|
voice_settings |= HCI_INP_DATA_FMT_SIGN_MAGNITUDE; |
|
break; |
|
|
|
case ESCO_PCM_DATA_FORMAT_UNSIGN: |
|
voice_settings |= HCI_INP_DATA_FMT_UNSIGNED; |
|
break; |
|
|
|
default: /* 2's Compliment */ |
|
voice_settings |= HCI_INP_DATA_FMT_2S_COMPLEMENT; |
|
break; |
|
} |
|
|
|
/* Convert Over the Air Coding. Use CVSD as the default */ |
|
switch (p_params->transmit_coding_format.coding_format) { |
|
case ESCO_CODING_FORMAT_ULAW: |
|
voice_settings |= HCI_AIR_CODING_FORMAT_U_LAW; |
|
break; |
|
|
|
case ESCO_CODING_FORMAT_ALAW: |
|
voice_settings |= HCI_AIR_CODING_FORMAT_A_LAW; |
|
break; |
|
|
|
case ESCO_CODING_FORMAT_MSBC: |
|
voice_settings |= HCI_AIR_CODING_FORMAT_TRANSPNT; |
|
break; |
|
|
|
default: /* CVSD (0) */ |
|
break; |
|
} |
|
|
|
/* Convert PCM payload MSB position (0000011100) */ |
|
voice_settings |= (uint16_t)(((p_params->input_pcm_payload_msb_position & 0x7) |
|
<< HCI_INP_LINEAR_PCM_BIT_POS_OFFS)); |
|
|
|
/* Convert Input Sample Size (0000011100) */ |
|
if (p_params->input_coded_data_size == 16) |
|
voice_settings |= HCI_INP_SAMPLE_SIZE_16BIT; |
|
else /* Use 8 bit for all others */ |
|
voice_settings |= HCI_INP_SAMPLE_SIZE_8BIT; |
|
|
|
BTM_TRACE_DEBUG("%s: voice setting for legacy 0x%03x", __func__, |
|
voice_settings); |
|
|
|
return (voice_settings); |
|
} |
|
|
|
#else /* SCO_EXCLUDED == TRUE (Link in stubs) */ |
|
|
|
tBTM_STATUS BTM_CreateSco(const RawAddress* remote_bda, bool is_orig, |
|
uint16_t pkt_types, uint16_t* p_sco_inx, |
|
tBTM_SCO_CB* p_conn_cb, tBTM_SCO_CB* p_disc_cb) { |
|
return (BTM_NO_RESOURCES); |
|
} |
|
tBTM_STATUS BTM_RemoveSco(uint16_t sco_inx) { return (BTM_NO_RESOURCES); } |
|
tBTM_STATUS BTM_SetScoPacketTypes(uint16_t sco_inx, uint16_t pkt_types) { |
|
return (BTM_NO_RESOURCES); |
|
} |
|
uint16_t BTM_ReadScoPacketTypes(uint16_t sco_inx) { return (0); } |
|
uint16_t BTM_ReadDeviceScoPacketTypes(void) { return (0); } |
|
uint16_t BTM_ReadScoHandle(uint16_t sco_inx) { |
|
return (BTM_INVALID_HCI_HANDLE); |
|
} |
|
const RawAddress* BTM_ReadScoBdAddr(uint16_t sco_inx) { |
|
return ((uint8_t*)NULL); |
|
} |
|
uint16_t BTM_ReadScoDiscReason(void) { return (BTM_INVALID_SCO_DISC_REASON); } |
|
tBTM_STATUS BTM_SetEScoMode(enh_esco_params_t* p_parms) { |
|
return (BTM_MODE_UNSUPPORTED); |
|
} |
|
tBTM_STATUS BTM_RegForEScoEvts(uint16_t sco_inx, |
|
tBTM_ESCO_CBACK* p_esco_cback) { |
|
return (BTM_ILLEGAL_VALUE); |
|
} |
|
tBTM_STATUS BTM_ReadEScoLinkParms(uint16_t sco_inx, tBTM_ESCO_DATA* p_parms) { |
|
return (BTM_MODE_UNSUPPORTED); |
|
} |
|
tBTM_STATUS BTM_ChangeEScoLinkParms(uint16_t sco_inx, |
|
tBTM_CHG_ESCO_PARAMS* p_parms) { |
|
return (BTM_MODE_UNSUPPORTED); |
|
} |
|
void BTM_EScoConnRsp(uint16_t sco_inx, uint8_t hci_status, |
|
enh_esco_params_t* p_parms) {} |
|
uint8_t BTM_GetNumScoLinks(void) { return (0); } |
|
|
|
#endif /* If SCO is being used */
|
|
|