summaryrefslogtreecommitdiffstats
path: root/stack/btm/btm_acl.c
diff options
context:
space:
mode:
Diffstat (limited to 'stack/btm/btm_acl.c')
-rw-r--r--stack/btm/btm_acl.c3126
1 files changed, 3126 insertions, 0 deletions
diff --git a/stack/btm/btm_acl.c b/stack/btm/btm_acl.c
new file mode 100644
index 0000000..aebbfec
--- /dev/null
+++ b/stack/btm/btm_acl.c
@@ -0,0 +1,3126 @@
+/*****************************************************************************
+**
+** Name: btm_acl.c
+**
+** Description: This file contains functions that handle ACL connections.
+** This includes operations such as hold and sniff modes,
+** supported packet types.
+**
+** This module contains both internal and external (API)
+** functions. External (API) functions are distinguishable
+** by their names beginning with uppercase BTM.
+**
+**
+** Copyright (c) 2000-2011, Broadcom Corp., All Rights Reserved.
+** Broadcom Bluetooth Core. Proprietary and confidential.
+******************************************************************************/
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <stddef.h>
+
+#include "bt_types.h"
+#include "bt_target.h"
+#include "gki.h"
+#include "hcimsgs.h"
+#include "btu.h"
+#include "btm_api.h"
+#include "btm_int.h"
+#include "l2c_int.h"
+#include "hcidefs.h"
+static void btm_establish_continue (tACL_CONN *p_acl_cb);
+
+#define BTM_DEV_REPLY_TIMEOUT 3 /* 3 second timeout waiting for responses */
+
+/*******************************************************************************
+**
+** Function btm_acl_init
+**
+** Description This function is called at BTM startup to initialize
+**
+** Returns void
+**
+*******************************************************************************/
+void btm_acl_init (void)
+{
+ BTM_TRACE_DEBUG0 ("btm_acl_init");
+#if 0 /* cleared in btm_init; put back in if called from anywhere else! */
+ memset (&btm_cb.acl_db, 0, sizeof (btm_cb.acl_db));
+#if RFCOMM_INCLUDED == TRUE
+ memset (btm_cb.btm_scn, 0, BTM_MAX_SCN); /* Initialize the SCN usage to FALSE */
+#endif
+ btm_cb.btm_def_link_policy = 0;
+#if (defined(BTM_BUSY_LEVEL_CHANGE_INCLUDED) && BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE)
+ btm_cb.p_bl_changed_cb = NULL;
+#else
+ btm_cb.p_acl_changed_cb = NULL;
+#endif
+#endif
+
+ /* Initialize nonzero defaults */
+ btm_cb.btm_def_link_super_tout = HCI_DEFAULT_INACT_TOUT;
+ btm_cb.acl_disc_reason = 0xff ;
+}
+
+/*******************************************************************************
+**
+** Function btm_bda_to_acl
+**
+** Description This function returns the FIRST acl_db entry for the passed BDA.
+**
+** Returns Returns pointer to the ACL DB for the requested BDA if found.
+** NULL if not found.
+**
+*******************************************************************************/
+tACL_CONN *btm_bda_to_acl (BD_ADDR bda)
+{
+ tACL_CONN *p = &btm_cb.acl_db[0];
+ UINT16 xx;
+ if (bda)
+ {
+ for (xx = 0; xx < MAX_L2CAP_LINKS; xx++, p++)
+ {
+ if ((p->in_use) && (!memcmp (p->remote_addr, bda, BD_ADDR_LEN)))
+ {
+ BTM_TRACE_DEBUG0 ("btm_bda_to_acl found");
+ return(p);
+ }
+ }
+ }
+ BTM_TRACE_DEBUG0 ("btm_bda_to_acl Not found");
+
+ /* If here, no BD Addr found */
+ return((tACL_CONN *)NULL);
+}
+
+/*******************************************************************************
+**
+** Function btm_handle_to_acl_index
+**
+** Description This function returns the FIRST acl_db entry for the passed hci_handle.
+**
+** Returns index to the acl_db or MAX_L2CAP_LINKS.
+**
+*******************************************************************************/
+UINT8 btm_handle_to_acl_index (UINT16 hci_handle)
+{
+ tACL_CONN *p = &btm_cb.acl_db[0];
+ UINT8 xx;
+ BTM_TRACE_DEBUG0 ("btm_handle_to_acl_index");
+ for (xx = 0; xx < MAX_L2CAP_LINKS; xx++, p++)
+ {
+ if ((p->in_use) && (p->hci_handle == hci_handle))
+ {
+ break;
+ }
+ }
+
+ /* If here, no BD Addr found */
+ return(xx);
+}
+
+
+/*******************************************************************************
+**
+** Function btm_acl_created
+**
+** Description This function is called by L2CAP when an ACL connection
+** is created.
+**
+** Returns void
+**
+*******************************************************************************/
+void btm_acl_created (BD_ADDR bda, DEV_CLASS dc, BD_NAME bdn,
+ UINT16 hci_handle, UINT8 link_role, UINT8 is_le_link)
+{
+ tBTM_SEC_DEV_REC *p_dev_rec;
+ UINT8 yy;
+ tACL_CONN *p;
+ UINT8 xx;
+
+ BTM_TRACE_DEBUG3 ("btm_acl_created hci_handle=%d link_role=%d is_le_link=%d",
+ hci_handle,link_role, is_le_link);
+ /* Ensure we don't have duplicates */
+ p = btm_bda_to_acl(bda);
+ if (p != (tACL_CONN *)NULL)
+ {
+ p->hci_handle = hci_handle;
+ p->link_role = link_role;
+#if BLE_INCLUDED == TRUE
+ p->is_le_link = is_le_link;
+#endif
+ BTM_TRACE_DEBUG6 ("Duplicate btm_acl_created: RemBdAddr: %02x%02x%02x%02x%02x%02x",
+ bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]);
+ BTM_SetLinkPolicy(p->remote_addr, &btm_cb.btm_def_link_policy);
+ return;
+ }
+
+ /* Allocate acl_db entry */
+ for (xx = 0, p = &btm_cb.acl_db[0]; xx < MAX_L2CAP_LINKS; xx++, p++)
+ {
+ if (!p->in_use)
+ {
+ p->in_use = TRUE;
+ p->hci_handle = hci_handle;
+ p->link_role = link_role;
+ p->link_up_issued = FALSE;
+#if BLE_INCLUDED == TRUE
+ p->is_le_link = is_le_link;
+#endif
+ p->restore_pkt_types = 0; /* Only exists while SCO is active */
+ p->switch_role_state = BTM_ACL_SWKEY_STATE_IDLE;
+
+#if BTM_PWR_MGR_INCLUDED == FALSE
+ p->mode = BTM_ACL_MODE_NORMAL;
+#else
+ btm_pm_sm_alloc(xx);
+#endif /* BTM_PWR_MGR_INCLUDED == FALSE */
+
+ memcpy (p->remote_addr, bda, BD_ADDR_LEN);
+
+ if (dc)
+ memcpy (p->remote_dc, dc, DEV_CLASS_LEN);
+
+ if (bdn)
+ memcpy (p->remote_name, bdn, BTM_MAX_REM_BD_NAME_LEN);
+
+
+ /* Check if we already know features for this device */
+ p_dev_rec = btm_find_dev_by_handle (hci_handle);
+
+#if (BLE_INCLUDED == TRUE)
+ if (p_dev_rec )
+ {
+ BTM_TRACE_DEBUG1 ("device_type=0x%x", p_dev_rec->device_type);
+ }
+#endif
+
+
+ if (p_dev_rec
+#if (BLE_INCLUDED == TRUE)
+ && p_dev_rec->device_type != BT_DEVICE_TYPE_BLE
+#endif
+ )
+ {
+
+ /* if BR/EDR do something more */
+ btsnd_hcic_read_rmt_clk_offset (p->hci_handle);
+ btsnd_hcic_rmt_ver_req (p->hci_handle);
+
+ for (yy = 0; yy < BD_FEATURES_LEN; yy++)
+ {
+ if (p_dev_rec->features[yy])
+ {
+ memcpy (p->features, p_dev_rec->features, BD_FEATURES_LEN);
+ if (BTM_SEC_MODE_SP == btm_cb.security_mode &&
+ HCI_SIMPLE_PAIRING_SUPPORTED(p->features))
+ {
+ /* if SM4 supported, check peer support for SM4
+ * The remote controller supports SSP according to saved remote features
+ * read the extended feature page 1 for the host support for SSP */
+ if (btsnd_hcic_rmt_ext_features (p_dev_rec->hci_handle, 1))
+ return;
+ }
+ /* peer does not support SSP */
+ p_dev_rec->sm4 |= BTM_SM4_KNOWN;
+
+ btm_establish_continue (p);
+ return;
+ }
+ }
+ }
+#if (BLE_INCLUDED == TRUE)
+ /* If here, features are not known yet */
+ if (p_dev_rec && p_dev_rec->device_type == BT_DEVICE_TYPE_BLE)
+ {
+ btm_establish_continue(p);
+
+ if (link_role == HCI_ROLE_MASTER)
+ {
+ btm_ble_update_bg_state();
+ btm_ble_resume_bg_conn (NULL, FALSE);
+
+ btsnd_hcic_ble_read_remote_feat(p->hci_handle);
+ }
+ }
+ else
+#endif
+ {
+ btsnd_hcic_rmt_features_req (p->hci_handle);
+ }
+
+ /* read page 1 - on rmt feature event for buffer reasons */
+ return;
+ }
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function btm_acl_report_role_change
+**
+** Description This function is called when the local device is deemed
+** to be down. It notifies L2CAP of the failure.
+**
+** Returns void
+**
+*******************************************************************************/
+void btm_acl_report_role_change (UINT8 hci_status, BD_ADDR bda)
+{
+ tBTM_ROLE_SWITCH_CMPL ref_data;
+ BTM_TRACE_DEBUG0 ("btm_acl_report_role_change");
+ if (btm_cb.devcb.p_switch_role_cb && (bda &&
+ (0 == memcmp(btm_cb.devcb.switch_role_ref_data.remote_bd_addr, bda, BD_ADDR_LEN))))
+ {
+ memset (&btm_cb.devcb.switch_role_ref_data, 0, sizeof(tBTM_ROLE_SWITCH_CMPL));
+ ref_data.hci_status = hci_status;
+ memcpy (&ref_data, &btm_cb.devcb.switch_role_ref_data, sizeof(tBTM_ROLE_SWITCH_CMPL));
+ (*btm_cb.devcb.p_switch_role_cb)(&ref_data);
+ btm_cb.devcb.p_switch_role_cb = NULL;
+ }
+}
+
+/*******************************************************************************
+**
+** Function btm_acl_removed
+**
+** Description This function is called by L2CAP when an ACL connection
+** is removed. Since only L2CAP creates ACL links, we use
+** the L2CAP link index as our index into the control blocks.
+**
+** Returns void
+**
+*******************************************************************************/
+void btm_acl_removed (BD_ADDR bda)
+{
+ tACL_CONN *p;
+#if (defined(BTM_BUSY_LEVEL_CHANGE_INCLUDED) && BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE)
+ tBTM_BL_EVENT_DATA evt_data;
+#endif
+#if (defined BLE_INCLUDED && BLE_INCLUDED == TRUE)
+ tBTM_SEC_DEV_REC *p_dev_rec=NULL;
+ UINT16 combined_mode;
+#endif
+
+ BTM_TRACE_DEBUG0 ("btm_acl_removed");
+ p = btm_bda_to_acl(bda);
+ if (p != (tACL_CONN *)NULL)
+ {
+ p->in_use = FALSE;
+
+ /* if the disconnected channel has a pending role switch, clear it now */
+ btm_acl_report_role_change(HCI_ERR_NO_CONNECTION, bda);
+
+ /* Only notify if link up has had a chance to be issued */
+ if (p->link_up_issued)
+ {
+ p->link_up_issued = FALSE;
+
+ /* If anyone cares, tell him database changed */
+#if (defined(BTM_BUSY_LEVEL_CHANGE_INCLUDED) && BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE)
+ if (btm_cb.p_bl_changed_cb)
+ {
+ evt_data.event = BTM_BL_DISCN_EVT;
+ evt_data.discn.p_bda = bda;
+
+ (*btm_cb.p_bl_changed_cb)(&evt_data);
+ }
+
+ btm_acl_update_busy_level (BTM_BLI_ACL_DOWN_EVT);
+#else
+ if (btm_cb.p_acl_changed_cb)
+ (*btm_cb.p_acl_changed_cb) (bda, NULL, NULL, NULL, FALSE);
+#endif
+ }
+
+#if (defined BLE_INCLUDED && BLE_INCLUDED == TRUE)
+
+ BTM_TRACE_DEBUG4 ("acl hci_handle=%d is_le_link=%d connectable_mode=0x%0x link_role=%d",
+ p->hci_handle,
+ p->is_le_link,
+ btm_cb.ble_ctr_cb.inq_var.connectable_mode,
+ p->link_role);
+
+
+ /* If we are LE connectable, check if we need to start advertising again */
+ if ( p->is_le_link && (btm_cb.ble_ctr_cb.inq_var.connectable_mode != BTM_BLE_NON_CONNECTABLE) )
+ {
+ tACL_CONN *pa = &btm_cb.acl_db[0];
+ UINT16 xx;
+
+ for (xx = 0; xx < MAX_L2CAP_LINKS; xx++, pa++)
+ {
+ /* If any other LE link is up, we are still not connectable */
+ if (pa->in_use && pa->is_le_link)
+ return;
+ }
+ combined_mode = (btm_cb.ble_ctr_cb.inq_var.connectable_mode | btm_cb.btm_inq_vars.connectable_mode);
+ btm_ble_set_connectability ( combined_mode );
+ }
+
+ p_dev_rec = btm_find_dev(bda);
+ if ( p_dev_rec)
+ {
+ BTM_TRACE_DEBUG1("before update p_dev_rec->sec_flags=0x%x", p_dev_rec->sec_flags);
+ if (p->is_le_link)
+ {
+ BTM_TRACE_DEBUG0("LE link down");
+ p_dev_rec->sec_flags &= ~(BTM_SEC_ENCRYPTED | BTM_SEC_ROLE_SWITCHED);
+ if ( (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN) == 0)
+ {
+ BTM_TRACE_DEBUG0("Not Bonded");
+ p_dev_rec->sec_flags &= ~(BTM_SEC_AUTHENTICATED | BTM_SEC_LINK_KEY_AUTHED);
+ }
+ else
+ {
+ BTM_TRACE_DEBUG0("Bonded");
+ }
+ }
+ else
+ {
+ BTM_TRACE_DEBUG0("Bletooth link down");
+ p_dev_rec->sec_flags &= ~(BTM_SEC_AUTHORIZED | BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED | BTM_SEC_ROLE_SWITCHED);
+ }
+ BTM_TRACE_DEBUG1("after update p_dev_rec->sec_flags=0x%x", p_dev_rec->sec_flags);
+ }
+ else
+ {
+ BTM_TRACE_ERROR0("Device not found");
+
+ }
+#endif
+
+ return;
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function btm_acl_device_down
+**
+** Description This function is called when the local device is deemed
+** to be down. It notifies L2CAP of the failure.
+**
+** Returns void
+**
+*******************************************************************************/
+void btm_acl_device_down (void)
+{
+ tACL_CONN *p = &btm_cb.acl_db[0];
+ UINT16 xx;
+ BTM_TRACE_DEBUG0 ("btm_acl_device_down");
+ for (xx = 0; xx < MAX_L2CAP_LINKS; xx++, p++)
+ {
+ if (p->in_use)
+ {
+ BTM_TRACE_DEBUG1 ("hci_handle=%d HCI_ERR_HW_FAILURE ",p->hci_handle );
+ l2c_link_hci_disc_comp (p->hci_handle, HCI_ERR_HW_FAILURE);
+ }
+ }
+}
+
+#if (defined(BTM_BUSY_LEVEL_CHANGE_INCLUDED) && BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE)
+/*******************************************************************************
+**
+** Function btm_acl_update_busy_level
+**
+** Description This function is called to update the busy level of the system
+** .
+**
+** Returns void
+**
+*******************************************************************************/
+void btm_acl_update_busy_level (tBTM_BLI_EVENT event)
+{
+ tBTM_BL_UPDATE_DATA evt;
+ UINT8 busy_level;
+ BTM_TRACE_DEBUG0 ("btm_acl_update_busy_level");
+ switch (event)
+ {
+ case BTM_BLI_ACL_UP_EVT:
+ BTM_TRACE_DEBUG0 ("BTM_BLI_ACL_UP_EVT");
+ btm_cb.num_acl++;
+ break;
+ case BTM_BLI_ACL_DOWN_EVT:
+ if (btm_cb.num_acl)
+ {
+ btm_cb.num_acl--;
+ BTM_TRACE_DEBUG1 ("BTM_BLI_ACL_DOWN_EVT", btm_cb.num_acl);
+ }
+ else
+ {
+ BTM_TRACE_ERROR0 ("BTM_BLI_ACL_DOWN_EVT issued, but num_acl already zero !!!");
+ }
+ break;
+ case BTM_BLI_PAGE_EVT:
+ BTM_TRACE_DEBUG0 ("BTM_BLI_PAGE_EVT");
+ btm_cb.is_paging = TRUE;
+ break;
+ case BTM_BLI_PAGE_DONE_EVT:
+ BTM_TRACE_DEBUG0 ("BTM_BLI_PAGE_DONE_EVT");
+ btm_cb.is_paging = FALSE;
+ break;
+ case BTM_BLI_INQ_EVT:
+ BTM_TRACE_DEBUG0 ("BTM_BLI_INQ_EVT");
+ btm_cb.is_inquiry = TRUE;
+ break;
+ case BTM_BLI_INQ_DONE_EVT:
+ BTM_TRACE_DEBUG0 ("BTM_BLI_INQ_DONE_EVT");
+ btm_cb.is_inquiry = FALSE;
+ break;
+ }
+ if (btm_cb.is_paging || btm_cb.is_inquiry)
+ busy_level = 10;
+ else
+ busy_level = (UINT8)btm_cb.num_acl;
+
+ if (busy_level != btm_cb.busy_level)
+ {
+ evt.event = BTM_BL_UPDATE_EVT;
+ evt.busy_level = busy_level;
+ btm_cb.busy_level = busy_level;
+ if (btm_cb.p_bl_changed_cb && (btm_cb.bl_evt_mask & BTM_BL_UPDATE_MASK))
+ {
+ (*btm_cb.p_bl_changed_cb)((tBTM_BL_EVENT_DATA *)&evt);
+ }
+ }
+}
+#endif
+
+
+/*******************************************************************************
+**
+** Function BTM_GetRole
+**
+** Description This function is called to get the role of the local device
+** for the ACL connection with the specified remote device
+**
+** Returns BTM_SUCCESS if connection exists.
+** BTM_UNKNOWN_ADDR if no active link with bd addr specified
+**
+*******************************************************************************/
+tBTM_STATUS BTM_GetRole (BD_ADDR remote_bd_addr, UINT8 *p_role)
+{
+ tACL_CONN *p;
+ BTM_TRACE_DEBUG0 ("BTM_GetRole");
+ if ((p = btm_bda_to_acl(remote_bd_addr)) == NULL)
+ {
+ *p_role = BTM_ROLE_UNDEFINED;
+ return(BTM_UNKNOWN_ADDR);
+ }
+
+ /* Get the current role */
+ *p_role = p->link_role;
+ return(BTM_SUCCESS);
+}
+
+
+/*******************************************************************************
+**
+** Function BTM_SwitchRole
+**
+** Description This function is called to switch role between master and
+** slave. If role is already set it will do nothing. If the
+** command was initiated, the callback function is called upon
+** completion.
+**
+** Returns BTM_SUCCESS if already in specified role.
+** BTM_CMD_STARTED if command issued to controller.
+** BTM_NO_RESOURCES if couldn't allocate memory to issue command
+** BTM_UNKNOWN_ADDR if no active link with bd addr specified
+** BTM_MODE_UNSUPPORTED if local device does not support role switching
+** BTM_BUSY if the previous command is not completed
+**
+*******************************************************************************/
+tBTM_STATUS BTM_SwitchRole (BD_ADDR remote_bd_addr, UINT8 new_role, tBTM_CMPL_CB *p_cb)
+{
+ tACL_CONN *p;
+ tBTM_SEC_DEV_REC *p_dev_rec = NULL;
+#if BTM_SCO_INCLUDED == TRUE
+ BOOLEAN is_sco_active;
+#endif
+#if BTM_PWR_MGR_INCLUDED == TRUE
+ tBTM_STATUS status;
+ tBTM_PM_MODE pwr_mode;
+ tBTM_PM_PWR_MD settings;
+#endif
+#if (BT_USE_TRACES == TRUE)
+ BD_ADDR_PTR p_bda;
+#endif
+ BTM_TRACE_API6 ("BTM_SwitchRole BDA: %02x-%02x-%02x-%02x-%02x-%02x",
+ remote_bd_addr[0], remote_bd_addr[1], remote_bd_addr[2],
+ remote_bd_addr[3], remote_bd_addr[4], remote_bd_addr[5]);
+
+ /* Make sure the local device supports switching */
+ if (!(HCI_SWITCH_SUPPORTED(btm_cb.devcb.local_features)))
+ return(BTM_MODE_UNSUPPORTED);
+
+ if (btm_cb.devcb.p_switch_role_cb && p_cb)
+ {
+#if (BT_USE_TRACES == TRUE)
+ p_bda = btm_cb.devcb.switch_role_ref_data.remote_bd_addr;
+ BTM_TRACE_DEBUG6 ("Role switch on other device is in progress 0x%02x%02x%02x%02x%02x%02x",
+ p_bda[0], p_bda[1], p_bda[2],
+ p_bda[3], p_bda[4], p_bda[5]);
+#endif
+ return(BTM_BUSY);
+ }
+
+ if ((p = btm_bda_to_acl(remote_bd_addr)) == NULL)
+ return(BTM_UNKNOWN_ADDR);
+
+ /* Finished if already in desired role */
+ if (p->link_role == new_role)
+ return(BTM_SUCCESS);
+
+#if BTM_SCO_INCLUDED == TRUE
+ /* Check if there is any SCO Active on this BD Address */
+ is_sco_active = btm_is_sco_active_by_bdaddr(remote_bd_addr);
+
+ if (is_sco_active == TRUE)
+ return(BTM_NO_RESOURCES);
+#endif
+
+ /* Ignore role switch request if the previous request was not completed */
+ if (p->switch_role_state != BTM_ACL_SWKEY_STATE_IDLE)
+ {
+ BTM_TRACE_DEBUG1 ("BTM_SwitchRole busy: %d",
+ p->switch_role_state);
+ return(BTM_BUSY);
+ }
+
+ /* Cannot switch role while parked or sniffing */
+#if BTM_PWR_MGR_INCLUDED == FALSE
+ if (p->mode == HCI_MODE_PARK)
+ {
+ if (!btsnd_hcic_exit_park_mode (p->hci_handle))
+ return(BTM_NO_RESOURCES);
+
+ p->switch_role_state = BTM_ACL_SWKEY_STATE_MODE_CHANGE;
+ }
+ else if (p->mode == HCI_MODE_SNIFF)
+ {
+ if (!btsnd_hcic_exit_sniff_mode (p->hci_handle))
+ return(BTM_NO_RESOURCES);
+
+ p->switch_role_state = BTM_ACL_SWKEY_STATE_MODE_CHANGE;
+ }
+#else /* power manager is in use */
+
+ if ((status = BTM_ReadPowerMode(p->remote_addr, &pwr_mode)) != BTM_SUCCESS)
+ return(status);
+
+ /* Wake up the link if in sniff or park before attempting switch */
+ if (pwr_mode == BTM_PM_MD_PARK || pwr_mode == 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
+*/
+ settings.mode = BTM_PM_MD_ACTIVE;
+ status = BTM_SetPowerMode (BTM_PM_SET_ONLY_ID, p->remote_addr, &settings);
+ if (status != BTM_CMD_STARTED)
+ return(BTM_WRONG_MODE);
+
+ p->switch_role_state = BTM_ACL_SWKEY_STATE_MODE_CHANGE;
+ }
+#endif
+ /* some devices do not support switch while encryption is on */
+ else
+ {
+ if (((p_dev_rec = btm_find_dev (remote_bd_addr)) != NULL)
+ && ((p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED) != 0)
+ && !BTM_EPR_AVAILABLE(p))
+ {
+ /* bypass turning off encryption if change link key is already doing it */
+ if (p->encrypt_state != BTM_ACL_ENCRYPT_STATE_ENCRYPT_OFF)
+ {
+ if (!btsnd_hcic_set_conn_encrypt (p->hci_handle, FALSE))
+ return(BTM_NO_RESOURCES);
+ else
+ p->encrypt_state = BTM_ACL_ENCRYPT_STATE_ENCRYPT_OFF;
+ }
+
+ p->switch_role_state = BTM_ACL_SWKEY_STATE_ENCRYPTION_OFF;
+ }
+ else
+ {
+ if (!btsnd_hcic_switch_role (remote_bd_addr, new_role))
+ return(BTM_NO_RESOURCES);
+
+ p->switch_role_state = BTM_ACL_SWKEY_STATE_IN_PROGRESS;
+
+#if BTM_DISC_DURING_RS == TRUE
+ if (p_dev_rec)
+ p_dev_rec->rs_disc_pending = BTM_SEC_RS_PENDING;
+#endif
+ }
+ }
+
+ /* Initialize return structure in case request fails */
+ if (p_cb)
+ {
+ memcpy (btm_cb.devcb.switch_role_ref_data.remote_bd_addr, remote_bd_addr,
+ BD_ADDR_LEN);
+ btm_cb.devcb.switch_role_ref_data.role = new_role;
+ /* initialized to an error code */
+ btm_cb.devcb.switch_role_ref_data.hci_status = HCI_ERR_UNSUPPORTED_VALUE;
+ btm_cb.devcb.p_switch_role_cb = p_cb;
+ }
+ return(BTM_CMD_STARTED);
+}
+
+/*******************************************************************************
+**
+** Function BTM_ChangeLinkKey
+**
+** Description This function is called to change the link key of the
+** connection.
+**
+** Returns BTM_CMD_STARTED if command issued to controller.
+** BTM_NO_RESOURCES if couldn't allocate memory to issue command
+** BTM_UNKNOWN_ADDR if no active link with bd addr specified
+** BTM_BUSY if the previous command is not completed
+**
+*******************************************************************************/
+tBTM_STATUS BTM_ChangeLinkKey (BD_ADDR remote_bd_addr, tBTM_CMPL_CB *p_cb)
+{
+ tACL_CONN *p;
+ tBTM_SEC_DEV_REC *p_dev_rec = NULL;
+#if BTM_PWR_MGR_INCLUDED == TRUE
+ tBTM_STATUS status;
+ tBTM_PM_MODE pwr_mode;
+ tBTM_PM_PWR_MD settings;
+#endif
+ BTM_TRACE_DEBUG0 ("BTM_ChangeLinkKey");
+ if ((p = btm_bda_to_acl(remote_bd_addr)) == NULL)
+ return(BTM_UNKNOWN_ADDR);
+
+ /* Ignore change link key request if the previsous request has not completed */
+ if (p->change_key_state != BTM_ACL_SWKEY_STATE_IDLE)
+ {
+ BTM_TRACE_DEBUG0 ("Link key change request declined since the previous request for this device has not completed ");
+ return(BTM_BUSY);
+ }
+
+ memset (&btm_cb.devcb.chg_link_key_ref_data, 0, sizeof(tBTM_CHANGE_KEY_CMPL));
+
+ /* Cannot change key while parked */
+#if BTM_PWR_MGR_INCLUDED == FALSE
+ if (p->mode == HCI_MODE_PARK)
+ {
+ if (!btsnd_hcic_exit_park_mode (p->hci_handle))
+ return(BTM_NO_RESOURCES);
+
+ p->change_key_state = BTM_ACL_SWKEY_STATE_MODE_CHANGE;
+ }
+#else /* power manager is in use */
+
+
+ if ((status = BTM_ReadPowerMode(p->remote_addr, &pwr_mode)) != BTM_SUCCESS)
+ return(status);
+
+ /* Wake up the link if in park before attempting to change link keys */
+ if (pwr_mode == BTM_PM_MD_PARK)
+ {
+/* 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
+*/
+ settings.mode = BTM_PM_MD_ACTIVE;
+ status = BTM_SetPowerMode (BTM_PM_SET_ONLY_ID, p->remote_addr, &settings);
+ if (status != BTM_CMD_STARTED)
+ return(BTM_WRONG_MODE);
+
+ p->change_key_state = BTM_ACL_SWKEY_STATE_MODE_CHANGE;
+ }
+#endif
+ /* some devices do not support change of link key while encryption is on */
+ else if (((p_dev_rec = btm_find_dev (remote_bd_addr)) != NULL)
+ && ((p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED) != 0) && !BTM_EPR_AVAILABLE(p))
+ {
+ /* bypass turning off encryption if switch role is already doing it */
+ if (p->encrypt_state != BTM_ACL_ENCRYPT_STATE_ENCRYPT_OFF)
+ {
+ if (!btsnd_hcic_set_conn_encrypt (p->hci_handle, FALSE))
+ return(BTM_NO_RESOURCES);
+ else
+ p->encrypt_state = BTM_ACL_ENCRYPT_STATE_ENCRYPT_OFF;
+ }
+
+ p->change_key_state = BTM_ACL_SWKEY_STATE_ENCRYPTION_OFF;
+ }
+ else /* Ok to initiate change of link key */
+ {
+ if (!btsnd_hcic_change_link_key (p->hci_handle))
+ return(BTM_NO_RESOURCES);
+
+ p->change_key_state = BTM_ACL_SWKEY_STATE_IN_PROGRESS;
+ }
+
+ /* Initialize return structure in case request fails */
+ memcpy (btm_cb.devcb.chg_link_key_ref_data.remote_bd_addr, remote_bd_addr,
+ BD_ADDR_LEN);
+ btm_cb.devcb.p_chg_link_key_cb = p_cb;
+ return(BTM_CMD_STARTED);
+}
+
+/*******************************************************************************
+**
+** Function btm_acl_link_key_change
+**
+** Description This function is called to when a change link key event
+** is received.
+**
+*******************************************************************************/
+void btm_acl_link_key_change (UINT16 handle, UINT8 status)
+{
+ tBTM_CHANGE_KEY_CMPL *p_data;
+ tACL_CONN *p;
+ UINT8 xx;
+ BTM_TRACE_DEBUG0 ("btm_acl_link_key_change");
+ /* Look up the connection by handle and set the current mode */
+ xx = btm_handle_to_acl_index(handle);
+
+ /* don't assume that we can never get a bad hci_handle */
+ if (xx >= MAX_L2CAP_LINKS)
+ return;
+
+ p_data = &btm_cb.devcb.chg_link_key_ref_data;
+ p = &btm_cb.acl_db[xx];
+ p_data->hci_status = status;
+
+ /* if switching state is switching we need to turn encryption on */
+ /* if idle, we did not change encryption */
+ if (p->change_key_state == BTM_ACL_SWKEY_STATE_SWITCHING)
+ {
+ /* Make sure there's not also a role switch going on before re-enabling */
+ if (p->switch_role_state != BTM_ACL_SWKEY_STATE_SWITCHING)
+ {
+ if (btsnd_hcic_set_conn_encrypt (p->hci_handle, TRUE))
+ {
+ p->encrypt_state = BTM_ACL_ENCRYPT_STATE_ENCRYPT_ON;
+ p->change_key_state = BTM_ACL_SWKEY_STATE_ENCRYPTION_ON;
+ return;
+ }
+ }
+ else /* Set the state and wait for change link key */
+ {
+ p->change_key_state = BTM_ACL_SWKEY_STATE_ENCRYPTION_ON;
+ return;
+ }
+ }
+
+ /* Set the switch_role_state to IDLE since the reply received from HCI */
+ /* regardless of its result either success or failed. */
+ if (p->change_key_state == BTM_ACL_SWKEY_STATE_IN_PROGRESS)
+ {
+ p->change_key_state = BTM_ACL_SWKEY_STATE_IDLE;
+ p->encrypt_state = BTM_ACL_ENCRYPT_STATE_IDLE;
+ }
+
+ if (btm_cb.devcb.p_chg_link_key_cb)
+ {
+ (*btm_cb.devcb.p_chg_link_key_cb)((void *)p_data);
+ btm_cb.devcb.p_chg_link_key_cb = NULL;
+ }
+
+ BTM_TRACE_ERROR2("Change Link Key Complete Event: Handle 0x%02x, HCI Status 0x%02x",
+ handle, p_data->hci_status);
+}
+
+/*******************************************************************************
+**
+** Function btm_acl_encrypt_change
+**
+** Description This function is when encryption of the connection is
+** completed by the LM. Checks to see if a role switch or
+** change of link key was active and initiates or continues
+** process if needed.
+**
+** Returns void
+**
+*******************************************************************************/
+void btm_acl_encrypt_change (UINT16 handle, UINT8 status, UINT8 encr_enable)
+{
+ tACL_CONN *p;
+ UINT8 xx;
+#if (defined(BTM_BUSY_LEVEL_CHANGE_INCLUDED) && BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE)
+ tBTM_BL_ROLE_CHG_DATA evt;
+#endif
+#if BTM_DISC_DURING_RS == TRUE
+ tBTM_SEC_DEV_REC *p_dev_rec;
+#endif
+ BTM_TRACE_DEBUG3 ("btm_acl_encrypt_change handle=%d status=%d encr_enabl=%d", handle, status, encr_enable);
+ xx = btm_handle_to_acl_index(handle);
+ /* don't assume that we can never get a bad hci_handle */
+ if (xx < MAX_L2CAP_LINKS)
+ p = &btm_cb.acl_db[xx];
+ else
+ return;
+
+ /* Process Role Switch if active */
+ if (p->switch_role_state == BTM_ACL_SWKEY_STATE_ENCRYPTION_OFF)
+ {
+ /* if encryption turn off failed we still will try to switch role */
+ if (encr_enable)
+ {
+ p->switch_role_state = BTM_ACL_SWKEY_STATE_IDLE;
+ p->encrypt_state = BTM_ACL_ENCRYPT_STATE_IDLE;
+ }
+ else
+ {
+ p->switch_role_state = BTM_ACL_SWKEY_STATE_SWITCHING;
+ p->encrypt_state = BTM_ACL_ENCRYPT_STATE_TEMP_FUNC;
+ }
+
+ if (!btsnd_hcic_switch_role (p->remote_addr, (UINT8)!p->link_role))
+ {
+ p->switch_role_state = BTM_ACL_SWKEY_STATE_IDLE;
+ p->encrypt_state = BTM_ACL_ENCRYPT_STATE_IDLE;
+ btm_acl_report_role_change(btm_cb.devcb.switch_role_ref_data.hci_status, p->remote_addr);
+ }
+#if BTM_DISC_DURING_RS == TRUE
+ else
+ {
+ if ((p_dev_rec = btm_find_dev (p->remote_addr)) != NULL)
+ p_dev_rec->rs_disc_pending = BTM_SEC_RS_PENDING;
+ }
+#endif
+
+ }
+ /* Finished enabling Encryption after role switch */
+ else if (p->switch_role_state == BTM_ACL_SWKEY_STATE_ENCRYPTION_ON)
+ {
+ p->switch_role_state = BTM_ACL_SWKEY_STATE_IDLE;
+ p->encrypt_state = BTM_ACL_ENCRYPT_STATE_IDLE;
+ btm_acl_report_role_change(btm_cb.devcb.switch_role_ref_data.hci_status, p->remote_addr);
+
+#if (defined(BTM_BUSY_LEVEL_CHANGE_INCLUDED) && BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE)
+ /* if role change event is registered, report it now */
+ if (btm_cb.p_bl_changed_cb && (btm_cb.bl_evt_mask & BTM_BL_ROLE_CHG_MASK))
+ {
+ evt.event = BTM_BL_ROLE_CHG_EVT;
+ evt.new_role = btm_cb.devcb.switch_role_ref_data.role;
+ evt.p_bda = btm_cb.devcb.switch_role_ref_data.remote_bd_addr;
+ evt.hci_status = btm_cb.devcb.switch_role_ref_data.hci_status;
+ (*btm_cb.p_bl_changed_cb)((tBTM_BL_EVENT_DATA *)&evt);
+
+ BTM_TRACE_DEBUG3("Role Switch Event: new_role 0x%02x, HCI Status 0x%02x, rs_st:%d",
+ evt.new_role, evt.hci_status, p->switch_role_state);
+ }
+#endif
+
+#if BTM_DISC_DURING_RS == TRUE
+ /* If a disconnect is pending, issue it now that role switch has completed */
+ if ((p_dev_rec = btm_find_dev (p->remote_addr)) != NULL)
+ {
+ if (p_dev_rec->rs_disc_pending == BTM_SEC_DISC_PENDING)
+ {
+ BTM_TRACE_WARNING0("btm_acl_encrypt_change -> Issuing delayed HCI_Disconnect!!!");
+ btsnd_hcic_disconnect(p_dev_rec->hci_handle, HCI_ERR_PEER_USER);
+ }
+ BTM_TRACE_ERROR2("btm_acl_encrypt_change: tBTM_SEC_DEV:0x%x rs_disc_pending=%d",
+ (UINT32)p_dev_rec, p_dev_rec->rs_disc_pending);
+ p_dev_rec->rs_disc_pending = BTM_SEC_RS_NOT_PENDING; /* reset flag */
+ }
+#endif
+ }
+
+
+ /* Process Change Link Key if active */
+ if (p->change_key_state == BTM_ACL_SWKEY_STATE_ENCRYPTION_OFF)
+ {
+ /* if encryption turn off failed we still will try to change link key */
+ if (encr_enable)
+ {
+ p->change_key_state = BTM_ACL_SWKEY_STATE_IDLE;
+ p->encrypt_state = BTM_ACL_ENCRYPT_STATE_IDLE;
+ }
+ else
+ {
+ p->encrypt_state = BTM_ACL_ENCRYPT_STATE_TEMP_FUNC;
+ p->change_key_state = BTM_ACL_SWKEY_STATE_SWITCHING;
+ }
+
+ if (!btsnd_hcic_change_link_key (p->hci_handle))
+ {
+ p->encrypt_state = BTM_ACL_ENCRYPT_STATE_IDLE;
+ p->change_key_state = BTM_ACL_SWKEY_STATE_IDLE;
+ if (btm_cb.devcb.p_chg_link_key_cb)
+ {
+ (*btm_cb.devcb.p_chg_link_key_cb)(&btm_cb.devcb.chg_link_key_ref_data);
+ btm_cb.devcb.p_chg_link_key_cb = NULL;
+ }
+ }
+ }
+ /* Finished enabling Encryption after changing link key */
+ else if (p->change_key_state == BTM_ACL_SWKEY_STATE_ENCRYPTION_ON)
+ {
+ p->encrypt_state = BTM_ACL_ENCRYPT_STATE_IDLE;
+ p->change_key_state = BTM_ACL_SWKEY_STATE_IDLE;
+ if (btm_cb.devcb.p_chg_link_key_cb)
+ {
+ (*btm_cb.devcb.p_chg_link_key_cb)(&btm_cb.devcb.chg_link_key_ref_data);
+ btm_cb.devcb.p_chg_link_key_cb = NULL;
+ }
+ }
+}
+/*******************************************************************************
+**
+** Function BTM_SetLinkPolicy
+**
+** Description Create and send HCI "Write Policy Set" command
+**
+** Returns status of the operation
+**
+*******************************************************************************/
+tBTM_STATUS BTM_SetLinkPolicy (BD_ADDR remote_bda, UINT16 *settings)
+{
+ tACL_CONN *p;
+ UINT8 *localFeatures = BTM_ReadLocalFeatures();
+ BTM_TRACE_DEBUG0 ("BTM_SetLinkPolicy");
+/* BTM_TRACE_API1 ("BTM_SetLinkPolicy: requested settings: 0x%04x", *settings ); */
+
+ /* First, check if hold mode is supported */
+ if (*settings != HCI_DISABLE_ALL_LM_MODES)
+ {
+ if ( (*settings & HCI_ENABLE_MASTER_SLAVE_SWITCH) && (!HCI_SWITCH_SUPPORTED(localFeatures)) )
+ {
+ *settings &= (~HCI_ENABLE_MASTER_SLAVE_SWITCH);
+ BTM_TRACE_API1 ("BTM_SetLinkPolicy switch not supported (settings: 0x%04x)", *settings );
+ }
+ if ( (*settings & HCI_ENABLE_HOLD_MODE) && (!HCI_HOLD_MODE_SUPPORTED(localFeatures)) )
+ {
+ *settings &= (~HCI_ENABLE_HOLD_MODE);
+ BTM_TRACE_API1 ("BTM_SetLinkPolicy hold not supported (settings: 0x%04x)", *settings );
+ }
+ if ( (*settings & HCI_ENABLE_SNIFF_MODE) && (!HCI_SNIFF_MODE_SUPPORTED(localFeatures)) )
+ {
+ *settings &= (~HCI_ENABLE_SNIFF_MODE);
+ BTM_TRACE_API1 ("BTM_SetLinkPolicy sniff not supported (settings: 0x%04x)", *settings );
+ }
+ if ( (*settings & HCI_ENABLE_PARK_MODE) && (!HCI_PARK_MODE_SUPPORTED(localFeatures)) )
+ {
+ *settings &= (~HCI_ENABLE_PARK_MODE);
+ BTM_TRACE_API1 ("BTM_SetLinkPolicy park not supported (settings: 0x%04x)", *settings );
+ }
+ }
+
+ if ((p = btm_bda_to_acl(remote_bda)) != NULL)
+ return(btsnd_hcic_write_policy_set (p->hci_handle, *settings) ? BTM_CMD_STARTED : BTM_NO_RESOURCES);
+
+ /* If here, no BD Addr found */
+ return(BTM_UNKNOWN_ADDR);
+}
+
+/*******************************************************************************
+**
+** Function BTM_SetDefaultLinkPolicy
+**
+** Description Set the default value for HCI "Write Policy Set" command
+** to use when an ACL link is created.
+**
+** Returns void
+**
+*******************************************************************************/
+void BTM_SetDefaultLinkPolicy (UINT16 settings)
+{
+ BTM_TRACE_DEBUG0 ("BTM_SetDefaultLinkPolicy");
+ btm_cb.btm_def_link_policy = settings;
+}
+
+
+/*******************************************************************************
+**
+** Function BTM_ReadLinkPolicy
+**
+** Description This function is called to read the link policy settings.
+** The address of link policy results are returned in the callback.
+** (tBTM_LNK_POLICY_RESULTS)
+**
+** Returns status of the operation
+**
+*******************************************************************************/
+tBTM_STATUS BTM_ReadLinkPolicy (BD_ADDR remote_bda, tBTM_CMPL_CB *p_cb)
+{
+ tACL_CONN *p;
+
+ BTM_TRACE_API6 ("BTM_ReadLinkPolicy: RemBdAddr: %02x%02x%02x%02x%02x%02x",
+ remote_bda[0], remote_bda[1], remote_bda[2],
+ remote_bda[3], remote_bda[4], remote_bda[5]);
+
+ /* If someone already waiting on the version, do not allow another */
+ if (btm_cb.devcb.p_rlinkp_cmpl_cb)
+ return(BTM_BUSY);
+
+ p = btm_bda_to_acl(remote_bda);
+ if (p != (tACL_CONN *)NULL)
+ {
+ btu_start_timer (&btm_cb.devcb.rlinkp_timer, BTU_TTYPE_BTM_ACL, BTM_DEV_REPLY_TIMEOUT);
+ btm_cb.devcb.p_rlinkp_cmpl_cb = p_cb;
+
+ if (!btsnd_hcic_read_policy_set (p->hci_handle))
+ {
+ btu_stop_timer (&btm_cb.devcb.rlinkp_timer);
+ btm_cb.devcb.p_rlinkp_cmpl_cb = NULL;
+ return(BTM_NO_RESOURCES);
+ }
+
+ return(BTM_CMD_STARTED);
+ }
+
+ /* If here, no BD Addr found */
+ return(BTM_UNKNOWN_ADDR);
+}
+
+
+/*******************************************************************************
+**
+** Function btm_read_link_policy_complete
+**
+** Description This function is called when the command complete message
+** is received from the HCI for the read local link policy request.
+**
+** Returns void
+**
+*******************************************************************************/
+void btm_read_link_policy_complete (UINT8 *p)
+{
+ tBTM_CMPL_CB *p_cb = btm_cb.devcb.p_rlinkp_cmpl_cb;
+ tBTM_LNK_POLICY_RESULTS lnkpol;
+ UINT16 handle;
+ tACL_CONN *p_acl_cb = &btm_cb.acl_db[0];
+ UINT16 index;
+ BTM_TRACE_DEBUG0 ("btm_read_link_policy_complete");
+ btu_stop_timer (&btm_cb.devcb.rlinkp_timer);
+
+ /* If there was a callback address for read local version, call it */
+ btm_cb.devcb.p_rlinkp_cmpl_cb = NULL;
+
+ if (p_cb)
+ {
+ STREAM_TO_UINT8 (lnkpol.hci_status, p);
+
+ if (lnkpol.hci_status == HCI_SUCCESS)
+ {
+ lnkpol.status = BTM_SUCCESS;
+
+ STREAM_TO_UINT16 (handle, p);
+
+ STREAM_TO_UINT16 (lnkpol.settings, p);
+
+ /* Search through the list of active channels for the correct BD Addr */
+ for (index = 0; index < MAX_L2CAP_LINKS; index++, p_acl_cb++)
+ {
+ if ((p_acl_cb->in_use) && (handle == p_acl_cb->hci_handle))
+ {
+ memcpy (lnkpol.rem_bda, p_acl_cb->remote_addr, BD_ADDR_LEN);
+ break;
+ }
+ }
+ }
+ else
+ lnkpol.status = BTM_ERR_PROCESSING;
+
+ (*p_cb)(&lnkpol);
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function btm_read_remote_version_complete
+**
+** Description This function is called when the command complete message
+** is received from the HCI for the remote version info.
+**
+** Returns void
+**
+*******************************************************************************/
+void btm_read_remote_version_complete (UINT8 *p)
+{
+ tACL_CONN *p_acl_cb = &btm_cb.acl_db[0];
+ UINT8 status;
+ UINT16 handle;
+ int xx;
+ BTM_TRACE_DEBUG0 ("btm_read_remote_version_complete");
+ STREAM_TO_UINT8 (status, p);
+ if (status == HCI_SUCCESS)
+ {
+ STREAM_TO_UINT16 (handle, p);
+
+ /* Look up the connection by handle and copy features */
+ for (xx = 0; xx < MAX_L2CAP_LINKS; xx++, p_acl_cb++)
+ {
+ if ((p_acl_cb->in_use) && (p_acl_cb->hci_handle == handle))
+ {
+ STREAM_TO_UINT8 (p_acl_cb->lmp_version, p);
+ STREAM_TO_UINT16 (p_acl_cb->manufacturer, p);
+ STREAM_TO_UINT16 (p_acl_cb->lmp_subversion, p);
+ break;
+ }
+ }
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function btm_read_remote_features_complete
+**
+** Description This function is called when the remote extended features
+** complete event is received from the HCI.
+**
+** Returns void
+**
+*******************************************************************************/
+void btm_read_remote_features_complete (UINT8 *p)
+{
+ tACL_CONN *p_acl_cb = &btm_cb.acl_db[0];
+ UINT8 status;
+ UINT16 handle;
+ int xx, yy;
+ UINT8 req_pend;
+ tBTM_SEC_DEV_REC *p_dev_rec;
+ BTM_TRACE_DEBUG0 ("btm_read_remote_features_complete");
+ STREAM_TO_UINT8 (status, p);
+ if (status == HCI_SUCCESS)
+ {
+ STREAM_TO_UINT16 (handle, p);
+
+ /* Look up the connection by handle and copy features */
+ for (xx = 0; xx < MAX_L2CAP_LINKS; xx++, p_acl_cb++)
+ {
+ if ((p_acl_cb->in_use) && (p_acl_cb->hci_handle == handle))
+ {
+ for (yy = 0; yy < BD_FEATURES_LEN; yy++)
+ STREAM_TO_UINT8 (p_acl_cb->features[yy], p);
+
+ p_dev_rec = btm_find_dev_by_handle (handle);
+ if (!p_dev_rec)
+ {
+ /* Get a new device; might be doing dedicated bonding */
+ p_dev_rec = btm_find_or_alloc_dev (p_acl_cb->remote_addr);
+ }
+
+ memcpy (p_dev_rec->features, p_acl_cb->features, BD_FEATURES_LEN);
+
+ if (BTM_SEC_MODE_SP == btm_cb.security_mode &&
+ HCI_SIMPLE_PAIRING_SUPPORTED(p_acl_cb->features))
+ {
+ /* if SM4 supported, check peer support for SM4
+ * The remote controller supports SSP
+ * read the extended feature page 1 for the host support for SSP */
+ if (btsnd_hcic_rmt_ext_features (handle, 1))
+ break;
+ }
+ else
+ {
+ req_pend = (p_dev_rec->sm4 & BTM_SM4_REQ_PEND);
+ p_dev_rec->sm4 = BTM_SM4_KNOWN;
+ if (req_pend)
+ {
+ l2cu_resubmit_pending_sec_req (p_dev_rec->bd_addr);
+ }
+ }
+
+ btm_establish_continue (p_acl_cb);
+ break;
+ }
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function btm_read_remote_ext_features_complete
+**
+** Description This function is called when the remote extended features
+** complete event is received from the HCI.
+**
+** Returns void
+**
+*******************************************************************************/
+void btm_read_remote_ext_features_complete (UINT8 *p)
+{
+ tACL_CONN *p_acl_cb = &btm_cb.acl_db[0];
+ tBTM_SEC_DEV_REC *p_dev_rec;
+ UINT8 status, page_num, max_page;
+ UINT16 handle;
+ int xx;
+ BD_FEATURES ext_features; /* extended Features suported by the device */
+ UINT8 req_pend;
+ BTM_TRACE_DEBUG0 ("btm_read_remote_ext_features_complete");
+ STREAM_TO_UINT8 (status, p);
+ if (status == HCI_SUCCESS)
+ {
+ STREAM_TO_UINT16 (handle, p);
+
+ /* Look up the connection by handle and copy features */
+ for (xx = 0; xx < MAX_L2CAP_LINKS; xx++, p_acl_cb++)
+ {
+ if ((p_acl_cb->in_use) && (p_acl_cb->hci_handle == handle))
+ {
+ STREAM_TO_UINT8 (page_num, p);
+ STREAM_TO_UINT8 (max_page, p);
+ p_dev_rec = btm_find_dev_by_handle (handle);
+ if (!p_dev_rec)
+ p_dev_rec = btm_find_or_alloc_dev (p_acl_cb->remote_addr);
+
+ req_pend = (p_dev_rec->sm4 & BTM_SM4_REQ_PEND);
+
+ if (page_num == 1 && max_page >= 1)
+ {
+ /* only the byte 0 of page 1 is used right now */
+ STREAM_TO_UINT8 (ext_features[0], p);
+
+ if (HCI_SSP_HOST_SUPPORTED(ext_features))
+ {
+ p_dev_rec->sm4 = BTM_SM4_TRUE;
+ }
+ }
+
+ BTM_TRACE_API5 ("ext_features_complt page_num:%d max_page:%d f[0]:x%02x, sm4:%x, pend:%d",
+ page_num, max_page, *p, p_dev_rec->sm4, req_pend);
+
+ if (!BTM_SEC_IS_SM4(p_dev_rec->sm4))
+ {
+ p_dev_rec->sm4 = BTM_SM4_KNOWN;
+ }
+
+ if (req_pend)
+ l2cu_resubmit_pending_sec_req (p_dev_rec->bd_addr);
+
+ btm_establish_continue (p_acl_cb);
+
+ break;
+ }
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function btm_read_remote_ext_features_failed
+**
+** Description This function is called when the remote extended features
+** complete event returns a failed status.
+**
+** Returns void
+**
+*******************************************************************************/
+void btm_read_remote_ext_features_failed (UINT8 status)
+{
+ BTM_TRACE_ERROR1 ("btm_read_remote_ext_features_failed (status 0x%02x)", status);
+}
+
+/*******************************************************************************
+**
+** Function btm_establish_continue
+**
+** Description This function is called when the command complete message
+** is received from the HCI for the read local link policy request.
+**
+** Returns void
+**
+*******************************************************************************/
+static void btm_establish_continue (tACL_CONN *p_acl_cb)
+{
+#if (defined(BTM_BUSY_LEVEL_CHANGE_INCLUDED) && BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE)
+ tBTM_BL_EVENT_DATA evt_data;
+#endif
+ BTM_TRACE_DEBUG0 ("btm_establish_continue");
+#if (!defined(BTM_BYPASS_EXTRA_ACL_SETUP) || BTM_BYPASS_EXTRA_ACL_SETUP == FALSE)
+#if (defined BLE_INCLUDED && BLE_INCLUDED == TRUE)
+ if (!p_acl_cb->is_le_link)
+#endif
+ {
+ /* For now there are a some devices that do not like sending */
+ /* commands events and data at the same time. */
+ /* Set the packet types to the default allowed by the device */
+ btm_set_packet_types (p_acl_cb, btm_cb.btm_acl_pkt_types_supported);
+
+ if (btm_cb.btm_def_link_policy)
+ BTM_SetLinkPolicy (p_acl_cb->remote_addr, &btm_cb.btm_def_link_policy);
+
+ BTM_SetLinkSuperTout (p_acl_cb->remote_addr, btm_cb.btm_def_link_super_tout);
+ }
+#endif
+ p_acl_cb->link_up_issued = TRUE;
+
+ /* If anyone cares, tell him database changed */
+#if (defined(BTM_BUSY_LEVEL_CHANGE_INCLUDED) && BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE)
+ if (btm_cb.p_bl_changed_cb)
+ {
+ evt_data.event = BTM_BL_CONN_EVT;
+ evt_data.conn.p_bda = p_acl_cb->remote_addr;
+ evt_data.conn.p_bdn = p_acl_cb->remote_name;
+ evt_data.conn.p_dc = p_acl_cb->remote_dc;
+ evt_data.conn.p_features = p_acl_cb->features;
+
+
+ (*btm_cb.p_bl_changed_cb)(&evt_data);
+ }
+ btm_acl_update_busy_level (BTM_BLI_ACL_UP_EVT);
+#else
+ if (btm_cb.p_acl_changed_cb)
+ (*btm_cb.p_acl_changed_cb) (p_acl_cb->remote_addr,
+ p_acl_cb->remote_dc,
+ p_acl_cb->remote_name,
+ p_acl_cb->features,
+ TRUE);
+#endif
+}
+
+
+/*******************************************************************************
+**
+** Function BTM_SetDefaultLinkSuperTout
+**
+** Description Set the default value for HCI "Write Link Supervision Timeout"
+** command to use when an ACL link is created.
+**
+** Returns void
+**
+*******************************************************************************/
+void BTM_SetDefaultLinkSuperTout (UINT16 timeout)
+{
+ BTM_TRACE_DEBUG0 ("BTM_SetDefaultLinkSuperTout");
+ btm_cb.btm_def_link_super_tout = timeout;
+}
+
+/*******************************************************************************
+**
+** Function BTM_SetLinkSuperTout
+**
+** Description Create and send HCI "Write Link Supervision Timeout" command
+**
+** Returns status of the operation
+**
+*******************************************************************************/
+tBTM_STATUS BTM_SetLinkSuperTout (BD_ADDR remote_bda, UINT16 timeout)
+{
+ tACL_CONN *p = btm_bda_to_acl(remote_bda);
+
+ BTM_TRACE_DEBUG0 ("BTM_SetLinkSuperTout");
+ if (p != (tACL_CONN *)NULL)
+ {
+ p->link_super_tout = timeout;
+
+ /* Only send if current role is Master; 2.0 spec requires this */
+ if (p->link_role == BTM_ROLE_MASTER)
+ {
+ if (!btsnd_hcic_write_link_super_tout (LOCAL_BR_EDR_CONTROLLER_ID,
+ p->hci_handle, timeout))
+ return(BTM_NO_RESOURCES);
+
+ return(BTM_CMD_STARTED);
+ }
+ else
+ return(BTM_SUCCESS);
+ }
+
+ /* If here, no BD Addr found */
+ return(BTM_UNKNOWN_ADDR);
+}
+
+/*******************************************************************************
+**
+** Function BTM_RegForLstoEvt
+**
+** Description register for the HCI "Link Supervision Timeout Change" event
+**
+** Returns void
+**
+*******************************************************************************/
+void BTM_RegForLstoEvt (tBTM_LSTO_CBACK *p_cback)
+{
+ BTM_TRACE_DEBUG0 ("BTM_RegForLstoEvt");
+ btm_cb.p_lsto_cback = p_cback;
+}
+
+/*******************************************************************************
+**
+** Function btm_proc_lsto_evt
+**
+** Description process the HCI "Link Supervision Timeout Change" event
+**
+** Returns void
+**
+*******************************************************************************/
+void btm_proc_lsto_evt(UINT16 handle, UINT16 timeout)
+{
+ UINT8 xx;
+
+ BTM_TRACE_DEBUG0 ("btm_proc_lsto_evt");
+ if (btm_cb.p_lsto_cback)
+ {
+ /* Look up the connection by handle and set the current mode */
+ xx = btm_handle_to_acl_index(handle);
+
+ /* don't assume that we can never get a bad hci_handle */
+ if (xx < MAX_L2CAP_LINKS)
+ {
+ (*btm_cb.p_lsto_cback)(btm_cb.acl_db[xx].remote_addr, timeout);
+ }
+ }
+}
+
+#if BTM_PWR_MGR_INCLUDED == FALSE
+/*******************************************************************************
+**
+** Function BTM_SetHoldMode
+**
+** Description This function is called to set a connection into hold mode.
+** A check is made if the connection is in sniff or park mode,
+** and if yes, the hold mode is ignored.
+**
+** Returns status of the operation
+**
+*******************************************************************************/
+tBTM_STATUS BTM_SetHoldMode (BD_ADDR remote_bda, UINT16 min_interval, UINT16 max_interval)
+{
+ tACL_CONN *p;
+
+ BTM_TRACE_DEBUG0 ("BTM_SetHoldMode");
+ /* First, check if hold mode is supported */
+ if (!HCI_HOLD_MODE_SUPPORTED(BTM_ReadLocalFeatures()))
+ return(BTM_MODE_UNSUPPORTED);
+
+ p = btm_bda_to_acl(remote_bda);
+ if (p != (tACL_CONN *)NULL)
+ {
+ /* If the connection is in park or sniff mode, forget about holding it */
+ if (p->mode != BTM_ACL_MODE_NORMAL)
+ return(BTM_SUCCESS);
+
+ if (!btsnd_hcic_hold_mode (p->hci_handle, max_interval, min_interval))
+ return(BTM_NO_RESOURCES);
+
+ return(BTM_CMD_STARTED);
+ }
+
+ /* If here, no BD Addr found */
+ return(BTM_UNKNOWN_ADDR);
+}
+
+
+/*******************************************************************************
+**
+** Function BTM_SetSniffMode
+**
+** Description This function is called to set a connection into sniff mode.
+** A check is made if the connection is already in sniff or park
+** mode, and if yes, the sniff mode is ignored.
+**
+** Returns status of the operation
+**
+*******************************************************************************/
+tBTM_STATUS BTM_SetSniffMode (BD_ADDR remote_bda, UINT16 min_period, UINT16 max_period,
+ UINT16 attempt, UINT16 timeout)
+{
+ tACL_CONN *p;
+ BTM_TRACE_DEBUG0 ("BTM_SetSniffMode");
+ /* First, check if sniff mode is supported */
+ if (!HCI_SNIFF_MODE_SUPPORTED(BTM_ReadLocalFeatures()))
+ return(BTM_MODE_UNSUPPORTED);
+
+ p = btm_bda_to_acl(remote_bda);
+ if (p != (tACL_CONN *)NULL)
+ {
+ /* If the connection is in park mode, forget about sniffing it */
+ if (p->mode != BTM_ACL_MODE_NORMAL)
+ return(BTM_WRONG_MODE);
+
+ if (!btsnd_hcic_sniff_mode (p->hci_handle, max_period,
+ min_period, attempt, timeout))
+ return(BTM_NO_RESOURCES);
+
+ return(BTM_CMD_STARTED);
+ }
+
+ /* If here, no BD Addr found */
+ return(BTM_UNKNOWN_ADDR);
+}
+
+
+
+
+/*******************************************************************************
+**
+** Function BTM_CancelSniffMode
+**
+** Description This function is called to put a connection out of sniff mode.
+** A check is made if the connection is already in sniff mode,
+** and if not, the cancel sniff mode is ignored.
+**
+** Returns status of the operation
+**
+*******************************************************************************/
+tBTM_STATUS BTM_CancelSniffMode (BD_ADDR remote_bda)
+{
+ tACL_CONN *p = btm_bda_to_acl(remote_bda);
+ BTM_TRACE_DEBUG0 ("BTM_CancelSniffMode ");
+ if (p == (tACL_CONN *)NULL)
+ return(BTM_UNKNOWN_ADDR);
+
+ /* If the connection is not in sniff mode, cannot cancel */
+ if (p->mode != BTM_ACL_MODE_SNIFF)
+ return(BTM_WRONG_MODE);
+
+ if (!btsnd_hcic_exit_sniff_mode (p->hci_handle))
+ return(BTM_NO_RESOURCES);
+
+ return(BTM_CMD_STARTED);
+}
+
+
+/*******************************************************************************
+**
+** Function BTM_SetParkMode
+**
+** Description This function is called to set a connection into park mode.
+** A check is made if the connection is already in sniff or park
+** mode, and if yes, the park mode is ignored.
+**
+** Returns status of the operation
+**
+*******************************************************************************/
+tBTM_STATUS BTM_SetParkMode (BD_ADDR remote_bda, UINT16 beacon_min_period, UINT16 beacon_max_period)
+{
+ tACL_CONN *p;
+
+ BTM_TRACE_DEBUG0 ("BTM_SetParkMode");
+ /* First, check if park mode is supported */
+ if (!HCI_PARK_MODE_SUPPORTED(BTM_ReadLocalFeatures()))
+ return(BTM_MODE_UNSUPPORTED);
+
+ p = btm_bda_to_acl(remote_bda);
+ if (p != (tACL_CONN *)NULL)
+ {
+ /* If the connection is in sniff mode, forget about parking it */
+ if (p->mode != BTM_ACL_MODE_NORMAL)
+ return(BTM_WRONG_MODE);
+
+ /* no park mode if SCO exists -- CR#1982, 1.1 errata 1124
+ command status event should be returned /w error code 0x0C "Command Disallowed"
+ Let LM do this.
+ */
+ if (!btsnd_hcic_park_mode (p->hci_handle,
+ beacon_max_period, beacon_min_period))
+ return(BTM_NO_RESOURCES);
+
+ return(BTM_CMD_STARTED);
+ }
+
+ /* If here, no BD Addr found */
+ return(BTM_UNKNOWN_ADDR);
+}
+
+/*******************************************************************************
+**
+** Function BTM_CancelParkMode
+**
+** Description This function is called to put a connection out of park mode.
+** A check is made if the connection is already in park mode,
+** and if not, the cancel sniff mode is ignored.
+**
+** Returns status of the operation
+**
+*******************************************************************************/
+tBTM_STATUS BTM_CancelParkMode (BD_ADDR remote_bda)
+{
+ tACL_CONN *p;
+
+ BTM_TRACE_DEBUG0 ("BTM_CancelParkMode");
+ p = btm_bda_to_acl(remote_bda);
+ if (p != (tACL_CONN *)NULL)
+ {
+ /* If the connection is not in park mode, cannot cancel */
+ if (p->mode != BTM_ACL_MODE_PARK)
+ return(BTM_WRONG_MODE);
+
+ if (!btsnd_hcic_exit_park_mode (p->hci_handle))
+ return(BTM_NO_RESOURCES);
+
+ return(BTM_CMD_STARTED);
+ }
+
+ /* If here, no BD Addr found */
+ return(BTM_UNKNOWN_ADDR);
+}
+#endif /* BTM_PWR_MGR_INCLUDED == FALSE */
+
+
+/*******************************************************************************
+**
+** Function BTM_SetPacketTypes
+**
+** Description This function is set the packet types used for a specific
+** ACL connection,
+**
+** Returns status of the operation
+**
+*******************************************************************************/
+tBTM_STATUS BTM_SetPacketTypes (BD_ADDR remote_bda, UINT16 pkt_types)
+{
+ tACL_CONN *p;
+ BTM_TRACE_DEBUG0 ("BTM_SetPacketTypes");
+
+ if ((p = btm_bda_to_acl(remote_bda)) != NULL)
+ return(btm_set_packet_types (p, pkt_types));
+
+ /* If here, no BD Addr found */
+ return(BTM_UNKNOWN_ADDR);
+}
+
+
+/*******************************************************************************
+**
+** Function BTM_ReadPacketTypes
+**
+** Description This function is set the packet types used for a specific
+** ACL connection,
+**
+** Returns packet types supported for the connection, or 0 if no BD address
+**
+*******************************************************************************/
+UINT16 BTM_ReadPacketTypes (BD_ADDR remote_bda)
+{
+ tACL_CONN *p;
+
+ BTM_TRACE_DEBUG0 ("BTM_ReadPacketTypes");
+ p = btm_bda_to_acl(remote_bda);
+ if (p != (tACL_CONN *)NULL)
+ {
+ return(p->pkt_types_mask);
+ }
+
+ /* If here, no BD Addr found */
+ return(0);
+}
+
+
+/*******************************************************************************
+**
+** Function BTM_ReadAclMode
+**
+** Description This returns the current mode for a specific
+** ACL connection.
+**
+** Input Param remote_bda - device address of desired ACL connection
+**
+** Output Param p_mode - address where the current mode is copied into.
+** BTM_ACL_MODE_NORMAL
+** BTM_ACL_MODE_HOLD
+** BTM_ACL_MODE_SNIFF
+** BTM_ACL_MODE_PARK
+** (valid only if return code is BTM_SUCCESS)
+**
+** Returns BTM_SUCCESS if successful,
+** BTM_UNKNOWN_ADDR if bd addr is not active or bad
+**
+*******************************************************************************/
+#if BTM_PWR_MGR_INCLUDED == FALSE
+tBTM_STATUS BTM_ReadAclMode (BD_ADDR remote_bda, UINT8 *p_mode)
+{
+ tACL_CONN *p;
+
+ BTM_TRACE_API6 ("BTM_ReadAclMode: RemBdAddr: %02x%02x%02x%02x%02x%02x",
+ remote_bda[0], remote_bda[1], remote_bda[2],
+ remote_bda[3], remote_bda[4], remote_bda[5]);
+
+ p = btm_bda_to_acl(remote_bda);
+ if (p != (tACL_CONN *)NULL)
+ {
+ *p_mode = p->mode;
+ return(BTM_SUCCESS);
+ }
+
+ /* If here, no BD Addr found */
+ return(BTM_UNKNOWN_ADDR);
+}
+#endif /* BTM_PWR_MGR_INCLUDED == FALSE */
+
+/*******************************************************************************
+**
+** Function BTM_ReadClockOffset
+**
+** Description This returns the clock offset for a specific
+** ACL connection.
+**
+** Input Param remote_bda - device address of desired ACL connection
+**
+** Returns clock-offset or 0 if unknown
+**
+*******************************************************************************/
+UINT16 BTM_ReadClockOffset (BD_ADDR remote_bda)
+{
+ tACL_CONN *p;
+
+ BTM_TRACE_API6 ("BTM_ReadClockOffset: RemBdAddr: %02x%02x%02x%02x%02x%02x",
+ remote_bda[0], remote_bda[1], remote_bda[2],
+ remote_bda[3], remote_bda[4], remote_bda[5]);
+
+ if ( (p = btm_bda_to_acl(remote_bda)) != NULL)
+ return(p->clock_offset);
+
+ /* If here, no BD Addr found */
+ return(0);
+}
+
+/*******************************************************************************
+**
+** Function BTM_IsAclConnectionUp
+**
+** Description This function is called to check if an ACL connection exists
+** to a specific remote BD Address.
+**
+** Returns TRUE if connection is up, else FALSE.
+**
+*******************************************************************************/
+BOOLEAN BTM_IsAclConnectionUp (BD_ADDR remote_bda)
+{
+ tACL_CONN *p;
+
+ BTM_TRACE_API6 ("BTM_ReadClockOffset: RemBdAddr: %02x%02x%02x%02x%02x%02x",
+ remote_bda[0], remote_bda[1], remote_bda[2],
+ remote_bda[3], remote_bda[4], remote_bda[5]);
+
+ p = btm_bda_to_acl(remote_bda);
+ if (p != (tACL_CONN *)NULL)
+ {
+ return(TRUE);
+ }
+
+ /* If here, no BD Addr found */
+ return(FALSE);
+}
+
+/*******************************************************************************
+**
+** Function BTM_GetNumAclLinks
+**
+** Description This function is called to count the number of
+** ACL links that are active.
+**
+** Returns UINT16 Number of active ACL links
+**
+*******************************************************************************/
+UINT16 BTM_GetNumAclLinks (void)
+{
+#if (defined(BTM_BUSY_LEVEL_CHANGE_INCLUDED) && BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE)
+ return(UINT16)btm_cb.num_acl;
+#else
+ tACL_CONN *p = &btm_cb.acl_db[0];
+ UINT16 xx, yy;
+ BTM_TRACE_DEBUG0 ("BTM_GetNumAclLinks");
+ for (xx = yy = 0; xx < MAX_L2CAP_LINKS; xx++, p++)
+ {
+ if (p->in_use)
+ yy++;
+ }
+
+ return(yy);
+#endif
+}
+
+/*******************************************************************************
+**
+** Function btm_get_acl_disc_reason_code
+**
+** Description This function is called to get the disconnection reason code
+** returned by the HCI at disconnection complete event.
+**
+** Returns TRUE if connection is up, else FALSE.
+**
+*******************************************************************************/
+UINT16 btm_get_acl_disc_reason_code (void)
+{
+ UINT8 res = btm_cb.acl_disc_reason;
+ BTM_TRACE_DEBUG0 ("btm_get_acl_disc_reason_code");
+ return(res);
+}
+
+
+/*******************************************************************************
+**
+** Function BTM_GetHCIConnHandle
+**
+** Description This function is called to get the handle for an ACL connection
+** to a specific remote BD Address.
+**
+** Returns the handle of the connection, or 0xFFFF if none.
+**
+*******************************************************************************/
+UINT16 BTM_GetHCIConnHandle (BD_ADDR remote_bda)
+{
+ tACL_CONN *p;
+ BTM_TRACE_DEBUG0 ("BTM_GetHCIConnHandle");
+ p = btm_bda_to_acl(remote_bda);
+ if (p != (tACL_CONN *)NULL)
+ {
+ return(p->hci_handle);
+ }
+
+ /* If here, no BD Addr found */
+ return(0xFFFF);
+}
+
+#if BTM_PWR_MGR_INCLUDED == FALSE
+/*******************************************************************************
+**
+** Function btm_process_mode_change
+**
+** Description This function is called when an HCI mode change event occurs.
+**
+** Input Parms hci_status - status of the event (HCI_SUCCESS if no errors)
+** hci_handle - connection handle associated with the change
+** mode - HCI_MODE_ACTIVE, HCI_MODE_HOLD, HCI_MODE_SNIFF, or HCI_MODE_PARK
+** interval - number of baseband slots (meaning depends on mode)
+**
+** Returns void
+**
+*******************************************************************************/
+void btm_process_mode_change (UINT8 hci_status, UINT16 hci_handle, UINT8 mode, UINT16 interval)
+{
+ tACL_CONN *p;
+ UINT8 xx;
+ BTM_TRACE_DEBUG0 ("btm_process_mode_change");
+ if (hci_status != HCI_SUCCESS)
+ {
+ BTM_TRACE_WARNING1 ("BTM: HCI Mode Change Error Status: 0x%02x", hci_status);
+ }
+
+ /* Look up the connection by handle and set the current mode */
+ xx = btm_handle_to_acl_index(hci_handle);
+
+ /* don't assume that we can never get a bad hci_handle */
+ if (xx >= MAX_L2CAP_LINKS)
+ return;
+
+ p = &btm_cb.acl_db[xx];
+
+ /* If status is not success mode does not mean anything */
+ if (hci_status == HCI_SUCCESS)
+ p->mode = mode;
+
+ /* If mode change was because of an active role switch or change link key */
+ btm_cont_rswitch_or_chglinkkey(p, btm_find_dev(p->remote_addr), hci_status);
+}
+#endif /* BTM_PWR_MGR_INCLUDED == FALSE */
+
+/*******************************************************************************
+**
+** Function btm_process_clk_off_comp_evt
+**
+** Description This function is called when clock offset command completes.
+**
+** Input Parms hci_handle - connection handle associated with the change
+** clock offset
+**
+** Returns void
+**
+*******************************************************************************/
+void btm_process_clk_off_comp_evt (UINT16 hci_handle, UINT16 clock_offset)
+{
+ UINT8 xx;
+ BTM_TRACE_DEBUG0 ("btm_process_clk_off_comp_evt");
+ /* Look up the connection by handle and set the current mode */
+ if ((xx = btm_handle_to_acl_index(hci_handle)) < MAX_L2CAP_LINKS)
+ btm_cb.acl_db[xx].clock_offset = clock_offset;
+}
+
+/*******************************************************************************
+**
+** Function btm_acl_role_changed
+**
+** Description This function is called whan a link's master/slave role change
+** event or command status event (with error) is received.
+** It updates the link control block, and calls
+** the registered callback with status and role (if registered).
+**
+** Returns void
+**
+*******************************************************************************/
+void btm_acl_role_changed (UINT8 hci_status, BD_ADDR bd_addr, UINT8 new_role)
+{
+ UINT8 *p_bda = (bd_addr) ? bd_addr : btm_cb.devcb.switch_role_ref_data.remote_bd_addr;
+ tACL_CONN *p = btm_bda_to_acl(p_bda);
+ tBTM_ROLE_SWITCH_CMPL *p_data = &btm_cb.devcb.switch_role_ref_data;
+#if (defined(BTM_BUSY_LEVEL_CHANGE_INCLUDED) && BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE)
+ tBTM_BL_ROLE_CHG_DATA evt;
+#endif
+#if BTM_DISC_DURING_RS == TRUE
+ tBTM_SEC_DEV_REC *p_dev_rec;
+#endif
+ BTM_TRACE_DEBUG0 ("btm_acl_role_changed");
+ /* Ignore any stray events */
+ if (p == NULL)
+ {
+ /* it could be a failure */
+ if (hci_status != HCI_SUCCESS)
+ btm_acl_report_role_change(hci_status, bd_addr);
+ return;
+ }
+
+ p_data->hci_status = hci_status;
+
+ if (hci_status == HCI_SUCCESS)
+ {
+ p_data->role = new_role;
+ memcpy(p_data->remote_bd_addr, p_bda, BD_ADDR_LEN);
+
+ /* Update cached value */
+ p->link_role = new_role;
+
+ /* Reload LSTO: link supervision timeout is reset in the LM after a role switch */
+ if (new_role == BTM_ROLE_MASTER)
+ {
+ BTM_SetLinkSuperTout (p->remote_addr, p->link_super_tout);
+ }
+ }
+ else
+ {
+ /* so the BTM_BL_ROLE_CHG_EVT uses the old role */
+ new_role = p->link_role;
+ }
+
+ /* Check if any SCO req is pending for role change */
+ btm_sco_chk_pend_rolechange (p->hci_handle);
+
+ /* if switching state is switching we need to turn encryption on */
+ /* if idle, we did not change encryption */
+ if (p->switch_role_state == BTM_ACL_SWKEY_STATE_SWITCHING)
+ {
+ /* Make sure there's not also a change link key going on before re-enabling */
+ if (p->change_key_state != BTM_ACL_SWKEY_STATE_SWITCHING)
+ {
+ if (btsnd_hcic_set_conn_encrypt (p->hci_handle, TRUE))
+ {
+ p->encrypt_state = BTM_ACL_ENCRYPT_STATE_ENCRYPT_ON;
+ p->switch_role_state = BTM_ACL_SWKEY_STATE_ENCRYPTION_ON;
+ return;
+ }
+ }
+ else /* Set the state and wait for change link key */
+ {
+ p->switch_role_state = BTM_ACL_SWKEY_STATE_ENCRYPTION_ON;
+ return;
+ }
+ }
+
+ /* Set the switch_role_state to IDLE since the reply received from HCI */
+ /* regardless of its result either success or failed. */
+ if (p->switch_role_state == BTM_ACL_SWKEY_STATE_IN_PROGRESS)
+ {
+ p->switch_role_state = BTM_ACL_SWKEY_STATE_IDLE;
+ p->encrypt_state = BTM_ACL_ENCRYPT_STATE_IDLE;
+ }
+
+ /* if role switch complete is needed, report it now */
+ btm_acl_report_role_change(hci_status, bd_addr);
+
+#if (defined(BTM_BUSY_LEVEL_CHANGE_INCLUDED) && BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE)
+ /* if role change event is registered, report it now */
+ if (btm_cb.p_bl_changed_cb && (btm_cb.bl_evt_mask & BTM_BL_ROLE_CHG_MASK))
+ {
+ evt.event = BTM_BL_ROLE_CHG_EVT;
+ evt.new_role = new_role;
+ evt.p_bda = p_bda;
+ evt.hci_status = hci_status;
+ (*btm_cb.p_bl_changed_cb)((tBTM_BL_EVENT_DATA *)&evt);
+ }
+
+ BTM_TRACE_DEBUG3("Role Switch Event: new_role 0x%02x, HCI Status 0x%02x, rs_st:%d",
+ p_data->role, p_data->hci_status, p->switch_role_state);
+#endif
+
+#if BTM_DISC_DURING_RS == TRUE
+ /* If a disconnect is pending, issue it now that role switch has completed */
+ if ((p_dev_rec = btm_find_dev (p_bda)) != NULL)
+ {
+ if (p_dev_rec->rs_disc_pending == BTM_SEC_DISC_PENDING)
+ {
+ BTM_TRACE_WARNING0("btm_acl_role_changed -> Issuing delayed HCI_Disconnect!!!");
+ btsnd_hcic_disconnect(p_dev_rec->hci_handle, HCI_ERR_PEER_USER);
+ }
+ BTM_TRACE_ERROR2("tBTM_SEC_DEV:0x%x rs_disc_pending=%d", (UINT32)p_dev_rec, p_dev_rec->rs_disc_pending);
+ p_dev_rec->rs_disc_pending = BTM_SEC_RS_NOT_PENDING; /* reset flag */
+ }
+
+#endif
+
+}
+
+#if (RFCOMM_INCLUDED==TRUE)
+/*******************************************************************************
+**
+** Function BTM_AllocateSCN
+**
+** Description Look through the Server Channel Numbers for a free one.
+**
+** Returns Allocated SCN number or 0 if none.
+**
+*******************************************************************************/
+
+UINT8 BTM_AllocateSCN(void)
+{
+ UINT8 x;
+ BTM_TRACE_DEBUG0 ("BTM_AllocateSCN");
+
+ // stack reserves scn 1 for HFP, HSP we still do the correct way
+ for (x = 1; x < BTM_MAX_SCN; x++)
+ {
+ if (!btm_cb.btm_scn[x])
+ {
+ btm_cb.btm_scn[x] = TRUE;
+ return(x+1);
+ }
+ }
+
+ return(0); /* No free ports */
+}
+
+/*******************************************************************************
+**
+** Function BTM_TryAllocateSCN
+**
+** Description Try to allocate a fixed server channel
+**
+** Returns Returns TRUE if server channel was available
+**
+*******************************************************************************/
+
+BOOLEAN BTM_TryAllocateSCN(UINT8 scn)
+{
+ UINT8 x;
+
+ /* Make sure we don't exceed max port range.
+ * Stack reserves scn 1 for HFP, HSP we still do the correct way.
+ */
+ if ( (scn>=BTM_MAX_SCN) || (scn == 1) )
+ return FALSE;
+
+ /* check if this port is available */
+ if (!btm_cb.btm_scn[scn-1])
+ {
+ btm_cb.btm_scn[scn-1] = TRUE;
+ return TRUE;
+ }
+
+ return (FALSE); /* Port was busy */
+}
+
+/*******************************************************************************
+**
+** Function BTM_FreeSCN
+**
+** Description Free the specified SCN.
+**
+** Returns TRUE or FALSE
+**
+*******************************************************************************/
+BOOLEAN BTM_FreeSCN(UINT8 scn)
+{
+ BTM_TRACE_DEBUG0 ("BTM_FreeSCN ");
+ if (scn <= BTM_MAX_SCN)
+ {
+ btm_cb.btm_scn[scn-1] = FALSE;
+ return(TRUE);
+ }
+ else
+ return(FALSE); /* Illegal SCN passed in */
+}
+
+#else
+
+/* Make dummy functions for the RPC to link against */
+UINT8 BTM_AllocateSCN(void)
+{
+ return(0);
+}
+
+BOOLEAN BTM_FreeSCN(UINT8 scn)
+{
+ return(FALSE);
+}
+
+#endif
+
+
+/*******************************************************************************
+**
+** Function btm_acl_timeout
+**
+** Description This function is called when a timer list entry expires.
+**
+** Returns void
+**
+*******************************************************************************/
+void btm_acl_timeout (TIMER_LIST_ENT *p_tle)
+{
+ UINT32 timer_type = p_tle->param;
+
+ BTM_TRACE_DEBUG0 ("btm_acl_timeout");
+ if (timer_type == TT_DEV_RLNKP)
+ {
+ tBTM_CMPL_CB *p_cb = btm_cb.devcb.p_rlinkp_cmpl_cb;
+ tBTM_LNK_POLICY_RESULTS lnkpol;
+
+ lnkpol.status = BTM_ERR_PROCESSING;
+ lnkpol.settings = 0;
+
+ btm_cb.devcb.p_rlinkp_cmpl_cb = NULL;
+
+ if (p_cb)
+ (*p_cb)(&lnkpol);
+ }
+}
+
+/*******************************************************************************
+**
+** Function btm_set_packet_types
+**
+** Description This function sets the packet types used for a specific
+** ACL connection. It is called internally by btm_acl_created
+** or by an application/profile by BTM_SetPacketTypes.
+**
+** Returns status of the operation
+**
+*******************************************************************************/
+tBTM_STATUS btm_set_packet_types (tACL_CONN *p, UINT16 pkt_types)
+{
+ UINT16 temp_pkt_types;
+ BTM_TRACE_DEBUG0 ("btm_set_packet_types");
+ /* Save in the ACL control blocks, types that we support */
+ temp_pkt_types = (pkt_types & BTM_ACL_SUPPORTED_PKTS_MASK &
+ btm_cb.btm_acl_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 |= ((pkt_types & BTM_ACL_EXCEPTION_PKTS_MASK) |
+ (btm_cb.btm_acl_pkt_types_supported & BTM_ACL_EXCEPTION_PKTS_MASK));
+ }
+ else
+ {
+ temp_pkt_types &= (~BTM_ACL_EXCEPTION_PKTS_MASK);
+ }
+
+ /* Exclude packet types not supported by the peer */
+ btm_acl_chk_peer_pkt_type_support (p, &temp_pkt_types);
+
+ BTM_TRACE_DEBUG1 ("SetPacketType Mask -> 0x%04x", temp_pkt_types);
+
+ if (!btsnd_hcic_change_conn_type (p->hci_handle, temp_pkt_types))
+ {
+ return(BTM_NO_RESOURCES);
+ }
+
+ p->pkt_types_mask = temp_pkt_types;
+
+ return(BTM_CMD_STARTED);
+}
+
+/*******************************************************************************
+**
+** Function btm_get_max_packet_size
+**
+** Returns Returns maximum packet size that can be used for current
+** connection, 0 if connection is not established
+**
+*******************************************************************************/
+UINT16 btm_get_max_packet_size (BD_ADDR addr)
+{
+ tACL_CONN *p = btm_bda_to_acl(addr);
+ UINT16 pkt_types = 0;
+ UINT16 pkt_size = 0;
+ BTM_TRACE_DEBUG0 ("btm_get_max_packet_size");
+ if (p != NULL)
+ {
+ pkt_types = p->pkt_types_mask;
+ }
+ else
+ {
+ /* Special case for when info for the local device is requested */
+ if (memcmp (btm_cb.devcb.local_addr, addr, BD_ADDR_LEN) == 0)
+ {
+ pkt_types = btm_cb.btm_acl_pkt_types_supported;
+ }
+ }
+
+ if (pkt_types)
+ {
+ if (!(pkt_types & BTM_ACL_PKT_TYPES_MASK_NO_3_DH5))
+ pkt_size = HCI_EDR3_DH5_PACKET_SIZE;
+ else if (!(pkt_types & BTM_ACL_PKT_TYPES_MASK_NO_2_DH5))
+ pkt_size = HCI_EDR2_DH5_PACKET_SIZE;
+ else if (!(pkt_types & BTM_ACL_PKT_TYPES_MASK_NO_3_DH3))
+ pkt_size = HCI_EDR3_DH3_PACKET_SIZE;
+ else if (pkt_types & BTM_ACL_PKT_TYPES_MASK_DH5)
+ pkt_size = HCI_DH5_PACKET_SIZE;
+ else if (!(pkt_types & BTM_ACL_PKT_TYPES_MASK_NO_2_DH3))
+ pkt_size = HCI_EDR2_DH3_PACKET_SIZE;
+ else if (pkt_types & BTM_ACL_PKT_TYPES_MASK_DM5)
+ pkt_size = HCI_DM5_PACKET_SIZE;
+ else if (pkt_types & BTM_ACL_PKT_TYPES_MASK_DH3)
+ pkt_size = HCI_DH3_PACKET_SIZE;
+ else if (pkt_types & BTM_ACL_PKT_TYPES_MASK_DM3)
+ pkt_size = HCI_DM3_PACKET_SIZE;
+ else if (!(pkt_types & BTM_ACL_PKT_TYPES_MASK_NO_3_DH1))
+ pkt_size = HCI_EDR3_DH1_PACKET_SIZE;
+ else if (!(pkt_types & BTM_ACL_PKT_TYPES_MASK_NO_2_DH1))
+ pkt_size = HCI_EDR2_DH1_PACKET_SIZE;
+ else if (pkt_types & BTM_ACL_PKT_TYPES_MASK_DH1)
+ pkt_size = HCI_DH1_PACKET_SIZE;
+ else if (pkt_types & BTM_ACL_PKT_TYPES_MASK_DM1)
+ pkt_size = HCI_DM1_PACKET_SIZE;
+ }
+
+#ifdef BRCM_VS
+ /* Using HCI size 1017 instead of 1021 */
+ if ((pkt_size == HCI_EDR3_DH5_PACKET_SIZE)
+ && (btu_cb.hcit_acl_data_size == 1017))
+ pkt_size = 1017;
+#endif
+
+ return(pkt_size);
+}
+
+/*******************************************************************************
+**
+** Function BTM_ReadRemoteVersion
+**
+** Returns If connected report peer device info
+**
+*******************************************************************************/
+tBTM_STATUS BTM_ReadRemoteVersion (BD_ADDR addr, UINT8 *lmp_version,
+ UINT16 *manufacturer, UINT16 *lmp_sub_version)
+{
+ tACL_CONN *p = btm_bda_to_acl(addr);
+ BTM_TRACE_DEBUG0 ("BTM_ReadRemoteVersion");
+ if (p == NULL)
+ return(BTM_UNKNOWN_ADDR);
+
+ if (lmp_version)
+ *lmp_version = p->lmp_version;
+
+ if (manufacturer)
+ *manufacturer = p->manufacturer;
+
+ if (lmp_sub_version)
+ *lmp_sub_version = p->lmp_subversion;
+
+ return(BTM_SUCCESS);
+}
+
+/*******************************************************************************
+**
+** Function BTM_ReadRemoteFeatures
+**
+** Returns pointer to the features string
+**
+*******************************************************************************/
+UINT8 *BTM_ReadRemoteFeatures (BD_ADDR addr)
+{
+ tACL_CONN *p = btm_bda_to_acl(addr);
+ BTM_TRACE_DEBUG0 ("BTM_ReadRemoteFeatures");
+ if (p == NULL)
+ {
+ return(NULL);
+ }
+
+ return(p->features);
+}
+
+/*******************************************************************************
+**
+** Function BTM_RegBusyLevelNotif
+**
+** Description This function is called to register a callback to receive
+** busy level change events.
+**
+** Returns BTM_SUCCESS if successfully registered, otherwise error
+**
+*******************************************************************************/
+#if (defined(BTM_BUSY_LEVEL_CHANGE_INCLUDED) && BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE)
+tBTM_STATUS BTM_RegBusyLevelNotif (tBTM_BL_CHANGE_CB *p_cb, UINT8 *p_level,
+ tBTM_BL_EVENT_MASK evt_mask)
+{
+ BTM_TRACE_DEBUG0 ("BTM_RegBusyLevelNotif");
+ if (p_level)
+ *p_level = btm_cb.busy_level;
+
+ btm_cb.bl_evt_mask = evt_mask;
+
+ if (!p_cb)
+ btm_cb.p_bl_changed_cb = NULL;
+ else if (btm_cb.p_bl_changed_cb)
+ return(BTM_BUSY);
+ else
+ btm_cb.p_bl_changed_cb = p_cb;
+
+ return(BTM_SUCCESS);
+}
+#else
+/*******************************************************************************
+**
+** Function BTM_AclRegisterForChanges
+**
+** Returns This function is called to register a callback for when the
+** ACL database changes, i.e. new entry or entry deleted.
+**
+*******************************************************************************/
+tBTM_STATUS BTM_AclRegisterForChanges (tBTM_ACL_DB_CHANGE_CB *p_cb)
+{
+ BTM_TRACE_DEBUG0 ("BTM_AclRegisterForChanges");
+ if (!p_cb)
+ btm_cb.p_acl_changed_cb = NULL;
+ else if (btm_cb.p_acl_changed_cb)
+ return(BTM_BUSY);
+ else
+ btm_cb.p_acl_changed_cb = p_cb;
+
+ return(BTM_SUCCESS);
+}
+#endif
+
+/*******************************************************************************
+**
+** Function BTM_SetQoS
+**
+** Description This function is called to setup QoS
+**
+** Returns status of the operation
+**
+*******************************************************************************/
+tBTM_STATUS BTM_SetQoS (BD_ADDR bd, FLOW_SPEC *p_flow, tBTM_CMPL_CB *p_cb)
+{
+ tACL_CONN *p = &btm_cb.acl_db[0];
+
+ BTM_TRACE_API6 ("BTM_SetQoS: BdAddr: %02x%02x%02x%02x%02x%02x",
+ bd[0], bd[1], bd[2],
+ bd[3], bd[4], bd[5]);
+
+ /* If someone already waiting on the version, do not allow another */
+ if (btm_cb.devcb.p_qossu_cmpl_cb)
+ return(BTM_BUSY);
+
+ if ( (p = btm_bda_to_acl(bd)) != NULL)
+ {
+ btu_start_timer (&btm_cb.devcb.qossu_timer, BTU_TTYPE_BTM_ACL, BTM_DEV_REPLY_TIMEOUT);
+ btm_cb.devcb.p_qossu_cmpl_cb = p_cb;
+
+ if (!btsnd_hcic_qos_setup (p->hci_handle, p_flow->qos_flags, p_flow->service_type,
+ p_flow->token_rate, p_flow->peak_bandwidth, p_flow->latency,p_flow->delay_variation))
+ {
+ btm_cb.devcb.p_qossu_cmpl_cb = NULL;
+ btu_stop_timer(&btm_cb.devcb.qossu_timer);
+ return(BTM_NO_RESOURCES);
+ }
+ else
+ return(BTM_CMD_STARTED);
+ }
+
+ /* If here, no BD Addr found */
+ return(BTM_UNKNOWN_ADDR);
+}
+
+/*******************************************************************************
+**
+** Function btm_qos_setup_complete
+**
+** Description This function is called when the command complete message
+** is received from the HCI for the qos setup request.
+**
+** Returns void
+**
+*******************************************************************************/
+void btm_qos_setup_complete (UINT8 status, UINT16 handle, FLOW_SPEC *p_flow)
+{
+ tBTM_CMPL_CB *p_cb = btm_cb.devcb.p_qossu_cmpl_cb;
+ tBTM_QOS_SETUP_CMPL qossu;
+ BTM_TRACE_DEBUG0 ("btm_qos_setup_complete");
+ btu_stop_timer (&btm_cb.devcb.qossu_timer);
+
+ btm_cb.devcb.p_qossu_cmpl_cb = NULL;
+
+ if (p_cb)
+ {
+ memset(&qossu, 0, sizeof(tBTM_QOS_SETUP_CMPL));
+ qossu.status = status;
+ qossu.handle = handle;
+ if (p_flow != NULL)
+ {
+ qossu.flow.qos_flags = p_flow->qos_flags;
+ qossu.flow.service_type = p_flow->service_type;
+ qossu.flow.token_rate = p_flow->token_rate;
+ qossu.flow.peak_bandwidth = p_flow->peak_bandwidth;
+ qossu.flow.latency = p_flow->latency;
+ qossu.flow.delay_variation = p_flow->delay_variation;
+ }
+ BTM_TRACE_DEBUG1 ("BTM: p_flow->delay_variation: 0x%02x",
+ qossu.flow.delay_variation);
+ (*p_cb)(&qossu);
+ }
+}
+
+
+/*******************************************************************************
+**
+** Function BTM_ReadRSSI
+**
+** Description This function is called to read the link policy settings.
+** The address of link policy results are returned in the callback.
+** (tBTM_RSSI_RESULTS)
+**
+** Returns BTM_CMD_STARTED if successfully initiated or error code
+**
+*******************************************************************************/
+tBTM_STATUS BTM_ReadRSSI (BD_ADDR remote_bda, tBTM_CMPL_CB *p_cb)
+{
+ tACL_CONN *p;
+
+ BTM_TRACE_API6 ("BTM_ReadRSSI: RemBdAddr: %02x%02x%02x%02x%02x%02x",
+ remote_bda[0], remote_bda[1], remote_bda[2],
+ remote_bda[3], remote_bda[4], remote_bda[5]);
+
+ /* If someone already waiting on the version, do not allow another */
+ if (btm_cb.devcb.p_rssi_cmpl_cb)
+ return(BTM_BUSY);
+
+ p = btm_bda_to_acl(remote_bda);
+ if (p != (tACL_CONN *)NULL)
+ {
+ btu_start_timer (&btm_cb.devcb.rssi_timer, BTU_TTYPE_BTM_ACL,
+ BTM_DEV_REPLY_TIMEOUT);
+
+ btm_cb.devcb.p_rssi_cmpl_cb = p_cb;
+
+ if (!btsnd_hcic_read_rssi (p->hci_handle))
+ {
+ btm_cb.devcb.p_rssi_cmpl_cb = NULL;
+ btu_stop_timer (&btm_cb.devcb.rssi_timer);
+ return(BTM_NO_RESOURCES);
+ }
+ else
+ return(BTM_CMD_STARTED);
+ }
+
+ /* If here, no BD Addr found */
+ return(BTM_UNKNOWN_ADDR);
+}
+
+/*******************************************************************************
+**
+** Function BTM_ReadLinkQuality
+**
+** Description This function is called to read the link qulaity.
+** The value of the link quality is returned in the callback.
+** (tBTM_LINK_QUALITY_RESULTS)
+**
+** Returns BTM_CMD_STARTED if successfully initiated or error code
+**
+*******************************************************************************/
+tBTM_STATUS BTM_ReadLinkQuality (BD_ADDR remote_bda, tBTM_CMPL_CB *p_cb)
+{
+ tACL_CONN *p;
+
+ BTM_TRACE_API6 ("BTM_ReadLinkQuality: RemBdAddr: %02x%02x%02x%02x%02x%02x",
+ remote_bda[0], remote_bda[1], remote_bda[2],
+ remote_bda[3], remote_bda[4], remote_bda[5]);
+
+ /* If someone already waiting on the version, do not allow another */
+ if (btm_cb.devcb.p_lnk_qual_cmpl_cb)
+ return(BTM_BUSY);
+
+ p = btm_bda_to_acl(remote_bda);
+ if (p != (tACL_CONN *)NULL)
+ {
+ btu_start_timer (&btm_cb.devcb.lnk_quality_timer, BTU_TTYPE_BTM_ACL,
+ BTM_DEV_REPLY_TIMEOUT);
+ btm_cb.devcb.p_lnk_qual_cmpl_cb = p_cb;
+
+ if (!btsnd_hcic_get_link_quality (p->hci_handle))
+ {
+ btu_stop_timer (&btm_cb.devcb.lnk_quality_timer);
+ btm_cb.devcb.p_lnk_qual_cmpl_cb = NULL;
+ return(BTM_NO_RESOURCES);
+ }
+ else
+ return(BTM_CMD_STARTED);
+ }
+
+ /* If here, no BD Addr found */
+ return(BTM_UNKNOWN_ADDR);
+}
+
+/*******************************************************************************
+**
+** Function BTM_ReadTxPower
+**
+** Description This function is called to read the current
+** TX power of the connection. The tx power level results
+** are returned in the callback.
+** (tBTM_RSSI_RESULTS)
+**
+** Returns BTM_CMD_STARTED if successfully initiated or error code
+**
+*******************************************************************************/
+tBTM_STATUS BTM_ReadTxPower (BD_ADDR remote_bda, tBTM_CMPL_CB *p_cb)
+{
+ tACL_CONN *p;
+ BOOLEAN ret;
+#define BTM_READ_RSSI_TYPE_CUR 0x00
+#define BTM_READ_RSSI_TYPE_MAX 0X01
+
+ BTM_TRACE_API6 ("BTM_ReadTxPower: RemBdAddr: %02x%02x%02x%02x%02x%02x",
+ remote_bda[0], remote_bda[1], remote_bda[2],
+ remote_bda[3], remote_bda[4], remote_bda[5]);
+
+ /* If someone already waiting on the version, do not allow another */
+ if (btm_cb.devcb.p_tx_power_cmpl_cb)
+ return(BTM_BUSY);
+
+ p = btm_bda_to_acl(remote_bda);
+ if (p != (tACL_CONN *)NULL)
+ {
+ btu_start_timer (&btm_cb.devcb.tx_power_timer, BTU_TTYPE_BTM_ACL,
+ BTM_DEV_REPLY_TIMEOUT);
+
+ btm_cb.devcb.p_tx_power_cmpl_cb = p_cb;
+
+#if BLE_INCLUDED == TRUE
+ if (p->is_le_link)
+ {
+ memcpy(btm_cb.devcb.read_tx_pwr_addr, remote_bda, BD_ADDR_LEN);
+ ret = btsnd_hcic_ble_read_adv_chnl_tx_power();
+ }
+ else
+#endif
+ {
+ ret = btsnd_hcic_read_tx_power (p->hci_handle, BTM_READ_RSSI_TYPE_CUR);
+ }
+ if (!ret)
+ {
+ btm_cb.devcb.p_tx_power_cmpl_cb = NULL;
+ btu_stop_timer (&btm_cb.devcb.tx_power_timer);
+ return(BTM_NO_RESOURCES);
+ }
+ else
+ return(BTM_CMD_STARTED);
+ }
+
+ /* If here, no BD Addr found */
+ return (BTM_UNKNOWN_ADDR);
+}
+/*******************************************************************************
+**
+** Function btm_read_tx_power_complete
+**
+** Description This function is called when the command complete message
+** is received from the HCI for the read tx power request.
+**
+** Returns void
+**
+*******************************************************************************/
+void btm_read_tx_power_complete (UINT8 *p, BOOLEAN is_ble)
+{
+ tBTM_CMPL_CB *p_cb = btm_cb.devcb.p_tx_power_cmpl_cb;
+ tBTM_TX_POWER_RESULTS results;
+ UINT16 handle;
+ tACL_CONN *p_acl_cb = &btm_cb.acl_db[0];
+ UINT16 index;
+ BTM_TRACE_DEBUG0 ("btm_read_tx_power_complete");
+ btu_stop_timer (&btm_cb.devcb.tx_power_timer);
+
+ /* If there was a callback registered for read rssi, call it */
+ btm_cb.devcb.p_tx_power_cmpl_cb = NULL;
+
+ if (p_cb)
+ {
+ STREAM_TO_UINT8 (results.hci_status, p);
+
+ if (results.hci_status == HCI_SUCCESS)
+ {
+ results.status = BTM_SUCCESS;
+
+ if (!is_ble)
+ {
+ STREAM_TO_UINT16 (handle, p);
+ STREAM_TO_UINT8 (results.tx_power, p);
+
+ /* Search through the list of active channels for the correct BD Addr */
+ for (index = 0; index < MAX_L2CAP_LINKS; index++, p_acl_cb++)
+ {
+ if ((p_acl_cb->in_use) && (handle == p_acl_cb->hci_handle))
+ {
+ memcpy (results.rem_bda, p_acl_cb->remote_addr, BD_ADDR_LEN);
+ break;
+ }
+ }
+ }
+#if BLE_INCLUDED == TRUE
+ else
+ {
+ STREAM_TO_UINT8 (results.tx_power, p);
+ memcpy(results.rem_bda, btm_cb.devcb.read_tx_pwr_addr, BD_ADDR_LEN);
+ }
+#endif
+ BTM_TRACE_DEBUG2 ("BTM TX power Complete: tx_power %d, hci status 0x%02x",
+ results.tx_power, results.hci_status);
+ }
+ else
+ results.status = BTM_ERR_PROCESSING;
+
+ (*p_cb)(&results);
+ }
+}
+
+/*******************************************************************************
+**
+** Function btm_read_rssi_complete
+**
+** Description This function is called when the command complete message
+** is received from the HCI for the read rssi request.
+**
+** Returns void
+**
+*******************************************************************************/
+void btm_read_rssi_complete (UINT8 *p)
+{
+ tBTM_CMPL_CB *p_cb = btm_cb.devcb.p_rssi_cmpl_cb;
+ tBTM_RSSI_RESULTS results;
+ UINT16 handle;
+ tACL_CONN *p_acl_cb = &btm_cb.acl_db[0];
+ UINT16 index;
+ BTM_TRACE_DEBUG0 ("btm_read_rssi_complete");
+ btu_stop_timer (&btm_cb.devcb.rssi_timer);
+
+ /* If there was a callback registered for read rssi, call it */
+ btm_cb.devcb.p_rssi_cmpl_cb = NULL;
+
+ if (p_cb)
+ {
+ STREAM_TO_UINT8 (results.hci_status, p);
+
+ if (results.hci_status == HCI_SUCCESS)
+ {
+ results.status = BTM_SUCCESS;
+
+ STREAM_TO_UINT16 (handle, p);
+
+ STREAM_TO_UINT8 (results.rssi, p);
+ BTM_TRACE_DEBUG2 ("BTM RSSI Complete: rssi %d, hci status 0x%02x",
+ results.rssi, results.hci_status);
+
+ /* Search through the list of active channels for the correct BD Addr */
+ for (index = 0; index < MAX_L2CAP_LINKS; index++, p_acl_cb++)
+ {
+ if ((p_acl_cb->in_use) && (handle == p_acl_cb->hci_handle))
+ {
+ memcpy (results.rem_bda, p_acl_cb->remote_addr, BD_ADDR_LEN);
+ break;
+ }
+ }
+ }
+ else
+ results.status = BTM_ERR_PROCESSING;
+
+ (*p_cb)(&results);
+ }
+}
+
+/*******************************************************************************
+**
+** Function btm_read_link_quality_complete
+**
+** Description This function is called when the command complete message
+** is received from the HCI for the read link quality.
+**
+** Returns void
+**
+*******************************************************************************/
+void btm_read_link_quality_complete (UINT8 *p)
+{
+ tBTM_CMPL_CB *p_cb = btm_cb.devcb.p_lnk_qual_cmpl_cb;
+ tBTM_LINK_QUALITY_RESULTS results;
+ UINT16 handle;
+ tACL_CONN *p_acl_cb = &btm_cb.acl_db[0];
+ UINT16 index;
+ BTM_TRACE_DEBUG0 ("btm_read_link_quality_complete");
+ btu_stop_timer (&btm_cb.devcb.rssi_timer);
+
+ /* If there was a callback registered for read rssi, call it */
+ btm_cb.devcb.p_lnk_qual_cmpl_cb = NULL;
+
+ if (p_cb)
+ {
+ STREAM_TO_UINT8 (results.hci_status, p);
+
+ if (results.hci_status == HCI_SUCCESS)
+ {
+ results.status = BTM_SUCCESS;
+
+ STREAM_TO_UINT16 (handle, p);
+
+ STREAM_TO_UINT8 (results.link_quality, p);
+ BTM_TRACE_DEBUG2 ("BTM Link Quality Complete: Link Quality %d, hci status 0x%02x",
+ results.link_quality, results.hci_status);
+
+ /* Search through the list of active channels for the correct BD Addr */
+ for (index = 0; index < MAX_L2CAP_LINKS; index++, p_acl_cb++)
+ {
+ if ((p_acl_cb->in_use) && (handle == p_acl_cb->hci_handle))
+ {
+ memcpy (results.rem_bda, p_acl_cb->remote_addr, BD_ADDR_LEN);
+ break;
+ }
+ }
+ }
+ else
+ results.status = BTM_ERR_PROCESSING;
+
+ (*p_cb)(&results);
+ }
+}
+
+/*******************************************************************************
+**
+** Function btm_remove_acl
+**
+** Description This function is called to disconnect an ACL connection
+**
+** Returns BTM_SUCCESS if successfully initiated, otherwise BTM_NO_RESOURCES.
+**
+*******************************************************************************/
+tBTM_STATUS btm_remove_acl (BD_ADDR bd_addr)
+{
+ UINT16 hci_handle = BTM_GetHCIConnHandle(bd_addr);
+ tBTM_STATUS status = BTM_SUCCESS;
+
+ BTM_TRACE_DEBUG0 ("btm_remove_acl");
+#if BTM_DISC_DURING_RS == TRUE
+ tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bd_addr);
+
+ /* Role Switch is pending, postpone until completed */
+ if (p_dev_rec && (p_dev_rec->rs_disc_pending == BTM_SEC_RS_PENDING))
+ {
+ p_dev_rec->rs_disc_pending = BTM_SEC_DISC_PENDING;
+ }
+ else /* otherwise can disconnect right away */
+#endif
+
+ if (hci_handle != 0xFFFF)
+ {
+ if (!btsnd_hcic_disconnect (hci_handle, HCI_ERR_PEER_USER))
+ status = BTM_NO_RESOURCES;
+ }
+ else
+ status = BTM_UNKNOWN_ADDR;
+
+ return status;
+}
+
+
+/*******************************************************************************
+**
+** Function BTM_SetTraceLevel
+**
+** Description This function sets the trace level for BTM. If called with
+** a value of 0xFF, it simply returns the current trace level.
+**
+** Returns The new or current trace level
+**
+*******************************************************************************/
+UINT8 BTM_SetTraceLevel (UINT8 new_level)
+{
+ BTM_TRACE_DEBUG0 ("BTM_SetTraceLevel");
+ if (new_level != 0xFF)
+ btm_cb.trace_level = new_level;
+
+ return(btm_cb.trace_level);
+}
+
+/*******************************************************************************
+**
+** Function btm_cont_rswitch_or_chglinkkey
+**
+** Description This function is called to continue processing an active
+** role switch or change of link key procedure. It first
+** disables encryption if enabled and EPR is not supported
+**
+** Returns void
+**
+*******************************************************************************/
+void btm_cont_rswitch_or_chglinkkey (tACL_CONN *p, tBTM_SEC_DEV_REC *p_dev_rec,
+ UINT8 hci_status)
+{
+ BOOLEAN sw_ok = TRUE;
+ BOOLEAN chlk_ok = TRUE;
+ BTM_TRACE_DEBUG0 ("btm_cont_rswitch_or_chglinkkey ");
+ /* Check to see if encryption needs to be turned off if pending
+ change of link key or role switch */
+ if (p->switch_role_state == BTM_ACL_SWKEY_STATE_MODE_CHANGE ||
+ p->change_key_state == BTM_ACL_SWKEY_STATE_MODE_CHANGE)
+ {
+ /* Must turn off Encryption first if necessary */
+ /* Some devices do not support switch or change of link key while encryption is on */
+ if (p_dev_rec != NULL && ((p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED) != 0)
+ && !BTM_EPR_AVAILABLE(p))
+ {
+ if (btsnd_hcic_set_conn_encrypt (p->hci_handle, FALSE))
+ {
+ p->encrypt_state = BTM_ACL_ENCRYPT_STATE_ENCRYPT_OFF;
+ if (p->switch_role_state == BTM_ACL_SWKEY_STATE_MODE_CHANGE)
+ p->switch_role_state = BTM_ACL_SWKEY_STATE_ENCRYPTION_OFF;
+
+ if (p->change_key_state == BTM_ACL_SWKEY_STATE_MODE_CHANGE)
+ p->change_key_state = BTM_ACL_SWKEY_STATE_ENCRYPTION_OFF;
+ }
+ else
+ {
+ /* Error occurred; set states back to Idle */
+ if (p->switch_role_state == BTM_ACL_SWKEY_STATE_MODE_CHANGE)
+ sw_ok = FALSE;
+
+ if (p->change_key_state == BTM_ACL_SWKEY_STATE_MODE_CHANGE)
+ chlk_ok = FALSE;
+ }
+ }
+ else /* Encryption not used or EPR supported, continue with switch
+ and/or change of link key */
+ {
+ if (p->switch_role_state == BTM_ACL_SWKEY_STATE_MODE_CHANGE)
+ {
+ p->switch_role_state = BTM_ACL_SWKEY_STATE_IN_PROGRESS;
+#if BTM_DISC_DURING_RS == TRUE
+ if (p_dev_rec)
+ p_dev_rec->rs_disc_pending = BTM_SEC_RS_PENDING;
+#endif
+ sw_ok = btsnd_hcic_switch_role (p->remote_addr, (UINT8)!p->link_role);
+ }
+
+ if (p->change_key_state == BTM_ACL_SWKEY_STATE_MODE_CHANGE)
+ {
+ p->switch_role_state = BTM_ACL_SWKEY_STATE_IN_PROGRESS;
+ chlk_ok = btsnd_hcic_change_link_key (p->hci_handle);
+ }
+ }
+
+ if (!sw_ok)
+ {
+ p->switch_role_state = BTM_ACL_SWKEY_STATE_IDLE;
+ btm_acl_report_role_change(hci_status, p->remote_addr);
+ }
+
+ if (!chlk_ok)
+ {
+ p->change_key_state = BTM_ACL_SWKEY_STATE_IDLE;
+ if (btm_cb.devcb.p_chg_link_key_cb)
+ {
+ btm_cb.devcb.chg_link_key_ref_data.hci_status = hci_status;
+ (*btm_cb.devcb.p_chg_link_key_cb)(&btm_cb.devcb.chg_link_key_ref_data);
+ btm_cb.devcb.p_chg_link_key_cb = NULL;
+ }
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function btm_acl_resubmit_page
+**
+** Description send pending page request
+**
+*******************************************************************************/
+void btm_acl_resubmit_page (void)
+{
+ tBTM_SEC_DEV_REC *p_dev_rec;
+ BT_HDR *p_buf;
+ UINT8 *pp;
+ BD_ADDR bda;
+ BTM_TRACE_DEBUG0 ("btm_acl_resubmit_page");
+ /* If there were other page request schedule can start the next one */
+ if ((p_buf = (BT_HDR *)GKI_dequeue (&btm_cb.page_queue)) != NULL)
+ {
+ /* skip 3 (2 bytes opcode and 1 byte len) to get to the bd_addr
+ * for both create_conn and rmt_name */
+ pp = (UINT8 *)(p_buf + 1) + p_buf->offset + 3;
+
+ STREAM_TO_BDADDR (bda, pp);
+
+ p_dev_rec = btm_find_or_alloc_dev (bda);
+
+ memcpy (btm_cb.connecting_bda, p_dev_rec->bd_addr, BD_ADDR_LEN);
+ memcpy (btm_cb.connecting_dc, p_dev_rec->dev_class, DEV_CLASS_LEN);
+
+ btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p_buf);
+ }
+ else
+ btm_cb.paging = FALSE;
+}
+
+/*******************************************************************************
+**
+** Function btm_acl_reset_paging
+**
+** Description set paging to FALSE and free the page queue - called at hci_reset
+**
+*******************************************************************************/
+void btm_acl_reset_paging (void)
+{
+ BT_HDR *p;
+ BTM_TRACE_DEBUG0 ("btm_acl_reset_paging");
+ /* If we sent reset we are definitely not paging any more */
+ while ((p = (BT_HDR *)GKI_dequeue(&btm_cb.page_queue)) != NULL)
+ GKI_freebuf (p);
+
+ btm_cb.paging = FALSE;
+}
+
+/*******************************************************************************
+**
+** Function btm_acl_set_discing
+**
+** Description set discing to the given value
+**
+*******************************************************************************/
+void btm_acl_set_discing (BOOLEAN discing)
+{
+ BTM_TRACE_DEBUG0 ("btm_acl_set_discing");
+ btm_cb.discing = discing;
+}
+
+/*******************************************************************************
+**
+** Function btm_acl_paging
+**
+** Description send a paging command or queue it in btm_cb
+**
+*******************************************************************************/
+void btm_acl_paging (BT_HDR *p, BD_ADDR bda)
+{
+ tBTM_SEC_DEV_REC *p_dev_rec;
+
+ BTM_TRACE_DEBUG4 ("btm_acl_paging discing:%d, paging:%d BDA: %06x%06x",
+ btm_cb.discing, btm_cb.paging,
+ (bda[0]<<16) + (bda[1]<<8) + bda[2], (bda[3]<<16) + (bda[4] << 8) + bda[5]);
+ if (btm_cb.discing)
+ {
+ btm_cb.paging = TRUE;
+ GKI_enqueue (&btm_cb.page_queue, p);
+ }
+ else
+ {
+ if (!BTM_ACL_IS_CONNECTED (bda))
+ {
+ BTM_TRACE_DEBUG2 ("connecting_bda: %06x%06x",
+ (btm_cb.connecting_bda[0]<<16) + (btm_cb.connecting_bda[1]<<8) + btm_cb.connecting_bda[2],
+ (btm_cb.connecting_bda[3]<<16) + (btm_cb.connecting_bda[4] << 8) + btm_cb.connecting_bda[5]);
+ if (btm_cb.paging &&
+ memcmp (bda, btm_cb.connecting_bda, BD_ADDR_LEN) != 0)
+ {
+ GKI_enqueue (&btm_cb.page_queue, p);
+ }
+ else
+ {
+ p_dev_rec = btm_find_or_alloc_dev (bda);
+ memcpy (btm_cb.connecting_bda, p_dev_rec->bd_addr, BD_ADDR_LEN);
+ memcpy (btm_cb.connecting_dc, p_dev_rec->dev_class, DEV_CLASS_LEN);
+
+ btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p);
+ }
+
+ btm_cb.paging = TRUE;
+ }
+ else /* ACL is already up */
+ {
+ btu_hcif_send_cmd (LOCAL_BR_EDR_CONTROLLER_ID, p);
+ }
+ }
+}
+
+/*******************************************************************************
+**
+** Function btm_acl_notif_conn_collision
+**
+** Description Send connection collision event to upper layer if registered
+**
+** Returns TRUE if sent out to upper layer,
+** FALSE if BTM_BUSY_LEVEL_CHANGE_INCLUDED == FALSE, or no one
+** needs the notification.
+**
+** Note: Function only used if BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE
+**
+*******************************************************************************/
+BOOLEAN btm_acl_notif_conn_collision (BD_ADDR bda)
+{
+#if (BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE)
+ tBTM_BL_EVENT_DATA evt_data;
+
+ /* Report possible collision to the upper layer. */
+ if (btm_cb.p_bl_changed_cb)
+ {
+ BTM_TRACE_DEBUG6 ("btm_acl_notif_conn_collision: RemBdAddr: %02x%02x%02x%02x%02x%02x",
+ bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]);
+
+ evt_data.event = BTM_BL_COLLISION_EVT;
+ evt_data.conn.p_bda = bda;
+ (*btm_cb.p_bl_changed_cb)(&evt_data);
+ return TRUE;
+ }
+ else
+ return FALSE;
+#else
+ return FALSE;
+#endif
+}
+
+
+/*******************************************************************************
+**
+** Function btm_acl_chk_peer_pkt_type_support
+**
+** Description Check if peer supports requested packets
+**
+*******************************************************************************/
+void btm_acl_chk_peer_pkt_type_support (tACL_CONN *p, UINT16 *p_pkt_type)
+{
+ /* 3 and 5 slot packets? */
+ if (!HCI_3_SLOT_PACKETS_SUPPORTED(p->features))
+ *p_pkt_type &= ~(BTM_ACL_PKT_TYPES_MASK_DH3 +BTM_ACL_PKT_TYPES_MASK_DM3);
+
+ if (!HCI_5_SLOT_PACKETS_SUPPORTED(p->features))
+ *p_pkt_type &= ~(BTM_ACL_PKT_TYPES_MASK_DH5 + BTM_ACL_PKT_TYPES_MASK_DM5);
+
+ /* If HCI version > 2.0, then also check EDR packet types */
+ if (btm_cb.devcb.local_version.hci_version >= HCI_PROTO_VERSION_2_0)
+ {
+ /* 2 and 3 MPS support? */
+ if (!HCI_EDR_ACL_2MPS_SUPPORTED(p->features))
+ /* Not supported. Add 'not_supported' mask for all 2MPS packet types */
+ *p_pkt_type |= (BTM_ACL_PKT_TYPES_MASK_NO_2_DH1 + BTM_ACL_PKT_TYPES_MASK_NO_2_DH3 + BTM_ACL_PKT_TYPES_MASK_NO_2_DH5);
+
+ if (!HCI_EDR_ACL_3MPS_SUPPORTED(p->features))
+ /* Not supported. Add 'not_supported' mask for all 3MPS packet types */
+ *p_pkt_type |= (BTM_ACL_PKT_TYPES_MASK_NO_3_DH1 + BTM_ACL_PKT_TYPES_MASK_NO_3_DH3 + BTM_ACL_PKT_TYPES_MASK_NO_3_DH5);
+
+ /* EDR 3 and 5 slot support? */
+ if (HCI_EDR_ACL_2MPS_SUPPORTED(p->features) || HCI_EDR_ACL_3MPS_SUPPORTED(p->features))
+ {
+ if (!HCI_3_SLOT_EDR_ACL_SUPPORTED(p->features))
+ /* Not supported. Add 'not_supported' mask for all 3-slot EDR packet types */
+ *p_pkt_type |= (BTM_ACL_PKT_TYPES_MASK_NO_2_DH3 + BTM_ACL_PKT_TYPES_MASK_NO_3_DH3);
+
+ if (!HCI_5_SLOT_EDR_ACL_SUPPORTED(p->features))
+ /* Not supported. Add 'not_supported' mask for all 5-slot EDR packet types */
+ *p_pkt_type |= (BTM_ACL_PKT_TYPES_MASK_NO_2_DH5 + BTM_ACL_PKT_TYPES_MASK_NO_3_DH5);
+ }
+ }
+}