diff options
author | Andre Eisenbach <andre@broadcom.com> | 2012-02-22 13:18:21 -0800 |
---|---|---|
committer | Matthew Xie <mattx@google.com> | 2012-07-14 11:19:11 -0700 |
commit | e448862a47c08eb23185aaed574b39264f5005fc (patch) | |
tree | 2bc6246e3091315e77224fd798ea2fe8074ef972 /stack/btm | |
parent | a2ca4b83ab8bbbfd8d5f6693e927ed4b82094624 (diff) | |
download | external_bluetooth_bluedroid-e448862a47c08eb23185aaed574b39264f5005fc.zip external_bluetooth_bluedroid-e448862a47c08eb23185aaed574b39264f5005fc.tar.gz external_bluetooth_bluedroid-e448862a47c08eb23185aaed574b39264f5005fc.tar.bz2 |
Initial Bluedroid stack commit
Diffstat (limited to 'stack/btm')
-rw-r--r-- | stack/btm/btm_acl.c | 3126 | ||||
-rw-r--r-- | stack/btm/btm_ble.c | 1888 | ||||
-rw-r--r-- | stack/btm/btm_ble_addr.c | 371 | ||||
-rw-r--r-- | stack/btm/btm_ble_bgconn.c | 604 | ||||
-rw-r--r-- | stack/btm/btm_ble_gap.c | 2072 | ||||
-rw-r--r-- | stack/btm/btm_ble_int.h | 285 | ||||
-rw-r--r-- | stack/btm/btm_dev.c | 451 | ||||
-rw-r--r-- | stack/btm/btm_devctl.c | 1935 | ||||
-rw-r--r-- | stack/btm/btm_inq.c | 3233 | ||||
-rw-r--r-- | stack/btm/btm_int.h | 1099 | ||||
-rw-r--r-- | stack/btm/btm_main.c | 59 | ||||
-rw-r--r-- | stack/btm/btm_pm.c | 985 | ||||
-rw-r--r-- | stack/btm/btm_sco.c | 1741 | ||||
-rw-r--r-- | stack/btm/btm_sec.c | 5612 |
14 files changed, 23461 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); + } + } +} diff --git a/stack/btm/btm_ble.c b/stack/btm/btm_ble.c new file mode 100644 index 0000000..e34b8e2 --- /dev/null +++ b/stack/btm/btm_ble.c @@ -0,0 +1,1888 @@ +/***************************************************************************** +** +** Name: btm_ble.c +** +** Description: This file contains functions for BLE device control utilities, +** and LE security functions. +** +** +** +** Copyright (c) 1999-2011, Broadcom Corp., All Rights Reserved. +** Broadcom Bluetooth Core. Proprietary and confidential. +******************************************************************************/ + +#include <string.h> + +#include "bt_types.h" +#include "hcimsgs.h" +#include "btu.h" +#include "btm_int.h" +#include "btm_ble_api.h" +#include "smp_api.h" + +#if SMP_INCLUDED == TRUE +extern BOOLEAN AES_CMAC ( BT_OCTET16 key, UINT8 *input, UINT16 length, UINT16 tlen, UINT8 *p_signature); +extern void smp_link_encrypted(BD_ADDR bda, UINT8 encr_enable); +extern BOOLEAN smp_proc_ltk_request(BD_ADDR bda); +#endif + +static void btm_ble_update_active_bgconn_scan_params(void); + +/*******************************************************************************/ +/* External Function to be called by other modules */ +/*******************************************************************************/ +/******************************************************** +** +** Function BTM_SecAddBleDevice +** +** Description Add/modify device. This function will be normally called +** during host startup to restore all required information +** for a LE device stored in the NVRAM. +** +** Parameters: bd_addr - BD address of the peer +** bd_name - Name of the peer device. NULL if unknown. +** dev_type - Remote device's device type. +** addr_type - LE device address type. +** +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ +BOOLEAN BTM_SecAddBleDevice (BD_ADDR bd_addr, BD_NAME bd_name, tBT_DEVICE_TYPE dev_type, + tBLE_ADDR_TYPE addr_type) +{ +#if BLE_INCLUDED == TRUE + tBTM_SEC_DEV_REC *p_dev_rec; + UINT8 i = 0; + tBTM_INQ_INFO *p_info=NULL; + + BTM_TRACE_DEBUG1 ("BTM_SecAddBleDevice dev_type=0x%x", dev_type); + p_dev_rec = btm_find_dev (bd_addr); + + if (!p_dev_rec) + { + BTM_TRACE_DEBUG0("Add a new device"); + + /* There is no device record, allocate one. + * If we can not find an empty spot for this one, let it fail. */ + for (i = 0; i < BTM_SEC_MAX_DEVICE_RECORDS; i++) + { + if (!(btm_cb.sec_dev_rec[i].sec_flags & BTM_SEC_IN_USE)) + { + BTM_TRACE_DEBUG1 ("allocate a new dev rec idx=0x%x ", i ); + p_dev_rec = &btm_cb.sec_dev_rec[i]; + + /* Mark this record as in use and initialize */ + memset (p_dev_rec, 0, sizeof (tBTM_SEC_DEV_REC)); + p_dev_rec->sec_flags = BTM_SEC_IN_USE; + memcpy (p_dev_rec->bd_addr, bd_addr, BD_ADDR_LEN); + p_dev_rec->hci_handle = BTM_GetHCIConnHandle (bd_addr); + + /* update conn params, use default value for background connection params */ + p_dev_rec->conn_params.min_conn_int = + p_dev_rec->conn_params.max_conn_int = + p_dev_rec->conn_params.supervision_tout = + p_dev_rec->conn_params.slave_latency = BTM_BLE_CONN_PARAM_UNDEF; + + BTM_TRACE_DEBUG1 ("hci_handl=0x%x ", p_dev_rec->hci_handle ); + break; + } + } + + if (!p_dev_rec) + return(FALSE); + } + else + { + BTM_TRACE_DEBUG0("Device already exist"); + } + + memset(p_dev_rec->sec_bd_name, 0, sizeof(tBTM_BD_NAME)); + + if (bd_name && bd_name[0]) + { + p_dev_rec->sec_flags |= BTM_SEC_NAME_KNOWN; + BCM_STRNCPY_S ((char *)p_dev_rec->sec_bd_name, sizeof (p_dev_rec->sec_bd_name), + (char *)bd_name, BTM_MAX_REM_BD_NAME_LEN); + } + p_dev_rec->device_type = dev_type; + p_dev_rec->ble.ble_addr_type = addr_type; + BTM_TRACE_DEBUG3 ("p_dev_rec->device_type =0x%x addr_type=0x%x sec_flags=0x%x", + dev_type, addr_type, p_dev_rec->sec_flags); + + /* sync up with the Inq Data base*/ + p_info = BTM_InqDbRead(bd_addr); + if (p_info) + { + p_info->results.ble_addr_type = p_dev_rec->ble.ble_addr_type ; + p_info->results.device_type = p_dev_rec->device_type; + BTM_TRACE_DEBUG2 ("InqDb device_type =0x%x addr_type=0x%x", + p_info->results.device_type, p_info->results.ble_addr_type); + } + +#endif + return(TRUE); +} + +/******************************************************************************* +** +** Function BTM_SecAddBleKey +** +** Description Add/modify LE device information. This function will be +** normally called during host startup to restore all required +** information stored in the NVRAM. +** +** Parameters: bd_addr - BD address of the peer +** p_le_key - LE key values. +** key_type - LE SMP key type. +* +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ +BOOLEAN BTM_SecAddBleKey (BD_ADDR bd_addr, tBTM_LE_KEY_VALUE *p_le_key, tBTM_LE_KEY_TYPE key_type) +{ +#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE) + tBTM_SEC_DEV_REC *p_dev_rec; + BTM_TRACE_DEBUG0 ("BTM_SecAddBleKey"); + p_dev_rec = btm_find_dev (bd_addr); + if (!p_dev_rec || !p_le_key || + (key_type != BTM_LE_KEY_PENC && key_type != BTM_LE_KEY_PID && + key_type != BTM_LE_KEY_PCSRK && key_type != BTM_LE_KEY_LENC)) + { + BTM_TRACE_WARNING3 ("BTM_SecAddLeKey() No BT Link Key, Wrong Type, or No Device record \ + for bdaddr: %08x%04x, Type: %d", + (bd_addr[0]<<24)+(bd_addr[1]<<16)+(bd_addr[2]<<8)+bd_addr[3], + (bd_addr[4]<<8)+bd_addr[5], key_type); + return(FALSE); + } + + BTM_TRACE_DEBUG3 ("BTM_SecAddLeKey() BDA: %08x%04x, Type: 0x%02x", + (bd_addr[0]<<24)+(bd_addr[1]<<16)+(bd_addr[2]<<8)+bd_addr[3], + (bd_addr[4]<<8)+bd_addr[5], key_type); + + if (key_type == BTM_LE_KEY_PENC || key_type == BTM_LE_KEY_PID || + key_type == BTM_LE_KEY_PCSRK || key_type == BTM_LE_KEY_LENC || + key_type == BTM_LE_KEY_LCSRK) + { + btm_sec_save_le_key (bd_addr, key_type, p_le_key, FALSE); + } + +#endif + + return(TRUE); +} + +/******************************************************************************* +** +** Function BTM_BleLoadLocalKeys +** +** Description Local local identity key, encryption root or sign counter. +** +** Parameters: key_type: type of key, can be BTM_BLE_KEY_TYPE_ID, BTM_BLE_KEY_TYPE_ER +** or BTM_BLE_KEY_TYPE_COUNTER. +** p_key: pointer to the key. +* +** Returns non2. +** +*******************************************************************************/ +void BTM_BleLoadLocalKeys(UINT8 key_type, tBTM_BLE_LOCAL_KEYS *p_key) +{ +#if BLE_INCLUDED == TRUE + tBTM_DEVCB *p_devcb = &btm_cb.devcb; + BTM_TRACE_DEBUG0 ("BTM_BleLoadLocalKeys"); + if (p_key != NULL) + { + switch (key_type) + { + case BTM_BLE_KEY_TYPE_ID: + memcpy(&p_devcb->id_keys, &p_key->id_keys, sizeof(tBTM_BLE_LOCAL_ID_KEYS)); + break; + + case BTM_BLE_KEY_TYPE_ER: + memcpy(p_devcb->er, p_key->er, sizeof(BT_OCTET16)); + break; + + default: + BTM_TRACE_ERROR1("unknow local key type: %d", key_type); + break; + } + } +#endif +} + +/******************************************************************************* +** +** Function BTM_GetDeviceEncRoot +** +** Description This function is called to read the local device encryption +** root. +** +** Returns void +** the local device ER is copied into er +** +*******************************************************************************/ +void BTM_GetDeviceEncRoot (BT_OCTET16 er) +{ + BTM_TRACE_DEBUG0 ("BTM_GetDeviceEncRoot"); + +#if BLE_INCLUDED == TRUE + memcpy (er, btm_cb.devcb.er, BT_OCTET16_LEN); +#endif +} + +/******************************************************************************* +** +** Function BTM_GetDeviceIDRoot +** +** Description This function is called to read the local device identity +** root. +** +** Returns void +** the local device IR is copied into irk +** +*******************************************************************************/ +void BTM_GetDeviceIDRoot (BT_OCTET16 irk) +{ + BTM_TRACE_DEBUG0 ("BTM_GetDeviceIDRoot "); + +#if BLE_INCLUDED == TRUE + memcpy (irk, btm_cb.devcb.id_keys.irk, BT_OCTET16_LEN); +#endif +} + +/******************************************************************************* +** +** Function BTM_GetDeviceDHK +** +** Description This function is called to read the local device DHK. +** +** Returns void +** the local device DHK is copied into dhk +** +*******************************************************************************/ +void BTM_GetDeviceDHK (BT_OCTET16 dhk) +{ +#if BLE_INCLUDED == TRUE + BTM_TRACE_DEBUG0 ("BTM_GetDeviceDHK"); + memcpy (dhk, btm_cb.devcb.id_keys.dhk, BT_OCTET16_LEN); +#endif +} + +/******************************************************************************* +** +** Function BTM_ReadConnectionAddr +** +** Description This function is called to set the local device random address +** . +** +** Returns void +** +*******************************************************************************/ +void BTM_ReadConnectionAddr (BD_ADDR conn_addr) +{ +#if BLE_INCLUDED == TRUE + BTM_TRACE_DEBUG0 ("BTM_ReadConnectionAddr"); + if (btm_cb.ble_ctr_cb.inq_var.own_addr_type == BLE_ADDR_PUBLIC) + { + BTM_GetLocalDeviceAddr(conn_addr); + } + else + { + memcpy (conn_addr, btm_cb.ble_ctr_cb.addr_mgnt_cb.private_addr, BD_ADDR_LEN); + } +#endif +} + +/******************************************************************************* +** +** Function BTM_SecurityGrant +** +** Description This function is called to grant security process. +** +** Parameters bd_addr - peer device bd address. +** res - result of the operation BTM_SUCCESS if success. +** Otherwise, BTM_REPEATED_ATTEMPTS is too many attempts. +** +** Returns None +** +*******************************************************************************/ +void BTM_SecurityGrant(BD_ADDR bd_addr, UINT8 res) +{ +#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE) + tSMP_STATUS res_smp = (res == BTM_SUCCESS) ? SMP_SUCCESS : SMP_REPEATED_ATTEMPTS; + BTM_TRACE_DEBUG0 ("BTM_SecurityGrant"); + SMP_SecurityGrant(bd_addr, res_smp); +#endif +} + +/******************************************************************************* +** +** Function BTM_BlePasskeyReply +** +** Description This function is called after Security Manager submitted +** passkey request to the application. +** +** Parameters: bd_addr - Address of the device for which passkey was requested +** res - result of the operation BTM_SUCCESS if success +** key_len - length in bytes of the Passkey +** p_passkey - pointer to array with the passkey +** trusted_mask - bitwise OR of trusted services (array of UINT32) +** +*******************************************************************************/ +void BTM_BlePasskeyReply (BD_ADDR bd_addr, UINT8 res, UINT32 passkey) +{ +#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE) + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bd_addr); + tSMP_STATUS res_smp = (res == BTM_SUCCESS) ? SMP_SUCCESS : SMP_PASSKEY_ENTRY_FAIL; + + p_dev_rec->sec_flags |= BTM_SEC_LINK_KEY_AUTHED; + BTM_TRACE_DEBUG0 ("BTM_BlePasskeyReply"); + SMP_PasskeyReply(bd_addr, res_smp, passkey); +#endif +} + +/******************************************************************************* +** +** Function BTM_BleOobDataReply +** +** Description This function is called to provide the OOB data for +** SMP in response to BTM_LE_OOB_REQ_EVT +** +** Parameters: bd_addr - Address of the peer device +** res - result of the operation SMP_SUCCESS if success +** p_data - simple pairing Randomizer C. +** +*******************************************************************************/ +void BTM_BleOobDataReply(BD_ADDR bd_addr, UINT8 res, UINT8 len, UINT8 *p_data) +{ +#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE) + tSMP_STATUS res_smp = (res == BTM_SUCCESS) ? SMP_SUCCESS : SMP_OOB_FAIL; + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bd_addr); + + BTM_TRACE_DEBUG0 ("BTM_BleOobDataReply"); + p_dev_rec->sec_flags |= BTM_SEC_LINK_KEY_AUTHED; + SMP_OobDataReply(bd_addr, res_smp, len, p_data); +#endif +} + +/****************************************************************************** +** +** Function BTM_BleSetConnScanParams +** +** Description Set scan parameter used in BLE connection request +** +** Parameters: scan_interval: scan interval +** scan_window: scan window +** +** Returns void +** +*******************************************************************************/ +void BTM_BleSetConnScanParams (UINT16 scan_interval, UINT16 scan_window) +{ +#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE) + tBTM_BLE_CB *p_ble_cb = &btm_cb.ble_ctr_cb; + BOOLEAN new_param = FALSE; + + if (BTM_BLE_VALID_PRAM(scan_interval, BTM_BLE_SCAN_INT_MIN, BTM_BLE_SCAN_INT_MAX) && + BTM_BLE_VALID_PRAM(scan_window, BTM_BLE_SCAN_WIN_MIN, BTM_BLE_SCAN_WIN_MAX)) + { + btu_stop_timer(&p_ble_cb->scan_param_idle_timer); + + if (p_ble_cb->scan_int != scan_interval) + { + p_ble_cb->scan_int = scan_interval; + new_param = TRUE; + } + + if (p_ble_cb->scan_win != scan_window) + { + p_ble_cb->scan_win = scan_window; + new_param = TRUE; + } + + if (new_param) + btm_ble_update_active_bgconn_scan_params(); + } + else + { + BTM_TRACE_ERROR0("Illegal Connection Scan Parameters"); + } +#endif +} + +/******************************************************** +** +** Function BTM_BleSetPrefConnParams +** +** Description Set a peripheral's preferred connection parameters +** +** Parameters: bd_addr - BD address of the peripheral +** scan_interval: scan interval +** scan_window: scan window +** min_conn_int - minimum preferred connection interval +** max_conn_int - maximum preferred connection interval +** slave_latency - preferred slave latency +** supervision_tout - preferred supervision timeout +** +** Returns void +** +*******************************************************************************/ +void BTM_BleSetPrefConnParams (BD_ADDR bd_addr, + UINT16 min_conn_int, UINT16 max_conn_int, + UINT16 slave_latency, UINT16 supervision_tout) +{ +#if BLE_INCLUDED == TRUE + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bd_addr); + + BTM_TRACE_API4 ("BTM_BleSetPrefConnParams min: %u max: %u latency: %u \ + tout: %u", + min_conn_int, max_conn_int, slave_latency, supervision_tout); + + if (BTM_BLE_VALID_PRAM(min_conn_int, BTM_BLE_CONN_INT_MIN, BTM_BLE_CONN_INT_MAX) && + BTM_BLE_VALID_PRAM(max_conn_int, BTM_BLE_CONN_INT_MIN, BTM_BLE_CONN_INT_MAX) && + BTM_BLE_VALID_PRAM(supervision_tout, BTM_BLE_CONN_SUP_TOUT_MIN, BTM_BLE_CONN_SUP_TOUT_MAX) && + slave_latency <= BTM_BLE_CONN_LATENCY_MAX) + { + if (p_dev_rec) + { + /* expect conn int and stout and slave latency to be updated all together */ + if (min_conn_int != BTM_BLE_CONN_PARAM_UNDEF || max_conn_int != BTM_BLE_CONN_PARAM_UNDEF) + { + if (min_conn_int != BTM_BLE_CONN_PARAM_UNDEF) + p_dev_rec->conn_params.min_conn_int = min_conn_int; + else + p_dev_rec->conn_params.min_conn_int = max_conn_int; + + if (max_conn_int != BTM_BLE_CONN_PARAM_UNDEF) + p_dev_rec->conn_params.max_conn_int = max_conn_int; + else + p_dev_rec->conn_params.max_conn_int = min_conn_int; + + if (slave_latency != BTM_BLE_CONN_PARAM_UNDEF) + p_dev_rec->conn_params.slave_latency = slave_latency; + else + p_dev_rec->conn_params.slave_latency = BTM_BLE_CONN_SLAVE_LATENCY_DEF; + + if (supervision_tout != BTM_BLE_CONN_PARAM_UNDEF) + p_dev_rec->conn_params.supervision_tout = supervision_tout; + else + p_dev_rec->conn_params.slave_latency = BTM_BLE_CONN_TIMEOUT_DEF; + + } + + } + else + { + BTM_TRACE_ERROR0("Unknown Device, setting rejected"); + } + } + else + { + BTM_TRACE_ERROR0("Illegal Connection Parameters"); + } +#endif /* BLE_INCLUDED */ +} + +/******************************************************************************* +** +** Function BTM_ReadDevInfo +** +** Description This function is called to read the device/address type +** of BD address. +** +** Parameter remote_bda: remote device address +** p_dev_type: output parameter to read the device type. +** p_addr_type: output parameter to read the address type. +** +*******************************************************************************/ +void BTM_ReadDevInfo (BD_ADDR remote_bda, tBT_DEVICE_TYPE *p_dev_type, tBLE_ADDR_TYPE *p_addr_type) +{ +#if BLE_INCLUDED == TRUE + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (remote_bda); + tBTM_INQ_INFO *p_inq_info = BTM_InqDbRead(remote_bda); + + *p_dev_type = BT_DEVICE_TYPE_BREDR; + *p_addr_type = BLE_ADDR_PUBLIC; + + if (!p_dev_rec) + { + /* Check with the BT manager if details about remote device are known */ + if (p_inq_info != NULL) + { + *p_dev_type = p_inq_info->results.device_type ; + *p_addr_type = p_inq_info->results.ble_addr_type; + } + /* unknown device, assume BR/EDR */ + } + else /* there is a security device record exisitng */ + { + /* new inquiry result, overwrite device type in security device record */ + if (p_inq_info) + { + p_dev_rec->device_type = p_inq_info->results.device_type; + p_dev_rec->ble.ble_addr_type = p_inq_info->results.ble_addr_type; + } + *p_dev_type = p_dev_rec->device_type; + *p_addr_type = p_dev_rec->ble.ble_addr_type; + + } + + BTM_TRACE_DEBUG2 ("btm_find_dev_type - device_type = %d addr_type = %d", *p_dev_type , *p_addr_type); +#endif + + return; +} + + +/******************************************************************************* +** Internal Functions +*******************************************************************************/ +#if BLE_INCLUDED == TRUE + +/******************************************************************************* +** +** Function btm_ble_update_active_bgconn_scan_params +** +** Description This function is called to update the scan parameter if background +** connection has been active. +** +*******************************************************************************/ +static void btm_ble_update_active_bgconn_scan_params(void) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + tBTM_BLE_SEL_CBACK *p_select_cback; + + /* if active , cancel and restart and apply the params */ + if (p_cb->bg_conn_state == BLE_BG_CONN_ACTIVE) + { + if (p_cb->bg_conn_type == BTM_BLE_CONN_AUTO) + { + if (btm_ble_start_auto_conn(FALSE)) + btm_ble_start_auto_conn(TRUE); + } + else if (p_cb->bg_conn_type == BTM_BLE_CONN_SELECTIVE) + { + p_select_cback = p_cb->p_select_cback; + if (btm_ble_start_select_conn(FALSE, NULL)) + btm_ble_start_select_conn(TRUE, p_select_cback); + } + } + return; +} + +/******************************************************************************* +** +** Function btm_ble_check_link_type +** +** Description This function is to check the link type is BLE or BR/EDR. +** +** Returns TRUE if BLE link; FALSE if BR/EDR. +** +*******************************************************************************/ +BOOLEAN btm_ble_check_link_type (BD_ADDR bd_addr) +{ + tACL_CONN *p; + BTM_TRACE_DEBUG0 ("btm_ble_check_link_type"); + if ((p = btm_bda_to_acl(bd_addr)) != NULL) + return p->is_le_link; + else + return FALSE; +} + +/******************************************************************************* +** +** Function btm_ble_rand_enc_complete +** +** Description This function is the callback functions for HCI_Rand command +** and HCI_Encrypt command is completed. +** This message is received from the HCI. +** +** Returns void +** +*******************************************************************************/ +void btm_ble_rand_enc_complete (UINT8 *p, UINT16 op_code, tBTM_RAND_ENC_CB *p_enc_cplt_cback) +{ + tBTM_RAND_ENC params; + UINT8 *p_dest = params.param_buf; + + BTM_TRACE_DEBUG0 ("btm_ble_rand_enc_complete"); + + memset(¶ms, 0, sizeof(tBTM_RAND_ENC)); + + /* If there was a callback address for vcs complete, call it */ + if (p_enc_cplt_cback && p) + { + /* Pass paramters to the callback function */ + STREAM_TO_UINT8(params.status, p); /* command status */ + + if (params.status == HCI_SUCCESS) + { + params.opcode = op_code; + + if (op_code == HCI_BLE_RAND) + params.param_len = BT_OCTET8_LEN; + else + params.param_len = BT_OCTET16_LEN; + + memcpy(p_dest, p, params.param_len); /* Fetch return info from HCI event message */ + } + if (p_enc_cplt_cback) + (*p_enc_cplt_cback)(¶ms); /* Call the Encryption complete callback function */ + } +} + + +#if (SMP_INCLUDED == TRUE) + +/******************************************************************************* +** +** Function btm_ble_get_enc_key_type +** +** Description This function is to increment local sign counter +** Returns None +** +*******************************************************************************/ +void btm_ble_increment_sign_ctr(BD_ADDR bd_addr, BOOLEAN is_local ) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + + BTM_TRACE_DEBUG1 ("btm_ble_increment_sign_ctr is_local=%d", is_local); + + if ((p_dev_rec = btm_find_dev (bd_addr)) != NULL) + { + if (is_local) + p_dev_rec->ble.keys.local_counter++; + else + p_dev_rec->ble.keys.counter++; + BTM_TRACE_DEBUG3 ("is_local=%d local sign counter=%d peer sign counter=%d", + is_local, + p_dev_rec->ble.keys.local_counter, + p_dev_rec->ble.keys.counter); + } +} + +/******************************************************************************* +** +** Function btm_ble_get_enc_key_type +** +** Description This function is to get the BLE key type that has been exchanged +** in betweem local device and peer device. +** +** Returns p_key_type: output parameter to carry the key type value. +** +*******************************************************************************/ +BOOLEAN btm_ble_get_enc_key_type(BD_ADDR bd_addr, UINT8 *p_key_types) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + + BTM_TRACE_DEBUG0 ("btm_ble_get_enc_key_type"); + + if ((p_dev_rec = btm_find_dev (bd_addr)) != NULL) + { + *p_key_types = p_dev_rec->ble.key_type; + return TRUE; + } + return FALSE; +} + +/******************************************************************************* +** +** Function btm_get_local_div +** +** Description This function is called to read the local DIV +** +** Returns TURE - if a valid DIV is availavle +*******************************************************************************/ +BOOLEAN btm_get_local_div (BD_ADDR bd_addr, UINT16 *p_div) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + BOOLEAN status = FALSE; + BTM_TRACE_DEBUG0 ("btm_get_local_div"); + + BTM_TRACE_DEBUG6("bd_addr:%02x-%02x-%02x-%02x-%02x-%02x", + bd_addr[0],bd_addr[1], + bd_addr[2],bd_addr[3], + bd_addr[4],bd_addr[5]); + + p_dev_rec = btm_find_dev (bd_addr); + + if (p_dev_rec && p_dev_rec->ble.keys.div) + { + status = TRUE; + *p_div = p_dev_rec->ble.keys.div; + } + BTM_TRACE_DEBUG2 ("btm_get_local_div status=%d (1-OK) DIV=0x%x", status, *p_div); + return status; +} + +/******************************************************************************* +** +** Function btm_sec_save_le_key +** +** Description This function is called by the SMP to update +** an BLE key. SMP is internal, whereas all the keys shall +** be sent to the application. The function is also called +** when application passes ble key stored in NVRAM to the btm_sec. +** pass_to_application parameter is false in this case. +** +** Returns void +** +*******************************************************************************/ +void btm_sec_save_le_key(BD_ADDR bd_addr, tBTM_LE_KEY_TYPE key_type, tBTM_LE_KEY_VALUE *p_keys, + BOOLEAN pass_to_application) +{ + tBTM_SEC_DEV_REC *p_rec; + tBTM_LE_EVT_DATA cb_data; + + BTM_TRACE_DEBUG2 ("btm_sec_save_le_key key_type=0x%x pass_to_application=%d",key_type, pass_to_application); + /* Store the updated key in the device database */ + + BTM_TRACE_DEBUG6("bd_addr:%02x-%02x-%02x-%02x-%02x-%02x", + bd_addr[0],bd_addr[1], + bd_addr[2],bd_addr[3], + bd_addr[4],bd_addr[5]); + + if ((p_rec = btm_find_dev (bd_addr)) != NULL && p_keys) + { + switch (key_type) + { + case BTM_LE_KEY_PENC: + memcpy(p_rec->ble.keys.ltk, p_keys->penc_key.ltk, BT_OCTET16_LEN); + memcpy(p_rec->ble.keys.rand, p_keys->penc_key.rand, BT_OCTET8_LEN); + p_rec->ble.keys.sec_level = p_keys->penc_key.sec_level; + p_rec->ble.keys.ediv = p_keys->penc_key.ediv; + p_rec->ble.keys.key_size = p_keys->penc_key.key_size; + p_rec->ble.key_type |= BTM_LE_KEY_PENC; + p_rec->sec_flags |= BTM_SEC_LINK_KEY_KNOWN; + if (p_keys->penc_key.sec_level == SMP_SEC_AUTHENTICATED) + p_rec->sec_flags |= BTM_SEC_LINK_KEY_AUTHED; + else + p_rec->sec_flags &= ~BTM_SEC_LINK_KEY_AUTHED; + BTM_TRACE_DEBUG3("BTM_LE_KEY_PENC key_type=0x%x sec_flags=0x%x sec_leve=0x%x", + p_rec->ble.key_type, + p_rec->sec_flags, + p_rec->ble.keys.sec_level); + break; + + case BTM_LE_KEY_PID: + memcpy(p_rec->ble.keys.irk, p_keys->pid_key, BT_OCTET16_LEN); + p_rec->ble.key_type |= BTM_LE_KEY_PID; + BTM_TRACE_DEBUG1("BTM_LE_KEY_PID key_type=0x%x save peer IRK", p_rec->ble.key_type); + break; + + case BTM_LE_KEY_PCSRK: + memcpy(p_rec->ble.keys.csrk, p_keys->pcsrk_key.csrk, BT_OCTET16_LEN); + p_rec->ble.keys.srk_sec_level = p_keys->pcsrk_key.sec_level; + p_rec->ble.keys.counter = p_keys->pcsrk_key.counter; + p_rec->ble.key_type |= BTM_LE_KEY_PCSRK; + p_rec->sec_flags |= BTM_SEC_LINK_KEY_KNOWN; + if ( p_keys->pcsrk_key.sec_level== SMP_SEC_AUTHENTICATED) + p_rec->sec_flags |= BTM_SEC_LINK_KEY_AUTHED; + else + p_rec->sec_flags &= ~BTM_SEC_LINK_KEY_AUTHED; + + BTM_TRACE_DEBUG4("BTM_LE_KEY_PCSRK key_type=0x%x sec_flags=0x%x sec_level=0x%x peer_counter=%d", + p_rec->ble.key_type, + p_rec->sec_flags, + p_rec->ble.keys.srk_sec_level, + p_rec->ble.keys.counter ); + break; + + case BTM_LE_KEY_LENC: + p_rec->ble.keys.div = p_keys->lenc_key.div; /* update DIV */ + p_rec->ble.keys.sec_level = p_keys->lenc_key.sec_level; + p_rec->ble.keys.key_size = p_keys->lenc_key.key_size; + p_rec->ble.key_type |= BTM_LE_KEY_LENC; + + BTM_TRACE_DEBUG4("BTM_LE_KEY_LENC key_type=0x%x DIV=0x%x key_size=0x%x sec_level=0x%x", + p_rec->ble.key_type, + p_rec->ble.keys.div, + p_rec->ble.keys.key_size, + p_rec->ble.keys.sec_level ); + break; + + case BTM_LE_KEY_LCSRK:/* local CSRK has been delivered */ + p_rec->ble.keys.div = p_keys->lcsrk_key.div; /* update DIV */ + p_rec->ble.keys.local_csrk_sec_level = p_keys->lcsrk_key.sec_level; + p_rec->ble.keys.local_counter = p_keys->lcsrk_key.counter; + p_rec->ble.key_type |= BTM_LE_KEY_LCSRK; + BTM_TRACE_DEBUG4("BTM_LE_KEY_LCSRK key_type=0x%x DIV=0x%x scrk_sec_level=0x%x local_counter=%d", + p_rec->ble.key_type, + p_rec->ble.keys.div, + p_rec->ble.keys.local_csrk_sec_level, + p_rec->ble.keys.local_counter ); + break; + + default: + BTM_TRACE_WARNING1("btm_sec_save_le_key (Bad key_type 0x%02x)", key_type); + return; + } + + BTM_TRACE_DEBUG3 ("BLE key type 0x%02x updated for BDA: %08x%04x (btm_sec_save_le_key)", key_type, + (bd_addr[0]<<24)+(bd_addr[1]<<16)+(bd_addr[2]<<8)+bd_addr[3], + (bd_addr[4]<<8)+bd_addr[5]); + + /* Notify the application that one of the BLE keys has been updated + If link key is in progress, it will get sent later.*/ + if (pass_to_application && btm_cb.api.p_le_callback) + { + cb_data.key.p_key_value = p_keys; + cb_data.key.key_type = key_type; + + (*btm_cb.api.p_le_callback) (BTM_LE_KEY_EVT, bd_addr, &cb_data); + } + return; + } + + BTM_TRACE_WARNING3 ("BLE key type 0x%02x called for Unknown BDA or type: %08x%04x !! (btm_sec_save_le_key)", key_type, + (bd_addr[0]<<24)+(bd_addr[1]<<16)+(bd_addr[2]<<8)+bd_addr[3], + (bd_addr[4]<<8)+bd_addr[5]); + + if (p_rec) + { + BTM_TRACE_DEBUG1 ("sec_flags=0x%x", p_rec->sec_flags); + } +} + +/******************************************************************************* +** +** Function btm_ble_update_sec_key_size +** +** Description update the current lin kencryption key size +** +** Returns void +** +*******************************************************************************/ +void btm_ble_update_sec_key_size(BD_ADDR bd_addr, UINT8 enc_key_size) +{ + tBTM_SEC_DEV_REC *p_rec; + + BTM_TRACE_DEBUG1("btm_ble_update_sec_key_size enc_key_size = %d", enc_key_size); + + if ((p_rec = btm_find_dev (bd_addr)) != NULL ) + { + p_rec->enc_key_size = enc_key_size; + } +} + +/******************************************************************************* +** +** Function btm_ble_read_sec_key_size +** +** Description update the current lin kencryption key size +** +** Returns void +** +*******************************************************************************/ +UINT8 btm_ble_read_sec_key_size(BD_ADDR bd_addr) +{ + tBTM_SEC_DEV_REC *p_rec; + + if ((p_rec = btm_find_dev (bd_addr)) != NULL ) + { + return p_rec->enc_key_size; + } + else + return 0; +} + +/******************************************************************************* +** +** Function btm_ble_link_sec_check +** +** Description Check BLE link security level match. +** +** Returns TRUE: check is OK and the *p_sec_req_act contain the action +** +*******************************************************************************/ +void btm_ble_link_sec_check(BD_ADDR bd_addr, tBTM_LE_AUTH_REQ auth_req, tBTM_BLE_SEC_REQ_ACT *p_sec_req_act) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bd_addr); + UINT8 req_sec_level, cur_sec_level; + + BTM_TRACE_DEBUG1 ("btm_ble_link_sec_check auth_req =0x%x", auth_req); + + if (p_dev_rec == NULL) + { + BTM_TRACE_ERROR0 ("btm_ble_link_sec_check received for unknown device"); + return; + } + + if (p_dev_rec->sec_state == BTM_SEC_STATE_ENCRYPTING) + { + /* race condition: discard the security request while master is encrypting the link */ + *p_sec_req_act = BTM_BLE_SEC_REQ_ACT_DISCARD; + } + else + { + req_sec_level = BTM_LE_SEC_UNAUTHENTICATE; + if ((auth_req == (BTM_LE_AUTH_REQ_BOND|BTM_LE_AUTH_REQ_MITM)) || + (auth_req == (BTM_LE_AUTH_REQ_MITM)) ) + { + req_sec_level = BTM_LE_SEC_AUTHENTICATED; + } + + BTM_TRACE_DEBUG1 ("dev_rec sec_flags=0x%x", p_dev_rec->sec_flags); + + /* currently encrpted */ + if (p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED) + { + if (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_AUTHED) + cur_sec_level = BTM_LE_SEC_AUTHENTICATED; + else + cur_sec_level = BTM_LE_SEC_UNAUTHENTICATE; + } + else /* unencrypted link */ + { + /* if bonded, get the key security level */ + if (p_dev_rec->ble.key_type & BTM_LE_KEY_PENC) + cur_sec_level = p_dev_rec->ble.keys.sec_level; + else + cur_sec_level = BTM_LE_SEC_NONE; + } + + if (cur_sec_level >= req_sec_level) + { + if (cur_sec_level == BTM_LE_SEC_NONE) + { + *p_sec_req_act = BTM_BLE_SEC_REQ_ACT_NONE; + } + else + { + /* To avoid re-encryption on an encrypted link for an equal condition encryption + if (p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED) + *p_sec_req_act = BTM_BLE_SEC_REQ_ACT_DISCARD; + else + */ + *p_sec_req_act = BTM_BLE_SEC_REQ_ACT_ENCRYPT; + } + } + else + { + *p_sec_req_act = BTM_BLE_SEC_REQ_ACT_PAIR; /* start the pariring process to upgrade the keys*/ + } + } + + BTM_TRACE_DEBUG3("cur_sec_level=%d req_sec_level=%d sec_req_act=%d", + cur_sec_level, + req_sec_level, + *p_sec_req_act); + +} + +/******************************************************************************* +** +** Function btm_ble_set_encryption +** +** Description This function is called to ensure that LE connection is +** encrypted. Should be called only on an open connection. +** Typically only needed for connections that first want to +** bring up unencrypted links, then later encrypt them. +** +** Returns void +** the local device ER is copied into er +** +*******************************************************************************/ +tBTM_STATUS btm_ble_set_encryption (BD_ADDR bd_addr, void *p_ref_data, UINT8 link_role) +{ + tBTM_STATUS cmd = BTM_NO_RESOURCES; + tBTM_BLE_SEC_ACT sec_act = *(tBTM_BLE_SEC_ACT *)p_ref_data ; + tBTM_SEC_DEV_REC *p_rec = btm_find_dev (bd_addr); + + BTM_TRACE_DEBUG2 ("btm_ble_set_encryption sec_act=0x%x role_master=%d", sec_act, p_rec->role_master); + + if (p_rec == NULL) + { + return(BTM_WRONG_MODE); + } + + if (sec_act == BTM_BLE_SEC_ENCRYPT_MITM) + { + p_rec->security_required |= BTM_SEC_IN_MITM; + } + + switch (sec_act) + { + case BTM_BLE_SEC_ENCRYPT: + if (link_role == BTM_ROLE_MASTER) + { + /* start link layer encryption using the security info stored */ + btm_ble_start_encrypt(bd_addr, FALSE, NULL); + p_rec->sec_state = BTM_SEC_STATE_ENCRYPTING; + cmd = BTM_CMD_STARTED; + break; + } + /* if salve role then fall through to call SMP_Pair below which will send a + sec_request to request the master to encrypt the link */ + case BTM_BLE_SEC_ENCRYPT_NO_MITM: + case BTM_BLE_SEC_ENCRYPT_MITM: + + if (SMP_Pair(bd_addr) == SMP_STARTED) + { + cmd = BTM_CMD_STARTED; + p_rec->sec_state = BTM_SEC_STATE_AUTHENTICATING; + } + break; + + default: + cmd = BTM_SUCCESS; + break; + } + return cmd; +} + +/******************************************************************************* +** +** Function btm_ble_ltk_request +** +** Description This function is called when encryption request is received +** on a slave device. +** +** +** Returns void +** +*******************************************************************************/ +void btm_ble_ltk_request(UINT16 handle, UINT8 rand[8], UINT16 ediv) +{ + tBTM_CB *p_cb = &btm_cb; + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev_by_handle (handle); + BT_OCTET8 dummy_stk = {0}; + + BTM_TRACE_DEBUG0 ("btm_ble_ltk_request"); + + p_cb->ediv = ediv; + + memcpy(p_cb->enc_rand, rand, BT_OCTET8_LEN); + + if (!smp_proc_ltk_request(p_dev_rec->bd_addr)) + btm_ble_ltk_request_reply(p_dev_rec->bd_addr, FALSE, dummy_stk); + + +} + +/******************************************************************************* +** +** Function btm_ble_start_encrypt +** +** Description This function is called to start LE encryption. +** +** +** Returns void +** +*******************************************************************************/ +BOOLEAN btm_ble_start_encrypt(BD_ADDR bda, BOOLEAN use_stk, BT_OCTET16 stk) +{ + tBTM_CB *p_cb = &btm_cb; + tBTM_SEC_DEV_REC *p_rec = btm_find_dev (bda); + BT_OCTET8 dummy_rand = {0}; + + BTM_TRACE_DEBUG0 ("btm_ble_start_encrypt"); + + if (!p_rec || + (p_rec && p_rec->sec_state == BTM_SEC_STATE_ENCRYPTING)) + return FALSE; + + if (p_rec->sec_state == BTM_SEC_STATE_IDLE) + p_rec->sec_state = BTM_SEC_STATE_ENCRYPTING; + p_cb->enc_handle = p_rec->hci_handle; + + if (use_stk) + { + if (!btsnd_hcic_ble_start_enc(p_rec->hci_handle, dummy_rand, 0, stk)) + return FALSE; + } + else + { + if (!btsnd_hcic_ble_start_enc(p_rec->hci_handle, p_rec->ble.keys.rand, + p_rec->ble.keys.ediv, p_rec->ble.keys.ltk)) + return FALSE; + } + return TRUE; +} + +/******************************************************************************* +** +** Function btm_ble_link_encrypted +** +** Description This function is called when LE link encrption status is changed. +** +** Returns void +** +*******************************************************************************/ +void btm_ble_link_encrypted(BD_ADDR bd_addr, UINT8 encr_enable) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bd_addr); + + BTM_TRACE_DEBUG1 ("btm_ble_link_encrypted encr_enable=%d", encr_enable); + + smp_link_encrypted(bd_addr, encr_enable); + + if (p_dev_rec) + { + BTM_TRACE_DEBUG1(" p_dev_rec->sec_flags=0x%x", p_dev_rec->sec_flags); + + if (encr_enable && p_dev_rec->enc_key_size == 0) + p_dev_rec->enc_key_size = p_dev_rec->ble.keys.key_size; + + if (p_dev_rec->p_callback) + { + if (p_dev_rec->sec_state == BTM_SEC_STATE_ENCRYPTING) + { + if (encr_enable) + btm_sec_dev_rec_cback_event(p_dev_rec, BTM_SUCCESS); + else if (p_dev_rec->role_master) + btm_sec_dev_rec_cback_event(p_dev_rec, BTM_ERR_PROCESSING); + } + } + p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; + } +} + +/******************************************************************************* +** Function btm_enc_proc_ltk +** Description send LTK reply when it's ready. +*******************************************************************************/ +static void btm_enc_proc_ltk(tSMP_ENC *p) +{ + UINT8 i; + BTM_TRACE_DEBUG0 ("btm_enc_proc_ltk"); + if (p && p->param_len == BT_OCTET16_LEN) + { + for (i = 0; i < (BT_OCTET16_LEN - btm_cb.key_size); i ++) + p->param_buf[BT_OCTET16_LEN - i - 1] = 0; + btsnd_hcic_ble_ltk_req_reply(btm_cb.enc_handle, p->param_buf); + } +} + +/******************************************************************************* +** Function btm_enc_proc_slave_y +** Description calculate LTK when Y is ready +*******************************************************************************/ +static void btm_enc_proc_slave_y(tSMP_ENC *p) +{ + UINT16 div, y; + UINT8 *pp = p->param_buf; + tBTM_CB *p_cb = &btm_cb; + tSMP_ENC output; + tBTM_SEC_DEV_REC *p_dev_rec; + + BTM_TRACE_DEBUG0 ("btm_enc_proc_slave_y"); + if (p != NULL) + { + STREAM_TO_UINT16(y, pp); + + div = p_cb->ediv ^ y; + p_dev_rec = btm_find_dev_by_handle (p_cb->enc_handle); + + if ( p_dev_rec && + p_dev_rec->ble.keys.div == div ) + { + BTM_TRACE_DEBUG0 ("LTK request OK"); + /* calculating LTK , LTK = E er(div) */ + SMP_Encrypt(p_cb->devcb.er, BT_OCTET16_LEN, (UINT8 *)&div, 2, &output); + btm_enc_proc_ltk(&output); + } + else + { + BTM_TRACE_DEBUG0 ("LTK request failed - send negative reply"); + btsnd_hcic_ble_ltk_req_neg_reply(p_cb->enc_handle); + if (p_dev_rec) + btm_ble_link_encrypted(p_dev_rec->bd_addr, 0); + + } + } +} + +/******************************************************************************* +** +** Function btm_ble_ltk_request_reply +** +** Description This function is called to send a LTK request reply on a slave +** device. +** +** Returns void +** +*******************************************************************************/ +void btm_ble_ltk_request_reply(BD_ADDR bda, BOOLEAN use_stk, BT_OCTET16 stk) +{ + tBTM_SEC_DEV_REC *p_rec = btm_find_dev (bda); + tBTM_CB *p_cb = &btm_cb; + tSMP_ENC output; + + if (p_rec == NULL) + { + BTM_TRACE_ERROR0("btm_ble_ltk_request_reply received for unknown device"); + return; + } + + BTM_TRACE_DEBUG0 ("btm_ble_ltk_request_reply"); + p_cb->enc_handle = p_rec->hci_handle; + p_cb->key_size = p_rec->ble.keys.key_size; + + BTM_TRACE_ERROR1("key size = %d", p_rec->ble.keys.key_size); + if (use_stk) + { + btsnd_hcic_ble_ltk_req_reply(btm_cb.enc_handle, stk); + } + else /* calculate LTK using peer device */ + { + /* generate Y= Encrypt(DHK, Rand) received from encrypt request */ + SMP_Encrypt(p_cb->devcb.id_keys.dhk, BT_OCTET16_LEN, p_cb->enc_rand, + BT_OCTET8_LEN, &output); + btm_enc_proc_slave_y(&output); + } +} + +/******************************************************************************* +** +** Function btm_ble_io_capabilities_req +** +** Description This function is called to handle SMP get IO capability request. +** +** Returns void +** +*******************************************************************************/ +UINT8 btm_ble_io_capabilities_req(tBTM_SEC_DEV_REC *p_dev_rec, tBTM_LE_IO_REQ *p_data) +{ + UINT8 callback_rc = BTM_SUCCESS; + BTM_TRACE_DEBUG0 ("btm_ble_io_capabilities_req"); + if (btm_cb.api.p_le_callback) + { + /* the callback function implementation may change the IO capability... */ + callback_rc = (*btm_cb.api.p_le_callback) (BTM_LE_IO_REQ_EVT, p_dev_rec->bd_addr, (tBTM_LE_EVT_DATA *)p_data); + } +#if BTM_OOB_INCLUDED == TRUE + if ((callback_rc == BTM_SUCCESS) || (BTM_OOB_UNKNOWN != p_data->oob_data)) +#else + if (callback_rc == BTM_SUCCESS) +#endif + { + p_data->auth_req &= BTM_LE_AUTH_REQ_MASK; + + BTM_TRACE_DEBUG2 ("btm_ble_io_capabilities_req 1: p_dev_rec->security_required = %d auth_req:%d", + p_dev_rec->security_required, p_data->auth_req); + BTM_TRACE_DEBUG2 ("btm_ble_io_capabilities_req 2: i_keys=0x%x r_keys=0x%x (bit 0-LTK 1-IRK 2-CSRK)", + p_data->init_keys, + p_data->resp_keys); + + /* if authentication requires MITM protection, put on the mask */ + if (p_dev_rec->security_required & BTM_SEC_IN_MITM) + p_data->auth_req |= BTM_LE_AUTH_REQ_MITM; + + if (!(p_data->auth_req & SMP_AUTH_BOND)) + { + BTM_TRACE_DEBUG0("Non bonding: No keys should be exchanged"); + p_data->init_keys = 0; + p_data->resp_keys = 0; + } + + BTM_TRACE_DEBUG1 ("btm_ble_io_capabilities_req 3: auth_req:%d", p_data->auth_req); + BTM_TRACE_DEBUG2 ("btm_ble_io_capabilities_req 4: i_keys=0x%x r_keys=0x%x", + p_data->init_keys, + p_data->resp_keys); + + BTM_TRACE_DEBUG2 ("btm_ble_io_capabilities_req 5: p_data->io_cap = %d auth_req:%d", + p_data->io_cap, p_data->auth_req); + + /* remove MITM protection requirement if IO cap does not allow it */ + if ((p_data->io_cap == BTM_IO_CAP_NONE) && p_data->oob_data == SMP_OOB_NONE) + p_data->auth_req &= ~BTM_LE_AUTH_REQ_MITM; + + BTM_TRACE_DEBUG3 ("btm_ble_io_capabilities_req 6: IO_CAP:%d oob_data:%d auth_req:%d", + p_data->io_cap, p_data->oob_data, p_data->auth_req); + } + return callback_rc; +} + +/***************************************************************************** +** Function btm_proc_smp_cback +** +** Description This function is the SMP callback handler. +** +******************************************************************************/ +UINT8 btm_proc_smp_cback(tSMP_EVT event, BD_ADDR bd_addr, tSMP_EVT_DATA *p_data) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bd_addr); + UINT8 res; + + BTM_TRACE_DEBUG1 ("btm_proc_smp_cback event = %d", event); + + if (p_dev_rec != NULL) + { + switch (event) + { + case SMP_IO_CAP_REQ_EVT: + btm_ble_io_capabilities_req(p_dev_rec, (tBTM_LE_IO_REQ *)&p_data->io_req); + break; + + case SMP_PASSKEY_REQ_EVT: + case SMP_PASSKEY_NOTIF_EVT: + case SMP_OOB_REQ_EVT: + p_dev_rec->sec_flags |= BTM_SEC_LINK_KEY_AUTHED; + case SMP_SEC_REQUEST_EVT: + case SMP_COMPLT_EVT: + if (btm_cb.api.p_le_callback) + { + /* the callback function implementation may change the IO capability... */ + BTM_TRACE_DEBUG1 ("btm_cb.api.p_le_callback=0x%x", btm_cb.api.p_le_callback ); + (*btm_cb.api.p_le_callback) (event, bd_addr, (tBTM_LE_EVT_DATA *)p_data); + } + else + { + BTM_TRACE_ERROR0 ("btm_proc_smp_cback: btm_cb.api.p_le_callback ==NULL"); + } + + if (event == SMP_COMPLT_EVT) + { + BTM_TRACE_DEBUG2 ("evt=SMP_COMPLT_EVT before update sec_level=0x%x sec_flags=0x%x", p_data->cmplt.sec_level , p_dev_rec->sec_flags ); + + res = (p_data->cmplt.reason == SMP_SUCCESS) ? BTM_SUCCESS : BTM_ERR_PROCESSING; + + BTM_TRACE_DEBUG3 ("after update result=%d sec_level=0x%x sec_flags=0x%x", + res, p_data->cmplt.sec_level , p_dev_rec->sec_flags ); + + btm_sec_dev_rec_cback_event(p_dev_rec, res); + + if (p_data->cmplt.is_pair_cancel && btm_cb.api.p_bond_cancel_cmpl_callback ) + { + BTM_TRACE_DEBUG0 ("Pairing Cancel completed"); + (*btm_cb.api.p_bond_cancel_cmpl_callback)(BTM_SUCCESS); + } +#if BTM_BLE_CONFORMANCE_TESTING == TRUE + if (res != BTM_SUCCESS) + { + if (!btm_cb.devcb.no_disc_if_pair_fail) + { + BTM_TRACE_DEBUG0 ("Pairing failed - Remove ACL"); + btm_remove_acl(bd_addr); + } + else + { + BTM_TRACE_DEBUG0 ("Pairing failed - Not Removing ACL"); + p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; + } + } +#else + if (res != BTM_SUCCESS) + btm_remove_acl(bd_addr); +#endif + + BTM_TRACE_DEBUG3 ("btm_cb pairing_state=%x pairing_flags=%x pin_code_len=%x", + btm_cb.pairing_state, + btm_cb.pairing_flags, + btm_cb.pin_code_len ); + BTM_TRACE_DEBUG6 ("btm_cb.pairing_bda %02x:%02x:%02x:%02x:%02x:%02x", + btm_cb.pairing_bda[0], btm_cb.pairing_bda[1], btm_cb.pairing_bda[2], + btm_cb.pairing_bda[3], btm_cb.pairing_bda[4], btm_cb.pairing_bda[5]); + + memset (btm_cb.pairing_bda, 0xff, BD_ADDR_LEN); + btm_cb.pairing_flags = 0; + } + break; + + default: + BTM_TRACE_DEBUG1 ("unknown event = %d", event); + break; + + + } + } + else + { + BTM_TRACE_ERROR0("btm_proc_smp_cback received for unknown device"); + } + + return 0; +} + +#endif /* SMP_INCLUDED */ +#endif /* BLE_INCLUDED */ + + +/******************************************************************************* +** +** Function BTM_BleDataSignature +** +** Description This function is called to sign the data using AES128 CMAC +** algorith. +** +** Parameter bd_addr: target device the data to be signed for. +** p_text: singing data +** len: length of the data to be signed. +** signature: output parameter where data signature is going to +** be stored. +** +** Returns TRUE if signing sucessul, otherwise FALSE. +** +*******************************************************************************/ +BOOLEAN BTM_BleDataSignature (BD_ADDR bd_addr, UINT8 *p_text, UINT16 len, + BLE_SIGNATURE signature) +{ + BOOLEAN ret = FALSE; +#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE) + tBTM_SEC_DEV_REC *p_rec = btm_find_dev (bd_addr); + UINT8 *p_buf, *pp; + + BT_OCTET16 er; + UINT16 div; + UINT8 temp[4]; /* for (r || DIV) r=1*/ + UINT16 r=1; + UINT8 *p=temp, *p_mac = (UINT8 *)signature; + tSMP_ENC output; + BT_OCTET16 local_csrk; + + BTM_TRACE_DEBUG0 ("BTM_BleDataSignature"); + if (p_rec == NULL) + { + BTM_TRACE_ERROR0("data signing can not be done from unknow device"); + } + else + { + if ((p_buf = (UINT8 *)GKI_getbuf((UINT16)(len + 4))) != NULL) + { + BTM_TRACE_DEBUG0("Start to generate Local CSRK"); + /* prepare plain text */ + if (p_text) + { + memcpy(p_buf, p_text, len); + pp = (p_buf + len); + } + +#if BTM_BLE_CONFORMANCE_TESTING == TRUE + if ( btm_cb.devcb.enable_test_local_sign_cntr) + { + BTM_TRACE_DEBUG1 ("Use Test local counter value from script counter_val=%d", btm_cb.devcb.test_local_sign_cntr); + UINT32_TO_STREAM(pp, btm_cb.devcb.test_local_sign_cntr); + } + else + { + UINT32_TO_STREAM(pp, p_rec->ble.keys.local_counter); + } +#else + UINT32_TO_STREAM(pp, p_rec->ble.keys.local_counter); +#endif + /* compute local csrk */ + if (btm_get_local_div(bd_addr, &div)) + { + BTM_TRACE_DEBUG1 ("compute_csrk div=%x", div); + BTM_GetDeviceEncRoot(er); + + /* CSRK = d1(ER, DIV, 1) */ + UINT16_TO_STREAM(p, div); + UINT16_TO_STREAM(p, r); + + if (!SMP_Encrypt(er, BT_OCTET16_LEN, temp, 4, &output)) + { + BTM_TRACE_ERROR0("Local CSRK generation failed "); + } + else + { + BTM_TRACE_DEBUG0("local CSRK generation success"); + memcpy((void *)local_csrk, output.param_buf, BT_OCTET16_LEN); + + +#if BTM_BLE_CONFORMANCE_TESTING == TRUE + if (btm_cb.devcb.enable_test_local_sign_cntr) + { + UINT32_TO_STREAM(p_mac, btm_cb.devcb.test_local_sign_cntr); + } + else + { + UINT32_TO_STREAM(p_mac, p_rec->ble.keys.local_counter); + } +#else + UINT32_TO_STREAM(p_mac, p_rec->ble.keys.local_counter); +#endif + + if ((ret = AES_CMAC(local_csrk, p_buf, (UINT16)(len + 4), BTM_CMAC_TLEN_SIZE, p_mac)) == TRUE) + { + btm_ble_increment_sign_ctr(bd_addr, TRUE); + +#if BTM_BLE_CONFORMANCE_TESTING == TRUE + if ( btm_cb.devcb.enable_test_mac_val) + { + BTM_TRACE_DEBUG0 ("Use MAC value from script"); + memcpy(p_mac, btm_cb.devcb.test_mac, BTM_CMAC_TLEN_SIZE); + } +#endif + } + BTM_TRACE_DEBUG1("BTM_BleDataSignature p_mac = %d", p_mac); + BTM_TRACE_DEBUG4("p_mac[0] = 0x%02x p_mac[1] = 0x%02x p_mac[2] = 0x%02x p_mac[3] = 0x%02x", + *p_mac, *(p_mac + 1), *(p_mac + 2), *(p_mac + 3)); + BTM_TRACE_DEBUG4("p_mac[4] = 0x%02x p_mac[5] = 0x%02x p_mac[6] = 0x%02x p_mac[7] = 0x%02x", + *(p_mac + 4), *(p_mac + 5), *(p_mac + 6), *(p_mac + 7)); + + GKI_freebuf(p_buf); + } + } + } + } +#endif /* BLE_INCLUDED */ + return ret; +} + +/******************************************************************************* +** +** Function BTM_BleVerifySignature +** +** Description This function is called to verify the data signature +** +** Parameter bd_addr: target device the data to be signed for. +** p_orig: original data before signature. +** len: length of the signing data +** counter: counter used when doing data signing +** p_comp: signature to be compared against. + +** Returns TRUE if signature verified correctly; otherwise FALSE. +** +*******************************************************************************/ +BOOLEAN BTM_BleVerifySignature (BD_ADDR bd_addr, UINT8 *p_orig, UINT16 len, UINT32 counter, + UINT8 *p_comp) +{ + BOOLEAN verified = FALSE; +#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE) + tBTM_SEC_DEV_REC *p_rec = btm_find_dev (bd_addr); + UINT8 p_mac[BTM_CMAC_TLEN_SIZE]; + + if (p_rec == NULL || (p_rec && !(p_rec->ble.key_type & BTM_LE_KEY_PCSRK))) + { + BTM_TRACE_ERROR0("can not verify signature for unknown device"); + } + else if (counter < p_rec->ble.keys.counter) + { + BTM_TRACE_ERROR0("signature received with out dated sign counter"); + } + else if (p_orig == NULL) + { + BTM_TRACE_ERROR0("No signature to verify"); + } + else + { + BTM_TRACE_DEBUG2 ("BTM_BleVerifySignature rcv_cnt=%d >= expected_cnt=%d", counter, p_rec->ble.keys.counter); + + if (AES_CMAC(p_rec->ble.keys.csrk, p_orig, len, BTM_CMAC_TLEN_SIZE, p_mac)) + { + if (memcmp(p_mac, p_comp, BTM_CMAC_TLEN_SIZE) == 0) + { + btm_ble_increment_sign_ctr(bd_addr, FALSE); + verified = TRUE; + } + } + } +#endif /* BLE_INCLUDED */ + return verified; +} + +#if BLE_INCLUDED == TRUE +/******************************************************************************* +** Utility functions for LE device IR/ER generation +*******************************************************************************/ +/******************************************************************************* +** +** Function btm_notify_new_key +** +** Description This function is to notify application new keys have been +** generated. +** +** Returns void +** +*******************************************************************************/ +static void btm_notify_new_key(UINT8 key_type) +{ + tBTM_BLE_LOCAL_KEYS *p_locak_keys = NULL; + + BTM_TRACE_DEBUG1 ("btm_notify_new_key key_type=%d", key_type); + + if (btm_cb.api.p_le_key_callback) + { + switch (key_type) + { + case BTM_BLE_KEY_TYPE_ID: + BTM_TRACE_DEBUG0 ("BTM_BLE_KEY_TYPE_ID"); + p_locak_keys = (tBTM_BLE_LOCAL_KEYS *)&btm_cb.devcb.id_keys; + break; + + case BTM_BLE_KEY_TYPE_ER: + BTM_TRACE_DEBUG0 ("BTM_BLE_KEY_TYPE_ER"); + p_locak_keys = (tBTM_BLE_LOCAL_KEYS *)&btm_cb.devcb.er; + break; + + default: + BTM_TRACE_ERROR1("unknown key type: %d", key_type); + break; + } + if (p_locak_keys != NULL) + (*btm_cb.api.p_le_key_callback) (key_type, p_locak_keys); + } +} + +/******************************************************************************* +** +** Function btm_ble_process_er2 +** +** Description This function is called when ER is generated, store it in +** local control block. +** +** Returns void +** +*******************************************************************************/ +static void btm_ble_process_er2(tBTM_RAND_ENC *p) +{ + BTM_TRACE_DEBUG0 ("btm_ble_process_er2"); + + if (p &&p->opcode == HCI_BLE_RAND) + { + memcpy(&btm_cb.devcb.er[8], p->param_buf, BT_OCTET8_LEN); + btm_notify_new_key(BTM_BLE_KEY_TYPE_ER); + } + else + { + BTM_TRACE_ERROR0("Generating ER2 exception."); + memset(&btm_cb.devcb.er, 0, sizeof(BT_OCTET16)); + } +} + +/******************************************************************************* +** +** Function btm_ble_process_er +** +** Description This function is called when ER is generated, store it in +** local control block. +** +** Returns void +** +*******************************************************************************/ +static void btm_ble_process_er(tBTM_RAND_ENC *p) +{ + BTM_TRACE_DEBUG0 ("btm_ble_process_er"); + + if (p &&p->opcode == HCI_BLE_RAND) + { + memcpy(&btm_cb.devcb.er[0], p->param_buf, BT_OCTET8_LEN); + + if (!btsnd_hcic_ble_rand((void *)btm_ble_process_er2)) + { + memset(&btm_cb.devcb.er, 0, sizeof(BT_OCTET16)); + BTM_TRACE_ERROR0("Generating ER2 failed."); + } + } + else + { + BTM_TRACE_ERROR0("Generating ER1 exception."); + } +} + +/******************************************************************************* +** +** Function btm_ble_process_irk +** +** Description This function is called when IRK is generated, store it in +** local control block. +** +** Returns void +** +*******************************************************************************/ +static void btm_ble_process_irk(tSMP_ENC *p) +{ + BTM_TRACE_DEBUG0 ("btm_ble_process_irk"); + if (p &&p->opcode == HCI_BLE_ENCRYPT) + { + memcpy(btm_cb.devcb.id_keys.irk, p->param_buf, BT_OCTET16_LEN); + btm_notify_new_key(BTM_BLE_KEY_TYPE_ID); + } + else + { + BTM_TRACE_ERROR0("Generating IRK exception."); + } + + /* proceed generate ER */ + if (!btsnd_hcic_ble_rand((void *)btm_ble_process_er)) + { + BTM_TRACE_ERROR0("Generating ER failed."); + } +} + +/******************************************************************************* +** +** Function btm_ble_process_dhk +** +** Description This function is called when DHK is calculated, store it in +** local control block, and proceed to generate ER, a 128-bits +** random number. +** +** Returns void +** +*******************************************************************************/ +static void btm_ble_process_dhk(tSMP_ENC *p) +{ +#if SMP_INCLUDED == TRUE + UINT8 btm_ble_irk_pt = 0x01; + tSMP_ENC output; + + BTM_TRACE_DEBUG0 ("btm_ble_process_dhk"); + + if (p && p->opcode == HCI_BLE_ENCRYPT) + { + memcpy(btm_cb.devcb.id_keys.dhk, p->param_buf, BT_OCTET16_LEN); + BTM_TRACE_DEBUG0("BLE DHK generated."); + + /* IRK = D1(IR, 1) */ + if (!SMP_Encrypt(btm_cb.devcb.id_keys.ir, BT_OCTET16_LEN, &btm_ble_irk_pt, + 1, &output)) + { + /* reset all identity root related key */ + memset(&btm_cb.devcb.id_keys, 0, sizeof(tBTM_BLE_LOCAL_ID_KEYS)); + } + else + { + btm_ble_process_irk(&output); + } + } + else + { + /* reset all identity root related key */ + memset(&btm_cb.devcb.id_keys, 0, sizeof(tBTM_BLE_LOCAL_ID_KEYS)); + } +#endif +} + +/******************************************************************************* +** +** Function btm_ble_process_ir2 +** +** Description This function is called when IR is generated, proceed to calculate +** DHK = Eir({0x03, 0, 0 ...}) +** +** +** Returns void +** +*******************************************************************************/ +static void btm_ble_process_ir2(tBTM_RAND_ENC *p) +{ +#if SMP_INCLUDED == TRUE + UINT8 btm_ble_dhk_pt = 0x03; + tSMP_ENC output; + + BTM_TRACE_DEBUG0 ("btm_ble_process_ir2"); + + if (p && p->opcode == HCI_BLE_RAND) + { + /* remembering in control block */ + memcpy(&btm_cb.devcb.id_keys.ir[8], p->param_buf, BT_OCTET8_LEN); + /* generate DHK= Eir({0x03, 0x00, 0x00 ...}) */ + + + SMP_Encrypt(btm_cb.devcb.id_keys.ir, BT_OCTET16_LEN, &btm_ble_dhk_pt, + 1, &output); + btm_ble_process_dhk(&output); + + BTM_TRACE_DEBUG0("BLE IR generated."); + } + else + { + memset(&btm_cb.devcb.id_keys, 0, sizeof(tBTM_BLE_LOCAL_ID_KEYS)); + } +#endif +} + +/******************************************************************************* +** +** Function btm_ble_process_ir +** +** Description This function is called when IR is generated, proceed to calculate +** DHK = Eir({0x02, 0, 0 ...}) +** +** +** Returns void +** +*******************************************************************************/ +static void btm_ble_process_ir(tBTM_RAND_ENC *p) +{ + BTM_TRACE_DEBUG0 ("btm_ble_process_ir"); + + if (p && p->opcode == HCI_BLE_RAND) + { + /* remembering in control block */ + memcpy(btm_cb.devcb.id_keys.ir, p->param_buf, BT_OCTET8_LEN); + + if (!btsnd_hcic_ble_rand((void *)btm_ble_process_ir2)) + { + BTM_TRACE_ERROR0("Generating IR2 failed."); + memset(&btm_cb.devcb.id_keys, 0, sizeof(tBTM_BLE_LOCAL_ID_KEYS)); + } + } +} + +/******************************************************************************* +** +** Function btm_ble_reset_id +** +** Description This function is called to reset LE device identity. +** +** Returns void +** +*******************************************************************************/ +void btm_ble_reset_id( void ) +{ + BTM_TRACE_DEBUG0 ("btm_ble_reset_id"); + + /* regenrate Identity Root*/ + if (!btsnd_hcic_ble_rand((void *)btm_ble_process_ir)) + { + BTM_TRACE_DEBUG0("Generating IR failed."); + } +} + +#if BTM_BLE_CONFORMANCE_TESTING == TRUE +/******************************************************************************* +** +** Function btm_ble_set_no_disc_if_pair_fail +** +** Description This function indicates that whether no disconnect of the ACL +** should be used if pairing failed +** +** Returns void +** +*******************************************************************************/ +void btm_ble_set_no_disc_if_pair_fail(BOOLEAN disable_disc ) +{ + BTM_TRACE_DEBUG1 ("btm_ble_set_disc_enable_if_pair_fail disable_disc=%d", disable_disc); + btm_cb.devcb.no_disc_if_pair_fail = disable_disc; +} + +/******************************************************************************* +** +** Function btm_ble_set_test_mac_value +** +** Description This function set test MAC value +** +** Returns void +** +*******************************************************************************/ +void btm_ble_set_test_mac_value(BOOLEAN enable, UINT8 *p_test_mac_val ) +{ + BTM_TRACE_DEBUG1 ("btm_ble_set_test_mac_value enable=%d", enable); + btm_cb.devcb.enable_test_mac_val = enable; + memcpy(btm_cb.devcb.test_mac, p_test_mac_val, BT_OCTET8_LEN); +} + +/******************************************************************************* +** +** Function btm_ble_set_test_local_sign_cntr_value +** +** Description This function set test local sign counter value +** +** Returns void +** +*******************************************************************************/ +void btm_ble_set_test_local_sign_cntr_value(BOOLEAN enable, UINT32 test_local_sign_cntr ) +{ + BTM_TRACE_DEBUG2 ("btm_ble_set_test_local_sign_cntr_value enable=%d local_sign_cntr=%d", + enable, test_local_sign_cntr); + btm_cb.devcb.enable_test_local_sign_cntr = enable; + btm_cb.devcb.test_local_sign_cntr = test_local_sign_cntr; +} + +#endif /* BTM_BLE_CONFORMANCE_TESTING */ + + +#endif /* BLE_INCLUDED */ diff --git a/stack/btm/btm_ble_addr.c b/stack/btm/btm_ble_addr.c new file mode 100644 index 0000000..08a3018 --- /dev/null +++ b/stack/btm/btm_ble_addr.c @@ -0,0 +1,371 @@ +/***************************************************************************** +** +** Name: btm_ble_addr.c +** +** Description: This file contains functions for BLE address management. +** +** +** +** Copyright (c) 1999-2010, Broadcom Corp., All Rights Reserved. +** WIDCOMM Bluetooth Core. Proprietary and confidential. +******************************************************************************/ + +#include <string.h> + +#include "bt_types.h" +#include "hcimsgs.h" +#include "btu.h" +#include "btm_int.h" + + +#if (defined BLE_INCLUDED && BLE_INCLUDED == TRUE) + #include "smp_api.h" + #define BTM_BLE_PRIVATE_ADDR_INT 900 /* 15 minutes minimum for random address refreshing */ + +/******************************************************************************* +** +** Function btm_gen_resolve_paddr_cmpl +** +** Description This is callback functioin when resolvable private address +** generation is complete. +** +** Returns void +** +*******************************************************************************/ +static void btm_gen_resolve_paddr_cmpl(tSMP_ENC *p) +{ + tBTM_LE_RANDOM_CB *p_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + tBTM_BLE_INQ_CB *p_inq_cb = &btm_cb.ble_ctr_cb.inq_var; + BTM_TRACE_EVENT0 ("btm_gen_resolve_paddr_cmpl"); + if (p && p->param_buf) + { + /* get the high bytes of the random address */ + p_cb->private_addr[2] = p->param_buf[0]; + p_cb->private_addr[1] = p->param_buf[1]; + p_cb->private_addr[0] = p->param_buf[2]; + /* mask off the 1st MSB */ + p_cb->private_addr[0] &= 0xfe; + /* set the 2nd MSB to be 1 */ + p_cb->private_addr[0] |= 0x02; + /* set it to controller */ + btsnd_hcic_ble_set_random_addr(p_cb->private_addr); + + p_inq_cb->own_addr_type = BLE_ADDR_RANDOM; + + /* start a periodical timer to refresh random addr */ + btu_stop_timer(&p_cb->raddr_timer_ent); + btu_start_timer (&p_cb->raddr_timer_ent, BTU_TTYPE_BLE_RANDOM_ADDR, + BTM_BLE_PRIVATE_ADDR_INT); + + /* if adv is active, restart adv with new private addr */ + if (p_inq_cb->adv_mode == BTM_BLE_ADV_ENABLE) + { + btsnd_hcic_ble_set_adv_enable (BTM_BLE_ADV_DISABLE); + + btsnd_hcic_ble_write_adv_params (p_inq_cb->adv_interval_min, + p_inq_cb->adv_interval_max, + p_inq_cb->evt_type, + p_inq_cb->own_addr_type, + p_inq_cb->direct_bda.type, + p_inq_cb->direct_bda.bda, + p_inq_cb->adv_chnl_map, + p_inq_cb->afp); + } + } + else + { + /* random address set failure */ + BTM_TRACE_DEBUG0("set random address failed"); + } +} +/******************************************************************************* +** +** Function btm_gen_resolve_paddr_low +** +** Description This function is called when random address has generate the +** random number base for low 3 byte bd address. +** +** Returns void +** +*******************************************************************************/ +static void btm_gen_resolve_paddr_low(tBTM_RAND_ENC *p) +{ +#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE) + tBTM_LE_RANDOM_CB *p_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + tSMP_ENC output; + + BTM_TRACE_EVENT0 ("btm_gen_resolve_paddr_low"); + if (p && p->param_buf) + { + p_cb->private_addr[5] = p->param_buf[0]; + p_cb->private_addr[4] = p->param_buf[1]; + p_cb->private_addr[3] = p->param_buf[2]; + + /* encrypt with ur IRK */ + if (!SMP_Encrypt(btm_cb.devcb.id_keys.irk, BT_OCTET16_LEN, p->param_buf, 3, &output)) + { + btm_gen_resolve_paddr_cmpl(NULL); + } + else + { + btm_gen_resolve_paddr_cmpl(&output); + } + } +#endif +} +/******************************************************************************* +** +** Function btm_gen_resolvable_private_addr +** +** Description This function generate a resolvable private address. +** +** Returns void +** +*******************************************************************************/ +void btm_gen_resolvable_private_addr (void) +{ + BTM_TRACE_EVENT0 ("btm_gen_resolvable_private_addr"); + /* generate 3B rand as BD LSB, SRK with it, get BD MSB */ + if (!btsnd_hcic_ble_rand((void *)btm_gen_resolve_paddr_low)) + btm_gen_resolve_paddr_cmpl(NULL); +} +/******************************************************************************* +** +** Function btm_gen_non_resolve_paddr_cmpl +** +** Description This is the callback function when non-resolvable private +** function is generated and write to controller. +** +** Returns void +** +*******************************************************************************/ +static void btm_gen_non_resolve_paddr_cmpl(tBTM_RAND_ENC *p) +{ + tBTM_LE_RANDOM_CB *p_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + UINT8 *pp; + BTM_TRACE_EVENT0 ("btm_gen_non_resolve_paddr_cmpl"); + if (p && p->param_buf) + { + pp = p->param_buf; + STREAM_TO_BDADDR(p_cb->private_addr, pp); + /* mask off the 2 MSB */ + p_cb->private_addr[0] &= 0xfc; + /* write to controller */ + btsnd_hcic_ble_set_random_addr(p_cb->private_addr); + + btm_cb.ble_ctr_cb.inq_var.own_addr_type = BLE_ADDR_RANDOM; + } + else + { + BTM_TRACE_DEBUG0("btm_gen_non_resolvable_private_addr failed"); + } +} +/******************************************************************************* +** +** Function btm_gen_non_resolvable_private_addr +** +** Description This function generate a non-resolvable private address. +** +** +** Returns void +** +*******************************************************************************/ +void btm_gen_non_resolvable_private_addr (void) +{ + BTM_TRACE_EVENT0 ("btm_gen_non_resolvable_private_addr"); + if (!btsnd_hcic_ble_rand((void *)btm_gen_non_resolve_paddr_cmpl)) + { + btm_gen_non_resolve_paddr_cmpl(NULL); + } +} + #if SMP_INCLUDED == TRUE +/******************************************************************************* +** Utility functions for Random address resolving +*******************************************************************************/ +/******************************************************************************* +** +** Function btm_ble_resolve_address_cmpl +** +** Description This function sends the random address resolving complete +** callback. +** +** Returns None. +** +*******************************************************************************/ +static void btm_ble_resolve_address_cmpl(void) +{ + tBTM_LE_RANDOM_CB *p_mgnt_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + tBTM_SEC_DEV_REC *p_dev_rec = NULL; + + BTM_TRACE_EVENT0 ("btm_ble_resolve_address_cmpl"); + if (p_mgnt_cb->index < BTM_SEC_MAX_DEVICE_RECORDS) + p_dev_rec = &btm_cb.sec_dev_rec[p_mgnt_cb->index]; + + p_mgnt_cb->busy = FALSE; + + (* p_mgnt_cb->p_resolve_cback)(p_dev_rec, p_mgnt_cb->p); +} +/******************************************************************************* +** +** Function btm_ble_proc_resolve_x +** +** Description This function compares the X with random address 3 MSO bytes +** to find a match, if not match, continue for next record. +** +** Returns None. +** +*******************************************************************************/ +static BOOLEAN btm_ble_proc_resolve_x(tSMP_ENC *p) +{ + tBTM_LE_RANDOM_CB *p_mgnt_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + UINT8 comp[3]; + BTM_TRACE_EVENT0 ("btm_ble_proc_resolve_x"); + /* compare the hash with 3 LSB of bd address */ + comp[0] = p_mgnt_cb->random_bda[5]; + comp[1] = p_mgnt_cb->random_bda[4]; + comp[2] = p_mgnt_cb->random_bda[3]; + + if (p && p->param_buf) + { + if (!memcmp(p->param_buf, &comp[0], 3)) + { + /* match is found */ + BTM_TRACE_EVENT0 ("match is found"); + btm_ble_resolve_address_cmpl(); + return TRUE; + } + } + return FALSE; +} +/******************************************************************************* +** +** Function btm_ble_match_random_bda +** +** Description This function match the random address to the appointed device +** record, starting from calculating IRK. If record index exceed +** the maximum record number, matching failed and send callback. +** +** Returns None. +** +*******************************************************************************/ +static BOOLEAN btm_ble_match_random_bda(UINT16 rec_index) +{ +#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE) + tBTM_SEC_DEV_REC *p_dev_rec; + tBTM_LE_RANDOM_CB *p_mgnt_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + UINT8 rand[3]; + tSMP_ENC output; + + /* use the 3 MSB of bd address as prand */ + rand[0] = p_mgnt_cb->random_bda[2]; + rand[1] = p_mgnt_cb->random_bda[1]; + rand[2] = p_mgnt_cb->random_bda[0]; + + BTM_TRACE_EVENT1("btm_ble_match_random_bda rec_index = %d", rec_index); + + if (rec_index < BTM_SEC_MAX_DEVICE_RECORDS) + { + p_dev_rec = &btm_cb.sec_dev_rec[rec_index]; + + BTM_TRACE_ERROR2("sec_flags = %02x device_type = %d", p_dev_rec->sec_flags, p_dev_rec->device_type); + + if ((p_dev_rec->device_type == BT_DEVICE_TYPE_BLE) && + (p_dev_rec->ble.key_type & BTM_LE_KEY_PID)) + { + /* generate X = E irk(R0, R1, R2) and R is random address 3 LSO */ + SMP_Encrypt(p_dev_rec->ble.keys.irk, BT_OCTET16_LEN, + &rand[0], 3, &output); + return btm_ble_proc_resolve_x(&output); + } + else + { + // not completed + return FALSE; + } + } + else /* no match found */ + { + btm_ble_resolve_address_cmpl(); + return TRUE; + } +#endif +} + +/******************************************************************************* +** +** Function btm_ble_resolve_random_addr +** +** Description This function is called to resolve a random address. +** +** Returns pointer to the security record of the device whom a random +** address is matched to. +** +*******************************************************************************/ +void btm_ble_resolve_random_addr(BD_ADDR random_bda, tBTM_BLE_RESOLVE_CBACK * p_cback, void *p) +{ + tBTM_LE_RANDOM_CB *p_mgnt_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + + BTM_TRACE_EVENT0 ("btm_ble_resolve_random_addr"); + if ( !p_mgnt_cb->busy) + { + p_mgnt_cb->p = p; + p_mgnt_cb->busy = TRUE; + p_mgnt_cb->index = 0; + p_mgnt_cb->p_resolve_cback = p_cback; + memcpy(p_mgnt_cb->random_bda, random_bda, BD_ADDR_LEN); + /* start to resolve random address */ + /* check for next security record */ + while (TRUE) + { + if (btm_ble_match_random_bda(p_mgnt_cb->index++)) + { + // match found or went through the list + break; + } + } + } + else + (*p_cback)(NULL, p); +} + #endif +/******************************************************************************* +** address mapping between pseudo address and real connection address +*******************************************************************************/ +/******************************************************************************* +** +** Function btm_ble_map_bda_to_conn_bda +** +** Description This function map a BD address to the real connection address +** and return the connection address type. +*******************************************************************************/ +tBLE_ADDR_TYPE btm_ble_map_bda_to_conn_bda(BD_ADDR bd_addr) +{ + tBTM_SEC_DEV_REC *p_dev_rec = NULL; + BTM_TRACE_EVENT0 ("btm_ble_map_bda_to_conn_bda"); + if ((p_dev_rec = btm_find_dev (bd_addr)) != NULL && + p_dev_rec->device_type == BT_DEVICE_TYPE_BLE) + { + if (p_dev_rec->ble.ble_addr_type != BLE_ADDR_PUBLIC) + { + memcpy(bd_addr, p_dev_rec->ble.static_addr, BD_ADDR_LEN); + } + return p_dev_rec->ble.ble_addr_type; + } + else + return BLE_ADDR_PUBLIC; +} +/******************************************************************************* +** +** Function btm_ble_map_bda_to_pseudo_bda +** +** Description This function map a BD address to a pseudo address when the +** address given is a random address. +** +*******************************************************************************/ +void btm_ble_map_bda_to_pseudo_bda(BD_ADDR bd_addr) +{ + BTM_TRACE_EVENT0 ("btm_ble_map_bda_to_pseudo_bda"); +} +#endif + + diff --git a/stack/btm/btm_ble_bgconn.c b/stack/btm/btm_ble_bgconn.c new file mode 100644 index 0000000..bf574ab --- /dev/null +++ b/stack/btm/btm_ble_bgconn.c @@ -0,0 +1,604 @@ +/***************************************************************************** +** +** Name: btm_ble_bgconn.c +** +** Description: This file contains functions for BLE whitelist operation. +** +** +** +** Copyright (c) 1999-2010, Broadcom Corp., All Rights Reserved. +** WIDCOMM Bluetooth Core. Proprietary and confidential. +******************************************************************************/ + +#include <string.h> + +#include "bt_types.h" +#include "btu.h" +#include "btm_int.h" +#include "l2c_int.h" +#include "hcimsgs.h" + +#ifndef BTM_BLE_SCAN_PARAM_TOUT +#define BTM_BLE_SCAN_PARAM_TOUT 50 /* 50 seconds */ +#endif + +#if (BLE_INCLUDED == TRUE) +/******************************************************************************* +** +** Function btm_update_scanner_filter_policy +** +** Description This function update the filter policy of scnner or advertiser. +*******************************************************************************/ +void btm_update_scanner_filter_policy(tBTM_BLE_SFP scan_policy) +{ + tBTM_BLE_INQ_CB *p_inq = &btm_cb.ble_ctr_cb.inq_var; + BTM_TRACE_EVENT0 ("btm_update_scanner_filter_policy"); + btm_cb.ble_ctr_cb.inq_var.sfp = scan_policy; + + btsnd_hcic_ble_set_scan_params ((UINT8)((p_inq->scan_type == BTM_BLE_SCAN_MODE_NONE) ? BTM_BLE_SCAN_MODE_ACTI: p_inq->scan_type), + (UINT16)(!p_inq->scan_interval ? BTM_BLE_GAP_DISC_SCAN_INT : p_inq->scan_interval), + (UINT16)(!p_inq->scan_window ? BTM_BLE_GAP_DISC_SCAN_WIN : p_inq->scan_window), + BLE_ADDR_PUBLIC, + scan_policy); +} +/******************************************************************************* +** +** Function btm_update_adv_filter_policy +** +** Description This function update the filter policy of scnner or advertiser. +*******************************************************************************/ +void btm_update_adv_filter_policy(tBTM_BLE_AFP adv_policy) +{ + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + BTM_TRACE_EVENT0 ("btm_update_adv_filter_policy"); + p_cb->afp = adv_policy; +} +/******************************************************************************* +** +** Function btm_update_dev_to_white_list +** +** Description This function adds a device into white list. +*******************************************************************************/ +BOOLEAN btm_update_dev_to_white_list(BOOLEAN to_add, BD_ADDR bd_addr, tBLE_ADDR_TYPE addr_type) +{ + /* look up the sec device record, and find the address */ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + tBTM_SEC_DEV_REC *p_dev_rec; + BD_ADDR dummy_bda = {0}; + BOOLEAN started = FALSE, suspend = FALSE; + + if (btm_cb.btm_inq_vars.inq_active) + { + suspend = TRUE; + btsnd_hcic_ble_set_scan_enable (BTM_BLE_SCAN_DISABLE, BTM_BLE_DUPLICATE_ENABLE); + } + + if ((p_dev_rec = btm_find_dev (bd_addr)) != NULL && + p_dev_rec->device_type == BT_DEVICE_TYPE_BLE) + { + BTM_TRACE_DEBUG0("btm_update_dev_to_white_list 1"); + + if ((to_add && p_cb->num_empty_filter == 0) || + (!to_add && p_cb->num_empty_filter == p_cb->max_filter_entries)) + { + BTM_TRACE_ERROR1("num_entry available in controller: %d", p_cb->num_empty_filter); + return started; + } + + + if ( p_dev_rec->ble.ble_addr_type == BLE_ADDR_PUBLIC) + { + if (to_add) + started = btsnd_hcic_ble_add_white_list (BLE_ADDR_PUBLIC, bd_addr); + else + started = btsnd_hcic_ble_remove_from_white_list (BLE_ADDR_PUBLIC, bd_addr); + } + else + { + if (BLE_ADDR_IS_STATIC(bd_addr)) + { + if (to_add) + started = btsnd_hcic_ble_add_white_list (BLE_ADDR_RANDOM, bd_addr); + else + started = btsnd_hcic_ble_remove_from_white_list (BLE_ADDR_RANDOM, bd_addr); + + } + if (memcmp(p_dev_rec->ble.reconn_addr, dummy_bda, BD_ADDR_LEN) != 0) + { + if (to_add) + started = btsnd_hcic_ble_add_white_list (BLE_ADDR_RANDOM, p_dev_rec->ble.reconn_addr); + else + started = btsnd_hcic_ble_remove_from_white_list (BLE_ADDR_RANDOM, p_dev_rec->ble.reconn_addr); + } + } + } + /* if not a known device, shall we add it? */ + else + { + if (to_add) + started = btsnd_hcic_ble_add_white_list (addr_type, bd_addr); + else + started = btsnd_hcic_ble_remove_from_white_list (addr_type, bd_addr); + } + + if (suspend) + { + btsnd_hcic_ble_set_scan_enable (BTM_BLE_SCAN_ENABLE, BTM_BLE_DUPLICATE_DISABLE); + } + + return started; +} +/******************************************************************************* +** +** Function btm_ble_clear_white_list +** +** Description This function clears the white list. +*******************************************************************************/ +void btm_ble_clear_white_list (void) +{ + BTM_TRACE_EVENT0 ("btm_ble_clear_white_list"); + btsnd_hcic_ble_clear_white_list(); +} + +/******************************************************************************* +** +** Function btm_ble_clear_white_list_complete +** +** Description This function clears the white list complete. +*******************************************************************************/ +void btm_ble_clear_white_list_complete(UINT8 *p_data, UINT16 evt_len) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + UINT8 status; + BTM_TRACE_EVENT0 ("btm_ble_clear_white_list_complete"); + STREAM_TO_UINT8 (status, p_data); + + if (status == HCI_SUCCESS) + p_cb->num_empty_filter = p_cb->max_filter_entries; + +} +/******************************************************************************* +** +** Function btm_ble_add_2_white_list_complete +** +** Description This function read the current white list size. +*******************************************************************************/ +void btm_ble_add_2_white_list_complete(UINT8 *p, UINT16 evt_len) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + BTM_TRACE_EVENT0 ("btm_ble_add_2_white_list_complete"); + + if (*p == HCI_SUCCESS) + { + p_cb->num_empty_filter --; + } +} +/******************************************************************************* +** +** Function btm_ble_add_2_white_list_complete +** +** Description This function read the current white list size. +*******************************************************************************/ +void btm_ble_remove_from_white_list_complete(UINT8 *p, UINT16 evt_len) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + BTM_TRACE_EVENT0 ("btm_ble_remove_from_white_list_complete"); + if (*p == HCI_SUCCESS) + { + p_cb->num_empty_filter ++; + } +} +/******************************************************************************* +** +** Function btm_ble_find_dev_in_whitelist +** +** Description This function check if the device is in the white list +*******************************************************************************/ +BOOLEAN btm_ble_find_dev_in_whitelist(BD_ADDR bd_addr) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + UINT8 i; + + BTM_TRACE_EVENT0 ("btm_ble_find_dev_in_whitelist"); + + /* empty wl */ + if (p_cb->num_empty_filter == p_cb->max_filter_entries) + { + BTM_TRACE_DEBUG0("white list empty"); + return FALSE; + } + + for (i = 0; i < BTM_BLE_MAX_BG_CONN_DEV_NUM && i < p_cb->max_filter_entries; i ++) + { + if (memcmp(p_cb->bg_conn_dev_list[i], bd_addr, BD_ADDR_LEN) == 0) + return TRUE; + } + return FALSE; +} +/******************************************************************************* +** +** Function btm_ble_count_unconn_dev_in_whitelist +** +** Description This function check the number of unconnected device in white list. +*******************************************************************************/ +UINT8 btm_ble_count_unconn_dev_in_whitelist(void) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + UINT8 count = 0, i; + BD_ADDR dummy_bda ={0}; + + BTM_TRACE_EVENT0 ("btm_ble_find_dev_in_whitelist"); + + for (i = 0; i < BTM_BLE_MAX_BG_CONN_DEV_NUM && i < p_cb->max_filter_entries; i ++) + { + if (memcmp(p_cb->bg_conn_dev_list[i], dummy_bda, BD_ADDR_LEN) != 0 && + !BTM_IsAclConnectionUp(p_cb->bg_conn_dev_list[i])) + { + count ++; + } + } + return count; +} +/******************************************************************************* +** +** Function btm_update_bg_conn_list +** +** Description This function update the local background connection device list. +*******************************************************************************/ +BOOLEAN btm_update_bg_conn_list(BOOLEAN to_add, BD_ADDR bd_addr) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + UINT8 i; + BD_ADDR dummy_bda = {0}; + BTM_TRACE_EVENT0 ("btm_update_bg_conn_list"); + if ((to_add && (p_cb->bg_conn_dev_num == BTM_BLE_MAX_BG_CONN_DEV_NUM || p_cb->num_empty_filter == 0)) || + (!to_add && p_cb->num_empty_filter == p_cb->max_filter_entries)) + { + BTM_TRACE_DEBUG1("num_empty_filter = %d", p_cb->num_empty_filter); + return FALSE; + } + + for (i = 0; i < BTM_BLE_MAX_BG_CONN_DEV_NUM && i < p_cb->max_filter_entries; i ++) + { + /* to add */ + if (memcmp(p_cb->bg_conn_dev_list[i], dummy_bda, BD_ADDR_LEN) == 0 && to_add) + { + memcpy(p_cb->bg_conn_dev_list[i], bd_addr, BD_ADDR_LEN); + p_cb->bg_conn_dev_num ++; + return TRUE; + } + /* to remove */ + if (!to_add && memcmp(p_cb->bg_conn_dev_list[i], bd_addr, BD_ADDR_LEN) == 0) + { + memset(p_cb->bg_conn_dev_list[i], 0, BD_ADDR_LEN); + p_cb->bg_conn_dev_num --; + return TRUE; + } + } + return FALSE; +} +/******************************************************************************* +** +** Function btm_write_bg_conn_wl +** +** Description This function write background connection device list into +** controller. +*******************************************************************************/ +void btm_write_bg_conn_wl(void) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + UINT8 i; + BTM_TRACE_EVENT0 ("btm_write_bg_conn_wl"); + btm_ble_clear_white_list(); + + for (i = 0; i < p_cb->bg_conn_dev_num; i ++) + { + if (!btm_update_dev_to_white_list(TRUE, p_cb->bg_conn_dev_list[i], BLE_ADDR_PUBLIC)) + break; + } + return; +} +/******************************************************************************* +** +** Function btm_ble_start_auto_conn +** +** Description This function is to start/stop auto connection procedure. +** +** Parameters start: TRUE to start; FALSE to stop. +** +** Returns void +** +*******************************************************************************/ +BOOLEAN btm_ble_start_auto_conn(BOOLEAN start) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + BD_ADDR dummy_bda = {0}; + BOOLEAN exec = TRUE; + UINT16 scan_int, scan_win; + + scan_int = (p_cb->scan_int == BTM_BLE_CONN_PARAM_UNDEF) ? BTM_BLE_CONN_EST_SCAN_INT : p_cb->scan_int; + scan_win = (p_cb->scan_win == BTM_BLE_CONN_PARAM_UNDEF) ? BTM_BLE_CONN_EST_SCAN_WIND : p_cb->scan_win; + + if (start) + { + if (!l2cb.is_ble_connecting && + btm_ble_count_unconn_dev_in_whitelist() > 0) + { + if (p_cb->bg_conn_state != BLE_BG_CONN_ACTIVE && p_cb->bg_conn_dev_num > 0) + { + if (!btsnd_hcic_ble_create_ll_conn (scan_int, /* UINT16 scan_int */ + scan_win, /* UINT16 scan_win */ + 0x01, /* UINT8 white_list */ + BLE_ADDR_PUBLIC, /* UINT8 addr_type_peer */ + dummy_bda, /* BD_ADDR bda_peer */ + BLE_ADDR_PUBLIC, /* UINT8 addr_type_own */ + BTM_BLE_CONN_INT_MIN_DEF, /* UINT16 conn_int_min */ + BTM_BLE_CONN_INT_MAX_DEF, /* UINT16 conn_int_max */ + BTM_BLE_CONN_SLAVE_LATENCY_DEF, /* UINT16 conn_latency */ + BTM_BLE_CONN_TIMEOUT_DEF, /* UINT16 conn_timeout */ + 0, /* UINT16 min_len */ + 0)) /* UINT16 max_len */ + { + /* start auto connection failed */ + exec = FALSE; + } + else + { + p_cb->bg_conn_state = BLE_BG_CONN_ACTIVE; + } + } + } + else + exec = FALSE; + } + else + { + if (p_cb->bg_conn_state == BLE_BG_CONN_ACTIVE) + { + if (!btsnd_hcic_ble_create_conn_cancel()) + exec = FALSE; + else + p_cb->bg_conn_state = BLE_BG_CONN_IDLE; + } + } + return exec; +} + +/******************************************************************************* +** +** Function btm_ble_start_select_conn +** +** Description This function is to start/stop selective connection procedure. +** +** Parameters start: TRUE to start; FALSE to stop. +** p_select_cback: callback function to return application +** selection. +** +** Returns BOOLEAN: selective connectino procedure is started. +** +*******************************************************************************/ +BOOLEAN btm_ble_start_select_conn(BOOLEAN start,tBTM_BLE_SEL_CBACK *p_select_cback) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + UINT16 scan_int, scan_win; + + BTM_TRACE_EVENT0 ("btm_ble_start_select_conn"); + + scan_int = (p_cb->scan_int == BTM_BLE_CONN_PARAM_UNDEF) ? BTM_BLE_CONN_EST_SCAN_INT : p_cb->scan_int; + scan_win = (p_cb->scan_win == BTM_BLE_CONN_PARAM_UNDEF) ? BTM_BLE_CONN_EST_SCAN_WIND : p_cb->scan_win; + + if (start) + { + if (!btm_cb.btm_inq_vars.inq_active) + { + btm_cb.ble_ctr_cb.p_select_cback = p_select_cback; + + btm_update_scanner_filter_policy(SP_ADV_WL); + + if (!btsnd_hcic_ble_set_scan_params(BTM_BLE_SCAN_MODE_PASS, /* use passive scan by default */ + scan_int, /* scan interval */ + scan_win, /* scan window */ + BLE_ADDR_PUBLIC, /* own device, DUMO always use public */ + SP_ADV_WL) /* process advertising packets only from devices in the White List */ + ) + return FALSE; + + if (p_cb->inq_var.adv_mode == BTM_BLE_ADV_ENABLE) + { + BTM_TRACE_ERROR0("peripheral device cannot initiate a selective connection"); + return FALSE; + } + else if (p_cb->bg_conn_dev_num > 0 && btm_ble_count_unconn_dev_in_whitelist() > 0 ) + { + + if (!btsnd_hcic_ble_set_scan_enable(TRUE, TRUE)) /* duplicate filtering enabled */ + return FALSE; + + /* mark up inquiry status flag */ + btm_cb.btm_inq_vars.inq_active = TRUE; + btm_cb.ble_ctr_cb.inq_var.proc_mode = BTM_BLE_SELECT_SCAN; + + p_cb->bg_conn_state = BLE_BG_CONN_ACTIVE; + + } + } + else + { + BTM_TRACE_ERROR0("scan active, can not start selective connection procedure"); + return FALSE; + } + } + else /* disable selective connection mode */ + { + p_cb->p_select_cback = NULL; + btm_cb.btm_inq_vars.inq_active = FALSE; + btm_cb.ble_ctr_cb.inq_var.proc_mode = BTM_BLE_INQUIRY_NONE; + + btm_update_scanner_filter_policy(SP_ADV_ALL); + + /* stop scanning */ + if (p_cb->bg_conn_dev_num > 0) + { + if (!btsnd_hcic_ble_set_scan_enable(FALSE, TRUE)) /* duplicate filtering enabled */ + return FALSE; + } + } + return TRUE; +} +/******************************************************************************* +** +** Function btm_ble_initiate_select_conn +** +** Description This function is to start/stop selective connection procedure. +** +** Parameters start: TRUE to start; FALSE to stop. +** p_select_cback: callback function to return application +** selection. +** +** Returns BOOLEAN: selective connectino procedure is started. +** +*******************************************************************************/ +void btm_ble_initiate_select_conn(BD_ADDR bda) +{ + UINT8 addr_type; + BTM_TRACE_EVENT0 ("btm_ble_initiate_select_conn"); + addr_type = btm_ble_map_bda_to_conn_bda(bda); + + /* use direct connection procedure to initiate connection */ + if (!L2CA_ConnectFixedChnl(L2CAP_ATT_CID, bda)) + { + BTM_TRACE_ERROR0("btm_ble_initiate_select_conn failed"); + } +} +/******************************************************************************* +** +** Function btm_ble_suspend_bg_sele_conn +** +** Description This function is to suspend an active background connection +** procedure. +** +** Parameters none. +** +** Returns none. +** +*******************************************************************************/ +void btm_ble_suspend_bg_sele_conn(void) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + BTM_TRACE_EVENT0 ("btm_ble_suspend_bg_sele_conn"); + + if (p_cb->bg_conn_type == BTM_BLE_CONN_SELECTIVE) + { + p_cb->bg_conn_state = BLE_BG_CONN_SUSPEND; + btm_ble_start_select_conn(FALSE, NULL); + } +} +/******************************************************************************* +** +** Function btm_ble_suspend_bg_conn +** +** Description This function is to suspend an active background connection +** procedure. +** +** Parameters none. +** +** Returns none. +** +*******************************************************************************/ +void btm_ble_suspend_bg_conn(void) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + BTM_TRACE_EVENT0 ("btm_ble_suspend_bg_conn"); + + if (p_cb->bg_conn_type == BTM_BLE_CONN_AUTO) + { + if (btm_ble_start_auto_conn(FALSE)) + p_cb->bg_conn_state = BLE_BG_CONN_SUSPEND; + } +} +/******************************************************************************* +** +** Function btm_ble_scan_param_idle +** +** Description This function is to process the scan parameter idle timeout +** timeout. +********************************************************************************/ +void btm_ble_scan_param_idle(void) +{ + BTM_BleSetConnScanParams(BTM_BLE_CONN_EST_SCAN_INT_LO, BTM_BLE_CONN_EST_SCAN_WIND_LO); +} +/******************************************************************************* +** +** Function btm_ble_resume_bg_conn +** +** Description This function is to resume a background auto connection +** procedure. +** +** Parameters none. +** +** Returns none. +** +*******************************************************************************/ +BOOLEAN btm_ble_resume_bg_conn(tBTM_BLE_SEL_CBACK *p_sele_callback, BOOLEAN def_param) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + BOOLEAN ret = FALSE; + + if (p_cb->bg_conn_state != BLE_BG_CONN_ACTIVE ) + { + if (def_param) + { + p_cb->scan_int = BTM_BLE_CONN_PARAM_UNDEF; + p_cb->scan_win = BTM_BLE_CONN_PARAM_UNDEF; + + /* start scan param idle timer */ + btu_start_timer(&p_cb->scan_param_idle_timer, + BTU_TTYPE_BLE_SCAN_PARAM_IDLE, + BTM_BLE_SCAN_PARAM_TOUT); + } + + if (p_cb->bg_conn_type == BTM_BLE_CONN_AUTO) + ret = btm_ble_start_auto_conn(TRUE); + + if (p_cb->bg_conn_type == BTM_BLE_CONN_SELECTIVE) + { + /* terminate selective connection mode if all devices are connected */ + if (btm_ble_count_unconn_dev_in_whitelist() == 0) + { + btsnd_hcic_ble_set_scan_enable (BTM_BLE_SCAN_DISABLE, BTM_BLE_DUPLICATE_DISABLE); + btm_cb.ble_ctr_cb.inq_var.proc_mode = BTM_BLE_INQUIRY_NONE; + btm_cb.btm_inq_vars.inq_active = FALSE; + } + else if (!btm_cb.btm_inq_vars.inq_active) + btm_ble_start_select_conn(TRUE, btm_cb.ble_ctr_cb.p_select_cback); + } + + if (ret) + p_cb->bg_conn_state = BLE_BG_CONN_ACTIVE; + + } + + return ret; +} +/******************************************************************************* +** +** Function btm_ble_update_bg_state +** +** Description This function is to update the bg connection status. +** +** Parameters none. +** +** Returns none. +** +*******************************************************************************/ +void btm_ble_update_bg_state(void) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + + if (!l2cb.is_ble_connecting && (p_cb->bg_conn_state != BLE_BG_CONN_SUSPEND)) + p_cb->bg_conn_state = BLE_BG_CONN_IDLE; + +} + +#endif + diff --git a/stack/btm/btm_ble_gap.c b/stack/btm/btm_ble_gap.c new file mode 100644 index 0000000..9092d5d --- /dev/null +++ b/stack/btm/btm_ble_gap.c @@ -0,0 +1,2072 @@ +/***************************************************************************** +** +** Name: btm_ble_gap.c +** +** Description: This file contains functions for BLE GAP. +** +** +** +** Copyright (c) 2008-2011, Broadcom Corp., All Rights Reserved. +** Broadcom Bluetooth Core. Proprietary and confidential. +******************************************************************************/ + +#include <string.h> +#include <stdio.h> +#include <stddef.h> + +#include "bt_types.h" +#include "btu.h" +#include "btm_int.h" +#include "hcimsgs.h" +#if (GAP_INCLUDED == TRUE) +#include "gap_api.h" +#endif + +#if (BLE_INCLUDED == TRUE) +#define BTM_BLE_NAME_SHORT 0x01 +#define BTM_BLE_NAME_CMPL 0x02 + +#define BTM_BLE_FILTER_TARGET_UNKNOWN 0xff +#define BTM_BLE_POLICY_UNKNOWN 0xff + +#define BLE_RESOLVE_ADDR_MSB 0x40 /* most significant bit, bit7, bit6 is 01 to be resolvable random */ +#define BLE_RESOLVE_ADDR_MASK 0xc0 /* bit 6, and bit7 */ +#define BTM_BLE_IS_RESOLVE_BDA(x) ((x[0] & BLE_RESOLVE_ADDR_MASK) == BLE_RESOLVE_ADDR_MSB) + +#define BTM_EXT_BLE_RMT_NAME_TIMEOUT 30 +static tBLE_BD_ADDR le_bda_any ={BLE_ADDR_PUBLIC, {0x00,0x00,0x00,0x00,0x00,0x00}}; + + +#define BTM_BLE_VALID_CONN_DIRECT(x) (memcmp(&le_bda_any, x, sizeof(tBLE_BD_ADDR)) != 0) + +/******************************************************************************* +** Local functions +*******************************************************************************/ +static void btm_ble_update_adv_flag(UINT8 flag); +static void btm_ble_process_adv_pkt_cont(BD_ADDR bda, UINT8 addr_type, UINT8 evt_type, UINT8 *p); +static UINT8 *btm_ble_build_adv_data(tBTM_BLE_AD_MASK *p_data_mask, UINT8 **p_dst, tBTM_BLE_ADV_DATA *p_data); + + + + + + + + +/******************************************************************************* +** +** Function BTM_BleReset +** +** Description This function is called to reset ULP controller. +** +** Parameters None. +** +** Returns void +** +*******************************************************************************/ +void BTM_BleReset(void) +{ + btsnd_hcic_ble_reset(); +} + +/******************************************************************************* +** +** Function BTM_BleObserve +** +** Description This procedure keep the device listening for advertising +** events from a broadcast device. +** +** Parameters start: start or stop observe. +** white_list: use white list in observer mode or not. +** +** Returns void +** +*******************************************************************************/ +tBTM_STATUS BTM_BleObserve(BOOLEAN start, UINT8 duration, + tBTM_INQ_RESULTS_CB *p_results_cb, tBTM_CMPL_CB *p_cmpl_cb) +{ + tBTM_BLE_INQ_CB *p_inq = &btm_cb.ble_ctr_cb.inq_var; + tBTM_STATUS status = BTM_NO_RESOURCES; + + BTM_TRACE_EVENT0 ("BTM_BleObserve "); + + if (start) + { + if (p_inq->proc_mode != BTM_BLE_INQUIRY_NONE) + return BTM_BUSY; + + btm_cb.btm_inq_vars.p_inq_results_cb = p_results_cb; + btm_cb.btm_inq_vars.p_inq_cmpl_cb = p_cmpl_cb; + + /* allow config scanning type */ + if (btsnd_hcic_ble_set_scan_params ((UINT8)((p_inq->scan_type == BTM_BLE_SCAN_MODE_NONE) ? BTM_BLE_SCAN_MODE_ACTI: p_inq->scan_type), + (UINT16)(!p_inq->scan_interval ? BTM_BLE_GAP_DISC_SCAN_INT : p_inq->scan_interval), + (UINT16)(!p_inq->scan_window ? BTM_BLE_GAP_DISC_SCAN_WIN : p_inq->scan_window), + BLE_ADDR_PUBLIC, + BTM_BLE_DEFAULT_SFP)) /* assume observe always not using white list */ + { + /* start scan, disable duplicate filtering */ + if (btsnd_hcic_ble_set_scan_enable (BTM_BLE_SCAN_ENABLE, BTM_BLE_DUPLICATE_DISABLE)) + { + status = BTM_SUCCESS; + p_inq->proc_mode = BTM_BLE_OBSERVE; + btm_cb.btm_inq_vars.inq_active = TRUE; + + if (duration != 0) + { + /* start inquiry timer */ + btu_start_timer (&p_inq->inq_timer_ent, BTU_TTYPE_BLE_INQUIRY, duration); + } + } + } + } + else if (p_inq->proc_mode == BTM_BLE_OBSERVE) + { + btm_ble_stop_scan(); + } + + return status; +} + +/******************************************************************************* +** +** Function BTM_BleBroadcast +** +** Description This function is to start or stop broadcasting. +** +** Parameters start: start or stop broadcasting. +** +** Returns status. +** +*******************************************************************************/ +tBTM_STATUS BTM_BleBroadcast(BOOLEAN start) +{ + tBTM_STATUS status = BTM_NO_RESOURCES; + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + UINT8 evt_type = p_cb->scan_rsp ? BTM_BLE_DISCOVER_EVT: BTM_BLE_NON_CONNECT_EVT; + +#ifdef BTM_BLE_PC_ADV_TEST_MODE + if (BTM_BLE_PC_ADV_TEST_MODE) + { + evt_type = p_cb->scan_rsp ? BTM_BLE_CONNECT_EVT: BTM_BLE_NON_CONNECT_EVT; + } +#endif + + if (start && p_cb->adv_mode == BTM_BLE_ADV_DISABLE) + { + /* update adv params */ + if (!btsnd_hcic_ble_write_adv_params ((UINT16)(p_cb->adv_interval_min ? p_cb->adv_interval_min : BTM_BLE_GAP_ADV_INT), + (UINT16)(p_cb->adv_interval_max ? p_cb->adv_interval_max : BTM_BLE_GAP_ADV_INT), + evt_type, + p_cb->own_addr_type, + p_cb->direct_bda.type, p_cb->direct_bda.bda, + p_cb->adv_chnl_map, + p_cb->afp)) + + status = BTM_NO_RESOURCES; + else + p_cb->evt_type = evt_type; + + if (btsnd_hcic_ble_set_adv_enable (BTM_BLE_ADV_ENABLE)) + { + p_cb->adv_mode = BTM_BLE_ADV_ENABLE; + + status = BTM_SUCCESS; + } + } + else if (!start && p_cb->adv_mode == BTM_BLE_ADV_ENABLE) + { + if (btsnd_hcic_ble_set_adv_enable (BTM_BLE_ADV_DISABLE)) + { + p_cb->adv_mode = BTM_BLE_ADV_DISABLE; + + status = BTM_SUCCESS; + } + } + else + { + status = BTM_WRONG_MODE; + BTM_TRACE_ERROR2("Can not %s Broadcast, device %s in Broadcast mode", + (start ? "Start" : "Stop"), (start ? "alerady" :"not")); + } + + return status; +} + + + + + + + +/******************************************************************************* +** +** Function BTM_RegisterScanReqEvt +** +** Description This function is called to register a scan request callback +** on the advertiser. +** +** Parameters p_scan_req_cback: scan request callback. If NULL, remove the +** registration. +** +** Returns void +** +*******************************************************************************/ +void BTM_RegisterScanReqEvt(tBTM_BLE_SCAN_REQ_CBACK *p_scan_req_cback) +{ +#ifdef BTM_BLE_PC_ADV_TEST_MODE // For general stack code (e.g. BTInsight testing), we simply do not define it to exclude or set it to TRUE to include + if (BTM_BLE_PC_ADV_TEST_MODE) // For stack component, it is always defined and maps to a global variable g_bDraculaAdvertisingMode + { + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + p_cb->p_scan_req_cback = p_scan_req_cback; + } +#endif +} +/******************************************************************************* +** +** Function BTM_BleConfigPrivacy +** +** Description This function is called to enable or disable the privacy in +** the local device. +** +** Parameters enable: TRUE to enable it; FALSE to disable it. +** +** Returns void +** +*******************************************************************************/ +void BTM_BleConfigPrivacy(BOOLEAN enable) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + BTM_TRACE_EVENT0 (" BTM_BleConfigPrivacy"); + p_cb->privacy = enable; +} + +/******************************************************************************* +** +** Function BTM_BleSetBgConnType +** +** Description This function is called to set BLE connectable mode for a +** peripheral device. +** +** Parameters bg_conn_type: it can be auto connection, or selective connection. +** p_select_cback: callback function when selective connection procedure +** is being used. +** +** Returns void +** +*******************************************************************************/ +BOOLEAN BTM_BleSetBgConnType(tBTM_BLE_CONN_TYPE bg_conn_type, + tBTM_BLE_SEL_CBACK *p_select_cback) +{ + BOOLEAN started = TRUE; + + BTM_TRACE_EVENT0 ("BTM_BleSetBgConnType "); + + if (btm_cb.ble_ctr_cb.bg_conn_type != bg_conn_type) + { + switch (bg_conn_type) + { + case BTM_BLE_CONN_AUTO: + btm_ble_start_auto_conn(TRUE); + break; + + case BTM_BLE_CONN_SELECTIVE: + if (btm_cb.ble_ctr_cb.bg_conn_type == BTM_BLE_CONN_AUTO) + { + btm_ble_start_auto_conn(FALSE); + } + started = btm_ble_start_select_conn(TRUE, p_select_cback); + break; + + case BTM_BLE_CONN_NONE: + if (btm_cb.ble_ctr_cb.bg_conn_type == BTM_BLE_CONN_AUTO) + { + btm_ble_start_auto_conn(FALSE); + } + else if (btm_cb.ble_ctr_cb.bg_conn_type == BTM_BLE_CONN_SELECTIVE) + { + btm_ble_start_select_conn(FALSE, NULL); + } + started = TRUE; + break; + + default: + BTM_TRACE_ERROR1("invalid bg connection type : %d ", bg_conn_type); + started = FALSE; + break; + } + + if (started) + btm_cb.ble_ctr_cb.bg_conn_type = bg_conn_type; + } + return started; +} + +/******************************************************************************* +** +** Function BTM_BleUpdateBgConnDev +** +** Description This function is called to add or remove a device into/from +** background connection procedure. The background connection +* procedure is decided by the background connection type, it can be +* auto connection, or selective connection. +** +** Parameters add_remove: TRUE to add; FALSE to remove. +** remote_bda: device address to add/remove. +** +** Returns void +** +*******************************************************************************/ +BOOLEAN BTM_BleUpdateBgConnDev(BOOLEAN add_remove, BD_ADDR remote_bda) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + tBTM_BLE_SEL_CBACK *p_select_cback; + BOOLEAN ret = TRUE; + + BTM_TRACE_EVENT0 (" BTM_BleUpdateBgConnDev"); + + /* if auto connection is active */ + if (p_cb->bg_conn_state == BLE_BG_CONN_ACTIVE) + { + if (p_cb->bg_conn_type == BTM_BLE_CONN_AUTO) + { + /* terminate auto connection first */ + ret = btm_ble_start_auto_conn(FALSE); + } + else if (p_cb->bg_conn_type == BTM_BLE_CONN_SELECTIVE) + { + p_select_cback = btm_cb.ble_ctr_cb.p_select_cback; + ret = btm_ble_start_select_conn(FALSE, NULL); + } + } + if (ret) + { + /* update white list */ + ret = btm_update_bg_conn_list(add_remove, remote_bda); + btm_update_dev_to_white_list(add_remove, remote_bda, BLE_ADDR_PUBLIC); + } + + if (ret && p_cb->bg_conn_state == BLE_BG_CONN_IDLE) + { + if (p_cb->bg_conn_type == BTM_BLE_CONN_AUTO) + { + /* restart auto connection */ + btm_ble_start_auto_conn(TRUE); + } + else if (p_cb->bg_conn_type == BTM_BLE_CONN_SELECTIVE) + { + p_select_cback = btm_cb.ble_ctr_cb.p_select_cback; + btm_ble_start_select_conn(TRUE, p_select_cback); + } + } + return ret; +} + +/******************************************************************************* +** +** Function BTM_BleSetConnMode +** +** Description This function is called to set BLE connectable mode for a +** peripheral device. +** +** Parameters directed: is directed connectable mode, or non-directed. +** p_dir_bda: connectable direct initiator's LE device address +** +** Returns void +** +*******************************************************************************/ +tBTM_STATUS BTM_BleSetConnMode(BOOLEAN directed, tBLE_BD_ADDR *p_dir_bda) +{ + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + tBTM_STATUS status = BTM_SUCCESS; + BD_ADDR reconn_bda; + + BTM_TRACE_EVENT0 ("BTM_BleSetConnMode "); + + memcpy(&p_cb->direct_bda, &le_bda_any, sizeof(tBLE_BD_ADDR)); + p_cb->own_addr_type = BLE_ADDR_PUBLIC; + + if (directed) + { + if (p_dir_bda) + { + memcpy(&p_cb->direct_bda, p_dir_bda, sizeof(tBLE_BD_ADDR)); + + if (btm_cb.ble_ctr_cb.privacy /* && GAP privacy ad reconnect addr exist */) + { + /* write reconnect address to controller*/ + btsnd_hcic_ble_set_random_addr(reconn_bda); + } + /* else use static address or publich address */ + + } + else + status = BTM_ILLEGAL_VALUE; + } + else /* undirected connecatable */ + { + if (btm_cb.ble_ctr_cb.privacy /* GAP privacy flag enabled */) + { + /* generate resolvable private address */ + btm_gen_resolvable_private_addr(); + } /* else use publich address */ + + } + return status; +} + +/******************************************************************************* +** +** Function BTM_BleSetAdvParams +** +** Description This function is called to set advertising parameters. +** +** Parameters adv_int_min: minimum advertising interval +** adv_int_max: maximum advertising interval +** p_dir_bda: connectable direct initiator's LE device address +** chnl_map: advertising channel map. +** +** Returns void +** +*******************************************************************************/ +tBTM_STATUS BTM_BleSetAdvParams(UINT16 adv_int_min, UINT16 adv_int_max, + tBLE_BD_ADDR *p_dir_bda, + tBTM_BLE_ADV_CHNL_MAP chnl_map) +{ + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + tBTM_STATUS status = BTM_SUCCESS; + + BTM_TRACE_EVENT0 ("BTM_BleSetAdvParams"); + + if (!BTM_BLE_VALID_PRAM(adv_int_min, BTM_BLE_ADV_INT_MIN, BTM_BLE_ADV_INT_MAX) || + !BTM_BLE_VALID_PRAM(adv_int_max, BTM_BLE_ADV_INT_MIN, BTM_BLE_ADV_INT_MAX)) + { + return BTM_ILLEGAL_VALUE; + } + + p_cb->adv_interval_min = adv_int_min; + p_cb->adv_interval_max = adv_int_max; + p_cb->adv_chnl_map = chnl_map; + + if (p_dir_bda) + { + memcpy(&p_cb->direct_bda, p_dir_bda, sizeof(tBLE_BD_ADDR)); + } + else + memcpy(&p_cb->direct_bda, &le_bda_any, sizeof(tBLE_BD_ADDR)); + + if (p_cb->adv_mode == BTM_BLE_ADV_ENABLE) + { + BTM_TRACE_EVENT0 ("update params for an active adv"); + + if (p_cb->connectable_mode == BTM_BLE_NON_CONNECTABLE) + { + if (BTM_BLE_VALID_CONN_DIRECT(&p_cb->direct_bda)) + p_cb->evt_type = BTM_BLE_CONNECT_DIR_EVT; + else + p_cb->evt_type = BTM_BLE_CONNECT_EVT; + + BTM_TRACE_DEBUG1(" evt_type = %d", p_cb->evt_type); + } + + if (!btsnd_hcic_ble_set_adv_enable (BTM_BLE_ADV_DISABLE)) + status = BTM_NO_RESOURCES; + else + /* update adv params */ + if (!btsnd_hcic_ble_write_adv_params (p_cb->adv_interval_min, + p_cb->adv_interval_max, + p_cb->evt_type, + p_cb->own_addr_type, + p_cb->direct_bda.type, + p_cb->direct_bda.bda, + p_cb->adv_chnl_map, + p_cb->afp)) + + status = BTM_NO_RESOURCES; + + else if (!btsnd_hcic_ble_set_adv_enable (BTM_BLE_ADV_ENABLE)) + { + p_cb->adv_mode = BTM_BLE_ADV_DISABLE; + + status = BTM_NO_RESOURCES; + } + + } + + return status; +} + +/******************************************************************************* +** +** Function BTM_BleReadAdvParams +** +** Description This function is called to set advertising parameters. +** +** Parameters adv_int_min: minimum advertising interval +** adv_int_max: maximum advertising interval +** p_dir_bda: connectable direct initiator's LE device address +** chnl_map: advertising channel map. +** +** Returns void +** +*******************************************************************************/ +void BTM_BleReadAdvParams (UINT16 *adv_int_min, UINT16 *adv_int_max, + tBLE_BD_ADDR *p_dir_bda, tBTM_BLE_ADV_CHNL_MAP *p_chnl_map) +{ + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + + BTM_TRACE_EVENT0 ("BTM_BleReadAdvParams "); + + *adv_int_min = p_cb->adv_interval_min; + *adv_int_max = p_cb->adv_interval_max; + *p_chnl_map = p_cb->adv_chnl_map; + + if (p_dir_bda != NULL) + { + memcpy(p_dir_bda, &p_cb->direct_bda, sizeof(tBLE_BD_ADDR)); + } +} + + +/******************************************************************************* +** +** Function BTM_BleSetScanParams +** +** Description This function is called to set Scan parameters. +** +** Parameters adv_int_min: minimum advertising interval +** adv_int_max: maximum advertising interval +** p_dir_bda: connectable direct initiator's LE device address +** chnl_map: advertising channel map. +** scan_type: active scan or passive scan +** +** Returns void +** +*******************************************************************************/ +void BTM_BleSetScanParams(UINT16 scan_interval, UINT16 scan_window, tBTM_BLE_SCAN_MODE scan_mode) +{ + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + + BTM_TRACE_EVENT0 (" BTM_BleSetScanParams"); + + if (BTM_BLE_VALID_PRAM(scan_interval, BTM_BLE_SCAN_INT_MIN, BTM_BLE_SCAN_INT_MAX) && + BTM_BLE_VALID_PRAM(scan_window, BTM_BLE_SCAN_WIN_MIN, BTM_BLE_SCAN_WIN_MAX) && + (scan_mode == BTM_BLE_SCAN_MODE_ACTI || scan_mode == BTM_BLE_SCAN_MODE_PASS)) + { + p_cb->scan_type = scan_mode; + + if (BTM_BLE_CONN_PARAM_UNDEF != scan_interval) + p_cb->scan_interval = scan_interval; + + if (BTM_BLE_CONN_PARAM_UNDEF != scan_window) + p_cb->scan_window = scan_window; + } + else + { + BTM_TRACE_ERROR2("Illegal params: scan_interval = %d scan_window = %d", + scan_interval, scan_window); + } + +} + +/******************************************************************************* +** +** Function BTM_BleWriteScanRsp +** +** Description This function is called to write LE scan response. +** +** Parameters: p_scan_rsp: scan response information. +** +** Returns void +** +*******************************************************************************/ +tBTM_STATUS BTM_BleWriteScanRsp(tBTM_BLE_AD_MASK data_mask, tBTM_BLE_ADV_DATA *p_data) +{ + tBTM_STATUS status = BTM_NO_RESOURCES; + UINT8 rsp_data[BTM_BLE_AD_DATA_LEN], + *p = rsp_data; + + BTM_TRACE_EVENT0 (" BTM_BleWriteScanRsp"); + memset(rsp_data, 0, BTM_BLE_AD_DATA_LEN); + btm_ble_build_adv_data(&data_mask, &p, p_data); + + if (btsnd_hcic_ble_set_scan_rsp_data((UINT8)(p - rsp_data), rsp_data)) + { + status = BTM_SUCCESS; + + if (p_data != NULL) + btm_cb.ble_ctr_cb.inq_var.scan_rsp = TRUE; + else + btm_cb.ble_ctr_cb.inq_var.scan_rsp = FALSE; + } + else + status = BTM_ILLEGAL_VALUE; + + return status; +} + +/******************************************************************************* +** +** Function BTM_BleWriteAdvData +** +** Description This function is called to write advertising data. +** +** Parameters: None. +** +** Returns void +** +*******************************************************************************/ +tBTM_STATUS BTM_BleWriteAdvData(tBTM_BLE_AD_MASK data_mask, tBTM_BLE_ADV_DATA *p_data) +{ + tBTM_BLE_LOCAL_ADV_DATA *p_cb_data = &btm_cb.ble_ctr_cb.inq_var.adv_data; + UINT8 *p; + UINT16 mask = data_mask; + + memset(p_cb_data, 0, sizeof(tBTM_BLE_LOCAL_ADV_DATA)); + p = p_cb_data->ad_data; + p_cb_data->data_mask = data_mask; + + BTM_TRACE_EVENT0 ("BTM_BleWriteAdvData "); + p_cb_data->p_flags = btm_ble_build_adv_data(&mask, &p, p_data); + + p_cb_data->p_pad = p; + + if (data_mask != 0) + { + BTM_TRACE_ERROR0("Partial data write into ADV"); + } + + p_cb_data->data_mask &= ~mask; + + if (btsnd_hcic_ble_set_adv_data((UINT8)(p_cb_data->p_pad - p_cb_data->ad_data), + p_cb_data->ad_data)) + return BTM_SUCCESS; + else + return BTM_NO_RESOURCES; + +} + +/******************************************************************************* +** +** Function BTM_CheckAdvData +** +** Description This function is called to get ADV data for a specific type. +** +** Parameters p_adv - pointer of ADV data +** type - finding ADV data type +** p_length - return the length of ADV data not including type +** +** Returns pointer of ADV data +** +*******************************************************************************/ +UINT8 *BTM_CheckAdvData( UINT8 *p_adv, UINT8 type, UINT8 *p_length) +{ + UINT8 *p = p_adv; + UINT8 length; + UINT8 adv_type; + BTM_TRACE_API1("BTM_CheckAdvData type=0x%02X", type); + + STREAM_TO_UINT8(length, p); + + while ( length && (p - p_adv <= BTM_BLE_CACHE_ADV_DATA_MAX)) + { + STREAM_TO_UINT8(adv_type, p); + + if ( adv_type == type ) + { + /* length doesn't include itself */ + *p_length = length - 1; /* minus the length of type */ + return p; + } + p += length - 1; /* skip the length of data */ + STREAM_TO_UINT8(length, p); + } + + *p_length = 0; + return NULL; +} + +/******************************************************************************* +** +** Function btm_ble_build_adv_data +** +** Description This function is called build the adv data and rsp data. +*******************************************************************************/ +static UINT8 *btm_ble_build_adv_data(tBTM_BLE_AD_MASK *p_data_mask, UINT8 **p_dst, tBTM_BLE_ADV_DATA *p_data) +{ + UINT16 data_mask = *p_data_mask; + UINT8 *p = *p_dst, + *p_flag = NULL; + UINT16 len = BTM_BLE_AD_DATA_LEN, cp_len = 0; + UINT8 i = 0; + tBTM_BLE_ATTR *p_attr; + tBTM_BLE_PROP_ELEM *p_elem; + + BTM_TRACE_EVENT0 (" btm_ble_build_adv_data"); + + /* build the adv data structure and build the data string */ + if (data_mask) + { + /* flags */ + if (data_mask & BTM_BLE_AD_BIT_FLAGS) + { + *p++ = 2; + *p++ = BTM_BLE_AD_TYPE_FLAG; + p_flag = p; + if (p_data) + *p++ = p_data->flag; + else + *p++ = 0; + + len -= 3; + + data_mask &= ~BTM_BLE_AD_BIT_FLAGS; + } + /* device name */ + if (len > 2 && data_mask & BTM_BLE_AD_BIT_DEV_NAME) + { + if (strlen(btm_cb.cfg.bd_name) > (UINT16)(len - 2)) + { + *p++ = len - 2 + 1; + *p++ = BTM_BLE_AD_TYPE_NAME_SHORT; + ARRAY_TO_STREAM(p, btm_cb.cfg.bd_name, len - 2); + } + else + { + cp_len = (UINT16)strlen(btm_cb.cfg.bd_name); + *p++ = cp_len + 1; + *p++ = BTM_BLE_AD_TYPE_NAME_CMPL; + ARRAY_TO_STREAM(p, btm_cb.cfg.bd_name, cp_len); + } + + len -= (cp_len + 2); + data_mask &= ~BTM_BLE_AD_BIT_DEV_NAME; + } + /* manufacturer data */ + if (len > 2 && data_mask & BTM_BLE_AD_BIT_MANU && + p_data && p_data->manu.len != 0 && p_data->manu.p_val) + { + if (p_data->manu.len > (len - 2)) + cp_len = len - 2; + else + cp_len = p_data->manu.len; + + *p++ = cp_len + 1; + *p++ = BTM_BLE_AD_TYPE_MANU; + ARRAY_TO_STREAM(p, p_data->manu.p_val, cp_len); + + len -= (cp_len + 2); + data_mask &= ~BTM_BLE_AD_BIT_MANU; + } + /* TX power */ + if (len > 2 && data_mask & BTM_BLE_AD_BIT_TX_PWR) + { + *p++ = 2; + *p++ = BTM_BLE_AD_TYPE_TX_PWR; + *p++ = btm_cb.ble_ctr_cb.inq_var.tx_power; + len -= 3; + + data_mask &= ~BTM_BLE_AD_BIT_TX_PWR; + } + /* services */ + if (len > 2 && data_mask & BTM_BLE_AD_BIT_SERVICE && + p_data && p_data->services.num_service != 0 && + p_data->services.p_uuid) + { + if (p_data->services.num_service * 2 > (len - 2)) + { + cp_len = (len - 2)/2; + *p ++ = 1 + cp_len * 2; + *p++ = BTM_BLE_AD_TYPE_SRV_PART; + } + else + { + cp_len = p_data->services.num_service; + *p++ = 1 + cp_len * 2; + *p++ = BTM_BLE_AD_TYPE_SRV_CMPL; + } + for (i = 0; i < cp_len; i ++) + { + UINT16_TO_STREAM(p, *(p_data->services.p_uuid + i)); + } + + len -= (cp_len * 2 + 2); + data_mask &= ~BTM_BLE_AD_BIT_SERVICE; + } + if (len >= 6 && data_mask & BTM_BLE_AD_BIT_INT_RANGE && + p_data) + { + *p++ = 5; + *p++ = BTM_BLE_AD_TYPE_INT_RANGE; + UINT16_TO_STREAM(p, p_data->int_range.low); + UINT16_TO_STREAM(p, p_data->int_range.hi); + len -= 6; + data_mask &= ~BTM_BLE_AD_BIT_INT_RANGE; + } + if (data_mask & BTM_BLE_AD_BIT_ATTR && p_data && p_data->attr.num_attr != 0) + { + for (i = 0; i < p_data->attr.num_attr ; i ++) + { + p_attr = p_data->attr.attr_list + i; + + if (len >= (2 + 2 + p_attr->data_len))/* len byte(1) + ATTR type(1) + Uuid len(2) + value length */ + { + *p ++ = p_attr->data_len + 2 + 1; /* Uuid len + value length */ + *p ++ = BTM_BLE_AD_TYPE_ATTR; + UINT16_TO_STREAM(p, p_attr->uuid); + ARRAY_TO_STREAM(p, p_attr->p_data, p_attr->data_len); + + len -= (4 + p_attr->data_len); + } + else + break; + } + if (i == p_data->attr.num_attr) + data_mask &= ~BTM_BLE_AD_BIT_ATTR; + } + if (data_mask & BTM_BLE_AD_BIT_PROPRIETARY && p_data && p_data->p_proprietary) + { + for (i = 0; i < p_data->p_proprietary->num_elem ; i ++) + { + p_elem = p_data->p_proprietary->p_elem + i; + + if (len >= (2 + p_elem->len))/* len byte(1) + ATTR type(1) + Uuid len(2) + value length */ + { + *p ++ = p_elem->len + 1; /* Uuid len + value length */ + *p ++ = p_elem->adv_type; + ARRAY_TO_STREAM(p, p_elem->p_val, p_elem->len); + + len -= (2 + p_elem->len); + } + else + { + BTM_TRACE_WARNING0("data exceed max adv packet length"); + break; + } + } + data_mask &= ~BTM_BLE_AD_BIT_PROPRIETARY; + } + } + + *p_data_mask = data_mask; + *p_dst = p; + + return p_flag; +} + +/******************************************************************************* +** +** Function btm_ble_set_discoverability +** +** Description This function is called to set BLE discoverable mode. +** +** Parameters: mode: discoverability mode. +** +** Returns void +** +*******************************************************************************/ +tBTM_STATUS btm_ble_set_discoverability(UINT16 combined_mode) +{ + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + UINT16 mode = (combined_mode & BTM_BLE_DISCOVERABLE_MASK); + UINT8 flag = 0; + UINT8 new_mode = BTM_BLE_ADV_ENABLE; + UINT8 evt_type = (p_cb->connectable_mode == BTM_BLE_NON_CONNECTABLE) ? \ + ((p_cb->scan_rsp) ? BTM_BLE_DISCOVER_EVT : BTM_BLE_NON_CONNECT_EVT )\ + : BTM_BLE_CONNECT_EVT; + tBTM_STATUS status = BTM_SUCCESS; + + BTM_TRACE_EVENT2 ("btm_ble_set_discoverability mode=0x%0x combined_mode=0x%x", mode, combined_mode); + + /*** Check mode parameter ***/ + if (mode > BTM_BLE_MAX_DISCOVERABLE) + return(BTM_ILLEGAL_VALUE); + + p_cb->br_edr_supported_flag |= (combined_mode & BTM_DISCOVERABLE_MASK); + p_cb->discoverable_mode = mode; + + if (!p_cb->br_edr_supported_flag) + { + flag = BTM_BLE_BREDR_NOT_SPT; + BTM_TRACE_DEBUG1("btm_ble_set_discoverability (BREDR not sup)flag=0x%x",flag); + } + + BTM_TRACE_DEBUG1 ("br_edr_supported=0x%x", p_cb->br_edr_supported_flag); + + if (mode == BTM_BLE_LIMITED_DISCOVERABLE || mode == BTM_BLE_GENERAL_DISCOVERABLE) + { + BTM_TRACE_EVENT0 ("mode == BTM_BLE_LIMITED_DISCOVERABLE "); + /* write ADV data with limited disc flag */ + if (mode == BTM_BLE_LIMITED_DISCOVERABLE) + flag |= BTM_BLE_LIMIT_DISC_FLAG ; + else + flag |= BTM_BLE_GEN_DISC_FLAG; + } + else /* non-discoverable */ + { + BTM_TRACE_EVENT0 ("mode == BTM_BLE_NON_DISCOVERABLE "); + + if (p_cb->connectable_mode == BTM_BLE_NON_CONNECTABLE) + { + p_cb->br_edr_supported_flag = 0; + + BTM_TRACE_EVENT0 ("always disable adv in non-discoverable non-connectable mode if no scan rsp "); + if (!p_cb->scan_rsp ) + new_mode = BTM_BLE_ADV_DISABLE; + } + else + { + if (BTM_BLE_VALID_CONN_DIRECT(&p_cb->direct_bda)) + { + BTM_TRACE_DEBUG0("evt_type = BTM_BLE_CONNECT_DIR_EVT"); + evt_type = BTM_BLE_CONNECT_DIR_EVT; + } + else + { + BTM_TRACE_DEBUG0(" evt_type = BTM_BLE_CONNECT_EVT"); + evt_type = BTM_BLE_CONNECT_EVT; + } + } + } + btm_ble_update_adv_flag(flag); + + /* update adv params if start advertising */ + BTM_TRACE_EVENT2 ("evt_type=0x%x p-cb->evt_type=0x%x ", evt_type, p_cb->evt_type); + if (new_mode == BTM_BLE_ADV_ENABLE && evt_type != p_cb->evt_type) + { + if (p_cb->adv_mode == BTM_BLE_ADV_ENABLE) + { + BTM_TRACE_EVENT0 ("Set Adv disable"); + p_cb->adv_mode = BTM_BLE_ADV_DISABLE; + btsnd_hcic_ble_set_adv_enable (BTM_BLE_ADV_DISABLE); + } + + /* update adv params */ + if (!btsnd_hcic_ble_write_adv_params ((UINT16)(p_cb->adv_interval_min ? p_cb->adv_interval_min : BTM_BLE_GAP_ADV_INT), + (UINT16)(p_cb->adv_interval_max ? p_cb->adv_interval_max : BTM_BLE_GAP_ADV_INT), + evt_type, + p_cb->own_addr_type, + p_cb->direct_bda.type, p_cb->direct_bda.bda, + p_cb->adv_chnl_map, + p_cb->afp)) + + status = BTM_NO_RESOURCES; + else + p_cb->evt_type = evt_type; + + } + if (status == BTM_SUCCESS && p_cb->adv_mode != new_mode) + { + /* update advertising mode */ + if (!btsnd_hcic_ble_set_adv_enable (new_mode)) + status = BTM_NO_RESOURCES; + else + p_cb->adv_mode = new_mode; + } + + /* set up stop advertising timer */ + if (status == BTM_SUCCESS && mode == BTM_BLE_LIMITED_DISCOVERABLE) + { + BTM_TRACE_EVENT1 ("start timer for limited disc mode duration=%d (30 secs)", BTM_BLE_GAP_LIM_TOUT); + /* start Tgap(lim_timeout) */ + btu_start_timer (&p_cb->inq_timer_ent, BTU_TTYPE_BLE_GAP_LIM_DISC, + BTM_BLE_GAP_LIM_TOUT); + } + return status; +} + +/******************************************************************************* +** +** Function btm_ble_set_connectability +** +** Description This function is called to set BLE connectability mode. +** +** Parameters: mode: connectability mode. +** +** Returns void +** +*******************************************************************************/ +tBTM_STATUS btm_ble_set_connectability(UINT16 combined_mode) +{ + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + UINT16 mode = (combined_mode & BTM_BLE_CONNECTABLE_MASK); + UINT8 cur_flag = 0; + UINT8 cur_br_edr_not_sup_flag; + UINT8 new_flag; + UINT8 new_mode = BTM_BLE_ADV_ENABLE; + UINT8 evt_type = (p_cb->scan_rsp) ? BTM_BLE_DISCOVER_EVT: BTM_BLE_NON_CONNECT_EVT; + tBTM_STATUS status = BTM_SUCCESS; + + BTM_TRACE_EVENT2 ("btm_ble_set_connectability mode=0x%0x combined_mode=0x%x", mode, combined_mode); + + /*** Check mode parameter ***/ + if (mode > BTM_BLE_MAX_CONNECTABLE) + return(BTM_ILLEGAL_VALUE); + if (btm_cb.ble_ctr_cb.inq_var.adv_data.p_flags) + cur_flag = *btm_cb.ble_ctr_cb.inq_var.adv_data.p_flags ; + cur_br_edr_not_sup_flag = (cur_flag & ((UINT8) BTM_BLE_BREDR_NOT_SPT)); + + p_cb->br_edr_supported_flag |= ((combined_mode & BTM_CONNECTABLE_MASK) << 4); + if (p_cb->br_edr_supported_flag && cur_br_edr_not_sup_flag) + { + new_flag = cur_flag & ((UINT8) (~BTM_BLE_BREDR_NOT_SPT)); + BTM_TRACE_EVENT2 ("new flag=0x%x cur flag=0x%x",new_flag, cur_flag); + btm_ble_update_adv_flag(new_flag); + } + p_cb->connectable_mode = mode; + + if (mode == BTM_BLE_NON_CONNECTABLE) + { + if (p_cb->discoverable_mode == BTM_BLE_NON_DISCOVERABLE) + { + p_cb->br_edr_supported_flag = 0; + BTM_TRACE_EVENT0 ("always disable adv in non-discoverable non-connectable mode with no scan rsp"); + if(!p_cb->scan_rsp) + new_mode = BTM_BLE_ADV_DISABLE; + } + } + else /* connectable */ + { + BTM_TRACE_DEBUG2("btm_ble_set_connectability: mode = %04x discoverable_mode= %02x", mode, p_cb->discoverable_mode); + + if (BTM_BLE_VALID_CONN_DIRECT(&p_cb->direct_bda)) + { + BTM_TRACE_DEBUG0("evt_type = BTM_BLE_CONNECT_DIR_EVT"); + evt_type = BTM_BLE_CONNECT_DIR_EVT; + } + else + { + BTM_TRACE_DEBUG0(" evt_type = BTM_BLE_CONNECT_EVT"); + evt_type = BTM_BLE_CONNECT_EVT; + } + } + + /* update adv params if needed */ + if (p_cb->evt_type != evt_type && new_mode == BTM_BLE_ADV_ENABLE) + { + if (p_cb->adv_mode == BTM_BLE_ADV_ENABLE) + { + p_cb->adv_mode = BTM_BLE_ADV_DISABLE; + btsnd_hcic_ble_set_adv_enable (BTM_BLE_ADV_DISABLE); + } + + if (!btsnd_hcic_ble_write_adv_params ((UINT16)(p_cb->adv_interval_min ? p_cb->adv_interval_min : BTM_BLE_GAP_ADV_INT), + (UINT16)(p_cb->adv_interval_max ? p_cb->adv_interval_max : BTM_BLE_GAP_ADV_INT), + evt_type, + p_cb->own_addr_type, + p_cb->direct_bda.type, + p_cb->direct_bda.bda, + p_cb->adv_chnl_map, + p_cb->afp)) + status = BTM_NO_RESOURCES; + else + p_cb->evt_type = evt_type; + } + /* update advertising mode */ + if (status == BTM_SUCCESS && new_mode != p_cb->adv_mode) + { + if (btsnd_hcic_ble_set_adv_enable (new_mode)) + { + status = BTM_SUCCESS; + + p_cb->adv_mode = new_mode; + } + } + + return status; +} + +/******************************************************************************* +** +** Function btm_ble_start_inquiry +** +** Description This function is called to start BLE inquiry procedure. +** If the duration is zero, the periodic inquiry mode is cancelled. +** +** Parameters: mode - GENERAL or LIMITED inquiry +** p_inq_params - pointer to the BLE inquiry parameter. +** p_results_cb - callback returning pointer to results (tBTM_INQ_RESULTS) +** p_cmpl_cb - callback indicating the end of an inquiry +** +** +** +** Returns BTM_CMD_STARTED if successfully started +** BTM_ILLEGAL_VALUE if a bad parameter is detected +** BTM_NO_RESOURCES if could not allocate a message buffer +** BTM_SUCCESS - if cancelling the periodic inquiry +** BTM_BUSY - if an inquiry is already active +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ +tBTM_STATUS btm_ble_start_inquiry (UINT8 mode, UINT8 duration) +{ + tBTM_STATUS status = BTM_NO_RESOURCES; + tBTM_BLE_INQ_CB *p_inq = &btm_cb.ble_ctr_cb.inq_var; + + BTM_TRACE_DEBUG2("btm_ble_start_inquiry: mode = %02x inq_active = %d", mode, btm_cb.btm_inq_vars.inq_active); + + if (p_inq->proc_mode != BTM_BLE_INQUIRY_NONE) + { + BTM_TRACE_ERROR0("LE scan is active, can not start inquiry"); + return(BTM_BUSY); + } + + btm_update_scanner_filter_policy(SP_ADV_ALL); + + /* start scan, already enable duplicate filtering */ + if (btsnd_hcic_ble_set_scan_enable (BTM_BLE_SCAN_ENABLE, BTM_BLE_DUPLICATE_DISABLE)) + { + status = BTM_SUCCESS; + p_inq->proc_mode = mode; + + if (duration != 0) + { + /* start inquiry timer */ + btu_start_timer (&p_inq->inq_timer_ent, BTU_TTYPE_BLE_INQUIRY, duration); + } + } + + return status; +} + +/******************************************************************************* +** +** Function btm_ble_read_remote_name_cmpl +** +** Description This function is called when BLE remote name is received. +** +** Returns void +** +*******************************************************************************/ +void btm_ble_read_remote_name_cmpl(BOOLEAN status, BD_ADDR bda, UINT16 length, char *p_name) +{ + UINT8 hci_status = HCI_SUCCESS; + BD_NAME bd_name; + + memset(bd_name, 0, BD_NAME_LEN); + memcpy((UINT8*)bd_name, p_name, length); + + if ((!status) || (length==0)) + { + hci_status = HCI_ERR_HOST_TIMEOUT; + } + + btm_process_remote_name(bda, bd_name, length, hci_status); + btm_sec_rmt_name_request_complete (bda, (UINT8 *)p_name, hci_status); +} + +/******************************************************************************* +** +** Function btm_ble_read_remote_name +** +** Description This function read remote LE device name using GATT read +** procedure. +** +** Parameters: None. +** +** Returns void +** +*******************************************************************************/ +tBTM_STATUS btm_ble_read_remote_name(BD_ADDR remote_bda, tBTM_INQ_INFO *p_cur, tBTM_CMPL_CB *p_cb) +{ + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + + if (p_cur && + p_cur->results.ble_evt_type != BTM_BLE_EVT_CONN_ADV && + p_cur->results.ble_evt_type != BTM_BLE_EVT_CONN_DIR_ADV) + { + BTM_TRACE_DEBUG0("name request to non-connectable device failed."); + return BTM_ERR_PROCESSING; + } + + /* read remote device name using GATT procedure */ + if (p_inq->remname_active) + return BTM_BUSY; + + if (!GAP_BleReadPeerDevName(remote_bda, btm_ble_read_remote_name_cmpl)) + return BTM_BUSY; + + p_inq->p_remname_cmpl_cb = p_cb; + p_inq->remname_active = TRUE; + + memcpy(p_inq->remname_bda, remote_bda, BD_ADDR_LEN); + + btu_start_timer (&p_inq->rmt_name_timer_ent, + BTU_TTYPE_BTM_RMT_NAME, + BTM_EXT_BLE_RMT_NAME_TIMEOUT); + + return BTM_CMD_STARTED; +} + +/******************************************************************************* +** +** Function btm_ble_cancel_remote_name +** +** Description This function cancel read remote LE device name. +** +** Parameters: None. +** +** Returns void +** +*******************************************************************************/ +BOOLEAN btm_ble_cancel_remote_name(BD_ADDR remote_bda) +{ + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + BOOLEAN status; + + status = GAP_BleCancelReadPeerDevName(remote_bda); + + p_inq->remname_active = FALSE; + memset(p_inq->remname_bda, 0, BD_ADDR_LEN); + btu_stop_timer(&p_inq->rmt_name_timer_ent); + + return status; +} + +/******************************************************************************* +** +** Function btm_ble_update_adv_flag +** +** Description This function update the limited discoverable flag in the adv +** data. +** +** Parameters: None. +** +** Returns void +** +*******************************************************************************/ +static void btm_ble_update_adv_flag(UINT8 flag) +{ + tBTM_BLE_LOCAL_ADV_DATA *p_adv_data = &btm_cb.ble_ctr_cb.inq_var.adv_data; + UINT8 *p; + + BTM_TRACE_DEBUG1 ("btm_ble_update_adv_flag new=0x%x", flag); + + if (p_adv_data->p_flags != NULL) + { + BTM_TRACE_DEBUG1 ("btm_ble_update_adv_flag old=0x%x", *p_adv_data->p_flags); + *p_adv_data->p_flags = flag; + } + else /* no FLAGS in ADV data*/ + { + p = (p_adv_data->p_pad == NULL) ? p_adv_data->ad_data : p_adv_data->p_pad; + /* need 3 bytes space to stuff in the flags, if not */ + /* erase all written data, just for flags */ + if ((BTM_BLE_AD_DATA_LEN - (p - p_adv_data->ad_data)) < 3) + { + p = p_adv_data->p_pad = p_adv_data->ad_data; + memset(p_adv_data->ad_data, 0, BTM_BLE_AD_DATA_LEN); + } + + *p++ = 2; + *p++ = BTM_BLE_AD_TYPE_FLAG; + p_adv_data->p_flags = p; + *p++ = flag; + p_adv_data->p_pad = p; + } + + if (btsnd_hcic_ble_set_adv_data((UINT8)(p_adv_data->p_pad - p_adv_data->ad_data), + p_adv_data->ad_data)) + p_adv_data->data_mask |= BTM_BLE_AD_BIT_FLAGS; + +} + +#if 0 +/******************************************************************************* +** +** Function btm_ble_parse_adv_data +** +** Description This function parse the adv data into a structure. +** +** Returns pointer to entry, or NULL if not found +** +*******************************************************************************/ +static void btm_ble_parse_adv_data(tBTM_INQ_INFO *p_info, UINT8 *p_data, + UINT8 len, tBTM_BLE_INQ_DATA *p_adv_data, UINT8 *p_buf) +{ + UINT8 *p_cur = p_data; + UINT8 ad_len, ad_type, ad_flag; + tBTM_BLE_ATTR *p_attr; + + BTM_TRACE_EVENT0 (" btm_ble_parse_adv_data"); + + while (len > 0) + { + BTM_TRACE_DEBUG1("btm_ble_parse_adv_data: len = %d", len); + if ((ad_len = *p_cur ++) == 0) + break; + + ad_type = *p_cur ++; + + BTM_TRACE_DEBUG2(" ad_type = %02x ad_len = %d", ad_type, ad_len); + + switch (ad_type) + { + case BTM_BLE_AD_TYPE_NAME_SHORT: + + case BTM_BLE_AD_TYPE_NAME_CMPL: + p_adv_data->ad_mask |= BTM_BLE_AD_BIT_DEV_NAME; + if (p_info) + { + p_info->remote_name_type =(ad_type == BTM_BLE_AD_TYPE_NAME_SHORT) ? + BTM_BLE_NAME_SHORT: BTM_BLE_NAME_CMPL; + memcpy(p_info->remote_name, p_cur, ad_len -1); + p_info->remote_name[ad_len] = 0; + p_adv_data->p_remote_name = p_info->remote_name; + p_info->remote_name_len = p_adv_data->remote_name_len = ad_len - 1; + BTM_TRACE_DEBUG1("BTM_BLE_AD_TYPE_NAME name = %s",p_adv_data->p_remote_name); + } + p_cur += (ad_len -1); + + break; + + case BTM_BLE_AD_TYPE_FLAG: + p_adv_data->ad_mask |= BTM_BLE_AD_BIT_FLAGS; + ad_flag = *p_cur ++; + p_adv_data->flag = (UINT8)(ad_flag & BTM_BLE_ADV_FLAG_MASK) ; + BTM_TRACE_DEBUG3("BTM_BLE_AD_TYPE_FLAG flag = %s | %s | %s", + (p_adv_data->flag & BTM_BLE_LIMIT_DISC_FLAG)? "LE_LIMIT_DISC" : "", + (p_adv_data->flag & BTM_BLE_GEN_DISC_FLAG)? "LE_GENERAL_DISC" : "", + (p_adv_data->flag & BTM_BLE_BREDR_NOT_SPT)? "LE Only device" : ""); + break; + + case BTM_BLE_AD_TYPE_TX_PWR: + p_adv_data->ad_mask |= BTM_BLE_AD_BIT_TX_PWR; + p_adv_data->tx_power_level = (INT8)*p_cur ++; + BTM_TRACE_DEBUG1("BTM_BLE_AD_TYPE_TX_PWR tx_level = %d", p_adv_data->tx_power_level); + break; + + case BTM_BLE_AD_TYPE_ATTR: + p_adv_data->ad_mask |= BTM_BLE_AD_BIT_ATTR; + p_attr = &p_adv_data->attr_data.attr_list[p_adv_data->attr_data.num_attr]; + p_attr->uuid = *p_cur ++; + + if (ad_len > 3) + { + p_attr->data_len = ad_len - 3; + p_attr->p_data = p_buf; + memcpy(p_attr->p_data, p_cur, p_attr->data_len); + p_buf += p_attr->data_len; + } + + p_adv_data->attr_data.num_attr ++; + BTM_TRACE_DEBUG2("BTM_BLE_AD_TYPE_ATTR[%d] uuid = 0x%04x",p_adv_data->attr_data.num_attr, p_attr->uuid); + break; + + case BTM_BLE_AD_TYPE_MANU: + + case BTM_BLE_AD_TYPE_SRV_CMPL: + case BTM_BLE_AD_TYPE_SRV_PART: + p_adv_data->ad_mask |= ad_type; + /* need allocate memory to store UUID list */ + p_adv_data->service.num_service = (ad_len - 1)/2; + BTM_TRACE_DEBUG1("service UUID list, num = %d", p_adv_data->service.num_service); + + default: + p_cur += (ad_len - 1); + break; + } + len -= (ad_len + 1); + } +} +#endif + +/******************************************************************************* +** +** Function btm_ble_cache_adv_data +** +** Description Update advertising cache data. +** +** Returns void +** +*******************************************************************************/ +void btm_ble_cache_adv_data(tBTM_INQ_RESULTS *p_cur, UINT8 data_len, UINT8 *p, UINT8 evt_type) +{ + tBTM_BLE_INQ_CB *p_le_inq_cb = &btm_cb.ble_ctr_cb.inq_var; + UINT8 *p_cache; + UINT8 length; + + /* cache adv report/scan response data */ + if (evt_type != BTM_BLE_SCAN_RSP_EVT) + { + p_le_inq_cb->adv_len = 0; + memset(p_le_inq_cb->adv_data_cache, 0, BTM_BLE_CACHE_ADV_DATA_MAX); + } + + if (data_len > 0) + { + p_cache = &p_le_inq_cb->adv_data_cache[p_le_inq_cb->adv_len]; + STREAM_TO_UINT8(length, p); + while ( length && ((p_le_inq_cb->adv_len + length + 1) <= BTM_BLE_CACHE_ADV_DATA_MAX)) + { + /* copy from the length byte & data into cache */ + memcpy(p_cache, p-1, length+1); + /* advance the cache pointer past data */ + p_cache += length+1; + /* increment cache length */ + p_le_inq_cb->adv_len += length+1; + /* skip the length of data */ + p += length; + STREAM_TO_UINT8(length, p); + } + } + + /* parse service UUID from adv packet and save it in inq db eir_uuid */ + /* TODO */ +} +/******************************************************************************* +** +** Function btm_ble_is_discoverable +** +** Description check ADV flag to make sure device is discoverable and match +** the search condition +** +** Parameters +** +** Returns void +** +*******************************************************************************/ +BOOLEAN btm_ble_is_discoverable(BD_ADDR bda, UINT8 evt_type, UINT8 *p) +{ + BOOLEAN is_discoverable = FALSE; + UINT8 *p_flag, flag = 0; + UINT8 data_len; + tBTM_INQ_PARMS *p_cond = &btm_cb.btm_inq_vars.inqparms; + + STREAM_TO_UINT8 (data_len, p); + + /* for observer, always "discoverable */ + if (btm_cb.ble_ctr_cb.inq_var.proc_mode == BTM_BLE_OBSERVE || + (btm_cb.ble_ctr_cb.inq_var.proc_mode == BTM_BLE_SELECT_SCAN && + btm_cb.ble_ctr_cb.bg_conn_type == BTM_BLE_CONN_SELECTIVE)) + return TRUE; + + /* does not match filter condition */ + if (p_cond->filter_cond_type == BTM_FILTER_COND_BD_ADDR && + memcmp(bda, p_cond->filter_cond.bdaddr_cond, BD_ADDR_LEN) != 0) + { + BTM_TRACE_DEBUG0("BD ADDR does not meet filter condition"); + return FALSE; + } + + /* scan response does not include the flag */ + if (evt_type == BTM_BLE_SCAN_RSP_EVT) + return FALSE; + + if (data_len > BTM_BLE_ADV_DATA_LEN_MAX) + { + BTM_TRACE_WARNING1("ADV data too long %d. discard", data_len); + return FALSE; + } + + if (data_len != 0) + { + if ((p_flag = BTM_CheckAdvData(p, BTM_BLE_AD_TYPE_FLAG, &data_len)) != NULL) + { + flag = * p_flag; + + if ((btm_cb.ble_ctr_cb.inq_var.proc_mode == BTM_BLE_GENERAL_INQUIRY) && + (flag & (BTM_BLE_LIMIT_DISC_FLAG|BTM_BLE_GEN_DISC_FLAG)) != 0) + { + BTM_TRACE_DEBUG0("Find Generable Discoverable device"); + is_discoverable = TRUE; + } + + else if (btm_cb.ble_ctr_cb.inq_var.proc_mode == BTM_BLE_LIMITED_INQUIRY && + (flag & BTM_BLE_LIMIT_DISC_FLAG) != 0) + { + BTM_TRACE_DEBUG0("Find limited discoverable device"); + is_discoverable = TRUE; + } + + } + } + + if (!is_discoverable) + { + BTM_TRACE_ERROR1("discoverable flag not desired: %d", flag); + } + + return is_discoverable; +} +/******************************************************************************* +** +** Function btm_ble_update_inq_result +** +** Description Update adv packet information into inquiry result. +** +** Parameters +** +** Returns void +** +*******************************************************************************/ +BOOLEAN btm_ble_update_inq_result(tINQ_DB_ENT *p_i, UINT8 addr_type, UINT8 evt_type, UINT8 *p) +{ + BOOLEAN to_report = TRUE; + tBTM_INQ_RESULTS *p_cur = &p_i->inq_info.results; + UINT8 len; + UINT8 *p_flag; + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + UINT8 data_len, rssi; + tBTM_BLE_INQ_CB *p_le_inq_cb = &btm_cb.ble_ctr_cb.inq_var; + UINT8 *p1; + + STREAM_TO_UINT8 (data_len, p); + + if (data_len > BTM_BLE_ADV_DATA_LEN_MAX) + { + BTM_TRACE_WARNING1("EIR data too long %d. discard", data_len); + return FALSE; + } + btm_ble_cache_adv_data(p_cur, data_len, p, evt_type); + + p1 = (p + data_len); + STREAM_TO_UINT8 (rssi, p1); + + /* Save the info */ + p_cur->inq_result_type = BTM_INQ_RESULT_BLE; + p_cur->ble_addr_type = addr_type; + p_cur->rssi = rssi; + + /* active scan, always wait until get scan_rsp to report the result */ + if ((btm_cb.ble_ctr_cb.inq_var.scan_type == BTM_BLE_SCAN_MODE_ACTI && + (evt_type == BTM_BLE_CONNECT_EVT || evt_type == BTM_BLE_DISCOVER_EVT))) + { + p_i->scan_rsp = FALSE; + to_report = FALSE; + } + else + p_i->scan_rsp = TRUE; + + if (p_i->inq_count != p_inq->inq_counter) + p_cur->device_type = BT_DEVICE_TYPE_BLE; + else + p_cur->device_type |= BT_DEVICE_TYPE_BLE; + + if (evt_type != BTM_BLE_SCAN_RSP_EVT) + p_cur->ble_evt_type = evt_type; + + p_i->inq_count = p_inq->inq_counter; /* Mark entry for current inquiry */ + + if (p_le_inq_cb->adv_len != 0) + { + if ((p_flag = BTM_CheckAdvData(p_le_inq_cb->adv_data_cache, BTM_BLE_AD_TYPE_FLAG, &len)) != NULL) + p_cur->flag = * p_flag; + } + + /* if BR/EDR not supported is not set, assume is a DUMO device */ + if ((p_cur->flag & BTM_BLE_BREDR_NOT_SPT) == 0) + { + BTM_TRACE_ERROR0("BR/EDR NOT support bit not set, treat as DUMO"); + p_cur->device_type |= BT_DEVICE_TYPE_DUMO; + } + else + { + BTM_TRACE_DEBUG0("BR/EDR NOT SUPPORT bit set, LE only device"); + } + + return to_report; + +} + +/******************************************************************************* +** +** Function btm_send_sel_conn_callback +** +** Description send selection connection request callback. +** +** Parameters +** +** Returns void +** +*******************************************************************************/ +void btm_send_sel_conn_callback(BD_ADDR remote_bda, UINT8 evt_type, UINT8 *p_data, UINT8 addr_type) +{ + UINT8 data_len, len; + UINT8 *p_dev_name, remname[31] = {0}; + + if (btm_cb.ble_ctr_cb.p_select_cback == NULL || + /* non-connectable device */ + (evt_type != BTM_BLE_EVT_CONN_ADV && evt_type != BTM_BLE_EVT_CONN_DIR_ADV)) + return; + + STREAM_TO_UINT8 (data_len, p_data); + + /* get the device name if exist in ADV data */ + if (data_len != 0) + { + p_dev_name = BTM_CheckAdvData(p_data, BTM_BLE_AD_TYPE_NAME_CMPL, &len); + + if (p_dev_name == NULL) + p_dev_name = BTM_CheckAdvData(p_data, BTM_BLE_AD_TYPE_NAME_SHORT, &len); + + if (p_dev_name) + memcpy(remname, p_dev_name, len); + } + /* allow connection */ + if ((* btm_cb.ble_ctr_cb.p_select_cback)(remote_bda, remname)) + { + /* terminate selective connection, initiate connection */ + btm_ble_initiate_select_conn(remote_bda); + } +} + +/******************************************************************************* +** +** Function btm_ble_resolve_random_addr_cmpl +** +** Description resolve random address complete callback. +** +** Returns void +** +*******************************************************************************/ +static void btm_ble_resolve_random_addr_cmpl(void * p_rec, void *p) +{ + tBTM_SEC_DEV_REC *match_rec = (tBTM_SEC_DEV_REC *) p_rec; + UINT8 addr_type = BLE_ADDR_RANDOM; + BD_ADDR bda; + UINT8 *pp = (UINT8 *)p + 1; + UINT8 evt_type; + + BTM_TRACE_EVENT0 ("btm_ble_resolve_random_addr_cmpl "); + + STREAM_TO_UINT8 (evt_type, pp); + STREAM_TO_UINT8 (addr_type, pp); + STREAM_TO_BDADDR (bda, pp); + + if (match_rec) + { + BTM_TRACE_ERROR0("Random match"); + memcpy(match_rec->ble.cur_rand_addr, bda, BD_ADDR_LEN); + memcpy(bda, match_rec->bd_addr, BD_ADDR_LEN); + } + else + { + BTM_TRACE_ERROR0("Random unmatch"); + } + + btm_ble_process_adv_pkt_cont(bda, addr_type, evt_type, pp); + + return; +} + +/******************************************************************************* +** +** Function btm_ble_process_adv_pkt +** +** Description This function is called when adv packet report events are +** received from the device. It updates the inquiry database. +** If the inquiry database is full, the oldest entry is discarded. +** +** Parameters +** +** Returns void +** +*******************************************************************************/ +void btm_ble_process_adv_pkt (UINT8 *p_data) +{ + BD_ADDR bda; + UINT8 evt_type = 0, *p = p_data; + UINT8 addr_type = 0; + + BTM_TRACE_EVENT0 ("btm_ble_process_adv_pkt "); + + /* always get one device at a time */ + p ++; + + /* Extract inquiry results */ + STREAM_TO_UINT8 (evt_type, p); + STREAM_TO_UINT8 (addr_type, p); + STREAM_TO_BDADDR (bda, p); + +#ifdef BTM_BLE_PC_ADV_TEST_MODE // For general stack code (e.g. BTInsight testing), we simply do not define it to exclude or set it to TRUE to include + if (BTM_BLE_PC_ADV_TEST_MODE) // For stack component, it is always defined and maps to a global variable g_bDraculaAdvertisingMode + { + if (btm_cb.ble_ctr_cb.p_scan_req_cback) + (*btm_cb.ble_ctr_cb.p_scan_req_cback)(bda, addr_type, evt_type); + } +#endif + + /* Only process the results if the inquiry is still active */ + if (!btm_cb.btm_inq_vars.inq_active && + (btm_cb.ble_ctr_cb.bg_conn_type != BTM_BLE_CONN_SELECTIVE || + /* or selective auto connection is active */ + btm_cb.ble_ctr_cb.p_select_cback == NULL)) + return; + + +#if SMP_INCLUDED == TRUE + if (addr_type == BLE_ADDR_RANDOM && BTM_BLE_IS_RESOLVE_BDA(bda)) + { + btm_ble_resolve_random_addr(bda, btm_ble_resolve_random_addr_cmpl, p_data); + } + else +#endif + btm_ble_process_adv_pkt_cont(bda, addr_type, evt_type, p); +} + +/******************************************************************************* +** +** Function btm_ble_process_adv_pkt_cont +** +** Description This function is called after random address resolution is +** done, and proceed to process adv packet. +** +** Parameters +** +** Returns void +** +*******************************************************************************/ +static void btm_ble_process_adv_pkt_cont(BD_ADDR bda, UINT8 addr_type, UINT8 evt_type, UINT8 *p) +{ + tINQ_DB_ENT *p_i; + BOOLEAN to_report = FALSE; + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + tBTM_INQ_RESULTS_CB *p_inq_results_cb = p_inq->p_inq_results_cb; + tBTM_BLE_INQ_CB *p_le_inq_cb = &btm_cb.ble_ctr_cb.inq_var; + + p_i = btm_inq_db_find (bda); + + /* Check if this address has already been processed for this inquiry */ + if (btm_inq_find_bdaddr(bda)) + { + /* never been report as an LE device */ + if ((p_i && + (!(p_i->inq_info.results.device_type & BT_DEVICE_TYPE_BLE) || + /* scan repsonse to be updated */ + (!p_i->scan_rsp))) + || + btm_cb.ble_ctr_cb.inq_var.proc_mode == BTM_BLE_OBSERVE) + { + BTM_TRACE_DEBUG0("update new BLE information "); + to_report = TRUE; + } + else + { + BTM_TRACE_DEBUG0("LE in le_bd_db already"); + /* if yes, skip it */ + return; /* assumption: one result per event */ + } + } + else /* not been processed int his round */ + { + BTM_TRACE_DEBUG0("new LE BD_ADDR"); + to_report = TRUE; + } + + /* If existing entry, use that, else get a new one (possibly reusing the oldest) */ + if (p_i == NULL) + { + if (btm_ble_is_discoverable(bda, evt_type, p)) + { + if ((p_i = btm_inq_db_new (bda)) != NULL) + { + p_inq->inq_cmpl_info.num_resp++; + BTM_TRACE_DEBUG0("adv pkt process: new record is added into inq db"); + to_report = TRUE; + } + else + return; + } + else + { + BTM_TRACE_ERROR0("discard adv pkt"); + return; + } + } + else if (p_i->inq_count != p_inq->inq_counter) /* first time seen in this inquiry */ + { + p_inq->inq_cmpl_info.num_resp++; + } + + /* update the LE device information in inquiry database */ + if (to_report) + { + to_report = btm_ble_update_inq_result(p_i, addr_type, evt_type, p); + } + +#if BTM_USE_INQ_RESULTS_FILTER == TRUE + /* If the number of responses found and limited, issue a cancel inquiry */ + if (p_inq->inqparms.max_resps && + p_inq->inq_cmpl_info.num_resp == p_inq->inqparms.max_resps) + { + /* new device */ + if (p_i == NULL || + (/* assume a DUMO device, BR/EDR inquiry is always active */ + p_i && p_i->inq_info.results.device_type == BT_DEVICE_TYPE_BLE && p_i->scan_rsp)) + { + BTM_TRACE_WARNING0("INQ RES: Extra Response Received...cancelling inquiry.."); + + if (!(p_inq->inq_active & BTM_PERIODIC_INQUIRY_ACTIVE) ) + btsnd_hcic_inq_cancel(); + + /* stop LE scan now */ + btm_ble_stop_scan(); + +#if BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE + btm_acl_update_busy_level (BTM_BLI_INQ_DONE_EVT); +#endif + } + } +#endif + + /* background connection in selective connection mode */ + if (btm_cb.ble_ctr_cb.bg_conn_type == BTM_BLE_CONN_SELECTIVE) + { + btm_send_sel_conn_callback(bda, evt_type, p, addr_type); + } + else if (p_inq_results_cb && to_report) + { + BTM_TRACE_DEBUG0("BTMINQ LE: Found devices, send callback btm_inqrslt_cb"); + + if (p_inq->inq_active) + (p_inq_results_cb)((tBTM_INQ_RESULTS *) &p_i->inq_info.results, p_le_inq_cb->adv_data_cache); + } +} + +/******************************************************************************* +** +** Function btm_ble_stop_scan +** +** Description Stop the BLE scan. +** +** Returns void +** +*******************************************************************************/ +void btm_ble_stop_scan(void) +{ + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + + BTM_TRACE_EVENT0 ("btm_ble_stop_scan "); + + btu_stop_timer (&p_cb->inq_timer_ent); + + /* Clear the inquiry callback if set */ + p_cb->scan_type = BTM_BLE_SCAN_MODE_NONE; + p_cb->proc_mode = BTM_BLE_INQUIRY_NONE; + + /* stop discovery now */ + btsnd_hcic_ble_set_scan_enable (BTM_BLE_SCAN_DISABLE, BTM_BLE_DUPLICATE_ENABLE); + + /* If we have a callback registered for inquiry complete, call it */ + BTM_TRACE_DEBUG2 ("BTM Inq Compl Callback: status 0x%02x, num results %d", + p_inq->inq_cmpl_info.status, p_inq->inq_cmpl_info.num_resp); + + btm_update_scanner_filter_policy(SP_ADV_ALL); + + btm_process_inq_complete(HCI_SUCCESS, (UINT8)(p_inq->inqparms.mode & BTM_BLE_INQUIRY_MASK)); + +} + +/******************************************************************************* +** +** Function btm_ble_stop_adv +** +** Description Stop the BLE advertising. +** +** Returns void +** +*******************************************************************************/ +void btm_ble_stop_adv(void) +{ + BTM_TRACE_EVENT0 (" btm_ble_stop_adv"); + + if (btsnd_hcic_ble_set_adv_enable (BTM_BLE_ADV_DISABLE)) + { + btm_cb.ble_ctr_cb.inq_var.adv_mode = BTM_BLE_ADV_DISABLE; + } +} + +/******************************************************************************* +** +** Function btm_ble_timeout +** +** Description Called when BTM BLE inquiry timer expires +** +** Returns void +** +*******************************************************************************/ +void btm_ble_timeout(TIMER_LIST_ENT *p_tle) +{ + BTM_TRACE_EVENT0 ("btm_ble_timeout"); + + switch (p_tle->event) + { + case BTU_TTYPE_BLE_INQUIRY: + btm_ble_stop_scan(); + break; + + case BTU_TTYPE_BLE_GAP_LIM_DISC: + /* lim_timeout expiried, limited discovery should exit now */ + btm_ble_update_adv_flag(BTM_BLE_NON_LIMIT_DISC_FLAG); + + btm_ble_stop_adv(); + break; + + case BTU_TTYPE_BLE_RANDOM_ADDR: + if (btm_cb.ble_ctr_cb.inq_var.adv_mode == BTM_BLE_ADV_ENABLE && + btm_cb.ble_ctr_cb.inq_var.own_addr_type == BLE_ADDR_RANDOM) + { + /* refresh the random addr */ + btm_gen_resolvable_private_addr(); + } + break; + + } +} + +/******************************************************************************* +** +** Function btm_ble_connected +** +** Description This function is when a LE connection to the peer device is +** establsihed +** +** Returns void +** +*******************************************************************************/ +void btm_ble_connected (UINT8 *bda, UINT16 handle, UINT8 enc_mode, UINT8 role) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bda); + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + + BTM_TRACE_EVENT0 ("btm_ble_connected"); + + /* Commenting out trace due to obf/compilation problems. + */ +#if (BT_USE_TRACES == TRUE) + if (p_dev_rec) + { + BTM_TRACE_EVENT4 ("Security Manager: btm_sec_connected : handle:%d enc_mode:%d bda:%x RName:%s", + handle, enc_mode, + (bda[2]<<24)+(bda[3]<<16)+(bda[4]<<8)+bda[5], + p_dev_rec->sec_bd_name); + + BTM_TRACE_DEBUG1 ("btm_ble_connected sec_flags=0x%x",p_dev_rec->sec_flags); + } + else + { + BTM_TRACE_EVENT3 ("Security Manager: btm_sec_connected: handle:%d enc_mode:%d bda:%x ", + handle, enc_mode, + (bda[2]<<24)+(bda[3]<<16)+(bda[4]<<8)+bda[5]); + } +#endif + + if (!p_dev_rec) + { + /* There is no device record for new connection. Allocate one */ + p_dev_rec = btm_sec_alloc_dev (bda); + } + else /* Update the timestamp for this device */ + { + p_dev_rec->timestamp = btm_cb.dev_rec_count++; + } + + p_dev_rec->device_type = BT_DEVICE_TYPE_BLE; + p_dev_rec->hci_handle = handle; + if (role == HCI_ROLE_MASTER) + p_dev_rec->role_master = TRUE; + + p_cb->inq_var.adv_mode = BTM_BLE_ADV_DISABLE; + + return; +} + +/******************************************************************************* +** +** Function btm_ble_read_remote_features_complete +** +** Description This function is called when the command complete message +** is received from the HCI for the read LE remote feature supported +** complete event. +** +** Returns void +** +*******************************************************************************/ +void btm_ble_read_remote_features_complete(UINT8 *p) +{ + tACL_CONN *p_acl_cb = &btm_cb.acl_db[0]; + UINT8 size; + UINT16 handle; + int xx, yy; + tBTM_SEC_DEV_REC *p_dev_rec; + + BTM_TRACE_EVENT0 ("btm_ble_read_remote_features_complete "); + + STREAM_TO_UINT16 (handle, p); + STREAM_TO_UINT8 (size, 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); + break; + } + } +} + +/******************************************************************************* +** +** Function btm_ble_write_adv_enable_complete +** +** Description This function process the write adv enable command complete. +** +** Returns void +** +*******************************************************************************/ +void btm_ble_write_adv_enable_complete(UINT8 * p) +{ + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + + /* if write adv enable/disbale not succeed */ + if (*p != HCI_SUCCESS) + { + /* toggle back the adv mode */ + p_cb->adv_mode = !p_cb->adv_mode; + } +} +/******************************************************************************* +** +** Function btm_ble_init +** +** Description Initialize the control block variable values. +** +** Returns void +** +*******************************************************************************/ +void btm_ble_init (void) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + + BTM_TRACE_EVENT0 ("btm_ble_init "); + + memset(p_cb, 0, sizeof(tBTM_BLE_CB)); + + p_cb->inq_var.adv_mode = BTM_BLE_ADV_DISABLE; + p_cb->inq_var.scan_type = BTM_BLE_SCAN_MODE_NONE; + p_cb->inq_var.adv_chnl_map = BTM_BLE_DEFAULT_ADV_CHNL_MAP; + p_cb->inq_var.afp = BTM_BLE_DEFAULT_AFP; + p_cb->inq_var.sfp = BTM_BLE_DEFAULT_SFP; + p_cb->inq_var.connectable_mode = BTM_BLE_NON_CONNECTABLE; + p_cb->inq_var.discoverable_mode = BTM_BLE_NON_DISCOVERABLE; + + /* for background connection, reset connection params to be undefined */ + p_cb->scan_int = p_cb->scan_win = BTM_BLE_CONN_PARAM_UNDEF; + + p_cb->inq_var.evt_type = BTM_BLE_UNKNOWN_EVT; +} + +#endif /* BLE_INCLUDED */ diff --git a/stack/btm/btm_ble_int.h b/stack/btm/btm_ble_int.h new file mode 100644 index 0000000..b03e124 --- /dev/null +++ b/stack/btm/btm_ble_int.h @@ -0,0 +1,285 @@ +/***************************************************************************** +** +** Name: btm_ble_int.h +** +** Description: this file contains the main Bluetooth Manager (BTM) +** internal definitions. +** +** Copyright (c) 1999-2008, Broadcom Corp., All Rights Reserved +** WIDCOMM Bluetooth Core. Proprietary and confidential. +******************************************************************************/ + +#ifndef BTM_BLE_INT_H +#define BTM_BLE_INT_H + +#include "bt_target.h" +#include "gki.h" +#include "hcidefs.h" +#include "btm_ble_api.h" +#include "btm_int.h" + +#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE +#include "smp_api.h" +#endif + +#define BTM_BLE_CONNECT_EVT 0x00 +#define BTM_BLE_CONNECT_DIR_EVT 0x01 +#define BTM_BLE_DISCOVER_EVT 0x02 +#define BTM_BLE_NON_CONNECT_EVT 0x03 +#define BTM_BLE_SCAN_RSP_EVT 0x04 +#define BTM_BLE_SCAN_REQ_EVT 0x06 +#define BTM_BLE_UNKNOWN_EVT 0xff + +#define BTM_BLE_UNKNOWN_EVT 0xff + +/* scanning enable status */ +#define BTM_BLE_SCAN_ENABLE 0x01 +#define BTM_BLE_SCAN_DISABLE 0x00 + +/* advertising enable status */ +#define BTM_BLE_ADV_ENABLE 0x01 +#define BTM_BLE_ADV_DISABLE 0x00 + +/* use the high 4 bits unused by inquiry mode */ +#define BTM_BLE_SELECT_SCAN 0x20 +#define BTM_BLE_NAME_REQUEST 0x40 +#define BTM_BLE_OBSERVE 0x80 + +#define BTM_BLE_MAX_WL_ENTRY 1 +#define BTM_BLE_AD_DATA_LEN 31 + +#define BTM_BLE_ENC_MASK 0x03 + +#define BTM_BLE_DUPLICATE_ENABLE 1 +#define BTM_BLE_DUPLICATE_DISABLE 0 + +#define BTM_BLE_GAP_DISC_SCAN_INT 18 /* Interval(scan_int) = 11.25 ms= 0x0010 * 0.625 ms */ +#define BTM_BLE_GAP_DISC_SCAN_WIN 18 /* scan_window = 11.25 ms= 0x0010 * 0.625 ms */ +#define BTM_BLE_GAP_ADV_INT 512 /* Tgap(gen_disc) = 1.28 s= 512 * 0.625 ms */ +#define BTM_BLE_GAP_LIM_TOUT 30 /* Tgap(lim_timeout) = 30.72 s max, round down to 30 */ + + +#define BTM_BLE_SEC_REQ_ACT_NONE 0 +#define BTM_BLE_SEC_REQ_ACT_ENCRYPT 1 /* encrypt the link using current key or key refresh */ +#define BTM_BLE_SEC_REQ_ACT_PAIR 2 +#define BTM_BLE_SEC_REQ_ACT_DISCARD 3 /* discard the sec request while encryption is started but not completed */ +typedef UINT8 tBTM_BLE_SEC_REQ_ACT; + + + +typedef struct +{ + UINT16 data_mask; + UINT8 *p_flags; + UINT8 ad_data[BTM_BLE_AD_DATA_LEN]; + UINT8 *p_pad; +}tBTM_BLE_LOCAL_ADV_DATA; + +typedef struct +{ + UINT32 inq_count; /* Used for determining if a response has already been */ + /* received for the current inquiry operation. (We do not */ + /* want to flood the caller with multiple responses from */ + /* the same device. */ + BOOLEAN scan_rsp; + tBLE_BD_ADDR le_bda; +} tINQ_LE_BDADDR; + +#define BTM_BLE_ADV_DATA_LEN_MAX 31 +#define BTM_BLE_CACHE_ADV_DATA_MAX 62 + +#define BTM_BLE_VALID_PRAM(x, min, max) (((x) >= (min) && (x) <= (max)) || ((x) == BTM_BLE_CONN_PARAM_UNDEF)) + +typedef struct +{ + + UINT16 discoverable_mode; + UINT16 connectable_mode; + UINT16 br_edr_supported_flag; /* combined BR EDR discoverable and connectable mode */ + /* only meaningful when it is zero. This means + BR EDR is not supported*/ + UINT8 proc_mode; /* current procedure mode : inquiry or discovery */ + + UINT16 scan_window; + UINT16 scan_interval; + UINT8 scan_type; /* current scan type: active or passive */ + UINT16 adv_interval_min; + UINT16 adv_interval_max; + tBLE_ADDR_TYPE own_addr_type; + tBTM_BLE_AFP afp; /* advertising filter policy */ + tBTM_BLE_SFP sfp; /* scanning filter policy */ + + UINT8 evt_type; + UINT8 adv_mode; + tBLE_BD_ADDR direct_bda; + + UINT8 adv_len; + UINT8 adv_data_cache[BTM_BLE_CACHE_ADV_DATA_MAX]; + + /* inquiry BD addr database */ + UINT8 num_bd_entries; + UINT8 max_bd_entries; + + tBLE_BD_ADDR local_bda; + + tBTM_BLE_LOCAL_ADV_DATA adv_data; + tBTM_BLE_ADV_CHNL_MAP adv_chnl_map; + + TIMER_LIST_ENT inq_timer_ent; + BOOLEAN scan_rsp; + UINT8 state; /* Current state that the inquiry process is in */ + UINT8 tx_power; +} tBTM_BLE_INQ_CB; + + +/* random address resolving complete callback */ +typedef void (tBTM_BLE_RESOLVE_CBACK) (void * match_rec, void *p); + +/* random address management control block */ +typedef struct +{ + BD_ADDR private_addr; + BD_ADDR random_bda; + BOOLEAN busy; + UINT16 index; + tBTM_BLE_RESOLVE_CBACK *p_resolve_cback; + void *p; + TIMER_LIST_ENT raddr_timer_ent; +} tBTM_LE_RANDOM_CB; + +#define BTM_BLE_MAX_BG_CONN_DEV_NUM 10 + +typedef struct +{ + UINT16 min_conn_int; + UINT16 max_conn_int; + UINT16 slave_latency; + UINT16 supervision_tout; + +}tBTM_LE_CONN_PRAMS; + +/* Define BLE Device Management control structure +*/ +typedef struct +{ + /***************************************************** + ** BLE Inquiry + *****************************************************/ + tBTM_BLE_INQ_CB inq_var; + + /* background connection procedure cb value */ + tBTM_BLE_CONN_TYPE bg_conn_type; + UINT16 scan_int; + UINT16 scan_win; + tBTM_BLE_SEL_CBACK *p_select_cback; + TIMER_LIST_ENT scan_param_idle_timer; + + UINT8 bg_conn_dev_num; + BD_ADDR bg_conn_dev_list[BTM_BLE_MAX_BG_CONN_DEV_NUM]; + +#define BLE_BG_CONN_IDLE 0 +#define BLE_BG_CONN_ACTIVE 1 +#define BLE_BG_CONN_SUSPEND 2 + + UINT8 bg_conn_state; + + /* random address management control block */ + tBTM_LE_RANDOM_CB addr_mgnt_cb; + + /* white list information */ + UINT8 num_empty_filter; /* Number of entries in white list */ + UINT8 max_filter_entries; /* Maximum number of entries that can be stored */ + BOOLEAN enabled; + BOOLEAN privacy; /* privacy enabled or disabled */ + +#ifdef BTM_BLE_PC_ADV_TEST_MODE + tBTM_BLE_SCAN_REQ_CBACK *p_scan_req_cback; +#endif + +} tBTM_BLE_CB; + +#ifdef __cplusplus +extern "C" { +#endif + +extern void btm_ble_timeout(TIMER_LIST_ENT *p_tle); +extern void btm_ble_process_adv_pkt (UINT8 *p); +extern void btm_ble_proc_scan_rsp_rpt (UINT8 *p); +extern tBTM_STATUS btm_ble_read_remote_name(BD_ADDR remote_bda, tBTM_INQ_INFO *p_cur, tBTM_CMPL_CB *p_cb); +extern BOOLEAN btm_ble_cancel_remote_name(BD_ADDR remote_bda); + +extern tBTM_STATUS btm_ble_set_discoverability(UINT16 combined_mode); +extern tBTM_STATUS btm_ble_set_connectability(UINT16 combined_mode); +extern tBTM_STATUS btm_ble_start_inquiry (UINT8 mode, UINT8 duration); + +extern void btm_ble_stop_scan(void); +extern void btm_ble_att_db_init(void); +extern void btm_ble_init (void); +extern void btm_ble_connected (UINT8 *bda, UINT16 handle, UINT8 enc_mode, UINT8 role); +extern void btm_ble_read_remote_features_complete(UINT8 *p); +extern void btm_ble_stop_adv(void); +extern void btm_ble_write_adv_enable_complete(UINT8 * p); + +/* LE security function from btm_sec.c */ +#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE +extern void btm_ble_link_sec_check(BD_ADDR bd_addr, tBTM_LE_AUTH_REQ auth_req, tBTM_BLE_SEC_REQ_ACT *p_sec_req_act); +extern void btm_ble_ltk_request_reply(BD_ADDR bda, BOOLEAN use_stk, BT_OCTET16 stk); +extern UINT8 btm_proc_smp_cback(tSMP_EVT event, BD_ADDR bd_addr, tSMP_EVT_DATA *p_data); +extern tBTM_STATUS btm_ble_set_encryption (BD_ADDR bd_addr, void *p_ref_data, UINT8 link_role); +extern void btm_ble_ltk_request(UINT16 handle, UINT8 rand[8], UINT16 ediv); +extern BOOLEAN btm_ble_start_encrypt(BD_ADDR bda, BOOLEAN use_stk, BT_OCTET16 stk); +extern void btm_ble_link_encrypted(BD_ADDR bd_addr, UINT8 encr_enable); +#endif + +/* LE device management functions */ +extern void btm_ble_reset_id( void ); + +/* security related functions */ +extern void btm_ble_increment_sign_ctr(BD_ADDR bd_addr, BOOLEAN is_local ); +extern BOOLEAN btm_get_local_div (BD_ADDR bd_addr, UINT16 *p_div); +extern BOOLEAN btm_ble_check_link_type (BD_ADDR bd_addr); +extern BOOLEAN btm_ble_get_enc_key_type(BD_ADDR bd_addr, UINT8 *p_key_types); + +extern void btm_ble_rand_enc_complete (UINT8 *p, UINT16 op_code, tBTM_RAND_ENC_CB *p_enc_cplt_cback); +extern void btm_sec_save_le_key(BD_ADDR bd_addr, tBTM_LE_KEY_TYPE key_type, tBTM_LE_KEY_VALUE *p_keys, BOOLEAN pass_to_application); +extern void btm_ble_update_sec_key_size(BD_ADDR bd_addr, UINT8 enc_key_size); +extern UINT8 btm_ble_read_sec_key_size(BD_ADDR bd_addr); + +/* white list function */ +extern BOOLEAN btm_update_dev_to_white_list(BOOLEAN to_add, BD_ADDR bd_addr,tBLE_ADDR_TYPE addr_type); +extern BOOLEAN btm_update_bg_conn_list(BOOLEAN to_add, BD_ADDR bd_addr); +extern void btm_update_scanner_filter_policy(tBTM_BLE_SFP scan_policy); +extern void btm_update_adv_filter_policy(tBTM_BLE_AFP adv_policy); +extern void btm_ble_clear_white_list (void); +extern void btm_write_bg_conn_wl(void); + +/* background connection function */ +extern void btm_ble_suspend_bg_conn(void); +extern BOOLEAN btm_ble_resume_bg_conn(tBTM_BLE_SEL_CBACK *p_sele_callback, BOOLEAN def_param); +extern void btm_ble_update_bg_state(void); +extern void btm_ble_initiate_select_conn(BD_ADDR bda); +extern BOOLEAN btm_ble_start_auto_conn(BOOLEAN start); +extern BOOLEAN btm_ble_start_select_conn(BOOLEAN start,tBTM_BLE_SEL_CBACK *p_select_cback); +extern BOOLEAN btm_ble_find_dev_in_whitelist(BD_ADDR bd_addr); +extern BOOLEAN btm_ble_renew_bg_conn_params(BOOLEAN add, BD_ADDR bd_addr); +extern void btm_ble_scan_param_idle(void); +extern UINT8 btm_ble_count_unconn_dev_in_whitelist(void); + +/* BLE address management */ +extern tBLE_ADDR_TYPE btm_ble_map_bda_to_conn_bda(BD_ADDR bda); +extern void btm_gen_resolvable_private_addr (void); +extern void btm_gen_non_resolvable_private_addr (void); +extern void btm_ble_resolve_random_addr(BD_ADDR random_bda, tBTM_BLE_RESOLVE_CBACK * p_cback, void *p); + +#if BTM_BLE_CONFORMANCE_TESTING == TRUE +BT_API extern void btm_ble_set_no_disc_if_pair_fail (BOOLEAN disble_disc); +BT_API extern void btm_ble_set_test_mac_value (BOOLEAN enable, UINT8 *p_test_mac_val); +BT_API extern void btm_ble_set_test_local_sign_cntr_value(BOOLEAN enable, UINT32 test_local_sign_cntr); +#endif + + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/stack/btm/btm_dev.c b/stack/btm/btm_dev.c new file mode 100644 index 0000000..3651589 --- /dev/null +++ b/stack/btm/btm_dev.c @@ -0,0 +1,451 @@ +/***************************************************************************** +** * +** Name: btm_dev.c * +** * +** Description: This file contains functions for the Bluetooth Device * +** Manager * +** * +** Copyright (c) 1999-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 "gki.h" +#include "hcimsgs.h" +#include "btu.h" +#include "btm_api.h" +#include "btm_int.h" +#include "hcidefs.h" +#include "l2c_api.h" +static tBTM_SEC_DEV_REC *btm_find_oldest_dev (void); + +/******************************************************************************* +** +** Function BTM_SecAddDevice +** +** Description Add/modify device. This function will be normally called +** during host startup to restore all required information +** stored in the NVRAM. +** +** Parameters: bd_addr - BD address of the peer +** dev_class - Device Class +** bd_name - Name of the peer device. NULL if unknown. +** features - Remote device's supported features. NULL if not known +** trusted_mask - Bitwise OR of services that do not +** require authorization. (array of UINT32) +** link_key - Connection link key. NULL if unknown. +** +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ +BOOLEAN BTM_SecAddDevice (BD_ADDR bd_addr, DEV_CLASS dev_class, BD_NAME bd_name, + BD_FEATURES features, UINT32 trusted_mask[], + LINK_KEY link_key, UINT8 key_type, tBTM_IO_CAP io_cap) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + int i; + + p_dev_rec = btm_find_dev (bd_addr); + if (!p_dev_rec) + { + /* There is no device record, allocate one. + * If we can not find an empty spot for this one, let it fail. */ + for (i = 0; i < BTM_SEC_MAX_DEVICE_RECORDS; i++) + { + if (!(btm_cb.sec_dev_rec[i].sec_flags & BTM_SEC_IN_USE)) + { + p_dev_rec = &btm_cb.sec_dev_rec[i]; + + /* Mark this record as in use and initialize */ + memset (p_dev_rec, 0, sizeof (tBTM_SEC_DEV_REC)); + p_dev_rec->sec_flags = BTM_SEC_IN_USE; + memcpy (p_dev_rec->bd_addr, bd_addr, BD_ADDR_LEN); + p_dev_rec->hci_handle = BTM_GetHCIConnHandle (bd_addr); + +#if BLE_INCLUDED == TRUE + /* use default value for background connection params */ + /* update conn params, use default value for background connection params */ + memset(&p_dev_rec->conn_params, 0xff, sizeof(tBTM_LE_CONN_PRAMS)); +#endif + break; + } + } + + if (!p_dev_rec) + return(FALSE); + } + + p_dev_rec->timestamp = btm_cb.dev_rec_count++; + + if (dev_class) + memcpy (p_dev_rec->dev_class, dev_class, DEV_CLASS_LEN); + + memset(p_dev_rec->sec_bd_name, 0, sizeof(tBTM_BD_NAME)); + + if (bd_name && bd_name[0]) + { + p_dev_rec->sec_flags |= BTM_SEC_NAME_KNOWN; + BCM_STRNCPY_S ((char *)p_dev_rec->sec_bd_name, sizeof (p_dev_rec->sec_bd_name), + (char *)bd_name, BTM_MAX_REM_BD_NAME_LEN); + } + + if (features) + memcpy (p_dev_rec->features, features, sizeof (BD_FEATURES)); + else + memset (p_dev_rec->features, 0, sizeof (BD_FEATURES)); + + BTM_SEC_COPY_TRUSTED_DEVICE(trusted_mask, p_dev_rec->trusted_mask); + + if (link_key) + { + BTM_TRACE_EVENT6 ("BTM_SecAddDevice() BDA: %02x:%02x:%02x:%02x:%02x:%02x", + bd_addr[0], bd_addr[1], bd_addr[2], + bd_addr[3], bd_addr[4], bd_addr[5]); + p_dev_rec->sec_flags |= BTM_SEC_LINK_KEY_KNOWN; + memcpy (p_dev_rec->link_key, link_key, LINK_KEY_LEN); + p_dev_rec->link_key_type = key_type; + } + +#if defined(BSA_MIXED_MODE_INCLUDED) && (BSA_MIXED_MODE_INCLUDED == TRUE) + p_dev_rec->sm4 = BTM_SM4_KNOWN; +#endif + + p_dev_rec->rmt_io_caps = io_cap; + + return(TRUE); +} + + +/******************************************************************************* +** +** Function BTM_SecDeleteDevice +** +** Description Free resources associated with the device. +** +** Parameters: bd_addr - BD address of the peer +** +** Returns TRUE if removed OK, FALSE if not found or ACL link is active +** +*******************************************************************************/ +BOOLEAN BTM_SecDeleteDevice (BD_ADDR bd_addr) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + + if (BTM_IsAclConnectionUp(bd_addr)) + { + BTM_TRACE_WARNING0("BTM_SecDeleteDevice FAILED: Cannot Delete when connection is active"); + return(FALSE); + } + + if ((p_dev_rec = btm_find_dev (bd_addr)) == NULL) + return(FALSE); + + btm_sec_free_dev (p_dev_rec); + + /* Tell controller to get rid of the link key if it has one stored */ + BTM_DeleteStoredLinkKey (bd_addr, NULL); + + return(TRUE); +} + +/******************************************************************************* +** +** Function BTM_SecReadDevName +** +** Description Looks for the device name in the security database for the +** specified BD address. +** +** Returns Pointer to the name or NULL +** +*******************************************************************************/ +char *BTM_SecReadDevName (BD_ADDR bd_addr) +{ + char *p_name = NULL; + tBTM_SEC_DEV_REC *p_srec; + + if ((p_srec = btm_find_dev(bd_addr)) != NULL) + p_name = (char *)p_srec->sec_bd_name; + + return(p_name); +} + +/******************************************************************************* +** +** Function btm_sec_alloc_dev +** +** Description Look for the record in the device database for the record +** with specified handle +** +** Returns Pointer to the record or NULL +** +*******************************************************************************/ +tBTM_SEC_DEV_REC *btm_sec_alloc_dev (BD_ADDR bd_addr) +{ + tBTM_SEC_DEV_REC *p_dev_rec = NULL; + tBTM_INQ_INFO *p_inq_info; + int i; + BTM_TRACE_EVENT0 ("btm_sec_alloc_dev"); + for (i = 0; i < BTM_SEC_MAX_DEVICE_RECORDS; i++) + { + if (!(btm_cb.sec_dev_rec[i].sec_flags & BTM_SEC_IN_USE)) + { + p_dev_rec = &btm_cb.sec_dev_rec[i]; + break; + } + } + + if (!p_dev_rec) + p_dev_rec = btm_find_oldest_dev(); + + memset (p_dev_rec, 0, sizeof (tBTM_SEC_DEV_REC)); + + p_dev_rec->sec_flags = BTM_SEC_IN_USE; + + /* Check with the BT manager if details about remote device are known */ + /* outgoing connection */ + if ((p_inq_info = BTM_InqDbRead(bd_addr)) != NULL) + { + memcpy (p_dev_rec->dev_class, p_inq_info->results.dev_class, DEV_CLASS_LEN); + +#if BLE_INCLUDED == TRUE + p_dev_rec->device_type = p_inq_info->results.device_type; + p_dev_rec->ble.ble_addr_type = p_inq_info->results.ble_addr_type; + + /* update conn params, use default value for background connection params */ + memset(&p_dev_rec->conn_params, 0xff, sizeof(tBTM_LE_CONN_PRAMS)); +#endif + +#if BTM_INQ_GET_REMOTE_NAME == TRUE + if (p_inq_info->remote_name_state == BTM_INQ_RMT_NAME_DONE) + { + BCM_STRNCPY_S ((char *)p_dev_rec->sec_bd_name, sizeof (p_dev_rec->sec_bd_name), + (char *)p_inq_info->remote_name, BTM_MAX_REM_BD_NAME_LEN); + p_dev_rec->sec_flags |= BTM_SEC_NAME_KNOWN; + } +#endif + } + else + { +#if BLE_INCLUDED == TRUE + p_dev_rec->device_type = BT_DEVICE_TYPE_BREDR; /* initialize it as BR/EDR device */ + /* update conn params, use default value for background connection params */ + memset(&p_dev_rec->conn_params, 0xff, sizeof(tBTM_LE_CONN_PRAMS)); +#endif + + if (!memcmp (bd_addr, btm_cb.connecting_bda, BD_ADDR_LEN)) + memcpy (p_dev_rec->dev_class, btm_cb.connecting_dc, DEV_CLASS_LEN); + } + + memcpy (p_dev_rec->bd_addr, bd_addr, BD_ADDR_LEN); + + p_dev_rec->hci_handle = BTM_GetHCIConnHandle (bd_addr); + p_dev_rec->timestamp = btm_cb.dev_rec_count++; + + return(p_dev_rec); +} + + +/******************************************************************************* +** +** Function btm_sec_free_dev +** +** Description Mark device record as not used +** +*******************************************************************************/ +void btm_sec_free_dev (tBTM_SEC_DEV_REC *p_dev_rec) +{ + p_dev_rec->sec_flags = 0; + +#if BLE_INCLUDED == TRUE + /* Clear out any saved BLE keys */ + btm_sec_clear_ble_keys (p_dev_rec); +#endif + + +} + +/******************************************************************************* +** +** Function btm_dev_support_switch +** +** Description This function is called by the L2CAP to check if remote +** device supports role switch +** +** Parameters: bd_addr - Address of the peer device +** +** Returns TRUE if device is known and role switch is supported +** +*******************************************************************************/ +BOOLEAN btm_dev_support_switch (BD_ADDR bd_addr) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + UINT8 xx; + BOOLEAN feature_empty = TRUE; + +#if BTM_SCO_INCLUDED == TRUE + /* Role switch is not allowed if a SCO is up */ + if (btm_is_sco_active_by_bdaddr(bd_addr)) + return(FALSE); +#endif + p_dev_rec = btm_find_dev (bd_addr); + if (p_dev_rec && HCI_SWITCH_SUPPORTED(btm_cb.devcb.local_features)) + { + if (HCI_SWITCH_SUPPORTED(p_dev_rec->features)) + { + BTM_TRACE_DEBUG0("btm_dev_support_switch return TRUE (feature found)"); + return (TRUE); + } + + /* If the feature field is all zero, we never received them */ + for (xx = 0 ; xx < BD_FEATURES_LEN ; xx++) + { + if (p_dev_rec->features[xx] != 0x00) + { + feature_empty = FALSE; /* at least one is != 0 */ + break; + } + } + + /* If we don't know peer's capabilities, assume it supports Role-switch */ + if (feature_empty) + { + BTM_TRACE_DEBUG0("btm_dev_support_switch return TRUE (feature empty)"); + return (TRUE); + } + } + + BTM_TRACE_DEBUG0("btm_dev_support_switch return FALSE"); + return(FALSE); +} + +/******************************************************************************* +** +** Function btm_find_dev_by_handle +** +** Description Look for the record in the device database for the record +** with specified handle +** +** Returns Pointer to the record or NULL +** +*******************************************************************************/ +tBTM_SEC_DEV_REC *btm_find_dev_by_handle (UINT16 handle) +{ + tBTM_SEC_DEV_REC *p_dev_rec = &btm_cb.sec_dev_rec[0]; + int i; + + for (i = 0; i < BTM_SEC_MAX_DEVICE_RECORDS; i++, p_dev_rec++) + { + if ((p_dev_rec->sec_flags & BTM_SEC_IN_USE) + && (p_dev_rec->hci_handle == handle)) + return(p_dev_rec); + } + return(NULL); +} + +/******************************************************************************* +** +** Function btm_find_dev +** +** Description Look for the record in the device database for the record +** with specified BD address +** +** Returns Pointer to the record or NULL +** +*******************************************************************************/ +tBTM_SEC_DEV_REC *btm_find_dev (BD_ADDR bd_addr) +{ + tBTM_SEC_DEV_REC *p_dev_rec = &btm_cb.sec_dev_rec[0]; + int i; + + if (bd_addr) + { + for (i = 0; i < BTM_SEC_MAX_DEVICE_RECORDS; i++, p_dev_rec++) + { + if ((p_dev_rec->sec_flags & BTM_SEC_IN_USE) + && (!memcmp (p_dev_rec->bd_addr, bd_addr, BD_ADDR_LEN))) + return(p_dev_rec); + } + } + return(NULL); +} + +/******************************************************************************* +** +** Function btm_find_or_alloc_dev +** +** Description Look for the record in the device database for the record +** with specified BD address +** +** Returns Pointer to the record or NULL +** +*******************************************************************************/ +tBTM_SEC_DEV_REC *btm_find_or_alloc_dev (BD_ADDR bd_addr) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + BTM_TRACE_EVENT0 ("btm_find_or_alloc_dev"); + if ((p_dev_rec = btm_find_dev (bd_addr)) == NULL) + { + + /* Allocate a new device record or reuse the oldest one */ + p_dev_rec = btm_sec_alloc_dev (bd_addr); + } + return(p_dev_rec); +} + +/******************************************************************************* +** +** Function btm_find_oldest_dev +** +** Description Locates the oldest device in use. It first looks for +** the oldest non-paired device. If all devices are paired it +** deletes the oldest paired device. +** +** Returns Pointer to the record or NULL +** +*******************************************************************************/ +tBTM_SEC_DEV_REC *btm_find_oldest_dev (void) +{ + tBTM_SEC_DEV_REC *p_dev_rec = &btm_cb.sec_dev_rec[0]; + tBTM_SEC_DEV_REC *p_oldest = p_dev_rec; + UINT32 ot = 0xFFFFFFFF; + int i; + + /* First look for the non-paired devices for the oldest entry */ + for (i = 0; i < BTM_SEC_MAX_DEVICE_RECORDS; i++, p_dev_rec++) + { + if (((p_dev_rec->sec_flags & BTM_SEC_IN_USE) == 0) + || ((p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN) != 0)) + continue; /* Device is paired so skip it */ + + if (p_dev_rec->timestamp < ot) + { + p_oldest = p_dev_rec; + ot = p_dev_rec->timestamp; + } + } + + if (ot != 0xFFFFFFFF) + return(p_oldest); + + /* All devices are paired; find the oldest */ + p_dev_rec = &btm_cb.sec_dev_rec[0]; + for (i = 0; i < BTM_SEC_MAX_DEVICE_RECORDS; i++, p_dev_rec++) + { + if ((p_dev_rec->sec_flags & BTM_SEC_IN_USE) == 0) + continue; + + if (p_dev_rec->timestamp < ot) + { + p_oldest = p_dev_rec; + ot = p_dev_rec->timestamp; + } + } + return(p_oldest); +} + + diff --git a/stack/btm/btm_devctl.c b/stack/btm/btm_devctl.c new file mode 100644 index 0000000..f668caf --- /dev/null +++ b/stack/btm/btm_devctl.c @@ -0,0 +1,1935 @@ +/***************************************************************************** +** +** Name: btm_devctl.c +** +** Description: This file contains functions that handle BTM interface +** functions for the Bluetooth device including Rest, HCI +** buffer size and others +** +** Copyright (c) 1999-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 "hcimsgs.h" +#include "btu.h" +#include "btm_int.h" +#include "l2c_int.h" + +#if BLE_INCLUDED == TRUE +#include "gatt_int.h" + +#if GAP_INCLUDED == TRUE +#include "gap_api.h" +#include "gattdefs.h" +#endif + +#endif /* BLE_INCLUDED */ + +/* BTM_APP_DEV_INIT should be defined if additional controller initialization is +** needed by the application to be performed after the HCI reset +*/ +#ifdef BTM_APP_DEV_INIT +extern void BTM_APP_DEV_INIT(void); +#endif + +#ifdef BTA_PRM_CHECK_FW_VER +extern BOOLEAN BTA_PRM_CHECK_FW_VER(UINT8 *p); +#endif + +#ifndef TT_DEV_RESET_MASK +#define TT_DEV_RESET_MASK 0xff +#endif + +/********************************************************************************/ +/* L O C A L D A T A D E F I N I T I O N S */ +/********************************************************************************/ + +#ifndef BTM_DEV_RESET_TIMEOUT +#define BTM_DEV_RESET_TIMEOUT 4 +#endif + +#define BTM_DEV_REPLY_TIMEOUT 2 /* 1 second expiration time is not good. Timer may start between 0 and 1 second. */ + /* if it starts at the very end of the 0 second, timer will expire really easily. */ + +#define BTM_INFO_TIMEOUT 5 /* 5 seconds for info response */ + +/* After Reset a timeout can be specified in the target.h for specific targets + * that may require additional time to reset + * otherwise no timeout is required +*/ +#ifndef BTM_AFTER_RESET_TIMEOUT +#define BTM_AFTER_RESET_TIMEOUT 0 +#endif + +/* Internal baseband so the parameters such as local features, version etc. are known +so there is no need to issue HCI commands and wait for responses at BTM initialization */ +#ifndef BTM_INTERNAL_BB +#define BTM_INTERNAL_BB FALSE +#endif + +/* The local version information in the format specified in the HCI read local version +response message */ +#ifndef BTM_INTERNAL_LOCAL_VER +#define BTM_INTERNAL_LOCAL_VER {0x00, 0x01, 0x05, 0x81, 0x01, 0x30, 0x00, 0x40, 0x8D} +#endif + +/* The local features information in the format specified in the HCI read local features +response message */ +#ifndef BTM_INTERNAL_LOCAL_FEA +#define BTM_INTERNAL_LOCAL_FEA {0x00, 0xFF, 0xF9, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00} +#endif + +#ifndef BTM_SET_DEV_NAME_UPON_RESET +#define BTM_SET_DEV_NAME_UPON_RESET TRUE +#endif + +/* host SCO buffer size */ +#ifndef BTM_SCO_HOST_BUF_SIZE +#define BTM_SCO_HOST_BUF_SIZE 0xff +#endif + +#ifndef BTM_GPS_UIPC_CH_NB +#define BTM_GPS_UIPC_CH_NB UIPC_CH_ID_1 +#endif + +/********************************************************************************/ +/* L O C A L F U N C T I O N P R O T O T Y P E S */ +/********************************************************************************/ +static void btm_dev_reset (void); +static void btm_after_reset_hold_complete (void); +static void btm_continue_reset (void); + +/******************************************************************************* +** +** Function btm_dev_init +** +** Description This function is on the BTM startup +** +** Returns void +** +*******************************************************************************/ +void btm_dev_init (void) +{ +#if 0 /* cleared in btm_init; put back in if called from anywhere else! */ + memset (&btm_cb.devcb, 0, sizeof (tBTM_DEVCB)); +#endif + + /* Initialize nonzero defaults */ +#if (BTM_MAX_LOC_BD_NAME_LEN > 0) + memset(btm_cb.cfg.bd_name, 0, sizeof(tBTM_LOC_BD_NAME)); +#if (BTM_USE_DEF_LOCAL_NAME == TRUE) + BCM_STRNCPY_S(btm_cb.cfg.bd_name, sizeof(btm_cb.cfg.bd_name), BTM_DEF_LOCAL_NAME, BTM_MAX_LOC_BD_NAME_LEN); +#endif +#endif + + btm_cb.devcb.reset_timer.param = (TIMER_PARAM_TYPE)TT_DEV_RESET; + btm_cb.devcb.rln_timer.param = (TIMER_PARAM_TYPE)TT_DEV_RLN; + btm_cb.devcb.rlinkp_timer.param = (TIMER_PARAM_TYPE)TT_DEV_RLNKP; + + btm_cb.btm_acl_pkt_types_supported = BTM_ACL_PKT_TYPES_MASK_DH1 + BTM_ACL_PKT_TYPES_MASK_DM1 + + BTM_ACL_PKT_TYPES_MASK_DH3 + BTM_ACL_PKT_TYPES_MASK_DM3 + + BTM_ACL_PKT_TYPES_MASK_DH5 + BTM_ACL_PKT_TYPES_MASK_DM5; + + btm_cb.btm_sco_pkt_types_supported = BTM_SCO_PKT_TYPES_MASK_HV1 + + BTM_SCO_PKT_TYPES_MASK_HV2 + + BTM_SCO_PKT_TYPES_MASK_HV3 + + BTM_SCO_PKT_TYPES_MASK_EV3 + + BTM_SCO_PKT_TYPES_MASK_EV4 + + BTM_SCO_PKT_TYPES_MASK_EV5; + + btm_cb.first_disabled_channel = 0xff; /* To allow disabling 0th channel alone */ + btm_cb.last_disabled_channel = 0xff; /* To allow disabling 0th channel alone */ + +#if (BTM_AUTOMATIC_HCI_RESET == TRUE) + +#if (BTM_FIRST_RESET_DELAY > 0) + btm_cb.devcb.state = BTM_DEV_STATE_WAIT_RESET_CMPLT; + btu_start_timer (&btm_cb.devcb.reset_timer, BTU_TTYPE_BTM_DEV_CTL, BTM_FIRST_RESET_DELAY); +#else + btm_dev_reset(); +#endif + +#else + BTM_TRACE_EVENT0 ("BTM_AUTOMATIC_HCI_RESET is FALSE, so skip btm reset for now"); +#endif + +} + + +/******************************************************************************* +** +** Function btm_db_reset +** +** Description This function is called by BTM_DeviceReset and clears out any +** pending callbacks for inquiries, discoveries, other pending +** functions that may be in progress. +** +** Returns void +** +*******************************************************************************/ +static void btm_db_reset (void) +{ + tBTM_CMPL_CB *p_cb; + tBTM_STATUS status = BTM_DEV_RESET; + + btm_inq_db_reset(); + + if (btm_cb.devcb.p_rln_cmpl_cb) + { + p_cb = btm_cb.devcb.p_rln_cmpl_cb; + btm_cb.devcb.p_rln_cmpl_cb = NULL; + + if (p_cb) + (*p_cb)((void *) NULL); + } + + if (btm_cb.devcb.p_rlinkp_cmpl_cb) + { + p_cb = btm_cb.devcb.p_rlinkp_cmpl_cb; + btm_cb.devcb.p_rlinkp_cmpl_cb = NULL; + + if (p_cb) + (*p_cb)((void *) &status); + } + + if (btm_cb.devcb.p_rssi_cmpl_cb) + { + p_cb = btm_cb.devcb.p_rssi_cmpl_cb; + btm_cb.devcb.p_rssi_cmpl_cb = NULL; + + if (p_cb) + (*p_cb)((tBTM_RSSI_RESULTS *) &status); + } +} + + + +/******************************************************************************* +** +** Function btm_dev_absent +** +** Description This function is called by when it is detected that the +** device is not connected any more. +** +** Returns void +** +*******************************************************************************/ +void btm_dev_absent (void) +{ + btm_cb.devcb.state = BTM_DEV_STATE_WAIT_RESET_CMPLT; + + btm_db_reset (); + btm_inq_db_reset(); + + /* If anyone wants device status notifications, give him one */ + btm_report_device_status (BTM_DEV_STATUS_DOWN); + + btu_stop_timer (&btm_cb.devcb.reset_timer); +} + + +/******************************************************************************* +** +** Function BTM_DeviceReset +** +** Description This function is called to reset the HCI. Callback function +** if provided is called when startup of the device is +** completed. +** +** Returns void +** +*******************************************************************************/ +void BTM_DeviceReset (tBTM_CMPL_CB *p_cb) +{ + tBTM_STATUS status; + + /* If device is already resetting, do not allow another */ + if ((!btm_cb.devcb.p_reset_cmpl_cb) || (btm_cb.devcb.p_reset_cmpl_cb == p_cb)) + { + /* Flush all ACL connections */ + btm_acl_device_down(); + + /* Clear the callback, so application would not hang on reset */ + btm_db_reset(); + + /* Save address of the completion routine, if provided */ + btm_cb.devcb.p_reset_cmpl_cb = p_cb; + + btm_dev_reset (); + } + else + { + /* pass an error to the bad callback, another one was already provided */ + if (p_cb) + { + status = BTM_ILLEGAL_VALUE; + p_cb (&status); + } + } +} + + +/******************************************************************************* +** +** Function BTM_IsDeviceUp +** +** Description This function is called to check if the device is up. +** +** Returns TRUE if device is up, else FALSE +** +*******************************************************************************/ +BOOLEAN BTM_IsDeviceUp (void) +{ + return ((BOOLEAN) (btm_cb.devcb.state == BTM_DEV_STATE_READY)); +} + +/******************************************************************************* +** +** Function BTM_SetAfhChannels +** +** Description This function is called disable channels +** +** Returns tBTM_STATUS +** +*******************************************************************************/ +tBTM_STATUS BTM_SetAfhChannels (UINT8 first, UINT8 last) +{ + BTM_TRACE_API4 ("BTM_SetAfhChannels first: %d (%d) last: %d (%d)", + first, btm_cb.first_disabled_channel, last, + btm_cb.last_disabled_channel); + + /* Make sure the local device supports the feature before sending */ + if ((!HCI_LMP_AFH_CAP_MASTR_SUPPORTED(btm_cb.devcb.local_features)) && + (!HCI_LMP_AFH_CLASS_SLAVE_SUPPORTED(btm_cb.devcb.local_features)) && + (!HCI_LMP_AFH_CLASS_MASTR_SUPPORTED(btm_cb.devcb.local_features))) + return (BTM_MODE_UNSUPPORTED); + + if (!BTM_IsDeviceUp()) + return (BTM_WRONG_MODE); + + if ((btm_cb.first_disabled_channel != first) + || (btm_cb.last_disabled_channel != last)) + { + if (btsnd_hcic_set_afh_channels (first, last)) + { + btm_cb.first_disabled_channel = first; + btm_cb.last_disabled_channel = last; + } + else + return (BTM_NO_RESOURCES); + } + return (BTM_SUCCESS); +} + +/******************************************************************************* +** +** Function BTM_SetAfhChannelAssessment +** +** Description This function is called to set the channel assessment mode on or off +** +** Returns none +** +*******************************************************************************/ +tBTM_STATUS BTM_SetAfhChannelAssessment (BOOLEAN enable_or_disable) +{ + /* whatever app wants if device is not 1.2 scan type should be STANDARD */ + if (!HCI_LMP_AFH_CAP_SLAVE_SUPPORTED(btm_cb.devcb.local_features)) + return (BTM_MODE_UNSUPPORTED); + + if (!btsnd_hcic_write_afh_channel_assessment_mode (enable_or_disable)) + return (BTM_NO_RESOURCES); + + return (BTM_SUCCESS); +} + +/******************************************************************************* +** +** Function BTM_ContinueReset +** +** Description This function is called by the application to continue +** initialization after the application has completed its +** vendor specific sequence. It is only used when +** BTM_APP_DEV_INIT is defined in target.h. +** +** Returns void +** +*******************************************************************************/ +void BTM_ContinueReset (void) +{ +#ifdef BTM_APP_DEV_INIT + btm_continue_reset(); +#endif +} + +/******************************************************************************* +** +** Function btm_dev_reset +** +** Description Local function called to send a reset command +** +** Returns void +** +*******************************************************************************/ +static void btm_dev_reset (void) +{ + btm_cb.devcb.state = BTM_DEV_STATE_WAIT_RESET_CMPLT; + + /* flush out the command complete queue and command transmit queue */ + btu_hcif_flush_cmd_queue(); + + /* Start reset timer. When timer expires we will send first command */ + /* from the setup sequence */ + + btu_start_timer (&btm_cb.devcb.reset_timer, BTU_TTYPE_BTM_DEV_CTL, + BTM_DEV_RESET_TIMEOUT); + btsnd_hcic_reset (LOCAL_BR_EDR_CONTROLLER_ID); +} + + +/******************************************************************************* +** +** Function btm_get_hci_buf_size +** +** Description Local function called to send a read buffer size command +** +** Returns void +** +*******************************************************************************/ +void btm_get_hci_buf_size (void) +{ + + btu_start_timer (&btm_cb.devcb.reset_timer, BTU_TTYPE_BTM_DEV_CTL, BTM_DEV_REPLY_TIMEOUT); + + /* Send a Read Buffer Size message to the Host Controller. */ + btsnd_hcic_read_buffer_size (); + +} +#if BLE_INCLUDED == TRUE +/******************************************************************************* +** +** Function btm_read_ble_wl_size +** +** Description Local function called to send a read BLE buffer size command +** +** Returns void +** +*******************************************************************************/ +void btm_read_ble_wl_size(void) +{ + BTM_TRACE_DEBUG0("btm_read_ble_wl_size "); + btu_start_timer (&btm_cb.devcb.reset_timer, BTU_TTYPE_BTM_DEV_CTL, BTM_DEV_REPLY_TIMEOUT); + + /* Send a Read Buffer Size message to the Host Controller. */ + btsnd_hcic_ble_read_white_list_size(); +} +/******************************************************************************* +** +** Function btm_get_ble_buffer_size +** +** Description Local function called to send a read BLE buffer size command +** +** Returns void +** +*******************************************************************************/ +void btm_get_ble_buffer_size(void) +{ + BTM_TRACE_DEBUG0("btm_get_ble_buffer_size "); + btu_start_timer (&btm_cb.devcb.reset_timer, BTU_TTYPE_BTM_DEV_CTL, BTM_DEV_REPLY_TIMEOUT); + + /* Send a Read Buffer Size message to the Host Controller. */ + btsnd_hcic_ble_read_buffer_size (); +} +#endif +/******************************************************************************* +** +** Function btm_get_local_version +** +** Description Local function called to send a read local version to controller +** +** Returns void +** +*******************************************************************************/ +void btm_get_local_version (void) +{ + + btu_start_timer (&btm_cb.devcb.reset_timer, BTU_TTYPE_BTM_DEV_CTL, BTM_DEV_REPLY_TIMEOUT); + + /* Send a Read Local Version message to the Host Controller. */ + btsnd_hcic_read_local_ver (LOCAL_BR_EDR_CONTROLLER_ID); + btsnd_hcic_read_bd_addr (); + +#if BTM_PWR_MGR_INCLUDED == TRUE + btm_pm_reset(); +#endif + +} + +/******************************************************************************* +** +** Function btm_get_local_features +** +** Description Local function called to send a read local features +** +** Returns void +** +*******************************************************************************/ +void btm_get_local_features (void) +{ + btu_start_timer (&btm_cb.devcb.reset_timer, BTU_TTYPE_BTM_DEV_CTL, BTM_DEV_REPLY_TIMEOUT); + + btsnd_hcic_read_local_features (); +} + +/******************************************************************************* +** +** Function btm_dev_timeout +** +** Description This function is called when a timer list entry expires. +** +** Returns void +** +*******************************************************************************/ +void btm_dev_timeout (TIMER_LIST_ENT *p_tle) +{ + TIMER_PARAM_TYPE timer_type = (TIMER_PARAM_TYPE)p_tle->param; + + if ((timer_type & TT_DEV_RESET_MASK) == TT_DEV_RESET) + { + /* Call device reset as long as there is timeout*/ + btm_dev_reset(); + } + else if (timer_type == (TIMER_PARAM_TYPE)TT_DEV_RLN) + { + tBTM_CMPL_CB *p_cb = btm_cb.devcb.p_rln_cmpl_cb; + + btm_cb.devcb.p_rln_cmpl_cb = NULL; + + if (p_cb) + (*p_cb)((void *) NULL); + } +} + +/******************************************************************************* +** +** Function btm_reset_complete +** +** Description This function is called when command complete for HCI_Reset +** is received. It does not make sense to send next command +** because device is resetting after command complete is +** received. Just start timer and set required state. +** +** Returns void +** +*******************************************************************************/ +void btm_reset_complete (void) +{ + int devinx; + + BTM_TRACE_EVENT0 ("btm_reset_complete"); + +#ifdef BRCM_VS + btm_vs_reset_complete(); +#endif + + + /* Handle if btm initiated the reset */ + if (btm_cb.devcb.state == BTM_DEV_STATE_WAIT_RESET_CMPLT) + { + /* Tell L2CAP that all connections are gone */ + l2cu_device_reset (); + + /* Clear current security state */ + for (devinx = 0; devinx < BTM_SEC_MAX_DEVICE_RECORDS; devinx++) + { + btm_cb.sec_dev_rec[devinx].sec_state = BTM_SEC_STATE_IDLE; + } + + /* After the reset controller should restore all parameters to defaults. */ + btm_cb.btm_inq_vars.inq_counter = 1; + btm_cb.btm_inq_vars.inq_scan_window = HCI_DEF_INQUIRYSCAN_WINDOW; + btm_cb.btm_inq_vars.inq_scan_period = HCI_DEF_INQUIRYSCAN_INTERVAL; + btm_cb.btm_inq_vars.inq_scan_type = HCI_DEF_SCAN_TYPE; + + btm_cb.btm_inq_vars.page_scan_window = HCI_DEF_PAGESCAN_WINDOW; + btm_cb.btm_inq_vars.page_scan_period = HCI_DEF_PAGESCAN_INTERVAL; + btm_cb.btm_inq_vars.page_scan_type = HCI_DEF_SCAN_TYPE; + +#if (BTM_AFTER_RESET_TIMEOUT > 0) + btu_start_timer (&btm_cb.devcb.reset_timer, BTU_TTYPE_BTM_DEV_CTL, + BTM_AFTER_RESET_TIMEOUT); +#else + btm_cb.devcb.state = BTM_DEV_STATE_WAIT_AFTER_RESET; + btm_after_reset_hold_complete(); +#endif + +#if (BLE_INCLUDED == TRUE) + btm_cb.ble_ctr_cb.bg_conn_state = BLE_BG_CONN_IDLE; + btm_cb.ble_ctr_cb.bg_conn_dev_num = 0; + btm_cb.ble_ctr_cb.bg_conn_type = BTM_BLE_CONN_NONE; + btm_cb.ble_ctr_cb.p_select_cback = NULL; + memset(&btm_cb.ble_ctr_cb.bg_conn_dev_list, 0, (sizeof(BD_ADDR)*BTM_BLE_MAX_BG_CONN_DEV_NUM)); + gatt_reset_bgdev_list(); +#endif + } +} + +/******************************************************************************* +** +** Function btm_continue_reset +** +** Description This function is called when wait period expired after +** device reset or called by the application to continue +** initialization after the application has completed its +** vendor specific sequence. +** +** Returns void +** +*******************************************************************************/ +void btm_continue_reset (void) +{ + + /* Reinitialize the default class of device */ +#if BTM_INTERNAL_BB == TRUE + btsnd_hcic_read_bd_addr (); +#if BTM_PWR_MGR_INCLUDED == TRUE + btm_pm_reset(); +#endif +#endif + + btm_get_hci_buf_size (); + + /* default device class */ + BTM_SetDeviceClass((UINT8 *) BTM_INIT_CLASS_OF_DEVICE); + +#if (BTM_MAX_LOC_BD_NAME_LEN > 0) && (BTM_SET_DEV_NAME_UPON_RESET == TRUE) + BTM_SetLocalDeviceName(btm_cb.cfg.bd_name); +#elif BTM_USE_DEF_LOCAL_NAME == TRUE + BTM_SetLocalDeviceName(BTM_DEF_LOCAL_NAME); +#endif + + BTM_SetPinType (btm_cb.cfg.pin_type, btm_cb.cfg.pin_code, btm_cb.cfg.pin_code_len); +} + +/******************************************************************************* +** +** Function btm_after_reset_hold_complete +** +** Description This function is called when wait period expired after +** device reset. Continue intitialization +** +** Returns void +** +*******************************************************************************/ +void btm_after_reset_hold_complete (void) +{ +#ifdef BTM_APP_DEV_INIT + btu_stop_timer(&btm_cb.devcb.reset_timer); + BTM_APP_DEV_INIT(); +#else + btm_continue_reset(); +#endif +} + + +/******************************************************************************* +** +** Function btm_read_hci_buf_size_complete +** +** Description This function is called when command complete for +** get HCI buffer size is received. Start timer and send +** read local featues request +** +** Returns void +** +*******************************************************************************/ +void btm_read_hci_buf_size_complete (UINT8 *p, UINT16 evt_len) +{ + UINT8 status; + UINT8 lm_sco_buf_size; + UINT16 lm_num_acl_bufs; + UINT16 lm_num_sco_bufs; + UINT16 acl_buf_size; + + STREAM_TO_UINT8 (status, p); + if (status == HCI_SUCCESS) + { + STREAM_TO_UINT16 (btu_cb.hcit_acl_data_size, p); + STREAM_TO_UINT8 (lm_sco_buf_size, p); + STREAM_TO_UINT16 (lm_num_acl_bufs, p); + STREAM_TO_UINT16 (lm_num_sco_bufs, p); + + btu_cb.hcit_acl_pkt_size = btu_cb.hcit_acl_data_size + HCI_DATA_PREAMBLE_SIZE; + + l2c_link_processs_num_bufs (lm_num_acl_bufs); + +#if BTM_ACL_BUF_SIZE > 0 + acl_buf_size = (BTM_ACL_BUF_SIZE < L2CAP_MTU_SIZE) ? BTM_ACL_BUF_SIZE : L2CAP_MTU_SIZE; +#else + acl_buf_size = L2CAP_MTU_SIZE; +#endif + /* Tell the controller what our buffer sizes are. ?? Need SCO info */ + btsnd_hcic_set_host_buf_size (acl_buf_size, BTM_SCO_HOST_BUF_SIZE, L2CAP_HOST_FC_ACL_BUFS, 10); + +#if L2CAP_HOST_FLOW_CTRL == TRUE + btsnd_hcic_set_host_flow_ctrl (HCI_HOST_FLOW_CTRL_ACL_ON); +#endif + } + + /* Set the device into connectable and/or discoverable mode (if configured to do so) */ +#if BTM_IS_CONNECTABLE == TRUE + (void) BTM_SetConnectability (BTM_CONNECTABLE, BTM_DEFAULT_CONN_WINDOW, BTM_DEFAULT_CONN_INTERVAL); +#endif + +#if BTM_IS_DISCOVERABLE == TRUE + (void) BTM_SetDiscoverability (BTM_DEFAULT_DISC_MODE, BTM_DEFAULT_DISC_WINDOW, BTM_DEFAULT_DISC_INTERVAL); +#endif + +#if BTM_INTERNAL_BB == TRUE + { + UINT8 buf[9] = BTM_INTERNAL_LOCAL_VER; + btm_read_local_version_complete( buf, 9 ); + } +#else + btm_get_local_version (); +#endif +} + +#if (BLE_INCLUDED == TRUE) +/******************************************************************************* +** +** Function btm_read_ble_buf_size_complete +** +** Description This function is called when command complete for +** get HCI buffer size is received. Start timer and send +** read local featues request +** +** Returns void +** +*******************************************************************************/ +void btm_read_ble_buf_size_complete (UINT8 *p, UINT16 evt_len) +{ + UINT8 status; + UINT16 lm_num_le_bufs; + + BTM_TRACE_DEBUG0("btm_read_ble_buf_size_complete "); + STREAM_TO_UINT8 (status, p); + if (status == HCI_SUCCESS) + { + STREAM_TO_UINT16 (btu_cb.hcit_ble_acl_data_size, p); + STREAM_TO_UINT8 (lm_num_le_bufs, p); + + btu_cb.hcit_ble_acl_pkt_size = btu_cb.hcit_ble_acl_data_size + HCI_DATA_PREAMBLE_SIZE; + + l2c_link_processs_ble_num_bufs (lm_num_le_bufs); + } + +#if BTM_INTERNAL_BB == TRUE + { + UINT8 buf[9] = BTM_INTERNAL_LOCAL_FEA; + btm_read_local_features_complete( buf, 9 ); + } +#else +#ifdef BRCM_VS + btm_brcm_feat_init(); +#else + /* get local feature if BRCM specific feature is not included */ + btm_get_local_features (); +#endif +#endif + +} + +/******************************************************************************* +** +** Function btm_read_white_list_size_complete +** +** Description This function read the current white list size. +*******************************************************************************/ +void btm_read_white_list_size_complete(UINT8 *p, UINT16 evt_len) +{ + UINT8 status; + + BTM_TRACE_DEBUG0("btm_read_white_list_size_complete "); + STREAM_TO_UINT8 (status, p); + + if (status == HCI_SUCCESS) + { + STREAM_TO_UINT8(btm_cb.ble_ctr_cb.max_filter_entries, p); + btm_cb.ble_ctr_cb.num_empty_filter = btm_cb.ble_ctr_cb.max_filter_entries; + } + + btm_get_ble_buffer_size(); +} + +#endif +/******************************************************************************* +** +** Function btm_read_local_version_complete +** +** Description This function is called when local BD Addr read complete +** message is received from the HCI. +** +** Returns void +** +*******************************************************************************/ +void btm_read_local_version_complete (UINT8 *p, UINT16 evt_len) +{ + tBTM_VERSION_INFO *p_vi = &btm_cb.devcb.local_version; + UINT8 status; + +#ifdef BTA_PRM_CHECK_FW_VER + if(BTA_PRM_CHECK_FW_VER(p)) + return; +#endif + + STREAM_TO_UINT8 (status, p); + if (status == HCI_SUCCESS) + { + + STREAM_TO_UINT8 (p_vi->hci_version, p); + STREAM_TO_UINT16 (p_vi->hci_revision, p); + STREAM_TO_UINT8 (p_vi->lmp_version, p); + STREAM_TO_UINT16 (p_vi->manufacturer, p); + STREAM_TO_UINT16 (p_vi->lmp_subversion, p); + } + +#if BLE_INCLUDED == TRUE + { + btm_read_ble_wl_size(); + } +#elif BTM_INTERNAL_BB == TRUE + { + UINT8 buf[9] = BTM_INTERNAL_LOCAL_FEA; + btm_read_local_features_complete( buf, 9 ); + } +#else +#ifdef BRCM_VS + btm_brcm_feat_init(); +#else + /* get local feature if BRCM specific feature is not included */ + btm_get_local_features (); +#endif +#endif +} + + +/******************************************************************************* +** +** Function btm_read_local_features_complete +** +** Description This function is called when local features read is complete. +** This is the last step of the startup sequence. +** +** Returns void +** +*******************************************************************************/ +void btm_read_local_features_complete (UINT8 *p, UINT16 evt_len) +{ + tBTM_DEVCB *p_devcb = &btm_cb.devcb; + tBTM_CMPL_CB *p_cb = p_devcb->p_reset_cmpl_cb; + UINT8 status; + UINT16 xx; + UINT8 last; + UINT8 first; + + btu_stop_timer (&btm_cb.devcb.reset_timer); + /* If there was a callback address for reset complete, call it */ + p_devcb->p_reset_cmpl_cb = NULL; + + STREAM_TO_UINT8 (status, p); + if (status == HCI_SUCCESS) + { + /* stop guard timer to avoid accidental timeout */ + btu_stop_timer(&p_devcb->reset_timer); + + p_devcb->state = BTM_DEV_STATE_READY; + + /* Extract features and create "btm_acl_pkt_types_supported" flag + */ + for (xx = 0; xx < HCI_NUM_FEATURE_BYTES; xx++) + STREAM_TO_UINT8 (p_devcb->local_features[xx], p); + + /* Create ACL supported packet types mask + */ + btm_cb.btm_acl_pkt_types_supported = (BTM_ACL_PKT_TYPES_MASK_DH1 + + BTM_ACL_PKT_TYPES_MASK_DM1); + + if (HCI_3_SLOT_PACKETS_SUPPORTED(p_devcb->local_features)) + btm_cb.btm_acl_pkt_types_supported |= (BTM_ACL_PKT_TYPES_MASK_DH3 + + BTM_ACL_PKT_TYPES_MASK_DM3); + + if (HCI_5_SLOT_PACKETS_SUPPORTED(p_devcb->local_features)) + btm_cb.btm_acl_pkt_types_supported |= (BTM_ACL_PKT_TYPES_MASK_DH5 + + BTM_ACL_PKT_TYPES_MASK_DM5); + + /* _NO_X_DXX masks are reserved before ver 2.0. + Set them only for later versions of controller */ + if (btm_cb.devcb.local_version.hci_version >= HCI_PROTO_VERSION_2_0) + { + + /* Add in EDR related ACL types */ + if (!HCI_EDR_ACL_2MPS_SUPPORTED(p_devcb->local_features)) + { + btm_cb.btm_acl_pkt_types_supported |= (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_devcb->local_features)) + { + btm_cb.btm_acl_pkt_types_supported |= (BTM_ACL_PKT_TYPES_MASK_NO_3_DH1 + + BTM_ACL_PKT_TYPES_MASK_NO_3_DH3 + + BTM_ACL_PKT_TYPES_MASK_NO_3_DH5); + } + + /* Check to see if 3 and 5 slot packets are available */ + if (HCI_EDR_ACL_2MPS_SUPPORTED(p_devcb->local_features) || + HCI_EDR_ACL_3MPS_SUPPORTED(p_devcb->local_features)) + { + if (!HCI_3_SLOT_EDR_ACL_SUPPORTED(p_devcb->local_features)) + btm_cb.btm_acl_pkt_types_supported |= (BTM_ACL_PKT_TYPES_MASK_NO_2_DH3 + + BTM_ACL_PKT_TYPES_MASK_NO_3_DH3); + + if (!HCI_5_SLOT_EDR_ACL_SUPPORTED(p_devcb->local_features)) + btm_cb.btm_acl_pkt_types_supported |= (BTM_ACL_PKT_TYPES_MASK_NO_2_DH5 + + BTM_ACL_PKT_TYPES_MASK_NO_3_DH5); + } + } + + BTM_TRACE_DEBUG1("Local supported ACL packet types: 0x%04x", + btm_cb.btm_acl_pkt_types_supported); + + /* Create (e)SCO supported packet types mask + */ + btm_cb.btm_sco_pkt_types_supported = 0; +#if BTM_SCO_INCLUDED == TRUE + btm_cb.sco_cb.esco_supported = FALSE; +#endif + if (HCI_SCO_LINK_SUPPORTED(p_devcb->local_features)) + { + btm_cb.btm_sco_pkt_types_supported = BTM_SCO_PKT_TYPES_MASK_HV1; + + if (HCI_HV2_PACKETS_SUPPORTED(p_devcb->local_features)) + btm_cb.btm_sco_pkt_types_supported |= BTM_SCO_PKT_TYPES_MASK_HV2; + + if (HCI_HV3_PACKETS_SUPPORTED(p_devcb->local_features)) + btm_cb.btm_sco_pkt_types_supported |= BTM_SCO_PKT_TYPES_MASK_HV3; + } + + if (HCI_ESCO_EV3_SUPPORTED(p_devcb->local_features)) + btm_cb.btm_sco_pkt_types_supported |= BTM_SCO_PKT_TYPES_MASK_EV3; + + if (HCI_ESCO_EV4_SUPPORTED(p_devcb->local_features)) + btm_cb.btm_sco_pkt_types_supported |= BTM_SCO_PKT_TYPES_MASK_EV4; + + if (HCI_ESCO_EV5_SUPPORTED(p_devcb->local_features)) + btm_cb.btm_sco_pkt_types_supported |= BTM_SCO_PKT_TYPES_MASK_EV5; +#if BTM_SCO_INCLUDED == TRUE + if (btm_cb.btm_sco_pkt_types_supported & BTM_ESCO_LINK_ONLY_MASK) + { + btm_cb.sco_cb.esco_supported = TRUE; + + /* Add in EDR related eSCO types */ + if (HCI_EDR_ESCO_2MPS_SUPPORTED(p_devcb->local_features)) + { + if (!HCI_3_SLOT_EDR_ESCO_SUPPORTED(p_devcb->local_features)) + btm_cb.btm_sco_pkt_types_supported |= BTM_SCO_PKT_TYPES_MASK_NO_2_EV5; + } + else + { + btm_cb.btm_sco_pkt_types_supported |= (BTM_SCO_PKT_TYPES_MASK_NO_2_EV3 + + BTM_SCO_PKT_TYPES_MASK_NO_2_EV5); + } + + if (HCI_EDR_ESCO_3MPS_SUPPORTED(p_devcb->local_features)) + { + if (!HCI_3_SLOT_EDR_ESCO_SUPPORTED(p_devcb->local_features)) + btm_cb.btm_sco_pkt_types_supported |= BTM_SCO_PKT_TYPES_MASK_NO_3_EV5; + } + else + { + btm_cb.btm_sco_pkt_types_supported |= (BTM_SCO_PKT_TYPES_MASK_NO_3_EV3 + + BTM_SCO_PKT_TYPES_MASK_NO_3_EV5); + } + } +#endif + + BTM_TRACE_DEBUG1("Local supported SCO packet types: 0x%04x", + btm_cb.btm_sco_pkt_types_supported); + + /* Create Default Policy Settings + */ + if (HCI_SWITCH_SUPPORTED(p_devcb->local_features)) + btm_cb.btm_def_link_policy |= HCI_ENABLE_MASTER_SLAVE_SWITCH; + else + btm_cb.btm_def_link_policy &= ~HCI_ENABLE_MASTER_SLAVE_SWITCH; + + if (HCI_HOLD_MODE_SUPPORTED(p_devcb->local_features)) + btm_cb.btm_def_link_policy |= HCI_ENABLE_HOLD_MODE; + else + btm_cb.btm_def_link_policy &= ~HCI_ENABLE_HOLD_MODE; + + if (HCI_SNIFF_MODE_SUPPORTED(p_devcb->local_features)) + btm_cb.btm_def_link_policy |= HCI_ENABLE_SNIFF_MODE; + else + btm_cb.btm_def_link_policy &= ~HCI_ENABLE_SNIFF_MODE; + + if (HCI_PARK_MODE_SUPPORTED(p_devcb->local_features)) + btm_cb.btm_def_link_policy |= HCI_ENABLE_PARK_MODE; + else + btm_cb.btm_def_link_policy &= ~HCI_ENABLE_PARK_MODE; + + btm_sec_dev_reset (); + + /* If 802.11 present might have to disable some channels */ + if (btm_cb.last_disabled_channel != 0xff) + { + last = btm_cb.last_disabled_channel; + first = btm_cb.first_disabled_channel; + btm_cb.last_disabled_channel = 0xff; + btm_cb.first_disabled_channel = 0xff; + BTM_SetAfhChannels(first, last); + } + +#if ((BTM_EIR_SERVER_INCLUDED == TRUE)||(BTM_EIR_CLIENT_INCLUDED == TRUE)) + if (HCI_LMP_INQ_RSSI_SUPPORTED(p_devcb->local_features)) + { + if (HCI_EXT_INQ_RSP_SUPPORTED(p_devcb->local_features)) + BTM_SetInquiryMode (BTM_INQ_RESULT_EXTENDED); + else + BTM_SetInquiryMode (BTM_INQ_RESULT_WITH_RSSI); + } +#else + if (HCI_LMP_INQ_RSSI_SUPPORTED(p_devcb->local_features)) + BTM_SetInquiryMode (BTM_INQ_RESULT_WITH_RSSI); +#endif +#if L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE + if( HCI_NON_FLUSHABLE_PB_SUPPORTED(p_devcb->local_features)) + l2cu_set_non_flushable_pbf(TRUE); + else + l2cu_set_non_flushable_pbf(FALSE); +#endif + BTM_SetPageScanType (BTM_DEFAULT_SCAN_TYPE); + BTM_SetInquiryScanType (BTM_DEFAULT_SCAN_TYPE); + + /* If anyone wants device status notifications, give him one */ + btm_report_device_status (BTM_DEV_STATUS_UP); + +#ifdef BRCM_VS + btm_brcm_arc_init(); +#endif + + /* Reset sequence is complete. If this was an application originated */ + /* reset, tell him its done. */ + if (p_cb) + (*p_cb)((void *) NULL); + } +} + +/******************************************************************************* +** +** Function btm_get_voice_coding_support +** +** Description This function is provides a way to get the voice coding schemes +** supported the device. +** +** Returns A bit mask - Bit 0 if set indicates CVSD support +** Bit 1 if set indicates PCM A-law support +** Bit 2 if set indicates PCM Mu-law support +** +*******************************************************************************/ + +UINT8 btm_get_voice_coding_support( void ) +{ + UINT8 code = 0; + + if( HCI_LMP_CVSD_SUPPORTED(btm_cb.devcb.local_features) ) code |= 0x01 ; + if( HCI_LMP_A_LAW_SUPPORTED(btm_cb.devcb.local_features) ) code |= 0x02 ; + if( HCI_LMP_U_LAW_SUPPORTED(btm_cb.devcb.local_features) ) code |= 0x04 ; + + return code ; +} + +/******************************************************************************* +** +** Function BTM_SetLocalDeviceName +** +** Description This function is called to set the local device name. +** +** Returns status of the operation +** +*******************************************************************************/ +tBTM_STATUS BTM_SetLocalDeviceName (char *p_name) +{ + UINT8 *p; +#if BLE_INCLUDED == TRUE && GAP_INCLUDED == TRUE + tGAP_BLE_ATTR_VALUE attr_value; +#endif + + if (!p_name || !p_name[0] || (strlen ((char *)p_name) > BD_NAME_LEN)) + return (BTM_ILLEGAL_VALUE); + + if (btm_cb.devcb.state == BTM_DEV_STATE_WAIT_RESET_CMPLT || + btm_cb.devcb.state == BTM_DEV_STATE_WAIT_AFTER_RESET) + return (BTM_DEV_RESET); + +#if BTM_MAX_LOC_BD_NAME_LEN > 0 + /* Save the device name if local storage is enabled */ + p = (UINT8 *)btm_cb.cfg.bd_name; + if (p != (UINT8 *)p_name) + { + BCM_STRNCPY_S(btm_cb.cfg.bd_name, sizeof(btm_cb.cfg.bd_name), p_name, BTM_MAX_LOC_BD_NAME_LEN); + btm_cb.cfg.bd_name[BTM_MAX_LOC_BD_NAME_LEN] = '\0'; + } +#else + p = (UINT8 *)p_name; +#endif + +#if BLE_INCLUDED == TRUE && GAP_INCLUDED == TRUE + attr_value.p_dev_name = (UINT8 *)p_name; + GAP_BleAttrDBUpdate(GATT_UUID_GAP_DEVICE_NAME, &attr_value); +#endif + + if (btsnd_hcic_change_name(p)) + return (BTM_CMD_STARTED); + else + return (BTM_NO_RESOURCES); +} + + + +/******************************************************************************* +** +** Function BTM_ReadLocalDeviceName +** +** Description This function is called to read the local device name. +** +** Returns status of the operation +** If success, BTM_SUCCESS is returned and p_name points stored +** local device name +** If BTM doesn't store local device name, BTM_NO_RESOURCES is +** is returned and p_name is set to NULL +** +*******************************************************************************/ +tBTM_STATUS BTM_ReadLocalDeviceName (char **p_name) +{ +#if BTM_MAX_LOC_BD_NAME_LEN > 0 + *p_name = btm_cb.cfg.bd_name; + return(BTM_SUCCESS); +#else + *p_name = NULL; + return(BTM_NO_RESOURCES); +#endif +} + + +/******************************************************************************* +** +** Function BTM_ReadLocalDeviceNameFromController +** +** Description Get local device name from controller. Do not use cached +** name (used to get chip-id prior to btm reset complete). +** +** Returns BTM_CMD_STARTED if successful, otherwise an error +** +*******************************************************************************/ +tBTM_STATUS BTM_ReadLocalDeviceNameFromController (tBTM_CMPL_CB *p_rln_cmpl_cback) +{ + /* Check if rln already in progress */ + if (btm_cb.devcb.p_rln_cmpl_cb) + return(BTM_NO_RESOURCES); + + /* Save callback */ + btm_cb.devcb.p_rln_cmpl_cb = p_rln_cmpl_cback; + + btsnd_hcic_read_name(); + btu_start_timer (&btm_cb.devcb.rln_timer, BTU_TTYPE_BTM_DEV_CTL, BTM_DEV_REPLY_TIMEOUT); + + return BTM_CMD_STARTED; +} + +/******************************************************************************* +** +** Function btm_read_local_name_complete +** +** Description This function is called when local name read complete. +** message is received from the HCI. +** +** Returns void +** +*******************************************************************************/ +void btm_read_local_name_complete (UINT8 *p, UINT16 evt_len) +{ + tBTM_CMPL_CB *p_cb = btm_cb.devcb.p_rln_cmpl_cb; + UINT8 status; + + btu_stop_timer (&btm_cb.devcb.rln_timer); + + /* If there was a callback address for read local name, call it */ + btm_cb.devcb.p_rln_cmpl_cb = NULL; + + if (p_cb) + { + STREAM_TO_UINT8 (status, p); + + if (status == HCI_SUCCESS) + (*p_cb)(p); + else + (*p_cb)(NULL); + } +} + + +/******************************************************************************* +** +** Function BTM_GetLocalDeviceAddr +** +** Description This function is called to read the local device address +** +** Returns void +** the local device address is copied into bd_addr +** +*******************************************************************************/ +void BTM_GetLocalDeviceAddr (BD_ADDR bd_addr) +{ + memcpy (bd_addr, btm_cb.devcb.local_addr, BD_ADDR_LEN); +} + +/******************************************************************************* +** +** Function BTM_ReadLocalDeviceAddr +** +** Description This function is called to read the local device address +** +** Returns status of the operation +** +*******************************************************************************/ +tBTM_STATUS BTM_ReadLocalDeviceAddr (tBTM_CMPL_CB *p_cb) +{ + if(p_cb) + (*p_cb)(btm_cb.devcb.local_addr); + + return (BTM_SUCCESS); +} + + +/******************************************************************************* +** +** Function btm_read_local_addr_complete +** +** Description This function is called when local BD Addr read complete +** message is received from the HCI. +** +** Returns void +** +*******************************************************************************/ +void btm_read_local_addr_complete (UINT8 *p, UINT16 evt_len) +{ + UINT8 status; + + STREAM_TO_UINT8 (status, p); + + if (status == HCI_SUCCESS) + { + STREAM_TO_BDADDR (btm_cb.devcb.local_addr, p); + } +} + + +/******************************************************************************* +** +** Function BTM_ReadLocalVersion +** +** Description This function is called to read the local device version +** +** Returns status of the operation +** +*******************************************************************************/ +tBTM_STATUS BTM_ReadLocalVersion (tBTM_VERSION_INFO *p_vers) +{ + /* Make sure the device has retrieved the info (not being reset) */ + if (btm_cb.devcb.state < BTM_DEV_STATE_READY) + return (BTM_DEV_RESET); + + *p_vers = btm_cb.devcb.local_version; + + return (BTM_SUCCESS); +} + + + + +/******************************************************************************* +** +** Function BTM_SetDeviceClass +** +** Description This function is called to set the local device class +** +** Returns status of the operation +** +*******************************************************************************/ +tBTM_STATUS BTM_SetDeviceClass (DEV_CLASS dev_class) +{ + if(!memcmp (btm_cb.devcb.dev_class, dev_class, DEV_CLASS_LEN)) + return(BTM_SUCCESS); + + memcpy (btm_cb.devcb.dev_class, dev_class, DEV_CLASS_LEN); + + if (btm_cb.devcb.state == BTM_DEV_STATE_WAIT_RESET_CMPLT || + btm_cb.devcb.state == BTM_DEV_STATE_WAIT_AFTER_RESET) + return (BTM_DEV_RESET); + + if (!btsnd_hcic_write_dev_class (dev_class)) + return (BTM_NO_RESOURCES); + + return (BTM_SUCCESS); +} + + +/******************************************************************************* +** +** Function BTM_ReadDeviceClass +** +** Description This function is called to read the local device class +** +** Returns pointer to the device class +** +*******************************************************************************/ +UINT8 *BTM_ReadDeviceClass (void) +{ + return ((UINT8 *)btm_cb.devcb.dev_class); +} + + +/******************************************************************************* +** +** Function BTM_ReadLocalFeatures +** +** Description This function is called to read the local features +** +** Returns pointer to the local features string +** +*******************************************************************************/ +UINT8 *BTM_ReadLocalFeatures (void) +{ + return (btm_cb.devcb.local_features); +} + +/******************************************************************************* +** +** Function BTM_ReadBrcmFeatures +** +** Description This function is called to read the Broadcom specific features +** +** Returns pointer to the Broadcom features string +** +*******************************************************************************/ +UINT8 *BTM_ReadBrcmFeatures (void) +{ + return (btm_cb.devcb.brcm_features); +} + +/******************************************************************************* +** +** Function BTM_RegisterForDeviceStatusNotif +** +** Description This function is called to register for device status +** change notifications. +** +** If one registration is already there calling function should +** save the pointer to the function that is return and +** call it when processing of the event is complete +** +** Returns status of the operation +** +*******************************************************************************/ +tBTM_DEV_STATUS_CB *BTM_RegisterForDeviceStatusNotif (tBTM_DEV_STATUS_CB *p_cb) +{ + tBTM_DEV_STATUS_CB *p_prev = btm_cb.devcb.p_dev_status_cb; + + btm_cb.devcb.p_dev_status_cb = p_cb; + return (p_prev); +} + +/******************************************************************************* +** +** Function BTM_VendorSpecificCommand +** +** Description Send a vendor specific HCI command to the controller. +** +** Returns +** BTM_SUCCESS Command sent. Does not expect command complete +** event. (command cmpl callback param is NULL) +** BTM_CMD_STARTED Command sent. Waiting for command cmpl event. +** +** Notes +** Opcode will be OR'd with HCI_GRP_VENDOR_SPECIFIC. +** +*******************************************************************************/ +tBTM_STATUS BTM_VendorSpecificCommand(UINT16 opcode, UINT8 param_len, + UINT8 *p_param_buf, tBTM_VSC_CMPL_CB *p_cb) +{ + void *p_buf; + + BTM_TRACE_EVENT2 ("BTM: BTM_VendorSpecificCommand: Opcode: 0x%04X, ParamLen: %i.", + opcode, param_len); + + /* Allocate a buffer to hold HCI command plus the callback function */ + if ((p_buf = GKI_getbuf((UINT16)(sizeof(BT_HDR) + sizeof (tBTM_CMPL_CB *) + + param_len + HCIC_PREAMBLE_SIZE))) != NULL) + { + /* Send the HCI command (opcode will be OR'd with HCI_GRP_VENDOR_SPECIFIC) */ + btsnd_hcic_vendor_spec_cmd (p_buf, opcode, param_len, p_param_buf, (void *)p_cb); + + /* Return value */ + if (p_cb != NULL) + return (BTM_CMD_STARTED); + else + return (BTM_SUCCESS); + } + else + return (BTM_NO_RESOURCES); + +} + + +/******************************************************************************* +** +** Function btm_vsc_complete +** +** Description This function is called when local HCI Vendor Specific +** Command complete message is received from the HCI. +** +** Returns void +** +*******************************************************************************/ +void btm_vsc_complete (UINT8 *p, UINT16 opcode, UINT16 evt_len, + tBTM_CMPL_CB *p_vsc_cplt_cback) +{ + tBTM_VSC_CMPL vcs_cplt_params; + + /* If there was a callback address for vcs complete, call it */ + if (p_vsc_cplt_cback) + { + /* Pass paramters to the callback function */ + vcs_cplt_params.opcode = opcode; /* Number of bytes in return info */ + vcs_cplt_params.param_len = evt_len; /* Number of bytes in return info */ + vcs_cplt_params.p_param_buf = p; + (*p_vsc_cplt_cback)(&vcs_cplt_params); /* Call the VSC complete callback function */ + } +} + +/******************************************************************************* +** +** Function BTM_RegisterForVSEvents +** +** Description This function is called to register/deregister for vendor +** specific HCI events. +** +** If is_register=TRUE, then the function will be registered; +** if is_register=FALSE, then the function will be deregistered. +** +** Returns BTM_SUCCESS if successful, +** BTM_BUSY if maximum number of callbacks have already been +** registered. +** +*******************************************************************************/ +tBTM_STATUS BTM_RegisterForVSEvents (tBTM_VS_EVT_CB *p_cb, BOOLEAN is_register) +{ + tBTM_STATUS retval = BTM_SUCCESS; + UINT8 i, free_idx = BTM_MAX_VSE_CALLBACKS; + + /* See if callback is already registered */ + for (i=0; i<BTM_MAX_VSE_CALLBACKS; i++) + { + if (btm_cb.devcb.p_vend_spec_cb[i] == NULL) + { + /* Found a free slot. Store index */ + free_idx = i; + } + else if (btm_cb.devcb.p_vend_spec_cb[i] == p_cb) + { + /* Found callback in lookup table. If deregistering, clear the entry. */ + if (is_register == FALSE) + { + btm_cb.devcb.p_vend_spec_cb[i] = NULL; + BTM_TRACE_EVENT0("BTM Deregister For VSEvents is successfully"); + } + return (BTM_SUCCESS); + } + } + + /* Didn't find callback. Add callback to free slot if registering */ + if (is_register) + { + if (free_idx < BTM_MAX_VSE_CALLBACKS) + { + btm_cb.devcb.p_vend_spec_cb[free_idx] = p_cb; + BTM_TRACE_EVENT0("BTM Register For VSEvents is successfully"); + } + else + { + /* No free entries available */ + BTM_TRACE_ERROR0 ("BTM_RegisterForVSEvents: too many callbacks registered"); + + retval = BTM_NO_RESOURCES; + } + } + + return (retval); +} + +/******************************************************************************* +** +** Function btm_vendor_specific_evt +** +** Description Process event HCI_VENDOR_SPECIFIC_EVT +** +** Note: Some controllers do not send command complete, so +** the callback and busy flag are cleared here also. +** +** Returns void +** +*******************************************************************************/ +void btm_vendor_specific_evt (UINT8 *p, UINT8 evt_len) +{ + UINT8 i; + + BTM_TRACE_DEBUG0 ("BTM Event: Vendor Specific event from controller"); + + for (i=0; i<BTM_MAX_VSE_CALLBACKS; i++) + { + if (btm_cb.devcb.p_vend_spec_cb[i]) + (*btm_cb.devcb.p_vend_spec_cb[i])(evt_len, p); + } +} + + +/******************************************************************************* +** +** Function BTM_WritePageTimeout +** +** Description Send HCI Write Page Timeout. +** +** Returns +** BTM_SUCCESS Command sent. +** BTM_NO_RESOURCES If out of resources to send the command. +** +** +*******************************************************************************/ +tBTM_STATUS BTM_WritePageTimeout(UINT16 timeout) +{ + BTM_TRACE_EVENT1 ("BTM: BTM_WritePageTimeout: Timeout: %d.", timeout); + + /* Send the HCI command */ + if (btsnd_hcic_write_page_tout (timeout)) + return (BTM_SUCCESS); + else + return (BTM_NO_RESOURCES); +} + +/******************************************************************************* +** +** Function BTM_WriteVoiceSettings +** +** Description Send HCI Write Voice Settings command. +** See hcidefs.h for settings bitmask values. +** +** Returns +** BTM_SUCCESS Command sent. +** BTM_NO_RESOURCES If out of resources to send the command. +** +** +*******************************************************************************/ +tBTM_STATUS BTM_WriteVoiceSettings(UINT16 settings) +{ + BTM_TRACE_EVENT1 ("BTM: BTM_WriteVoiceSettings: Settings: 0x%04x.", settings); + + /* Send the HCI command */ + if (btsnd_hcic_write_voice_settings ((UINT16)(settings & 0x03ff))) + return (BTM_SUCCESS); + + return (BTM_NO_RESOURCES); +} + +/******************************************************************************* +** +** Function BTM_EnableTestMode +** +** Description Send HCI the enable device under test command. +** +** Note: Controller can only be taken out of this mode by +** resetting the controller. +** +** Returns +** BTM_SUCCESS Command sent. +** BTM_NO_RESOURCES If out of resources to send the command. +** +** +*******************************************************************************/ +tBTM_STATUS BTM_EnableTestMode(void) +{ + UINT8 cond; + + BTM_TRACE_EVENT0 ("BTM: BTM_EnableTestMode"); + + /* set auto accept connection as this is needed during test mode */ + /* Allocate a buffer to hold HCI command */ + cond = HCI_DO_AUTO_ACCEPT_CONNECT; + if (!btsnd_hcic_set_event_filter(HCI_FILTER_CONNECTION_SETUP, + HCI_FILTER_COND_NEW_DEVICE, + &cond, sizeof(cond))) + { + return (BTM_NO_RESOURCES); + } + + /* put device to connectable mode */ + if (!BTM_SetConnectability(BTM_CONNECTABLE, BTM_DEFAULT_CONN_WINDOW, + BTM_DEFAULT_CONN_INTERVAL) == BTM_SUCCESS) + { + return BTM_NO_RESOURCES; + } + + /* put device to discoverable mode */ + if (!BTM_SetDiscoverability(BTM_GENERAL_DISCOVERABLE, BTM_DEFAULT_DISC_WINDOW, + BTM_DEFAULT_DISC_INTERVAL) == BTM_SUCCESS) + { + return BTM_NO_RESOURCES; + } + + /* mask off all of event from controller */ + if (!btsnd_hcic_set_event_mask(LOCAL_BR_EDR_CONTROLLER_ID, + (UINT8 *)"\x00\x00\x00\x00\x00\x00\x00\x00")) + { + return BTM_NO_RESOURCES; + } + + /* Send the HCI command */ + if (btsnd_hcic_enable_test_mode ()) + return (BTM_SUCCESS); + else + return (BTM_NO_RESOURCES); +} + +/******************************************************************************* +** +** Function btm_get_hci_version +** +** Description Local function called to retrieve the current HCI version +** +** Returns Bluetooth HCI Version returned by the controller +** +*******************************************************************************/ +UINT8 btm_get_hci_version (void) +{ + return (btm_cb.devcb.local_version.hci_version); +} + + + +/******************************************************************************* +** +** Function BTM_ReadStoredLinkKey +** +** Description This function is called to obtain link key for the specified +** device from the NVRAM storage attached to the Bluetooth +** controller. +** +** Parameters: bd_addr - Address of the device +** p_cb - Call back function to be called to return +** the results +** +*******************************************************************************/ +tBTM_STATUS BTM_ReadStoredLinkKey (BD_ADDR bd_addr, tBTM_CMPL_CB *p_cb) +{ + BD_ADDR local_bd_addr; + BOOLEAN read_all_flag = FALSE; + + /* Check if the previous command is completed */ + if (btm_cb.devcb.p_stored_link_key_cmpl_cb) + return (BTM_BUSY); + + if (!bd_addr) + { + /* This is to read all the link keys */ + read_all_flag = TRUE; + + /* We don't care the BD address. Just pass a non zero pointer */ + bd_addr = local_bd_addr; + } + + BTM_TRACE_EVENT1 ("BTM: BTM_ReadStoredLinkKey: Read_All: %s", + read_all_flag ? "TRUE" : "FALSE"); + + /* Send the HCI command */ + btm_cb.devcb.p_stored_link_key_cmpl_cb = p_cb; + if (btsnd_hcic_read_stored_key (bd_addr, read_all_flag)) + return (BTM_SUCCESS); + else + return (BTM_NO_RESOURCES); + +} + + +/******************************************************************************* +** +** Function BTM_WriteStoredLinkKey +** +** Description This function is called to write link keys for the specified +** device addresses to the NVRAM storage attached to the Bluetooth +** controller. +** +** Parameters: num_keys - Number of link keys +** bd_addr - Addresses of the devices +** link_key - Link Keys to be stored +** p_cb - Call back function to be called to return +** the results +** +*******************************************************************************/ +tBTM_STATUS BTM_WriteStoredLinkKey (UINT8 num_keys, + BD_ADDR *bd_addr, + LINK_KEY *link_key, + tBTM_CMPL_CB *p_cb) +{ + /* Check if the previous command is completed */ + if (btm_cb.devcb.p_stored_link_key_cmpl_cb) + return (BTM_BUSY); + + BTM_TRACE_EVENT1 ("BTM: BTM_WriteStoredLinkKey: num_keys: %d", num_keys); + + /* Check the maximum number of link keys */ + if(num_keys > HCI_MAX_NUM_OF_LINK_KEYS_PER_CMMD) + num_keys = HCI_MAX_NUM_OF_LINK_KEYS_PER_CMMD; + + /* Send the HCI command */ + btm_cb.devcb.p_stored_link_key_cmpl_cb = p_cb; + if (btsnd_hcic_write_stored_key (num_keys, bd_addr, link_key)) + return (BTM_SUCCESS); + else + return (BTM_NO_RESOURCES); + +} + + +/******************************************************************************* +** +** Function BTM_DeleteStoredLinkKey +** +** Description This function is called to delete link key for the specified +** device addresses from the NVRAM storage attached to the Bluetooth +** controller. +** +** Parameters: bd_addr - Addresses of the devices +** p_cb - Call back function to be called to return +** the results +** +*******************************************************************************/ +tBTM_STATUS BTM_DeleteStoredLinkKey(BD_ADDR bd_addr, tBTM_CMPL_CB *p_cb) +{ + BD_ADDR local_bd_addr; + BOOLEAN delete_all_flag = FALSE; + + /* Check if the previous command is completed */ + if (btm_cb.devcb.p_stored_link_key_cmpl_cb) + return (BTM_BUSY); + + if (!bd_addr) + { + /* This is to delete all link keys */ + delete_all_flag = TRUE; + + /* We don't care the BD address. Just pass a non zero pointer */ + bd_addr = local_bd_addr; + } + + BTM_TRACE_EVENT1 ("BTM: BTM_DeleteStoredLinkKey: delete_all_flag: %s", + delete_all_flag ? "TRUE" : "FALSE"); + + /* Send the HCI command */ + btm_cb.devcb.p_stored_link_key_cmpl_cb = p_cb; + if (!btsnd_hcic_delete_stored_key (bd_addr, delete_all_flag)) + { + return (BTM_NO_RESOURCES); + } + else + return (BTM_SUCCESS); +} + + +/******************************************************************************* +** +** Function btm_read_stored_link_key_complete +** +** Description This function is called when the command complete message +** is received from the HCI for the read stored link key command. +** +** Returns void +** +*******************************************************************************/ +void btm_read_stored_link_key_complete (UINT8 *p) +{ + tBTM_CMPL_CB *p_cb = btm_cb.devcb.p_stored_link_key_cmpl_cb; + tBTM_READ_STORED_LINK_KEY_COMPLETE result; + + /* If there was a callback registered for read stored link key, call it */ + btm_cb.devcb.p_stored_link_key_cmpl_cb = NULL; + + if (p_cb) + { + /* Set the call back event to indicate command complete */ + result.event = BTM_CB_EVT_READ_STORED_LINK_KEYS; + + /* Extract the result fields from the HCI event if status is success */ + STREAM_TO_UINT8 (result.status, p); + if (result.status == HCI_SUCCESS) + { + STREAM_TO_UINT16 (result.max_keys, p); + STREAM_TO_UINT16 (result.read_keys, p); + } + else + { + BTM_TRACE_WARNING1("Read stored link key status %d", result.status); + result.max_keys = 0; + result.read_keys = 0; + } + /* Call the call back and pass the result */ + (*p_cb)(&result); + } +} + + +/******************************************************************************* +** +** Function btm_write_stored_link_key_complete +** +** Description This function is called when the command complete message +** is received from the HCI for the write stored link key command. +** +** Returns void +** +*******************************************************************************/ +void btm_write_stored_link_key_complete (UINT8 *p) +{ + tBTM_CMPL_CB *p_cb = btm_cb.devcb.p_stored_link_key_cmpl_cb; + tBTM_WRITE_STORED_LINK_KEY_COMPLETE result; + + /* If there was a callback registered for read stored link key, call it */ + btm_cb.devcb.p_stored_link_key_cmpl_cb = NULL; + + if (p_cb) + { + /* Set the call back event to indicate command complete */ + result.event = BTM_CB_EVT_WRITE_STORED_LINK_KEYS; + + /* Extract the result fields from the HCI event */ + STREAM_TO_UINT8 (result.status, p); + STREAM_TO_UINT8 (result.num_keys, p); + + /* Call the call back and pass the result */ + (*p_cb)(&result); + } +} + + +/******************************************************************************* +** +** Function btm_delete_stored_link_key_complete +** +** Description This function is called when the command complete message +** is received from the HCI for the delete stored link key command. +** +** Returns void +** +*******************************************************************************/ +void btm_delete_stored_link_key_complete (UINT8 *p) +{ + tBTM_CMPL_CB *p_cb = btm_cb.devcb.p_stored_link_key_cmpl_cb; + tBTM_DELETE_STORED_LINK_KEY_COMPLETE result; + + /* If there was a callback registered for read stored link key, call it */ + btm_cb.devcb.p_stored_link_key_cmpl_cb = NULL; + + if (p_cb) + { + /* Set the call back event to indicate command complete */ + result.event = BTM_CB_EVT_DELETE_STORED_LINK_KEYS; + + /* Extract the result fields from the HCI event */ + STREAM_TO_UINT8 (result.status, p); + STREAM_TO_UINT16 (result.num_keys, p); + + /* Call the call back and pass the result */ + (*p_cb)(&result); + } +} + + +/******************************************************************************* +** +** Function btm_return_link_keys_evt +** +** Description This function is called when the return link keys event +** is received from the HCI for the read stored link key command. +** +** Returns void +** +*******************************************************************************/ +void btm_return_link_keys_evt (tBTM_RETURN_LINK_KEYS_EVT *result) +{ + tBTM_CMPL_CB *p_cb = btm_cb.devcb.p_stored_link_key_cmpl_cb; + UINT8 i, *p, *p1; + UINT8 bd_addr[BD_ADDR_LEN]; + UINT8 link_key[LINK_KEY_LEN]; + + /* Call the call back to pass the link keys to application */ + if (p_cb) + { + /* Change the BD addr and Link key in to big endian order */ + p = (UINT8 *)(result + 1); + for (i=0; i<result->num_keys; i++) + { + /* Initialize the backup pointer */ + p1 = p; + + /* Extract the BD Addr and Link Key */ + REVERSE_STREAM_TO_ARRAY(bd_addr, p1, BD_ADDR_LEN); + REVERSE_STREAM_TO_ARRAY(link_key, p1, LINK_KEY_LEN); + + /* Write the BD Addr and Link Key back in big endian format */ + ARRAY_TO_STREAM(p, bd_addr, BD_ADDR_LEN); + ARRAY_TO_STREAM(p, link_key, LINK_KEY_LEN); + } + + (*p_cb)(result); + } +} + + + +/******************************************************************************* +** +** Function btm_report_device_status +** +** Description This function is called when there is a change in the device +** status. This function will report the new device status to +** the application +** +** Returns void +** +*******************************************************************************/ +void btm_report_device_status (tBTM_DEV_STATUS status) +{ + tBTM_DEV_STATUS_CB *p_cb = btm_cb.devcb.p_dev_status_cb; + + /* Call the call back to pass the device status to application */ + if (p_cb) + (*p_cb)(status); +} + + diff --git a/stack/btm/btm_inq.c b/stack/btm/btm_inq.c new file mode 100644 index 0000000..f42cdc1 --- /dev/null +++ b/stack/btm/btm_inq.c @@ -0,0 +1,3233 @@ +/***************************************************************************** +** +** Name: btm_inq.c +** +** Description: This file contains functions that handle inquiries. These +** include setting discoverable mode, controlling the mode +** of the Baseband, and maintaining a small database of +** inquiry responses, with API for people to browse it. +** +** NOTE: Only ONE inquiry is allowed to run at a time +** (including periodic inquiries); +** +** +** Copyright (c) 1999-2012, 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 "gki.h" +#include "hcimsgs.h" +#include "btu.h" +#include "btm_api.h" +#include "btm_int.h" +#include "hcidefs.h" + +#define BTM_INQ_REPLY_TIMEOUT 3 /* 3 second timeout waiting for responses */ + +/* TRUE to enable DEBUG traces for btm_inq */ +#ifndef BTM_INQ_DEBUG +#define BTM_INQ_DEBUG FALSE +#endif +/********************************************************************************/ +/* L O C A L D A T A D E F I N I T I O N S */ +/********************************************************************************/ +static const LAP general_inq_lap = {0x9e,0x8b,0x33}; +static const LAP limited_inq_lap = {0x9e,0x8b,0x00}; + +#if (( BTM_EIR_CLIENT_INCLUDED == TRUE )||( BTM_EIR_SERVER_INCLUDED == TRUE )) +#ifndef BTM_EIR_UUID_LKUP_TBL +const UINT16 BTM_EIR_UUID_LKUP_TBL[BTM_EIR_MAX_SERVICES] = +{ + UUID_SERVCLASS_SERVICE_DISCOVERY_SERVER, +/* UUID_SERVCLASS_BROWSE_GROUP_DESCRIPTOR, */ +/* UUID_SERVCLASS_PUBLIC_BROWSE_GROUP, */ + UUID_SERVCLASS_SERIAL_PORT, + UUID_SERVCLASS_LAN_ACCESS_USING_PPP, + UUID_SERVCLASS_DIALUP_NETWORKING, + UUID_SERVCLASS_IRMC_SYNC, + UUID_SERVCLASS_OBEX_OBJECT_PUSH, + UUID_SERVCLASS_OBEX_FILE_TRANSFER, + UUID_SERVCLASS_IRMC_SYNC_COMMAND, + UUID_SERVCLASS_HEADSET, + UUID_SERVCLASS_CORDLESS_TELEPHONY, + UUID_SERVCLASS_AUDIO_SOURCE, + UUID_SERVCLASS_AUDIO_SINK, + UUID_SERVCLASS_AV_REM_CTRL_TARGET, +/* UUID_SERVCLASS_ADV_AUDIO_DISTRIBUTION, */ + UUID_SERVCLASS_AV_REMOTE_CONTROL, +/* UUID_SERVCLASS_VIDEO_CONFERENCING, */ + UUID_SERVCLASS_INTERCOM, + UUID_SERVCLASS_FAX, + UUID_SERVCLASS_HEADSET_AUDIO_GATEWAY, +/* UUID_SERVCLASS_WAP, */ +/* UUID_SERVCLASS_WAP_CLIENT, */ + UUID_SERVCLASS_PANU, + UUID_SERVCLASS_NAP, + UUID_SERVCLASS_GN, + UUID_SERVCLASS_DIRECT_PRINTING, +/* UUID_SERVCLASS_REFERENCE_PRINTING, */ + UUID_SERVCLASS_IMAGING, + UUID_SERVCLASS_IMAGING_RESPONDER, + UUID_SERVCLASS_IMAGING_AUTO_ARCHIVE, + UUID_SERVCLASS_IMAGING_REF_OBJECTS, + UUID_SERVCLASS_HF_HANDSFREE, + UUID_SERVCLASS_AG_HANDSFREE, + UUID_SERVCLASS_DIR_PRT_REF_OBJ_SERVICE, +/* UUID_SERVCLASS_REFLECTED_UI, */ + UUID_SERVCLASS_BASIC_PRINTING, + UUID_SERVCLASS_PRINTING_STATUS, + UUID_SERVCLASS_HUMAN_INTERFACE, + UUID_SERVCLASS_CABLE_REPLACEMENT, + UUID_SERVCLASS_HCRP_PRINT, + UUID_SERVCLASS_HCRP_SCAN, +/* UUID_SERVCLASS_COMMON_ISDN_ACCESS, */ +/* UUID_SERVCLASS_VIDEO_CONFERENCING_GW, */ +/* UUID_SERVCLASS_UDI_MT, */ +/* UUID_SERVCLASS_UDI_TA, */ +/* UUID_SERVCLASS_VCP, */ + UUID_SERVCLASS_SAP, + UUID_SERVCLASS_PBAP_PCE, + UUID_SERVCLASS_PBAP_PSE, + UUID_SERVCLASS_PHONE_ACCESS, + UUID_SERVCLASS_HEADSET_HS, + UUID_SERVCLASS_PNP_INFORMATION, +/* UUID_SERVCLASS_GENERIC_NETWORKING, */ +/* UUID_SERVCLASS_GENERIC_FILETRANSFER, */ +/* UUID_SERVCLASS_GENERIC_AUDIO, */ +/* UUID_SERVCLASS_GENERIC_TELEPHONY, */ +/* UUID_SERVCLASS_UPNP_SERVICE, */ +/* UUID_SERVCLASS_UPNP_IP_SERVICE, */ +/* UUID_SERVCLASS_ESDP_UPNP_IP_PAN, */ +/* UUID_SERVCLASS_ESDP_UPNP_IP_LAP, */ +/* UUID_SERVCLASS_ESDP_UPNP_IP_L2CAP, */ + UUID_SERVCLASS_VIDEO_SOURCE, + UUID_SERVCLASS_VIDEO_SINK, +/* UUID_SERVCLASS_VIDEO_DISTRIBUTION */ + UUID_SERVCLASS_MESSAGE_ACCESS, + UUID_SERVCLASS_MESSAGE_NOTIFICATION, + UUID_SERVCLASS_HDP_SOURCE, + UUID_SERVCLASS_HDP_SINK +}; +#else +/* +If customized UUID look-up table needs to be used, +the followings should be defined in buildcfg.h. +BTM_EIR_UUID_LKUP_TBL = <customized UUID list> +BTM_EIR_MAX_SERVICES = <number of UUID in list> +*/ +#if (BTM_EIR_MAX_SERVICES == 0) +const UINT16 BTM_EIR_UUID_LKUP_TBL[]; +#else +extern UINT16 BTM_EIR_UUID_LKUP_TBL[BTM_EIR_MAX_SERVICES]; +#endif +#endif +#endif /* BTM_EIR_UUID_LKUP_TBL*/ + +/********************************************************************************/ +/* L O C A L F U N C T I O N P R O T O T Y P E S */ +/********************************************************************************/ +static void btm_initiate_inquiry (tBTM_INQUIRY_VAR_ST *p_inq); +static tBTM_STATUS btm_set_inq_event_filter (UINT8 filter_cond_type, tBTM_INQ_FILT_COND *p_filt_cond); +static void btm_clr_inq_result_flt (void); + +#if ((BTM_EIR_SERVER_INCLUDED == TRUE)||(BTM_EIR_CLIENT_INCLUDED == TRUE)) +static UINT8 btm_convert_uuid_to_eir_service( UINT16 uuid16 ); +#endif +#if (BTM_EIR_CLIENT_INCLUDED == TRUE) +static void btm_set_eir_uuid( UINT8 *p_eir, tBTM_INQ_RESULTS *p_results ); +static UINT8 *btm_eir_get_uuid_list( UINT8 *p_eir, UINT8 uuid_size, + UINT8 *p_num_uuid, UINT8 *p_uuid_list_type ); +static UINT16 btm_convert_uuid_to_uuid16( UINT8 *p_uuid, UINT8 uuid_size ); +#endif + +/******************************************************************************* +** +** Function BTM_SetDiscoverability +** +** Description This function is called to set the device into or out of +** discoverable mode. Discoverable mode means inquiry +** scans are enabled. If a value of '0' is entered for window or +** interval, the default values are used. +** +** Returns BTM_SUCCESS if successful +** BTM_BUSY if a setting of the filter is already in progress +** BTM_NO_RESOURCES if couldn't get a memory pool buffer +** BTM_ILLEGAL_VALUE if a bad parameter was detected +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ +tBTM_STATUS BTM_SetDiscoverability (UINT16 inq_mode, UINT16 window, UINT16 interval) +{ + UINT8 scan_mode = 0; + UINT16 service_class; + UINT8 *p_cod; + UINT8 major, minor; + DEV_CLASS cod; + LAP temp_lap[2]; + BOOLEAN is_limited; + BOOLEAN cod_limited; + + BTM_TRACE_API0 ("BTM_SetDiscoverability"); +#if (BLE_INCLUDED == TRUE && BLE_INCLUDED == TRUE) + if (btm_ble_set_discoverability((UINT16)(inq_mode)) + == BTM_SUCCESS) + { + btm_cb.btm_inq_vars.discoverable_mode &= (~BTM_BLE_DISCOVERABLE_MASK); + btm_cb.btm_inq_vars.discoverable_mode |= (inq_mode & BTM_BLE_CONNECTABLE_MASK); + } + inq_mode &= ~BTM_BLE_DISCOVERABLE_MASK; +#endif + + /*** Check mode parameter ***/ + if (inq_mode > BTM_MAX_DISCOVERABLE) + return (BTM_ILLEGAL_VALUE); + + /* Make sure the controller is active */ + if (btm_cb.devcb.state < BTM_DEV_STATE_READY) + return (BTM_DEV_RESET); + + /* If the window and/or interval is '0', set to default values */ + if (!window) + window = BTM_DEFAULT_DISC_WINDOW; + + if (!interval) + interval = BTM_DEFAULT_DISC_INTERVAL; + + BTM_TRACE_API3 ("BTM_SetDiscoverability: mode %d [NonDisc-0, Lim-1, Gen-2], window 0x%04x, interval 0x%04x", + inq_mode, window, interval); + + /*** Check for valid window and interval parameters ***/ + /*** Only check window and duration if mode is connectable ***/ + if (inq_mode != BTM_NON_DISCOVERABLE) + { + /* window must be less than or equal to interval */ + if (window < HCI_MIN_INQUIRYSCAN_WINDOW || + window > HCI_MAX_INQUIRYSCAN_WINDOW || + interval < HCI_MIN_INQUIRYSCAN_INTERVAL || + interval > HCI_MAX_INQUIRYSCAN_INTERVAL || + window > interval) + { + return (BTM_ILLEGAL_VALUE); + } + } + + /* Set the IAC if needed */ + if (inq_mode != BTM_NON_DISCOVERABLE) + { + if (inq_mode & BTM_LIMITED_DISCOVERABLE) + { + /* Use the GIAC and LIAC codes for limited discoverable mode */ + memcpy (temp_lap[0], limited_inq_lap, LAP_LEN); + memcpy (temp_lap[1], general_inq_lap, LAP_LEN); + + if (!btsnd_hcic_write_cur_iac_lap (2, (LAP * const) temp_lap)) + return (BTM_NO_RESOURCES); /* Cannot continue */ + } + else + { + if (!btsnd_hcic_write_cur_iac_lap (1, (LAP * const) &general_inq_lap)) + return (BTM_NO_RESOURCES); /* Cannot continue */ + } + + scan_mode |= HCI_INQUIRY_SCAN_ENABLED; + } + + /* Send down the inquiry scan window and period if changed */ + if ((window != btm_cb.btm_inq_vars.inq_scan_window) || + (interval != btm_cb.btm_inq_vars.inq_scan_period)) + { + if (btsnd_hcic_write_inqscan_cfg (interval, window)) + { + btm_cb.btm_inq_vars.inq_scan_window = window; + btm_cb.btm_inq_vars.inq_scan_period = interval; + } + else + return (BTM_NO_RESOURCES); + } + + if (btm_cb.btm_inq_vars.connectable_mode & BTM_CONNECTABLE_MASK) + scan_mode |= HCI_PAGE_SCAN_ENABLED; + + if (btsnd_hcic_write_scan_enable (scan_mode)) + { + btm_cb.btm_inq_vars.discoverable_mode &= (~BTM_DISCOVERABLE_MASK); + btm_cb.btm_inq_vars.discoverable_mode |= inq_mode; + } + else + return (BTM_NO_RESOURCES); + + /* Change the service class bit if mode has changed */ + p_cod = BTM_ReadDeviceClass(); + BTM_COD_SERVICE_CLASS(service_class, p_cod); + is_limited = (inq_mode & BTM_LIMITED_DISCOVERABLE) ? TRUE : FALSE; + cod_limited = (service_class & BTM_COD_SERVICE_LMTD_DISCOVER) ? TRUE : FALSE; + if (is_limited ^ cod_limited) + { + BTM_COD_MINOR_CLASS(minor, p_cod ); + BTM_COD_MAJOR_CLASS(major, p_cod ); + if (is_limited) + service_class |= BTM_COD_SERVICE_LMTD_DISCOVER; + else + service_class &= ~BTM_COD_SERVICE_LMTD_DISCOVER; + + FIELDS_TO_COD(cod, minor, major, service_class); + (void) BTM_SetDeviceClass (cod); + } + + return (BTM_SUCCESS); +} + +/******************************************************************************* +** +** Function BTM_SetInquiryScanType +** +** Description This function is called to set the iquiry scan-type to +** standard or interlaced. +** +** Returns BTM_SUCCESS if successful +** BTM_MODE_UNSUPPORTED if not a 1.2 device +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ +tBTM_STATUS BTM_SetInquiryScanType (UINT16 scan_type) +{ + + BTM_TRACE_API0 ("BTM_SetInquiryScanType"); + if (scan_type != BTM_SCAN_TYPE_STANDARD && scan_type != BTM_SCAN_TYPE_INTERLACED) + return (BTM_ILLEGAL_VALUE); + + /* whatever app wants if device is not 1.2 scan type should be STANDARD */ + if (!HCI_LMP_INTERLACED_INQ_SCAN_SUPPORTED(btm_cb.devcb.local_features)) + return (BTM_MODE_UNSUPPORTED); + + /* Check for scan type if configuration has been changed */ + if (scan_type != btm_cb.btm_inq_vars.inq_scan_type) + { + if (BTM_IsDeviceUp()) + { + if (btsnd_hcic_write_inqscan_type ((UINT8)scan_type)) + btm_cb.btm_inq_vars.inq_scan_type = scan_type; + else + return (BTM_NO_RESOURCES); + } + else return (BTM_WRONG_MODE); + } + return (BTM_SUCCESS); +} + +/******************************************************************************* +** +** Function BTM_SetPageScanType +** +** Description This function is called to set the page scan-type to +** standard or interlaced. +** +** Returns BTM_SUCCESS if successful +** BTM_MODE_UNSUPPORTED if not a 1.2 device +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ +tBTM_STATUS BTM_SetPageScanType (UINT16 scan_type) +{ + BTM_TRACE_API0 ("BTM_SetPageScanType"); + if (scan_type != BTM_SCAN_TYPE_STANDARD && scan_type != BTM_SCAN_TYPE_INTERLACED) + return (BTM_ILLEGAL_VALUE); + + /* whatever app wants if device is not 1.2 scan type should be STANDARD */ + if (!HCI_LMP_INTERLACED_PAGE_SCAN_SUPPORTED(btm_cb.devcb.local_features)) + return (BTM_MODE_UNSUPPORTED); + + /* Check for scan type if configuration has been changed */ + if (scan_type != btm_cb.btm_inq_vars.page_scan_type) + { + if (BTM_IsDeviceUp()) + { + if (btsnd_hcic_write_pagescan_type ((UINT8)scan_type)) + btm_cb.btm_inq_vars.page_scan_type = scan_type; + else + return (BTM_NO_RESOURCES); + } + else return (BTM_WRONG_MODE); + } + return (BTM_SUCCESS); +} + + +/******************************************************************************* +** +** Function BTM_SetInquiryMode +** +** Description This function is called to set standard or with RSSI +** mode of the inquiry for local device. +** +** Output Params: mode - standard, with RSSI, extended +** +** Returns BTM_SUCCESS if successful +** BTM_NO_RESOURCES if couldn't get a memory pool buffer +** BTM_ILLEGAL_VALUE if a bad parameter was detected +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ +tBTM_STATUS BTM_SetInquiryMode (UINT8 mode) +{ + BTM_TRACE_API0 ("BTM_SetInquiryMode"); + if (mode == BTM_INQ_RESULT_STANDARD) + { + /* mandatory mode */ + } + else if (mode == BTM_INQ_RESULT_WITH_RSSI) + { + if (!HCI_LMP_INQ_RSSI_SUPPORTED(btm_cb.devcb.local_features)) + return (BTM_MODE_UNSUPPORTED); + } +#if (( BTM_EIR_CLIENT_INCLUDED == TRUE )||( BTM_EIR_SERVER_INCLUDED == TRUE )) + else if (mode == BTM_INQ_RESULT_EXTENDED) + { + if (!HCI_EXT_INQ_RSP_SUPPORTED(btm_cb.devcb.local_features)) + return (BTM_MODE_UNSUPPORTED); + } +#endif + else + return (BTM_ILLEGAL_VALUE); + + if (!BTM_IsDeviceUp()) + return (BTM_WRONG_MODE); + + if (!btsnd_hcic_write_inquiry_mode (mode)) + return (BTM_NO_RESOURCES); + + return (BTM_SUCCESS); +} + +/******************************************************************************* +** +** Function BTM_ReadDiscoverability +** +** Description This function is called to read the current discoverability +** mode of the device. +** +** Output Params: p_window - current inquiry scan duration +** p_interval - current inquiry scan interval +** +** Returns BTM_NON_DISCOVERABLE, BTM_LIMITED_DISCOVERABLE, or +** BTM_GENERAL_DISCOVERABLE +** +*******************************************************************************/ +UINT16 BTM_ReadDiscoverability (UINT16 *p_window, UINT16 *p_interval) +{ + BTM_TRACE_API0 ("BTM_ReadDiscoverability"); + if (p_window) + *p_window = btm_cb.btm_inq_vars.inq_scan_window; + + if (p_interval) + *p_interval = btm_cb.btm_inq_vars.inq_scan_period; + + return (btm_cb.btm_inq_vars.discoverable_mode); +} + + +/******************************************************************************* +** +** Function BTM_SetPeriodicInquiryMode +** +** Description This function is called to set the device periodic inquiry mode. +** If the duration is zero, the periodic inquiry mode is cancelled. +** +** Note: We currently do not allow concurrent inquiry and periodic inquiry. +** +** Parameters: p_inqparms - pointer to the inquiry information +** mode - GENERAL or LIMITED inquiry +** duration - length in 1.28 sec intervals (If '0', the inquiry is CANCELLED) +** max_resps - maximum amount of devices to search for before ending the inquiry +** filter_cond_type - BTM_CLR_INQUIRY_FILTER, BTM_FILTER_COND_DEVICE_CLASS, or +** BTM_FILTER_COND_BD_ADDR +** filter_cond - value for the filter (based on filter_cond_type) +** +** max_delay - maximum amount of time between successive inquiries +** min_delay - minimum amount of time between successive inquiries +** p_results_cb - callback returning pointer to results (tBTM_INQ_RESULTS) +** +** Returns BTM_CMD_STARTED if successfully started +** BTM_ILLEGAL_VALUE if a bad parameter is detected +** BTM_NO_RESOURCES if could not allocate a message buffer +** BTM_SUCCESS - if cancelling the periodic inquiry +** BTM_BUSY - if an inquiry is already active +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ +tBTM_STATUS BTM_SetPeriodicInquiryMode (tBTM_INQ_PARMS *p_inqparms, UINT16 max_delay, + UINT16 min_delay, tBTM_INQ_RESULTS_CB *p_results_cb) +{ + tBTM_STATUS status; + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + + BTM_TRACE_API6 ("BTM_SetPeriodicInquiryMode: mode: %d, dur: %d, rsps: %d, flt: %d, min: %d, max: %d", + p_inqparms->mode, p_inqparms->duration, p_inqparms->max_resps, + p_inqparms->filter_cond_type, min_delay, max_delay); + + /*** Make sure the device is ready ***/ + if (!BTM_IsDeviceUp()) + return (BTM_WRONG_MODE); + + /* Only one active inquiry is allowed in this implementation. + Also do not allow an inquiry if the inquiry filter is being updated */ + if (p_inq->inq_active || p_inq->inqfilt_active) + return (BTM_BUSY); + + /* If illegal parameters return FALSE */ + if (p_inqparms->mode != BTM_GENERAL_INQUIRY && + p_inqparms->mode != BTM_LIMITED_INQUIRY) + return (BTM_ILLEGAL_VALUE); + + /* Verify the parameters for this command */ + if (p_inqparms->duration < BTM_MIN_INQUIRY_LEN || + p_inqparms->duration > BTM_MAX_INQUIRY_LENGTH || + min_delay <= p_inqparms->duration || + min_delay < BTM_PER_INQ_MIN_MIN_PERIOD || + min_delay > BTM_PER_INQ_MAX_MIN_PERIOD || + max_delay <= min_delay || + max_delay < BTM_PER_INQ_MIN_MAX_PERIOD || + max_delay > BTM_PER_INQ_MAX_MAX_PERIOD) + { + return (BTM_ILLEGAL_VALUE); + } + + /* Save the inquiry parameters to be used upon the completion of setting/clearing the inquiry filter */ + p_inq->inqparms = *p_inqparms; + p_inq->per_min_delay = min_delay; + p_inq->per_max_delay = max_delay; + p_inq->inq_cmpl_info.num_resp = 0; /* Clear the results counter */ + p_inq->p_inq_results_cb = p_results_cb; + + p_inq->inq_active = (UINT8)((p_inqparms->mode == BTM_LIMITED_INQUIRY) ? + (BTM_LIMITED_INQUIRY_ACTIVE | BTM_PERIODIC_INQUIRY_ACTIVE) : + (BTM_GENERAL_INQUIRY_ACTIVE | BTM_PERIODIC_INQUIRY_ACTIVE)); + +#if (defined(BTM_BYPASS_EVENT_FILTERING) && BTM_BYPASS_EVENT_FILTERING == TRUE) + BTM_TRACE_WARNING0("BTM: Bypassing event filtering..."); + + p_inq->state = BTM_INQ_ACTIVE_STATE; + p_inq->inqfilt_active = FALSE; + btm_initiate_inquiry (p_inq); + status = BTM_CMD_STARTED; +#else + /* If a filter is specified, then save it for later and clear the current filter. + The setting of the filter is done upon completion of clearing of the previous + filter. + */ + if (p_inqparms->filter_cond_type != BTM_CLR_INQUIRY_FILTER) + { + p_inq->state = BTM_INQ_CLR_FILT_STATE; + p_inqparms->filter_cond_type = BTM_CLR_INQUIRY_FILTER; + } + else /* The filter is not being used so simply clear it; the inquiry can start after this operation */ + p_inq->state = BTM_INQ_SET_FILT_STATE; + + /* Before beginning the inquiry the current filter must be cleared, so initiate the command */ + if ((status = btm_set_inq_event_filter (p_inqparms->filter_cond_type, &p_inqparms->filter_cond)) != BTM_CMD_STARTED) + { + /* If set filter command is not succesful reset the state */ + p_inq->p_inq_results_cb = NULL; + p_inq->state = BTM_INQ_INACTIVE_STATE; + + } + +#endif + return (status); +} + + +/******************************************************************************* +** +** Function BTM_CancelPeriodicInquiry +** +** Description This function cancels a periodic inquiry +** +** Returns +** BTM_NO_RESOURCES if could not allocate a message buffer +** BTM_SUCCESS - if cancelling the periodic inquiry +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ +tBTM_STATUS BTM_CancelPeriodicInquiry(void) +{ + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + tBTM_STATUS status = BTM_SUCCESS; + BTM_TRACE_API0 ("BTM_CancelPeriodicInquiry called"); + + /*** Make sure the device is ready ***/ + if (!BTM_IsDeviceUp()) + return (BTM_WRONG_MODE); + + /* Only cancel if one is active */ + if (btm_cb.btm_inq_vars.inq_active & BTM_PERIODIC_INQUIRY_ACTIVE) + { + btm_cb.btm_inq_vars.inq_active = BTM_INQUIRY_INACTIVE; + btm_cb.btm_inq_vars.p_inq_results_cb = (tBTM_INQ_RESULTS_CB *) NULL; + + if (!btsnd_hcic_exit_per_inq ()) + status = BTM_NO_RESOURCES; + + /* If the event filter is in progress, mark it so that the processing of the return + event will be ignored */ + if(p_inq->inqfilt_active) + p_inq->pending_filt_complete_event++; + + p_inq->inqfilt_active = FALSE; + p_inq->inq_counter++; + } + + return (status); +} + + +/******************************************************************************* +** +** Function BTM_SetConnectability +** +** Description This function is called to set the device into or out of +** connectable mode. Discoverable mode means page scans enabled. +** +** Returns BTM_SUCCESS if successful +** BTM_ILLEGAL_VALUE if a bad parameter is detected +** BTM_NO_RESOURCES if could not allocate a message buffer +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ +tBTM_STATUS BTM_SetConnectability (UINT16 page_mode, UINT16 window, UINT16 interval) +{ + UINT8 scan_mode = 0; + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + + BTM_TRACE_API0 ("BTM_SetConnectability"); + +#if (BLE_INCLUDED == TRUE && BLE_INCLUDED == TRUE) + if (btm_ble_set_connectability(page_mode) == BTM_SUCCESS) + { + p_inq->connectable_mode &= (~BTM_BLE_CONNECTABLE_MASK); + p_inq->connectable_mode |= (page_mode & BTM_BLE_CONNECTABLE_MASK); + } + page_mode &= ~BTM_BLE_CONNECTABLE_MASK; + +#endif + + /*** Check mode parameter ***/ + if (page_mode != BTM_NON_CONNECTABLE && page_mode != BTM_CONNECTABLE) + return (BTM_ILLEGAL_VALUE); + + /* Make sure the controller is active */ + if (btm_cb.devcb.state < BTM_DEV_STATE_READY) + return (BTM_DEV_RESET); + + /* If the window and/or interval is '0', set to default values */ + if (!window) + window = BTM_DEFAULT_CONN_WINDOW; + + if (!interval) + interval = BTM_DEFAULT_CONN_INTERVAL; + + BTM_TRACE_API3 ("BTM_SetConnectability: mode %d [NonConn-0, Conn-1], window 0x%04x, interval 0x%04x", + page_mode, window, interval); + + /*** Check for valid window and interval parameters ***/ + /*** Only check window and duration if mode is connectable ***/ + if (page_mode == BTM_CONNECTABLE) + { + /* window must be less than or equal to interval */ + if (window < HCI_MIN_PAGESCAN_WINDOW || + window > HCI_MAX_PAGESCAN_WINDOW || + interval < HCI_MIN_PAGESCAN_INTERVAL || + interval > HCI_MAX_PAGESCAN_INTERVAL || + window > interval) + { + return (BTM_ILLEGAL_VALUE); + } + + scan_mode |= HCI_PAGE_SCAN_ENABLED; + } + + if ((window != p_inq->page_scan_window) || + (interval != p_inq->page_scan_period)) + { + p_inq->page_scan_window = window; + p_inq->page_scan_period = interval; + if (!btsnd_hcic_write_pagescan_cfg (interval, window)) + return (BTM_NO_RESOURCES); + } + + /* Keep the inquiry scan as previouosly set */ + if (p_inq->discoverable_mode & BTM_DISCOVERABLE_MASK) + scan_mode |= HCI_INQUIRY_SCAN_ENABLED; + + if (btsnd_hcic_write_scan_enable (scan_mode)) + { + p_inq->connectable_mode &= (~BTM_CONNECTABLE_MASK); + p_inq->connectable_mode |= page_mode; + + return (BTM_SUCCESS); + } + + return (BTM_NO_RESOURCES); +} + + +/******************************************************************************* +** +** Function BTM_ReadConnectability +** +** Description This function is called to read the current discoverability +** mode of the device. +** Output Params p_window - current page scan duration +** p_interval - current time between page scans +** +** Returns BTM_NON_CONNECTABLE or BTM_CONNECTABLE +** +*******************************************************************************/ +UINT16 BTM_ReadConnectability (UINT16 *p_window, UINT16 *p_interval) +{ + BTM_TRACE_API0 ("BTM_ReadConnectability"); + if (p_window) + *p_window = btm_cb.btm_inq_vars.page_scan_window; + + if (p_interval) + *p_interval = btm_cb.btm_inq_vars.page_scan_period; + + return (btm_cb.btm_inq_vars.connectable_mode); +} + + + +/******************************************************************************* +** +** Function BTM_IsInquiryActive +** +** Description This function returns a bit mask of the current inquiry state +** +** Returns BTM_INQUIRY_INACTIVE if inactive (0) +** BTM_LIMITED_INQUIRY_ACTIVE if a limted inquiry is active +** BTM_GENERAL_INQUIRY_ACTIVE if a general inquiry is active +** BTM_PERIODIC_INQUIRY_ACTIVE if a periodic inquiry is active +** +*******************************************************************************/ +UINT16 BTM_IsInquiryActive (void) +{ + BTM_TRACE_API0 ("BTM_IsInquiryActive"); + + return(btm_cb.btm_inq_vars.inq_active); +} + + + +/******************************************************************************* +** +** Function BTM_CancelInquiry +** +** Description This function cancels an inquiry if active +** +** Returns BTM_SUCCESS if successful +** BTM_NO_RESOURCES if could not allocate a message buffer +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ +tBTM_STATUS BTM_CancelInquiry(void) +{ + tBTM_STATUS status = BTM_SUCCESS; + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + + BTM_TRACE_API0 ("BTM_CancelInquiry called"); + + /*** Make sure the device is ready ***/ + if (!BTM_IsDeviceUp()) + return (BTM_WRONG_MODE); + + /* Only cancel if not in periodic mode, otherwise the caller should call BTM_CancelPeriodicMode */ + if (p_inq->inq_active && + (!(p_inq->inq_active & BTM_PERIODIC_INQUIRY_ACTIVE))) + { + p_inq->inq_active = BTM_INQUIRY_INACTIVE; + p_inq->state = BTM_INQ_INACTIVE_STATE; + p_inq->p_inq_results_cb = (tBTM_INQ_RESULTS_CB *) NULL; /* Do not notify caller anymore */ + p_inq->p_inq_cmpl_cb = (tBTM_CMPL_CB *) NULL; /* Do not notify caller anymore */ + + /* If the event filter is in progress, mark it so that the processing of the return + event will be ignored */ + if (p_inq->inqfilt_active) + { + p_inq->inqfilt_active = FALSE; + p_inq->pending_filt_complete_event++; + } + /* Initiate the cancel inquiry */ + else + { + if (!btsnd_hcic_inq_cancel()) + status = BTM_NO_RESOURCES; +#if BLE_INCLUDED == TRUE + if ((p_inq->inqparms.mode & BTM_BLE_INQUIRY_MASK) != 0) + btm_ble_stop_scan(); +#endif + } + +#if (defined(BTM_BUSY_LEVEL_CHANGE_INCLUDED) && BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE) + btm_acl_update_busy_level (BTM_BLI_INQ_DONE_EVT); +#endif + + p_inq->inq_counter++; + btm_clr_inq_result_flt(); + } + + return (status); +} + + +/******************************************************************************* +** +** Function BTM_StartInquiry +** +** Description This function is called to start an inquiry. +** +** Parameters: p_inqparms - pointer to the inquiry information +** mode - GENERAL or LIMITED inquiry +** duration - length in 1.28 sec intervals (If '0', the inquiry is CANCELLED) +** max_resps - maximum amount of devices to search for before ending the inquiry +** filter_cond_type - BTM_CLR_INQUIRY_FILTER, BTM_FILTER_COND_DEVICE_CLASS, or +** BTM_FILTER_COND_BD_ADDR +** filter_cond - value for the filter (based on filter_cond_type) +** +** p_results_cb - Pointer to the callback routine which gets called +** upon receipt of an inquiry result. If this field is +** NULL, the application is not notified. +** +** p_cmpl_cb - Pointer to the callback routine which gets called +** upon completion. If this field is NULL, the +** application is not notified when completed. +** Returns tBTM_STATUS +** BTM_CMD_STARTED if successfully initiated +** BTM_BUSY if already in progress +** BTM_ILLEGAL_VALUE if parameter(s) are out of range +** BTM_NO_RESOURCES if could not allocate resources to start the command +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ +tBTM_STATUS BTM_StartInquiry (tBTM_INQ_PARMS *p_inqparms, tBTM_INQ_RESULTS_CB *p_results_cb, + tBTM_CMPL_CB *p_cmpl_cb) +{ + tBTM_STATUS status; + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + + BTM_TRACE_API4 ("BTM_StartInquiry: mode: %d, dur: %d, rsps: %d, flt: %d", + p_inqparms->mode, p_inqparms->duration, p_inqparms->max_resps, + p_inqparms->filter_cond_type); + + /* Only one active inquiry is allowed in this implementation. + Also do not allow an inquiry if the inquiry filter is being updated */ + if (p_inq->inq_active || p_inq->inqfilt_active) + return (BTM_BUSY); + + /*** Make sure the device is ready ***/ + if (!BTM_IsDeviceUp()) + return (BTM_WRONG_MODE); + + if ((p_inqparms->mode & BTM_BR_INQUIRY_MASK)!= BTM_GENERAL_INQUIRY && + (p_inqparms->mode & BTM_BR_INQUIRY_MASK)!= BTM_LIMITED_INQUIRY) + return (BTM_ILLEGAL_VALUE); + + /* Save the inquiry parameters to be used upon the completion of setting/clearing the inquiry filter */ + p_inq->inqparms = *p_inqparms; +#if (BLE_INCLUDED == TRUE) + p_inq->inqparms.mode = (UINT8)(p_inq->inqparms.mode & BTM_BLE_INQUIRY_MASK) | (UINT8)(1 << (p_inqparms->mode & BTM_BR_INQUIRY_MASK)); +#else + p_inq->inqparms.mode = (UINT8)(1 << (p_inqparms->mode & BTM_BR_INQUIRY_MASK)); +#endif + + /* Initialize the inquiry variables */ + p_inq->state = BTM_INQ_ACTIVE_STATE; + p_inq->p_inq_cmpl_cb = p_cmpl_cb; + p_inq->p_inq_results_cb = p_results_cb; + p_inq->inq_cmpl_info.num_resp = 0; /* Clear the results counter */ + p_inq->inq_active = (UINT8)(1 << (p_inqparms->mode & BTM_BR_INQUIRY_MASK)); + + BTM_TRACE_DEBUG1("BTM_StartInquiry: p_inq->inq_active = 0x%02x", p_inq->inq_active); + +/* start LE inquiry here if requested */ +#if BLE_INCLUDED == TRUE + if (p_inqparms->mode & BTM_BLE_INQUIRY_MASK) + { + /* BLE for now does not support filter condition for inquiry */ + if (btm_ble_start_inquiry((UINT8)(p_inqparms->mode & BTM_BLE_INQUIRY_MASK), + p_inq->inqparms.duration) != BTM_SUCCESS) + { + BTM_TRACE_ERROR0("Err Starting LE Inquiry."); + p_inq->inqparms.mode &= ~ BTM_BLE_INQUIRY_MASK; + } + + p_inqparms->mode &= ~BTM_BLE_INQUIRY_MASK; + + BTM_TRACE_DEBUG1("BTM_StartInquiry: mode = %02x", p_inqparms->mode); + } +#endif /* end of BLE_INCLUDED */ + +#if (defined(BTM_BYPASS_EVENT_FILTERING) && BTM_BYPASS_EVENT_FILTERING == TRUE) + BTM_TRACE_WARNING0("BTM: Bypassing event filtering..."); + p_inq->inqfilt_active = FALSE; + btm_initiate_inquiry (p_inq); + status = BTM_CMD_STARTED; +#else + /* If a filter is specified, then save it for later and clear the current filter. + The setting of the filter is done upon completion of clearing of the previous + filter. + */ + switch (p_inqparms->filter_cond_type) + { + case BTM_CLR_INQUIRY_FILTER: + p_inq->state = BTM_INQ_SET_FILT_STATE; + break; + + case BTM_FILTER_COND_DEVICE_CLASS: + case BTM_FILTER_COND_BD_ADDR: + /* The filter is not being used so simply clear it; + the inquiry can start after this operation */ + p_inq->state = BTM_INQ_CLR_FILT_STATE; + p_inqparms->filter_cond_type = BTM_CLR_INQUIRY_FILTER; + /* =============>>>> adding LE filtering here ????? */ + break; + + default: + return (BTM_ILLEGAL_VALUE); + } + + /* Before beginning the inquiry the current filter must be cleared, so initiate the command */ + if ((status = btm_set_inq_event_filter (p_inqparms->filter_cond_type, &p_inqparms->filter_cond)) != BTM_CMD_STARTED) + p_inq->state = BTM_INQ_INACTIVE_STATE; +#endif + return (status); +} + + +/******************************************************************************* +** +** Function BTM_ReadRemoteDeviceName +** +** Description This function initiates a remote device HCI command to the +** controller and calls the callback when the process has completed. +** +** Input Params: remote_bda - device address of name to retrieve +** p_cb - callback function called when BTM_CMD_STARTED +** is returned. +** A pointer to tBTM_REMOTE_DEV_NAME is passed to the +** callback. +** +** Returns +** BTM_CMD_STARTED is returned if the request was successfully sent +** to HCI. +** BTM_BUSY if already in progress +** BTM_UNKNOWN_ADDR if device address is bad +** BTM_NO_RESOURCES if could not allocate resources to start the command +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ +tBTM_STATUS BTM_ReadRemoteDeviceName (BD_ADDR remote_bda, tBTM_CMPL_CB *p_cb) +{ + tBTM_INQ_INFO *p_cur = NULL; + tINQ_DB_ENT *p_i; + +#if BLE_INCLUDED == TRUE + tBT_DEVICE_TYPE dev_type; + tBLE_ADDR_TYPE addr_type; +#endif + + BTM_TRACE_API6 ("BTM_ReadRemoteDeviceName: bd addr [%02x%02x%02x%02x%02x%02x]", + remote_bda[0], remote_bda[1], remote_bda[2], + remote_bda[3], remote_bda[4], remote_bda[5]); + + /* Use the remote device's clock offset if it is in the local inquiry database */ + if ((p_i = btm_inq_db_find (remote_bda)) != NULL) + { + p_cur = &p_i->inq_info; + +#if (BTM_INQ_GET_REMOTE_NAME == TRUE) + p_cur->remote_name_state = BTM_INQ_RMT_NAME_EMPTY; +#endif + } + BTM_TRACE_API0 ("no device found in inquiry db"); + +#if (BLE_INCLUDED == TRUE) + BTM_ReadDevInfo(remote_bda, &dev_type, &addr_type); + if (dev_type == BT_DEVICE_TYPE_BLE) + { + return btm_ble_read_remote_name(remote_bda, p_cur, p_cb); + } + else +#endif + + return (btm_initiate_rem_name (remote_bda, p_cur, BTM_RMT_NAME_EXT, + BTM_EXT_RMT_NAME_TIMEOUT, p_cb)); +} + +/******************************************************************************* +** +** Function BTM_CancelRemoteDeviceName +** +** Description This function initiates the cancel request for the specified +** remote device. +** +** Input Params: None +** +** Returns +** BTM_CMD_STARTED is returned if the request was successfully sent +** to HCI. +** BTM_NO_RESOURCES if could not allocate resources to start the command +** BTM_WRONG_MODE if there is not an active remote name request. +** +*******************************************************************************/ +tBTM_STATUS BTM_CancelRemoteDeviceName (void) +{ + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + +#if BLE_INCLUDED == TRUE + tBT_DEVICE_TYPE dev_type; + tBLE_ADDR_TYPE addr_type; +#endif + + BTM_TRACE_API0 ("BTM_CancelRemoteDeviceName()"); + + /* Make sure there is not already one in progress */ + if (p_inq->remname_active) + { +#if BLE_INCLUDED == TRUE + BTM_ReadDevInfo(p_inq->remname_bda, &dev_type, &addr_type); + if (dev_type == BT_DEVICE_TYPE_BLE) + { + if (btm_ble_cancel_remote_name(p_inq->remname_bda)) + return (BTM_CMD_STARTED); + else + return (BTM_UNKNOWN_ADDR); + } + else +#endif + if (btsnd_hcic_rmt_name_req_cancel (p_inq->remname_bda)) + return (BTM_CMD_STARTED); + else + return (BTM_NO_RESOURCES); + } + else + return (BTM_WRONG_MODE); +} + +/******************************************************************************* +** +** Function BTM_InqFirstResult +** +** Description This function looks through the inquiry database for the first +** used entrysince the LAST inquiry. This is used in conjunction +** with BTM_InqNext by applications as a way to walk through the +** inquiry results database. +** +** Returns pointer to first in-use entry, or NULL if DB is empty +** +*******************************************************************************/ +tBTM_INQ_INFO *BTM_InqFirstResult (void) +{ + UINT16 xx; + tINQ_DB_ENT *p_ent = btm_cb.btm_inq_vars.inq_db; + UINT32 cur_inq_count = btm_cb.btm_inq_vars.inq_counter - 1; + + for (xx = 0; xx < BTM_INQ_DB_SIZE; xx++, p_ent++) + { + if (p_ent->in_use && p_ent->inq_count == cur_inq_count) + return (&p_ent->inq_info); + } + + /* If here, no used entry found */ + return ((tBTM_INQ_INFO *)NULL); +} + + +/******************************************************************************* +** +** Function BTM_InqNextResult +** +** Description This function looks through the inquiry database for the next +** used entrysince the LAST inquiry. If the input parameter is NULL, +** the first entry is returned. +** +** Returns pointer to next in-use entry, or NULL if no more found. +** +*******************************************************************************/ +tBTM_INQ_INFO *BTM_InqNextResult (tBTM_INQ_INFO *p_cur) +{ + tINQ_DB_ENT *p_ent; + UINT16 inx; + UINT32 cur_inq_count = btm_cb.btm_inq_vars.inq_counter - 1; + + if (p_cur) + { + p_ent = (tINQ_DB_ENT *) ((UINT8 *)p_cur - offsetof (tINQ_DB_ENT, inq_info)); + inx = (UINT16)((p_ent - btm_cb.btm_inq_vars.inq_db) + 1); + + for (p_ent = &btm_cb.btm_inq_vars.inq_db[inx]; inx < BTM_INQ_DB_SIZE; inx++, p_ent++) + { + if (p_ent->in_use && p_ent->inq_count == cur_inq_count) + return (&p_ent->inq_info); + } + + /* If here, more entries found */ + return ((tBTM_INQ_INFO *)NULL); + } + else + return (BTM_InqDbFirst()); +} + + +/******************************************************************************* +** +** Function BTM_InqDbRead +** +** Description This function looks through the inquiry database for a match +** based on Bluetooth Device Address. This is the application's +** interface to get the inquiry details of a specific BD address. +** +** Returns pointer to entry, or NULL if not found +** +*******************************************************************************/ +tBTM_INQ_INFO *BTM_InqDbRead (BD_ADDR p_bda) +{ + UINT16 xx; + tINQ_DB_ENT *p_ent = btm_cb.btm_inq_vars.inq_db; + + BTM_TRACE_API6 ("BTM_InqDbRead: bd addr [%02x%02x%02x%02x%02x%02x]", + p_bda[0], p_bda[1], p_bda[2], p_bda[3], p_bda[4], p_bda[5]); + + for (xx = 0; xx < BTM_INQ_DB_SIZE; xx++, p_ent++) + { + if ((p_ent->in_use) && (!memcmp (p_ent->inq_info.results.remote_bd_addr, p_bda, BD_ADDR_LEN))) + return (&p_ent->inq_info); + } + + /* If here, not found */ + return ((tBTM_INQ_INFO *)NULL); +} + + +/******************************************************************************* +** +** Function BTM_InqDbFirst +** +** Description This function looks through the inquiry database for the first +** used entry, and returns that. This is used in conjunction with +** BTM_InqDbNext by applications as a way to walk through the +** inquiry database. +** +** Returns pointer to first in-use entry, or NULL if DB is empty +** +*******************************************************************************/ +tBTM_INQ_INFO *BTM_InqDbFirst (void) +{ + UINT16 xx; + tINQ_DB_ENT *p_ent = btm_cb.btm_inq_vars.inq_db; + + for (xx = 0; xx < BTM_INQ_DB_SIZE; xx++, p_ent++) + { + if (p_ent->in_use) + return (&p_ent->inq_info); + } + + /* If here, no used entry found */ + return ((tBTM_INQ_INFO *)NULL); +} + + +/******************************************************************************* +** +** Function BTM_InqDbNext +** +** Description This function looks through the inquiry database for the next +** used entry, and returns that. If the input parameter is NULL, +** the first entry is returned. +** +** Returns pointer to next in-use entry, or NULL if no more found. +** +*******************************************************************************/ +tBTM_INQ_INFO *BTM_InqDbNext (tBTM_INQ_INFO *p_cur) +{ + tINQ_DB_ENT *p_ent; + UINT16 inx; + + if (p_cur) + { + p_ent = (tINQ_DB_ENT *) ((UINT8 *)p_cur - offsetof (tINQ_DB_ENT, inq_info)); + inx = (UINT16)((p_ent - btm_cb.btm_inq_vars.inq_db) + 1); + + for (p_ent = &btm_cb.btm_inq_vars.inq_db[inx]; inx < BTM_INQ_DB_SIZE; inx++, p_ent++) + { + if (p_ent->in_use) + return (&p_ent->inq_info); + } + + /* If here, more entries found */ + return ((tBTM_INQ_INFO *)NULL); + } + else + return (BTM_InqDbFirst()); +} + + +/******************************************************************************* +** +** Function BTM_ClearInqDb +** +** Description This function is called to clear out a device or all devices +** from the inquiry database. +** +** Parameter p_bda - (input) BD_ADDR -> Address of device to clear +** (NULL clears all entries) +** +** Returns BTM_BUSY if an inquiry, get remote name, or event filter +** is active, otherwise BTM_SUCCESS +** +*******************************************************************************/ +tBTM_STATUS BTM_ClearInqDb (BD_ADDR p_bda) +{ + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + + /* If an inquiry or remote name is in progress return busy */ + if (p_inq->inq_active != BTM_INQUIRY_INACTIVE || + p_inq->inqfilt_active) + return (BTM_BUSY); + + btm_clr_inq_db(p_bda); + + return (BTM_SUCCESS); +} + + +/******************************************************************************* +** +** Function BTM_ReadNumInqDbEntries +** +** Returns This function returns the number of entries in the inquiry database. +** +*******************************************************************************/ +UINT8 BTM_ReadNumInqDbEntries (void) +{ + UINT8 num_entries; + UINT8 num_results; + tINQ_DB_ENT *p_ent = btm_cb.btm_inq_vars.inq_db; + + for (num_entries = 0, num_results = 0; num_entries < BTM_INQ_DB_SIZE; num_entries++, p_ent++) + { + if (p_ent->in_use) + num_results++; + } + + return (num_results); +} + + +/******************************************************************************* +** +** Function BTM_InquiryRegisterForChanges +** +** Returns This function is called to register a callback for when the +** inquiry database changes, i.e. new entry or entry deleted. +** +*******************************************************************************/ +tBTM_STATUS BTM_InquiryRegisterForChanges (tBTM_INQ_DB_CHANGE_CB *p_cb) +{ + if (!p_cb) + btm_cb.btm_inq_vars.p_inq_change_cb = NULL; + else if (btm_cb.btm_inq_vars.p_inq_change_cb) + return (BTM_BUSY); + else + btm_cb.btm_inq_vars.p_inq_change_cb = p_cb; + + return (BTM_SUCCESS); +} + + +/******************************************************************************* +** +** Function BTM_SetInquiryFilterCallback +** +** Description Host can register to be asked whenever an inquiry result +** is received. If host does not like the device no name +** request is issued for the device +** +** Returns void +** +*******************************************************************************/ +void BTM_SetInquiryFilterCallback (tBTM_FILTER_CB *p_callback) +{ + btm_cb.p_inq_filter_cb = p_callback; +} + +/******************************************************************************* +** +** Function BTM_ReadInquiryRspTxPower +** +** Description This command will read the inquiry Transmit Power level used +** to transmit the FHS and EIR data packets. +** This can be used directly in the Tx Power Level EIR data type. +** +** Returns BTM_SUCCESS if successful +** +*******************************************************************************/ +tBTM_STATUS BTM_ReadInquiryRspTxPower (tBTM_CMPL_CB *p_cb) +{ + if (btm_cb.devcb.p_txpwer_cmpl_cb) + return (BTM_BUSY); + + btu_start_timer (&btm_cb.devcb.txpwer_timer, BTU_TTYPE_BTM_ACL, BTM_INQ_REPLY_TIMEOUT ); + + + btm_cb.devcb.p_txpwer_cmpl_cb = p_cb; + + if (!btsnd_hcic_read_inq_tx_power ()) + { + btm_cb.devcb.p_txpwer_cmpl_cb = NULL; + btu_stop_timer (&btm_cb.devcb.txpwer_timer); + return (BTM_NO_RESOURCES); + } + else + return (BTM_CMD_STARTED); +} +/******************************************************************************* +** +** Function BTM_WriteInquiryTxPower +** +** Description This command is used to write the inquiry transmit power level +** used to transmit the inquiry (ID) data packets. The Controller +** should use the supported TX power level closest to the Tx_Power +** parameter. +** +** Returns BTM_SUCCESS if successful +** +*******************************************************************************/ +tBTM_STATUS BTM_WriteInquiryTxPower (INT8 tx_power) +{ + tBTM_STATUS status = BTM_SUCCESS; + + if (tx_power < BTM_MIN_INQ_TX_POWER || tx_power > BTM_MAX_INQ_TX_POWER) + { + status = BTM_ILLEGAL_VALUE; + } + else if (!btsnd_hcic_write_inq_tx_power(tx_power)) + status = BTM_NO_RESOURCES; + + return status; +} +/********************************************************************************* +********************************************************************************** +** ** +** BTM Internal Inquiry Functions ** +** ** +********************************************************************************** +*********************************************************************************/ +/******************************************************************************* +** +** Function btm_inq_db_reset +** +** Description This function is called at at reset to clear the inquiry +** database & pending callback. +** +** Returns void +** +*******************************************************************************/ +void btm_inq_db_reset (void) +{ + tBTM_REMOTE_DEV_NAME rem_name; + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + UINT8 num_responses; + UINT8 temp_inq_active; + tBTM_STATUS status; + + btu_stop_timer (&p_inq->inq_timer_ent); + + /* If an inquiry or periodic inquiry is active, reset the mode to inactive */ + if (p_inq->inq_active != BTM_INQUIRY_INACTIVE) + { + temp_inq_active = p_inq->inq_active; /* Save so state can change BEFORE + callback is called */ + p_inq->inq_active = BTM_INQUIRY_INACTIVE; + + /* If not a periodic inquiry, the complete callback must be called to notify caller */ + if (temp_inq_active == BTM_LIMITED_INQUIRY_ACTIVE || + temp_inq_active == BTM_GENERAL_INQUIRY_ACTIVE) + { + if (p_inq->p_inq_cmpl_cb) + { + num_responses = 0; + (*p_inq->p_inq_cmpl_cb)(&num_responses); + } + } + } + + /* Cancel a remote name request if active, and notify the caller (if waiting) */ + if (p_inq->remname_active ) + { + btu_stop_timer (&p_inq->rmt_name_timer_ent); + p_inq->remname_active = FALSE; + memset(p_inq->remname_bda, 0, BD_ADDR_LEN); + + if (p_inq->p_remname_cmpl_cb) + { + rem_name.status = BTM_DEV_RESET; + + (*p_inq->p_remname_cmpl_cb)(&rem_name); + p_inq->p_remname_cmpl_cb = NULL; + } + } + + /* Cancel an inquiry filter request if active, and notify the caller (if waiting) */ + if (p_inq->inqfilt_active) + { + p_inq->inqfilt_active = FALSE; + + if (p_inq->p_inqfilter_cmpl_cb) + { + status = BTM_DEV_RESET; + (*p_inq->p_inqfilter_cmpl_cb)(&status); + } + } + + p_inq->state = BTM_INQ_INACTIVE_STATE; + p_inq->pending_filt_complete_event = 0; + p_inq->p_inq_results_cb = NULL; + btm_clr_inq_db(NULL); /* Clear out all the entries in the database */ + btm_clr_inq_result_flt(); + + p_inq->discoverable_mode = BTM_NON_DISCOVERABLE; + p_inq->connectable_mode = BTM_NON_CONNECTABLE; + p_inq->page_scan_type = BTM_SCAN_TYPE_STANDARD; + p_inq->inq_scan_type = BTM_SCAN_TYPE_STANDARD; + +#if BLE_INCLUDED == TRUE + p_inq->discoverable_mode |= BTM_BLE_NON_DISCOVERABLE; + p_inq->connectable_mode |= BTM_BLE_NON_CONNECTABLE; +#endif + return; +} + + +/********************************************************************************* +** +** Function btm_inq_db_init +** +** Description This function is called at startup to initialize the inquiry +** database. +** +** Returns void +** +*******************************************************************************/ +void btm_inq_db_init (void) +{ +#if 0 /* cleared in btm_init; put back in if called from anywhere else! */ + memset (&btm_cb.btm_inq_vars, 0, sizeof (tBTM_INQUIRY_VAR_ST)); +#endif + btm_cb.btm_inq_vars.no_inc_ssp = BTM_NO_SSP_ON_INQUIRY; +} + +/********************************************************************************* +** +** Function btm_inq_stop_on_ssp +** +** Description This function is called on incoming SSP +** +** Returns void +** +*******************************************************************************/ +void btm_inq_stop_on_ssp(void) +{ + UINT8 normal_active = (BTM_GENERAL_INQUIRY_ACTIVE|BTM_LIMITED_INQUIRY_ACTIVE); + +#if (BTM_INQ_DEBUG == TRUE) + BTM_TRACE_DEBUG4 ("btm_inq_stop_on_ssp: no_inc_ssp=%d inq_active:0x%x state:%d inqfilt_active:%d", + btm_cb.btm_inq_vars.no_inc_ssp, btm_cb.btm_inq_vars.inq_active, btm_cb.btm_inq_vars.state, btm_cb.btm_inq_vars.inqfilt_active); +#endif + if (btm_cb.btm_inq_vars.no_inc_ssp) + { + if (btm_cb.btm_inq_vars.state == BTM_INQ_ACTIVE_STATE) + { + if (btm_cb.btm_inq_vars.inq_active & BTM_PERIODIC_INQUIRY_ACTIVE) + { + BTM_CancelPeriodicInquiry(); + } + else if (btm_cb.btm_inq_vars.inq_active & normal_active) + { + /* can not call BTM_CancelInquiry() here. We need to report inquiry complete evt */ + btsnd_hcic_inq_cancel(); + } + } + /* do not allow inquiry to start */ + btm_cb.btm_inq_vars.inq_active |= BTM_SSP_INQUIRY_ACTIVE; + } +} + +/********************************************************************************* +** +** Function btm_inq_clear_ssp +** +** Description This function is called when pairing_state becomes idle +** +** Returns void +** +*******************************************************************************/ +void btm_inq_clear_ssp(void) +{ + btm_cb.btm_inq_vars.inq_active &= ~BTM_SSP_INQUIRY_ACTIVE; +} + +/********************************************************************************* +** +** Function btm_clr_inq_db +** +** Description This function is called to clear out a device or all devices +** from the inquiry database. +** +** Parameter p_bda - (input) BD_ADDR -> Address of device to clear +** (NULL clears all entries) +** +** Returns void +** +*******************************************************************************/ +void btm_clr_inq_db (BD_ADDR p_bda) +{ + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + tINQ_DB_ENT *p_ent = p_inq->inq_db; + UINT16 xx; + +#if (BTM_INQ_DEBUG == TRUE) + BTM_TRACE_DEBUG2 ("btm_clr_inq_db: inq_active:0x%x state:%d", + btm_cb.btm_inq_vars.inq_active, btm_cb.btm_inq_vars.state); +#endif + for (xx = 0; xx < BTM_INQ_DB_SIZE; xx++, p_ent++) + { + if (p_ent->in_use) + { + /* If this is the specified BD_ADDR or clearing all devices */ + if (p_bda == NULL || + (!memcmp (p_ent->inq_info.results.remote_bd_addr, p_bda, BD_ADDR_LEN))) + { + p_ent->in_use = FALSE; +#if (BTM_INQ_GET_REMOTE_NAME == TRUE) + p_ent->inq_info.remote_name_state = BTM_INQ_RMT_NAME_EMPTY; +#endif + + if (btm_cb.btm_inq_vars.p_inq_change_cb) + (*btm_cb.btm_inq_vars.p_inq_change_cb) (&p_ent->inq_info, FALSE); + } + } + } +#if (BTM_INQ_DEBUG == TRUE) + BTM_TRACE_DEBUG2 ("inq_active:0x%x state:%d", + btm_cb.btm_inq_vars.inq_active, btm_cb.btm_inq_vars.state); +#endif +} + + +/******************************************************************************* +** +** Function btm_clr_inq_result_flt +** +** Description This function looks through the bdaddr database for a match +** based on Bluetooth Device Address +** +** Returns TRUE if found, else FALSE (new entry) +** +*******************************************************************************/ +static void btm_clr_inq_result_flt (void) +{ +#if BTM_USE_INQ_RESULTS_FILTER == TRUE + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + + if (p_inq->p_bd_db) + { + GKI_freebuf(p_inq->p_bd_db); + p_inq->p_bd_db = NULL; + } + p_inq->num_bd_entries = 0; + p_inq->max_bd_entries = 0; +#endif +} + +/******************************************************************************* +** +** Function btm_inq_find_bdaddr +** +** Description This function looks through the bdaddr database for a match +** based on Bluetooth Device Address +** +** Returns TRUE if found, else FALSE (new entry) +** +*******************************************************************************/ +BOOLEAN btm_inq_find_bdaddr (BD_ADDR p_bda) +{ +#if BTM_USE_INQ_RESULTS_FILTER == TRUE + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + tINQ_BDADDR *p_db = &p_inq->p_bd_db[0]; + UINT16 xx; + + /* Don't bother searching, database doesn't exist or periodic mode */ + if ((p_inq->inq_active & BTM_PERIODIC_INQUIRY_ACTIVE) || !p_db) + return (FALSE); + + for (xx = 0; xx < p_inq->num_bd_entries; xx++, p_db++) + { + if (!memcmp(p_db->bd_addr, p_bda, BD_ADDR_LEN) + && p_db->inq_count == p_inq->inq_counter) + return (TRUE); + } + + if (xx < p_inq->max_bd_entries) + { + p_db->inq_count = p_inq->inq_counter; + memcpy(p_db->bd_addr, p_bda, BD_ADDR_LEN); + p_inq->num_bd_entries++; + } + +#endif + /* If here, New Entry */ + return (FALSE); +} + +/******************************************************************************* +** +** Function btm_inq_db_find +** +** Description This function looks through the inquiry database for a match +** based on Bluetooth Device Address +** +** Returns pointer to entry, or NULL if not found +** +*******************************************************************************/ +tINQ_DB_ENT *btm_inq_db_find (BD_ADDR p_bda) +{ + UINT16 xx; + tINQ_DB_ENT *p_ent = btm_cb.btm_inq_vars.inq_db; + + for (xx = 0; xx < BTM_INQ_DB_SIZE; xx++, p_ent++) + { + if ((p_ent->in_use) && (!memcmp (p_ent->inq_info.results.remote_bd_addr, p_bda, BD_ADDR_LEN))) + return (p_ent); + } + + /* If here, not found */ + return (NULL); +} + + +/******************************************************************************* +** +** Function btm_inq_db_new +** +** Description This function looks through the inquiry database for an unused +** entry. If no entry is free, it allocates the oldest entry. +** +** Returns pointer to entry +** +*******************************************************************************/ +tINQ_DB_ENT *btm_inq_db_new (BD_ADDR p_bda) +{ + UINT16 xx; + tINQ_DB_ENT *p_ent = btm_cb.btm_inq_vars.inq_db; + tINQ_DB_ENT *p_old = btm_cb.btm_inq_vars.inq_db; + UINT32 ot = 0xFFFFFFFF; + + for (xx = 0; xx < BTM_INQ_DB_SIZE; xx++, p_ent++) + { + if (!p_ent->in_use) + { + memset (p_ent, 0, sizeof (tINQ_DB_ENT)); + memcpy (p_ent->inq_info.results.remote_bd_addr, p_bda, BD_ADDR_LEN); + p_ent->in_use = TRUE; + +#if (BTM_INQ_GET_REMOTE_NAME==TRUE) + p_ent->inq_info.remote_name_state = BTM_INQ_RMT_NAME_EMPTY; +#endif + + return (p_ent); + } + + if (p_ent->time_of_resp < ot) + { + p_old = p_ent; + ot = p_ent->time_of_resp; + } + } + + /* If here, no free entry found. Return the oldest. */ + + /* Before deleting the oldest, if anyone is registered for change */ + /* notifications, then tell him we are deleting an entry. */ + if (btm_cb.btm_inq_vars.p_inq_change_cb) + (*btm_cb.btm_inq_vars.p_inq_change_cb) (&p_old->inq_info, FALSE); + + memset (p_old, 0, sizeof (tINQ_DB_ENT)); + memcpy (p_old->inq_info.results.remote_bd_addr, p_bda, BD_ADDR_LEN); + p_old->in_use = TRUE; + +#if (BTM_INQ_GET_REMOTE_NAME==TRUE) + p_old->inq_info.remote_name_state = BTM_INQ_RMT_NAME_EMPTY; +#endif + + return (p_old); +} + + +/******************************************************************************* +** +** Function btm_set_inq_event_filter +** +** Description This function is called to set the inquiry event filter. +** It is called by either internally, or by the external API function +** (BTM_SetInqEventFilter). It is used internally as part of the +** inquiry processing. +** +** Input Params: +** filter_cond_type - this is the type of inquiry filter to apply: +** BTM_FILTER_COND_DEVICE_CLASS, +** BTM_FILTER_COND_BD_ADDR, or +** BTM_CLR_INQUIRY_FILTER +** +** p_filt_cond - this is either a BD_ADDR or DEV_CLASS depending on the +** filter_cond_type (See section 4.7.3 of Core Spec 1.0b). +** +** Returns BTM_CMD_STARTED if successfully initiated +** BTM_NO_RESOURCES if couldn't get a memory pool buffer +** BTM_ILLEGAL_VALUE if a bad parameter was detected +** +*******************************************************************************/ +static tBTM_STATUS btm_set_inq_event_filter (UINT8 filter_cond_type, + tBTM_INQ_FILT_COND *p_filt_cond) +{ + UINT8 condition_length = DEV_CLASS_LEN * 2; + UINT8 condition_buf[DEV_CLASS_LEN * 2]; + UINT8 *p_cond = condition_buf; /* points to the condition to pass to HCI */ + +#if (BTM_INQ_DEBUG == TRUE) + BTM_TRACE_DEBUG1 ("btm_set_inq_event_filter: filter type %d [Clear-0, COD-1, BDADDR-2]", + filter_cond_type); + BTM_TRACE_DEBUG6 (" condition [%02x%02x%02x %02x%02x%02x]", + p_filt_cond->bdaddr_cond[0], p_filt_cond->bdaddr_cond[1], p_filt_cond->bdaddr_cond[2], + p_filt_cond->bdaddr_cond[3], p_filt_cond->bdaddr_cond[4], p_filt_cond->bdaddr_cond[5]); +#endif + + /* Load the correct filter condition to pass to the lower layer */ + switch (filter_cond_type) + { + case BTM_FILTER_COND_DEVICE_CLASS: + /* copy the device class and device class fields into contiguous memory to send to HCI */ + memcpy (condition_buf, p_filt_cond->cod_cond.dev_class, DEV_CLASS_LEN); + memcpy (&condition_buf[DEV_CLASS_LEN], + p_filt_cond->cod_cond.dev_class_mask, DEV_CLASS_LEN); + + /* condition length should already be set as the default */ + break; + + case BTM_FILTER_COND_BD_ADDR: + p_cond = p_filt_cond->bdaddr_cond; + + /* condition length should already be set as the default */ + break; + + case BTM_CLR_INQUIRY_FILTER: + condition_length = 0; + break; + + default: + return (BTM_ILLEGAL_VALUE); /* Bad parameter was passed in */ + } + + btm_cb.btm_inq_vars.inqfilt_active = TRUE; + + /* Filter the inquiry results for the specified condition type and value */ + if (btsnd_hcic_set_event_filter(HCI_FILTER_INQUIRY_RESULT, filter_cond_type, + p_cond, condition_length)) + + return (BTM_CMD_STARTED); + else + return (BTM_NO_RESOURCES); +} + + +/******************************************************************************* +** +** Function btm_event_filter_complete +** +** Description This function is called when a set event filter has completed. +** Note: This routine currently only handles inquiry filters. +** Connection filters are ignored for now. +** +** Returns void +** +*******************************************************************************/ +void btm_event_filter_complete (UINT8 *p) +{ + UINT8 hci_status; + tBTM_STATUS status; + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + tBTM_CMPL_CB *p_cb = p_inq->p_inqfilter_cmpl_cb; + +#if (BTM_INQ_DEBUG == TRUE) + BTM_TRACE_DEBUG3 ("btm_event_filter_complete: inq_active:0x%x state:%d inqfilt_active:%d", + btm_cb.btm_inq_vars.inq_active, btm_cb.btm_inq_vars.state, btm_cb.btm_inq_vars.inqfilt_active); +#endif + /* If the filter complete event is from an old or cancelled request, ignore it */ + if(p_inq->pending_filt_complete_event) + { + p_inq->pending_filt_complete_event--; + return; + } + + /* Only process the inquiry filter; Ignore the connection filter until it + is used by the upper layers */ + if (p_inq->inqfilt_active == TRUE ) + { + /* Extract the returned status from the buffer */ + STREAM_TO_UINT8 (hci_status, p); + if (hci_status != HCI_SUCCESS) + { + /* If standalone operation, return the error status; if embedded in the inquiry, continue the inquiry */ + BTM_TRACE_WARNING1 ("BTM Warning: Set Event Filter Failed (HCI returned 0x%x)", hci_status); + status = BTM_ERR_PROCESSING; + } + else + status = BTM_SUCCESS; + + /* If the set filter was initiated externally (via BTM_SetInqEventFilter), call the + callback function to notify the initiator that it has completed */ + if (p_inq->state == BTM_INQ_INACTIVE_STATE) + { + p_inq->inqfilt_active = FALSE; + if (p_cb) + (*p_cb) (&status); + } + else /* An inquiry is active (the set filter command was internally generated), + process the next state of the process (Set a new filter or start the inquiry). */ + { + if(status != BTM_SUCCESS) + { + /* Process the inquiry complete (Error Status) */ + btm_process_inq_complete (BTM_ERR_PROCESSING, (UINT8)(p_inq->inqparms.mode & BTM_BR_INQUIRY_MASK)); + + /* btm_process_inq_complete() does not restore the following settings on periodic inquiry */ + p_inq->inqfilt_active = FALSE; + p_inq->inq_active = BTM_INQUIRY_INACTIVE; + p_inq->state = BTM_INQ_INACTIVE_STATE; + + return; + } + + /* Check to see if a new filter needs to be set up */ + if (p_inq->state == BTM_INQ_CLR_FILT_STATE) + { + if ((status = btm_set_inq_event_filter (p_inq->inqparms.filter_cond_type, &p_inq->inqparms.filter_cond)) == BTM_CMD_STARTED) + { + p_inq->state = BTM_INQ_SET_FILT_STATE; + } + else /* Error setting the filter: Call the initiator's callback function to indicate a failure */ + { + p_inq->inqfilt_active = FALSE; + + /* Process the inquiry complete (Error Status) */ + btm_process_inq_complete (BTM_ERR_PROCESSING, (UINT8)(p_inq->inqparms.mode & BTM_BR_INQUIRY_MASK)); + } + } + else /* Initiate the Inquiry or Periodic Inquiry */ + { + p_inq->state = BTM_INQ_ACTIVE_STATE; + p_inq->inqfilt_active = FALSE; + btm_initiate_inquiry (p_inq); + } + } + } +} + + +/******************************************************************************* +** +** Function btm_initiate_inquiry +** +** Description This function is called to start an inquiry or periodic inquiry +** upon completion of the setting and/or clearing of the inquiry filter. +** +** Inputs: p_inq (btm_cb.btm_inq_vars) - pointer to saved inquiry information +** mode - GENERAL or LIMITED inquiry +** duration - length in 1.28 sec intervals (If '0', the inquiry is CANCELLED) +** max_resps - maximum amount of devices to search for before ending the inquiry +** filter_cond_type - BTM_CLR_INQUIRY_FILTER, BTM_FILTER_COND_DEVICE_CLASS, or +** BTM_FILTER_COND_BD_ADDR +** filter_cond - value for the filter (based on filter_cond_type) +** +** Returns If an error occurs the initiator's callback is called with the error status. +** +*******************************************************************************/ +static void btm_initiate_inquiry (tBTM_INQUIRY_VAR_ST *p_inq) +{ + const LAP *lap; + tBTM_INQ_PARMS *p_inqparms = &p_inq->inqparms; + +#if (BTM_INQ_DEBUG == TRUE) + BTM_TRACE_DEBUG3 ("btm_initiate_inquiry: inq_active:0x%x state:%d inqfilt_active:%d", + btm_cb.btm_inq_vars.inq_active, btm_cb.btm_inq_vars.state, btm_cb.btm_inq_vars.inqfilt_active); +#endif +#if (defined(BTM_BUSY_LEVEL_CHANGE_INCLUDED) && BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE) + btm_acl_update_busy_level (BTM_BLI_INQ_EVT); +#endif + + if (p_inq->inq_active & BTM_SSP_INQUIRY_ACTIVE) + { + btm_process_inq_complete (BTM_NO_RESOURCES, (UINT8)(p_inqparms->mode & BTM_BR_INQUIRY_MASK)); + return; + } + + /* Make sure the number of responses doesn't overflow the database configuration */ + p_inqparms->max_resps = (UINT8)((p_inqparms->max_resps <= BTM_INQ_DB_SIZE) ? p_inqparms->max_resps : BTM_INQ_DB_SIZE); + + lap = (p_inq->inq_active & BTM_LIMITED_INQUIRY_ACTIVE) ? &limited_inq_lap : &general_inq_lap; + + if (p_inq->inq_active & BTM_PERIODIC_INQUIRY_ACTIVE) + { + if (!btsnd_hcic_per_inq_mode (p_inq->per_max_delay, + p_inq->per_min_delay, + *lap, p_inqparms->duration, + p_inqparms->max_resps)) + btm_process_inq_complete (BTM_NO_RESOURCES, (UINT8)(p_inqparms->mode & BTM_BR_INQUIRY_MASK)); + } + else + { +#if BTM_USE_INQ_RESULTS_FILTER == TRUE + btm_clr_inq_result_flt(); + + /* Allocate memory to hold bd_addrs responding */ + if ((p_inq->p_bd_db = (tINQ_BDADDR *)GKI_getbuf(GKI_MAX_BUF_SIZE)) != NULL) + { + p_inq->max_bd_entries = (UINT16)(GKI_MAX_BUF_SIZE / sizeof(tINQ_BDADDR)); + memset(p_inq->p_bd_db, 0, GKI_MAX_BUF_SIZE); +/* BTM_TRACE_DEBUG1("btm_initiate_inquiry: memory allocated for %d bdaddrs", + p_inq->max_bd_entries); */ + } + + if (!btsnd_hcic_inquiry(*lap, p_inqparms->duration, 0)) +#else + if (!btsnd_hcic_inquiry(*lap, p_inqparms->duration, p_inqparms->max_resps)) +#endif /* BTM_USE_INQ_RESULTS_FILTER */ + btm_process_inq_complete (BTM_NO_RESOURCES, (UINT8)(p_inqparms->mode & BTM_BR_INQUIRY_MASK)); + } +} + +/******************************************************************************* +** +** Function btm_process_inq_results +** +** Description This function is called when inquiry results are received from +** the device. It updates the inquiry database. If the inquiry +** database is full, the oldest entry is discarded. +** +** Parameters inq_res_mode - BTM_INQ_RESULT_STANDARD +** BTM_INQ_RESULT_WITH_RSSI +** BTM_INQ_RESULT_EXTENDED +** +** Returns void +** +*******************************************************************************/ +void btm_process_inq_results (UINT8 *p, UINT8 inq_res_mode) +{ + UINT8 num_resp, xx; + BD_ADDR bda; + tINQ_DB_ENT *p_i; + tBTM_INQ_RESULTS *p_cur; + BOOLEAN is_new = TRUE; + BOOLEAN update = FALSE; + INT8 i_rssi; + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + tBTM_INQ_RESULTS_CB *p_inq_results_cb = p_inq->p_inq_results_cb; + UINT8 page_scan_rep_mode = 0; + UINT8 page_scan_per_mode = 0; + UINT8 page_scan_mode = 0; + UINT8 rssi = 0; + DEV_CLASS dc; + UINT16 clock_offset; +#if (BTM_EIR_CLIENT_INCLUDED == TRUE) + UINT8 *p_eir_data = NULL; +#if (BTM_INQ_GET_REMOTE_NAME==TRUE) + UINT8 remote_name_len; +#endif +#endif + +#if (BTM_INQ_DEBUG == TRUE) + BTM_TRACE_DEBUG3 ("btm_process_inq_results inq_active:0x%x state:%d inqfilt_active:%d", + btm_cb.btm_inq_vars.inq_active, btm_cb.btm_inq_vars.state, btm_cb.btm_inq_vars.inqfilt_active); +#endif + /* Only process the results if the inquiry is still active */ + if (!p_inq->inq_active) + return; + + STREAM_TO_UINT8 (num_resp, p); + + for (xx = 0; xx < num_resp; xx++) + { + update = FALSE; + /* Extract inquiry results */ + STREAM_TO_BDADDR (bda, p); + STREAM_TO_UINT8 (page_scan_rep_mode, p); + STREAM_TO_UINT8 (page_scan_per_mode, p); + + if (inq_res_mode == BTM_INQ_RESULT_STANDARD) + { + STREAM_TO_UINT8(page_scan_mode, p); + } + + STREAM_TO_DEVCLASS (dc, p); + STREAM_TO_UINT16 (clock_offset, p); + if (inq_res_mode != BTM_INQ_RESULT_STANDARD) + { + STREAM_TO_UINT8(rssi, p); + } + + p_i = btm_inq_db_find (bda); + +#if BTM_USE_INQ_RESULTS_FILTER == TRUE + /* Only process the num_resp is smaller than max_resps. + If results are queued to BTU task while canceling inquiry, + or when more than one result is in this response, > max_resp + responses could be processed which can confuse some apps + */ + if (p_inq->inqparms.max_resps && + p_inq->inq_cmpl_info.num_resp >= p_inq->inqparms.max_resps +#if BLE_INCLUDED == TRUE + /* new device response */ + && ( p_i == NULL || + /* exisiting device with BR/EDR info */ + (p_i && (p_i->inq_info.results.device_type & BT_DEVICE_TYPE_BREDR) != 0) + ) +#endif + + ) + { +/* BTM_TRACE_WARNING0("INQ RES: Extra Response Received...ignoring"); */ + return; + } +#endif + + /* Check if this address has already been processed for this inquiry */ + if (btm_inq_find_bdaddr(bda)) + { +/* BTM_TRACE_DEBUG6("BDA seen before [%02x%02x %02x%02x %02x%02x]", + bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]);*/ + /* By default suppose no update needed */ + i_rssi = (INT8)rssi; + + /* If this new RSSI is higher than the last one */ + if(p_inq->inqparms.report_dup && (rssi != 0) && + p_i && (i_rssi > p_i->inq_info.results.rssi || p_i->inq_info.results.rssi == 0 +#if BLE_INCLUDED == TRUE + /* BR/EDR inquiry information update */ + || (p_i->inq_info.results.device_type & BT_DEVICE_TYPE_BREDR) != 0 +#endif + )) + { + p_cur = &p_i->inq_info.results; + BTM_TRACE_DEBUG2("update RSSI new:%d, old:%d", i_rssi, p_cur->rssi); + p_cur->rssi = i_rssi; + update = TRUE; + } + /* If we received a second Extended Inq Event for an already */ + /* discovered device, this is because for the first one EIR was not received */ + else if ((inq_res_mode == BTM_INQ_RESULT_EXTENDED) && (p_i)) + { + p_cur = &p_i->inq_info.results; + update = TRUE; + } + /* If no update needed continue with next response (if any) */ + else + continue; + } + + /* Host can be registered to verify comming BDA or DC */ + if (btm_cb.p_inq_filter_cb) + { + if (!(* btm_cb.p_inq_filter_cb) (bda, dc)) + { + continue; + } + } + + /* If existing entry, use that, else get a new one (possibly reusing the oldest) */ + if (p_i == NULL) + { + p_i = btm_inq_db_new (bda); + is_new = TRUE; + } + + /* If an entry for the device already exists, overwrite it ONLY if it is from + a previous inquiry. (Ignore it if it is a duplicate response from the same + inquiry. + */ + else if (p_i->inq_count == p_inq->inq_counter +#if (BLE_INCLUDED == TRUE ) + && (p_i->inq_info.results.device_type & BT_DEVICE_TYPE_BREDR) +#endif + ) + is_new = FALSE; + + /* keep updating RSSI to have latest value */ + if( inq_res_mode != BTM_INQ_RESULT_STANDARD ) + p_i->inq_info.results.rssi = (INT8)rssi; + else + p_i->inq_info.results.rssi = BTM_INQ_RES_IGNORE_RSSI; + + if (is_new == TRUE) + { + /* Save the info */ + p_cur = &p_i->inq_info.results; + p_cur->page_scan_rep_mode = page_scan_rep_mode; + p_cur->page_scan_per_mode = page_scan_per_mode; + p_cur->page_scan_mode = page_scan_mode; + p_cur->dev_class[0] = dc[0]; + p_cur->dev_class[1] = dc[1]; + p_cur->dev_class[2] = dc[2]; + p_cur->clock_offset = clock_offset | BTM_CLOCK_OFFSET_VALID; + + p_i->time_of_resp = GKI_get_tick_count (); + + if (p_i->inq_count != p_inq->inq_counter) + p_inq->inq_cmpl_info.num_resp++; /* A new response was found */ + +#if (defined BLE_INCLUDED && BLE_INCLUDED == TRUE) + p_cur->inq_result_type = BTM_INQ_RESULT_BR; + if (p_i->inq_count != p_inq->inq_counter) + { + p_cur->device_type = BT_DEVICE_TYPE_BREDR; + p_i->scan_rsp = FALSE; + } + else + p_cur->device_type |= BT_DEVICE_TYPE_BREDR; +#endif + p_i->inq_count = p_inq->inq_counter; /* Mark entry for current inquiry */ + +#if BTM_USE_INQ_RESULTS_FILTER == TRUE + /* If the number of responses found and not unlimited, issue a cancel inquiry */ + if (!(p_inq->inq_active & BTM_PERIODIC_INQUIRY_ACTIVE) && + p_inq->inqparms.max_resps && + p_inq->inq_cmpl_info.num_resp == p_inq->inqparms.max_resps +#if BLE_INCLUDED == TRUE + /* BLE scanning is active and received adv */ + && ((((p_inq->inqparms.mode & BTM_BLE_INQUIRY_MASK) != 0) && + p_cur->device_type == BT_DEVICE_TYPE_DUMO && p_i->scan_rsp) || + (p_inq->inqparms.mode & BTM_BLE_INQUIRY_MASK) == 0) +#endif + ) + { +/* BTM_TRACE_DEBUG0("BTMINQ: Found devices, cancelling inquiry..."); */ + btsnd_hcic_inq_cancel(); + +#if BLE_INCLUDED == TRUE + if ((p_inq->inqparms.mode & BTM_BLE_INQUIRY_MASK) != 0) + btm_ble_stop_scan(); +#endif + + +#if (defined(BTM_BUSY_LEVEL_CHANGE_INCLUDED) && BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE) + btm_acl_update_busy_level (BTM_BLI_INQ_DONE_EVT); +#endif + } +#endif + /* Initialize flag to FALSE. This flag is set/used by application */ + p_i->inq_info.appl_knows_rem_name = FALSE; + } + + if (is_new || update) + { +#if (BTM_INQ_GET_REMOTE_NAME==TRUE) +#if (BTM_EIR_CLIENT_INCLUDED == TRUE) + if( inq_res_mode == BTM_INQ_RESULT_EXTENDED ) + { + if((p_eir_data = BTM_CheckEirData( p, BTM_EIR_COMPLETE_LOCAL_NAME_TYPE, + &remote_name_len )) == NULL) + { + p_eir_data = BTM_CheckEirData( p, BTM_EIR_SHORTENED_LOCAL_NAME_TYPE, + &remote_name_len ); + } + + if( p_eir_data ) + { + if( remote_name_len > BTM_MAX_REM_BD_NAME_LEN ) + remote_name_len = BTM_MAX_REM_BD_NAME_LEN; + + p_i->inq_info.remote_name_len = remote_name_len; + memcpy( p_i->inq_info.remote_name, p_eir_data, p_i->inq_info.remote_name_len ); + p_i->inq_info.remote_name[p_i->inq_info.remote_name_len] = 0; + p_i->inq_info.remote_name_state = BTM_INQ_RMT_NAME_DONE; + } + else + p_i->inq_info.remote_name_state = BTM_INQ_RMT_NAME_EMPTY; + } + else +#endif + { + /* Clear out the device name so that it can be re-read */ + p_i->inq_info.remote_name_state = BTM_INQ_RMT_NAME_EMPTY; + } +#endif /*(BTM_INQ_GET_REMOTE_NAME==TRUE)*/ + +#if (BTM_EIR_CLIENT_INCLUDED == TRUE) + if( inq_res_mode == BTM_INQ_RESULT_EXTENDED ) + { + memset( p_cur->eir_uuid, 0, + BTM_EIR_SERVICE_ARRAY_SIZE * (BTM_EIR_ARRAY_BITS/8)); + /* set bit map of UUID list from received EIR */ + btm_set_eir_uuid( p, p_cur ); + p_eir_data = p; + } + else + p_eir_data = NULL; +#endif + + /* If a callback is registered, call it with the results */ + if (p_inq_results_cb) +#if (BTM_EIR_CLIENT_INCLUDED == TRUE) + (p_inq_results_cb)((tBTM_INQ_RESULTS *) p_cur, p_eir_data); +#else + (p_inq_results_cb)((tBTM_INQ_RESULTS *) p_cur, NULL); +#endif + + /* If anyone is registered for change notifications, then tell him we added an entry. */ + if (p_inq->p_inq_change_cb) + (*p_inq->p_inq_change_cb) (&p_i->inq_info, TRUE); + } + } +} + +/******************************************************************************* +** +** Function btm_sort_inq_result +** +** Description This function is called when inquiry complete is received +** from the device to sort inquiry results based on rssi. +** +** Returns void +** +*******************************************************************************/ +void btm_sort_inq_result(void) +{ + UINT8 xx, yy, num_resp; + tINQ_DB_ENT *p_tmp = NULL; + tINQ_DB_ENT *p_ent = btm_cb.btm_inq_vars.inq_db; + tINQ_DB_ENT *p_next = btm_cb.btm_inq_vars.inq_db+1; + int size; + + num_resp = (btm_cb.btm_inq_vars.inq_cmpl_info.num_resp<BTM_INQ_DB_SIZE)? + btm_cb.btm_inq_vars.inq_cmpl_info.num_resp: BTM_INQ_DB_SIZE; + + if((p_tmp = (tINQ_DB_ENT *)GKI_getbuf(sizeof(tINQ_DB_ENT))) != NULL) + { + size = sizeof(tINQ_DB_ENT); + for(xx = 0; xx < num_resp-1; xx++, p_ent++) + { + for(yy = xx+1, p_next = p_ent+1; yy < num_resp; yy++, p_next++) + { + if(p_ent->inq_info.results.rssi < p_next->inq_info.results.rssi) + { + memcpy (p_tmp, p_next, size); + memcpy (p_next, p_ent, size); + memcpy (p_ent, p_tmp, size); + } + } + } + + GKI_freebuf(p_tmp); + } +} + +/******************************************************************************* +** +** Function btm_process_inq_complete +** +** Description This function is called when inquiry complete is received +** from the device. Call the callback if not in periodic inquiry +** mode AND it is not NULL (The caller wants the event). +** +** The callback pass back the status and the number of responses +** +** Returns void +** +*******************************************************************************/ +void btm_process_inq_complete (UINT8 status, UINT8 mode) +{ + tBTM_CMPL_CB *p_inq_cb = btm_cb.btm_inq_vars.p_inq_cmpl_cb; + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + +#if (BTM_INQ_GET_REMOTE_NAME==TRUE) + tBTM_INQ_INFO *p_cur; + UINT8 tempstate; +#endif + + p_inq->inqparms.mode &= ~(mode); + +#if (BTM_INQ_DEBUG == TRUE) + BTM_TRACE_DEBUG3 ("btm_process_inq_complete inq_active:0x%x state:%d inqfilt_active:%d", + btm_cb.btm_inq_vars.inq_active, btm_cb.btm_inq_vars.state, btm_cb.btm_inq_vars.inqfilt_active); +#endif +#if (defined(BTM_BUSY_LEVEL_CHANGE_INCLUDED) && BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE) + btm_acl_update_busy_level (BTM_BLI_INQ_DONE_EVT); +#endif + /* Ignore any stray or late complete messages if the inquiry is not active */ + if (p_inq->inq_active) + { + p_inq->inq_cmpl_info.status = (tBTM_STATUS)((status == HCI_SUCCESS) ? BTM_SUCCESS : BTM_ERR_PROCESSING); + +#if (BTM_INQ_GET_REMOTE_NAME==TRUE) + if (p_inq->inq_cmpl_info.status == BTM_SUCCESS) + { + for (p_cur = BTM_InqDbFirst(); p_cur; p_cur = BTM_InqDbNext (p_cur)) + { + if (p_cur->remote_name_state == BTM_INQ_RMT_NAME_EMPTY) + { + tempstate = p_cur->remote_name_state; + p_cur->remote_name_state = BTM_INQ_RMT_NAME_PENDING; + + if (btm_initiate_rem_name (p_cur->results.remote_bd_addr, + p_cur, BTM_RMT_NAME_INQ, + BTM_INQ_RMT_NAME_TIMEOUT, NULL) != BTM_CMD_STARTED) + p_cur->remote_name_state = tempstate; + else + return; + } + } + } +#endif + + /* Notify caller that the inquiry has completed; (periodic inquiries do not send completion events */ + if (!(p_inq->inq_active & BTM_PERIODIC_INQUIRY_ACTIVE) && p_inq->inqparms.mode == 0) + { + p_inq->state = BTM_INQ_INACTIVE_STATE; + + /* Increment so the start of a next inquiry has a new count */ + p_inq->inq_counter++; + + btm_clr_inq_result_flt(); + + if((p_inq->inq_cmpl_info.status == BTM_SUCCESS) && HCI_LMP_INQ_RSSI_SUPPORTED(btm_cb.devcb.local_features)) + { + btm_sort_inq_result(); + } + + /* Clear the results callback if set */ + p_inq->p_inq_results_cb = (tBTM_INQ_RESULTS_CB *) NULL; + p_inq->inq_active = BTM_INQUIRY_INACTIVE; + p_inq->p_inq_cmpl_cb = (tBTM_CMPL_CB *) NULL; + + /* If we have a callback registered for inquiry complete, call it */ + BTM_TRACE_DEBUG2 ("BTM Inq Compl Callback: status 0x%02x, num results %d", + p_inq->inq_cmpl_info.status, p_inq->inq_cmpl_info.num_resp); + + if (p_inq_cb) + (p_inq_cb)((tBTM_INQUIRY_CMPL *) &p_inq->inq_cmpl_info); + } + + } +#if (BTM_INQ_DEBUG == TRUE) + BTM_TRACE_DEBUG3 ("inq_active:0x%x state:%d inqfilt_active:%d", + btm_cb.btm_inq_vars.inq_active, btm_cb.btm_inq_vars.state, btm_cb.btm_inq_vars.inqfilt_active); +#endif +} + +/******************************************************************************* +** +** Function btm_initiate_rem_name +** +** Description This function looks initiates a remote name request. It is called +** either by GAP or by the API call BTM_ReadRemoteDeviceName. +** +** Input Params: p_cur - pointer to an inquiry result structure (NULL if nonexistent) +** p_cb - callback function called when BTM_CMD_STARTED +** is returned. +** A pointer to tBTM_REMOTE_DEV_NAME is passed to the +** callback. +** +** Returns +** BTM_CMD_STARTED is returned if the request was sent to HCI. +** BTM_BUSY if already in progress +** BTM_NO_RESOURCES if could not allocate resources to start the command +** BTM_WRONG_MODE if the device is not up. +** +*******************************************************************************/ +tBTM_STATUS btm_initiate_rem_name (BD_ADDR remote_bda, tBTM_INQ_INFO *p_cur, + UINT8 origin, UINT32 timeout, tBTM_CMPL_CB *p_cb) +{ + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + BOOLEAN cmd_ok; + + + /*** Make sure the device is ready ***/ + if (!BTM_IsDeviceUp()) + return (BTM_WRONG_MODE); + + + if (origin == BTM_RMT_NAME_SEC) + { + cmd_ok = btsnd_hcic_rmt_name_req (remote_bda, HCI_PAGE_SCAN_REP_MODE_R1, + HCI_MANDATARY_PAGE_SCAN_MODE, 0); + if (cmd_ok) + return BTM_CMD_STARTED; + else + return BTM_NO_RESOURCES; + } + /* Make sure there are no two remote name requests from external API in progress */ + else if (origin == BTM_RMT_NAME_EXT) + { + if (p_inq->remname_active) + { + return (BTM_BUSY); + } + else + { + /* If there is no remote name request running,call the callback function and start timer */ + p_inq->p_remname_cmpl_cb = p_cb; + memcpy(p_inq->remname_bda, remote_bda, BD_ADDR_LEN); + btu_start_timer (&p_inq->rmt_name_timer_ent, + BTU_TTYPE_BTM_RMT_NAME, + timeout); + + /* If the database entry exists for the device, use its clock offset */ + if (p_cur) + { + cmd_ok = btsnd_hcic_rmt_name_req (remote_bda, + p_cur->results.page_scan_rep_mode, + p_cur->results.page_scan_mode, + (UINT16)(p_cur->results.clock_offset | + BTM_CLOCK_OFFSET_VALID)); + } + else /* Otherwise use defaults and mark the clock offset as invalid */ + { + cmd_ok = btsnd_hcic_rmt_name_req (remote_bda, HCI_PAGE_SCAN_REP_MODE_R1, + HCI_MANDATARY_PAGE_SCAN_MODE, 0); + } + if (cmd_ok) + { + p_inq->remname_active = TRUE; + return BTM_CMD_STARTED; + } + else + return BTM_NO_RESOURCES; + } + } + /* If the inquire feature is on */ +#if (BTM_INQ_GET_REMOTE_NAME==TRUE) + + else if (origin == BTM_RMT_NAME_INQ) + { + /* If the database entry exists for the device, use its clock offset */ + if (p_cur) + { + cmd_ok = btsnd_hcic_rmt_name_req (remote_bda, + p_cur->results.page_scan_rep_mode, + p_cur->results.page_scan_mode, + (UINT16)(p_cur->results.clock_offset | + BTM_CLOCK_OFFSET_VALID)); + } + else + { + cmd_ok = FALSE + } + + if (cmd_ok) + return BTM_CMD_STARTED; + else + return BTM_NO_RESOURCES; + } +#endif + else + { + + return BTM_ILLEGAL_VALUE; + + + } + + +} + +/******************************************************************************* +** +** Function btm_process_remote_name +** +** Description This function is called when a remote name is received from +** the device. If remote names are cached, it updates the inquiry +** database. +** +** Returns void +** +*******************************************************************************/ +void btm_process_remote_name (BD_ADDR bda, BD_NAME bdn, UINT16 evt_len, UINT8 hci_status) +{ + tBTM_REMOTE_DEV_NAME rem_name = {0}; + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + tBTM_CMPL_CB *p_cb = p_inq->p_remname_cmpl_cb; + UINT8 *p_n1; + + UINT16 temp_evt_len; + +#if (BTM_INQ_GET_REMOTE_NAME==TRUE) + /*** These are only used if part of the Inquiry Process ***/ + tBTM_CMPL_CB *p_inq_cb; + tINQ_DB_ENT *p_i = NULL; + UINT8 *p_n; + tBTM_INQ_INFO *p_cur; +#endif +#if BLE_INCLUDED == TRUE + tBT_DEVICE_TYPE dev_type; + tBLE_ADDR_TYPE addr_type; +#endif + + + if (bda != NULL) + { + BTM_TRACE_EVENT6("BDA %02x:%02x:%02x:%02x:%02x:%02x",bda[0], bda[1], + bda[2], bda[3], + bda[4], bda[5]); + } + + BTM_TRACE_EVENT6("Inquire BDA %02x:%02x:%02x:%02x:%02x:%02x",p_inq->remname_bda[0], p_inq->remname_bda[1], + p_inq->remname_bda[2], p_inq->remname_bda[3], + p_inq->remname_bda[4], p_inq->remname_bda[5]); + + + + /* If the inquire BDA and remote DBA are the same, then stop the timer and set the active to false */ + if ((p_inq->remname_active ==TRUE)&& + (((bda != NULL) && + (memcmp(bda, p_inq->remname_bda,BD_ADDR_LEN)==0)) || bda == NULL)) + + { +#if BLE_INCLUDED == TRUE + BTM_ReadDevInfo(p_inq->remname_bda, &dev_type, &addr_type); + if (dev_type == BT_DEVICE_TYPE_BLE) + { + if (hci_status == HCI_ERR_UNSPECIFIED) + btm_ble_cancel_remote_name(p_inq->remname_bda); + } +#endif + btu_stop_timer (&p_inq->rmt_name_timer_ent); + p_inq->remname_active = FALSE; + /* Clean up and return the status if the command was not successful */ + /* Note: If part of the inquiry, the name is not stored, and the */ + /* inquiry complete callback is called. */ + + if ((hci_status == HCI_SUCCESS)) + { + /* Copy the name from the data stream into the return structure */ + /* Note that even if it is not being returned, it is used as a */ + /* temporary buffer. */ + p_n1 = (UINT8 *)rem_name.remote_bd_name; + rem_name.length = (evt_len < BD_NAME_LEN) ? evt_len : BD_NAME_LEN; + rem_name.status = BTM_SUCCESS; + temp_evt_len = rem_name.length; + + while (temp_evt_len > 0) + { + *p_n1++ = *bdn++; + temp_evt_len--; + } + } + + + /* If processing a stand alone remote name then report the error in the callback */ + else + { + rem_name.status = BTM_BAD_VALUE_RET; + rem_name.length = 0; + rem_name.remote_bd_name[0] = 0; + } + /* Reset the remote BAD to zero and call callback if possible */ + memset(p_inq->remname_bda, 0, BD_ADDR_LEN); + + p_inq->p_remname_cmpl_cb = NULL; + if (p_cb) + (p_cb)((tBTM_REMOTE_DEV_NAME *)&rem_name); + } + + +#if (BTM_INQ_GET_REMOTE_NAME==TRUE) + /* If existing entry, update the name */ + if ((bda != NULL) && ((p_i = btm_inq_db_find (bda)) != NULL) + && (hci_status == HCI_SUCCESS)) + { + p_i->inq_info.remote_name_state = BTM_INQ_RMT_NAME_DONE; + p_n = p_i->inq_info.remote_name; + memset(p_n, 0, BTM_MAX_REM_BD_NAME_LEN + 1); + p_i->inq_info.remote_name_len = (rem_name.length < BTM_MAX_REM_BD_NAME_LEN) ? + rem_name.length : BTM_MAX_REM_BD_NAME_LEN; + evt_len = p_i->inq_info.remote_name_len; + p_n1 = (UINT8 *)rem_name.remote_bd_name; + while (evt_len > 0) + { + *p_n++ = *p_n1++; + evt_len--; + } + + if (btm_cb.btm_inq_vars.p_inq_change_cb) + (*btm_cb.btm_inq_vars.p_inq_change_cb) (&p_i->inq_info, TRUE); + } + else + { + if (p_i) + p_i->inq_info.remote_name_state = BTM_INQ_RMT_NAME_FAILED; + else + { + /* Find the entry which is currently doing name request */ + for (p_cur = BTM_InqDbFirst(); p_cur; p_cur = BTM_InqDbNext (p_cur)) + { + if (p_cur->remote_name_state == BTM_INQ_RMT_NAME_PENDING) + { + /* Should be only one */ + p_cur->remote_name_state = BTM_INQ_RMT_NAME_FAILED; + break; + } + } + } + } + + /* If an inquiry is in progress then update other entries */ + if (p_inq->inq_active) + { + /* Check if there are any more entries inquired but not named */ + for (p_cur = BTM_InqDbFirst(); p_cur; p_cur = BTM_InqDbNext (p_cur)) + { + if (p_cur->remote_name_state == BTM_INQ_RMT_NAME_EMPTY) + { + p_cur->remote_name_state = BTM_INQ_RMT_NAME_PENDING; +#if (BLE_INCLUDED == TRUE) + BTM_ReadDevInfo(remote_bda, &dev_type, &addr_type); + if (dev_type == BT_DEVICE_TYPE_BLE) + { + if (btm_ble_read_remote_name(remote_bda, p_cur, p_cb) != BTM_CMD_STARTED) + p_cur->remote_name_state = BTM_INQ_RMT_NAME_FAILED; + else + return; + } + else +#endif + { + if (btm_initiate_rem_name (p_cur->results.remote_bd_addr, + p_cur, BTM_RMT_NAME_INQ, + BTM_INQ_RMT_NAME_TIMEOUT, NULL) != BTM_CMD_STARTED) + p_cur->remote_name_state = BTM_INQ_RMT_NAME_FAILED; + else + return; + } + } + } + + /* The inquiry has finished so call the callback for the inquiry */ + p_inq_cb = p_inq->p_inq_cmpl_cb; + p_inq->state = BTM_INQ_INACTIVE_STATE; + p_inq->inq_active = BTM_INQUIRY_INACTIVE; + p_inq->p_inq_cmpl_cb = NULL; + + /* If we have a callback registered for inquiry complete, call it */ + if (p_inq_cb) + (p_inq_cb)((tBTM_INQUIRY_CMPL *) &p_inq->inq_cmpl_info); + + /* In some cases we can not get name of the device once but will be */ + /* able to do it next time. Until we have better solution we will */ + /* try to get name every time */ + for (p_cur = BTM_InqDbFirst(); p_cur; p_cur = BTM_InqDbNext (p_cur)) + { + if (p_cur->remote_name_state == BTM_INQ_RMT_NAME_FAILED) + p_cur->remote_name_state = BTM_INQ_RMT_NAME_EMPTY; + } + } +#endif /* BTM_INQ_GET_REMOTE_NAME == TRUE */ +} + +/******************************************************************************* +** +** Function btm_inq_rmt_name_failed +** +** Description This function is if timeout expires while getting remote +** name. This is done for devices that incorrectly do not +** report operation failure +** +** Returns void +** +*******************************************************************************/ +void btm_inq_rmt_name_failed (void) +{ + BTM_TRACE_ERROR1 ("btm_inq_rmt_name_failed() remname_active=%d", btm_cb.btm_inq_vars.remname_active); + + if (btm_cb.btm_inq_vars.remname_active) + btm_process_remote_name (btm_cb.btm_inq_vars.remname_bda, NULL, 0, HCI_ERR_UNSPECIFIED); + else + btm_process_remote_name (NULL, NULL, 0, HCI_ERR_UNSPECIFIED); + + btm_sec_rmt_name_request_complete (NULL, NULL, HCI_ERR_UNSPECIFIED); +} +/******************************************************************************* +** +** Function btm_read_linq_tx_power_complete +** +** Description read inquiry tx power level complete callback function. +** +** Returns void +** +*******************************************************************************/ +void btm_read_linq_tx_power_complete(UINT8 *p) +{ + tBTM_CMPL_CB *p_cb = btm_cb.devcb.p_txpwer_cmpl_cb; + tBTM_INQ_TXPWR_RESULTS results; + + btu_stop_timer (&btm_cb.devcb.txpwer_timer); + /* If there was a callback registered for read inq tx power, call it */ + btm_cb.devcb.p_txpwer_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_UINT8 (results.tx_power, p); + BTM_TRACE_EVENT2 ("BTM INQ 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_WriteEIR +** +** Description This function is called to write EIR data to controller. +** +** Parameters p_buff - allocated HCI command buffer including extended +** inquriry response +** +** Returns BTM_SUCCESS - if successful +** BTM_MODE_UNSUPPORTED - if local device cannot support it +** +*******************************************************************************/ +tBTM_STATUS BTM_WriteEIR( BT_HDR *p_buff ) +{ +#if (BTM_EIR_SERVER_INCLUDED == TRUE) + if (HCI_EXT_INQ_RSP_SUPPORTED(btm_cb.devcb.local_features)) + { + BTM_TRACE_API0("Write Extended Inquiry Response to controller"); + btsnd_hcic_write_ext_inquiry_response (p_buff, BTM_EIR_DEFAULT_FEC_REQUIRED); + return BTM_SUCCESS; + } + else + { + GKI_freebuf(p_buff); + return BTM_MODE_UNSUPPORTED; + } +#else + GKI_freebuf(p_buff); + return BTM_SUCCESS; +#endif +} + +/******************************************************************************* +** +** Function BTM_CheckEirData +** +** Description This function is called to get EIR data from significant part. +** +** Parameters p_eir - pointer of EIR significant part +** type - finding EIR data type +** p_length - return the length of EIR data not including type +** +** Returns pointer of EIR data +** +*******************************************************************************/ +UINT8 *BTM_CheckEirData( UINT8 *p_eir, UINT8 type, UINT8 *p_length ) +{ +#if (BTM_EIR_CLIENT_INCLUDED == TRUE) + UINT8 *p = p_eir; + UINT8 length; + UINT8 eir_type; + BTM_TRACE_API1("BTM_CheckEirData type=0x%02X", type); + + STREAM_TO_UINT8(length, p); + while( length && (p - p_eir <= HCI_EXT_INQ_RESPONSE_LEN)) + { + STREAM_TO_UINT8(eir_type, p); + if( eir_type == type ) + { + /* length doesn't include itself */ + *p_length = length - 1; /* minus the length of type */ + return p; + } + p += length - 1; /* skip the length of data */ + STREAM_TO_UINT8(length, p); + } + + *p_length = 0; + return NULL; +#else + return NULL; +#endif +} + +/******************************************************************************* +** +** Function btm_convert_uuid_to_eir_service +** +** Description This function is called to get the bit position of UUID. +** +** Parameters uuid16 - UUID 16-bit +** +** Returns BTM EIR service ID if found +** BTM_EIR_MAX_SERVICES - if not found +** +*******************************************************************************/ +#if (( BTM_EIR_CLIENT_INCLUDED == TRUE )||( BTM_EIR_SERVER_INCLUDED == TRUE )) +static UINT8 btm_convert_uuid_to_eir_service( UINT16 uuid16 ) +{ + UINT8 xx; + + for( xx = 0; xx < BTM_EIR_MAX_SERVICES; xx++ ) + { + if( uuid16 == BTM_EIR_UUID_LKUP_TBL[xx]) + { + return xx; + } + } + return BTM_EIR_MAX_SERVICES; +} +#endif + +/******************************************************************************* +** +** Function BTM_HasEirService +** +** Description This function is called to know if UUID in bit map of UUID. +** +** Parameters p_eir_uuid - bit map of UUID list +** uuid16 - UUID 16-bit +** +** Returns TRUE - if found +** FALSE - if not found +** +*******************************************************************************/ +BOOLEAN BTM_HasEirService( UINT32 *p_eir_uuid, UINT16 uuid16 ) +{ +#if ((BTM_EIR_SERVER_INCLUDED == TRUE)||(BTM_EIR_CLIENT_INCLUDED == TRUE)) + UINT8 service_id; + + service_id = btm_convert_uuid_to_eir_service(uuid16); + if( service_id < BTM_EIR_MAX_SERVICES ) + return( BTM_EIR_HAS_SERVICE( p_eir_uuid, service_id )); + else + return( FALSE ); +#else + return( FALSE ); +#endif +} + +/******************************************************************************* +** +** Function BTM_HasInquiryEirService +** +** Description This function is called to know if UUID in bit map of UUID list. +** +** Parameters p_results - inquiry results +** uuid16 - UUID 16-bit +** +** Returns BTM_EIR_FOUND - if found +** BTM_EIR_NOT_FOUND - if not found and it is complete list +** BTM_EIR_UNKNOWN - if not found and it is not complete list +** +*******************************************************************************/ +tBTM_EIR_SEARCH_RESULT BTM_HasInquiryEirService( tBTM_INQ_RESULTS *p_results, UINT16 uuid16 ) +{ +#if ((BTM_EIR_SERVER_INCLUDED == TRUE)||(BTM_EIR_CLIENT_INCLUDED == TRUE)) + if( BTM_HasEirService( p_results->eir_uuid, uuid16 )) + { + return BTM_EIR_FOUND; + } + else if( p_results->eir_complete_list ) + { + return BTM_EIR_NOT_FOUND; + } + else + return BTM_EIR_UNKNOWN; +#else + return BTM_EIR_UNKNOWN; +#endif +} + +/******************************************************************************* +** +** Function BTM_AddEirService +** +** Description This function is called to add a service in bit map of UUID list. +** +** Parameters p_eir_uuid - bit mask of UUID list for EIR +** uuid16 - UUID 16-bit +** +** Returns None +** +*******************************************************************************/ +void BTM_AddEirService( UINT32 *p_eir_uuid, UINT16 uuid16 ) +{ +#if ((BTM_EIR_SERVER_INCLUDED == TRUE)||(BTM_EIR_CLIENT_INCLUDED == TRUE)) + UINT8 service_id; + + service_id = btm_convert_uuid_to_eir_service(uuid16); + if( service_id < BTM_EIR_MAX_SERVICES ) + BTM_EIR_SET_SERVICE( p_eir_uuid, service_id ); +#endif +} + +/******************************************************************************* +** +** Function BTM_RemoveEirService +** +** Description This function is called to remove a service in bit map of UUID list. +** +** Parameters p_eir_uuid - bit mask of UUID list for EIR +** uuid16 - UUID 16-bit +** +** Returns None +** +*******************************************************************************/ +void BTM_RemoveEirService( UINT32 *p_eir_uuid, UINT16 uuid16 ) +{ +#if (BTM_EIR_SERVER_INCLUDED == TRUE) + UINT8 service_id; + + service_id = btm_convert_uuid_to_eir_service(uuid16); + if( service_id < BTM_EIR_MAX_SERVICES ) + BTM_EIR_CLR_SERVICE( p_eir_uuid, service_id ); +#endif +} + +/******************************************************************************* +** +** Function BTM_GetEirSupportedServices +** +** Description This function is called to get UUID list from bit map of UUID list. +** +** Parameters p_eir_uuid - bit mask of UUID list for EIR +** p - reference of current pointer of EIR +** max_num_uuid16 - max number of UUID can be written in EIR +** num_uuid16 - number of UUID have been written in EIR +** +** Returns BTM_EIR_MORE_16BITS_UUID_TYPE, if it has more than max +** BTM_EIR_COMPLETE_16BITS_UUID_TYPE, otherwise +** +*******************************************************************************/ +UINT8 BTM_GetEirSupportedServices( UINT32 *p_eir_uuid, UINT8 **p, + UINT8 max_num_uuid16, UINT8 *p_num_uuid16) +{ +#if (BTM_EIR_SERVER_INCLUDED == TRUE) + UINT8 service_index; + + *p_num_uuid16 = 0; + + for(service_index = 0; service_index < BTM_EIR_MAX_SERVICES; service_index++) + { + if( BTM_EIR_HAS_SERVICE( p_eir_uuid, service_index )) + { + if( *p_num_uuid16 < max_num_uuid16 ) + { + UINT16_TO_STREAM(*p, BTM_EIR_UUID_LKUP_TBL[service_index]); + (*p_num_uuid16)++; + } + /* if max number of UUIDs are stored and found one more */ + else + { + return BTM_EIR_MORE_16BITS_UUID_TYPE; + } + } + } + return BTM_EIR_COMPLETE_16BITS_UUID_TYPE; +#else + return BTM_EIR_COMPLETE_16BITS_UUID_TYPE; +#endif +} + +/******************************************************************************* +** +** Function BTM_GetEirUuidList +** +** Description This function parses EIR and returns UUID list. +** +** Parameters p_eir - EIR +** uuid_size - LEN_UUID_16, LEN_UUID_32, LEN_UUID_128 +** p_num_uuid - return number of UUID in found list +** p_uuid_list - return UUID list +** max_num_uuid - maximum number of UUID to be returned +** +** Returns 0 - if not found +** BTM_EIR_COMPLETE_16BITS_UUID_TYPE +** BTM_EIR_MORE_16BITS_UUID_TYPE +** BTM_EIR_COMPLETE_32BITS_UUID_TYPE +** BTM_EIR_MORE_32BITS_UUID_TYPE +** BTM_EIR_COMPLETE_128BITS_UUID_TYPE +** BTM_EIR_MORE_128BITS_UUID_TYPE +** +*******************************************************************************/ +UINT8 BTM_GetEirUuidList( UINT8 *p_eir, UINT8 uuid_size, UINT8 *p_num_uuid, + UINT8 *p_uuid_list, UINT8 max_num_uuid) +{ +#if (BTM_EIR_CLIENT_INCLUDED == TRUE) + UINT8 *p_uuid_data; + UINT8 type; + UINT8 yy, xx; + UINT16 *p_uuid16 = (UINT16 *)p_uuid_list; + UINT32 *p_uuid32 = (UINT32 *)p_uuid_list; + char buff[LEN_UUID_128 * 2 + 1]; + + p_uuid_data = btm_eir_get_uuid_list( p_eir, uuid_size, p_num_uuid, &type ); + if( p_uuid_data == NULL ) + { + return 0x00; + } + + if( *p_num_uuid > max_num_uuid ) + { + BTM_TRACE_WARNING2("BTM_GetEirUuidList number of uuid in EIR = %d, size of uuid list = %d", + *p_num_uuid, max_num_uuid ); + *p_num_uuid = max_num_uuid; + } + + BTM_TRACE_DEBUG2("BTM_GetEirUuidList type = %02X, number of uuid = %d", type, *p_num_uuid ); + + if( uuid_size == LEN_UUID_16 ) + { + for( yy = 0; yy < *p_num_uuid; yy++ ) + { + STREAM_TO_UINT16(*(p_uuid16 + yy), p_uuid_data); + BTM_TRACE_DEBUG1(" 0x%04X", *(p_uuid16 + yy)); + } + } + else if( uuid_size == LEN_UUID_32 ) + { + for( yy = 0; yy < *p_num_uuid; yy++ ) + { + STREAM_TO_UINT32(*(p_uuid32 + yy), p_uuid_data); + BTM_TRACE_DEBUG1(" 0x%08X", *(p_uuid32 + yy)); + } + } + else if( uuid_size == LEN_UUID_128 ) + { + for( yy = 0; yy < *p_num_uuid; yy++ ) + { + STREAM_TO_ARRAY16(p_uuid_list + yy * LEN_UUID_128, p_uuid_data); + for( xx = 0; xx < LEN_UUID_128; xx++ ) + sprintf(buff + xx*2, "%02X", *(p_uuid_list + yy * LEN_UUID_128 + xx)); + BTM_TRACE_DEBUG1(" 0x%s", buff); + } + } + + return type; +#else + *p_num_uuid = 0; + return 0x00; +#endif +} + + +#if (BTM_EIR_CLIENT_INCLUDED == TRUE) +/******************************************************************************* +** +** Function btm_eir_get_uuid_list +** +** Description This function searches UUID list in EIR. +** +** Parameters p_eir - address of EIR +** uuid_size - size of UUID to find +** p_num_uuid - number of UUIDs found +** p_uuid_list_type - EIR data type +** +** Returns NULL - if UUID list with uuid_size is not found +** beginning of UUID list in EIR - otherwise +** +*******************************************************************************/ +static UINT8 *btm_eir_get_uuid_list( UINT8 *p_eir, UINT8 uuid_size, + UINT8 *p_num_uuid, UINT8 *p_uuid_list_type ) +{ + UINT8 *p_uuid_data; + UINT8 complete_type, more_type; + UINT8 uuid_len; + + switch( uuid_size ) + { + case LEN_UUID_16: + complete_type = BTM_EIR_COMPLETE_16BITS_UUID_TYPE; + more_type = BTM_EIR_MORE_16BITS_UUID_TYPE; + break; + case LEN_UUID_32: + complete_type = BTM_EIR_COMPLETE_32BITS_UUID_TYPE; + more_type = BTM_EIR_MORE_32BITS_UUID_TYPE; + break; + case LEN_UUID_128: + complete_type = BTM_EIR_COMPLETE_128BITS_UUID_TYPE; + more_type = BTM_EIR_MORE_128BITS_UUID_TYPE; + break; + default: + *p_num_uuid = 0; + return NULL; + break; + } + + p_uuid_data = BTM_CheckEirData( p_eir, complete_type, &uuid_len ); + if(p_uuid_data == NULL) + { + p_uuid_data = BTM_CheckEirData( p_eir, more_type, &uuid_len ); + *p_uuid_list_type = more_type; + } + else + { + *p_uuid_list_type = complete_type; + } + + *p_num_uuid = uuid_len / uuid_size; + return p_uuid_data; +} + +/******************************************************************************* +** +** Function btm_convert_uuid_to_uuid16 +** +** Description This function converts UUID to UUID 16-bit. +** +** Parameters p_uuid - address of UUID +** uuid_size - size of UUID +** +** Returns 0 - if UUID cannot be converted to UUID 16-bit +** UUID 16-bit - otherwise +** +*******************************************************************************/ +static UINT16 btm_convert_uuid_to_uuid16( UINT8 *p_uuid, UINT8 uuid_size ) +{ + static const UINT8 base_uuid[LEN_UUID_128] = {0xFB, 0x34, 0x9B, 0x5F, 0x80, 0x00, 0x00, 0x80, + 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + UINT16 uuid16 = 0; + UINT32 uuid32; + BOOLEAN is_base_uuid; + UINT8 xx; + + switch (uuid_size) + { + case LEN_UUID_16: + STREAM_TO_UINT16 (uuid16, p_uuid); + break; + case LEN_UUID_32: + STREAM_TO_UINT32 (uuid32, p_uuid); + if (uuid32 < 0x10000) + uuid16 = (UINT16) uuid32; + break; + case LEN_UUID_128: + /* See if we can compress his UUID down to 16 or 32bit UUIDs */ + is_base_uuid = TRUE; + for (xx = 0; xx < LEN_UUID_128 - 4; xx++) + { + if (p_uuid[xx] != base_uuid[xx]) + { + is_base_uuid = FALSE; + break; + } + } + if (is_base_uuid) + { + if ((p_uuid[LEN_UUID_128 - 1] == 0) && (p_uuid[LEN_UUID_128 - 2] == 0)) + { + p_uuid += (LEN_UUID_128 - 4); + STREAM_TO_UINT16(uuid16, p_uuid); + } + } + break; + default: + BTM_TRACE_WARNING0("btm_convert_uuid_to_uuid16 invalid uuid size"); + break; + } + + return( uuid16); +} + +/******************************************************************************* +** +** Function btm_set_eir_uuid +** +** Description This function is called to store received UUID into inquiry result. +** +** Parameters p_eir - pointer of EIR significant part +** p_results - pointer of inquiry result +** +** Returns None +** +*******************************************************************************/ +void btm_set_eir_uuid( UINT8 *p_eir, tBTM_INQ_RESULTS *p_results ) +{ + UINT8 *p_uuid_data; + UINT8 num_uuid; + UINT16 uuid16; + UINT8 yy; + UINT8 type = BTM_EIR_MORE_16BITS_UUID_TYPE; + + p_uuid_data = btm_eir_get_uuid_list( p_eir, LEN_UUID_16, &num_uuid, &type ); + + if(type == BTM_EIR_COMPLETE_16BITS_UUID_TYPE) + { + p_results->eir_complete_list = TRUE; + } + else + { + p_results->eir_complete_list = FALSE; + } + + BTM_TRACE_API1("btm_set_eir_uuid eir_complete_list=0x%02X", p_results->eir_complete_list); + + if( p_uuid_data ) + { + for( yy = 0; yy < num_uuid; yy++ ) + { + STREAM_TO_UINT16(uuid16, p_uuid_data); + BTM_AddEirService( p_results->eir_uuid, uuid16 ); + } + } + + p_uuid_data = btm_eir_get_uuid_list( p_eir, LEN_UUID_32, &num_uuid, &type ); + if( p_uuid_data ) + { + for( yy = 0; yy < num_uuid; yy++ ) + { + uuid16 = btm_convert_uuid_to_uuid16( p_uuid_data, LEN_UUID_32 ); + p_uuid_data += LEN_UUID_32; + if( uuid16 ) + BTM_AddEirService( p_results->eir_uuid, uuid16 ); + } + } + + p_uuid_data = btm_eir_get_uuid_list( p_eir, LEN_UUID_128, &num_uuid, &type ); + if( p_uuid_data ) + { + for( yy = 0; yy < num_uuid; yy++ ) + { + uuid16 = btm_convert_uuid_to_uuid16( p_uuid_data, LEN_UUID_128 ); + p_uuid_data += LEN_UUID_128; + if( uuid16 ) + BTM_AddEirService( p_results->eir_uuid, uuid16 ); + } + } + + BTM_TRACE_DEBUG2("btm_set_eir_uuid eir_uuid=0x%08X %08X", + p_results->eir_uuid[1], p_results->eir_uuid[0] ); +} +#endif + diff --git a/stack/btm/btm_int.h b/stack/btm/btm_int.h new file mode 100644 index 0000000..644359c --- /dev/null +++ b/stack/btm/btm_int.h @@ -0,0 +1,1099 @@ +/***************************************************************************** +** +** Name: btm_int.h +** +** Description: this file contains the main Bluetooth Manager (BTM) +** internal definitions. +** +** Copyright (c) 1999-2011, Broadcom Corp., All Rights Reserved +** Broadcom Bluetooth Core. Proprietary and confidential. +******************************************************************************/ +#ifndef BTM_INT_H +#define BTM_INT_H + +#include "bt_target.h" +#include "gki.h" +#include "hcidefs.h" + +#if RFCOMM_INCLUDED == TRUE +#include "rfcdefs.h" +#endif + +#include "btm_api.h" + +#if (BLE_INCLUDED == TRUE) +#include "btm_ble_int.h" +#if (SMP_INCLUDED == TRUE) +#include "smp_api.h" +#endif +#endif + +#if BTM_MAX_LOC_BD_NAME_LEN > 0 +typedef char tBTM_LOC_BD_NAME[BTM_MAX_LOC_BD_NAME_LEN + 1]; +#endif + +#define BTM_ACL_IS_CONNECTED(bda) (btm_bda_to_acl (bda) != NULL) + +/* Definitions for Server Channel Number (SCN) management +*/ +#define BTM_MAX_SCN PORT_MAX_RFC_PORTS + +/* Define masks for supported and exception 2.0 ACL packet types +*/ +#define BTM_ACL_SUPPORTED_PKTS_MASK (HCI_PKT_TYPES_MASK_DM1 | \ + HCI_PKT_TYPES_MASK_DH1 | \ + HCI_PKT_TYPES_MASK_DM3 | \ + HCI_PKT_TYPES_MASK_DH3 | \ + HCI_PKT_TYPES_MASK_DM5 | \ + HCI_PKT_TYPES_MASK_DH5) + +#define BTM_ACL_EXCEPTION_PKTS_MASK (HCI_PKT_TYPES_MASK_NO_2_DH1 | \ + HCI_PKT_TYPES_MASK_NO_3_DH1 | \ + HCI_PKT_TYPES_MASK_NO_2_DH3 | \ + HCI_PKT_TYPES_MASK_NO_3_DH3 | \ + HCI_PKT_TYPES_MASK_NO_2_DH5 | \ + HCI_PKT_TYPES_MASK_NO_3_DH5) + +#define BTM_EPR_AVAILABLE(p) ((HCI_ATOMIC_ENCRYPT_SUPPORTED((p)->features) && \ + HCI_ATOMIC_ENCRYPT_SUPPORTED(btm_cb.devcb.local_features)) \ + ? TRUE : FALSE) + +#define BTM_IS_BRCM_CONTROLLER() (btm_cb.devcb.local_version.manufacturer == LMP_COMPID_BROADCOM) + + +/* Define the ACL Management control structure +*/ +typedef struct +{ + UINT16 hci_handle; + UINT16 pkt_types_mask; + UINT16 restore_pkt_types; /* when coming in/out of SCO connection restore the packet types */ + UINT16 clock_offset; + BD_ADDR remote_addr; + DEV_CLASS remote_dc; + BD_NAME remote_name; + + UINT16 manufacturer; + UINT16 lmp_subversion; + UINT16 link_super_tout; + BD_FEATURES features; /* Features suported by the device */ + UINT8 lmp_version; + + BOOLEAN in_use; + UINT8 link_role; + BOOLEAN link_up_issued; /* True if busy_level link up has been issued */ + +#define BTM_ACL_SWKEY_STATE_IDLE 0 +#define BTM_ACL_SWKEY_STATE_MODE_CHANGE 1 +#define BTM_ACL_SWKEY_STATE_ENCRYPTION_OFF 2 +#define BTM_ACL_SWKEY_STATE_SWITCHING 3 +#define BTM_ACL_SWKEY_STATE_ENCRYPTION_ON 4 +#define BTM_ACL_SWKEY_STATE_IN_PROGRESS 5 + UINT8 switch_role_state; + UINT8 change_key_state; + +#define BTM_ACL_ENCRYPT_STATE_IDLE 0 +#define BTM_ACL_ENCRYPT_STATE_ENCRYPT_OFF 1 /* encryption turning off */ +#define BTM_ACL_ENCRYPT_STATE_TEMP_FUNC 2 /* temporarily off for change link key or role switch */ +#define BTM_ACL_ENCRYPT_STATE_ENCRYPT_ON 3 /* encryption turning on */ + UINT8 encrypt_state; /* overall BTM encryption state */ + +#if BTM_PWR_MGR_INCLUDED == FALSE + UINT8 mode; +#endif /* BTM_PWR_MGR_INCLUDED */ +#if BLE_INCLUDED == TRUE + UINT8 is_le_link; +#endif + +} tACL_CONN; + +/***************************************************** +** TIMER Definitions +******************************************************/ +#define TT_DEV_RESET 1 +#define TT_DEV_RLN 2 +#define TT_DEV_RLNKP 4 /* Read Link Policy Settings */ + +/* Define the Device Management control structure +*/ +typedef struct +{ + tBTM_DEV_STATUS_CB *p_dev_status_cb; /* Device status change callback */ + tBTM_VS_EVT_CB *p_vend_spec_cb[BTM_MAX_VSE_CALLBACKS]; /* Register for vendor specific events */ + + tBTM_CMPL_CB *p_stored_link_key_cmpl_cb; /* Read/Write/Delete stored link key */ + + TIMER_LIST_ENT reset_timer; + tBTM_CMPL_CB *p_reset_cmpl_cb; /* Callback function to be called */ + /* when startup of the device is done */ + TIMER_LIST_ENT rln_timer; + tBTM_CMPL_CB *p_rln_cmpl_cb; /* Callback function to be called when */ + /* read local name function complete */ + TIMER_LIST_ENT rlinkp_timer; + tBTM_CMPL_CB *p_rlinkp_cmpl_cb; /* Callback function to be called when */ + /* read link policy function completes */ + TIMER_LIST_ENT rssi_timer; + tBTM_CMPL_CB *p_rssi_cmpl_cb; /* Callback function to be called when */ + /* read rssi function completes */ + TIMER_LIST_ENT lnk_quality_timer; + tBTM_CMPL_CB *p_lnk_qual_cmpl_cb;/* Callback function to be called when */ + /* read link quality function completes */ + TIMER_LIST_ENT txpwer_timer; + tBTM_CMPL_CB *p_txpwer_cmpl_cb; /* Callback function to be called when */ + /* read inq tx power function completes */ + + TIMER_LIST_ENT qossu_timer; + tBTM_CMPL_CB *p_qossu_cmpl_cb; /* Callback function to be called when */ + /* qos setup function completes */ + + tBTM_ROLE_SWITCH_CMPL switch_role_ref_data; + tBTM_CMPL_CB *p_switch_role_cb; /* Callback function to be called when */ + /* requested switch role is completed */ + + tBTM_CHANGE_KEY_CMPL chg_link_key_ref_data; + tBTM_CMPL_CB *p_chg_link_key_cb; /* Callback function to be called when */ + /* change of link key is completed */ + + TIMER_LIST_ENT tx_power_timer; + tBTM_CMPL_CB *p_tx_power_cmpl_cb; /* Callback function to be called */ + + BD_ADDR local_addr; /* BD_ADDR of the local device */ + tBTM_VERSION_INFO local_version; /* Local Version Information */ + BD_FEATURES local_features; /* Local features bit mask */ + DEV_CLASS dev_class; /* Local device class */ +#if BLE_INCLUDED == TRUE + BD_ADDR read_tx_pwr_addr; /* read TX power target address */ + + tBTM_BLE_LOCAL_ID_KEYS id_keys; /* local BLE ID keys */ + BT_OCTET16 er; /* BLE encryption key */ + +#if BTM_BLE_CONFORMANCE_TESTING == TRUE + BOOLEAN no_disc_if_pair_fail; + BOOLEAN enable_test_mac_val; + BT_OCTET8 test_mac; + BOOLEAN enable_test_local_sign_cntr; + UINT32 test_local_sign_cntr; +#endif + + +#endif /* BLE_INCLUDED */ + +#define BTM_DEV_STATE_WAIT_RESET_CMPLT 0 +#define BTM_DEV_STATE_WAIT_AFTER_RESET 1 +#define BTM_DEV_STATE_READY 2 + + UINT8 state; + tBTM_IO_CAP loc_io_caps; /* IO capability of the local device */ + BOOLEAN loc_auth_req; /* the auth_req flag */ + BD_FEATURES brcm_features; /* Broadcom specific features bit mask */ +} tBTM_DEVCB; + + +/* Define the structures and constants used for inquiry +*/ + +/* Definitions of limits for inquiries */ +#define BTM_PER_INQ_MIN_MAX_PERIOD HCI_PER_INQ_MIN_MAX_PERIOD +#define BTM_PER_INQ_MAX_MAX_PERIOD HCI_PER_INQ_MAX_MAX_PERIOD +#define BTM_PER_INQ_MIN_MIN_PERIOD HCI_PER_INQ_MIN_MIN_PERIOD +#define BTM_PER_INQ_MAX_MIN_PERIOD HCI_PER_INQ_MAX_MIN_PERIOD +#define BTM_MAX_INQUIRY_LENGTH HCI_MAX_INQUIRY_LENGTH +#define BTM_MIN_INQUIRY_LEN 0x01 + +#define BTM_MIN_INQ_TX_POWER -70 +#define BTM_MAX_INQ_TX_POWER 20 + +#if BTM_USE_INQ_RESULTS_FILTER == TRUE +typedef struct +{ + UINT32 inq_count; /* Used for determining if a response has already been */ + /* received for the current inquiry operation. (We do not */ + /* want to flood the caller with multiple responses from */ + /* the same device. */ + BD_ADDR bd_addr; +} tINQ_BDADDR; +#endif + +typedef struct +{ + UINT32 time_of_resp; + UINT32 inq_count; /* "timestamps" the entry with a particular inquiry count */ + /* Used for determining if a response has already been */ + /* received for the current inquiry operation. (We do not */ + /* want to flood the caller with multiple responses from */ + /* the same device. */ + tBTM_INQ_INFO inq_info; + BOOLEAN in_use; + +#if (BLE_INCLUDED == TRUE) + BOOLEAN scan_rsp; +#endif +} tINQ_DB_ENT; + + +typedef struct +{ + tBTM_CMPL_CB *p_remname_cmpl_cb; + +#define BTM_EXT_RMT_NAME_TIMEOUT 40 + + + TIMER_LIST_ENT rmt_name_timer_ent; + + UINT16 discoverable_mode; + UINT16 connectable_mode; + UINT16 page_scan_window; + UINT16 page_scan_period; + UINT16 inq_scan_window; + UINT16 inq_scan_period; + UINT16 inq_scan_type; + UINT16 page_scan_type; /* current page scan type */ + + BD_ADDR remname_bda; /* Name of bd addr for active remote name request */ +#define BTM_RMT_NAME_INACTIVE 0 +#define BTM_RMT_NAME_EXT 0x1 /* Initiated through API */ +#define BTM_RMT_NAME_SEC 0x2 /* Initiated internally by security manager */ +#define BTM_RMT_NAME_INQ 0x4 /* Remote name initiated internally by inquiry */ + BOOLEAN remname_active; /* State of a remote name request by external API */ + + tBTM_CMPL_CB *p_inq_cmpl_cb; + tBTM_INQ_RESULTS_CB *p_inq_results_cb; + tBTM_CMPL_CB *p_inqfilter_cmpl_cb; /* Called (if not NULL) after inquiry filter completed */ + tBTM_INQ_DB_CHANGE_CB *p_inq_change_cb; /* Inquiry database changed callback */ + UINT32 inq_counter; /* Counter incremented each time an inquiry completes */ + /* Used for determining whether or not duplicate devices */ + /* have responded to the same inquiry */ + TIMER_LIST_ENT inq_timer_ent; +#if BTM_USE_INQ_RESULTS_FILTER == TRUE + tINQ_BDADDR *p_bd_db; /* Pointer to memory that holds bdaddrs */ + UINT16 num_bd_entries; /* Number of entries in database */ + UINT16 max_bd_entries; /* Maximum number of entries that can be stored */ +#endif + tINQ_DB_ENT inq_db[BTM_INQ_DB_SIZE]; + tBTM_INQ_PARMS inqparms; /* Contains the parameters for the current inquiry */ + tBTM_INQUIRY_CMPL inq_cmpl_info; /* Status and number of responses from the last inquiry */ + + UINT16 per_min_delay; /* Current periodic minimum delay */ + UINT16 per_max_delay; /* Current periodic maximum delay */ + BOOLEAN inqfilt_active; + UINT8 pending_filt_complete_event; /* to take care of btm_event_filter_complete corresponding to */ + /* inquiry that has been cancelled*/ + UINT8 inqfilt_type; /* Contains the inquiry filter type (BD ADDR, COD, or Clear) */ + +#define BTM_INQ_INACTIVE_STATE 0 +#define BTM_INQ_CLR_FILT_STATE 1 /* Currently clearing the inquiry filter preceeding the inquiry request */ + /* (bypassed if filtering is not used) */ +#define BTM_INQ_SET_FILT_STATE 2 /* Sets the new filter (or turns off filtering) in this state */ +#define BTM_INQ_ACTIVE_STATE 3 /* Actual inquiry or periodic inquiry is in progress */ +#define BTM_INQ_REMNAME_STATE 4 /* Remote name requests are active */ + + UINT8 state; /* Current state that the inquiry process is in */ + UINT8 inq_active; /* Bit Mask indicating type of inquiry is active */ + BOOLEAN no_inc_ssp; /* TRUE, to stop inquiry on incoming SSP */ +} tBTM_INQUIRY_VAR_ST; + +/* The MSB of the clock offset field indicates that the offset is valid if TRUE */ +#define BTM_CLOCK_OFFSET_VALID 0x8000 + +/* Define the structures needed by security management +*/ + +#define BTM_SEC_INVALID_HANDLE 0xFFFF + +typedef UINT8 *BTM_BD_NAME_PTR; /* Pointer to Device name */ + +/* Security callback is called by this unit when security +** procedures are completed. Parameters are +** BD Address of remote +** Result of the operation +*/ +typedef tBTM_SEC_CBACK tBTM_SEC_CALLBACK; + +typedef void (tBTM_SCO_IND_CBACK) (UINT16 sco_inx) ; + +/* MACROs to convert from SCO packet types mask to ESCO and back */ +#define BTM_SCO_PKT_TYPE_MASK ( HCI_PKT_TYPES_MASK_HV1 \ + | HCI_PKT_TYPES_MASK_HV2 \ + | HCI_PKT_TYPES_MASK_HV3) + +/* Mask defining only the SCO types of an esco packet type */ +#define BTM_ESCO_PKT_TYPE_MASK ( HCI_ESCO_PKT_TYPES_MASK_HV1 \ + | HCI_ESCO_PKT_TYPES_MASK_HV2 \ + | HCI_ESCO_PKT_TYPES_MASK_HV3) + +#define BTM_SCO_2_ESCO(scotype) ((UINT16)(((scotype) & BTM_SCO_PKT_TYPE_MASK) >> 5)) +#define BTM_ESCO_2_SCO(escotype) ((UINT16)(((escotype) & BTM_ESCO_PKT_TYPE_MASK) << 5)) + +/* Define masks for supported and exception 2.0 SCO packet types +*/ +#define BTM_SCO_SUPPORTED_PKTS_MASK (HCI_ESCO_PKT_TYPES_MASK_HV1 | \ + HCI_ESCO_PKT_TYPES_MASK_HV2 | \ + HCI_ESCO_PKT_TYPES_MASK_HV3 | \ + HCI_ESCO_PKT_TYPES_MASK_EV3 | \ + HCI_ESCO_PKT_TYPES_MASK_EV4 | \ + HCI_ESCO_PKT_TYPES_MASK_EV5) + +#define BTM_SCO_EXCEPTION_PKTS_MASK (HCI_ESCO_PKT_TYPES_MASK_NO_2_EV3 | \ + HCI_ESCO_PKT_TYPES_MASK_NO_3_EV3 | \ + HCI_ESCO_PKT_TYPES_MASK_NO_2_EV5 | \ + HCI_ESCO_PKT_TYPES_MASK_NO_3_EV5) + + +#define BTM_SCO_ROUTE_UNKNOWN 0xff + +/* Define the structure that contains (e)SCO data */ +typedef struct +{ + tBTM_ESCO_CBACK *p_esco_cback; /* Callback for eSCO events */ + tBTM_ESCO_PARAMS setup; + tBTM_ESCO_DATA data; /* Connection complete information */ + UINT8 hci_status; +} tBTM_ESCO_INFO; + +/* Define the structure used for SCO Management +*/ +typedef struct +{ + tBTM_ESCO_INFO esco; /* Current settings */ +#if BTM_SCO_HCI_INCLUDED == TRUE + BUFFER_Q xmit_data_q; /* SCO data transmitting queue */ +#endif + tBTM_SCO_CB *p_conn_cb; /* Callback for when connected */ + tBTM_SCO_CB *p_disc_cb; /* Callback for when disconnect */ + UINT16 state; /* The state of the SCO link */ + UINT16 hci_handle; /* HCI Handle */ + BOOLEAN is_orig; /* TRUE if the originator */ + BOOLEAN rem_bd_known; /* TRUE if remote BD addr known */ + +} tSCO_CONN; + +/* SCO Management control block */ +typedef struct +{ + tBTM_SCO_IND_CBACK *app_sco_ind_cb; +#if BTM_SCO_HCI_INCLUDED == TRUE + tBTM_SCO_DATA_CB *p_data_cb; /* Callback for SCO data over HCI */ + UINT32 xmit_window_size; /* Total SCO window in bytes */ +#endif + tSCO_CONN sco_db[BTM_MAX_SCO_LINKS]; + tBTM_ESCO_PARAMS def_esco_parms; + BD_ADDR xfer_addr; + UINT16 sco_disc_reason; + BOOLEAN esco_supported; /* TRUE if 1.2 cntlr AND supports eSCO links */ + tBTM_SCO_TYPE desired_sco_mode; + tBTM_SCO_TYPE xfer_sco_type; + tBTM_SCO_PCM_PARAM sco_pcm_param; + tBTM_SCO_CODEC_TYPE codec_in_use; /* None, CVSD, MSBC, etc. */ +#if BTM_SCO_HCI_INCLUDED == TRUE + tBTM_SCO_ROUTE_TYPE sco_path; +#endif + +} tSCO_CB; + + +#if BTM_SCO_INCLUDED == TRUE +extern void btm_set_sco_ind_cback( tBTM_SCO_IND_CBACK *sco_ind_cb ); +extern void btm_accept_sco_link(UINT16 sco_inx, tBTM_ESCO_PARAMS *p_setup, + tBTM_SCO_CB *p_conn_cb, tBTM_SCO_CB *p_disc_cb); +extern void btm_reject_sco_link(UINT16 sco_inx ); +extern void btm_sco_chk_pend_rolechange (UINT16 hci_handle); +#else +#define btm_accept_sco_link(sco_inx, p_setup, p_conn_cb, p_disc_cb) +#define btm_reject_sco_link(sco_inx) +#define btm_set_sco_ind_cback(sco_ind_cb) +#define btm_sco_chk_pend_rolechange(hci_handle) +#endif /* BTM_SCO_INCLUDED */ + +/* +** Define structure for Security Service Record. +** A record exists for each service registered with the Security Manager +*/ +typedef struct +{ + UINT32 mx_proto_id; /* Service runs over this multiplexer protocol */ + UINT32 orig_mx_chan_id; /* Channel on the multiplexer protocol */ + UINT32 term_mx_chan_id; /* Channel on the multiplexer protocol */ + UINT16 psm; /* L2CAP PSM value */ + UINT16 security_flags; /* Bitmap of required security features */ + UINT8 service_id; /* Passed in authorization callback */ +#if (L2CAP_UCD_INCLUDED == TRUE) + UINT16 ucd_security_flags; /* Bitmap of required security features for UCD */ +#endif +#if BTM_SEC_SERVICE_NAME_LEN > 0 + UINT8 orig_service_name[BTM_SEC_SERVICE_NAME_LEN + 1]; + UINT8 term_service_name[BTM_SEC_SERVICE_NAME_LEN + 1]; +#endif +} tBTM_SEC_SERV_REC; + +#if BLE_INCLUDED == TRUE +/* LE Security information of device in Slave Role */ +typedef struct +{ + BT_OCTET16 irk; /* peer diverified identity root */ + BT_OCTET16 ltk; /* peer long term key */ + BT_OCTET16 csrk; /* peer SRK peer device used to secured sign local data */ + + BT_OCTET8 rand; /* random vector for LTK generation */ + UINT16 ediv; /* LTK diversifier of this slave device */ + UINT16 div; /* local DIV to generate local LTK=d1(ER,DIV,0) and CSRK=d1(ER,DIV,1) */ + UINT8 sec_level; /* local pairing security level */ + UINT8 key_size; /* key size of the LTK delivered to peer device */ + UINT8 srk_sec_level; /* security property of peer SRK for this device */ + UINT8 local_csrk_sec_level; /* security property of local CSRK for this device */ + + UINT32 counter; /* peer sign counter for verifying rcv signed cmd */ + UINT32 local_counter; /* local sign counter for sending signed write cmd*/ +}tBTM_SEC_BLE_KEYS; + +#endif /* BLE_INCLUDED */ + +typedef struct +{ + tBLE_ADDR_TYPE ble_addr_type; /* LE device type: public or random address */ + BD_ADDR reconn_addr; /* reconnect address */ + BD_ADDR cur_rand_addr; /* current random address */ + BD_ADDR static_addr; /* static address */ + +#if SMP_INCLUDED == TRUE + tBTM_LE_KEY_TYPE key_type; /* bit mask of valid key types in record */ + tBTM_SEC_BLE_KEYS keys; /* LE device security info in slave rode */ +#endif +} tBTM_SEC_BLE; + +/* +** Define structure for Security Device Record. +** A record exists for each device authenticated with this device +*/ +typedef struct +{ + tBTM_SEC_SERV_REC *p_cur_service; + tBTM_SEC_CALLBACK *p_callback; + void *p_ref_data; + UINT32 timestamp; /* Timestamp of the last connection */ + UINT32 trusted_mask[BTM_SEC_SERVICE_ARRAY_SIZE]; /* Bitwise OR of trusted services */ + UINT16 hci_handle; /* Handle to connection when exists */ + UINT16 clock_offset; /* Latest known clock offset */ + BD_ADDR bd_addr; /* BD_ADDR of the device */ + DEV_CLASS dev_class; /* DEV_CLASS of the device */ + LINK_KEY link_key; /* Device link key */ + +#define BTM_SEC_AUTHORIZED BTM_SEC_FLAG_AUTHORIZED +#define BTM_SEC_AUTHENTICATED BTM_SEC_FLAG_AUTHENTICATED +#define BTM_SEC_ENCRYPTED BTM_SEC_FLAG_ENCRYPTED +#define BTM_SEC_NAME_KNOWN 0x08 +#define BTM_SEC_LINK_KEY_KNOWN BTM_SEC_FLAG_LKEY_KNOWN +#define BTM_SEC_LINK_KEY_AUTHED 0x20 +#define BTM_SEC_ROLE_SWITCHED 0x40 +#define BTM_SEC_IN_USE 0x80 + + tBTM_BD_NAME sec_bd_name; /* User friendly name of the device. (may be truncated to save space in dev_rec table) */ + UINT8 sec_flags; /* Current device security state */ + BD_FEATURES features; /* Features suported by the device */ + +#define BTM_SEC_STATE_IDLE 0 +#define BTM_SEC_STATE_AUTHENTICATING 1 +#define BTM_SEC_STATE_ENCRYPTING 2 +#define BTM_SEC_STATE_GETTING_NAME 3 +#define BTM_SEC_STATE_AUTHORIZING 4 +#define BTM_SEC_STATE_SWITCHING_ROLE 5 +#define BTM_SEC_STATE_DISCONNECTING 6 +#define BTM_SEC_STATE_DELAY_FOR_ENC 7 /* delay to check for encryption to work around controller problems */ + + UINT8 sec_state; /* Operating state */ + BOOLEAN is_originator; /* TRUE if device is originating connection */ +#if (L2CAP_UCD_INCLUDED == TRUE) + BOOLEAN is_ucd; /* TRUE if device is sending or receiving UCD */ + /* if incoming security failed, received UCD will be discarded */ +#endif + BOOLEAN role_master; /* TRUE if current mode is master */ + UINT16 security_required; /* Security required for connection */ + BOOLEAN link_key_not_sent; /* link key notification has not been sent waiting for name */ + UINT8 link_key_type; /* Type of key used in pairing */ + BOOLEAN link_key_changed; /* Changed link key during current connection */ + +#define BTM_SM4_UNKNOWN 0x00 +#define BTM_SM4_KNOWN 0x10 +#define BTM_SM4_TRUE 0x11 +#define BTM_SM4_REQ_PEND 0x08 /* set this bit when getting remote features */ +#define BTM_SM4_UPGRADE 0x04 /* set this bit when upgrading link key */ +#define BTM_SM4_RETRY 0x02 /* set this bit to retry on HCI_ERR_KEY_MISSING or HCI_ERR_LMP_ERR_TRANS_COLLISION */ +#define BTM_SM4_DD_ACP 0x20 /* set this bit to indicate peer initiated dedicated bonding */ +#define BTM_SM4_CONN_PEND 0x40 /* set this bit to indicate accepting acl conn; to be cleared on btm_acl_created */ + UINT8 sm4; /* BTM_SM4_TRUE, if the peer supports SM4 */ + tBTM_IO_CAP rmt_io_caps; /* IO capability of the peer device */ + BOOLEAN rmt_auth_req; /* the auth_req flag as in the IO caps rsp evt */ + +#if (BLE_INCLUDED == TRUE) + UINT8 enc_key_size; /* current link encryption key size */ + tBTM_SEC_BLE ble; + tBT_DEVICE_TYPE device_type; + tBTM_LE_CONN_PRAMS conn_params; +#endif + +// btla-specific ++ +#if BTM_DISC_DURING_RS == TRUE +#define BTM_SEC_RS_NOT_PENDING 0 /* Role Switch not in progress */ +#define BTM_SEC_RS_PENDING 1 /* Role Switch in progress */ +#define BTM_SEC_DISC_PENDING 2 /* Disconnect is pending */ + UINT8 rs_disc_pending; +#endif +// btla-specific -- +} tBTM_SEC_DEV_REC; + +#define BTM_SEC_IS_SM4(sm) ((BOOLEAN)(BTM_SM4_TRUE == ((sm)&BTM_SM4_TRUE))) +#define BTM_SEC_IS_SM4_LEGACY(sm) ((BOOLEAN)(BTM_SM4_KNOWN == ((sm)&BTM_SM4_TRUE))) +#define BTM_SEC_IS_SM4_UNKNOWN(sm) ((BOOLEAN)(BTM_SM4_UNKNOWN == ((sm)&BTM_SM4_TRUE))) + + +/* +** Define device configuration structure +*/ +typedef struct +{ +#if BTM_MAX_LOC_BD_NAME_LEN > 0 + tBTM_LOC_BD_NAME bd_name; /* local Bluetooth device name */ +#endif + BOOLEAN pin_type; /* TRUE if PIN type is fixed */ + UINT8 pin_code_len; /* Bonding information */ + PIN_CODE pin_code; /* PIN CODE if pin type is fixed */ + BOOLEAN connectable; /* If TRUE page scan should be enabled */ + UINT8 def_inq_scan_mode; /* ??? limited/general/none */ +} tBTM_CFG; + +#if BTM_PWR_MGR_INCLUDED == TRUE +enum +{ + BTM_PM_ST_ACTIVE = BTM_PM_STS_ACTIVE, + BTM_PM_ST_HOLD = BTM_PM_STS_HOLD, + BTM_PM_ST_SNIFF = BTM_PM_STS_SNIFF, + BTM_PM_ST_PARK = BTM_PM_STS_PARK, + BTM_PM_ST_PENDING = BTM_PM_STS_PENDING +}; +typedef UINT8 tBTM_PM_STATE; + +enum +{ + BTM_PM_SET_MODE_EVT, /* Set power mode API is called. */ + BTM_PM_UPDATE_EVT, + BTM_PM_RD_MODE_EVT /* Read power mode API is called. */ +}; +typedef UINT8 tBTM_PM_EVENT; + +typedef struct +{ + UINT16 event; + UINT16 len; + UINT8 link_ind; +} tBTM_PM_MSG_DATA; + +typedef struct +{ + UINT8 hci_status; + UINT8 mode; + UINT16 interval; +} tBTM_PM_MD_CHG_DATA; + +typedef struct +{ + UINT8 pm_id; /* the entity that calls SetPowerMode API */ + tBTM_PM_PWR_MD *p_pmd; +} tBTM_PM_SET_MD_DATA; + +typedef struct +{ + void *p_data; + UINT8 link_ind; +} tBTM_PM_SM_DATA; + +typedef struct +{ + tBTM_PM_PWR_MD req_mode[BTM_MAX_PM_RECORDS+1]; /* the desired mode and parameters of the connection*/ + tBTM_PM_PWR_MD set_mode; /* the mode and parameters sent down to the host controller. */ + UINT16 interval; /* the interval from last mode change event. */ +#if (BTM_SSR_INCLUDED == TRUE) + UINT16 max_lat; /* stored SSR maximum latency */ + UINT16 min_rmt_to;/* stored SSR minimum remote timeout */ + UINT16 min_loc_to;/* stored SSR minimum local timeout */ +#endif + tBTM_PM_STATE state; /* contains the current mode of the connection */ + BOOLEAN chg_ind; /* a request change indication */ +} tBTM_PM_MCB; + +#define BTM_PM_REC_NOT_USED 0 +typedef struct +{ + tBTM_PM_STATUS_CBACK *cback;/* to notify the registered party of mode change event */ + UINT8 mask; /* registered request mask. 0, if this entry is not used */ +} tBTM_PM_RCB; +#endif /* BTM_PWR_MGR_INCLUDED */ + +enum +{ + BTM_BLI_ACL_UP_EVT, + BTM_BLI_ACL_DOWN_EVT, + BTM_BLI_PAGE_EVT, + BTM_BLI_PAGE_DONE_EVT, + BTM_BLI_INQ_EVT, + BTM_BLI_INQ_DONE_EVT +}; +typedef UINT8 tBTM_BLI_EVENT; + +/* Pairing State */ +enum +{ + BTM_PAIR_STATE_IDLE, /* Idle */ + BTM_PAIR_STATE_GET_REM_NAME, /* Getting the remote name (to check for SM4) */ + BTM_PAIR_STATE_WAIT_PIN_REQ, /* Started authentication, waiting for PIN req (PIN is pre-fetched) */ + BTM_PAIR_STATE_WAIT_LOCAL_PIN, /* Waiting for local PIN code */ + BTM_PAIR_STATE_WAIT_NUMERIC_CONFIRM, /* Waiting user 'yes' to numeric confirmation */ + BTM_PAIR_STATE_KEY_ENTRY, /* Key entry state (we are a keyboard) */ + BTM_PAIR_STATE_WAIT_LOCAL_OOB_RSP, /* Waiting for local response to peer OOB data */ + BTM_PAIR_STATE_WAIT_LOCAL_IOCAPS, /* Waiting for local IO capabilities and OOB data */ + BTM_PAIR_STATE_INCOMING_SSP, /* Incoming SSP (got peer IO caps when idle) */ + BTM_PAIR_STATE_WAIT_AUTH_COMPLETE, /* All done, waiting authentication cpmplete */ + BTM_PAIR_STATE_WAIT_DISCONNECT /* Waiting to disconnect the ACL */ +}; +typedef UINT8 tBTM_PAIRING_STATE; + +#define BTM_PAIR_FLAGS_WE_STARTED_DD 0x01 /* We want to do dedicated bonding */ +#define BTM_PAIR_FLAGS_PEER_STARTED_DD 0x02 /* Peer initiated dedicated bonding */ +#define BTM_PAIR_FLAGS_DISC_WHEN_DONE 0x04 +#define BTM_PAIR_FLAGS_PIN_REQD 0x08 /* set this bit when pin_callback is called */ +#define BTM_PAIR_FLAGS_PRE_FETCH_PIN 0x10 /* set this bit when pre-fetch pin */ +#define BTM_PAIR_FLAGS_REJECTED_CONNECT 0x20 /* set this bit when rejected incoming connection */ + +typedef struct +{ + BOOLEAN is_mux; + BD_ADDR bd_addr; + UINT16 psm; + BOOLEAN is_orig; + tBTM_SEC_CALLBACK *p_callback; + void *p_ref_data; + UINT32 mx_proto_id; + UINT32 mx_chan_id; +} tBTM_SEC_QUEUE_ENTRY; + +#if (L2CAP_UCD_INCLUDED == TRUE) + +#define CONN_ORIENT_TERM 0x00 /* incoming connection oriented */ +#define CONN_ORIENT_ORIG 0x01 /* outgoing connection oriented */ +#define CONNLESS_TERM 0x02 /* incoming connectionless */ +#define CONNLESS_ORIG 0x03 /* outgoing connectionless */ +#define CONNECTION_TYPE_ORIG_MASK 0x01 /* mask for direction */ +#define CONNECTION_TYPE_CONNLESS_MASK 0x02 /* mask for connectionless or not */ +typedef UINT8 CONNECTION_TYPE; + +#else + +#define CONN_ORIENT_TERM FALSE +#define CONN_ORIENT_ORIG TRUE +typedef BOOLEAN CONNECTION_TYPE; + +#endif /* (L2CAP_UCD_INCLUDED == TRUE) */ + +/* Define a structure to hold all the BTM data +*/ + +#define BTM_STATE_BUFFER_SIZE 5 /* size of state buffer */ + +#if (BTM_PCM2_INCLUDED == TRUE) +/* Define pcm2_action */ +enum +{ + BTM_PCM2_ACT_NONE, + BTM_PCM2_ACT_SENT_ARC, + BTM_PCM2_READ_PARAM, + BTM_PCM2_WRITE_PARAM, +}; +typedef UINT8 tBTM_PCM2_ACTION; +#endif + +typedef struct +{ + tBTM_CFG cfg; /* Device configuration */ + + /**************************************************** + ** ACL Management + ****************************************************/ + tACL_CONN acl_db[MAX_L2CAP_LINKS]; +#if( RFCOMM_INCLUDED==TRUE) + UINT8 btm_scn[BTM_MAX_SCN]; /* current SCNs: TRUE if SCN is in use */ +#endif + UINT16 btm_def_link_policy; + UINT16 btm_def_link_super_tout; + +#if (defined(BTM_BUSY_LEVEL_CHANGE_INCLUDED) && BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE) + tBTM_BL_EVENT_MASK bl_evt_mask; + tBTM_BL_CHANGE_CB *p_bl_changed_cb; /* Callback for when Busy Level changed */ +#else + tBTM_ACL_DB_CHANGE_CB *p_acl_changed_cb; /* Callback for when ACL DB changed */ +#endif + + tBTM_LSTO_CBACK *p_lsto_cback; /* for link supervision timeout change event */ + + /**************************************************** + ** Power Management + ****************************************************/ +#if BTM_PWR_MGR_INCLUDED == TRUE + tBTM_PM_MCB pm_mode_db[MAX_L2CAP_LINKS]; /* per ACL link */ + tBTM_PM_RCB pm_reg_db[BTM_MAX_PM_RECORDS+1]; /* per application/module */ + UINT8 pm_pend_link; /* the index of acl_db, which has a pending PM cmd */ + UINT8 pm_pend_id; /* the id pf the module, which has a pending PM cmd */ +#endif /* BTM_PWR_MGR_INCLUDED == TRUE */ + + /***************************************************** + ** Device control + *****************************************************/ + tBTM_DEVCB devcb; + + /***************************************************** + ** BLE Device controllers + *****************************************************/ +#if (BLE_INCLUDED == TRUE) + tBTM_BLE_CB ble_ctr_cb; + + UINT16 enc_handle; + BT_OCTET8 enc_rand; /* received rand value from LTK request*/ + UINT16 ediv; /* received ediv value from LTK request */ + UINT8 key_size; +#endif + + /* Packet types supported by the local device */ + UINT16 btm_acl_pkt_types_supported; + UINT16 btm_sco_pkt_types_supported; + + + /***************************************************** + ** Inquiry + *****************************************************/ + tBTM_INQUIRY_VAR_ST btm_inq_vars; + tBTM_FILTER_CB *p_inq_filter_cb; /* Callback that can be set if host */ + /* wants to verify inquiry filters */ + + /***************************************************** + ** SCO Management + *****************************************************/ +#if BTM_SCO_INCLUDED == TRUE + tSCO_CB sco_cb; +#endif + + /***************************************************** + ** Security Management + *****************************************************/ + tBTM_APPL_INFO api; + +#define BTM_SEC_MAX_RMT_NAME_CALLBACKS 2 + tBTM_RMT_NAME_CALLBACK *p_rmt_name_callback[BTM_SEC_MAX_RMT_NAME_CALLBACKS]; + + tBTM_FILTER_CB *p_conn_filter_cb; /* Callback that can be set if host */ + /* wants to verify connectability filters*/ + + tBTM_SEC_DEV_REC *p_collided_dev_rec; + TIMER_LIST_ENT sec_collision_tle; + UINT32 collision_start_time; + UINT32 max_collision_delay; + UINT32 dev_rec_count; /* Counter used for device record timestamp */ + UINT8 security_mode; + BOOLEAN pairing_disabled; + BOOLEAN connect_only_paired; + BOOLEAN security_mode_changed; /* mode changed during bonding */ + BOOLEAN pin_type_changed; /* pin type changed during bonding */ + BOOLEAN sec_req_pending; /* TRUE if a request is pending */ +// btla-specific ++ +#ifdef PORCHE_PAIRING_CONFLICT + UINT8 pin_code_len_saved; /* for legacy devices */ +#endif +// btla-specific -- + + UINT8 pin_code_len; /* for legacy devices */ + PIN_CODE pin_code; /* for legacy devices */ + tBTM_PAIRING_STATE pairing_state; /* The current pairing state */ + UINT8 pairing_flags; /* The current pairing flags */ + BD_ADDR pairing_bda; /* The device currently pairing */ + TIMER_LIST_ENT pairing_tle; /* Timer for pairing process */ + UINT16 disc_handle; /* for legacy devices */ + UINT8 disc_reason; /* for legacy devices */ + tBTM_SEC_SERV_REC sec_serv_rec[BTM_SEC_MAX_SERVICE_RECORDS]; + tBTM_SEC_DEV_REC sec_dev_rec[BTM_SEC_MAX_DEVICE_RECORDS]; + tBTM_SEC_SERV_REC *p_out_serv; + tBTM_MKEY_CALLBACK *mkey_cback; + + BD_ADDR connecting_bda; + DEV_CLASS connecting_dc; + + UINT8 first_disabled_channel; + UINT8 last_disabled_channel; + + UINT8 acl_disc_reason; + UINT8 trace_level; +#if (defined(BTM_BUSY_LEVEL_CHANGE_INCLUDED) && BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE) + UINT8 num_acl; /* num of active ACL links */ + UINT8 busy_level; /* the current busy level */ + BOOLEAN is_paging; /* TRUE, if paging is in progess */ + BOOLEAN is_inquiry; /* TRUE, if inquiry is in progess */ +#endif + BUFFER_Q page_queue; + BOOLEAN paging; + BOOLEAN discing; + BUFFER_Q sec_pending_q; /* pending sequrity requests in tBTM_SEC_QUEUE_ENTRY format */ + +#if (!defined(BT_TRACE_VERBOSE) || (BT_TRACE_VERBOSE == FALSE)) + char state_temp_buffer[BTM_STATE_BUFFER_SIZE]; +#endif + +#if (defined(BTM_PCM2_INCLUDED) && BTM_PCM2_INCLUDED == TRUE) + UINT16 sys_features; + UINT8 pcm2_params[BRCM_PCM2_SETUP_WRITE_SIZE]; + tBTM_PCM2_ACTION pcm2_action; +#endif + +} tBTM_CB; + + +#ifdef __cplusplus +extern "C" +{ +#endif + +#if BTM_DYNAMIC_MEMORY == FALSE +BTM_API extern tBTM_CB btm_cb; +#else +BTM_API extern tBTM_CB *btm_cb_ptr; +#define btm_cb (*btm_cb_ptr) +#endif + +/* Internal functions provided by btm_main.c +******************************************** +*/ +extern void btm_init (void); + +/* Internal functions provided by btm_inq.c +******************************************* +*/ +extern tBTM_STATUS btm_initiate_rem_name (BD_ADDR remote_bda, + tBTM_INQ_INFO *p_cur, + UINT8 origin, UINT32 timeout, + tBTM_CMPL_CB *p_cb); + +extern void btm_process_remote_name (BD_ADDR bda, BD_NAME name, UINT16 evt_len, + UINT8 hci_status); +extern void btm_inq_rmt_name_failed(void); + +/* Inquiry related functions */ +extern void btm_clr_inq_db (BD_ADDR p_bda); +extern void btm_inq_db_init (void); +extern void btm_process_inq_results (UINT8 *p, UINT8 inq_res_mode); +extern void btm_process_inq_complete (UINT8 status, UINT8 mode); +extern void btm_event_filter_complete (UINT8 *p); +extern void btm_inq_stop_on_ssp(void); +extern void btm_inq_clear_ssp(void); +extern tINQ_DB_ENT *btm_inq_db_find (BD_ADDR p_bda); +extern BOOLEAN btm_inq_find_bdaddr (BD_ADDR p_bda); + +#if (BTM_EIR_CLIENT_INCLUDED == TRUE) +extern BOOLEAN btm_lookup_eir(BD_ADDR_PTR p_rem_addr); +#endif + +/* Internal functions provided by btm_acl.c +******************************************** +*/ +extern void btm_acl_init (void); +extern void btm_acl_timeout (TIMER_LIST_ENT *p_tle); +extern void btm_acl_created (BD_ADDR bda, DEV_CLASS dc, BD_NAME bdn, + UINT16 hci_handle, UINT8 link_role, UINT8 is_le_link); +extern void btm_acl_removed (BD_ADDR bda); +extern void btm_acl_device_down (void); +extern void btm_acl_update_busy_level (tBTM_BLI_EVENT event); +extern void btm_acl_link_key_change (UINT16 handle, UINT8 status); + +extern void btm_cont_rswitch_or_chglinkkey (tACL_CONN *p, + tBTM_SEC_DEV_REC *p_dev_rec, + UINT8 hci_status); + +extern UINT8 btm_handle_to_acl_index (UINT16 hci_handle); +extern void btm_read_link_policy_complete (UINT8 *p); +extern void btm_read_rssi_complete (UINT8 *p); +extern void btm_read_tx_power_complete (UINT8 *p, BOOLEAN is_ble); +extern void btm_read_link_quality_complete (UINT8 *p); +extern tBTM_STATUS btm_set_packet_types (tACL_CONN *p, UINT16 pkt_types); +extern void btm_process_clk_off_comp_evt (UINT16 hci_handle, UINT16 clock_offset); +extern void btm_acl_role_changed (UINT8 hci_status, BD_ADDR bd_addr, UINT8 new_role); +extern void btm_acl_encrypt_change (UINT16 handle, UINT8 status, UINT8 encr_enable); +BTM_API extern UINT16 btm_get_acl_disc_reason_code (void); +BTM_API extern tBTM_STATUS btm_remove_acl (BD_ADDR bd_addr); +extern void btm_read_remote_features_complete (UINT8 *p); +extern void btm_read_remote_ext_features_complete (UINT8 *p); +extern void btm_read_remote_ext_features_failed (UINT8 status); +extern void btm_read_remote_version_complete (UINT8 *p); +// btla-specific ++ +extern void btm_acl_chk_peer_pkt_type_support (tACL_CONN *p, UINT16 *p_pkt_type); +// btla-specific -- +/* Read maximum data packet that can be sent over current connection */ +extern UINT16 btm_get_max_packet_size (BD_ADDR addr); +extern tACL_CONN *btm_bda_to_acl (BD_ADDR bda); +extern BOOLEAN btm_acl_notif_conn_collision (BD_ADDR bda); + +#if BTM_PWR_MGR_INCLUDED == FALSE +extern void btm_process_mode_change (UINT8 hci_status, UINT16 hci_handle, UINT8 mode, + UINT16 interval); + +/* Internal functions provided by btm_pm.c +******************************************** +*/ +#else +extern void btm_pm_reset(void); +extern void btm_pm_sm_alloc(UINT8 ind); +extern void btm_pm_proc_cmd_status(UINT8 status); +extern void btm_pm_proc_mode_change (UINT8 hci_status, UINT16 hci_handle, UINT8 mode, + UINT16 interval); +extern void btm_pm_proc_ssr_evt (UINT8 *p, UINT16 evt_len); +#if BTM_SCO_INCLUDED == TRUE +extern void btm_sco_chk_pend_unpark (UINT8 hci_status, UINT16 hci_handle); +#else +#define btm_sco_chk_pend_unpark(hci_status, hci_handle) +#endif /* BTM_SCO_INCLUDED */ +#endif /* BTM_PWR_MGR_INCLUDED == FALSE */ +extern void btm_qos_setup_complete (UINT8 status, UINT16 handle, FLOW_SPEC *p_flow); + + +/* Internal functions provided by btm_sco.c +******************************************** +*/ +extern void btm_sco_init (void); +extern void btm_sco_connected (UINT8 hci_status, BD_ADDR bda, UINT16 hci_handle, + tBTM_ESCO_DATA *p_esco_data); +extern void btm_esco_proc_conn_chg (UINT8 status, UINT16 handle, UINT8 tx_interval, + UINT8 retrans_window, UINT16 rx_pkt_len, + UINT16 tx_pkt_len); +extern void btm_sco_conn_req (BD_ADDR bda, DEV_CLASS dev_class, UINT8 link_type); +extern void btm_sco_removed (UINT16 hci_handle, UINT8 reason); +extern void btm_sco_acl_removed (BD_ADDR bda); +extern void btm_route_sco_data (BT_HDR *p_msg); +extern BOOLEAN btm_is_sco_active (UINT16 handle); +extern void btm_remove_sco_links (BD_ADDR bda); +extern BOOLEAN btm_is_sco_active_by_bdaddr (BD_ADDR remote_bda); + +extern tBTM_SCO_TYPE btm_read_def_esco_mode (tBTM_ESCO_PARAMS *p_parms); +extern UINT16 btm_find_scb_by_handle (UINT16 handle); +extern void btm_sco_flush_sco_data(UINT16 sco_inx); + +/* Internal functions provided by btm_devctl.c +********************************************** +*/ +extern void btm_dev_init (void); +extern void btm_dev_absent (void); +extern void btm_dev_timeout (TIMER_LIST_ENT *p_tle); +extern void btm_reset_complete (void); +extern void btm_read_local_version_complete (UINT8 *p, UINT16 evt_len); +extern void btm_read_hci_buf_size_complete (UINT8 *p, UINT16 evt_len); +extern void btm_read_local_features_complete (UINT8 *p, UINT16 evt_len); +extern void btm_read_local_name_complete (UINT8 *p, UINT16 evt_len); +extern void btm_read_local_addr_complete (UINT8 *p, UINT16 evt_len); +extern void btm_get_local_features (void); + +#if (BLE_INCLUDED == TRUE) +extern void btm_read_ble_buf_size_complete (UINT8 *p, UINT16 evt_len); +extern void btm_read_white_list_size_complete(UINT8 *p, UINT16 evt_len); +extern void btm_ble_add_2_white_list_complete(UINT8 *p, UINT16 evt_len); +extern void btm_ble_remove_from_white_list_complete(UINT8 *p, UINT16 evt_len); +extern void btm_ble_clear_white_list_complete(UINT8 *p, UINT16 evt_len); +#endif /* BLE_INCLUDED */ + +/* Vendor Specific Command complete evt handler */ +extern void btm_vsc_complete (UINT8 *p, UINT16 cc_opcode, UINT16 evt_len, + tBTM_CMPL_CB *p_vsc_cplt_cback); +extern void btm_inq_db_reset (void); +extern void btm_vendor_specific_evt (UINT8 *p, UINT8 evt_len); +extern UINT8 btm_get_hci_version (void); +extern void btm_read_stored_link_key_complete (UINT8 *p); +extern void btm_write_stored_link_key_complete (UINT8 *p); +extern void btm_delete_stored_link_key_complete (UINT8 *p); +extern void btm_return_link_keys_evt (tBTM_RETURN_LINK_KEYS_EVT *result); +extern void btm_report_device_status (tBTM_DEV_STATUS status); + + +/* Internal functions provided by btm_dev.c +********************************************** +*/ +extern BOOLEAN btm_dev_support_switch (BD_ADDR bd_addr); +extern UINT8 btm_get_voice_coding_support (void); + +extern tBTM_SEC_DEV_REC *btm_sec_alloc_dev (BD_ADDR bd_addr); +extern void btm_sec_free_dev (tBTM_SEC_DEV_REC *p_dev_rec); +extern tBTM_SEC_DEV_REC *btm_find_dev (BD_ADDR bd_addr); +extern tBTM_SEC_DEV_REC *btm_find_or_alloc_dev (BD_ADDR bd_addr); +extern tBTM_SEC_DEV_REC *btm_find_dev_by_handle (UINT16 handle); + +/* Internal functions provided by btm_sec.c +********************************************** +*/ +extern BOOLEAN btm_dev_support_switch (BD_ADDR bd_addr); +extern tBTM_STATUS btm_sec_l2cap_access_req (BD_ADDR bd_addr, UINT16 psm, + UINT16 handle, CONNECTION_TYPE conn_type, + tBTM_SEC_CALLBACK *p_callback, void *p_ref_data); +extern tBTM_STATUS btm_sec_mx_access_request (BD_ADDR bd_addr, UINT16 psm, BOOLEAN is_originator, + UINT32 mx_proto_id, UINT32 mx_chan_id, + tBTM_SEC_CALLBACK *p_callback, void *p_ref_data); +extern void btm_sec_conn_req (UINT8 *bda, UINT8 *dc); +extern void btm_create_conn_cancel_complete (UINT8 *p); +extern void btm_proc_lsto_evt(UINT16 handle, UINT16 timeout); +extern void btm_read_linq_tx_power_complete (UINT8 *p); + +extern void btm_sec_init (UINT8 sec_mode); +extern void btm_sec_dev_reset (void); +extern void btm_sec_abort_access_req (BD_ADDR bd_addr); +extern void btm_sec_auth_complete (UINT16 handle, UINT8 status); +extern void btm_sec_mkey_comp_event (UINT16 handle, UINT8 status, UINT8 key_flg); +extern void btm_sec_encrypt_change (UINT16 handle, UINT8 status, UINT8 encr_enable); +extern void btm_sec_connected (UINT8 *bda, UINT16 handle, UINT8 status, UINT8 enc_mode); +extern tBTM_STATUS btm_sec_disconnect (UINT16 handle, UINT8 reason); +extern void btm_sec_disconnected (UINT16 handle, UINT8 reason); +extern void btm_sec_rmt_name_request_complete (UINT8 *bd_addr, UINT8 *bd_name, UINT8 status); +extern void btm_sec_rmt_host_support_feat_evt (UINT8 *p); +extern void btm_io_capabilities_req (UINT8 *p); +extern void btm_io_capabilities_rsp (UINT8 *p); +extern void btm_proc_sp_req_evt (tBTM_SP_EVT event, UINT8 *p); +extern void btm_keypress_notif_evt (UINT8 *p); +extern void btm_simple_pair_complete (UINT8 *p); +extern void btm_sec_link_key_notification (UINT8 *p_bda, UINT8 *p_link_key, UINT8 key_type); +extern void btm_sec_link_key_request (UINT8 *p_bda); +extern void btm_sec_pin_code_request (UINT8 *p_bda); +extern void btm_sec_update_clock_offset (UINT16 handle, UINT16 clock_offset); +extern void btm_sec_dev_rec_cback_event (tBTM_SEC_DEV_REC *p_dev_rec, UINT8 res); + +#if BLE_INCLUDED == TRUE +extern void btm_sec_clear_ble_keys (tBTM_SEC_DEV_REC *p_dev_rec); +extern BOOLEAN btm_sec_find_bonded_dev (UINT8 start_idx, UINT8 *p_found_idx, tBTM_SEC_DEV_REC *p_rec); +extern BOOLEAN btm_sec_is_a_bonded_dev (BD_ADDR bda); +#endif /* BLE_INCLUDED */ + +extern tINQ_DB_ENT *btm_inq_db_new (BD_ADDR p_bda); + +#if BTM_OOB_INCLUDED == TRUE +extern void btm_rem_oob_req (UINT8 *p); +extern void btm_read_local_oob_complete (UINT8 *p); +#else +#define btm_rem_oob_req(p) +#define btm_read_local_oob_complete(p) +#endif + +extern void btm_acl_resubmit_page (void); +extern void btm_acl_reset_paging (void); +extern void btm_acl_paging (BT_HDR *p, BD_ADDR dest); +extern void btm_acl_set_discing (BOOLEAN discing); +extern UINT8 btm_sec_clr_service_by_psm (UINT16 psm); + +#ifdef BRCM_VS +extern void btm_brcm_feat_init(void); +extern void btm_vs_reset_complete (void); +extern void btm_brcm_arc_init (void); + +#endif + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/stack/btm/btm_main.c b/stack/btm/btm_main.c new file mode 100644 index 0000000..42489e1 --- /dev/null +++ b/stack/btm/btm_main.c @@ -0,0 +1,59 @@ +/***************************************************************************** +** +** Name: btm_main.c +** +** Description: This file contains the definition of the btm control block +** when BTM_DYNAMIC_MEMORY is used. +** +** +** Copyright (c) 2002-2011, Broadcom Corp., All Rights Reserved. +** Broadcom Bluetooth Core. Proprietary and confidential. +******************************************************************************/ + +#include "bt_types.h" +#include "bt_target.h" +#include <string.h> +#include "btm_int.h" + +/* Global BTM control block structure +*/ +#if BTM_DYNAMIC_MEMORY == FALSE +tBTM_CB btm_cb; +#endif + +/******************************************************************************* +** +** Function btm_init +** +** Description This function is called at BTM startup to allocate the +** control block (if using dynamic memory), and initializes the +** tracing level. It then initializes the various components of +** btm. +** +** Returns void +** +*******************************************************************************/ +void btm_init (void) +{ + /* All fields are cleared; nonzero fields are reinitialized in appropriate function */ + memset(&btm_cb, 0, sizeof(tBTM_CB)); + +#if defined(BTM_INITIAL_TRACE_LEVEL) + btm_cb.trace_level = BTM_INITIAL_TRACE_LEVEL; +#else + btm_cb.trace_level = BT_TRACE_LEVEL_NONE; /* No traces */ +#endif + /* TODO Bluedroid - Hardcoded trace level. Needs to be configurable */ + btm_cb.trace_level = BT_TRACE_LEVEL_DEBUG; + /* Initialize BTM component structures */ + btm_inq_db_init(); /* Inquiry Database and Structures */ + btm_acl_init(); /* ACL Database and Structures */ + btm_sec_init(BTM_SEC_MODE_SP); /* Security Manager Database and Structures */ +#if BTM_SCO_INCLUDED == TRUE + btm_sco_init(); /* SCO Database and Structures (If included) */ +#endif + + btm_dev_init(); /* Device Manager Structures & HCI_Reset */ +} + + diff --git a/stack/btm/btm_pm.c b/stack/btm/btm_pm.c new file mode 100644 index 0000000..169a1b3 --- /dev/null +++ b/stack/btm/btm_pm.c @@ -0,0 +1,985 @@ +/*****************************************************************************/ +/* */ +/* Name: btm_pm.c */ +/* */ +/* Description: This file contains functions that manages ACL link modes. */ +/* This includes operations such as active, hold, */ +/* park and sniff modes. */ +/* */ +/* This module contains both internal and external (API) */ +/* functions. External (API) functions are distinguishable */ +/* by their names beginning with uppercase BTM. */ +/* */ +/* */ +/* Copyright (c) 2000-2009, Broadcom Corp., All Rights Reserved. */ +/* WIDCOMM Bluetooth Core. Proprietary and confidential. */ +/*****************************************************************************/ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <stddef.h> +#include "bt_types.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" + + +#if BTM_PWR_MGR_INCLUDED == TRUE + +/* This compile option is only useful when the FW has a bug + * it automatically uses single slot when entering SNIFF mode, but does not restore the setting + * This issue was found when A2DP link goes into sniff and existing sniff still has choppy audio. + * If this issue is seen, look for FW solution first. + * This work around code will be removed later. */ +#ifndef BTM_PM_SNIFF_SLOT_WORK_AROUND +#define BTM_PM_SNIFF_SLOT_WORK_AROUND FALSE +#endif + +/*****************************************************************************/ +/* to handle different modes */ +/*****************************************************************************/ +#define BTM_PM_STORED_MASK 0x80 /* set this mask if the command is stored */ +#define BTM_PM_NUM_SET_MODES 3 /* only hold, sniff & park */ + +/* Usage: (ptr_features[ offset ] & mask )?TRUE:FALSE */ +/* offset to supported feature */ +const UINT8 btm_pm_mode_off[BTM_PM_NUM_SET_MODES] = {0, 0, 1}; +/* mask to supported feature */ +const UINT8 btm_pm_mode_msk[BTM_PM_NUM_SET_MODES] = {0x40, 0x80, 0x01}; + +#define BTM_PM_GET_MD1 1 +#define BTM_PM_GET_MD2 2 +#define BTM_PM_GET_COMP 3 + +const UINT8 btm_pm_md_comp_matrix[BTM_PM_NUM_SET_MODES*BTM_PM_NUM_SET_MODES] = +{ + BTM_PM_GET_COMP, + BTM_PM_GET_MD2, + BTM_PM_GET_MD2, + + BTM_PM_GET_MD1, + BTM_PM_GET_COMP, + BTM_PM_GET_MD1, + + BTM_PM_GET_MD1, + BTM_PM_GET_MD2, + BTM_PM_GET_COMP +}; + +/* function prototype */ +static int btm_pm_find_acl_ind(BD_ADDR remote_bda); +static tBTM_STATUS btm_pm_snd_md_req( UINT8 pm_id, int link_ind, tBTM_PM_PWR_MD *p_mode ); + +/* +#ifdef BTM_PM_DEBUG +#undef BTM_PM_DEBUG +#define BTM_PM_DEBUG TRUE +#endif +*/ + +#if BTM_PM_DEBUG == TRUE +const char * btm_pm_state_str[] = +{ + "pm_active_state", + "pm_hold_state", + "pm_sniff_state", + "pm_park_state", + "pm_pend_state" +}; + +const char * btm_pm_event_str[] = +{ + "pm_set_mode_event", + "pm_hci_sts_event", + "pm_mod_chg_event", + "pm_update_event" +}; + +const char * btm_pm_action_str[] = +{ + "pm_set_mode_action", + "pm_update_db_action", + "pm_mod_chg_action", + "pm_hci_sts_action", + "pm_update_action" +}; +#endif + +/*****************************************************************************/ +/* P U B L I C F U N C T I O N S */ +/*****************************************************************************/ +/******************************************************************************* +** +** Function BTM_PmRegister +** +** Description register or deregister with power manager +** +** Returns BTM_SUCCESS if successful, +** BTM_NO_RESOURCES if no room to hold registration +** BTM_ILLEGAL_VALUE +** +*******************************************************************************/ +tBTM_STATUS BTM_PmRegister (UINT8 mask, UINT8 *p_pm_id, tBTM_PM_STATUS_CBACK *p_cb) +{ + int xx; + + /* de-register */ + if(mask & BTM_PM_DEREG) + { + if(*p_pm_id >= BTM_MAX_PM_RECORDS) + return BTM_ILLEGAL_VALUE; + btm_cb.pm_reg_db[*p_pm_id].mask = BTM_PM_REC_NOT_USED; + return BTM_SUCCESS; + } + + for(xx=0; xx<BTM_MAX_PM_RECORDS; xx++) + { + /* find an unused entry */ + if(btm_cb.pm_reg_db[xx].mask == BTM_PM_REC_NOT_USED) + { + /* if register for notification, should provide callback routine */ + if(mask & BTM_PM_REG_NOTIF) + { + if(p_cb == NULL) + return BTM_ILLEGAL_VALUE; + btm_cb.pm_reg_db[xx].cback = p_cb; + } + btm_cb.pm_reg_db[xx].mask = mask; + *p_pm_id = xx; + return BTM_SUCCESS; + } + } + + return BTM_NO_RESOURCES; +} + +/******************************************************************************* +** +** Function BTM_SetPowerMode +** +** Description store the mode in control block or +** alter ACL connection behavior. +** +** Returns BTM_SUCCESS if successful, +** BTM_UNKNOWN_ADDR if bd addr is not active or bad +** +*******************************************************************************/ +tBTM_STATUS BTM_SetPowerMode (UINT8 pm_id, BD_ADDR remote_bda, tBTM_PM_PWR_MD *p_mode) +{ + UINT8 *p_features; + int ind, acl_ind; + tBTM_PM_MCB *p_cb = NULL; /* per ACL link */ + tBTM_PM_MODE mode; + int temp_pm_id; + + + if(pm_id >= BTM_MAX_PM_RECORDS) + pm_id = BTM_PM_SET_ONLY_ID; + + if(p_mode == NULL) + return BTM_ILLEGAL_VALUE; + + BTM_TRACE_API3( "BTM_SetPowerMode: pm_id %d BDA: %08x mode:0x%x", pm_id, + (remote_bda[2]<<24)+(remote_bda[3]<<16)+(remote_bda[4]<<8)+remote_bda[5], p_mode->mode); + + /* take out the force bit */ + mode = p_mode->mode & ~BTM_PM_MD_FORCE; + + acl_ind = btm_pm_find_acl_ind(remote_bda); + if(acl_ind == MAX_L2CAP_LINKS) + return (BTM_UNKNOWN_ADDR); + + p_cb = &(btm_cb.pm_mode_db[acl_ind]); + + if(mode != BTM_PM_MD_ACTIVE) + { + /* check if the requested mode is supported */ + ind = mode - BTM_PM_MD_HOLD; /* make it base 0 */ + p_features = BTM_ReadLocalFeatures(); + if( !(p_features[ btm_pm_mode_off[ind] ] & btm_pm_mode_msk[ind] ) ) + return BTM_MODE_UNSUPPORTED; + } + + if(mode == p_cb->state) /* the requested mode is current mode */ + { + /* already in the requested mode and the current interval has less latency than the max */ + if( (mode == BTM_PM_MD_ACTIVE) || + ((p_mode->mode & BTM_PM_MD_FORCE) && (p_mode->max >= p_cb->interval) && (p_mode->min <= p_cb->interval)) || + ((p_mode->mode & BTM_PM_MD_FORCE)==0 && (p_mode->max >= p_cb->interval)) ) + { + BTM_TRACE_DEBUG4( "BTM_SetPowerMode: mode:0x%x interval %d max:%d, min:%d", p_mode->mode, p_cb->interval, p_mode->max, p_mode->min); + return BTM_SUCCESS; + } + } + + temp_pm_id = pm_id; + if(pm_id == BTM_PM_SET_ONLY_ID) + temp_pm_id = BTM_MAX_PM_RECORDS; + + /* update mode database */ + if( ((pm_id != BTM_PM_SET_ONLY_ID) && + (btm_cb.pm_reg_db[pm_id].mask & BTM_PM_REG_SET)) + || ((pm_id == BTM_PM_SET_ONLY_ID) && (btm_cb.pm_pend_link != MAX_L2CAP_LINKS)) ) + { +#if BTM_PM_DEBUG == TRUE + BTM_TRACE_DEBUG2( "BTM_SetPowerMode: Saving cmd acl_ind %d temp_pm_id %d", acl_ind,temp_pm_id); +#endif + /* Make sure mask is set to BTM_PM_REG_SET */ + btm_cb.pm_reg_db[temp_pm_id].mask |= BTM_PM_REG_SET; + *(&p_cb->req_mode[temp_pm_id]) = *((tBTM_PM_PWR_MD *)p_mode); + p_cb->chg_ind = TRUE; + } + +#if BTM_PM_DEBUG == TRUE + BTM_TRACE_DEBUG2( "btm_pm state:0x%x, pm_pend_link: %d", p_cb->state, btm_cb.pm_pend_link); +#endif + /* if mode == hold or pending, return */ + if( (p_cb->state == BTM_PM_STS_HOLD) || + (p_cb->state == BTM_PM_STS_PENDING) || + (btm_cb.pm_pend_link != MAX_L2CAP_LINKS) ) /* command pending */ + { + if(acl_ind != btm_cb.pm_pend_link) + { + /* set the stored mask */ + p_cb->state |= BTM_PM_STORED_MASK; + BTM_TRACE_DEBUG1( "btm_pm state stored:%d",acl_ind); + } + return BTM_CMD_STORED; + } + + + + return btm_pm_snd_md_req(pm_id, acl_ind, p_mode); +} + +/******************************************************************************* +** +** Function BTM_ReadPowerMode +** +** 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 +** +*******************************************************************************/ +tBTM_STATUS BTM_ReadPowerMode (BD_ADDR remote_bda, tBTM_PM_MODE *p_mode) +{ + int acl_ind; + + if( (acl_ind = btm_pm_find_acl_ind(remote_bda)) == MAX_L2CAP_LINKS) + return (BTM_UNKNOWN_ADDR); + + *p_mode = btm_cb.pm_mode_db[acl_ind].state; + return BTM_SUCCESS; +} + +/******************************************************************************* +** +** Function BTM_SetSsrParams +** +** Description This sends the given SSR parameters for the given ACL +** connection if it is in ACTIVE mode. +** +** Input Param remote_bda - device address of desired ACL connection +** max_lat - maximum latency (in 0.625ms)(0-0xFFFE) +** min_rmt_to - minimum remote timeout +** min_loc_to - minimum local timeout +** +** +** Returns BTM_SUCCESS if the HCI command is issued successful, +** BTM_UNKNOWN_ADDR if bd addr is not active or bad +** BTM_CMD_STORED if the command is stored +** +*******************************************************************************/ +tBTM_STATUS BTM_SetSsrParams (BD_ADDR remote_bda, UINT16 max_lat, + UINT16 min_rmt_to, UINT16 min_loc_to) +{ +#if (BTM_SSR_INCLUDED == TRUE) + int acl_ind; + tBTM_PM_MCB *p_cb; + + if( (acl_ind = btm_pm_find_acl_ind(remote_bda)) == MAX_L2CAP_LINKS) + return (BTM_UNKNOWN_ADDR); + + if(BTM_PM_STS_ACTIVE == btm_cb.pm_mode_db[acl_ind].state || + BTM_PM_STS_SNIFF == btm_cb.pm_mode_db[acl_ind].state) + { + if (btsnd_hcic_sniff_sub_rate(btm_cb.acl_db[acl_ind].hci_handle, max_lat, + min_rmt_to, min_loc_to)) + return BTM_SUCCESS; + else + return BTM_NO_RESOURCES; + } + p_cb = &btm_cb.pm_mode_db[acl_ind]; + p_cb->max_lat = max_lat; + p_cb->min_rmt_to = min_rmt_to; + p_cb->min_loc_to = min_loc_to; + return BTM_CMD_STORED; +#else + return BTM_ILLEGAL_ACTION; +#endif +} + +/******************************************************************************* +** +** Function btm_pm_reset +** +** Description as a part of the BTM reset process. +** +** Returns void +** +*******************************************************************************/ +void btm_pm_reset(void) +{ + int xx; + tBTM_PM_STATUS_CBACK *cb = NULL; + + /* clear the pending request for application */ + if( (btm_cb.pm_pend_id != BTM_PM_SET_ONLY_ID) && + (btm_cb.pm_reg_db[btm_cb.pm_pend_id].mask & BTM_PM_REG_NOTIF) ) + { + cb = btm_cb.pm_reg_db[btm_cb.pm_pend_id].cback; + } + + /* no command pending */ + btm_cb.pm_pend_link = MAX_L2CAP_LINKS; + + /* clear the register record */ + for(xx=0; xx<BTM_MAX_PM_RECORDS; xx++) + { + btm_cb.pm_reg_db[xx].mask = BTM_PM_REC_NOT_USED; + } + + if(cb != NULL) + (*cb)(btm_cb.acl_db[btm_cb.pm_pend_link].remote_addr, BTM_PM_STS_ERROR, BTM_DEV_RESET, 0); +} + +/******************************************************************************* +** +** Function btm_pm_sm_alloc +** +** Description This function initializes the control block of an ACL link. +** It is called when an ACL connection is created. +** +** Returns void +** +*******************************************************************************/ +void btm_pm_sm_alloc(UINT8 ind) +{ + tBTM_PM_MCB *p_db = &btm_cb.pm_mode_db[ind]; /* per ACL link */ + memset (p_db, 0, sizeof(tBTM_PM_MCB)); + p_db->state = BTM_PM_ST_ACTIVE; +#if BTM_PM_DEBUG == TRUE + BTM_TRACE_DEBUG2( "btm_pm_sm_alloc ind:%d st:%d", ind, p_db->state); +#endif +} + +/******************************************************************************* +** +** Function btm_pm_find_acl_ind +** +** Description This function initializes the control block of an ACL link. +** It is called when an ACL connection is created. +** +** Returns void +** +*******************************************************************************/ +static int btm_pm_find_acl_ind(BD_ADDR remote_bda) +{ + tACL_CONN *p = &btm_cb.acl_db[0]; + UINT8 xx; + + for (xx = 0; xx < MAX_L2CAP_LINKS; xx++, p++) + { + if ((p->in_use) && (!memcmp (p->remote_addr, remote_bda, BD_ADDR_LEN))) + { +#if BTM_PM_DEBUG == TRUE + BTM_TRACE_DEBUG2( "btm_pm_find_acl_ind ind:%d, st:%d", xx, btm_cb.pm_mode_db[xx].state); +#endif + break; + } + } + return xx; +} + +/******************************************************************************* +** +** Function btm_pm_compare_modes +** Description get the "more active" mode of the 2 +** Returns void +** +*******************************************************************************/ +static tBTM_PM_PWR_MD * btm_pm_compare_modes(tBTM_PM_PWR_MD *p_md1, tBTM_PM_PWR_MD *p_md2, tBTM_PM_PWR_MD *p_res) +{ + UINT8 res; + + if(p_md1 == NULL) + { + *p_res = *p_md2; + p_res->mode &= ~BTM_PM_MD_FORCE; + + return p_md2; + } + + if(p_md2->mode == BTM_PM_MD_ACTIVE || p_md1->mode == BTM_PM_MD_ACTIVE) + { + return NULL; + } + + /* check if force bit is involved */ + if(p_md1->mode & BTM_PM_MD_FORCE) + { + *p_res = *p_md1; + p_res->mode &= ~BTM_PM_MD_FORCE; + return p_res; + } + + if(p_md2->mode & BTM_PM_MD_FORCE) + { + *p_res = *p_md2; + p_res->mode &= ~BTM_PM_MD_FORCE; + return p_res; + } + + res = (p_md1->mode - 1) * BTM_PM_NUM_SET_MODES + (p_md2->mode - 1); + res = btm_pm_md_comp_matrix[res]; + switch(res) + { + case BTM_PM_GET_MD1: + *p_res = *p_md1; + return p_md1; + + case BTM_PM_GET_MD2: + *p_res = *p_md2; + return p_md2; + + case BTM_PM_GET_COMP: + p_res->mode = p_md1->mode; + /* min of the two */ + p_res->max = (p_md1->max < p_md2->max)? (p_md1->max) : (p_md2->max); + /* max of the two */ + p_res->min = (p_md1->min > p_md2->min)? (p_md1->min) : (p_md2->min); + + /* the intersection is NULL */ + if( p_res->max < p_res->min) + return NULL; + + if(p_res->mode == BTM_PM_MD_SNIFF) + { + /* max of the two */ + p_res->attempt = (p_md1->attempt > p_md2->attempt)? (p_md1->attempt) : (p_md2->attempt); + p_res->timeout = (p_md1->timeout > p_md2->timeout)? (p_md1->timeout) : (p_md2->timeout); + } + return p_res; + } + return NULL; +} + +/******************************************************************************* +** +** Function btm_pm_get_set_mode +** Description get the resulting mode from the registered parties, then compare it +** with the requested mode, if the command is from an unregistered party. +** Returns void +** +*******************************************************************************/ +static tBTM_PM_MODE btm_pm_get_set_mode(UINT8 pm_id, tBTM_PM_MCB *p_cb, tBTM_PM_PWR_MD *p_mode, tBTM_PM_PWR_MD *p_res) +{ + int xx, loop_max; + tBTM_PM_PWR_MD *p_md = NULL; + + if(p_mode != NULL && p_mode->mode & BTM_PM_MD_FORCE) + { + *p_res = *p_mode; + p_res->mode &= ~BTM_PM_MD_FORCE; + return p_res->mode; + } + + if(!p_mode) + loop_max = BTM_MAX_PM_RECORDS+1; + else + loop_max = BTM_MAX_PM_RECORDS; + + for( xx=0; xx<loop_max; xx++) + { + /* g through all the registered "set" parties */ + if(btm_cb.pm_reg_db[xx].mask & BTM_PM_REG_SET) + { + if(p_cb->req_mode[xx].mode == BTM_PM_MD_ACTIVE) + { + /* if at least one registered (SET) party says ACTIVE, stay active */ + return BTM_PM_MD_ACTIVE; + } + else + { + /* if registered parties give conflicting information, stay active */ + if( (btm_pm_compare_modes(p_md, &p_cb->req_mode[xx], p_res)) == NULL) + return BTM_PM_MD_ACTIVE; + p_md = p_res; + } + } + } + + /* if the resulting mode is NULL(nobody registers SET), use the requested mode */ + if(p_md == NULL) + { + if(p_mode) + *p_res = *((tBTM_PM_PWR_MD *)p_mode); + else /* p_mode is NULL when btm_pm_snd_md_req is called from btm_pm_proc_mode_change */ + return BTM_PM_MD_ACTIVE; + } + else + { + /* if the command is from unregistered party, + compare the resulting mode from registered party*/ + if( (pm_id == BTM_PM_SET_ONLY_ID) && + ((btm_pm_compare_modes(p_mode, p_md, p_res)) == NULL) ) + return BTM_PM_MD_ACTIVE; + } + + return p_res->mode; +} + +/******************************************************************************* +** +** Function btm_pm_snd_md_req +** Description get the resulting mode and send the resuest to host controller +** Returns tBTM_STATUS +**, BOOLEAN *p_chg_ind +*******************************************************************************/ +static tBTM_STATUS btm_pm_snd_md_req(UINT8 pm_id, int link_ind, tBTM_PM_PWR_MD *p_mode) +{ + tBTM_PM_PWR_MD md_res; + tBTM_PM_MODE mode; + tBTM_PM_MCB *p_cb = &btm_cb.pm_mode_db[link_ind]; + BOOLEAN chg_ind = FALSE; + + mode = btm_pm_get_set_mode(pm_id, p_cb, p_mode, &md_res); + md_res.mode = mode; + +#if BTM_PM_DEBUG == TRUE + BTM_TRACE_DEBUG2( "btm_pm_snd_md_req link_ind:%d, mode: %d", + link_ind, mode); +#endif + + if( p_cb->state == mode) + { + /* already in the resulting mode */ + if( (mode == BTM_PM_MD_ACTIVE) || + ((md_res.max >= p_cb->interval) && (md_res.min <= p_cb->interval)) ) + return BTM_CMD_STORED; + /* Otherwise, needs to wake, then sleep */ + chg_ind = TRUE; + } + p_cb->chg_ind = chg_ind; + + /* cannot go directly from current mode to resulting mode. */ + if( mode != BTM_PM_MD_ACTIVE && p_cb->state != BTM_PM_MD_ACTIVE) + p_cb->chg_ind = TRUE; /* needs to wake, then sleep */ + + if(p_cb->chg_ind == TRUE) /* needs to wake first */ + md_res.mode = BTM_PM_MD_ACTIVE; +#if (BTM_SSR_INCLUDED == TRUE) + else if(BTM_PM_MD_SNIFF == md_res.mode && p_cb->max_lat) + { + btsnd_hcic_sniff_sub_rate(btm_cb.acl_db[link_ind].hci_handle, p_cb->max_lat, + p_cb->min_rmt_to, p_cb->min_loc_to); + p_cb->max_lat = 0; + } +#endif + /* Default is failure */ + btm_cb.pm_pend_link = MAX_L2CAP_LINKS; + + /* send the appropriate HCI command */ + btm_cb.pm_pend_id = pm_id; + +#if BTM_PM_DEBUG == TRUE + BTM_TRACE_DEBUG2("btm_pm_snd_md_req state:0x%x, link_ind: %d", p_cb->state, link_ind); +#endif + switch(md_res.mode) + { + case BTM_PM_MD_ACTIVE: + switch(p_cb->state) + { + case BTM_PM_MD_SNIFF: + if (btsnd_hcic_exit_sniff_mode(btm_cb.acl_db[link_ind].hci_handle)) + { + btm_cb.pm_pend_link = link_ind; + } + break; + case BTM_PM_MD_PARK: + if (btsnd_hcic_exit_park_mode(btm_cb.acl_db[link_ind].hci_handle)) + { + btm_cb.pm_pend_link = link_ind; + } + break; + default: + /* Failure btm_cb.pm_pend_link = MAX_L2CAP_LINKS */ + break; + } + break; + + case BTM_PM_MD_HOLD: + if (btsnd_hcic_hold_mode (btm_cb.acl_db[link_ind].hci_handle, + md_res.max, md_res.min)) + { + btm_cb.pm_pend_link = link_ind; + } + break; + + case BTM_PM_MD_SNIFF: + if (btsnd_hcic_sniff_mode (btm_cb.acl_db[link_ind].hci_handle, + md_res.max, md_res.min, md_res.attempt, + md_res.timeout)) + { + btm_cb.pm_pend_link = link_ind; + } + break; + + case BTM_PM_MD_PARK: + if (btsnd_hcic_park_mode (btm_cb.acl_db[link_ind].hci_handle, + md_res.max, md_res.min)) + { + btm_cb.pm_pend_link = link_ind; + } + break; + default: + /* Failure btm_cb.pm_pend_link = MAX_L2CAP_LINKS */ + break; + } + + if(btm_cb.pm_pend_link == MAX_L2CAP_LINKS) + { + /* the command was not sent */ +#if BTM_PM_DEBUG == TRUE + BTM_TRACE_DEBUG1( "pm_pend_link: %d",btm_cb.pm_pend_link); +#endif + return (BTM_NO_RESOURCES); + } + + return BTM_CMD_STARTED; +} + +/******************************************************************************* +** +** Function btm_pm_check_stored +** +** Description This function is called when an HCI command status event occurs +** to check if there's any PM command issued while waiting for +** HCI command status. +** +** Returns none. +** +*******************************************************************************/ +static void btm_pm_check_stored(void) +{ + int xx; + for(xx=0; xx<MAX_L2CAP_LINKS; xx++) + { + if(btm_cb.pm_mode_db[xx].state & BTM_PM_STORED_MASK) + { + btm_cb.pm_mode_db[xx].state &= ~BTM_PM_STORED_MASK; + BTM_TRACE_DEBUG1( "btm_pm_check_stored :%d", xx); + btm_pm_snd_md_req(BTM_PM_SET_ONLY_ID, xx, NULL); + break; + } + } +} + + +/******************************************************************************* +** +** Function btm_pm_proc_cmd_status +** +** Description This function is called when an HCI command status event occurs +** for power manager related commands. +** +** Input Parms status - status of the event (HCI_SUCCESS if no errors) +** +** Returns none. +** +*******************************************************************************/ +void btm_pm_proc_cmd_status(UINT8 status) +{ + tBTM_PM_MCB *p_cb; + tBTM_PM_STATUS pm_status; + + if(btm_cb.pm_pend_link >= MAX_L2CAP_LINKS) + return; + + p_cb = &btm_cb.pm_mode_db[btm_cb.pm_pend_link]; + + if(status == HCI_SUCCESS) + { + p_cb->state = BTM_PM_ST_PENDING; + pm_status = BTM_PM_STS_PENDING; +#if BTM_PM_DEBUG == TRUE + BTM_TRACE_DEBUG1( "btm_pm_proc_cmd_status new state:0x%x", p_cb->state); +#endif + } + else /* the command was not successfull. Stay in the same state */ + { + pm_status = BTM_PM_STS_ERROR; + } + + /* notify the caller is appropriate */ + if( (btm_cb.pm_pend_id != BTM_PM_SET_ONLY_ID) && + (btm_cb.pm_reg_db[btm_cb.pm_pend_id].mask & BTM_PM_REG_NOTIF) ) + { + (*btm_cb.pm_reg_db[btm_cb.pm_pend_id].cback)(btm_cb.acl_db[btm_cb.pm_pend_link].remote_addr, pm_status, 0, status); + } + + /* no pending cmd now */ +#if BTM_PM_DEBUG == TRUE + BTM_TRACE_DEBUG3( "btm_pm_proc_cmd_status state:0x%x, pm_pend_link: %d(new: %d)", + p_cb->state, btm_cb.pm_pend_link, MAX_L2CAP_LINKS); +#endif + btm_cb.pm_pend_link = MAX_L2CAP_LINKS; + + btm_pm_check_stored(); +} + +/******************************************************************************* +** +** 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 none. +** +*******************************************************************************/ +void btm_pm_proc_mode_change (UINT8 hci_status, UINT16 hci_handle, UINT8 mode, UINT16 interval) +{ + tACL_CONN *p; + tBTM_PM_MCB *p_cb = NULL; + int xx, yy, zz; + tBTM_PM_STATE old_state; + tL2C_LCB *p_lcb; + + /* get the index to acl_db */ + if ((xx = btm_handle_to_acl_index(hci_handle)) >= MAX_L2CAP_LINKS) + return; + + p = &btm_cb.acl_db[xx]; + + /*** 2035 and 2045 work around: If mode is active and coming out of a SCO disconnect, restore packet types ***/ + if (mode == HCI_MODE_ACTIVE) + { + if(BTM_GetNumScoLinks() == 0) + { + if(p->restore_pkt_types) + { + BTM_TRACE_DEBUG3("btm mode change AFTER unsniffing; hci hdl 0x%x, types 0x%02x/0x%02x", + hci_handle, p->pkt_types_mask, p->restore_pkt_types); + p->pkt_types_mask = p->restore_pkt_types; + p->restore_pkt_types = 0; /* Only exists while SCO is active */ + btsnd_hcic_change_conn_type (p->hci_handle, p->pkt_types_mask); + } +#if (BTM_PM_SNIFF_SLOT_WORK_AROUND == TRUE) + else + { + BTM_TRACE_DEBUG2("btm mode change AFTER unsniffing; hci hdl 0x%x, types 0x%02x", + hci_handle, btm_cb.btm_acl_pkt_types_supported); + btm_set_packet_types (p, btm_cb.btm_acl_pkt_types_supported); + } +#endif + } +#if (BTM_PM_SNIFF_SLOT_WORK_AROUND == TRUE) + else + { + /* Mode changed from Sniff to Active while SCO is open. */ + /* Packet types of active mode, not sniff mode, should be used for ACL when SCO is closed. */ + p->restore_pkt_types = btm_cb.btm_acl_pkt_types_supported; + + /* Exclude packet types not supported by the peer */ + btm_acl_chk_peer_pkt_type_support (p, &p->restore_pkt_types); + } +#endif + } +#if (BTM_PM_SNIFF_SLOT_WORK_AROUND == TRUE) + else if (mode == HCI_MODE_SNIFF) + { + BTM_TRACE_DEBUG1("btm mode change to sniff; hci hdl 0x%x use single slot", + hci_handle); + btm_set_packet_types (p, (HCI_PKT_TYPES_MASK_DM1 | HCI_PKT_TYPES_MASK_DH1)); + } +#endif + + /* update control block */ + p_cb = &(btm_cb.pm_mode_db[xx]); + old_state = p_cb->state; + p_cb->state = mode; + p_cb->interval = interval; +#if BTM_PM_DEBUG == TRUE + BTM_TRACE_DEBUG2( "btm_pm_proc_mode_change new state:0x%x (old:0x%x)", p_cb->state, old_state); +#endif + + if ((p_cb->state == HCI_MODE_ACTIVE) && + ((p_lcb = l2cu_find_lcb_by_bd_addr (p->remote_addr)) != NULL)) + { + /* There might be any pending packets due to SNIFF or PENDING state */ + /* Trigger L2C to start transmission of the pending packets. */ + BTM_TRACE_DEBUG0 ("btm mode change to active; check l2c_link for outgoing packets"); + l2c_link_check_send_pkts (p_lcb, NULL, NULL); + + btu_stop_timer (&p_lcb->timer_entry); + } + + /* notify registered parties */ + for(yy=0; yy<=BTM_MAX_PM_RECORDS; yy++) + { + /* set req_mode HOLD mode->ACTIVE */ + if( (mode == BTM_PM_MD_ACTIVE) && (p_cb->req_mode[yy].mode == BTM_PM_MD_HOLD) ) + p_cb->req_mode[yy].mode = BTM_PM_MD_ACTIVE; + } + + /* new request has been made. - post a message to BTU task */ + if(old_state & BTM_PM_STORED_MASK) + { +#if BTM_PM_DEBUG == TRUE + BTM_TRACE_DEBUG1( "btm_pm_proc_mode_change: Sending stored req:%d", xx); +#endif + btm_pm_snd_md_req(BTM_PM_SET_ONLY_ID, xx, NULL); + } + else + { + for(zz=0; zz<MAX_L2CAP_LINKS; zz++) + { + if(btm_cb.pm_mode_db[zz].chg_ind == TRUE) + { +#if BTM_PM_DEBUG == TRUE + BTM_TRACE_DEBUG1( "btm_pm_proc_mode_change: Sending PM req :%d", zz); +#endif + btm_pm_snd_md_req(BTM_PM_SET_ONLY_ID, zz, NULL); + break; + } + } + } + + + /* notify registered parties */ + for(yy=0; yy<BTM_MAX_PM_RECORDS; yy++) + { + if(btm_cb.pm_reg_db[yy].mask & BTM_PM_REG_NOTIF) + { + (*btm_cb.pm_reg_db[yy].cback)( p->remote_addr, mode, interval, hci_status); + } + } + + /* 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); +} + +/******************************************************************************* +** +** Function btm_pm_proc_ssr_evt +** +** Description This function is called when an HCI sniff subrating event occurs. +** +** Returns none. +** +*******************************************************************************/ +#if (BTM_SSR_INCLUDED == TRUE) +void btm_pm_proc_ssr_evt (UINT8 *p, UINT16 evt_len) +{ + UINT8 status; + UINT16 handle; + UINT16 max_tx_lat, max_rx_lat; + int xx, yy; + tBTM_PM_MCB *p_cb; + tACL_CONN *p_acl=NULL; + UINT16 use_ssr = TRUE; + + STREAM_TO_UINT8 (status, p); + + STREAM_TO_UINT16 (handle, p); + /* get the index to acl_db */ + if ((xx = btm_handle_to_acl_index(handle)) >= MAX_L2CAP_LINKS) + return; + + STREAM_TO_UINT16 (max_tx_lat, p); + STREAM_TO_UINT16 (max_rx_lat, p); + p_cb = &(btm_cb.pm_mode_db[xx]); + + p_acl = &btm_cb.acl_db[xx]; + if(p_cb->interval == max_rx_lat) + { + /* using legacy sniff */ + use_ssr = FALSE; + } + + /* notify registered parties */ + for(yy=0; yy<BTM_MAX_PM_RECORDS; yy++) + { + if(btm_cb.pm_reg_db[yy].mask & BTM_PM_REG_NOTIF) + { + if( p_acl) + { + (*btm_cb.pm_reg_db[yy].cback)( p_acl->remote_addr, BTM_PM_STS_SSR, use_ssr, status); + } + } + } +} +#endif +#else /* BTM_PWR_MGR_INCLUDED == TRUE */ + +/******************************************************************************* +** +** Functions BTM_PmRegister, BTM_SetPowerMode, and BTM_ReadPowerMode +** +** Description Stubbed versions for BTM_PWR_MGR_INCLUDED = FALSE +** +** Returns BTM_MODE_UNSUPPORTED. +** +*******************************************************************************/ +tBTM_STATUS BTM_PmRegister (UINT8 mask, UINT8 *p_pm_id, tBTM_PM_STATUS_CBACK *p_cb) +{ + return BTM_MODE_UNSUPPORTED; +} + +tBTM_STATUS BTM_SetPowerMode (UINT8 pm_id, BD_ADDR remote_bda, tBTM_PM_PWR_MD *p_mode) +{ + return BTM_MODE_UNSUPPORTED; +} + +tBTM_STATUS BTM_ReadPowerMode (BD_ADDR remote_bda, tBTM_PM_MODE *p_mode) +{ + return BTM_MODE_UNSUPPORTED; +} + +#endif + +/******************************************************************************* +** +** Function BTM_IsPowerManagerOn +** +** Description This function is called to check if power manager is included. +** in the BTE version. +** +** Returns BTM_PWR_MGR_INCLUDED. +** +*******************************************************************************/ +BOOLEAN BTM_IsPowerManagerOn (void) +{ + return BTM_PWR_MGR_INCLUDED; +} diff --git a/stack/btm/btm_sco.c b/stack/btm/btm_sco.c new file mode 100644 index 0000000..394ac0a --- /dev/null +++ b/stack/btm/btm_sco.c @@ -0,0 +1,1741 @@ +/***************************************************************************** +** +** Name: btm_sco.c +** +** Description: This file contains functions that handle SCO connections. +** This includes operations such as connect, disconnect, +** change 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 <string.h> +#include "bt_types.h" +#include "bt_target.h" +#include "gki.h" +#include "bt_types.h" +#include "hcimsgs.h" +#include "btu.h" +#include "btm_api.h" +#include "btm_int.h" +#include "hcidefs.h" + +#if BTM_SCO_INCLUDED == TRUE + +/********************************************************************************/ +/* L O C A L D A T A D E F I N I T I O N S */ +/********************************************************************************/ + +#define SCO_ST_UNUSED 0 +#define SCO_ST_LISTENING 1 +#define SCO_ST_W4_CONN_RSP 2 +#define SCO_ST_CONNECTING 3 +#define SCO_ST_CONNECTED 4 +#define SCO_ST_DISCONNECTING 5 +#define SCO_ST_PEND_UNPARK 6 +#define SCO_ST_PEND_ROLECHANGE 7 + +/********************************************************************************/ +/* L O C A L F U N C T I O N P R O T O T Y P E S */ +/********************************************************************************/ + +static const tBTM_ESCO_PARAMS btm_esco_defaults = +{ + BTM_64KBITS_RATE, /* TX Bandwidth (64 kbits/sec) */ + BTM_64KBITS_RATE, /* RX Bandwidth (64 kbits/sec) */ + 0x000a, /* 10 ms (HS/HF can use EV3, 2-EV3, 3-EV3) */ + 0x0060, /* Inp Linear, Air CVSD, 2s Comp, 16bit */ + (BTM_SCO_PKT_TYPES_MASK_HV1 + /* Packet Types */ + BTM_SCO_PKT_TYPES_MASK_HV2 + + BTM_SCO_PKT_TYPES_MASK_HV3 + + BTM_SCO_PKT_TYPES_MASK_EV3 + + BTM_SCO_PKT_TYPES_MASK_EV4 + + BTM_SCO_PKT_TYPES_MASK_EV5), + BTM_ESCO_RETRANS_POWER /* Retransmission Effort (Power) */ +}; + +/******************************************************************************* +** +** Function btm_sco_flush_sco_data +** +** Description This function is called to flush the SCO data for this channel. +** +** Returns void +** +*******************************************************************************/ +void btm_sco_flush_sco_data(UINT16 sco_inx) +{ +#if BTM_SCO_HCI_INCLUDED == TRUE +#if (BTM_MAX_SCO_LINKS>0) + tSCO_CONN *p ; + BT_HDR *p_buf; + + if (sco_inx < BTM_MAX_SCO_LINKS) + { + p = &btm_cb.sco_cb.sco_db[sco_inx]; + while (p->xmit_data_q.p_first) + { + if ((p_buf = (BT_HDR *)GKI_dequeue (&p->xmit_data_q)) != NULL) + GKI_freebuf (p_buf); + } + } +#endif +#endif +} +/******************************************************************************* +** +** Function btm_sco_init +** +** Description This function is called at BTM startup to initialize +** +** Returns void +** +*******************************************************************************/ +void btm_sco_init (void) +{ +#if 0 /* cleared in btm_init; put back in if called from anywhere else! */ + memset (&btm_cb.sco_cb, 0, sizeof(tSCO_CB)); +#endif + /* Initialize nonzero defaults */ + btm_cb.sco_cb.sco_disc_reason = BTM_INVALID_SCO_DISC_REASON; + + btm_cb.sco_cb.def_esco_parms = btm_esco_defaults; /* Initialize with defaults */ + btm_cb.sco_cb.desired_sco_mode = BTM_DEFAULT_SCO_MODE; +} + +/******************************************************************************* +** +** Function btm_esco_conn_rsp +** +** Description This function is called upon receipt of an (e)SCO connection +** request event (BTM_ESCO_CONN_REQ_EVT) to accept or reject +** the request. Parameters used to negotiate eSCO links. +** If p_parms is NULL, then default values are used. +** If the link type of the incoming request is SCO, then only +** the tx_bw, max_latency, content format, and packet_types are +** valid. The hci_status parameter should be +** ([0x0] to accept, [0x0d..0x0f] to reject) +** +** Returns void +** +*******************************************************************************/ +static void btm_esco_conn_rsp (UINT16 sco_inx, UINT8 hci_status, BD_ADDR bda, + tBTM_ESCO_PARAMS *p_parms) +{ +#if (BTM_MAX_SCO_LINKS>0) + tSCO_CONN *p_sco = NULL; + tBTM_ESCO_PARAMS *p_setup; + UINT16 temp_pkt_types; + + if (sco_inx < BTM_MAX_SCO_LINKS) + p_sco = &btm_cb.sco_cb.sco_db[sco_inx]; + + /* Reject the connect request if refused by caller or wrong state */ + if (hci_status != HCI_SUCCESS || p_sco == NULL) + { + if (p_sco) + { + p_sco->state = (p_sco->state == SCO_ST_W4_CONN_RSP) ? SCO_ST_LISTENING + : SCO_ST_UNUSED; + } + + if (!btm_cb.sco_cb.esco_supported) + { + if (!btsnd_hcic_reject_conn (bda, hci_status)) + { + BTM_TRACE_ERROR0("Could not reject (e)SCO conn: No Buffer!!!"); + } + } + else + { + if (!btsnd_hcic_reject_esco_conn (bda, hci_status)) + { + BTM_TRACE_ERROR0("Could not reject (e)SCO conn: No Buffer!!!"); + } + } + } + else /* Connection is being accepted */ + { + p_sco->state = SCO_ST_CONNECTING; + if (btm_cb.devcb.local_version.hci_version >= HCI_PROTO_VERSION_1_2) + { + p_setup = &p_sco->esco.setup; + /* If parameters not specified use the default */ + if (p_parms) + *p_setup = *p_parms; + else /* Use the last setup passed thru BTM_SetEscoMode (or defaults) */ + { + *p_setup = btm_cb.sco_cb.def_esco_parms; + } + + temp_pkt_types = (p_setup->packet_types & + BTM_SCO_SUPPORTED_PKTS_MASK & + btm_cb.btm_sco_pkt_types_supported); + + /* Make sure at least one eSCO packet type is sent, else might confuse peer */ + /* Taking this out to confirm with BQB tests + ** Real application would like to include this though, as many devices + ** do not retry with SCO only if an eSCO connection fails. + if (!(temp_pkt_types & BTM_ESCO_LINK_ONLY_MASK)) + { + temp_pkt_types |= BTM_SCO_PKT_TYPES_MASK_EV3; + } + */ + /* If SCO request, remove eSCO packet types (conformance) */ + if (p_sco->esco.data.link_type == BTM_LINK_TYPE_SCO) + { + temp_pkt_types &= BTM_SCO_LINK_ONLY_MASK; + + if (btm_cb.devcb.local_version.hci_version >= HCI_PROTO_VERSION_2_0) + { + temp_pkt_types |= BTM_SCO_EXCEPTION_PKTS_MASK; + } + } + /* OR in any exception packet types if at least 2.0 version of spec */ + else if (btm_cb.devcb.local_version.hci_version >= HCI_PROTO_VERSION_2_0) + { + temp_pkt_types |= ((p_setup->packet_types & BTM_SCO_EXCEPTION_PKTS_MASK) | + (btm_cb.btm_sco_pkt_types_supported & BTM_SCO_EXCEPTION_PKTS_MASK)); + } + + if (btsnd_hcic_accept_esco_conn (bda, p_setup->tx_bw, p_setup->rx_bw, + p_setup->max_latency, p_setup->voice_contfmt, + p_setup->retrans_effort, temp_pkt_types)) + { + p_setup->packet_types = temp_pkt_types; + } + else + { + BTM_TRACE_ERROR0("Could not accept SCO conn: No Buffer!!!"); + } + } + else /* Controller is version 1.1 or earlier */ + { + btsnd_hcic_accept_conn (bda, 0); + } + } +#endif +} + + +#if BTM_SCO_HCI_INCLUDED == TRUE +/******************************************************************************* +** +** Function btm_sco_check_send_pkts +** +** Description This function is called to check if it can send packets +** to the Host Controller. +** +** Returns void +** +*******************************************************************************/ +void btm_sco_check_send_pkts (UINT16 sco_inx) +{ + BT_HDR *p_buf; + tSCO_CB *p_cb = &btm_cb.sco_cb; + tSCO_CONN *p_ccb = &p_cb->sco_db[sco_inx]; + + /* If there is data to send, send it now */ + while (p_ccb->xmit_data_q.p_first != NULL) + { + p_buf = NULL; + +#if BTM_SCO_HCI_DEBUG + BTM_TRACE_DEBUG1 ("btm: [%d] buf in xmit_data_q", p_ccb->xmit_data_q.count ); +#endif + p_buf = (BT_HDR *)GKI_dequeue (&p_ccb->xmit_data_q); + + HCI_SCO_DATA_TO_LOWER (p_buf); + } +} +#endif /* BTM_SCO_HCI_INCLUDED == TRUE */ + +/******************************************************************************* +** +** Function btm_route_sco_data +** +** Description Route received SCO data. +** +** Returns void +** +*******************************************************************************/ +void btm_route_sco_data(BT_HDR *p_msg) +{ +#if BTM_SCO_HCI_INCLUDED == TRUE + UINT16 sco_inx, handle; + UINT8 *p = (UINT8 *)(p_msg + 1) + p_msg->offset; + UINT8 pkt_size = 0; + UINT8 pkt_status = 0; + + /* Extract Packet_Status_Flag and handle */ + STREAM_TO_UINT16 (handle, p); + pkt_status = HCID_GET_EVENT(handle); + handle = HCID_GET_HANDLE (handle); + + STREAM_TO_UINT8 (pkt_size, p); + + if ((sco_inx = btm_find_scb_by_handle(handle)) != BTM_MAX_SCO_LINKS ) + { + /* send data callback */ + if (!btm_cb.sco_cb.p_data_cb ) + /* if no data callback registered, just free the buffer */ + GKI_freebuf (p_msg); + else + { + (*btm_cb.sco_cb.p_data_cb)(sco_inx, p_msg, (tBTM_SCO_DATA_FLAG) pkt_status); + } + } + else /* no mapping handle SCO connection is active, free the buffer */ + { + GKI_freebuf (p_msg); + } +#else + GKI_freebuf(p_msg); +#endif +} + + + +/******************************************************************************* +** +** Function BTM_WriteScoData +** +** Description This function write SCO data to a specified instance. The data +** to be written p_buf needs to carry an offset of +** HCI_SCO_PREAMBLE_SIZE bytes, and the data length can not +** exceed BTM_SCO_DATA_SIZE_MAX bytes, whose default value is set +** to 60 and is configurable. Data longer than the maximum bytes +** will be truncated. +** +** Returns BTM_SUCCESS: data write is successful +** BTM_ILLEGAL_VALUE: SCO data contains illegal offset value. +** BTM_SCO_BAD_LENGTH: SCO data length exceeds the max SCO packet +** size. +** BTM_NO_RESOURCES: no resources. +** BTM_UNKNOWN_ADDR: unknown SCO connection handle, or SCO is not +** routed via HCI. +** +** +*******************************************************************************/ +tBTM_STATUS BTM_WriteScoData (UINT16 sco_inx, BT_HDR *p_buf) +{ +#if (BTM_SCO_HCI_INCLUDED == TRUE) && (BTM_MAX_SCO_LINKS>0) + tSCO_CONN *p_ccb = &btm_cb.sco_cb.sco_db[sco_inx]; + UINT8 *p; + tBTM_STATUS status = BTM_SUCCESS; + + if (sco_inx < BTM_MAX_SCO_LINKS && btm_cb.sco_cb.p_data_cb && + p_ccb->state == SCO_ST_CONNECTED) + { + /* Ensure we have enough space in the buffer for the SCO and HCI headers */ + if (p_buf->offset < HCI_SCO_PREAMBLE_SIZE) + { + BTM_TRACE_ERROR1 ("BTM SCO - cannot send buffer, offset: %d", p_buf->offset); + GKI_freebuf (p_buf); + status = BTM_ILLEGAL_VALUE; + } + else /* write HCI header */ + { + /* Step back 3 bytes to add the headers */ + p_buf->offset -= HCI_SCO_PREAMBLE_SIZE; + /* Set the pointer to the beginning of the data */ + p = (UINT8 *)(p_buf + 1) + p_buf->offset; + /* add HCI handle */ + UINT16_TO_STREAM (p, p_ccb->hci_handle); + /* only sent the first BTM_SCO_DATA_SIZE_MAX bytes data if more than max, + and set warning status */ + if (p_buf->len > BTM_SCO_DATA_SIZE_MAX) + { + p_buf->len = BTM_SCO_DATA_SIZE_MAX; + status = BTM_SCO_BAD_LENGTH; + } + + UINT8_TO_STREAM (p, (UINT8)p_buf->len); + p_buf->len += HCI_SCO_PREAMBLE_SIZE; + + GKI_enqueue (&p_ccb->xmit_data_q, p_buf); + + btm_sco_check_send_pkts (sco_inx); + } + } + else + { + GKI_freebuf(p_buf); + + BTM_TRACE_WARNING2 ("BTM_WriteScoData, invalid sco index: %d at state [%d]", + sco_inx, btm_cb.sco_cb.sco_db[sco_inx].state); + status = BTM_UNKNOWN_ADDR; + } + + return (status); + +#else + return (BTM_NO_RESOURCES); +#endif +} + +#if (BTM_MAX_SCO_LINKS>0) +/******************************************************************************* +** +** Function btm_send_connect_request +** +** Description This function is called to respond to SCO connect indications +** +** Returns void +** +*******************************************************************************/ +static tBTM_STATUS btm_send_connect_request(UINT16 acl_handle, + tBTM_ESCO_PARAMS *p_setup) +{ + UINT16 temp_pkt_types; + UINT8 xx; + tACL_CONN *p_acl; + + /* Send connect request depending on version of spec */ + if (!btm_cb.sco_cb.esco_supported) + { + if (!btsnd_hcic_add_SCO_conn (acl_handle, BTM_ESCO_2_SCO(p_setup->packet_types))) + return (BTM_NO_RESOURCES); + } + else + { + temp_pkt_types = (p_setup->packet_types & BTM_SCO_SUPPORTED_PKTS_MASK & + btm_cb.btm_sco_pkt_types_supported); + + /* OR in any exception packet types if at least 2.0 version of spec */ + if (btm_cb.devcb.local_version.hci_version >= HCI_PROTO_VERSION_2_0) + { + temp_pkt_types |= ((p_setup->packet_types & BTM_SCO_EXCEPTION_PKTS_MASK) | + (btm_cb.btm_sco_pkt_types_supported & BTM_SCO_EXCEPTION_PKTS_MASK)); + } + + /* Finally, remove EDR eSCO if the remote device doesn't support it */ + /* UPF25: Only SCO was brought up in this case */ + btm_handle_to_acl_index(acl_handle); + if ((xx = btm_handle_to_acl_index(acl_handle)) < MAX_L2CAP_LINKS) + { + p_acl = &btm_cb.acl_db[xx]; + if (!HCI_EDR_ESCO_2MPS_SUPPORTED(p_acl->features)) + { + + BTM_TRACE_WARNING0("BTM Remote does not support 2-EDR eSCO"); + temp_pkt_types |= (HCI_ESCO_PKT_TYPES_MASK_NO_2_EV3 | + HCI_ESCO_PKT_TYPES_MASK_NO_2_EV5); + } + if (!HCI_EDR_ESCO_3MPS_SUPPORTED(p_acl->features)) + { + + BTM_TRACE_WARNING0("BTM Remote does not support 3-EDR eSCO"); + temp_pkt_types |= (HCI_ESCO_PKT_TYPES_MASK_NO_3_EV3 | + HCI_ESCO_PKT_TYPES_MASK_NO_3_EV5); + } + } + + + BTM_TRACE_API6(" txbw 0x%x, rxbw 0x%x, lat 0x%x, voice 0x%x, retrans 0x%02x, pkt 0x%04x", + p_setup->tx_bw, p_setup->rx_bw, + p_setup->max_latency, p_setup->voice_contfmt, + p_setup->retrans_effort, temp_pkt_types); + + if (!btsnd_hcic_setup_esco_conn(acl_handle, + p_setup->tx_bw, + p_setup->rx_bw, + p_setup->max_latency, + p_setup->voice_contfmt, + p_setup->retrans_effort, + temp_pkt_types)) + return (BTM_NO_RESOURCES); + else + p_setup->packet_types = temp_pkt_types; + } + + return (BTM_CMD_STARTED); +} +#endif + +/******************************************************************************* +** +** Function btm_set_sco_ind_cback +** +** Description This function is called to register for TCS SCO connect +** indications. +** +** Returns void +** +*******************************************************************************/ +void btm_set_sco_ind_cback( tBTM_SCO_IND_CBACK *sco_ind_cb ) +{ + btm_cb.sco_cb.app_sco_ind_cb = sco_ind_cb; +} + +/******************************************************************************* +** +** Function btm_accept_sco_link +** +** Description This function is called to respond to TCS SCO connect +** indications +** +** Returns void +** +*******************************************************************************/ +void btm_accept_sco_link(UINT16 sco_inx, tBTM_ESCO_PARAMS *p_setup, + tBTM_SCO_CB *p_conn_cb, tBTM_SCO_CB *p_disc_cb) +{ +#if (BTM_MAX_SCO_LINKS>0) + tSCO_CONN *p_sco; + + if (sco_inx >= BTM_MAX_SCO_LINKS) + { + BTM_TRACE_ERROR1("btm_accept_sco_link: Invalid sco_inx(%d)", sco_inx); + return; + } + + /* Link role is ignored in for this message */ + p_sco = &btm_cb.sco_cb.sco_db[sco_inx]; + p_sco->p_conn_cb = p_conn_cb; + p_sco->p_disc_cb = p_disc_cb; + p_sco->esco.data.link_type = BTM_LINK_TYPE_ESCO; /* Accept with all supported types */ + + BTM_TRACE_DEBUG1("TCS accept SCO: Packet Types 0x%04x", p_setup->packet_types); + + btm_esco_conn_rsp(sco_inx, HCI_SUCCESS, p_sco->esco.data.bd_addr, p_setup); +#else + btm_reject_sco_link(sco_inx); +#endif +} + +/******************************************************************************* +** +** Function btm_reject_sco_link +** +** Description This function is called to respond to SCO connect indications +** +** Returns void +** +*******************************************************************************/ +void btm_reject_sco_link( UINT16 sco_inx ) +{ + btm_esco_conn_rsp(sco_inx, HCI_ERR_HOST_REJECT_RESOURCES, + btm_cb.sco_cb.sco_db[sco_inx].esco.data.bd_addr, NULL); +} + +/******************************************************************************* +** +** Function BTM_CreateSco +** +** Description This function is called to create an SCO connection. If the +** "is_orig" flag is TRUE, the connection will be originated, +** otherwise BTM will wait for the other side to connect. +** +** NOTE: If BTM_IGNORE_SCO_PKT_TYPE is passed in the pkt_types +** parameter the default packet types is used. +** +** Returns BTM_UNKNOWN_ADDR if the ACL connection is not up +** BTM_BUSY if another SCO being set up to +** the same BD address +** BTM_NO_RESOURCES if the max SCO limit has been reached +** BTM_CMD_STARTED if the connection establishment is started. +** In this case, "*p_sco_inx" is filled in +** with the sco index used for the connection. +** +*******************************************************************************/ +tBTM_STATUS BTM_CreateSco (BD_ADDR remote_bda, BOOLEAN is_orig, UINT16 pkt_types, + UINT16 *p_sco_inx, tBTM_SCO_CB *p_conn_cb, + tBTM_SCO_CB *p_disc_cb) +{ +#if (BTM_MAX_SCO_LINKS > 0) + tBTM_ESCO_PARAMS *p_setup; + tSCO_CONN *p = &btm_cb.sco_cb.sco_db[0]; + UINT16 xx; + UINT16 acl_handle = 0; + UINT16 temp_pkt_types; + tACL_CONN *p_acl; + +#if (BTM_PWR_MGR_INCLUDED == TRUE) && (BTM_SCO_WAKE_PARKED_LINK == TRUE) + tBTM_PM_MODE md; + tBTM_PM_PWR_MD pm; +#else + UINT8 mode; +#endif + + *p_sco_inx = BTM_INVALID_SCO_INDEX; + + /* If originating, ensure that there is an ACL connection to the BD Address */ + if (is_orig) + { + if ((!remote_bda) || ((acl_handle = BTM_GetHCIConnHandle (remote_bda)) == 0xFFFF)) + return (BTM_UNKNOWN_ADDR); + } + + if (remote_bda) + { + /* If any SCO is being established to the remote BD address, refuse this */ + for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) + { + if (((p->state == SCO_ST_CONNECTING) || (p->state == SCO_ST_LISTENING) + || (p->state == SCO_ST_PEND_UNPARK)) + && (!memcmp (p->esco.data.bd_addr, remote_bda, BD_ADDR_LEN))) + { + return (BTM_BUSY); + } + } + } + else + { + /* Support only 1 wildcard BD address at a time */ + for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) + { + if ((p->state == SCO_ST_LISTENING) && (!p->rem_bd_known)) + return (BTM_BUSY); + } + } + + /* Now, try to find an unused control block, and kick off the SCO establishment */ + for (xx = 0, p = &btm_cb.sco_cb.sco_db[0]; xx < BTM_MAX_SCO_LINKS; xx++, p++) + { + if (p->state == SCO_ST_UNUSED) + { + if (remote_bda) + { + if (is_orig) + { + /* can not create SCO link if in park mode */ +#if (BTM_PWR_MGR_INCLUDED == TRUE) && (BTM_SCO_WAKE_PARKED_LINK == TRUE) + if(BTM_ReadPowerMode(remote_bda, &md) == BTM_SUCCESS) + { + if (md == BTM_PM_MD_PARK || md == BTM_PM_MD_SNIFF) + { +/* Coverity: FALSE-POSITIVE error from Coverity tool. Please do NOT remove following comment. */ +/* coverity[uninit_use_in_call] False-positive: setting the mode to BTM_PM_MD_ACTIVE only uses settings.mode + the other data members of tBTM_PM_PWR_MD are ignored +*/ + pm.mode = BTM_PM_MD_ACTIVE; + BTM_SetPowerMode(BTM_PM_SET_ONLY_ID, remote_bda, &pm); + p->state = SCO_ST_PEND_UNPARK; + } + } +#elif BTM_PWR_MGR_INCLUDED == TRUE + if( (BTM_ReadPowerMode(remote_bda, &mode) == BTM_SUCCESS) && (mode == BTM_PM_MD_PARK) ) + return (BTM_WRONG_MODE); +#else + if( (BTM_ReadAclMode(remote_bda, &mode) == BTM_SUCCESS) && (mode == BTM_ACL_MODE_PARK) ) + return (BTM_WRONG_MODE); +#endif + } + memcpy (p->esco.data.bd_addr, remote_bda, BD_ADDR_LEN); + p->rem_bd_known = TRUE; + } + else + p->rem_bd_known = FALSE; + + /* Link role is ignored in for this message */ + if (pkt_types == BTM_IGNORE_SCO_PKT_TYPE) + pkt_types = btm_cb.sco_cb.def_esco_parms.packet_types; + + p_setup = &p->esco.setup; + *p_setup = btm_cb.sco_cb.def_esco_parms; + p_setup->packet_types = (btm_cb.sco_cb.desired_sco_mode == BTM_LINK_TYPE_SCO) + ? (pkt_types & BTM_SCO_LINK_ONLY_MASK) : pkt_types; + + temp_pkt_types = (p_setup->packet_types & BTM_SCO_SUPPORTED_PKTS_MASK & + btm_cb.btm_sco_pkt_types_supported); + + /* OR in any exception packet types if at least 2.0 version of spec */ + if (btm_cb.devcb.local_version.hci_version >= HCI_PROTO_VERSION_2_0) + { + if (btm_cb.sco_cb.desired_sco_mode == HCI_LINK_TYPE_ESCO) + { + temp_pkt_types |= ((p_setup->packet_types & BTM_SCO_EXCEPTION_PKTS_MASK) | + (btm_cb.btm_sco_pkt_types_supported & BTM_SCO_EXCEPTION_PKTS_MASK)); + } + else /* Only using SCO packet types; turn off EDR also */ + { + temp_pkt_types |= BTM_SCO_EXCEPTION_PKTS_MASK; + } + } + + p_setup->packet_types = temp_pkt_types; + p->p_conn_cb = p_conn_cb; + p->p_disc_cb = p_disc_cb; + p->hci_handle = BTM_INVALID_HCI_HANDLE; + p->is_orig = is_orig; + + if( p->state != SCO_ST_PEND_UNPARK ) + { + if (is_orig) + { + /* If role change is in progress, do not proceed with SCO setup + * Wait till role change is complete */ + p_acl = btm_bda_to_acl(remote_bda); + if (p_acl && p_acl->switch_role_state != BTM_ACL_SWKEY_STATE_IDLE) + { + BTM_TRACE_API1("Role Change is in progress for ACL handle 0x%04x",acl_handle); + p->state = SCO_ST_PEND_ROLECHANGE; + + } + } + } + + if( p->state != SCO_ST_PEND_UNPARK && p->state != SCO_ST_PEND_ROLECHANGE ) + { + if (is_orig) + { + BTM_TRACE_API2("BTM_CreateSco -> (e)SCO Link for ACL handle 0x%04x, Desired Type %d", + acl_handle, btm_cb.sco_cb.desired_sco_mode); + + if ((btm_send_connect_request(acl_handle, p_setup)) != BTM_CMD_STARTED) + return (BTM_NO_RESOURCES); + + p->state = SCO_ST_CONNECTING; + } + else + p->state = SCO_ST_LISTENING; + } + + *p_sco_inx = xx; + + return (BTM_CMD_STARTED); + } + } + +#endif + /* If here, all SCO blocks in use */ + return (BTM_NO_RESOURCES); +} + +#if (BTM_PWR_MGR_INCLUDED == TRUE) && (BTM_SCO_WAKE_PARKED_LINK == TRUE) +/******************************************************************************* +** +** Function btm_sco_chk_pend_unpark +** +** Description This function is called by BTIF when there is a mode change +** event to see if there are SCO commands waiting for the unpark. +** +** Returns void +** +*******************************************************************************/ +void btm_sco_chk_pend_unpark (UINT8 hci_status, UINT16 hci_handle) +{ +#if (BTM_MAX_SCO_LINKS>0) + UINT16 xx; + UINT16 acl_handle; + tSCO_CONN *p = &btm_cb.sco_cb.sco_db[0]; + + for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) + { + if ((p->state == SCO_ST_PEND_UNPARK) && + ((acl_handle = BTM_GetHCIConnHandle (p->esco.data.bd_addr)) == hci_handle)) + + { + BTM_TRACE_API3("btm_sco_chk_pend_unpark -> (e)SCO Link for ACL handle 0x%04x, Desired Type %d, hci_status 0x%02x", + acl_handle, btm_cb.sco_cb.desired_sco_mode, hci_status); + + if ((btm_send_connect_request(acl_handle, &p->esco.setup)) == BTM_CMD_STARTED) + p->state = SCO_ST_CONNECTING; + } + } +#endif +} +#endif + +/******************************************************************************* +** +** Function btm_sco_chk_pend_rolechange +** +** Description This function is called by BTIF when there is a role change +** event to see if there are SCO commands waiting for the role change. +** +** Returns void +** +*******************************************************************************/ +void btm_sco_chk_pend_rolechange (UINT16 hci_handle) +{ +#if (BTM_MAX_SCO_LINKS>0) + UINT16 xx; + UINT16 acl_handle; + tSCO_CONN *p = &btm_cb.sco_cb.sco_db[0]; + + for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) + { + if ((p->state == SCO_ST_PEND_ROLECHANGE) && + ((acl_handle = BTM_GetHCIConnHandle (p->esco.data.bd_addr)) == hci_handle)) + + { + BTM_TRACE_API1("btm_sco_chk_pend_rolechange -> (e)SCO Link for ACL handle 0x%04x", acl_handle); + + if ((btm_send_connect_request(acl_handle, &p->esco.setup)) == BTM_CMD_STARTED) + p->state = SCO_ST_CONNECTING; + } + } +#endif +} + +/******************************************************************************* +** +** Function btm_sco_conn_req +** +** Description This function is called by BTIF when an SCO connection +** request is received from a remote. +** +** Returns void +** +*******************************************************************************/ +void btm_sco_conn_req (BD_ADDR bda, DEV_CLASS dev_class, UINT8 link_type) +{ +#if (BTM_MAX_SCO_LINKS>0) + tSCO_CB *p_sco = &btm_cb.sco_cb; + tSCO_CONN *p = &p_sco->sco_db[0]; + UINT16 xx; + tBTM_ESCO_CONN_REQ_EVT_DATA evt_data; + + for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) + { + /* + * If the sco state is in the SCO_ST_CONNECTING state, we still need + * to return accept sco to avoid race conditon for sco creation + */ + if (((p->state == SCO_ST_LISTENING && p->rem_bd_known) || p->state == SCO_ST_CONNECTING) + && (!memcmp (p->esco.data.bd_addr, bda, BD_ADDR_LEN))) + { + /* If this guy was a wildcard, he is not one any more */ + p->rem_bd_known = TRUE; + p->esco.data.link_type = link_type; + p->state = SCO_ST_W4_CONN_RSP; + memcpy (p->esco.data.bd_addr, bda, BD_ADDR_LEN); + + /* If no callback, auto-accept the connection if packet types match */ + if (!p->esco.p_esco_cback) + { + /* If requesting eSCO reject if default parameters are SCO only */ + if ((link_type == BTM_LINK_TYPE_ESCO + && !(p_sco->def_esco_parms.packet_types & BTM_ESCO_LINK_ONLY_MASK) + && ((p_sco->def_esco_parms.packet_types & BTM_SCO_EXCEPTION_PKTS_MASK) + == BTM_SCO_EXCEPTION_PKTS_MASK)) + + /* Reject request if SCO is desired but no SCO packets delected */ + || (link_type == BTM_LINK_TYPE_SCO + && !(p_sco->def_esco_parms.packet_types & BTM_SCO_LINK_ONLY_MASK))) + { + btm_esco_conn_rsp(xx, HCI_ERR_HOST_REJECT_RESOURCES, bda, NULL); + } + else /* Accept the request */ + { + btm_esco_conn_rsp(xx, HCI_SUCCESS, bda, NULL); + } + } + else /* Notify upper layer of connect indication */ + { + memcpy(evt_data.bd_addr, bda, BD_ADDR_LEN); + memcpy(evt_data.dev_class, dev_class, DEV_CLASS_LEN); + evt_data.link_type = link_type; + evt_data.sco_inx = xx; + p->esco.p_esco_cback(BTM_ESCO_CONN_REQ_EVT, (tBTM_ESCO_EVT_DATA *)&evt_data); + } + + return; + } + } + + /* TCS usage */ + if (btm_cb.sco_cb.app_sco_ind_cb) + { + /* Now, try to find an unused control block */ + for (xx = 0, p = &btm_cb.sco_cb.sco_db[0]; xx < BTM_MAX_SCO_LINKS; xx++, p++) + { + if (p->state == SCO_ST_UNUSED) + { + p->is_orig = FALSE; + p->state = SCO_ST_LISTENING; + + p->esco.data.link_type = link_type; + memcpy (p->esco.data.bd_addr, bda, BD_ADDR_LEN); + p->rem_bd_known = TRUE; + break; + } + } + if( xx < BTM_MAX_SCO_LINKS) + { + btm_cb.sco_cb.app_sco_ind_cb(xx); + return; + } + } + +#endif + /* If here, no one wants the SCO connection. Reject it */ + BTM_TRACE_WARNING0("btm_sco_conn_req: No one wants this SCO connection; rejecting it"); + btm_esco_conn_rsp(BTM_MAX_SCO_LINKS, HCI_ERR_HOST_REJECT_RESOURCES, bda, NULL); +} + +/******************************************************************************* +** +** Function btm_sco_connected +** +** Description This function is called by BTIF when an (e)SCO connection +** is connected. +** +** Returns void +** +*******************************************************************************/ +void btm_sco_connected (UINT8 hci_status, BD_ADDR bda, UINT16 hci_handle, + tBTM_ESCO_DATA *p_esco_data) +{ +#if (BTM_MAX_SCO_LINKS>0) + tSCO_CONN *p = &btm_cb.sco_cb.sco_db[0]; + UINT16 xx; + BOOLEAN spt = FALSE; + tBTM_CHG_ESCO_PARAMS parms; +#endif + + btm_cb.sco_cb.sco_disc_reason = hci_status; + +#if (BTM_MAX_SCO_LINKS>0) + for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) + { + if (((p->state == SCO_ST_CONNECTING) || + (p->state == SCO_ST_LISTENING) || + (p->state == SCO_ST_W4_CONN_RSP)) + && (p->rem_bd_known) + && (!bda || !memcmp (p->esco.data.bd_addr, bda, BD_ADDR_LEN))) + { + if (hci_status != HCI_SUCCESS) + { + /* Report the error if originator, otherwise remain in Listen mode */ + if (p->is_orig) + { + /* If role switch is pending, we need try again after role switch is complete */ + if(hci_status == HCI_ERR_ROLE_SWITCH_PENDING) + { + BTM_TRACE_API1("Role Change pending for HCI handle 0x%04x",hci_handle); + p->state = SCO_ST_PEND_ROLECHANGE; + } + /* avoid calling disconnect callback because of sco creation race */ + else if (hci_status != HCI_ERR_LMP_ERR_TRANS_COLLISION) + { + p->state = SCO_ST_UNUSED; + (*p->p_disc_cb)(xx); + } + } + else + { + /* Notify the upper layer that incoming sco connection has failed. */ + if (p->state == SCO_ST_CONNECTING) + { + p->state = SCO_ST_UNUSED; + (*p->p_disc_cb)(xx); + } + else + p->state = SCO_ST_LISTENING; + } + + return; + } + + if (p->state == SCO_ST_LISTENING) + spt = TRUE; + + p->state = SCO_ST_CONNECTED; + p->hci_handle = hci_handle; + + if (!btm_cb.sco_cb.esco_supported) + { + p->esco.data.link_type = BTM_LINK_TYPE_SCO; + if (spt) + { + parms.packet_types = p->esco.setup.packet_types; + /* Keep the other parameters the same for SCO */ + parms.max_latency = p->esco.setup.max_latency; + parms.retrans_effort = p->esco.setup.retrans_effort; + + BTM_ChangeEScoLinkParms(xx, &parms); + } + } + else + { + if (p_esco_data) + p->esco.data = *p_esco_data; + } + + (*p->p_conn_cb)(xx); + + return; + } + } +#endif +} + + +/******************************************************************************* +** +** Function btm_find_scb_by_handle +** +** Description Look through all active SCO connection for a match based on the +** HCI handle. +** +** Returns index to matched SCO connection CB, or BTM_MAX_SCO_LINKS if +** no match. +** +*******************************************************************************/ +UINT16 btm_find_scb_by_handle (UINT16 handle) +{ + int xx; + tSCO_CONN *p = &btm_cb.sco_cb.sco_db[0]; + + for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) + { + if ((p->state == SCO_ST_CONNECTED) && (p->hci_handle == handle)) + { + return (xx); + } + } + + /* If here, no match found */ + return (xx); +} + +/******************************************************************************* +** +** Function BTM_RemoveSco +** +** Description This function is called to remove a specific SCO connection. +** +** Returns status of the operation +** +*******************************************************************************/ +tBTM_STATUS BTM_RemoveSco (UINT16 sco_inx) +{ +#if (BTM_MAX_SCO_LINKS>0) + tSCO_CONN *p = &btm_cb.sco_cb.sco_db[sco_inx]; + UINT16 tempstate; + + /* Validity check */ + if ((sco_inx >= BTM_MAX_SCO_LINKS) || (p->state == SCO_ST_UNUSED)) + return (BTM_UNKNOWN_ADDR); + + /* If no HCI handle, simply drop the connection and return */ + if (p->hci_handle == BTM_INVALID_HCI_HANDLE || p->state == SCO_ST_PEND_UNPARK) + { + p->hci_handle = BTM_INVALID_HCI_HANDLE; + p->state = SCO_ST_UNUSED; + p->esco.p_esco_cback = NULL; /* Deregister the eSCO event callback */ + return (BTM_SUCCESS); + } + + tempstate = p->state; + p->state = SCO_ST_DISCONNECTING; + + if (!btsnd_hcic_disconnect (p->hci_handle, HCI_ERR_PEER_USER)) + { + p->state = tempstate; + return (BTM_NO_RESOURCES); + } + + return (BTM_CMD_STARTED); +#else + return (BTM_NO_RESOURCES); +#endif +} + +/******************************************************************************* +** +** Function btm_remove_sco_links +** +** Description This function is called to remove all sco links for an ACL link. +** +** Returns void +** +*******************************************************************************/ +void btm_remove_sco_links (BD_ADDR bda) +{ +#if (BTM_MAX_SCO_LINKS>0) + tSCO_CONN *p = &btm_cb.sco_cb.sco_db[0]; + UINT16 xx; + + for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) + { + if (p->rem_bd_known && (!memcmp (p->esco.data.bd_addr, bda, BD_ADDR_LEN))) + { + BTM_RemoveSco(xx); + } + } +#endif +} + +/******************************************************************************* +** +** Function btm_sco_removed +** +** Description This function is called by BTIF when an SCO connection +** is removed. +** +** Returns void +** +*******************************************************************************/ +void btm_sco_removed (UINT16 hci_handle, UINT8 reason) +{ +#if (BTM_MAX_SCO_LINKS>0) + tSCO_CONN *p = &btm_cb.sco_cb.sco_db[0]; + UINT16 xx; +#endif + + btm_cb.sco_cb.sco_disc_reason = reason; + +#if (BTM_MAX_SCO_LINKS>0) + p = &btm_cb.sco_cb.sco_db[0]; + for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) + { + if ((p->state != SCO_ST_UNUSED) && (p->state != SCO_ST_LISTENING) && (p->hci_handle == hci_handle)) + { + btm_sco_flush_sco_data(xx); + + p->state = SCO_ST_UNUSED; + p->hci_handle = BTM_INVALID_HCI_HANDLE; + p->rem_bd_known = FALSE; + p->esco.p_esco_cback = NULL; /* Deregister eSCO callback */ + (*p->p_disc_cb)(xx); + + return; + } + } +#endif +} + + +/******************************************************************************* +** +** Function btm_sco_acl_removed +** +** Description This function is called when an ACL connection is +** removed. If the BD address is NULL, it is assumed that +** the local device is down, and all SCO links are removed. +** If a specific BD address is passed, only SCO connections +** to that BD address are removed. +** +** Returns void +** +*******************************************************************************/ +void btm_sco_acl_removed (BD_ADDR bda) +{ +#if (BTM_MAX_SCO_LINKS>0) + tSCO_CONN *p = &btm_cb.sco_cb.sco_db[0]; + UINT16 xx; + + for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) + { + if (p->state != SCO_ST_UNUSED) + { + if ((!bda) || (!memcmp (p->esco.data.bd_addr, bda, BD_ADDR_LEN) && p->rem_bd_known)) + { + btm_sco_flush_sco_data(xx); + + p->state = SCO_ST_UNUSED; + p->esco.p_esco_cback = NULL; /* Deregister eSCO callback */ + (*p->p_disc_cb)(xx); + } + } + } +#endif +} + + +/******************************************************************************* +** +** Function BTM_SetScoPacketTypes +** +** Description This function is called to set the packet types used for +** a specific SCO connection, +** +** Parameters pkt_types - One or more of the following +** BTM_SCO_PKT_TYPES_MASK_HV1 +** BTM_SCO_PKT_TYPES_MASK_HV2 +** BTM_SCO_PKT_TYPES_MASK_HV3 +** BTM_SCO_PKT_TYPES_MASK_EV3 +** BTM_SCO_PKT_TYPES_MASK_EV4 +** BTM_SCO_PKT_TYPES_MASK_EV5 +** BTM_SCO_PKT_TYPES_MASK_NO_2_EV3 +** BTM_SCO_PKT_TYPES_MASK_NO_3_EV3 +** BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 +** BTM_SCO_PKT_TYPES_MASK_NO_3_EV5 +** +** BTM_SCO_LINK_ALL_MASK - enables all supported types +** +** Returns status of the operation +** +*******************************************************************************/ +tBTM_STATUS BTM_SetScoPacketTypes (UINT16 sco_inx, UINT16 pkt_types) +{ +#if (BTM_MAX_SCO_LINKS>0) + tBTM_CHG_ESCO_PARAMS parms; + tSCO_CONN *p; + + /* Validity check */ + if (sco_inx >= BTM_MAX_SCO_LINKS) + return (BTM_UNKNOWN_ADDR); + + p = &btm_cb.sco_cb.sco_db[sco_inx]; + parms.packet_types = pkt_types; + + /* Keep the other parameters the same for SCO */ + parms.max_latency = p->esco.setup.max_latency; + parms.retrans_effort = p->esco.setup.retrans_effort; + + return (BTM_ChangeEScoLinkParms(sco_inx, &parms)); +#else + return (BTM_UNKNOWN_ADDR); +#endif +} + + +/******************************************************************************* +** +** Function BTM_ReadScoPacketTypes +** +** Description This function is read the packet types used for a specific +** SCO connection. +** +** Returns Packet types supported for the connection +** One or more of the following (bitmask): +** BTM_SCO_PKT_TYPES_MASK_HV1 +** BTM_SCO_PKT_TYPES_MASK_HV2 +** BTM_SCO_PKT_TYPES_MASK_HV3 +** BTM_SCO_PKT_TYPES_MASK_EV3 +** BTM_SCO_PKT_TYPES_MASK_EV4 +** BTM_SCO_PKT_TYPES_MASK_EV5 +** BTM_SCO_PKT_TYPES_MASK_NO_2_EV3 +** BTM_SCO_PKT_TYPES_MASK_NO_3_EV3 +** BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 +** BTM_SCO_PKT_TYPES_MASK_NO_3_EV5 +** +*******************************************************************************/ +UINT16 BTM_ReadScoPacketTypes (UINT16 sco_inx) +{ +#if (BTM_MAX_SCO_LINKS>0) + tSCO_CONN *p = &btm_cb.sco_cb.sco_db[sco_inx]; + + /* Validity check */ + if ((sco_inx < BTM_MAX_SCO_LINKS) && (p->state == SCO_ST_CONNECTED)) + return (p->esco.setup.packet_types); + else + return (0); +#else + return (0); +#endif +} + +/******************************************************************************* +** +** Function BTM_ReadScoDiscReason +** +** Description This function is returns the reason why an (e)SCO connection +** has been removed. It contains the value until read, or until +** another (e)SCO connection has disconnected. +** +** Returns HCI reason or BTM_INVALID_SCO_DISC_REASON if not set. +** +*******************************************************************************/ +UINT16 BTM_ReadScoDiscReason (void) +{ + UINT16 res = btm_cb.sco_cb.sco_disc_reason; + btm_cb.sco_cb.sco_disc_reason = BTM_INVALID_SCO_DISC_REASON; + return (res); +} + +/******************************************************************************* +** +** Function BTM_ReadDeviceScoPacketTypes +** +** Description This function is read the SCO packet types that +** the device supports. +** +** Returns Packet types supported by the device. +** One or more of the following (bitmask): +** BTM_SCO_PKT_TYPES_MASK_HV1 +** BTM_SCO_PKT_TYPES_MASK_HV2 +** BTM_SCO_PKT_TYPES_MASK_HV3 +** BTM_SCO_PKT_TYPES_MASK_EV3 +** BTM_SCO_PKT_TYPES_MASK_EV4 +** BTM_SCO_PKT_TYPES_MASK_EV5 +** BTM_SCO_PKT_TYPES_MASK_NO_2_EV3 +** BTM_SCO_PKT_TYPES_MASK_NO_3_EV3 +** BTM_SCO_PKT_TYPES_MASK_NO_2_EV5 +** BTM_SCO_PKT_TYPES_MASK_NO_3_EV5 +** +*******************************************************************************/ +UINT16 BTM_ReadDeviceScoPacketTypes (void) +{ + return (btm_cb.btm_sco_pkt_types_supported); +} + +/******************************************************************************* +** +** Function BTM_ReadScoHandle +** +** Description This function is used to read the HCI handle used for a specific +** SCO connection, +** +** Returns handle for the connection, or 0xFFFF if invalid SCO index. +** +*******************************************************************************/ +UINT16 BTM_ReadScoHandle (UINT16 sco_inx) +{ +#if (BTM_MAX_SCO_LINKS>0) + tSCO_CONN *p = &btm_cb.sco_cb.sco_db[sco_inx]; + + /* Validity check */ + if ((sco_inx < BTM_MAX_SCO_LINKS) && (p->state == SCO_ST_CONNECTED)) + return (p->hci_handle); + else + return (BTM_INVALID_HCI_HANDLE); +#else + return (BTM_INVALID_HCI_HANDLE); +#endif +} + +/******************************************************************************* +** +** Function BTM_ReadScoBdAddr +** +** Description This function is read the remote BD Address for a specific +** SCO connection, +** +** Returns pointer to BD address or NULL if not known +** +*******************************************************************************/ +UINT8 *BTM_ReadScoBdAddr (UINT16 sco_inx) +{ +#if (BTM_MAX_SCO_LINKS>0) + tSCO_CONN *p = &btm_cb.sco_cb.sco_db[sco_inx]; + + /* Validity check */ + if ((sco_inx < BTM_MAX_SCO_LINKS) && (p->rem_bd_known)) + return (p->esco.data.bd_addr); + else + return (NULL); +#else + return (NULL); +#endif +} + +/******************************************************************************* +** +** Function BTM_SetEScoMode +** +** Description This function sets up the negotiated parameters for SCO or +** eSCO, and sets as the default mode used for outgoing calls to +** BTM_CreateSco. It does not change any currently active (e)SCO links. +** Note: Incoming (e)SCO connections will always use packet types +** supported by the controller. If eSCO is not desired the +** feature should be disabled in the controller's feature mask. +** +** Returns BTM_SUCCESS if the successful. +** BTM_BUSY if there are one or more active (e)SCO links. +** +*******************************************************************************/ +tBTM_STATUS BTM_SetEScoMode (tBTM_SCO_TYPE sco_mode, tBTM_ESCO_PARAMS *p_parms) +{ + tSCO_CB *p_esco = &btm_cb.sco_cb; + tBTM_ESCO_PARAMS *p_def = &p_esco->def_esco_parms; + + if (p_esco->esco_supported) + { + if (p_parms) + { + if (sco_mode == BTM_LINK_TYPE_ESCO) + *p_def = *p_parms; /* Save as the default parameters */ + else /* Load only the SCO packet types */ + { + p_def->packet_types = p_parms->packet_types; + p_def->tx_bw = BTM_64KBITS_RATE; + p_def->rx_bw = BTM_64KBITS_RATE; + p_def->max_latency = 0x000a; + p_def->voice_contfmt = 0x0060; + p_def->retrans_effort = 0; + + /* OR in any exception packet types if at least 2.0 version of spec */ + if (btm_cb.devcb.local_version.hci_version >= HCI_PROTO_VERSION_2_0) + { + p_def->packet_types |= BTM_SCO_EXCEPTION_PKTS_MASK; + } + } + } + p_esco->desired_sco_mode = sco_mode; + BTM_TRACE_API1("BTM_SetEScoMode -> mode %d", sco_mode); + } + else + { + p_esco->desired_sco_mode = BTM_LINK_TYPE_SCO; + p_def->packet_types &= BTM_SCO_LINK_ONLY_MASK; + p_def->retrans_effort = 0; + BTM_TRACE_API0("BTM_SetEScoMode -> mode SCO (eSCO not supported)"); + } + + BTM_TRACE_DEBUG6(" txbw 0x%08x, rxbw 0x%08x, max_lat 0x%04x, voice 0x%04x, pkt 0x%04x, rtx effort 0x%02x", + p_def->tx_bw, p_def->rx_bw, p_def->max_latency, + p_def->voice_contfmt, p_def->packet_types, + p_def->retrans_effort); + + return (BTM_SUCCESS); +} + + + +/******************************************************************************* +** +** Function BTM_RegForEScoEvts +** +** Description This function registers a SCO event callback with the +** specified instance. It should be used to received +** connection indication events and change of link parameter +** events. +** +** Returns BTM_SUCCESS if the successful. +** BTM_ILLEGAL_VALUE if there is an illegal sco_inx +** BTM_MODE_UNSUPPORTED if controller version is not BT1.2 or +** later or does not support eSCO. +** +*******************************************************************************/ +tBTM_STATUS BTM_RegForEScoEvts (UINT16 sco_inx, tBTM_ESCO_CBACK *p_esco_cback) +{ +#if (BTM_MAX_SCO_LINKS>0) + if (!btm_cb.sco_cb.esco_supported) + { + btm_cb.sco_cb.sco_db[sco_inx].esco.p_esco_cback = NULL; + return (BTM_MODE_UNSUPPORTED); + } + + if (sco_inx < BTM_MAX_SCO_LINKS && + btm_cb.sco_cb.sco_db[sco_inx].state != SCO_ST_UNUSED) + { + btm_cb.sco_cb.sco_db[sco_inx].esco.p_esco_cback = p_esco_cback; + return (BTM_SUCCESS); + } + return (BTM_ILLEGAL_VALUE); +#else + return (BTM_MODE_UNSUPPORTED); +#endif +} + +/******************************************************************************* +** +** Function BTM_ReadEScoLinkParms +** +** Description This function returns the current eSCO link parameters for +** the specified handle. This can be called anytime a connection +** is active, but is typically called after receiving the SCO +** opened callback. +** +** Note: If called over a 1.1 controller, only the packet types +** field has meaning. +** +** Returns BTM_SUCCESS if returned data is valid connection. +** BTM_WRONG_MODE if no connection with a peer device or bad sco_inx. +** +*******************************************************************************/ +tBTM_STATUS BTM_ReadEScoLinkParms (UINT16 sco_inx, tBTM_ESCO_DATA *p_parms) +{ +#if (BTM_MAX_SCO_LINKS>0) + BTM_TRACE_API1("BTM_ReadEScoLinkParms -> sco_inx 0x%04x", sco_inx); + + if (sco_inx < BTM_MAX_SCO_LINKS && + btm_cb.sco_cb.sco_db[sco_inx].state >= SCO_ST_CONNECTED) + { + *p_parms = btm_cb.sco_cb.sco_db[sco_inx].esco.data; + return (BTM_SUCCESS); + } +#endif + + memset(p_parms, 0, sizeof(tBTM_ESCO_DATA)); + return (BTM_WRONG_MODE); +} + +/******************************************************************************* +** +** Function BTM_ChangeEScoLinkParms +** +** Description This function requests renegotiation of the parameters on +** the current eSCO Link. If any of the changes are accepted +** by the controllers, the BTM_ESCO_CHG_EVT event is sent in +** the tBTM_ESCO_CBACK function with the current settings of +** the link. The callback is registered through the call to +** BTM_SetEScoMode. +** +** Note: If called over a SCO link (including 1.1 controller), +** a change packet type request is sent out instead. +** +** Returns BTM_CMD_STARTED if command is successfully initiated. +** BTM_NO_RESOURCES - not enough resources to initiate command. +** BTM_WRONG_MODE if no connection with a peer device or bad sco_inx. +** +*******************************************************************************/ +tBTM_STATUS BTM_ChangeEScoLinkParms (UINT16 sco_inx, tBTM_CHG_ESCO_PARAMS *p_parms) +{ +#if (BTM_MAX_SCO_LINKS>0) + tBTM_ESCO_PARAMS *p_setup; + tSCO_CONN *p_sco; + UINT16 temp_pkt_types; + + /* Make sure sco handle is valid and on an active link */ + if (sco_inx >= BTM_MAX_SCO_LINKS || + btm_cb.sco_cb.sco_db[sco_inx].state != SCO_ST_CONNECTED) + return (BTM_WRONG_MODE); + + p_sco = &btm_cb.sco_cb.sco_db[sco_inx]; + p_setup = &p_sco->esco.setup; + + /* If SCO connection OR eSCO not supported just send change packet types */ + if (p_sco->esco.data.link_type == BTM_LINK_TYPE_SCO || + !btm_cb.sco_cb.esco_supported) + { + p_setup->packet_types = p_parms->packet_types & + (btm_cb.btm_sco_pkt_types_supported & BTM_SCO_LINK_ONLY_MASK); + + + BTM_TRACE_API2("BTM_ChangeEScoLinkParms -> SCO Link for handle 0x%04x, pkt 0x%04x", + p_sco->hci_handle, p_setup->packet_types); + + if (!btsnd_hcic_change_conn_type (p_sco->hci_handle, + BTM_ESCO_2_SCO(p_setup->packet_types))) + return (BTM_NO_RESOURCES); + } + else + { + temp_pkt_types = (p_parms->packet_types & BTM_SCO_SUPPORTED_PKTS_MASK & + btm_cb.btm_sco_pkt_types_supported); + + /* OR in any exception packet types if at least 2.0 version of spec */ + if (btm_cb.devcb.local_version.hci_version >= HCI_PROTO_VERSION_2_0) + { + temp_pkt_types |= ((p_parms->packet_types & BTM_SCO_EXCEPTION_PKTS_MASK) | + (btm_cb.btm_sco_pkt_types_supported & BTM_SCO_EXCEPTION_PKTS_MASK)); + } + + BTM_TRACE_API1("BTM_ChangeEScoLinkParms -> eSCO Link for handle 0x%04x", p_sco->hci_handle); + BTM_TRACE_API6(" txbw 0x%x, rxbw 0x%x, lat 0x%x, voice 0x%x, retrans 0x%02x, pkt 0x%04x", + p_setup->tx_bw, p_setup->rx_bw, p_parms->max_latency, + p_setup->voice_contfmt, p_parms->retrans_effort, temp_pkt_types); + + /* When changing an existing link, only change latency, retrans, and pkts */ + if (!btsnd_hcic_setup_esco_conn(p_sco->hci_handle, p_setup->tx_bw, + p_setup->rx_bw, p_parms->max_latency, + p_setup->voice_contfmt, + p_parms->retrans_effort, + temp_pkt_types)) + return (BTM_NO_RESOURCES); + else + p_parms->packet_types = temp_pkt_types; + } + + return (BTM_CMD_STARTED); +#else + return (BTM_WRONG_MODE); +#endif +} + +/******************************************************************************* +** +** Function BTM_EScoConnRsp +** +** Description This function is called upon receipt of an (e)SCO connection +** request event (BTM_ESCO_CONN_REQ_EVT) to accept or reject +** the request. Parameters used to negotiate eSCO links. +** If p_parms is NULL, then values set through BTM_SetEScoMode +** are used. +** If the link type of the incoming request is SCO, then only +** the tx_bw, max_latency, content format, and packet_types are +** valid. The hci_status parameter should be +** ([0x0] to accept, [0x0d..0x0f] to reject) +** +** +** Returns void +** +*******************************************************************************/ +void BTM_EScoConnRsp (UINT16 sco_inx, UINT8 hci_status, tBTM_ESCO_PARAMS *p_parms) +{ +#if (BTM_MAX_SCO_LINKS>0) + if (sco_inx < BTM_MAX_SCO_LINKS && + btm_cb.sco_cb.sco_db[sco_inx].state == SCO_ST_W4_CONN_RSP) + { + btm_esco_conn_rsp(sco_inx, hci_status, + btm_cb.sco_cb.sco_db[sco_inx].esco.data.bd_addr, + p_parms); + } +#endif +} + +/******************************************************************************* +** +** Function btm_read_def_esco_mode +** +** Description This function copies the current default esco settings into +** the return buffer. +** +** Returns tBTM_SCO_TYPE +** +*******************************************************************************/ +tBTM_SCO_TYPE btm_read_def_esco_mode (tBTM_ESCO_PARAMS *p_parms) +{ +#if (BTM_MAX_SCO_LINKS>0) + *p_parms = btm_cb.sco_cb.def_esco_parms; + return btm_cb.sco_cb.desired_sco_mode; +#else + return BTM_LINK_TYPE_SCO; +#endif +} + +/******************************************************************************* +** +** Function btm_esco_proc_conn_chg +** +** Description This function is called by BTIF when an SCO connection +** is changed. +** +** Returns void +** +*******************************************************************************/ +void btm_esco_proc_conn_chg (UINT8 status, UINT16 handle, UINT8 tx_interval, + UINT8 retrans_window, UINT16 rx_pkt_len, + UINT16 tx_pkt_len) +{ +#if (BTM_MAX_SCO_LINKS>0) + tSCO_CONN *p = &btm_cb.sco_cb.sco_db[0]; + tBTM_CHG_ESCO_EVT_DATA data; + UINT16 xx; + + BTM_TRACE_EVENT2("btm_esco_proc_conn_chg -> handle 0x%04x, status 0x%02x", + handle, status); + + for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) + { + if (p->state == SCO_ST_CONNECTED && handle == p->hci_handle) + { + /* If upper layer wants notification */ + if (p->esco.p_esco_cback) + { + memcpy(data.bd_addr, p->esco.data.bd_addr, BD_ADDR_LEN); + data.hci_status = status; + data.sco_inx = xx; + data.rx_pkt_len = p->esco.data.rx_pkt_len = rx_pkt_len; + data.tx_pkt_len = p->esco.data.tx_pkt_len = tx_pkt_len; + data.tx_interval = p->esco.data.tx_interval = tx_interval; + data.retrans_window = p->esco.data.retrans_window = retrans_window; + + (*p->esco.p_esco_cback)(BTM_ESCO_CHG_EVT, + (tBTM_ESCO_EVT_DATA *)&data); + } + return; + } + } +#endif +} + +/******************************************************************************* +** +** Function btm_is_sco_active +** +** Description This function is called to see if a SCO handle is already in +** use. +** +** Returns BOOLEAN +** +*******************************************************************************/ +BOOLEAN btm_is_sco_active (UINT16 handle) +{ +#if (BTM_MAX_SCO_LINKS>0) + UINT16 xx; + tSCO_CONN *p = &btm_cb.sco_cb.sco_db[0]; + + for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) + { + if (handle == p->hci_handle && p->state == SCO_ST_CONNECTED) + return (TRUE); + } +#endif + return (FALSE); +} + +/******************************************************************************* +** +** Function BTM_GetNumScoLinks +** +** Description This function returns the number of active sco links. +** +** Returns UINT8 +** +*******************************************************************************/ +UINT8 BTM_GetNumScoLinks (void) +{ +#if (BTM_MAX_SCO_LINKS>0) + tSCO_CONN *p = &btm_cb.sco_cb.sco_db[0]; + UINT16 xx; + UINT8 num_scos = 0; + + for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) + { + switch (p->state) + { + case SCO_ST_W4_CONN_RSP: + case SCO_ST_CONNECTING: + case SCO_ST_CONNECTED: + case SCO_ST_DISCONNECTING: + case SCO_ST_PEND_UNPARK: + num_scos++; + } + } + return (num_scos); +#else + return (0); +#endif +} + + +/******************************************************************************* +** +** Function btm_is_sco_active_by_bdaddr +** +** Description This function is called to see if a SCO active to a bd address. +** +** Returns BOOLEAN +** +*******************************************************************************/ +BOOLEAN btm_is_sco_active_by_bdaddr (BD_ADDR remote_bda) +{ +#if (BTM_MAX_SCO_LINKS>0) + UINT8 xx; + tSCO_CONN *p = &btm_cb.sco_cb.sco_db[0]; + + /* If any SCO is being established to the remote BD address, refuse this */ + for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) + { + if ((!memcmp (p->esco.data.bd_addr, remote_bda, BD_ADDR_LEN)) && (p->state == SCO_ST_CONNECTED)) + { + return (TRUE); + } + } +#endif + return (FALSE); +} +#else /* SCO_EXCLUDED == TRUE (Link in stubs) */ + +tBTM_STATUS BTM_CreateSco (BD_ADDR remote_bda, BOOLEAN is_orig, + UINT16 pkt_types, UINT16 *p_sco_inx, + tBTM_SCO_CB *p_conn_cb, + tBTM_SCO_CB *p_disc_cb) {return (BTM_NO_RESOURCES);} +tBTM_STATUS BTM_RemoveSco (UINT16 sco_inx) {return (BTM_NO_RESOURCES);} +tBTM_STATUS BTM_SetScoPacketTypes (UINT16 sco_inx, UINT16 pkt_types) {return (BTM_NO_RESOURCES);} +UINT16 BTM_ReadScoPacketTypes (UINT16 sco_inx) {return (0);} +UINT16 BTM_ReadDeviceScoPacketTypes (void) {return (0);} +UINT16 BTM_ReadScoHandle (UINT16 sco_inx) {return (BTM_INVALID_HCI_HANDLE);} +UINT8 *BTM_ReadScoBdAddr(UINT16 sco_inx) {return((UINT8 *) NULL);} +UINT16 BTM_ReadScoDiscReason (void) {return (BTM_INVALID_SCO_DISC_REASON);} +tBTM_STATUS BTM_SetEScoMode (tBTM_SCO_TYPE sco_mode, tBTM_ESCO_PARAMS *p_parms) {return (BTM_MODE_UNSUPPORTED);} +tBTM_STATUS BTM_RegForEScoEvts (UINT16 sco_inx, tBTM_ESCO_CBACK *p_esco_cback) { return (BTM_ILLEGAL_VALUE);} +tBTM_STATUS BTM_ReadEScoLinkParms (UINT16 sco_inx, tBTM_ESCO_DATA *p_parms) { return (BTM_MODE_UNSUPPORTED);} +tBTM_STATUS BTM_ChangeEScoLinkParms (UINT16 sco_inx, tBTM_CHG_ESCO_PARAMS *p_parms) { return (BTM_MODE_UNSUPPORTED);} +void BTM_EScoConnRsp (UINT16 sco_inx, UINT8 hci_status, tBTM_ESCO_PARAMS *p_parms) {} +UINT8 BTM_GetNumScoLinks (void) {return (0);} + +#endif /* If SCO is being used */ diff --git a/stack/btm/btm_sec.c b/stack/btm/btm_sec.c new file mode 100644 index 0000000..7ccf7a0 --- /dev/null +++ b/stack/btm/btm_sec.c @@ -0,0 +1,5612 @@ +/***************************************************************************** +** * +** Name: btm_sec.c * +** * +** Description: This file contains functions for the Bluetooth Security * +** Manager * +** * +** Copyright (c) 1999-2011, Broadcom Corp., All Rights Reserved. * +** Broadcom Bluetooth Core. Proprietary and confidential. * +******************************************************************************/ + +#include <string.h> +#include "bt_types.h" +#include "hcimsgs.h" +#include "btu.h" +#include "btm_int.h" +#include "l2c_int.h" + +#if (BT_USE_TRACES == TRUE && BT_TRACE_VERBOSE == FALSE) +/* needed for sprintf() */ +#include <stdio.h> +#endif + +#if BLE_INCLUDED == TRUE + #include "gatt_int.h" +#endif + +#define BTM_SEC_MAX_COLLISION_DELAY (GKI_SECS_TO_TICKS(5)) + +#ifdef APPL_AUTH_WRITE_EXCEPTION +BOOLEAN (APPL_AUTH_WRITE_EXCEPTION)(BD_ADDR bd_addr); +#endif + +/********************************************************************************/ +/* L O C A L F U N C T I O N P R O T O T Y P E S */ +/********************************************************************************/ +static tBTM_SEC_SERV_REC *btm_sec_find_first_serv (BOOLEAN is_originator, UINT16 psm); +static tBTM_SEC_SERV_REC *btm_sec_find_next_serv (tBTM_SEC_SERV_REC *p_cur); +static tBTM_SEC_SERV_REC *btm_sec_find_mx_serv (UINT8 is_originator, UINT16 psm, + UINT32 mx_proto_id, + UINT32 mx_chan_id); + +static tBTM_STATUS btm_sec_execute_procedure (tBTM_SEC_DEV_REC *p_dev_rec); +static BOOLEAN btm_sec_start_get_name (tBTM_SEC_DEV_REC *p_dev_rec); +static BOOLEAN btm_sec_start_authentication (tBTM_SEC_DEV_REC *p_dev_rec); +static BOOLEAN btm_sec_start_encryption (tBTM_SEC_DEV_REC *p_dev_rec); +static void btm_sec_collision_timeout (TIMER_LIST_ENT *p_tle); +static void btm_restore_mode(void); +static void btm_sec_pairing_timeout (TIMER_LIST_ENT *p_tle); +static tBTM_STATUS btm_sec_dd_create_conn (tBTM_SEC_DEV_REC *p_dev_rec); +static void btm_sec_change_pairing_state (tBTM_PAIRING_STATE new_state); + +#if (BT_USE_TRACES == TRUE) +static char *btm_pair_state_descr (tBTM_PAIRING_STATE state); +#endif + +static void btm_sec_check_pending_reqs(void); +static BOOLEAN btm_sec_queue_mx_request (BD_ADDR bd_addr, UINT16 psm, BOOLEAN is_orig, + UINT32 mx_proto_id, UINT32 mx_chan_id, + tBTM_SEC_CALLBACK *p_callback, void *p_ref_data); +static void btm_sec_bond_cancel_complete (void); +static void btm_send_link_key_notif (tBTM_SEC_DEV_REC *p_dev_rec); +static BOOLEAN btm_sec_check_prefetch_pin (tBTM_SEC_DEV_REC *p_dev_rec); + +static UINT8 btm_sec_start_authorization (tBTM_SEC_DEV_REC *p_dev_rec); +BOOLEAN btm_sec_are_all_trusted(UINT32 p_mask[]); + +static tBTM_STATUS btm_sec_send_hci_disconnect (tBTM_SEC_DEV_REC *p_dev_rec, UINT8 reason); +tBTM_SEC_DEV_REC *btm_sec_find_dev_by_sec_state (UINT8 state); + +static BOOLEAN btm_sec_set_security_level ( CONNECTION_TYPE conn_type, char *p_name, UINT8 service_id, + UINT16 sec_level, UINT16 psm, UINT32 mx_proto_id, + UINT32 mx_chan_id); + +/* TRUE - authenticated link key is possible */ +static const BOOLEAN btm_sec_io_map [BTM_IO_CAP_MAX][BTM_IO_CAP_MAX] = +{ + /* OUT, IO, IN, NONE */ +/* OUT */ {FALSE, FALSE, TRUE, FALSE}, +/* IO */ {FALSE, TRUE, TRUE, FALSE}, +/* IN */ {TRUE, TRUE, TRUE, FALSE}, +/* NONE */ {FALSE, FALSE, FALSE, FALSE} +}; +/* BTM_IO_CAP_OUT 0 DisplayOnly */ +/* BTM_IO_CAP_IO 1 DisplayYesNo */ +/* BTM_IO_CAP_IN 2 KeyboardOnly */ +/* BTM_IO_CAP_NONE 3 NoInputNoOutput */ + +/******************************************************************************* +** +** Function BTM_SecRegister +** +** Description Application manager calls this function to register for +** security services. There can be one and only one application +** saving link keys. BTM allows only first registration. +** +** Returns TRUE if registered OK, else FALSE +** +*******************************************************************************/ +BOOLEAN BTM_SecRegister (tBTM_APPL_INFO *p_cb_info) +{ +#if BLE_INCLUDED == TRUE + BT_OCTET16 temp_value = {0}; +#endif + + BTM_TRACE_EVENT0 ("BTM_Sec: application registered"); + +#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE + BTM_TRACE_ERROR1 ("BTM_SecRegister:p_cb_info->p_le_callback == 0x%x ", p_cb_info->p_le_callback); + + if (p_cb_info->p_le_callback) + { +#if SMP_INCLUDED == TRUE + BTM_TRACE_EVENT0 ("BTM_Sec: SMP_Register( btm_proc_smp_cback )"); + SMP_Register(btm_proc_smp_cback); +#endif + /* if no IR is loaded, need to regenerate all the keys */ + if (memcmp(btm_cb.devcb.id_keys.ir, &temp_value, sizeof(BT_OCTET16)) == 0) + { + btm_ble_reset_id(); + } + } + else + { + BTM_TRACE_ERROR0 ("BTM_SecRegister:p_cb_info->p_le_callback == NULL "); + } +#endif + + + + btm_cb.api = *p_cb_info; +#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE + BTM_TRACE_ERROR1 ("BTM_SecRegister: btm_cb.api.p_le_callback = 0x%x ", btm_cb.api.p_le_callback); +#endif + BTM_TRACE_EVENT0 ("BTM_Sec: application registered"); + return(TRUE); +} + + +/******************************************************************************* +** +** Function BTM_SecRegisterLinkKeyNotificationCallback +** +** Description Application manager calls this function to register for +** link key notification. When there is nobody registered +** we should avoid changing link key +** +** Returns TRUE if registered OK, else FALSE +** +*******************************************************************************/ +BOOLEAN BTM_SecRegisterLinkKeyNotificationCallback (tBTM_LINK_KEY_CALLBACK *p_callback) +{ + btm_cb.api.p_link_key_callback = p_callback; + return(TRUE); +} + + +/******************************************************************************* +** +** Function BTM_SecAddRmtNameNotifyCallback +** +** Description Any profile can register to be notified when name of the +** remote device is resolved. +** +** Returns TRUE if registered OK, else FALSE +** +*******************************************************************************/ +BOOLEAN BTM_SecAddRmtNameNotifyCallback (tBTM_RMT_NAME_CALLBACK *p_callback) +{ + int i; + + for (i = 0; i < BTM_SEC_MAX_RMT_NAME_CALLBACKS; i++) + { + if (btm_cb.p_rmt_name_callback[i] == NULL) + { + btm_cb.p_rmt_name_callback[i] = p_callback; + return(TRUE); + } + } + + return(FALSE); +} + + +/******************************************************************************* +** +** Function BTM_SecDeleteRmtNameNotifyCallback +** +** Description Any profile can deregister notification when a new Link Key +** is generated per connection. +** +** Returns TRUE if OK, else FALSE +** +*******************************************************************************/ +BOOLEAN BTM_SecDeleteRmtNameNotifyCallback (tBTM_RMT_NAME_CALLBACK *p_callback) +{ + int i; + + for (i = 0; i < BTM_SEC_MAX_RMT_NAME_CALLBACKS; i++) + { + if (btm_cb.p_rmt_name_callback[i] == p_callback) + { + btm_cb.p_rmt_name_callback[i] = NULL; + return(TRUE); + } + } + + return(FALSE); +} + + +/******************************************************************************* +** +** Function BTM_SecSetConnectFilterCallback +** +** Description Host can register to be asked whenever a HCI connection +** request is received. In the registered function host +** suppose to check connectibility filters. Yes/No result +** should be returned syncronously +** +** Returns void +** +*******************************************************************************/ +void BTM_SecSetConnectFilterCallback (tBTM_FILTER_CB *p_callback) +{ + btm_cb.p_conn_filter_cb = p_callback; +} + +/******************************************************************************* +** +** Function BTM_GetSecurityMode +** +** Description Get security mode for the device +** +** Returns void +** +*******************************************************************************/ +UINT8 BTM_GetSecurityMode (void) +{ + return(btm_cb.security_mode); +} + +/******************************************************************************* +** +** Function BTM_GetSecurityFlags +** +** Description Get security flags for the device +** +** Returns BOOLEAN TRUE or FALSE is device found +** +*******************************************************************************/ +BOOLEAN BTM_GetSecurityFlags (BD_ADDR bd_addr, UINT8 * p_sec_flags) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + + if ((p_dev_rec = btm_find_dev (bd_addr)) != NULL) + { + *p_sec_flags = p_dev_rec->sec_flags; + return(TRUE); + } + BTM_TRACE_ERROR0 ("BTM_GetSecurityFlags false"); + return(FALSE); +} + +/******************************************************************************* +** +** Function BTM_SetSecurityMode +** +** Description Set security mode for the device +** +** Returns void +** +*******************************************************************************/ +void BTM_SetSecurityMode (UINT8 security_mode) +{ + UINT8 old_mode = btm_cb.security_mode; + + UINT8 sp_mode = HCI_SPD_MODE_ENABLED; + UINT8 sp_debug_mode = HCI_SPD_MODE_DISABLED; + + switch (security_mode) + { +#if (BTM_PRE_LISBON_INCLUDED == TRUE) + case BTM_SEC_MODE_NONE: + case BTM_SEC_MODE_SERVICE: + case BTM_SEC_MODE_LINK: + break; +#endif + + case BTM_SEC_MODE_SP_DEBUG: + sp_debug_mode = HCI_SPD_MODE_ENABLED; + break; + case BTM_SEC_MODE_SP: + /* the default is enabled */ + break; + default: + BTM_TRACE_ERROR1 ("BTM_SetSecurityMode: unknown mode:%d", security_mode); + return; + } + btm_cb.security_mode = security_mode; + + if (HCI_SIMPLE_PAIRING_SUPPORTED(btm_cb.devcb.local_features)) + { + /* Lisbon devices and only use BTM_SEC_MODE_SP */ + btm_cb.security_mode = BTM_SEC_MODE_SP; + BTM_TRACE_DEBUG2("BTM_SetSecurityMode: SP:%d, debug:%d", sp_mode, sp_debug_mode); + btsnd_hcic_write_simple_pairing_mode(sp_mode); + btsnd_hcic_write_simp_pair_debug_mode(sp_debug_mode); + return; + } + + /* must be a pre-Lisbon device */ +#if (BTM_PRE_LISBON_INCLUDED == TRUE) + /* If previously security mode was Link Level and now lesser notify */ + /* controller not to perform authentication, encryption on startup */ + if ((old_mode == BTM_SEC_MODE_LINK) + && ( security_mode != BTM_SEC_MODE_LINK)) + { + BTM_TRACE_DEBUG0("BTM_SetSecurityMode: Authen Enable -> FALSE"); + btsnd_hcic_write_auth_enable (FALSE); + btsnd_hcic_write_encr_mode (HCI_ENCRYPT_MODE_DISABLED); + } + + /* If previously security is increased to Link Level notify */ + /* controller to perform authentication, encryption on startup */ + if ((old_mode != BTM_SEC_MODE_LINK) + && ( security_mode == BTM_SEC_MODE_LINK)) + { + BTM_TRACE_DEBUG0("BTM_SetSecurityMode: Authen Enable -> TRUE"); + btsnd_hcic_write_auth_enable (TRUE); + btsnd_hcic_write_encr_mode (HCI_ENCRYPT_MODE_POINT_TO_POINT); + } +#endif /* BTM_PRE_LISBON_INCLUDED == TRUE */ +} + +/******************************************************************************* +** +** Function BTM_SetPinType +** +** Description Set PIN type for the device. +** +** Returns void +** +*******************************************************************************/ +void BTM_SetPinType (UINT8 pin_type, PIN_CODE pin_code, UINT8 pin_code_len) +{ + BTM_TRACE_API3 ("BTM_SetPinType: pin type %d [variable-0, fixed-1], code %s, length %d", + pin_type, (char *) pin_code, pin_code_len); + + /* If device is not up security mode will be set as a part of startup */ + if ( (btm_cb.cfg.pin_type != pin_type) + && (btm_cb.devcb.state > BTM_DEV_STATE_WAIT_AFTER_RESET) ) + { + btsnd_hcic_write_pin_type (pin_type); + } + + btm_cb.cfg.pin_type = pin_type; + btm_cb.cfg.pin_code_len = pin_code_len; + memcpy (btm_cb.cfg.pin_code, pin_code, pin_code_len); +} + +/******************************************************************************* +** +** Function BTM_SetPairableMode +** +** Description Enable or disable pairing +** +** Parameters allow_pairing - (TRUE or FALSE) whether or not the device +** allows pairing. +** connect_only_paired - (TRUE or FALSE) whether or not to +** only allow paired devices to connect. +** +** Returns void +** +*******************************************************************************/ +void BTM_SetPairableMode (BOOLEAN allow_pairing, BOOLEAN connect_only_paired) +{ + BTM_TRACE_API2 ("BTM_SetPairableMode() allow_pairing: %u connect_only_paired: %u", allow_pairing, connect_only_paired); + + btm_cb.pairing_disabled = !allow_pairing; + btm_cb.connect_only_paired = connect_only_paired; +} + + +#define BTM_NO_AVAIL_SEC_SERVICES ((UINT16) 0xffff) + +/******************************************************************************* +** +** Function BTM_SetUCDSecurityLevel +** +** Description Register UCD service security level with Security Manager +** +** Parameters: is_originator - TRUE if originating the connection, FALSE if not +** p_name - Name of the service relevant only if +** authorization will show this name to user. ignored +** if BTM_SEC_SERVICE_NAME_LEN is 0. +** service_id - service ID for the service passed to authorization callback +** sec_level - bit mask of the security features +** psm - L2CAP PSM +** mx_proto_id - protocol ID of multiplexing proto below +** mx_chan_id - channel ID of multiplexing proto below +** +** Returns TRUE if registered OK, else FALSE +** +*******************************************************************************/ +BOOLEAN BTM_SetUCDSecurityLevel (BOOLEAN is_originator, char *p_name, UINT8 service_id, + UINT16 sec_level, UINT16 psm, UINT32 mx_proto_id, + UINT32 mx_chan_id) +{ +#if (L2CAP_UCD_INCLUDED == TRUE) + CONNECTION_TYPE conn_type; + + if (is_originator) + conn_type = CONNLESS_ORIG; + else + conn_type = CONNLESS_TERM; + + return(btm_sec_set_security_level (conn_type, p_name, service_id, + sec_level, psm, mx_proto_id, mx_chan_id)); +#else + return FALSE; +#endif +} + +/******************************************************************************* +** +** Function BTM_SetSecurityLevel +** +** Description Register service security level with Security Manager +** +** Parameters: is_originator - TRUE if originating the connection, FALSE if not +** p_name - Name of the service relevant only if +** authorization will show this name to user. ignored +** if BTM_SEC_SERVICE_NAME_LEN is 0. +** service_id - service ID for the service passed to authorization callback +** sec_level - bit mask of the security features +** psm - L2CAP PSM +** mx_proto_id - protocol ID of multiplexing proto below +** mx_chan_id - channel ID of multiplexing proto below +** +** Returns TRUE if registered OK, else FALSE +** +*******************************************************************************/ +BOOLEAN BTM_SetSecurityLevel (BOOLEAN is_originator, char *p_name, UINT8 service_id, + UINT16 sec_level, UINT16 psm, UINT32 mx_proto_id, + UINT32 mx_chan_id) +{ +#if (L2CAP_UCD_INCLUDED == TRUE) + CONNECTION_TYPE conn_type; + + if (is_originator) + conn_type = CONN_ORIENT_ORIG; + else + conn_type = CONN_ORIENT_TERM; + + return(btm_sec_set_security_level (conn_type, p_name, service_id, + sec_level, psm, mx_proto_id, mx_chan_id)); +#else + return(btm_sec_set_security_level (is_originator, p_name, service_id, + sec_level, psm, mx_proto_id, mx_chan_id)); +#endif +} + +/******************************************************************************* +** +** Function btm_sec_set_security_level +** +** Description Register service security level with Security Manager +** +** Parameters: conn_type - TRUE if originating the connection, FALSE if not +** p_name - Name of the service relevant only if +** authorization will show this name to user. ignored +** if BTM_SEC_SERVICE_NAME_LEN is 0. +** service_id - service ID for the service passed to authorization callback +** sec_level - bit mask of the security features +** psm - L2CAP PSM +** mx_proto_id - protocol ID of multiplexing proto below +** mx_chan_id - channel ID of multiplexing proto below +** +** Returns TRUE if registered OK, else FALSE +** +*******************************************************************************/ +static BOOLEAN btm_sec_set_security_level (CONNECTION_TYPE conn_type, char *p_name, UINT8 service_id, + UINT16 sec_level, UINT16 psm, UINT32 mx_proto_id, + UINT32 mx_chan_id) +{ + tBTM_SEC_SERV_REC *p_srec; + UINT16 index; + UINT16 first_unused_record = BTM_NO_AVAIL_SEC_SERVICES; + BOOLEAN record_allocated = FALSE; + BOOLEAN is_originator; +#if (L2CAP_UCD_INCLUDED == TRUE) + BOOLEAN is_ucd; + + if (conn_type & CONNECTION_TYPE_ORIG_MASK) + is_originator = TRUE; + else + is_originator = FALSE; + + if (conn_type & CONNECTION_TYPE_CONNLESS_MASK ) + { + is_ucd = TRUE; + } + else + { + is_ucd = FALSE; + } +#else + is_originator = conn_type; +#endif + + /* See if the record can be reused (same service name, psm, mx_proto_id, + service_id, and mx_chan_id), or obtain the next unused record */ + + p_srec = &btm_cb.sec_serv_rec[0]; + + + for (index = 0; index < BTM_SEC_MAX_SERVICE_RECORDS; index++, p_srec++) + { + /* Check if there is already a record for this service */ + if (p_srec->security_flags & BTM_SEC_IN_USE) + { +#if BTM_SEC_SERVICE_NAME_LEN > 0 + if (p_srec->psm == psm && + p_srec->mx_proto_id == mx_proto_id && + service_id == p_srec->service_id && + (!strncmp (p_name, (char *) p_srec->orig_service_name, + BTM_SEC_SERVICE_NAME_LEN) || + !strncmp (p_name, (char *) p_srec->term_service_name, + BTM_SEC_SERVICE_NAME_LEN))) +#else + if (p_srec->psm == psm && + p_srec->mx_proto_id == mx_proto_id && + service_id == p_srec->service_id) +#endif + { + record_allocated = TRUE; + break; + } + } + /* Mark the first available service record */ + else if (!record_allocated) + { + memset (p_srec, 0, sizeof(tBTM_SEC_SERV_REC)); + record_allocated = TRUE; + first_unused_record = index; + } + } + + if (!record_allocated) + { + BTM_TRACE_WARNING1("BTM_SEC_REG: Out of Service Records (%d)", BTM_SEC_MAX_SERVICE_RECORDS); + return(record_allocated); + } + + /* Process the request if service record is valid */ + /* If a duplicate service wasn't found, use the first available */ + if (index >= BTM_SEC_MAX_SERVICE_RECORDS) + { + index = first_unused_record; + p_srec = &btm_cb.sec_serv_rec[index]; + } + + p_srec->psm = psm; + p_srec->service_id = service_id; + p_srec->mx_proto_id = mx_proto_id; + + if (is_originator) + { + p_srec->orig_mx_chan_id = mx_chan_id; +#if BTM_SEC_SERVICE_NAME_LEN > 0 + BCM_STRNCPY_S ((char *)p_srec->orig_service_name, sizeof(p_srec->orig_service_name), p_name, BTM_SEC_SERVICE_NAME_LEN); +#endif + /* clear out the old setting, just in case it exists */ +#if (L2CAP_UCD_INCLUDED == TRUE) + if ( is_ucd ) + { + p_srec->ucd_security_flags &= + ~(BTM_SEC_OUT_AUTHORIZE | BTM_SEC_OUT_ENCRYPT | BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_MITM | + BTM_SEC_FORCE_MASTER | BTM_SEC_ATTEMPT_MASTER | BTM_SEC_FORCE_SLAVE | BTM_SEC_ATTEMPT_SLAVE); + } + else +#endif + { + p_srec->security_flags &= + ~(BTM_SEC_OUT_AUTHORIZE | BTM_SEC_OUT_ENCRYPT | BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_MITM | + BTM_SEC_FORCE_MASTER | BTM_SEC_ATTEMPT_MASTER | BTM_SEC_FORCE_SLAVE | BTM_SEC_ATTEMPT_SLAVE); + } + + /* Parameter validation. Originator should not set requirements for incoming connections */ + sec_level &= ~(BTM_SEC_IN_AUTHORIZE | BTM_SEC_IN_ENCRYPT | BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_MITM); + + if (btm_cb.security_mode == BTM_SEC_MODE_SP) + { + if (sec_level & BTM_SEC_OUT_AUTHENTICATE) + sec_level |= BTM_SEC_OUT_MITM; + } + + /* Make sure the authenticate bit is set, when encrypt bit is set */ + if (sec_level & BTM_SEC_OUT_ENCRYPT) + sec_level |= BTM_SEC_OUT_AUTHENTICATE; + + /* outgoing connections usually set the security level right before + * the connection is initiated. + * set it to be the outgoing service */ +#if (L2CAP_UCD_INCLUDED == TRUE) + if ( is_ucd == FALSE ) +#endif + { + btm_cb.p_out_serv = p_srec; + } + } + else + { + p_srec->term_mx_chan_id = mx_chan_id; +#if BTM_SEC_SERVICE_NAME_LEN > 0 + BCM_STRNCPY_S ((char *)p_srec->term_service_name, sizeof(p_srec->term_service_name), p_name, BTM_SEC_SERVICE_NAME_LEN); +#endif + /* clear out the old setting, just in case it exists */ +#if (L2CAP_UCD_INCLUDED == TRUE) + if ( is_ucd ) + { + p_srec->ucd_security_flags &= + ~(BTM_SEC_IN_AUTHORIZE | BTM_SEC_IN_ENCRYPT | BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_MITM | + BTM_SEC_FORCE_MASTER | BTM_SEC_ATTEMPT_MASTER | BTM_SEC_FORCE_SLAVE | BTM_SEC_ATTEMPT_SLAVE); + } + else +#endif + { + p_srec->security_flags &= + ~(BTM_SEC_IN_AUTHORIZE | BTM_SEC_IN_ENCRYPT | BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_MITM | + BTM_SEC_FORCE_MASTER | BTM_SEC_ATTEMPT_MASTER | BTM_SEC_FORCE_SLAVE | BTM_SEC_ATTEMPT_SLAVE); + } + + /* Parameter validation. Acceptor should not set requirements for outgoing connections */ + sec_level &= ~(BTM_SEC_OUT_AUTHORIZE | BTM_SEC_OUT_ENCRYPT | BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_MITM); + + if (btm_cb.security_mode == BTM_SEC_MODE_SP) + { + if (sec_level & BTM_SEC_IN_AUTHENTICATE) + sec_level |= BTM_SEC_IN_MITM; + } + + /* Make sure the authenticate bit is set, when encrypt bit is set */ + if (sec_level & BTM_SEC_IN_ENCRYPT) + sec_level |= BTM_SEC_IN_AUTHENTICATE; + } + +#if (L2CAP_UCD_INCLUDED == TRUE) + if ( is_ucd ) + { + p_srec->security_flags |= (UINT16)(BTM_SEC_IN_USE); + p_srec->ucd_security_flags |= (UINT16)(sec_level | BTM_SEC_IN_USE); + } + else + { + p_srec->security_flags |= (UINT16)(sec_level | BTM_SEC_IN_USE); + } + + BTM_TRACE_API6("BTM_SEC_REG[%d]: id %d, conn_type 0x%x, psm 0x%04x, proto_id %d, chan_id %d", + index, service_id, conn_type, psm, mx_proto_id, mx_chan_id); + + BTM_TRACE_API2(" : security_flags: 0x%04x, ucd_security_flags: 0x%04x", + p_srec->security_flags, p_srec->ucd_security_flags); + +#if BTM_SEC_SERVICE_NAME_LEN > 0 + BTM_TRACE_API2(" : service name [%s] (up to %d chars saved)", + p_name, BTM_SEC_SERVICE_NAME_LEN); +#endif +#else + p_srec->security_flags |= (UINT16)(sec_level | BTM_SEC_IN_USE); + + BTM_TRACE_API6("BTM_SEC_REG[%d]: id %d, is_orig %d, psm 0x%04x, proto_id %d, chan_id %d", + index, service_id, is_originator, psm, mx_proto_id, mx_chan_id); + +#if BTM_SEC_SERVICE_NAME_LEN > 0 + BTM_TRACE_API3(" : sec: 0x%x, service name [%s] (up to %d chars saved)", + p_srec->security_flags, p_name, BTM_SEC_SERVICE_NAME_LEN); +#endif +#endif + + + return(record_allocated); +} + +/******************************************************************************* +** +** Function BTM_SecClrService +** +** Description Removes specified service record(s) from the security database. +** All service records with the specified name are removed. +** Typically used only by devices with limited RAM so that it can +** reuse an old security service record. +** +** Note: Unpredictable results may occur if a service is cleared +** that is still in use by an application/profile. +** +** Parameters Service ID - Id of the service to remove. ('0' removes all service +** records (except SDP). +** +** Returns Number of records that were freed. +** +*******************************************************************************/ +UINT8 BTM_SecClrService (UINT8 service_id) +{ + tBTM_SEC_SERV_REC *p_srec = &btm_cb.sec_serv_rec[0]; + UINT8 num_freed = 0; + int i; + + for (i = 0; i < BTM_SEC_MAX_SERVICE_RECORDS; i++, p_srec++) + { + /* Delete services with specified name (if in use and not SDP) */ + if ((p_srec->security_flags & BTM_SEC_IN_USE) && (p_srec->psm != BT_PSM_SDP) && + (!service_id || (service_id == p_srec->service_id))) + { + BTM_TRACE_API2("BTM_SEC_CLR[%d]: id %d", i, service_id); + p_srec->security_flags = 0; +#if (L2CAP_UCD_INCLUDED == TRUE) + p_srec->ucd_security_flags = 0; +#endif + num_freed++; + } + } + + return(num_freed); +} + +/******************************************************************************* +** +** Function btm_sec_clr_service_by_psm +** +** Description Removes specified service record from the security database. +** All service records with the specified psm are removed. +** Typically used by L2CAP to free up the service record used +** by dynamic PSM clients when the channel is closed. +** The given psm must be a virtual psm. +** +** Parameters Service ID - Id of the service to remove. ('0' removes all service +** records (except SDP). +** +** Returns Number of records that were freed. +** +*******************************************************************************/ +UINT8 btm_sec_clr_service_by_psm (UINT16 psm) +{ + tBTM_SEC_SERV_REC *p_srec = &btm_cb.sec_serv_rec[0]; + UINT8 num_freed = 0; + int i; + + for (i = 0; i < BTM_SEC_MAX_SERVICE_RECORDS; i++, p_srec++) + { + /* Delete services with specified name (if in use and not SDP) */ + if ((p_srec->security_flags & BTM_SEC_IN_USE) && (p_srec->psm == psm) ) + { + BTM_TRACE_API2("BTM_SEC_CLR[%d]: id %d ", i, p_srec->service_id); + p_srec->security_flags = 0; + num_freed++; + } + } + BTM_TRACE_API2("btm_sec_clr_service_by_psm psm:0x%x num_freed:%d", psm, num_freed); + + return(num_freed); +} + +/******************************************************************************* +** +** Function BTM_SecClrUCDService +** +** Description +** +** Parameters Service ID - Id of the service to remove. +** ('0' removes all service records ) +** +** Returns Number of records that were cleared. +** +*******************************************************************************/ +UINT8 BTM_SecClrUCDService (UINT8 service_id) +{ +#if (L2CAP_UCD_INCLUDED == TRUE) + tBTM_SEC_SERV_REC *p_srec = &btm_cb.sec_serv_rec[0]; + UINT8 num_cleared = 0; + int i; + + for (i = 0; i < BTM_SEC_MAX_SERVICE_RECORDS; i++, p_srec++) + { + /* Delete services with specified name (if in use and not SDP) */ + if ((p_srec->security_flags & BTM_SEC_IN_USE) && + (!service_id || (service_id == (UINT32)p_srec->service_id))) + { + BTM_TRACE_API2("BTM_UCD_SEC_CLR[%d]: id %d", i, service_id); + p_srec->ucd_security_flags = 0; + num_cleared++; + } + } + + return(num_cleared); +#else + return(0); +#endif +} + +/******************************************************************************* +** +** Function BTM_PINCodeReply +** +** Description This function is called after Security Manager submitted +** PIN code request to the UI. +** +** Parameters: bd_addr - Address of the device for which PIN was requested +** res - result of the operation BTM_SUCCESS if success +** pin_len - length in bytes of the PIN Code +** p_pin - pointer to array with the PIN Code +** trusted_mask - bitwise OR of trusted services (array of UINT32) +** +*******************************************************************************/ +void BTM_PINCodeReply (BD_ADDR bd_addr, UINT8 res, UINT8 pin_len, UINT8 *p_pin, UINT32 trusted_mask[]) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + + BTM_TRACE_API4 ("BTM_PINCodeReply(): PairState: %s PairFlags: 0x%02x PinLen:%d Result:%d", + btm_pair_state_descr(btm_cb.pairing_state), btm_cb.pairing_flags, pin_len, res); + + /* If timeout already expired or has been canceled, ignore the reply */ + if (btm_cb.pairing_state != BTM_PAIR_STATE_WAIT_LOCAL_PIN) + { + BTM_TRACE_WARNING1 ("BTM_PINCodeReply() - Wrong State: %d", btm_cb.pairing_state); + return; + } + + if (memcmp (bd_addr, btm_cb.pairing_bda, BD_ADDR_LEN) != 0) + { + BTM_TRACE_ERROR0 ("BTM_PINCodeReply() - Wrong BD Addr"); + return; + } + + if ((p_dev_rec = btm_find_dev (bd_addr)) == NULL) + { + BTM_TRACE_ERROR0 ("BTM_PINCodeReply() - no dev CB"); + return; + } + + if ( (pin_len > PIN_CODE_LEN) || (pin_len == 0) || (p_pin == NULL) ) + res = BTM_ILLEGAL_VALUE; + + if (res != BTM_SUCCESS) + { + if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_PEER_STARTED_DD) + { + /* use BTM_PAIR_STATE_WAIT_AUTH_COMPLETE to report authentication failed event */ + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_AUTH_COMPLETE); + btm_cb.acl_disc_reason = HCI_ERR_HOST_REJECT_SECURITY; + + btsnd_hcic_pin_code_neg_reply (bd_addr); + } + else + { + p_dev_rec->security_required = BTM_SEC_NONE; + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + } + return; + } + if (trusted_mask) + BTM_SEC_COPY_TRUSTED_DEVICE(trusted_mask, p_dev_rec->trusted_mask); + p_dev_rec->sec_flags |= BTM_SEC_LINK_KEY_AUTHED; + + if ( (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) + && (p_dev_rec->hci_handle == BTM_SEC_INVALID_HANDLE) + && (btm_cb.security_mode_changed == FALSE) ) + { + /* This is start of the dedicated bonding if local device is 2.0 */ + btm_cb.pin_code_len = pin_len; + memcpy (btm_cb.pin_code, p_pin, pin_len); + + btm_cb.security_mode_changed = TRUE; +#ifdef APPL_AUTH_WRITE_EXCEPTION + if(!(APPL_AUTH_WRITE_EXCEPTION)(p_dev_rec->bd_addr)) +#endif + btsnd_hcic_write_auth_enable (TRUE); + + btm_cb.acl_disc_reason = 0xff ; + + /* if we rejected incoming connection request, we have to wait HCI_Connection_Complete event */ + /* before originating */ + if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_REJECTED_CONNECT) + { + BTM_TRACE_WARNING0 ("BTM_PINCodeReply(): waiting HCI_Connection_Complete after rejected incoming connection"); + /* we change state little bit early so btm_sec_connected() will originate connection */ + /* when existing ACL link is down completely */ + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_PIN_REQ); + } + /* if we already accepted incoming connection from pairing device */ + else if (p_dev_rec->sm4 & BTM_SM4_CONN_PEND) + { + BTM_TRACE_WARNING0 ("BTM_PINCodeReply(): link is connecting so wait pin code request from peer"); + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_PIN_REQ); + } + else if (btm_sec_dd_create_conn(p_dev_rec) != BTM_CMD_STARTED) + { + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + p_dev_rec->sec_flags &= ~BTM_SEC_LINK_KEY_AUTHED; + + (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, HCI_ERR_AUTH_FAILURE); + } + return; + } + + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_AUTH_COMPLETE); + btm_cb.acl_disc_reason = HCI_SUCCESS; + +#ifdef PORCHE_PAIRING_CONFLICT + BTM_TRACE_EVENT2("BTM_PINCodeReply(): Saving pin_len: %d btm_cb.pin_code_len: %d", pin_len, btm_cb.pin_code_len); + /* if this was not pre-fetched, save the PIN */ + if (btm_cb.pin_code_len == 0) + memcpy (btm_cb.pin_code, p_pin, pin_len); + btm_cb.pin_code_len_saved = pin_len; +#endif + btsnd_hcic_pin_code_req_reply (bd_addr, pin_len, p_pin); +} + + +/******************************************************************************* +** +** Function BTM_DeviceAuthorized +** +** Description This function is called after Security Manager submitted +** authorization request to the UI. +** +** Parameters: bd_addr - Address of the device for which PIN was requested +** res - result of the operation BTM_SUCCESS if success +** +*******************************************************************************/ +void BTM_DeviceAuthorized (BD_ADDR bd_addr, UINT8 res, UINT32 trusted_mask[]) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + + if ((p_dev_rec = btm_find_dev (bd_addr)) == NULL) + { + BTM_TRACE_WARNING6 ("Security Manager: Attempting Authorization of Unknown Device Address [%02x%02x%02x%02x%02x%02x]", + bd_addr[0], bd_addr[1], bd_addr[2], bd_addr[3], bd_addr[4], bd_addr[5]); + return; + } + + BTM_TRACE_EVENT4 ("Security Manager: authorized status:%d State:%d Trusted:%08x %08x", + res, (p_dev_rec) ? p_dev_rec->sec_state : 0, trusted_mask[0], trusted_mask[1]); + + if (res == BTM_SUCCESS) + { + p_dev_rec->sec_flags |= BTM_SEC_AUTHORIZED; + if (trusted_mask) + BTM_SEC_COPY_TRUSTED_DEVICE(trusted_mask, p_dev_rec->trusted_mask); + } + + if (p_dev_rec->sec_state != BTM_SEC_STATE_AUTHORIZING) + return; + + p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; + + if (res != BTM_SUCCESS) + { + btm_sec_dev_rec_cback_event (p_dev_rec, res); + return; + } + + if ((res = (UINT8)btm_sec_execute_procedure (p_dev_rec)) != BTM_CMD_STARTED) + { + btm_sec_dev_rec_cback_event (p_dev_rec, res); + } +} + + + +/******************************************************************************* +** +** Function BTM_SecBond +** +** Description This function is called to perform bonding with peer device. +** If the connection is already up, but not secure, pairing +** is attempted. If already paired BTM_SUCCESS is returned. +** +** Parameters: bd_addr - Address of the device to bond +** pin_len - length in bytes of the PIN Code +** p_pin - pointer to array with the PIN Code +** trusted_mask - bitwise OR of trusted services (array of UINT32) +** +** Note: After 2.1 parameters are not used and preserved here not to change API +*******************************************************************************/ +tBTM_STATUS BTM_SecBond (BD_ADDR bd_addr, UINT8 pin_len, UINT8 *p_pin, UINT32 trusted_mask[]) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + tBTM_STATUS status; +#if SMP_INCLUDED == TRUE + tACL_CONN *p=NULL; + BOOLEAN is_le_slave_role=FALSE; +#endif + BTM_TRACE_API6 ("BTM_SecBond BDA: %02x:%02x:%02x:%02x:%02x:%02x", + bd_addr[0], bd_addr[1], bd_addr[2], bd_addr[3], bd_addr[4], bd_addr[5]); + + /* Other security process is in progress */ + if (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) + { + BTM_TRACE_ERROR1 ("BTM_SecBond: already busy in state: %s", btm_pair_state_descr(btm_cb.pairing_state)); + return(BTM_WRONG_MODE); + } + + + p_dev_rec = btm_find_or_alloc_dev (bd_addr); + BTM_TRACE_DEBUG1 ("before update sec_flags=0x%x", p_dev_rec->sec_flags); + + +#if SMP_INCLUDED == TRUE + p = btm_bda_to_acl(bd_addr); + if (p && p->is_le_link ) + { + if (p_dev_rec->sec_state == BTM_SEC_STATE_AUTHENTICATING) + { + BTM_TRACE_ERROR1 ("BTM_SecBond: LE already busy in state: %x", p_dev_rec->sec_state ); + return(BTM_WRONG_MODE); + } + + if (p->link_role == BTM_ROLE_SLAVE) + { + is_le_slave_role = TRUE; + BTM_TRACE_DEBUG0 ("LE Link Slave" ); + } + else + { + BTM_TRACE_DEBUG0 ("LE Link Maste" ); + } + } + else + { + BTM_TRACE_DEBUG0 ("No LE Link" ); + } + + if (!is_le_slave_role) + { + /* Finished if connection is active and already paired */ + if ( (p_dev_rec->hci_handle != BTM_SEC_INVALID_HANDLE) + && (p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED) ) + { + BTM_TRACE_WARNING0("BTM_SecBond -> Already Paired"); + return(BTM_SUCCESS); + } + } +#else + /* Finished if connection is active and already paired */ + if ( (p_dev_rec->hci_handle != BTM_SEC_INVALID_HANDLE) + && (p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED) ) + { + BTM_TRACE_WARNING0("BTM_SecBond -> Already Paired"); + return(BTM_SUCCESS); + } +#endif + + /* Tell controller to get rid of the link key if it has one stored */ + if ((BTM_DeleteStoredLinkKey (bd_addr, NULL)) != BTM_SUCCESS) + return(BTM_NO_RESOURCES); + + /* Save the PIN code if we got a valid one */ + if (p_pin && (pin_len <= PIN_CODE_LEN) && (pin_len != 0)) + { + btm_cb.pin_code_len = pin_len; + memcpy (btm_cb.pin_code, p_pin, PIN_CODE_LEN); + } + + memcpy (btm_cb.pairing_bda, bd_addr, BD_ADDR_LEN); + + btm_cb.pairing_flags = BTM_PAIR_FLAGS_WE_STARTED_DD; + + p_dev_rec->security_required = BTM_SEC_OUT_AUTHENTICATE; + p_dev_rec->is_originator = TRUE; + if (trusted_mask) + BTM_SEC_COPY_TRUSTED_DEVICE(trusted_mask, p_dev_rec->trusted_mask); + + + +#if SMP_INCLUDED == TRUE + + if (!is_le_slave_role) + { + p_dev_rec->sec_flags &= ~(BTM_SEC_LINK_KEY_KNOWN | BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED + | BTM_SEC_ROLE_SWITCHED | BTM_SEC_LINK_KEY_AUTHED); + + } + + /* LE device, do SMP pairing */ + if (p_dev_rec->device_type == BT_DEVICE_TYPE_BLE) + { + if (SMP_Pair(p_dev_rec->bd_addr) == SMP_STARTED) + { + p_dev_rec->sec_state = BTM_SEC_STATE_AUTHENTICATING; + return BTM_CMD_STARTED; + } + else + return(BTM_NO_RESOURCES); + } +#else + p_dev_rec->sec_flags &= ~(BTM_SEC_LINK_KEY_KNOWN | BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED + | BTM_SEC_ROLE_SWITCHED | BTM_SEC_LINK_KEY_AUTHED); +#endif + + BTM_TRACE_DEBUG1 ("after update sec_flags=0x%x", p_dev_rec->sec_flags); + if (!HCI_SIMPLE_PAIRING_SUPPORTED(btm_cb.devcb.local_features)) + { + /* The special case when we authenticate keyboard. Set pin type to fixed */ + /* It would be probably better to do it from the application, but it is */ + /* complicated */ + if (((p_dev_rec->dev_class[1] & BTM_COD_MAJOR_CLASS_MASK) == BTM_COD_MAJOR_PERIPHERAL) + && (p_dev_rec->dev_class[2] & BTM_COD_MINOR_KEYBOARD) + && (btm_cb.cfg.pin_type != HCI_PIN_TYPE_FIXED)) + { + btm_cb.pin_type_changed = TRUE; + btsnd_hcic_write_pin_type (HCI_PIN_TYPE_FIXED); + } + } + + BTM_TRACE_EVENT1("BTM_SecBond: Local device supports SSP=%d", HCI_SIMPLE_PAIRING_SUPPORTED(btm_cb.devcb.local_features)); + + BTM_TRACE_EVENT4(" remote_features=%02x-%02x-%02x-%02x", + p_dev_rec->features[0], p_dev_rec->features[1], p_dev_rec->features[2], p_dev_rec->features[3]); + BTM_TRACE_EVENT4(" %02x-%02x-%02x-%02x", + p_dev_rec->features[4], p_dev_rec->features[5], p_dev_rec->features[6], p_dev_rec->features[7]); + + BTM_TRACE_EVENT2 ("BTM_SecBond: Remote sm4: 0x%x HCI Handle: 0x%04x", p_dev_rec->sm4, p_dev_rec->hci_handle); + +#if BTM_SEC_FORCE_RNR_FOR_DBOND == TRUE + p_dev_rec->sec_flags &= ~BTM_SEC_NAME_KNOWN; +#endif + + /* If connection already exists... */ + if (p_dev_rec->hci_handle != BTM_SEC_INVALID_HANDLE) + { + if (!btm_sec_start_authentication (p_dev_rec)) + return(BTM_NO_RESOURCES); + + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_PIN_REQ); + + /* Mark lcb as bonding */ + l2cu_update_lcb_4_bonding (bd_addr, TRUE); + return(BTM_CMD_STARTED); + } + + BTM_TRACE_DEBUG2 ("sec mode: %d sm4:x%x", btm_cb.security_mode, p_dev_rec->sm4); + if (!HCI_SIMPLE_PAIRING_SUPPORTED(btm_cb.devcb.local_features) + || (p_dev_rec->sm4 == BTM_SM4_KNOWN)) + { + if ( btm_sec_check_prefetch_pin (p_dev_rec) ) + return(BTM_CMD_STARTED); + } + if (BTM_SEC_MODE_SP == btm_cb.security_mode && BTM_SEC_IS_SM4_UNKNOWN(p_dev_rec->sm4)) + { + /* local is 2.1 and peer is unknown */ + if ((p_dev_rec->sm4 & BTM_SM4_CONN_PEND) == 0) + { + /* we are not accepting connection request from peer + * -> RNR (to learn if peer is 2.1) + * RNR when no ACL causes HCI_RMT_HOST_SUP_FEAT_NOTIFY_EVT */ + btm_sec_change_pairing_state (BTM_PAIR_STATE_GET_REM_NAME); + BTM_ReadRemoteDeviceName(bd_addr, NULL); + } + else + { + /* We are accepting connection request from peer */ + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_PIN_REQ); + } + BTM_TRACE_DEBUG3 ("State:%s sm4: 0x%x sec_state:%d", btm_pair_state_descr (btm_cb.pairing_state), p_dev_rec->sm4, p_dev_rec->sec_state); + return BTM_CMD_STARTED; + } + + /* both local and peer are 2.1 */ + status = btm_sec_dd_create_conn(p_dev_rec); + + if (status != BTM_CMD_STARTED) + { + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + } + + return status; +} + + +/******************************************************************************* +** +** Function BTM_SecBondCancel +** +** Description This function is called to cancel ongoing bonding process +** with peer device. +** +** Parameters: bd_addr - Address of the peer device +** +*******************************************************************************/ +tBTM_STATUS BTM_SecBondCancel (BD_ADDR bd_addr) +{ + tBTM_SEC_DEV_REC *p_dev_rec; +#if SMP_INCLUDED == TRUE + tACL_CONN *p=NULL; +#endif + + BTM_TRACE_API2 ("BTM_SecBondCancel() State: %s flags:0x%x", + btm_pair_state_descr (btm_cb.pairing_state), btm_cb.pairing_flags); + + if (((p_dev_rec = btm_find_dev (bd_addr)) == NULL) + || (memcmp (btm_cb.pairing_bda, bd_addr, BD_ADDR_LEN) != 0) ) + return BTM_UNKNOWN_ADDR; + +#if SMP_INCLUDED == TRUE + p = btm_bda_to_acl(bd_addr); + if (p && p->is_le_link && + (p_dev_rec->sec_state == BTM_SEC_STATE_AUTHENTICATING)) + { + BTM_TRACE_DEBUG0 ("Cancel LE pairing"); + if (SMP_PairCancel(bd_addr)) + { + return BTM_CMD_STARTED; + } + else + { + return BTM_WRONG_MODE; + } + } + +#endif + BTM_TRACE_DEBUG2 ("hci_handle:0x%x sec_state:%d", p_dev_rec->hci_handle, p_dev_rec->sec_state ); + if (BTM_PAIR_STATE_WAIT_LOCAL_PIN == btm_cb.pairing_state && + BTM_PAIR_FLAGS_WE_STARTED_DD & btm_cb.pairing_flags) + { + /* pre-fetching pin for dedicated bonding */ + btm_sec_bond_cancel_complete(); + return BTM_SUCCESS; + } + + /* If this BDA is in a bonding procedure */ + if ( (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) + && (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD)) + { + /* If the HCI link is up */ + if (p_dev_rec->hci_handle != BTM_SEC_INVALID_HANDLE) + { + /* If some other thread disconnecting, we do not send second command */ + if (p_dev_rec->sec_state == BTM_SEC_STATE_DISCONNECTING) + return(BTM_CMD_STARTED); + + /* If the HCI link was set up by Bonding process */ + if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_DISC_WHEN_DONE) + return btm_sec_send_hci_disconnect(p_dev_rec, HCI_ERR_PEER_USER); + else + l2cu_update_lcb_4_bonding(bd_addr, FALSE); + + return BTM_NOT_AUTHORIZED; + } + else /*HCI link is not up */ + { + /* If the HCI link creation was started by Bonding process */ + if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_DISC_WHEN_DONE) + { + if (btsnd_hcic_create_conn_cancel(bd_addr)) + return BTM_CMD_STARTED; + + return BTM_NO_RESOURCES; + } + + return BTM_NOT_AUTHORIZED; + } + } + + return BTM_WRONG_MODE; +} + +/******************************************************************************* +** +** Function BTM_SecUseMasterLinkKey +** +** Description This function is called to tell master of the piconet to +** switch to master link key +** +** Parameters: use_master_key - If true Master Link Key shoul be used +** +*******************************************************************************/ +tBTM_STATUS BTM_SecUseMasterLinkKey (BOOLEAN use_master_key) +{ + return(btsnd_hcic_master_link_key (use_master_key) ? BTM_SUCCESS : + BTM_NO_RESOURCES); +} + +/******************************************************************************* +** +** Function BTM_SetMasterKeyCompCback +** +** Description This function is called to register for the master key complete +** status event. +** +** Parameters: mkey_cback - callback registered with the security manager +** +*******************************************************************************/ +void BTM_SetMasterKeyCompCback( tBTM_MKEY_CALLBACK *mkey_cback ) +{ + btm_cb.mkey_cback = mkey_cback; +} + +/******************************************************************************* +** +** Function BTM_SecGetDeviceLinkKey +** +** Description This function is called to obtain link key for the device +** it returns BTM_SUCCESS if link key is available, or +** BTM_UNKNOWN_ADDR if Security Manager does not know about +** the device or device record does not contain link key info +** +** Parameters: bd_addr - Address of the device +** link_key - Link Key is copied into this array +** +*******************************************************************************/ +tBTM_STATUS BTM_SecGetDeviceLinkKey (BD_ADDR bd_addr, LINK_KEY link_key) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + + if (((p_dev_rec = btm_find_dev (bd_addr)) != NULL) + && (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN)) + { + memcpy (link_key, p_dev_rec->link_key, LINK_KEY_LEN); + return(BTM_SUCCESS); + } + return(BTM_UNKNOWN_ADDR); +} + + +/******************************************************************************* +** +** Function BTM_SetEncryption +** +** Description This function is called to ensure that connection is +** encrypted. Should be called only on an open connection. +** Typically only needed for connections that first want to +** bring up unencrypted links, then later encrypt them. +** +** Parameters: bd_addr - Address of the peer device +** p_callback - Pointer to callback function called if +** this function returns PENDING after required +** procedures are completed. Can be set to NULL +** if status is not desired. +** p_ref_data - pointer to any data the caller wishes to receive +** in the callback function upon completion. +* can be set to NULL if not used. +** +** Returns BTM_SUCCESS - already encrypted +** BTM_PENDING - command will be returned in the callback +** BTM_WRONG_MODE- connection not up. +** BTM_BUSY - security procedures are currently active +** BTM_MODE_UNSUPPORTED - if security manager not linked in. +** +*******************************************************************************/ +tBTM_STATUS BTM_SetEncryption (BD_ADDR bd_addr, tBTM_SEC_CBACK *p_callback, + void *p_ref_data) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + tBTM_STATUS rc; + +#if BLE_INCLUDED == TRUE + tACL_CONN *p; + p = btm_bda_to_acl(bd_addr); +#endif + + p_dev_rec = btm_find_dev (bd_addr); + if (!p_dev_rec || (p_dev_rec->hci_handle == BTM_SEC_INVALID_HANDLE)) + { + /* Connection should be up and runnning */ + BTM_TRACE_WARNING0 ("Security Manager: BTM_SetEncryption not connected"); + + if (p_callback) + (*p_callback) (bd_addr, p_ref_data, BTM_WRONG_MODE); + + return(BTM_WRONG_MODE); + } + + + if ( +#if BLE_INCLUDED == TRUE + !p->is_le_link && +#endif + (p_dev_rec->sec_flags & (BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED)) + == (BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED)) + { + BTM_TRACE_EVENT0 ("Security Manager: BTM_SetEncryption already encrypted"); + + if (p_callback) + (*p_callback) (bd_addr, p_ref_data, BTM_SUCCESS); + + return(BTM_SUCCESS); + } + + if (p_dev_rec->p_callback) + { + /* Connection should be up and runnning */ + BTM_TRACE_WARNING0 ("Security Manager: BTM_SetEncryption busy"); + + if (p_callback) + (*p_callback) (bd_addr, p_ref_data, BTM_BUSY); + + return(BTM_BUSY); + } + + p_dev_rec->p_callback = p_callback; + p_dev_rec->p_ref_data = p_ref_data; + p_dev_rec->security_required |= (BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_ENCRYPT); + p_dev_rec->is_originator = FALSE; + + BTM_TRACE_API4 ("Security Manager: BTM_SetEncryption Handle:%d State:%d Flags:0x%x Required:0x%x", + p_dev_rec->hci_handle, p_dev_rec->sec_state, p_dev_rec->sec_flags, + p_dev_rec->security_required); +#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE + if (p->is_le_link) + { + rc = btm_ble_set_encryption(bd_addr, p_ref_data, p->link_role); + } + else +#endif + + rc = btm_sec_execute_procedure (p_dev_rec); + + if ( rc != BTM_CMD_STARTED) + { + if (p_callback) + { + p_dev_rec->p_callback = NULL; + (*p_callback) (bd_addr, p_dev_rec->p_ref_data, rc); + } + } + return(rc); +} + +/******************************************************************************* + * disconnect the ACL link, if it's not done yet. +*******************************************************************************/ +static tBTM_STATUS btm_sec_send_hci_disconnect (tBTM_SEC_DEV_REC *p_dev_rec, UINT8 reason) +{ + UINT8 old_state = p_dev_rec->sec_state; + + BTM_TRACE_EVENT2 ("btm_sec_send_hci_disconnect: handle:0x%x, reason=0x%x", + p_dev_rec->hci_handle, reason); + + /* if some other thread disconnecting, we do not send second command */ + if (BTM_SEC_STATE_DISCONNECTING != old_state) + { + p_dev_rec->sec_state = BTM_SEC_STATE_DISCONNECTING; + +#if BTM_DISC_DURING_RS == TRUE + /* If a Role Switch is in progress, delay the HCI Disconnect to avoid controller problem (4329B1) */ + if (p_dev_rec->rs_disc_pending == BTM_SEC_RS_PENDING) + { + BTM_TRACE_ERROR0("RS in progress - Set DISC Pending flag in btm_sec_send_hci_disconnect to delay disconnect"); + p_dev_rec->rs_disc_pending = BTM_SEC_DISC_PENDING; + return BTM_SUCCESS; + } +#endif + /* Tear down the HCI link */ + if (!btsnd_hcic_disconnect (p_dev_rec->hci_handle, reason)) + { + /* could not send disconnect. restore old state */ + p_dev_rec->sec_state = old_state; + return(BTM_NO_RESOURCES); + } + } + return(BTM_CMD_STARTED); +} + +/******************************************************************************* +** +** Function BTM_ConfirmReqReply +** +** Description This function is called to confirm the numeric value for +** Simple Pairing in response to BTM_SP_CFM_REQ_EVT +** +** Parameters: res - result of the operation BTM_SUCCESS if success +** bd_addr - Address of the peer device +** +*******************************************************************************/ +void BTM_ConfirmReqReply(tBTM_STATUS res, BD_ADDR bd_addr) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + + BTM_TRACE_EVENT2 ("BTM_ConfirmReqReply() State: %s Res: %u", + btm_pair_state_descr(btm_cb.pairing_state), res); + + /* If timeout already expired or has been canceled, ignore the reply */ + if ( (btm_cb.pairing_state != BTM_PAIR_STATE_WAIT_NUMERIC_CONFIRM) + || (memcmp (btm_cb.pairing_bda, bd_addr, BD_ADDR_LEN) != 0) ) + return; + + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_AUTH_COMPLETE); + + if ( (res == BTM_SUCCESS) || (res == BTM_SUCCESS_NO_SECURITY) ) + { + btm_cb.acl_disc_reason = HCI_SUCCESS; + + if (res == BTM_SUCCESS) + { + if ((p_dev_rec = btm_find_dev (bd_addr)) != NULL) + p_dev_rec->sec_flags |= BTM_SEC_LINK_KEY_AUTHED; + } + + btsnd_hcic_user_conf_reply (bd_addr, TRUE); + } + else + { + /* Report authentication failed event from state BTM_PAIR_STATE_WAIT_AUTH_COMPLETE */ + btm_cb.acl_disc_reason = HCI_ERR_HOST_REJECT_SECURITY; + btsnd_hcic_user_conf_reply (bd_addr, FALSE); + } +} + +/******************************************************************************* +** +** Function BTM_PasskeyReqReply +** +** Description This function is called to provide the passkey for +** Simple Pairing in response to BTM_SP_KEY_REQ_EVT +** +** Parameters: res - result of the operation BTM_SUCCESS if success +** bd_addr - Address of the peer device +** passkey - numeric value in the range of +** BTM_MIN_PASSKEY_VAL(0) - BTM_MAX_PASSKEY_VAL(999999(0xF423F)). +** +*******************************************************************************/ +#if (BTM_LOCAL_IO_CAPS != BTM_IO_CAP_NONE) +void BTM_PasskeyReqReply(tBTM_STATUS res, BD_ADDR bd_addr, UINT32 passkey) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + + BTM_TRACE_API2 ("BTM_PasskeyReqReply: State: %s res:%d", + btm_pair_state_descr(btm_cb.pairing_state), res); + + if ( (btm_cb.pairing_state == BTM_PAIR_STATE_IDLE) + || (memcmp (btm_cb.pairing_bda, bd_addr, BD_ADDR_LEN) != 0) ) + { + return; + } + + /* If timeout already expired or has been canceled, ignore the reply */ + if ( (btm_cb.pairing_state == BTM_PAIR_STATE_WAIT_AUTH_COMPLETE) && (res != BTM_SUCCESS) ) + { + if ((p_dev_rec = btm_find_dev (bd_addr)) != NULL) + { + btm_cb.acl_disc_reason = HCI_ERR_HOST_REJECT_SECURITY; + + if (p_dev_rec->hci_handle != BTM_SEC_INVALID_HANDLE) + btm_sec_send_hci_disconnect (p_dev_rec, HCI_ERR_AUTH_FAILURE); + else + BTM_SecBondCancel(bd_addr); + + p_dev_rec->sec_flags &= ~(BTM_SEC_LINK_KEY_AUTHED | BTM_SEC_LINK_KEY_KNOWN); + + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + return; + } + } + else if (btm_cb.pairing_state != BTM_PAIR_STATE_KEY_ENTRY) + return; + + if (passkey > BTM_MAX_PASSKEY_VAL) + res = BTM_ILLEGAL_VALUE; + + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_AUTH_COMPLETE); + + if (res != BTM_SUCCESS) + { + /* use BTM_PAIR_STATE_WAIT_AUTH_COMPLETE to report authentication failed event */ + btm_cb.acl_disc_reason = HCI_ERR_HOST_REJECT_SECURITY; + btsnd_hcic_user_passkey_neg_reply (bd_addr); + } + else + { + btm_cb.acl_disc_reason = HCI_SUCCESS; + btsnd_hcic_user_passkey_reply (bd_addr, passkey); + } +} +#endif + +/******************************************************************************* +** +** Function BTM_SendKeypressNotif +** +** Description This function is used during the passkey entry model +** by a device with KeyboardOnly IO capabilities +** (very likely to be a HID Device). +** It is called by a HID Device to inform the remote device when +** a key has been entered or erased. +** +** Parameters: bd_addr - Address of the peer device +** type - notification type +** +*******************************************************************************/ +#if (BTM_LOCAL_IO_CAPS != BTM_IO_CAP_NONE) +void BTM_SendKeypressNotif(BD_ADDR bd_addr, tBTM_SP_KEY_TYPE type) +{ + /* This API only make sense between PASSKEY_REQ and SP complete */ + if (btm_cb.pairing_state == BTM_PAIR_STATE_KEY_ENTRY) + btsnd_hcic_send_keypress_notif (bd_addr, type); +} +#endif + +#if BTM_OOB_INCLUDED == TRUE +/******************************************************************************* +** +** Function BTM_IoCapRsp +** +** Description This function is called in response to BTM_SP_IO_REQ_EVT +** When the event data io_req.oob_data is set to BTM_OOB_UNKNOWN +** by the tBTM_SP_CALLBACK implementation, this function is +** called to provide the actual response +** +** Parameters: bd_addr - Address of the peer device +** io_cap - The IO capability of local device. +** oob - BTM_OOB_NONE or BTM_OOB_PRESENT. +** auth_req- MITM protection required or not. +** +*******************************************************************************/ +void BTM_IoCapRsp(BD_ADDR bd_addr, tBTM_IO_CAP io_cap, tBTM_OOB_DATA oob, tBTM_AUTH_REQ auth_req) +{ + BTM_TRACE_EVENT3 ("BTM_IoCapRsp: state: %s oob: %d io_cap: %d", + btm_pair_state_descr(btm_cb.pairing_state), oob, io_cap); + + if ( (btm_cb.pairing_state != BTM_PAIR_STATE_WAIT_LOCAL_IOCAPS) + || (memcmp (btm_cb.pairing_bda, bd_addr, BD_ADDR_LEN) != 0) ) + return; + + if (oob < BTM_OOB_UNKNOWN && io_cap < BTM_IO_CAP_MAX) + { + btm_cb.devcb.loc_auth_req = auth_req; + btm_cb.devcb.loc_io_caps = io_cap; + + if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) + auth_req = (BTM_AUTH_DD_BOND | (auth_req&BTM_AUTH_YN_BIT)); + + btsnd_hcic_io_cap_req_reply (bd_addr, io_cap, oob, auth_req); + } +} + +/******************************************************************************* +** +** Function BTM_ReadLocalOobData +** +** Description This function is called to read the local OOB data from +** LM +** +*******************************************************************************/ +tBTM_STATUS BTM_ReadLocalOobData(void) +{ + tBTM_STATUS status = BTM_SUCCESS; + + if (btsnd_hcic_read_local_oob_data() == FALSE) + status = BTM_NO_RESOURCES; + + return status; +} + +/******************************************************************************* +** +** Function BTM_RemoteOobDataReply +** +** Description This function is called to provide the remote OOB data for +** Simple Pairing in response to BTM_SP_RMT_OOB_EVT +** +** Parameters: bd_addr - Address of the peer device +** c - simple pairing Hash C. +** r - simple pairing Randomizer C. +** +*******************************************************************************/ +void BTM_RemoteOobDataReply(tBTM_STATUS res, BD_ADDR bd_addr, BT_OCTET16 c, BT_OCTET16 r) +{ + BTM_TRACE_EVENT2 ("BTM_RemoteOobDataReply(): State: %s res:%d", + btm_pair_state_descr(btm_cb.pairing_state), res); + + /* If timeout already expired or has been canceled, ignore the reply */ + if (btm_cb.pairing_state != BTM_PAIR_STATE_WAIT_LOCAL_OOB_RSP) + return; + + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_AUTH_COMPLETE); + + if (res != BTM_SUCCESS) + { + /* use BTM_PAIR_STATE_WAIT_AUTH_COMPLETE to report authentication failed event */ + btm_cb.acl_disc_reason = HCI_ERR_HOST_REJECT_SECURITY; + btsnd_hcic_rem_oob_neg_reply (bd_addr); + } + else + { + btm_cb.acl_disc_reason = HCI_SUCCESS; + btsnd_hcic_rem_oob_reply (bd_addr, c, r); + } +} + +/******************************************************************************* +** +** Function BTM_BuildOobData +** +** Description This function is called to build the OOB data payload to +** be sent over OOB (non-Bluetooth) link +** +** Parameters: p_data - the location for OOB data +** max_len - p_data size. +** c - simple pairing Hash C. +** r - simple pairing Randomizer C. +** name_len- 0, local device name would not be included. +** otherwise, the local device name is included for +** up to this specified length +** +** Returns Number of bytes in p_data. +** +*******************************************************************************/ +UINT16 BTM_BuildOobData(UINT8 *p_data, UINT16 max_len, BT_OCTET16 c, + BT_OCTET16 r, UINT8 name_len) +{ + UINT8 *p = p_data; + UINT16 len = 0; + UINT16 delta; +#if BTM_MAX_LOC_BD_NAME_LEN > 0 + UINT16 name_size; + UINT8 name_type = BTM_EIR_SHORTENED_LOCAL_NAME_TYPE; +#endif + + if (p_data && max_len >= BTM_OOB_MANDATORY_SIZE) + { + /* add mandatory part */ + UINT16_TO_STREAM(p, len); + BDADDR_TO_STREAM(p, btm_cb.devcb.local_addr); + + len = BTM_OOB_MANDATORY_SIZE; + max_len -= len; + + /* now optional part */ + + /* add Hash C */ + delta = BTM_OOB_HASH_C_SIZE + 2; + if (max_len >= delta) + { + *p++ = BTM_OOB_HASH_C_SIZE + 1; + *p++ = BTM_EIR_OOB_SSP_HASH_C_TYPE; + ARRAY_TO_STREAM(p, c, BTM_OOB_HASH_C_SIZE); + len += delta; + max_len -= delta; + } + + /* add Rand R */ + delta = BTM_OOB_RAND_R_SIZE + 2; + if (max_len >= delta) + { + *p++ = BTM_OOB_RAND_R_SIZE + 1; + *p++ = BTM_EIR_OOB_SSP_RAND_R_TYPE; + ARRAY_TO_STREAM(p, r, BTM_OOB_RAND_R_SIZE); + len += delta; + max_len -= delta; + } + + /* add class of device */ + delta = BTM_OOB_COD_SIZE + 2; + if (max_len >= delta) + { + *p++ = BTM_OOB_COD_SIZE + 1; + *p++ = BTM_EIR_OOB_COD_TYPE; + DEVCLASS_TO_STREAM(p, btm_cb.devcb.dev_class); + len += delta; + max_len -= delta; + } +#if BTM_MAX_LOC_BD_NAME_LEN > 0 + name_size = name_len; + if (name_size > strlen(btm_cb.cfg.bd_name)) + { + name_type = BTM_EIR_COMPLETE_LOCAL_NAME_TYPE; + name_size = (UINT16)strlen(btm_cb.cfg.bd_name); + } + delta = name_size + 2; + if (max_len >= delta) + { + *p++ = name_size + 1; + *p++ = name_type; + ARRAY_TO_STREAM (p, btm_cb.cfg.bd_name, name_size); + len += delta; + max_len -= delta; + } +#endif + /* update len */ + p = p_data; + UINT16_TO_STREAM(p, len); + } + return len; +} + +/******************************************************************************* +** +** Function BTM_ReadOobData +** +** Description This function is called to parse the OOB data payload +** received over OOB (non-Bluetooth) link +** +** Parameters: p_data - the location for OOB data +** eir_tag - The associated EIR tag to read the data. +** *p_len(output) - the length of the data with the given tag. +** +** Returns the beginning of the data with the given tag. +** NULL, if the tag is not found. +** +*******************************************************************************/ +UINT8 * BTM_ReadOobData(UINT8 *p_data, UINT8 eir_tag, UINT8 *p_len) +{ + UINT8 *p = p_data; + UINT16 max_len; + UINT8 len, type; + UINT8 *p_ret = NULL; + UINT8 ret_len = 0; + + if (p_data) + { + STREAM_TO_UINT16(max_len, p); + if (max_len >= BTM_OOB_MANDATORY_SIZE) + { + if (BTM_EIR_OOB_BD_ADDR_TYPE == eir_tag) + { + p_ret = p; /* the location for bd_addr */ + ret_len = BTM_OOB_BD_ADDR_SIZE; + } + else + { + p += BD_ADDR_LEN; + max_len -= BTM_OOB_MANDATORY_SIZE; + /* now the optional data in EIR format */ + while (max_len > 0) + { + len = *p++; /* tag data len + 1 */ + type = *p++; + if (eir_tag == type) + { + p_ret = p; + ret_len = len - 1; + break; + } + /* the data size of this tag is len + 1 (tag data len + 2) */ + if (max_len > len) + { + max_len -= len; + max_len--; + len--; + p += len; + } + else + max_len = 0; + } + } + } + } + + if (p_len) + *p_len = ret_len; + + return p_ret; +} +#endif + +/******************************************************************************* +** +** Function BTM_SetOutService +** +** Description This function is called to set the service for +** outgoing connections. +** +** If the profile/application calls BTM_SetSecurityLevel +** before initiating a connection, this function does not +** need to be called. +** +** Returns void +** +*******************************************************************************/ +void BTM_SetOutService(BD_ADDR bd_addr, UINT8 service_id, UINT32 mx_chan_id) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + tBTM_SEC_SERV_REC *p_serv_rec = &btm_cb.sec_serv_rec[0]; + int i; + + btm_cb.p_out_serv = p_serv_rec; + p_dev_rec = btm_find_dev (bd_addr); + + for (i = 0; i < BTM_SEC_MAX_SERVICE_RECORDS; i++, p_serv_rec++) + { + if ((p_serv_rec->security_flags & BTM_SEC_IN_USE) + && (p_serv_rec->service_id == service_id) + && (p_serv_rec->orig_mx_chan_id == mx_chan_id)) + { + BTM_TRACE_API4("BTM_SetOutService p_out_serv id %d, psm 0x%04x, proto_id %d, chan_id %d", + p_serv_rec->service_id, p_serv_rec->psm, p_serv_rec->mx_proto_id, p_serv_rec->orig_mx_chan_id); + btm_cb.p_out_serv = p_serv_rec; + if (p_dev_rec) + p_dev_rec->p_cur_service = p_serv_rec; + break; + } + } +} + +/************************************************************************ +** I N T E R N A L F U N C T I O N S +*************************************************************************/ +/******************************************************************************* +** +** Function btm_sec_check_upgrade +** +** Description This function is called to check if the existing link key +** needs to be upgraded. +** +** Returns void +** +*******************************************************************************/ +static void btm_sec_check_upgrade(tBTM_SEC_DEV_REC *p_dev_rec, BOOLEAN is_originator) +{ + tBTM_SP_UPGRADE evt_data; + UINT16 mtm_check = is_originator ? BTM_SEC_OUT_MITM : BTM_SEC_IN_MITM; + + if (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN) + { + + BTM_TRACE_DEBUG5 ("btm_sec_check_upgrade id:%d, link_key_typet:%d, rmt_io_caps:%d, chk flags:x%x, flags:x%x", + p_dev_rec->p_cur_service->service_id, p_dev_rec->link_key_type, p_dev_rec->rmt_io_caps, + mtm_check, p_dev_rec->p_cur_service->security_flags); + /* Already have a link key to the connected peer. Is the link key secure enough? + ** Is a link key upgrade even possible? + */ + if ((p_dev_rec->security_required & mtm_check) /* needs MITM */ + && (p_dev_rec->link_key_type == BTM_LKEY_TYPE_UNAUTH_COMB) /* has unauthenticated link key */ + && (p_dev_rec->rmt_io_caps < BTM_IO_CAP_MAX) /* a valid peer IO cap */ + && (btm_sec_io_map[p_dev_rec->rmt_io_caps][btm_cb.devcb.loc_io_caps])) /* authenticated link key is possible */ + { + BTM_TRACE_DEBUG1 ("need upgrade!! sec_flags:0x%x", p_dev_rec->sec_flags); + /* upgrade is possible: check if the application wants the upgrade. + * If the application is configured to use a global MITM flag, + * it probably would not want to upgrade the link key based on the security level database */ + memcpy (evt_data.bd_addr, p_dev_rec->bd_addr, BD_ADDR_LEN); + evt_data.upgrade = TRUE; + if (btm_cb.api.p_sp_callback) + (*btm_cb.api.p_sp_callback) (BTM_SP_UPGRADE_EVT, (tBTM_SP_EVT_DATA *)&evt_data); + + BTM_TRACE_DEBUG1 ("evt_data.upgrade:0x%x", evt_data.upgrade); + if (evt_data.upgrade) + { + /* if the application confirms the upgrade, set the upgrade bit */ + p_dev_rec->sm4 |= BTM_SM4_UPGRADE; + + /* Clear the link key known to go through authentication/pairing again */ + p_dev_rec->sec_flags &= ~(BTM_SEC_LINK_KEY_KNOWN | BTM_SEC_LINK_KEY_AUTHED); + p_dev_rec->sec_flags &= ~BTM_SEC_AUTHENTICATED; + BTM_TRACE_DEBUG1 ("sec_flags:0x%x", p_dev_rec->sec_flags); + } + } + } +} + +/******************************************************************************* +** +** Function btm_sec_l2cap_access_req +** +** Description This function is called by the L2CAP to grant permission to +** establish L2CAP connection to or from the peer device. +** +** Parameters: bd_addr - Address of the peer device +** psm - L2CAP PSM +** is_originator - TRUE if protocol above L2CAP originates +** connection +** p_callback - Pointer to callback function called if +** this function returns PENDING after required +** procedures are complete. MUST NOT BE NULL. +** +** Returns tBTM_STATUS +** +*******************************************************************************/ +#define BTM_SEC_OUT_FLAGS (BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_ENCRYPT | BTM_SEC_OUT_AUTHORIZE) +#define BTM_SEC_IN_FLAGS (BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_ENCRYPT | BTM_SEC_IN_AUTHORIZE) + +tBTM_STATUS btm_sec_l2cap_access_req (BD_ADDR bd_addr, UINT16 psm, UINT16 handle, + CONNECTION_TYPE conn_type, + tBTM_SEC_CALLBACK *p_callback, + void *p_ref_data) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + tBTM_SEC_SERV_REC *p_serv_rec; + UINT16 security_required; + UINT16 old_security_required; + BOOLEAN old_is_originator; + tBTM_STATUS rc = BTM_SUCCESS; + BOOLEAN chk_acp_auth_done = FALSE; + BOOLEAN is_originator; + +#if (L2CAP_UCD_INCLUDED == TRUE) + if (conn_type & CONNECTION_TYPE_ORIG_MASK) + is_originator = TRUE; + else + is_originator = FALSE; + + BTM_TRACE_DEBUG2 ("btm_sec_l2cap_access_req conn_type:0x%x, 0x%x", conn_type, p_ref_data); +#else + is_originator = conn_type; + + BTM_TRACE_DEBUG2 ("btm_sec_l2cap_access_req is_originator:%d, 0x%x", is_originator, p_ref_data); +#endif + + /* Find or get oldest record */ + p_dev_rec = btm_find_or_alloc_dev (bd_addr); + + p_dev_rec->hci_handle = handle; + + /* Find the service record for the PSM */ + p_serv_rec = btm_sec_find_first_serv (conn_type, psm); + + /* If there is no application registered with this PSM do not allow connection */ + if (!p_serv_rec) + { + BTM_TRACE_WARNING1 ("btm_sec_l2cap_access_req() PSM:%d no application registerd", psm); + + (*p_callback) (bd_addr, p_ref_data, BTM_MODE_UNSUPPORTED); + + return(BTM_MODE_UNSUPPORTED); + } + + /* SDP connection we will always let through */ + if (BT_PSM_SDP == psm) + { + (*p_callback) (bd_addr, p_ref_data, BTM_SUCCESS_NO_SECURITY); + + return(BTM_SUCCESS); + } +#if (L2CAP_UCD_INCLUDED == TRUE) + if ( conn_type & CONNECTION_TYPE_CONNLESS_MASK ) + { + security_required = p_serv_rec->ucd_security_flags; + + rc = BTM_CMD_STARTED; + if (is_originator) + { + if (((security_required & BTM_SEC_OUT_FLAGS) == 0) || + ((((security_required & BTM_SEC_OUT_FLAGS) == BTM_SEC_OUT_AUTHENTICATE) && (p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED))) || + ((((security_required & BTM_SEC_OUT_FLAGS) == (BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_ENCRYPT)) && (p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED))) || + ((((security_required & BTM_SEC_OUT_FLAGS) == BTM_SEC_OUT_FLAGS) && (p_dev_rec->sec_flags & BTM_SEC_AUTHORIZED))) ) + { + rc = BTM_SUCCESS; + } + } + else + { + if (((security_required & BTM_SEC_IN_FLAGS) == 0) || + ((((security_required & BTM_SEC_IN_FLAGS) == BTM_SEC_IN_AUTHENTICATE) && (p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED))) || + ((((security_required & BTM_SEC_IN_FLAGS) == (BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_ENCRYPT)) && (p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED))) || + ((((security_required & BTM_SEC_IN_FLAGS) == BTM_SEC_IN_FLAGS) && (p_dev_rec->sec_flags & BTM_SEC_AUTHORIZED))) ) + { + rc = BTM_SUCCESS; + } + } + + if (rc == BTM_SUCCESS) + { + if (p_callback) + (*p_callback) (bd_addr, (void *)p_ref_data, BTM_SUCCESS); + + return(BTM_SUCCESS); + } + } + else +#endif + { + security_required = p_serv_rec->security_flags; + } + + /* there are some devices (moto KRZR) which connects to several services at the same time */ + /* we will process one after another */ + if ( (p_dev_rec->p_callback) || (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) ) + { + BTM_TRACE_EVENT2 ("btm_sec_l2cap_access_req() - busy - PSM:%d delayed state: %s", + psm, btm_pair_state_descr(btm_cb.pairing_state)); + rc = BTM_CMD_STARTED; + if ((BTM_SEC_MODE_SP != btm_cb.security_mode) + || ((BTM_SEC_MODE_SP == btm_cb.security_mode) && (BTM_SM4_KNOWN == p_dev_rec->sm4)) + ) + { + BTM_TRACE_EVENT2 ("security_flags:x%x, sec_flags:x%x", security_required, p_dev_rec->sec_flags); + /* legacy mode - local is legacy or local is lisbon/peer is legacy */ + if (is_originator) + { + if (((security_required & BTM_SEC_OUT_FLAGS) == 0) || + ((((security_required & BTM_SEC_OUT_FLAGS) == BTM_SEC_OUT_AUTHENTICATE) && (p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED))) || + ((((security_required & BTM_SEC_OUT_FLAGS) == (BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_ENCRYPT)) && (p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED))) || + ((((security_required & BTM_SEC_OUT_FLAGS) == BTM_SEC_OUT_FLAGS) && (p_dev_rec->sec_flags & BTM_SEC_AUTHORIZED))) ) + { + rc = BTM_SUCCESS; + } + } + else + { + if (((security_required & BTM_SEC_IN_FLAGS) == 0) || + ((((security_required & BTM_SEC_IN_FLAGS) == BTM_SEC_IN_AUTHENTICATE) && (p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED))) || + ((((security_required & BTM_SEC_IN_FLAGS) == (BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_ENCRYPT)) && (p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED))) || + ((((security_required & BTM_SEC_IN_FLAGS) == BTM_SEC_IN_FLAGS) && (p_dev_rec->sec_flags & BTM_SEC_AUTHORIZED))) ) + { + rc = BTM_SUCCESS; + } + } + + if (rc == BTM_SUCCESS) + { + if (p_callback) + (*p_callback) (bd_addr, (void *)p_ref_data, BTM_SUCCESS); + + return(BTM_SUCCESS); + } + } + + btm_cb.sec_req_pending = TRUE; + return(BTM_CMD_STARTED); + } + + /* Save pointer to service record */ + p_dev_rec->p_cur_service = p_serv_rec; + + + /* mess /w security_required in btm_sec_l2cap_access_req for Lisbon */ + if (btm_cb.security_mode == BTM_SEC_MODE_SP) + { + if (is_originator) + { + if (BTM_SEC_IS_SM4(p_dev_rec->sm4)) + { + /* SM4 to SM4 -> always authenticate & encrypt */ + security_required |= (BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_ENCRYPT); + } + else + { + if ( !(BTM_SM4_KNOWN & p_dev_rec->sm4)) + { + BTM_TRACE_DEBUG1 ("remote features unknown!!sec_flags:0x%x", p_dev_rec->sec_flags); + /* the remote features are not known yet */ + p_dev_rec->sm4 |= BTM_SM4_REQ_PEND; + + return(BTM_CMD_STARTED); + } + } + } + else + { + /* responder */ + if (BTM_SEC_IS_SM4(p_dev_rec->sm4)) + { + /* SM4 to SM4: the acceptor needs to make sure the authentication is already done */ + chk_acp_auth_done = TRUE; + /* SM4 to SM4 -> always authenticate & encrypt */ + security_required |= (BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_ENCRYPT); + } + else + { + if ( !(BTM_SM4_KNOWN & p_dev_rec->sm4)) + { + BTM_TRACE_DEBUG1 ("(rsp) remote features unknown!!sec_flags:0x%x", p_dev_rec->sec_flags); + /* the remote features are not known yet */ + p_dev_rec->sm4 |= BTM_SM4_REQ_PEND; + + return(BTM_CMD_STARTED); + } + } + } + } + + BTM_TRACE_DEBUG4 ("btm_sec_l2cap_access_req() sm4:0x%x, sec_flags:0x%x, security_required:0x%x chk:%d", + p_dev_rec->sm4, p_dev_rec->sec_flags, security_required, chk_acp_auth_done); + + old_security_required = p_dev_rec->security_required; + old_is_originator = p_dev_rec->is_originator; + p_dev_rec->security_required = security_required; + p_dev_rec->p_ref_data = p_ref_data; + p_dev_rec->is_originator = is_originator; + +#if (L2CAP_UCD_INCLUDED == TRUE) + if ( conn_type & CONNECTION_TYPE_CONNLESS_MASK ) + p_dev_rec->is_ucd = TRUE; + else + p_dev_rec->is_ucd = FALSE; +#endif + + /* If there are multiple service records used through the same PSM */ + /* leave security decision for the multiplexor on the top */ +#if (L2CAP_UCD_INCLUDED == TRUE) + if (((btm_sec_find_next_serv (p_serv_rec)) != NULL) + &&(!( conn_type & CONNECTION_TYPE_CONNLESS_MASK ))) /* if not UCD */ +#else + if ((btm_sec_find_next_serv (p_serv_rec)) != NULL) +#endif + { + BTM_TRACE_DEBUG2 ("no next_serv sm4:0x%x, chk:%d", p_dev_rec->sm4, chk_acp_auth_done); + if (!BTM_SEC_IS_SM4(p_dev_rec->sm4)) + { + BTM_TRACE_EVENT1 ("Security Manager: l2cap_access_req PSM:%d postponed for multiplexer", psm); + /* pre-Lisbon: restore the old settings */ + p_dev_rec->security_required = old_security_required; + p_dev_rec->is_originator = old_is_originator; + + (*p_callback) (bd_addr, p_ref_data, BTM_SUCCESS); + + return(BTM_SUCCESS); + } + } + + /* if the originator is using dynamic PSM in legacy mode, do not start any security process now. + * The layer above L2CAP needs to carry out the security requirement after L2CAP connect response is received*/ + if (is_originator && (btm_cb.security_mode != BTM_SEC_MODE_SP || !BTM_SEC_IS_SM4(p_dev_rec->sm4)) && (psm >= 0x1001)) + { + BTM_TRACE_EVENT1 ("dynamic PSM:0x%x in legacy mode - postponed for upper layer", psm); + /* restore the old settings */ + p_dev_rec->security_required = old_security_required; + p_dev_rec->is_originator = old_is_originator; + + (*p_callback) (bd_addr, p_ref_data, BTM_SUCCESS); + + return(BTM_SUCCESS); + } + + if (chk_acp_auth_done) + { + BTM_TRACE_DEBUG2 ("(SM4 to SM4) btm_sec_l2cap_access_req rspd. authenticated: x%x, enc: x%x", + (p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED), (p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED)); + /* SM4, but we do not know for sure which level of security we need. + * as long as we have a link key, it's OK */ + if ((0 == (p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED)) + ||(0 == (p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED))) + { + rc = BTM_DELAY_CHECK; + /* + 2046 may report HCI_Encryption_Change and L2C Connection Request out of sequence + because of data path issues. Delay this disconnect a little bit + */ + BTM_TRACE_ERROR0 ("peer should have initiated security process by now (SM4 to SM4)"); + p_dev_rec->p_callback = p_callback; + p_dev_rec->sec_state = BTM_SEC_STATE_DELAY_FOR_ENC; + (*p_callback) (bd_addr, p_ref_data, rc); + + return(BTM_SUCCESS); + } + } + + p_dev_rec->p_callback = p_callback; + + /* Although authentication and encryption are per connection */ + /* authorization is per access request. For example when serial connection */ + /* is up and authorized and client requests to read file (access to other */ + /* scn, we need to request user's permission again. */ + p_dev_rec->sec_flags &= ~BTM_SEC_AUTHORIZED; + + if (BTM_SEC_IS_SM4(p_dev_rec->sm4)) + { + /* If we already have a link key to the connected peer, is the link key secure enough ? */ + btm_sec_check_upgrade(p_dev_rec, is_originator); + } + + BTM_TRACE_EVENT6 ("Security Manager: l2cap_access_req PSM:%d Handle:%d State:%d Flags:0x%x Required:0x%x Service ID:%d", + psm, handle, p_dev_rec->sec_state, p_dev_rec->sec_flags, p_dev_rec->security_required, p_dev_rec->p_cur_service->service_id); + + if ((rc = btm_sec_execute_procedure (p_dev_rec)) != BTM_CMD_STARTED) + { + p_dev_rec->p_callback = NULL; + (*p_callback) (bd_addr, p_dev_rec->p_ref_data, (UINT8)rc); + } + + return(rc); +} + +/******************************************************************************* +** +** Function btm_sec_mx_access_request +** +** Description This function is called by all Multiplexing Protocols during +** establishing connection to or from peer device to grant +** permission to establish application connection. +** +** Parameters: bd_addr - Address of the peer device +** psm - L2CAP PSM +** is_originator - TRUE if protocol above L2CAP originates +** connection +** mx_proto_id - protocol ID of the multiplexer +** mx_chan_id - multiplexer channel to reach application +** p_callback - Pointer to callback function called if +** this function returns PENDING after required +** procedures are completed +** p_ref_data - Pointer to any reference data needed by the +** the callback function. +** +** Returns BTM_CMD_STARTED +** +*******************************************************************************/ +tBTM_STATUS btm_sec_mx_access_request (BD_ADDR bd_addr, UINT16 psm, BOOLEAN is_originator, + UINT32 mx_proto_id, UINT32 mx_chan_id, + tBTM_SEC_CALLBACK *p_callback, void *p_ref_data) + +{ + tBTM_SEC_DEV_REC *p_dev_rec; + tBTM_SEC_SERV_REC *p_serv_rec; + tBTM_STATUS rc; + + BTM_TRACE_DEBUG1 ("btm_sec_mx_access_request is_originator:%d", is_originator); + /* Find or get oldest record */ + p_dev_rec = btm_find_or_alloc_dev (bd_addr); + + /* Find the service record for the PSM */ + p_serv_rec = btm_sec_find_mx_serv (is_originator, psm, mx_proto_id, mx_chan_id); + + /* If there is no application registered with this PSM do not allow connection */ + if (!p_serv_rec) + { + if (p_callback) + (*p_callback) (bd_addr, p_ref_data, BTM_MODE_UNSUPPORTED); + + BTM_TRACE_ERROR3 ("Security Manager: MX service not found PSM:%d Proto:%d SCN:%d", + psm, mx_proto_id, mx_chan_id); + return BTM_NO_RESOURCES; + } + + /* there are some devices (moto phone) which connects to several services at the same time */ + /* we will process one after another */ + if ( (p_dev_rec->p_callback) || (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) ) + { + BTM_TRACE_EVENT4 ("btm_sec_mx_access_request service PSM:%d Proto:%d SCN:%d delayed state: %s", + psm, mx_proto_id, mx_chan_id, btm_pair_state_descr(btm_cb.pairing_state)); + + btm_sec_queue_mx_request (bd_addr, psm, is_originator, mx_proto_id, mx_chan_id, p_callback, p_ref_data); + return BTM_CMD_STARTED; + } + + p_dev_rec->p_cur_service = p_serv_rec; + p_dev_rec->security_required = p_serv_rec->security_flags; + + if (BTM_SEC_MODE_SP == btm_cb.security_mode) + { + if (BTM_SEC_IS_SM4(p_dev_rec->sm4)) + { + /* If we already have a link key, check if that link key is good enough */ + btm_sec_check_upgrade(p_dev_rec, is_originator); + } + } + + p_dev_rec->is_originator = is_originator; + p_dev_rec->p_callback = p_callback; + p_dev_rec->p_ref_data = p_ref_data; + + /* Although authentication and encryption are per connection */ + /* authorization is per access request. For example when serial connection */ + /* is up and authorized and client requests to read file (access to other */ + /* scn, we need to request user's permission again. */ + p_dev_rec->sec_flags &= ~(BTM_SEC_AUTHORIZED); + + BTM_TRACE_EVENT6 ("Security Manager: mx_access_req proto_id:%d chan_id:%d State:%d Flags:0x%x Required:0x%x Service ID:%d", + mx_proto_id, mx_chan_id, p_dev_rec->sec_state, p_dev_rec->sec_flags, p_dev_rec->security_required, p_dev_rec->p_cur_service->service_id); + + if ((rc = btm_sec_execute_procedure (p_dev_rec)) != BTM_CMD_STARTED) + { + if (p_callback) + { + p_dev_rec->p_callback = NULL; + + (*p_callback) (bd_addr, p_ref_data, (UINT8)rc); + } + } + + return rc; +} + +/******************************************************************************* +** +** Function btm_sec_conn_req +** +** Description This function is when the peer device is requesting +** connection +** +** Returns void +** +*******************************************************************************/ +void btm_sec_conn_req (UINT8 *bda, UINT8 *dc) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bda); + + /* Some device may request a connection before we are done with the HCI_Reset sequence */ + if (btm_cb.devcb.state != BTM_DEV_STATE_READY) + { + BTM_TRACE_EVENT0 ("Security Manager: connect request when device not ready"); + btsnd_hcic_reject_conn (bda, HCI_ERR_HOST_REJECT_DEVICE); + return; + } + + /* Security guys wants us not to allow connection from not paired devices */ + + /* Check if connection is allowed for only paired devices */ + if (btm_cb.connect_only_paired) + { + if (!p_dev_rec || !(p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_AUTHED)) + { + BTM_TRACE_EVENT0 ("Security Manager: connect request from non-paired device"); + btsnd_hcic_reject_conn (bda, HCI_ERR_HOST_REJECT_DEVICE); + return; + } + } + +#if BTM_ALLOW_CONN_IF_NONDISCOVER == FALSE + /* If non-discoverable, only allow known devices to connect */ + if (btm_cb.btm_inq_vars.discoverable_mode == BTM_NON_DISCOVERABLE) + { + if (!p_dev_rec) + { + BTM_TRACE_EVENT0 ("Security Manager: connect request from not paired device"); + btsnd_hcic_reject_conn (bda, HCI_ERR_HOST_REJECT_DEVICE); + return; + } + } +#endif + + /* Host can be registered to verify comming BDA or DC */ + if (btm_cb.p_conn_filter_cb) + { + if (!(* btm_cb.p_conn_filter_cb) (bda, dc)) + { + BTM_TRACE_EVENT0 ("Security Manager: connect request did not pass filter"); + + /* incomming call did not pass connection filters. Reject */ + btsnd_hcic_reject_conn (bda, HCI_ERR_HOST_REJECT_DEVICE); + return; + } + } + + if ((btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) + &&(btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) + &&(!memcmp (btm_cb.pairing_bda, bda, BD_ADDR_LEN))) + { + BTM_TRACE_EVENT0 ("Security Manager: reject connect request from bonding device"); + + /* incoming connection from bonding device is rejected */ + btm_cb.pairing_flags |= BTM_PAIR_FLAGS_REJECTED_CONNECT; + btsnd_hcic_reject_conn (bda, HCI_ERR_HOST_REJECT_DEVICE); + return; + } + + /* Host is not interested or approved connection. Save BDA and DC and */ + /* pass request to L2CAP */ + memcpy (btm_cb.connecting_bda, bda, BD_ADDR_LEN); + memcpy (btm_cb.connecting_dc, dc, DEV_CLASS_LEN); + + if (l2c_link_hci_conn_req (bda)) + { + if (!p_dev_rec) + { + /* accept the connection -> allocate a device record */ + p_dev_rec = btm_sec_alloc_dev (bda); + } + if (p_dev_rec) + { + p_dev_rec->sm4 |= BTM_SM4_CONN_PEND; + } + } +} + +/******************************************************************************* +** +** Function btm_sec_bond_cancel_complete +** +** Description This function is called to report bond cancel complete +** event. +** +** Returns void +** +*******************************************************************************/ +static void btm_sec_bond_cancel_complete (void) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + + if ((btm_cb.pairing_flags & BTM_PAIR_FLAGS_DISC_WHEN_DONE) || + (BTM_PAIR_STATE_WAIT_LOCAL_PIN == btm_cb.pairing_state && + BTM_PAIR_FLAGS_WE_STARTED_DD & btm_cb.pairing_flags)) + { + /* for dedicated bonding in legacy mode, authentication happens at "link level" + * btm_sec_connected is called with failed status. + * In theory, the code that handles is_pairing_device/TRUE should clean out security related code. + * However, this function may clean out the security related flags and btm_sec_connected would not know + * this function also needs to do proper clean up. + */ + if ((p_dev_rec = btm_find_dev (btm_cb.pairing_bda)) != NULL) + p_dev_rec->security_required = BTM_SEC_NONE; + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + + /* Notify application that the cancel succeeded */ + if (btm_cb.api.p_bond_cancel_cmpl_callback) + btm_cb.api.p_bond_cancel_cmpl_callback(BTM_SUCCESS); + } +} + +/******************************************************************************* +** +** Function btm_create_conn_cancel_complete +** +** Description This function is called when the command complete message +** is received from the HCI for the create connection cancel +** command. +** +** Returns void +** +*******************************************************************************/ +void btm_create_conn_cancel_complete (UINT8 *p) +{ + UINT8 status; + + STREAM_TO_UINT8 (status, p); + BTM_TRACE_EVENT2 ("btm_create_conn_cancel_complete(): in State: %s status:%d", + btm_pair_state_descr(btm_cb.pairing_state), status); + + /* if the create conn cancel cmd was issued by the bond cancel, + ** the application needs to be notified that bond cancel succeeded + */ + switch (status) + { + case HCI_SUCCESS: + btm_sec_bond_cancel_complete(); + break; + case HCI_ERR_CONNECTION_EXISTS: + case HCI_ERR_NO_CONNECTION: + default: + /* Notify application of the error */ + if (btm_cb.api.p_bond_cancel_cmpl_callback) + btm_cb.api.p_bond_cancel_cmpl_callback(BTM_ERR_PROCESSING); + break; + } +} + +/******************************************************************************* +** +** Function btm_sec_check_pending_reqs +** +** Description This function is called at the end of the security procedure +** to let L2CAP and RFCOMM know to re-submit any pending requests +** +** Returns void +** +*******************************************************************************/ +void btm_sec_check_pending_reqs (void) +{ + tBTM_SEC_QUEUE_ENTRY *p_e; + BUFFER_Q bq; + + if (btm_cb.pairing_state == BTM_PAIR_STATE_IDLE) + { + /* First, resubmit L2CAP requests */ + if (btm_cb.sec_req_pending) + { + btm_cb.sec_req_pending = FALSE; + l2cu_resubmit_pending_sec_req (NULL); + } + + /* Now, re-submit anything in the mux queue */ + bq = btm_cb.sec_pending_q; + + GKI_init_q (&btm_cb.sec_pending_q); + + while ((p_e = (tBTM_SEC_QUEUE_ENTRY *)GKI_dequeue (&bq)) != NULL) + { + /* Check that the ACL is still up before starting security procedures */ + if (btm_bda_to_acl(p_e->bd_addr) != NULL) + { + BTM_TRACE_EVENT4 ("btm_sec_check_pending_reqs() submitting PSM: 0x%04x Is_Orig: %u mx_proto_id: %u mx_chan_id: %u", + p_e->psm, p_e->is_orig, p_e->mx_proto_id, p_e->mx_chan_id); + + btm_sec_mx_access_request (p_e->bd_addr, p_e->psm, p_e->is_orig, + p_e->mx_proto_id, p_e->mx_chan_id, + p_e->p_callback, p_e->p_ref_data); + } + + GKI_freebuf (p_e); + } + } +} + +/******************************************************************************* +** +** Function btm_sec_init +** +** Description This function is on the SEC startup +** +** Returns void +** +*******************************************************************************/ +void btm_sec_init (UINT8 sec_mode) +{ +#if 0 /* cleared in btm_init; put back in if calling from anywhere else! */ + int i; + + memset (btm_cb.sec_serv_rec, 0, sizeof (btm_cb.sec_serv_rec)); + memset (btm_cb.sec_dev_rec, 0, sizeof (btm_cb.sec_dev_rec)); + memset (&btm_cb.pairing_tle, 0, sizeof(TIMER_LIST_ENT)); + +#endif + btm_cb.security_mode = sec_mode; + memset (btm_cb.pairing_bda, 0xff, BD_ADDR_LEN); + btm_cb.max_collision_delay = BTM_SEC_MAX_COLLISION_DELAY; +} + +/******************************************************************************* +** +** Function btm_sec_device_down +** +** Description This function should be called when device is disabled or +** turned off +** +** Returns void +** +*******************************************************************************/ +void btm_sec_device_down (void) +{ + BTM_TRACE_EVENT1 ("btm_sec_device_down() State: %s", btm_pair_state_descr(btm_cb.pairing_state)); + + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); +} + +/******************************************************************************* +** +** Function btm_sec_dev_reset +** +** Description This function should be called after device reset +** +** Returns void +** +*******************************************************************************/ +void btm_sec_dev_reset (void) +{ +#if (BTM_PRE_LISBON_INCLUDED == TRUE) + if (btm_cb.security_mode == BTM_SEC_MODE_LINK) + { + btsnd_hcic_write_auth_enable (TRUE); + btsnd_hcic_write_encr_mode (HCI_ENCRYPT_MODE_POINT_TO_POINT); + } +#endif +#if (BTM_PRE_LISBON_INCLUDED == TRUE) + else +#endif + /* this function is only called from btm_read_local_features_complete() + * right now. */ + if (HCI_SIMPLE_PAIRING_SUPPORTED(btm_cb.devcb.local_features)) + { + btsnd_hcic_write_simple_pairing_mode(HCI_SP_MODE_ENABLED); +#if BLE_INCLUDED == TRUE + btsnd_hcic_set_event_mask(LOCAL_BR_EDR_CONTROLLER_ID, + (UINT8 *)HCI_DUMO_EVENT_MASK_EXT); +#else + btsnd_hcic_set_event_mask(LOCAL_BR_EDR_CONTROLLER_ID, + (UINT8 *)HCI_LISBON_EVENT_MASK_EXT); +#endif + /* set the default IO capabilities */ + btm_cb.devcb.loc_io_caps = BTM_LOCAL_IO_CAPS; + /* add mx service to use no security */ +#if (RFCOMM_INCLUDED == TRUE) + BTM_SetSecurityLevel(FALSE, "RFC_MUX", BTM_SEC_SERVICE_RFC_MUX, + BTM_SEC_NONE, BT_PSM_RFCOMM, BTM_SEC_PROTO_RFCOMM, 0); +#endif + } + else + { + btm_cb.security_mode = BTM_SEC_MODE_SERVICE; + } + + BTM_TRACE_DEBUG1 ("btm_sec_dev_reset sec mode: %d", btm_cb.security_mode); +} + +/******************************************************************************* +** +** Function btm_sec_abort_access_req +** +** Description This function is called by the L2CAP or RFCOMM to abort +** the pending operation. +** +** Parameters: bd_addr - Address of the peer device +** +** Returns void +** +*******************************************************************************/ +void btm_sec_abort_access_req (BD_ADDR bd_addr) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bd_addr); + + if (!p_dev_rec) + return; + + if (btm_cb.api.p_abort_callback) + (*btm_cb.api.p_abort_callback)(bd_addr, p_dev_rec->dev_class, p_dev_rec->sec_bd_name); + + if ((p_dev_rec->sec_state != BTM_SEC_STATE_AUTHORIZING) + && (p_dev_rec->sec_state != BTM_SEC_STATE_AUTHENTICATING)) + return; + + p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; + p_dev_rec->p_callback = NULL; +} + +/******************************************************************************* +** +** Function btm_sec_dd_create_conn +** +** Description This function is called to create the ACL connection for +** the dedicated boding process +** +** Returns void +** +*******************************************************************************/ +static tBTM_STATUS btm_sec_dd_create_conn (tBTM_SEC_DEV_REC *p_dev_rec) +{ + tL2C_LCB *p_lcb; + + /* Make sure an L2cap link control block is available */ + if ((p_lcb = l2cu_allocate_lcb (p_dev_rec->bd_addr, TRUE)) == NULL) + { + BTM_TRACE_WARNING6 ("Security Manager: failed allocate LCB [%02x%02x%02x%02x%02x%02x]", + p_dev_rec->bd_addr[0], p_dev_rec->bd_addr[1], p_dev_rec->bd_addr[2], + p_dev_rec->bd_addr[3], p_dev_rec->bd_addr[4], p_dev_rec->bd_addr[5]); + + return(BTM_NO_RESOURCES); + } + + /* set up the control block to indicated dedicated bonding */ + btm_cb.pairing_flags |= BTM_PAIR_FLAGS_DISC_WHEN_DONE; + + if (l2cu_create_conn(p_lcb) == FALSE) + { + BTM_TRACE_WARNING6 ("Security Manager: failed create [%02x%02x%02x%02x%02x%02x]", + p_dev_rec->bd_addr[0], p_dev_rec->bd_addr[1], p_dev_rec->bd_addr[2], + p_dev_rec->bd_addr[3], p_dev_rec->bd_addr[4], p_dev_rec->bd_addr[5]); + + l2cu_release_lcb(p_lcb); + return(BTM_NO_RESOURCES); + } + +#if (defined(BTM_BUSY_LEVEL_CHANGE_INCLUDED) && BTM_BUSY_LEVEL_CHANGE_INCLUDED == TRUE) + btm_acl_update_busy_level (BTM_BLI_PAGE_EVT); +#endif + + BTM_TRACE_DEBUG6 ("Security Manager: btm_sec_dd_create_conn [%02x%02x%02x%02x%02x%02x]", + p_dev_rec->bd_addr[0], p_dev_rec->bd_addr[1], p_dev_rec->bd_addr[2], + p_dev_rec->bd_addr[3], p_dev_rec->bd_addr[4], p_dev_rec->bd_addr[5]); + + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_PIN_REQ); + + return(BTM_CMD_STARTED); +} + +/******************************************************************************* +** +** Function btm_sec_rmt_name_request_complete +** +** Description This function is called when remote name was obtained from +** the peer device +** +** Returns void +** +*******************************************************************************/ +void btm_sec_rmt_name_request_complete (UINT8 *p_bd_addr, UINT8 *p_bd_name, UINT8 status) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + int i; + DEV_CLASS dev_class; + UINT8 old_sec_state; + + BTM_TRACE_EVENT0 ("btm_sec_rmt_name_request_complete"); + if (((p_bd_addr == NULL) && !BTM_ACL_IS_CONNECTED(btm_cb.connecting_bda)) + || ((p_bd_addr != NULL) && !BTM_ACL_IS_CONNECTED(p_bd_addr))) + { + btm_acl_resubmit_page(); + } + + /* If remote name request failed, p_bd_addr is null and we need to search */ + /* based on state assuming that we are doing 1 at a time */ + if (p_bd_addr) + p_dev_rec = btm_find_dev (p_bd_addr); + else + { + p_dev_rec = &btm_cb.sec_dev_rec[0]; + + for (i = 0; i < BTM_SEC_MAX_DEVICE_RECORDS; i++, p_dev_rec++) + { + if ((p_dev_rec->sec_flags & BTM_SEC_IN_USE) + && (p_dev_rec->sec_state == BTM_SEC_STATE_GETTING_NAME)) + { + p_bd_addr = p_dev_rec->bd_addr; + break; + } + } + + if (i == BTM_SEC_MAX_DEVICE_RECORDS) + p_dev_rec = NULL; + } + + + /* Commenting out trace due to obf/compilation problems. + */ +#if (BT_USE_TRACES == TRUE) + if (!p_bd_name) + p_bd_name = (UINT8 *)""; + + if (p_dev_rec) + { + BTM_TRACE_EVENT5 ("Security Manager: rmt_name_complete PairState: %s RemName: %s status: %d State:%d p_dev_rec: 0x%08x ", + btm_pair_state_descr (btm_cb.pairing_state), p_bd_name, + status, p_dev_rec->sec_state, p_dev_rec); + } + else + { + BTM_TRACE_EVENT3 ("Security Manager: rmt_name_complete PairState: %s RemName: %s status: %d", + btm_pair_state_descr (btm_cb.pairing_state), p_bd_name, + status); + } +#endif + + if (p_dev_rec) + { + old_sec_state = p_dev_rec->sec_state; + if (status == HCI_SUCCESS) + { + BCM_STRNCPY_S ((char *)p_dev_rec->sec_bd_name, sizeof (p_dev_rec->sec_bd_name), (char *)p_bd_name, BTM_MAX_REM_BD_NAME_LEN); + p_dev_rec->sec_flags |= BTM_SEC_NAME_KNOWN; + BTM_TRACE_EVENT1 ("setting BTM_SEC_NAME_KNOWN sec_flags:0x%x", p_dev_rec->sec_flags); + } + else + { + /* Notify all clients waiting for name to be resolved even if it failed so clients can continue */ + p_dev_rec->sec_bd_name[0] = 0; + } + + if (p_dev_rec->sec_state == BTM_SEC_STATE_GETTING_NAME) + p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; + + /* Notify all clients waiting for name to be resolved */ + for (i = 0;i < BTM_SEC_MAX_RMT_NAME_CALLBACKS; i++) + { + if (btm_cb.p_rmt_name_callback[i]) + (*btm_cb.p_rmt_name_callback[i])(p_bd_addr, p_dev_rec->dev_class, + p_dev_rec->sec_bd_name); + } + } + else + { + dev_class[0] = 0; + dev_class[1] = 0; + dev_class[2] = 0; + + /* Notify all clients waiting for name to be resolved even if not found so clients can continue */ + for (i = 0;i < BTM_SEC_MAX_RMT_NAME_CALLBACKS; i++) + { + if (btm_cb.p_rmt_name_callback[i]) + (*btm_cb.p_rmt_name_callback[i])(p_bd_addr, dev_class, (UINT8 *)""); + } + + return; + } + + /* If we were delaying asking UI for a PIN because name was not resolved, ask now */ + if ( (btm_cb.pairing_state == BTM_PAIR_STATE_WAIT_LOCAL_PIN) && p_bd_addr + && (memcmp (btm_cb.pairing_bda, p_bd_addr, BD_ADDR_LEN) == 0) ) + { + BTM_TRACE_EVENT2 ("btm_sec_rmt_name_request_complete() delayed pin now being requested flags:0x%x, (p_pin_callback=0x%p)", btm_cb.pairing_flags, btm_cb.api.p_pin_callback); + + if (((btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) == 0) && + ((btm_cb.pairing_flags & BTM_PAIR_FLAGS_PIN_REQD) == 0) && + btm_cb.api.p_pin_callback) + { + BTM_TRACE_EVENT0 ("btm_sec_rmt_name_request_complete() calling pin_callback"); + btm_cb.pairing_flags |= BTM_PAIR_FLAGS_PIN_REQD; + (*btm_cb.api.p_pin_callback) (p_dev_rec->bd_addr, p_dev_rec->dev_class, p_bd_name); + } + + /* Set the same state again to force the timer to be restarted */ + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_LOCAL_PIN); + return; + } + + /* Check if we were delaying bonding because name was not resolved */ + if ( btm_cb.pairing_state == BTM_PAIR_STATE_GET_REM_NAME) + { + if (p_bd_addr && memcmp (btm_cb.pairing_bda, p_bd_addr, BD_ADDR_LEN) == 0) + { + BTM_TRACE_EVENT2 ("btm_sec_rmt_name_request_complete() continue bonding sm4: 0x%04x, status:0x%x", p_dev_rec->sm4, status); + if (status != HCI_SUCCESS) + { + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + + (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, status); + return; + } + + /* if peer is very old legacy devices, HCI_RMT_HOST_SUP_FEAT_NOTIFY_EVT is not reported */ + if (BTM_SEC_IS_SM4_UNKNOWN(p_dev_rec->sm4)) + { + /* set the KNOWN flag only if BTM_PAIR_FLAGS_REJECTED_CONNECT is not set. + * If it is set, there may be a race condition */ + BTM_TRACE_EVENT1 ("btm_sec_rmt_name_request_complete IS_SM4_UNKNOWN Flags:0x%04x", btm_cb.pairing_flags); + if ((btm_cb.pairing_flags & BTM_PAIR_FLAGS_REJECTED_CONNECT) == 0) + { + p_dev_rec->sm4 |= BTM_SM4_KNOWN; + } + } + + /* BT 2.1 or carkit, bring up the connection to force the peer to request PIN. + ** Else prefetch (btm_sec_check_prefetch_pin will do the prefetching if needed) + */ + if ((p_dev_rec->sm4 != BTM_SM4_KNOWN) || !btm_sec_check_prefetch_pin(p_dev_rec)) + { + /* if we rejected incoming connection request, we have to wait HCI_Connection_Complete event */ + /* before originating */ + if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_REJECTED_CONNECT) + { + BTM_TRACE_WARNING0 ("btm_sec_rmt_name_request_complete: waiting HCI_Connection_Complete after rejecting connection"); + } + /* Both we and the peer are 2.1 - continue to create connection */ + else if (btm_sec_dd_create_conn(p_dev_rec) != BTM_CMD_STARTED) + { + BTM_TRACE_WARNING0 ("btm_sec_rmt_name_request_complete: failed to start connection"); + + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + + (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, HCI_ERR_MEMORY_FULL); + } + } + return; + } + else + { + BTM_TRACE_WARNING0 ("btm_sec_rmt_name_request_complete: wrong BDA, retry with pairing BDA"); + + BTM_ReadRemoteDeviceName (btm_cb.pairing_bda, NULL); + return; + } + } + + /* check if we were delaying link_key_callback because name was not resolved */ + if (p_dev_rec->link_key_not_sent) + { + /* If HCI connection complete has not arrived, wait for it */ + if (p_dev_rec->hci_handle == BTM_SEC_INVALID_HANDLE) + return; + + p_dev_rec->link_key_not_sent = FALSE; + btm_send_link_key_notif(p_dev_rec); + + /* If its not us who perform authentication, we should tell stackserver */ + /* that some authentication has been completed */ + /* This is required when different entities receive link notification and auth complete */ + if (!(p_dev_rec->security_required & BTM_SEC_OUT_AUTHENTICATE)) + { + if (btm_cb.api.p_auth_complete_callback) + (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, + p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, HCI_SUCCESS); + + } + } + + /* If this is a bonding procedure can disconnect the link now */ + if ((btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) + && (p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED)) + { + BTM_TRACE_WARNING0 ("btm_sec_rmt_name_request_complete (none/ce)"); + p_dev_rec->security_required &= ~(BTM_SEC_OUT_AUTHENTICATE); + l2cu_start_post_bond_timer(p_dev_rec->hci_handle); + return; + } + + if (old_sec_state != BTM_SEC_STATE_GETTING_NAME) + return; + + /* If get name failed, notify the waiting layer */ + if (status != HCI_SUCCESS) + { + btm_sec_dev_rec_cback_event (p_dev_rec, BTM_ERR_PROCESSING); + return; + } + + if (p_dev_rec->sm4 & BTM_SM4_REQ_PEND) + { + BTM_TRACE_EVENT0 ("waiting for remote features!!"); + return; + } + + /* Remote Name succeeded, execute the next security procedure, if any */ + status = (UINT8)btm_sec_execute_procedure (p_dev_rec); + + /* If result is pending reply from the user or from the device is pending */ + if (status == BTM_CMD_STARTED) + return; + + /* There is no next procedure or start of procedure failed, notify the waiting layer */ + btm_sec_dev_rec_cback_event (p_dev_rec, status); +} + +/******************************************************************************* +** +** Function btm_sec_rmt_host_support_feat_evt +** +** Description This function is called when the +** HCI_RMT_HOST_SUP_FEAT_NOTIFY_EVT is received +** +** Returns void +** +*******************************************************************************/ +void btm_sec_rmt_host_support_feat_evt (UINT8 *p) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + BD_ADDR bd_addr; /* peer address */ + BD_FEATURES features; + + STREAM_TO_BDADDR (bd_addr, p); + p_dev_rec = btm_find_or_alloc_dev (bd_addr); + + BTM_TRACE_EVENT2 ("btm_sec_rmt_host_support_feat_evt sm4: 0x%x p[0]: 0x%x", p_dev_rec->sm4, p[0]); + + if (BTM_SEC_IS_SM4_UNKNOWN(p_dev_rec->sm4)) + { + p_dev_rec->sm4 = BTM_SM4_KNOWN; + STREAM_TO_ARRAY(features, p, BD_FEATURES_LEN); + if (HCI_SSP_HOST_SUPPORTED(features)) + { + p_dev_rec->sm4 = BTM_SM4_TRUE; + } + BTM_TRACE_EVENT2 ("btm_sec_rmt_host_support_feat_evt sm4: 0x%x features[0]: 0x%x", p_dev_rec->sm4, features[0]); + } +} + +/******************************************************************************* +** +** Function btm_io_capabilities_req +** +** Description This function is called when LM request for the IO +** capability of the local device and +** if the OOB data is present for the device in the event +** +** Returns void +** +*******************************************************************************/ +void btm_io_capabilities_req (UINT8 *p) +{ + tBTM_SP_IO_REQ evt_data; + UINT8 err_code = 0; + tBTM_SEC_DEV_REC *p_dev_rec; + BOOLEAN is_orig = TRUE; + UINT8 callback_rc = BTM_SUCCESS; + + STREAM_TO_BDADDR (evt_data.bd_addr, p); + + /* setup the default response according to compile options */ + /* assume that the local IO capability does not change + * loc_io_caps is initialized with the default value */ + evt_data.io_cap = btm_cb.devcb.loc_io_caps; + evt_data.oob_data = BTM_OOB_NONE; + evt_data.auth_req = BTM_DEFAULT_AUTH_REQ; + + BTM_TRACE_EVENT1 ("btm_io_capabilities_req() State: %s", btm_pair_state_descr(btm_cb.pairing_state)); + + p_dev_rec = btm_find_or_alloc_dev (evt_data.bd_addr); + p_dev_rec->sm4 |= BTM_SM4_TRUE; + + BTM_TRACE_EVENT3 ("btm_io_capabilities_req() State: %s Flags: 0x%04x p_cur_service: 0x%08x", + btm_pair_state_descr(btm_cb.pairing_state), btm_cb.pairing_flags, p_dev_rec->p_cur_service); + + if (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) + { + if (btm_cb.pairing_state == BTM_PAIR_STATE_INCOMING_SSP) + { + /* received IO capability response already-> not the originator of SSP */ + is_orig = FALSE; + + if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_PEER_STARTED_DD) + evt_data.auth_req = BTM_DEFAULT_DD_AUTH_REQ; + } + /* security is already in progress */ + else if (btm_cb.pairing_state == BTM_PAIR_STATE_WAIT_PIN_REQ) + { +/* coverity[uninit_use_in_call] +Event uninit_use_in_call: Using uninitialized element of array "evt_data.bd_addr" in call to function "memcmp" +False-positive: evt_data.bd_addr is set at the beginning with: STREAM_TO_BDADDR (evt_data.bd_addr, p); +*/ + if (memcmp (evt_data.bd_addr, btm_cb.pairing_bda, BD_ADDR_LEN)) + { + /* and it's not the device in bonding -> reject it */ + err_code = HCI_ERR_HOST_BUSY_PAIRING; + } + else + { + /* local device initiated dedicated bonding */ + evt_data.auth_req = BTM_DEFAULT_DD_AUTH_REQ; + } + } + else + { + err_code = HCI_ERR_HOST_BUSY_PAIRING; + } + } + + /* paring is not allowed */ + if (btm_cb.pairing_disabled) + err_code = HCI_ERR_PAIRING_NOT_ALLOWED; + + if (err_code != 0) + { +/* coverity[uninit_use_in_call] +Event uninit_use_in_call: Using uninitialized element of array "evt_data.bd_addr" in call to function "memcmp" +False-positive: evt_data.bd_addr is set at the beginning with: STREAM_TO_BDADDR (evt_data.bd_addr, p); +*/ + btsnd_hcic_io_cap_req_neg_reply(evt_data.bd_addr, err_code); + return; + } + + evt_data.is_orig = is_orig; + + if (is_orig) + { + /* local device initiated the pairing non-bonding -> use p_cur_service */ + if (!(btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) && + p_dev_rec->p_cur_service && + (p_dev_rec->p_cur_service->security_flags & BTM_SEC_OUT_AUTHENTICATE)) + { + evt_data.auth_req = (p_dev_rec->p_cur_service->security_flags & BTM_SEC_OUT_MITM) ? BTM_AUTH_SP_YES : BTM_AUTH_SP_NO; + } + } + + /* Notify L2CAP to increase timeout */ + l2c_pin_code_request (evt_data.bd_addr); + + memcpy (btm_cb.pairing_bda, evt_data.bd_addr, BD_ADDR_LEN); + +/* coverity[uninit_use_in_call] +Event uninit_use_in_call: Using uninitialized element of array "evt_data.bd_addr" in call to function "memcmp" +False-positive: False-positive: evt_data.bd_addr is set at the beginning with: STREAM_TO_BDADDR (evt_data.bd_addr, p); +*/ + if (!memcmp (evt_data.bd_addr, btm_cb.connecting_bda, BD_ADDR_LEN)) + memcpy (p_dev_rec->dev_class, btm_cb.connecting_dc, DEV_CLASS_LEN); + + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_LOCAL_IOCAPS); + + callback_rc = BTM_SUCCESS; + if (p_dev_rec->sm4 & BTM_SM4_UPGRADE) + { + p_dev_rec->sm4 &= ~BTM_SM4_UPGRADE; + + /* link key upgrade: always use SPGB_YES - assuming we want to save the link key */ + evt_data.auth_req = BTM_AUTH_SPGB_YES; + } + else if (btm_cb.api.p_sp_callback) + { + /* the callback function implementation may change the IO capability... */ + callback_rc = (*btm_cb.api.p_sp_callback) (BTM_SP_IO_REQ_EVT, (tBTM_SP_EVT_DATA *)&evt_data); + } + +#if BTM_OOB_INCLUDED == TRUE + if ((callback_rc == BTM_SUCCESS) || (BTM_OOB_UNKNOWN != evt_data.oob_data)) +#else + if (callback_rc == BTM_SUCCESS) +#endif + { + if ((btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD)) + { + evt_data.auth_req = (BTM_AUTH_DD_BOND | (evt_data.auth_req & BTM_AUTH_YN_BIT)); + } + + /* if the user does not indicate "reply later" by setting the oob_data to unknown + * send the response right now. Save the current IO capability in the control block */ + btm_cb.devcb.loc_auth_req = evt_data.auth_req; + btm_cb.devcb.loc_io_caps = evt_data.io_cap; + + BTM_TRACE_EVENT4 ("btm_io_capabilities_req: State: %s IO_CAP:%d oob_data:%d auth_req:%d", + btm_pair_state_descr(btm_cb.pairing_state), evt_data.io_cap, + evt_data.oob_data, evt_data.auth_req); + + btsnd_hcic_io_cap_req_reply(evt_data.bd_addr, evt_data.io_cap, + evt_data.oob_data, evt_data.auth_req); + } +} + +/******************************************************************************* +** +** Function btm_io_capabilities_rsp +** +** Description This function is called when the IO capability of the +** specified device is received +** +** Returns void +** +*******************************************************************************/ +void btm_io_capabilities_rsp (UINT8 *p) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + tBTM_SP_IO_RSP evt_data; + + STREAM_TO_BDADDR (evt_data.bd_addr, p); + STREAM_TO_UINT8 (evt_data.io_cap, p); + STREAM_TO_UINT8 (evt_data.oob_data, p); + STREAM_TO_UINT8 (evt_data.auth_req, p); + + /* Allocate a new device record or reuse the oldest one */ + p_dev_rec = btm_find_or_alloc_dev (evt_data.bd_addr); + + /* If no security is in progress, this indicates incoming security */ + if (btm_cb.pairing_state == BTM_PAIR_STATE_IDLE) + { + memcpy (btm_cb.pairing_bda, evt_data.bd_addr, BD_ADDR_LEN); + + btm_sec_change_pairing_state (BTM_PAIR_STATE_INCOMING_SSP); + + /* Make sure we reset the trusted mask to help against attacks */ + BTM_SEC_CLR_TRUSTED_DEVICE(p_dev_rec->trusted_mask); + + /* work around for FW bug */ + btm_inq_stop_on_ssp(); + } + + /* Notify L2CAP to increase timeout */ + l2c_pin_code_request (evt_data.bd_addr); + + /* We must have a device record here. + * Use the connecting device's CoD for the connection */ +/* coverity[uninit_use_in_call] +Event uninit_use_in_call: Using uninitialized element of array "evt_data.bd_addr" in call to function "memcmp" +FALSE-POSITIVE error from Coverity test-tool. evt_data.bd_addr is set at the beginning with: STREAM_TO_BDADDR (evt_data.bd_addr, p); +*/ + if (!memcmp (evt_data.bd_addr, btm_cb.connecting_bda, BD_ADDR_LEN)) + memcpy (p_dev_rec->dev_class, btm_cb.connecting_dc, DEV_CLASS_LEN); + + /* peer sets dedicated bonding bit and we did not initiate dedicated bonding */ + if (btm_cb.pairing_state == BTM_PAIR_STATE_INCOMING_SSP /* peer initiated bonding */ + && (evt_data.auth_req & BTM_AUTH_DD_BOND) ) /* and dedicated bonding bit is set */ + { + btm_cb.pairing_flags |= BTM_PAIR_FLAGS_PEER_STARTED_DD; + } + + /* save the IO capability in the device record */ + p_dev_rec->rmt_io_caps = evt_data.io_cap; + p_dev_rec->rmt_auth_req = evt_data.auth_req; + + if (btm_cb.api.p_sp_callback) + (*btm_cb.api.p_sp_callback) (BTM_SP_IO_RSP_EVT, (tBTM_SP_EVT_DATA *)&evt_data); +} + +/******************************************************************************* +** +** Function btm_proc_sp_req_evt +** +** Description This function is called to process/report +** HCI_USER_CONFIRMATION_REQUEST_EVT +** or HCI_USER_PASSKEY_REQUEST_EVT +** or HCI_USER_PASSKEY_NOTIFY_EVT +** +** Returns void +** +*******************************************************************************/ +void btm_proc_sp_req_evt (tBTM_SP_EVT event, UINT8 *p) +{ + tBTM_STATUS status = BTM_ERR_PROCESSING; + tBTM_SP_EVT_DATA evt_data; + UINT8 *p_bda = evt_data.cfm_req.bd_addr; + tBTM_SEC_DEV_REC *p_dev_rec; + + /* All events start with bd_addr */ + STREAM_TO_BDADDR (p_bda, p); + + BTM_TRACE_EVENT4 ("btm_proc_sp_req_evt() BDA: %08x%04x event: 0x%x, State: %s", + (p_bda[0]<<24) + (p_bda[1]<<16) + (p_bda[2]<<8) + p_bda[3], (p_bda[4] << 8) + p_bda[5], + event, btm_pair_state_descr(btm_cb.pairing_state)); + + if ( ((p_dev_rec = btm_find_dev (p_bda)) != NULL) + && (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) + && (memcmp (btm_cb.pairing_bda, p_bda, BD_ADDR_LEN) == 0) ) + { + memcpy (evt_data.cfm_req.bd_addr, p_dev_rec->bd_addr, BD_ADDR_LEN); + memcpy (evt_data.cfm_req.dev_class, p_dev_rec->dev_class, DEV_CLASS_LEN); + + BCM_STRNCPY_S ((char *)evt_data.cfm_req.bd_name, sizeof(evt_data.cfm_req.bd_name), (char *)p_dev_rec->sec_bd_name, BTM_MAX_REM_BD_NAME_LEN); + + switch (event) + { + case BTM_SP_CFM_REQ_EVT: + /* Numeric confirmation. Need user to conf the passkey */ + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_NUMERIC_CONFIRM); + + /* The device record must be allocated in the "IO cap exchange" step */ + STREAM_TO_UINT32 (evt_data.cfm_req.num_val, p); + + evt_data.cfm_req.just_works = TRUE; + + /* process user confirm req in association with the auth_req param */ +#if (BTM_LOCAL_IO_CAPS == BTM_IO_CAP_IO) + if ( (p_dev_rec->rmt_io_caps == BTM_IO_CAP_IO) + && (btm_cb.devcb.loc_io_caps == BTM_IO_CAP_IO) + && ((p_dev_rec->rmt_auth_req & BTM_AUTH_SP_YES) || (btm_cb.devcb.loc_auth_req & BTM_AUTH_SP_YES)) ) + { + /* Both devices are DisplayYesNo and one or both devices want to authenticate + -> use authenticated link key */ + evt_data.cfm_req.just_works = FALSE; + } +#endif + BTM_TRACE_DEBUG5 ("btm_proc_sp_req_evt() just_works:%d, io loc:%d, rmt:%d, auth loc:%d, rmt:%d", + evt_data.cfm_req.just_works, btm_cb.devcb.loc_io_caps, p_dev_rec->rmt_io_caps, + btm_cb.devcb.loc_auth_req, p_dev_rec->rmt_auth_req); + + evt_data.cfm_req.loc_auth_req = btm_cb.devcb.loc_auth_req; + evt_data.cfm_req.rmt_auth_req = p_dev_rec->rmt_auth_req; + break; + + case BTM_SP_KEY_NOTIF_EVT: + /* Passkey notification (other side is a keyboard) */ + STREAM_TO_UINT32 (evt_data.key_notif.passkey, p); + + BTM_TRACE_DEBUG1 ("BTM_SP_KEY_NOTIF_EVT: passkey: %u", evt_data.key_notif.passkey); + + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_AUTH_COMPLETE); + break; + +#if (BTM_LOCAL_IO_CAPS != BTM_IO_CAP_NONE) + case BTM_SP_KEY_REQ_EVT: + /* HCI_USER_PASSKEY_REQUEST_EVT */ + btm_sec_change_pairing_state (BTM_PAIR_STATE_KEY_ENTRY); + break; +#endif + } + + if (btm_cb.api.p_sp_callback) + { + status = (*btm_cb.api.p_sp_callback) (event, (tBTM_SP_EVT_DATA *)&evt_data); + if (status != BTM_NOT_AUTHORIZED) + { + return; + } + /* else BTM_NOT_AUTHORIZED means when the app wants to reject the req right now */ + } + else if ( (event == BTM_SP_CFM_REQ_EVT) && (evt_data.cfm_req.just_works == TRUE) ) + { + /* automatically reply with just works if no sp_cback */ + status = BTM_SUCCESS; + } + + if (event == BTM_SP_CFM_REQ_EVT) + { + BTM_TRACE_DEBUG1 ("calling BTM_ConfirmReqReply with status: %d", status); + BTM_ConfirmReqReply (status, p_bda); + } +#if (BTM_LOCAL_IO_CAPS != BTM_IO_CAP_NONE) + else if (event == BTM_SP_KEY_REQ_EVT) + { + BTM_PasskeyReqReply(status, p_bda, 0); + } +#endif + return; + } + + /* Something bad. we can only fail this connection */ + btm_cb.acl_disc_reason = HCI_ERR_HOST_REJECT_SECURITY; + + if (BTM_SP_CFM_REQ_EVT == event) + { + btsnd_hcic_user_conf_reply (p_bda, FALSE); + } + else if (BTM_SP_KEY_NOTIF_EVT == event) + { + /* do nothing -> it very unlikely to happen. + This event is most likely to be received by a HID host when it first connects to a HID device. + Usually the Host initiated the connection in this case. + On Mobile platforms, if there's a security process happening, + the host probably can not initiate another connection. + BTW (PC) is another story. */ + if (NULL != (p_dev_rec = btm_find_dev (p_bda)) ) + { + btm_sec_disconnect (p_dev_rec->hci_handle, HCI_ERR_AUTH_FAILURE); + } + } +#if (BTM_LOCAL_IO_CAPS != BTM_IO_CAP_NONE) + else + { + btsnd_hcic_user_passkey_neg_reply(p_bda); + } +#endif +} + +/******************************************************************************* +** +** Function btm_keypress_notif_evt +** +** Description This function is called when a key press notification is +** received +** +** Returns void +** +*******************************************************************************/ +void btm_keypress_notif_evt (UINT8 *p) +{ + tBTM_SP_KEYPRESS evt_data; + UINT8 *p_bda; + + /* parse & report BTM_SP_KEYPRESS_EVT */ + if (btm_cb.api.p_sp_callback) + { + p_bda = evt_data.bd_addr; + + STREAM_TO_BDADDR (p_bda, p); + evt_data.notif_type = *p; + + (*btm_cb.api.p_sp_callback) (BTM_SP_KEYPRESS_EVT, (tBTM_SP_EVT_DATA *)&evt_data); + } +} + +/******************************************************************************* +** +** Function btm_simple_pair_complete +** +** Description This function is called when simple pairing process is +** complete +** +** Returns void +** +*******************************************************************************/ +void btm_simple_pair_complete (UINT8 *p) +{ + tBTM_SP_COMPLT evt_data; + tBTM_SEC_DEV_REC *p_dev_rec; + UINT8 status; + BOOLEAN disc = FALSE; + + status = *p++; + STREAM_TO_BDADDR (evt_data.bd_addr, p); + + if ((p_dev_rec = btm_find_dev (evt_data.bd_addr)) == NULL) + { + BTM_TRACE_ERROR2 ("btm_simple_pair_complete() with unknown BDA: %08x%04x", + (evt_data.bd_addr[0]<<24) + (evt_data.bd_addr[1]<<16) + (evt_data.bd_addr[2]<<8) + evt_data.bd_addr[3], + (evt_data.bd_addr[4] << 8) + evt_data.bd_addr[5]); + return; + } + + BTM_TRACE_EVENT3 ("btm_simple_pair_complete() Pair State: %s Status:%d sec_state: %u", + btm_pair_state_descr(btm_cb.pairing_state), status, p_dev_rec->sec_state); + + evt_data.status = BTM_ERR_PROCESSING; + if (status == HCI_SUCCESS) + { + evt_data.status = BTM_SUCCESS; + p_dev_rec->sec_flags |= BTM_SEC_AUTHENTICATED; + } + else + { + if (status == HCI_ERR_PAIRING_NOT_ALLOWED) + { + /* The test spec wants the peer device to get this failure code. */ + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_DISCONNECT); + + /* Change the timer to 1 second */ + btu_start_timer (&btm_cb.pairing_tle, BTU_TTYPE_USER_FUNC, BT_1SEC_TIMEOUT); + } + else if (memcmp (btm_cb.pairing_bda, evt_data.bd_addr, BD_ADDR_LEN) == 0) + { + /* stop the timer */ + btu_stop_timer (&btm_cb.pairing_tle); + + if (p_dev_rec->sec_state != BTM_SEC_STATE_AUTHENTICATING) + { + /* the initiating side: will receive auth complete event. disconnect ACL at that time */ + disc = TRUE; + } + } + else + disc = TRUE; + } + + /* Let the pairing state stay active, p_auth_complete_callback will report the failure */ + memcpy (evt_data.bd_addr, p_dev_rec->bd_addr, BD_ADDR_LEN); + memcpy (evt_data.dev_class, p_dev_rec->dev_class, DEV_CLASS_LEN); + + if (btm_cb.api.p_sp_callback) + (*btm_cb.api.p_sp_callback) (BTM_SP_COMPLT_EVT, (tBTM_SP_EVT_DATA *)&evt_data); + + if (disc) + { + /* simple pairing failed */ + btm_sec_send_hci_disconnect (p_dev_rec, HCI_ERR_AUTH_FAILURE); + } +} + +#if BTM_OOB_INCLUDED == TRUE +/******************************************************************************* +** +** Function btm_rem_oob_req +** +** Description This function is called to process/report +** HCI_REMOTE_OOB_DATA_REQUEST_EVT +** +** Returns void +** +*******************************************************************************/ +void btm_rem_oob_req (UINT8 *p) +{ + UINT8 *p_bda; + tBTM_SP_RMT_OOB evt_data; + tBTM_SEC_DEV_REC *p_dev_rec; + BT_OCTET16 c; + BT_OCTET16 r; + + p_bda = evt_data.bd_addr; + + STREAM_TO_BDADDR (p_bda, p); + + BTM_TRACE_EVENT6 ("btm_rem_oob_req() BDA: %02x:%02x:%02x:%02x:%02x:%02x", + p_bda[0], p_bda[1], p_bda[2], p_bda[3], p_bda[4], p_bda[5]); + + if ( (NULL != (p_dev_rec = btm_find_dev (p_bda))) && + btm_cb.api.p_sp_callback) + { + memcpy (evt_data.bd_addr, p_dev_rec->bd_addr, BD_ADDR_LEN); + memcpy (evt_data.dev_class, p_dev_rec->dev_class, DEV_CLASS_LEN); + BCM_STRNCPY_S((char *)evt_data.bd_name, sizeof(evt_data.bd_name), (char *)p_dev_rec->sec_bd_name, BTM_MAX_REM_BD_NAME_LEN+1); + + btm_sec_change_pairing_state(BTM_PAIR_STATE_WAIT_LOCAL_OOB_RSP); + if ((*btm_cb.api.p_sp_callback) (BTM_SP_RMT_OOB_EVT, (tBTM_SP_EVT_DATA *)&evt_data) == BTM_NOT_AUTHORIZED) + { + BTM_RemoteOobDataReply(TRUE, p_bda, c, r); + } + return; + } + + /* something bad. we can only fail this connection */ + btm_cb.acl_disc_reason = HCI_ERR_HOST_REJECT_SECURITY; + btsnd_hcic_rem_oob_neg_reply (p_bda); +} + +/******************************************************************************* +** +** Function btm_read_local_oob_complete +** +** Description This function is called when read local oob data is +** completed by the LM +** +** Returns void +** +*******************************************************************************/ +void btm_read_local_oob_complete (UINT8 *p) +{ + tBTM_SP_LOC_OOB evt_data; + UINT8 status = *p++; + + BTM_TRACE_EVENT1 ("btm_read_local_oob_complete:%d", status); + if (status == HCI_SUCCESS) + { + evt_data.status = BTM_SUCCESS; + STREAM_TO_ARRAY16(evt_data.c, p); + STREAM_TO_ARRAY16(evt_data.r, p); + } + else + evt_data.status = BTM_ERR_PROCESSING; + + if (btm_cb.api.p_sp_callback) + (*btm_cb.api.p_sp_callback) (BTM_SP_LOC_OOB_EVT, (tBTM_SP_EVT_DATA *)&evt_data); +} +#endif /* BTM_OOB_INCLUDED */ + +/******************************************************************************* +** +** Function btm_sec_auth_collision +** +** Description This function is called when authentication or encryption +** needs to be retried at a later time. +** +** Returns void +** +*******************************************************************************/ +static void btm_sec_auth_collision (UINT16 handle) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + + if (!btm_cb.collision_start_time) + btm_cb.collision_start_time = GKI_get_tick_count (); + + if ((GKI_get_tick_count () - btm_cb.collision_start_time) < btm_cb.max_collision_delay) + { + if (handle == BTM_SEC_INVALID_HANDLE) + { + if ((p_dev_rec = btm_sec_find_dev_by_sec_state (BTM_SEC_STATE_AUTHENTICATING)) == NULL) + p_dev_rec = btm_sec_find_dev_by_sec_state (BTM_SEC_STATE_ENCRYPTING); + } + else + p_dev_rec = btm_find_dev_by_handle (handle); + + if (p_dev_rec != NULL) + { + BTM_TRACE_DEBUG1 ("btm_sec_auth_collision: state %d (retrying in a moment...)", p_dev_rec->sec_state); + /* We will restart authentication after timeout */ + if (p_dev_rec->sec_state == BTM_SEC_STATE_AUTHENTICATING || p_dev_rec->sec_state == BTM_SEC_STATE_ENCRYPTING) + p_dev_rec->sec_state = 0; + + btm_cb.p_collided_dev_rec = p_dev_rec; + btm_cb.sec_collision_tle.param = (UINT32) btm_sec_collision_timeout; + btu_start_timer (&btm_cb.sec_collision_tle, BTU_TTYPE_USER_FUNC, BT_1SEC_TIMEOUT); + } + } +} + +/******************************************************************************* +** +** Function btm_sec_auth_complete +** +** Description This function is when authentication of the connection is +** completed by the LM +** +** Returns void +** +*******************************************************************************/ +void btm_sec_auth_complete (UINT16 handle, UINT8 status) +{ + UINT8 old_sm4; + tBTM_PAIRING_STATE old_state = btm_cb.pairing_state; + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev_by_handle (handle); + BOOLEAN are_bonding = FALSE; + + /* Commenting out trace due to obf/compilation problems. + */ +#if (BT_USE_TRACES == TRUE) + if (p_dev_rec) + { + BTM_TRACE_EVENT6 ("Security Manager: auth_complete PairState: %s handle:%u status:%d dev->sec_state: %u Bda:%08x, RName:%s", + btm_pair_state_descr (btm_cb.pairing_state), + handle, status, + p_dev_rec->sec_state, + (p_dev_rec->bd_addr[2]<<24)+(p_dev_rec->bd_addr[3]<<16)+(p_dev_rec->bd_addr[4]<<8)+p_dev_rec->bd_addr[5], + p_dev_rec->sec_bd_name); + } + else + { + BTM_TRACE_EVENT3 ("Security Manager: auth_complete PairState: %s handle:%u status:%d", + btm_pair_state_descr (btm_cb.pairing_state), + handle, status); + } +#endif + + /* For transaction collision we need to wait and repeat. There is no need */ + /* for random timeout because only slave should receive the result */ + if ((status == HCI_ERR_LMP_ERR_TRANS_COLLISION) || (status == HCI_ERR_DIFF_TRANSACTION_COLLISION)) + { + btm_sec_auth_collision(handle); + return; + } + btm_cb.collision_start_time = 0; + + btm_restore_mode(); + + /* Check if connection was made just to do bonding. If we authenticate + the connection that is up, this is the last event received. + */ + if (p_dev_rec + && (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) + && !(btm_cb.pairing_flags & BTM_PAIR_FLAGS_DISC_WHEN_DONE)) + { + p_dev_rec->security_required &= ~BTM_SEC_OUT_AUTHENTICATE; + + l2cu_start_post_bond_timer (p_dev_rec->hci_handle); + } + + if (!p_dev_rec) + return; + + /* keep the old sm4 flag and clear the retry bit in control block */ + old_sm4 = p_dev_rec->sm4; + p_dev_rec->sm4 &= ~BTM_SM4_RETRY; + + if ( (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) + && (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) + && (memcmp (p_dev_rec->bd_addr, btm_cb.pairing_bda, BD_ADDR_LEN) == 0) ) + are_bonding = TRUE; + + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + + if (p_dev_rec->sec_state != BTM_SEC_STATE_AUTHENTICATING) + { + if ( (btm_cb.api.p_auth_complete_callback && status != HCI_SUCCESS) + && (old_state != BTM_PAIR_STATE_IDLE) ) + { + (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, + p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, status); + } + return; + } + + /* There can be a race condition, when we are starting authentication and + ** the peer device is doing encryption. + ** If first we receive encryption change up, then initiated authentication + ** can not be performed. According to the spec we can not do authentication + ** on the encrypted link, so device is correct. + */ + if ((status == HCI_ERR_COMMAND_DISALLOWED) + && ((p_dev_rec->sec_flags & (BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED)) == + (BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED))) + { + status = HCI_SUCCESS; + } + /* Currently we do not notify user if it is a keyboard which connects */ + /* User probably Disabled the keyboard while it was asleap. Let her try */ + if (btm_cb.api.p_auth_complete_callback) + { + /* report the suthentication status */ + if (old_state != BTM_PAIR_STATE_IDLE) + (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, + p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, status); + } + + p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; + + /* If this is a bonding procedure can disconnect the link now */ + if (are_bonding) + { + p_dev_rec->security_required &= ~BTM_SEC_OUT_AUTHENTICATE; + + if (status != HCI_SUCCESS) + btm_sec_send_hci_disconnect (p_dev_rec, HCI_ERR_PEER_USER); + else + l2cu_start_post_bond_timer (p_dev_rec->hci_handle); + + return; + } + + /* If authentication failed, notify the waiting layer */ + if (status != HCI_SUCCESS) + { + if ((old_sm4 & BTM_SM4_RETRY) == 0) + { + /* allow retry only once */ + if (status == HCI_ERR_LMP_ERR_TRANS_COLLISION) + { + /* not retried yet. set the retry bit */ + p_dev_rec->sm4 |= BTM_SM4_RETRY; + BTM_TRACE_DEBUG2 ("Collision retry sm4:x%x sec_flags:0x%x", p_dev_rec->sm4, p_dev_rec->sec_flags); + } + /* this retry for missing key is for Lisbon or later only. + * Legacy device do not need this. the controller will drive the retry automatically */ + else if (HCI_ERR_KEY_MISSING == status && BTM_SEC_IS_SM4(p_dev_rec->sm4)) + { + /* not retried yet. set the retry bit */ + p_dev_rec->sm4 |= BTM_SM4_RETRY; + p_dev_rec->sec_flags &= ~BTM_SEC_LINK_KEY_KNOWN; + BTM_TRACE_DEBUG2 ("Retry for missing key sm4:x%x sec_flags:0x%x", p_dev_rec->sm4, p_dev_rec->sec_flags); + + /* With BRCM controller, we do not need to delete the stored link key in controller. + If the stack may sit on top of other controller, we may need this + BTM_DeleteStoredLinkKey (bd_addr, NULL); */ + } + + if (p_dev_rec->sm4 & BTM_SM4_RETRY) + { + btm_sec_execute_procedure (p_dev_rec); + return; + } + } + + btm_sec_dev_rec_cback_event (p_dev_rec, BTM_ERR_PROCESSING); + + if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_DISC_WHEN_DONE) + { + btm_sec_send_hci_disconnect (p_dev_rec, HCI_ERR_AUTH_FAILURE); + } + return; + } + + p_dev_rec->sec_flags |= BTM_SEC_AUTHENTICATED; + + /* Authentication succeeded, execute the next security procedure, if any */ + status = btm_sec_execute_procedure (p_dev_rec); + + /* If there is no next procedure, or procedure failed to start, notify the caller */ + if (status != BTM_CMD_STARTED) + btm_sec_dev_rec_cback_event (p_dev_rec, status); +} + +/******************************************************************************* +** +** Function btm_sec_mkey_comp_event +** +** Description This function is when encryption of the connection is +** completed by the LM +** +** Returns void +** +*******************************************************************************/ +void btm_sec_mkey_comp_event (UINT16 handle, UINT8 status, UINT8 key_flg) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev_by_handle (handle); + UINT8 bd_addr[BD_ADDR_LEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff} ; + + BTM_TRACE_EVENT2 ("Security Manager: mkey comp status:%d State:%d", + status, (p_dev_rec) ? p_dev_rec->sec_state : 0); + + /* If encryption setup failed, notify the waiting layer */ + /* There is no next procedure or start of procedure failed, notify the waiting layer */ + if (btm_cb.mkey_cback) + { + if (!p_dev_rec) + (btm_cb.mkey_cback)(bd_addr, status, key_flg ); + else + (btm_cb.mkey_cback)(p_dev_rec->bd_addr, status, key_flg ); + } +} + +/******************************************************************************* +** +** Function btm_sec_encrypt_change +** +** Description This function is when encryption of the connection is +** completed by the LM +** +** Returns void +** +*******************************************************************************/ +void btm_sec_encrypt_change (UINT16 handle, UINT8 status, UINT8 encr_enable) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev_by_handle (handle); + + BTM_TRACE_EVENT3 ("Security Manager: encrypt_change status:%d State:%d, encr_enable = %d", + status, (p_dev_rec) ? p_dev_rec->sec_state : 0, encr_enable); + BTM_TRACE_DEBUG1 ("before update p_dev_rec->sec_flags=0x%x", p_dev_rec->sec_flags ); + + /* For transaction collision we need to wait and repeat. There is no need */ + /* for random timeout because only slave should receive the result */ + if ((status == HCI_ERR_LMP_ERR_TRANS_COLLISION) || (status == HCI_ERR_DIFF_TRANSACTION_COLLISION)) + { + btm_sec_auth_collision(handle); + return; + } + btm_cb.collision_start_time = 0; + + if (!p_dev_rec) + return; + + if ((status == HCI_SUCCESS) && encr_enable) + p_dev_rec->sec_flags |= (BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED); + + /* It is possible that we decrypted the link to perform role switch */ + /* mark link not to be encrypted, so that when we execute security next time it will kick in again */ + if ((status == HCI_SUCCESS) && !encr_enable) + p_dev_rec->sec_flags &= ~BTM_SEC_ENCRYPTED; + + BTM_TRACE_DEBUG1 ("after update p_dev_rec->sec_flags=0x%x", p_dev_rec->sec_flags ); +#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE + if (p_dev_rec->device_type == BT_DEVICE_TYPE_BLE) + { + btm_ble_link_encrypted(p_dev_rec->bd_addr, encr_enable); + return; + } + else + /* BR/EDR connection, update the encryption key size to be 16 as always */ + p_dev_rec->enc_key_size = 16; +#endif + + /* If this encryption was started by peer do not need to do anything */ + if (p_dev_rec->sec_state != BTM_SEC_STATE_ENCRYPTING) + { + if (BTM_SEC_STATE_DELAY_FOR_ENC == p_dev_rec->sec_state) + { + p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; + p_dev_rec->p_callback = NULL; + l2cu_resubmit_pending_sec_req (p_dev_rec->bd_addr); + } + return; + } + + p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; + + /* If encryption setup failed, notify the waiting layer */ + if (status != HCI_SUCCESS) + { + btm_sec_dev_rec_cback_event (p_dev_rec, BTM_ERR_PROCESSING); + return; + } + + /* Encryption setup succeeded, execute the next security procedure, if any */ + status = (UINT8)btm_sec_execute_procedure (p_dev_rec); + + /* If there is no next procedure, or procedure failed to start, notify the caller */ + if (status != BTM_CMD_STARTED) + btm_sec_dev_rec_cback_event (p_dev_rec, status); +} + +/******************************************************************************* +** +** Function btm_sec_create_conn +** +** Description This function records current role and forwards request to +** HCI +** +** Returns void +** +*******************************************************************************/ +BOOLEAN btm_sec_create_conn (BD_ADDR bda, UINT16 packet_types, + UINT8 page_scan_rep_mode, UINT8 page_scan_mode, + UINT16 clock_offset, UINT8 allow_switch) +{ + tBTM_SEC_DEV_REC *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); + + btm_cb.acl_disc_reason = 0xff ; + + p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; + p_dev_rec->role_master = TRUE; + + /* If any SCO link up, do not allow a switch */ + if (BTM_GetNumScoLinks() != 0) + allow_switch = HCI_CR_CONN_NOT_ALLOW_SWITCH; + + return(btsnd_hcic_create_conn (bda, packet_types, page_scan_rep_mode, + page_scan_mode, clock_offset, allow_switch)); +} + +/******************************************************************************* +** +** Function btm_sec_connect_after_reject_timeout +** +** Description Connection for bonding could not start because of the collision +** Initiate outgoing connection +** +** Returns Pointer to the TLE struct +** +*******************************************************************************/ +static void btm_sec_connect_after_reject_timeout (TIMER_LIST_ENT *p_tle) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_cb.p_collided_dev_rec; + + BTM_TRACE_EVENT0 ("btm_sec_connect_after_reject_timeout()"); + btm_cb.sec_collision_tle.param = 0; + btm_cb.p_collided_dev_rec = 0; + + if (btm_sec_dd_create_conn(p_dev_rec) != BTM_CMD_STARTED) + { + BTM_TRACE_WARNING0 ("Security Manager: btm_sec_connect_after_reject_timeout: failed to start connection"); + + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + + (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, HCI_ERR_MEMORY_FULL); + } +} + +/******************************************************************************* +** +** Function btm_sec_connected +** +** Description This function is when a connection to the peer device is +** establsihed +** +** Returns void +** +*******************************************************************************/ +void btm_sec_connected (UINT8 *bda, UINT16 handle, UINT8 status, UINT8 enc_mode) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bda); + UINT8 res; + BOOLEAN is_pairing_device = FALSE; + tACL_CONN *p_acl_cb; + + btm_acl_resubmit_page(); + + /* Commenting out trace due to obf/compilation problems. + */ +#if (BT_USE_TRACES == TRUE) + if (p_dev_rec) + { + BTM_TRACE_EVENT6 ("Security Manager: btm_sec_connected in state: %s handle:%d status:%d enc_mode:%d bda:%x RName:%s", + btm_pair_state_descr(btm_cb.pairing_state), handle, status, enc_mode, + (bda[2]<<24)+(bda[3]<<16)+(bda[4]<<8)+bda[5], + p_dev_rec->sec_bd_name); + } + else + { + BTM_TRACE_EVENT5 ("Security Manager: btm_sec_connected in state: %s handle:%d status:%d enc_mode:%d bda:%x ", + btm_pair_state_descr(btm_cb.pairing_state), handle, status, enc_mode, + (bda[2]<<24)+(bda[3]<<16)+(bda[4]<<8)+bda[5]); + } +#endif + + if (!p_dev_rec) + { + /* There is no device record for new connection. Allocate one */ + if (status == HCI_SUCCESS) + { + p_dev_rec = btm_sec_alloc_dev (bda); + } + else + { + /* can not find the device record and the status is error, + * just ignore it */ + return; + } + } + else /* Update the timestamp for this device */ + { + p_dev_rec->timestamp = btm_cb.dev_rec_count++; + if (p_dev_rec->sm4 & BTM_SM4_CONN_PEND) + { + /* tell L2CAP it's a bonding connection. */ + if ( (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) + && (memcmp (btm_cb.pairing_bda, p_dev_rec->bd_addr, BD_ADDR_LEN) == 0) + && (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) ) + { + /* if incoming connection failed while pairing, then try to connect and continue */ + /* Motorola S9 disconnects without asking pin code */ + if ((status != HCI_SUCCESS)&&(btm_cb.pairing_state == BTM_PAIR_STATE_WAIT_PIN_REQ)) + { + BTM_TRACE_WARNING0 ("Security Manager: btm_sec_connected: incoming connection failed without asking PIN"); + + p_dev_rec->sm4 &= ~BTM_SM4_CONN_PEND; + if (p_dev_rec->sec_flags & BTM_SEC_NAME_KNOWN) + { + /* Start timer with 0 to initiate connection with new LCB */ + /* because L2CAP will delete current LCB with this event */ + btm_cb.p_collided_dev_rec = p_dev_rec; + btm_cb.sec_collision_tle.param = (UINT32) btm_sec_connect_after_reject_timeout; + btu_start_timer (&btm_cb.sec_collision_tle, BTU_TTYPE_USER_FUNC, 0); + } + else + { + btm_sec_change_pairing_state (BTM_PAIR_STATE_GET_REM_NAME); + BTM_ReadRemoteDeviceName(p_dev_rec->bd_addr, NULL); + } +#if BTM_DISC_DURING_RS == TRUE + p_dev_rec->rs_disc_pending = BTM_SEC_RS_NOT_PENDING; /* reset flag */ +#endif + return; + } + else + { + l2cu_update_lcb_4_bonding(p_dev_rec->bd_addr, TRUE); + } + } + /* always clear the pending flag */ + p_dev_rec->sm4 &= ~BTM_SM4_CONN_PEND; + } + } + +#if BTM_DISC_DURING_RS == TRUE + p_dev_rec->rs_disc_pending = BTM_SEC_RS_NOT_PENDING; /* reset flag */ +#endif + + if ( (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) + && (memcmp (btm_cb.pairing_bda, bda, BD_ADDR_LEN) == 0) ) + { + /* if we rejected incoming connection from bonding device */ + if ((status == HCI_ERR_HOST_REJECT_DEVICE) + &&(btm_cb.pairing_flags & BTM_PAIR_FLAGS_REJECTED_CONNECT)) + { + BTM_TRACE_WARNING2 ("Security Manager: btm_sec_connected: HCI_Conn_Comp Flags:0x%04x, sm4: 0x%x", + btm_cb.pairing_flags, p_dev_rec->sm4); + + btm_cb.pairing_flags &= ~BTM_PAIR_FLAGS_REJECTED_CONNECT; + if (BTM_SEC_IS_SM4_UNKNOWN(p_dev_rec->sm4)) + { + /* Try again: RNR when no ACL causes HCI_RMT_HOST_SUP_FEAT_NOTIFY_EVT */ + btm_sec_change_pairing_state (BTM_PAIR_STATE_GET_REM_NAME); + BTM_ReadRemoteDeviceName(bda, NULL); + return; + } + + /* if we already have pin code */ + if (btm_cb.pairing_state != BTM_PAIR_STATE_WAIT_LOCAL_PIN) + { + /* Start timer with 0 to initiate connection with new LCB */ + /* because L2CAP will delete current LCB with this event */ + btm_cb.p_collided_dev_rec = p_dev_rec; + btm_cb.sec_collision_tle.param = (UINT32) btm_sec_connect_after_reject_timeout; + btu_start_timer (&btm_cb.sec_collision_tle, BTU_TTYPE_USER_FUNC, 0); + } + + return; + } + /* wait for incoming connection without resetting pairing state */ + else if (status == HCI_ERR_CONNECTION_EXISTS) + { + BTM_TRACE_WARNING0 ("Security Manager: btm_sec_connected: Wait for incoming connection"); + return; + } + + is_pairing_device = TRUE; + } + + /* If connection was made to do bonding restore link security if changed */ + btm_restore_mode(); + + /* if connection fails during pin request, notify application */ + if (status != HCI_SUCCESS) + { + /* If connection failed because of during pairing, need to tell user */ + if (is_pairing_device) + { + p_dev_rec->security_required &= ~BTM_SEC_OUT_AUTHENTICATE; + p_dev_rec->sec_flags &= ~(BTM_SEC_LINK_KEY_KNOWN | BTM_SEC_LINK_KEY_AUTHED); + BTM_TRACE_DEBUG1 ("security_required:%x ", p_dev_rec->security_required ); + + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + + /* We need to notify host that the key is not known any more */ + if (btm_cb.api.p_auth_complete_callback) + { + (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, + p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, status); + } + } + else if ((status == HCI_ERR_AUTH_FAILURE) || + (status == HCI_ERR_KEY_MISSING) || + (status == HCI_ERR_HOST_REJECT_SECURITY) || + (status == HCI_ERR_PAIRING_NOT_ALLOWED) || + (status == HCI_ERR_UNIT_KEY_USED) || + (status == HCI_ERR_PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED) || + (status == HCI_ERR_ENCRY_MODE_NOT_ACCEPTABLE) || + (status == HCI_ERR_REPEATED_ATTEMPTS)) + { + p_dev_rec->security_required &= ~BTM_SEC_OUT_AUTHENTICATE; + p_dev_rec->sec_flags &= ~BTM_SEC_LINK_KEY_KNOWN; + + /* We need to notify host that the key is not known any more */ + if (btm_cb.api.p_auth_complete_callback) + { + (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, + p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, status); + } + } + + if (status == HCI_ERR_CONNECTION_TOUT || status == HCI_ERR_LMP_RESPONSE_TIMEOUT || + status == HCI_ERR_UNSPECIFIED || status == HCI_ERR_PAGE_TIMEOUT) + btm_sec_dev_rec_cback_event (p_dev_rec, BTM_DEVICE_TIMEOUT); + else + btm_sec_dev_rec_cback_event (p_dev_rec, BTM_ERR_PROCESSING); + + return; + } + + /* If initiated dedicated bonding, return the link key now, and initiate disconnect */ + /* If dedicated bonding, and we now have a link key, we are all done */ + if ( is_pairing_device + && (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN) ) + { + if (p_dev_rec->link_key_not_sent) + { + p_dev_rec->link_key_not_sent = FALSE; + btm_send_link_key_notif(p_dev_rec); + } + + p_dev_rec->security_required &= ~BTM_SEC_OUT_AUTHENTICATE; + + /* remember flag before it is initialized */ + if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) + res = TRUE; + else + res = FALSE; + + if (btm_cb.api.p_auth_complete_callback) + (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, + p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, HCI_SUCCESS); + + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + + if ( res ) + { + /* Let l2cap start bond timer */ + l2cu_update_lcb_4_bonding (p_dev_rec->bd_addr, TRUE); + } + + return; + } + + p_dev_rec->hci_handle = handle; + + /* role may not be correct here, it will be updated by l2cap, but we need to */ + /* notify btm_acl that link is up, so starting of rmt name request will not */ + /* set paging flag up */ + p_acl_cb = btm_bda_to_acl(bda); + if (p_acl_cb) + { + /* whatever is in btm_establish_continue() without reporting the BTM_BL_CONN_EVT event */ +#if (!defined(BTM_BYPASS_EXTRA_ACL_SETUP) || BTM_BYPASS_EXTRA_ACL_SETUP == FALSE) + /* 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); +#endif + + BTM_SetLinkSuperTout (p_acl_cb->remote_addr, btm_cb.btm_def_link_super_tout); + } + btm_acl_created (bda, p_dev_rec->dev_class, p_dev_rec->sec_bd_name, handle, HCI_ROLE_SLAVE, FALSE); + + /* Initialize security flags. We need to do that because some */ + /* authorization complete could have come after the connection is dropped */ + /* and that would set wrong flag that link has been authorized already */ + p_dev_rec->sec_flags &= ~(BTM_SEC_AUTHORIZED | BTM_SEC_AUTHENTICATED | + BTM_SEC_ENCRYPTED | BTM_SEC_ROLE_SWITCHED); + + if (enc_mode != HCI_ENCRYPT_MODE_DISABLED) + p_dev_rec->sec_flags |= (BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED); + + if (btm_cb.security_mode == BTM_SEC_MODE_LINK) + p_dev_rec->sec_flags |= BTM_SEC_AUTHENTICATED; + + p_dev_rec->link_key_changed = FALSE; + + /* After connection is established we perform security if we do not know */ + /* the name, or if we are originator because some procedure can have */ + /* been scheduled while connection was down */ + BTM_TRACE_DEBUG1 ("is_originator:%d ", p_dev_rec->is_originator); + if (!(p_dev_rec->sec_flags & BTM_SEC_NAME_KNOWN) || p_dev_rec->is_originator) + { + if ((res = btm_sec_execute_procedure (p_dev_rec)) != BTM_CMD_STARTED) + btm_sec_dev_rec_cback_event (p_dev_rec, res); + } + return; +} + +/******************************************************************************* +** +** Function btm_sec_role_changed +** +** Description This function is colled when controller reports role +** changed, or failed command status for Role Change request +** +** Returns void +** +*******************************************************************************/ +void btm_sec_role_changed (void *p_ref_data) +{ + tBTM_SEC_DEV_REC *p_dev_rec = (tBTM_SEC_DEV_REC *)p_ref_data; + UINT8 res; + + BTM_TRACE_EVENT0 ("Security Manager: role changed"); + + /* If this role switch was started by peer do not need to do anything */ + if (p_dev_rec->sec_state != BTM_SEC_STATE_SWITCHING_ROLE) + return; + + /* If serurity required was to FORCE switch and it failed, notify the waiting layer */ + if (((p_dev_rec->security_required & BTM_SEC_FORCE_MASTER) && !p_dev_rec->role_master) + || ((p_dev_rec->security_required & BTM_SEC_FORCE_SLAVE) && p_dev_rec->role_master)) + { + btm_sec_dev_rec_cback_event (p_dev_rec, BTM_ERR_PROCESSING); + return; + } + + p_dev_rec->sec_flags |= BTM_SEC_ROLE_SWITCHED; + + p_dev_rec->security_required &= ~(BTM_SEC_FORCE_MASTER | BTM_SEC_ATTEMPT_MASTER | + BTM_SEC_FORCE_SLAVE | BTM_SEC_ATTEMPT_SLAVE); + + p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; + + if ((res = (UINT8)btm_sec_execute_procedure (p_dev_rec)) != BTM_CMD_STARTED) + { + btm_sec_dev_rec_cback_event (p_dev_rec, res); + } +} + +/******************************************************************************* +** +** Function btm_sec_disconnect +** +** Description This function is called to disconnect HCI link +** +** Returns btm status +** +*******************************************************************************/ +tBTM_STATUS btm_sec_disconnect (UINT16 handle, UINT8 reason) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev_by_handle (handle); + + /* In some weird race condition we may not have a record */ + if (!p_dev_rec) + { + btsnd_hcic_disconnect (handle, reason); + return(BTM_SUCCESS); + } + + /* If we are in the process of bonding we need to tell client that auth failed */ + if ( (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) + && (memcmp (btm_cb.pairing_bda, p_dev_rec->bd_addr, BD_ADDR_LEN) == 0) + && (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) ) + { + /* we are currently doing bonding. Link will be disconnected when done */ + btm_cb.pairing_flags |= BTM_PAIR_FLAGS_DISC_WHEN_DONE; + return(BTM_BUSY); + } + + return(btm_sec_send_hci_disconnect(p_dev_rec, reason)); +} + +/******************************************************************************* +** +** Function btm_sec_disconnected +** +** Description This function is when a connection to the peer device is +** dropped +** +** Returns void +** +*******************************************************************************/ +void btm_sec_disconnected (UINT16 handle, UINT8 reason) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev_by_handle (handle); + UINT8 old_pairing_flags = btm_cb.pairing_flags; + int result = HCI_ERR_AUTH_FAILURE; + + /* If page was delayed for disc complete, can do it now */ + btm_cb.discing = FALSE; + + btm_acl_resubmit_page(); + + if (!p_dev_rec) + return; + +#if BTM_DISC_DURING_RS == TRUE + BTM_TRACE_ERROR0("btm_sec_disconnected - Clearing Pending flag"); + p_dev_rec->rs_disc_pending = BTM_SEC_RS_NOT_PENDING; /* reset flag */ +#endif + + /* clear unused flags */ + p_dev_rec->sm4 &= BTM_SM4_TRUE; + + BTM_TRACE_EVENT6("btm_sec_disconnected() sec_req:x%x State: %s reason:%d bda:%04x%08x RName:%s", + p_dev_rec->security_required, btm_pair_state_descr(btm_cb.pairing_state), reason, (p_dev_rec->bd_addr[0]<<8)+p_dev_rec->bd_addr[1], + (p_dev_rec->bd_addr[2]<<24)+(p_dev_rec->bd_addr[3]<<16)+(p_dev_rec->bd_addr[4]<<8)+p_dev_rec->bd_addr[5], p_dev_rec->sec_bd_name); + + BTM_TRACE_EVENT1("before Update sec_flags=0x%x", p_dev_rec->sec_flags); + + /* If we are in the process of bonding we need to tell client that auth failed */ + if ( (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) + && (memcmp (btm_cb.pairing_bda, p_dev_rec->bd_addr, BD_ADDR_LEN) == 0)) + { + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + p_dev_rec->sec_flags &= ~BTM_SEC_LINK_KEY_KNOWN; + if (btm_cb.api.p_auth_complete_callback) + { + /* If the disconnection reason is REPEATED_ATTEMPTS, + send this error message to complete callback function + to display the error message of Repeated attempts. + All others, send HCI_ERR_AUTH_FAILURE. */ + if (reason == HCI_ERR_REPEATED_ATTEMPTS) + { + result = HCI_ERR_REPEATED_ATTEMPTS; + } + else if (old_pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) + { + result = HCI_ERR_HOST_REJECT_SECURITY; + } + (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, reason); + } + } + + p_dev_rec->hci_handle = BTM_SEC_INVALID_HANDLE; + p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; + +#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE + p_dev_rec->enc_key_size = 0; + btm_ble_resume_bg_conn(NULL, TRUE); + /* see sec_flags processing in btm_acl_removed */ +#endif + p_dev_rec->sec_flags &= ~(BTM_SEC_AUTHORIZED | BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED | BTM_SEC_ROLE_SWITCHED); + + p_dev_rec->security_required = BTM_SEC_NONE; + p_dev_rec->p_callback = NULL; /* when the peer device time out the authentication before we do, this call back must be reset here */ + BTM_TRACE_EVENT1("after Update sec_flags=0x%x", p_dev_rec->sec_flags); +} + +/******************************************************************************* +** +** Function btm_sec_link_key_notification +** +** Description This function is called when a new connection link key is +** generated +** +** Returns Pointer to the record or NULL +** +*******************************************************************************/ +void btm_sec_link_key_notification (UINT8 *p_bda, UINT8 *p_link_key, UINT8 key_type) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_or_alloc_dev (p_bda); + BOOLEAN we_are_bonding = FALSE; + + BTM_TRACE_EVENT3 ("btm_sec_link_key_notification() BDA:%04x%08x, TYPE: %d", + (p_bda[0]<<8)+p_bda[1], (p_bda[2]<<24)+(p_bda[3]<<16)+(p_bda[4]<<8)+p_bda[5], + key_type); + + /* If connection was made to do bonding restore link security if changed */ + btm_restore_mode(); + + /* Override the key type if version is pre-1.1 */ + if (btm_cb.devcb.local_version.hci_version < HCI_VERSION_1_1) + p_dev_rec->link_key_type = BTM_LKEY_TYPE_IGNORE; + if (key_type != BTM_LKEY_TYPE_CHANGED_COMB) + p_dev_rec->link_key_type = key_type; + + p_dev_rec->sec_flags |= BTM_SEC_LINK_KEY_KNOWN; + + memcpy (p_dev_rec->link_key, p_link_key, LINK_KEY_LEN); + + if ( (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) + && (memcmp (btm_cb.pairing_bda, p_bda, BD_ADDR_LEN) == 0) ) + { + if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) + we_are_bonding = TRUE; + else + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + } + + /* If name is not known at this point delay calling callback until the name is */ + /* resolved. Unless it is a HID Device and we really need to send all link keys. */ + if ((!(p_dev_rec->sec_flags & BTM_SEC_NAME_KNOWN) + && ((p_dev_rec->dev_class[1] & BTM_COD_MAJOR_CLASS_MASK) != BTM_COD_MAJOR_PERIPHERAL)) ) + { + BTM_TRACE_EVENT3 ("btm_sec_link_key_notification() Delayed BDA: %08x%04x Type:%d", + (p_bda[0]<<24) + (p_bda[1]<<16) + (p_bda[2]<<8) + p_bda[3], (p_bda[4] << 8) + p_bda[5], key_type); + + p_dev_rec->link_key_not_sent = TRUE; + + /* If it is for bonding nothing else will follow, so we need to start name resolution */ + if (we_are_bonding) + { + if (!(btsnd_hcic_rmt_name_req (p_bda, HCI_PAGE_SCAN_REP_MODE_R1, HCI_MANDATARY_PAGE_SCAN_MODE, 0))) + btm_inq_rmt_name_failed(); + } + + BTM_TRACE_EVENT3 ("rmt_io_caps:%d, sec_flags:x%x, dev_class[1]:x%02x", p_dev_rec->rmt_io_caps, p_dev_rec->sec_flags, p_dev_rec->dev_class[1]) + return; + } + + /* If its not us who perform authentication, we should tell stackserver */ + /* that some authentication has been completed */ + /* This is required when different entities receive link notification and auth complete */ + if (!(p_dev_rec->security_required & BTM_SEC_OUT_AUTHENTICATE)) + { + if (btm_cb.api.p_auth_complete_callback) + (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, HCI_SUCCESS); + } + + /* We will save link key only if the user authorized it - BTE report link key in all cases */ +#ifdef BRCM_NONE_BTE + if (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_AUTHED) +#endif + { + if (btm_cb.api.p_link_key_callback) + { + (*btm_cb.api.p_link_key_callback) (p_bda, p_dev_rec->dev_class, p_dev_rec->sec_bd_name, + p_link_key, p_dev_rec->link_key_type); + } + } +} + +/******************************************************************************* +** +** Function btm_sec_link_key_request +** +** Description This function is called when controller requests link key +** +** Returns Pointer to the record or NULL +** +*******************************************************************************/ +void btm_sec_link_key_request (UINT8 *p_bda) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_or_alloc_dev (p_bda); + + BTM_TRACE_EVENT6 ("btm_sec_link_key_request() BDA: %02x:%02x:%02x:%02x:%02x:%02x", + p_bda[0], p_bda[1], p_bda[2], p_bda[3], p_bda[4], p_bda[5]); + + if (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN) + { + btsnd_hcic_link_key_req_reply (p_bda, p_dev_rec->link_key); + return; + } + + /* Notify L2CAP to increase timeout */ + l2c_pin_code_request (p_bda); + + /* Only ask the host for a key if this guy is not already bonding */ + if ( (btm_cb.pairing_state == BTM_PAIR_STATE_IDLE) + || (memcmp (p_bda, btm_cb.pairing_bda, BD_ADDR_LEN) != 0) ) + { + if (btm_cb.api.p_link_key_req_callback) + { + if ((*btm_cb.api.p_link_key_req_callback)(p_bda, p_dev_rec->link_key) == BTM_SUCCESS) + { + btsnd_hcic_link_key_req_reply (p_bda, p_dev_rec->link_key); + return; + } + } + } + + /* The link key is not in the database and it is not known to the manager */ + btsnd_hcic_link_key_neg_reply (p_bda); +} + +/******************************************************************************* +** +** Function btm_sec_pairing_timeout +** +** Description This function is called when host does not provide PIN +** within requested time +** +** Returns Pointer to the TLE struct +** +*******************************************************************************/ +static void btm_sec_pairing_timeout (TIMER_LIST_ENT *p_tle) +{ + tBTM_CB *p_cb = &btm_cb; + tBTM_SEC_DEV_REC *p_dev_rec; +#if BTM_OOB_INCLUDED == TRUE +#if (BTM_LOCAL_IO_CAPS == BTM_IO_CAP_NONE) + tBTM_AUTH_REQ auth_req = BTM_AUTH_AP_NO; +#else + tBTM_AUTH_REQ auth_req = BTM_AUTH_AP_YES; +#endif +#endif + UINT8 name[2]; + + p_cb->pairing_tle.param = 0; +/* Coverity: FALSE-POSITIVE error from Coverity tool. Please do NOT remove following comment. */ +/* coverity[UNUSED_VALUE] pointer p_dev_rec is actually used several times... This is a Coverity false-positive, i.e. a fake issue. +*/ + p_dev_rec = btm_find_dev (p_cb->pairing_bda); + + BTM_TRACE_EVENT2 ("btm_sec_pairing_timeout() State: %s Flags: %u", + btm_pair_state_descr(p_cb->pairing_state), p_cb->pairing_flags); + + switch (p_cb->pairing_state) + { + case BTM_PAIR_STATE_WAIT_PIN_REQ: + btm_sec_bond_cancel_complete(); + break; + + case BTM_PAIR_STATE_WAIT_LOCAL_PIN: + if ( (btm_cb.pairing_flags & BTM_PAIR_FLAGS_PRE_FETCH_PIN) == 0) + btsnd_hcic_pin_code_neg_reply (p_cb->pairing_bda); + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + /* We need to notify the UI that no longer need the PIN */ + if (btm_cb.api.p_auth_complete_callback) + { + if (p_dev_rec == NULL) + { + name[0] = 0; + (*btm_cb.api.p_auth_complete_callback) (p_cb->pairing_bda, + NULL, + name, HCI_ERR_CONNECTION_TOUT); + } + else + (*btm_cb.api.p_auth_complete_callback) (p_dev_rec->bd_addr, + p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, HCI_ERR_CONNECTION_TOUT); + } + break; + + case BTM_PAIR_STATE_WAIT_NUMERIC_CONFIRM: + btsnd_hcic_user_conf_reply (p_cb->pairing_bda, FALSE); + /* btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); */ + break; + +#if (BTM_LOCAL_IO_CAPS != BTM_IO_CAP_NONE) + case BTM_PAIR_STATE_KEY_ENTRY: + btsnd_hcic_user_passkey_neg_reply(p_cb->pairing_bda); + /* btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); */ + break; +#endif /* !BTM_IO_CAP_NONE */ + +#if BTM_OOB_INCLUDED == TRUE + case BTM_PAIR_STATE_WAIT_LOCAL_IOCAPS: + if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) + auth_req |= BTM_AUTH_DD_BOND; + + btsnd_hcic_io_cap_req_reply (p_cb->pairing_bda, btm_cb.devcb.loc_io_caps, + BTM_OOB_NONE, auth_req); + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + break; + + case BTM_PAIR_STATE_WAIT_LOCAL_OOB_RSP: + btsnd_hcic_rem_oob_neg_reply (p_cb->pairing_bda); + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + break; +#endif /* BTM_OOB_INCLUDED */ + + case BTM_PAIR_STATE_WAIT_DISCONNECT: + /* simple pairing failed. Started a 1-sec timer at simple pairing complete. + * now it's time to tear down the ACL link*/ + if (p_dev_rec == NULL) + { + BTM_TRACE_ERROR2 ("btm_sec_pairing_timeout() BTM_PAIR_STATE_WAIT_DISCONNECT unknown BDA: %08x%04x", + (p_cb->pairing_bda[0]<<24) + (p_cb->pairing_bda[1]<<16) + (p_cb->pairing_bda[2]<<8) + p_cb->pairing_bda[3], + (p_cb->pairing_bda[4] << 8) + p_cb->pairing_bda[5]); + break; + } + btm_sec_send_hci_disconnect (p_dev_rec, HCI_ERR_AUTH_FAILURE); + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + break; + + default: + BTM_TRACE_WARNING1 ("btm_sec_pairing_timeout() not processed state: %s", btm_pair_state_descr(btm_cb.pairing_state)); + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + break; + } +} + +/******************************************************************************* +** +** Function btm_sec_pin_code_request +** +** Description This function is called when controller requests PIN code +** +** Returns Pointer to the record or NULL +** +*******************************************************************************/ +void btm_sec_pin_code_request (UINT8 *p_bda) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + tBTM_CB *p_cb = &btm_cb; + + BTM_TRACE_EVENT3 ("btm_sec_pin_code_request() State: %s, BDA:%04x%08x", + btm_pair_state_descr(btm_cb.pairing_state), + (p_bda[0]<<8)+p_bda[1], (p_bda[2]<<24)+(p_bda[3]<<16)+(p_bda[4]<<8)+p_bda[5] ); + + if (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) + { + if ( (memcmp (p_bda, btm_cb.pairing_bda, BD_ADDR_LEN) == 0) && + (btm_cb.pairing_state == BTM_PAIR_STATE_WAIT_AUTH_COMPLETE) ) + { + /* fake this out - porshe carkit issue - */ +// btm_cb.pairing_state = BTM_PAIR_STATE_IDLE; + if(! btm_cb.pin_code_len_saved) + { + btsnd_hcic_pin_code_neg_reply (p_bda); + return; + } + else + { + btsnd_hcic_pin_code_req_reply (p_bda, btm_cb.pin_code_len_saved, p_cb->pin_code); + return; + } + } + else if ((btm_cb.pairing_state != BTM_PAIR_STATE_WAIT_PIN_REQ) + || memcmp (p_bda, btm_cb.pairing_bda, BD_ADDR_LEN) != 0) + { + BTM_TRACE_WARNING1 ("btm_sec_pin_code_request() rejected - state: %s", + btm_pair_state_descr(btm_cb.pairing_state)); + +#ifdef PORCHE_PAIRING_CONFLICT + /* reply pin code again due to counter in_rand when local initiates pairing */ + BTM_TRACE_EVENT0 ("btm_sec_pin_code_request from remote dev. for local initiated pairing"); + if(! btm_cb.pin_code_len_saved) + { + btsnd_hcic_pin_code_neg_reply (p_bda); + } + else + { + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_AUTH_COMPLETE); + btsnd_hcic_pin_code_req_reply (p_bda, btm_cb.pin_code_len_saved, p_cb->pin_code); + } +#else + btsnd_hcic_pin_code_neg_reply (p_bda); +#endif + return; + } + } + + p_dev_rec = btm_find_or_alloc_dev (p_bda); + /* received PIN code request. must be non-sm4 */ + p_dev_rec->sm4 = BTM_SM4_KNOWN; + + if (btm_cb.pairing_state == BTM_PAIR_STATE_IDLE) + { + memcpy (btm_cb.pairing_bda, p_bda, BD_ADDR_LEN); + + btm_cb.pairing_flags = BTM_PAIR_FLAGS_PEER_STARTED_DD; + /* Make sure we reset the trusted mask to help against attacks */ + BTM_SEC_CLR_TRUSTED_DEVICE(p_dev_rec->trusted_mask); + } + + if (!p_cb->pairing_disabled && (p_cb->cfg.pin_type == HCI_PIN_TYPE_FIXED)) + { + BTM_TRACE_EVENT0 ("btm_sec_pin_code_request fixed pin replying"); + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_AUTH_COMPLETE); + btsnd_hcic_pin_code_req_reply (p_bda, p_cb->cfg.pin_code_len, p_cb->cfg.pin_code); + return; + } + + /* Use the connecting device's CoD for the connection */ + if ( (!memcmp (p_bda, p_cb->connecting_bda, BD_ADDR_LEN)) + && (p_cb->connecting_dc[0] || p_cb->connecting_dc[1] || p_cb->connecting_dc[2]) ) + memcpy (p_dev_rec->dev_class, p_cb->connecting_dc, DEV_CLASS_LEN); + + /* We could have started connection after asking user for the PIN code */ + if (btm_cb.pin_code_len != 0) + { + BTM_TRACE_EVENT0 ("btm_sec_pin_code_request bonding sending reply"); + btsnd_hcic_pin_code_req_reply (p_bda, btm_cb.pin_code_len, p_cb->pin_code); + +#ifdef PORCHE_PAIRING_CONFLICT + btm_cb.pin_code_len_saved = btm_cb.pin_code_len; +#endif + + /* Mark that we forwarded received from the user PIN code */ + btm_cb.pin_code_len = 0; + + /* We can change mode back right away, that other connection being established */ + /* is not forced to be secure - found a FW issue, so we can not do this + btm_restore_mode(); */ + + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_AUTH_COMPLETE); + } + + /* If pairing disabled OR (no PIN callback and not bonding) */ + /* OR we could not allocate entry in the database reject pairing request */ + else if (p_cb->pairing_disabled + || (p_cb->api.p_pin_callback == NULL) + + /* OR Microsoft keyboard can for some reason try to establish connection */ + /* the only thing we can do here is to shut it up. Normally we will be originator */ + /* for keyboard bonding */ + || (!p_dev_rec->is_originator + && ((p_dev_rec->dev_class[1] & BTM_COD_MAJOR_CLASS_MASK) == BTM_COD_MAJOR_PERIPHERAL) + && (p_dev_rec->dev_class[2] & BTM_COD_MINOR_KEYBOARD)) ) + { + BTM_TRACE_WARNING3("btm_sec_pin_code_request(): Pairing disabled:%d; PIN callback:%x, Dev Rec:%x!", + p_cb->pairing_disabled, p_cb->api.p_pin_callback, p_dev_rec); + + btsnd_hcic_pin_code_neg_reply (p_bda); + } + /* Notify upper layer of PIN request and start expiration timer */ + else + { + btm_cb.pin_code_len_saved = 0; + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_LOCAL_PIN); + /* Pin code request can not come at the same time as connection request */ + memcpy (p_cb->connecting_bda, p_bda, BD_ADDR_LEN); + memcpy (p_cb->connecting_dc, p_dev_rec->dev_class, DEV_CLASS_LEN); + + /* Check if the name is known */ + /* Even if name is not known we might not be able to get one */ + /* this is the case when we are already getting something from the */ + /* device, so HCI level is flow controlled */ + /* Also cannot send remote name request while paging, i.e. connection is not completed */ + if (p_dev_rec->sec_flags & BTM_SEC_NAME_KNOWN) + { + BTM_TRACE_EVENT0 ("btm_sec_pin_code_request going for callback"); + + btm_cb.pairing_flags |= BTM_PAIR_FLAGS_PIN_REQD; + if (p_cb->api.p_pin_callback) + (*p_cb->api.p_pin_callback) (p_bda, p_dev_rec->dev_class, p_dev_rec->sec_bd_name); + } + else + { + BTM_TRACE_EVENT0 ("btm_sec_pin_code_request going for remote name"); + + /* We received PIN code request for the device with unknown name */ + /* it is not user friendly just to ask for the PIN without name */ + /* try to get name at first */ + if (!btsnd_hcic_rmt_name_req (p_dev_rec->bd_addr, + HCI_PAGE_SCAN_REP_MODE_R1, + HCI_MANDATARY_PAGE_SCAN_MODE, 0)) + { + p_dev_rec->sec_flags |= BTM_SEC_NAME_KNOWN; + p_dev_rec->sec_bd_name[0] = 'f'; + p_dev_rec->sec_bd_name[1] = '0'; + BTM_TRACE_ERROR0 ("can not send rmt_name_req?? fake a name and call callback"); + + btm_cb.pairing_flags |= BTM_PAIR_FLAGS_PIN_REQD; + if (p_cb->api.p_pin_callback) + (*p_cb->api.p_pin_callback) (p_bda, p_dev_rec->dev_class, p_dev_rec->sec_bd_name); + } + } + } + + return; +} + +/******************************************************************************* +** +** Function btm_sec_update_clock_offset +** +** Description This function is called to update clock offset +** +** Returns void +** +*******************************************************************************/ +void btm_sec_update_clock_offset (UINT16 handle, UINT16 clock_offset) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + tBTM_INQ_INFO *p_inq_info; + + if ((p_dev_rec = btm_find_dev_by_handle (handle)) == NULL) + return; + + p_dev_rec->clock_offset = clock_offset | BTM_CLOCK_OFFSET_VALID; + + if ((p_inq_info = BTM_InqDbRead(p_dev_rec->bd_addr)) == NULL) + return; + + p_inq_info->results.clock_offset = clock_offset | BTM_CLOCK_OFFSET_VALID; +} + + +/****************************************************************** +** S T A T I C F U N C T I O N S +*******************************************************************/ + +/******************************************************************************* +** +** Function btm_sec_execute_procedure +** +** Description This function is called to start required security +** procedure. There is a case when multiplexing protocol +** calls this function on the originating side, connection to +** the peer will not be established. This function in this +** case performs only authorization. +** +** Returns BTM_SUCCESS - permission is granted +** BTM_CMD_STARTED - in process +** BTM_NO_RESOURCES - permission declined +** +*******************************************************************************/ +static tBTM_STATUS btm_sec_execute_procedure (tBTM_SEC_DEV_REC *p_dev_rec) +{ + BTM_TRACE_EVENT3 ("btm_sec_execute_procedure: Required:0x%x Flags:0x%x State:%d", + p_dev_rec->security_required, p_dev_rec->sec_flags, p_dev_rec->sec_state); + + /* There is a chance that we are getting name. Wait until done. */ + if (p_dev_rec->sec_state != 0) + return(BTM_CMD_STARTED); + + /* If any security is required, get the name first */ + if (!(p_dev_rec->sec_flags & BTM_SEC_NAME_KNOWN) + && (p_dev_rec->hci_handle != BTM_SEC_INVALID_HANDLE)) + { + BTM_TRACE_EVENT0 ("Security Manager: Start get name"); + if (!btm_sec_start_get_name (p_dev_rec)) + { + return(BTM_NO_RESOURCES); + } + return(BTM_CMD_STARTED); + } + + /* If connection is not authenticated and authentication is required */ + /* start authentication and return PENDING to the caller */ + if ((!(p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED)) + && (( p_dev_rec->is_originator && (p_dev_rec->security_required & BTM_SEC_OUT_AUTHENTICATE)) + || (!p_dev_rec->is_originator && (p_dev_rec->security_required & BTM_SEC_IN_AUTHENTICATE))) + && (p_dev_rec->hci_handle != BTM_SEC_INVALID_HANDLE)) + { +#if (L2CAP_UCD_INCLUDED == TRUE) + /* if incoming UCD packet, discard it */ + if ( !p_dev_rec->is_originator && (p_dev_rec->is_ucd == TRUE )) + return(BTM_FAILED_ON_SECURITY); +#endif + + BTM_TRACE_EVENT0 ("Security Manager: Start authentication"); + + if (!btm_sec_start_authentication (p_dev_rec)) + { + return(BTM_NO_RESOURCES); + } + return(BTM_CMD_STARTED); + } + + /* If connection is not encrypted and encryption is required */ + /* start encryption and return PENDING to the caller */ + if (!(p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED) + && (( p_dev_rec->is_originator && (p_dev_rec->security_required & BTM_SEC_OUT_ENCRYPT)) + || (!p_dev_rec->is_originator && (p_dev_rec->security_required & BTM_SEC_IN_ENCRYPT))) + && (p_dev_rec->hci_handle != BTM_SEC_INVALID_HANDLE)) + { +#if (L2CAP_UCD_INCLUDED == TRUE) + /* if incoming UCD packet, discard it */ + if ( !p_dev_rec->is_originator && (p_dev_rec->is_ucd == TRUE )) + return(BTM_FAILED_ON_SECURITY); +#endif + + BTM_TRACE_EVENT0 ("Security Manager: Start encryption"); + + if (!btm_sec_start_encryption (p_dev_rec)) + { + return(BTM_NO_RESOURCES); + } + return(BTM_CMD_STARTED); + } + + /* If connection is not authorized and authorization is required */ + /* start authorization and return PENDING to the caller */ + if (!(p_dev_rec->sec_flags & BTM_SEC_AUTHORIZED) + && (( p_dev_rec->is_originator && (p_dev_rec->security_required & BTM_SEC_OUT_AUTHORIZE)) + || (!p_dev_rec->is_originator && (p_dev_rec->security_required & BTM_SEC_IN_AUTHORIZE)))) + { + BTM_TRACE_EVENT2 ("service id:%d, is trusted:%d", + p_dev_rec->p_cur_service->service_id, + (BTM_SEC_IS_SERVICE_TRUSTED(p_dev_rec->trusted_mask, + p_dev_rec->p_cur_service->service_id))); + if ((btm_sec_are_all_trusted(p_dev_rec->trusted_mask) == FALSE) && + (p_dev_rec->p_cur_service->service_id < BTM_SEC_MAX_SERVICES) && + (BTM_SEC_IS_SERVICE_TRUSTED(p_dev_rec->trusted_mask, + p_dev_rec->p_cur_service->service_id) == FALSE)) + { + BTM_TRACE_EVENT0 ("Security Manager: Start authorization"); + return(btm_sec_start_authorization (p_dev_rec)); + } + } + + /* All required security procedures already established */ + p_dev_rec->security_required &= ~(BTM_SEC_OUT_AUTHORIZE | BTM_SEC_IN_AUTHORIZE | + BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_IN_AUTHENTICATE | + BTM_SEC_OUT_ENCRYPT | BTM_SEC_IN_ENCRYPT | + BTM_SEC_FORCE_MASTER | BTM_SEC_ATTEMPT_MASTER | + BTM_SEC_FORCE_SLAVE | BTM_SEC_ATTEMPT_SLAVE); + + BTM_TRACE_EVENT2 ("Security Manager: trusted:0x%04x%04x", p_dev_rec->trusted_mask[1], p_dev_rec->trusted_mask[0]); + BTM_TRACE_EVENT0 ("Security Manager: access granted"); + + return(BTM_SUCCESS); +} + + +/******************************************************************************* +** +** Function btm_sec_start_get_name +** +** Description This function is called to start get name procedure +** +** Returns TRUE if started +** +*******************************************************************************/ +static BOOLEAN btm_sec_start_get_name (tBTM_SEC_DEV_REC *p_dev_rec) +{ + UINT8 tempstate = p_dev_rec->sec_state; + + p_dev_rec->sec_state = BTM_SEC_STATE_GETTING_NAME; + + /* Device should be connected, no need to provide correct page params */ + /* 0 and NULL are as timeout and callback params because they are not used in security get name case */ + if ((btm_initiate_rem_name (p_dev_rec->bd_addr, NULL, BTM_RMT_NAME_SEC, + 0, NULL)) != BTM_CMD_STARTED) + { + p_dev_rec->sec_state = tempstate; + return(FALSE); + } + + return(TRUE); +} + +/******************************************************************************* +** +** Function btm_sec_start_authentication +** +** Description This function is called to start authentication +** +** Returns TRUE if started +** +*******************************************************************************/ +static BOOLEAN btm_sec_start_authentication (tBTM_SEC_DEV_REC *p_dev_rec) +{ + p_dev_rec->sec_state = BTM_SEC_STATE_AUTHENTICATING; + + return(btsnd_hcic_auth_request (p_dev_rec->hci_handle)); +} + +/******************************************************************************* +** +** Function btm_sec_start_encryption +** +** Description This function is called to start encryption +** +** Returns TRUE if started +** +*******************************************************************************/ +static BOOLEAN btm_sec_start_encryption (tBTM_SEC_DEV_REC *p_dev_rec) +{ + if (!btsnd_hcic_set_conn_encrypt (p_dev_rec->hci_handle, TRUE)) + return(FALSE); + + p_dev_rec->sec_state = BTM_SEC_STATE_ENCRYPTING; + return(TRUE); +} + + +/******************************************************************************* +** +** Function btm_sec_start_authorization +** +** Description This function is called to start authorization +** +** Returns TRUE if started +** +*******************************************************************************/ +static UINT8 btm_sec_start_authorization (tBTM_SEC_DEV_REC *p_dev_rec) +{ + UINT8 result; + UINT8 *p_service_name = NULL; + UINT8 service_id; + + if ((p_dev_rec->sec_flags & BTM_SEC_NAME_KNOWN) + || (p_dev_rec->hci_handle == BTM_SEC_INVALID_HANDLE)) + { + if (!btm_cb.api.p_authorize_callback) + return(BTM_MODE_UNSUPPORTED); + + if (p_dev_rec->p_cur_service) + { +#if BTM_SEC_SERVICE_NAME_LEN > 0 + if (p_dev_rec->is_originator) + p_service_name = p_dev_rec->p_cur_service->orig_service_name; + else + p_service_name = p_dev_rec->p_cur_service->term_service_name; +#endif + service_id = p_dev_rec->p_cur_service->service_id; + } + else + service_id = 0; + + p_dev_rec->sec_state = BTM_SEC_STATE_AUTHORIZING; + result = (*btm_cb.api.p_authorize_callback) (p_dev_rec->bd_addr, + p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, + p_service_name, + service_id, + p_dev_rec->is_originator); + if (result == BTM_SUCCESS) + { + p_dev_rec->sec_flags |= BTM_SEC_AUTHORIZED; + p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; + } + return(result); + } + btm_sec_start_get_name (p_dev_rec); + return(BTM_CMD_STARTED); +} + +/******************************************************************************* +** +** Function btm_sec_are_all_trusted +** +** Description This function is called check if all services are trusted +** +** Returns TRUE if all are trusted, otherwise FALSE +** +*******************************************************************************/ +BOOLEAN btm_sec_are_all_trusted(UINT32 p_mask[]) +{ + int trusted_inx; + for (trusted_inx = 0; trusted_inx < BTM_SEC_SERVICE_ARRAY_SIZE; trusted_inx++) + { + if (p_mask[trusted_inx] != BTM_SEC_TRUST_ALL) + return(FALSE); + } + + return(TRUE); +} + +/******************************************************************************* +** +** Function btm_sec_find_first_serv +** +** Description Look for the first record in the service database +** with specified PSM +** +** Returns Pointer to the record or NULL +** +*******************************************************************************/ +static tBTM_SEC_SERV_REC *btm_sec_find_first_serv (CONNECTION_TYPE conn_type, UINT16 psm) +{ + tBTM_SEC_SERV_REC *p_serv_rec = &btm_cb.sec_serv_rec[0]; + int i; + BOOLEAN is_originator; + +#if (L2CAP_UCD_INCLUDED == TRUE) + + if ( conn_type & CONNECTION_TYPE_ORIG_MASK ) + is_originator = TRUE; + else + is_originator = FALSE; +#else + is_originator = conn_type; +#endif + + if (is_originator && btm_cb.p_out_serv && btm_cb.p_out_serv->psm == psm) + { + /* If this is outgoing connection and the PSM matches p_out_serv, + * use it as the current service */ + return btm_cb.p_out_serv; + } + + /* otherwise, just find the first record with the specified PSM */ + for (i = 0; i < BTM_SEC_MAX_SERVICE_RECORDS; i++, p_serv_rec++) + { + if ( (p_serv_rec->security_flags & BTM_SEC_IN_USE) && (p_serv_rec->psm == psm) ) + return(p_serv_rec); + } + return(NULL); +} + + +/******************************************************************************* +** +** Function btm_sec_find_next_serv +** +** Description Look for the next record in the service database +** with specified PSM +** +** Returns Pointer to the record or NULL +** +*******************************************************************************/ +static tBTM_SEC_SERV_REC *btm_sec_find_next_serv (tBTM_SEC_SERV_REC *p_cur) +{ + tBTM_SEC_SERV_REC *p_serv_rec = &btm_cb.sec_serv_rec[0]; + int i; + + for (i = 0; i < BTM_SEC_MAX_SERVICE_RECORDS; i++, p_serv_rec++) + { + if ((p_serv_rec->security_flags & BTM_SEC_IN_USE) + && (p_serv_rec->psm == p_cur->psm) ) + { + if (p_cur != p_serv_rec) + { + return(p_serv_rec); + } + } + } + return(NULL); +} + + +/******************************************************************************* +** +** Function btm_sec_find_mx_serv +** +** Description Look for the record in the service database with specified +** PSM and multiplexor channel information +** +** Returns Pointer to the record or NULL +** +*******************************************************************************/ +static tBTM_SEC_SERV_REC *btm_sec_find_mx_serv (UINT8 is_originator, UINT16 psm, + UINT32 mx_proto_id, UINT32 mx_chan_id) +{ + tBTM_SEC_SERV_REC *p_out_serv = btm_cb.p_out_serv; + tBTM_SEC_SERV_REC *p_serv_rec = &btm_cb.sec_serv_rec[0]; + int i; + + BTM_TRACE_DEBUG0 ("btm_sec_find_mx_serv"); + if (is_originator && p_out_serv && p_out_serv->psm == psm + && p_out_serv->mx_proto_id == mx_proto_id + && p_out_serv->orig_mx_chan_id == mx_chan_id) + { + /* If this is outgoing connection and the parameters match p_out_serv, + * use it as the current service */ + return btm_cb.p_out_serv; + } + + /* otherwise, the old way */ + for (i = 0; i < BTM_SEC_MAX_SERVICE_RECORDS; i++, p_serv_rec++) + { + if ((p_serv_rec->security_flags & BTM_SEC_IN_USE) + && (p_serv_rec->psm == psm) + && (p_serv_rec->mx_proto_id == mx_proto_id) + && (( is_originator && (p_serv_rec->orig_mx_chan_id == mx_chan_id)) + || (!is_originator && (p_serv_rec->term_mx_chan_id == mx_chan_id)))) + { + return(p_serv_rec); + } + } + return(NULL); +} + + +/******************************************************************************* +** +** Function btm_sec_collision_timeout +** +** Description Encryption could not start because of the collision +** try to do it again +** +** Returns Pointer to the TLE struct +** +*******************************************************************************/ +static void btm_sec_collision_timeout (TIMER_LIST_ENT *p_tle) +{ + tBTM_STATUS status; + + BTM_TRACE_EVENT0 ("btm_sec_collision_timeout()"); + btm_cb.sec_collision_tle.param = 0; + + status = btm_sec_execute_procedure (btm_cb.p_collided_dev_rec); + + /* If result is pending reply from the user or from the device is pending */ + if (status != BTM_CMD_STARTED) + { + /* There is no next procedure or start of procedure failed, notify the waiting layer */ + btm_sec_dev_rec_cback_event (btm_cb.p_collided_dev_rec, status); + } +} + +/******************************************************************************* +** +** Function btm_sec_link_key_request +** +** Description This function is called when controller requests link key +** +** Returns Pointer to the record or NULL +** +*******************************************************************************/ +static void btm_send_link_key_notif (tBTM_SEC_DEV_REC *p_dev_rec) +{ + if (btm_cb.api.p_link_key_callback) + (*btm_cb.api.p_link_key_callback) (p_dev_rec->bd_addr, p_dev_rec->dev_class, + p_dev_rec->sec_bd_name, p_dev_rec->link_key, + p_dev_rec->link_key_type); +} + +/******************************************************************************* +** +** Function BTM_ReadTrustedMask +** +** Description Get trusted mask for the peer device +** +** Parameters: bd_addr - Address of the device +** +** Returns NULL, if the device record is not found. +** otherwise, the trusted mask +** +*******************************************************************************/ +UINT32 * BTM_ReadTrustedMask (BD_ADDR bd_addr) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + + if ((p_dev_rec = btm_find_dev (bd_addr)) != NULL) + { + return(p_dev_rec->trusted_mask); + } + else + { + return NULL; + } +} + +/******************************************************************************* +** +** Function btm_restore_mode +** +** Description This function returns the security mode to previous setting +** if it was changed during bonding. +** +** +** Parameters: void +** +*******************************************************************************/ +static void btm_restore_mode(void) +{ + if (btm_cb.security_mode_changed) + { + btm_cb.security_mode_changed = FALSE; + BTM_TRACE_DEBUG1("btm_restore_mode: Authen Enable -> %d", (btm_cb.security_mode == BTM_SEC_MODE_LINK)); + btsnd_hcic_write_auth_enable ((UINT8)(btm_cb.security_mode == BTM_SEC_MODE_LINK)); + } + + if (btm_cb.pin_type_changed) + { + btm_cb.pin_type_changed = FALSE; + btsnd_hcic_write_pin_type (btm_cb.cfg.pin_type); + } +} + + +/******************************************************************************* +** +** Function btm_sec_find_dev_by_sec_state +** +** Description Look for the record in the device database for the device +** which is being authenticated or encrypted +** +** Returns Pointer to the record or NULL +** +*******************************************************************************/ +tBTM_SEC_DEV_REC *btm_sec_find_dev_by_sec_state (UINT8 state) +{ + tBTM_SEC_DEV_REC *p_dev_rec = &btm_cb.sec_dev_rec[0]; + int i; + + for (i = 0; i < BTM_SEC_MAX_DEVICE_RECORDS; i++, p_dev_rec++) + { + if ((p_dev_rec->sec_flags & BTM_SEC_IN_USE) + && (p_dev_rec->sec_state == state)) + return(p_dev_rec); + } + return(NULL); +} + +/******************************************************************************* +** +** Function BTM_snd_conn_encrypt +** +** Description This function is called to start/stop encryption +** Used by JSR-82 +** +** Returns TRUE if request started +** +*******************************************************************************/ +BOOLEAN BTM_snd_conn_encrypt (UINT16 handle, BOOLEAN enable) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev_by_handle (handle); + + BTM_TRACE_EVENT2 ("BTM_snd_conn_encrypt Security Manager: encrypt_change p_dev_rec : 0x%x, enable = %s", p_dev_rec, (enable == TRUE) ? "TRUE" : "FALSE"); + + if (!p_dev_rec) + { + BTM_TRACE_EVENT1 ("BTM_snd_conn_encrypt Error no p_dev_rec : 0x%x\n", p_dev_rec); + return(FALSE); + } + + if ( p_dev_rec->sec_state == BTM_SEC_STATE_IDLE) + { + if (!btsnd_hcic_set_conn_encrypt (handle, enable)) + return(FALSE); + + p_dev_rec->sec_state = BTM_SEC_STATE_ENCRYPTING; + + return(TRUE); + } + else + return(FALSE); +} + +/******************************************************************************* +** +** Function btm_sec_change_pairing_state +** +** Description This function is called to change pairing state +** +*******************************************************************************/ +static void btm_sec_change_pairing_state (tBTM_PAIRING_STATE new_state) +{ + tBTM_PAIRING_STATE old_state = btm_cb.pairing_state; + + BTM_TRACE_EVENT1 ("btm_sec_change_pairing_state Old: %s", btm_pair_state_descr(btm_cb.pairing_state)); + BTM_TRACE_EVENT2 ("btm_sec_change_pairing_state New: %s pairing_flags:0x%x",btm_pair_state_descr(new_state), btm_cb.pairing_flags); + + btm_cb.pairing_state = new_state; + + if (new_state == BTM_PAIR_STATE_IDLE) + { + btu_stop_timer (&btm_cb.pairing_tle); + + btm_cb.pairing_flags = 0; + btm_cb.pin_code_len = 0; + + /* Make sure the the lcb shows we are not bonding */ + l2cu_update_lcb_4_bonding (btm_cb.pairing_bda, FALSE); + + btm_restore_mode(); + btm_sec_check_pending_reqs(); + btm_inq_clear_ssp(); + + memset (btm_cb.pairing_bda, 0xFF, BD_ADDR_LEN); + } + else + { + /* If transitionng out of idle, mark the lcb as bonding */ + if (old_state == BTM_PAIR_STATE_IDLE) + l2cu_update_lcb_4_bonding (btm_cb.pairing_bda, TRUE); + + btm_cb.pairing_tle.param = (TIMER_PARAM_TYPE)btm_sec_pairing_timeout; + + btu_start_timer (&btm_cb.pairing_tle, BTU_TTYPE_USER_FUNC, BTM_SEC_TIMEOUT_VALUE); + } +} + + +/******************************************************************************* +** +** Function btm_pair_state_descr +** +** Description Return state description for tracing +** +*******************************************************************************/ +#if (BT_USE_TRACES == TRUE) +static char *btm_pair_state_descr (tBTM_PAIRING_STATE state) +{ +#if (BT_TRACE_VERBOSE == TRUE) + switch (state) + { + case BTM_PAIR_STATE_IDLE: return("IDLE"); + case BTM_PAIR_STATE_GET_REM_NAME: return("GET_REM_NAME"); + case BTM_PAIR_STATE_WAIT_PIN_REQ: return("WAIT_PIN_REQ"); + case BTM_PAIR_STATE_WAIT_LOCAL_PIN: return("WAIT_LOCAL_PIN"); + case BTM_PAIR_STATE_WAIT_NUMERIC_CONFIRM: return("WAIT_NUM_CONFIRM"); + case BTM_PAIR_STATE_KEY_ENTRY: return("KEY_ENTRY"); + case BTM_PAIR_STATE_WAIT_LOCAL_OOB_RSP: return("WAIT_LOCAL_OOB_RSP"); + case BTM_PAIR_STATE_WAIT_LOCAL_IOCAPS: return("WAIT_LOCAL_IOCAPS"); + case BTM_PAIR_STATE_INCOMING_SSP: return("INCOMING_SSP"); + case BTM_PAIR_STATE_WAIT_AUTH_COMPLETE: return("WAIT_AUTH_COMPLETE"); + case BTM_PAIR_STATE_WAIT_DISCONNECT: return("WAIT_DISCONNECT"); + } + + return("???"); +#else + sprintf(btm_cb.state_temp_buffer,"%hu",state); + + return(btm_cb.state_temp_buffer); +#endif +} +#endif + + +/******************************************************************************* +** +** Function btm_sec_dev_rec_cback_event +** +** Description This function calls the callback function with the given +** result and clear the callback function. +** +** Parameters: void +** +*******************************************************************************/ +void btm_sec_dev_rec_cback_event (tBTM_SEC_DEV_REC *p_dev_rec, UINT8 res) +{ + tBTM_SEC_CALLBACK *p_callback = p_dev_rec->p_callback; + + if (p_dev_rec->p_callback) + { + p_dev_rec->p_callback = NULL; + + (*p_callback) (p_dev_rec->bd_addr, p_dev_rec->p_ref_data, res); + + } + btm_sec_check_pending_reqs(); +} + +/******************************************************************************* +** +** Function btm_sec_queue_mx_request +** +** Description Return state description for tracing +** +*******************************************************************************/ +static BOOLEAN btm_sec_queue_mx_request (BD_ADDR bd_addr, UINT16 psm, BOOLEAN is_orig, + UINT32 mx_proto_id, UINT32 mx_chan_id, + tBTM_SEC_CALLBACK *p_callback, void *p_ref_data) +{ + tBTM_SEC_QUEUE_ENTRY *p_e; + + p_e = (tBTM_SEC_QUEUE_ENTRY *)GKI_getbuf (sizeof(tBTM_SEC_QUEUE_ENTRY)); + + if (p_e) + { + p_e->psm = psm; + p_e->is_orig = is_orig; + p_e->p_callback = p_callback; + p_e->p_ref_data = p_ref_data; + p_e->mx_proto_id = mx_proto_id; + p_e->mx_chan_id = mx_chan_id; + + memcpy (p_e->bd_addr, bd_addr, BD_ADDR_LEN); + + BTM_TRACE_EVENT4 ("btm_sec_queue_mx_request() PSM: 0x%04x Is_Orig: %u mx_proto_id: %u mx_chan_id: %u", + psm, is_orig, mx_proto_id, mx_chan_id); + + GKI_enqueue (&btm_cb.sec_pending_q, p_e); + + return(TRUE); + } + + return(FALSE); +} + +static BOOLEAN btm_sec_check_prefetch_pin (tBTM_SEC_DEV_REC *p_dev_rec) +{ + UINT8 major = (UINT8)(p_dev_rec->dev_class[1] & BTM_COD_MAJOR_CLASS_MASK); + UINT8 minor = (UINT8)(p_dev_rec->dev_class[2] & BTM_COD_MINOR_CLASS_MASK); + BOOLEAN rv = FALSE; + + if ((major == BTM_COD_MAJOR_AUDIO) + && ((minor == BTM_COD_MINOR_CONFM_HANDSFREE) || (minor == BTM_COD_MINOR_CAR_AUDIO)) ) + { + BTM_TRACE_EVENT2 ("btm_sec_check_prefetch_pin: Skipping pre-fetch PIN for carkit COD Major: 0x%02x Minor: 0x%02x", major, minor); + + if (btm_cb.security_mode_changed == FALSE) + { + btm_cb.security_mode_changed = TRUE; +#ifdef APPL_AUTH_WRITE_EXCEPTION + if(!(APPL_AUTH_WRITE_EXCEPTION)(p_dev_rec->bd_addr)) +#endif + btsnd_hcic_write_auth_enable (TRUE); + } + } + else + { + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_LOCAL_PIN); + + /* If we got a PIN, use that, else try to get one */ + if (btm_cb.pin_code_len) + { + BTM_PINCodeReply (p_dev_rec->bd_addr, BTM_SUCCESS, btm_cb.pin_code_len, btm_cb.pin_code, p_dev_rec->trusted_mask); + } + else + { + /* pin was not supplied - pre-fetch pin code now */ + if (btm_cb.api.p_pin_callback && ((btm_cb.pairing_flags & BTM_PAIR_FLAGS_PIN_REQD) == 0)) + { + BTM_TRACE_DEBUG0("btm_sec_check_prefetch_pin: PIN code callback called"); + if (btm_bda_to_acl(p_dev_rec->bd_addr) == NULL) + btm_cb.pairing_flags |= BTM_PAIR_FLAGS_PIN_REQD; + (btm_cb.api.p_pin_callback) (p_dev_rec->bd_addr, p_dev_rec->dev_class, p_dev_rec->sec_bd_name); + } + } + + rv = TRUE; + } + + return rv; +} + +#if (BLE_INCLUDED == TRUE) +/******************************************************************************* +** +** Function btm_sec_clear_ble_keys +** +** Description This function is called to clear out the BLE keys. +** Typically when devices are removed in BTM_SecDeleteDevice, +** or when a new BT Link key is generated. +** +** Returns void +** +*******************************************************************************/ +void btm_sec_clear_ble_keys (tBTM_SEC_DEV_REC *p_dev_rec) +{ + + BTM_TRACE_DEBUG0 ("btm_sec_clear_ble_keys: Clearing BLE Keys"); +#if (SMP_INCLUDED== TRUE) + p_dev_rec->ble.key_type = 0; + memset (&p_dev_rec->ble.keys, 0, sizeof(tBTM_SEC_BLE_KEYS)); +#endif + gatt_delete_dev_from_srv_chg_clt_list(p_dev_rec->bd_addr); +} + + +/******************************************************************************* +** +** Function btm_sec_is_a_bonded_dev +** +** Description Is the specified device is a bonded device +** +** Returns TRUE - dev is bonded +** +*******************************************************************************/ +BOOLEAN btm_sec_is_a_bonded_dev (BD_ADDR bda) +{ + + tBTM_SEC_DEV_REC *p_dev_rec= btm_find_dev (bda); + BOOLEAN is_bonded= FALSE; + +#if (SMP_INCLUDED== TRUE) + if (p_dev_rec && (p_dev_rec->ble.key_type || (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN))) + { + is_bonded = TRUE; + } +#endif + BTM_TRACE_DEBUG1 ("btm_sec_is_a_bonded_dev is_bonded=%d", is_bonded); + return(is_bonded); +} + +/******************************************************************************* +** +** Function btm_sec_find_bonded_dev +** +** Description Find a bonded device starting from the specified index +** +** Returns TRUE - found a bonded device +** +*******************************************************************************/ +BOOLEAN btm_sec_find_bonded_dev (UINT8 start_idx, UINT8 *p_found_idx, tBTM_SEC_DEV_REC *p_rec) +{ + BOOLEAN found= FALSE; + +#if (SMP_INCLUDED== TRUE) + tBTM_SEC_DEV_REC *p_dev_rec; + int i; + if (start_idx >= BTM_SEC_MAX_DEVICE_RECORDS) + { + BTM_TRACE_DEBUG0 ("LE bonded device not found"); + return found; + } + + p_dev_rec = &btm_cb.sec_dev_rec[start_idx]; + for (i = start_idx; i < BTM_SEC_MAX_DEVICE_RECORDS; i++, p_dev_rec++) + { + if (p_dev_rec->ble.key_type || (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN)) + { + *p_found_idx = i; + p_rec = p_dev_rec; + break; + } + } + BTM_TRACE_DEBUG1 ("btm_sec_find_bonded_dev=%d", found); +#endif + return(found); +} +#endif /* BLE_INCLUDED */ + |