| /****************************************************************************** |
| * |
| * 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" |
| #include "bt_utils.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); |
| } |
| } |
| #else |
| UNUSED(sco_inx); |
| #endif |
| #else |
| UNUSED(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 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 |
| UNUSED(sco_inx); |
| UNUSED(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 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->peer_lmp_features[HCI_EXT_FEATURES_PAGE_0])) |
| { |
| |
| 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->peer_lmp_features[HCI_EXT_FEATURES_PAGE_0])) |
| { |
| |
| 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) |
| { |
| 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; |
| } |
| } |
| #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) |
| UINT8 index; |
| |
| 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); |
| } |
| |
| 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_API1("BTM_ReadEScoLinkParms the first active SCO index is %d",index); |
| *p_parms = btm_cb.sco_cb.sco_db[index].esco.data; |
| return (BTM_SUCCESS); |
| } |
| } |
| } |
| |
| #endif |
| |
| BTM_TRACE_API0("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 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 */ |