summaryrefslogtreecommitdiffstats
path: root/stack/btm/btm_sco.c
diff options
context:
space:
mode:
Diffstat (limited to 'stack/btm/btm_sco.c')
-rw-r--r--stack/btm/btm_sco.c1750
1 files changed, 1750 insertions, 0 deletions
diff --git a/stack/btm/btm_sco.c b/stack/btm/btm_sco.c
new file mode 100644
index 0000000..5a11404
--- /dev/null
+++ b/stack/btm/btm_sco.c
@@ -0,0 +1,1750 @@
+/******************************************************************************
+ *
+ * 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_types.h"
+#include "bt_target.h"
+#include "gki.h"
+#include "bt_types.h"
+#include "hcimsgs.h"
+#include "btu.h"
+#include "btm_api.h"
+#include "btm_int.h"
+#include "hcidefs.h"
+
+#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
+
+/********************************************************************************/
+/* L O C A L F U N C T I O N P R O T O T Y P E S */
+/********************************************************************************/
+
+static const tBTM_ESCO_PARAMS btm_esco_defaults =
+{
+ 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_ESCO_RETRANS_POWER /* Retransmission Effort (Power) */
+};
+
+/*******************************************************************************
+**
+** Function btm_sco_flush_sco_data
+**
+** Description This function is called to flush the SCO data for this channel.
+**
+** Returns void
+**
+*******************************************************************************/
+void btm_sco_flush_sco_data(UINT16 sco_inx)
+{
+#if BTM_SCO_HCI_INCLUDED == TRUE
+#if (BTM_MAX_SCO_LINKS>0)
+ 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->xmit_data_q.p_first)
+ {
+ if ((p_buf = (BT_HDR *)GKI_dequeue (&p->xmit_data_q)) != NULL)
+ GKI_freebuf (p_buf);
+ }
+ }
+#endif
+#endif
+}
+/*******************************************************************************
+**
+** Function btm_sco_init
+**
+** Description This function is called at BTM startup to initialize
+**
+** Returns void
+**
+*******************************************************************************/
+void btm_sco_init (void)
+{
+#if 0 /* cleared in btm_init; put back in if called from anywhere else! */
+ memset (&btm_cb.sco_cb, 0, sizeof(tSCO_CB));
+#endif
+ /* Initialize nonzero defaults */
+ btm_cb.sco_cb.sco_disc_reason = BTM_INVALID_SCO_DISC_REASON;
+
+ btm_cb.sco_cb.def_esco_parms = btm_esco_defaults; /* Initialize with defaults */
+ btm_cb.sco_cb.desired_sco_mode = BTM_DEFAULT_SCO_MODE;
+}
+
+/*******************************************************************************
+**
+** 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 sco_inx, UINT8 hci_status, BD_ADDR bda,
+ tBTM_ESCO_PARAMS *p_parms)
+{
+#if (BTM_MAX_SCO_LINKS>0)
+ tSCO_CONN *p_sco = NULL;
+ tBTM_ESCO_PARAMS *p_setup;
+ UINT16 temp_pkt_types;
+
+ 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)
+ {
+ if (!btsnd_hcic_reject_conn (bda, hci_status))
+ {
+ BTM_TRACE_ERROR0("Could not reject (e)SCO conn: No Buffer!!!");
+ }
+ }
+ else
+ {
+ if (!btsnd_hcic_reject_esco_conn (bda, hci_status))
+ {
+ BTM_TRACE_ERROR0("Could not reject (e)SCO conn: No Buffer!!!");
+ }
+ }
+ }
+ else /* Connection is being accepted */
+ {
+ p_sco->state = SCO_ST_CONNECTING;
+ if (btm_cb.devcb.local_version.hci_version >= HCI_PROTO_VERSION_1_2)
+ {
+ 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;
+ }
+
+ temp_pkt_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_pkt_types & BTM_ESCO_LINK_ONLY_MASK))
+ {
+ temp_pkt_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_pkt_types &= BTM_SCO_LINK_ONLY_MASK;
+
+ if (btm_cb.devcb.local_version.hci_version >= HCI_PROTO_VERSION_2_0)
+ {
+ temp_pkt_types |= BTM_SCO_EXCEPTION_PKTS_MASK;
+ }
+ }
+ /* OR in any exception packet types if at least 2.0 version of spec */
+ else if (btm_cb.devcb.local_version.hci_version >= HCI_PROTO_VERSION_2_0)
+ {
+ temp_pkt_types |= ((p_setup->packet_types & BTM_SCO_EXCEPTION_PKTS_MASK) |
+ (btm_cb.btm_sco_pkt_types_supported & BTM_SCO_EXCEPTION_PKTS_MASK));
+ }
+
+ if (btsnd_hcic_accept_esco_conn (bda, p_setup->tx_bw, p_setup->rx_bw,
+ p_setup->max_latency, p_setup->voice_contfmt,
+ p_setup->retrans_effort, temp_pkt_types))
+ {
+ p_setup->packet_types = temp_pkt_types;
+ }
+ else
+ {
+ BTM_TRACE_ERROR0("Could not accept SCO conn: No Buffer!!!");
+ }
+ }
+ else /* Controller is version 1.1 or earlier */
+ {
+ btsnd_hcic_accept_conn (bda, 0);
+ }
+ }
+#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 sco_inx)
+{
+ BT_HDR *p_buf;
+ 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 */
+ while (p_ccb->xmit_data_q.p_first != NULL)
+ {
+ p_buf = NULL;
+
+#if BTM_SCO_HCI_DEBUG
+ BTM_TRACE_DEBUG1 ("btm: [%d] buf in xmit_data_q", p_ccb->xmit_data_q.count );
+#endif
+ p_buf = (BT_HDR *)GKI_dequeue (&p_ccb->xmit_data_q);
+
+ 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 sco_inx, handle;
+ UINT8 *p = (UINT8 *)(p_msg + 1) + p_msg->offset;
+ UINT8 pkt_size = 0;
+ UINT8 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);
+
+ if ((sco_inx = btm_find_scb_by_handle(handle)) != BTM_MAX_SCO_LINKS )
+ {
+ /* send data callback */
+ if (!btm_cb.sco_cb.p_data_cb )
+ /* if no data callback registered, just free the buffer */
+ GKI_freebuf (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 */
+ {
+ GKI_freebuf (p_msg);
+ }
+#else
+ GKI_freebuf(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.
+**
+**
+*******************************************************************************/
+tBTM_STATUS BTM_WriteScoData (UINT16 sco_inx, BT_HDR *p_buf)
+{
+#if (BTM_SCO_HCI_INCLUDED == TRUE) && (BTM_MAX_SCO_LINKS>0)
+ tSCO_CONN *p_ccb = &btm_cb.sco_cb.sco_db[sco_inx];
+ UINT8 *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_ERROR1 ("BTM SCO - cannot send buffer, offset: %d", p_buf->offset);
+ GKI_freebuf (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 *)(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)p_buf->len);
+ p_buf->len += HCI_SCO_PREAMBLE_SIZE;
+
+ GKI_enqueue (&p_ccb->xmit_data_q, p_buf);
+
+ btm_sco_check_send_pkts (sco_inx);
+ }
+ }
+ else
+ {
+ GKI_freebuf(p_buf);
+
+ BTM_TRACE_WARNING2 ("BTM_WriteScoData, invalid sco index: %d at state [%d]",
+ sco_inx, btm_cb.sco_cb.sco_db[sco_inx].state);
+ status = BTM_UNKNOWN_ADDR;
+ }
+
+ return (status);
+
+#else
+ 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 acl_handle,
+ tBTM_ESCO_PARAMS *p_setup)
+{
+ UINT16 temp_pkt_types;
+ UINT8 xx;
+ tACL_CONN *p_acl;
+
+ /* Send connect request depending on version of spec */
+ if (!btm_cb.sco_cb.esco_supported)
+ {
+ if (!btsnd_hcic_add_SCO_conn (acl_handle, BTM_ESCO_2_SCO(p_setup->packet_types)))
+ return (BTM_NO_RESOURCES);
+ }
+ else
+ {
+ temp_pkt_types = (p_setup->packet_types & BTM_SCO_SUPPORTED_PKTS_MASK &
+ btm_cb.btm_sco_pkt_types_supported);
+
+ /* OR in any exception packet types if at least 2.0 version of spec */
+ if (btm_cb.devcb.local_version.hci_version >= HCI_PROTO_VERSION_2_0)
+ {
+ temp_pkt_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);
+ if ((xx = btm_handle_to_acl_index(acl_handle)) < MAX_L2CAP_LINKS)
+ {
+ p_acl = &btm_cb.acl_db[xx];
+ if (!HCI_EDR_ESCO_2MPS_SUPPORTED(p_acl->features))
+ {
+
+ BTM_TRACE_WARNING0("BTM Remote does not support 2-EDR eSCO");
+ temp_pkt_types |= (HCI_ESCO_PKT_TYPES_MASK_NO_2_EV3 |
+ HCI_ESCO_PKT_TYPES_MASK_NO_2_EV5);
+ }
+ if (!HCI_EDR_ESCO_3MPS_SUPPORTED(p_acl->features))
+ {
+
+ BTM_TRACE_WARNING0("BTM Remote does not support 3-EDR eSCO");
+ temp_pkt_types |= (HCI_ESCO_PKT_TYPES_MASK_NO_3_EV3 |
+ HCI_ESCO_PKT_TYPES_MASK_NO_3_EV5);
+ }
+ }
+
+
+ BTM_TRACE_API6(" txbw 0x%x, rxbw 0x%x, lat 0x%x, voice 0x%x, retrans 0x%02x, pkt 0x%04x",
+ p_setup->tx_bw, p_setup->rx_bw,
+ p_setup->max_latency, p_setup->voice_contfmt,
+ p_setup->retrans_effort, temp_pkt_types);
+
+ if (!btsnd_hcic_setup_esco_conn(acl_handle,
+ p_setup->tx_bw,
+ p_setup->rx_bw,
+ p_setup->max_latency,
+ p_setup->voice_contfmt,
+ p_setup->retrans_effort,
+ temp_pkt_types))
+ return (BTM_NO_RESOURCES);
+ else
+ p_setup->packet_types = temp_pkt_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 sco_inx, tBTM_ESCO_PARAMS *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_ERROR1("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_DEBUG1("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 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 (BD_ADDR remote_bda, BOOLEAN is_orig, UINT16 pkt_types,
+ UINT16 *p_sco_inx, tBTM_SCO_CB *p_conn_cb,
+ tBTM_SCO_CB *p_disc_cb)
+{
+#if (BTM_MAX_SCO_LINKS > 0)
+ tBTM_ESCO_PARAMS *p_setup;
+ tSCO_CONN *p = &btm_cb.sco_cb.sco_db[0];
+ UINT16 xx;
+ UINT16 acl_handle = 0;
+ UINT16 temp_pkt_types;
+ tACL_CONN *p_acl;
+
+#if (BTM_PWR_MGR_INCLUDED == TRUE) && (BTM_SCO_WAKE_PARKED_LINK == TRUE)
+ tBTM_PM_MODE md;
+ tBTM_PM_PWR_MD pm;
+#else
+ UINT8 mode;
+#endif
+
+ *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) || ((acl_handle = BTM_GetHCIConnHandle (remote_bda)) == 0xFFFF))
+ 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))
+ && (!memcmp (p->esco.data.bd_addr, remote_bda, BD_ADDR_LEN)))
+ {
+ 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);
+ }
+ }
+
+ /* Now, 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_PWR_MGR_INCLUDED == TRUE) && (BTM_SCO_WAKE_PARKED_LINK == TRUE)
+ if(BTM_ReadPowerMode(remote_bda, &md) == BTM_SUCCESS)
+ {
+ if (md == BTM_PM_MD_PARK || md == BTM_PM_MD_SNIFF)
+ {
+/* Coverity: FALSE-POSITIVE error from Coverity tool. Please do NOT remove following comment. */
+/* coverity[uninit_use_in_call] False-positive: setting the mode to BTM_PM_MD_ACTIVE only uses settings.mode
+ the other data members of tBTM_PM_PWR_MD are ignored
+*/
+ pm.mode = BTM_PM_MD_ACTIVE;
+ BTM_SetPowerMode(BTM_PM_SET_ONLY_ID, remote_bda, &pm);
+ p->state = SCO_ST_PEND_UNPARK;
+ }
+ }
+#elif BTM_PWR_MGR_INCLUDED == TRUE
+ if( (BTM_ReadPowerMode(remote_bda, &mode) == BTM_SUCCESS) && (mode == BTM_PM_MD_PARK) )
+ return (BTM_WRONG_MODE);
+#else
+ if( (BTM_ReadAclMode(remote_bda, &mode) == BTM_SUCCESS) && (mode == BTM_ACL_MODE_PARK) )
+ return (BTM_WRONG_MODE);
+#endif
+ }
+ memcpy (p->esco.data.bd_addr, remote_bda, BD_ADDR_LEN);
+ p->rem_bd_known = TRUE;
+ }
+ else
+ p->rem_bd_known = FALSE;
+
+ /* Link role is ignored in for this message */
+ if (pkt_types == BTM_IGNORE_SCO_PKT_TYPE)
+ pkt_types = btm_cb.sco_cb.def_esco_parms.packet_types;
+
+ p_setup = &p->esco.setup;
+ *p_setup = btm_cb.sco_cb.def_esco_parms;
+ p_setup->packet_types = (btm_cb.sco_cb.desired_sco_mode == BTM_LINK_TYPE_SCO)
+ ? (pkt_types & BTM_SCO_LINK_ONLY_MASK) : pkt_types;
+
+ temp_pkt_types = (p_setup->packet_types & BTM_SCO_SUPPORTED_PKTS_MASK &
+ btm_cb.btm_sco_pkt_types_supported);
+
+ /* OR in any exception packet types if at least 2.0 version of spec */
+ if (btm_cb.devcb.local_version.hci_version >= HCI_PROTO_VERSION_2_0)
+ {
+ if (btm_cb.sco_cb.desired_sco_mode == HCI_LINK_TYPE_ESCO)
+ {
+ temp_pkt_types |= ((p_setup->packet_types & BTM_SCO_EXCEPTION_PKTS_MASK) |
+ (btm_cb.btm_sco_pkt_types_supported & BTM_SCO_EXCEPTION_PKTS_MASK));
+ }
+ else /* Only using SCO packet types; turn off EDR also */
+ {
+ temp_pkt_types |= BTM_SCO_EXCEPTION_PKTS_MASK;
+ }
+ }
+
+ p_setup->packet_types = temp_pkt_types;
+ 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);
+ if (p_acl && p_acl->switch_role_state != BTM_ACL_SWKEY_STATE_IDLE)
+ {
+ BTM_TRACE_API1("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_API2("BTM_CreateSco -> (e)SCO Link for ACL handle 0x%04x, Desired Type %d",
+ acl_handle, btm_cb.sco_cb.desired_sco_mode);
+
+ 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_PWR_MGR_INCLUDED == TRUE) && (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 hci_status, UINT16 hci_handle)
+{
+#if (BTM_MAX_SCO_LINKS>0)
+ UINT16 xx;
+ UINT16 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)) == hci_handle))
+
+ {
+ BTM_TRACE_API3("btm_sco_chk_pend_unpark -> (e)SCO Link for ACL handle 0x%04x, Desired Type %d, hci_status 0x%02x",
+ acl_handle, btm_cb.sco_cb.desired_sco_mode, hci_status);
+
+ if ((btm_send_connect_request(acl_handle, &p->esco.setup)) == BTM_CMD_STARTED)
+ p->state = SCO_ST_CONNECTING;
+ }
+ }
+#endif
+}
+#endif
+
+/*******************************************************************************
+**
+** 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 hci_handle)
+{
+#if (BTM_MAX_SCO_LINKS>0)
+ UINT16 xx;
+ UINT16 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)) == hci_handle))
+
+ {
+ BTM_TRACE_API1("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_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 (BD_ADDR bda, DEV_CLASS dev_class, UINT8 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 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
+ */
+ if (((p->state == SCO_ST_LISTENING && p->rem_bd_known) || p->state == SCO_ST_CONNECTING)
+ && (!memcmp (p->esco.data.bd_addr, bda, BD_ADDR_LEN)))
+ {
+ /* 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;
+ memcpy (p->esco.data.bd_addr, bda, BD_ADDR_LEN);
+
+ /* 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 */
+ {
+ memcpy(evt_data.bd_addr, bda, BD_ADDR_LEN);
+ memcpy(evt_data.dev_class, dev_class, DEV_CLASS_LEN);
+ evt_data.link_type = link_type;
+ evt_data.sco_inx = xx;
+ p->esco.p_esco_cback(BTM_ESCO_CONN_REQ_EVT, (tBTM_ESCO_EVT_DATA *)&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;
+ memcpy (p->esco.data.bd_addr, bda, BD_ADDR_LEN);
+ 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_WARNING0("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 hci_status, BD_ADDR bda, UINT16 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 xx;
+ BOOLEAN 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++)
+ {
+ if (((p->state == SCO_ST_CONNECTING) ||
+ (p->state == SCO_ST_LISTENING) ||
+ (p->state == SCO_ST_W4_CONN_RSP))
+ && (p->rem_bd_known)
+ && (!bda || !memcmp (p->esco.data.bd_addr, bda, BD_ADDR_LEN)))
+ {
+ 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_API1("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;
+ }
+
+ return;
+ }
+
+ if (p->state == SCO_ST_LISTENING)
+ spt = TRUE;
+
+ p->state = SCO_ST_CONNECTED;
+ p->hci_handle = hci_handle;
+
+ 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 = p->esco.setup.max_latency;
+ parms.retrans_effort = p->esco.setup.retrans_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 btm_find_scb_by_handle (UINT16 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 sco_inx)
+{
+#if (BTM_MAX_SCO_LINKS>0)
+ tSCO_CONN *p = &btm_cb.sco_cb.sco_db[sco_inx];
+ UINT16 tempstate;
+
+ /* 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);
+ }
+
+ tempstate = p->state;
+ p->state = SCO_ST_DISCONNECTING;
+
+ if (!btsnd_hcic_disconnect (p->hci_handle, HCI_ERR_PEER_USER))
+ {
+ p->state = tempstate;
+ return (BTM_NO_RESOURCES);
+ }
+
+ 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 (BD_ADDR bda)
+{
+#if (BTM_MAX_SCO_LINKS>0)
+ tSCO_CONN *p = &btm_cb.sco_cb.sco_db[0];
+ UINT16 xx;
+
+ for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++)
+ {
+ if (p->rem_bd_known && (!memcmp (p->esco.data.bd_addr, bda, BD_ADDR_LEN)))
+ {
+ 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 hci_handle, UINT8 reason)
+{
+#if (BTM_MAX_SCO_LINKS>0)
+ tSCO_CONN *p = &btm_cb.sco_cb.sco_db[0];
+ UINT16 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 (BD_ADDR bda)
+{
+#if (BTM_MAX_SCO_LINKS>0)
+ tSCO_CONN *p = &btm_cb.sco_cb.sco_db[0];
+ UINT16 xx;
+
+ for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++)
+ {
+ if (p->state != SCO_ST_UNUSED)
+ {
+ if ((!bda) || (!memcmp (p->esco.data.bd_addr, bda, BD_ADDR_LEN) && 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 sco_inx, UINT16 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 = p->esco.setup.max_latency;
+ parms.retrans_effort = p->esco.setup.retrans_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 BTM_ReadScoPacketTypes (UINT16 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 BTM_ReadScoDiscReason (void)
+{
+ UINT16 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 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 BTM_ReadScoHandle (UINT16 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
+**
+*******************************************************************************/
+UINT8 *BTM_ReadScoBdAddr (UINT16 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 (tBTM_SCO_TYPE sco_mode, tBTM_ESCO_PARAMS *p_parms)
+{
+ tSCO_CB *p_esco = &btm_cb.sco_cb;
+ tBTM_ESCO_PARAMS *p_def = &p_esco->def_esco_parms;
+
+ if (p_esco->esco_supported)
+ {
+ if (p_parms)
+ {
+ if (sco_mode == BTM_LINK_TYPE_ESCO)
+ *p_def = *p_parms; /* Save as the default parameters */
+ else /* Load only the SCO packet types */
+ {
+ p_def->packet_types = p_parms->packet_types;
+ p_def->tx_bw = BTM_64KBITS_RATE;
+ p_def->rx_bw = BTM_64KBITS_RATE;
+ p_def->max_latency = 0x000a;
+ p_def->voice_contfmt = 0x0060;
+ p_def->retrans_effort = 0;
+
+ /* OR in any exception packet types if at least 2.0 version of spec */
+ if (btm_cb.devcb.local_version.hci_version >= HCI_PROTO_VERSION_2_0)
+ {
+ p_def->packet_types |= BTM_SCO_EXCEPTION_PKTS_MASK;
+ }
+ }
+ }
+ p_esco->desired_sco_mode = sco_mode;
+ BTM_TRACE_API1("BTM_SetEScoMode -> mode %d", sco_mode);
+ }
+ else
+ {
+ p_esco->desired_sco_mode = BTM_LINK_TYPE_SCO;
+ p_def->packet_types &= BTM_SCO_LINK_ONLY_MASK;
+ p_def->retrans_effort = 0;
+ BTM_TRACE_API0("BTM_SetEScoMode -> mode SCO (eSCO not supported)");
+ }
+
+ BTM_TRACE_DEBUG6(" txbw 0x%08x, rxbw 0x%08x, max_lat 0x%04x, voice 0x%04x, pkt 0x%04x, rtx effort 0x%02x",
+ p_def->tx_bw, p_def->rx_bw, p_def->max_latency,
+ p_def->voice_contfmt, p_def->packet_types,
+ p_def->retrans_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 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 sco_inx, tBTM_ESCO_DATA *p_parms)
+{
+#if (BTM_MAX_SCO_LINKS>0)
+ BTM_TRACE_API1("BTM_ReadEScoLinkParms -> sco_inx 0x%04x", 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);
+ }
+#endif
+
+ 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 sco_inx, tBTM_CHG_ESCO_PARAMS *p_parms)
+{
+#if (BTM_MAX_SCO_LINKS>0)
+ tBTM_ESCO_PARAMS *p_setup;
+ tSCO_CONN *p_sco;
+ UINT16 temp_pkt_types;
+
+ /* 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);
+
+ p_sco = &btm_cb.sco_cb.sco_db[sco_inx];
+ p_setup = &p_sco->esco.setup;
+
+ /* 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_API2("BTM_ChangeEScoLinkParms -> SCO Link for handle 0x%04x, pkt 0x%04x",
+ p_sco->hci_handle, p_setup->packet_types);
+
+ if (!btsnd_hcic_change_conn_type (p_sco->hci_handle,
+ BTM_ESCO_2_SCO(p_setup->packet_types)))
+ return (BTM_NO_RESOURCES);
+ }
+ else
+ {
+ temp_pkt_types = (p_parms->packet_types & BTM_SCO_SUPPORTED_PKTS_MASK &
+ btm_cb.btm_sco_pkt_types_supported);
+
+ /* OR in any exception packet types if at least 2.0 version of spec */
+ if (btm_cb.devcb.local_version.hci_version >= HCI_PROTO_VERSION_2_0)
+ {
+ temp_pkt_types |= ((p_parms->packet_types & BTM_SCO_EXCEPTION_PKTS_MASK) |
+ (btm_cb.btm_sco_pkt_types_supported & BTM_SCO_EXCEPTION_PKTS_MASK));
+ }
+
+ BTM_TRACE_API1("BTM_ChangeEScoLinkParms -> eSCO Link for handle 0x%04x", p_sco->hci_handle);
+ BTM_TRACE_API6(" txbw 0x%x, rxbw 0x%x, lat 0x%x, voice 0x%x, retrans 0x%02x, pkt 0x%04x",
+ p_setup->tx_bw, p_setup->rx_bw, p_parms->max_latency,
+ p_setup->voice_contfmt, p_parms->retrans_effort, temp_pkt_types);
+
+ /* When changing an existing link, only change latency, retrans, and pkts */
+ if (!btsnd_hcic_setup_esco_conn(p_sco->hci_handle, p_setup->tx_bw,
+ p_setup->rx_bw, p_parms->max_latency,
+ p_setup->voice_contfmt,
+ p_parms->retrans_effort,
+ temp_pkt_types))
+ return (BTM_NO_RESOURCES);
+ else
+ p_parms->packet_types = temp_pkt_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 sco_inx, UINT8 hci_status, tBTM_ESCO_PARAMS *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
+**
+*******************************************************************************/
+tBTM_SCO_TYPE btm_read_def_esco_mode (tBTM_ESCO_PARAMS *p_parms)
+{
+#if (BTM_MAX_SCO_LINKS>0)
+ *p_parms = btm_cb.sco_cb.def_esco_parms;
+ return btm_cb.sco_cb.desired_sco_mode;
+#else
+ return BTM_LINK_TYPE_SCO;
+#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 status, UINT16 handle, UINT8 tx_interval,
+ UINT8 retrans_window, UINT16 rx_pkt_len,
+ UINT16 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 xx;
+
+ BTM_TRACE_EVENT2("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)
+ {
+ memcpy(data.bd_addr, p->esco.data.bd_addr, BD_ADDR_LEN);
+ 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;
+
+ (*p->esco.p_esco_cback)(BTM_ESCO_CHG_EVT,
+ (tBTM_ESCO_EVT_DATA *)&data);
+ }
+ return;
+ }
+ }
+#endif
+}
+
+/*******************************************************************************
+**
+** Function btm_is_sco_active
+**
+** Description This function is called to see if a SCO handle is already in
+** use.
+**
+** Returns BOOLEAN
+**
+*******************************************************************************/
+BOOLEAN btm_is_sco_active (UINT16 handle)
+{
+#if (BTM_MAX_SCO_LINKS>0)
+ UINT16 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
+**
+*******************************************************************************/
+UINT8 BTM_GetNumScoLinks (void)
+{
+#if (BTM_MAX_SCO_LINKS>0)
+ tSCO_CONN *p = &btm_cb.sco_cb.sco_db[0];
+ UINT16 xx;
+ UINT8 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 active to a bd address.
+**
+** Returns BOOLEAN
+**
+*******************************************************************************/
+BOOLEAN btm_is_sco_active_by_bdaddr (BD_ADDR remote_bda)
+{
+#if (BTM_MAX_SCO_LINKS>0)
+ UINT8 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 ((!memcmp (p->esco.data.bd_addr, remote_bda, BD_ADDR_LEN)) && (p->state == SCO_ST_CONNECTED))
+ {
+ return (TRUE);
+ }
+ }
+#endif
+ return (FALSE);
+}
+#else /* SCO_EXCLUDED == TRUE (Link in stubs) */
+
+tBTM_STATUS BTM_CreateSco (BD_ADDR remote_bda, BOOLEAN is_orig,
+ UINT16 pkt_types, UINT16 *p_sco_inx,
+ tBTM_SCO_CB *p_conn_cb,
+ tBTM_SCO_CB *p_disc_cb) {return (BTM_NO_RESOURCES);}
+tBTM_STATUS BTM_RemoveSco (UINT16 sco_inx) {return (BTM_NO_RESOURCES);}
+tBTM_STATUS BTM_SetScoPacketTypes (UINT16 sco_inx, UINT16 pkt_types) {return (BTM_NO_RESOURCES);}
+UINT16 BTM_ReadScoPacketTypes (UINT16 sco_inx) {return (0);}
+UINT16 BTM_ReadDeviceScoPacketTypes (void) {return (0);}
+UINT16 BTM_ReadScoHandle (UINT16 sco_inx) {return (BTM_INVALID_HCI_HANDLE);}
+UINT8 *BTM_ReadScoBdAddr(UINT16 sco_inx) {return((UINT8 *) NULL);}
+UINT16 BTM_ReadScoDiscReason (void) {return (BTM_INVALID_SCO_DISC_REASON);}
+tBTM_STATUS BTM_SetEScoMode (tBTM_SCO_TYPE sco_mode, tBTM_ESCO_PARAMS *p_parms) {return (BTM_MODE_UNSUPPORTED);}
+tBTM_STATUS BTM_RegForEScoEvts (UINT16 sco_inx, tBTM_ESCO_CBACK *p_esco_cback) { return (BTM_ILLEGAL_VALUE);}
+tBTM_STATUS BTM_ReadEScoLinkParms (UINT16 sco_inx, tBTM_ESCO_DATA *p_parms) { return (BTM_MODE_UNSUPPORTED);}
+tBTM_STATUS BTM_ChangeEScoLinkParms (UINT16 sco_inx, tBTM_CHG_ESCO_PARAMS *p_parms) { return (BTM_MODE_UNSUPPORTED);}
+void BTM_EScoConnRsp (UINT16 sco_inx, UINT8 hci_status, tBTM_ESCO_PARAMS *p_parms) {}
+UINT8 BTM_GetNumScoLinks (void) {return (0);}
+
+#endif /* If SCO is being used */