diff options
| author | jacqueline <me@jacqueline.id.au> | 2024-03-28 14:32:49 +1100 |
|---|---|---|
| committer | jacqueline <me@jacqueline.id.au> | 2024-03-28 14:32:49 +1100 |
| commit | ee29c25b29eaa4fac4e897442634b69ecc8d8125 (patch) | |
| tree | 8c5f1a140463f20f104316fa3492984e191154e9 /lib/bt/host/bluedroid/stack/btm | |
| parent | 239e6d89507a24c849385f4bfa93ac4ad58e5de5 (diff) | |
| download | tangara-fw-ee29c25b29eaa4fac4e897442634b69ecc8d8125.tar.gz | |
Fork ESP-IDF's bluetooth component
i want better sbc encoding, and no cla will stop me
Diffstat (limited to 'lib/bt/host/bluedroid/stack/btm')
20 files changed, 33865 insertions, 0 deletions
diff --git a/lib/bt/host/bluedroid/stack/btm/btm_acl.c b/lib/bt/host/bluedroid/stack/btm/btm_acl.c new file mode 100644 index 00000000..4b7e7eed --- /dev/null +++ b/lib/bt/host/bluedroid/stack/btm/btm_acl.c @@ -0,0 +1,2757 @@ +/****************************************************************************** + * + * Copyright (C) 2000-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/***************************************************************************** +** +** 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. +** +** +******************************************************************************/ + +#include <stdlib.h> +#include <string.h> +//#include <stdio.h> +#include <stddef.h> + +#include "stack/bt_types.h" +#include "common/bt_target.h" +#include "device/controller.h" +#include "stack/hcimsgs.h" +#include "stack/btu.h" +#include "stack/btm_api.h" +#include "btm_int.h" +#include "stack/acl_hci_link_interface.h" +#include "l2c_int.h" +#include "stack/l2cap_hci_link_interface.h" +#include "stack/hcidefs.h" +//#include "bt_utils.h" +#include "osi/list.h" + +static void btm_read_remote_features (UINT16 handle); +static void btm_read_remote_ext_features (UINT16 handle, UINT8 page_number); +static void btm_process_remote_ext_features (tACL_CONN *p_acl_cb, UINT8 num_read_pages); + +#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_DEBUG ("btm_acl_init\n"); +#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)); + memset (btm_cb.btm_scn, 0, BTM_MAX_SCN); /* Initialize the SCN usage to FALSE */ + btm_cb.btm_def_link_policy = 0; + btm_cb.p_bl_changed_cb = NULL; +#endif + btm_cb.p_acl_db_list = list_new(osi_free_func); + btm_cb.p_pm_mode_db_list = list_new(osi_free_func); + + /* 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. +** +** Parameters bda : BD address of the remote device +** transport : Physical transport used for ACL connection (BR/EDR or LE) +** +** Returns Returns pointer to the ACL DB for the requested BDA if found. +** NULL if not found. +** +*******************************************************************************/ +BOOLEAN btm_get_acl_db(void *p_acl_db_node, void *context) +{ + tACL_CONN *p_acl_db =(tACL_CONN *)p_acl_db_node; + BOOLEAN ret = TRUE; + tACL_DB_PARAM *p_param = (tACL_DB_PARAM *)context; + switch(p_param->type) { + case ACL_DB_BDA: + { + UINT8 *p_bda = (UINT8 *)p_param->p_data1; +#if BLE_INCLUDED == TRUE + tBT_TRANSPORT transport = (tBT_TRANSPORT)(*((UINT8 *)p_param->p_data2)); +#endif + if (p_acl_db->in_use + && !memcmp(p_bda, p_acl_db->remote_addr, BD_ADDR_LEN) +#if BLE_INCLUDED == TRUE + && transport == p_acl_db->transport +#endif + ) { + ret = FALSE; + } + break; + } + case ACL_DB_HANDLE: + { + UINT16 handle = (UINT16) *((UINT16 *)p_param->p_data1); + if (p_acl_db->in_use && handle == p_acl_db->hci_handle) { + ret = FALSE; + } + break; + } + default: + break; + } + return ret; +} + +tACL_CONN *btm_bda_to_acl (BD_ADDR bda, tBT_TRANSPORT transport) +{ + tACL_CONN *p_acl_db = NULL; + list_node_t *p_node = NULL; + tACL_DB_PARAM acl_params; + acl_params.type = ACL_DB_BDA; + acl_params.p_data1 = (void *)bda; + acl_params.p_data2 = (void *)&transport; + p_node = list_foreach(btm_cb.p_acl_db_list, btm_get_acl_db, (void *)&acl_params); + if (p_node) { + p_acl_db = list_node(p_node); + } + + return (p_acl_db); +} + +/******************************************************************************* +** +** Function btm_handle_to_acl +** +** Description This function returns the FIRST acl_db entry for the passed hci_handle. +** +** Returns Returns pointer to the ACL DB for the requested BDA if found. +** NULL if not found. +** +*******************************************************************************/ +tACL_CONN *btm_handle_to_acl (UINT16 hci_handle) +{ + tACL_CONN *p_acl_db = NULL; + tACL_DB_PARAM acl_params; + list_node_t *p_node = NULL; + + BTM_TRACE_DEBUG ("btm_handle_to_acl_index: %d\n", hci_handle); + + acl_params.type = ACL_DB_HANDLE; + acl_params.p_data1 = (void *)&hci_handle; + acl_params.p_data2 = NULL; + p_node = list_foreach(btm_cb.p_acl_db_list, btm_get_acl_db, (void *)&acl_params); + if (p_node) { + p_acl_db = list_node(p_node); + } + + return (p_acl_db); +} + +#if BLE_PRIVACY_SPT == TRUE +/******************************************************************************* +** +** Function btm_ble_get_acl_remote_addr +** +** Description This function reads the active remote address used for the +** connection. +** +** Returns success return TRUE, otherwise FALSE. +** +*******************************************************************************/ +BOOLEAN btm_ble_get_acl_remote_addr(tBTM_SEC_DEV_REC *p_dev_rec, BD_ADDR conn_addr, + tBLE_ADDR_TYPE *p_addr_type) +{ +#if BLE_INCLUDED == TRUE + BOOLEAN st = TRUE; + + if (p_dev_rec == NULL) { + BTM_TRACE_ERROR("btm_ble_get_acl_remote_addr can not find device with matching address\n"); + return FALSE; + } + + switch (p_dev_rec->ble.active_addr_type) { + case BTM_BLE_ADDR_PSEUDO: + memcpy(conn_addr, p_dev_rec->bd_addr, BD_ADDR_LEN); + * p_addr_type = p_dev_rec->ble.ble_addr_type; + break; + + case BTM_BLE_ADDR_RRA: + memcpy(conn_addr, p_dev_rec->ble.cur_rand_addr, BD_ADDR_LEN); + * p_addr_type = BLE_ADDR_RANDOM; + break; + + case BTM_BLE_ADDR_STATIC: + memcpy(conn_addr, p_dev_rec->ble.static_addr, BD_ADDR_LEN); + * p_addr_type = p_dev_rec->ble.static_addr_type; + break; + + default: + BTM_TRACE_ERROR("Unknown active address: %d\n", p_dev_rec->ble.active_addr_type); + st = FALSE; + break; + } + + return st; +#else + UNUSED(p_dev_rec); + UNUSED(conn_addr); + UNUSED(p_addr_type); + return FALSE; +#endif +} +#endif +/******************************************************************************* +** +** 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, UINT8 bdn[BTM_MAX_REM_BD_NAME_LEN], + UINT16 hci_handle, UINT8 link_role, tBT_TRANSPORT transport) +{ + tBTM_SEC_DEV_REC *p_dev_rec = NULL; + tACL_CONN *p; + + BTM_TRACE_DEBUG ("btm_acl_created hci_handle=%d link_role=%d transport=%d\n", + hci_handle, link_role, transport); + /* Ensure we don't have duplicates */ + p = btm_bda_to_acl(bda, transport); + if (p != (tACL_CONN *)NULL) { + p->hci_handle = hci_handle; + p->link_role = link_role; +#if BLE_INCLUDED == TRUE + p->transport = transport; +#endif + BTM_TRACE_DEBUG ("Duplicate btm_acl_created: RemBdAddr: %02x%02x%02x%02x%02x%02x\n", + 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 */ + if (list_length(btm_cb.p_acl_db_list) >= MAX_L2CAP_LINKS) { + return; + } + else { + p = (tACL_CONN *)osi_malloc(sizeof(tACL_CONN)); + if (p && list_append(btm_cb.p_acl_db_list, p)) { + memset(p, 0, sizeof(tACL_CONN)); + p->in_use = TRUE; + p->hci_handle = hci_handle; + p->link_role = link_role; + p->link_up_issued = FALSE; + memcpy (p->remote_addr, bda, BD_ADDR_LEN); + /* Set the default version of the peer device to version4.0 before exchange the version with it. + If the peer device act as a master and don't exchange the version with us, then it can only use the + legacy connect instead of secure connection in the pairing step. */ + p->lmp_version = HCI_PROTO_VERSION_4_0; +#if BLE_INCLUDED == TRUE + p->transport = transport; +#if BLE_PRIVACY_SPT == TRUE + if (transport == BT_TRANSPORT_LE) { + btm_ble_refresh_local_resolvable_private_addr(bda, + btm_cb.ble_ctr_cb.addr_mgnt_cb.private_addr); + } +#else + p->conn_addr_type = BLE_ADDR_PUBLIC; + memcpy(p->conn_addr, &controller_get_interface()->get_address()->address, BD_ADDR_LEN); + BTM_TRACE_DEBUG ("conn_addr: RemBdAddr: %02x%02x%02x%02x%02x%02x\n", + p->conn_addr[0], p->conn_addr[1], p->conn_addr[2], p->conn_addr[3], p->conn_addr[4], p->conn_addr[5]); +#endif +#endif + p->switch_role_state = BTM_ACL_SWKEY_STATE_IDLE; + + p->p_pm_mode_db = btm_pm_sm_alloc(); +#if BTM_PM_DEBUG == TRUE + BTM_TRACE_DEBUG( "btm_pm_sm_alloc handle:%d st:%d", hci_handle, p->p_pm_mode_db->state); +#endif // BTM_PM_DEBUG + +#if (CLASSIC_BT_INCLUDED == TRUE) + btm_sec_update_legacy_auth_state(p, BTM_ACL_LEGACY_AUTH_NONE); +#endif + + if (dc) { + memcpy (p->remote_dc, dc, DEV_CLASS_LEN); + } + + if (bdn) { + memcpy (p->remote_name, bdn, BTM_MAX_REM_BD_NAME_LEN); + } + + /* if BR/EDR do something more */ + if (transport == BT_TRANSPORT_BR_EDR) { + btsnd_hcic_read_rmt_clk_offset (p->hci_handle); + btsnd_hcic_rmt_ver_req (p->hci_handle); + } + p_dev_rec = btm_find_dev_by_handle (hci_handle); + +#if (BLE_INCLUDED == TRUE) + if (p_dev_rec ) { + BTM_TRACE_DEBUG ("device_type=0x%x\n", p_dev_rec->device_type); + } +#endif + + if (p_dev_rec && !(transport == BT_TRANSPORT_LE)) { + if (!p_dev_rec->remote_secure_connection_previous_state) { + /* If remote features already known, copy them and continue connection setup */ + if ((p_dev_rec->num_read_pages) && + (p_dev_rec->num_read_pages <= (HCI_EXT_FEATURES_PAGE_MAX + 1))) { + memcpy (p->peer_lmp_features, p_dev_rec->features, + (HCI_FEATURE_BYTES_PER_PAGE * p_dev_rec->num_read_pages)); + p->num_read_pages = p_dev_rec->num_read_pages; +#if (CLASSIC_BT_INCLUDED == TRUE) + const UINT8 req_pend = (p_dev_rec->sm4 & BTM_SM4_REQ_PEND); +#endif ///CLASSIC_BT_INCLUDED == TRUE + /* Store the Peer Security Capabilites (in SM4 and rmt_sec_caps) */ +#if (SMP_INCLUDED == TRUE) + btm_sec_set_peer_sec_caps(p, p_dev_rec); +#endif ///SMP_INCLUDED == TRUE +#if (CLASSIC_BT_INCLUDED == TRUE) + BTM_TRACE_API("%s: pend:%d\n", __FUNCTION__, req_pend); + if (req_pend) { + /* Request for remaining Security Features (if any) */ + l2cu_resubmit_pending_sec_req (p_dev_rec->bd_addr); + } +#endif ///CLASSIC_BT_INCLUDED == TRUE + btm_establish_continue (p); + return; + } + } else { + /* If remote features indicated secure connection (SC) mode, check the remote feautres again*/ + /* this is to prevent from BIAS attack where attacker can downgrade SC mode*/ + btm_read_remote_features (p->hci_handle); + } + } + +#if (BLE_INCLUDED == TRUE) + /* If here, features are not known yet */ + if (p_dev_rec && transport == BT_TRANSPORT_LE) { +#if BLE_PRIVACY_SPT == TRUE + btm_ble_get_acl_remote_addr (p_dev_rec, p->active_remote_addr, + &p->active_remote_addr_type); +#endif + + if (link_role == HCI_ROLE_MASTER) { + btsnd_hcic_ble_read_remote_feat(p->hci_handle); + } else if (HCI_LE_SLAVE_INIT_FEAT_EXC_SUPPORTED(controller_get_interface()->get_features_ble()->as_array) + && link_role == HCI_ROLE_SLAVE) { + btsnd_hcic_rmt_ver_req (p->hci_handle); + } else { + btm_establish_continue(p); + } + } else +#endif + { + btm_read_remote_features (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_DEBUG ("btm_acl_report_role_change\n"); + 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)))) { + memcpy (&ref_data, &btm_cb.devcb.switch_role_ref_data, sizeof(tBTM_ROLE_SWITCH_CMPL)); + ref_data.hci_status = hci_status; + (*btm_cb.devcb.p_switch_role_cb)(&ref_data); + memset (&btm_cb.devcb.switch_role_ref_data, 0, sizeof(tBTM_ROLE_SWITCH_CMPL)); + 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, tBT_TRANSPORT transport) +{ + tACL_CONN *p; + tBTM_BL_EVENT_DATA evt_data; +#if (defined BLE_INCLUDED && BLE_INCLUDED == TRUE) + tBTM_SEC_DEV_REC *p_dev_rec = NULL; +#endif + BTM_TRACE_DEBUG ("btm_acl_removed\n"); + p = btm_bda_to_acl(bda, transport); + 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 (btm_cb.p_bl_changed_cb) { + evt_data.event = BTM_BL_DISCN_EVT; + evt_data.discn.p_bda = bda; +#if BLE_INCLUDED == TRUE + evt_data.discn.handle = p->hci_handle; + evt_data.discn.transport = p->transport; +#endif + (*btm_cb.p_bl_changed_cb)(&evt_data); + } + + btm_acl_update_busy_level (BTM_BLI_ACL_DOWN_EVT); + } + +#if (defined BLE_INCLUDED && BLE_INCLUDED == TRUE) + + BTM_TRACE_DEBUG ("acl hci_handle=%d transport=%d connectable_mode=0x%0x link_role=%d\n", + p->hci_handle, + p->transport, + btm_cb.ble_ctr_cb.inq_var.connectable_mode, + p->link_role); + + p_dev_rec = btm_find_dev(bda); + if ( p_dev_rec) { + BTM_TRACE_DEBUG("before update p_dev_rec->sec_flags=0x%x\n", p_dev_rec->sec_flags); + if (p->transport == BT_TRANSPORT_LE) { + BTM_TRACE_DEBUG("LE link down\n"); + p_dev_rec->sec_flags &= ~(BTM_SEC_LE_ENCRYPTED | BTM_SEC_ROLE_SWITCHED); + if ( (p_dev_rec->sec_flags & BTM_SEC_LE_LINK_KEY_KNOWN) == 0) { + BTM_TRACE_DEBUG("Not Bonded\n"); + p_dev_rec->sec_flags &= ~(BTM_SEC_LE_LINK_KEY_AUTHED | BTM_SEC_LE_AUTHENTICATED); + } else { + BTM_TRACE_DEBUG("Bonded\n"); + } + } else { + BTM_TRACE_DEBUG("Bletooth link down\n"); + p_dev_rec->sec_flags &= ~(BTM_SEC_AUTHORIZED | BTM_SEC_AUTHENTICATED + | BTM_SEC_ENCRYPTED | BTM_SEC_ROLE_SWITCHED); + } + BTM_TRACE_DEBUG("after update p_dev_rec->sec_flags=0x%x\n", p_dev_rec->sec_flags); + } else { + BTM_TRACE_ERROR("Device not found\n"); + + } +#endif + + list_remove(btm_cb.p_pm_mode_db_list, p->p_pm_mode_db); + /* Clear the ACL connection data */ + memset(p, 0, sizeof(tACL_CONN)); + if (list_remove(btm_cb.p_acl_db_list, p)) { + p = NULL; + } + } +} + + +/******************************************************************************* +** +** 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 = NULL; + BTM_TRACE_DEBUG ("btm_acl_device_down\n"); + for (list_node_t *p_node = list_begin(btm_cb.p_acl_db_list); p_node;) { + list_node_t *p_next = list_next(p_node); + p = list_node(p_node); + if (p && p->in_use) { + BTM_TRACE_DEBUG ("hci_handle=%d HCI_ERR_HW_FAILURE \n", p->hci_handle ); + l2c_link_hci_disc_comp (p->hci_handle, HCI_ERR_HW_FAILURE); + } + p_node = p_next; + } +} +/******************************************************************************* +** +** 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_DEBUG ("btm_acl_update_busy_level\n"); + BOOLEAN old_inquiry_state = btm_cb.is_inquiry; + switch (event) { + case BTM_BLI_ACL_UP_EVT: + BTM_TRACE_DEBUG ("BTM_BLI_ACL_UP_EVT\n"); + break; + case BTM_BLI_ACL_DOWN_EVT: + BTM_TRACE_DEBUG ("BTM_BLI_ACL_DOWN_EVT\n"); + break; + case BTM_BLI_PAGE_EVT: + BTM_TRACE_DEBUG ("BTM_BLI_PAGE_EVT\n"); + btm_cb.is_paging = TRUE; + evt.busy_level_flags = BTM_BL_PAGING_STARTED; + break; + case BTM_BLI_PAGE_DONE_EVT: + BTM_TRACE_DEBUG ("BTM_BLI_PAGE_DONE_EVT\n"); + btm_cb.is_paging = FALSE; + evt.busy_level_flags = BTM_BL_PAGING_COMPLETE; + break; + case BTM_BLI_INQ_EVT: + BTM_TRACE_DEBUG ("BTM_BLI_INQ_EVT\n"); + btm_cb.is_inquiry = TRUE; + evt.busy_level_flags = BTM_BL_INQUIRY_STARTED; + break; + case BTM_BLI_INQ_CANCEL_EVT: + BTM_TRACE_DEBUG ("BTM_BLI_INQ_CANCEL_EVT\n"); + btm_cb.is_inquiry = FALSE; + evt.busy_level_flags = BTM_BL_INQUIRY_CANCELLED; + break; + case BTM_BLI_INQ_DONE_EVT: + BTM_TRACE_DEBUG ("BTM_BLI_INQ_DONE_EVT\n"); + btm_cb.is_inquiry = FALSE; + evt.busy_level_flags = BTM_BL_INQUIRY_COMPLETE; + break; + } + + if (btm_cb.is_paging || btm_cb.is_inquiry) { + busy_level = 10; + } else { + busy_level = BTM_GetNumAclLinks(); + } + + if ((busy_level != btm_cb.busy_level) || (old_inquiry_state != btm_cb.is_inquiry)) { + 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); + } + } +} + +/******************************************************************************* +** +** Function btm_acl_link_stat_report +** +** Description This function is called when the ACL link related + events are received from controller. It reports the ACL + link status to upper layer. + +** Returns void +** +*******************************************************************************/ +void btm_acl_link_stat_report(tBTM_ACL_LINK_STAT_EVENT_DATA *p_data) +{ + BTM_TRACE_DEBUG ("btm_acl_link_stat_report\n"); + + if (btm_cb.p_acl_link_stat_cb) { + (*btm_cb.p_acl_link_stat_cb)(p_data); + } +} + +/******************************************************************************* +** +** 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_DEBUG ("BTM_GetRole\n"); + if ((p = btm_bda_to_acl(remote_bd_addr, BT_TRANSPORT_BR_EDR)) == 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 + tBTM_STATUS status; + tBTM_PM_MODE pwr_mode; + tBTM_PM_PWR_MD settings; +#if (BT_USE_TRACES == TRUE) + BD_ADDR_PTR p_bda; +#endif + BTM_TRACE_API ("BTM_SwitchRole BDA: %02x-%02x-%02x-%02x-%02x-%02x\n", + 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 (!controller_get_interface()->supports_master_slave_role_switch()) { + 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_DEBUG ("Role switch on other device is in progress 0x%02x%02x%02x%02x%02x%02x\n", + 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, BT_TRANSPORT_BR_EDR)) == 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_DEBUG ("BTM_SwitchRole busy: %d\n", + p->switch_role_state); + return (BTM_BUSY); + } + + 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) { + memset( (void *)&settings, 0, sizeof(settings)); + 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; + } + /* some devices do not support switch while encryption is on */ + else { + p_dev_rec = btm_find_dev (remote_bd_addr); + if ((p_dev_rec != 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_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; + tBTM_SEC_DEV_REC *p_dev_rec; + tBTM_BL_ROLE_CHG_DATA evt; + + BTM_TRACE_DEBUG ("btm_acl_encrypt_change handle=%d status=%d encr_enabl=%d\n", + handle, status, encr_enable); + p = btm_handle_to_acl(handle); + if (p == NULL) { + 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 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_DEBUG("Role Switch Event: new_role 0x%02x, HCI Status 0x%02x, rs_st:%d\n", + evt.new_role, evt.hci_status, p->switch_role_state); + } + +#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_WARNING("btm_acl_encrypt_change -> Issuing delayed HCI_Disconnect!!!\n"); + btsnd_hcic_disconnect(p_dev_rec->hci_handle, HCI_ERR_PEER_USER); + } + BTM_TRACE_WARNING("btm_acl_encrypt_change: tBTM_SEC_DEV:0x%x rs_disc_pending=%d\n", + (UINT32)p_dev_rec, p_dev_rec->rs_disc_pending); + p_dev_rec->rs_disc_pending = BTM_SEC_RS_NOT_PENDING; /* reset flag */ + } +#endif + } +} +/******************************************************************************* +** +** 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_DEBUG ("BTM_SetLinkPolicy\n"); + /* BTM_TRACE_API ("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_API ("BTM_SetLinkPolicy switch not supported (settings: 0x%04x)\n", *settings ); + } + if ( (*settings & HCI_ENABLE_HOLD_MODE) && (!HCI_HOLD_MODE_SUPPORTED(localFeatures)) ) { + *settings &= (~HCI_ENABLE_HOLD_MODE); + BTM_TRACE_API ("BTM_SetLinkPolicy hold not supported (settings: 0x%04x)\n", *settings ); + } + if ( (*settings & HCI_ENABLE_SNIFF_MODE) && (!HCI_SNIFF_MODE_SUPPORTED(localFeatures)) ) { + *settings &= (~HCI_ENABLE_SNIFF_MODE); + BTM_TRACE_API ("BTM_SetLinkPolicy sniff not supported (settings: 0x%04x)\n", *settings ); + } + if ( (*settings & HCI_ENABLE_PARK_MODE) && (!HCI_PARK_MODE_SUPPORTED(localFeatures)) ) { + *settings &= (~HCI_ENABLE_PARK_MODE); + BTM_TRACE_API ("BTM_SetLinkPolicy park not supported (settings: 0x%04x)\n", *settings ); + } + } + + if ((p = btm_bda_to_acl(remote_bda, BT_TRANSPORT_BR_EDR)) != 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) +{ + UINT8 *localFeatures = BTM_ReadLocalFeatures(); + + BTM_TRACE_DEBUG("BTM_SetDefaultLinkPolicy setting:0x%04x\n", settings); + + if ((settings & HCI_ENABLE_MASTER_SLAVE_SWITCH) && (!HCI_SWITCH_SUPPORTED(localFeatures))) { + settings &= ~HCI_ENABLE_MASTER_SLAVE_SWITCH; + BTM_TRACE_DEBUG("BTM_SetDefaultLinkPolicy switch not supported (settings: 0x%04x)\n", settings); + } + if ((settings & HCI_ENABLE_HOLD_MODE) && (!HCI_HOLD_MODE_SUPPORTED(localFeatures))) { + settings &= ~HCI_ENABLE_HOLD_MODE; + BTM_TRACE_DEBUG("BTM_SetDefaultLinkPolicy hold not supported (settings: 0x%04x)\n", settings); + } + if ((settings & HCI_ENABLE_SNIFF_MODE) && (!HCI_SNIFF_MODE_SUPPORTED(localFeatures))) { + settings &= ~HCI_ENABLE_SNIFF_MODE; + BTM_TRACE_DEBUG("BTM_SetDefaultLinkPolicy sniff not supported (settings: 0x%04x)\n", settings); + } + if ((settings & HCI_ENABLE_PARK_MODE) && (!HCI_PARK_MODE_SUPPORTED(localFeatures))) { + settings &= ~HCI_ENABLE_PARK_MODE; + BTM_TRACE_DEBUG("BTM_SetDefaultLinkPolicy park not supported (settings: 0x%04x)\n", settings); + } + BTM_TRACE_DEBUG("Set DefaultLinkPolicy:0x%04x\n", settings); + + btm_cb.btm_def_link_policy = settings; + + /* Set the default Link Policy of the controller */ + btsnd_hcic_write_def_policy_set(settings); +} + +/******************************************************************************* +** +** 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 = NULL; + UINT8 status; + UINT16 handle; + BTM_TRACE_DEBUG ("btm_read_remote_version_complete\n"); + + STREAM_TO_UINT8 (status, p); + STREAM_TO_UINT16 (handle, p); + + /* Look up the connection by handle and copy features */ + p_acl_cb = btm_handle_to_acl(handle); + if (p_acl_cb) { + if (status == HCI_SUCCESS) { + 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); + } +#if BLE_INCLUDED == TRUE + if (p_acl_cb->transport == BT_TRANSPORT_LE) { + if(p_acl_cb->link_role == HCI_ROLE_MASTER) { + if (HCI_LE_DATA_LEN_EXT_SUPPORTED(p_acl_cb->peer_le_features)) { + uint16_t data_length = controller_get_interface()->get_ble_default_data_packet_length(); + uint16_t data_txtime = controller_get_interface()->get_ble_default_data_packet_txtime(); + if (data_length != p_acl_cb->data_length_params.tx_len) { + p_acl_cb->data_len_updating = true; + btsnd_hcic_ble_set_data_length(p_acl_cb->hci_handle, data_length, data_txtime); + } + } + l2cble_notify_le_connection (p_acl_cb->remote_addr); + } else { + //slave role, read remote feature + btsnd_hcic_ble_read_remote_feat(p_acl_cb->hci_handle); + } + } +#endif + } +} + +/******************************************************************************* +** +** Function btm_process_remote_ext_features +** +** Description Local function called to process all extended features pages +** read from a remote device. +** +** Returns void +** +*******************************************************************************/ +void btm_process_remote_ext_features (tACL_CONN *p_acl_cb, UINT8 num_read_pages) +{ + UINT16 handle = p_acl_cb->hci_handle; + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev_by_handle (handle); + UINT8 page_idx; + + BTM_TRACE_DEBUG ("btm_process_remote_ext_features\n"); + + /* Make sure we have the record to save remote features information */ + if (p_dev_rec == NULL) { + /* Get a new device; might be doing dedicated bonding */ + p_dev_rec = btm_find_or_alloc_dev (p_acl_cb->remote_addr); + } + + p_acl_cb->num_read_pages = num_read_pages; + p_dev_rec->num_read_pages = num_read_pages; + + /* Move the pages to placeholder */ + for (page_idx = 0; page_idx < num_read_pages; page_idx++) { + if (page_idx > HCI_EXT_FEATURES_PAGE_MAX) { + BTM_TRACE_ERROR("%s: page=%d unexpected\n", __FUNCTION__, page_idx); + break; + } + memcpy (p_dev_rec->features[page_idx], p_acl_cb->peer_lmp_features[page_idx], + HCI_FEATURE_BYTES_PER_PAGE); + } + + const UINT8 req_pend = (p_dev_rec->sm4 & BTM_SM4_REQ_PEND); +#if (SMP_INCLUDED == TRUE) + /* Store the Peer Security Capabilites (in SM4 and rmt_sec_caps) */ + btm_sec_set_peer_sec_caps(p_acl_cb, p_dev_rec); +#endif ///SMP_INCLUDED == TRUE + BTM_TRACE_API("%s: pend:%d\n", __FUNCTION__, req_pend); + if (req_pend) { +#if (CLASSIC_BT_INCLUDED == TRUE) + /* Request for remaining Security Features (if any) */ + l2cu_resubmit_pending_sec_req (p_dev_rec->bd_addr); +#endif ///CLASSIC_BT_INCLUDED == TRUE + } +} + + +/******************************************************************************* +** +** Function btm_read_remote_features +** +** Description Local function called to send a read remote supported features/ +** remote extended features page[0]. +** +** Returns void +** +*******************************************************************************/ +void btm_read_remote_features (UINT16 handle) +{ + tACL_CONN *p_acl_cb; + + BTM_TRACE_DEBUG("btm_read_remote_features() handle: %d\n", handle); + + p_acl_cb = btm_handle_to_acl(handle); + if (p_acl_cb == NULL) { + BTM_TRACE_ERROR("btm_read_remote_features handle=%d invalid\n", handle); + return; + } + + p_acl_cb->num_read_pages = 0; + memset (p_acl_cb->peer_lmp_features, 0, sizeof(p_acl_cb->peer_lmp_features)); + + /* first send read remote supported features HCI command */ + /* because we don't know whether the remote support extended feature command */ + btsnd_hcic_rmt_features_req (handle); +} + + +/******************************************************************************* +** +** Function btm_read_remote_ext_features +** +** Description Local function called to send a read remote extended features +** +** Returns void +** +*******************************************************************************/ +void btm_read_remote_ext_features (UINT16 handle, UINT8 page_number) +{ + BTM_TRACE_DEBUG("btm_read_remote_ext_features() handle: %d page: %d\n", handle, page_number); + + btsnd_hcic_rmt_ext_features(handle, page_number); +} + + +/******************************************************************************* +** +** Function btm_read_remote_features_complete +** +** Description This function is called when the remote supported features +** complete event is received from the HCI. +** +** Returns void +** +*******************************************************************************/ +void btm_read_remote_features_complete (UINT8 *p) +{ + tACL_CONN *p_acl_cb; + UINT8 status; + UINT16 handle; + + BTM_TRACE_DEBUG ("btm_read_remote_features_complete\n"); + STREAM_TO_UINT8 (status, p); + + if (status != HCI_SUCCESS) { + BTM_TRACE_ERROR ("btm_read_remote_features_complete failed (status 0x%02x)\n", status); + return; + } + + STREAM_TO_UINT16 (handle, p); + + p_acl_cb = btm_handle_to_acl(handle); + if (p_acl_cb == NULL) { + BTM_TRACE_ERROR("btm_read_remote_features_complete handle=%d invalid\n", handle); + return; + } + + /* Copy the received features page */ + STREAM_TO_ARRAY(p_acl_cb->peer_lmp_features[HCI_EXT_FEATURES_PAGE_0], p, + HCI_FEATURE_BYTES_PER_PAGE); + + if ((HCI_LMP_EXTENDED_SUPPORTED(p_acl_cb->peer_lmp_features[HCI_EXT_FEATURES_PAGE_0])) && + (controller_get_interface()->supports_reading_remote_extended_features())) { + /* if the remote controller has extended features and local controller supports + ** HCI_Read_Remote_Extended_Features command then start reading these feature starting + ** with extended features page 1 */ + BTM_TRACE_DEBUG ("Start reading remote extended features\n"); + btm_read_remote_ext_features(handle, HCI_EXT_FEATURES_PAGE_1); + return; + } + + /* Remote controller has no extended features. Process remote controller supported features + (features page HCI_EXT_FEATURES_PAGE_0). */ + btm_process_remote_ext_features (p_acl_cb, 1); + + /* Continue with HCI connection establishment */ + btm_establish_continue (p_acl_cb); +} + +/******************************************************************************* +** +** 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; + UINT8 page_num, max_page; + UINT16 handle; + + BTM_TRACE_DEBUG ("btm_read_remote_ext_features_complete\n"); + + ++p; + STREAM_TO_UINT16 (handle, p); + STREAM_TO_UINT8 (page_num, p); + STREAM_TO_UINT8 (max_page, p); + + /* Validate parameters */ + p_acl_cb = btm_handle_to_acl(handle); + if (p_acl_cb == NULL) { + BTM_TRACE_ERROR("btm_read_remote_ext_features_complete handle=%d invalid\n", handle); + return; + } + + if (max_page > HCI_EXT_FEATURES_PAGE_MAX) { + BTM_TRACE_ERROR("btm_read_remote_ext_features_complete page=%d unknown", max_page); + } + + + /* Copy the received features page */ + STREAM_TO_ARRAY(p_acl_cb->peer_lmp_features[page_num], p, HCI_FEATURE_BYTES_PER_PAGE); + + /* If there is the next remote features page and + * we have space to keep this page data - read this page */ + if ((page_num < max_page) && (page_num < HCI_EXT_FEATURES_PAGE_MAX)) { + page_num++; + BTM_TRACE_DEBUG("BTM reads next remote extended features page (%d)\n", page_num); + btm_read_remote_ext_features (handle, page_num); + return; + } + + /* Reading of remote feature pages is complete */ + BTM_TRACE_DEBUG("BTM reached last remote extended features page (%d)\n", page_num); + + /* Process the pages */ + btm_process_remote_ext_features (p_acl_cb, (UINT8) (page_num + 1)); + + /* Continue with HCI connection establishment */ + btm_establish_continue (p_acl_cb); +} + +/******************************************************************************* +** +** 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, UINT16 handle) +{ + tACL_CONN *p_acl_cb; + + BTM_TRACE_WARNING ("btm_read_remote_ext_features_failed (status 0x%02x) for handle %d\n", + status, handle); + + p_acl_cb = btm_handle_to_acl(handle); + if (p_acl_cb == NULL) { + BTM_TRACE_ERROR("btm_read_remote_ext_features_failed handle=%d invalid\n", handle); + return; + } + + /* Process supported features only */ + btm_process_remote_ext_features (p_acl_cb, 1); + + /* Continue HCI connection establishment */ + btm_establish_continue (p_acl_cb); +} + +/******************************************************************************* +** +** 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 +** +*******************************************************************************/ +void btm_establish_continue (tACL_CONN *p_acl_cb) +{ + tBTM_BL_EVENT_DATA evt_data; + BTM_TRACE_DEBUG ("btm_establish_continue\n"); +#if (!defined(BTM_BYPASS_EXTRA_ACL_SETUP) || BTM_BYPASS_EXTRA_ACL_SETUP == FALSE) +#if (defined BLE_INCLUDED && BLE_INCLUDED == TRUE) + if (p_acl_cb->transport == BT_TRANSPORT_BR_EDR) +#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); + } + } +#endif + p_acl_cb->link_up_issued = TRUE; + + /* If anyone cares, tell him database changed */ + 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->peer_lmp_features[HCI_EXT_FEATURES_PAGE_0]; + evt_data.conn.sc_downgrade = p_acl_cb->sc_downgrade; +#if BLE_INCLUDED == TRUE + evt_data.conn.handle = p_acl_cb->hci_handle; + evt_data.conn.transport = p_acl_cb->transport; +#endif + + (*btm_cb.p_bl_changed_cb)(&evt_data); + } + btm_acl_update_busy_level (BTM_BLI_ACL_UP_EVT); +} + + +/******************************************************************************* +** +** 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_DEBUG ("BTM_SetDefaultLinkSuperTout\n"); + btm_cb.btm_def_link_super_tout = timeout; +} + +/******************************************************************************* +** +** Function BTM_GetLinkSuperTout +** +** Description Read the link supervision timeout value of the connection +** +** Returns status of the operation +** +*******************************************************************************/ +tBTM_STATUS BTM_GetLinkSuperTout (BD_ADDR remote_bda, UINT16 *p_timeout) +{ + tACL_CONN *p = btm_bda_to_acl(remote_bda, BT_TRANSPORT_BR_EDR); + + BTM_TRACE_DEBUG ("BTM_GetLinkSuperTout\n"); + if (p != (tACL_CONN *)NULL) { + *p_timeout = p->link_super_tout; + return (BTM_SUCCESS); + } + /* If here, no BD Addr found */ + return (BTM_UNKNOWN_ADDR); +} + + +/******************************************************************************* +** +** 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, BT_TRANSPORT_BR_EDR); + + BTM_TRACE_DEBUG ("BTM_SetLinkSuperTout\n"); + 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_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, tBT_TRANSPORT transport) +{ + tACL_CONN *p; + + BTM_TRACE_API ("BTM_IsAclConnectionUp: RemBdAddr: %02x%02x%02x%02x%02x%02x\n", + 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, transport); + 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) +{ + uint16_t num_acl = 0; + + num_acl = list_length(btm_cb.p_acl_db_list); + return num_acl; +} + +/******************************************************************************* +** +** 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_DEBUG ("btm_get_acl_disc_reason_code\n"); + 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, tBT_TRANSPORT transport) +{ + tACL_CONN *p; + BTM_TRACE_DEBUG ("BTM_GetHCIConnHandle\n"); + p = btm_bda_to_acl(remote_bda, transport); + if (p != (tACL_CONN *)NULL) { + return (p->hci_handle); + } + + /* If here, no BD Addr found */ + return (0xFFFF); +} + +/******************************************************************************* +** +** 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) +{ + tACL_CONN *p_acl_cb = NULL; + BTM_TRACE_DEBUG ("btm_process_clk_off_comp_evt\n"); + /* Look up the connection by handle and set the current mode */ + p_acl_cb = btm_handle_to_acl(hci_handle); + if (p_acl_cb) { + p_acl_cb->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, BT_TRANSPORT_BR_EDR); + tBTM_ROLE_SWITCH_CMPL *p_data = &btm_cb.devcb.switch_role_ref_data; + tBTM_SEC_DEV_REC *p_dev_rec; + tBTM_BL_ROLE_CHG_DATA evt; + + BTM_TRACE_DEBUG ("btm_acl_role_changed\n"); + /* 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) { + 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; + } + } + + /* 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 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_DEBUG("Role Switch Event: new_role 0x%02x, HCI Status 0x%02x, rs_st:%d\n", + p_data->role, p_data->hci_status, p->switch_role_state); + +#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_WARNING("btm_acl_role_changed -> Issuing delayed HCI_Disconnect!!!\n"); + btsnd_hcic_disconnect(p_dev_rec->hci_handle, HCI_ERR_PEER_USER); + } + BTM_TRACE_ERROR("tBTM_SEC_DEV:0x%x rs_disc_pending=%d\n", + (UINT32)p_dev_rec, p_dev_rec->rs_disc_pending); + p_dev_rec->rs_disc_pending = BTM_SEC_RS_NOT_PENDING; /* reset flag */ + } + +#endif + +} + +/******************************************************************************* +** +** Function BTM_AllocateSCN +** +** Description Look through the Server Channel Numbers for a free one. +** +** Returns Allocated SCN number or 0 if none. +** +*******************************************************************************/ +#if (CLASSIC_BT_INCLUDED == TRUE) +UINT8 BTM_AllocateSCN(void) +{ + UINT8 x; + BTM_TRACE_DEBUG ("BTM_AllocateSCN\n"); + for (x = 1; x < BTM_MAX_SCN; x++) { + if (!btm_cb.btm_scn[x - 1]) { + btm_cb.btm_scn[x - 1] = TRUE; + return x; + } + } + return (0); /* No free ports */ +} +#endif ///CLASSIC_BT_INCLUDED == TRUE + +/******************************************************************************* +** +** Function BTM_TryAllocateSCN +** +** Description Try to allocate a fixed server channel +** +** Returns Returns TRUE if server channel was available +** +*******************************************************************************/ +#if (CLASSIC_BT_INCLUDED == TRUE) +BOOLEAN BTM_TryAllocateSCN(UINT8 scn) +{ + if (scn >= BTM_MAX_SCN) { + 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_DEBUG ("BTM_FreeSCN \n"); + if (scn <= BTM_MAX_SCN) { + btm_cb.btm_scn[scn - 1] = FALSE; + return (TRUE); + } else { + return (FALSE); /* Illegal SCN passed in */ + } + return (FALSE); +} +#endif ///CLASSIC_BT_INCLUDED == TRUE + +/******************************************************************************* +** +** 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_DEBUG ("btm_set_packet_types\n"); + /* 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 */ + temp_pkt_types |= ((pkt_types & BTM_ACL_EXCEPTION_PKTS_MASK) | + (btm_cb.btm_acl_pkt_types_supported & 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_DEBUG ("SetPacketType Mask -> 0x%04x\n", 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, BT_TRANSPORT_BR_EDR); + UINT16 pkt_types = 0; + UINT16 pkt_size = 0; + BTM_TRACE_DEBUG ("btm_get_max_packet_size\n"); + if (p != NULL) { + pkt_types = p->pkt_types_mask; + } else { + /* Special case for when info for the local device is requested */ + if (memcmp (controller_get_interface()->get_address(), 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; + } + } + + 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, BT_TRANSPORT_BR_EDR); + BTM_TRACE_DEBUG ("BTM_ReadRemoteVersion\n"); + 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 remote supported features mask (8 bytes) +** +*******************************************************************************/ +UINT8 *BTM_ReadRemoteFeatures (BD_ADDR addr) +{ + tACL_CONN *p = btm_bda_to_acl(addr, BT_TRANSPORT_BR_EDR); + BTM_TRACE_DEBUG ("BTM_ReadRemoteFeatures\n"); + if (p == NULL) { + return (NULL); + } + + return (p->peer_lmp_features[HCI_EXT_FEATURES_PAGE_0]); +} + +/******************************************************************************* +** +** Function BTM_ReadRemoteExtendedFeatures +** +** Returns pointer to the remote extended features mask (8 bytes) +** or NULL if bad page +** +*******************************************************************************/ +UINT8 *BTM_ReadRemoteExtendedFeatures (BD_ADDR addr, UINT8 page_number) +{ + tACL_CONN *p = btm_bda_to_acl(addr, BT_TRANSPORT_BR_EDR); + BTM_TRACE_DEBUG ("BTM_ReadRemoteExtendedFeatures\n"); + if (p == NULL) { + return (NULL); + } + + if (page_number > HCI_EXT_FEATURES_PAGE_MAX) { + BTM_TRACE_ERROR("Warning: BTM_ReadRemoteExtendedFeatures page %d unknown\n", page_number); + return NULL; + } + + return (p->peer_lmp_features[page_number]); +} + +/******************************************************************************* +** +** Function BTM_ReadNumberRemoteFeaturesPages +** +** Returns number of features pages read from the remote device. +** +*******************************************************************************/ +UINT8 BTM_ReadNumberRemoteFeaturesPages (BD_ADDR addr) +{ + tACL_CONN *p = btm_bda_to_acl(addr, BT_TRANSPORT_BR_EDR); + BTM_TRACE_DEBUG ("BTM_ReadNumberRemoteFeaturesPages\n"); + if (p == NULL) { + return (0); + } + + return (p->num_read_pages); +} + +/******************************************************************************* +** +** Function BTM_ReadAllRemoteFeatures +** +** Returns pointer to all features of the remote (24 bytes). +** +*******************************************************************************/ +UINT8 *BTM_ReadAllRemoteFeatures (BD_ADDR addr) +{ + tACL_CONN *p = btm_bda_to_acl(addr, BT_TRANSPORT_BR_EDR); + BTM_TRACE_DEBUG ("BTM_ReadAllRemoteFeatures\n"); + if (p == NULL) { + return (NULL); + } + + return (p->peer_lmp_features[HCI_EXT_FEATURES_PAGE_0]); +} + +/******************************************************************************* +** +** 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 +** +*******************************************************************************/ +tBTM_STATUS BTM_RegBusyLevelNotif (tBTM_BL_CHANGE_CB *p_cb, UINT8 *p_level, + tBTM_BL_EVENT_MASK evt_mask) +{ + BTM_TRACE_DEBUG ("BTM_RegBusyLevelNotif\n"); + 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); +} + + +tBTM_STATUS BTM_RegAclLinkStatNotif(tBTM_ACL_LINK_STAT_CB *p_cb) +{ + BTM_TRACE_DEBUG ("BTM_RegAclLinkStatNotif\n"); + + if (!p_cb) { + btm_cb.p_acl_link_stat_cb = NULL; + } else if (btm_cb.p_acl_link_stat_cb) { + return BTM_BUSY; + } else { + btm_cb.p_acl_link_stat_cb = p_cb; + } + + return BTM_SUCCESS; +} + +/******************************************************************************* +** +** 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 = NULL; + + BTM_TRACE_API ("BTM_SetQoS: BdAddr: %02x%02x%02x%02x%02x%02x\n", + 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, BT_TRANSPORT_BR_EDR)) != NULL) { + btu_start_timer (&btm_cb.devcb.qossu_timer, BTU_TTYPE_BTM_QOS, 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_DEBUG ("btm_qos_setup_complete\n"); + 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; + tACL_CONN *p = btm_handle_to_acl(handle); + if (p != NULL) { + memcpy (qossu.rem_bda, p->remote_addr, BD_ADDR_LEN); + } + 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_DEBUG ("BTM: p_flow->delay_variation: 0x%02x\n", + qossu.flow.delay_variation); + (*p_cb)(&qossu); + } +} + +/******************************************************************************* +** +** Function btm_qos_setup_timeout +** +** Description This function processes a timeout. +** Currently, we just report an error log +** +** Returns void +** +*******************************************************************************/ +void btm_qos_setup_timeout (void *p_tle) +{ + BTM_TRACE_DEBUG ("%s\n", __func__); + + btm_qos_setup_complete (HCI_ERR_HOST_TIMEOUT, 0, NULL); +} + +/******************************************************************************* +** +** 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, tBT_TRANSPORT transport, tBTM_CMPL_CB *p_cb) +{ + tACL_CONN *p; + + BTM_TRACE_API ("BTM_ReadRSSI: RemBdAddr: %02x%02x%02x%02x%02x%02x\n", + remote_bda[0], remote_bda[1], remote_bda[2], + remote_bda[3], remote_bda[4], remote_bda[5]); + tBTM_RSSI_RESULTS result; + /* If someone already waiting on the version, do not allow another */ + if (btm_cb.devcb.p_rssi_cmpl_cb) { + result.status = BTM_BUSY; + (*p_cb)(&result); + return (BTM_BUSY); + } + + p = btm_bda_to_acl(remote_bda, transport); + 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); + result.status = BTM_NO_RESOURCES; + (*p_cb)(&result); + 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_API ("BTM_ReadLinkQuality: RemBdAddr: %02x%02x%02x%02x%02x%02x\n", + 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, BT_TRANSPORT_BR_EDR); + 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, tBT_TRANSPORT transport, 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_API ("BTM_ReadTxPower: RemBdAddr: %02x%02x%02x%02x%02x%02x\n", + 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, transport); + 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->transport == BT_TRANSPORT_LE) { + 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); +} + +tBTM_STATUS BTM_SetAclPktTypes(BD_ADDR remote_bda, UINT16 pkt_types, tBTM_CMPL_CB *p_cb) +{ +#if CLASSIC_BT_INCLUDED == TRUE + tBTM_STATUS ret = BTM_UNKNOWN_ADDR; + tACL_CONN *p; + tBTM_SET_ACL_PKT_TYPES_RESULTS result; + + /* If someone already waiting on the version, do not allow another */ + if (btm_cb.devcb.p_set_acl_pkt_types_cmpl_cb) { + result.status = HCI_ERR_REPEATED_ATTEMPTS; + (*p_cb)(&result); + return (BTM_BUSY);; + } + + p = btm_bda_to_acl(remote_bda, BT_TRANSPORT_BR_EDR); + + if (p != (tACL_CONN *)NULL) { + btu_start_timer (&btm_cb.devcb.set_acl_pkt_types_timer, BTU_TTYPE_BTM_ACL, + BTM_DEV_REPLY_TIMEOUT); + + btm_cb.devcb.p_set_acl_pkt_types_cmpl_cb = p_cb; + if (btm_set_packet_types(p, pkt_types) != BTM_CMD_STARTED) { + btm_cb.devcb.p_set_acl_pkt_types_cmpl_cb = NULL; + btu_stop_timer (&btm_cb.devcb.set_acl_pkt_types_timer); + result.status = HCI_ERR_MEMORY_FULL; + (*p_cb)(&result); + ret = BTM_NO_RESOURCES; + } else { + ret = BTM_CMD_STARTED; + } + } + /* If here, no BD Addr found */ + return ret; +#else + return BTM_NO_RESOURCES; +#endif +} + +void btm_acl_pkt_types_changed(UINT8 status, UINT16 handle, UINT16 pkt_types) +{ +#if CLASSIC_BT_INCLUDED == TRUE + BTM_TRACE_DEBUG ("btm_acl_pkt_types_changed\n"); + tACL_CONN *conn = NULL; + tBTM_SET_ACL_PKT_TYPES_RESULTS results; + btu_stop_timer (&btm_cb.devcb.set_acl_pkt_types_timer); + + /* If there is a callback registered for packet types changed, call it */ + if (btm_cb.devcb.p_set_acl_pkt_types_cmpl_cb) { + if (status == HCI_SUCCESS) { + results.status = BTM_SUCCESS; + } else { + results.status = BTM_BAD_VALUE_RET; + } + results.pkt_types = pkt_types; + /* Search through the list of active channels for the correct BD Addr */ + if ((conn = btm_handle_to_acl(handle)) != NULL) { + memcpy(results.rem_bda, conn->remote_addr, BD_ADDR_LEN); + (*btm_cb.devcb.p_set_acl_pkt_types_cmpl_cb)(&results); + } + btm_cb.devcb.p_set_acl_pkt_types_cmpl_cb = NULL; + } +#endif +} + +#if (BLE_INCLUDED == TRUE) +tBTM_STATUS BTM_BleReadAdvTxPower(tBTM_CMPL_CB *p_cb) +{ + BOOLEAN ret; + tBTM_TX_POWER_RESULTS result; + /* If someone already waiting on the version, do not allow another */ + if (btm_cb.devcb.p_tx_power_cmpl_cb) { + result.status = BTM_BUSY; + (*p_cb)(&result); + return (BTM_BUSY); + } + + btm_cb.devcb.p_tx_power_cmpl_cb = p_cb; + btu_start_timer (&btm_cb.devcb.tx_power_timer, BTU_TTYPE_BTM_ACL, + BTM_DEV_REPLY_TIMEOUT); + ret = btsnd_hcic_ble_read_adv_chnl_tx_power(); + + if(!ret) { + btm_cb.devcb.p_tx_power_cmpl_cb = NULL; + btu_stop_timer (&btm_cb.devcb.tx_power_timer); + result.status = BTM_NO_RESOURCES; + (*p_cb)(&result); + return (BTM_NO_RESOURCES); + } else { + return BTM_CMD_STARTED; + } +} + +void BTM_BleGetWhiteListSize(uint16_t *length) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + if (p_cb->white_list_avail_size == 0) { + BTM_TRACE_WARNING("%s Whitelist full.", __func__); + } + *length = p_cb->white_list_avail_size; + return; +} +#endif ///BLE_INCLUDED == TRUE + +/******************************************************************************* +** +** 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 = NULL; + BTM_TRACE_DEBUG ("btm_read_tx_power_complete\n"); + 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 */ + p_acl_cb = btm_handle_to_acl(handle); + if (p_acl_cb) { + memcpy (results.rem_bda, p_acl_cb->remote_addr, BD_ADDR_LEN); + } + } +#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_DEBUG ("BTM TX power Complete: tx_power %d, hci status 0x%02x\n", + 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 = NULL; + BTM_TRACE_DEBUG ("btm_read_rssi_complete\n"); + 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_DEBUG ("BTM RSSI Complete: rssi %d, hci status 0x%02x\n", + results.rssi, results.hci_status); + + /* Search through the list of active channels for the correct BD Addr */ + p_acl_cb = btm_handle_to_acl(handle); + if (p_acl_cb) { + memcpy (results.rem_bda, p_acl_cb->remote_addr, BD_ADDR_LEN); + } + } 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 = NULL; + BTM_TRACE_DEBUG ("btm_read_link_quality_complete\n"); + btu_stop_timer (&btm_cb.devcb.lnk_quality_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_DEBUG ("BTM Link Quality Complete: Link Quality %d, hci status 0x%02x\n", + results.link_quality, results.hci_status); + + /* Search through the list of active channels for the correct BD Addr */ + p_acl_cb = btm_handle_to_acl(handle); + if (p_acl_cb) { + memcpy (results.rem_bda, p_acl_cb->remote_addr, BD_ADDR_LEN); + } + } 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, tBT_TRANSPORT transport) +{ + UINT16 hci_handle = BTM_GetHCIConnHandle(bd_addr, transport); + tBTM_STATUS status = BTM_SUCCESS; + + BTM_TRACE_DEBUG ("btm_remove_acl\n"); +#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 && p_dev_rec && + p_dev_rec->sec_state != BTM_SEC_STATE_DISCONNECTING) { + 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_DEBUG ("BTM_SetTraceLevel\n"); + if (new_level != 0xFF) { + btm_cb.trace_level = new_level; + } + + return (btm_cb.trace_level); +} + +/******************************************************************************* +** +** Function btm_cont_rswitch +** +** Description This function is called to continue processing an active +** role switch. It first disables encryption if enabled and +** EPR is not supported +** +** Returns void +** +*******************************************************************************/ +void btm_cont_rswitch (tACL_CONN *p, tBTM_SEC_DEV_REC *p_dev_rec, + UINT8 hci_status) +{ + BOOLEAN sw_ok = TRUE; + BTM_TRACE_DEBUG ("btm_cont_rswitch\n"); + /* 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) { + /* 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; + } + } else { + /* Error occurred; set states back to Idle */ + if (p->switch_role_state == BTM_ACL_SWKEY_STATE_MODE_CHANGE) { + sw_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 (!sw_ok) { + p->switch_role_state = BTM_ACL_SWKEY_STATE_IDLE; + btm_acl_report_role_change(hci_status, p->remote_addr); + } + } +} + +/******************************************************************************* +** +** Function btm_acl_resubmit_page +** +** Description send pending page request +** +*******************************************************************************/ +void btm_acl_resubmit_page (void) +{ +#if (SMP_INCLUDED == TRUE) + tBTM_SEC_DEV_REC *p_dev_rec; + BT_HDR *p_buf; + UINT8 *pp; + BD_ADDR bda; + BTM_TRACE_DEBUG ("btm_acl_resubmit_page\n"); + /* If there were other page request schedule can start the next one */ + if ((p_buf = (BT_HDR *)fixed_queue_dequeue(btm_cb.page_queue, 0)) != 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; + } +#endif ///SMP_INCLUDED == TRUE +} + +/******************************************************************************* +** +** 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_DEBUG ("btm_acl_reset_paging\n"); + /* If we sent reset we are definitely not paging any more */ + while ((p = (BT_HDR *)fixed_queue_dequeue(btm_cb.page_queue, 0)) != NULL) { + osi_free (p); + } + + btm_cb.paging = FALSE; +} + +/******************************************************************************* +** +** Function btm_acl_paging +** +** Description send a paging command or queue it in btm_cb +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE && CLASSIC_BT_INCLUDED == TRUE) +void btm_acl_paging (BT_HDR *p, BD_ADDR bda) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + + BTM_TRACE_DEBUG ("btm_acl_paging discing:%d, paging:%d BDA: %06x%06x\n", + 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; + fixed_queue_enqueue(btm_cb.page_queue, p, FIXED_QUEUE_MAX_TIMEOUT); + } else { + if (!BTM_ACL_IS_CONNECTED (bda)) { + BTM_TRACE_DEBUG ("connecting_bda: %06x%06x\n", + (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) { + fixed_queue_enqueue(btm_cb.page_queue, p, FIXED_QUEUE_MAX_TIMEOUT); + } 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); + } + } +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** 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 no one needs the notification. +** +*******************************************************************************/ +BOOLEAN btm_acl_notif_conn_collision (BD_ADDR bda) +{ + tBTM_BL_EVENT_DATA evt_data; + + /* Report possible collision to the upper layer. */ + if (btm_cb.p_bl_changed_cb) { + BTM_TRACE_DEBUG ("btm_acl_notif_conn_collision: RemBdAddr: %02x%02x%02x%02x%02x%02x\n", + 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; + +#if BLE_INCLUDED == TRUE + evt_data.conn.transport = BT_TRANSPORT_BR_EDR; + evt_data.conn.handle = BTM_INVALID_HCI_HANDLE; +#endif + (*btm_cb.p_bl_changed_cb)(&evt_data); + return TRUE; + } else { + return FALSE; + } +} + + +/******************************************************************************* +** +** 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->peer_lmp_features[HCI_EXT_FEATURES_PAGE_0])) { + *p_pkt_type &= ~(BTM_ACL_PKT_TYPES_MASK_DH3 + BTM_ACL_PKT_TYPES_MASK_DM3); + } + + if (!HCI_5_SLOT_PACKETS_SUPPORTED(p->peer_lmp_features[HCI_EXT_FEATURES_PAGE_0])) { + *p_pkt_type &= ~(BTM_ACL_PKT_TYPES_MASK_DH5 + BTM_ACL_PKT_TYPES_MASK_DM5); + } + + /* 2 and 3 MPS support? */ + if (!HCI_EDR_ACL_2MPS_SUPPORTED(p->peer_lmp_features[HCI_EXT_FEATURES_PAGE_0])) { + /* 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->peer_lmp_features[HCI_EXT_FEATURES_PAGE_0])) { + /* 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->peer_lmp_features[HCI_EXT_FEATURES_PAGE_0]) + || HCI_EDR_ACL_3MPS_SUPPORTED(p->peer_lmp_features[HCI_EXT_FEATURES_PAGE_0])) { + if (!HCI_3_SLOT_EDR_ACL_SUPPORTED(p->peer_lmp_features[HCI_EXT_FEATURES_PAGE_0])) + /* 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->peer_lmp_features[HCI_EXT_FEATURES_PAGE_0])) + /* 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); + } + } +} + +/******************************************************************************* +** +** Function btm_acl_free +** +** Description Free acl specific lists from btm control block +** +*******************************************************************************/ +void btm_acl_free(void) +{ + list_free(btm_cb.p_acl_db_list); + list_free(btm_cb.p_pm_mode_db_list); +} + +/******************************************************************************* +** +** Function btm_acl_connected +** +** Description Handle ACL connection complete event +** +*******************************************************************************/ +void btm_acl_connected(BD_ADDR bda, UINT16 handle, UINT8 link_type, UINT8 enc_mode, UINT8 status) +{ +#if BTM_SCO_INCLUDED == TRUE + tBTM_ESCO_DATA esco_data; +#endif + + if (link_type == HCI_LINK_TYPE_ACL) { +#if SMP_INCLUDED == TRUE + btm_sec_connected (bda, handle, status, enc_mode); +#endif /* SMP_INCLUDED == TRUE */ + /* report acl connection result to upper layer */ + do { + tBTM_ACL_LINK_STAT_EVENT_DATA evt_data = { + .event = BTM_ACL_CONN_CMPL_EVT, + .link_act.conn_cmpl.status = status, + .link_act.conn_cmpl.handle = handle, + }; + bdcpy(evt_data.link_act.conn_cmpl.bd_addr, bda); + btm_acl_link_stat_report(&evt_data); + } while (0); + + l2c_link_hci_conn_comp(status, handle, bda); + } +#if BTM_SCO_INCLUDED == TRUE + else { + memset(&esco_data, 0, sizeof(tBTM_ESCO_DATA)); + esco_data.link_type = HCI_LINK_TYPE_SCO; + memcpy (esco_data.bd_addr, bda, BD_ADDR_LEN); + btm_sco_connected(status, bda, handle, &esco_data); + } +#endif /* BTM_SCO_INCLUDED == TRUE */ +} + +/******************************************************************************* +** +** Function btm_acl_disconnected +** +** Description Handle ACL disconnection complete event +** +*******************************************************************************/ +void btm_acl_disconnected(UINT16 handle, UINT8 reason) +{ + + /* Report BR/EDR ACL disconnection result to upper layer */ + tACL_CONN *conn = btm_handle_to_acl(handle); + if (conn) { +#if BLE_INCLUDED == TRUE + if (conn->transport == BT_TRANSPORT_BR_EDR) +#endif + { + tBTM_ACL_LINK_STAT_EVENT_DATA evt_data = { + .event = BTM_ACL_DISCONN_CMPL_EVT, + .link_act.disconn_cmpl.reason = reason, + .link_act.disconn_cmpl.handle = handle, + }; + bdcpy(evt_data.link_act.disconn_cmpl.bd_addr, conn->remote_addr); + btm_acl_link_stat_report(&evt_data); + } + } + +#if BTM_SCO_INCLUDED == TRUE + /* If L2CAP doesn't know about it, send it to SCO */ + if (!l2c_link_hci_disc_comp (handle, reason)) { + btm_sco_removed (handle, reason); + } +#else + l2c_link_hci_disc_comp(handle, reason); +#endif /* BTM_SCO_INCLUDED */ + +#if (SMP_INCLUDED == TRUE) + /* Notify security manager */ + btm_sec_disconnected(handle, reason); +#endif /* SMP_INCLUDED == TRUE */ +} diff --git a/lib/bt/host/bluedroid/stack/btm/btm_ble.c b/lib/bt/host/bluedroid/stack/btm/btm_ble.c new file mode 100644 index 00000000..afa46b8a --- /dev/null +++ b/lib/bt/host/bluedroid/stack/btm/btm_ble.c @@ -0,0 +1,2934 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains functions for BLE device control utilities, and LE + * security functions. + * + ******************************************************************************/ +#include "common/bt_target.h" + +#include <string.h> + +#include "stack/bt_types.h" +#include "stack/hcimsgs.h" +#include "stack/btu.h" +#include "btm_int.h" +#include "stack/btm_ble_api.h" +#include "stack/smp_api.h" +#include "l2c_int.h" +#include "stack/gap_api.h" +//#include "bt_utils.h" +#include "device/controller.h" + +//#define LOG_TAG "bt_btm_ble" +//#include "osi/include/log.h" +#if BLE_INCLUDED == TRUE +extern void BTM_UpdateAddrInfor(uint8_t addr_type, BD_ADDR bda); +#if SMP_INCLUDED == TRUE +// The temp variable to pass parameter between functions when in the connected event callback. +static BOOLEAN temp_enhanced = FALSE; +extern BOOLEAN aes_cipher_msg_auth_code(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 +extern void gatt_notify_enc_cmpl(BD_ADDR bd_addr); +/*******************************************************************************/ +/* 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. +** auth_mode - auth mode +** +** Returns TRUE if added OK, else FALSE +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +BOOLEAN BTM_SecAddBleDevice (BD_ADDR bd_addr, BD_NAME bd_name, tBT_DEVICE_TYPE dev_type, + tBLE_ADDR_TYPE addr_type, UINT32 auth_mode) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + tBTM_INQ_INFO *p_info = NULL; + + BTM_TRACE_DEBUG ("BTM_SecAddBleDevice dev_type=0x%x", dev_type); + p_dev_rec = btm_find_dev (bd_addr); + + if (!p_dev_rec) { + BTM_TRACE_DEBUG("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. */ + if (list_length(btm_cb.p_sec_dev_rec_list) < BTM_SEC_MAX_DEVICE_RECORDS) { + p_dev_rec = (tBTM_SEC_DEV_REC *)osi_malloc(sizeof(tBTM_SEC_DEV_REC)); + if(p_dev_rec) { + list_append(btm_cb.p_sec_dev_rec_list, p_dev_rec); + BTM_TRACE_DEBUG ("allocate a new dev rec idx=0x%x\n", list_length(btm_cb.p_sec_dev_rec_list)); + + /* 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, BT_TRANSPORT_BR_EDR); + p_dev_rec->ble_hci_handle = BTM_GetHCIConnHandle (bd_addr, BT_TRANSPORT_LE); + + /* 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_DEBUG ("hci_handl=0x%x ", p_dev_rec->ble_hci_handle ); + } + } + + if (!p_dev_rec) { + return (FALSE); + } + } else { + BTM_TRACE_DEBUG("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, (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; + p_dev_rec->ble.auth_mode = auth_mode; + + memcpy (p_dev_rec->ble.pseudo_addr, bd_addr, BD_ADDR_LEN); + /* 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_DEBUG ("InqDb device_type =0x%x addr_type=0x%x", + p_info->results.device_type, p_info->results.ble_addr_type); + } + return (TRUE); +} +#endif ///SMP_INCLUDED == 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 +** +*******************************************************************************/ +#if SMP_INCLUDED == TRUE +BOOLEAN BTM_SecAddBleKey (BD_ADDR bd_addr, tBTM_LE_KEY_VALUE *p_le_key, tBTM_LE_KEY_TYPE key_type) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + BTM_TRACE_DEBUG ("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 && + key_type != BTM_LE_KEY_LCSRK && key_type != BTM_LE_KEY_LID)) { + BTM_TRACE_WARNING ("BTM_SecAddBleKey() 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_DEBUG ("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); + + btm_sec_save_le_key (bd_addr, key_type, p_le_key, FALSE); + +#if (BLE_PRIVACY_SPT == TRUE) + if (key_type == BTM_LE_KEY_PID || key_type == BTM_LE_KEY_LID) { + btm_ble_resolving_list_load_dev (p_dev_rec); + } +#endif + + + return (TRUE); +} +#endif + + +/******************************************************************************* +** +** 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) +{ + tBTM_DEVCB *p_devcb = &btm_cb.devcb; + BTM_TRACE_DEBUG ("%s", __func__); + 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->ble_encryption_key_value, p_key->er, sizeof(BT_OCTET16)); + break; + + default: + BTM_TRACE_ERROR("unknow local key type: %d", key_type); + break; + } + } +} + +/******************************************************************************* +** +** Function BTM_GetDeviceEncRoot +** +** Description This function is called to read the local device encryption +** root. +** +** Returns void +** the local device ER is copied into ble_encr_key_value +** +*******************************************************************************/ +void BTM_GetDeviceEncRoot (BT_OCTET16 ble_encr_key_value) +{ + BTM_TRACE_DEBUG ("%s", __func__); + memcpy (ble_encr_key_value, btm_cb.devcb.ble_encryption_key_value, BT_OCTET16_LEN); +} + +/******************************************************************************* +** +** 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_DEBUG ("BTM_GetDeviceIDRoot "); + + memcpy (irk, btm_cb.devcb.id_keys.irk, BT_OCTET16_LEN); +} + +/******************************************************************************* +** +** 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) +{ + BTM_TRACE_DEBUG ("BTM_GetDeviceDHK"); + memcpy (dhk, btm_cb.devcb.id_keys.dhk, BT_OCTET16_LEN); +} + +/******************************************************************************* +** +** Function BTM_ReadConnectionAddr +** +** Description This function is called to get the local device address information +** . +** +** Returns void +** +*******************************************************************************/ +void BTM_ReadConnectionAddr (BD_ADDR remote_bda, BD_ADDR local_conn_addr, tBLE_ADDR_TYPE *p_addr_type) +{ + tACL_CONN *p_acl = btm_bda_to_acl(remote_bda, BT_TRANSPORT_LE); + + if (p_acl == NULL) { + BTM_TRACE_ERROR("No connection exist!"); + return; + } + memcpy(local_conn_addr, p_acl->conn_addr, BD_ADDR_LEN); + * p_addr_type = p_acl->conn_addr_type; + + BTM_TRACE_DEBUG ("BTM_ReadConnectionAddr address type: %d addr: 0x%02x", + p_acl->conn_addr_type, p_acl->conn_addr[0]); +} + +/******************************************************************************* +** +** Function BTM_IsBleConnection +** +** Description This function is called to check if the connection handle +** for an LE link +** +** Returns TRUE if connection is LE link, otherwise FALSE. +** +*******************************************************************************/ +BOOLEAN BTM_IsBleConnection (UINT16 conn_handle) +{ +#if (BLE_INCLUDED == TRUE) + tACL_CONN *p; + + BTM_TRACE_API ("BTM_IsBleConnection: conn_handle: %d", conn_handle); + + p = btm_handle_to_acl(conn_handle); + if (!p) { + return FALSE; + } + return (p->transport == BT_TRANSPORT_LE); +#else + return FALSE; +#endif +} + +/******************************************************************************* +** +** Function BTM_ReadRemoteConnectionAddr +** +** Description This function is read the remote device address currently used +** +** Parameters pseudo_addr: pseudo random address available +** conn_addr:connection address used +** p_addr_type : BD Address type, Public or Random of the address used +** +** Returns BOOLEAN , TRUE if connection to remote device exists, else FALSE +** +*******************************************************************************/ +BOOLEAN BTM_ReadRemoteConnectionAddr(BD_ADDR pseudo_addr, BD_ADDR conn_addr, + tBLE_ADDR_TYPE *p_addr_type) +{ + BOOLEAN st = TRUE; +#if (BLE_PRIVACY_SPT == TRUE) + tACL_CONN *p = btm_bda_to_acl (pseudo_addr, BT_TRANSPORT_LE); + + if (p == NULL) { + BTM_TRACE_ERROR("BTM_ReadRemoteConnectionAddr can not find connection" + " with matching address"); + return FALSE; + } + + memcpy(conn_addr, p->active_remote_addr, BD_ADDR_LEN); + *p_addr_type = p->active_remote_addr_type; +#else + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev(pseudo_addr); + + memcpy(conn_addr, pseudo_addr, BD_ADDR_LEN); + if (p_dev_rec != NULL) { + *p_addr_type = p_dev_rec->ble.ble_addr_type; + } +#endif + return st; + +} +/******************************************************************************* +** +** 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 SMP_INCLUDED == TRUE + tSMP_STATUS res_smp = (res == BTM_SUCCESS) ? SMP_SUCCESS : SMP_REPEATED_ATTEMPTS; + BTM_TRACE_DEBUG ("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 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; + + if (p_dev_rec == NULL) { + BTM_TRACE_ERROR("Passkey reply to Unknown device"); + return; + } + + p_dev_rec->sec_flags |= BTM_SEC_LE_AUTHENTICATED; + BTM_TRACE_DEBUG ("BTM_BlePasskeyReply"); + SMP_PasskeyReply(bd_addr, res_smp, passkey); +#endif +} + +/******************************************************************************* +** +** Function BTM_BleSetStaticPasskey +** +** Description This function is called to set static passkey +** +** +** Parameters: add - set static passkey when add is TRUE +** clear static passkey when add is FALSE +** passkey - static passkey +** +** +*******************************************************************************/ +void BTM_BleSetStaticPasskey(BOOLEAN add, UINT32 passkey) +{ +#if SMP_INCLUDED == TRUE + SMP_SetStaticPasskey(add, passkey); +#endif +} + +/******************************************************************************* +** +** Function BTM_BleConfirmReply +** +** Description This function is called after Security Manager submitted +** numeric comparison request to the application. +** +** Parameters: bd_addr - Address of the device with which numeric +** comparison was requested +** res - comparison result BTM_SUCCESS if success +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +void BTM_BleConfirmReply (BD_ADDR bd_addr, UINT8 res) +{ + 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; + + if (p_dev_rec == NULL) { + BTM_TRACE_ERROR("Passkey reply to Unknown device"); + return; + } + + p_dev_rec->sec_flags |= BTM_SEC_LE_AUTHENTICATED; + BTM_TRACE_DEBUG ("%s\n", __func__); + SMP_ConfirmReply(bd_addr, res_smp); +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** 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 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_DEBUG ("BTM_BleOobDataReply"); + + if (p_dev_rec == NULL) { + BTM_TRACE_ERROR("BTM_BleOobDataReply() to Unknown device"); + return; + } + + p_dev_rec->sec_flags |= BTM_SEC_LE_AUTHENTICATED; + SMP_OobDataReply(bd_addr, res_smp, len, p_data); +#endif +} + +/******************************************************************************* +** +** Function BTM_BleSecureConnectionOobDataReply +** +** Description This function is called to provide the OOB data for +** SMP in response to BTM_LE_SC_OOB_REQ_EVT when secure connection +** +** Parameters: bd_addr - Address of the peer device +** p_c - pointer to Confirmation +** p_r - pointer to Randomizer +** +*******************************************************************************/ +void BTM_BleSecureConnectionOobDataReply(BD_ADDR bd_addr, UINT8 *p_c, UINT8 *p_r) +{ +#if SMP_INCLUDED == TRUE + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bd_addr); + + BTM_TRACE_DEBUG ("%s", __func__); + + if (p_dev_rec == NULL) { + BTM_TRACE_ERROR("%s Unknown device", __func__); + return; + } + + p_dev_rec->sec_flags |= BTM_SEC_LE_AUTHENTICATED; + + tSMP_SC_OOB_DATA oob; + memset(&oob, 0, sizeof(tSMP_SC_OOB_DATA)); + + oob.peer_oob_data.present = true; + memcpy(&oob.peer_oob_data.commitment, p_c, BT_OCTET16_LEN); + memcpy(&oob.peer_oob_data.randomizer, p_r, BT_OCTET16_LEN); + oob.peer_oob_data.addr_rcvd_from.type = p_dev_rec->ble.ble_addr_type; + memcpy(oob.peer_oob_data.addr_rcvd_from.bda, bd_addr, BD_ADDR_LEN); + + SMP_SecureConnectionOobDataReply((UINT8 *)&oob); +#endif +} + +/******************************************************************************* +** +** Function BTM_BleSecureConnectionCreateOobData +** +** Description This function is called to create the OOB data for +** SMP when secure connection +** +*******************************************************************************/ +void BTM_BleSecureConnectionCreateOobData(void) +{ +#if SMP_INCLUDED == TRUE + BTM_TRACE_DEBUG ("%s", __func__); + + SMP_CreateLocalSecureConnectionsOobData(); +#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 (UINT32 scan_interval, UINT32 scan_window) +{ +#if SMP_INCLUDED == TRUE + tBTM_BLE_CB *p_ble_cb = &btm_cb.ble_ctr_cb; + BOOLEAN new_param = FALSE; + + if (BTM_BLE_ISVALID_PARAM(scan_interval, BTM_BLE_SCAN_INT_MIN, BTM_BLE_SCAN_INT_MAX) && + BTM_BLE_ISVALID_PARAM(scan_window, BTM_BLE_SCAN_WIN_MIN, BTM_BLE_SCAN_WIN_MAX)) { + 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 && p_ble_cb->conn_state == BLE_BG_CONN) { + btm_ble_suspend_bg_conn(); + } + } else { + BTM_TRACE_ERROR("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) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_or_alloc_dev (bd_addr); + + BTM_TRACE_API ("BTM_BleSetPrefConnParams min: %u max: %u latency: %u \ + tout: %u", + min_conn_int, max_conn_int, slave_latency, supervision_tout); + + if (BTM_BLE_ISVALID_PARAM(min_conn_int, BTM_BLE_CONN_INT_MIN, BTM_BLE_CONN_INT_MAX) && + BTM_BLE_ISVALID_PARAM(max_conn_int, BTM_BLE_CONN_INT_MIN, BTM_BLE_CONN_INT_MAX) && + BTM_BLE_ISVALID_PARAM(supervision_tout, BTM_BLE_CONN_SUP_TOUT_MIN, BTM_BLE_CONN_SUP_TOUT_MAX) && + (slave_latency <= BTM_BLE_CONN_LATENCY_MAX || slave_latency == BTM_BLE_CONN_PARAM_UNDEF)) { + 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.supervision_tout = BTM_BLE_CONN_TIMEOUT_DEF; + } + + } + + } else { + BTM_TRACE_ERROR("Unknown Device, setting rejected"); + } + } else { + BTM_TRACE_ERROR("Illegal Connection Parameters"); + } +} + +/******************************************************************************* +** +** 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) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (remote_bda); + tBTM_INQ_INFO *p_inq_info = BTM_InqDbRead(remote_bda); + tBLE_ADDR_TYPE temp_addr_type = (*p_addr_type); + + *p_addr_type = BLE_ADDR_PUBLIC; + + if (!p_dev_rec) { + *p_dev_type = BT_DEVICE_TYPE_BREDR; + /* 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; + } else { + if(temp_addr_type <= BLE_ADDR_TYPE_MAX) { + *p_addr_type = temp_addr_type; + } else { + /* unknown device, assume BR/EDR */ + BTM_TRACE_DEBUG ("btm_find_dev_type - unknown device, BR/EDR assumed"); + } + } + } 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; + } + if (memcmp(p_dev_rec->bd_addr, remote_bda, BD_ADDR_LEN) == 0 && + memcmp(p_dev_rec->ble.pseudo_addr, remote_bda, BD_ADDR_LEN) == 0) { + *p_dev_type = p_dev_rec->device_type; + *p_addr_type = p_dev_rec->ble.ble_addr_type; + } else if (memcmp(p_dev_rec->ble.pseudo_addr, remote_bda, BD_ADDR_LEN) == 0) { + *p_dev_type = BT_DEVICE_TYPE_BLE; + *p_addr_type = p_dev_rec->ble.ble_addr_type; + } else { /* matching static adddress only */ + *p_dev_type = BT_DEVICE_TYPE_BREDR; + *p_addr_type = BLE_ADDR_PUBLIC; + } + + } + + BTM_TRACE_DEBUG ("btm_find_dev_type - device_type = %d addr_type = %d", *p_dev_type , *p_addr_type); +} +#endif ///BLE_INCLUDED == TRUE + +/******************************************************************************* +** +** Function BTM_ReadConnectedTransportAddress +** +** Description This function is called to read the paired device/address type of other device paired +** corresponding to the BD_address +** +** Parameter remote_bda: remote device address, carry out the transport address +** transport: active transport +** +** Return TRUE if an active link is identified; FALSE otherwise +** +*******************************************************************************/ +BOOLEAN BTM_ReadConnectedTransportAddress(BD_ADDR remote_bda, tBT_TRANSPORT transport) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev(remote_bda); + + /* if no device can be located, return */ + if (p_dev_rec == NULL) { + memset(remote_bda, 0, BD_ADDR_LEN); + return FALSE; + } + + if (transport == BT_TRANSPORT_BR_EDR) { + if (btm_bda_to_acl(p_dev_rec->bd_addr, transport) != NULL) { + memcpy(remote_bda, p_dev_rec->bd_addr, BD_ADDR_LEN); + return TRUE; + } else if (p_dev_rec->device_type & BT_DEVICE_TYPE_BREDR) { + memcpy(remote_bda, p_dev_rec->bd_addr, BD_ADDR_LEN); + } else { + memset(remote_bda, 0, BD_ADDR_LEN); + } + return FALSE; + } +#if (BLE_INCLUDED == TRUE) + if (transport == BT_TRANSPORT_LE) { + memcpy(remote_bda, p_dev_rec->ble.pseudo_addr, BD_ADDR_LEN); + if (btm_bda_to_acl(p_dev_rec->ble.pseudo_addr, transport) != NULL) { + return TRUE; + } else { + return FALSE; + } + } +#endif ///BLE_INCLUDED == TRUE + return FALSE; +} + +#if (BLE_INCLUDED == TRUE) +/******************************************************************************* +** +** Function BTM_BleReceiverTest +** +** Description This function is called to start the LE Receiver test +** +** Parameter rx_freq - Frequency Range +** p_cmd_cmpl_cback - Command Complete callback +** +*******************************************************************************/ +void BTM_BleReceiverTest(UINT8 rx_freq, tBTM_CMPL_CB *p_cmd_cmpl_cback) +{ + btm_cb.devcb.p_le_test_cmd_cmpl_cb = p_cmd_cmpl_cback; + + if (btsnd_hcic_ble_receiver_test(rx_freq) == FALSE) { + BTM_TRACE_ERROR("%s: Unable to Trigger LE receiver test", __FUNCTION__); + } +} + +/******************************************************************************* +** +** Function BTM_BleTransmitterTest +** +** Description This function is called to start the LE Transmitter test +** +** Parameter tx_freq - Frequency Range +** test_data_len - Length in bytes of payload data in each packet +** packet_payload - Pattern to use in the payload +** p_cmd_cmpl_cback - Command Complete callback +** +*******************************************************************************/ +void BTM_BleTransmitterTest(UINT8 tx_freq, UINT8 test_data_len, + UINT8 packet_payload, tBTM_CMPL_CB *p_cmd_cmpl_cback) +{ + btm_cb.devcb.p_le_test_cmd_cmpl_cb = p_cmd_cmpl_cback; + if (btsnd_hcic_ble_transmitter_test(tx_freq, test_data_len, packet_payload) == FALSE) { + BTM_TRACE_ERROR("%s: Unable to Trigger LE transmitter test", __FUNCTION__); + } +} + +/******************************************************************************* +** +** Function BTM_BleTestEnd +** +** Description This function is called to stop the in-progress TX or RX test +** +** Parameter p_cmd_cmpl_cback - Command complete callback +** +*******************************************************************************/ +void BTM_BleTestEnd(tBTM_CMPL_CB *p_cmd_cmpl_cback) +{ + btm_cb.devcb.p_le_test_cmd_cmpl_cb = p_cmd_cmpl_cback; + + if (btsnd_hcic_ble_test_end() == FALSE) { + BTM_TRACE_ERROR("%s: Unable to End the LE TX/RX test", __FUNCTION__); + } +} + +/******************************************************************************* +** Internal Functions +*******************************************************************************/ +void btm_ble_test_command_complete(UINT8 *p) +{ + tBTM_CMPL_CB *p_cb = btm_cb.devcb.p_le_test_cmd_cmpl_cb; + + btm_cb.devcb.p_le_test_cmd_cmpl_cb = NULL; + + if (p_cb) { + (*p_cb)(p); + } +} + + +#if (BLE_50_FEATURE_SUPPORT == TRUE) +/******************************************************************************* +** +** Function BTM_BleEnhancedReceiverTest +** +** Description This function is called to start the LE Enhanced Receiver test +** +** Parameter rx_freq - Frequency Range +** phy - The type of phy that receives data +** modulation_index - modulation index +** p_cmd_cmpl_cback - Command Complete callback +** +*******************************************************************************/ +void BTM_BleEnhancedReceiverTest(UINT8 rx_freq, UINT8 phy, UINT8 modulation_index, tBTM_CMPL_CB *p_cmd_cmpl_cback) +{ + btm_cb.devcb.p_le_test_cmd_cmpl_cb = p_cmd_cmpl_cback; + + if (btsnd_hcic_ble_enhand_rx_test(rx_freq, phy, modulation_index) == FALSE) { + BTM_TRACE_ERROR("%s: Unable to Trigger LE enhanced receiver test", __FUNCTION__); + } +} + +/******************************************************************************* +** +** Function BTM_BleEnhancedTransmitterTest +** +** Description This function is called to start the LE Enhanced Transmitter test +** +** Parameter tx_freq - Frequency Range +** test_data_len - Length in bytes of payload data in each packet +** packet_payload - Pattern to use in the payload +** phy - The type of phy that sends data +** p_cmd_cmpl_cback - Command Complete callback +** +*******************************************************************************/ +void BTM_BleEnhancedTransmitterTest(UINT8 tx_freq, UINT8 test_data_len, + UINT8 packet_payload, UINT8 phy, tBTM_CMPL_CB *p_cmd_cmpl_cback) +{ + btm_cb.devcb.p_le_test_cmd_cmpl_cb = p_cmd_cmpl_cback; + if (btsnd_hcic_ble_enhand_tx_test(tx_freq, test_data_len, packet_payload, phy) == FALSE) { + BTM_TRACE_ERROR("%s: Unable to Trigger LE enhanced transmitter test", __FUNCTION__); + } +} +#endif // BLE_50_FEATURE_SUPPORT + +/******************************************************************************* +** +** Function BTM_UseLeLink +** +** Description This function is to select the underneath physical link to use. +** +** Returns TRUE to use LE, FALSE use BR/EDR. +** +*******************************************************************************/ +BOOLEAN BTM_UseLeLink (BD_ADDR bd_addr) +{ + tACL_CONN *p; + tBT_DEVICE_TYPE dev_type; + tBLE_ADDR_TYPE addr_type = 0; + BOOLEAN use_le = FALSE; + + if ((p = btm_bda_to_acl(bd_addr, BT_TRANSPORT_BR_EDR)) != NULL) { + return use_le; + } else if ((p = btm_bda_to_acl(bd_addr, BT_TRANSPORT_LE)) != NULL) { + use_le = TRUE; + } else { + BTM_ReadDevInfo(bd_addr, &dev_type, &addr_type); + use_le = (dev_type == BT_DEVICE_TYPE_BLE); + } + return use_le; +} + + +/******************************************************************************* +** +** Function BTM_SetBleDataLength +** +** Description This function is to set maximum BLE transmission packet size +** +** Returns BTM_SUCCESS if success; otherwise failed. +** +*******************************************************************************/ +tBTM_STATUS BTM_SetBleDataLength(BD_ADDR bd_addr, UINT16 tx_pdu_length) +{ + tACL_CONN *p_acl = btm_bda_to_acl(bd_addr, BT_TRANSPORT_LE); + + BTM_TRACE_DEBUG("%s: tx_pdu_length =%d", __FUNCTION__, tx_pdu_length); + + if (!controller_get_interface()->supports_ble_packet_extension()) { + BTM_TRACE_ERROR("%s failed, request not supported", __FUNCTION__); + return BTM_CONTROL_LE_DATA_LEN_UNSUPPORTED; + } + + if (p_acl != NULL) { + if (!HCI_LE_DATA_LEN_EXT_SUPPORTED(p_acl->peer_le_features)) { + BTM_TRACE_ERROR("%s failed, peer does not support request", __FUNCTION__); + return BTM_PEER_LE_DATA_LEN_UNSUPPORTED; + } + + if (tx_pdu_length > BTM_BLE_DATA_SIZE_MAX) { + tx_pdu_length = BTM_BLE_DATA_SIZE_MAX; + } else if (tx_pdu_length < BTM_BLE_DATA_SIZE_MIN) { + tx_pdu_length = BTM_BLE_DATA_SIZE_MIN; + } + + /* always set the TxTime to be max, as controller does not care for now */ + btsnd_hcic_ble_set_data_length(p_acl->hci_handle, tx_pdu_length, + BTM_BLE_DATA_TX_TIME_MAX); + + return BTM_SUCCESS; + } else { + BTM_TRACE_ERROR("%s: Wrong mode: no LE link exist or LE not supported", __FUNCTION__); + return BTM_WRONG_MODE; + } +} + +#if (SMP_INCLUDED == TRUE) +/******************************************************************************* +** +** Function btm_ble_determine_security_act +** +** Description This function checks the security of current LE link +** and returns the appropriate action that needs to be +** taken to achieve the required security. +** +** Parameter is_originator - True if outgoing connection +** bdaddr: remote device address +** security_required: Security required for the service. +** +** Returns The appropriate security action required. +** +*******************************************************************************/ +tBTM_SEC_ACTION btm_ble_determine_security_act(BOOLEAN is_originator, BD_ADDR bdaddr, UINT16 security_required) +{ + tBTM_LE_AUTH_REQ auth_req = 0x00; + + if (is_originator) + { + if ((security_required & BTM_SEC_OUT_FLAGS) == 0 && + (security_required & BTM_SEC_OUT_MITM) == 0) + { + BTM_TRACE_DEBUG ("%s No security required for outgoing connection", __func__); + return BTM_SEC_OK; + } + + if (security_required & BTM_SEC_OUT_MITM) { + auth_req |= BTM_LE_AUTH_REQ_MITM; + } + } + else + { + if ((security_required & BTM_SEC_IN_FLAGS) == 0&& (security_required & BTM_SEC_IN_MITM) == 0) + { + BTM_TRACE_DEBUG ("%s No security required for incoming connection", __func__); + return BTM_SEC_OK; + } + + if (security_required & BTM_SEC_IN_MITM) { + auth_req |= BTM_LE_AUTH_REQ_MITM; + } + } + + tBTM_BLE_SEC_REQ_ACT ble_sec_act = BTM_BLE_SEC_REQ_ACT_NONE; + btm_ble_link_sec_check(bdaddr, auth_req, &ble_sec_act); + + BTM_TRACE_DEBUG ("%s ble_sec_act %d", __func__ , ble_sec_act); + + if (ble_sec_act == BTM_BLE_SEC_REQ_ACT_DISCARD) { + return BTM_SEC_ENC_PENDING; + } + + if (ble_sec_act == BTM_BLE_SEC_REQ_ACT_NONE) { + return BTM_SEC_OK; + } + + UINT8 sec_flag = 0; + BTM_GetSecurityFlagsByTransport(bdaddr, &sec_flag, BT_TRANSPORT_LE); + + BOOLEAN is_link_encrypted = FALSE; + BOOLEAN is_key_mitm = FALSE; + if (sec_flag & (BTM_SEC_FLAG_ENCRYPTED| BTM_SEC_FLAG_LKEY_KNOWN)) + { + if (sec_flag & BTM_SEC_FLAG_ENCRYPTED) { + is_link_encrypted = TRUE; + } + + if (sec_flag & BTM_SEC_FLAG_LKEY_AUTHED) { + is_key_mitm = TRUE; + } + } + + if (auth_req & BTM_LE_AUTH_REQ_MITM) + { + if (!is_key_mitm) + { + return BTM_SEC_ENCRYPT_MITM; + } else { + if (is_link_encrypted) { + return BTM_SEC_OK; + } else { + return BTM_SEC_ENCRYPT; + } + } + } else { + if (is_link_encrypted) { + return BTM_SEC_OK; + } else { + return BTM_SEC_ENCRYPT_NO_MITM; + } + } + + return BTM_SEC_OK; +} + +/******************************************************************************* +** +** Function btm_ble_start_sec_check +** +** Description This function is to check and set the security required for +** LE link for LE COC. +** +** Parameter bdaddr: remote device address. +** psm : PSM of the LE COC sevice. +** is_originator: TRUE if outgoing connection. +** p_callback : Pointer to the callback function. +** p_ref_data : Pointer to be returned along with the callback. +** +** Returns TRUE if link already meets the required security; otherwise FALSE. +** +*******************************************************************************/ +BOOLEAN btm_ble_start_sec_check(BD_ADDR bd_addr, UINT16 psm, BOOLEAN is_originator, + tBTM_SEC_CALLBACK *p_callback, void *p_ref_data) +{ + /* Find the service record for the PSM */ + tBTM_SEC_SERV_REC *p_serv_rec = btm_sec_find_first_serv (is_originator, psm); + + /* If there is no application registered with this PSM do not allow connection */ + if (!p_serv_rec) + { + BTM_TRACE_WARNING ("%s PSM: %d no application registerd", __func__, psm); + (*p_callback) (bd_addr, BT_TRANSPORT_LE, p_ref_data, BTM_MODE_UNSUPPORTED); + return FALSE; + } + + tBTM_SEC_ACTION sec_act = btm_ble_determine_security_act(is_originator, + bd_addr, p_serv_rec->security_flags); + + tBTM_BLE_SEC_ACT ble_sec_act = BTM_BLE_SEC_NONE; + BOOLEAN status = FALSE; + + switch (sec_act) + { + case BTM_SEC_OK: + BTM_TRACE_DEBUG ("%s Security met", __func__); + p_callback(bd_addr, BT_TRANSPORT_LE, p_ref_data, BTM_SUCCESS); + status = TRUE; + break; + + case BTM_SEC_ENCRYPT: + BTM_TRACE_DEBUG ("%s Encryption needs to be done", __func__); + ble_sec_act = BTM_BLE_SEC_ENCRYPT; + break; + + case BTM_SEC_ENCRYPT_MITM: + BTM_TRACE_DEBUG ("%s Pairing with MITM needs to be done", __func__); + ble_sec_act = BTM_BLE_SEC_ENCRYPT_MITM; + break; + + case BTM_SEC_ENCRYPT_NO_MITM: + BTM_TRACE_DEBUG ("%s Pairing with No MITM needs to be done", __func__); + ble_sec_act = BTM_BLE_SEC_ENCRYPT_NO_MITM; + break; + + case BTM_SEC_ENC_PENDING: + BTM_TRACE_DEBUG ("%s Ecryption pending", __func__); + break; + } + + if (ble_sec_act == BTM_BLE_SEC_NONE) { + return status; + } + + tL2C_LCB *p_lcb = l2cu_find_lcb_by_bd_addr(bd_addr, BT_TRANSPORT_LE); + p_lcb->sec_act = sec_act; + BTM_SetEncryption(bd_addr, BT_TRANSPORT_LE, p_callback, p_ref_data); + + 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_DEBUG ("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 */ + } + } +} +#endif ///SMP_INCLUDED == TRUE + + +/******************************************************************************* +** +** Function btm_ble_get_enc_key_type +** +** Description This function is to increment local sign counter +** Returns None +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +void btm_ble_increment_sign_ctr(BD_ADDR bd_addr, BOOLEAN is_local ) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + + BTM_TRACE_DEBUG ("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_DEBUG ("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); + } +} +#endif ///SMP_INCLUDED == TRUE +#endif ///BLE_INCLUDED == TRUE + +/******************************************************************************* +** +** 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. +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +#if (BLE_INCLUDED == TRUE) +BOOLEAN btm_ble_get_enc_key_type(BD_ADDR bd_addr, UINT8 *p_key_types) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + + BTM_TRACE_DEBUG ("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 TRUE - 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_DEBUG ("btm_get_local_div"); + + BTM_TRACE_DEBUG("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_div = 0; + 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_DEBUG ("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; + UINT8 i; + + BTM_TRACE_DEBUG ("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_DEBUG("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 || key_type == BTM_LE_KEY_LID)) { + btm_ble_init_pseudo_addr (p_rec, bd_addr); + + switch (key_type) { + case BTM_LE_KEY_PENC: + memcpy(p_rec->ble.keys.pltk, 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_LE_LINK_KEY_KNOWN; + if (p_keys->penc_key.sec_level == SMP_SEC_AUTHENTICATED) { + p_rec->sec_flags |= BTM_SEC_LE_LINK_KEY_AUTHED; + } else { + p_rec->sec_flags &= ~BTM_SEC_LE_LINK_KEY_AUTHED; + } + BTM_TRACE_DEBUG("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: + for (i = 0; i < BT_OCTET16_LEN; i++) { + p_rec->ble.keys.irk[i] = p_keys->pid_key.irk[i]; + } + + //memcpy( p_rec->ble.keys.irk, p_keys->pid_key, BT_OCTET16_LEN); todo will crash the system + memcpy(p_rec->ble.static_addr, p_keys->pid_key.static_addr, BD_ADDR_LEN); + p_rec->ble.static_addr_type = p_keys->pid_key.addr_type; + p_rec->ble.key_type |= BTM_LE_KEY_PID; + BTM_TRACE_DEBUG("BTM_LE_KEY_PID key_type=0x%x save peer IRK", p_rec->ble.key_type); + /* update device record address as static address */ + memcpy(p_rec->bd_addr, p_keys->pid_key.static_addr, BD_ADDR_LEN); + /* combine DUMO device security record if needed */ + btm_consolidate_dev(p_rec); + break; + + case BTM_LE_KEY_PCSRK: + memcpy(p_rec->ble.keys.pcsrk, 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_LE_LINK_KEY_KNOWN; + if ( p_keys->pcsrk_key.sec_level == SMP_SEC_AUTHENTICATED) { + p_rec->sec_flags |= BTM_SEC_LE_LINK_KEY_AUTHED; + } else { + p_rec->sec_flags &= ~BTM_SEC_LE_LINK_KEY_AUTHED; + } + + BTM_TRACE_DEBUG("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: + memcpy(p_rec->ble.keys.lltk, p_keys->lenc_key.ltk, BT_OCTET16_LEN); + 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; + + /* Set that link key is known since this shares field with BTM_SEC_FLAG_LKEY_KNOWN flag in stack/btm_api.h*/ + p_rec->sec_flags |= BTM_SEC_LE_LINK_KEY_KNOWN; + if ( p_keys->lenc_key.sec_level == SMP_SEC_AUTHENTICATED) { + p_rec->sec_flags |= BTM_SEC_LE_LINK_KEY_AUTHED; + } else { + p_rec->sec_flags &= ~BTM_SEC_LE_LINK_KEY_AUTHED; + } + + BTM_TRACE_DEBUG("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 */ + memcpy (p_rec->ble.keys.lcsrk, p_keys->lcsrk_key.csrk, BT_OCTET16_LEN); + 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_DEBUG("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; + + case BTM_LE_KEY_LID: + p_rec->ble.key_type |= BTM_LE_KEY_LID; + break; + default: + BTM_TRACE_WARNING("btm_sec_save_le_key (Bad key_type 0x%02x)", key_type); + return; + } + + BTM_TRACE_DEBUG ("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_WARNING ("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_DEBUG ("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_DEBUG("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; + } + 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 = BTM_LE_SEC_NONE, cur_sec_level = BTM_LE_SEC_NONE; + + BTM_TRACE_DEBUG ("btm_ble_link_sec_check auth_req =0x%x", auth_req); + + if (p_dev_rec == NULL) { + BTM_TRACE_ERROR ("btm_ble_link_sec_check received for unknown device"); + return; + } + + if (p_dev_rec->sec_state == BTM_SEC_STATE_ENCRYPTING || + p_dev_rec->sec_state == BTM_SEC_STATE_AUTHENTICATING) { + /* 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_MITM) { + req_sec_level = BTM_LE_SEC_AUTHENTICATED; + } + + BTM_TRACE_DEBUG ("dev_rec sec_flags=0x%x", p_dev_rec->sec_flags); + + /* currently encrpted */ + if (p_dev_rec->sec_flags & BTM_SEC_LE_ENCRYPTED) { + if (p_dev_rec->sec_flags & BTM_SEC_LE_AUTHENTICATED) { + 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) { + /* To avoid re-encryption on an encrypted link for an equal condition encryption */ + *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_DEBUG("cur_sec_level=%d req_sec_level=%d sec_req_act=%d", + cur_sec_level, + req_sec_level, + *p_sec_req_act); + + +} +#endif ///BLE_INCLUDED == TRUE +#endif ///SMP_INCLUDED == TRUE + +#if (BLE_INCLUDED == TRUE) +/******************************************************************************* +** +** 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; +#if (SMP_INCLUDED == TRUE) + tBTM_BLE_SEC_ACT sec_act = *(tBTM_BLE_SEC_ACT *)p_ref_data ; + tBTM_SEC_DEV_REC *p_rec = btm_find_dev (bd_addr); + tBTM_BLE_SEC_REQ_ACT sec_req_act; + tBTM_LE_AUTH_REQ auth_req; + + if (p_rec == NULL) { + BTM_TRACE_WARNING ("btm_ble_set_encryption (NULL device record!! sec_act=0x%x", sec_act); + return (BTM_WRONG_MODE); + } + + BTM_TRACE_DEBUG ("btm_ble_set_encryption sec_act=0x%x role_master=%d", sec_act, p_rec->role_master); + + 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 && (p_rec->ble.key_type & BTM_LE_KEY_PENC)) { + /* start link layer encryption using the security info stored */ + cmd = btm_ble_start_encrypt(bd_addr, FALSE, NULL); + 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 ((link_role == BTM_ROLE_MASTER) && (sec_act != BTM_BLE_SEC_ENCRYPT)) { + auth_req = (sec_act == BTM_BLE_SEC_ENCRYPT_NO_MITM) + ? SMP_AUTH_GEN_BOND : (SMP_AUTH_GEN_BOND | SMP_AUTH_YN_BIT); + btm_ble_link_sec_check (bd_addr, auth_req, &sec_req_act); + + if (sec_req_act == BTM_BLE_SEC_REQ_ACT_ENCRYPT) { + cmd = btm_ble_start_encrypt(bd_addr, FALSE, NULL); + break; + } + } +#if (SMP_SLAVE_CON_PARAMS_UPD_ENABLE == TRUE) + // already have encrypted information, do not need to update connection parameters + if(link_role == BTM_ROLE_SLAVE && (p_rec->ble.key_type & BTM_LE_KEY_PENC)) { + p_rec->ble.skip_update_conn_param = true; + } else { + p_rec->ble.skip_update_conn_param = false; + } +#endif + if (SMP_Pair(bd_addr) == SMP_STARTED) { + cmd = BTM_CMD_STARTED; + p_rec->sec_state = BTM_SEC_STATE_AUTHENTICATING; + } + break; + + default: + cmd = BTM_WRONG_MODE; + break; + } +#endif ///SMP_INCLUDED == TRUE + return cmd; +} + +/******************************************************************************* +** +** Function btm_ble_ltk_request +** +** Description This function is called when encryption request is received +** on a slave device. +** +** +** Returns void +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +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_OCTET16 dummy_stk = {0}; + + BTM_TRACE_DEBUG ("btm_ble_ltk_request"); + + p_cb->ediv = ediv; + + memcpy(p_cb->enc_rand, rand, BT_OCTET8_LEN); + + if (p_dev_rec != NULL) { + if (!smp_proc_ltk_request(p_dev_rec->bd_addr)) { + btm_ble_ltk_request_reply(p_dev_rec->bd_addr, FALSE, dummy_stk); + } + } + +} +#endif ///SMP_INCLUDED == TRUE + + +/******************************************************************************* +** +** Function btm_ble_start_encrypt +** +** Description This function is called to start LE encryption. +** +** +** Returns BTM_SUCCESS if encryption was started successfully +** +*******************************************************************************/ +tBTM_STATUS btm_ble_start_encrypt(BD_ADDR bda, BOOLEAN use_stk, BT_OCTET16 stk) +{ +#if (SMP_INCLUDED == TRUE) + tBTM_CB *p_cb = &btm_cb; + tBTM_SEC_DEV_REC *p_rec = btm_find_dev (bda); + BT_OCTET8 dummy_rand = {0}; +#endif ///SMP_INCLUDED == TRUE + + tBTM_STATUS rt = BTM_NO_RESOURCES; +#if (SMP_INCLUDED == TRUE) + BTM_TRACE_DEBUG ("btm_ble_start_encrypt"); + + if (!p_rec ) { + BTM_TRACE_ERROR("Link is not active, can not encrypt!"); + return BTM_WRONG_MODE; + } + + if (p_rec->sec_state == BTM_SEC_STATE_ENCRYPTING) { + BTM_TRACE_WARNING("Link Encryption is active, Busy!"); + return BTM_BUSY; + } + + p_cb->enc_handle = p_rec->ble_hci_handle; + + if (use_stk) { + if (btsnd_hcic_ble_start_enc(p_rec->ble_hci_handle, dummy_rand, 0, stk)) { + rt = BTM_CMD_STARTED; + } + } else if (p_rec->ble.key_type & BTM_LE_KEY_PENC) { + if (btsnd_hcic_ble_start_enc(p_rec->ble_hci_handle, p_rec->ble.keys.rand, + p_rec->ble.keys.ediv, p_rec->ble.keys.pltk)) { + rt = BTM_CMD_STARTED; + } + } else { + BTM_TRACE_ERROR("No key available to encrypt the link"); + } + if (rt == BTM_CMD_STARTED) { + if (p_rec->sec_state == BTM_SEC_STATE_IDLE) { + p_rec->sec_state = BTM_SEC_STATE_ENCRYPTING; + } + } +#endif ///SMP_INCLUDED == TRUE + return rt; +} + +/******************************************************************************* +** +** Function btm_ble_link_encrypted +** +** Description This function is called when LE link encrption status is changed. +** +** Returns void +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +void btm_ble_link_encrypted(BD_ADDR bd_addr, UINT8 encr_enable) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bd_addr); + BOOLEAN enc_cback; + + if (!p_dev_rec) { + BTM_TRACE_WARNING ("btm_ble_link_encrypted (No Device Found!) encr_enable=%d", encr_enable); + return; + } + + BTM_TRACE_DEBUG ("btm_ble_link_encrypted encr_enable=%d", encr_enable); + + enc_cback = (p_dev_rec->sec_state == BTM_SEC_STATE_ENCRYPTING); + + smp_link_encrypted(bd_addr, encr_enable); + + BTM_TRACE_DEBUG(" 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; + } + + p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; + if (p_dev_rec->p_callback && enc_cback) { + if (encr_enable) { + btm_sec_dev_rec_cback_event(p_dev_rec, BTM_SUCCESS, TRUE); + } else if (p_dev_rec->role_master) { + btm_sec_dev_rec_cback_event(p_dev_rec, BTM_ERR_PROCESSING, TRUE); + } + + } + /* to notify GATT to send data if any request is pending */ + gatt_notify_enc_cmpl(p_dev_rec->ble.pseudo_addr); +} +#endif ///SMP_INCLUDED == TRUE + + +/******************************************************************************* +** +** Function btm_ble_ltk_request_reply +** +** Description This function is called to send a LTK request reply on a slave +** device. +** +** Returns void +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +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; + + if (p_rec == NULL) { + BTM_TRACE_ERROR("btm_ble_ltk_request_reply received for unknown device"); + return; + } + + BTM_TRACE_DEBUG ("btm_ble_ltk_request_reply"); + p_cb->enc_handle = p_rec->ble_hci_handle; + p_cb->key_size = p_rec->ble.keys.key_size; + + BTM_TRACE_DEBUG("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 */ + if (p_rec->ble.key_type & BTM_LE_KEY_LENC) { + btsnd_hcic_ble_ltk_req_reply(btm_cb.enc_handle, p_rec->ble.keys.lltk); + } else { + btsnd_hcic_ble_ltk_req_neg_reply(btm_cb.enc_handle); + } + } +} + +/******************************************************************************* +** +** 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_DEBUG ("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 + { +#if BTM_BLE_CONFORMANCE_TESTING == TRUE + if (btm_cb.devcb.keep_rfu_in_auth_req) { + BTM_TRACE_DEBUG ("btm_ble_io_capabilities_req keep_rfu_in_auth_req = %u", + btm_cb.devcb.keep_rfu_in_auth_req); + p_data->auth_req &= BTM_LE_AUTH_REQ_MASK_KEEP_RFU; + btm_cb.devcb.keep_rfu_in_auth_req = FALSE; + } else { + /* default */ + p_data->auth_req &= BTM_LE_AUTH_REQ_MASK; + } +#else + p_data->auth_req &= BTM_LE_AUTH_REQ_MASK; +#endif + + BTM_TRACE_DEBUG ("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_DEBUG ("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_DEBUG("Non bonding: No keys should be exchanged"); + p_data->init_keys = 0; + p_data->resp_keys = 0; + } + + BTM_TRACE_DEBUG ("btm_ble_io_capabilities_req 3: auth_req:%d\n", p_data->auth_req); + BTM_TRACE_DEBUG ("btm_ble_io_capabilities_req 4: i_keys=0x%x r_keys=0x%x\n", + p_data->init_keys, + p_data->resp_keys); + + BTM_TRACE_DEBUG ("btm_ble_io_capabilities_req 5: p_data->io_cap = %d auth_req:%d\n", + 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; + } + + if (!(p_data->auth_req & SMP_SC_SUPPORT_BIT)) { + /* if Secure Connections are not supported then remove LK derivation, + ** and keypress notifications. + */ + BTM_TRACE_DEBUG("%s-SC not supported -> No LK derivation, no keypress notifications", + __func__); + p_data->auth_req &= ~SMP_KP_SUPPORT_BIT; + p_data->init_keys &= ~SMP_SEC_KEY_TYPE_LK; + p_data->resp_keys &= ~SMP_SEC_KEY_TYPE_LK; + } + + BTM_TRACE_DEBUG ("btm_ble_io_capabilities_req 6: IO_CAP:%d oob_data:%d auth_req:0x%02x\n", + p_data->io_cap, p_data->oob_data, p_data->auth_req); + } + return callback_rc; +} +#endif ///SMP_INCLUDED == TRUE + + +/******************************************************************************* +** +** Function btm_ble_br_keys_req +** +** Description This function is called to handle SMP request for keys sent +** over BR/EDR. +** +** Returns void +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +UINT8 btm_ble_br_keys_req(tBTM_SEC_DEV_REC *p_dev_rec, tBTM_LE_IO_REQ *p_data) +{ + UINT8 callback_rc = BTM_SUCCESS; + BTM_TRACE_DEBUG ("%s\n", __func__); + 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); + } + return callback_rc; +} +#endif ///SMP_INCLUDED + + +#if (BLE_PRIVACY_SPT == TRUE ) +/******************************************************************************* +** +** Function btm_ble_resolve_random_addr_on_conn_cmpl +** +** Description resolve random address complete on connection complete event. +** +** Returns void +** +*******************************************************************************/ +static void btm_ble_resolve_random_addr_on_conn_cmpl(void *p_rec, void *p_data) +{ + UINT8 *p = (UINT8 *)p_data; + tBTM_SEC_DEV_REC *match_rec = (tBTM_SEC_DEV_REC *) p_rec; + UINT8 role, bda_type; + UINT16 handle; + BD_ADDR bda, local_rpa, peer_rpa; + UINT16 conn_interval, conn_latency, conn_timeout; + BOOLEAN match = FALSE; + + ++p; + STREAM_TO_UINT16 (handle, p); + STREAM_TO_UINT8 (role, p); + STREAM_TO_UINT8 (bda_type, p); + STREAM_TO_BDADDR (bda, p); + // if the enhanced is true, means the connection is enhanced connect, + // so the packet should include the local Resolvable Private Address and Peer Resolvable Private Address + if(temp_enhanced) { + STREAM_TO_BDADDR(local_rpa, p); + STREAM_TO_BDADDR(peer_rpa, p); + } + STREAM_TO_UINT16 (conn_interval, p); + STREAM_TO_UINT16 (conn_latency, p); + STREAM_TO_UINT16 (conn_timeout, p); + + handle = HCID_GET_HANDLE (handle); + BTM_TRACE_EVENT ("%s\n", __func__); + + if (match_rec) { + BTM_TRACE_DEBUG("%s matched and resolved random address", __func__); + match = TRUE; + match_rec->ble.active_addr_type = BTM_BLE_ADDR_RRA; + memcpy(match_rec->ble.cur_rand_addr, bda, BD_ADDR_LEN); + if (!btm_ble_init_pseudo_addr (match_rec, bda)) { + /* assign the original address to be the current report address */ + memcpy(bda, match_rec->ble.pseudo_addr, BD_ADDR_LEN); + } else { + memcpy(bda, match_rec->bd_addr, BD_ADDR_LEN); + } + } else { + BTM_TRACE_DEBUG("%s unable to match and resolve random address", __func__); + } + + btm_ble_connected(bda, handle, HCI_ENCRYPT_MODE_DISABLED, role, bda_type, match); + + l2cble_conn_comp (handle, role, bda, bda_type, conn_interval, + conn_latency, conn_timeout); + + return; +} +#endif + +/******************************************************************************* +** +** 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, + tBLE_ADDR_TYPE addr_type, BOOLEAN addr_matched) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bda); + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + UNUSED(addr_matched); + + BTM_TRACE_EVENT ("btm_ble_connected"); + + /* Commenting out trace due to obf/compilation problems. + */ +#if (BT_USE_TRACES == TRUE) + if (p_dev_rec) { + BTM_TRACE_EVENT ("Security Manager: btm_ble_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_DEBUG ("btm_ble_connected sec_flags=0x%x", p_dev_rec->sec_flags); + } else { + BTM_TRACE_EVENT ("Security Manager: btm_ble_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 */ + if ((p_dev_rec = btm_sec_alloc_dev (bda)) == NULL) { + return; + } + } else { /* Update the timestamp for this device */ + p_dev_rec->timestamp = btm_cb.dev_rec_count++; + } + + /* update device information */ + p_dev_rec->device_type |= BT_DEVICE_TYPE_BLE; + p_dev_rec->ble_hci_handle = handle; + p_dev_rec->ble.ble_addr_type = addr_type; + /* update pseudo address */ + memcpy(p_dev_rec->ble.pseudo_addr, bda, BD_ADDR_LEN); + + p_dev_rec->role_master = FALSE; + if (role == HCI_ROLE_MASTER) { + p_dev_rec->role_master = TRUE; + } + +#if (defined BLE_PRIVACY_SPT && BLE_PRIVACY_SPT == TRUE) + if (!addr_matched) { + p_dev_rec->ble.active_addr_type = BTM_BLE_ADDR_PSEUDO; + } + + if (p_dev_rec->ble.ble_addr_type == BLE_ADDR_RANDOM && !addr_matched) { + memcpy(p_dev_rec->ble.cur_rand_addr, bda, BD_ADDR_LEN); + } +#endif + + p_cb->inq_var.directed_conn = BTM_BLE_CONNECT_EVT; + + return; +} + +/***************************************************************************** +** Function btm_ble_conn_complete +** +** Description LE connection complete. +** +******************************************************************************/ +void btm_ble_conn_complete(UINT8 *p, UINT16 evt_len, BOOLEAN enhanced) +{ +#if (BLE_PRIVACY_SPT == TRUE ) + UINT8 *p_data = p, peer_addr_type; +#endif ///BLE_PRIVACY_SPT == TRUE + UINT8 role, status, bda_type; + UINT16 handle; + BD_ADDR bda; + BD_ADDR local_rpa, peer_rpa; + UINT16 conn_interval, conn_latency, conn_timeout; + BOOLEAN match = FALSE; + UNUSED(evt_len); + STREAM_TO_UINT8 (status, p); + STREAM_TO_UINT16 (handle, p); + STREAM_TO_UINT8 (role, p); + STREAM_TO_UINT8 (bda_type, p); + STREAM_TO_BDADDR (bda, p); + BTM_TRACE_DEBUG("status = %d, handle = %d, role = %d, bda_type = %d",status,handle,role,bda_type); + if (status == 0) { + if (enhanced) { + STREAM_TO_BDADDR (local_rpa, p); + STREAM_TO_BDADDR (peer_rpa, p); +#if (CONTROLLER_RPA_LIST_ENABLE == TRUE) + BD_ADDR dummy_bda = {0}; + /* For controller generates RPA, if resolving list contains no matching entry, it use identity address. + * So we should update own addr type in Host */ + if (memcmp(local_rpa, dummy_bda, BD_ADDR_LEN)) { + btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type |= (BLE_ADDR_TYPE_ID_BIT); + BTM_UpdateAddrInfor(btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type, local_rpa); + } else { + btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type &= (~BLE_ADDR_TYPE_ID_BIT); + } +#endif + } +#if (BLE_PRIVACY_SPT == TRUE ) + peer_addr_type = bda_type; + match = btm_identity_addr_to_random_pseudo (bda, &bda_type, FALSE); + + /* possiblly receive connection complete with resolvable random on + slave role while the device has been paired */ + + /* It will cause that scanner doesn't send scan request to advertiser + * which has sent IRK to us and we have stored the IRK in controller. + * It is a hardware limitation. The preliminary solution is not to + * send key to the controller, but to resolve the random address in host. + * so we need send the real address information to controller to connect. + * Once the connection is successful, resolve device address whether it is + * slave or master*/ + +#if CONTROLLER_RPA_LIST_ENABLE + if (!match && role == HCI_ROLE_SLAVE && bda_type != BLE_ADDR_PUBLIC && BTM_BLE_IS_RESOLVE_BDA(bda)) { +#else + if (!match && bda_type != BLE_ADDR_PUBLIC && BTM_BLE_IS_RESOLVE_BDA(bda)) { +#endif + // save the enhanced value to used in btm_ble_resolve_random_addr_on_conn_cmpl func. + temp_enhanced = enhanced; + btm_ble_resolve_random_addr(bda, btm_ble_resolve_random_addr_on_conn_cmpl, p_data); + // set back the temp enhanced to default after used. + temp_enhanced = FALSE; + } else +#endif + { + STREAM_TO_UINT16 (conn_interval, p); + STREAM_TO_UINT16 (conn_latency, p); + STREAM_TO_UINT16 (conn_timeout, p); + handle = HCID_GET_HANDLE (handle); + + btm_ble_connected(bda, handle, HCI_ENCRYPT_MODE_DISABLED, role, bda_type, match); + l2cble_conn_comp (handle, role, bda, bda_type, conn_interval, + conn_latency, conn_timeout); + +#if (BLE_PRIVACY_SPT == TRUE) + if (enhanced) { + btm_ble_refresh_local_resolvable_private_addr(bda, local_rpa); + + if (peer_addr_type & BLE_ADDR_TYPE_ID_BIT) { + btm_ble_refresh_peer_resolvable_private_addr(bda, peer_rpa, BLE_ADDR_RANDOM); + } + } +#endif + } + } else { + role = HCI_ROLE_UNKNOWN; + if (status != HCI_ERR_DIRECTED_ADVERTISING_TIMEOUT) { + btm_ble_set_conn_st(BLE_CONN_IDLE); +#if (defined BLE_PRIVACY_SPT && BLE_PRIVACY_SPT == TRUE) + btm_ble_disable_resolving_list(BTM_BLE_RL_INIT, TRUE); +#endif + } else { +#if (defined BLE_PRIVACY_SPT && BLE_PRIVACY_SPT == TRUE) + btm_cb.ble_ctr_cb.inq_var.adv_mode = BTM_BLE_ADV_DISABLE; + btm_ble_disable_resolving_list(BTM_BLE_RL_ADV, TRUE); +#endif + } + + } + + BOOLEAN bg_con = btm_ble_update_mode_operation(role, bda, status); + if (status != HCI_SUCCESS && !bg_con) { + // notify connection failed + l2c_link_hci_disc_comp (handle, status); +#if (SMP_INCLUDED == TRUE) + /* Notify security manager */ + btm_sec_disconnected (handle, status); +#endif ///SMP_INCLUDED == TRUE + } +} + + + +/***************************************************************************** +** Function btm_ble_create_ll_conn_complete +** +** Description LE connection complete. +** +******************************************************************************/ +void btm_ble_create_ll_conn_complete (UINT8 status) +{ + if (status != HCI_SUCCESS) { + btm_ble_set_conn_st(BLE_CONN_IDLE); + btm_ble_update_mode_operation(HCI_ROLE_UNKNOWN, NULL, status); + } +} + +/***************************************************************************** +** Function btm_ble_create_conn_cancel_complete +** +** Description LE connection cancel complete. +** +******************************************************************************/ +void btm_ble_create_conn_cancel_complete (UINT8 *p) +{ + UINT8 status; + + STREAM_TO_UINT8 (status, p); + + switch (status) { + case HCI_SUCCESS: + if (btm_ble_get_conn_st() == BLE_CONN_CANCEL) { + btm_ble_set_conn_st (BLE_CONN_IDLE); + } + break; + default: + break; + } +} + +/***************************************************************************** +** Function btm_proc_smp_cback +** +** Description This function is the SMP callback handler. +** +******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +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 = 0; + + BTM_TRACE_DEBUG ("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_BR_KEYS_REQ_EVT: + btm_ble_br_keys_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: + case SMP_NC_REQ_EVT: + case SMP_SC_OOB_REQ_EVT: + /* fall through */ + p_dev_rec->sec_flags |= BTM_SEC_LE_AUTHENTICATED; + + case SMP_SEC_REQUEST_EVT: + if (event == SMP_SEC_REQUEST_EVT && btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) { + BTM_TRACE_DEBUG("%s: Ignoring SMP Security request", __func__); + break; + } + memcpy (btm_cb.pairing_bda, bd_addr, BD_ADDR_LEN); + p_dev_rec->sec_state = BTM_SEC_STATE_AUTHENTICATING; + btm_cb.pairing_flags |= BTM_PAIR_FLAGS_LE_ACTIVE; + /* fall through */ + + case SMP_COMPLT_EVT: + if (btm_cb.api.p_le_callback) { + /* the callback function implementation may change the IO capability... */ + BTM_TRACE_DEBUG ("btm_cb.api.p_le_callback=%p", btm_cb.api.p_le_callback ); + (*btm_cb.api.p_le_callback) (event, bd_addr, (tBTM_LE_EVT_DATA *)p_data); + } + + if (event == SMP_COMPLT_EVT) { + BTM_TRACE_DEBUG ("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_DEBUG ("after update result=%d sec_level=0x%x sec_flags=0x%x", + res, p_data->cmplt.sec_level , p_dev_rec->sec_flags ); + + if (p_data->cmplt.is_pair_cancel && btm_cb.api.p_bond_cancel_cmpl_callback ) { + BTM_TRACE_DEBUG ("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 && p_data->cmplt.reason != SMP_CONN_TOUT) { + BTM_TRACE_DEBUG ("Pairing failed - prepare to remove ACL"); + l2cu_start_post_bond_timer(p_dev_rec->ble_hci_handle); + } else { + BTM_TRACE_DEBUG ("Pairing failed - Not Removing ACL"); + p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; + } + } +#else + if (res != BTM_SUCCESS && p_data->cmplt.reason != SMP_CONN_TOUT) { + BTM_TRACE_DEBUG ("Pairing failed - prepare to remove ACL"); + l2cu_start_post_bond_timer(p_dev_rec->ble_hci_handle); + } +#endif + + BTM_TRACE_DEBUG ("btm_cb pairing_state=%x pairing_flags=%x", + btm_cb.pairing_state, + btm_cb.pairing_flags); + BTM_TRACE_DEBUG ("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]); + + /* Reset btm state only if the callback address matches pairing address*/ + if (memcmp(bd_addr, btm_cb.pairing_bda, BD_ADDR_LEN) == 0) { + memset (btm_cb.pairing_bda, 0xff, BD_ADDR_LEN); + btm_cb.pairing_state = BTM_PAIR_STATE_IDLE; + btm_cb.pairing_flags = 0; + } + + if (res == BTM_SUCCESS) { + p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; +#if (defined BLE_PRIVACY_SPT && BLE_PRIVACY_SPT == TRUE) + /* add all bonded device into resolving list if IRK is available*/ + btm_ble_resolving_list_load_dev(p_dev_rec); +#endif + } + + btm_sec_dev_rec_cback_event(p_dev_rec, res, TRUE); + } + break; + + default: + BTM_TRACE_DEBUG ("unknown event = %d", event); + break; + + + } + } else { + if (event == SMP_SC_LOC_OOB_DATA_UP_EVT) { + tBTM_LE_EVT_DATA evt_data; + memcpy(&evt_data.local_oob_data, &p_data->loc_oob_data, sizeof(tSMP_LOC_OOB_DATA)); + if (btm_cb.api.p_le_callback) { + (*btm_cb.api.p_le_callback)(event, bd_addr, &evt_data); + } + } else { + BTM_TRACE_ERROR("btm_proc_smp_cback received for unknown device"); + } + } + return 0; +} +#endif ///SMP_INCLUDED == TRUE + + +/******************************************************************************* +** +** 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. +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +BOOLEAN BTM_BleDataSignature (BD_ADDR bd_addr, UINT8 *p_text, UINT16 len, + BLE_SIGNATURE signature) +{ + tBTM_SEC_DEV_REC *p_rec = btm_find_dev (bd_addr); + + BTM_TRACE_DEBUG ("%s", __func__); +#endif ///SMP_INCLUDED == TRUE + BOOLEAN ret = FALSE; +#if (SMP_INCLUDED == TRUE) + if (p_rec == NULL) { + BTM_TRACE_ERROR("%s-data signing can not be done from unknown device", __func__); + } else { + UINT8 *p_mac = (UINT8 *)signature; + UINT8 *p_buf, *pp; + if ((p_buf = (UINT8 *)osi_malloc((UINT16)(len + 4))) != NULL) { + BTM_TRACE_DEBUG("%s-Start to generate Local CSRK", __func__); + pp = p_buf; + /* prepare plain text */ + if (p_text) { + memcpy(p_buf, p_text, len); + pp = (p_buf + len); + } + + UINT32_TO_STREAM(pp, p_rec->ble.keys.local_counter); + UINT32_TO_STREAM(p_mac, p_rec->ble.keys.local_counter); + + if ((ret = aes_cipher_msg_auth_code(p_rec->ble.keys.lcsrk, p_buf, (UINT16)(len + 4), + BTM_CMAC_TLEN_SIZE, p_mac)) == TRUE) { + btm_ble_increment_sign_ctr(bd_addr, TRUE); + } + + BTM_TRACE_DEBUG("%s p_mac = %p", __func__, p_mac); + BTM_TRACE_DEBUG("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_DEBUG("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)); + osi_free(p_buf); + } + } + 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; + 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_ERROR("can not verify signature for unknown device"); + } else if (counter < p_rec->ble.keys.counter) { + BTM_TRACE_ERROR("signature received with out dated sign counter"); + } else if (p_orig == NULL) { + BTM_TRACE_ERROR("No signature to verify"); + } else { + BTM_TRACE_DEBUG ("%s rcv_cnt=%d >= expected_cnt=%d", __func__, counter, + p_rec->ble.keys.counter); + + if (aes_cipher_msg_auth_code(p_rec->ble.keys.pcsrk, 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; + } + } + } + return verified; +} +#endif /* SMP_INCLUDED */ + + +/******************************************************************************* +** +** Function BTM_GetLeSecurityState +** +** Description This function is called to get security mode 1 flags and +** encryption key size for LE peer. +** +** Returns BOOLEAN TRUE if LE device is found, FALSE otherwise. +** +*******************************************************************************/ +BOOLEAN BTM_GetLeSecurityState (BD_ADDR bd_addr, UINT8 *p_le_dev_sec_flags, UINT8 *p_le_key_size) +{ +#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE) + tBTM_SEC_DEV_REC *p_dev_rec; + UINT16 dev_rec_sec_flags; +#endif + + *p_le_dev_sec_flags = 0; + *p_le_key_size = 0; + +#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE) + if ((p_dev_rec = btm_find_dev (bd_addr)) == NULL) { + BTM_TRACE_ERROR ("%s fails", __func__); + return (FALSE); + } + + if (p_dev_rec->ble_hci_handle == BTM_SEC_INVALID_HANDLE) { + BTM_TRACE_ERROR ("%s-this is not LE device", __func__); + return (FALSE); + } + + dev_rec_sec_flags = p_dev_rec->sec_flags; + + if (dev_rec_sec_flags & BTM_SEC_LE_ENCRYPTED) { + /* link is encrypted with LTK or STK */ + *p_le_key_size = p_dev_rec->enc_key_size; + *p_le_dev_sec_flags |= BTM_SEC_LE_LINK_ENCRYPTED; + + *p_le_dev_sec_flags |= (dev_rec_sec_flags & BTM_SEC_LE_AUTHENTICATED) + ? BTM_SEC_LE_LINK_PAIRED_WITH_MITM /* set auth LTK flag */ + : BTM_SEC_LE_LINK_PAIRED_WITHOUT_MITM; /* set unauth LTK flag */ + } else if (p_dev_rec->ble.key_type & BTM_LE_KEY_PENC) { + /* link is unencrypted, still LTK is available */ + *p_le_key_size = p_dev_rec->ble.keys.key_size; + + *p_le_dev_sec_flags |= (dev_rec_sec_flags & BTM_SEC_LE_LINK_KEY_AUTHED) + ? BTM_SEC_LE_LINK_PAIRED_WITH_MITM /* set auth LTK flag */ + : BTM_SEC_LE_LINK_PAIRED_WITHOUT_MITM; /* set unauth LTK flag */ + } + + BTM_TRACE_DEBUG ("%s - le_dev_sec_flags: 0x%02x, le_key_size: %d", + __func__, *p_le_dev_sec_flags, *p_le_key_size); + + return TRUE; +#else + return FALSE; +#endif +} + +/******************************************************************************* +** +** Function BTM_BleSecurityProcedureIsRunning +** +** Description This function indicates if LE security procedure is +** currently running with the peer. +** +** Returns BOOLEAN TRUE if security procedure is running, FALSE otherwise. +** +*******************************************************************************/ +BOOLEAN BTM_BleSecurityProcedureIsRunning(BD_ADDR bd_addr) +{ +#if (BLE_INCLUDED == TRUE) + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bd_addr); + + if (p_dev_rec == NULL) { + BTM_TRACE_ERROR ("%s device with BDA: %08x%04x is not found", + __func__, (bd_addr[0] << 24) + (bd_addr[1] << 16) + (bd_addr[2] << 8) + bd_addr[3], + (bd_addr[4] << 8) + bd_addr[5]); + return FALSE; + } + + return (p_dev_rec->sec_state == BTM_SEC_STATE_ENCRYPTING || + p_dev_rec->sec_state == BTM_SEC_STATE_AUTHENTICATING); +#else + return FALSE; +#endif +} + +/******************************************************************************* +** +** Function BTM_BleGetSupportedKeySize +** +** Description This function gets the maximum encryption key size in bytes +** the local device can suport. +** record. +** +** Returns the key size or 0 if the size can't be retrieved. +** +*******************************************************************************/ +extern UINT8 BTM_BleGetSupportedKeySize (BD_ADDR bd_addr) +{ +#ifndef L2CAP_LE_COC_INCLUDED +#define L2CAP_LE_COC_INCLUDED FALSE +#endif +#if ((BLE_INCLUDED == TRUE) && (L2CAP_LE_COC_INCLUDED == TRUE)) + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bd_addr); + tBTM_LE_IO_REQ dev_io_cfg; + UINT8 callback_rc; + + if (!p_dev_rec) { + BTM_TRACE_ERROR ("%s device with BDA: %08x%04x is not found", + __func__, (bd_addr[0] << 24) + (bd_addr[1] << 16) + (bd_addr[2] << 8) + bd_addr[3], + (bd_addr[4] << 8) + bd_addr[5]); + return 0; + } + + if (btm_cb.api.p_le_callback == NULL) { + BTM_TRACE_ERROR ("%s can't access supported key size", __func__); + return 0; + } + + callback_rc = (*btm_cb.api.p_le_callback) (BTM_LE_IO_REQ_EVT, p_dev_rec->bd_addr, + (tBTM_LE_EVT_DATA *) &dev_io_cfg); + + if (callback_rc != BTM_SUCCESS) { + BTM_TRACE_ERROR ("%s can't access supported key size", __func__); + return 0; + } + + BTM_TRACE_DEBUG ("%s device supports key size = %d", __func__, dev_io_cfg.max_key_size); + return (dev_io_cfg.max_key_size); +#else + return 0; +#endif +} + +/******************************************************************************* +** 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 +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +static void btm_notify_new_key(UINT8 key_type) +{ + tBTM_BLE_LOCAL_KEYS *p_locak_keys = NULL; + + BTM_TRACE_DEBUG ("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_DEBUG ("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_DEBUG ("BTM_BLE_KEY_TYPE_ER"); + p_locak_keys = (tBTM_BLE_LOCAL_KEYS *)&btm_cb.devcb.ble_encryption_key_value; + break; + + default: + BTM_TRACE_ERROR("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_DEBUG ("btm_ble_process_er2"); + + if (p && p->opcode == HCI_BLE_RAND) { + memcpy(&btm_cb.devcb.ble_encryption_key_value[8], p->param_buf, BT_OCTET8_LEN); + btm_notify_new_key(BTM_BLE_KEY_TYPE_ER); + } else { + BTM_TRACE_ERROR("Generating ER2 exception."); + memset(&btm_cb.devcb.ble_encryption_key_value, 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_DEBUG ("btm_ble_process_er"); + + if (p && p->opcode == HCI_BLE_RAND) { + memcpy(&btm_cb.devcb.ble_encryption_key_value[0], p->param_buf, BT_OCTET8_LEN); + + if (!btsnd_hcic_ble_rand((void *)btm_ble_process_er2)) { + memset(&btm_cb.devcb.ble_encryption_key_value, 0, sizeof(BT_OCTET16)); + BTM_TRACE_ERROR("Generating ER2 failed."); + } + } else { + BTM_TRACE_ERROR("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_DEBUG ("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); + +#if (CONTROLLER_RPA_LIST_ENABLE == TRUE) + btm_ble_add_default_entry_to_resolving_list(); +#endif + +#if (BLE_PRIVACY_SPT == TRUE) && (CONTROLLER_RPA_LIST_ENABLE == FALSE) + /* if privacy is enabled, new RPA should be calculated */ + if (btm_cb.ble_ctr_cb.privacy_mode != BTM_PRIVACY_NONE) { + btm_gen_resolvable_private_addr((void *)btm_gen_resolve_paddr_low); + } +#endif + } else { + BTM_TRACE_ERROR("Generating IRK exception."); + } + + /* proceed generate ER */ + if (!btsnd_hcic_ble_rand((void *)btm_ble_process_er)) { + BTM_TRACE_ERROR("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) +{ + UINT8 btm_ble_irk_pt = 0x01; + tSMP_ENC output; + + BTM_TRACE_DEBUG ("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_DEBUG("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)); + } +} + +/******************************************************************************* +** +** 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) +{ + UINT8 btm_ble_dhk_pt = 0x03; + tSMP_ENC output; + + BTM_TRACE_DEBUG ("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_DEBUG("BLE IR generated."); + } else { + memset(&btm_cb.devcb.id_keys, 0, sizeof(tBTM_BLE_LOCAL_ID_KEYS)); + } +} + +/******************************************************************************* +** +** 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_DEBUG ("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_ERROR("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_DEBUG ("btm_ble_reset_id"); + + /* regenrate Identity Root*/ + if (!btsnd_hcic_ble_rand((void *)btm_ble_process_ir)) { + BTM_TRACE_DEBUG("Generating IR failed."); + } +} +#endif ///SMP_INCLUDED == TRUE + +#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_DEBUG ("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_DEBUG ("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_DEBUG ("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; +} + +/******************************************************************************* +** +** Function btm_set_random_address +** +** Description This function set a random address to local controller. +** +** Returns void +** +*******************************************************************************/ +void btm_set_random_address(BD_ADDR random_bda) +{ + tBTM_LE_RANDOM_CB *p_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + BOOLEAN adv_mode = btm_cb.ble_ctr_cb.inq_var.adv_mode ; + + BTM_TRACE_DEBUG ("btm_set_random_address"); + + if (adv_mode == BTM_BLE_ADV_ENABLE) { + btsnd_hcic_ble_set_adv_enable (BTM_BLE_ADV_DISABLE); + } + + memcpy(p_cb->private_addr, random_bda, BD_ADDR_LEN); + btsnd_hcic_ble_set_random_addr(p_cb->private_addr); + + if (adv_mode == BTM_BLE_ADV_ENABLE) { + btsnd_hcic_ble_set_adv_enable (BTM_BLE_ADV_ENABLE); + } + + +} + +/******************************************************************************* +** +** Function btm_ble_set_keep_rfu_in_auth_req +** +** Description This function indicates if RFU bits have to be kept as is +** (by default they have to be set to 0 by the sender). +** +** Returns void +** +*******************************************************************************/ +void btm_ble_set_keep_rfu_in_auth_req(BOOLEAN keep_rfu) +{ + BTM_TRACE_DEBUG ("btm_ble_set_keep_rfu_in_auth_req keep_rfus=%d", keep_rfu); + btm_cb.devcb.keep_rfu_in_auth_req = keep_rfu; +} + +#endif /* BTM_BLE_CONFORMANCE_TESTING */ + +/******************************************************************************* +** +** Function btm_get_current_conn_params +** +** Description This function is called to get current connection parameters +** information of the device +** +** Returns TRUE if the information is geted, else FALSE +** +*******************************************************************************/ + +BOOLEAN btm_get_current_conn_params(BD_ADDR bda, UINT16 *interval, UINT16 *latency, UINT16 *timeout) +{ + if( (interval == NULL) || (latency == NULL) || (timeout == NULL) ) { + BTM_TRACE_ERROR("%s invalid parameters ", __func__); + return FALSE; + } + + tL2C_LCB *p_lcb = l2cu_find_lcb_by_bd_addr(bda, BT_TRANSPORT_LE); + if(p_lcb != NULL) { + (*interval) = p_lcb->current_used_conn_interval; + (*latency) = p_lcb->current_used_conn_latency; + (*timeout) = p_lcb->current_used_conn_timeout; + return TRUE; + } + BTM_TRACE_WARNING("%s Device is not connected", __func__); + + return FALSE; +} + +uint8_t btm_ble_adv_active_count(void) +{ + uint8_t count = 0; + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + + if (p_cb->state & BTM_BLE_ADVERTISING) { + count++; + } + + return count; +} + +uint8_t btm_ble_scan_active_count(void) +{ + uint8_t count = 0; + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + + if (p_cb->state & BTM_BLE_SCANNING) { + count++; + } + + return count; +} + +#endif /* BLE_INCLUDED */ diff --git a/lib/bt/host/bluedroid/stack/btm/btm_ble_5_gap.c b/lib/bt/host/bluedroid/stack/btm/btm_ble_5_gap.c new file mode 100644 index 00000000..e5dc3b42 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/btm/btm_ble_5_gap.c @@ -0,0 +1,1455 @@ +/* + * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "btm_int.h" +#include "stack/hcimsgs.h" +#include "osi/allocator.h" +#include "device/controller.h" +#include <string.h> +#include "l2c_int.h" +#if (BLE_50_FEATURE_SUPPORT == TRUE) +#define SET_BIT(t, n) (t |= 1UL << (n)) +tBTM_BLE_EXTENDED_CB extend_adv_cb; + +tBTM_BLE_5_HCI_CBACK ble_5_hci_cb; + +#define INVALID_VALUE 0XFF +extern BOOLEAN BTM_GetLocalResolvablePrivateAddr(BD_ADDR bda); +extern void BTM_UpdateAddrInfor(uint8_t addr_type, BD_ADDR bda); +extern void BTM_BleSetStaticAddr(BD_ADDR rand_addr); +extern uint32_t BTM_BleUpdateOwnType(uint8_t *own_bda_type, tBTM_START_ADV_CMPL_CBACK *cb); +static tBTM_STATUS btm_ble_ext_adv_params_validate(tBTM_BLE_GAP_EXT_ADV_PARAMS *params); +static tBTM_STATUS btm_ble_ext_adv_set_data_validate(UINT8 instance, UINT16 len, UINT8 *data); + +typedef struct { + uint16_t ter_con_handle; + bool invalid; + UINT8 instance; + int duration; + int max_events; + uint8_t retry_count; +} tBTM_EXT_ADV_RECORD; + +tBTM_EXT_ADV_RECORD adv_record[MAX_BLE_ADV_INSTANCE] = {0}; +extern void btm_ble_inter_set(bool extble_inter); + +#if !UC_BT_STACK_NO_LOG +static const char *btm_ble_hci_status_to_str(tHCI_STATUS status) +{ + switch(status) { + case HCI_SUCCESS: + return "HCI_SUCCESS"; + case HCI_ERR_ILLEGAL_COMMAND: + return "HCI_ERR_ILLEGAL_COMMAND"; + case HCI_ERR_NO_CONNECTION: + return "HCI_ERR_NO_CONNECTION"; + case HCI_ERR_HW_FAILURE: + return "HCI_ERR_HW_FAILURE"; + case HCI_ERR_PAGE_TIMEOUT: + return "HCI_ERR_PAGE_TIMEOUT"; + case HCI_ERR_AUTH_FAILURE: + return "HCI_ERR_AUTH_FAILURE"; + case HCI_ERR_KEY_MISSING: + return "HCI_ERR_KEY_MISSING"; + case HCI_ERR_MEMORY_FULL: + return "HCI_ERR_MEMORY_FULL"; + case HCI_ERR_CONNECTION_TOUT: + return "HCI_ERR_CONNECTION_TOUT"; + case HCI_ERR_MAX_NUM_OF_CONNECTIONS: + return "HCI_ERR_MAX_NUM_OF_CONNECTIONS"; + case HCI_ERR_MAX_NUM_OF_SCOS: + return "HCI_ERR_MAX_NUM_OF_SCOS"; + case HCI_ERR_CONNECTION_EXISTS: + return "HCI_ERR_CONNECTION_EXISTS"; + case HCI_ERR_COMMAND_DISALLOWED: + return "HCI_ERR_COMMAND_DISALLOWED"; + case HCI_ERR_HOST_REJECT_RESOURCES: + return "HCI_ERR_HOST_REJECT_RESOURCES"; + case HCI_ERR_HOST_REJECT_SECURITY: + return "HCI_ERR_HOST_REJECT_SECURITY"; + case HCI_ERR_HOST_REJECT_DEVICE: + return "HCI_ERR_HOST_REJECT_DEVICE"; + case HCI_ERR_HOST_TIMEOUT: + return "HCI_ERR_HOST_TIMEOUT"; + case HCI_ERR_UNSUPPORTED_VALUE: + return "HCI_ERR_UNSUPPORTED_VALUE"; + case HCI_ERR_ILLEGAL_PARAMETER_FMT: + return "HCI_ERR_ILLEGAL_PARAMETER_FMT"; + case HCI_ERR_PEER_USER: + return "HCI_ERR_PEER_USER"; + case HCI_ERR_PEER_LOW_RESOURCES: + return "HCI_ERR_PEER_LOW_RESOURCES"; + case HCI_ERR_PEER_POWER_OFF: + return "HCI_ERR_PEER_POWER_OFF"; + case HCI_ERR_CONN_CAUSE_LOCAL_HOST: + return "HCI_ERR_CONN_CAUSE_LOCAL_HOST"; + case HCI_ERR_REPEATED_ATTEMPTS: + return "HCI_ERR_REPEATED_ATTEMPTS"; + case HCI_ERR_PAIRING_NOT_ALLOWED: + return "HCI_ERR_PAIRING_NOT_ALLOWED"; + case HCI_ERR_UNKNOWN_LMP_PDU: + return "HCI_ERR_UNKNOWN_LMP_PDU"; + case HCI_ERR_UNSUPPORTED_REM_FEATURE: + return "HCI_ERR_UNSUPPORTED_REM_FEATURE"; + case HCI_ERR_SCO_OFFSET_REJECTED: + return "HCI_ERR_SCO_OFFSET_REJECTED"; + case HCI_ERR_SCO_INTERVAL_REJECTED: + return "HCI_ERR_SCO_INTERVAL_REJECTED"; + case HCI_ERR_SCO_AIR_MODE: + return "HCI_ERR_SCO_AIR_MODE"; + case HCI_ERR_INVALID_LMP_PARAM: + return "HCI_ERR_INVALID_LMP_PARAM"; + case HCI_ERR_UNSPECIFIED: + return "HCI_ERR_UNSPECIFIED"; + case HCI_ERR_UNSUPPORTED_LMP_PARAMETERS: + return "HCI_ERR_UNSUPPORTED_LMP_PARAMETERS"; + case HCI_ERR_ROLE_CHANGE_NOT_ALLOWED: + return "HCI_ERR_ROLE_CHANGE_NOT_ALLOWED"; + case HCI_ERR_LMP_RESPONSE_TIMEOUT: + return "HCI_ERR_LMP_RESPONSE_TIMEOUT"; + case HCI_ERR_LMP_ERR_TRANS_COLLISION: + return "HCI_ERR_LMP_ERR_TRANS_COLLISION"; + case HCI_ERR_LMP_PDU_NOT_ALLOWED: + return "HCI_ERR_LMP_PDU_NOT_ALLOWED"; + case HCI_ERR_ENCRY_MODE_NOT_ACCEPTABLE: + return "HCI_ERR_ENCRY_MODE_NOT_ACCEPTABLE"; + case HCI_ERR_UNIT_KEY_USED: + return "HCI_ERR_UNIT_KEY_USED"; + case HCI_ERR_QOS_NOT_SUPPORTED: + return "HCI_ERR_QOS_NOT_SUPPORTED"; + case HCI_ERR_INSTANT_PASSED: + return "HCI_ERR_INSTANT_PASSED"; + case HCI_ERR_PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED: + return "HCI_ERR_PAIRING_WITH_UNIT_KEY_NOT_SUPPORTED"; + case HCI_ERR_DIFF_TRANSACTION_COLLISION: + return "HCI_ERR_DIFF_TRANSACTION_COLLISION"; + case HCI_ERR_UNDEFINED_0x2B: + return "HCI_ERR_UNDEFINED_0x2B"; + case HCI_ERR_QOS_UNACCEPTABLE_PARAM: + return "HCI_ERR_QOS_UNACCEPTABLE_PARAM"; + case HCI_ERR_QOS_REJECTED: + return "HCI_ERR_QOS_REJECTED"; + case HCI_ERR_CHAN_CLASSIF_NOT_SUPPORTED: + return "HCI_ERR_CHAN_CLASSIF_NOT_SUPPORTED"; + case HCI_ERR_INSUFFCIENT_SECURITY: + return "HCI_ERR_INSUFFCIENT_SECURITY"; + case HCI_ERR_PARAM_OUT_OF_RANGE: + return "HCI_ERR_PARAM_OUT_OF_RANGE"; + case HCI_ERR_UNDEFINED_0x31: + return "HCI_ERR_UNDEFINED_0x31"; + case HCI_ERR_ROLE_SWITCH_PENDING: + return "HCI_ERR_ROLE_SWITCH_PENDING"; + case HCI_ERR_UNDEFINED_0x33: + return "HCI_ERR_UNDEFINED_0x33"; + case HCI_ERR_RESERVED_SLOT_VIOLATION: + return "HCI_ERR_RESERVED_SLOT_VIOLATION"; + case HCI_ERR_ROLE_SWITCH_FAILED: + return "HCI_ERR_ROLE_SWITCH_FAILED"; + case HCI_ERR_INQ_RSP_DATA_TOO_LARGE: + return "HCI_ERR_INQ_RSP_DATA_TOO_LARGE"; + case HCI_ERR_SIMPLE_PAIRING_NOT_SUPPORTED: + return "HCI_ERR_SIMPLE_PAIRING_NOT_SUPPORTED"; + case HCI_ERR_HOST_BUSY_PAIRING: + return "HCI_ERR_HOST_BUSY_PAIRING"; + case HCI_ERR_REJ_NO_SUITABLE_CHANNEL: + return "HCI_ERR_REJ_NO_SUITABLE_CHANNEL"; + case HCI_ERR_CONTROLLER_BUSY: + return "HCI_ERR_CONTROLLER_BUSY"; + case HCI_ERR_UNACCEPT_CONN_INTERVAL: + return "HCI_ERR_UNACCEPT_CONN_INTERVAL"; + case HCI_ERR_DIRECTED_ADVERTISING_TIMEOUT: + return "HCI_ERR_DIRECTED_ADVERTISING_TIMEOUT"; + case HCI_ERR_CONN_TOUT_DUE_TO_MIC_FAILURE: + return "HCI_ERR_CONN_TOUT_DUE_TO_MIC_FAILURE"; + case HCI_ERR_CONN_FAILED_ESTABLISHMENT: + return "HCI_ERR_CONN_FAILED_ESTABLISHMENT"; + case HCI_ERR_MAC_CONNECTION_FAILED: + return "HCI_ERR_MAC_CONNECTION_FAILED"; + case HCI_ERR_LT_ADDR_ALREADY_IN_USE: + return "HCI_ERR_LT_ADDR_ALREADY_IN_USE"; + case HCI_ERR_LT_ADDR_NOT_ALLOCATED: + return "HCI_ERR_LT_ADDR_NOT_ALLOCATED"; + case HCI_ERR_CLB_NOT_ENABLED: + return "HCI_ERR_CLB_NOT_ENABLED"; + case HCI_ERR_MAX_ERR: + return "HCI_ERR_MAX_ERR"; + case HCI_ERR_ESP_VENDOR_FAIL: + return "HCI_ERR_ESP_VENDOR_FAIL"; + case HCI_HINT_TO_RECREATE_AMP_PHYS_LINK: + return "HCI_HINT_TO_RECREATE_AMP_PHYS_LINK"; + default: + return "Invalid HCI status code."; + } + + return NULL; +} +#endif /* !UC_BT_STACK_NO_LOG */ + +void btm_ble_extendadvcb_init(void) +{ + memset(&extend_adv_cb, 0, sizeof(tBTM_BLE_EXTENDED_CB)); +} + +void btm_ble_advrecod_init(void) +{ + memset(&adv_record[0], 0, sizeof(tBTM_EXT_ADV_RECORD)*MAX_BLE_ADV_INSTANCE); +} + +void BTM_BleGapRegisterCallback(tBTM_BLE_5_HCI_CBACK cb) +{ + if (cb) { + ble_5_hci_cb = cb; + } else { + BTM_TRACE_ERROR("%s, register fail, the cb function is NULL.", __func__); + } +} + +void BTM_ExtBleCallbackTrigger(tBTM_BLE_5_GAP_EVENT event, tBTM_BLE_5_GAP_CB_PARAMS *params) +{ + if(params && params->status == BTM_SUCCESS) { + btm_ble_inter_set(true); + } + if (ble_5_hci_cb) { + ble_5_hci_cb(event, params); + } +} + +tBTM_STATUS BTM_BleReadPhy(BD_ADDR bd_addr, UINT8 *tx_phy, UINT8 *rx_phy) +{ + tL2C_LCB *p_lcb = l2cu_find_lcb_by_bd_addr(bd_addr, BT_TRANSPORT_LE); + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + if (!tx_phy || !rx_phy || !p_lcb) { + cb_params.read_phy.status = BTM_ILLEGAL_VALUE; + memcpy(cb_params.read_phy.addr, bd_addr, BD_ADDR_LEN); + + if (ble_5_hci_cb) { + ble_5_hci_cb(BTM_BLE_5_GAP_READ_PHY_COMPLETE_EVT, &cb_params); + } + BTM_TRACE_ERROR("%s, invalid parameters", __func__); + return BTM_ILLEGAL_VALUE; + } + + btsnd_hcic_ble_read_phy(p_lcb->handle); + + return BTM_SUCCESS; +} + +tBTM_STATUS BTM_BleSetPreferDefaultPhy(UINT8 tx_phy_mask, UINT8 rx_phy_mask) +{ + UINT8 all_phys = 0; + tBTM_STATUS status = BTM_SUCCESS; + tHCI_STATUS err = HCI_SUCCESS; + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + if ((err = btsnd_hcic_ble_set_prefered_default_phy(all_phys, tx_phy_mask, rx_phy_mask)) != HCI_SUCCESS) { + BTM_TRACE_ERROR("%s, fail to send the hci command, the error code = %s(0x%x)", + __func__, btm_ble_hci_status_to_str(err), err); + status = BTM_ILLEGAL_VALUE; + } + + cb_params.set_perf_def_phy.status = err; + + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_SET_PREFERED_DEFAULT_PHY_COMPLETE_EVT, &cb_params); + + return status; + +} + +tBTM_STATUS BTM_BleSetPreferPhy(BD_ADDR bd_addr, UINT8 all_phys, UINT8 tx_phy_mask, + UINT8 rx_phy_mask, UINT16 phy_options) +{ + tL2C_LCB *p_lcb = l2cu_find_lcb_by_bd_addr(bd_addr, BT_TRANSPORT_LE); + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + if (!p_lcb) { + cb_params.status = BTM_ILLEGAL_VALUE; + if (ble_5_hci_cb) { + ble_5_hci_cb(BTM_BLE_5_GAP_SET_PREFERED_PHY_COMPLETE_EVT, &cb_params); + } + BTM_TRACE_ERROR("%s, invalid parameters", __func__); + return BTM_ILLEGAL_VALUE; + } + + + + if (!btsnd_hcic_ble_set_phy(p_lcb->handle, all_phys, tx_phy_mask, rx_phy_mask, phy_options)) { + cb_params.status = BTM_ILLEGAL_VALUE; + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_SET_PREFERED_PHY_COMPLETE_EVT, &cb_params); + } + + + return BTM_SUCCESS; +} + +tBTM_STATUS BTM_BleSetExtendedAdvRandaddr(UINT8 instance, BD_ADDR rand_addr) +{ + tBTM_STATUS status = BTM_SUCCESS; + tHCI_STATUS err = HCI_SUCCESS; + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + if (instance >= MAX_BLE_ADV_INSTANCE || rand_addr == NULL) { + status = BTM_ILLEGAL_VALUE; + goto end; + } + + /* + A static address is a 48-bit randomly generated address and shall meet the following requirements: + • The two most significant bits of the address shall be equal to 1 + • All bits of the random part of the address shall not be equal to 1 + • All bits of the random part of the address shall not be equal to 0 + */ + BD_ADDR invalid_rand_addr_a, invalid_rand_addr_b; + memset(invalid_rand_addr_a, 0xff, sizeof(BD_ADDR)); + memset(invalid_rand_addr_b, 0x00, sizeof(BD_ADDR)); + if((rand_addr[0] & BT_STATIC_RAND_ADDR_MASK) == BT_STATIC_RAND_ADDR_MASK) { + invalid_rand_addr_b[0] = invalid_rand_addr_b[0] | BT_STATIC_RAND_ADDR_MASK; + if (memcmp(invalid_rand_addr_a, rand_addr, BD_ADDR_LEN) == 0 + || memcmp(invalid_rand_addr_b, rand_addr, BD_ADDR_LEN) == 0) { + status = BTM_ILLEGAL_VALUE; + goto end; + } + } else if ((rand_addr[0] | BT_NON_RPA_MASK) == BT_NON_RPA_MASK) { + invalid_rand_addr_a[0] = invalid_rand_addr_a[0] & BT_NON_RPA_MASK; + if (memcmp(invalid_rand_addr_a, rand_addr, BD_ADDR_LEN) == 0 + || memcmp(invalid_rand_addr_b, rand_addr, BD_ADDR_LEN) == 0) { + status = BTM_ILLEGAL_VALUE; + goto end; + } + } else { + BTM_TRACE_ERROR("%s invalid random address", __func__); + status = BTM_ILLEGAL_VALUE; + goto end; + } + + // set random address + if((err = btsnd_hcic_ble_set_extend_rand_address(instance, rand_addr)) != HCI_SUCCESS) { + BTM_TRACE_ERROR("%s, fail to send the hci command, the error code = %s(0x%x)", + __func__, btm_ble_hci_status_to_str(err), err); + status = BTM_ILLEGAL_VALUE; + } else { + // set random address success, update address infor + if(extend_adv_cb.inst[instance].configured && extend_adv_cb.inst[instance].connetable) { + BTM_BleSetStaticAddr(rand_addr); + BTM_UpdateAddrInfor(BLE_ADDR_RANDOM, rand_addr); + } + } + +end: + cb_params.status = status; + + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_EXT_ADV_SET_RAND_ADDR_COMPLETE_EVT, &cb_params); + + return status; + +} +tBTM_STATUS BTM_BleSetExtendedAdvParams(UINT8 instance, tBTM_BLE_GAP_EXT_ADV_PARAMS *params) +{ + tBTM_STATUS status = BTM_SUCCESS; + tHCI_STATUS err = HCI_SUCCESS; + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + bool use_rpa_addr = false; + BD_ADDR rand_addr; + + if (instance >= MAX_BLE_ADV_INSTANCE) { + status = BTM_ILLEGAL_VALUE; + BTM_TRACE_ERROR("%s invalid instance %d", __func__, instance); + goto end; + } + + if ((status = btm_ble_ext_adv_params_validate(params)) != BTM_SUCCESS) { + BTM_TRACE_ERROR("%s, invalid extend adv params.", __func__); + } + + if (params->type & BTM_BLE_GAP_SET_EXT_ADV_PROP_CONNECTABLE) { + extend_adv_cb.inst[instance].connetable = true; + } else { + extend_adv_cb.inst[instance].connetable = false; + } + + if (params->type & BTM_BLE_GAP_SET_EXT_ADV_PROP_SCANNABLE) { + extend_adv_cb.inst[instance].scannable = true; + } else { + extend_adv_cb.inst[instance].scannable = false; + } + + if (params->type & BTM_BLE_GAP_SET_EXT_ADV_PROP_LEGACY) { + extend_adv_cb.inst[instance].legacy_pdu = true; + } else { + extend_adv_cb.inst[instance].legacy_pdu = false; + } + +#if (CONTROLLER_RPA_LIST_ENABLE == FALSE) + // if own_addr_type == BLE_ADDR_PUBLIC_ID or BLE_ADDR_RANDOM_ID, + if((params->own_addr_type == BLE_ADDR_PUBLIC_ID || params->own_addr_type == BLE_ADDR_RANDOM_ID) && BTM_GetLocalResolvablePrivateAddr(rand_addr)) { + params->own_addr_type = BLE_ADDR_RANDOM; + use_rpa_addr = true; + } else if(params->own_addr_type == BLE_ADDR_PUBLIC_ID){ + params->own_addr_type = BLE_ADDR_PUBLIC; + } else if (params->own_addr_type == BLE_ADDR_RANDOM_ID) { + params->own_addr_type = BLE_ADDR_RANDOM; + } +#else + btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type = params->own_addr_type; +#endif + + if ((err = btsnd_hcic_ble_set_ext_adv_params(instance, params->type, params->interval_min, params->interval_max, + params->channel_map, params->own_addr_type, params->peer_addr_type, + params->peer_addr, params->filter_policy, params->tx_power, + params->primary_phy, params->max_skip, + params->secondary_phy, params->sid, params->scan_req_notif)) != HCI_SUCCESS) { + BTM_TRACE_ERROR("LE EA SetParams: cmd err=0x%x", err); + status = BTM_ILLEGAL_VALUE; + goto end; + } + + extend_adv_cb.inst[instance].configured = true; + +end: + if(use_rpa_addr) { + // update RPA address + if((err = btsnd_hcic_ble_set_extend_rand_address(instance, rand_addr)) != HCI_SUCCESS) { + BTM_TRACE_ERROR("LE EA SetParams: cmd err=0x%x", err); + status = BTM_ILLEGAL_VALUE; + } else { + // set addr success, update address infor + BTM_UpdateAddrInfor(BLE_ADDR_RANDOM, rand_addr); + } + } + cb_params.status = status; + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_EXT_ADV_SET_PARAMS_COMPLETE_EVT, &cb_params); + + return status; +} + +tBTM_STATUS BTM_BleConfigExtendedAdvDataRaw(BOOLEAN is_scan_rsp, UINT8 instance, UINT16 len, UINT8 *data) +{ + tBTM_STATUS status = BTM_SUCCESS; + tHCI_STATUS err = HCI_SUCCESS; + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + UINT16 rem_len = len; + UINT8 operation = 0; + UINT16 data_offset = 0; + + + if ((status = btm_ble_ext_adv_set_data_validate(instance, len, data)) != BTM_SUCCESS) { + BTM_TRACE_ERROR("%s, invalid extend adv data.", __func__); + goto end; + } + + do { + UINT8 send_data_len = (rem_len > BTM_BLE_EXT_ADV_DATA_LEN_MAX) ? BTM_BLE_EXT_ADV_DATA_LEN_MAX : rem_len; + if (len <= BTM_BLE_EXT_ADV_DATA_LEN_MAX) { + operation = BTM_BLE_ADV_DATA_OP_COMPLETE; + } else { + if (rem_len == len) { + operation = BTM_BLE_ADV_DATA_OP_FIRST_FRAG; + } else if (rem_len <= BTM_BLE_EXT_ADV_DATA_LEN_MAX) { + operation = BTM_BLE_ADV_DATA_OP_LAST_FRAG; + } else { + operation = BTM_BLE_ADV_DATA_OP_INTERMEDIATE_FRAG; + } + } + if (!is_scan_rsp) { + if ((err = btsnd_hcic_ble_set_ext_adv_data(instance, operation, 0, send_data_len, &data[data_offset])) != HCI_SUCCESS) { + BTM_TRACE_ERROR("LE EA SetAdvData: cmd err=0x%x", err); + status = BTM_ILLEGAL_VALUE; + } + } else { + if ((err = btsnd_hcic_ble_set_ext_adv_scan_rsp_data(instance, operation, 0, send_data_len, &data[data_offset])) != HCI_SUCCESS) { + BTM_TRACE_ERROR("LE EA SetScanRspData: cmd err=0x%x", err); + status = BTM_ILLEGAL_VALUE; + } + } + + rem_len -= send_data_len; + data_offset += send_data_len; + } while (rem_len); + +end: + cb_params.status = status; + BTM_ExtBleCallbackTrigger(is_scan_rsp ? BTM_BLE_5_GAP_EXT_SCAN_RSP_DATA_SET_COMPLETE_EVT : BTM_BLE_5_GAP_EXT_ADV_DATA_SET_COMPLETE_EVT, &cb_params); + + return status; +} + +tBTM_STATUS BTM_BleStartExtAdv(BOOLEAN enable, UINT8 num, tBTM_BLE_EXT_ADV *ext_adv) +{ + tBTM_STATUS status = BTM_SUCCESS; + tHCI_STATUS err = HCI_SUCCESS; + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + UINT8 *instance = NULL; + UINT16 *duration = NULL; + UINT8 *max_events = NULL; + + // when enable = true, ext_adv = NULL or num = 0, goto end + if ((!ext_adv || num == 0) && enable) { + status = BTM_ILLEGAL_VALUE; + BTM_TRACE_ERROR("%s invalid parameters", __func__); + goto end; + } + + if (num != 0 && ext_adv != NULL) { + instance = osi_malloc(num); + duration = osi_malloc(num*sizeof(UINT16)); + max_events = osi_malloc(num*sizeof(UINT8)); + + if (!instance || !duration || !max_events) { + status = BTM_NO_RESOURCES; + BTM_TRACE_ERROR("%s invalid parameters", __func__); + goto end; + } + + for (int i = 0; i < num; i++) { + instance[i] = ext_adv[i].instance; + duration[i] = ext_adv[i].duration; + max_events[i] = ext_adv[i].max_events; + } + + if ((err = btsnd_hcic_ble_ext_adv_enable(enable, num, instance, + duration, max_events)) != HCI_SUCCESS) { + BTM_TRACE_ERROR("LE EA En=%d: cmd err=0x%x", enable, err); + status = BTM_ILLEGAL_VALUE; + } + + osi_free(instance); + osi_free(duration); + osi_free(max_events); + } else { + // enable = false, num == 0 or ext_adv = NULL + + if ((err = btsnd_hcic_ble_ext_adv_enable(enable, num, NULL, NULL, NULL)) != HCI_SUCCESS) { + BTM_TRACE_ERROR("LE EA En=%d: cmd err=0x%x", enable, err); + status = BTM_ILLEGAL_VALUE; + } + goto end; + } + + + +end: + + if (!enable && status == BTM_SUCCESS) { + // disable all ext adv + if(num == 0) { + + for (uint8_t i = 0; i < MAX_BLE_ADV_INSTANCE; i++) + { + adv_record[i].invalid = false; + adv_record[i].instance = INVALID_VALUE; + adv_record[i].duration = INVALID_VALUE; + adv_record[i].max_events = INVALID_VALUE; + adv_record[i].retry_count = 0; + } + } else { + for (uint8_t i = 0; i < num; i++) + { + uint8_t index = ext_adv[i].instance; + adv_record[index].invalid = false; + adv_record[index].instance = INVALID_VALUE; + adv_record[index].duration = INVALID_VALUE; + adv_record[index].max_events = INVALID_VALUE; + adv_record[index].retry_count = 0; + } + } + } + // start extend adv success, save the adv information + if(enable && status == BTM_SUCCESS) { + for (uint8_t i = 0; i < num; i++) + { + uint8_t index = ext_adv[i].instance; + adv_record[index].invalid = true; + adv_record[index].instance = ext_adv[i].instance; + adv_record[index].duration = ext_adv[i].duration; + adv_record[index].max_events = ext_adv[i].max_events; + adv_record[index].retry_count = 0; + } + } + + cb_params.status = status; + BTM_ExtBleCallbackTrigger(enable ? BTM_BLE_5_GAP_EXT_ADV_START_COMPLETE_EVT : BTM_BLE_5_GAP_EXT_ADV_STOP_COMPLETE_EVT, &cb_params); + + return status; +} + +tBTM_STATUS BTM_BleStartExtAdvRestart(uint8_t con_handle) +{ + tBTM_BLE_EXT_ADV ext_adv; + uint8_t index = INVALID_VALUE; + for (uint8_t i = 0; i < MAX_BLE_ADV_INSTANCE; i++) + { + if(adv_record[i].ter_con_handle == con_handle) { + index = i; + break; + } + } + + if((index >= MAX_BLE_ADV_INSTANCE) || (!adv_record[index].invalid) || (adv_record[index].retry_count > GATTC_CONNECT_RETRY_COUNT)) { + return BTM_WRONG_MODE; + } + + adv_record[index].retry_count ++; + BTM_TRACE_DEBUG("remote device did not reveive aux connect response, retatrt the extend adv to reconnect, adv handle %d con_handle %d\n", index, con_handle); + ext_adv.instance = adv_record[index].instance; + ext_adv.duration = adv_record[index].duration; + ext_adv.max_events = adv_record[index].max_events; + return BTM_BleStartExtAdv(true, 1, &ext_adv); +} + +tBTM_STATUS BTM_BleExtAdvSetRemove(UINT8 instance) +{ + tBTM_STATUS status = BTM_SUCCESS; + tHCI_STATUS err = HCI_SUCCESS; + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + if (instance >= MAX_BLE_ADV_INSTANCE) { + status = BTM_ILLEGAL_VALUE; + BTM_TRACE_ERROR("%s invalid instance %d", __func__, instance); + goto end; + } + + if ((err = btsnd_hcic_ble_remove_adv_set(instance)) != HCI_SUCCESS) { + BTM_TRACE_ERROR("LE EAS Rm: cmd err=0x%x", err); + status = BTM_ILLEGAL_VALUE; + } else { + extend_adv_cb.inst[instance].configured = false; + extend_adv_cb.inst[instance].legacy_pdu = false; + extend_adv_cb.inst[instance].directed = false; + extend_adv_cb.inst[instance].scannable = false; + extend_adv_cb.inst[instance].connetable = false; + } + +end: + + cb_params.status = status; + + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_EXT_ADV_SET_REMOVE_COMPLETE_EVT, &cb_params); + + return status; +} + +tBTM_STATUS BTM_BleExtAdvSetClear(void) +{ + tBTM_STATUS status = BTM_SUCCESS; + tHCI_STATUS err = HCI_SUCCESS; + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + if ((err = btsnd_hcic_ble_clear_adv_set()) != HCI_SUCCESS) { + BTM_TRACE_ERROR("LE EAS Clr: cmd err=0x%x", err); + status = BTM_ILLEGAL_VALUE; + } else { + for (uint8_t i = 0; i < MAX_BLE_ADV_INSTANCE; i++) { + extend_adv_cb.inst[i].configured = false; + extend_adv_cb.inst[i].legacy_pdu = false; + extend_adv_cb.inst[i].directed = false; + extend_adv_cb.inst[i].scannable = false; + extend_adv_cb.inst[i].connetable = false; + } + } + + cb_params.status = status; + + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_EXT_ADV_SET_CLEAR_COMPLETE_EVT, &cb_params); + + return status; +} + +tBTM_STATUS BTM_BlePeriodicAdvSetParams(UINT8 instance, tBTM_BLE_Periodic_Adv_Params *params) +{ + tBTM_STATUS status = BTM_SUCCESS; + tHCI_STATUS err = HCI_SUCCESS; + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + //ext_adv_flag = true; + + if (instance >= MAX_BLE_ADV_INSTANCE) { + status = BTM_ILLEGAL_VALUE; + BTM_TRACE_ERROR("%s invalid instance %d", __func__, instance); + goto end; + } + + if (!extend_adv_cb.inst[instance].configured || + extend_adv_cb.inst[instance].scannable || + extend_adv_cb.inst[instance].connetable || + extend_adv_cb.inst[instance].legacy_pdu) { + BTM_TRACE_ERROR("%s, instance = %d, Before set the periodic adv parameters, please configure the the \ + extend adv to nonscannable and nonconnectable first, and it shouldn't include the legacy bit.", __func__, instance); + status = BTM_ILLEGAL_VALUE; + goto end; + } + + if ((err= btsnd_hcic_ble_set_periodic_adv_params(instance, params->interval_min, + params->interval_max, params->properties)) != HCI_SUCCESS) { + BTM_TRACE_ERROR("LE PA SetParams: cmd err=0x%x", err); + status = BTM_ILLEGAL_VALUE; + } + +end: + + cb_params.status = status; + + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_PERIODIC_ADV_SET_PARAMS_COMPLETE_EVT, &cb_params); + + return status; +} + +tBTM_STATUS BTM_BlePeriodicAdvCfgDataRaw(UINT8 instance, UINT16 len, UINT8 *data,BOOLEAN only_update_did) +{ + tBTM_STATUS status = BTM_SUCCESS; + tHCI_STATUS err = HCI_SUCCESS; + uint16_t rem_len = len; + UINT8 operation = 0; + UINT16 data_offset = 0; + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + if (only_update_did) + { + len = 0; + data = NULL; + rem_len = 0; + operation = BTM_BLE_ADV_DATA_OP_UNCHANGED_DATA; + } + + if ((status = btm_ble_ext_adv_set_data_validate(instance, len, data)) != BTM_SUCCESS) { + BTM_TRACE_ERROR("%s, invalid extend adv data.", __func__); + goto end; + } + + do { + UINT8 send_data_len = (rem_len > BTM_BLE_PERIODIC_ADV_DATA_LEN_MAX) ? BTM_BLE_PERIODIC_ADV_DATA_LEN_MAX : rem_len; + + if (len <= BTM_BLE_PERIODIC_ADV_DATA_LEN_MAX) { + if (!only_update_did) { + operation = BTM_BLE_ADV_DATA_OP_COMPLETE; + } + } else { + if (rem_len == len) { + operation = BTM_BLE_ADV_DATA_OP_FIRST_FRAG; + } else if (rem_len <= BTM_BLE_PERIODIC_ADV_DATA_LEN_MAX) { + operation = BTM_BLE_ADV_DATA_OP_LAST_FRAG; + } else { + operation = BTM_BLE_ADV_DATA_OP_INTERMEDIATE_FRAG; + } + } + + if ((err = btsnd_hcic_ble_set_periodic_adv_data(instance, operation, send_data_len, &data[data_offset])) != HCI_SUCCESS) { + BTM_TRACE_ERROR("LE PA SetData: cmd err=0x%x", err); + status = BTM_ILLEGAL_VALUE; + } + rem_len -= send_data_len; + data_offset += send_data_len; + } while(rem_len); + +end: + cb_params.status = status; + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_PERIODIC_ADV_DATA_SET_COMPLETE_EVT, &cb_params); + + return status; +} + +tBTM_STATUS BTM_BlePeriodicAdvEnable(UINT8 instance, UINT8 enable) +{ + tBTM_STATUS status = BTM_SUCCESS; + tHCI_STATUS err = HCI_SUCCESS; + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + if (instance >= MAX_BLE_ADV_INSTANCE) { + BTM_TRACE_ERROR("%s, invalid instance %d", __func__, instance); + status = BTM_ILLEGAL_VALUE; + goto end; + } + + if ((err = btsnd_hcic_ble_periodic_adv_enable(enable, instance)) != HCI_SUCCESS) { + BTM_TRACE_ERROR("LE PA En=%d: cmd err=0x%x", enable, err); + status = BTM_ILLEGAL_VALUE; + } + +end: + + cb_params.status = status; + + BTM_ExtBleCallbackTrigger(enable ? BTM_BLE_5_GAP_PERIODIC_ADV_START_COMPLETE_EVT : BTM_BLE_5_GAP_PERIODIC_ADV_STOP_COMPLETE_EVT, &cb_params); + + return status; + +} + +tBTM_STATUS BTM_BlePeriodicAdvCreateSync(tBTM_BLE_Periodic_Sync_Params *params) +{ + //tHCI_STATUS err = HCI_SUCCESS; + tBTM_STATUS status = BTM_SUCCESS; + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + if (!params) { + status = BTM_ILLEGAL_VALUE; + BTM_TRACE_ERROR("%s, the parameter is NULL.", __func__); + goto end; + } + + if ((params->sync_timeout < 0x0a || params->sync_timeout > 0x4000) + || (params->filter_policy > 0x01) + #if (CONFIG_BT_BLE_FEAT_CREATE_SYNC_ENH) + || (params->reports_disabled > 0x01) + || (params->filter_duplicates > 0x01) + #endif + || (params->addr_type > 0x01) || + (params->sid > 0xf) || (params->skip > 0x01F3)) { + status = BTM_ILLEGAL_VALUE; + BTM_TRACE_ERROR("%s, The sync parameters is invalid.", __func__); + goto end; + } + uint8_t option = 0x00; + if (params->filter_policy) { + SET_BIT(option, 0); + } + + #if (CONFIG_BT_BLE_FEAT_CREATE_SYNC_ENH) + if (params->reports_disabled) { + SET_BIT(option, 1); + } + if (params->filter_duplicates) { + SET_BIT(option, 2); + } + #endif + + if (!btsnd_hcic_ble_periodic_adv_create_sync(option, params->sid, params->addr_type, + params->addr, params->sync_timeout, 0)) { + BTM_TRACE_ERROR("LE PA CreateSync cmd failed"); + status = BTM_ILLEGAL_VALUE; + } + +end: + if(status != BTM_SUCCESS) { + cb_params.status = status; + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_PERIODIC_ADV_CREATE_SYNC_COMPLETE_EVT, &cb_params); + } + + return status; +} +void btm_set_phy_callback(UINT8 status) +{ + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + cb_params.status = status; + + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_SET_PREFERED_PHY_COMPLETE_EVT, &cb_params); + +} +void btm_create_sync_callback(UINT8 status) +{ + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + cb_params.status = status; + + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_PERIODIC_ADV_CREATE_SYNC_COMPLETE_EVT, &cb_params); +} + +void btm_read_phy_callback(uint8_t hci_status, uint16_t conn_handle, uint8_t tx_phy, uint8_t rx_phy) +{ + tBTM_STATUS status = BTM_SUCCESS; + tL2C_LCB *p_lcb = l2cu_find_lcb_by_handle(conn_handle); + if(hci_status != HCI_SUCCESS) { + status = BTM_ILLEGAL_VALUE; + BTM_TRACE_ERROR("%s error status %d", __func__, hci_status); + } + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + if(p_lcb) { + memcpy(cb_params.read_phy.addr, p_lcb->remote_bd_addr, BD_ADDR_LEN); + } + cb_params.read_phy.status = status; + cb_params.read_phy.tx_phy = tx_phy; + cb_params.read_phy.rx_phy = rx_phy; + + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_READ_PHY_COMPLETE_EVT, &cb_params); +} + +tBTM_STATUS BTM_BlePeriodicAdvSyncCancel(void) +{ + tHCI_STATUS err = HCI_SUCCESS; + tBTM_STATUS status = BTM_SUCCESS; + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + if ((err = btsnd_hcic_ble_periodic_adv_create_sync_cancel()) != HCI_SUCCESS) { + BTM_TRACE_ERROR("LE PA SyncCancel, cmd err=0x%x", err); + status = BTM_ILLEGAL_VALUE; + } + + cb_params.status = status; + + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_PERIODIC_ADV_SYNC_CANCEL_COMPLETE_EVT, &cb_params); + + return status; +} + +tBTM_STATUS BTM_BlePeriodicAdvSyncTerm(UINT16 sync_handle) +{ + tHCI_STATUS err = HCI_SUCCESS; + tBTM_STATUS status = BTM_SUCCESS; + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + if (( err = btsnd_hcic_ble_periodic_adv_term_sync(sync_handle)) != HCI_SUCCESS) { + BTM_TRACE_ERROR("LE PA SyncTerm: cmd err=0x%x", err); + status = BTM_ILLEGAL_VALUE; + } + + cb_params.status = status; + + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_PERIODIC_ADV_SYNC_TERMINATE_COMPLETE_EVT, &cb_params); + + return status; +} + +tBTM_STATUS BTM_BlePeriodicAdvAddDevToList(tBLE_ADDR_TYPE addr_type, BD_ADDR addr, UINT16 sid) +{ + tHCI_STATUS err = HCI_SUCCESS; + tBTM_STATUS status = BTM_SUCCESS; + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + if (addr_type > BLE_ADDR_TYPE_MAX) { + status = BTM_ILLEGAL_VALUE; + BTM_TRACE_ERROR("%s invalid addr_type %d", __func__, addr_type); + goto end; + } + + if ((err = btsnd_hcic_ble_add_dev_to_periodic_adv_list(addr_type, addr, sid)) != HCI_SUCCESS) { + BTM_TRACE_ERROR("LE PA AddDevToList: cmd err=0x%x", err); + status = BTM_ILLEGAL_VALUE; + } + +end: + cb_params.status = status; + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_PERIODIC_ADV_ADD_DEV_COMPLETE_EVT, &cb_params); + + return status; +} + +tBTM_STATUS BTM_BlePeriodicAdvRemoveDevFromList(tBLE_ADDR_TYPE addr_type, BD_ADDR addr, UINT16 sid) +{ + tHCI_STATUS err = HCI_SUCCESS; + tBTM_STATUS status = BTM_SUCCESS; + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + if (addr_type > BLE_ADDR_TYPE_MAX) { + status = BTM_ILLEGAL_VALUE; + BTM_TRACE_ERROR("%s invalid addr_type %d", __func__, addr_type); + goto end; + } + + if ((err = btsnd_hcic_ble_rm_dev_from_periodic_adv_list(addr_type, addr, sid)) != HCI_SUCCESS) { + BTM_TRACE_ERROR("LE PA RmDevFromList: cmd err=0x%x", err); + status = BTM_ILLEGAL_VALUE; + } + +end: + + cb_params.status = status; + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_PERIODIC_ADV_REMOVE_DEV_COMPLETE_EVT, &cb_params); + return status; +} + +tBTM_STATUS BTM_BlePeriodicAdvClearDev(void) +{ + tHCI_STATUS err = HCI_SUCCESS; + tBTM_STATUS status = BTM_SUCCESS; + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + if ((err = btsnd_hcic_ble_clear_periodic_adv_list()) != HCI_SUCCESS) { + BTM_TRACE_ERROR("LE PA ClrDev: cmd err=0x%x", err); + status = BTM_ILLEGAL_VALUE; + } + + cb_params.status = status; + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_PERIODIC_ADV_CLEAR_DEV_COMPLETE_EVT, &cb_params); + + return status; +} + +tBTM_STATUS BTM_BleSetExtendedScanParams(tBTM_BLE_EXT_SCAN_PARAMS *params) +{ + UINT8 phy_mask = 0; + tHCI_STATUS err = HCI_SUCCESS; + tBTM_STATUS status = BTM_SUCCESS; + tHCI_EXT_SCAN_PARAMS hci_params[2]; + int phy_count = 0; + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + if (!params) { + status = BTM_ILLEGAL_VALUE; + BTM_TRACE_ERROR("%s invalid parameters", __func__); + goto end; + } + + if (params->own_addr_type > BLE_ADDR_TYPE_MAX) { + status = BTM_ILLEGAL_VALUE; + goto end; + } + + if (params->cfg_mask & BTM_BLE_GAP_EXT_SCAN_UNCODE_MASK) { + phy_mask |= 0x01; + memcpy(&hci_params[phy_count], ¶ms->uncoded_cfg, sizeof(tHCI_EXT_SCAN_PARAMS)); + phy_count++; + } + + if (params->cfg_mask & BTM_BLE_GAP_EXT_SCAN_CODE_MASK) { + phy_mask |= 0x04; + memcpy(&hci_params[phy_count], ¶ms->coded_cfg, sizeof(tHCI_EXT_SCAN_PARAMS)); + phy_count++; + } + + if (BTM_BleUpdateOwnType(¶ms->own_addr_type, NULL) != 0 ) { + status = BTM_ILLEGAL_VALUE; + BTM_TRACE_ERROR("LE UpdateOwnType err"); + goto end; + } + + extend_adv_cb.scan_duplicate = params->scan_duplicate; + + if ((err = btsnd_hcic_ble_set_ext_scan_params(params->own_addr_type, params->filter_policy, phy_mask, phy_count, + hci_params)) != HCI_SUCCESS) { + BTM_TRACE_ERROR("LE ES SetParams: cmd err=0x%x", err); + status = BTM_ILLEGAL_VALUE; + } + +end: + + cb_params.status = status; + + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_SET_EXT_SCAN_PARAMS_COMPLETE_EVT, &cb_params); + + return cb_params.status; +} + +tBTM_STATUS BTM_BleExtendedScan(BOOLEAN enable, UINT16 duration, UINT16 period) +{ + tHCI_STATUS err = HCI_SUCCESS; + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + tBTM_STATUS status = BTM_SUCCESS; + + if (extend_adv_cb.scan_duplicate > 0x03) { + status = BTM_ILLEGAL_VALUE; + BTM_TRACE_ERROR("%s invalid scan_duplicate %d", __func__, extend_adv_cb.scan_duplicate); + goto end; + } + + if ((err = btsnd_hcic_ble_ext_scan_enable(enable, extend_adv_cb.scan_duplicate, duration, period)) != HCI_SUCCESS) { + BTM_TRACE_ERROR("LE ES En=%d: cmd err=0x%x", enable, err); + status = BTM_ILLEGAL_VALUE; + } + +end: + + cb_params.status = status; + + BTM_ExtBleCallbackTrigger(enable ? BTM_BLE_5_GAP_EXT_SCAN_START_COMPLETE_EVT : BTM_BLE_5_GAP_EXT_SCAN_STOP_COMPLETE_EVT, &cb_params); + + return status; +} + +void BTM_BleSetPreferExtenedConnParams (BD_ADDR bd_addr, tBTM_EXT_CONN_PARAMS *params) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_or_alloc_dev (bd_addr); + + if (p_dev_rec) { + if (params) { + memcpy(&p_dev_rec->ext_conn_params, params, sizeof(tBTM_EXT_CONN_PARAMS)); + } else { + BTM_TRACE_ERROR("Invalid Extand connection parameters"); + } + } else { + BTM_TRACE_ERROR("Unknown Device, setting rejected"); + } + + return; +} + +void btm_ble_extended_init(void) +{ + +} + +void btm_ble_extended_cleanup(void) +{ + +} + +static tBTM_STATUS btm_ble_ext_adv_params_validate(tBTM_BLE_GAP_EXT_ADV_PARAMS *params) +{ + if (!params) { + return BTM_ILLEGAL_VALUE; + } + + if (params->own_addr_type > BLE_ADDR_TYPE_MAX) { + BTM_TRACE_ERROR("%s, invalid own address type, line %d, addr type %d", __func__, __LINE__, params->own_addr_type); + return BTM_ILLEGAL_VALUE; + } + + if (params->type & BTM_BLE_GAP_SET_EXT_ADV_PROP_LEGACY) { + /* Not allowed for legacy PDUs. */ + if (params->type & BTM_BLE_GAP_SET_EXT_ADV_PROP_INCLUDE_TX_PWR) { + BTM_TRACE_ERROR("%s, The Legacy adv can't include tx power bit, line %d", __func__, __LINE__); + return BTM_ILLEGAL_VALUE; + } + } + + if (!(params->type & BTM_BLE_GAP_SET_EXT_ADV_PROP_LEGACY)) { + /* Not allowed for extended advertising PDUs */ + if ((params->type & BTM_BLE_GAP_SET_EXT_ADV_PROP_CONNECTABLE) && + (params->type & BTM_BLE_GAP_SET_EXT_ADV_PROP_SCANNABLE)) { + BTM_TRACE_ERROR("%s, For the Extend adv, the properties can't be connectable and scannable at the same time, line %d", __func__, __LINE__); + return BTM_ILLEGAL_VALUE; + } + + /* HD directed advertising allowed only for legacy PDUs */ + if (params->type & BTM_BLE_GAP_SET_EXT_ADV_PROP_HD_DIRECTED) { + BTM_TRACE_ERROR("%s, HD directed advertising allowed only for legacy PDUs. line %d", __func__, __LINE__); + return BTM_ILLEGAL_VALUE; + } + } + + return BTM_SUCCESS; +} + +static tBTM_STATUS btm_ble_ext_adv_set_data_validate(UINT8 instance, UINT16 len, UINT8 *data) +{ + if (data == NULL && len > 0) { + BTM_TRACE_ERROR("%s, the extend adv data is NULL. line %d", __func__, __LINE__); + return BTM_ILLEGAL_VALUE; + } + + if (instance >= MAX_BLE_ADV_INSTANCE) { + BTM_TRACE_ERROR("%s, adv instance is %d, Exceeded the maximum. line %d", __func__, instance, __LINE__); + return BTM_ILLEGAL_VALUE; + } + + if (!extend_adv_cb.inst[instance].configured) { + BTM_TRACE_ERROR("%s, The extend adv hasn't configured, please use the set_ext_adv_params API to set the ext adv parameters first. line %d", __func__, __LINE__); + return BTM_ILLEGAL_VALUE; + } + + /* Not allowed with the direted advertising for legacy */ + if (extend_adv_cb.inst[instance].legacy_pdu && extend_adv_cb.inst[instance].directed) { + BTM_TRACE_ERROR("%s, Not allowed with the direted advertising for legacy. line %d", __func__, __LINE__); + return BTM_ILLEGAL_VALUE; + } + + /* Always allowed with legacy PDU but limited to legacy length */ + if (extend_adv_cb.inst[instance].legacy_pdu) { + if (len > 31) { + BTM_TRACE_ERROR("%s, for the legacy adv, the adv data length can't exceed 31. line %d", __func__, __LINE__); + return BTM_ILLEGAL_VALUE; + } + } else { + if (len > controller_get_interface()->ble_get_ext_adv_data_max_len()) { + BTM_TRACE_ERROR("%s, The adv data len(%d) is longer then the controller adv max len(%d)", + __func__, len, controller_get_interface()->ble_get_ext_adv_data_max_len()); + return BTM_ILLEGAL_VALUE; + } + } + + return BTM_SUCCESS; +} + +void btm_ble_update_phy_evt(tBTM_BLE_UPDATE_PHY *params) +{ + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + if (!params) { + BTM_TRACE_ERROR("%s, Invalid params.", __func__); + return; + } + tL2C_LCB *p_lcb = l2cu_find_lcb_by_handle(params->conn_idx); + if(!p_lcb) { + BTM_TRACE_ERROR("%s, Invalid params.", __func__); + return; + } + + cb_params.phy_update.status = params->status; + cb_params.phy_update.tx_phy = params->tx_phy; + cb_params.phy_update.rx_phy = params->rx_phy; + memcpy(cb_params.phy_update.addr, p_lcb->remote_bd_addr, BD_ADDR_LEN); + + // If the user has register the callback function, should callback it to the application. + + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_PHY_UPDATE_COMPLETE_EVT, &cb_params); + + return; +} + +void btm_ble_scan_timeout_evt(void) +{ + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_SCAN_TIMEOUT_EVT, NULL); +} + +void btm_ble_adv_set_terminated_evt(tBTM_BLE_ADV_TERMINAT *params) +{ + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + if (!params) { + BTM_TRACE_ERROR("%s, Invalid params.", __func__); + return; + } + + // adv terminated due to connection, save the adv handle and connection handle + if(params->completed_event == 0x00) { + adv_record[params->adv_handle].ter_con_handle = params->conn_handle; + } else { + adv_record[params->adv_handle].ter_con_handle = INVALID_VALUE; + adv_record[params->adv_handle].invalid = false; + } + + memcpy(&cb_params.adv_term, params, sizeof(tBTM_BLE_ADV_TERMINAT)); + + // If the user has register the callback function, should callback it to the application. + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_ADV_TERMINATED_EVT, &cb_params); + + return; +} + +void btm_ble_ext_adv_report_evt(tBTM_BLE_EXT_ADV_REPORT *params) +{ + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + if (!params) { + BTM_TRACE_ERROR("%s, Invalid params.", __func__); + return; + } + + memcpy(&cb_params.ext_adv_report, params, sizeof(tBTM_BLE_EXT_ADV_REPORT)); + + // If the user has register the callback function, should callback it to the application. + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_EXT_ADV_REPORT_EVT, &cb_params); + + return; + +} + +void btm_ble_scan_req_received_evt(tBTM_BLE_SCAN_REQ_RECEIVED *params) +{ + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + if (!params) { + BTM_TRACE_ERROR("%s, Invalid params.", __func__); + return; + } + + memcpy(&cb_params.scan_req, params, sizeof(tBTM_BLE_SCAN_REQ_RECEIVED)); + + // If the user has register the callback function, should callback it to the application. + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_SCAN_REQ_RECEIVED_EVT, &cb_params); + + return; +} + +void btm_ble_channel_select_algorithm_evt(tBTM_BLE_CHANNEL_SEL_ALG *params) +{ + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + if (!params) { + BTM_TRACE_ERROR("%s, Invalid params.", __func__); + return; + } + + memcpy(&cb_params.channel_sel, params, sizeof(tBTM_BLE_CHANNEL_SEL_ALG)); + + // If the user has register the callback function, should callback it to the application. + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_CHANNEL_SELETE_ALGORITHM_EVT, &cb_params); + + return; +} + +void btm_ble_periodic_adv_report_evt(tBTM_PERIOD_ADV_REPORT *params) +{ + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + if (!params) { + BTM_TRACE_ERROR("%s, Invalid params.", __func__); + return; + } + + memcpy(&cb_params.period_adv_report, params, sizeof(tBTM_PERIOD_ADV_REPORT)); + + // If the user has register the callback function, should callback it to the application. + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_PERIODIC_ADV_REPORT_EVT, &cb_params); + + return; + +} + +void btm_ble_periodic_adv_sync_lost_evt(tBTM_BLE_PERIOD_ADV_SYNC_LOST *params) +{ + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + if (!params) { + BTM_TRACE_ERROR("%s, Invalid params.", __func__); + return; + } + + memcpy(&cb_params.sync_lost, params, sizeof(tBTM_BLE_PERIOD_ADV_SYNC_LOST)); + + // If the user has register the callback function, should callback it to the application. + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_PERIODIC_ADV_SYNC_LOST_EVT, &cb_params); + + return; + +} + +void btm_ble_periodic_adv_sync_establish_evt(tBTM_BLE_PERIOD_ADV_SYNC_ESTAB *params) +{ + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + if (!params) { + BTM_TRACE_ERROR("%s, Invalid params.", __func__); + return; + } + + memcpy(&cb_params.sync_estab, params, sizeof(tBTM_BLE_PERIOD_ADV_SYNC_ESTAB)); + + // If the user has register the callback function, should callback it to the application. + BTM_ExtBleCallbackTrigger(BTM_BLE_5_GAP_PERIODIC_ADV_SYNC_ESTAB_EVT, &cb_params); + + return; + +} + +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) + +#if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) +void btm_ble_periodic_adv_sync_trans_complete(UINT16 op_code, UINT8 hci_status, UINT16 conn_handle) +{ + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + UINT8 evt = BTM_BLE_5_GAP_UNKNOWN_EVT; + tL2C_LCB *p_lcb = l2cu_find_lcb_by_handle(conn_handle); + + switch (op_code) { + case HCI_BLE_PERIOD_ADV_SYNC_TRANS: + evt = BTM_BLE_GAP_PERIODIC_ADV_SYNC_TRANS_COMPLETE_EVT; + break; + case HCI_BLE_PERIOD_ADV_SET_INFO_TRANS: + evt = BTM_BLE_GAP_PERIODIC_ADV_SET_INFO_TRANS_COMPLETE_EVT; + break; + case HCI_BLE_SET_PAST_PARAMS: + evt = BTM_BLE_GAP_SET_PAST_PARAMS_COMPLETE_EVT; + break; + default: + return; + } + + cb_params.per_adv_sync_trans.status = BTM_SUCCESS; + if(hci_status != HCI_SUCCESS) { + cb_params.per_adv_sync_trans.status = BTM_ILLEGAL_VALUE; + BTM_TRACE_ERROR("%s error status %d", __func__, hci_status); + } + + if(p_lcb) { + memcpy(cb_params.per_adv_sync_trans.addr, p_lcb->remote_bd_addr, BD_ADDR_LEN); + } + + BTM_ExtBleCallbackTrigger(evt, &cb_params); +} + +void BTM_BlePeriodicAdvRecvEnable(UINT16 sync_handle, UINT8 enable) +{ + tHCI_STATUS err = HCI_SUCCESS; + tBTM_STATUS status = BTM_SUCCESS; + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + if ((err = btsnd_hcic_ble_set_periodic_adv_recv_enable(sync_handle, enable)) != HCI_SUCCESS) { + BTM_TRACE_ERROR("%s cmd err=0x%x", __func__, err); + status = BTM_ILLEGAL_VALUE; + } + + cb_params.status = status; + BTM_ExtBleCallbackTrigger(BTM_BLE_GAP_PERIODIC_ADV_RECV_ENABLE_COMPLETE_EVT, &cb_params); +} + +void BTM_BlePeriodicAdvSyncTrans(BD_ADDR bd_addr, UINT16 service_data, UINT16 sync_handle) +{ + tBTM_STATUS status = BTM_SUCCESS; + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + tL2C_LCB *p_lcb = l2cu_find_lcb_by_bd_addr(bd_addr, BT_TRANSPORT_LE); + if (!p_lcb) { + BTM_TRACE_ERROR("%s, invalid parameters", __func__); + status = BTM_ILLEGAL_VALUE; + } + + if (status != BTM_SUCCESS) { + cb_params.per_adv_sync_trans.status = status; + memcpy(cb_params.per_adv_sync_trans.addr, bd_addr, sizeof(BD_ADDR)); + BTM_ExtBleCallbackTrigger(BTM_BLE_GAP_PERIODIC_ADV_SYNC_TRANS_COMPLETE_EVT, &cb_params); + return; + } + + btsnd_hcic_ble_periodic_adv_sync_trans(p_lcb->handle, service_data, sync_handle); +} + +void BTM_BlePeriodicAdvSetInfoTrans(BD_ADDR bd_addr, UINT16 service_data, UINT8 adv_handle) +{ + tBTM_STATUS status = BTM_SUCCESS; + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + tL2C_LCB *p_lcb = l2cu_find_lcb_by_bd_addr(bd_addr, BT_TRANSPORT_LE); + + if (!p_lcb) { + BTM_TRACE_ERROR("%s, invalid parameters", __func__); + status = BTM_ILLEGAL_VALUE; + } + + if (status != BTM_SUCCESS) { + cb_params.per_adv_sync_trans.status = status; + memcpy(cb_params.per_adv_sync_trans.addr, bd_addr, sizeof(BD_ADDR)); + BTM_ExtBleCallbackTrigger(BTM_BLE_GAP_PERIODIC_ADV_SET_INFO_TRANS_COMPLETE_EVT, &cb_params); + return; + } + + btsnd_hcic_ble_periodic_adv_set_info_trans(p_lcb->handle, service_data, adv_handle); +} + +void BTM_BleSetPeriodicAdvSyncTransParams(BD_ADDR bd_addr, UINT8 mode, UINT16 skip, UINT16 sync_timeout, UINT8 cte_type) +{ + tBTM_STATUS status = BTM_SUCCESS; + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + // Set default past params + if (bdaddr_is_empty((bt_bdaddr_t *)bd_addr)) { + tHCI_STATUS err = HCI_SUCCESS; + if ((err = btsnd_hcic_ble_set_default_periodic_adv_sync_trans_params(mode, skip, sync_timeout, cte_type)) != HCI_SUCCESS) { + BTM_TRACE_ERROR("%s cmd err=0x%x", __func__, err); + status = BTM_ILLEGAL_VALUE; + } + + cb_params.set_past_params.status = status; + memset(cb_params.set_past_params.addr, 0, sizeof(BD_ADDR)); + BTM_ExtBleCallbackTrigger(BTM_BLE_GAP_SET_PAST_PARAMS_COMPLETE_EVT, &cb_params); + return; + } + + tL2C_LCB *p_lcb = l2cu_find_lcb_by_bd_addr(bd_addr, BT_TRANSPORT_LE); + if (!p_lcb) { + BTM_TRACE_ERROR("%s, invalid parameters", __func__); + status = BTM_ILLEGAL_VALUE; + } + + if (status != BTM_SUCCESS) { + cb_params.set_past_params.status = status; + memcpy(cb_params.set_past_params.addr, bd_addr, sizeof(BD_ADDR)); + BTM_ExtBleCallbackTrigger(BTM_BLE_GAP_SET_PAST_PARAMS_COMPLETE_EVT, &cb_params); + return; + } + + btsnd_hcic_ble_set_periodic_adv_sync_trans_params(p_lcb->handle, mode, skip, sync_timeout, cte_type); +} + +void btm_ble_periodic_adv_sync_trans_recv_evt(tBTM_BLE_PERIOD_ADV_SYNC_TRANS_RECV *params) +{ + tBTM_BLE_5_GAP_CB_PARAMS cb_params = {0}; + + if (!params) { + BTM_TRACE_ERROR("%s, Invalid params.", __func__); + return; + } + + memcpy(&cb_params.past_recv, params, sizeof(tBTM_BLE_PERIOD_ADV_SYNC_TRANS_RECV)); + + BTM_ExtBleCallbackTrigger(BTM_BLE_GAP_PERIODIC_ADV_SYNC_TRANS_RECV_EVT, &cb_params); +} +#endif // #if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) diff --git a/lib/bt/host/bluedroid/stack/btm/btm_ble_addr.c b/lib/bt/host/bluedroid/stack/btm/btm_ble_addr.c new file mode 100644 index 00000000..d7ed2095 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/btm/btm_ble_addr.c @@ -0,0 +1,608 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains functions for BLE address management. + * + ******************************************************************************/ + +#include <string.h> + +#include "stack/bt_types.h" +#include "stack/hcimsgs.h" +#include "stack/btu.h" +#include "btm_int.h" +#include "stack/gap_api.h" +#include "device/controller.h" + +#if (defined BLE_INCLUDED && BLE_INCLUDED == TRUE) +#include "btm_ble_int.h" +#include "stack/smp_api.h" + + +/******************************************************************************* +** +** 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; + BTM_TRACE_EVENT ("btm_gen_resolve_paddr_cmpl"); + + if (p) { + /* set hash to be LSB of rpAddress */ + 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]; + + /* set it to controller */ + btm_ble_set_random_addr(p_cb->private_addr); + + p_cb->exist_addr_bit |= BTM_BLE_GAP_ADDR_BIT_RESOLVABLE; + memcpy(p_cb->resolvale_addr, p_cb->private_addr, BD_ADDR_LEN); + if (p_cb->set_local_privacy_cback){ + (*p_cb->set_local_privacy_cback)(BTM_SET_PRIVACY_SUCCESS); + p_cb->set_local_privacy_cback = NULL; + } + + /* start a periodical timer to refresh random addr */ + btu_stop_timer_oneshot(&p_cb->raddr_timer_ent); +#if (BTM_BLE_CONFORMANCE_TESTING == TRUE) + btu_start_timer_oneshot(&p_cb->raddr_timer_ent, BTU_TTYPE_BLE_RANDOM_ADDR, + btm_cb.ble_ctr_cb.rpa_tout); +#else + btu_start_timer_oneshot(&p_cb->raddr_timer_ent, BTU_TTYPE_BLE_RANDOM_ADDR, + BTM_BLE_PRIVATE_ADDR_INT); +#endif + } else { + /* random address set failure */ + BTM_TRACE_DEBUG("set random address failed"); + if (p_cb->set_local_privacy_cback){ + (*p_cb->set_local_privacy_cback)(BTM_SET_PRIVACY_FAIL); + p_cb->set_local_privacy_cback = NULL; + } + } +} +/******************************************************************************* +** +** 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 +** +*******************************************************************************/ +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_EVENT ("btm_gen_resolve_paddr_low"); + if (p) { + p->param_buf[2] &= (~BLE_RESOLVE_ADDR_MASK); + p->param_buf[2] |= BLE_RESOLVE_ADDR_MSB; + + 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]; + + /* 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 *p_cmd_cplt_cback) +{ + BTM_TRACE_EVENT ("btm_gen_resolvable_private_addr"); + /* generate 3B rand as BD LSB, SRK with it, get BD MSB */ + if (!btsnd_hcic_ble_rand((void *)p_cmd_cplt_cback)) { + 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; + tBTM_BLE_ADDR_CBACK *p_cback = p_cb->p_generate_cback; + void *p_data = p_cb->p; + UINT8 *pp; + BD_ADDR static_random; + + BTM_TRACE_EVENT ("btm_gen_non_resolve_paddr_cmpl"); + + p_cb->p_generate_cback = NULL; + if (p) { + + pp = p->param_buf; + STREAM_TO_BDADDR(static_random, pp); + /* mask off the 2 MSB */ + static_random[0] &= BLE_STATIC_PRIVATE_MSB_MASK; + + /* report complete */ + if (p_cback) { + (* p_cback)(static_random, p_data); + } + } else { + BTM_TRACE_DEBUG("btm_gen_non_resolvable_private_addr failed"); + if (p_cback) { + (* p_cback)(NULL, p_data); + } + } +} +/******************************************************************************* +** +** 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 (tBTM_BLE_ADDR_CBACK *p_cback, void *p) +{ + tBTM_LE_RANDOM_CB *p_mgnt_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + + BTM_TRACE_EVENT ("btm_gen_non_resolvable_private_addr"); + + if (p_mgnt_cb->p_generate_cback != NULL) { + return; + } + + p_mgnt_cb->p_generate_cback = p_cback; + p_mgnt_cb->p = p; + if (!btsnd_hcic_ble_rand((void *)btm_gen_non_resolve_paddr_cmpl)) { + btm_gen_non_resolve_paddr_cmpl(NULL); + } + +} + +/******************************************************************************* +** Utility functions for Random address resolving +*******************************************************************************/ +/******************************************************************************* +** +** Function btm_ble_resolve_address_cmpl +** +** Description This function sends the random address resolving complete +** callback. +** +** Returns None. +** +*******************************************************************************/ +#if SMP_INCLUDED == TRUE +static void btm_ble_resolve_address_cmpl(void) +{ + tBTM_LE_RANDOM_CB *p_mgnt_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + + BTM_TRACE_EVENT ("btm_ble_resolve_address_cmpl p_mgnt_cb->p_dev_rec = 0x%08x", (uint32_t)p_mgnt_cb->p_dev_rec); + + p_mgnt_cb->busy = FALSE; + + (* p_mgnt_cb->p_resolve_cback)(p_mgnt_cb->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_EVENT ("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) { + if (!memcmp(p->param_buf, &comp[0], 3)) { + /* match is found */ + BTM_TRACE_EVENT ("match is found"); + btm_ble_resolve_address_cmpl(); + return TRUE; + } + } + + return FALSE; +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function btm_ble_init_pseudo_addr +** +** Description This function is used to initialize pseudo address. +** If pseudo address is not available, use dummy address +** +** Returns TRUE is updated; FALSE otherwise. +** +*******************************************************************************/ +BOOLEAN btm_ble_init_pseudo_addr (tBTM_SEC_DEV_REC *p_dev_rec, BD_ADDR new_pseudo_addr) +{ +#if (SMP_INCLUDED == TRUE) + BD_ADDR dummy_bda = {0}; + + if (memcmp(p_dev_rec->ble.pseudo_addr, dummy_bda, BD_ADDR_LEN) == 0) { + memcpy(p_dev_rec->ble.pseudo_addr, new_pseudo_addr, BD_ADDR_LEN); + return TRUE; + } +#endif ///SMP_INCLUDED == TRUE + return FALSE; +} + +/******************************************************************************* +** +** Function btm_ble_addr_resolvable +** +** Description This function checks if a RPA is resolvable by the device key. +** +** Returns TRUE is resolvable; FALSE otherwise. +** +*******************************************************************************/ +BOOLEAN btm_ble_addr_resolvable (BD_ADDR rpa, tBTM_SEC_DEV_REC *p_dev_rec) +{ + BOOLEAN rt = FALSE; +#if (SMP_INCLUDED == TRUE) + if (!BTM_BLE_IS_RESOLVE_BDA(rpa)) { + return rt; + } + + UINT8 rand[3]; + tSMP_ENC output; + if ((p_dev_rec->device_type & BT_DEVICE_TYPE_BLE) && + (p_dev_rec->ble.key_type & BTM_LE_KEY_PID)) { + BTM_TRACE_DEBUG("%s try to resolve", __func__); + /* use the 3 MSB of bd address as prand */ + rand[0] = rpa[2]; + rand[1] = rpa[1]; + rand[2] = rpa[0]; + + /* 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); + + rand[0] = rpa[5]; + rand[1] = rpa[4]; + rand[2] = rpa[3]; + + if (!memcmp(output.param_buf, &rand[0], 3)) { + btm_ble_init_pseudo_addr (p_dev_rec, rpa); + rt = TRUE; + } + } +#endif ///SMP_INCLUDED == TRUE + return rt; +} + +#if (BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE) +/******************************************************************************* +** +** 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(tBTM_SEC_DEV_REC *p_dev_rec) +{ + /* use the 3 MSB of bd address as prand */ + + tBTM_LE_RANDOM_CB *p_mgnt_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + UINT8 rand[3]; + 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_EVENT("%s p_dev_rec = 0x%08x", __func__, (uint32_t)p_dev_rec); + + { + tSMP_ENC output; + + BTM_TRACE_DEBUG("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; + } + } +} +#endif ///BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** 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) +{ +#if (SMP_INCLUDED == TRUE) + tBTM_LE_RANDOM_CB *p_mgnt_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + list_node_t *p_node = NULL; + tBTM_SEC_DEV_REC *p_dev_rec = NULL; + + BTM_TRACE_EVENT ("btm_ble_resolve_random_addr"); + if ( !p_mgnt_cb->busy) { + p_mgnt_cb->p = p; + p_mgnt_cb->busy = TRUE; + p_mgnt_cb->p_dev_rec = NULL; + 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 */ + for (p_node = list_begin(btm_cb.p_sec_dev_rec_list); p_node; p_node = list_next(p_node)) { + p_dev_rec = list_node(p_node); + p_mgnt_cb->p_dev_rec = p_dev_rec; + if (btm_ble_match_random_bda(p_dev_rec)) { + break; + } + p_mgnt_cb->p_dev_rec = NULL; + } + btm_ble_resolve_address_cmpl(); + } else { + (*p_cback)(NULL, p); + } +#endif + +} + + +/******************************************************************************* +** address mapping between pseudo address and real connection address +*******************************************************************************/ +/******************************************************************************* +** +** Function btm_find_dev_by_identity_addr +** +** Description find the security record whose LE static address is matching +** +*******************************************************************************/ +tBTM_SEC_DEV_REC *btm_find_dev_by_identity_addr(BD_ADDR bd_addr, UINT8 addr_type) +{ +#if BLE_PRIVACY_SPT == TRUE + tBTM_SEC_DEV_REC *p_dev_rec = NULL; + list_node_t *p_node = NULL; + tSecDevContext context; + context.type = SEC_DEV_ID_ADDR; + context.context.p_bd_addr = bd_addr; + context.free_check = FALSE; + p_node = list_foreach(btm_cb.p_sec_dev_rec_list, btm_find_sec_dev_in_list, &context); + if (p_node) { + p_dev_rec = list_node(p_node); + if ((p_dev_rec->ble.static_addr_type & (~BLE_ADDR_TYPE_ID_BIT)) != + (addr_type & (~BLE_ADDR_TYPE_ID_BIT))) { + BTM_TRACE_WARNING("%s find pseudo->random match with diff addr type: %d vs %d", + __func__, p_dev_rec->ble.static_addr_type, addr_type); + } + } + return p_dev_rec; +#endif + return NULL; +} + +/******************************************************************************* +** +** Function btm_identity_addr_to_random_pseudo +** +** Description This function map a static BD address to a pseudo random address +** in security database. +** +*******************************************************************************/ +BOOLEAN btm_identity_addr_to_random_pseudo(BD_ADDR bd_addr, UINT8 *p_addr_type, BOOLEAN refresh) +{ +#if BLE_PRIVACY_SPT == TRUE + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev_by_identity_addr(bd_addr, *p_addr_type); + + BTM_TRACE_EVENT ("%s", __func__); + /* evt reported on static address, map static address to random pseudo */ + if (p_dev_rec != NULL) { + /* if RPA offloading is supported, or 4.2 controller, do RPA refresh */ + if (refresh && controller_get_interface()->get_ble_resolving_list_max_size() != 0) { + btm_ble_read_resolving_list_entry(p_dev_rec); + } + + /* assign the original address to be the current report address */ + if (!btm_ble_init_pseudo_addr (p_dev_rec, bd_addr)) { + memcpy(bd_addr, p_dev_rec->ble.pseudo_addr, BD_ADDR_LEN); + } + + *p_addr_type = p_dev_rec->ble.ble_addr_type; + return TRUE; + } +#endif + return FALSE; +} + +/******************************************************************************* +** +** Function btm_random_pseudo_to_identity_addr +** +** Description This function map a random pseudo address to a public address +** random_pseudo is input and output parameter +** +*******************************************************************************/ +BOOLEAN btm_random_pseudo_to_identity_addr(BD_ADDR random_pseudo, UINT8 *p_static_addr_type) +{ +#if BLE_PRIVACY_SPT == TRUE + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (random_pseudo); + + if (p_dev_rec != NULL) { + if (p_dev_rec->ble.in_controller_list & BTM_RESOLVING_LIST_BIT) { + * p_static_addr_type = p_dev_rec->ble.static_addr_type; + memcpy(random_pseudo, p_dev_rec->ble.static_addr, BD_ADDR_LEN); + if (controller_get_interface()->supports_ble_privacy() && p_dev_rec->ble.ble_addr_type != BLE_ADDR_PUBLIC) { + *p_static_addr_type |= BLE_ADDR_TYPE_ID_BIT; + } + return TRUE; + } + } +#endif + return FALSE; +} + +/******************************************************************************* +** +** Function btm_ble_refresh_peer_resolvable_private_addr +** +** Description This function refresh the currently used resolvable remote private address into security +** database and set active connection address. +** +*******************************************************************************/ +void btm_ble_refresh_peer_resolvable_private_addr(BD_ADDR pseudo_bda, BD_ADDR rpa, + UINT8 rra_type) +{ +#if BLE_PRIVACY_SPT == TRUE + UINT8 rra_dummy = FALSE; + BD_ADDR dummy_bda = {0}; + + if (memcmp(dummy_bda, rpa, BD_ADDR_LEN) == 0) { + rra_dummy = TRUE; + } + + /* update security record here, in adv event or connection complete process */ + tBTM_SEC_DEV_REC *p_sec_rec = btm_find_dev(pseudo_bda); + if (p_sec_rec != NULL) { + memcpy(p_sec_rec->ble.cur_rand_addr, rpa, BD_ADDR_LEN); + + /* unknown, if dummy address, set to static */ + if (rra_type == BTM_BLE_ADDR_PSEUDO) { + p_sec_rec->ble.active_addr_type = rra_dummy ? BTM_BLE_ADDR_STATIC : BTM_BLE_ADDR_RRA; + } else { + p_sec_rec->ble.active_addr_type = rra_type; + } + } else { + BTM_TRACE_ERROR("No matching known device in record"); + return; + } + + BTM_TRACE_DEBUG("%s: active_addr_type: %d ", + __func__, p_sec_rec->ble.active_addr_type); + + /* connection refresh remote address */ + tACL_CONN *p_acl = btm_bda_to_acl(p_sec_rec->bd_addr, BT_TRANSPORT_LE); + if (p_acl == NULL) { + p_acl = btm_bda_to_acl(p_sec_rec->ble.pseudo_addr, BT_TRANSPORT_LE); + } + + if (p_acl != NULL) { + if (rra_type == BTM_BLE_ADDR_PSEUDO) { + /* use static address, resolvable_private_addr is empty */ + if (rra_dummy) { + p_acl->active_remote_addr_type = p_sec_rec->ble.static_addr_type; + memcpy(p_acl->active_remote_addr, p_sec_rec->ble.static_addr, BD_ADDR_LEN); + } else { + p_acl->active_remote_addr_type = BLE_ADDR_RANDOM; + memcpy(p_acl->active_remote_addr, rpa, BD_ADDR_LEN); + } + } else { + p_acl->active_remote_addr_type = rra_type; + memcpy(p_acl->active_remote_addr, rpa, BD_ADDR_LEN); + } + + BTM_TRACE_DEBUG("p_acl->active_remote_addr_type: %d ", p_acl->active_remote_addr_type); + BTM_TRACE_DEBUG("%s conn_addr: %02x:%02x:%02x:%02x:%02x:%02x", + __func__, p_acl->active_remote_addr[0], p_acl->active_remote_addr[1], + p_acl->active_remote_addr[2], p_acl->active_remote_addr[3], + p_acl->active_remote_addr[4], p_acl->active_remote_addr[5]); + } +#endif +} + +/******************************************************************************* +** +** Function btm_ble_refresh_local_resolvable_private_addr +** +** Description This function refresh the currently used resolvable private address for the +** active link to the remote device +** +*******************************************************************************/ +void btm_ble_refresh_local_resolvable_private_addr(BD_ADDR pseudo_addr, + BD_ADDR local_rpa) +{ +#if BLE_PRIVACY_SPT == TRUE + tACL_CONN *p = btm_bda_to_acl(pseudo_addr, BT_TRANSPORT_LE); + BD_ADDR dummy_bda = {0}; + + if (p != NULL) { + if (btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type >= BLE_ADDR_RANDOM) { + p->conn_addr_type = BLE_ADDR_RANDOM; + if (memcmp(local_rpa, dummy_bda, BD_ADDR_LEN)) { + memcpy(p->conn_addr, local_rpa, BD_ADDR_LEN); + } else { + memcpy(p->conn_addr, btm_cb.ble_ctr_cb.addr_mgnt_cb.private_addr, BD_ADDR_LEN); + } + } else { + p->conn_addr_type = BLE_ADDR_PUBLIC; + memcpy(p->conn_addr, &controller_get_interface()->get_address()->address, BD_ADDR_LEN); + } + } +#endif +} +#endif diff --git a/lib/bt/host/bluedroid/stack/btm/btm_ble_adv_filter.c b/lib/bt/host/bluedroid/stack/btm/btm_ble_adv_filter.c new file mode 100644 index 00000000..c0ae0aaa --- /dev/null +++ b/lib/bt/host/bluedroid/stack/btm/btm_ble_adv_filter.c @@ -0,0 +1,1306 @@ +/****************************************************************************** + * + * Copyright (C) 2014 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +//#define LOG_TAG "bt_btm_ble" + +#include <string.h> +#include "common/bt_target.h" + +#if (BLE_INCLUDED == TRUE) +#include "stack/bt_types.h" +#include "stack/hcimsgs.h" +#include "stack/btu.h" +#include "btm_int.h" +#include "osi/allocator.h" +#include "stack/hcidefs.h" +#include "stack/btm_ble_api.h" +#include "device/controller.h" + +#define BTM_BLE_ADV_FILT_META_HDR_LENGTH 3 +#define BTM_BLE_ADV_FILT_FEAT_SELN_LEN 13 +#define BTM_BLE_ADV_FILT_TRACK_NUM 2 + +#define BTM_BLE_PF_SELECT_NONE 0 + +/* BLE meta vsc header: 1 bytes of sub_code, 1 byte of PCF action */ +#define BTM_BLE_META_HDR_LENGTH 3 +#define BTM_BLE_PF_FEAT_SEL_LEN 18 +#define BTM_BLE_PCF_ENABLE_LEN 2 + +#define BTM_BLE_META_ADDR_LEN 7 +#define BTM_BLE_META_UUID_LEN 40 + +#define BTM_BLE_PF_BIT_TO_MASK(x) (UINT16)(1 << (x)) + + +#if BTM_DYNAMIC_MEMORY == FALSE +tBTM_BLE_ADV_FILTER_CB btm_ble_adv_filt_cb; +tBTM_BLE_VSC_CB cmn_ble_adv_vsc_cb; +#else +tBTM_BLE_ADV_FILTER_CB *btm_ble_adv_filt_cb_ptr; +tBTM_BLE_VSC_CB *cmn_ble_adv_vsc_cb_ptr; +#define btm_ble_adv_filt_cb (*btm_ble_adv_filt_cb_ptr) +#define cmn_ble_adv_vsc_cb (*cmn_ble_adv_vsc_cb_ptr) +#endif + +static const BD_ADDR na_bda = {0}; + +static UINT8 btm_ble_cs_update_pf_counter(tBTM_BLE_SCAN_COND_OP action, + UINT8 cond_type, tBLE_BD_ADDR *p_bd_addr, UINT8 num_available); + +#define BTM_BLE_SET_SCAN_PF_OPCODE(x, y) (((x)<<4)|y) +#define BTM_BLE_GET_SCAN_PF_SUBCODE(x) ((x) >> 4) +#define BTM_BLE_GET_SCAN_PF_ACTION(x) ((x) & 0x0f) +#define BTM_BLE_INVALID_COUNTER 0xff + + +/* length of each multi adv sub command */ +#define BTM_BLE_ADV_FILTER_ENB_LEN 3 + +/* length of each batch scan command */ +#define BTM_BLE_ADV_FILTER_CLEAR_LEN 3 +#define BTM_BLE_ADV_FILTER_LEN 2 + +#define BTM_BLE_ADV_FILT_CB_EVT_MASK 0xF0 +#define BTM_BLE_ADV_FILT_SUBCODE_MASK 0x0F + +/******************************************************************************* +** +** Function btm_ble_obtain_vsc_details +** +** Description This function obtains the VSC details +** +** Parameters +** +** Returns status +** +*******************************************************************************/ +tBTM_STATUS btm_ble_obtain_vsc_details(void) +{ + tBTM_STATUS st = BTM_SUCCESS; + +#if BLE_VND_INCLUDED == TRUE + BTM_BleGetVendorCapabilities(&cmn_ble_adv_vsc_cb); + if (0 == cmn_ble_adv_vsc_cb.max_filter) { + st = BTM_MODE_UNSUPPORTED; + return st; + } +#else + cmn_ble_adv_vsc_cb.max_filter = BTM_BLE_MAX_FILTER_COUNTER; +#endif + return st; +} + +/******************************************************************************* +** +** Function btm_ble_advfilt_enq_op_q +** +** Description enqueue an adv filter operation in q to check command complete +** status +** +** Returns void +** +*******************************************************************************/ +void btm_ble_advfilt_enq_op_q(UINT8 action, UINT8 ocf, tBTM_BLE_FILT_CB_EVT cb_evt, + tBTM_BLE_REF_VALUE ref, tBTM_BLE_PF_CFG_CBACK *p_cmpl_cback, + tBTM_BLE_PF_PARAM_CBACK *p_filt_param_cback) +{ + btm_ble_adv_filt_cb.op_q.action_ocf[btm_ble_adv_filt_cb.op_q.next_idx] = (action | (ocf << 4)); + btm_ble_adv_filt_cb.op_q.ref_value[btm_ble_adv_filt_cb.op_q.next_idx] = ref; + btm_ble_adv_filt_cb.op_q.cb_evt[btm_ble_adv_filt_cb.op_q.next_idx] = cb_evt; + btm_ble_adv_filt_cb.op_q.p_scan_cfg_cback[btm_ble_adv_filt_cb.op_q.next_idx] = p_cmpl_cback; + btm_ble_adv_filt_cb.op_q.p_filt_param_cback[btm_ble_adv_filt_cb.op_q.next_idx] + = p_filt_param_cback; + BTM_TRACE_DEBUG("btm_ble_advfilt_enq_op_q: act_ocf:%d, action:%d, ocf:%d,cb_evt;%d, cback:%p", + btm_ble_adv_filt_cb.op_q.action_ocf[btm_ble_adv_filt_cb.op_q.next_idx], action, + ocf, cb_evt, p_cmpl_cback); + btm_ble_adv_filt_cb.op_q.next_idx = (btm_ble_adv_filt_cb.op_q.next_idx + 1) + % BTM_BLE_PF_TYPE_MAX; +} + +/******************************************************************************* +** +** Function btm_ble_advfilt_deq_op_q +** +** Description dequeue an adv filter operation from q when command complete +** is received +** +** Returns void +** +*******************************************************************************/ +void btm_ble_advfilt_deq_op_q(UINT8 *p_action, UINT8 *p_ocf, tBTM_BLE_FILT_CB_EVT *p_cb_evt, + tBTM_BLE_REF_VALUE *p_ref, tBTM_BLE_PF_CFG_CBACK **p_cmpl_cback, + tBTM_BLE_PF_PARAM_CBACK **p_filt_param_cback) +{ + *p_ocf = (btm_ble_adv_filt_cb.op_q.action_ocf[btm_ble_adv_filt_cb.op_q.pending_idx] >> 4); + *p_action = (btm_ble_adv_filt_cb.op_q.action_ocf[btm_ble_adv_filt_cb.op_q.pending_idx] + & BTM_BLE_ADV_FILT_SUBCODE_MASK); + *p_ref = btm_ble_adv_filt_cb.op_q.ref_value[btm_ble_adv_filt_cb.op_q.pending_idx]; + *p_cb_evt = btm_ble_adv_filt_cb.op_q.cb_evt[btm_ble_adv_filt_cb.op_q.pending_idx]; + *p_cmpl_cback = btm_ble_adv_filt_cb.op_q.p_scan_cfg_cback[btm_ble_adv_filt_cb.op_q.pending_idx]; + *p_filt_param_cback = + btm_ble_adv_filt_cb.op_q.p_filt_param_cback[btm_ble_adv_filt_cb.op_q.pending_idx]; + + btm_ble_adv_filt_cb.op_q.pending_idx = (btm_ble_adv_filt_cb.op_q.pending_idx + 1) + % BTM_BLE_PF_TYPE_MAX; + BTM_TRACE_DEBUG("btm_ble_advfilt_deq_op_q: ocf:%d, action:%d, ref_value:%d, cb_evt:%x", + *p_ocf, *p_action, *p_ref, *p_cb_evt); +} + +/******************************************************************************* +** +** Function btm_ble_condtype_to_ocf +** +** Description Convert cond_type to OCF +** +** Returns Returns ocf value +** +*******************************************************************************/ +UINT8 btm_ble_condtype_to_ocf(UINT8 cond_type) +{ + UINT8 ocf = 0; + + switch (cond_type) { + case BTM_BLE_PF_ADDR_FILTER: + ocf = BTM_BLE_META_PF_ADDR; + break; + case BTM_BLE_PF_SRVC_UUID: + ocf = BTM_BLE_META_PF_UUID; + break; + case BTM_BLE_PF_SRVC_SOL_UUID: + ocf = BTM_BLE_META_PF_SOL_UUID; + break; + case BTM_BLE_PF_LOCAL_NAME: + ocf = BTM_BLE_META_PF_LOCAL_NAME; + break; + case BTM_BLE_PF_MANU_DATA: + ocf = BTM_BLE_META_PF_MANU_DATA; + break; + case BTM_BLE_PF_SRVC_DATA_PATTERN: + ocf = BTM_BLE_META_PF_SRVC_DATA; + break; + case BTM_BLE_PF_TYPE_ALL: + ocf = BTM_BLE_META_PF_ALL; + break; + default: + ocf = BTM_BLE_PF_TYPE_MAX; + break; + } + return ocf; +} + +/******************************************************************************* +** +** Function btm_ble_ocf_to_condtype +** +** Description Convert OCF to cond type +** +** Returns Returns condtype value +** +*******************************************************************************/ +UINT8 btm_ble_ocf_to_condtype(UINT8 ocf) +{ + UINT8 cond_type = 0; + + switch (ocf) { + case BTM_BLE_META_PF_FEAT_SEL: + cond_type = BTM_BLE_META_PF_FEAT_SEL; + break; + case BTM_BLE_META_PF_ADDR: + cond_type = BTM_BLE_PF_ADDR_FILTER; + break; + case BTM_BLE_META_PF_UUID: + cond_type = BTM_BLE_PF_SRVC_UUID; + break; + case BTM_BLE_META_PF_SOL_UUID: + cond_type = BTM_BLE_PF_SRVC_SOL_UUID; + break; + case BTM_BLE_META_PF_LOCAL_NAME: + cond_type = BTM_BLE_PF_LOCAL_NAME; + break; + case BTM_BLE_META_PF_MANU_DATA: + cond_type = BTM_BLE_PF_MANU_DATA; + break; + case BTM_BLE_META_PF_SRVC_DATA: + cond_type = BTM_BLE_PF_SRVC_DATA_PATTERN; + break; + case BTM_BLE_META_PF_ALL: + cond_type = BTM_BLE_PF_TYPE_ALL; + break; + default: + cond_type = BTM_BLE_PF_TYPE_MAX; + break; + } + return cond_type; +} + +/******************************************************************************* +** +** Function btm_ble_scan_pf_cmpl_cback +** +** Description the BTM BLE customer feature VSC complete callback for ADV PF filtering +** +** Returns pointer to the counter if found; NULL otherwise. +** +*******************************************************************************/ +void btm_ble_scan_pf_cmpl_cback(tBTM_VSC_CMPL *p_params) +{ + UINT8 status = 0; + UINT8 *p = p_params->p_param_buf, op_subcode = 0, action = 0xff; + UINT16 evt_len = p_params->param_len; + UINT8 ocf = BTM_BLE_META_PF_ALL, cond_type = 0; + UINT8 num_avail = 0, cb_evt = 0; + tBTM_BLE_REF_VALUE ref_value = 0; + tBTM_BLE_PF_CFG_CBACK *p_scan_cfg_cback = NULL; + tBTM_BLE_PF_PARAM_CBACK *p_filt_param_cback = NULL; + + if (evt_len < 3 || evt_len > 4) { + BTM_TRACE_ERROR("%s cannot interpret APCF callback status = %d, length = %d", + __func__, status, evt_len); + btm_ble_advfilt_deq_op_q(&action, &ocf, &cb_evt, &ref_value, &p_scan_cfg_cback, + &p_filt_param_cback); + return; + } + + btm_ble_advfilt_deq_op_q(&action, &ocf, &cb_evt, &ref_value, &p_scan_cfg_cback, + &p_filt_param_cback); + + STREAM_TO_UINT8(status, p); + STREAM_TO_UINT8(op_subcode, p); + STREAM_TO_UINT8(action, p); + + /* Ignore the event, if it is not the same one expected */ + if (3 == evt_len) { + if (ocf != op_subcode) { + BTM_TRACE_ERROR("btm_ble_scan_pf_cmpl_cback:3-Incorrect opcode :%d, %d, %d, %d, %d, %d", + ocf, op_subcode, action, evt_len, ref_value, status); + return; + } else { + if (NULL != btm_ble_adv_filt_cb.p_filt_stat_cback) { + btm_ble_adv_filt_cb.p_filt_stat_cback(action, status, ref_value); + } + BTM_TRACE_DEBUG("btm_ble_scan_pf_cmpl_cback enabled/disabled, %d, %d, %d, %d", + ocf, action, status, ref_value); + return; + } + } + + if (4 == evt_len && ocf != op_subcode) { + BTM_TRACE_ERROR("btm_ble_scan_pf_cmpl_cback:4-Incorrect opcode: %d, %d, %d, %d, %d", + ocf, op_subcode, action, status, ref_value); + return; + } + + STREAM_TO_UINT8(num_avail, p); + switch (op_subcode) { + case BTM_BLE_META_PF_ADDR: + case BTM_BLE_META_PF_UUID: + case BTM_BLE_META_PF_SOL_UUID: + case BTM_BLE_META_PF_LOCAL_NAME: + case BTM_BLE_META_PF_MANU_DATA: + case BTM_BLE_META_PF_SRVC_DATA: + cond_type = btm_ble_ocf_to_condtype(ocf); + BTM_TRACE_DEBUG("btm_ble_scan_pf_cmpl_cback Recd: %d, %d, %d, %d, %d, %d", op_subcode, + ocf, action, status, ref_value, num_avail); + if (HCI_SUCCESS == status) { + if (memcmp(&btm_ble_adv_filt_cb.cur_filter_target.bda, &na_bda, BD_ADDR_LEN) == 0) { + btm_ble_cs_update_pf_counter(action, cond_type, NULL, num_avail); + } else { + btm_ble_cs_update_pf_counter(action, cond_type, + &btm_ble_adv_filt_cb.cur_filter_target, num_avail); + } + } + + /* send ADV PF operation complete */ + btm_ble_adv_filt_cb.op_type = 0; + break; + + case BTM_BLE_META_PF_FEAT_SEL: + BTM_TRACE_DEBUG("btm_ble_scan_pf_cmpl_cback-Feat sel event: %d, %d, %d, %d", + action, status, ref_value, num_avail); + break; + + default: + BTM_TRACE_ERROR("btm_ble_scan_pf_cmpl_cback: unknown operation: %d", op_subcode); + break; + } + + BTM_TRACE_DEBUG("btm_ble_scan_pf_cmpl_cback: calling the cback: %d", cb_evt); + switch (cb_evt) { + case BTM_BLE_FILT_CFG: + if (NULL != p_scan_cfg_cback) { + p_scan_cfg_cback(action, cond_type, num_avail, status, ref_value); + } + break; + case BTM_BLE_FILT_ADV_PARAM: + if (NULL != p_filt_param_cback) { + p_filt_param_cback(action, num_avail, ref_value, status); + } + break; + default: + break; + } +} + +/******************************************************************************* +** +** Function btm_ble_find_addr_filter_counter +** +** Description find the per bd address ADV payload filter counter by BD_ADDR. +** +** Returns pointer to the counter if found; NULL otherwise. +** +*******************************************************************************/ +tBTM_BLE_PF_COUNT *btm_ble_find_addr_filter_counter(tBLE_BD_ADDR *p_le_bda) +{ + UINT8 i; + tBTM_BLE_PF_COUNT *p_addr_filter = &btm_ble_adv_filt_cb.p_addr_filter_count[1]; + + if (p_le_bda == NULL) { + return &btm_ble_adv_filt_cb.p_addr_filter_count[0]; + } + + for (i = 0; i < cmn_ble_adv_vsc_cb.max_filter; i ++, p_addr_filter ++) { + if (p_addr_filter->in_use && + memcmp(p_le_bda->bda, p_addr_filter->bd_addr, BD_ADDR_LEN) == 0) { + return p_addr_filter; + } + } + return NULL; +} + +/******************************************************************************* +** +** Function btm_ble_alloc_addr_filter_counter +** +** Description allocate the per device adv payload filter counter. +** +** Returns pointer to the counter if allocation succeed; NULL otherwise. +** +*******************************************************************************/ +tBTM_BLE_PF_COUNT *btm_ble_alloc_addr_filter_counter(BD_ADDR bd_addr) +{ + UINT8 i; + tBTM_BLE_PF_COUNT *p_addr_filter = &btm_ble_adv_filt_cb.p_addr_filter_count[1]; + + for (i = 0; i < cmn_ble_adv_vsc_cb.max_filter; i ++, p_addr_filter ++) { + if (memcmp(na_bda, p_addr_filter->bd_addr, BD_ADDR_LEN) == 0) { + memcpy(p_addr_filter->bd_addr, bd_addr, BD_ADDR_LEN); + p_addr_filter->in_use = TRUE; + return p_addr_filter; + } + } + return NULL; +} +/******************************************************************************* +** +** Function btm_ble_dealloc_addr_filter_counter +** +** Description de-allocate the per device adv payload filter counter. +** +** Returns TRUE if deallocation succeed; FALSE otherwise. +** +*******************************************************************************/ +BOOLEAN btm_ble_dealloc_addr_filter_counter(tBLE_BD_ADDR *p_bd_addr, UINT8 filter_type) +{ + UINT8 i; + tBTM_BLE_PF_COUNT *p_addr_filter = &btm_ble_adv_filt_cb.p_addr_filter_count[1]; + BOOLEAN found = FALSE; + + if (BTM_BLE_PF_TYPE_ALL == filter_type && NULL == p_bd_addr) { + memset(&btm_ble_adv_filt_cb.p_addr_filter_count[0], 0, sizeof(tBTM_BLE_PF_COUNT)); + } + + for (i = 0; i < cmn_ble_adv_vsc_cb.max_filter; i ++, p_addr_filter ++) { + if ((p_addr_filter->in_use) && (NULL == p_bd_addr || + (NULL != p_bd_addr && + memcmp(p_bd_addr->bda, p_addr_filter->bd_addr, BD_ADDR_LEN) == 0))) { + found = TRUE; + memset(p_addr_filter, 0, sizeof(tBTM_BLE_PF_COUNT)); + + if (NULL != p_bd_addr) { + break; + } + } + } + return found; +} + +/******************************************************************************* +** +** Function btm_ble_update_pf_local_name +** +** Description this function update(add,delete or clear) the adv lcoal name filtering condition. +** +** +** Returns BTM_SUCCESS if sucessful, +** BTM_ILLEGAL_VALUE if paramter is not valid. +** +*******************************************************************************/ +tBTM_STATUS btm_ble_update_pf_local_name(tBTM_BLE_SCAN_COND_OP action, + tBTM_BLE_PF_FILT_INDEX filt_index, + tBTM_BLE_PF_COND_PARAM *p_cond) +{ + tBTM_BLE_PF_LOCAL_NAME_COND *p_local_name = (p_cond == NULL) ? NULL : &p_cond->local_name; + UINT8 param[BTM_BLE_PF_STR_LEN_MAX + BTM_BLE_ADV_FILT_META_HDR_LENGTH], + *p = param, + len = BTM_BLE_ADV_FILT_META_HDR_LENGTH; + tBTM_STATUS st = BTM_ILLEGAL_VALUE; + + memset(param, 0, BTM_BLE_PF_STR_LEN_MAX + BTM_BLE_ADV_FILT_META_HDR_LENGTH); + + UINT8_TO_STREAM(p, BTM_BLE_META_PF_LOCAL_NAME); + UINT8_TO_STREAM(p, action); + + /* Filter index */ + UINT8_TO_STREAM(p, filt_index); + + if (BTM_BLE_SCAN_COND_ADD == action || + BTM_BLE_SCAN_COND_DELETE == action) { + if (NULL == p_local_name) { + return st; + } + + if (p_local_name->data_len > BTM_BLE_PF_STR_LEN_MAX) { + p_local_name->data_len = BTM_BLE_PF_STR_LEN_MAX; + } + + ARRAY_TO_STREAM(p, p_local_name->p_data, p_local_name->data_len); + len += p_local_name->data_len; + } + + /* send local name filter */ + if ((st = BTM_VendorSpecificCommand (HCI_BLE_ADV_FILTER_OCF, + len, + param, + btm_ble_scan_pf_cmpl_cback)) + != BTM_NO_RESOURCES) { + memset(&btm_ble_adv_filt_cb.cur_filter_target, 0, sizeof(tBLE_BD_ADDR)); + } else { + BTM_TRACE_ERROR("Local Name PF filter update failed"); + } + + return st; +} + + +/******************************************************************************* +** +** Function btm_ble_update_srvc_data_change +** +** Description this function update(add/remove) service data change filter. +** +** +** Returns BTM_SUCCESS if sucessful, +** BTM_ILLEGAL_VALUE if paramter is not valid. +** +*******************************************************************************/ +tBTM_STATUS btm_ble_update_srvc_data_change(tBTM_BLE_SCAN_COND_OP action, + tBTM_BLE_PF_FILT_INDEX filt_index, + tBTM_BLE_PF_COND_PARAM *p_cond) +{ + tBTM_STATUS st = BTM_ILLEGAL_VALUE; + tBLE_BD_ADDR *p_bd_addr = p_cond ? &p_cond->target_addr : NULL; + UINT8 num_avail = (action == BTM_BLE_SCAN_COND_ADD) ? 0 : 1; + + if (btm_ble_cs_update_pf_counter (action, BTM_BLE_PF_SRVC_DATA, p_bd_addr, num_avail) + != BTM_BLE_INVALID_COUNTER) { + st = BTM_SUCCESS; + } + + return st; +} + +/******************************************************************************* +** +** Function btm_ble_update_pf_manu_data +** +** Description this function update(add,delete or clear) the adv manufacturer +** data filtering condition. +** +** +** Returns BTM_SUCCESS if sucessful, +** BTM_ILLEGAL_VALUE if paramter is not valid. +** +*******************************************************************************/ +tBTM_STATUS btm_ble_update_pf_manu_data(tBTM_BLE_SCAN_COND_OP action, + tBTM_BLE_PF_FILT_INDEX filt_index, + tBTM_BLE_PF_COND_PARAM *p_data, + tBTM_BLE_PF_COND_TYPE cond_type, + tBTM_BLE_FILT_CB_EVT cb_evt, + tBTM_BLE_REF_VALUE ref_value) +{ + tBTM_BLE_PF_MANU_COND *p_manu_data = (p_data == NULL) ? NULL : &p_data->manu_data; + tBTM_BLE_PF_SRVC_PATTERN_COND *p_srvc_data = (p_data == NULL) ? NULL : &p_data->srvc_data; + + UINT8 param[BTM_BLE_PF_STR_LEN_MAX + BTM_BLE_PF_STR_LEN_MAX + BTM_BLE_ADV_FILT_META_HDR_LENGTH], + *p = param, + len = BTM_BLE_ADV_FILT_META_HDR_LENGTH; + tBTM_STATUS st = BTM_ILLEGAL_VALUE; + + if (NULL == p_data) { + return st; + } + + memset(param, 0, BTM_BLE_PF_STR_LEN_MAX + BTM_BLE_PF_STR_LEN_MAX + + BTM_BLE_ADV_FILT_META_HDR_LENGTH); + + if (BTM_BLE_PF_SRVC_DATA_PATTERN == cond_type) { + UINT8_TO_STREAM(p, BTM_BLE_META_PF_SRVC_DATA); + } else { + UINT8_TO_STREAM(p, BTM_BLE_META_PF_MANU_DATA); + } + + UINT8_TO_STREAM(p, action); + + /* Filter index */ + UINT8_TO_STREAM(p, filt_index); + + if (BTM_BLE_SCAN_COND_ADD == action || BTM_BLE_SCAN_COND_DELETE == action) { + if (BTM_BLE_PF_SRVC_DATA_PATTERN == cond_type) { + if (NULL == p_srvc_data) { + return st; + } + if (p_srvc_data->data_len > (BTM_BLE_PF_STR_LEN_MAX - 2)) { + p_srvc_data->data_len = (BTM_BLE_PF_STR_LEN_MAX - 2); + } + + if (p_srvc_data->data_len > 0) { + ARRAY_TO_STREAM(p, p_srvc_data->p_pattern, p_srvc_data->data_len); + len += (p_srvc_data->data_len); + ARRAY_TO_STREAM(p, p_srvc_data->p_pattern_mask, p_srvc_data->data_len); + } + + len += (p_srvc_data->data_len); + BTM_TRACE_DEBUG("Service data length: %d", len); + } else { + if (NULL == p_manu_data) { + BTM_TRACE_ERROR("btm_ble_update_pf_manu_data - No manuf data"); + return st; + } + BTM_TRACE_EVENT("btm_ble_update_pf_manu_data length: %d", + p_manu_data->data_len); + if (p_manu_data->data_len > (BTM_BLE_PF_STR_LEN_MAX - 2)) { + p_manu_data->data_len = (BTM_BLE_PF_STR_LEN_MAX - 2); + } + + UINT16_TO_STREAM(p, p_manu_data->company_id); + if (p_manu_data->data_len > 0 && p_manu_data->p_pattern_mask != NULL) { + ARRAY_TO_STREAM(p, p_manu_data->p_pattern, p_manu_data->data_len); + len += (p_manu_data->data_len + 2); + } else { + len += 2; + } + + if (p_manu_data->company_id_mask != 0) { + UINT16_TO_STREAM (p, p_manu_data->company_id_mask); + } else { + memset(p, 0xff, 2); + p += 2; + } + len += 2; + + if (p_manu_data->data_len > 0 && p_manu_data->p_pattern_mask != NULL) { + ARRAY_TO_STREAM(p, p_manu_data->p_pattern_mask, p_manu_data->data_len); + len += (p_manu_data->data_len); + } + + BTM_TRACE_DEBUG("Manuf data length: %d", len); + } + } + + /* send manufacturer*/ + if ((st = BTM_VendorSpecificCommand (HCI_BLE_ADV_FILTER_OCF, + len, + param, + btm_ble_scan_pf_cmpl_cback)) != BTM_NO_RESOURCES) { + memset(&btm_ble_adv_filt_cb.cur_filter_target, 0, sizeof(tBLE_BD_ADDR)); + } else { + BTM_TRACE_ERROR("manufacturer data PF filter update failed"); + } + + return st; +} + +/******************************************************************************* +** +** Function btm_ble_cs_update_pf_counter +** +** Description this function is to update the adv data payload filter counter +** +** Returns current number of the counter; BTM_BLE_INVALID_COUNTER if +** counter update failed. +** +*******************************************************************************/ +UINT8 btm_ble_cs_update_pf_counter(tBTM_BLE_SCAN_COND_OP action, + UINT8 cond_type, tBLE_BD_ADDR *p_bd_addr, + UINT8 num_available) +{ + tBTM_BLE_PF_COUNT *p_addr_filter = NULL; + UINT8 *p_counter = NULL; + + btm_ble_obtain_vsc_details(); + + if (cond_type > BTM_BLE_PF_TYPE_ALL) { + BTM_TRACE_ERROR("unknown PF filter condition type %d", cond_type); + return BTM_BLE_INVALID_COUNTER; + } + + /* for these three types of filter, always generic */ + if (BTM_BLE_PF_ADDR_FILTER == cond_type || + BTM_BLE_PF_MANU_DATA == cond_type || + BTM_BLE_PF_LOCAL_NAME == cond_type || + BTM_BLE_PF_SRVC_DATA_PATTERN == cond_type) { + p_bd_addr = NULL; + } + + if ((p_addr_filter = btm_ble_find_addr_filter_counter(p_bd_addr)) == NULL && + BTM_BLE_SCAN_COND_ADD == action) { + p_addr_filter = btm_ble_alloc_addr_filter_counter(p_bd_addr->bda); + } + + if (NULL != p_addr_filter) { + /* all filter just cleared */ + if ((BTM_BLE_PF_TYPE_ALL == cond_type && BTM_BLE_SCAN_COND_CLEAR == action) || + /* or bd address filter been deleted */ + (BTM_BLE_PF_ADDR_FILTER == cond_type && + (BTM_BLE_SCAN_COND_DELETE == action || BTM_BLE_SCAN_COND_CLEAR == action))) { + btm_ble_dealloc_addr_filter_counter(p_bd_addr, cond_type); + } + /* if not feature selection, update new addition/reduction of the filter counter */ + else if (cond_type != BTM_BLE_PF_TYPE_ALL) { + p_counter = p_addr_filter->pf_counter; + if (num_available > 0) { + p_counter[cond_type] += 1; + } + + BTM_TRACE_DEBUG("counter = %d, maxfilt = %d, num_avbl=%d", + p_counter[cond_type], cmn_ble_adv_vsc_cb.max_filter, num_available); + return p_counter[cond_type]; + } + } else { + BTM_TRACE_ERROR("no matching filter counter found"); + } + /* no matching filter located and updated */ + return BTM_BLE_INVALID_COUNTER; +} + + +/******************************************************************************* +** +** Function btm_ble_update_addr_filter +** +** Description this function update(add,delete or clear) the address filter of adv. +** +** +** Returns BTM_SUCCESS if sucessful, +** BTM_ILLEGAL_VALUE if paramter is not valid. +** +*******************************************************************************/ +tBTM_STATUS btm_ble_update_addr_filter(tBTM_BLE_SCAN_COND_OP action, + tBTM_BLE_PF_FILT_INDEX filt_index, + tBTM_BLE_PF_COND_PARAM *p_cond) +{ + UINT8 param[BTM_BLE_META_ADDR_LEN + BTM_BLE_ADV_FILT_META_HDR_LENGTH], + * p = param; + tBTM_STATUS st = BTM_ILLEGAL_VALUE; + tBLE_BD_ADDR *p_addr = (p_cond == NULL) ? NULL : &p_cond->target_addr; + + memset(param, 0, BTM_BLE_META_ADDR_LEN + BTM_BLE_ADV_FILT_META_HDR_LENGTH); + + UINT8_TO_STREAM(p, BTM_BLE_META_PF_ADDR); + UINT8_TO_STREAM(p, action); + + /* Filter index */ + UINT8_TO_STREAM(p, filt_index); + + if (BTM_BLE_SCAN_COND_ADD == action || + BTM_BLE_SCAN_COND_DELETE == action) { + if (NULL == p_addr) { + return st; + } + + BDADDR_TO_STREAM(p, p_addr->bda); + UINT8_TO_STREAM(p, p_addr->type); + } + /* send address filter */ + if ((st = BTM_VendorSpecificCommand (HCI_BLE_ADV_FILTER_OCF, + (UINT8)(BTM_BLE_ADV_FILT_META_HDR_LENGTH + BTM_BLE_META_ADDR_LEN), + param, + btm_ble_scan_pf_cmpl_cback)) != BTM_NO_RESOURCES) { + memset(&btm_ble_adv_filt_cb.cur_filter_target, 0, sizeof(tBLE_BD_ADDR)); + } else { + BTM_TRACE_ERROR("Broadcaster Address Filter Update failed"); + } + return st; +} + +/******************************************************************************* +** +** Function btm_ble_update_uuid_filter +** +** Description this function update(add,delete or clear) service UUID filter. +** +** +** Returns BTM_SUCCESS if sucessful, +** BTM_ILLEGAL_VALUE if paramter is not valid. +** +*******************************************************************************/ +tBTM_STATUS btm_ble_update_uuid_filter(tBTM_BLE_SCAN_COND_OP action, + tBTM_BLE_PF_FILT_INDEX filt_index, + tBTM_BLE_PF_COND_TYPE filter_type, + tBTM_BLE_PF_COND_PARAM *p_cond, + tBTM_BLE_FILT_CB_EVT cb_evt, + tBTM_BLE_REF_VALUE ref_value) +{ + UINT8 param[BTM_BLE_META_UUID_LEN + BTM_BLE_ADV_FILT_META_HDR_LENGTH], + * p = param, + len = BTM_BLE_ADV_FILT_META_HDR_LENGTH; + tBTM_STATUS st = BTM_ILLEGAL_VALUE; + tBTM_BLE_PF_UUID_COND *p_uuid_cond; + UINT8 evt_type; + + memset(param, 0, BTM_BLE_META_UUID_LEN + BTM_BLE_ADV_FILT_META_HDR_LENGTH); + + if (BTM_BLE_PF_SRVC_UUID == filter_type) { + evt_type = BTM_BLE_META_PF_UUID; + p_uuid_cond = p_cond ? &p_cond->srvc_uuid : NULL; + } else { + evt_type = BTM_BLE_META_PF_SOL_UUID; + p_uuid_cond = p_cond ? &p_cond->solicitate_uuid : NULL; + } + + if (NULL == p_uuid_cond && action != BTM_BLE_SCAN_COND_CLEAR) { + BTM_TRACE_ERROR("Illegal param for add/delete UUID filter"); + return st; + } + + /* need to add address filter first, if adding per bda UUID filter without address filter */ + if (BTM_BLE_SCAN_COND_ADD == action && NULL != p_uuid_cond && + p_uuid_cond->p_target_addr && + btm_ble_find_addr_filter_counter(p_uuid_cond->p_target_addr) == NULL) { + UINT8_TO_STREAM(p, BTM_BLE_META_PF_ADDR); + UINT8_TO_STREAM(p, action); + + /* Filter index */ + UINT8_TO_STREAM(p, filt_index); + + BDADDR_TO_STREAM(p, p_uuid_cond->p_target_addr->bda); + UINT8_TO_STREAM(p, p_uuid_cond->p_target_addr->type); + + /* send address filter */ + if ((st = BTM_VendorSpecificCommand (HCI_BLE_ADV_FILTER_OCF, + (UINT8)(BTM_BLE_ADV_FILT_META_HDR_LENGTH + BTM_BLE_META_ADDR_LEN), + param, + btm_ble_scan_pf_cmpl_cback)) == BTM_NO_RESOURCES) { + BTM_TRACE_ERROR("Update Address filter into controller failed."); + return st; + } + + btm_ble_advfilt_enq_op_q(action, BTM_BLE_META_PF_ADDR, cb_evt, ref_value, NULL, NULL); + BTM_TRACE_DEBUG("Updated Address filter"); + } + + p = param; + UINT8_TO_STREAM(p, evt_type); + UINT8_TO_STREAM(p, action); + + /* Filter index */ + UINT8_TO_STREAM(p, filt_index); + + if ((BTM_BLE_SCAN_COND_ADD == action || + BTM_BLE_SCAN_COND_DELETE == action) && + NULL != p_uuid_cond) { + if (p_uuid_cond->uuid.len == LEN_UUID_16) { + UINT16_TO_STREAM(p, p_uuid_cond->uuid.uu.uuid16); + len += LEN_UUID_16; + } else if (p_uuid_cond->uuid.len == LEN_UUID_32) { /*4 bytes */ + UINT32_TO_STREAM(p, p_uuid_cond->uuid.uu.uuid32); + len += LEN_UUID_32; + } else if (p_uuid_cond->uuid.len == LEN_UUID_128) { + ARRAY_TO_STREAM (p, p_uuid_cond->uuid.uu.uuid128, LEN_UUID_128); + len += LEN_UUID_128; + } else { + BTM_TRACE_ERROR("illegal UUID length: %d", p_uuid_cond->uuid.len); + return BTM_ILLEGAL_VALUE; + } + + if (NULL != p_uuid_cond->p_uuid_mask) { + if (p_uuid_cond->uuid.len == LEN_UUID_16) { + UINT16_TO_STREAM(p, p_uuid_cond->p_uuid_mask->uuid16_mask); + len += LEN_UUID_16; + } else if (p_uuid_cond->uuid.len == LEN_UUID_32) { /*4 bytes */ + UINT32_TO_STREAM(p, p_uuid_cond->p_uuid_mask->uuid32_mask); + len += LEN_UUID_32; + } else if (p_uuid_cond->uuid.len == LEN_UUID_128) { + ARRAY_TO_STREAM (p, p_uuid_cond->p_uuid_mask->uuid128_mask, LEN_UUID_128); + len += LEN_UUID_128; + } + } else { + memset(p, 0xff, p_uuid_cond->uuid.len); + len += p_uuid_cond->uuid.len; + } + BTM_TRACE_DEBUG("btm_ble_update_uuid_filter : %d, %d, %d, %d", filter_type, evt_type, + p_uuid_cond->uuid.len, len); + } + + /* send UUID filter update */ + if ((st = BTM_VendorSpecificCommand (HCI_BLE_ADV_FILTER_OCF, + len, + param, + btm_ble_scan_pf_cmpl_cback)) != BTM_NO_RESOURCES) { + if (p_uuid_cond && p_uuid_cond->p_target_addr) { + memcpy(&btm_ble_adv_filt_cb.cur_filter_target, p_uuid_cond->p_target_addr, + sizeof(tBLE_BD_ADDR)); + } + else { + memset(&btm_ble_adv_filt_cb.cur_filter_target, 0, sizeof(tBLE_BD_ADDR)); + } + } else { + BTM_TRACE_ERROR("UUID filter udpating failed"); + } + + return st; +} + + +/******************************************************************************* +** +** Function btm_ble_clear_scan_pf_filter +** +** Description clear all adv payload filter by de-select all the adv pf feature bits +** +** +** Returns BTM_SUCCESS if sucessful, +** BTM_ILLEGAL_VALUE if paramter is not valid. +** +*******************************************************************************/ +tBTM_STATUS btm_ble_clear_scan_pf_filter(tBTM_BLE_SCAN_COND_OP action, + tBTM_BLE_PF_FILT_INDEX filt_index, + tBTM_BLE_PF_COND_PARAM *p_cond, + tBTM_BLE_PF_CFG_CBACK *p_cmpl_cback, + tBTM_BLE_FILT_CB_EVT cb_evt, + tBTM_BLE_REF_VALUE ref_value) +{ + tBLE_BD_ADDR *p_target = (p_cond == NULL) ? NULL : &p_cond->target_addr; + tBTM_BLE_PF_COUNT *p_bda_filter; + tBTM_STATUS st = BTM_WRONG_MODE; + UINT8 param[20], *p; + + if (BTM_BLE_SCAN_COND_CLEAR != action) { + BTM_TRACE_ERROR("unable to perform action:%d for generic adv filter type", action); + return BTM_ILLEGAL_VALUE; + } + + p = param; + memset(param, 0, 20); + + p_bda_filter = btm_ble_find_addr_filter_counter(p_target); + + if (NULL == p_bda_filter || + /* not a generic filter */ + (p_target != NULL && p_bda_filter)) { + BTM_TRACE_ERROR("Error: Can not clear filter, No PF filter has been configured!"); + return st; + } + + /* clear the general filter entry */ + if (NULL == p_target) { + /* clear manufacturer data filter */ + st = btm_ble_update_pf_manu_data(BTM_BLE_SCAN_COND_CLEAR, filt_index, NULL, + BTM_BLE_PF_MANU_DATA, cb_evt, ref_value); + if (BTM_CMD_STARTED == st) { + btm_ble_advfilt_enq_op_q(action, BTM_BLE_META_PF_MANU_DATA, cb_evt, + ref_value, NULL, NULL); + } + + /* clear local name filter */ + st = btm_ble_update_pf_local_name(BTM_BLE_SCAN_COND_CLEAR, filt_index, NULL); + if (BTM_CMD_STARTED == st) { + btm_ble_advfilt_enq_op_q(action, BTM_BLE_META_PF_LOCAL_NAME, cb_evt, + ref_value, NULL, NULL); + } + + /* update the counter for service data */ + st = btm_ble_update_srvc_data_change(BTM_BLE_SCAN_COND_CLEAR, filt_index, NULL); + + /* clear UUID filter */ + st = btm_ble_update_uuid_filter(BTM_BLE_SCAN_COND_CLEAR, filt_index, + BTM_BLE_PF_SRVC_UUID, NULL, cb_evt, ref_value); + if (BTM_CMD_STARTED == st) { + btm_ble_advfilt_enq_op_q(action, BTM_BLE_META_PF_UUID, cb_evt, ref_value, NULL, NULL); + } + + st = btm_ble_update_uuid_filter(BTM_BLE_SCAN_COND_CLEAR, filt_index, + BTM_BLE_PF_SRVC_SOL_UUID, NULL, cb_evt, ref_value); + if (BTM_CMD_STARTED == st) { + btm_ble_advfilt_enq_op_q(action, BTM_BLE_META_PF_SOL_UUID, cb_evt, + ref_value, NULL, NULL); + } + + /* clear service data filter */ + st = btm_ble_update_pf_manu_data(BTM_BLE_SCAN_COND_CLEAR, filt_index, NULL, + BTM_BLE_PF_SRVC_DATA_PATTERN, cb_evt, ref_value); + if (BTM_CMD_STARTED == st) { + btm_ble_advfilt_enq_op_q(action, BTM_BLE_META_PF_SRVC_DATA, cb_evt, + ref_value, NULL, NULL); + } + } + + /* select feature based on control block settings */ + UINT8_TO_STREAM(p, BTM_BLE_META_PF_FEAT_SEL); + UINT8_TO_STREAM(p, BTM_BLE_SCAN_COND_CLEAR); + + /* Filter index */ + UINT8_TO_STREAM(p, filt_index); + + /* set PCF selection */ + UINT32_TO_STREAM(p, BTM_BLE_PF_SELECT_NONE); + /* set logic condition as OR as default */ + UINT8_TO_STREAM(p, BTM_BLE_PF_LOGIC_OR); + + if ((st = BTM_VendorSpecificCommand (HCI_BLE_ADV_FILTER_OCF, + (UINT8)(BTM_BLE_ADV_FILT_META_HDR_LENGTH + BTM_BLE_PF_FEAT_SEL_LEN), + param, + btm_ble_scan_pf_cmpl_cback)) + != BTM_NO_RESOURCES) { + if (p_target) { + memcpy(&btm_ble_adv_filt_cb.cur_filter_target, p_target, sizeof(tBLE_BD_ADDR)); + } else { + memset(&btm_ble_adv_filt_cb.cur_filter_target, 0, sizeof(tBLE_BD_ADDR)); + } + } + return st; +} + +/******************************************************************************* +** +** Function BTM_BleAdvFilterParamSetup +** +** Description This function is called to setup the adv data payload filter +** condition. +** +** Parameters action - Type of action to be performed +** filt_index - Filter index +** p_filt_params - Filter parameters +** p_target - Target device +** p_cmpl_back - Callback pointer +** ref_value - reference value +** +** Returns void +** +*******************************************************************************/ +tBTM_STATUS BTM_BleAdvFilterParamSetup(int action, tBTM_BLE_PF_FILT_INDEX filt_index, + tBTM_BLE_PF_FILT_PARAMS *p_filt_params, + tBLE_BD_ADDR *p_target, tBTM_BLE_PF_PARAM_CBACK *p_cmpl_cback, + tBTM_BLE_REF_VALUE ref_value) +{ + tBTM_STATUS st = BTM_WRONG_MODE; + tBTM_BLE_PF_COUNT *p_bda_filter = NULL; + UINT8 len = BTM_BLE_ADV_FILT_META_HDR_LENGTH + BTM_BLE_ADV_FILT_FEAT_SELN_LEN + + BTM_BLE_ADV_FILT_TRACK_NUM; + UINT8 param[len], *p; + + if (BTM_SUCCESS != btm_ble_obtain_vsc_details()) { + return st; + } + + p = param; + memset(param, 0, len); + BTM_TRACE_EVENT (" BTM_BleAdvFilterParamSetup"); + + if (BTM_BLE_SCAN_COND_ADD == action) { + p_bda_filter = btm_ble_find_addr_filter_counter(p_target); + if (NULL == p_bda_filter) { + BTM_TRACE_ERROR("BD Address not found!"); + return st; + } + + BTM_TRACE_DEBUG("BTM_BleAdvFilterParamSetup : Feat mask:%d", p_filt_params->feat_seln); + /* select feature based on control block settings */ + UINT8_TO_STREAM(p, BTM_BLE_META_PF_FEAT_SEL); + UINT8_TO_STREAM(p, BTM_BLE_SCAN_COND_ADD); + + /* Filter index */ + UINT8_TO_STREAM(p, filt_index); + + /* set PCF selection */ + UINT16_TO_STREAM(p, p_filt_params->feat_seln); + /* set logic type */ + UINT16_TO_STREAM(p, p_filt_params->logic_type); + /* set logic condition */ + UINT8_TO_STREAM(p, p_filt_params->filt_logic_type); + /* set RSSI high threshold */ + UINT8_TO_STREAM(p, p_filt_params->rssi_high_thres); + /* set delivery mode */ + UINT8_TO_STREAM(p, p_filt_params->dely_mode); + + if (0x01 == p_filt_params->dely_mode) { + /* set onfound timeout */ + UINT16_TO_STREAM(p, p_filt_params->found_timeout); + /* set onfound timeout count*/ + UINT8_TO_STREAM(p, p_filt_params->found_timeout_cnt); + /* set RSSI low threshold */ + UINT8_TO_STREAM(p, p_filt_params->rssi_low_thres); + /* set onlost timeout */ + UINT16_TO_STREAM(p, p_filt_params->lost_timeout); + /* set num_of_track_entries for firmware greater than L-release version */ + if (cmn_ble_adv_vsc_cb.version_supported > BTM_VSC_CHIP_CAPABILITY_L_VERSION) { + UINT16_TO_STREAM(p, p_filt_params->num_of_tracking_entries); + } + } + + if (cmn_ble_adv_vsc_cb.version_supported == BTM_VSC_CHIP_CAPABILITY_L_VERSION) { + len = BTM_BLE_ADV_FILT_META_HDR_LENGTH + BTM_BLE_ADV_FILT_FEAT_SELN_LEN; + } else { + len = BTM_BLE_ADV_FILT_META_HDR_LENGTH + BTM_BLE_ADV_FILT_FEAT_SELN_LEN + + BTM_BLE_ADV_FILT_TRACK_NUM; + } + + if ((st = BTM_VendorSpecificCommand (HCI_BLE_ADV_FILTER_OCF, + (UINT8)len, + param, + btm_ble_scan_pf_cmpl_cback)) + == BTM_NO_RESOURCES) { + return st; + } + btm_ble_advfilt_enq_op_q(action, BTM_BLE_META_PF_FEAT_SEL, BTM_BLE_FILT_ADV_PARAM, + ref_value, NULL, p_cmpl_cback); + } else if (BTM_BLE_SCAN_COND_DELETE == action) { + /* select feature based on control block settings */ + UINT8_TO_STREAM(p, BTM_BLE_META_PF_FEAT_SEL); + UINT8_TO_STREAM(p, BTM_BLE_SCAN_COND_DELETE); + /* Filter index */ + UINT8_TO_STREAM(p, filt_index); + + if ((st = BTM_VendorSpecificCommand (HCI_BLE_ADV_FILTER_OCF, + (UINT8)(BTM_BLE_ADV_FILT_META_HDR_LENGTH), + param, + btm_ble_scan_pf_cmpl_cback)) + == BTM_NO_RESOURCES) { + return st; + } + btm_ble_advfilt_enq_op_q(action, BTM_BLE_META_PF_FEAT_SEL, BTM_BLE_FILT_ADV_PARAM, + ref_value, NULL, p_cmpl_cback); + } else if (BTM_BLE_SCAN_COND_CLEAR == action) { + /* Deallocate all filters here */ + btm_ble_dealloc_addr_filter_counter(NULL, BTM_BLE_PF_TYPE_ALL); + + /* select feature based on control block settings */ + UINT8_TO_STREAM(p, BTM_BLE_META_PF_FEAT_SEL); + UINT8_TO_STREAM(p, BTM_BLE_SCAN_COND_CLEAR); + + if ((st = BTM_VendorSpecificCommand (HCI_BLE_ADV_FILTER_OCF, + (UINT8)(BTM_BLE_ADV_FILT_META_HDR_LENGTH - 1), + param, + btm_ble_scan_pf_cmpl_cback)) + == BTM_NO_RESOURCES) { + return st; + } + btm_ble_advfilt_enq_op_q(action, BTM_BLE_META_PF_FEAT_SEL, BTM_BLE_FILT_ADV_PARAM, + ref_value, NULL, p_cmpl_cback); + } + + return st; +} + +/******************************************************************************* +** +** Function BTM_BleEnableDisableFilterFeature +** +** Description This function is called to enable / disable the APCF feature +** +** Parameters enable the generic scan condition. +** enable: enable or disable the filter condition +** p_stat_cback - Status callback pointer +** ref_value - Ref value +** Returns void +** +*******************************************************************************/ +tBTM_STATUS BTM_BleEnableDisableFilterFeature(UINT8 enable, + tBTM_BLE_PF_STATUS_CBACK *p_stat_cback, + tBTM_BLE_REF_VALUE ref_value) +{ + UINT8 param[20], *p; + tBTM_STATUS st = BTM_WRONG_MODE; + + if (BTM_SUCCESS != btm_ble_obtain_vsc_details()) { + return st; + } + + p = param; + memset(param, 0, 20); + + /* enable the content filter in controller */ + p = param; + UINT8_TO_STREAM(p, BTM_BLE_META_PF_ENABLE); + /* enable adv data payload filtering */ + UINT8_TO_STREAM(p, enable); + + if ((st = BTM_VendorSpecificCommand (HCI_BLE_ADV_FILTER_OCF, + BTM_BLE_PCF_ENABLE_LEN, param, + btm_ble_scan_pf_cmpl_cback)) == BTM_CMD_STARTED) { + btm_ble_adv_filt_cb.p_filt_stat_cback = p_stat_cback; + btm_ble_advfilt_enq_op_q(enable, BTM_BLE_META_PF_ENABLE, BTM_BLE_FILT_ENABLE_DISABLE, + ref_value, NULL, NULL); + } + return st; +} + +/******************************************************************************* +** +** Function BTM_BleCfgFilterCondition +** +** Description This function is called to configure the adv data payload filter +** condition. +** +** Parameters action: to read/write/clear +** cond_type: filter condition type. +** filt_index - Filter index +** p_cond: filter condition parameter +** p_cmpl_cback - Config callback pointer +** ref_value - Reference value +** +** Returns void +** +*******************************************************************************/ +tBTM_STATUS BTM_BleCfgFilterCondition(tBTM_BLE_SCAN_COND_OP action, + tBTM_BLE_PF_COND_TYPE cond_type, + tBTM_BLE_PF_FILT_INDEX filt_index, + tBTM_BLE_PF_COND_PARAM *p_cond, + tBTM_BLE_PF_CFG_CBACK *p_cmpl_cback, + tBTM_BLE_REF_VALUE ref_value) +{ + tBTM_STATUS st = BTM_ILLEGAL_VALUE; + UINT8 ocf = 0; + BTM_TRACE_EVENT (" BTM_BleCfgFilterCondition action:%d, cond_type:%d, index:%d", action, + cond_type, filt_index); + + if (BTM_SUCCESS != btm_ble_obtain_vsc_details()) { + return st; + } + + switch (cond_type) { + /* write service data filter */ + case BTM_BLE_PF_SRVC_DATA_PATTERN: + /* write manufacturer data filter */ + case BTM_BLE_PF_MANU_DATA: + st = btm_ble_update_pf_manu_data(action, filt_index, p_cond, cond_type, 0, ref_value); + break; + + /* write local name filter */ + case BTM_BLE_PF_LOCAL_NAME: + st = btm_ble_update_pf_local_name(action, filt_index, p_cond); + break; + + /* filter on advertiser address */ + case BTM_BLE_PF_ADDR_FILTER: + st = btm_ble_update_addr_filter(action, filt_index, p_cond); + break; + + /* filter on service/solicited UUID */ + case BTM_BLE_PF_SRVC_UUID: + case BTM_BLE_PF_SRVC_SOL_UUID: + st = btm_ble_update_uuid_filter(action, filt_index, cond_type, p_cond, 0, ref_value); + break; + + case BTM_BLE_PF_SRVC_DATA: + st = btm_ble_update_srvc_data_change(action, filt_index, p_cond); + break; + + case BTM_BLE_PF_TYPE_ALL: /* only used to clear filter */ + st = btm_ble_clear_scan_pf_filter(action, filt_index, p_cond, p_cmpl_cback, + 0, ref_value); + break; + + default: + BTM_TRACE_WARNING("condition type [%d] not supported currently.", cond_type); + break; + } + + if (BTM_CMD_STARTED == st && cond_type != BTM_BLE_PF_TYPE_ALL) { + ocf = btm_ble_condtype_to_ocf(cond_type); + btm_ble_advfilt_enq_op_q(action, ocf, BTM_BLE_FILT_CFG, ref_value, p_cmpl_cback, NULL); + } else if (BTM_CMD_STARTED == st && BTM_BLE_PF_TYPE_ALL == cond_type) { + btm_ble_advfilt_enq_op_q(action, BTM_BLE_META_PF_FEAT_SEL, BTM_BLE_FILT_CFG, + ref_value, p_cmpl_cback, NULL); + } + return st; +} + +/******************************************************************************* +** +** Function btm_ble_adv_filter_init +** +** Description This function initializes the adv filter control block +** +** Parameters +** +** Returns status +** +*******************************************************************************/ +void btm_ble_adv_filter_init(void) +{ +#if BTM_DYNAMIC_MEMORY == TRUE + btm_ble_adv_filt_cb_ptr = (tBTM_BLE_ADV_FILTER_CB *)osi_malloc(sizeof(tBTM_BLE_ADV_FILTER_CB)); + cmn_ble_adv_vsc_cb_ptr = (tBTM_BLE_VSC_CB *)osi_malloc(sizeof(tBTM_BLE_VSC_CB)); + if (btm_ble_adv_filt_cb_ptr == NULL || cmn_ble_adv_vsc_cb_ptr == NULL) { + BTM_TRACE_ERROR("%s malloc failed", __func__); + return; + } + memset((void *)btm_ble_adv_filt_cb_ptr, 0, sizeof(tBTM_BLE_ADV_FILTER_CB)); + memset((void *)cmn_ble_adv_vsc_cb_ptr, 0, sizeof(tBTM_BLE_VSC_CB)); +#endif + memset(&btm_ble_adv_filt_cb, 0, sizeof(tBTM_BLE_ADV_FILTER_CB)); + if (BTM_SUCCESS != btm_ble_obtain_vsc_details()) { + return; + } + + if (cmn_ble_adv_vsc_cb.max_filter > 0) { + btm_ble_adv_filt_cb.p_addr_filter_count = + (tBTM_BLE_PF_COUNT *) osi_malloc( sizeof(tBTM_BLE_PF_COUNT) * cmn_ble_adv_vsc_cb.max_filter); + } +} + +/******************************************************************************* +** +** Function btm_ble_adv_filter_cleanup +** +** Description This function de-initializes the adv filter control block +** +** Parameters +** +** Returns status +** +*******************************************************************************/ +void btm_ble_adv_filter_cleanup(void) +{ + if (btm_ble_adv_filt_cb.p_addr_filter_count) { + osi_free(btm_ble_adv_filt_cb.p_addr_filter_count); + btm_ble_adv_filt_cb.p_addr_filter_count = NULL; + } + +#if BTM_DYNAMIC_MEMORY == TRUE + osi_free(btm_ble_adv_filt_cb_ptr); + btm_ble_adv_filt_cb_ptr = NULL; + osi_free(cmn_ble_adv_vsc_cb_ptr); + cmn_ble_adv_vsc_cb_ptr = NULL; +#endif +} + +#endif diff --git a/lib/bt/host/bluedroid/stack/btm/btm_ble_batchscan.c b/lib/bt/host/bluedroid/stack/btm/btm_ble_batchscan.c new file mode 100644 index 00000000..5b0fcd8f --- /dev/null +++ b/lib/bt/host/bluedroid/stack/btm/btm_ble_batchscan.c @@ -0,0 +1,953 @@ +/****************************************************************************** + * + * Copyright (C) 2014 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ +#include <string.h> +//#include <stdio.h> +#include <stddef.h> +#include "common/bt_target.h" + +#include "stack/btm_ble_api.h" +#include "stack/bt_types.h" +//#include "bt_utils.h" +#include "stack/btu.h" +#include "btm_int.h" +#include "device/controller.h" +#include "stack/hcimsgs.h" + +#if (BLE_INCLUDED == TRUE) + +#if BTM_DYNAMIC_MEMORY == FALSE +tBTM_BLE_BATCH_SCAN_CB ble_batchscan_cb; +tBTM_BLE_ADV_TRACK_CB ble_advtrack_cb; +#else +tBTM_BLE_BATCH_SCAN_CB *ble_batchscan_cb_ptr; +tBTM_BLE_ADV_TRACK_CB *ble_advtrack_cb_ptr; +#define ble_batchscan_cb (*ble_batchscan_cb_ptr) +#define ble_advtrack_cb (*ble_advtrack_cb_ptr) +#endif + +/* length of each batch scan command */ +#define BTM_BLE_BATCH_SCAN_STORAGE_CFG_LEN 4 +#define BTM_BLE_BATCH_SCAN_PARAM_CONFIG_LEN 12 +#define BTM_BLE_BATCH_SCAN_ENB_DISB_LEN 2 +#define BTM_BLE_BATCH_SCAN_READ_RESULTS_LEN 2 + +#define BTM_BLE_BATCH_SCAN_CB_EVT_MASK 0xF0 +#define BTM_BLE_BATCH_SCAN_SUBCODE_MASK 0x0F + +/******************************************************************************* +** Local functions +*******************************************************************************/ +void btm_ble_batchscan_vsc_cmpl_cback (tBTM_VSC_CMPL *p_params); +void btm_ble_batchscan_cleanup(void); + +/******************************************************************************* +** +** Function btm_ble_batchscan_filter_track_adv_vse_cback +** +** Description VSE callback for batch scan, filter, and tracking events. +** +** Returns None +** +*******************************************************************************/ +void btm_ble_batchscan_filter_track_adv_vse_cback(UINT8 len, UINT8 *p) +{ + tBTM_BLE_TRACK_ADV_DATA adv_data; + + UINT8 sub_event = 0; + tBTM_BLE_VSC_CB cmn_ble_vsc_cb; + STREAM_TO_UINT8(sub_event, p); + + BTM_TRACE_EVENT("btm_ble_batchscan_filter_track_adv_vse_cback called with event:%x", sub_event); + if (HCI_VSE_SUBCODE_BLE_THRESHOLD_SUB_EVT == sub_event && + NULL != ble_batchscan_cb.p_thres_cback) { + ble_batchscan_cb.p_thres_cback(ble_batchscan_cb.ref_value); + return; + } + + if (HCI_VSE_SUBCODE_BLE_TRACKING_SUB_EVT == sub_event && NULL != ble_advtrack_cb.p_track_cback) { + if (len < 10) { + return; + } + + memset(&adv_data, 0 , sizeof(tBTM_BLE_TRACK_ADV_DATA)); + BTM_BleGetVendorCapabilities(&cmn_ble_vsc_cb); + adv_data.client_if = (UINT8)ble_advtrack_cb.ref_value; + if (cmn_ble_vsc_cb.version_supported > BTM_VSC_CHIP_CAPABILITY_L_VERSION) { + STREAM_TO_UINT8(adv_data.filt_index, p); + STREAM_TO_UINT8(adv_data.advertiser_state, p); + STREAM_TO_UINT8(adv_data.advertiser_info_present, p); + STREAM_TO_BDADDR(adv_data.bd_addr.address, p); + STREAM_TO_UINT8(adv_data.addr_type, p); + + /* Extract the adv info details */ + if (ADV_INFO_PRESENT == adv_data.advertiser_info_present) { + STREAM_TO_UINT8(adv_data.tx_power, p); + STREAM_TO_UINT8(adv_data.rssi_value, p); + STREAM_TO_UINT16(adv_data.time_stamp, p); + + STREAM_TO_UINT8(adv_data.adv_pkt_len, p); + if (adv_data.adv_pkt_len > 0) { + adv_data.p_adv_pkt_data = osi_malloc(adv_data.adv_pkt_len); + memcpy(adv_data.p_adv_pkt_data, p, adv_data.adv_pkt_len); + } + + STREAM_TO_UINT8(adv_data.scan_rsp_len, p); + if (adv_data.scan_rsp_len > 0) { + adv_data.p_scan_rsp_data = osi_malloc(adv_data.scan_rsp_len); + memcpy(adv_data.p_scan_rsp_data, p, adv_data.scan_rsp_len); + } + } + } else { + /* Based on L-release version */ + STREAM_TO_UINT8(adv_data.filt_index, p); + STREAM_TO_UINT8(adv_data.addr_type, p); + STREAM_TO_BDADDR(adv_data.bd_addr.address, p); + STREAM_TO_UINT8(adv_data.advertiser_state, p); + } + + BTM_TRACE_EVENT("track_adv_vse_cback called: %d, %d, %d", adv_data.filt_index, + adv_data.addr_type, adv_data.advertiser_state); + ble_advtrack_cb.p_track_cback(&adv_data); + return; + } +} + +/******************************************************************************* +** +** Function btm_ble_batchscan_enq_op_q +** +** Description enqueue a batchscan operation in q to check command complete +** status +** +** Returns void +** +*******************************************************************************/ +void btm_ble_batchscan_enq_op_q(UINT8 opcode, tBTM_BLE_BATCH_SCAN_STATE cur_state, + UINT8 cb_evt, tBTM_BLE_REF_VALUE ref_value) +{ + ble_batchscan_cb.op_q.sub_code[ble_batchscan_cb.op_q.next_idx] = (opcode | (cb_evt << 4)); + ble_batchscan_cb.op_q.cur_state[ble_batchscan_cb.op_q.next_idx] = cur_state; + ble_batchscan_cb.op_q.ref_value[ble_batchscan_cb.op_q.next_idx] = ref_value; + BTM_TRACE_DEBUG("btm_ble_batchscan_enq_op_q: subcode:%d, Cur_state:%d, ref_value:%d", + ble_batchscan_cb.op_q.sub_code[ble_batchscan_cb.op_q.next_idx], + ble_batchscan_cb.op_q.cur_state[ble_batchscan_cb.op_q.next_idx], + ble_batchscan_cb.op_q.ref_value[ble_batchscan_cb.op_q.next_idx]); + ble_batchscan_cb.op_q.next_idx = (ble_batchscan_cb.op_q.next_idx + 1) + % BTM_BLE_BATCH_SCAN_MAX; +} + +/******************************************************************************* +** +** Function btm_ble_batchscan_enq_rep_q +** +** Description enqueue a batchscan report operation in q to check command complete +** status +** +** Returns void +** +*******************************************************************************/ +tBTM_STATUS btm_ble_batchscan_enq_rep_q(UINT8 report_format, tBTM_BLE_REF_VALUE ref_value) +{ + int i = 0; + for (i = 0; i < BTM_BLE_BATCH_REP_MAIN_Q_SIZE; i++) { + if (report_format == ble_batchscan_cb.main_rep_q.rep_mode[i]) { + return BTM_ILLEGAL_VALUE; + } + } + + ble_batchscan_cb.main_rep_q.rep_mode[ble_batchscan_cb.main_rep_q.next_idx] = report_format; + ble_batchscan_cb.main_rep_q.ref_value[ble_batchscan_cb.main_rep_q.next_idx] = ref_value; + ble_batchscan_cb.main_rep_q.num_records[ble_batchscan_cb.main_rep_q.next_idx] = 0; + ble_batchscan_cb.main_rep_q.data_len[ble_batchscan_cb.main_rep_q.next_idx] = 0; + ble_batchscan_cb.main_rep_q.p_data[ble_batchscan_cb.main_rep_q.next_idx] = NULL; + BTM_TRACE_DEBUG("btm_ble_batchscan_enq_rep_q: index:%d, rep %d, ref %d", + ble_batchscan_cb.main_rep_q.next_idx, report_format, ref_value); + + ble_batchscan_cb.main_rep_q.next_idx = (ble_batchscan_cb.main_rep_q.next_idx + 1) + % BTM_BLE_BATCH_REP_MAIN_Q_SIZE; + return BTM_SUCCESS; +} + +/******************************************************************************* +** +** Function btm_ble_batchscan_enq_rep_data +** +** Description setup the data in the main report queue +** +** Returns void +** +*******************************************************************************/ +void btm_ble_batchscan_enq_rep_data(UINT8 report_format, UINT8 num_records, UINT8 *p_data, + UINT8 data_len) +{ + int index = 0, len = 0; + UINT8 *p_orig_data = NULL, *p_app_data = NULL; + + for (index = 0; index < BTM_BLE_BATCH_REP_MAIN_Q_SIZE; index++) { + if (report_format == ble_batchscan_cb.main_rep_q.rep_mode[index]) { + break; + } + } + + BTM_TRACE_DEBUG("btm_ble_batchscan_enq_rep_data: index:%d, rep %d, num %d len : %d", + index, report_format, num_records, data_len); + + if (index < BTM_BLE_BATCH_REP_MAIN_Q_SIZE && data_len > 0 && num_records > 0) { + len = ble_batchscan_cb.main_rep_q.data_len[index]; + p_orig_data = ble_batchscan_cb.main_rep_q.p_data[index]; + if (NULL != p_orig_data) { + p_app_data = osi_malloc(len + data_len); + memcpy(p_app_data, p_orig_data, len); + memcpy(p_app_data + len, p_data, data_len); + osi_free(p_orig_data); + ble_batchscan_cb.main_rep_q.p_data[index] = p_app_data; + ble_batchscan_cb.main_rep_q.num_records[index] += num_records; + ble_batchscan_cb.main_rep_q.data_len[index] += data_len; + } else { + p_app_data = osi_malloc(data_len); + memcpy(p_app_data, p_data, data_len); + ble_batchscan_cb.main_rep_q.p_data[index] = p_app_data; + ble_batchscan_cb.main_rep_q.num_records[index] = num_records; + ble_batchscan_cb.main_rep_q.data_len[index] = data_len; + } + } +} + +/******************************************************************************* +** +** Function btm_ble_batchscan_deq_rep_q +** +** Description dequeue a batchscan report in q when command complete +** is received +** +** Returns void +** +*******************************************************************************/ +void btm_ble_batchscan_deq_rep_data(UINT8 report_format, tBTM_BLE_REF_VALUE *p_ref_value, + UINT8 *p_num_records, UINT8 **p_data, UINT16 *p_data_len) +{ + int index = 0; + + for (index = 0; index < BTM_BLE_BATCH_REP_MAIN_Q_SIZE; index++) { + if (report_format == ble_batchscan_cb.main_rep_q.rep_mode[index]) { + break; + } + } + + if (BTM_BLE_BATCH_REP_MAIN_Q_SIZE == index) { + BTM_TRACE_ERROR("btm_ble_batchscan_deq_rep_data: rep_format:%d not found", report_format); + return; + } + + *p_num_records = ble_batchscan_cb.main_rep_q.num_records[index]; + *p_ref_value = ble_batchscan_cb.main_rep_q.ref_value[index]; + *p_data = ble_batchscan_cb.main_rep_q.p_data[index]; + *p_data_len = ble_batchscan_cb.main_rep_q.data_len[index]; + + ble_batchscan_cb.main_rep_q.p_data[index] = NULL; + ble_batchscan_cb.main_rep_q.data_len[index] = 0; + ble_batchscan_cb.main_rep_q.rep_mode[index] = 0; + ble_batchscan_cb.main_rep_q.ref_value[index] = 0; + ble_batchscan_cb.main_rep_q.num_records[index] = 0; + + BTM_TRACE_DEBUG("btm_ble_batchscan_deq_rep_data: index:%d, rep %d, num %d, data_len %d", + index, report_format, *p_num_records, *p_data_len); + + ble_batchscan_cb.main_rep_q.pending_idx = (ble_batchscan_cb.main_rep_q.pending_idx + 1) + % BTM_BLE_BATCH_SCAN_MAX; +} + +/******************************************************************************* +** +** Function btm_ble_batchscan_deq_op_q +** +** Description dequeue a batch scan operation from q when command complete +** is received +** +** Returns void +** +*******************************************************************************/ +void btm_ble_batchscan_deq_op_q(UINT8 *p_opcode, tBTM_BLE_BATCH_SCAN_STATE *cur_state, + UINT8 *p_cb_evt, tBTM_BLE_REF_VALUE *p_ref) +{ + *p_cb_evt = (ble_batchscan_cb.op_q.sub_code[ble_batchscan_cb.op_q.pending_idx] >> 4); + *p_opcode = (ble_batchscan_cb.op_q.sub_code[ble_batchscan_cb.op_q.pending_idx] + & BTM_BLE_BATCH_SCAN_SUBCODE_MASK); + *p_ref = ble_batchscan_cb.op_q.ref_value[ble_batchscan_cb.op_q.pending_idx]; + *cur_state = (ble_batchscan_cb.op_q.cur_state[ble_batchscan_cb.op_q.pending_idx]); + ble_batchscan_cb.op_q.pending_idx = (ble_batchscan_cb.op_q.pending_idx + 1) + % BTM_BLE_BATCH_SCAN_MAX; +} + +/******************************************************************************* +** +** Function btm_ble_read_batchscan_reports +** +** Description This function reads the reports from controller +** +** Parameters scan_mode - The mode for which the reports are to be read out from the controller +** ref_value - Reference value +** +** Returns status +** +*******************************************************************************/ +tBTM_STATUS btm_ble_read_batchscan_reports(tBTM_BLE_BATCH_SCAN_MODE scan_mode, + tBTM_BLE_REF_VALUE ref_value) +{ + tBTM_STATUS status = BTM_NO_RESOURCES; + UINT8 param[BTM_BLE_BATCH_SCAN_READ_RESULTS_LEN], *pp; + pp = param; + + memset(param, 0, BTM_BLE_BATCH_SCAN_READ_RESULTS_LEN); + + UINT8_TO_STREAM (pp, BTM_BLE_BATCH_SCAN_READ_RESULTS); + UINT8_TO_STREAM (pp, scan_mode); + + if ((status = BTM_VendorSpecificCommand (HCI_BLE_BATCH_SCAN_OCF, + BTM_BLE_BATCH_SCAN_READ_RESULTS_LEN, param, btm_ble_batchscan_vsc_cmpl_cback)) + != BTM_CMD_STARTED) { + BTM_TRACE_ERROR("btm_ble_read_batchscan_reports %d", status); + return BTM_ILLEGAL_VALUE; + } + + if (BTM_CMD_STARTED == status) { + /* The user needs to be provided scan read reports event */ + btm_ble_batchscan_enq_op_q(BTM_BLE_BATCH_SCAN_READ_RESULTS, ble_batchscan_cb.cur_state, + BTM_BLE_BATCH_SCAN_READ_REPTS_EVT, ref_value); + } + + return status; +} + +/******************************************************************************* +** +** Function btm_ble_batchscan_vsc_cmpl_cback +** +** Description Batch scan VSC complete callback +** +** Parameters p_params - VSC completed callback parameters +** +** Returns void +** +*******************************************************************************/ +void btm_ble_batchscan_vsc_cmpl_cback (tBTM_VSC_CMPL *p_params) +{ + UINT8 *p = p_params->p_param_buf; + UINT16 len = p_params->param_len; + tBTM_BLE_REF_VALUE ref_value = 0; + + UINT8 status = 0, subcode = 0, opcode = 0; + UINT8 report_format = 0, num_records = 0, cb_evt = 0; + UINT16 data_len = 0; + tBTM_BLE_BATCH_SCAN_STATE cur_state = 0; + tBTM_STATUS btm_status = 0; + UINT8 *p_data = NULL; + + if (len < 2) { + BTM_TRACE_ERROR("wrong length for btm_ble_batch_scan_vsc_cmpl_cback"); + btm_ble_batchscan_deq_op_q(&opcode, &cur_state, &cb_evt, &ref_value); + return; + } + + STREAM_TO_UINT8(status, p); + STREAM_TO_UINT8(subcode, p); + + btm_ble_batchscan_deq_op_q(&opcode, &cur_state, &cb_evt, &ref_value); + + BTM_TRACE_DEBUG("btm_ble_batchscan op_code = %02x state = %02x cb_evt = %02x,ref_value=%d", + opcode, cur_state, cb_evt, ref_value); + + if (opcode != subcode) { + BTM_TRACE_ERROR("Got unexpected VSC cmpl, expected: %d got: %d", subcode, opcode); + return; + } + + switch (subcode) { + case BTM_BLE_BATCH_SCAN_ENB_DISAB_CUST_FEATURE: { + if (BTM_SUCCESS == status && BTM_BLE_SCAN_ENABLE_CALLED == cur_state) { + ble_batchscan_cb.cur_state = BTM_BLE_SCAN_ENABLED_STATE; + } else if (BTM_BLE_SCAN_ENABLE_CALLED == cur_state) { + BTM_TRACE_ERROR("SCAN_ENB_DISAB_CUST_FEATURE - Invalid state after enb"); + ble_batchscan_cb.cur_state = BTM_BLE_SCAN_INVALID_STATE; + } + + BTM_TRACE_DEBUG("BTM_BLE_BATCH_SCAN_ENB_DISAB_CUST_FEAT status = %d, state: %d,evt=%d", + status, ble_batchscan_cb.cur_state, cb_evt); + + if (cb_evt != 0 && NULL != ble_batchscan_cb.p_setup_cback) { + ble_batchscan_cb.p_setup_cback(cb_evt, ref_value, status); + } + break; + } + + case BTM_BLE_BATCH_SCAN_SET_STORAGE_PARAM: { + BTM_TRACE_DEBUG("BTM_BLE_BATCH_SCAN_SET_STORAGE_PARAM status = %d, evt=%d", + status, cb_evt); + if (cb_evt != 0 && NULL != ble_batchscan_cb.p_setup_cback) { + ble_batchscan_cb.p_setup_cback(cb_evt, ref_value, status); + } + break; + } + + case BTM_BLE_BATCH_SCAN_SET_PARAMS: { + BTM_TRACE_DEBUG("BTM_BLE_BATCH_SCAN_SET_PARAMS status = %d,evt=%d", status, cb_evt); + + if (BTM_BLE_SCAN_DISABLE_CALLED == cur_state) { + if (BTM_SUCCESS == status) { + ble_batchscan_cb.cur_state = BTM_BLE_SCAN_DISABLED_STATE; + } else { + BTM_TRACE_ERROR("BTM_BLE_BATCH_SCAN_SET_PARAMS - Invalid state after disabled"); + ble_batchscan_cb.cur_state = BTM_BLE_SCAN_INVALID_STATE; + } + } + + if (cb_evt != 0 && NULL != ble_batchscan_cb.p_setup_cback) { + ble_batchscan_cb.p_setup_cback(cb_evt, ref_value, status); + } + break; + } + + case BTM_BLE_BATCH_SCAN_READ_RESULTS: { + if (cb_evt != 0 && NULL != ble_batchscan_cb.p_scan_rep_cback) { + STREAM_TO_UINT8(report_format, p); + STREAM_TO_UINT8(num_records, p); + p = (uint8_t *)(p_params->p_param_buf + 4); + BTM_TRACE_DEBUG("BTM_BLE_BATCH_SCAN_READ_RESULTS status=%d,len=%d,rec=%d", + status, len - 4, num_records); + + if (0 == num_records) { + btm_ble_batchscan_deq_rep_data(report_format, &ref_value, &num_records, + &p_data, &data_len); + if (NULL != ble_batchscan_cb.p_scan_rep_cback) { + ble_batchscan_cb.p_scan_rep_cback(ref_value, report_format, num_records, + data_len, p_data, status); + } + } else { + if ((len - 4) > 0) { + btm_ble_batchscan_enq_rep_data(report_format, num_records, p, len - 4); + /* More records could be in the buffer and needs to be pulled out */ + btm_status = btm_ble_read_batchscan_reports(report_format, ref_value); + if (BTM_CMD_STARTED != btm_status) { + btm_ble_batchscan_deq_rep_data(report_format, &ref_value, &num_records, + &p_data, &data_len); + /* Send whatever is available, in case of a command failure */ + if (NULL != ble_batchscan_cb.p_scan_rep_cback && NULL != p_data) { + ble_batchscan_cb.p_scan_rep_cback(ref_value, report_format, + num_records, data_len, p_data, status); + } + } + } + } + } + break; + } + + default: + break; + } + + return; +} + +/******************************************************************************* +** +** Function btm_ble_set_storage_config +** +** Description This function writes the storage configuration in controller +** +** Parameters batch_scan_full_max -Max storage space (in %) allocated to full scanning +** batch_scan_trunc_max -Max storage space (in %) allocated to truncated scanning +** batch_scan_notify_threshold - Setup notification level based on total space +** +** Returns status +** +*******************************************************************************/ +tBTM_STATUS btm_ble_set_storage_config(UINT8 batch_scan_full_max, UINT8 batch_scan_trunc_max, + UINT8 batch_scan_notify_threshold) +{ + tBTM_STATUS status = BTM_NO_RESOURCES; + UINT8 param[BTM_BLE_BATCH_SCAN_STORAGE_CFG_LEN], *pp; + + pp = param; + memset(param, 0, BTM_BLE_BATCH_SCAN_STORAGE_CFG_LEN); + + UINT8_TO_STREAM (pp, BTM_BLE_BATCH_SCAN_SET_STORAGE_PARAM); + UINT8_TO_STREAM (pp, batch_scan_full_max); + UINT8_TO_STREAM (pp, batch_scan_trunc_max); + UINT8_TO_STREAM (pp, batch_scan_notify_threshold); + + if ((status = BTM_VendorSpecificCommand (HCI_BLE_BATCH_SCAN_OCF, + BTM_BLE_BATCH_SCAN_STORAGE_CFG_LEN, param, + btm_ble_batchscan_vsc_cmpl_cback)) != BTM_CMD_STARTED) { + BTM_TRACE_ERROR("btm_ble_set_storage_config %d", status); + return BTM_ILLEGAL_VALUE; + } + + return status; +} + +/******************************************************************************* +** +** Function btm_ble_set_batchscan_param +** +** Description This function writes the batch scan params in controller +** +** Parameters scan_mode -Batch scan mode +** scan_interval - Scan interval +** scan_window - Scan window +** discard_rule -Discard rules +** addr_type - Address type +** +** Returns status +** +*******************************************************************************/ +tBTM_STATUS btm_ble_set_batchscan_param(tBTM_BLE_BATCH_SCAN_MODE scan_mode, + UINT32 scan_interval, UINT32 scan_window, tBLE_ADDR_TYPE addr_type, + tBTM_BLE_DISCARD_RULE discard_rule) +{ + tBTM_STATUS status = BTM_NO_RESOURCES; + UINT8 scan_param[BTM_BLE_BATCH_SCAN_PARAM_CONFIG_LEN], *pp_scan; + + pp_scan = scan_param; + memset(scan_param, 0, BTM_BLE_BATCH_SCAN_PARAM_CONFIG_LEN); + + // Override param and decide addr_type based on own addr type + // TODO: Remove upper layer parameter? + addr_type = btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type; + + UINT8_TO_STREAM (pp_scan, BTM_BLE_BATCH_SCAN_SET_PARAMS); + UINT8_TO_STREAM (pp_scan, scan_mode); + UINT32_TO_STREAM (pp_scan, scan_window); + UINT32_TO_STREAM (pp_scan, scan_interval); + UINT8_TO_STREAM (pp_scan, addr_type); + UINT8_TO_STREAM (pp_scan, discard_rule); + + if ((status = BTM_VendorSpecificCommand (HCI_BLE_BATCH_SCAN_OCF, + BTM_BLE_BATCH_SCAN_PARAM_CONFIG_LEN, + scan_param, btm_ble_batchscan_vsc_cmpl_cback)) != BTM_CMD_STARTED) { + BTM_TRACE_ERROR("btm_ble_set_batchscan_param %d", status); + return BTM_ILLEGAL_VALUE; + } + + return status; +} + +/******************************************************************************* +** +** Function btm_ble_enable_disable_batchscan +** +** Description This function enables the customer specific feature in controller +** +** Parameters enable_disable: true - enable, false - disable +** +** Returns status +** +*******************************************************************************/ +tBTM_STATUS btm_ble_enable_disable_batchscan(BOOLEAN should_enable) +{ + tBTM_STATUS status = BTM_NO_RESOURCES; + UINT8 shld_enable = 0x01; + UINT8 enable_param[BTM_BLE_BATCH_SCAN_ENB_DISB_LEN], *pp_enable; + + if (!should_enable) { + shld_enable = 0x00; + } + + if (should_enable) { + pp_enable = enable_param; + memset(enable_param, 0, BTM_BLE_BATCH_SCAN_ENB_DISB_LEN); + + UINT8_TO_STREAM (pp_enable, BTM_BLE_BATCH_SCAN_ENB_DISAB_CUST_FEATURE); + UINT8_TO_STREAM (pp_enable, shld_enable); + + if ((status = BTM_VendorSpecificCommand(HCI_BLE_BATCH_SCAN_OCF, + BTM_BLE_BATCH_SCAN_ENB_DISB_LEN, enable_param, + btm_ble_batchscan_vsc_cmpl_cback)) != BTM_CMD_STARTED) { + status = BTM_MODE_UNSUPPORTED; + BTM_TRACE_ERROR("btm_ble_enable_disable_batchscan %d", status); + return BTM_ILLEGAL_VALUE; + } + } else if ((status = btm_ble_set_batchscan_param(BTM_BLE_BATCH_SCAN_MODE_DISABLE, + ble_batchscan_cb.scan_interval, ble_batchscan_cb.scan_window, + ble_batchscan_cb.addr_type, ble_batchscan_cb.discard_rule)) != BTM_CMD_STARTED) { + status = BTM_MODE_UNSUPPORTED; + BTM_TRACE_ERROR("btm_ble_enable_disable_batchscan %d", status); + return BTM_ILLEGAL_VALUE; + } + + if (should_enable) { + ble_batchscan_cb.cur_state = BTM_BLE_SCAN_ENABLE_CALLED; + } else { + ble_batchscan_cb.cur_state = BTM_BLE_SCAN_DISABLE_CALLED; + } + return status; +} + +/******************************************************************************* +** +** Function BTM_BleSetStorageConfig +** +** Description This function is called to write storage config params. +** +** Parameters: batch_scan_full_max - Max storage space (in %) allocated to full style +** batch_scan_trunc_max - Max storage space (in %) allocated to trunc style +** batch_scan_notify_threshold - Setup notification level based on total space +** p_setup_cback - Setup callback pointer +** p_thres_cback - Threshold callback pointer +** p_rep_cback - Reports callback pointer +** ref_value - Reference value +** +** Returns tBTM_STATUS +** +*******************************************************************************/ +tBTM_STATUS BTM_BleSetStorageConfig(UINT8 batch_scan_full_max, UINT8 batch_scan_trunc_max, + UINT8 batch_scan_notify_threshold, + tBTM_BLE_SCAN_SETUP_CBACK *p_setup_cback, + tBTM_BLE_SCAN_THRESHOLD_CBACK *p_thres_cback, + tBTM_BLE_SCAN_REP_CBACK *p_rep_cback, + tBTM_BLE_REF_VALUE ref_value) +{ + tBTM_STATUS status = BTM_NO_RESOURCES; + tBTM_BLE_VSC_CB cmn_ble_vsc_cb; + + BTM_TRACE_EVENT (" BTM_BleSetStorageConfig: %d, %d, %d, %d, %d", + ble_batchscan_cb.cur_state, ref_value, batch_scan_full_max, batch_scan_trunc_max, + batch_scan_notify_threshold); + + if (!controller_get_interface()->supports_ble()) { + return BTM_ILLEGAL_VALUE; + } + + BTM_BleGetVendorCapabilities(&cmn_ble_vsc_cb); + + if (0 == cmn_ble_vsc_cb.tot_scan_results_strg) { + BTM_TRACE_ERROR("Controller does not support batch scan"); + return BTM_ERR_PROCESSING; + } + + ble_batchscan_cb.p_setup_cback = p_setup_cback; + ble_batchscan_cb.p_thres_cback = p_thres_cback; + ble_batchscan_cb.p_scan_rep_cback = p_rep_cback; + ble_batchscan_cb.ref_value = ref_value; + + if (batch_scan_full_max > BTM_BLE_ADV_SCAN_FULL_MAX || + batch_scan_trunc_max > BTM_BLE_ADV_SCAN_TRUNC_MAX || + batch_scan_notify_threshold > BTM_BLE_ADV_SCAN_THR_MAX) { + BTM_TRACE_ERROR("Illegal set storage config params"); + return BTM_ILLEGAL_VALUE; + } + + if (BTM_BLE_SCAN_INVALID_STATE == ble_batchscan_cb.cur_state || + BTM_BLE_SCAN_DISABLED_STATE == ble_batchscan_cb.cur_state || + BTM_BLE_SCAN_DISABLE_CALLED == ble_batchscan_cb.cur_state) { + status = btm_ble_enable_disable_batchscan(TRUE); + if (BTM_CMD_STARTED != status) { + return status; + } + + ble_batchscan_cb.cur_state = BTM_BLE_SCAN_ENABLE_CALLED; + btm_ble_batchscan_enq_op_q(BTM_BLE_BATCH_SCAN_ENB_DISAB_CUST_FEATURE, + BTM_BLE_SCAN_ENABLE_CALLED, 0, ref_value); + } + + status = btm_ble_set_storage_config(batch_scan_full_max, batch_scan_trunc_max, + batch_scan_notify_threshold); + if (BTM_CMD_STARTED != status) { + return status; + } + /* The user needs to be provided scan config storage event */ + btm_ble_batchscan_enq_op_q(BTM_BLE_BATCH_SCAN_SET_STORAGE_PARAM, ble_batchscan_cb.cur_state, + BTM_BLE_BATCH_SCAN_CFG_STRG_EVT, ref_value); + + return status; +} + + +/******************************************************************************* +** +** Function BTM_BleEnableBatchScan +** +** Description This function is called to configure and enable batch scanning +** +** Parameters: scan_mode -Batch scan mode +** scan_interval - Scan interval value +** scan_window - Scan window value +** discard_rule - Data discard rule +** ref_value - Reference value +** +** Returns tBTM_STATUS +** +*******************************************************************************/ +tBTM_STATUS BTM_BleEnableBatchScan(tBTM_BLE_BATCH_SCAN_MODE scan_mode, + UINT32 scan_interval, UINT32 scan_window, tBLE_ADDR_TYPE addr_type, + tBTM_BLE_DISCARD_RULE discard_rule, tBTM_BLE_REF_VALUE ref_value) +{ + tBTM_STATUS status = BTM_NO_RESOURCES; + tBTM_BLE_VSC_CB cmn_ble_vsc_cb; + BTM_TRACE_EVENT (" BTM_BleEnableBatchScan: %d, %d, %d, %d, %d, %d", + scan_mode, scan_interval, scan_window, addr_type, discard_rule, ref_value); + + if (!controller_get_interface()->supports_ble()) { + return BTM_ILLEGAL_VALUE; + } + + BTM_BleGetVendorCapabilities(&cmn_ble_vsc_cb); + + if (0 == cmn_ble_vsc_cb.tot_scan_results_strg) { + BTM_TRACE_ERROR("Controller does not support batch scan"); + return BTM_ERR_PROCESSING; + } + + BTM_TRACE_DEBUG("BTM_BleEnableBatchScan: %d, %x, %x, %d, %d", scan_mode, scan_interval, + scan_window, discard_rule, ble_batchscan_cb.cur_state); + + /* Only 16 bits will be used for scan interval and scan window as per agreement with Google */ + /* So the standard LE range would suffice for scan interval and scan window */ + if ((BTM_BLE_ISVALID_PARAM(scan_interval, BTM_BLE_SCAN_INT_MIN, BTM_BLE_SCAN_INT_MAX) || + BTM_BLE_ISVALID_PARAM(scan_window, BTM_BLE_SCAN_WIN_MIN, BTM_BLE_SCAN_WIN_MAX)) + && (BTM_BLE_BATCH_SCAN_MODE_PASS == scan_mode || BTM_BLE_BATCH_SCAN_MODE_ACTI == scan_mode + || BTM_BLE_BATCH_SCAN_MODE_PASS_ACTI == scan_mode) + && (BTM_BLE_DISCARD_OLD_ITEMS == discard_rule || + BTM_BLE_DISCARD_LOWER_RSSI_ITEMS == discard_rule)) { + if (BTM_BLE_SCAN_INVALID_STATE == ble_batchscan_cb.cur_state || + BTM_BLE_SCAN_DISABLED_STATE == ble_batchscan_cb.cur_state || + BTM_BLE_SCAN_DISABLE_CALLED == ble_batchscan_cb.cur_state) { + status = btm_ble_enable_disable_batchscan(TRUE); + if (BTM_CMD_STARTED != status) { + return status; + } + btm_ble_batchscan_enq_op_q(BTM_BLE_BATCH_SCAN_ENB_DISAB_CUST_FEATURE, + BTM_BLE_SCAN_ENABLE_CALLED, 0, ref_value); + } + + ble_batchscan_cb.scan_mode = scan_mode; + ble_batchscan_cb.scan_interval = scan_interval; + ble_batchscan_cb.scan_window = scan_window; + ble_batchscan_cb.addr_type = addr_type; + ble_batchscan_cb.discard_rule = discard_rule; + /* This command starts batch scanning, if enabled */ + status = btm_ble_set_batchscan_param(scan_mode, scan_interval, scan_window, addr_type, + discard_rule); + if (BTM_CMD_STARTED != status) { + return status; + } + + /* The user needs to be provided scan enable event */ + btm_ble_batchscan_enq_op_q(BTM_BLE_BATCH_SCAN_SET_PARAMS, ble_batchscan_cb.cur_state, + BTM_BLE_BATCH_SCAN_ENABLE_EVT, ref_value); + } else { + BTM_TRACE_ERROR("Illegal enable scan params"); + return BTM_ILLEGAL_VALUE; + } + return status; +} + +/******************************************************************************* +** +** Function BTM_BleDisableBatchScan +** +** Description This function is called to disable batch scanning +** +** Parameters: ref_value - Reference value +** +** Returns tBTM_STATUS +** +*******************************************************************************/ +tBTM_STATUS BTM_BleDisableBatchScan(tBTM_BLE_REF_VALUE ref_value) +{ + tBTM_STATUS status = BTM_NO_RESOURCES; + tBTM_BLE_VSC_CB cmn_ble_vsc_cb; + BTM_TRACE_EVENT (" BTM_BleDisableBatchScan"); + + if (!controller_get_interface()->supports_ble()) { + return BTM_ILLEGAL_VALUE; + } + + BTM_BleGetVendorCapabilities(&cmn_ble_vsc_cb); + + if (0 == cmn_ble_vsc_cb.tot_scan_results_strg) { + BTM_TRACE_ERROR("Controller does not support batch scan"); + return BTM_ERR_PROCESSING; + } + + status = btm_ble_enable_disable_batchscan(FALSE); + if (BTM_CMD_STARTED == status) { + /* The user needs to be provided scan disable event */ + btm_ble_batchscan_enq_op_q(BTM_BLE_BATCH_SCAN_SET_PARAMS, + BTM_BLE_SCAN_DISABLE_CALLED, BTM_BLE_BATCH_SCAN_DISABLE_EVT, + ref_value); + } + + return status; +} + +/******************************************************************************* +** +** Function BTM_BleReadScanReports +** +** Description This function is called to start reading batch scan reports +** +** Parameters: scan_mode - Batch scan mode +** ref_value - Reference value +** +** Returns tBTM_STATUS +** +*******************************************************************************/ +tBTM_STATUS BTM_BleReadScanReports(tBTM_BLE_BATCH_SCAN_MODE scan_mode, + tBTM_BLE_REF_VALUE ref_value) +{ + tBTM_STATUS status = BTM_NO_RESOURCES; + tBTM_BLE_VSC_CB cmn_ble_vsc_cb; + UINT8 read_scan_mode = 0; + UINT8 *p_data = NULL, num_records = 0; + UINT16 data_len = 0; + + BTM_TRACE_EVENT (" BTM_BleReadScanReports; %d, %d", scan_mode, ref_value); + + if (!controller_get_interface()->supports_ble()) { + return BTM_ILLEGAL_VALUE; + } + + BTM_BleGetVendorCapabilities(&cmn_ble_vsc_cb); + + if (0 == cmn_ble_vsc_cb.tot_scan_results_strg) { + BTM_TRACE_ERROR("Controller does not support batch scan"); + return BTM_ERR_PROCESSING; + } + + /* Check if the requested scan mode has already been setup by the user */ + read_scan_mode = ble_batchscan_cb.scan_mode & BTM_BLE_BATCH_SCAN_MODE_ACTI; + if (0 == read_scan_mode) { + read_scan_mode = ble_batchscan_cb.scan_mode & BTM_BLE_BATCH_SCAN_MODE_PASS; + } + + /* Check only for modes, as scan reports can be called after disabling batch scan */ + if (read_scan_mode > 0 && (BTM_BLE_BATCH_SCAN_MODE_PASS == scan_mode || + BTM_BLE_BATCH_SCAN_MODE_ACTI == scan_mode)) { + status = btm_ble_batchscan_enq_rep_q(scan_mode, ref_value); + if (BTM_SUCCESS == status) { + status = btm_ble_read_batchscan_reports(scan_mode, ref_value); + if (BTM_CMD_STARTED != status) { + btm_ble_batchscan_deq_rep_data(scan_mode, &ref_value, + &num_records, &p_data, &data_len); + } + } + } else { + BTM_TRACE_ERROR("Illegal read scan params: %d, %d, %d", read_scan_mode, scan_mode, + ble_batchscan_cb.cur_state); + return BTM_ILLEGAL_VALUE; + } + return status; +} + + +/******************************************************************************* +** +** Function BTM_BleTrackAdvertiser +** +** Description This function is called to setup the callback for tracking advertisers +** +** Parameters: p_track_cback - Tracking callback pointer +** ref_value - Reference value +** +** Returns tBTM_STATUS +** +*******************************************************************************/ +tBTM_STATUS BTM_BleTrackAdvertiser(tBTM_BLE_TRACK_ADV_CBACK *p_track_cback, + tBTM_BLE_REF_VALUE ref_value) +{ + tBTM_BLE_VSC_CB cmn_ble_vsc_cb; + BTM_TRACE_EVENT (" BTM_BleTrackAdvertiser"); + if (!controller_get_interface()->supports_ble()) { + return BTM_ILLEGAL_VALUE; + } + + BTM_BleGetVendorCapabilities(&cmn_ble_vsc_cb); + + if (0 == cmn_ble_vsc_cb.tot_scan_results_strg) { + BTM_TRACE_ERROR("Controller does not support scan storage"); + return BTM_ERR_PROCESSING; + } + + ble_advtrack_cb.p_track_cback = p_track_cback; + ble_advtrack_cb.ref_value = ref_value; + return BTM_CMD_STARTED; +} + +/******************************************************************************* +** +** Function btm_ble_batchscan_init +** +** Description This function initialize the batch scan control block. +** +** Parameters None +** +** Returns status +** +*******************************************************************************/ +void btm_ble_batchscan_init(void) +{ +#if BTM_DYNAMIC_MEMORY == TRUE + ble_batchscan_cb_ptr = (tBTM_BLE_BATCH_SCAN_CB *)osi_malloc(sizeof(tBTM_BLE_BATCH_SCAN_CB)); + ble_advtrack_cb_ptr = (tBTM_BLE_ADV_TRACK_CB *)osi_malloc(sizeof(tBTM_BLE_ADV_TRACK_CB)); + if (ble_batchscan_cb_ptr == NULL || ble_advtrack_cb_ptr == NULL) { + BTM_TRACE_ERROR("%s malloc failed", __func__); + return; + } +#endif + BTM_TRACE_EVENT (" btm_ble_batchscan_init"); + memset(&ble_batchscan_cb, 0, sizeof(tBTM_BLE_BATCH_SCAN_CB)); + memset(&ble_advtrack_cb, 0, sizeof(tBTM_BLE_ADV_TRACK_CB)); + BTM_RegisterForVSEvents(btm_ble_batchscan_filter_track_adv_vse_cback, TRUE); +} + +/******************************************************************************* +** +** Function btm_ble_batchscan_cleanup +** +** Description This function cleans the batch scan control block. +** +** Parameters None +** +** Returns void +** +*******************************************************************************/ +void btm_ble_batchscan_cleanup(void) +{ + int index = 0; + BTM_TRACE_EVENT (" btm_ble_batchscan_cleanup"); + + for (index = 0; index < BTM_BLE_BATCH_REP_MAIN_Q_SIZE; index++) { + if (NULL != ble_batchscan_cb.main_rep_q.p_data[index]) { + osi_free(ble_batchscan_cb.main_rep_q.p_data[index]); + ble_batchscan_cb.main_rep_q.p_data[index] = NULL; + } + } + + memset(&ble_batchscan_cb, 0, sizeof(tBTM_BLE_BATCH_SCAN_CB)); + memset(&ble_advtrack_cb, 0, sizeof(tBTM_BLE_ADV_TRACK_CB)); + +#if BTM_DYNAMIC_MEMORY == TRUE + osi_free(ble_batchscan_cb_ptr); + osi_free(ble_advtrack_cb_ptr); + ble_batchscan_cb_ptr = NULL; + ble_advtrack_cb_ptr = NULL; +#endif +} + +#endif diff --git a/lib/bt/host/bluedroid/stack/btm/btm_ble_bgconn.c b/lib/bt/host/bluedroid/stack/btm/btm_ble_bgconn.c new file mode 100644 index 00000000..4cb282b7 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/btm/btm_ble_bgconn.c @@ -0,0 +1,836 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains functions for BLE whitelist operation. + * + ******************************************************************************/ + +#include <string.h> +#include "common/bt_trace.h" +#include "device/controller.h" +#include "osi/allocator.h" +#include "osi/hash_map.h" +#include "stack/bt_types.h" +#include "stack/btu.h" +#include "btm_int.h" +#include "l2c_int.h" +#include "stack/hcimsgs.h" +//#include "bt_utils.h" + +#ifndef BTM_BLE_SCAN_PARAM_TOUT +#define BTM_BLE_SCAN_PARAM_TOUT 50 /* 50 seconds */ +#endif + +#if (BLE_INCLUDED == TRUE) + +static void btm_suspend_wl_activity(tBTM_BLE_WL_STATE wl_state); +static void btm_wl_update_to_controller(void); + +// Unfortunately (for now?) we have to maintain a copy of the device whitelist +// on the host to determine if a device is pending to be connected or not. This +// controls whether the host should keep trying to scan for whitelisted +// peripherals or not. +// TODO: Move all of this to controller/le/background_list or similar? +static const size_t background_connection_buckets = 42; +static hash_map_t *background_connections = NULL; + +typedef struct background_connection_t { + bt_bdaddr_t address; +} background_connection_t; + +static bool bdaddr_equality_fn(const void *x, const void *y) +{ + return bdaddr_equals((bt_bdaddr_t *)x, (bt_bdaddr_t *)y); +} + +static void background_connections_lazy_init(void) +{ + if (!background_connections) { + background_connections = hash_map_new(background_connection_buckets, + hash_function_bdaddr, NULL, osi_free_func, bdaddr_equality_fn); + assert(background_connections); + } +} + +static BOOLEAN background_connection_add(bt_bdaddr_t *address) +{ + assert(address); + background_connections_lazy_init(); + background_connection_t *connection = hash_map_get(background_connections, address); + if (!connection) { + connection = osi_calloc(sizeof(background_connection_t)); + connection->address = *address; + hash_map_set(background_connections, &(connection->address), connection); + return TRUE; + } + return FALSE; +} + +static BOOLEAN background_connection_remove(bt_bdaddr_t *address) +{ + if (address && background_connections) { + return hash_map_erase(background_connections, address); + } + return FALSE; +} + +static void background_connections_clear(void) +{ + if (background_connections) { + hash_map_clear(background_connections); + } +} + +static bool background_connections_pending_cb(hash_map_entry_t *hash_entry, void *context) +{ + bool *pending_connections = context; + background_connection_t *connection = hash_entry->data; + const bool connected = BTM_IsAclConnectionUp(connection->address.address, BT_TRANSPORT_LE); + if (!connected) { + *pending_connections = true; + return false; + } + return true; +} + +static bool background_connections_pending(void) +{ + bool pending_connections = false; + if (background_connections) { + hash_map_foreach(background_connections, background_connections_pending_cb, &pending_connections); + } + return pending_connections; +} + +/******************************************************************************* +** +** Function btm_update_scanner_filter_policy +** +** Description This function updates the filter policy of scanner +*******************************************************************************/ +void btm_update_scanner_filter_policy(tBTM_BLE_SFP scan_policy) +{ + tBTM_BLE_INQ_CB *p_inq = &btm_cb.ble_ctr_cb.inq_var; + + UINT32 scan_interval = !p_inq->scan_interval ? BTM_BLE_GAP_DISC_SCAN_INT : p_inq->scan_interval; + UINT32 scan_window = !p_inq->scan_window ? BTM_BLE_GAP_DISC_SCAN_WIN : p_inq->scan_window; + + BTM_TRACE_EVENT ("%s\n", __func__); + + p_inq->sfp = scan_policy; + p_inq->scan_type = p_inq->scan_type == BTM_BLE_SCAN_MODE_NONE ? BTM_BLE_SCAN_MODE_ACTI : p_inq->scan_type; + + if (btm_cb.cmn_ble_vsc_cb.extended_scan_support == 0) { + btsnd_hcic_ble_set_scan_params(p_inq->scan_type, (UINT16)scan_interval, + (UINT16)scan_window, + btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type, + scan_policy); + } else { + btm_ble_send_extended_scan_params(p_inq->scan_type, scan_interval, scan_window, + btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type, + scan_policy); + } +} +/******************************************************************************* +** +** Function btm_add_dev_to_controller +** +** Description This function load the device into controller white list +*******************************************************************************/ +BOOLEAN btm_add_dev_to_controller (BOOLEAN to_add, BD_ADDR bd_addr, tBLE_ADDR_TYPE wl_addr_type) +{ + /* + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bd_addr); + tBLE_ADDR_TYPE addr_type = BLE_ADDR_PUBLIC; + BOOLEAN started = FALSE; + BD_ADDR dummy_bda = {0}; + tBT_DEVICE_TYPE dev_type; + + if (p_dev_rec != NULL && + p_dev_rec->device_type & BT_DEVICE_TYPE_BLE) { + if (to_add) { + if (p_dev_rec->ble.ble_addr_type == BLE_ADDR_PUBLIC || !BTM_BLE_IS_RESOLVE_BDA(bd_addr)) { + started = btsnd_hcic_ble_add_white_list (p_dev_rec->ble.ble_addr_type, bd_addr); + p_dev_rec->ble.in_controller_list |= BTM_WHITE_LIST_BIT; + } else if (memcmp(p_dev_rec->ble.static_addr, bd_addr, BD_ADDR_LEN) != 0 && + memcmp(p_dev_rec->ble.static_addr, dummy_bda, BD_ADDR_LEN) != 0) { + started = btsnd_hcic_ble_add_white_list (p_dev_rec->ble.static_addr_type, + p_dev_rec->ble.static_addr); + p_dev_rec->ble.in_controller_list |= BTM_WHITE_LIST_BIT; + } + } else { + if (p_dev_rec->ble.ble_addr_type == BLE_ADDR_PUBLIC || !BTM_BLE_IS_RESOLVE_BDA(bd_addr)) { + started = btsnd_hcic_ble_remove_from_white_list (p_dev_rec->ble.ble_addr_type, bd_addr); + } + if (memcmp(p_dev_rec->ble.static_addr, dummy_bda, BD_ADDR_LEN) != 0 && + memcmp(p_dev_rec->ble.static_addr, bd_addr, BD_ADDR_LEN) != 0) { + started = btsnd_hcic_ble_remove_from_white_list (p_dev_rec->ble.static_addr_type, p_dev_rec->ble.static_addr); + } + p_dev_rec->ble.in_controller_list &= ~BTM_WHITE_LIST_BIT; + } + } // if not a known device, shall we add it? + else { + BTM_ReadDevInfo(bd_addr, &dev_type, &addr_type); + + 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); + } + } + + return started; + */ + + /* Controller do not support resolvable address now, only support public address and static random address */ + BOOLEAN started = FALSE; + if(wl_addr_type > BLE_ADDR_RANDOM) { + BTM_TRACE_ERROR("wl_addr_type is error\n"); + return started; + } + + if (to_add) { + started = btsnd_hcic_ble_add_white_list (wl_addr_type, bd_addr); + }else{ + started = btsnd_hcic_ble_remove_from_white_list (wl_addr_type, bd_addr); + } + + return started; + + +} +/******************************************************************************* +** +** Function btm_execute_wl_dev_operation +** +** Description execute the pending whitelist device operation(loading or removing) +*******************************************************************************/ +BOOLEAN btm_execute_wl_dev_operation(void) +{ + tBTM_BLE_WL_OP *p_dev_op = btm_cb.ble_ctr_cb.wl_op_q; + UINT8 i = 0; + BOOLEAN rt = TRUE; + + for (i = 0; i < BTM_BLE_MAX_BG_CONN_DEV_NUM && rt; i ++, p_dev_op ++) { + if (p_dev_op->in_use) { + rt = btm_add_dev_to_controller(p_dev_op->to_add, p_dev_op->bd_addr, p_dev_op->addr_type); + memset(p_dev_op, 0, sizeof(tBTM_BLE_WL_OP)); + } else { + break; + } + } + return rt; +} +/******************************************************************************* +** +** Function btm_enq_wl_dev_operation +** +** Description enqueue the pending whitelist device operation(loading or removing). +*******************************************************************************/ +void btm_enq_wl_dev_operation(BOOLEAN to_add, BD_ADDR bd_addr, tBLE_ADDR_TYPE addr_type) +{ + tBTM_BLE_WL_OP *p_dev_op = btm_cb.ble_ctr_cb.wl_op_q; + UINT8 i = 0; + + for (i = 0; i < BTM_BLE_MAX_BG_CONN_DEV_NUM; i ++, p_dev_op ++) { + if (p_dev_op->in_use && p_dev_op->addr_type == addr_type && !memcmp(p_dev_op->bd_addr, bd_addr, BD_ADDR_LEN)) { + p_dev_op->to_add = to_add; + return; + } else if (!p_dev_op->in_use) { + break; + } + } + if (i != BTM_BLE_MAX_BG_CONN_DEV_NUM) { + p_dev_op->in_use = TRUE; + p_dev_op->to_add = to_add; + p_dev_op->addr_type = addr_type; + memcpy(p_dev_op->bd_addr, bd_addr, BD_ADDR_LEN); + } else { + BTM_TRACE_ERROR("max pending WL operation reached, discard"); + } + return; +} + +/******************************************************************************* +** +** Function btm_update_dev_to_white_list +** +** Description This function adds or removes a device into/from +** the white list. +** +*******************************************************************************/ +BOOLEAN btm_update_dev_to_white_list(BOOLEAN to_add, BD_ADDR bd_addr, tBLE_ADDR_TYPE addr_type, tBTM_UPDATE_WHITELIST_CBACK *update_wl_cb) +{ + if(addr_type > BLE_ADDR_RANDOM) { + BTM_TRACE_ERROR("%s address type is error, unable to add device", __func__); + if (update_wl_cb){ + update_wl_cb(HCI_ERR_ILLEGAL_PARAMETER_FMT,to_add); + } + return FALSE; + } + + BD_ADDR invalid_rand_addr_a, invalid_rand_addr_b; + memset(invalid_rand_addr_a, 0xff, sizeof(BD_ADDR)); + memset(invalid_rand_addr_b, 0x00, sizeof(BD_ADDR)); + + // look for public address information + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev(bd_addr); + // p_dev_rec is created at bluetooth initialization, p_dev_rec->ble.static_addr maybe be all 0 before pairing + if(p_dev_rec && memcmp(invalid_rand_addr_b, p_dev_rec->ble.static_addr, BD_ADDR_LEN) != 0) { + memcpy(bd_addr, p_dev_rec->ble.static_addr, BD_ADDR_LEN); + addr_type = p_dev_rec->ble.static_addr_type; + } + + // The device to be added to white list must be public address or random address + if(addr_type == BLE_ADDR_RANDOM) { + /* + A static address is a 48-bit randomly generated address and shall meet the following requirements: + • The two most significant bits of the address shall be equal to 1 + • All bits of the random part of the address shall not be equal to 1 + • All bits of the random part of the address shall not be equal to 0 + */ + invalid_rand_addr_b[0] = invalid_rand_addr_b[0] | BT_STATIC_RAND_ADDR_MASK; + if(memcmp(invalid_rand_addr_a, bd_addr, BD_ADDR_LEN) != 0 + && memcmp(invalid_rand_addr_b, bd_addr, BD_ADDR_LEN) != 0){ + // do nothing + } else { + BTC_TRACE_ERROR(" controller not support resolvable address"); + if (update_wl_cb){ + update_wl_cb(HCI_ERR_ILLEGAL_PARAMETER_FMT,to_add); + } + return FALSE; + } + + } + + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + + if (to_add && p_cb->white_list_avail_size == 0) { + BTM_TRACE_ERROR("%s Whitelist full, unable to add device", __func__); + if (update_wl_cb){ + update_wl_cb(HCI_ERR_MEMORY_FULL,to_add); + } + return FALSE; + } + + if (to_add) { + /* added the bd_addr to the connection hash map queue */ + if(!background_connection_add((bt_bdaddr_t *)bd_addr)) { + /* if the bd_addr already exist in whitelist, just callback return TRUE */ + if (update_wl_cb){ + update_wl_cb(HCI_SUCCESS,to_add); + } + return TRUE; + } + } else { + /* remove the bd_addr to the connection hash map queue */ + if(!background_connection_remove((bt_bdaddr_t *)bd_addr)){ + /* if the bd_addr don't exist in whitelist, just callback return TRUE */ + if (update_wl_cb){ + update_wl_cb(HCI_SUCCESS,to_add); + } + return TRUE; + } + } + + if (update_wl_cb){ + //save add whitelist complete callback + p_cb->update_wl_cb = update_wl_cb; + } + /* stop the auto connect */ + btm_suspend_wl_activity(p_cb->wl_state); + /* save the bd_addr to the btm_cb env */ + btm_enq_wl_dev_operation(to_add, bd_addr, addr_type); + /* save the ba_addr to the controller white list */ + btm_wl_update_to_controller(); + return TRUE; +} + +/******************************************************************************* +** +** Function btm_ble_clear_white_list +** +** Description This function clears the white list. +** +*******************************************************************************/ +void btm_ble_clear_white_list (tBTM_UPDATE_WHITELIST_CBACK *update_wl_cb) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + + BTM_TRACE_EVENT ("btm_ble_clear_white_list"); + btsnd_hcic_ble_clear_white_list(); + background_connections_clear(); + + if (update_wl_cb) { + p_cb->update_wl_cb = update_wl_cb; + } +} + +/******************************************************************************* +** +** Function btm_ble_clear_white_list_complete +** +** Description Indicates white list cleared. +** +*******************************************************************************/ +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; + UNUSED(evt_len); + + BTM_TRACE_EVENT ("btm_ble_clear_white_list_complete"); + STREAM_TO_UINT8 (status, p_data); + + if (status == HCI_SUCCESS) { + p_cb->white_list_avail_size = controller_get_interface()->get_ble_white_list_size(); + } else { + BTM_TRACE_ERROR ("%s failed, status 0x%x\n", __func__, status); + } + + if (p_cb->update_wl_cb) { + (*p_cb->update_wl_cb)(status, BTM_WHITELIST_CLEAR); + } +} + +/******************************************************************************* +** +** Function btm_ble_white_list_init +** +** Description Initialize white list size +** +*******************************************************************************/ +void btm_ble_white_list_init(UINT8 white_list_size) +{ + BTM_TRACE_DEBUG("%s white_list_size = %d", __func__, white_list_size); + btm_cb.ble_ctr_cb.white_list_avail_size = white_list_size; +} + +/******************************************************************************* +** +** Function btm_ble_add_2_white_list_complete +** +** Description White list element added +** +*******************************************************************************/ +void btm_ble_add_2_white_list_complete(UINT8 status) +{ + BTM_TRACE_EVENT("%s status=%d", __func__, status); + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + if (status == HCI_SUCCESS) { + --btm_cb.ble_ctr_cb.white_list_avail_size; + } + // add whitelist complete callback + if (p_cb->update_wl_cb) + { + (*p_cb->update_wl_cb)(status, BTM_WHITELIST_ADD); + } + +} + +/******************************************************************************* +** +** Function btm_ble_remove_from_white_list_complete +** +** Description White list element removal complete +** +*******************************************************************************/ +void btm_ble_remove_from_white_list_complete(UINT8 *p, UINT16 evt_len) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + UNUSED(evt_len); + BTM_TRACE_EVENT ("%s status=%d", __func__, *p); + if (*p == HCI_SUCCESS) { + ++btm_cb.ble_ctr_cb.white_list_avail_size; + } + if (p_cb->update_wl_cb) + { + (*p_cb->update_wl_cb)(*p, BTM_WHITELIST_REMOVE); + } +} + +/******************************************************************************* +** +** 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; + UINT16 scan_win; + UINT8 own_addr_type = p_cb->addr_mgnt_cb.own_addr_type; + UINT8 peer_addr_type = BLE_ADDR_PUBLIC; + + if (start) { + if (p_cb->conn_state == BLE_CONN_IDLE && background_connections_pending() + && btm_ble_topology_check(BTM_BLE_STATE_INIT)) { + p_cb->wl_state |= BTM_BLE_WL_INIT; + + btm_execute_wl_dev_operation(); + +#if BLE_PRIVACY_SPT == TRUE + btm_ble_enable_resolving_list_for_platform(BTM_BLE_RL_INIT); +#endif + scan_int = (p_cb->scan_int == BTM_BLE_SCAN_PARAM_UNDEF) ? + BTM_BLE_SCAN_SLOW_INT_1 : p_cb->scan_int; + scan_win = (p_cb->scan_win == BTM_BLE_SCAN_PARAM_UNDEF) ? + BTM_BLE_SCAN_SLOW_WIN_1 : p_cb->scan_win; + +#if BLE_PRIVACY_SPT == TRUE + if (btm_cb.ble_ctr_cb.rl_state != BTM_BLE_RL_IDLE + && controller_get_interface()->supports_ble_privacy()) { + own_addr_type |= BLE_ADDR_TYPE_ID_BIT; + peer_addr_type |= BLE_ADDR_TYPE_ID_BIT; + } +#endif + + if (!btsnd_hcic_ble_create_ll_conn (scan_int, /* UINT16 scan_int */ + scan_win, /* UINT16 scan_win */ + 0x01, /* UINT8 white_list */ + peer_addr_type, /* UINT8 addr_type_peer */ + dummy_bda, /* BD_ADDR bda_peer */ + own_addr_type, /* 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; + p_cb->wl_state &= ~BTM_BLE_WL_INIT; + } else { + btm_ble_set_conn_st (BLE_BG_CONN); + } + } else { + exec = FALSE; + } + } else { + if (p_cb->conn_state == BLE_BG_CONN) { + btsnd_hcic_ble_create_conn_cancel(); + btm_ble_set_conn_st (BLE_CONN_CANCEL); + p_cb->wl_state &= ~BTM_BLE_WL_INIT; + } else { + BTM_TRACE_DEBUG("conn_st = %d, not in auto conn state, cannot stop", p_cb->conn_state); + exec = FALSE; + } + } + 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 connection 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; + UINT32 scan_int = p_cb->scan_int == BTM_BLE_SCAN_PARAM_UNDEF ? BTM_BLE_SCAN_FAST_INT : p_cb->scan_int; + UINT32 scan_win = p_cb->scan_win == BTM_BLE_SCAN_PARAM_UNDEF ? BTM_BLE_SCAN_FAST_WIN : p_cb->scan_win; + + BTM_TRACE_EVENT ("%s", __func__); + + if (start) { + if (!BTM_BLE_IS_SCAN_ACTIVE(p_cb->scan_activity)) { + if (p_select_cback != NULL) { + btm_cb.ble_ctr_cb.p_select_cback = p_select_cback; + } + + btm_execute_wl_dev_operation(); + + btm_update_scanner_filter_policy(SP_ADV_WL); + btm_cb.ble_ctr_cb.inq_var.scan_type = BTM_BLE_SCAN_MODE_PASS; + + /* Process advertising packets only from devices in the white list */ + if (btm_cb.cmn_ble_vsc_cb.extended_scan_support == 0) { + /* use passive scan by default */ + if (!btsnd_hcic_ble_set_scan_params(BTM_BLE_SCAN_MODE_PASS, + scan_int, + scan_win, + p_cb->addr_mgnt_cb.own_addr_type, + SP_ADV_WL)) { + return FALSE; + } + } else { + if (!btm_ble_send_extended_scan_params(BTM_BLE_SCAN_MODE_PASS, + scan_int, + scan_win, + p_cb->addr_mgnt_cb.own_addr_type, + SP_ADV_WL)) { + return FALSE; + } + } + + if (!btm_ble_topology_check(BTM_BLE_STATE_PASSIVE_SCAN)) { + BTM_TRACE_ERROR("peripheral device cannot initiate passive scan for a selective connection"); + return FALSE; + } else if (background_connections_pending()) { +#if BLE_PRIVACY_SPT == TRUE + btm_ble_enable_resolving_list_for_platform(BTM_BLE_RL_SCAN); +#endif + if (!btsnd_hcic_ble_set_scan_enable(TRUE, TRUE)) { /* duplicate filtering enabled */ + return FALSE; + } + /* mark up inquiry status flag */ + p_cb->scan_activity |= BTM_LE_SELECT_CONN_ACTIVE; + p_cb->wl_state |= BTM_BLE_WL_SCAN; + } + } else { + BTM_TRACE_ERROR("scan active, can not start selective connection procedure"); + return FALSE; + } + } else { /* disable selective connection mode */ + p_cb->scan_activity &= ~BTM_LE_SELECT_CONN_ACTIVE; + p_cb->p_select_cback = NULL; + p_cb->wl_state &= ~BTM_BLE_WL_SCAN; + + /* stop scanning */ + if (!BTM_BLE_IS_SCAN_ACTIVE(p_cb->scan_activity)) { + btm_ble_stop_scan(); /* duplicate filtering enabled */ + } + } + 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 connection procedure is started. +** +*******************************************************************************/ +void btm_ble_initiate_select_conn(BD_ADDR bda) +{ + BTM_TRACE_EVENT ("btm_ble_initiate_select_conn"); + + /* use direct connection procedure to initiate connection */ + if (!L2CA_ConnectFixedChnl(L2CAP_ATT_CID, bda, BLE_ADDR_UNKNOWN_TYPE, FALSE)) { + BTM_TRACE_ERROR("btm_ble_initiate_select_conn failed"); + } +} +/******************************************************************************* +** +** Function btm_ble_suspend_bg_conn +** +** Description This function is to suspend an active background connection +** procedure. +** +** Parameters none. +** +** Returns none. +** +*******************************************************************************/ +BOOLEAN btm_ble_suspend_bg_conn(void) +{ + BTM_TRACE_EVENT ("%s\n", __func__); + + if (btm_cb.ble_ctr_cb.bg_conn_type == BTM_BLE_CONN_AUTO) { + return btm_ble_start_auto_conn(FALSE); + } else if (btm_cb.ble_ctr_cb.bg_conn_type == BTM_BLE_CONN_SELECTIVE) { + return btm_ble_start_select_conn(FALSE, NULL); + } + + return FALSE; +} +/******************************************************************************* +** +** Function btm_suspend_wl_activity +** +** Description This function is to suspend white list related activity +** +** Returns none. +** +*******************************************************************************/ +static void btm_suspend_wl_activity(tBTM_BLE_WL_STATE wl_state) +{ + if (wl_state & BTM_BLE_WL_INIT) { + btm_ble_start_auto_conn(FALSE); + } + if (wl_state & BTM_BLE_WL_SCAN) { + btm_ble_start_select_conn(FALSE, NULL); + } + if (wl_state & BTM_BLE_WL_ADV) { + btm_ble_stop_adv(); + } + +} +/******************************************************************************* +** +** Function btm_resume_wl_activity +** +** Description This function is to resume white list related activity +** +** Returns none. +** +*******************************************************************************/ +void btm_resume_wl_activity(tBTM_BLE_WL_STATE wl_state) +{ + btm_ble_resume_bg_conn(); + if (wl_state & BTM_BLE_WL_ADV) { + btm_ble_start_adv(); + } + +} + +/******************************************************************************* +** +** Function btm_wl_update_to_controller +** +** Description This function is to update white list to controller +** +** Returns none. +** +*******************************************************************************/ +static void btm_wl_update_to_controller(void) +{ + /* whitelist will be added in the btm_ble_resume_bg_conn(), we do not + support background connection now, so we nedd to use btm_execute_wl_dev_operation + to add whitelist directly ,if we support background connection in the future, + please delete btm_execute_wl_dev_operation(). */ + btm_execute_wl_dev_operation(); + +} +/******************************************************************************* +** +** 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(void) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + BOOLEAN ret = FALSE; + + if (p_cb->bg_conn_type != BTM_BLE_CONN_NONE) { + 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) { + ret = btm_ble_start_select_conn(TRUE, btm_cb.ble_ctr_cb.p_select_cback); + } + } + + return ret; +} +/******************************************************************************* +** +** Function btm_ble_get_conn_st +** +** Description This function get BLE connection state +** +** Returns connection state +** +*******************************************************************************/ +tBTM_BLE_CONN_ST btm_ble_get_conn_st(void) +{ + return btm_cb.ble_ctr_cb.conn_state; +} +/******************************************************************************* +** +** Function btm_ble_set_conn_st +** +** Description This function set BLE connection state +** +** Returns None. +** +*******************************************************************************/ +void btm_ble_set_conn_st(tBTM_BLE_CONN_ST new_st) +{ + btm_cb.ble_ctr_cb.conn_state = new_st; + + if (new_st == BLE_BG_CONN || new_st == BLE_DIR_CONN) { + btm_ble_set_topology_mask(BTM_BLE_STATE_INIT_BIT); + } else { + btm_ble_clear_topology_mask(BTM_BLE_STATE_INIT_BIT); + } +} + +/******************************************************************************* +** +** Function btm_ble_enqueue_direct_conn_req +** +** Description This function enqueue the direct connection request +** +** Returns None. +** +*******************************************************************************/ +void btm_ble_enqueue_direct_conn_req(void *p_param) +{ + tBTM_BLE_CONN_REQ *p = (tBTM_BLE_CONN_REQ *)osi_malloc(sizeof(tBTM_BLE_CONN_REQ)); + + p->p_param = p_param; + + fixed_queue_enqueue(btm_cb.ble_ctr_cb.conn_pending_q, p, FIXED_QUEUE_MAX_TIMEOUT); +} +/******************************************************************************* +** +** Function btm_send_pending_direct_conn +** +** Description This function send the pending direct connection request in queue +** +** Returns TRUE if started, FALSE otherwise +** +*******************************************************************************/ +BOOLEAN btm_send_pending_direct_conn(void) +{ + tBTM_BLE_CONN_REQ *p_req; + BOOLEAN rt = FALSE; + + p_req = (tBTM_BLE_CONN_REQ*)fixed_queue_dequeue(btm_cb.ble_ctr_cb.conn_pending_q, 0); + if (p_req != NULL) { + rt = l2cble_init_direct_conn((tL2C_LCB *)(p_req->p_param)); + + osi_free((void *)p_req); + } + + return rt; +} + +#endif diff --git a/lib/bt/host/bluedroid/stack/btm/btm_ble_cont_energy.c b/lib/bt/host/bluedroid/stack/btm/btm_ble_cont_energy.c new file mode 100644 index 00000000..dd23d3e9 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/btm/btm_ble_cont_energy.c @@ -0,0 +1,108 @@ +/****************************************************************************** + * + * Copyright (C) 2014 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#include <string.h> +#include "common/bt_target.h" + +#if (BLE_INCLUDED == TRUE) +#include "stack/bt_types.h" +#include "stack/hcimsgs.h" +#include "stack/btu.h" +#include "btm_int.h" +//#include "bt_utils.h" +#include "stack/hcidefs.h" +#include "stack/btm_ble_api.h" + +tBTM_BLE_ENERGY_INFO_CB ble_energy_info_cb; + +/******************************************************************************* +** +** Function btm_ble_cont_energy_cmpl_cback +** +** Description Controller VSC complete callback +** +** Parameters +** +** Returns void +** +*******************************************************************************/ +void btm_ble_cont_energy_cmpl_cback (tBTM_VSC_CMPL *p_params) +{ + UINT8 *p = p_params->p_param_buf; + UINT16 len = p_params->param_len; + UINT8 status = 0; + UINT32 total_tx_time = 0, total_rx_time = 0, total_idle_time = 0, total_energy_used = 0; + + if (len < 17) { + BTM_TRACE_ERROR("wrong length for btm_ble_cont_energy_cmpl_cback"); + return; + } + + STREAM_TO_UINT8(status, p); + STREAM_TO_UINT32(total_tx_time, p); + STREAM_TO_UINT32(total_rx_time, p); + STREAM_TO_UINT32(total_idle_time, p); + STREAM_TO_UINT32(total_energy_used, p); + + BTM_TRACE_DEBUG("energy_info status=%d,tx_t=%u, rx_t=%u, ener_used=%u, idle_t=%u", + status, total_tx_time, total_rx_time, total_energy_used, total_idle_time); + + if (NULL != ble_energy_info_cb.p_ener_cback) { + ble_energy_info_cb.p_ener_cback(total_tx_time, total_rx_time, total_idle_time, + total_energy_used, status); + } + + return; +} + +/******************************************************************************* +** +** Function BTM_BleGetEnergyInfo +** +** Description This function obtains the energy info +** +** Parameters p_ener_cback - Callback pointer +** +** Returns status +** +*******************************************************************************/ +tBTM_STATUS BTM_BleGetEnergyInfo(tBTM_BLE_ENERGY_INFO_CBACK *p_ener_cback) +{ + tBTM_STATUS status = BTM_ILLEGAL_VALUE; + tBTM_BLE_VSC_CB cmn_ble_vsc_cb; + + BTM_BleGetVendorCapabilities(&cmn_ble_vsc_cb); + + BTM_TRACE_EVENT("BTM_BleGetEnergyInfo\n"); + + if (0 == cmn_ble_vsc_cb.energy_support) { + BTM_TRACE_ERROR("Controller does not support get energy info\n"); + return BTM_ERR_PROCESSING; + } + + ble_energy_info_cb.p_ener_cback = p_ener_cback; + if ((status = BTM_VendorSpecificCommand (HCI_BLE_ENERGY_INFO_OCF, 0, NULL, + btm_ble_cont_energy_cmpl_cback)) != BTM_CMD_STARTED) { + BTM_TRACE_ERROR("BTM_BleGetEnergyInfo status: %d", status); + return BTM_ILLEGAL_VALUE; + } + + return status; +} + +#endif diff --git a/lib/bt/host/bluedroid/stack/btm/btm_ble_gap.c b/lib/bt/host/bluedroid/stack/btm/btm_ble_gap.c new file mode 100644 index 00000000..fb27ba0f --- /dev/null +++ b/lib/bt/host/bluedroid/stack/btm/btm_ble_gap.c @@ -0,0 +1,4724 @@ +/****************************************************************************** + * + * Copyright (C) 2008-2014 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains functions for BLE GAP. + * + ******************************************************************************/ + +#include <string.h> +//#include <stdio.h> +#include <stddef.h> + +#include "stack/bt_types.h" +//#include "bt_utils.h" +#include "btm_int.h" +#include "stack/btm_ble_api.h" +#include "stack/btu.h" +#include "device/controller.h" +#include "stack/hcimsgs.h" +#include "stack/gap_api.h" +#include "hci/hci_layer.h" +#if BLE_INCLUDED == TRUE +#include "l2c_int.h" + +#include "stack/gattdefs.h" +#include "gatt_int.h" + +#include "btm_ble_int.h" +//#define LOG_TAG "bt_btm_ble" +//#include "osi/include/log.h" +#include "osi/osi.h" +#include "osi/mutex.h" + +#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 BTM_EXT_BLE_RMT_NAME_TIMEOUT 30 +#define MIN_ADV_LENGTH 2 +#define BTM_VSC_CHIP_CAPABILITY_RSP_LEN_L_RELEASE 9 + +#define BTM_BLE_GAP_ADV_RPT_BATCH_SIZE (10) + +#if BTM_DYNAMIC_MEMORY == FALSE +static tBTM_BLE_VSC_CB cmn_ble_gap_vsc_cb; +#else +static tBTM_BLE_VSC_CB *cmn_ble_gap_vsc_cb_ptr; +#define cmn_ble_gap_vsc_cb (*cmn_ble_gap_vsc_cb_ptr) +#endif + +#if BLE_VND_INCLUDED == TRUE +static tBTM_BLE_CTRL_FEATURES_CBACK *p_ctrl_le_feature_rd_cmpl_cback = NULL; +#endif + +tBTM_CallbackFunc conn_param_update_cb; +/******************************************************************************* +** 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); +UINT8 *btm_ble_build_adv_data(tBTM_BLE_AD_MASK *p_data_mask, UINT8 **p_dst, + tBTM_BLE_ADV_DATA *p_data); +static UINT8 btm_set_conn_mode_adv_init_addr(tBTM_BLE_INQ_CB *p_cb, + BD_ADDR_PTR p_peer_addr_ptr, + tBLE_ADDR_TYPE *p_peer_addr_type, + tBLE_ADDR_TYPE *p_own_addr_type); +static void btm_ble_stop_observe(void); +static void btm_ble_stop_discover(void); +static void btm_adv_pkt_handler(void *arg); +uint32_t BTM_BleUpdateOwnType(uint8_t *own_bda_type, tBTM_START_ADV_CMPL_CBACK *cb); + +#define BTM_BLE_INQ_RESULT 0x01 +#define BTM_BLE_OBS_RESULT 0x02 +#define BTM_BLE_SEL_CONN_RESULT 0x04 +#define BTM_BLE_DISCO_RESULT 0x08 + +static bool is_ble50_inter = false; + +void btm_ble_inter_set(bool extble_inter) +{ + is_ble50_inter = extble_inter; +} + +bool btm_ble_inter_get(void) +{ + return is_ble50_inter; +} + +/* LE states combo bit to check */ +const UINT8 btm_le_state_combo_tbl[BTM_BLE_STATE_MAX][BTM_BLE_STATE_MAX][2] = { + {/* single state support */ + {HCI_SUPP_LE_STATES_CONN_ADV_MASK, HCI_SUPP_LE_STATES_CONN_ADV_OFF}, /* conn_adv */ + {HCI_SUPP_LE_STATES_INIT_MASK, HCI_SUPP_LE_STATES_INIT_OFF}, /* init */ + {HCI_SUPP_LE_STATES_INIT_MASK, HCI_SUPP_LE_STATES_INIT_OFF}, /* master */ + {HCI_SUPP_LE_STATES_SLAVE_MASK, HCI_SUPP_LE_STATES_SLAVE_OFF}, /* slave */ + {HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_MASK, HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_OFF}, /* lo du dir adv */ + {HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_MASK, HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_OFF}, /* hi duty dir adv */ + {HCI_SUPP_LE_STATES_NON_CONN_ADV_MASK, HCI_SUPP_LE_STATES_NON_CONN_ADV_OFF}, /* non connectable adv */ + {HCI_SUPP_LE_STATES_PASS_SCAN_MASK, HCI_SUPP_LE_STATES_PASS_SCAN_OFF}, /* passive scan */ + {HCI_SUPP_LE_STATES_ACTIVE_SCAN_MASK, HCI_SUPP_LE_STATES_ACTIVE_SCAN_OFF}, /* active scan */ + {HCI_SUPP_LE_STATES_SCAN_ADV_MASK, HCI_SUPP_LE_STATESSCAN_ADV_OFF} /* scanable adv */ + }, + { /* conn_adv =0 */ + {0, 0}, /* conn_adv */ + {HCI_SUPP_LE_STATES_CONN_ADV_INIT_MASK, HCI_SUPP_LE_STATES_CONN_ADV_INIT_OFF}, /* init: 32 */ + {HCI_SUPP_LE_STATES_CONN_ADV_MASTER_MASK, HCI_SUPP_LE_STATES_CONN_ADV_MASTER_OFF}, /* master: 35 */ + {HCI_SUPP_LE_STATES_CONN_ADV_SLAVE_MASK, HCI_SUPP_LE_STATES_CONN_ADV_SLAVE_OFF}, /* slave: 38,*/ + {0, 0}, /* lo du dir adv */ + {0, 0}, /* hi duty dir adv */ + {0, 0}, /* non connectable adv */ + {HCI_SUPP_LE_STATES_CONN_ADV_PASS_SCAN_MASK, HCI_SUPP_LE_STATES_CONN_ADV_PASS_SCAN_OFF}, /* passive scan */ + {HCI_SUPP_LE_STATES_CONN_ADV_ACTIVE_SCAN_MASK, HCI_SUPP_LE_STATES_CONN_ADV_ACTIVE_SCAN_OFF}, /* active scan */ + {0, 0} /* scanable adv */ + }, + { /* init */ + {HCI_SUPP_LE_STATES_CONN_ADV_INIT_MASK, HCI_SUPP_LE_STATES_CONN_ADV_INIT_OFF}, /* conn_adv: 32 */ + {0, 0}, /* init */ + {HCI_SUPP_LE_STATES_INIT_MASTER_MASK, HCI_SUPP_LE_STATES_INIT_MASTER_OFF}, /* master 28 */ + {HCI_SUPP_LE_STATES_INIT_MASTER_SLAVE_MASK, HCI_SUPP_LE_STATES_INIT_MASTER_SLAVE_OFF}, /* slave 41 */ + {HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_INIT_MASK, HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_INIT_OFF} ,/* lo du dir adv 34 */ + {HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_INIT_MASK, HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_INIT_OFF}, /* hi duty dir adv 33 */ + {HCI_SUPP_LE_STATES_NON_CONN_INIT_MASK, HCI_SUPP_LE_STATES_NON_CONN_INIT_OFF}, /* non connectable adv */ + {HCI_SUPP_LE_STATES_PASS_SCAN_INIT_MASK, HCI_SUPP_LE_STATES_PASS_SCAN_INIT_OFF}, /* passive scan */ + {HCI_SUPP_LE_STATES_ACTIVE_SCAN_INIT_MASK, HCI_SUPP_LE_STATES_ACTIVE_SCAN_INIT_OFF}, /* active scan */ + {HCI_SUPP_LE_STATES_SCAN_ADV_INIT_MASK, HCI_SUPP_LE_STATES_SCAN_ADV_INIT_OFF} /* scanable adv */ + + }, + { /* master */ + {HCI_SUPP_LE_STATES_CONN_ADV_MASTER_MASK, HCI_SUPP_LE_STATES_CONN_ADV_MASTER_OFF}, /* conn_adv: 35 */ + {HCI_SUPP_LE_STATES_INIT_MASTER_MASK, HCI_SUPP_LE_STATES_INIT_MASTER_OFF}, /* init 28 */ + {HCI_SUPP_LE_STATES_INIT_MASTER_MASK, HCI_SUPP_LE_STATES_INIT_MASTER_OFF}, /* master 28 */ + {HCI_SUPP_LE_STATES_CONN_ADV_INIT_MASK, HCI_SUPP_LE_STATES_CONN_ADV_INIT_OFF}, /* slave: 32 */ + {HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_MASTER_MASK, HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_MASTER_OFF}, /* lo duty cycle adv 37 */ + {HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_MASTER_MASK, HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_MASTER_OFF}, /* hi duty cycle adv 36 */ + {HCI_SUPP_LE_STATES_NON_CONN_ADV_MASTER_MASK, HCI_SUPP_LE_STATES_NON_CONN_ADV_MASTER_OFF}, /* non connectable adv */ + {HCI_SUPP_LE_STATES_PASS_SCAN_MASTER_MASK, HCI_SUPP_LE_STATES_PASS_SCAN_MASTER_OFF}, /* passive scan */ + {HCI_SUPP_LE_STATES_ACTIVE_SCAN_MASTER_MASK, HCI_SUPP_LE_STATES_ACTIVE_SCAN_MASTER_OFF}, /* active scan */ + {HCI_SUPP_LE_STATES_SCAN_ADV_MASTER_MASK, HCI_SUPP_LE_STATES_SCAN_ADV_MASTER_OFF} /* scanable adv */ + + }, + { /* slave */ + {HCI_SUPP_LE_STATES_CONN_ADV_SLAVE_MASK, HCI_SUPP_LE_STATES_CONN_ADV_SLAVE_OFF}, /* conn_adv: 38,*/ + {HCI_SUPP_LE_STATES_INIT_MASTER_SLAVE_MASK, HCI_SUPP_LE_STATES_INIT_MASTER_SLAVE_OFF}, /* init 41 */ + {HCI_SUPP_LE_STATES_INIT_MASTER_SLAVE_MASK, HCI_SUPP_LE_STATES_INIT_MASTER_SLAVE_OFF}, /* master 41 */ + {HCI_SUPP_LE_STATES_CONN_ADV_SLAVE_MASK, HCI_SUPP_LE_STATES_CONN_ADV_SLAVE_OFF}, /* slave: 38,*/ + {HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_SLAVE_MASK, HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_SLAVE_OFF}, /* lo duty cycle adv 40 */ + {HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_SLAVE_MASK, HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_SLAVE_OFF}, /* hi duty cycle adv 39 */ + {HCI_SUPP_LE_STATES_NON_CONN_ADV_SLAVE_MASK, HCI_SUPP_LE_STATES_NON_CONN_ADV_SLAVE_OFF}, /* non connectable adv */ + {HCI_SUPP_LE_STATES_PASS_SCAN_SLAVE_MASK, HCI_SUPP_LE_STATES_PASS_SCAN_SLAVE_OFF}, /* passive scan */ + {HCI_SUPP_LE_STATES_ACTIVE_SCAN_SLAVE_MASK, HCI_SUPP_LE_STATES_ACTIVE_SCAN_SLAVE_OFF}, /* active scan */ + {HCI_SUPP_LE_STATES_SCAN_ADV_SLAVE_MASK, HCI_SUPP_LE_STATES_SCAN_ADV_SLAVE_OFF} /* scanable adv */ + + }, + { /* lo duty cycle adv */ + {0, 0}, /* conn_adv: 38,*/ + {HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_INIT_MASK, HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_INIT_OFF} ,/* init 34 */ + {HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_MASTER_MASK, HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_MASTER_OFF}, /* master 37 */ + {HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_SLAVE_MASK, HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_SLAVE_OFF}, /* slave: 40 */ + {0, 0}, /* lo duty cycle adv 40 */ + {0, 0}, /* hi duty cycle adv 39 */ + {0, 0}, /* non connectable adv */ + {HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_PASS_SCAN_MASK, HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_PASS_SCAN_OFF}, /* passive scan */ + {HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_ACTIVE_SCAN_MASK, HCI_SUPP_LE_STATES_LO_DUTY_DIR_ADV_ACTIVE_SCAN_OFF}, /* active scan */ + {0, 0} /* scanable adv */ + }, + { /* hi duty cycle adv */ + {0, 0}, /* conn_adv: 38,*/ + {HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_INIT_MASK, HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_INIT_OFF}, /* init 33 */ + {HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_MASTER_MASK, HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_MASTER_OFF}, /* master 36 */ + {HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_SLAVE_MASK, HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_SLAVE_OFF}, /* slave: 39*/ + {0, 0}, /* lo duty cycle adv 40 */ + {0, 0}, /* hi duty cycle adv 39 */ + {0, 0}, /* non connectable adv */ + {HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_PASS_SCAN_MASK, HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_PASS_SCAN_OFF}, /* passive scan */ + {HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_ACTIVE_SCAN_MASK, HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_ACTIVE_SCAN_OFF}, /* active scan */ + {0, 0} /* scanable adv */ + }, + { /* non connectable adv */ + {0, 0}, /* conn_adv: */ + {HCI_SUPP_LE_STATES_NON_CONN_INIT_MASK, HCI_SUPP_LE_STATES_NON_CONN_INIT_OFF}, /* init */ + {HCI_SUPP_LE_STATES_NON_CONN_ADV_MASTER_MASK, HCI_SUPP_LE_STATES_NON_CONN_ADV_MASTER_OFF}, /* master */ + {HCI_SUPP_LE_STATES_NON_CONN_ADV_SLAVE_MASK, HCI_SUPP_LE_STATES_NON_CONN_ADV_SLAVE_OFF}, /* slave: */ + {0, 0}, /* lo duty cycle adv */ + {0, 0}, /* hi duty cycle adv */ + {0, 0}, /* non connectable adv */ + {HCI_SUPP_LE_STATES_NON_CONN_ADV_PASS_SCAN_MASK, HCI_SUPP_LE_STATES_NON_CONN_ADV_PASS_SCAN_OFF}, /* passive scan */ + {HCI_SUPP_LE_STATES_NON_CONN_ADV_ACTIVE_SCAN_MASK, HCI_SUPP_LE_STATES_NON_CONN_ADV_ACTIVE_SCAN_OFF}, /* active scan */ + {0, 0} /* scanable adv */ + }, + { /* passive scan */ + {HCI_SUPP_LE_STATES_CONN_ADV_PASS_SCAN_MASK, HCI_SUPP_LE_STATES_CONN_ADV_PASS_SCAN_OFF}, /* conn_adv: */ + {HCI_SUPP_LE_STATES_PASS_SCAN_INIT_MASK, HCI_SUPP_LE_STATES_PASS_SCAN_INIT_OFF}, /* init */ + {HCI_SUPP_LE_STATES_PASS_SCAN_MASTER_MASK, HCI_SUPP_LE_STATES_PASS_SCAN_MASTER_OFF}, /* master */ + {HCI_SUPP_LE_STATES_PASS_SCAN_SLAVE_MASK, HCI_SUPP_LE_STATES_PASS_SCAN_SLAVE_OFF}, /* slave: */ + {0, 0}, /* lo duty cycle adv */ + {HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_PASS_SCAN_MASK, HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_PASS_SCAN_OFF}, /* hi duty cycle adv */ + {HCI_SUPP_LE_STATES_NON_CONN_ADV_PASS_SCAN_MASK, HCI_SUPP_LE_STATES_NON_CONN_ADV_PASS_SCAN_OFF}, /* non connectable adv */ + {0, 0}, /* passive scan */ + {0, 0}, /* active scan */ + {HCI_SUPP_LE_STATES_SCAN_ADV_PASS_SCAN_MASK, HCI_SUPP_LE_STATES_SCAN_ADV_PASS_SCAN_OFF} /* scanable adv */ + }, + { /* active scan */ + {HCI_SUPP_LE_STATES_CONN_ADV_ACTIVE_SCAN_MASK, HCI_SUPP_LE_STATES_CONN_ADV_ACTIVE_SCAN_OFF}, /* conn_adv: */ + {HCI_SUPP_LE_STATES_ACTIVE_SCAN_INIT_MASK, HCI_SUPP_LE_STATES_ACTIVE_SCAN_INIT_OFF}, /* init */ + {HCI_SUPP_LE_STATES_ACTIVE_SCAN_MASTER_MASK, HCI_SUPP_LE_STATES_ACTIVE_SCAN_MASTER_OFF}, /* master */ + {HCI_SUPP_LE_STATES_ACTIVE_SCAN_SLAVE_MASK, HCI_SUPP_LE_STATES_ACTIVE_SCAN_SLAVE_OFF}, /* slave: */ + {0, 0}, /* lo duty cycle adv */ + {HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_ACTIVE_SCAN_MASK, HCI_SUPP_LE_STATES_HI_DUTY_DIR_ADV_ACTIVE_SCAN_OFF}, /* hi duty cycle adv */ + {HCI_SUPP_LE_STATES_NON_CONN_ADV_ACTIVE_SCAN_MASK, HCI_SUPP_LE_STATES_NON_CONN_ADV_ACTIVE_SCAN_OFF}, /* non connectable adv */ + {0, 0}, /* TODO: passive scan */ + {0, 0}, /* TODO: active scan */ + {HCI_SUPP_LE_STATES_SCAN_ADV_ACTIVE_SCAN_MASK, HCI_SUPP_LE_STATES_SCAN_ADV_ACTIVE_SCAN_OFF} /* scanable adv */ + }, + { /* scanable adv */ + {0, 0}, /* conn_adv: */ + {HCI_SUPP_LE_STATES_SCAN_ADV_INIT_MASK, HCI_SUPP_LE_STATES_SCAN_ADV_INIT_OFF}, /* init */ + {HCI_SUPP_LE_STATES_SCAN_ADV_MASTER_MASK, HCI_SUPP_LE_STATES_SCAN_ADV_MASTER_OFF}, /* master */ + {HCI_SUPP_LE_STATES_SCAN_ADV_SLAVE_MASK, HCI_SUPP_LE_STATES_SCAN_ADV_SLAVE_OFF}, /* slave: */ + {0, 0}, /* lo duty cycle adv */ + {0, 0}, /* hi duty cycle adv */ + {0, 0}, /* non connectable adv */ + {HCI_SUPP_LE_STATES_SCAN_ADV_PASS_SCAN_MASK, HCI_SUPP_LE_STATES_SCAN_ADV_PASS_SCAN_OFF}, /* passive scan */ + {HCI_SUPP_LE_STATES_SCAN_ADV_ACTIVE_SCAN_MASK, HCI_SUPP_LE_STATES_SCAN_ADV_ACTIVE_SCAN_OFF}, /* active scan */ + {0, 0} /* scanable adv */ + } + +}; +/* check LE combo state supported */ +#define BTM_LE_STATES_SUPPORTED(x, y, z) ((x)[(z)] & (y)) + +static osi_mutex_t adv_enable_lock; +static osi_mutex_t adv_data_lock; +static osi_mutex_t adv_param_lock; +static osi_mutex_t scan_enable_lock; +static osi_mutex_t scan_param_lock; +osi_sem_t adv_enable_sem; +osi_sem_t adv_data_sem; +osi_sem_t adv_param_sem; +osi_sem_t scan_enable_sem; +osi_sem_t scan_param_sem; +uint8_t adv_enable_status = 0; +uint8_t adv_data_status = 0; +uint8_t adv_param_status = 0; +uint8_t scan_enable_status = 0; +uint8_t scan_param_status = 0; + +void btm_ble_lock_init(void) +{ + osi_mutex_new(&adv_enable_lock); + osi_mutex_new(&adv_data_lock); + osi_mutex_new(&adv_param_lock); + osi_mutex_new(&scan_enable_lock); + osi_mutex_new(&scan_param_lock); +} + +void btm_ble_lock_free(void) +{ + osi_mutex_free(&adv_enable_lock); + osi_mutex_free(&adv_data_lock); + osi_mutex_free(&adv_param_lock); + osi_mutex_free(&scan_enable_lock); + osi_mutex_free(&scan_param_lock); +} + +void btm_ble_sem_init(void) +{ + osi_sem_new(&adv_enable_sem, 1, 0); + osi_sem_new(&adv_data_sem, 1, 0); + osi_sem_new(&adv_param_sem, 1, 0); + osi_sem_new(&scan_enable_sem, 1, 0); + osi_sem_new(&scan_param_sem, 1, 0); +} + +void btm_ble_sem_free(void) +{ + osi_sem_free(&adv_enable_sem); + osi_sem_free(&adv_data_sem); + osi_sem_free(&adv_param_sem); + osi_sem_free(&scan_enable_sem); + osi_sem_free(&scan_param_sem); +} + +/******************************************************************************* +** +** Function BTM_BleRegiseterConnParamCallback +** +** Description register connection parameters update callback func +** +** Returns void +** +*******************************************************************************/ +void BTM_BleRegiseterConnParamCallback(tBTM_UPDATE_CONN_PARAM_CBACK *update_conn_param_cb) +{ + conn_param_update_cb.update_conn_param_cb = update_conn_param_cb; +} + +/******************************************************************************* +** +** Function BTM_BleUpdateAdvWhitelist +** +** Description Add or remove device from advertising white list +** +** Returns void +** +*******************************************************************************/ +BOOLEAN BTM_BleUpdateAdvWhitelist(BOOLEAN add_remove, BD_ADDR remote_bda, tBLE_ADDR_TYPE addr_type, tBTM_UPDATE_WHITELIST_CBACK *update_wl_cb) +{ + return btm_update_dev_to_white_list(add_remove, remote_bda, addr_type, update_wl_cb); +} + +/******************************************************************************* +** +** Function BTM_BleUpdateAdvWhitelist +** +** Description Add or remove device from advertising white list +** +** Returns void +** +*******************************************************************************/ +void BTM_BleClearWhitelist(tBTM_UPDATE_WHITELIST_CBACK *update_wl_cb) +{ + btm_ble_clear_white_list(update_wl_cb); +} + +/******************************************************************************* +** +** Function BTM_BleUpdateAdvFilterPolicy +** +** Description This function update the filter policy of advertiser. +** +** Parameter adv_policy: advertising filter policy +** +** Return void +*******************************************************************************/ +void BTM_BleUpdateAdvFilterPolicy(tBTM_BLE_AFP adv_policy) +{ + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + tBLE_ADDR_TYPE init_addr_type = BLE_ADDR_PUBLIC; + BD_ADDR p_addr_ptr = {0}; + UINT8 adv_mode = p_cb->adv_mode; + + BTM_TRACE_EVENT ("BTM_BleUpdateAdvFilterPolicy\n"); + + if (!controller_get_interface()->supports_ble()) { + return; + } + + if (p_cb->afp != adv_policy) { + p_cb->afp = adv_policy; + + /* if adv active, stop and restart */ + btm_ble_stop_adv (); + + if (p_cb->connectable_mode & BTM_BLE_CONNECTABLE) { + p_cb->evt_type = btm_set_conn_mode_adv_init_addr(p_cb, p_addr_ptr, &init_addr_type, + &p_cb->adv_addr_type); + } + + btsnd_hcic_ble_write_adv_params ((UINT16)(p_cb->adv_interval_min ? p_cb->adv_interval_min : + BTM_BLE_GAP_ADV_SLOW_INT), + (UINT16)(p_cb->adv_interval_max ? p_cb->adv_interval_max : + BTM_BLE_GAP_ADV_SLOW_INT), + p_cb->evt_type, + p_cb->adv_addr_type, + init_addr_type, + p_addr_ptr, + p_cb->adv_chnl_map, + p_cb->afp); + + if (adv_mode == BTM_BLE_ADV_ENABLE) { + btm_ble_start_adv (); + } + + } +} + +/******************************************************************************* +** +** Function btm_ble_send_extended_scan_params +** +** Description This function sends out the extended scan parameters command to the controller +** +** Parameters scan_type - Scan type +** scan_int - Scan interval +** scan_win - Scan window +** addr_type_own - Own address type +** scan_filter_policy - Scan filter policy +** +** Returns TRUE or FALSE +** +*******************************************************************************/ +BOOLEAN btm_ble_send_extended_scan_params(UINT8 scan_type, UINT32 scan_int, + UINT32 scan_win, UINT8 addr_type_own, + UINT8 scan_filter_policy) +{ + UINT8 scan_param[HCIC_PARAM_SIZE_BLE_WRITE_EXTENDED_SCAN_PARAM]; + UINT8 *pp_scan = scan_param; + + memset(scan_param, 0, HCIC_PARAM_SIZE_BLE_WRITE_EXTENDED_SCAN_PARAM); + + UINT8_TO_STREAM(pp_scan, scan_type); + UINT32_TO_STREAM(pp_scan, scan_int); + UINT32_TO_STREAM(pp_scan, scan_win); + UINT8_TO_STREAM(pp_scan, addr_type_own); + UINT8_TO_STREAM(pp_scan, scan_filter_policy); + + BTM_TRACE_DEBUG("%s, %d, %d", __func__, scan_int, scan_win); + if ((BTM_VendorSpecificCommand(HCI_BLE_EXTENDED_SCAN_PARAMS_OCF, + HCIC_PARAM_SIZE_BLE_WRITE_EXTENDED_SCAN_PARAM, scan_param, NULL)) != BTM_SUCCESS) { + BTM_TRACE_ERROR("%s error sending extended scan parameters", __func__); + return FALSE; + } + return TRUE; +} + +/******************************************************************************* +** +** 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, UINT32 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_WRONG_MODE; + + UINT32 scan_interval = !p_inq->scan_interval ? BTM_BLE_GAP_DISC_SCAN_INT : p_inq->scan_interval; + UINT32 scan_window = !p_inq->scan_window ? BTM_BLE_GAP_DISC_SCAN_WIN : p_inq->scan_window; + + BTM_TRACE_EVENT ("%s : scan_type:%d, %d, %d\n", __func__, btm_cb.btm_inq_vars.scan_type, + p_inq->scan_interval, p_inq->scan_window); + + if (!controller_get_interface()->supports_ble()) { + return BTM_ILLEGAL_VALUE; + } + + if (start) { + /* shared inquiry database, do not allow observe if any inquiry is active */ + if (BTM_BLE_IS_OBS_ACTIVE(btm_cb.ble_ctr_cb.scan_activity)) { + BTM_TRACE_ERROR("%s Observe Already Active", __func__); + return status; + } + + btm_cb.ble_ctr_cb.p_obs_results_cb = p_results_cb; + btm_cb.ble_ctr_cb.p_obs_cmpl_cb = p_cmpl_cb; + status = BTM_CMD_STARTED; + + /* scan is not started */ + if (!BTM_BLE_IS_SCAN_ACTIVE(btm_cb.ble_ctr_cb.scan_activity)) { + /* allow config of scan type */ + p_inq->scan_type = (p_inq->scan_type == BTM_BLE_SCAN_MODE_NONE) ? + BTM_BLE_SCAN_MODE_ACTI : p_inq->scan_type; + /* assume observe always not using white list */ +#if (defined BLE_PRIVACY_SPT && BLE_PRIVACY_SPT == TRUE) + /* enable resolving list */ + //btm_ble_enable_resolving_list_for_platform(BTM_BLE_RL_SCAN); +#endif + + if (cmn_ble_gap_vsc_cb.extended_scan_support == 0) { + btsnd_hcic_ble_set_scan_params(p_inq->scan_type, (UINT16)scan_interval, + (UINT16)scan_window, + btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type, + BTM_BLE_DEFAULT_SFP); + } else { + btm_ble_send_extended_scan_params(p_inq->scan_type, scan_interval, scan_window, + btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type, + BTM_BLE_DEFAULT_SFP); + } + + status = btm_ble_start_scan(); + } + + if (status == BTM_CMD_STARTED) { + btm_cb.ble_ctr_cb.scan_activity |= BTM_LE_OBSERVE_ACTIVE; + if (duration != 0) + /* start observer timer */ + { + btu_start_timer (&btm_cb.ble_ctr_cb.obs_timer_ent, BTU_TTYPE_BLE_OBSERVE, duration); + } + } + } else if (BTM_BLE_IS_OBS_ACTIVE(btm_cb.ble_ctr_cb.scan_activity)) { + status = BTM_CMD_STARTED; + btm_ble_stop_observe(); + } else { + BTM_TRACE_ERROR("%s Observe not active\n", __func__); + } + + return status; + +} + +/******************************************************************************* +** +** Function BTM_BleScan +** +** Description This procedure keep the device listening for advertising +** events from a broadcast device. +** +** Parameters start: start or stop scan. +** white_list: use white list in observer mode or not. +** +** Returns void +** +*******************************************************************************/ +tBTM_STATUS BTM_BleScan(BOOLEAN start, UINT32 duration, + tBTM_INQ_RESULTS_CB *p_results_cb, tBTM_CMPL_CB *p_cmpl_cb, tBTM_INQ_DIS_CB *p_discard_cb) +{ + tBTM_BLE_INQ_CB *p_inq = &btm_cb.ble_ctr_cb.inq_var; + tBTM_STATUS status = BTM_WRONG_MODE; + + if (!controller_get_interface()->supports_ble()) { + return BTM_ILLEGAL_VALUE; + } + + if (start) { + /* shared inquiry database, do not allow scan if any inquiry is active */ + if (BTM_BLE_IS_DISCO_ACTIVE(btm_cb.ble_ctr_cb.scan_activity)) { + BTM_TRACE_ERROR("%s scan already active", __func__); + return status; + } + + btm_cb.ble_ctr_cb.p_scan_results_cb = p_results_cb; + btm_cb.ble_ctr_cb.p_scan_cmpl_cb = p_cmpl_cb; + btm_cb.ble_ctr_cb.p_obs_discard_cb = p_discard_cb; + status = BTM_CMD_STARTED; + + /* scan is not started */ + if (!BTM_BLE_IS_SCAN_ACTIVE(btm_cb.ble_ctr_cb.scan_activity)) { + /* assume observe always not using white list */ +#if (defined BLE_PRIVACY_SPT && BLE_PRIVACY_SPT == TRUE) + /* enable resolving list */ + //btm_ble_enable_resolving_list_for_platform(BTM_BLE_RL_SCAN); +#endif + // if not set scan params, set default scan params + if (!p_inq->scan_params_set) { + /* allow config of scan type */ + p_inq->scan_type = BTM_BLE_SCAN_MODE_ACTI; + p_inq->scan_interval = BTM_BLE_GAP_DISC_SCAN_INT; + p_inq->scan_window = BTM_BLE_GAP_DISC_SCAN_WIN; + p_inq->sfp = BTM_BLE_DEFAULT_SFP; + p_inq->scan_params_set = TRUE; + p_inq->scan_duplicate_filter = BTM_BLE_DUPLICATE_DISABLE; + btsnd_hcic_ble_set_scan_params(p_inq->scan_type, p_inq->scan_interval, + p_inq->scan_window, + btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type, + p_inq->sfp); + } + status = btm_ble_start_scan(); + } + + if (status == BTM_CMD_STARTED) { + btm_cb.ble_ctr_cb.scan_activity |= BTM_LE_DISCOVER_ACTIVE; + if (duration != 0) + /* start observer timer */ + { + btu_start_timer (&btm_cb.ble_ctr_cb.scan_timer_ent, BTU_TTYPE_BLE_SCAN, duration); + } + } + } else if (BTM_BLE_IS_DISCO_ACTIVE(btm_cb.ble_ctr_cb.scan_activity)) { + status = BTM_CMD_STARTED; + btm_ble_stop_discover(); + } else { + BTM_TRACE_ERROR("%s scan not active\n", __func__); + } + + 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_START_STOP_ADV_CMPL_CBACK *p_stop_adv_cback) +{ + tBTM_STATUS status = BTM_NO_RESOURCES; + tBTM_LE_RANDOM_CB *p_addr_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + 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; + + if (!controller_get_interface()->supports_ble()) { + return BTM_ILLEGAL_VALUE; + } + +#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) { + /* 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_addr_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; + } + + status = btm_ble_start_adv (); + } else { + //save the stop adv callback to the BTM env. + p_cb->p_stop_adv_cb = p_stop_adv_cback; + status = btm_ble_stop_adv(); +#if BLE_PRIVACY_SPT == TRUE + btm_ble_disable_resolving_list(BTM_BLE_RL_ADV, TRUE); +#endif + } + return status; +} + +#if BLE_VND_INCLUDED == TRUE +/******************************************************************************* +** +** Function btm_vsc_brcm_features_complete +** +** Description Command Complete callback for HCI_BLE_VENDOR_CAP_OCF +** +** Returns void +** +*******************************************************************************/ +static void btm_ble_vendor_capability_vsc_cmpl_cback (tBTM_VSC_CMPL *p_vcs_cplt_params) +{ + UINT8 status = 0xFF; + UINT8 *p; + + BTM_TRACE_DEBUG("%s", __func__); + + /* Check status of command complete event */ + if ((p_vcs_cplt_params->opcode == HCI_BLE_VENDOR_CAP_OCF) && + (p_vcs_cplt_params->param_len > 0)) { + p = p_vcs_cplt_params->p_param_buf; + STREAM_TO_UINT8(status, p); + } + + if (status == HCI_SUCCESS) { + STREAM_TO_UINT8(btm_cb.cmn_ble_vsc_cb.adv_inst_max, p); + STREAM_TO_UINT8(btm_cb.cmn_ble_vsc_cb.rpa_offloading, p); + STREAM_TO_UINT16(btm_cb.cmn_ble_vsc_cb.tot_scan_results_strg, p); + STREAM_TO_UINT8(btm_cb.cmn_ble_vsc_cb.max_irk_list_sz, p); + STREAM_TO_UINT8(btm_cb.cmn_ble_vsc_cb.filter_support, p); + STREAM_TO_UINT8(btm_cb.cmn_ble_vsc_cb.max_filter, p); + STREAM_TO_UINT8(btm_cb.cmn_ble_vsc_cb.energy_support, p); + + if (p_vcs_cplt_params->param_len > BTM_VSC_CHIP_CAPABILITY_RSP_LEN_L_RELEASE) { + STREAM_TO_UINT16(btm_cb.cmn_ble_vsc_cb.version_supported, p); + } else { + btm_cb.cmn_ble_vsc_cb.version_supported = BTM_VSC_CHIP_CAPABILITY_L_VERSION; + } + + if (btm_cb.cmn_ble_vsc_cb.version_supported >= BTM_VSC_CHIP_CAPABILITY_M_VERSION) { + STREAM_TO_UINT16(btm_cb.cmn_ble_vsc_cb.total_trackable_advertisers, p); + STREAM_TO_UINT16(btm_cb.cmn_ble_vsc_cb.extended_scan_support, p); + STREAM_TO_UINT16(btm_cb.cmn_ble_vsc_cb.debug_logging_supported, p); + } + btm_cb.cmn_ble_vsc_cb.values_read = TRUE; + } + + BTM_TRACE_DEBUG("%s: stat=%d, irk=%d, ADV ins:%d, rpa=%d, ener=%d, ext_scan=%d", + __func__, status, btm_cb.cmn_ble_vsc_cb.max_irk_list_sz, + btm_cb.cmn_ble_vsc_cb.adv_inst_max, btm_cb.cmn_ble_vsc_cb.rpa_offloading, + btm_cb.cmn_ble_vsc_cb.energy_support, btm_cb.cmn_ble_vsc_cb.extended_scan_support); + + if (BTM_BleMaxMultiAdvInstanceCount() > 0) { + btm_ble_multi_adv_init(); + } + + if (btm_cb.cmn_ble_vsc_cb.max_filter > 0) { + btm_ble_adv_filter_init(); + } + +#if (defined BLE_PRIVACY_SPT && BLE_PRIVACY_SPT == TRUE) + /* VS capability included and non-4.2 device */ + if (btm_cb.cmn_ble_vsc_cb.max_irk_list_sz > 0 && + controller_get_interface()->get_ble_resolving_list_max_size() == 0) { + btm_ble_resolving_list_init(btm_cb.cmn_ble_vsc_cb.max_irk_list_sz); + } +#endif + + if (btm_cb.cmn_ble_vsc_cb.tot_scan_results_strg > 0) { + btm_ble_batchscan_init(); + } + + if (p_ctrl_le_feature_rd_cmpl_cback != NULL) { + p_ctrl_le_feature_rd_cmpl_cback(status); + } +} +#endif + +/******************************************************************************* +** +** Function BTM_BleGetVendorCapabilities +** +** Description This function reads local LE features +** +** Parameters p_cmn_vsc_cb : Locala LE capability structure +** +** Returns void +** +*******************************************************************************/ +extern void BTM_BleGetVendorCapabilities(tBTM_BLE_VSC_CB *p_cmn_vsc_cb) +{ + BTM_TRACE_DEBUG("BTM_BleGetVendorCapabilities"); + + if (NULL != p_cmn_vsc_cb) { + *p_cmn_vsc_cb = btm_cb.cmn_ble_vsc_cb; + } +} + +/****************************************************************************** +** +** Function BTM_BleReadControllerFeatures +** +** Description Reads BLE specific controller features +** +** Parameters: tBTM_BLE_CTRL_FEATURES_CBACK : Callback to notify when features are read +** +** Returns void +** +*******************************************************************************/ +extern void BTM_BleReadControllerFeatures(tBTM_BLE_CTRL_FEATURES_CBACK *p_vsc_cback) +{ + if (TRUE == btm_cb.cmn_ble_vsc_cb.values_read) { + return; + } + +#if BLE_VND_INCLUDED == TRUE + BTM_TRACE_DEBUG("BTM_BleReadControllerFeatures"); + + p_ctrl_le_feature_rd_cmpl_cback = p_vsc_cback; + if ( BTM_VendorSpecificCommand (HCI_BLE_VENDOR_CAP_OCF, + 0, + NULL, + btm_ble_vendor_capability_vsc_cmpl_cback) + != BTM_CMD_STARTED) { + BTM_TRACE_ERROR("LE Get_Vendor Capabilities Command Failed."); + } +#else + UNUSED(p_vsc_cback); +#endif + return ; +} + +void BTM_VendorHciEchoCmdCallback(tBTM_VSC_CMPL *p1) +{ +#if (!CONFIG_BT_STACK_NO_LOG) + if (!p1) { + return; + } + uint8_t *p = p1->p_param_buf; + uint8_t status, echo; + STREAM_TO_UINT8 (status, p); + STREAM_TO_UINT8 (echo, p); +#endif + BTM_TRACE_DEBUG("%s status 0x%x echo 0x%x", __func__, status, echo); +} + +/****************************************************************************** +** +** Function BTM_VendorHciEchoCmdTest +** +** Description vendor common echo hci cmd test, controller will return status and echo +** +** Parameters: echo : echo value +** +** Returns void +** +*******************************************************************************/ +void BTM_VendorHciEchoCmdTest(uint8_t echo) +{ + BTM_VendorSpecificCommand (HCI_VENDOR_COMMON_ECHO_CMD_OPCODE, + 1, + &echo, + BTM_VendorHciEchoCmdCallback); +} + +/******************************************************************************* +** +** Function BTM_BleEnableMixedPrivacyMode +** +** Description This function is called to enabled Mixed mode if privacy 1.2 +** is applicable in controller. +** +** Parameters mixed_on: mixed mode to be used or not. +** +** Returns void +** +*******************************************************************************/ +void BTM_BleEnableMixedPrivacyMode(BOOLEAN mixed_on) +{ + +#if BLE_PRIVACY_SPT == TRUE + btm_cb.ble_ctr_cb.mixed_mode = mixed_on; + + /* TODO: send VSC to enabled mixed mode */ +#endif +} + +/******************************************************************************* +** +** Function BTM_BleConfigPrivacy +** +** Description This function is called to enable or disable the privacy in +** LE channel of the local device. +** +** Parameters privacy_mode: privacy mode on or off. +** +** Returns BOOLEAN privacy mode set success; otherwise failed. +** +*******************************************************************************/ +BOOLEAN BTM_BleConfigPrivacy(BOOLEAN privacy_mode, tBTM_SET_LOCAL_PRIVACY_CBACK *set_local_privacy_cback) +{ +#if BLE_PRIVACY_SPT == TRUE + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + tBTM_LE_RANDOM_CB *random_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + if (random_cb){ + random_cb->set_local_privacy_cback = set_local_privacy_cback; + }else{ + BTM_TRACE_ERROR("%s,random_cb = NULL", __func__); + } + + BTM_TRACE_EVENT ("%s\n", __func__); + + /* if LE is not supported, return error */ + if (!controller_get_interface()->supports_ble()) { + return FALSE; + } + +#if (defined(GAP_INCLUDED) && GAP_INCLUDED == TRUE && GATTS_INCLUDED == TRUE) + uint8_t addr_resolution = 0; +#endif /* defined(GAP_INCLUDED) && GAP_INCLUDED == TRUE && GATTS_INCLUDED == TRUE */ + if (!privacy_mode) { /* if privacy disabled, always use public address */ + p_cb->addr_mgnt_cb.exist_addr_bit &= (~BTM_BLE_GAP_ADDR_BIT_RESOLVABLE); + memset(p_cb->addr_mgnt_cb.resolvale_addr, 0, BD_ADDR_LEN); + p_cb->addr_mgnt_cb.own_addr_type = BLE_ADDR_PUBLIC; + p_cb->privacy_mode = BTM_PRIVACY_NONE; + // Disable RPA function + btsnd_hcic_ble_set_addr_resolution_enable(FALSE); + } else { /* privacy is turned on*/ +#if (CONTROLLER_RPA_LIST_ENABLE == FALSE) + /* always set host random address, used when privacy 1.1 or priavcy 1.2 is disabled */ + btm_gen_resolvable_private_addr((void *)btm_gen_resolve_paddr_low); +#endif + + if (BTM_BleMaxMultiAdvInstanceCount() > 0) { + btm_ble_multi_adv_enb_privacy(privacy_mode); + } + + /* 4.2 controller only allow privacy 1.2 or mixed mode, resolvable private address in controller */ + if (controller_get_interface()->supports_ble_privacy()) { +#if (defined(GAP_INCLUDED) && GAP_INCLUDED == TRUE && GATTS_INCLUDED == TRUE) + addr_resolution = 1; +#endif /* defined(GAP_INCLUDED) && GAP_INCLUDED == TRUE && GATTS_INCLUDED == TRUE */ + /* check vendor specific capability */ + p_cb->privacy_mode = btm_cb.ble_ctr_cb.mixed_mode ? BTM_PRIVACY_MIXED : BTM_PRIVACY_1_2; + } else { /* 4.1/4.0 controller */ + p_cb->privacy_mode = BTM_PRIVACY_1_1; + } + // Enable RPA function + btsnd_hcic_ble_set_addr_resolution_enable(TRUE); + } + +#if (defined(GAP_INCLUDED) && GAP_INCLUDED == TRUE && GATTS_INCLUDED == TRUE) + GAP_BleAttrDBUpdate (GATT_UUID_GAP_CENTRAL_ADDR_RESOL, (tGAP_BLE_ATTR_VALUE *)&addr_resolution); +#endif + + return TRUE; +#else + return FALSE; +#endif +} + +/******************************************************************************* +** +** Function BTMGetLocalResolvablePrivateAddr +** +** Description This function is called to get local RPA address +** +** Parameters bda: address pointer. +** +** +*******************************************************************************/ + +BOOLEAN BTM_GetLocalResolvablePrivateAddr(BD_ADDR bda) +{ + tBTM_LE_RANDOM_CB *p_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + BTM_TRACE_DEBUG ("get owm resolvable random address"); + + if (bda) { + /* if privacy disabled, return false */ + if ((p_cb->exist_addr_bit & BTM_BLE_GAP_ADDR_BIT_RESOLVABLE) == BTM_BLE_GAP_ADDR_BIT_RESOLVABLE) { + memcpy(bda, p_cb->resolvale_addr, BD_ADDR_LEN); + BTM_TRACE_DEBUG("own resolvable random address: 0x%02x:%02x:%02x:%02x:%02x:%02x", + p_cb->resolvale_addr[0], p_cb->resolvale_addr[1], + p_cb->resolvale_addr[2], p_cb->resolvale_addr[3], + p_cb->resolvale_addr[4], p_cb->resolvale_addr[5]); + return TRUE; + } + + return FALSE; + } + + return FALSE; +} + +/******************************************************************************* +** +** Function BTM_UpdateAddrInfor +** +** Description This function is called to update address information +** +** Parameters addr_type: address type +** bda: address pointer. +** +** +*******************************************************************************/ +void BTM_UpdateAddrInfor(uint8_t addr_type, BD_ADDR bda) +{ + btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type = addr_type; + memcpy(btm_cb.ble_ctr_cb.addr_mgnt_cb.private_addr, bda, BD_ADDR_LEN); +} + +/******************************************************************************* +** +** Function BTM_BleSetStaticAddr +** +** Description This function is called to save random address +** +** Parameters rand_addr: address pointer. +** +** +*******************************************************************************/ +void BTM_BleSetStaticAddr(BD_ADDR rand_addr) +{ + memcpy(btm_cb.ble_ctr_cb.addr_mgnt_cb.static_rand_addr, rand_addr, BD_ADDR_LEN); + btm_cb.ble_ctr_cb.addr_mgnt_cb.exist_addr_bit |= BTM_BLE_GAP_ADDR_BIT_RANDOM; +} + +#if (CONTROLLER_RPA_LIST_ENABLE == FALSE) +uint32_t BTM_BleUpdateOwnType(uint8_t *own_bda_type, tBTM_START_ADV_CMPL_CBACK *cb) +{ + if(*own_bda_type == BLE_ADDR_RANDOM) { + if((btm_cb.ble_ctr_cb.addr_mgnt_cb.exist_addr_bit & BTM_BLE_GAP_ADDR_BIT_RANDOM) == BTM_BLE_GAP_ADDR_BIT_RANDOM) { + //close privacy + #if BLE_PRIVACY_SPT == TRUE + if (btm_cb.ble_ctr_cb.privacy_mode != BTM_PRIVACY_NONE) { + BTM_BleConfigPrivacy(FALSE, NULL); + } + #endif + btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type = BLE_ADDR_RANDOM; + memcpy(btm_cb.ble_ctr_cb.addr_mgnt_cb.private_addr, btm_cb.ble_ctr_cb.addr_mgnt_cb.static_rand_addr, BD_ADDR_LEN); + // set address to controller + btsnd_hcic_ble_set_random_addr(btm_cb.ble_ctr_cb.addr_mgnt_cb.static_rand_addr); + + } else if((btm_cb.ble_ctr_cb.addr_mgnt_cb.exist_addr_bit & BTM_BLE_GAP_ADDR_BIT_RESOLVABLE) == BTM_BLE_GAP_ADDR_BIT_RESOLVABLE) { + btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type = BLE_ADDR_RANDOM; + memcpy(btm_cb.ble_ctr_cb.addr_mgnt_cb.private_addr, btm_cb.ble_ctr_cb.addr_mgnt_cb.resolvale_addr, BD_ADDR_LEN); + btsnd_hcic_ble_set_random_addr(btm_cb.ble_ctr_cb.addr_mgnt_cb.resolvale_addr); + }else { + BTM_TRACE_ERROR ("No random address yet, please set random address using API \"esp_ble_gap_set_rand_addr\" and retry\n"); + if(cb) { + (* cb)(HCI_ERR_ESP_VENDOR_FAIL); + } + return BTM_ILLEGAL_VALUE; + } + } else if(*own_bda_type == BLE_ADDR_PUBLIC_ID || *own_bda_type == BLE_ADDR_RANDOM_ID) { + if((btm_cb.ble_ctr_cb.addr_mgnt_cb.exist_addr_bit & BTM_BLE_GAP_ADDR_BIT_RESOLVABLE) == BTM_BLE_GAP_ADDR_BIT_RESOLVABLE) { + *own_bda_type = BLE_ADDR_RANDOM; + btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type = BLE_ADDR_RANDOM; + memcpy(btm_cb.ble_ctr_cb.addr_mgnt_cb.private_addr, btm_cb.ble_ctr_cb.addr_mgnt_cb.resolvale_addr, BD_ADDR_LEN); + btsnd_hcic_ble_set_random_addr(btm_cb.ble_ctr_cb.addr_mgnt_cb.resolvale_addr); + } else { + #if BLE_PRIVACY_SPT == TRUE + if(btm_cb.ble_ctr_cb.privacy_mode != BTM_PRIVACY_NONE) { + BTM_TRACE_ERROR ("Error state\n"); + if(cb) { + (* cb)(HCI_ERR_ESP_VENDOR_FAIL); + } + return BTM_ILLEGAL_VALUE; + } + #endif + if(*own_bda_type == BLE_ADDR_PUBLIC_ID) { + *own_bda_type = BLE_ADDR_PUBLIC; + btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type = BLE_ADDR_PUBLIC; + } else { //own_bda_type == BLE_ADDR_RANDOM_ID + if((btm_cb.ble_ctr_cb.addr_mgnt_cb.exist_addr_bit & BTM_BLE_GAP_ADDR_BIT_RANDOM) == BTM_BLE_GAP_ADDR_BIT_RANDOM) { + *own_bda_type = BLE_ADDR_RANDOM; + btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type = BLE_ADDR_RANDOM; + memcpy(btm_cb.ble_ctr_cb.addr_mgnt_cb.private_addr, btm_cb.ble_ctr_cb.addr_mgnt_cb.static_rand_addr, BD_ADDR_LEN); + btsnd_hcic_ble_set_random_addr(btm_cb.ble_ctr_cb.addr_mgnt_cb.static_rand_addr); + } else { + BTM_TRACE_ERROR ("No RPA and no random address yet, please set RPA or random address and try\n"); + if(cb) { + (* cb)(HCI_ERR_ESP_VENDOR_FAIL); + } + return BTM_ILLEGAL_VALUE; + } + } + } + } else { + btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type = BLE_ADDR_PUBLIC; + } + + return BTM_SUCCESS; +} +#else +uint32_t BTM_BleUpdateOwnType(uint8_t *own_bda_type, tBTM_START_ADV_CMPL_CBACK *cb) +{ + tBTM_LE_RANDOM_CB *p_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + + if((*own_bda_type == BLE_ADDR_RANDOM) || (*own_bda_type == BLE_ADDR_RANDOM_ID)) { + if((p_cb->exist_addr_bit & BTM_BLE_GAP_ADDR_BIT_RANDOM) != BTM_BLE_GAP_ADDR_BIT_RANDOM) { + BTM_TRACE_ERROR("No random address yet, please set random address and try\n"); + if(cb) { + (* cb)(HCI_ERR_ESP_VENDOR_FAIL); + } + return BTM_ILLEGAL_VALUE; + } + + // If a device is using RPA, it shall also have an Identity Address + if ((*own_bda_type == BLE_ADDR_RANDOM_ID) && BTM_BLE_IS_NON_RESLVE_BDA(p_cb->static_rand_addr)) { + BTM_TRACE_ERROR("No identity address yet, please set static random address and try\n"); + if (cb) { + (* cb)(HCI_ERR_ESP_VENDOR_FAIL); + } + return BTM_ILLEGAL_VALUE; + } + } + + p_cb->own_addr_type = *own_bda_type; + + return BTM_SUCCESS; +} +#endif + + +/******************************************************************************* +** +** Function BTM_BleConfigLocalIcon +** +** Description This function is called to set local icon +** +** Parameters icon: appearance value. +** +** +*******************************************************************************/ +void BTM_BleConfigLocalIcon(uint16_t icon) +{ +#if (defined(GAP_INCLUDED) && GAP_INCLUDED == TRUE && GATTS_INCLUDED == TRUE) + tGAP_BLE_ATTR_VALUE p_value; + p_value.icon = icon; + GAP_BleAttrDBUpdate(GATT_UUID_GAP_ICON, &p_value); +#else + BTM_TRACE_ERROR("%s\n", __func__); +#endif +} + +/******************************************************************************* +** +** Function BTM_BleConfigConnParams +** +** Description This function is called to set the connection parameters +** +** Parameters int_min: minimum connection interval +** int_max: maximum connection interval +** latency: slave latency +** timeout: supervision timeout +** +*******************************************************************************/ +void BTM_BleConfigConnParams(uint16_t int_min, uint16_t int_max, uint16_t latency, uint16_t timeout) +{ +#if (defined(GAP_INCLUDED) && GAP_INCLUDED == TRUE && GATTS_INCLUDED == TRUE) + tGAP_BLE_ATTR_VALUE p_value; + + p_value.conn_param.int_min = int_min; + p_value.conn_param.int_max = int_max; + p_value.conn_param.latency = latency; + p_value.conn_param.sp_tout = timeout; + GAP_BleAttrDBUpdate(GATT_UUID_GAP_PREF_CONN_PARAM, &p_value); +#else + BTM_TRACE_ERROR("%s\n", __func__); +#endif +} + +/******************************************************************************* +** +** Function BTM_BleMaxMultiAdvInstanceCount +** +** Description Returns max number of multi adv instances supported by controller +** +** Returns Max multi adv instance count +** +*******************************************************************************/ +extern UINT8 BTM_BleMaxMultiAdvInstanceCount(void) +{ + return btm_cb.cmn_ble_vsc_cb.adv_inst_max < BTM_BLE_MULTI_ADV_MAX ? + btm_cb.cmn_ble_vsc_cb.adv_inst_max : BTM_BLE_MULTI_ADV_MAX; +} + +#if BLE_PRIVACY_SPT == TRUE +/******************************************************************************* +** +** Function btm_ble_resolve_random_addr_on_adv +** +** Description resolve random address complete callback. +** +** Returns void +** +*******************************************************************************/ +static void btm_ble_resolve_random_addr_on_adv(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_EVENT ("btm_ble_resolve_random_addr_on_adv "); + + STREAM_TO_UINT8 (evt_type, pp); + STREAM_TO_UINT8 (addr_type, pp); + STREAM_TO_BDADDR (bda, pp); + + if (match_rec) { + BTM_TRACE_DEBUG("Random match"); + match_rec->ble.active_addr_type = BTM_BLE_ADDR_RRA; + memcpy(match_rec->ble.cur_rand_addr, bda, BD_ADDR_LEN); + + if (btm_ble_init_pseudo_addr(match_rec, bda)) { + memcpy(bda, match_rec->bd_addr, BD_ADDR_LEN); + } else { + // Assign the original address to be the current report address + memcpy(bda, match_rec->ble.pseudo_addr, BD_ADDR_LEN); + } + } + + btm_ble_process_adv_pkt_cont(bda, addr_type, evt_type, pp); + + return; +} +#endif + +/******************************************************************************* +** +** Function BTM_BleLocalPrivacyEnabled +** +** Description Checks if local device supports private address +** +** Returns Return TRUE if local privacy is enabled else FALSE +** +*******************************************************************************/ +BOOLEAN BTM_BleLocalPrivacyEnabled(void) +{ +#if BLE_PRIVACY_SPT == TRUE + return (btm_cb.ble_ctr_cb.privacy_mode != BTM_PRIVACY_NONE); +#else + return false; +#endif +} + +/******************************************************************************* +** +** 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_EVENT ("BTM_BleSetBgConnType "); + if (!controller_get_interface()->supports_ble()) { + return FALSE; + } + + 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); + } + 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_ERROR("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_BleClearBgConnDev +** +** Description This function is called to clear the whitelist, +** end any pending whitelist connections, +* and reset the local bg device list. +** +** Parameters void +** +** Returns void +** +*******************************************************************************/ +void BTM_BleClearBgConnDev(void) +{ + btm_ble_start_auto_conn(FALSE); + btm_ble_clear_white_list(NULL); + gatt_reset_bgdev_list(); +} + +/******************************************************************************* +** +** 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) +{ + BTM_TRACE_EVENT("%s() add=%d", __func__, add_remove); + return btm_update_dev_to_white_list(add_remove, remote_bda, 0, NULL); +} + +/******************************************************************************* +** +** Function BTM_BleSetConnectableMode +** +** Description This function is called to set BLE connectable mode for a +** peripheral device. +** +** Parameters conn_mode: directed connectable mode, or non-directed.It can +** be BTM_BLE_CONNECT_EVT, BTM_BLE_CONNECT_DIR_EVT or +** BTM_BLE_CONNECT_LO_DUTY_DIR_EVT +** +** Returns BTM_ILLEGAL_VALUE if controller does not support BLE. +** BTM_SUCCESS is status set successfully; otherwise failure. +** +*******************************************************************************/ +tBTM_STATUS BTM_BleSetConnectableMode(tBTM_BLE_CONN_MODE connectable_mode) +{ + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + + BTM_TRACE_EVENT ("%s connectable_mode = %d ", __func__, connectable_mode); + if (!controller_get_interface()->supports_ble()) { + return BTM_ILLEGAL_VALUE; + } + + p_cb->directed_conn = connectable_mode; + return btm_ble_set_connectability( p_cb->connectable_mode); +} + +/******************************************************************************* +** +** Function btm_set_conn_mode_adv_init_addr +** +** Description set initator address type and local address type based on adv +** mode. +** +** +*******************************************************************************/ +static UINT8 btm_set_conn_mode_adv_init_addr(tBTM_BLE_INQ_CB *p_cb, + BD_ADDR_PTR p_peer_addr_ptr, + tBLE_ADDR_TYPE *p_peer_addr_type, + tBLE_ADDR_TYPE *p_own_addr_type) +{ + UINT8 evt_type; +#if BLE_PRIVACY_SPT == TRUE + UINT8 i = BTM_SEC_MAX_DEVICE_RECORDS; + tBTM_SEC_DEV_REC *p_dev_rec; + list_node_t *p_node = NULL; +#endif ///BLE_PRIVACY_SPT == TRUE + 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; + + if (evt_type == BTM_BLE_CONNECT_EVT) { + evt_type = p_cb->directed_conn; + + if ( p_cb->directed_conn == BTM_BLE_CONNECT_DIR_EVT || + p_cb->directed_conn == BTM_BLE_CONNECT_LO_DUTY_DIR_EVT) { + +#if BLE_PRIVACY_SPT == TRUE + /* for privacy 1.2, convert peer address as static, own address set as ID addr */ + if (btm_cb.ble_ctr_cb.privacy_mode == BTM_PRIVACY_1_2 || + btm_cb.ble_ctr_cb.privacy_mode == BTM_PRIVACY_MIXED) { + /* only do so for bonded device */ + if ((p_dev_rec = btm_find_or_alloc_dev (p_cb->direct_bda.bda)) != NULL && + p_dev_rec->ble.in_controller_list & BTM_RESOLVING_LIST_BIT) { + //btm_ble_enable_resolving_list(BTM_BLE_RL_ADV); + memcpy(p_peer_addr_ptr, p_dev_rec->ble.static_addr, BD_ADDR_LEN); + *p_peer_addr_type = p_dev_rec->ble.static_addr_type; + *p_own_addr_type = BLE_ADDR_RANDOM_ID; + return evt_type; + } + /* otherwise fall though as normal directed adv */ + else { + btm_ble_disable_resolving_list(BTM_BLE_RL_ADV, TRUE); + } + } +#endif + /* direct adv mode does not have privacy, if privacy is not enabled */ + *p_peer_addr_type = p_cb->direct_bda.type; + memcpy(p_peer_addr_ptr, p_cb->direct_bda.bda, BD_ADDR_LEN); + return evt_type; + } + } + + /* undirect adv mode or non-connectable mode*/ +#if BLE_PRIVACY_SPT == TRUE + /* when privacy 1.2 privacy only mode is used, or mixed mode */ + if ((btm_cb.ble_ctr_cb.privacy_mode == BTM_PRIVACY_1_2 && p_cb->afp != AP_SCAN_CONN_ALL) || + btm_cb.ble_ctr_cb.privacy_mode == BTM_PRIVACY_MIXED) { + /* if enhanced privacy is required, set Identity address and matching IRK peer */ + for (p_node = list_begin(btm_cb.p_sec_dev_rec_list); p_node; p_node = list_next(p_node)) { + p_dev_rec = list_node(p_node); + if ((p_dev_rec->sec_flags & BTM_SEC_IN_USE) != 0 && + (p_dev_rec->ble.in_controller_list & BTM_RESOLVING_LIST_BIT) != 0) { + memcpy(p_peer_addr_ptr, p_dev_rec->ble.static_addr, BD_ADDR_LEN); + *p_peer_addr_type = p_dev_rec->ble.static_addr_type; + break; + } + } + + if (i != BTM_SEC_MAX_DEVICE_RECORDS) { + *p_own_addr_type = BLE_ADDR_RANDOM_ID; + } else + /* resolving list is empty, not enabled */ + { + *p_own_addr_type = BLE_ADDR_RANDOM; + } + } + /* privacy 1.1, or privacy 1.2, general discoverable/connectable mode, disable privacy in */ + /* controller fall back to host based privacy */ + else if (btm_cb.ble_ctr_cb.privacy_mode != BTM_PRIVACY_NONE) { + *p_own_addr_type = BLE_ADDR_RANDOM; + } +#endif + + /* if no privacy,do not set any peer address,*/ + /* local address type go by global privacy setting */ + return evt_type; +} + +/******************************************************************************* +** +** 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_LE_RANDOM_CB *p_addr_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + tBTM_STATUS status = BTM_SUCCESS; + BD_ADDR p_addr_ptr = {0}; + tBLE_ADDR_TYPE init_addr_type = BLE_ADDR_PUBLIC; + tBLE_ADDR_TYPE own_addr_type = p_addr_cb->own_addr_type; + UINT8 adv_mode = p_cb->adv_mode; + + BTM_TRACE_EVENT ("BTM_BleSetAdvParams"); + + if (!controller_get_interface()->supports_ble()) { + return BTM_ILLEGAL_VALUE; + } + + if (!BTM_BLE_ISVALID_PARAM(adv_int_min, BTM_BLE_ADV_INT_MIN, BTM_BLE_ADV_INT_MAX) || + !BTM_BLE_ISVALID_PARAM(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)); + } + + BTM_TRACE_EVENT ("update params for an active adv\n"); + + btm_ble_stop_adv(); + + p_cb->evt_type = btm_set_conn_mode_adv_init_addr(p_cb, p_addr_ptr, &init_addr_type, + &own_addr_type); + + /* update adv params */ + btsnd_hcic_ble_write_adv_params (p_cb->adv_interval_min, + p_cb->adv_interval_max, + p_cb->evt_type, + own_addr_type, + init_addr_type, + p_addr_ptr, + p_cb->adv_chnl_map, + p_cb->afp); + + if (adv_mode == BTM_BLE_ADV_ENABLE) { + btm_ble_start_adv(); + } + + return status; +} + + +/******************************************************************************* +** +** Function BTM_BleSetAdvParamsAll +** +** Description This function is called to set all of the advertising parameters. +** +** Parameters: None. +** +** Returns void +** +*******************************************************************************/ +tBTM_STATUS BTM_BleSetAdvParamsAll(UINT16 adv_int_min, UINT16 adv_int_max, UINT8 adv_type, + tBLE_ADDR_TYPE own_bda_type, tBLE_BD_ADDR *p_dir_bda, + tBTM_BLE_ADV_CHNL_MAP chnl_map, tBTM_BLE_AFP afp, tBTM_START_ADV_CMPL_CBACK *adv_cb) +{ + tBTM_LE_RANDOM_CB *p_addr_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + + BTM_TRACE_EVENT ("BTM_BleSetAdvParamsAll\n"); + + if (!controller_get_interface()->supports_ble()) { + return BTM_ILLEGAL_VALUE; + } + if (BTM_BleUpdateOwnType(&own_bda_type, adv_cb) != 0) { + return BTM_ILLEGAL_VALUE; + } + if (!BTM_BLE_ISVALID_PARAM(adv_int_min, BTM_BLE_ADV_INT_MIN, BTM_BLE_ADV_INT_MAX) || + !BTM_BLE_ISVALID_PARAM(adv_int_max, BTM_BLE_ADV_INT_MIN, BTM_BLE_ADV_INT_MAX)) { + BTM_TRACE_ERROR ("adv_int_min or adv_int_max is invalid\n"); + if(adv_cb) { + (* adv_cb)(HCI_ERR_ESP_VENDOR_FAIL); + } + return BTM_ILLEGAL_VALUE; + } + + btm_ble_stop_adv(); + + osi_mutex_lock(&adv_param_lock, OSI_MUTEX_MAX_TIMEOUT); + if(adv_type == BTM_BLE_CONNECT_DIR_EVT){ + btm_ble_set_topology_mask(BTM_BLE_STATE_HI_DUTY_DIR_ADV_BIT); + }else if(adv_type == BTM_BLE_CONNECT_LO_DUTY_DIR_EVT){ + btm_ble_set_topology_mask(BTM_BLE_STATE_LO_DUTY_DIR_ADV_BIT); + }else if(adv_type == BTM_BLE_NON_CONNECT_EVT){ + btm_ble_set_topology_mask(BTM_BLE_STATE_NON_CONN_ADV_BIT); + } + + p_cb->adv_interval_min = adv_int_min; + p_cb->adv_interval_max = adv_int_max; + p_cb->adv_chnl_map = chnl_map; + p_addr_cb->own_addr_type = own_bda_type; + p_cb->evt_type = adv_type; + p_cb->afp = afp; + p_cb->p_adv_cb = adv_cb; + + if (p_dir_bda) { + memcpy(&p_cb->direct_bda, p_dir_bda, sizeof(tBLE_BD_ADDR)); + } else { + return BTM_ILLEGAL_VALUE; + } + + BTM_TRACE_EVENT ("update params for an active adv\n"); + + tBTM_STATUS status = BTM_SUCCESS; + /* update adv params */ + if (btsnd_hcic_ble_write_adv_params (adv_int_min, + adv_int_max, + adv_type, + own_bda_type, + p_dir_bda->type, + p_dir_bda->bda, + chnl_map, + p_cb->afp)) { + osi_sem_take(&adv_param_sem, OSI_SEM_MAX_TIMEOUT); + status = adv_param_status; + } else { + status = BTM_NO_RESOURCES; + } + osi_mutex_unlock(&adv_param_lock); + return status; +} + +tBTM_STATUS BTM_BleStartAdv(void) +{ + tBTM_STATUS status = BTM_SUCCESS; + if (!controller_get_interface()->supports_ble()) { + return BTM_ILLEGAL_VALUE; + } + + btm_ble_stop_adv(); + + status = btm_ble_start_adv(); + + 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_EVENT ("BTM_BleReadAdvParams "); + if (!controller_get_interface()->supports_ble()) { + return ; + } + + *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 client_if - Client IF +** scan_interval - Scan interval +** scan_window - Scan window +** scan_mode - Scan mode +** scan_setup_status_cback - Scan param setup status callback +** +** Returns void +** +*******************************************************************************/ +void BTM_BleSetScanParams(tGATT_IF client_if, UINT32 scan_interval, UINT32 scan_window, + tBLE_SCAN_MODE scan_mode, + tBLE_SCAN_PARAM_SETUP_CBACK scan_setup_status_cback) +{ + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + UINT32 max_scan_interval; + UINT32 max_scan_window; + + BTM_TRACE_EVENT ("%s\n", __func__); + if (!controller_get_interface()->supports_ble()) { + return; + } + + /* If not supporting extended scan support, use the older range for checking */ + if (btm_cb.cmn_ble_vsc_cb.extended_scan_support == 0) { + max_scan_interval = BTM_BLE_SCAN_INT_MAX; + max_scan_window = BTM_BLE_SCAN_WIN_MAX; + } else { + /* If supporting extended scan support, use the new extended range for checking */ + max_scan_interval = BTM_BLE_EXT_SCAN_INT_MAX; + max_scan_window = BTM_BLE_EXT_SCAN_WIN_MAX; + } + + if (BTM_BLE_ISVALID_PARAM(scan_interval, BTM_BLE_SCAN_INT_MIN, max_scan_interval) && + BTM_BLE_ISVALID_PARAM(scan_window, BTM_BLE_SCAN_WIN_MIN, max_scan_window) && + (scan_mode == BTM_BLE_SCAN_MODE_ACTI || scan_mode == BTM_BLE_SCAN_MODE_PASS)) { + p_cb->scan_type = scan_mode; + p_cb->scan_interval = scan_interval; + p_cb->scan_window = scan_window; + + if (scan_setup_status_cback != NULL) { + scan_setup_status_cback(client_if, BTM_SUCCESS); + } + } else { + if (scan_setup_status_cback != NULL) { + scan_setup_status_cback(client_if, BTM_ILLEGAL_VALUE); + } + + BTM_TRACE_ERROR("Illegal params: scan_interval = %d scan_window = %d\n", + scan_interval, scan_window); + } + +} + +tBTM_STATUS BTM_BleSetScanFilterParams(tGATT_IF client_if, UINT32 scan_interval, UINT32 scan_window, + tBLE_SCAN_MODE scan_mode, UINT8 addr_type_own, UINT8 scan_duplicate_filter, tBTM_BLE_SFP scan_filter_policy, + tBLE_SCAN_PARAM_SETUP_CBACK scan_setup_status_cback) +{ + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + UINT32 max_scan_interval; + UINT32 max_scan_window; + tBTM_STATUS ret = BTM_SUCCESS; + + BTM_TRACE_EVENT ("%s\n", __func__); + if (!controller_get_interface()->supports_ble()) { + return BTM_ILLEGAL_VALUE; + } + if (BTM_BleUpdateOwnType(&addr_type_own, NULL) != 0) { + return BTM_ILLEGAL_VALUE; + } + /* If not supporting extended scan support, use the older range for checking */ + if (btm_cb.cmn_ble_vsc_cb.extended_scan_support == 0) { + max_scan_interval = BTM_BLE_SCAN_INT_MAX; + max_scan_window = BTM_BLE_SCAN_WIN_MAX; + } else { + /* If supporting extended scan support, use the new extended range for checking */ + max_scan_interval = BTM_BLE_EXT_SCAN_INT_MAX; + max_scan_window = BTM_BLE_EXT_SCAN_WIN_MAX; + } + + osi_mutex_lock(&scan_param_lock, OSI_MUTEX_MAX_TIMEOUT); + + if (BTM_BLE_ISVALID_PARAM(scan_interval, BTM_BLE_SCAN_INT_MIN, max_scan_interval) && + BTM_BLE_ISVALID_PARAM(scan_window, BTM_BLE_SCAN_WIN_MIN, max_scan_window) && + (scan_mode == BTM_BLE_SCAN_MODE_ACTI || scan_mode == BTM_BLE_SCAN_MODE_PASS) && + (scan_duplicate_filter < BTM_BLE_SCAN_DUPLICATE_MAX) && (scan_window <= scan_interval)) { + p_cb->scan_type = scan_mode; + p_cb->scan_interval = scan_interval; + p_cb->scan_window = scan_window; + p_cb->sfp = scan_filter_policy; + p_cb->scan_params_set = TRUE; + p_cb->scan_duplicate_filter = scan_duplicate_filter; + + + if (btsnd_hcic_ble_set_scan_params(p_cb->scan_type, (UINT16)scan_interval, + (UINT16)scan_window, + addr_type_own, + scan_filter_policy)) { + osi_sem_take(&scan_param_sem, OSI_SEM_MAX_TIMEOUT); + ret = scan_param_status; + } + } else { + ret = BTM_ILLEGAL_VALUE; + BTM_TRACE_ERROR("Illegal params: scan_interval = %d scan_window = %d\n", + scan_interval, scan_window); + } + osi_mutex_unlock(&scan_param_lock); + return ret; +} + + +/******************************************************************************* +** +** 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 ret; + UINT8 rsp_data[BTM_BLE_AD_DATA_LEN], + *p = rsp_data; + + BTM_TRACE_EVENT (" BTM_BleWriteScanRsp"); + + if (!controller_get_interface()->supports_ble()) { + return BTM_ILLEGAL_VALUE; + } + + osi_mutex_lock(&adv_data_lock, OSI_MUTEX_MAX_TIMEOUT); + memset(rsp_data, 0, BTM_BLE_AD_DATA_LEN); + btm_ble_build_adv_data(&data_mask, &p, p_data); + if (data_mask != 0) { + //data length should not exceed 31 bytes + BTM_TRACE_WARNING("%s, Partial data write into ADV", __func__); + } + + if (btsnd_hcic_ble_set_scan_rsp_data((UINT8)(p - rsp_data), rsp_data)) { + osi_sem_take(&adv_data_sem, OSI_SEM_MAX_TIMEOUT); + ret = adv_data_status; + + if (adv_data_status == BTM_SUCCESS && data_mask != 0) { + btm_cb.ble_ctr_cb.inq_var.scan_rsp = TRUE; + } else { + btm_cb.ble_ctr_cb.inq_var.scan_rsp = FALSE; + } + } else { + ret = BTM_ILLEGAL_VALUE; + } + + osi_mutex_unlock(&adv_data_lock); + return ret; +} + +/******************************************************************************* +** +** Function BTM_BleWriteScanRspRaw +** +** Description This function is called to write raw scan response data +** +** Parameters: None. +** +** Returns void +** +*******************************************************************************/ +tBTM_STATUS BTM_BleWriteScanRspRaw(UINT8 *p_raw_scan_rsp, UINT32 raw_scan_rsp_len) +{ + tBTM_STATUS ret; + + osi_mutex_lock(&adv_data_lock, OSI_MUTEX_MAX_TIMEOUT); + if (btsnd_hcic_ble_set_scan_rsp_data((UINT8)raw_scan_rsp_len, p_raw_scan_rsp)) { + osi_sem_take(&adv_data_sem, OSI_SEM_MAX_TIMEOUT); + ret = adv_data_status; + } else { + ret = BTM_NO_RESOURCES; + } + osi_mutex_unlock(&adv_data_lock); + + return ret; +} + +/******************************************************************************* +** +** Function BTM_UpdateBleDuplicateExceptionalList +** +** Description This function is called to update duplicate scan exceptional list. +** +** Parameters: subcode: add, remove or clean duplicate scan exceptional list. +** type: device info type +** device_info: device information +** update_exceptional_list_cmp_cb: complete callback +** +** Returns status +** +*******************************************************************************/ +tBTM_STATUS BTM_UpdateBleDuplicateExceptionalList(uint8_t subcode, uint32_t type, BD_ADDR device_info, + tBTM_UPDATE_DUPLICATE_EXCEPTIONAL_LIST_CMPL_CBACK update_exceptional_list_cmp_cb) +{ + tBTM_BLE_CB *ble_cb = &btm_cb.ble_ctr_cb; + tBTM_STATUS status = BTM_NO_RESOURCES; + + ble_cb->update_exceptional_list_cmp_cb = update_exceptional_list_cmp_cb; + + if (!controller_get_interface()->supports_ble()) { + return BTM_ILLEGAL_VALUE; + } + if(!device_info) { + return BTM_ILLEGAL_VALUE; + } + // subcoe + type + device info + uint8_t device_info_array[1 + 4 + BD_ADDR_LEN] = {0}; + device_info_array[0] = subcode; + device_info_array[1] = type & 0xff; + device_info_array[2] = (type >> 8) & 0xff; + device_info_array[3] = (type >> 16) & 0xff; + device_info_array[4] = (type >> 24) & 0xff; + switch (type) + { + case BTM_DUPLICATE_SCAN_EXCEPTIONAL_INFO_ADV_ADDR: + bt_rcopy(&device_info_array[5], device_info, BD_ADDR_LEN); + break; + case BTM_DUPLICATE_SCAN_EXCEPTIONAL_INFO_MESH_LINK_ID: + memcpy(&device_info_array[5], device_info, 4); + break; + case BTM_DUPLICATE_SCAN_EXCEPTIONAL_INFO_MESH_BEACON_TYPE: + //do nothing + break; + case BTM_DUPLICATE_SCAN_EXCEPTIONAL_INFO_MESH_PROV_SRV_ADV: + //do nothing + break; + case BTM_DUPLICATE_SCAN_EXCEPTIONAL_INFO_MESH_PROXY_SRV_ADV: + //do nothing + break; + default: + //do nothing + break; + } + + status = BTM_VendorSpecificCommand(HCI_VENDOR_BLE_UPDATE_DUPLICATE_EXCEPTIONAL_LIST, 1 + 4 + BD_ADDR_LEN, device_info_array, NULL); + if(status == BTM_CMD_STARTED) { + status = BTM_SUCCESS; + } + + 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; + tBTM_BLE_AD_MASK mask = data_mask; + tBTM_STATUS ret; + + BTM_TRACE_EVENT ("BTM_BleWriteAdvData "); + + if (!controller_get_interface()->supports_ble()) { + return BTM_ILLEGAL_VALUE; + } + osi_mutex_lock(&adv_data_lock, OSI_MUTEX_MAX_TIMEOUT); + memset(p_cb_data, 0, sizeof(tBTM_BLE_LOCAL_ADV_DATA)); + p = p_cb_data->ad_data; + p_cb_data->data_mask = data_mask; + + p_cb_data->p_flags = btm_ble_build_adv_data(&mask, &p, p_data); + + p_cb_data->p_pad = p; + + if (mask != 0) { + //data length should not exceed 31 bytes + BTM_TRACE_WARNING("%s, Partial data write into ADV", __func__); + } + + 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)) { + osi_sem_take(&adv_data_sem, OSI_SEM_MAX_TIMEOUT); + ret = adv_data_status; + } else { + ret = BTM_NO_RESOURCES; + } + osi_mutex_unlock(&adv_data_lock); + return ret; +} + +/******************************************************************************* +** +** Function BTM_BleWriteLongAdvData +** +** Description This function is called to write long advertising data. +** +** Parameters: adv_data: long advertising data +** adv_data_len: the length of long advertising data +** +** Returns void +** +*******************************************************************************/ +tBTM_STATUS BTM_BleWriteLongAdvData(uint8_t *adv_data, uint8_t adv_data_len) +{ + tBTM_STATUS status = BTM_NO_RESOURCES; + if (!controller_get_interface()->supports_ble()) { + return BTM_ILLEGAL_VALUE; + } + if(!adv_data || adv_data_len <= 0 || adv_data_len > BTM_BLE_LONG_ADV_MAX_LEN) { + return BTM_ILLEGAL_VALUE; + } + uint8_t long_adv[BTM_BLE_LONG_ADV_MAX_LEN + 1] = {0}; + long_adv[0] = adv_data_len; + memcpy(&long_adv[1], adv_data, adv_data_len); + status = BTM_VendorSpecificCommand(HCI_VENDOR_BLE_LONG_ADV_DATA, BTM_BLE_LONG_ADV_MAX_LEN + 1, long_adv, NULL); + if(status == BTM_CMD_STARTED) { + status = BTM_SUCCESS; + } + + return status; +} + +/******************************************************************************* +** +** Function BTM_BleWriteAdvDataRaw +** +** Description This function is called to write raw advertising data. +** +** Parameters: None. +** +** Returns void +** +*******************************************************************************/ +tBTM_STATUS BTM_BleWriteAdvDataRaw(UINT8 *p_raw_adv, UINT32 raw_adv_len) +{ + tBTM_STATUS ret; + osi_mutex_lock(&adv_data_lock, OSI_MUTEX_MAX_TIMEOUT); + if (btsnd_hcic_ble_set_adv_data((UINT8)raw_adv_len, p_raw_adv)) { + osi_sem_take(&adv_data_sem, OSI_SEM_MAX_TIMEOUT); + ret = adv_data_status; + } else { + ret = BTM_NO_RESOURCES; + } + osi_mutex_unlock(&adv_data_lock); + + return ret; +} + + +/******************************************************************************* +** +** Function BTM_BleSetRandAddress +** +** Description This function is called to set the LE random address. +** +** Parameters: None. +** +** Returns void +** +*******************************************************************************/ +tBTM_STATUS BTM_BleSetRandAddress(BD_ADDR rand_addr) +{ + if (rand_addr == NULL) { + return BTM_SET_STATIC_RAND_ADDR_FAIL; + } + + if (btm_cb.ble_ctr_cb.inq_var.state != BTM_BLE_IDLE) { + BTM_TRACE_ERROR("Advertising or scaning now, can't set randaddress %d", btm_cb.ble_ctr_cb.inq_var.state); + return BTM_SET_STATIC_RAND_ADDR_FAIL; + } + memcpy(btm_cb.ble_ctr_cb.addr_mgnt_cb.private_addr, rand_addr, BD_ADDR_LEN); + memcpy(btm_cb.ble_ctr_cb.addr_mgnt_cb.static_rand_addr, rand_addr, BD_ADDR_LEN); + //send the set random address to the controller + if(btsnd_hcic_ble_set_random_addr(rand_addr)) { + btm_cb.ble_ctr_cb.addr_mgnt_cb.exist_addr_bit |= BTM_BLE_GAP_ADDR_BIT_RANDOM; + return BTM_SUCCESS; + } else { + return BTM_SET_STATIC_RAND_ADDR_FAIL; + } +} + +/******************************************************************************* +** +** Function BTM_BleClearRandAddress +** +** Description This function is called to clear the LE random address. +** +** Parameters: None. +** +** Returns void +** +*******************************************************************************/ +void BTM_BleClearRandAddress(void) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + if (btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type == BLE_ADDR_RANDOM && (p_cb->inq_var.state != BTM_BLE_IDLE)) { + BTM_TRACE_ERROR("Advertising or scaning now, can't restore public address "); + return; + } + memset(btm_cb.ble_ctr_cb.addr_mgnt_cb.static_rand_addr, 0, BD_ADDR_LEN); + btm_cb.ble_ctr_cb.addr_mgnt_cb.exist_addr_bit &= (~BTM_BLE_GAP_ADDR_BIT_RANDOM); + btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type = BLE_ADDR_PUBLIC; +} +/******************************************************************************* +** +** Function BTM_BleGetCurrentAddress +** +** Description This function is called to get local used BLE address. +** +** Parameters: None. +** +** Returns success or fail +** +*******************************************************************************/ +BOOLEAN BTM_BleGetCurrentAddress(BD_ADDR addr, uint8_t *addr_type) +{ + if(addr == NULL || addr_type == NULL) { + BTM_TRACE_ERROR("%s addr or addr_type is NULL\n", __func__); + return FALSE; + } + if(btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type == BLE_ADDR_RANDOM) { + *addr_type = BLE_ADDR_RANDOM; + memcpy(addr, btm_cb.ble_ctr_cb.addr_mgnt_cb.private_addr, BD_ADDR_LEN); + } else if(btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type == BLE_ADDR_PUBLIC) { + *addr_type = BLE_ADDR_PUBLIC; + memcpy(addr, &controller_get_interface()->get_address()->address, BD_ADDR_LEN); + } else { + BTM_TRACE_ERROR("%s\n", __func__); + memset(addr, 0, BD_ADDR_LEN); + return FALSE; + } + return TRUE; +} + +/******************************************************************************* +** +** 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_API("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 */ + + /* Break loop if advertising data is in an incorrect format, + as it may lead to memory overflow */ + if (p >= p_adv + BTM_BLE_CACHE_ADV_DATA_MAX) { + break; + } + + STREAM_TO_UINT8(length, p); + } + + *p_length = 0; + return NULL; +} + +/******************************************************************************* +** +** Function BTM__BLEReadDiscoverability +** +** Description This function is called to read the current LE discoverability +** mode of the device. +** +** Returns BTM_BLE_NON_DISCOVERABLE ,BTM_BLE_LIMITED_DISCOVERABLE or +** BTM_BLE_GENRAL_DISCOVERABLE +** +*******************************************************************************/ +UINT16 BTM_BleReadDiscoverability(void) +{ + BTM_TRACE_API("%s\n", __FUNCTION__); + + return (btm_cb.ble_ctr_cb.inq_var.discoverable_mode); +} + +/******************************************************************************* +** +** Function BTM__BLEReadConnectability +** +** Description This function is called to read the current LE connectibility +** mode of the device. +** +** Returns BTM_BLE_NON_CONNECTABLE or BTM_BLE_CONNECTABLE +** +*******************************************************************************/ +UINT16 BTM_BleReadConnectability(void) +{ + BTM_TRACE_API ("%s\n", __FUNCTION__); + + return (btm_cb.ble_ctr_cb.inq_var.connectable_mode); +} + +void BTM_Recovery_Pre_State(void) +{ + tBTM_BLE_INQ_CB *ble_inq_cb = &btm_cb.ble_ctr_cb.inq_var; + + if (ble_inq_cb->state & BTM_BLE_ADVERTISING) { + btm_ble_stop_adv(); + btm_ble_start_adv(); + } + if (ble_inq_cb->state & BTM_BLE_SCANNING) { + btm_ble_start_scan(); + } + + return; +} + +/******************************************************************************* +** +** Function BTM_GetCurrentConnParams +** +** Description This function is called to read the current connection parameters +** of the device +** +** Returns TRUE or FALSE +** +*******************************************************************************/ + +BOOLEAN BTM_GetCurrentConnParams(BD_ADDR bda, uint16_t *interval, uint16_t *latency, uint16_t *timeout) +{ + if( (interval == NULL) || (latency == NULL) || (timeout == NULL) ) { + BTM_TRACE_ERROR("%s error ", __func__); + return FALSE; + } + + if(btm_get_current_conn_params(bda, interval, latency, timeout)) { + return TRUE; + } + + return FALSE; +} + +/******************************************************************************* +** +** Function btm_ble_build_adv_data +** +** Description This function is called build the adv data and rsp data. +*******************************************************************************/ +UINT8 *btm_ble_build_adv_data(tBTM_BLE_AD_MASK *p_data_mask, UINT8 **p_dst, + tBTM_BLE_ADV_DATA *p_data) +{ + UINT32 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_PROP_ELEM *p_elem; + + BTM_TRACE_EVENT (" 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++ = MIN_ADV_LENGTH; + *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; + } + /* appearance data */ + if (len > 3 && data_mask & BTM_BLE_AD_BIT_APPEARANCE) { + *p++ = 3; /* length */ + *p++ = BTM_BLE_AD_TYPE_APPEARANCE; + UINT16_TO_STREAM(p, p_data->appearance); + len -= 4; + + data_mask &= ~BTM_BLE_AD_BIT_APPEARANCE; + } + /* device name */ +#if BTM_MAX_LOC_BD_NAME_LEN > 0 + if (len > MIN_ADV_LENGTH && data_mask & BTM_BLE_AD_BIT_DEV_NAME) { + if (strlen(btm_cb.cfg.bd_name) > (UINT16)(len - MIN_ADV_LENGTH)) { + cp_len = (UINT16)(len - MIN_ADV_LENGTH); + *p++ = cp_len + 1; + *p++ = BTM_BLE_AD_TYPE_NAME_SHORT; + ARRAY_TO_STREAM(p, btm_cb.cfg.bd_name, cp_len); + } 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 + MIN_ADV_LENGTH); + data_mask &= ~BTM_BLE_AD_BIT_DEV_NAME; + } +#endif + /* manufacturer data */ + if (len > MIN_ADV_LENGTH && data_mask & BTM_BLE_AD_BIT_MANU && + p_data && p_data->p_manu && + p_data->p_manu->len != 0 && p_data->p_manu->p_val) { + if (p_data->p_manu->len > (len - MIN_ADV_LENGTH)) { + cp_len = len - MIN_ADV_LENGTH; + } else { + cp_len = p_data->p_manu->len; + } + BTM_TRACE_DEBUG("cp_len = %d\n,p_data->p_manu->len=%d\n", cp_len, p_data->p_manu->len); + for (int i = 0; i < p_data->p_manu->len; i++) { + BTM_TRACE_DEBUG("p_data->p_manu->p_val[%d] = %x\n", i, p_data->p_manu->p_val[i]); + } + *p++ = cp_len + 1; + *p++ = BTM_BLE_AD_TYPE_MANU; + ARRAY_TO_STREAM(p, p_data->p_manu->p_val, cp_len); + BTM_TRACE_DEBUG("p_addr = %p\n,p_data->p_manu->p_val = %p\n", p, p_data->p_manu->p_val); + len -= (cp_len + MIN_ADV_LENGTH); + data_mask &= ~BTM_BLE_AD_BIT_MANU; + } + /* TX power */ + if (len > MIN_ADV_LENGTH && data_mask & BTM_BLE_AD_BIT_TX_PWR) { + *p++ = MIN_ADV_LENGTH; + *p++ = BTM_BLE_AD_TYPE_TX_PWR; + if (p_data->tx_power > BTM_BLE_ADV_TX_POWER_MAX) { + p_data->tx_power = BTM_BLE_ADV_TX_POWER_MAX; + } + *p++ = btm_ble_map_adv_tx_power(p_data->tx_power); + len -= 3; + data_mask &= ~BTM_BLE_AD_BIT_TX_PWR; + } + /* 16 bits services */ + if (len > MIN_ADV_LENGTH && data_mask & BTM_BLE_AD_BIT_SERVICE && + p_data && p_data->p_services && + p_data->p_services->num_service != 0 && + p_data->p_services->p_uuid) { + if (p_data->p_services->num_service * LEN_UUID_16 > (len - MIN_ADV_LENGTH)) { + cp_len = (len - MIN_ADV_LENGTH) / LEN_UUID_16; + *p ++ = 1 + cp_len * LEN_UUID_16; + *p++ = BTM_BLE_AD_TYPE_16SRV_PART; + } else { + cp_len = p_data->p_services->num_service; + *p++ = 1 + cp_len * LEN_UUID_16; + *p++ = BTM_BLE_AD_TYPE_16SRV_CMPL; + } + for (i = 0; i < cp_len; i ++) { + UINT16_TO_STREAM(p, *(p_data->p_services->p_uuid + i)); + } + + len -= (cp_len * MIN_ADV_LENGTH + MIN_ADV_LENGTH); + data_mask &= ~BTM_BLE_AD_BIT_SERVICE; + } + /* 32 bits service uuid */ + if (len > MIN_ADV_LENGTH && data_mask & BTM_BLE_AD_BIT_SERVICE_32 && + p_data && p_data->p_service_32b && + p_data->p_service_32b->num_service != 0 && + p_data->p_service_32b->p_uuid) { + if ((p_data->p_service_32b->num_service * LEN_UUID_32) > (len - MIN_ADV_LENGTH)) { + cp_len = (len - MIN_ADV_LENGTH) / LEN_UUID_32; + *p ++ = 1 + cp_len * LEN_UUID_32; + *p++ = BTM_BLE_AD_TYPE_32SRV_PART; + } else { + cp_len = p_data->p_service_32b->num_service; + *p++ = 1 + cp_len * LEN_UUID_32; + *p++ = BTM_BLE_AD_TYPE_32SRV_CMPL; + } + for (i = 0; i < cp_len; i ++) { + UINT32_TO_STREAM(p, *(p_data->p_service_32b->p_uuid + i)); + } + + len -= (cp_len * LEN_UUID_32 + MIN_ADV_LENGTH); + data_mask &= ~BTM_BLE_AD_BIT_SERVICE_32; + } + /* 128 bits services */ + if (len >= (MAX_UUID_SIZE + 2) && data_mask & BTM_BLE_AD_BIT_SERVICE_128 && + p_data && p_data->p_services_128b) { + *p ++ = 1 + MAX_UUID_SIZE; + if (!p_data->p_services_128b->list_cmpl) { + *p++ = BTM_BLE_AD_TYPE_128SRV_PART; + } else { + *p++ = BTM_BLE_AD_TYPE_128SRV_CMPL; + } + + ARRAY_TO_STREAM(p, p_data->p_services_128b->uuid128, MAX_UUID_SIZE); + + len -= (MAX_UUID_SIZE + MIN_ADV_LENGTH); + data_mask &= ~BTM_BLE_AD_BIT_SERVICE_128; + } + /* 32 bits Service Solicitation UUIDs */ + if (len > MIN_ADV_LENGTH && data_mask & BTM_BLE_AD_BIT_SERVICE_32SOL && + p_data && p_data->p_sol_service_32b && + p_data->p_sol_service_32b->num_service != 0 && + p_data->p_sol_service_32b->p_uuid) { + if ((p_data->p_sol_service_32b->num_service * LEN_UUID_32) > (len - MIN_ADV_LENGTH)) { + cp_len = (len - MIN_ADV_LENGTH) / LEN_UUID_32; + *p ++ = 1 + cp_len * LEN_UUID_32; + } else { + cp_len = p_data->p_sol_service_32b->num_service; + *p++ = 1 + cp_len * LEN_UUID_32; + } + + *p++ = BTM_BLE_AD_TYPE_32SOL_SRV_UUID; + for (i = 0; i < cp_len; i ++) { + UINT32_TO_STREAM(p, *(p_data->p_sol_service_32b->p_uuid + i)); + } + + len -= (cp_len * LEN_UUID_32 + MIN_ADV_LENGTH); + data_mask &= ~BTM_BLE_AD_BIT_SERVICE_32SOL; + } + /* 128 bits Solicitation services UUID */ + if (len >= (MAX_UUID_SIZE + MIN_ADV_LENGTH) && data_mask & BTM_BLE_AD_BIT_SERVICE_128SOL && + p_data && p_data->p_sol_service_128b) { + *p ++ = 1 + MAX_UUID_SIZE; + *p++ = BTM_BLE_AD_TYPE_128SOL_SRV_UUID; + ARRAY_TO_STREAM(p, p_data->p_sol_service_128b->uuid128, MAX_UUID_SIZE); + len -= (MAX_UUID_SIZE + MIN_ADV_LENGTH); + data_mask &= ~BTM_BLE_AD_BIT_SERVICE_128SOL; + } + /* 16bits/32bits/128bits Service Data */ + if (len > MIN_ADV_LENGTH && data_mask & BTM_BLE_AD_BIT_SERVICE_DATA && + p_data && p_data->p_service_data->len != 0 && p_data->p_service_data->p_val) { + if (len > (p_data->p_service_data->service_uuid.len + MIN_ADV_LENGTH)) { + if (p_data->p_service_data->len > (len - MIN_ADV_LENGTH)) { + cp_len = len - MIN_ADV_LENGTH - p_data->p_service_data->service_uuid.len; + } else { + cp_len = p_data->p_service_data->len; + } + + *p++ = cp_len + 1 + p_data->p_service_data->service_uuid.len; + if (p_data->p_service_data->service_uuid.len == LEN_UUID_16) { + *p++ = BTM_BLE_AD_TYPE_SERVICE_DATA; + UINT16_TO_STREAM(p, p_data->p_service_data->service_uuid.uu.uuid16); + } else if (p_data->p_service_data->service_uuid.len == LEN_UUID_32) { + *p++ = BTM_BLE_AD_TYPE_32SERVICE_DATA; + UINT32_TO_STREAM(p, p_data->p_service_data->service_uuid.uu.uuid32); + } else { + *p++ = BTM_BLE_AD_TYPE_128SERVICE_DATA; + ARRAY_TO_STREAM(p, p_data->p_service_data->service_uuid.uu.uuid128, + LEN_UUID_128); + } + + ARRAY_TO_STREAM(p, p_data->p_service_data->p_val, cp_len); + + len -= (cp_len + MIN_ADV_LENGTH + p_data->p_service_data->service_uuid.len); + data_mask &= ~BTM_BLE_AD_BIT_SERVICE_DATA; + } else { + BTM_TRACE_WARNING("service data does not fit"); + } + } + + 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_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 >= (MIN_ADV_LENGTH + 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 -= (MIN_ADV_LENGTH + p_elem->len); + } else { + BTM_TRACE_WARNING("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_select_adv_interval +** +** Description select adv interval based on device mode +** +** Returns void +** +*******************************************************************************/ +void btm_ble_select_adv_interval(tBTM_BLE_INQ_CB *p_cb, UINT8 evt_type, UINT16 *p_adv_int_min, UINT16 *p_adv_int_max) +{ + if (p_cb->adv_interval_min && p_cb->adv_interval_max) { + *p_adv_int_min = p_cb->adv_interval_min; + *p_adv_int_max = p_cb->adv_interval_max; + } else { + switch (evt_type) { + case BTM_BLE_CONNECT_EVT: + case BTM_BLE_CONNECT_LO_DUTY_DIR_EVT: + *p_adv_int_min = *p_adv_int_max = BTM_BLE_GAP_ADV_FAST_INT_1; + break; + + case BTM_BLE_NON_CONNECT_EVT: + case BTM_BLE_DISCOVER_EVT: + *p_adv_int_min = *p_adv_int_max = BTM_BLE_GAP_ADV_FAST_INT_2; + break; + + /* connectable directed event */ + case BTM_BLE_CONNECT_DIR_EVT: + *p_adv_int_min = BTM_BLE_GAP_ADV_DIR_MIN_INT; + *p_adv_int_max = BTM_BLE_GAP_ADV_DIR_MAX_INT; + break; + + default: + *p_adv_int_min = *p_adv_int_max = BTM_BLE_GAP_ADV_SLOW_INT; + break; + } + } + return; +} + +/******************************************************************************* +** +** Function btm_ble_update_dmt_flag_bits +** +** Description Obtain updated adv flag value based on connect and discoverability mode. +** Also, setup DMT support value in the flag based on whether the controller +** supports both LE and BR/EDR. +** +** Parameters: flag_value (Input / Output) - flag value +** connect_mode (Input) - Connect mode value +** disc_mode (Input) - discoverability mode +** +** Returns void +** +*******************************************************************************/ +void btm_ble_update_dmt_flag_bits(UINT8 *adv_flag_value, const UINT16 connect_mode, + const UINT16 disc_mode) +{ + /* BR/EDR non-discoverable , non-connectable */ + if ((disc_mode & BTM_DISCOVERABLE_MASK) == 0 && + (connect_mode & BTM_CONNECTABLE_MASK) == 0) { + *adv_flag_value |= BTM_BLE_BREDR_NOT_SPT; + } else { + *adv_flag_value &= ~BTM_BLE_BREDR_NOT_SPT; + } + + /* if local controller support, mark both controller and host support in flag */ + if (controller_get_interface()->supports_simultaneous_le_bredr()) { + *adv_flag_value |= (BTM_BLE_DMT_CONTROLLER_SPT | BTM_BLE_DMT_HOST_SPT); + } else { + *adv_flag_value &= ~(BTM_BLE_DMT_CONTROLLER_SPT | BTM_BLE_DMT_HOST_SPT); + } +} + +/******************************************************************************* +** +** Function btm_ble_set_adv_flag +** +** Description Set adv flag in adv data. +** +** Parameters: connect_mode (Input)- Connect mode value +** disc_mode (Input) - discoverability mode +** +** Returns void +** +*******************************************************************************/ +void btm_ble_set_adv_flag(UINT16 connect_mode, UINT16 disc_mode) +{ + UINT8 flag = 0, old_flag = 0; + tBTM_BLE_LOCAL_ADV_DATA *p_adv_data = &btm_cb.ble_ctr_cb.inq_var.adv_data; + + if (p_adv_data->p_flags != NULL) { + flag = old_flag = *(p_adv_data->p_flags); + } + + btm_ble_update_dmt_flag_bits (&flag, connect_mode, disc_mode); + + BTM_TRACE_DEBUG("disc_mode %04x", disc_mode); + /* update discoverable flag */ + if (disc_mode & BTM_BLE_LIMITED_DISCOVERABLE) { + flag &= ~BTM_BLE_GEN_DISC_FLAG; + flag |= BTM_BLE_LIMIT_DISC_FLAG; + } else if (disc_mode & BTM_BLE_GENERAL_DISCOVERABLE) { + flag |= BTM_BLE_GEN_DISC_FLAG; + flag &= ~BTM_BLE_LIMIT_DISC_FLAG; + } else { /* remove all discoverable flags */ + flag &= ~(BTM_BLE_LIMIT_DISC_FLAG | BTM_BLE_GEN_DISC_FLAG); + } + + if (flag != old_flag) { + BTM_TRACE_ERROR("flag = 0x%x,old_flag = 0x%x", flag, old_flag); + btm_ble_update_adv_flag(flag); + } +} +/******************************************************************************* +** +** Function btm_ble_set_discoverability +** +** Description This function is called to set BLE discoverable mode. +** +** Parameters: combined_mode: discoverability mode. +** +** Returns BTM_SUCCESS is status set successfully; otherwise failure. +** +*******************************************************************************/ +tBTM_STATUS btm_ble_set_discoverability(UINT16 combined_mode) +{ + tBTM_LE_RANDOM_CB *p_addr_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + UINT16 mode = (combined_mode & BTM_BLE_DISCOVERABLE_MASK); + UINT8 new_mode = BTM_BLE_ADV_ENABLE; + UINT8 evt_type; + tBTM_STATUS status = BTM_SUCCESS; + BD_ADDR p_addr_ptr = {0}; + tBLE_ADDR_TYPE init_addr_type = BLE_ADDR_PUBLIC, + own_addr_type = p_addr_cb->own_addr_type; + UINT16 adv_int_min, adv_int_max; + + BTM_TRACE_EVENT ("%s mode=0x%0x combined_mode=0x%x\n", __FUNCTION__, mode, combined_mode); + + /*** Check mode parameter ***/ + if (mode > BTM_BLE_MAX_DISCOVERABLE) { + return (BTM_ILLEGAL_VALUE); + } + + p_cb->discoverable_mode = mode; + + evt_type = btm_set_conn_mode_adv_init_addr(p_cb, p_addr_ptr, &init_addr_type, &own_addr_type); + + if (p_cb->connectable_mode == BTM_BLE_NON_CONNECTABLE && mode == BTM_BLE_NON_DISCOVERABLE) { + new_mode = BTM_BLE_ADV_DISABLE; + } + + btm_ble_select_adv_interval(p_cb, evt_type, &adv_int_min, &adv_int_max); + + btu_stop_timer(&p_cb->fast_adv_timer); + + /* update adv params if start advertising */ + BTM_TRACE_EVENT ("evt_type=0x%x p-cb->evt_type=0x%x\n ", evt_type, p_cb->evt_type); + + if (new_mode == BTM_BLE_ADV_ENABLE) { + btm_ble_set_adv_flag (btm_cb.btm_inq_vars.connectable_mode, combined_mode); + + if (evt_type != p_cb->evt_type || p_cb->adv_addr_type != own_addr_type + || !p_cb->fast_adv_on) { + btm_ble_stop_adv(); + + /* update adv params */ + if (!btsnd_hcic_ble_write_adv_params (adv_int_min, + adv_int_max, + evt_type, + own_addr_type, + init_addr_type, + p_addr_ptr, + p_cb->adv_chnl_map, + p_cb->afp)) { + status = BTM_NO_RESOURCES; + } else { + p_cb->evt_type = evt_type; + p_cb->adv_addr_type = own_addr_type; + } + } + } + + if (status == BTM_SUCCESS && p_cb->adv_mode != new_mode) { + if (new_mode == BTM_BLE_ADV_ENABLE) { + status = btm_ble_start_adv(); + } else { + status = btm_ble_stop_adv(); + } + } + + if (p_cb->adv_mode == BTM_BLE_ADV_ENABLE) { + p_cb->fast_adv_on = TRUE; + /* start initial GAP mode adv timer */ + btu_start_timer (&p_cb->fast_adv_timer, BTU_TTYPE_BLE_GAP_FAST_ADV, + BTM_BLE_GAP_FAST_ADV_TOUT); + } else { +#if BLE_PRIVACY_SPT == TRUE + btm_ble_disable_resolving_list(BTM_BLE_RL_ADV, TRUE); +#endif + } + + /* set up stop advertising timer */ + if (status == BTM_SUCCESS && mode == BTM_BLE_LIMITED_DISCOVERABLE) { + BTM_TRACE_EVENT ("start timer for limited disc mode duration=%d (180 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: combined_mode: connectability mode. +** +** Returns BTM_SUCCESS is status set successfully; otherwise failure. +** +*******************************************************************************/ +tBTM_STATUS btm_ble_set_connectability(UINT16 combined_mode) +{ + tBTM_LE_RANDOM_CB *p_addr_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + UINT16 mode = (combined_mode & BTM_BLE_CONNECTABLE_MASK); + UINT8 new_mode = BTM_BLE_ADV_ENABLE; + UINT8 evt_type; + tBTM_STATUS status = BTM_SUCCESS; + BD_ADDR p_addr_ptr = {0}; + tBLE_ADDR_TYPE peer_addr_type = BLE_ADDR_PUBLIC, + own_addr_type = p_addr_cb->own_addr_type; + UINT16 adv_int_min, adv_int_max; + + BTM_TRACE_EVENT ("%s mode=0x%0x combined_mode=0x%x\n", __FUNCTION__, mode, combined_mode); + + /*** Check mode parameter ***/ + if (mode > BTM_BLE_MAX_CONNECTABLE) { + return (BTM_ILLEGAL_VALUE); + } + + p_cb->connectable_mode = mode; + + evt_type = btm_set_conn_mode_adv_init_addr(p_cb, p_addr_ptr, &peer_addr_type, &own_addr_type); + + if (mode == BTM_BLE_NON_CONNECTABLE && p_cb->discoverable_mode == BTM_BLE_NON_DISCOVERABLE) { + new_mode = BTM_BLE_ADV_DISABLE; + } + + btm_ble_select_adv_interval(p_cb, evt_type, &adv_int_min, &adv_int_max); + + btu_stop_timer(&p_cb->fast_adv_timer); + /* update adv params if needed */ + if (new_mode == BTM_BLE_ADV_ENABLE) { + btm_ble_set_adv_flag (combined_mode, btm_cb.btm_inq_vars.discoverable_mode); + if (p_cb->evt_type != evt_type || p_cb->adv_addr_type != p_addr_cb->own_addr_type + || !p_cb->fast_adv_on) { + btm_ble_stop_adv(); + + if (!btsnd_hcic_ble_write_adv_params (adv_int_min, + adv_int_max, + evt_type, + own_addr_type, + peer_addr_type, + p_addr_ptr, + p_cb->adv_chnl_map, + p_cb->afp)) { + status = BTM_NO_RESOURCES; + } else { + p_cb->evt_type = evt_type; + p_cb->adv_addr_type = own_addr_type; + } + } + } + + /* update advertising mode */ + if (status == BTM_SUCCESS && new_mode != p_cb->adv_mode) { + if (new_mode == BTM_BLE_ADV_ENABLE) { + status = btm_ble_start_adv(); + } else { + status = btm_ble_stop_adv(); + } + } + + if (p_cb->adv_mode == BTM_BLE_ADV_ENABLE) { + p_cb->fast_adv_on = TRUE; + /* start initial GAP mode adv timer */ + btu_start_timer (&p_cb->fast_adv_timer, BTU_TTYPE_BLE_GAP_FAST_ADV, + BTM_BLE_GAP_FAST_ADV_TOUT); + } else { +#if BLE_PRIVACY_SPT == TRUE + btm_ble_disable_resolving_list(BTM_BLE_RL_ADV, TRUE); +#endif + } + 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_NO_RESOURCES if could not allocate a message buffer +** BTM_BUSY - if an inquiry is already active +** +*******************************************************************************/ +tBTM_STATUS btm_ble_start_inquiry (UINT8 mode, UINT8 duration) +{ + tBTM_STATUS status = BTM_CMD_STARTED; + tBTM_BLE_CB *p_ble_cb = &btm_cb.ble_ctr_cb; + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + + BTM_TRACE_DEBUG("btm_ble_start_inquiry: mode = %02x inq_active = 0x%02x", mode, btm_cb.btm_inq_vars.inq_active); + + /* if selective connection is active, or inquiry is already active, reject it */ + if (BTM_BLE_IS_INQ_ACTIVE(p_ble_cb->scan_activity) || + BTM_BLE_IS_SEL_CONN_ACTIVE (p_ble_cb->scan_activity)) { + BTM_TRACE_ERROR("LE Inquiry is active, can not start inquiry"); + return (BTM_BUSY); + } + + if (!BTM_BLE_IS_SCAN_ACTIVE(p_ble_cb->scan_activity)) { + btsnd_hcic_ble_set_scan_params(BTM_BLE_SCAN_MODE_ACTI, + BTM_BLE_LOW_LATENCY_SCAN_INT, + BTM_BLE_LOW_LATENCY_SCAN_WIN, + btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type, + SP_ADV_ALL); +#if (defined BLE_PRIVACY_SPT && BLE_PRIVACY_SPT == TRUE) + /* enable IRK list */ + //btm_ble_enable_resolving_list_for_platform(BTM_BLE_RL_SCAN); +#endif + status = btm_ble_start_scan(); + } else if ((p_ble_cb->inq_var.scan_interval != BTM_BLE_LOW_LATENCY_SCAN_INT) || + (p_ble_cb->inq_var.scan_window != BTM_BLE_LOW_LATENCY_SCAN_WIN)) { + BTM_TRACE_DEBUG("%s, restart LE scan with low latency scan params", __FUNCTION__); + btsnd_hcic_ble_set_scan_enable(BTM_BLE_SCAN_DISABLE, BTM_BLE_DUPLICATE_ENABLE); + btsnd_hcic_ble_set_scan_params(BTM_BLE_SCAN_MODE_ACTI, + BTM_BLE_LOW_LATENCY_SCAN_INT, + BTM_BLE_LOW_LATENCY_SCAN_WIN, + btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type, + SP_ADV_ALL); + btsnd_hcic_ble_set_scan_enable(BTM_BLE_SCAN_ENABLE, BTM_BLE_DUPLICATE_DISABLE); + } + + if (status == BTM_CMD_STARTED) { + p_inq->inq_active |= mode; + p_ble_cb->scan_activity |= mode; + + BTM_TRACE_DEBUG("btm_ble_start_inquiry inq_active = 0x%02x", p_inq->inq_active); + + if (duration != 0) { + /* start inquiry timer */ + btu_start_timer (&p_ble_cb->inq_var.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 + 1)); + if (length > BD_NAME_LEN) { + length = 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 + 1, hci_status); +#if (SMP_INCLUDED == TRUE) + btm_sec_rmt_name_request_complete (bda, (UINT8 *)p_name, hci_status); +#endif ///SMP_INCLUDED == TRUE +} + +/******************************************************************************* +** +** 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 (!controller_get_interface()->supports_ble()) { + return BTM_ERR_PROCESSING; + } + + 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_DEBUG("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 (defined(GAP_INCLUDED) && GAP_INCLUDED == TRUE && GATTS_INCLUDED == TRUE) + if (!GAP_BleReadPeerDevName(remote_bda, btm_ble_read_remote_name_cmpl)) { + return BTM_BUSY; + } +#endif + + 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 = TRUE; + +#if (defined(GAP_INCLUDED) && GAP_INCLUDED == TRUE && GATTS_INCLUDED == TRUE) + status = GAP_BleCancelReadPeerDevName(remote_bda); +#endif + + 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_DEBUG ("btm_ble_update_adv_flag new=0x%x", flag); + + if (p_adv_data->p_flags != NULL) { + BTM_TRACE_DEBUG ("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; + + BTM_TRACE_EVENT (" btm_ble_parse_adv_data"); + + while (len > 0) { + BTM_TRACE_DEBUG("btm_ble_parse_adv_data: len = %d", len); + if ((ad_len = *p_cur ++) == 0) { + break; + } + + ad_type = *p_cur ++; + + BTM_TRACE_DEBUG(" 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_DEBUG("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_DEBUG("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_DEBUG("BTM_BLE_AD_TYPE_TX_PWR tx_level = %d", p_adv_data->tx_power_level); + break; + + case BTM_BLE_AD_TYPE_MANU: + + case BTM_BLE_AD_TYPE_16SRV_PART: + case BTM_BLE_AD_TYPE_16SRV_CMPL: + p_adv_data->ad_mask |= BTM_BLE_AD_BIT_SERVICE; + /* need allocate memory to store UUID list */ + p_adv_data->service.num_service = (ad_len - 1) / 2; + BTM_TRACE_DEBUG("service UUID list, num = %d", p_adv_data->service.num_service); + p_cur += (ad_len - 1); + break; + + case BTM_BLE_AD_TYPE_SOL_SRV_UUID: + p_adv_data->ad_mask |= BTM_BLE_AD_BIT_SERVICE_SOL; + /* need allocate memory to store UUID list */ + p_adv_data->service.num_service = (ad_len - 1) / 2; + BTM_TRACE_DEBUG("service UUID list, num = %d", p_adv_data->service.num_service); + p_cur += (ad_len - 1); + break; + + case BTM_BLE_AD_TYPE_128SOL_SRV_UUID: + p_adv_data->ad_mask |= BTM_BLE_AD_BIT_SERVICE_128SOL; + /* need allocate memory to store UUID list */ + p_adv_data->service.num_service = (ad_len - 1) / 16; + BTM_TRACE_DEBUG("service UUID list, num = %d", p_adv_data->service.num_service); + p_cur += (ad_len - 1); + break; + + case BTM_BLE_AD_TYPE_APPEARANCE: + case BTM_BLE_AD_TYPE_PUBLIC_TARGET: + case BTM_BLE_AD_TYPE_RANDOM_TARGET: + default: + 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(BD_ADDR bda, 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); + p_cur->adv_data_len = 0; + p_cur->scan_rsp_len = 0; + } + + //Clear the adv cache if the addresses are not equal + if(memcmp(bda, p_le_inq_cb->adv_addr, BD_ADDR_LEN) != 0) { + p_le_inq_cb->adv_len = 0; + memcpy(p_le_inq_cb->adv_addr, bda, BD_ADDR_LEN); + memset(p_le_inq_cb->adv_data_cache, 0, BTM_BLE_CACHE_ADV_DATA_MAX); + p_cur->adv_data_len = 0; + p_cur->scan_rsp_len = 0; + } + + if (data_len > 0) { + p_cache = &p_le_inq_cb->adv_data_cache[p_le_inq_cb->adv_len]; + if((data_len + p_le_inq_cb->adv_len) <= BTM_BLE_CACHE_ADV_DATA_MAX) { + memcpy(p_cache, p, data_len); + p_le_inq_cb->adv_len += data_len; + } + } + + if (evt_type != BTM_BLE_SCAN_RSP_EVT) { + p_cur->adv_data_len = p_le_inq_cb->adv_len; + } + else { + p_cur->scan_rsp_len = p_le_inq_cb->adv_len - p_cur->adv_data_len; + } + + /* 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 +** +*******************************************************************************/ +UINT8 btm_ble_is_discoverable(BD_ADDR bda, UINT8 evt_type, UINT8 *p) +{ + UINT8 *p_flag, flag = 0, rt = 0; + UINT8 data_len; + tBTM_INQ_PARMS *p_cond = &btm_cb.btm_inq_vars.inqparms; + tBTM_BLE_INQ_CB *p_le_inq_cb = &btm_cb.ble_ctr_cb.inq_var; + + UNUSED(p); + + /* for observer, always "discoverable */ + if (BTM_BLE_IS_OBS_ACTIVE(btm_cb.ble_ctr_cb.scan_activity)) { + rt |= BTM_BLE_OBS_RESULT; + } + /* for discover, always "discoverable */ + if (BTM_BLE_IS_DISCO_ACTIVE(btm_cb.ble_ctr_cb.scan_activity)) { + rt |= BTM_BLE_DISCO_RESULT; + } + + if (BTM_BLE_IS_SEL_CONN_ACTIVE(btm_cb.ble_ctr_cb.scan_activity) && + (evt_type == BTM_BLE_CONNECT_EVT || evt_type == BTM_BLE_CONNECT_DIR_EVT)) { + rt |= BTM_BLE_SEL_CONN_RESULT; + } + + /* 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_DEBUG("BD ADDR does not meet filter condition"); + return rt; + } + + 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, &data_len)) != NULL) { + flag = * p_flag; + + if ((btm_cb.btm_inq_vars.inq_active & BTM_BLE_GENERAL_INQUIRY) && + (flag & (BTM_BLE_LIMIT_DISC_FLAG | BTM_BLE_GEN_DISC_FLAG)) != 0) { + BTM_TRACE_DEBUG("Find Generable Discoverable device"); + rt |= BTM_BLE_INQ_RESULT; + } + + else if (btm_cb.btm_inq_vars.inq_active & BTM_BLE_LIMITED_INQUIRY && + (flag & BTM_BLE_LIMIT_DISC_FLAG) != 0) { + BTM_TRACE_DEBUG("Find limited discoverable device"); + rt |= BTM_BLE_INQ_RESULT; + } + } + } + return rt; +} + +static void btm_ble_appearance_to_cod(UINT16 appearance, UINT8 *dev_class) +{ + dev_class[0] = 0; + + switch (appearance) { + case BTM_BLE_APPEARANCE_GENERIC_PHONE: + dev_class[1] = BTM_COD_MAJOR_PHONE; + dev_class[2] = BTM_COD_MINOR_UNCLASSIFIED; + break; + case BTM_BLE_APPEARANCE_GENERIC_COMPUTER: + dev_class[1] = BTM_COD_MAJOR_COMPUTER; + dev_class[2] = BTM_COD_MINOR_UNCLASSIFIED; + break; + case BTM_BLE_APPEARANCE_GENERIC_REMOTE: + dev_class[1] = BTM_COD_MAJOR_PERIPHERAL; + dev_class[2] = BTM_COD_MINOR_REMOTE_CONTROL; + break; + case BTM_BLE_APPEARANCE_GENERIC_THERMOMETER: + case BTM_BLE_APPEARANCE_THERMOMETER_EAR: + dev_class[1] = BTM_COD_MAJOR_HEALTH; + dev_class[2] = BTM_COD_MINOR_THERMOMETER; + break; + case BTM_BLE_APPEARANCE_GENERIC_HEART_RATE: + case BTM_BLE_APPEARANCE_HEART_RATE_BELT: + dev_class[1] = BTM_COD_MAJOR_HEALTH; + dev_class[2] = BTM_COD_MINOR_HEART_PULSE_MONITOR; + break; + case BTM_BLE_APPEARANCE_GENERIC_BLOOD_PRESSURE: + case BTM_BLE_APPEARANCE_BLOOD_PRESSURE_ARM: + case BTM_BLE_APPEARANCE_BLOOD_PRESSURE_WRIST: + dev_class[1] = BTM_COD_MAJOR_HEALTH; + dev_class[2] = BTM_COD_MINOR_BLOOD_MONITOR; + break; + case BTM_BLE_APPEARANCE_GENERIC_PULSE_OXIMETER: + case BTM_BLE_APPEARANCE_PULSE_OXIMETER_FINGERTIP: + case BTM_BLE_APPEARANCE_PULSE_OXIMETER_WRIST: + dev_class[1] = BTM_COD_MAJOR_HEALTH; + dev_class[2] = BTM_COD_MINOR_PULSE_OXIMETER; + break; + case BTM_BLE_APPEARANCE_GENERIC_GLUCOSE: + dev_class[1] = BTM_COD_MAJOR_HEALTH; + dev_class[2] = BTM_COD_MINOR_GLUCOSE_METER; + break; + case BTM_BLE_APPEARANCE_GENERIC_WEIGHT: + dev_class[1] = BTM_COD_MAJOR_HEALTH; + dev_class[2] = BTM_COD_MINOR_WEIGHING_SCALE; + break; + case BTM_BLE_APPEARANCE_GENERIC_WALKING: + case BTM_BLE_APPEARANCE_WALKING_IN_SHOE: + case BTM_BLE_APPEARANCE_WALKING_ON_SHOE: + case BTM_BLE_APPEARANCE_WALKING_ON_HIP: + dev_class[1] = BTM_COD_MAJOR_HEALTH; + dev_class[2] = BTM_COD_MINOR_STEP_COUNTER; + break; + case BTM_BLE_APPEARANCE_GENERIC_WATCH: + case BTM_BLE_APPEARANCE_SPORTS_WATCH: + dev_class[1] = BTM_COD_MAJOR_WEARABLE; + dev_class[2] = BTM_COD_MINOR_WRIST_WATCH; + break; + case BTM_BLE_APPEARANCE_GENERIC_EYEGLASSES: + dev_class[1] = BTM_COD_MAJOR_WEARABLE; + dev_class[2] = BTM_COD_MINOR_GLASSES; + break; + case BTM_BLE_APPEARANCE_GENERIC_DISPLAY: + dev_class[1] = BTM_COD_MAJOR_IMAGING; + dev_class[2] = BTM_COD_MINOR_DISPLAY; + break; + case BTM_BLE_APPEARANCE_GENERIC_MEDIA_PLAYER: + dev_class[1] = BTM_COD_MAJOR_AUDIO; + dev_class[2] = BTM_COD_MINOR_UNCLASSIFIED; + break; + case BTM_BLE_APPEARANCE_GENERIC_BARCODE_SCANNER: + case BTM_BLE_APPEARANCE_HID_BARCODE_SCANNER: + case BTM_BLE_APPEARANCE_GENERIC_HID: + dev_class[1] = BTM_COD_MAJOR_PERIPHERAL; + dev_class[2] = BTM_COD_MINOR_UNCLASSIFIED; + break; + case BTM_BLE_APPEARANCE_HID_KEYBOARD: + dev_class[1] = BTM_COD_MAJOR_PERIPHERAL; + dev_class[2] = BTM_COD_MINOR_KEYBOARD; + break; + case BTM_BLE_APPEARANCE_HID_MOUSE: + dev_class[1] = BTM_COD_MAJOR_PERIPHERAL; + dev_class[2] = BTM_COD_MINOR_POINTING; + break; + case BTM_BLE_APPEARANCE_HID_JOYSTICK: + dev_class[1] = BTM_COD_MAJOR_PERIPHERAL; + dev_class[2] = BTM_COD_MINOR_JOYSTICK; + break; + case BTM_BLE_APPEARANCE_HID_GAMEPAD: + dev_class[1] = BTM_COD_MAJOR_PERIPHERAL; + dev_class[2] = BTM_COD_MINOR_GAMEPAD; + break; + case BTM_BLE_APPEARANCE_HID_DIGITIZER_TABLET: + dev_class[1] = BTM_COD_MAJOR_PERIPHERAL; + dev_class[2] = BTM_COD_MINOR_DIGITIZING_TABLET; + break; + case BTM_BLE_APPEARANCE_HID_CARD_READER: + dev_class[1] = BTM_COD_MAJOR_PERIPHERAL; + dev_class[2] = BTM_COD_MINOR_CARD_READER; + break; + case BTM_BLE_APPEARANCE_HID_DIGITAL_PEN: + dev_class[1] = BTM_COD_MAJOR_PERIPHERAL; + dev_class[2] = BTM_COD_MINOR_DIGITAL_PAN; + break; + case BTM_BLE_APPEARANCE_UNKNOWN: + case BTM_BLE_APPEARANCE_GENERIC_CLOCK: + case BTM_BLE_APPEARANCE_GENERIC_TAG: + case BTM_BLE_APPEARANCE_GENERIC_KEYRING: + case BTM_BLE_APPEARANCE_GENERIC_CYCLING: + case BTM_BLE_APPEARANCE_CYCLING_COMPUTER: + case BTM_BLE_APPEARANCE_CYCLING_SPEED: + case BTM_BLE_APPEARANCE_CYCLING_CADENCE: + case BTM_BLE_APPEARANCE_CYCLING_POWER: + case BTM_BLE_APPEARANCE_CYCLING_SPEED_CADENCE: + case BTM_BLE_APPEARANCE_GENERIC_OUTDOOR_SPORTS: + case BTM_BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION: + case BTM_BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION_AND_NAV: + case BTM_BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION_POD: + case BTM_BLE_APPEARANCE_OUTDOOR_SPORTS_LOCATION_POD_AND_NAV: + default: + dev_class[1] = BTM_COD_MAJOR_UNCLASSIFIED; + dev_class[2] = BTM_COD_MINOR_UNCLASSIFIED; + }; +} + +/******************************************************************************* +** +** Function btm_ble_update_inq_result +** +** Description Update adv packet information into inquiry result. +** +** Parameters +** +** Returns void +** +*******************************************************************************/ +BOOLEAN btm_ble_update_inq_result(BD_ADDR bda, 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; + UINT8 *p_uuid16; + + STREAM_TO_UINT8 (data_len, p); + + if (data_len > BTM_BLE_ADV_DATA_LEN_MAX) { + BTM_TRACE_WARNING("EIR data too long %d. discard", data_len); + return FALSE; + } + btm_ble_cache_adv_data(bda, 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))) { + BTM_TRACE_DEBUG("btm_ble_update_inq_result scan_rsp=false, to_report=false,\ + scan_type_active=%d", btm_cb.ble_ctr_cb.inq_var.scan_type); + p_i->scan_rsp = FALSE; +#if BTM_BLE_ACTIVE_SCAN_REPORT_ADV_SCAN_RSP_INDIVIDUALLY == FALSE + to_report = FALSE; +#endif + } 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; + } +#if BTM_BLE_ACTIVE_SCAN_REPORT_ADV_SCAN_RSP_INDIVIDUALLY + if (evt_type == BTM_BLE_SCAN_RSP_EVT) { + p_cur->ble_evt_type = evt_type; + } +#endif + 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 (p_le_inq_cb->adv_len != 0) { + /* Check to see the BLE device has the Appearance UUID in the advertising data. If it does + * then try to convert the appearance value to a class of device value Bluedroid can use. + * Otherwise fall back to trying to infer if it is a HID device based on the service class. + */ + p_uuid16 = BTM_CheckAdvData(p_le_inq_cb->adv_data_cache, BTM_BLE_AD_TYPE_APPEARANCE, &len); + if (p_uuid16 && len == 2) { + btm_ble_appearance_to_cod((UINT16)p_uuid16[0] | (p_uuid16[1] << 8), p_cur->dev_class); + } else { + if ((p_uuid16 = BTM_CheckAdvData(p_le_inq_cb->adv_data_cache, + BTM_BLE_AD_TYPE_16SRV_CMPL, &len)) != NULL) { + UINT8 i; + for (i = 0; i + 2 <= len; i = i + 2) { +#if BTA_HH_LE_INCLUDED == TRUE + /* if this BLE device support HID over LE, set HID Major in class of device */ + if ((p_uuid16[i] | (p_uuid16[i + 1] << 8)) == UUID_SERVCLASS_LE_HID) { + p_cur->dev_class[0] = 0; + p_cur->dev_class[1] = BTM_COD_MAJOR_PERIPHERAL; + p_cur->dev_class[2] = 0; + break; + } +#endif /* BTA_HH_LE_INCLUDED */ + } + } + } + } + + /* if BR/EDR not supported is not set, assume is a DUMO device */ + if ((p_cur->flag & BTM_BLE_BREDR_NOT_SPT) == 0 && + evt_type != BTM_BLE_CONNECT_DIR_EVT) { + if (p_cur->ble_addr_type != BLE_ADDR_RANDOM) { + BTM_TRACE_DEBUG("BR/EDR NOT support bit not set, treat as DUMO"); + p_cur->device_type |= BT_DEVICE_TYPE_DUMO; + } else { + BTM_TRACE_DEBUG("Random address, treating device as LE only"); + } + } else { + BTM_TRACE_DEBUG("BR/EDR NOT SUPPORT bit set, LE only device"); + } + + return to_report; + +} + +/******************************************************************************* +** +** Function btm_clear_all_pending_le_entry +** +** Description This function is called to clear all LE pending entry in +** inquiry database. +** +** Returns void +** +*******************************************************************************/ +void btm_clear_all_pending_le_entry(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++) { + /* mark all pending LE entry as unused if an LE only device has scan response outstanding */ + if ((p_ent->in_use) && + (p_ent->inq_info.results.device_type == BT_DEVICE_TYPE_BLE) && + !p_ent->scan_rsp) { + p_ent->in_use = FALSE; + } + } +} + +/******************************************************************************* +** +** 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}; + UNUSED(addr_type); + + 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); + } +} + +static void btm_adv_pkt_handler(void *arg) +{ + UINT8 hci_evt_code, hci_evt_len; + UINT8 ble_sub_code; + + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + size_t pkts_to_process = pkt_queue_length(p_cb->adv_rpt_queue); + if (pkts_to_process > BTM_BLE_GAP_ADV_RPT_BATCH_SIZE) { + pkts_to_process = BTM_BLE_GAP_ADV_RPT_BATCH_SIZE; + } + + for (size_t i = 0; i < pkts_to_process; i++) { + pkt_linked_item_t *linked_pkt = pkt_queue_dequeue(p_cb->adv_rpt_queue); + assert(linked_pkt != NULL); + BT_HDR *packet = (BT_HDR *)linked_pkt->data; + uint8_t *p = packet->data + packet->offset; + STREAM_TO_UINT8 (hci_evt_code, p); + STREAM_TO_UINT8 (hci_evt_len, p); + STREAM_TO_UINT8 (ble_sub_code, p); + if (ble_sub_code == HCI_BLE_ADV_PKT_RPT_EVT) { + btm_ble_process_adv_pkt(p); + } else if (ble_sub_code == HCI_BLE_ADV_DISCARD_REPORT_EVT) { + btm_ble_process_adv_discard_evt(p); + } else if (ble_sub_code == HCI_BLE_DIRECT_ADV_EVT) { + btm_ble_process_direct_adv_pkt(p); + } else { + assert (0); + } + + osi_free(linked_pkt); +#if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE) + hci_adv_credits_try_release(1); +#endif + } + + if (pkt_queue_length(p_cb->adv_rpt_queue) != 0) { + btu_task_post(SIG_BTU_HCI_ADV_RPT_MSG, NULL, OSI_THREAD_MAX_TIMEOUT); + } + + UNUSED(hci_evt_code); + UNUSED(hci_evt_len); +} + +/******************************************************************************* +** +** 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; + UINT8 num_reports; + UINT8 data_len; +#if (defined BLE_PRIVACY_SPT && BLE_PRIVACY_SPT == TRUE) + BOOLEAN match = FALSE; +#if (!CONTROLLER_RPA_LIST_ENABLE) + BD_ADDR temp_bda; + UINT8 temp_addr_type = 0; +#endif // (!CONTROLLER_RPA_LIST_ENABLE) +#endif//(defined BLE_PRIVACY_SPT && BLE_PRIVACY_SPT == TRUE) + + /* Only process the results if the inquiry is still active */ + if (!BTM_BLE_IS_SCAN_ACTIVE(btm_cb.ble_ctr_cb.scan_activity)) { + return; + } + + /* Extract the number of reports in this event. */ + STREAM_TO_UINT8(num_reports, p); + + while (num_reports--) { + /* Extract inquiry results */ + STREAM_TO_UINT8 (evt_type, p); + STREAM_TO_UINT8 (addr_type, p); + STREAM_TO_BDADDR (bda, p); + //BTM_TRACE_ERROR("btm_ble_process_adv_pkt:bda= %0x:%0x:%0x:%0x:%0x:%0x\n", + // bda[0],bda[1],bda[2],bda[3],bda[4],bda[5]); +#if (defined BLE_PRIVACY_SPT && BLE_PRIVACY_SPT == TRUE) + +#if (!CONTROLLER_RPA_LIST_ENABLE) + temp_addr_type = addr_type; + memcpy(temp_bda, bda, BD_ADDR_LEN); +#endif // (!CONTROLLER_RPA_LIST_ENABLE) + + /* map address to security record */ + match = btm_identity_addr_to_random_pseudo(bda, &addr_type, FALSE); + + // BTM_TRACE_ERROR("btm_ble_process_adv_pkt:bda= %0x:%0x:%0x:%0x:%0x:%0x\n", + // bda[0],bda[1],bda[2],bda[3],bda[4],bda[5]); + /* always do RRA resolution on host */ + if (!match && BTM_BLE_IS_RESOLVE_BDA(bda)) { + btm_ble_resolve_random_addr(bda, btm_ble_resolve_random_addr_on_adv, p_data); + } else +#endif + btm_ble_process_adv_pkt_cont(bda, addr_type, evt_type, p); +#if (defined BLE_PRIVACY_SPT && BLE_PRIVACY_SPT == TRUE && (!CONTROLLER_RPA_LIST_ENABLE)) + //save current adv addr information if p_dev_rec!= NULL + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bda); + if(p_dev_rec) { + p_dev_rec->ble.current_addr_type = temp_addr_type; + memcpy(p_dev_rec->ble.current_addr, temp_bda, BD_ADDR_LEN); + p_dev_rec->ble.current_addr_valid = true; + } +#endif + STREAM_TO_UINT8(data_len, p); + + /* Advance to the next event data_len + rssi byte */ + p += data_len + 1; + } +} + +/******************************************************************************* +** +** Function btm_ble_process_last_adv_pkt +** +** Description This function is called to report last adv packet +** +** Parameters +** +** Returns void +** +*******************************************************************************/ + +static void btm_ble_process_last_adv_pkt(void) +{ + UINT8 result = 0; + UINT8 null_bda[6] = {0}; + 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_INQ_RESULTS_CB *p_obs_results_cb = btm_cb.ble_ctr_cb.p_obs_results_cb; + tBTM_INQ_RESULTS_CB *p_scan_results_cb = btm_cb.ble_ctr_cb.p_scan_results_cb; + tBTM_BLE_INQ_CB *p_le_inq_cb = &btm_cb.ble_ctr_cb.inq_var; + tINQ_DB_ENT *p_i = btm_inq_db_find (p_le_inq_cb->adv_addr); + + if(memcmp(null_bda, p_le_inq_cb->adv_addr, BD_ADDR_LEN) == 0) { + return; + } + + if(p_i == NULL) { + BTM_TRACE_DEBUG("no last adv"); + return; + } + + if ((result = btm_ble_is_discoverable(p_le_inq_cb->adv_addr, p_i->inq_info.results.ble_evt_type, NULL)) == 0) { + BTM_TRACE_WARNING("%s device is no longer discoverable so discarding advertising packet pkt", + __func__); + return; + } + /* background connection in selective connection mode */ + if (btm_cb.ble_ctr_cb.bg_conn_type == BTM_BLE_CONN_SELECTIVE) { + //do nothing + } else { + if (p_inq_results_cb && (result & BTM_BLE_INQ_RESULT)) { + (p_inq_results_cb)((tBTM_INQ_RESULTS *) &p_i->inq_info.results, p_le_inq_cb->adv_data_cache); + p_le_inq_cb->adv_len = 0; + memset(p_le_inq_cb->adv_addr, 0, BD_ADDR_LEN); + p_i->inq_info.results.adv_data_len = 0; + p_i->inq_info.results.scan_rsp_len = 0; + } + if (p_obs_results_cb && (result & BTM_BLE_OBS_RESULT)) { + (p_obs_results_cb)((tBTM_INQ_RESULTS *) &p_i->inq_info.results, p_le_inq_cb->adv_data_cache); + p_le_inq_cb->adv_len = 0; + memset(p_le_inq_cb->adv_addr, 0, BD_ADDR_LEN); + p_i->inq_info.results.adv_data_len = 0; + p_i->inq_info.results.scan_rsp_len = 0; + } + if (p_scan_results_cb && (result & BTM_BLE_DISCO_RESULT)) { + (p_scan_results_cb)((tBTM_INQ_RESULTS *) &p_i->inq_info.results, p_le_inq_cb->adv_data_cache); + p_le_inq_cb->adv_len = 0; + memset(p_le_inq_cb->adv_addr, 0, BD_ADDR_LEN); + p_i->inq_info.results.adv_data_len = 0; + p_i->inq_info.results.scan_rsp_len = 0; + } + } +} + +/******************************************************************************* +** +** 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; + 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_INQ_RESULTS_CB *p_obs_results_cb = btm_cb.ble_ctr_cb.p_obs_results_cb; + tBTM_INQ_RESULTS_CB *p_scan_results_cb = btm_cb.ble_ctr_cb.p_scan_results_cb; + tBTM_BLE_INQ_CB *p_le_inq_cb = &btm_cb.ble_ctr_cb.inq_var; + BOOLEAN update = TRUE; + UINT8 result = 0; + + /* Event_Type: + 0x00 Connectable undirected advertising (ADV_IND). + 0x01 Connectable directed advertising (ADV_DIRECT_IND) + 0x02 Scannable undirected advertising (ADV_SCAN_IND) + 0x03 Non connectable undirected advertising (ADV_NONCONN_IND) + 0x04 Scan Response (SCAN_RSP) + 0x05-0xFF Reserved for future use + */ + // The adv packet without scan response is allowed to report to higher layer + /* + Bluedroid will put the advertising packet and scan response into a packet and send it to the higher layer. + If two advertising packets are not with the same address, or can't be combined into a packet, then the first advertising + packet will be discarded. So we added the following judgment: + 1. For different addresses, send the last advertising packet to higher layer + 2. For same address and same advertising type (not scan response), send the last advertising packet to higher layer + 3. For same address and scan response, do nothing + */ + int same_addr = memcmp(bda, p_le_inq_cb->adv_addr, BD_ADDR_LEN); + if (same_addr != 0 || (same_addr == 0 && evt_type != BTM_BLE_SCAN_RSP_EVT)) { + btm_ble_process_last_adv_pkt(); + } + + + 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))) { + update = TRUE; + } else if (BTM_BLE_IS_DISCO_ACTIVE(btm_cb.ble_ctr_cb.scan_activity)) { + update = FALSE; + } else { + /* if yes, skip it */ + return; /* assumption: one result per event */ + } + } + /* If existing entry, use that, else get a new one (possibly reusing the oldest) */ + if (p_i == NULL) { + if ((p_i = btm_inq_db_new (bda)) != NULL) { + p_inq->inq_cmpl_info.num_resp++; + } else { + 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 (!btm_ble_update_inq_result(bda, p_i, addr_type, evt_type, p)) { + return; + } + + if ((result = btm_ble_is_discoverable(bda, evt_type, p)) == 0) { + BTM_TRACE_WARNING("%s device is no longer discoverable so discarding advertising packet pkt", + __func__); + return; + } + if (!update) { + result &= ~BTM_BLE_INQ_RESULT; + } + /* 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) == BT_DEVICE_TYPE_BLE && + p_i->scan_rsp)) { + BTM_TRACE_WARNING("INQ RES: Extra Response Received...cancelling inquiry.."); + + /* if is non-periodic inquiry active, cancel now */ + if ((p_inq->inq_active & BTM_BR_INQ_ACTIVE_MASK) != 0 && + (p_inq->inq_active & BTM_PERIODIC_INQUIRY_ACTIVE) == 0) { + btsnd_hcic_inq_cancel(); + } + + btm_ble_stop_inquiry(); + + btm_acl_update_busy_level (BTM_BLI_INQ_DONE_EVT); + } + } + /* background connection in selective connection mode */ + if (btm_cb.ble_ctr_cb.bg_conn_type == BTM_BLE_CONN_SELECTIVE) { + if (result & BTM_BLE_SEL_CONN_RESULT) { + btm_send_sel_conn_callback(bda, evt_type, p, addr_type); + } else { + BTM_TRACE_DEBUG("None LE device, can not initiate selective connection\n"); + } + } else { + if (p_inq_results_cb && (result & BTM_BLE_INQ_RESULT)) { + (p_inq_results_cb)((tBTM_INQ_RESULTS *) &p_i->inq_info.results, p_le_inq_cb->adv_data_cache); + p_le_inq_cb->adv_len = 0; + memset(p_le_inq_cb->adv_addr, 0, BD_ADDR_LEN); + p_i->inq_info.results.adv_data_len = 0; + p_i->inq_info.results.scan_rsp_len = 0; + } + if (p_obs_results_cb && (result & BTM_BLE_OBS_RESULT)) { + (p_obs_results_cb)((tBTM_INQ_RESULTS *) &p_i->inq_info.results, p_le_inq_cb->adv_data_cache); + p_le_inq_cb->adv_len = 0; + memset(p_le_inq_cb->adv_addr, 0, BD_ADDR_LEN); + p_i->inq_info.results.adv_data_len = 0; + p_i->inq_info.results.scan_rsp_len = 0; + } + if (p_scan_results_cb && (result & BTM_BLE_DISCO_RESULT)) { + (p_scan_results_cb)((tBTM_INQ_RESULTS *) &p_i->inq_info.results, p_le_inq_cb->adv_data_cache); + p_le_inq_cb->adv_len = 0; + memset(p_le_inq_cb->adv_addr, 0, BD_ADDR_LEN); + p_i->inq_info.results.adv_data_len = 0; + p_i->inq_info.results.scan_rsp_len = 0; + } + } +} + +void btm_ble_process_adv_discard_evt(UINT8 *p) +{ +#if (BLE_ADV_REPORT_FLOW_CONTROL == TRUE) + uint32_t num_dis = 0; + STREAM_TO_UINT32 (num_dis, p); + tBTM_INQ_DIS_CB *p_obs_discard_cb = btm_cb.ble_ctr_cb.p_obs_discard_cb; + if(p_obs_discard_cb) { + (p_obs_discard_cb)(num_dis); + } +#endif +} + +void btm_ble_process_direct_adv_pkt(UINT8 *p) +{ + // TODO +} + +/******************************************************************************* +** +** Function btm_ble_start_scan +** +** Description Start the BLE scan. +** +** Returns void +** +*******************************************************************************/ +tBTM_STATUS btm_ble_start_scan(void) +{ + tBTM_BLE_INQ_CB *p_inq = &btm_cb.ble_ctr_cb.inq_var; + tBTM_STATUS status = BTM_CMD_STARTED; + + osi_mutex_lock(&scan_enable_lock, OSI_MUTEX_MAX_TIMEOUT); + + if(p_inq->scan_duplicate_filter > BTM_BLE_DUPLICATE_MAX) { + p_inq->scan_duplicate_filter = BTM_BLE_DUPLICATE_DISABLE; + } + /* start scan, disable duplicate filtering */ + if (!btsnd_hcic_ble_set_scan_enable (BTM_BLE_SCAN_ENABLE, p_inq->scan_duplicate_filter)) { + status = BTM_NO_RESOURCES; + } else { + osi_sem_take(&scan_enable_sem, OSI_SEM_MAX_TIMEOUT); + if(scan_enable_status != BTM_SUCCESS) { + status = BTM_NO_RESOURCES; + } + btm_cb.ble_ctr_cb.inq_var.state |= BTM_BLE_SCANNING; + if (p_inq->scan_type == BTM_BLE_SCAN_MODE_ACTI) { + btm_ble_set_topology_mask(BTM_BLE_STATE_ACTIVE_SCAN_BIT); + } else { + btm_ble_set_topology_mask(BTM_BLE_STATE_PASSIVE_SCAN_BIT); + } + } + osi_mutex_unlock(&scan_enable_lock); + return status; +} + +/******************************************************************************* +** +** Function btm_ble_stop_scan +** +** Description Stop the BLE scan. +** +** Returns void +** +*******************************************************************************/ +void btm_ble_stop_scan(void) +{ + BTM_TRACE_EVENT ("btm_ble_stop_scan "); + + /* Clear the inquiry callback if set */ + btm_cb.ble_ctr_cb.inq_var.scan_type = BTM_BLE_SCAN_MODE_NONE; + btm_cb.ble_ctr_cb.inq_var.state &= ~BTM_BLE_SCANNING; + /* stop discovery now */ + btsnd_hcic_ble_set_scan_enable (BTM_BLE_SCAN_DISABLE, BTM_BLE_DUPLICATE_ENABLE); + + btm_update_scanner_filter_policy(SP_ADV_ALL); + + btm_cb.ble_ctr_cb.wl_state &= ~BTM_BLE_WL_SCAN; +} + +/******************************************************************************* +** +** Function btm_ble_stop_inquiry +** +** Description Stop the BLE Inquiry. +** +** Returns void +** +*******************************************************************************/ +void btm_ble_stop_inquiry(void) +{ + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + tBTM_BLE_CB *p_ble_cb = &btm_cb.ble_ctr_cb; + + btu_stop_timer (&p_ble_cb->inq_var.inq_timer_ent); + + p_ble_cb->scan_activity &= ~BTM_BLE_INQUIRY_MASK; + + /* If no more scan activity, stop LE scan now */ + if (!BTM_BLE_IS_SCAN_ACTIVE(p_ble_cb->scan_activity)) { + btm_ble_stop_scan(); + } else if ((p_ble_cb->inq_var.scan_interval != BTM_BLE_LOW_LATENCY_SCAN_INT) || + (p_ble_cb->inq_var.scan_window != BTM_BLE_LOW_LATENCY_SCAN_WIN)) { + BTM_TRACE_DEBUG("%s: setting default params for ongoing observe", __FUNCTION__); + btm_ble_stop_scan(); + btm_ble_start_scan(); + } + + /* If we have a callback registered for inquiry complete, call it */ + BTM_TRACE_DEBUG ("BTM Inq Compl Callback: status 0x%02x, num results %d", + p_inq->inq_cmpl_info.status, p_inq->inq_cmpl_info.num_resp); + + btm_process_inq_complete(HCI_SUCCESS, (UINT8)(p_inq->inqparms.mode & BTM_BLE_INQUIRY_MASK)); +} + +/******************************************************************************* +** +** Function btm_ble_stop_observe +** +** Description Stop the BLE Observe. +** +** Returns void +** +*******************************************************************************/ +static void btm_ble_stop_observe(void) +{ + tBTM_BLE_CB *p_ble_cb = & btm_cb.ble_ctr_cb; + tBTM_CMPL_CB *p_obs_cb = p_ble_cb->p_obs_cmpl_cb; + + btu_stop_timer (&p_ble_cb->obs_timer_ent); + + p_ble_cb->scan_activity &= ~BTM_LE_OBSERVE_ACTIVE; + + p_ble_cb->p_obs_results_cb = NULL; + p_ble_cb->p_obs_cmpl_cb = NULL; + + if (!BTM_BLE_IS_SCAN_ACTIVE(p_ble_cb->scan_activity)) { + btm_ble_stop_scan(); + } + + if (p_obs_cb) { + (p_obs_cb)((tBTM_INQUIRY_CMPL *) &btm_cb.btm_inq_vars.inq_cmpl_info); + } +} + +/******************************************************************************* +** +** Function btm_ble_stop_observe +** +** Description Stop the BLE Observe. +** +** Returns void +** +*******************************************************************************/ +static void btm_ble_stop_discover(void) +{ + tBTM_BLE_CB *p_ble_cb = & btm_cb.ble_ctr_cb; + tBTM_CMPL_CB *p_scan_cb = p_ble_cb->p_scan_cmpl_cb; + btu_stop_timer (&p_ble_cb->scan_timer_ent); + + osi_mutex_lock(&scan_enable_lock, OSI_MUTEX_MAX_TIMEOUT); + p_ble_cb->scan_activity &= ~BTM_LE_DISCOVER_ACTIVE; + + p_ble_cb->p_scan_results_cb = NULL; + p_ble_cb->p_scan_cmpl_cb = NULL; + + if (!BTM_BLE_IS_SCAN_ACTIVE(p_ble_cb->scan_activity)) { + /* Clear the inquiry callback if set */ + btm_cb.ble_ctr_cb.inq_var.scan_type = BTM_BLE_SCAN_MODE_NONE; + btm_cb.ble_ctr_cb.inq_var.state &= ~BTM_BLE_SCANNING; + /* stop discovery now */ + if(btsnd_hcic_ble_set_scan_enable (BTM_BLE_SCAN_DISABLE, BTM_BLE_DUPLICATE_ENABLE)) { + osi_sem_take(&scan_enable_sem, OSI_SEM_MAX_TIMEOUT); + } + } + + if (p_scan_cb) { + (p_scan_cb)((tBTM_INQUIRY_CMPL *) &btm_cb.btm_inq_vars.inq_cmpl_info); + } + osi_mutex_unlock(&scan_enable_lock); +} + +/******************************************************************************* +** +** Function btm_ble_adv_states_operation +** +** Description Set or clear adv states in topology mask +** +** Returns operation status. TRUE if sucessful, FALSE otherwise. +** +*******************************************************************************/ +typedef BOOLEAN (BTM_TOPOLOGY_FUNC_PTR)(tBTM_BLE_STATE_MASK); +static BOOLEAN btm_ble_adv_states_operation(BTM_TOPOLOGY_FUNC_PTR *p_handler, UINT8 adv_evt) +{ + BOOLEAN rt = FALSE; + + switch (adv_evt) { + case BTM_BLE_CONNECT_EVT: + rt = (*p_handler)(BTM_BLE_STATE_CONN_ADV_BIT); + break; + + case BTM_BLE_NON_CONNECT_EVT: + rt = (*p_handler) (BTM_BLE_STATE_NON_CONN_ADV_BIT); + break; + case BTM_BLE_CONNECT_DIR_EVT: + rt = (*p_handler) (BTM_BLE_STATE_HI_DUTY_DIR_ADV_BIT); + break; + + case BTM_BLE_DISCOVER_EVT: + rt = (*p_handler) (BTM_BLE_STATE_SCAN_ADV_BIT); + break; + + case BTM_BLE_CONNECT_LO_DUTY_DIR_EVT: + rt = (*p_handler) (BTM_BLE_STATE_LO_DUTY_DIR_ADV_BIT); + break; + + default: + BTM_TRACE_ERROR("unknown adv event : %d", adv_evt); + break; + } + + return rt; +} + + +/******************************************************************************* +** +** Function btm_ble_start_adv +** +** Description start the BLE advertising. +** +** Returns void +** +*******************************************************************************/ +tBTM_STATUS btm_ble_start_adv(void) +{ + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + tBTM_STATUS rt = BTM_NO_RESOURCES; + BTM_TRACE_EVENT ("btm_ble_start_adv\n"); + + + if (!btm_ble_adv_states_operation (btm_ble_topology_check, p_cb->evt_type)) { + return BTM_WRONG_MODE; + } + + osi_mutex_lock(&adv_enable_lock, OSI_MUTEX_MAX_TIMEOUT); + +#if (defined BLE_PRIVACY_SPT && BLE_PRIVACY_SPT == TRUE) + /* To relax resolving list, always have resolving list enabled, unless directed adv */ + if (p_cb->evt_type != BTM_BLE_CONNECT_LO_DUTY_DIR_EVT && + p_cb->evt_type != BTM_BLE_CONNECT_DIR_EVT) + /* enable resolving list is desired */ + { + //btm_ble_enable_resolving_list_for_platform(BTM_BLE_RL_ADV); + } +#endif + if (p_cb->afp != AP_SCAN_CONN_ALL) { + //find the device in the btm dev buffer and write it to the controller white list + btm_execute_wl_dev_operation(); + btm_cb.ble_ctr_cb.wl_state |= BTM_BLE_WL_ADV; + } + /* The complete event comes up immediately after the 'btsnd_hcic_ble_set_adv_enable' being called in dual core, + this causes the 'adv_mode' and 'state' not be set yet, so we set the state first */ + tBTM_BLE_GAP_STATE temp_state = p_cb->state; + UINT8 adv_mode = p_cb->adv_mode; + p_cb->adv_mode = BTM_BLE_ADV_ENABLE; + p_cb->state |= BTM_BLE_ADVERTISING; + btm_ble_adv_states_operation(btm_ble_set_topology_mask, p_cb->evt_type); + if (btsnd_hcic_ble_set_adv_enable (BTM_BLE_ADV_ENABLE)) { + osi_sem_take(&adv_enable_sem, OSI_SEM_MAX_TIMEOUT); + rt = adv_enable_status; + BTM_TRACE_EVENT ("BTM_SUCCESS\n"); + } else { + p_cb->adv_mode = BTM_BLE_ADV_DISABLE; + p_cb->state = temp_state; + p_cb->adv_mode = adv_mode; + btm_ble_adv_states_operation(btm_ble_clear_topology_mask, p_cb->evt_type); + btm_cb.ble_ctr_cb.wl_state &= ~BTM_BLE_WL_ADV; + } + + if(adv_enable_status != HCI_SUCCESS) { + p_cb->adv_mode = adv_mode; + } + osi_mutex_unlock(&adv_enable_lock); + return rt; +} + +/******************************************************************************* +** +** Function btm_ble_stop_adv +** +** Description Stop the BLE advertising. +** +** Returns void +** +*******************************************************************************/ +tBTM_STATUS btm_ble_stop_adv(void) +{ + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + tBTM_STATUS rt = BTM_SUCCESS; + if (p_cb) { + osi_mutex_lock(&adv_enable_lock, OSI_MUTEX_MAX_TIMEOUT); + UINT8 temp_adv_mode = p_cb->adv_mode; + BOOLEAN temp_fast_adv_on = p_cb->fast_adv_on; + tBTM_BLE_GAP_STATE temp_state = p_cb->state; + tBTM_BLE_WL_STATE temp_wl_state = btm_cb.ble_ctr_cb.wl_state; + tBTM_BLE_STATE_MASK temp_mask = btm_ble_get_topology_mask (); + + p_cb->fast_adv_on = FALSE; + p_cb->adv_mode = BTM_BLE_ADV_DISABLE; + p_cb->state &= ~BTM_BLE_ADVERTISING; + btm_cb.ble_ctr_cb.wl_state &= ~BTM_BLE_WL_ADV; + + /* clear all adv states */ + btm_ble_clear_topology_mask (BTM_BLE_STATE_ALL_ADV_MASK); + + if (btsnd_hcic_ble_set_adv_enable (BTM_BLE_ADV_DISABLE)) { + osi_sem_take(&adv_enable_sem, OSI_SEM_MAX_TIMEOUT); + rt = adv_enable_status; + } else { + // reset state + p_cb->fast_adv_on = temp_fast_adv_on; + p_cb->adv_mode = temp_adv_mode; + p_cb->state = temp_state; + btm_cb.ble_ctr_cb.wl_state = temp_wl_state; + btm_ble_set_topology_mask (temp_mask); + + rt = BTM_NO_RESOURCES; + } + if(adv_enable_status != HCI_SUCCESS) { + p_cb->adv_mode = temp_adv_mode; + } + osi_mutex_unlock(&adv_enable_lock); + } + return rt; +} + +tBTM_STATUS btm_ble_set_random_addr(BD_ADDR random_bda) +{ + tBTM_STATUS rt = BTM_SUCCESS; + + osi_mutex_lock(&adv_enable_lock, OSI_MUTEX_MAX_TIMEOUT); + osi_mutex_lock(&scan_enable_lock, OSI_MUTEX_MAX_TIMEOUT); + + if (btm_cb.ble_ctr_cb.inq_var.adv_mode == BTM_BLE_ADV_ENABLE) { + if (btsnd_hcic_ble_set_adv_enable (BTM_BLE_ADV_DISABLE)) { + osi_sem_take(&adv_enable_sem, OSI_SEM_MAX_TIMEOUT); + rt = adv_enable_status; + } else { + rt = BTM_BAD_VALUE_RET; + } + } + + if (BTM_BLE_IS_DISCO_ACTIVE(btm_cb.ble_ctr_cb.scan_activity)) { + if (btsnd_hcic_ble_set_scan_enable (BTM_BLE_SCAN_DISABLE, BTM_BLE_SCAN_DUPLICATE_DISABLE)) { + osi_sem_take(&scan_enable_sem, OSI_SEM_MAX_TIMEOUT); + rt = scan_enable_status; + } else { + rt = BTM_BAD_VALUE_RET; + } + } + + if (rt == BTM_SUCCESS) { + btsnd_hcic_ble_set_random_addr(random_bda); + } + + if (btm_cb.ble_ctr_cb.inq_var.adv_mode == BTM_BLE_ADV_ENABLE) { + if (btsnd_hcic_ble_set_adv_enable (BTM_BLE_ADV_ENABLE)) { + osi_sem_take(&adv_enable_sem, OSI_SEM_MAX_TIMEOUT); + rt = adv_enable_status; + } else { + rt = BTM_BAD_VALUE_RET; + } + } + + if (BTM_BLE_IS_DISCO_ACTIVE(btm_cb.ble_ctr_cb.scan_activity)) { + if (btsnd_hcic_ble_set_scan_enable (BTM_BLE_SCAN_ENABLE, btm_cb.ble_ctr_cb.inq_var.scan_duplicate_filter)) { + osi_sem_take(&scan_enable_sem, OSI_SEM_MAX_TIMEOUT); + rt = scan_enable_status; + } else { + rt = BTM_BAD_VALUE_RET; + } + } + + osi_mutex_unlock(&adv_enable_lock); + osi_mutex_unlock(&scan_enable_lock); + + return rt; +} + + +/******************************************************************************* +** +** Function btm_ble_start_slow_adv +** +** Description Restart adv with slow adv interval +** +** Returns void +** +*******************************************************************************/ +static void btm_ble_start_slow_adv (void) +{ + tBTM_BLE_INQ_CB *p_cb = &btm_cb.ble_ctr_cb.inq_var; + + if (p_cb->adv_mode == BTM_BLE_ADV_ENABLE) { + tBTM_LE_RANDOM_CB *p_addr_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + BD_ADDR p_addr_ptr = {0}; + tBLE_ADDR_TYPE init_addr_type = BLE_ADDR_PUBLIC; + tBLE_ADDR_TYPE own_addr_type = p_addr_cb->own_addr_type; + + btm_ble_stop_adv(); + + p_cb->evt_type = btm_set_conn_mode_adv_init_addr(p_cb, p_addr_ptr, &init_addr_type, + &own_addr_type); + + /* slow adv mode never goes into directed adv */ + btsnd_hcic_ble_write_adv_params (BTM_BLE_GAP_ADV_SLOW_INT, BTM_BLE_GAP_ADV_SLOW_INT, + p_cb->evt_type, own_addr_type, + init_addr_type, p_addr_ptr, + p_cb->adv_chnl_map, p_cb->afp); + + btm_ble_start_adv(); + } +} +/******************************************************************************* +** +** 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_EVENT ("btm_ble_timeout"); + + switch (p_tle->event) { + case BTU_TTYPE_BLE_OBSERVE: + btm_ble_stop_observe(); + break; + case BTU_TTYPE_BLE_SCAN: + btm_ble_stop_discover(); + break; + case BTU_TTYPE_BLE_INQUIRY: + btm_ble_stop_inquiry(); + break; + + case BTU_TTYPE_BLE_GAP_LIM_DISC: + /* lim_timeout expiried, limited discovery should exit now */ + btm_cb.btm_inq_vars.discoverable_mode &= ~BTM_BLE_LIMITED_DISCOVERABLE; + btm_ble_set_adv_flag(btm_cb.btm_inq_vars.connectable_mode, btm_cb.btm_inq_vars.discoverable_mode); + break; + + case BTU_TTYPE_BLE_RANDOM_ADDR: + if (btm_cb.ble_ctr_cb.addr_mgnt_cb.own_addr_type == BLE_ADDR_RANDOM) { + if (NULL == (void *)(p_tle->param)) { +#if (CONTROLLER_RPA_LIST_ENABLE == FALSE) + /* refresh the random addr */ + btm_gen_resolvable_private_addr((void *)btm_gen_resolve_paddr_low); +#endif + } else { + if (BTM_BleMaxMultiAdvInstanceCount() > 0) { + btm_ble_multi_adv_configure_rpa((tBTM_BLE_MULTI_ADV_INST *)p_tle->param); + } + } + } + break; + + case BTU_TTYPE_BLE_GAP_FAST_ADV: + /* fast adv is completed, fall back to slow adv interval */ + btm_ble_start_slow_adv(); + break; + + default: + break; + + } +} + + +/******************************************************************************* +** +** 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 = NULL; + UINT16 handle; + UINT8 status; + + BTM_TRACE_EVENT ("btm_ble_read_remote_features_complete "); + + STREAM_TO_UINT8(status, p); + + // if LE read remote feature failed for HCI_ERR_CONN_FAILED_ESTABLISHMENT, + // expect disconnect complete to be received + if (status != HCI_ERR_CONN_FAILED_ESTABLISHMENT) { + STREAM_TO_UINT16 (handle, p); + + /* Look up the connection by handle and copy features */ + p_acl_cb = btm_handle_to_acl(handle); + if (p_acl_cb) { + { + STREAM_TO_ARRAY(p_acl_cb->peer_le_features, p, BD_FEATURES_LEN); +#if BLE_INCLUDED == TRUE + /* In the original Bluedroid version, slave need to send LL_VERSION_IND(call btsnd_hcic_rmt_ver_req) + * to remote device if it has not received ll_version_ind. + * Delete it to resolve Android 7.0 incompatible problem. But it may cause that slave host + * can't get remote device's version.*/ + if (p_acl_cb->link_role == HCI_ROLE_MASTER){ + btsnd_hcic_rmt_ver_req (p_acl_cb->hci_handle); + } + else{ + uint16_t data_length = controller_get_interface()->get_ble_default_data_packet_length(); + uint16_t data_txtime = controller_get_interface()->get_ble_default_data_packet_txtime(); + if (p_acl_cb->transport == BT_TRANSPORT_LE) { + if (HCI_LE_DATA_LEN_EXT_SUPPORTED(p_acl_cb->peer_le_features) && + (p_acl_cb->data_length_params.tx_len != data_length)) { + p_acl_cb->data_len_updating = true; + btsnd_hcic_ble_set_data_length(p_acl_cb->hci_handle, data_length, data_txtime); + } + l2cble_notify_le_connection (p_acl_cb->remote_addr); + } + } +#endif + } + } + } + +} + +/******************************************************************************* +** +** 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) +{ + /* if write adv enable/disbale not succeed */ + if (*p != HCI_SUCCESS) { + BTM_TRACE_ERROR("%s failed", __func__); + } +} + +/******************************************************************************* +** +** Function btm_ble_dir_adv_tout +** +** Description when directed adv time out +** +** Returns void +** +*******************************************************************************/ +void btm_ble_dir_adv_tout(void) +{ + btm_cb.ble_ctr_cb.inq_var.adv_mode = BTM_BLE_ADV_DISABLE; + + /* make device fall back into undirected adv mode by default */ + btm_cb.ble_ctr_cb.inq_var.directed_conn = FALSE; +} + +/******************************************************************************* +** +** Function btm_ble_set_topology_mask +** +** Description set BLE topology mask +** +** Returns TRUE is request is allowed, FALSE otherwise. +** +*******************************************************************************/ +BOOLEAN btm_ble_set_topology_mask(tBTM_BLE_STATE_MASK request_state_mask) +{ + request_state_mask &= BTM_BLE_STATE_ALL_MASK; + btm_cb.ble_ctr_cb.cur_states |= (request_state_mask & BTM_BLE_STATE_ALL_MASK); + return TRUE; +} + +/******************************************************************************* +** +** Function btm_ble_clear_topology_mask +** +** Description Clear BLE topology bit mask +** +** Returns TRUE is request is allowed, FALSE otherwise. +** +*******************************************************************************/ +BOOLEAN btm_ble_clear_topology_mask (tBTM_BLE_STATE_MASK request_state_mask) +{ + request_state_mask &= BTM_BLE_STATE_ALL_MASK; + btm_cb.ble_ctr_cb.cur_states &= ~request_state_mask; + return TRUE; +} + +/******************************************************************************* +** +** Function btm_ble_get_topology_mask +** +** Description Get BLE topology bit mask +** +** Returns state mask. +** +*******************************************************************************/ +tBTM_BLE_STATE_MASK btm_ble_get_topology_mask (void) +{ + return btm_cb.ble_ctr_cb.cur_states; +} + +/******************************************************************************* +** +** Function btm_ble_update_link_topology_mask +** +** Description This function update the link topology mask +** +** Returns void +** +*******************************************************************************/ +void btm_ble_update_link_topology_mask(UINT8 link_role, BOOLEAN increase) +{ + btm_ble_clear_topology_mask (BTM_BLE_STATE_ALL_CONN_MASK); + + if (increase) { + btm_cb.ble_ctr_cb.link_count[link_role]++; + } else if (btm_cb.ble_ctr_cb.link_count[link_role] > 0) { + btm_cb.ble_ctr_cb.link_count[link_role]--; + } + + if (btm_cb.ble_ctr_cb.link_count[HCI_ROLE_MASTER]) { + btm_ble_set_topology_mask (BTM_BLE_STATE_MASTER_BIT); + } + + if (btm_cb.ble_ctr_cb.link_count[HCI_ROLE_SLAVE]) { + btm_ble_set_topology_mask(BTM_BLE_STATE_SLAVE_BIT); + } + + if (link_role == HCI_ROLE_SLAVE && increase) { + btm_cb.ble_ctr_cb.inq_var.adv_mode = BTM_BLE_ADV_DISABLE; + /* make device fall back into undirected adv mode by default */ + btm_cb.ble_ctr_cb.inq_var.directed_conn = BTM_BLE_CONNECT_EVT; + /* clear all adv states */ + btm_ble_clear_topology_mask(BTM_BLE_STATE_ALL_ADV_MASK); + } +} + +/******************************************************************************* +** +** Function btm_ble_update_mode_operation +** +** Description This function update the GAP role operation when a link status +** is updated. +** +** Returns void +** +*******************************************************************************/ +BOOLEAN btm_ble_update_mode_operation(UINT8 link_role, BD_ADDR bd_addr, UINT8 status) +{ + BOOLEAN bg_con = FALSE; + if (status == HCI_ERR_DIRECTED_ADVERTISING_TIMEOUT) { + btm_cb.ble_ctr_cb.inq_var.adv_mode = BTM_BLE_ADV_DISABLE; + /* make device fall back into undirected adv mode by default */ + btm_cb.ble_ctr_cb.inq_var.directed_conn = BTM_BLE_CONNECT_EVT; + /* clear all adv states */ + btm_ble_clear_topology_mask (BTM_BLE_STATE_ALL_ADV_MASK); + } + + if (btm_cb.ble_ctr_cb.inq_var.connectable_mode == BTM_BLE_CONNECTABLE) { + btm_ble_set_connectability(btm_cb.btm_inq_vars.connectable_mode | + btm_cb.ble_ctr_cb.inq_var.connectable_mode); + } + + /* when no connection is attempted, and controller is not rejecting last request + due to resource limitation, start next direct connection or background connection + now in order */ + if (btm_ble_get_conn_st() == BLE_CONN_IDLE && status != HCI_ERR_HOST_REJECT_RESOURCES && + !btm_send_pending_direct_conn()) { + bg_con = btm_ble_resume_bg_conn(); + } + + return bg_con; +} + +/******************************************************************************* +** +** Function btm_ble_init +** +** Description Initialize the control block variable values. +** +** Returns void +** +*******************************************************************************/ +void btm_ble_init (void) +{ + BTM_TRACE_DEBUG("%s", __func__); + +#if BTM_DYNAMIC_MEMORY == TRUE + cmn_ble_gap_vsc_cb_ptr = (tBTM_BLE_VSC_CB *)osi_malloc(sizeof(tBTM_BLE_VSC_CB)); + if (cmn_ble_gap_vsc_cb_ptr == NULL) { + BTM_TRACE_ERROR("%s malloc failed", __func__); + return; + } +#endif + + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + + btu_free_timer(&p_cb->obs_timer_ent); + btu_free_timer(&p_cb->scan_timer_ent); + btu_free_timer(&p_cb->inq_var.fast_adv_timer); + memset(p_cb, 0, sizeof(tBTM_BLE_CB)); + memset(&(btm_cb.cmn_ble_vsc_cb), 0 , sizeof(tBTM_BLE_VSC_CB)); + btm_cb.cmn_ble_vsc_cb.values_read = FALSE; + p_cb->cur_states = 0; + + p_cb->conn_pending_q = fixed_queue_new(QUEUE_SIZE_MAX); + + 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_SCAN_PARAM_UNDEF; + + p_cb->inq_var.evt_type = BTM_BLE_NON_CONNECT_EVT; + + p_cb->adv_rpt_queue = pkt_queue_create(); + assert(p_cb->adv_rpt_queue != NULL); + + p_cb->adv_rpt_ready = osi_event_create(btm_adv_pkt_handler, NULL); + assert(p_cb->adv_rpt_ready != NULL); + osi_event_bind(p_cb->adv_rpt_ready, btu_get_current_thread(), 0); + +#if BLE_VND_INCLUDED == FALSE + btm_ble_adv_filter_init(); +#endif +} + +/******************************************************************************* +** +** Function btm_ble_free +** +** Description free the control block variable values. +** +** Returns void +** +*******************************************************************************/ +void btm_ble_free (void) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + + BTM_TRACE_DEBUG("%s", __func__); + + fixed_queue_free(p_cb->conn_pending_q, osi_free_func); + + pkt_queue_destroy(p_cb->adv_rpt_queue, NULL); + p_cb->adv_rpt_queue = NULL; + + osi_event_delete(p_cb->adv_rpt_ready); + p_cb->adv_rpt_ready = NULL; + +#if BTM_DYNAMIC_MEMORY == TRUE + osi_free(cmn_ble_gap_vsc_cb_ptr); + cmn_ble_gap_vsc_cb_ptr = NULL; +#endif +} + +/******************************************************************************* +** +** Function btm_ble_topology_check +** +** Description check to see requested state is supported. One state check at +** a time is supported +** +** Returns TRUE is request is allowed, FALSE otherwise. +** +*******************************************************************************/ +BOOLEAN btm_ble_topology_check(tBTM_BLE_STATE_MASK request_state_mask) +{ + BOOLEAN rt = FALSE; + + UINT8 state_offset = 0; + UINT16 cur_states = btm_cb.ble_ctr_cb.cur_states; + UINT8 mask, offset; + UINT8 request_state = 0; + + /* check only one bit is set and within valid range */ + if (request_state_mask == BTM_BLE_STATE_INVALID || + request_state_mask > BTM_BLE_STATE_SCAN_ADV_BIT || + (request_state_mask & (request_state_mask - 1 )) != 0) { + BTM_TRACE_ERROR("illegal state requested: %d", request_state_mask); + return rt; + } + + while (request_state_mask) { + request_state_mask >>= 1; + request_state ++; + } + + /* check if the requested state is supported or not */ + mask = btm_le_state_combo_tbl[0][request_state - 1][0]; + offset = btm_le_state_combo_tbl[0][request_state - 1][1]; + + const uint8_t *ble_supported_states = controller_get_interface()->get_ble_supported_states(); + + if (!BTM_LE_STATES_SUPPORTED(ble_supported_states, mask, offset)) { + BTM_TRACE_ERROR("state requested not supported: %d", request_state); + return rt; + } + + rt = TRUE; + /* make sure currently active states are all supported in conjunction with the requested + state. If the bit in table is not set, the combination is not supported */ + while (cur_states != 0) { + if (cur_states & 0x01) { + mask = btm_le_state_combo_tbl[request_state][state_offset][0]; + offset = btm_le_state_combo_tbl[request_state][state_offset][1]; + + if (mask != 0 && offset != 0) { + if (!BTM_LE_STATES_SUPPORTED(ble_supported_states, mask, offset)) { + rt = FALSE; + break; + } + } + } + cur_states >>= 1; + state_offset ++; + } + return rt; +} + +/******************************************************************************* + ** + ** Function BTM_Ble_Authorization + ** + ** Description This function is used to authorize a specified device + ** + ** Returns TRUE or FALSE + ** + *******************************************************************************/ +BOOLEAN BTM_Ble_Authorization(BD_ADDR bd_addr, BOOLEAN authorize) +{ + if (bd_addr == NULL) { + BTM_TRACE_ERROR("bd_addr is NULL"); + return FALSE; + } + + if (btm_sec_dev_authorization(bd_addr, authorize)) { + return TRUE; + } + + BTM_TRACE_ERROR("Authorization fail"); + return FALSE; +} + +/******************************************************************************* +** +** Function BTM_BleClearAdv +** +** Description This function is called to clear legacy advertising +** +** Parameter p_clear_adv_cback - Command complete callback +** +*******************************************************************************/ +BOOLEAN BTM_BleClearAdv(tBTM_CLEAR_ADV_CMPL_CBACK *p_clear_adv_cback) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + + if (btsnd_hcic_ble_clear_adv() == FALSE) { + BTM_TRACE_ERROR("%s: Unable to Clear Advertising", __FUNCTION__); + return FALSE; + } + + p_cb->inq_var.p_clear_adv_cb = p_clear_adv_cback; + return TRUE; +} + +bool btm_ble_adv_pkt_ready(void) +{ + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + osi_thread_post_event(p_cb->adv_rpt_ready, OSI_THREAD_MAX_TIMEOUT); + + return true; +} + +bool btm_ble_adv_pkt_post(pkt_linked_item_t *pkt) +{ + if (pkt == NULL) { + return false; + } + + tBTM_BLE_CB *p_cb = &btm_cb.ble_ctr_cb; + pkt_queue_enqueue(p_cb->adv_rpt_queue, pkt); + return true; +} +#endif /* BLE_INCLUDED */ diff --git a/lib/bt/host/bluedroid/stack/btm/btm_ble_multi_adv.c b/lib/bt/host/bluedroid/stack/btm/btm_ble_multi_adv.c new file mode 100644 index 00000000..443dd64e --- /dev/null +++ b/lib/bt/host/bluedroid/stack/btm/btm_ble_multi_adv.c @@ -0,0 +1,878 @@ +/****************************************************************************** + * + * Copyright (C) 2014 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +#include <string.h> + +#include "common/bt_target.h" +#include "device/controller.h" + +#if (BLE_INCLUDED == TRUE) +#include "stack/bt_types.h" +#include "stack/hcimsgs.h" +#include "stack/btu.h" +#include "btm_int.h" +//#include "bt_utils.h" +#include "stack/hcidefs.h" +#include "stack/btm_ble_api.h" + +/************************************************************************************ +** Constants & Macros +************************************************************************************/ +/* length of each multi adv sub command */ +#define BTM_BLE_MULTI_ADV_ENB_LEN 3 +#define BTM_BLE_MULTI_ADV_SET_PARAM_LEN 24 +#define BTM_BLE_MULTI_ADV_WRITE_DATA_LEN (BTM_BLE_AD_DATA_LEN + 3) +#define BTM_BLE_MULTI_ADV_SET_RANDOM_ADDR_LEN 8 + +#define BTM_BLE_MULTI_ADV_CB_EVT_MASK 0xF0 +#define BTM_BLE_MULTI_ADV_SUBCODE_MASK 0x0F + +/************************************************************************************ +** Static variables +************************************************************************************/ +#if BTM_DYNAMIC_MEMORY == FALSE +tBTM_BLE_MULTI_ADV_CB btm_multi_adv_cb; +tBTM_BLE_MULTI_ADV_INST_IDX_Q btm_multi_adv_idx_q; +#else +tBTM_BLE_MULTI_ADV_CB *btm_multi_adv_cb_ptr; +tBTM_BLE_MULTI_ADV_INST_IDX_Q *btm_multi_adv_idx_q_ptr; +#define btm_multi_adv_cb (*btm_multi_adv_cb_ptr) +#define btm_multi_adv_idx_q (*btm_multi_adv_idx_q_ptr) +#endif + +/************************************************************************************ +** Externs +************************************************************************************/ +extern void btm_ble_update_dmt_flag_bits(UINT8 *flag_value, + const UINT16 connect_mode, const UINT16 disc_mode); + +/******************************************************************************* +** +** Function btm_ble_multi_adv_enq_op_q +** +** Description enqueue a multi adv operation in q to check command complete +** status. +** +** Returns void +** +*******************************************************************************/ +void btm_ble_multi_adv_enq_op_q(UINT8 opcode, UINT8 inst_id, UINT8 cb_evt) +{ + tBTM_BLE_MULTI_ADV_OPQ *p_op_q = &btm_multi_adv_cb.op_q; + + p_op_q->p_inst_id[p_op_q->next_idx] = inst_id; + + p_op_q->p_sub_code[p_op_q->next_idx] = (opcode | (cb_evt << 4)); + + p_op_q->next_idx = (p_op_q->next_idx + 1) % BTM_BleMaxMultiAdvInstanceCount(); +} + +/******************************************************************************* +** +** Function btm_ble_multi_adv_deq_op_q +** +** Description dequeue a multi adv operation from q when command complete +** is received. +** +** Returns void +** +*******************************************************************************/ +void btm_ble_multi_adv_deq_op_q(UINT8 *p_opcode, UINT8 *p_inst_id, UINT8 *p_cb_evt) +{ + tBTM_BLE_MULTI_ADV_OPQ *p_op_q = &btm_multi_adv_cb.op_q; + + *p_inst_id = p_op_q->p_inst_id[p_op_q->pending_idx] & 0x7F; + *p_cb_evt = (p_op_q->p_sub_code[p_op_q->pending_idx] >> 4); + *p_opcode = (p_op_q->p_sub_code[p_op_q->pending_idx] & BTM_BLE_MULTI_ADV_SUBCODE_MASK); + + p_op_q->pending_idx = (p_op_q->pending_idx + 1) % BTM_BleMaxMultiAdvInstanceCount(); +} + +/******************************************************************************* +** +** Function btm_ble_multi_adv_vsc_cmpl_cback +** +** Description Multi adv VSC complete callback +** +** Parameters +** +** Returns void +** +*******************************************************************************/ +void btm_ble_multi_adv_vsc_cmpl_cback (tBTM_VSC_CMPL *p_params) +{ + UINT8 status, subcode; + UINT8 *p = p_params->p_param_buf, inst_id; + UINT16 len = p_params->param_len; + tBTM_BLE_MULTI_ADV_INST *p_inst ; + UINT8 cb_evt = 0, opcode; + + if (len < 2) { + BTM_TRACE_ERROR("wrong length for btm_ble_multi_adv_vsc_cmpl_cback"); + return; + } + + STREAM_TO_UINT8(status, p); + STREAM_TO_UINT8(subcode, p); + + btm_ble_multi_adv_deq_op_q(&opcode, &inst_id, &cb_evt); + + BTM_TRACE_DEBUG("op_code = %02x inst_id = %d cb_evt = %02x", opcode, inst_id, cb_evt); + + if (opcode != subcode || inst_id == 0) { + BTM_TRACE_ERROR("get unexpected VSC cmpl, expect: %d get: %d", subcode, opcode); + return; + } + + p_inst = &btm_multi_adv_cb.p_adv_inst[inst_id - 1]; + + switch (subcode) { + case BTM_BLE_MULTI_ADV_ENB: { + BTM_TRACE_DEBUG("BTM_BLE_MULTI_ADV_ENB status = %d", status); + + /* Mark as not in use here, if instance cannot be enabled */ + if (HCI_SUCCESS != status && BTM_BLE_MULTI_ADV_ENB_EVT == cb_evt) { + btm_multi_adv_cb.p_adv_inst[inst_id - 1].in_use = FALSE; + } + break; + } + + case BTM_BLE_MULTI_ADV_SET_PARAM: { + BTM_TRACE_DEBUG("BTM_BLE_MULTI_ADV_SET_PARAM status = %d", status); + break; + } + + case BTM_BLE_MULTI_ADV_WRITE_ADV_DATA: { + BTM_TRACE_DEBUG("BTM_BLE_MULTI_ADV_WRITE_ADV_DATA status = %d", status); + break; + } + + case BTM_BLE_MULTI_ADV_WRITE_SCAN_RSP_DATA: { + BTM_TRACE_DEBUG("BTM_BLE_MULTI_ADV_WRITE_SCAN_RSP_DATA status = %d", status); + break; + } + + case BTM_BLE_MULTI_ADV_SET_RANDOM_ADDR: { + BTM_TRACE_DEBUG("BTM_BLE_MULTI_ADV_SET_RANDOM_ADDR status = %d", status); + break; + } + + default: + break; + } + + if (cb_evt != 0 && p_inst->p_cback != NULL) { + (p_inst->p_cback)(cb_evt, inst_id, p_inst->p_ref, status); + } + return; +} + +/******************************************************************************* +** +** Function btm_ble_enable_multi_adv +** +** Description This function enable the customer specific feature in controller +** +** Parameters enable: enable or disable +** inst_id: adv instance ID, can not be 0 +** +** Returns status +** +*******************************************************************************/ +tBTM_STATUS btm_ble_enable_multi_adv (BOOLEAN enable, UINT8 inst_id, UINT8 cb_evt) +{ + UINT8 param[BTM_BLE_MULTI_ADV_ENB_LEN], *pp; + UINT8 enb = enable ? 1 : 0; + tBTM_STATUS rt; + + pp = param; + memset(param, 0, BTM_BLE_MULTI_ADV_ENB_LEN); + + UINT8_TO_STREAM (pp, BTM_BLE_MULTI_ADV_ENB); + UINT8_TO_STREAM (pp, enb); + UINT8_TO_STREAM (pp, inst_id); + + BTM_TRACE_EVENT (" btm_ble_enable_multi_adv: enb %d, Inst ID %d", enb, inst_id); + + if ((rt = BTM_VendorSpecificCommand (HCI_BLE_MULTI_ADV_OCF, + BTM_BLE_MULTI_ADV_ENB_LEN, + param, + btm_ble_multi_adv_vsc_cmpl_cback)) + == BTM_CMD_STARTED) { + btm_ble_multi_adv_enq_op_q(BTM_BLE_MULTI_ADV_ENB, inst_id, cb_evt); + } + return rt; +} +/******************************************************************************* +** +** Function btm_ble_map_adv_tx_power +** +** Description return the actual power in dBm based on the mapping in config file +** +** Parameters advertise parameters used for this instance. +** +** Returns tx power in dBm +** +*******************************************************************************/ +static const int btm_ble_tx_power[BTM_BLE_ADV_TX_POWER_MAX + 1] = BTM_BLE_ADV_TX_POWER; +char btm_ble_map_adv_tx_power(int tx_power_index) +{ + if (0 <= tx_power_index && tx_power_index <= BTM_BLE_ADV_TX_POWER_MAX) { + return (char)btm_ble_tx_power[tx_power_index]; + } + return 0; +} +/******************************************************************************* +** +** Function btm_ble_multi_adv_set_params +** +** Description This function enable the customer specific feature in controller +** +** Parameters advertise parameters used for this instance. +** +** Returns status +** +*******************************************************************************/ +tBTM_STATUS btm_ble_multi_adv_set_params (tBTM_BLE_MULTI_ADV_INST *p_inst, + tBTM_BLE_ADV_PARAMS *p_params, + UINT8 cb_evt) +{ + UINT8 param[BTM_BLE_MULTI_ADV_SET_PARAM_LEN], *pp; + tBTM_STATUS rt; + BD_ADDR dummy = {0, 0, 0, 0, 0, 0}; + + pp = param; + memset(param, 0, BTM_BLE_MULTI_ADV_SET_PARAM_LEN); + + UINT8_TO_STREAM(pp, BTM_BLE_MULTI_ADV_SET_PARAM); + + UINT16_TO_STREAM (pp, p_params->adv_int_min); + UINT16_TO_STREAM (pp, p_params->adv_int_max); + UINT8_TO_STREAM (pp, p_params->adv_type); + +#if (defined BLE_PRIVACY_SPT && BLE_PRIVACY_SPT == TRUE) + if (btm_cb.ble_ctr_cb.privacy_mode != BTM_PRIVACY_NONE) { + UINT8_TO_STREAM (pp, BLE_ADDR_RANDOM); + BDADDR_TO_STREAM (pp, p_inst->rpa); + } else +#endif + { + UINT8_TO_STREAM (pp, BLE_ADDR_PUBLIC); + BDADDR_TO_STREAM (pp, controller_get_interface()->get_address()->address); + } + + BTM_TRACE_EVENT (" btm_ble_multi_adv_set_params,Min %d, Max %d,adv_type %d", + p_params->adv_int_min, p_params->adv_int_max, p_params->adv_type); + + UINT8_TO_STREAM (pp, 0); + BDADDR_TO_STREAM (pp, dummy); + + if (p_params->channel_map == 0 || p_params->channel_map > BTM_BLE_DEFAULT_ADV_CHNL_MAP) { + p_params->channel_map = BTM_BLE_DEFAULT_ADV_CHNL_MAP; + } + UINT8_TO_STREAM (pp, p_params->channel_map); + + if (p_params->adv_filter_policy >= AP_SCAN_CONN_POLICY_MAX) { + p_params->adv_filter_policy = AP_SCAN_CONN_ALL; + } + UINT8_TO_STREAM (pp, p_params->adv_filter_policy); + + UINT8_TO_STREAM (pp, p_inst->inst_id); + + if (p_params->tx_power > BTM_BLE_ADV_TX_POWER_MAX) { + p_params->tx_power = BTM_BLE_ADV_TX_POWER_MAX; + } + UINT8_TO_STREAM (pp, btm_ble_map_adv_tx_power(p_params->tx_power)); + + BTM_TRACE_EVENT("set_params:Chnl Map %d,adv_fltr policy %d,ID:%d, TX Power%d", + p_params->channel_map, p_params->adv_filter_policy, p_inst->inst_id, p_params->tx_power); + + if ((rt = BTM_VendorSpecificCommand (HCI_BLE_MULTI_ADV_OCF, + BTM_BLE_MULTI_ADV_SET_PARAM_LEN, + param, + btm_ble_multi_adv_vsc_cmpl_cback)) + == BTM_CMD_STARTED) { + p_inst->adv_evt = p_params->adv_type; + +#if (defined BLE_PRIVACY_SPT && BLE_PRIVACY_SPT == TRUE) + if (btm_cb.ble_ctr_cb.privacy_mode != BTM_PRIVACY_NONE) { + /* start timer */ + p_inst->raddr_timer_ent.param = (TIMER_PARAM_TYPE) p_inst; + btu_start_timer_oneshot(&p_inst->raddr_timer_ent, BTU_TTYPE_BLE_RANDOM_ADDR, + BTM_BLE_PRIVATE_ADDR_INT); + } +#endif + btm_ble_multi_adv_enq_op_q(BTM_BLE_MULTI_ADV_SET_PARAM, p_inst->inst_id, cb_evt); + } + return rt; +} + +/******************************************************************************* +** +** Function btm_ble_multi_adv_write_rpa +** +** Description This function write the random address for the adv instance into +** controller +** +** Parameters +** +** Returns status +** +*******************************************************************************/ +tBTM_STATUS btm_ble_multi_adv_write_rpa (tBTM_BLE_MULTI_ADV_INST *p_inst, BD_ADDR random_addr) +{ + UINT8 param[BTM_BLE_MULTI_ADV_SET_RANDOM_ADDR_LEN], *pp = param; + tBTM_STATUS rt; + + BTM_TRACE_EVENT ("%s-BD_ADDR:%02x-%02x-%02x-%02x-%02x-%02x,inst_id:%d", + __FUNCTION__, random_addr[5], random_addr[4], random_addr[3], random_addr[2], + random_addr[1], random_addr[0], p_inst->inst_id); + + memset(param, 0, BTM_BLE_MULTI_ADV_SET_RANDOM_ADDR_LEN); + + UINT8_TO_STREAM (pp, BTM_BLE_MULTI_ADV_SET_RANDOM_ADDR); + BDADDR_TO_STREAM(pp, random_addr); + UINT8_TO_STREAM(pp, p_inst->inst_id); + + if ((rt = BTM_VendorSpecificCommand (HCI_BLE_MULTI_ADV_OCF, + BTM_BLE_MULTI_ADV_SET_RANDOM_ADDR_LEN, + param, + btm_ble_multi_adv_vsc_cmpl_cback)) == BTM_CMD_STARTED) { + /* start a periodical timer to refresh random addr */ + btu_stop_timer_oneshot(&p_inst->raddr_timer_ent); + p_inst->raddr_timer_ent.param = (TIMER_PARAM_TYPE) p_inst; + btu_start_timer_oneshot(&p_inst->raddr_timer_ent, BTU_TTYPE_BLE_RANDOM_ADDR, + BTM_BLE_PRIVATE_ADDR_INT); + + btm_ble_multi_adv_enq_op_q(BTM_BLE_MULTI_ADV_SET_RANDOM_ADDR, p_inst->inst_id, 0); + } + return rt; +} + +/******************************************************************************* +** +** Function btm_ble_multi_adv_gen_rpa_cmpl +** +** Description RPA generation completion callback for each adv instance. Will +** continue write the new RPA into controller. +** +** Returns none. +** +*******************************************************************************/ +void btm_ble_multi_adv_gen_rpa_cmpl(tBTM_RAND_ENC *p) +{ +#if (SMP_INCLUDED == TRUE) + tSMP_ENC output; + UINT8 index = 0; + tBTM_BLE_MULTI_ADV_INST *p_inst = NULL; + + /* Retrieve the index of adv instance from stored Q */ + if (btm_multi_adv_idx_q.front == -1) { + BTM_TRACE_ERROR(" %s can't locate advertise instance", __FUNCTION__); + return; + } else { + index = btm_multi_adv_idx_q.inst_index_queue[btm_multi_adv_idx_q.front]; + if (btm_multi_adv_idx_q.front == btm_multi_adv_idx_q.rear) { + btm_multi_adv_idx_q.front = -1; + btm_multi_adv_idx_q.rear = -1; + } else { + btm_multi_adv_idx_q.front = (btm_multi_adv_idx_q.front + 1) % BTM_BLE_MULTI_ADV_MAX; + } + } + + p_inst = &(btm_multi_adv_cb.p_adv_inst[index]); + + BTM_TRACE_EVENT ("btm_ble_multi_adv_gen_rpa_cmpl inst_id = %d", p_inst->inst_id); + if (p) { + p->param_buf[2] &= (~BLE_RESOLVE_ADDR_MASK); + p->param_buf[2] |= BLE_RESOLVE_ADDR_MSB; + + p_inst->rpa[2] = p->param_buf[0]; + p_inst->rpa[1] = p->param_buf[1]; + p_inst->rpa[0] = p->param_buf[2]; + + if (!SMP_Encrypt(btm_cb.devcb.id_keys.irk, BT_OCTET16_LEN, p->param_buf, 3, &output)) { + BTM_TRACE_DEBUG("generate random address failed"); + } else { + /* set hash to be LSB of rpAddress */ + p_inst->rpa[5] = output.param_buf[0]; + p_inst->rpa[4] = output.param_buf[1]; + p_inst->rpa[3] = output.param_buf[2]; + } + + if (p_inst->inst_id != BTM_BLE_MULTI_ADV_DEFAULT_STD && + p_inst->inst_id < BTM_BleMaxMultiAdvInstanceCount()) { + /* set it to controller */ + btm_ble_multi_adv_write_rpa(p_inst, p_inst->rpa); + } + } +#endif +} + +/******************************************************************************* +** +** Function btm_ble_multi_adv_configure_rpa +** +** Description This function set the random address for the adv instance +** +** Parameters advertise parameters used for this instance. +** +** Returns none +** +*******************************************************************************/ +void btm_ble_multi_adv_configure_rpa (tBTM_BLE_MULTI_ADV_INST *p_inst) +{ + if (btm_multi_adv_idx_q.front == (btm_multi_adv_idx_q.rear + 1) % BTM_BLE_MULTI_ADV_MAX) { + BTM_TRACE_ERROR("outstanding rand generation exceeded max allowed "); + return; + } else { + if (btm_multi_adv_idx_q.front == -1) { + btm_multi_adv_idx_q.front = 0; + btm_multi_adv_idx_q.rear = 0; + } else { + btm_multi_adv_idx_q.rear = (btm_multi_adv_idx_q.rear + 1) % BTM_BLE_MULTI_ADV_MAX; + } + btm_multi_adv_idx_q.inst_index_queue[btm_multi_adv_idx_q.rear] = p_inst->index; + } + btm_gen_resolvable_private_addr((void *)btm_ble_multi_adv_gen_rpa_cmpl); +} + +/******************************************************************************* +** +** Function btm_ble_multi_adv_reenable +** +** Description This function re-enable adv instance upon a connection establishment. +** +** Parameters advertise parameters used for this instance. +** +** Returns none. +** +*******************************************************************************/ +void btm_ble_multi_adv_reenable(UINT8 inst_id) +{ + tBTM_BLE_MULTI_ADV_INST *p_inst = &btm_multi_adv_cb.p_adv_inst[inst_id - 1]; + + if (TRUE == p_inst->in_use) { + if (p_inst->adv_evt != BTM_BLE_CONNECT_DIR_EVT) { + btm_ble_enable_multi_adv (TRUE, p_inst->inst_id, 0); + } else + /* mark directed adv as disabled if adv has been stopped */ + { + (p_inst->p_cback)(BTM_BLE_MULTI_ADV_DISABLE_EVT, p_inst->inst_id, p_inst->p_ref, 0); + p_inst->in_use = FALSE; + } + } +} + +/******************************************************************************* +** +** Function btm_ble_multi_adv_enb_privacy +** +** Description This function enable/disable privacy setting in multi adv +** +** Parameters enable: enable or disable the adv instance. +** +** Returns none. +** +*******************************************************************************/ +void btm_ble_multi_adv_enb_privacy(BOOLEAN enable) +{ + UINT8 i; + tBTM_BLE_MULTI_ADV_INST *p_inst = &btm_multi_adv_cb.p_adv_inst[0]; + + for (i = 0; i < BTM_BleMaxMultiAdvInstanceCount() - 1; i ++, p_inst++) { + p_inst->in_use = FALSE; + if (enable) { + btm_ble_multi_adv_configure_rpa (p_inst); + } else { + btu_stop_timer_oneshot(&p_inst->raddr_timer_ent); + } + } +} + +/******************************************************************************* +** +** Function BTM_BleEnableAdvInstance +** +** Description This function enable a Multi-ADV instance with the specified +** adv parameters +** +** Parameters p_params: pointer to the adv parameter structure, set as default +** adv parameter when the instance is enabled. +** p_cback: callback function for the adv instance. +** p_ref: reference data attach to the adv instance to be enabled. +** +** Returns status +** +*******************************************************************************/ +tBTM_STATUS BTM_BleEnableAdvInstance (tBTM_BLE_ADV_PARAMS *p_params, + tBTM_BLE_MULTI_ADV_CBACK *p_cback, void *p_ref) +{ + UINT8 i; + tBTM_STATUS rt = BTM_NO_RESOURCES; + tBTM_BLE_MULTI_ADV_INST *p_inst = &btm_multi_adv_cb.p_adv_inst[0]; + + BTM_TRACE_EVENT("BTM_BleEnableAdvInstance called"); + + if (0 == btm_cb.cmn_ble_vsc_cb.adv_inst_max) { + BTM_TRACE_ERROR("Controller does not support Multi ADV"); + return BTM_ERR_PROCESSING; + } + + if (NULL == p_inst) { + BTM_TRACE_ERROR("Invalid instance in BTM_BleEnableAdvInstance"); + return BTM_ERR_PROCESSING; + } + + for (i = 0; i < BTM_BleMaxMultiAdvInstanceCount() - 1; i ++, p_inst++) { + if (FALSE == p_inst->in_use) { + p_inst->in_use = TRUE; + /* configure adv parameter */ + if (p_params) { + rt = btm_ble_multi_adv_set_params(p_inst, p_params, 0); + } else { + rt = BTM_CMD_STARTED; + } + + /* enable adv */ + BTM_TRACE_EVENT("btm_ble_enable_multi_adv being called with inst_id:%d", + p_inst->inst_id); + + if (BTM_CMD_STARTED == rt) { + if ((rt = btm_ble_enable_multi_adv (TRUE, p_inst->inst_id, + BTM_BLE_MULTI_ADV_ENB_EVT)) == BTM_CMD_STARTED) { + p_inst->p_cback = p_cback; + p_inst->p_ref = p_ref; + } + } + + if (BTM_CMD_STARTED != rt) { + p_inst->in_use = FALSE; + BTM_TRACE_ERROR("BTM_BleEnableAdvInstance failed"); + } + break; + } + } + return rt; +} + +/******************************************************************************* +** +** Function BTM_BleUpdateAdvInstParam +** +** Description This function update a Multi-ADV instance with the specified +** adv parameters. +** +** Parameters inst_id: adv instance ID +** p_params: pointer to the adv parameter structure. +** +** Returns status +** +*******************************************************************************/ +tBTM_STATUS BTM_BleUpdateAdvInstParam (UINT8 inst_id, tBTM_BLE_ADV_PARAMS *p_params) +{ + tBTM_STATUS rt = BTM_ILLEGAL_VALUE; + tBTM_BLE_MULTI_ADV_INST *p_inst = &btm_multi_adv_cb.p_adv_inst[inst_id - 1]; + + BTM_TRACE_EVENT("BTM_BleUpdateAdvInstParam called with inst_id:%d", inst_id); + + if (0 == btm_cb.cmn_ble_vsc_cb.adv_inst_max) { + BTM_TRACE_ERROR("Controller does not support Multi ADV"); + return BTM_ERR_PROCESSING; + } + + if (inst_id < BTM_BleMaxMultiAdvInstanceCount() && + inst_id != BTM_BLE_MULTI_ADV_DEFAULT_STD && + p_params != NULL) { + if (FALSE == p_inst->in_use) { + BTM_TRACE_DEBUG("adv instance %d is not active", inst_id); + return BTM_WRONG_MODE; + } else { + btm_ble_enable_multi_adv(FALSE, inst_id, 0); + } + + if (BTM_CMD_STARTED == btm_ble_multi_adv_set_params(p_inst, p_params, 0)) { + rt = btm_ble_enable_multi_adv(TRUE, inst_id, BTM_BLE_MULTI_ADV_PARAM_EVT); + } + } + return rt; +} + +/******************************************************************************* +** +** Function BTM_BleCfgAdvInstData +** +** Description This function configure a Multi-ADV instance with the specified +** adv data or scan response data. +** +** Parameters inst_id: adv instance ID +** is_scan_rsp: is this scan response. if no, set as adv data. +** data_mask: adv data mask. +** p_data: pointer to the adv data structure. +** +** Returns status +** +*******************************************************************************/ +tBTM_STATUS BTM_BleCfgAdvInstData (UINT8 inst_id, BOOLEAN is_scan_rsp, + tBTM_BLE_AD_MASK data_mask, + tBTM_BLE_ADV_DATA *p_data) +{ + UINT8 param[BTM_BLE_MULTI_ADV_WRITE_DATA_LEN], *pp = param; + UINT8 sub_code = (is_scan_rsp) ? + BTM_BLE_MULTI_ADV_WRITE_SCAN_RSP_DATA : BTM_BLE_MULTI_ADV_WRITE_ADV_DATA; + UINT8 *p_len; + tBTM_STATUS rt; + UINT8 *pp_temp = (UINT8 *)(param + BTM_BLE_MULTI_ADV_WRITE_DATA_LEN - 1); + tBTM_BLE_VSC_CB cmn_ble_vsc_cb; + + BTM_BleGetVendorCapabilities(&cmn_ble_vsc_cb); + if (0 == cmn_ble_vsc_cb.adv_inst_max) { + BTM_TRACE_ERROR("Controller does not support Multi ADV"); + return BTM_ERR_PROCESSING; + } + + btm_ble_update_dmt_flag_bits(&p_data->flag, btm_cb.btm_inq_vars.connectable_mode, + btm_cb.btm_inq_vars.discoverable_mode); + + BTM_TRACE_EVENT("BTM_BleCfgAdvInstData called with inst_id:%d", inst_id); + if (inst_id > BTM_BLE_MULTI_ADV_MAX || inst_id == BTM_BLE_MULTI_ADV_DEFAULT_STD) { + return BTM_ILLEGAL_VALUE; + } + + memset(param, 0, BTM_BLE_MULTI_ADV_WRITE_DATA_LEN); + + UINT8_TO_STREAM(pp, sub_code); + p_len = pp ++; + btm_ble_build_adv_data(&data_mask, &pp, p_data); + *p_len = (UINT8)(pp - param - 2); + UINT8_TO_STREAM(pp_temp, inst_id); + + if ((rt = BTM_VendorSpecificCommand (HCI_BLE_MULTI_ADV_OCF, + (UINT8)BTM_BLE_MULTI_ADV_WRITE_DATA_LEN, + param, + btm_ble_multi_adv_vsc_cmpl_cback)) + == BTM_CMD_STARTED) { + btm_ble_multi_adv_enq_op_q(sub_code, inst_id, BTM_BLE_MULTI_ADV_DATA_EVT); + } + return rt; +} + +/******************************************************************************* +** +** Function BTM_BleDisableAdvInstance +** +** Description This function disables a Multi-ADV instance. +** +** Parameters inst_id: adv instance ID +** +** Returns status +** +*******************************************************************************/ +tBTM_STATUS BTM_BleDisableAdvInstance (UINT8 inst_id) +{ + tBTM_STATUS rt = BTM_ILLEGAL_VALUE; + tBTM_BLE_VSC_CB cmn_ble_vsc_cb; + + BTM_TRACE_EVENT("BTM_BleDisableAdvInstance with inst_id:%d", inst_id); + + BTM_BleGetVendorCapabilities(&cmn_ble_vsc_cb); + + if (0 == cmn_ble_vsc_cb.adv_inst_max) { + BTM_TRACE_ERROR("Controller does not support Multi ADV"); + return BTM_ERR_PROCESSING; + } + + if (inst_id < BTM_BleMaxMultiAdvInstanceCount() && + inst_id != BTM_BLE_MULTI_ADV_DEFAULT_STD) { + if ((rt = btm_ble_enable_multi_adv(FALSE, inst_id, BTM_BLE_MULTI_ADV_DISABLE_EVT)) + == BTM_CMD_STARTED) { + btm_ble_multi_adv_configure_rpa(&btm_multi_adv_cb.p_adv_inst[inst_id - 1]); + btu_stop_timer_oneshot(&btm_multi_adv_cb.p_adv_inst[inst_id - 1].raddr_timer_ent); + btm_multi_adv_cb.p_adv_inst[inst_id - 1].in_use = FALSE; + } + } + return rt; +} +/******************************************************************************* +** +** Function btm_ble_multi_adv_vse_cback +** +** Description VSE callback for multi adv events. +** +** Returns +** +*******************************************************************************/ +void btm_ble_multi_adv_vse_cback(UINT8 len, UINT8 *p) +{ + UINT8 sub_event; + UINT8 adv_inst; + UINT16 conn_handle; + tACL_CONN *p_acl_cb = NULL; + /* Check if this is a BLE RSSI vendor specific event */ + STREAM_TO_UINT8(sub_event, p); + len--; + + BTM_TRACE_EVENT("btm_ble_multi_adv_vse_cback called with event:%d", sub_event); + if ((sub_event == HCI_VSE_SUBCODE_BLE_MULTI_ADV_ST_CHG) && (len >= 4)) { + STREAM_TO_UINT8(adv_inst, p); + ++p; + STREAM_TO_UINT16(conn_handle, p); + + if ((p_acl_cb = btm_handle_to_acl(conn_handle)) != NULL) { +#if (defined BLE_PRIVACY_SPT && BLE_PRIVACY_SPT == TRUE) + if (btm_cb.ble_ctr_cb.privacy_mode != BTM_PRIVACY_NONE && + adv_inst <= BTM_BLE_MULTI_ADV_MAX && adv_inst != BTM_BLE_MULTI_ADV_DEFAULT_STD) { + memcpy(p_acl_cb->conn_addr, btm_multi_adv_cb.p_adv_inst[adv_inst - 1].rpa, + BD_ADDR_LEN); + } +#endif + } + + if (adv_inst < BTM_BleMaxMultiAdvInstanceCount() && + adv_inst != BTM_BLE_MULTI_ADV_DEFAULT_STD) { + BTM_TRACE_EVENT("btm_ble_multi_adv_reenable called"); + btm_ble_multi_adv_reenable(adv_inst); + } + /* re-enable connectibility */ + else if (adv_inst == BTM_BLE_MULTI_ADV_DEFAULT_STD) { + if (btm_cb.ble_ctr_cb.inq_var.connectable_mode == BTM_BLE_CONNECTABLE) { + btm_ble_set_connectability ( btm_cb.ble_ctr_cb.inq_var.connectable_mode ); + } + } + + } + +} +/******************************************************************************* +** +** Function btm_ble_multi_adv_init +** +** Description This function initialize the multi adv control block. +** +** Parameters None +** +** Returns void +** +*******************************************************************************/ +void btm_ble_multi_adv_init(void) +{ +#if BTM_DYNAMIC_MEMORY == TRUE + btm_multi_adv_cb_ptr = (tBTM_BLE_MULTI_ADV_CB *)osi_malloc(sizeof(tBTM_BLE_MULTI_ADV_CB)); + btm_multi_adv_idx_q_ptr = (tBTM_BLE_MULTI_ADV_INST_IDX_Q *)osi_malloc(sizeof(tBTM_BLE_MULTI_ADV_INST_IDX_Q)); + if (btm_multi_adv_cb_ptr == NULL || btm_multi_adv_idx_q_ptr == NULL) { + BTM_TRACE_ERROR("%s malloc failed", __func__); + return; + } +#endif + + UINT8 i = 0; + memset(&btm_multi_adv_cb, 0, sizeof(tBTM_BLE_MULTI_ADV_CB)); + memset (&btm_multi_adv_idx_q, 0, sizeof (tBTM_BLE_MULTI_ADV_INST_IDX_Q)); + btm_multi_adv_idx_q.front = -1; + btm_multi_adv_idx_q.rear = -1; + + if (btm_cb.cmn_ble_vsc_cb.adv_inst_max > 0) { + btm_multi_adv_cb.p_adv_inst = osi_malloc( sizeof(tBTM_BLE_MULTI_ADV_INST) * + (btm_cb.cmn_ble_vsc_cb.adv_inst_max)); + memset(btm_multi_adv_cb.p_adv_inst, 0, sizeof(tBTM_BLE_MULTI_ADV_INST) * + (btm_cb.cmn_ble_vsc_cb.adv_inst_max)); + + btm_multi_adv_cb.op_q.p_sub_code = osi_malloc( sizeof(UINT8) * + (btm_cb.cmn_ble_vsc_cb.adv_inst_max)); + memset(btm_multi_adv_cb.op_q.p_sub_code, 0, + sizeof(UINT8) * (btm_cb.cmn_ble_vsc_cb.adv_inst_max)); + + btm_multi_adv_cb.op_q.p_inst_id = osi_malloc( sizeof(UINT8) * + (btm_cb.cmn_ble_vsc_cb.adv_inst_max)); + memset(btm_multi_adv_cb.op_q.p_inst_id, 0, + sizeof(UINT8) * (btm_cb.cmn_ble_vsc_cb.adv_inst_max)); + } + + /* Initialize adv instance indices and IDs. */ + for (i = 0; i < btm_cb.cmn_ble_vsc_cb.adv_inst_max; i++) { + btm_multi_adv_cb.p_adv_inst[i].index = i; + btm_multi_adv_cb.p_adv_inst[i].inst_id = i + 1; + } + + BTM_RegisterForVSEvents(btm_ble_multi_adv_vse_cback, TRUE); +} + +/******************************************************************************* +** +** Function btm_ble_multi_adv_cleanup +** +** Description This function cleans up multi adv control block. +** +** Parameters +** Returns void +** +*******************************************************************************/ +void btm_ble_multi_adv_cleanup(void) +{ + if (btm_multi_adv_cb.p_adv_inst) { + osi_free(btm_multi_adv_cb.p_adv_inst); + btm_multi_adv_cb.p_adv_inst = NULL; + } + + if (btm_multi_adv_cb.op_q.p_sub_code) { + osi_free(btm_multi_adv_cb.op_q.p_sub_code); + btm_multi_adv_cb.op_q.p_sub_code = NULL; + } + + if (btm_multi_adv_cb.op_q.p_inst_id) { + osi_free(btm_multi_adv_cb.op_q.p_inst_id); + btm_multi_adv_cb.op_q.p_inst_id = NULL; + } + +#if BTM_DYNAMIC_MEMORY == TRUE + if(btm_multi_adv_cb_ptr) { + osi_free(btm_multi_adv_cb_ptr); + btm_multi_adv_cb_ptr = NULL; + } + if(btm_multi_adv_idx_q_ptr) { + osi_free(btm_multi_adv_idx_q_ptr); + btm_multi_adv_idx_q_ptr = NULL; + } +#endif +} + +/******************************************************************************* +** +** Function btm_ble_multi_adv_get_ref +** +** Description This function obtains the reference pointer for the instance ID provided +** +** Parameters inst_id - Instance ID +** +** Returns void* +** +*******************************************************************************/ +void *btm_ble_multi_adv_get_ref(UINT8 inst_id) +{ + tBTM_BLE_MULTI_ADV_INST *p_inst = NULL; + + if (inst_id < BTM_BleMaxMultiAdvInstanceCount()) { + p_inst = &btm_multi_adv_cb.p_adv_inst[inst_id - 1]; + if (NULL != p_inst) { + return p_inst->p_ref; + } + } + + return NULL; +} +#endif diff --git a/lib/bt/host/bluedroid/stack/btm/btm_ble_privacy.c b/lib/bt/host/bluedroid/stack/btm/btm_ble_privacy.c new file mode 100644 index 00000000..7058df20 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/btm/btm_ble_privacy.c @@ -0,0 +1,1086 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains functions for BLE controller based privacy. + * + ******************************************************************************/ +#include <string.h> +#include "common/bt_target.h" + +#if (BLE_INCLUDED == TRUE && BLE_PRIVACY_SPT == TRUE) +#include "stack/bt_types.h" +#include "stack/hcimsgs.h" +#include "stack/btu.h" +//#include "vendor_hcidefs.h" +#include "btm_int.h" +#include "device/controller.h" +#include "stack/hcidefs.h" + +#define HCI_VENDOR_BLE_RPA_VSC (0x0155 | HCI_GRP_VENDOR_SPECIFIC) + +/* RPA offload VSC specifics */ +#define BTM_BLE_META_IRK_ENABLE 0x01 +#define BTM_BLE_META_ADD_IRK_ENTRY 0x02 +#define BTM_BLE_META_REMOVE_IRK_ENTRY 0x03 +#define BTM_BLE_META_CLEAR_IRK_LIST 0x04 +#define BTM_BLE_META_READ_IRK_ENTRY 0x05 +#define BTM_BLE_META_CS_RESOLVE_ADDR 0x00000001 +#define BTM_BLE_IRK_ENABLE_LEN 2 + +#define BTM_BLE_META_ADD_IRK_LEN 24 +#define BTM_BLE_META_REMOVE_IRK_LEN 8 +#define BTM_BLE_META_CLEAR_IRK_LEN 1 +#define BTM_BLE_META_READ_IRK_LEN 2 +#define BTM_BLE_META_ADD_WL_ATTR_LEN 9 + +/******************************************************************************* +** Functions implemented controller based privacy using Resolving List +*******************************************************************************/ +/******************************************************************************* +** +** Function btm_ble_enq_resolving_list_pending +** +** Description add target address into resolving pending operation queue +** +** Parameters target_bda: target device address +** add_entry: TRUE for add entry, FALSE for remove entry +** +** Returns void +** +*******************************************************************************/ +void btm_ble_enq_resolving_list_pending(BD_ADDR pseudo_bda, UINT8 op_code) +{ + tBTM_BLE_RESOLVE_Q *p_q = &btm_cb.ble_ctr_cb.resolving_list_pend_q; + + memcpy(p_q->resolve_q_random_pseudo[p_q->q_next], pseudo_bda, BD_ADDR_LEN); + p_q->resolve_q_action[p_q->q_next] = op_code; + p_q->q_next ++; + p_q->q_next %= controller_get_interface()->get_ble_resolving_list_max_size(); +} + +/******************************************************************************* +** +** Function btm_ble_brcm_find_resolving_pending_entry +** +** Description check to see if the action is in pending list +** +** Parameters TRUE: action pending; +** FALSE: new action +** +** Returns void +** +*******************************************************************************/ +BOOLEAN btm_ble_brcm_find_resolving_pending_entry(BD_ADDR pseudo_addr, UINT8 action) +{ + tBTM_BLE_RESOLVE_Q *p_q = &btm_cb.ble_ctr_cb.resolving_list_pend_q; + + for (UINT8 i = p_q->q_pending; i != p_q->q_next;) { + if (memcmp(p_q->resolve_q_random_pseudo[i], pseudo_addr, BD_ADDR_LEN) == 0 && + action == p_q->resolve_q_action[i]) { + return TRUE; + } + + i ++; + i %= controller_get_interface()->get_ble_resolving_list_max_size(); + } + return FALSE; +} + +/******************************************************************************* +** +** Function btm_ble_deq_resolving_pending +** +** Description dequeue target address from resolving pending operation queue +** +** Parameters pseudo_addr: pseudo_addr device address +** +** Returns void +** +*******************************************************************************/ +BOOLEAN btm_ble_deq_resolving_pending(BD_ADDR pseudo_addr) +{ + tBTM_BLE_RESOLVE_Q *p_q = &btm_cb.ble_ctr_cb.resolving_list_pend_q; + + if (p_q->q_next != p_q->q_pending) { + memcpy(pseudo_addr, p_q->resolve_q_random_pseudo[p_q->q_pending], BD_ADDR_LEN); + memset(p_q->resolve_q_random_pseudo[p_q->q_pending], 0, BD_ADDR_LEN); + p_q->q_pending ++; + p_q->q_pending %= controller_get_interface()->get_ble_resolving_list_max_size(); + return TRUE; + } + + return FALSE; +} + +/******************************************************************************* +** +** Function btm_ble_clear_irk_index +** +** Description clear IRK list index mask for availability +** +** Returns none +** +*******************************************************************************/ +void btm_ble_clear_irk_index(UINT8 index) +{ + UINT8 byte; + UINT8 bit; + + if (index < controller_get_interface()->get_ble_resolving_list_max_size()) { + byte = index / 8; + bit = index % 8; + btm_cb.ble_ctr_cb.irk_list_mask[byte] &= (~(1 << bit)); + } +} + +/******************************************************************************* +** +** Function btm_ble_find_irk_index +** +** Description find the first available IRK list index +** +** Returns index from 0 ~ max (127 default) +** +*******************************************************************************/ +UINT8 btm_ble_find_irk_index(void) +{ + UINT8 i = 0; + UINT8 byte; + UINT8 bit; + + while (i < controller_get_interface()->get_ble_resolving_list_max_size()) { + byte = i / 8; + bit = i % 8; + + if ((btm_cb.ble_ctr_cb.irk_list_mask[byte] & (1 << bit)) == 0) { + btm_cb.ble_ctr_cb.irk_list_mask[byte] |= (1 << bit); + return i; + } + i++; + } + + BTM_TRACE_ERROR ("%s failed, list full", __func__); + return i; +} + +/******************************************************************************* +** +** Function btm_ble_update_resolving_list +** +** Description update resolving list entry in host maintained record +** +** Returns void +** +*******************************************************************************/ +void btm_ble_update_resolving_list(BD_ADDR pseudo_bda, BOOLEAN add) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev(pseudo_bda); + if (p_dev_rec == NULL) { + return; + } + + if (add) { + p_dev_rec->ble.in_controller_list |= BTM_RESOLVING_LIST_BIT; + if (!controller_get_interface()->supports_ble_privacy()) { + p_dev_rec->ble.resolving_list_index = btm_ble_find_irk_index(); + } + } else { + p_dev_rec->ble.in_controller_list &= ~BTM_RESOLVING_LIST_BIT; + if (!controller_get_interface()->supports_ble_privacy()) { + /* clear IRK list index mask */ + btm_ble_clear_irk_index(p_dev_rec->ble.resolving_list_index); + p_dev_rec->ble.resolving_list_index = 0; + } + } +} + +/******************************************************************************* +** +** Function btm_ble_clear_resolving_list_complete +** +** Description This function is called when command complete for +** clear resolving list +** +** Returns void +** +*******************************************************************************/ +void btm_ble_clear_resolving_list_complete(UINT8 *p, UINT16 evt_len) +{ + UINT8 status = 0; + STREAM_TO_UINT8(status, p); + + BTM_TRACE_DEBUG("%s status=%d", __func__, status); + + if (status == HCI_SUCCESS) { + if (evt_len >= 3) { + /* VSC complete has one extra byte for op code and list size, skip it here */ + p ++; + + /* updated the available list size, and current list size */ + uint8_t irk_list_sz_max = 0; + STREAM_TO_UINT8(irk_list_sz_max, p); + + if (controller_get_interface()->get_ble_resolving_list_max_size() == 0) { + btm_ble_resolving_list_init(irk_list_sz_max); + } + + uint8_t irk_mask_size = (irk_list_sz_max % 8) ? + (irk_list_sz_max / 8 + 1) : (irk_list_sz_max / 8); + memset(btm_cb.ble_ctr_cb.irk_list_mask, 0, irk_mask_size); + } + + btm_cb.ble_ctr_cb.resolving_list_avail_size = + controller_get_interface()->get_ble_resolving_list_max_size(); + + BTM_TRACE_DEBUG("%s resolving_list_avail_size=%d", + __func__, btm_cb.ble_ctr_cb.resolving_list_avail_size); + + list_node_t *p_node = NULL; + tBTM_SEC_DEV_REC *p_dev_rec = NULL; + for (p_node = list_begin(btm_cb.p_sec_dev_rec_list); p_node; p_node = list_next(p_node)) { + p_dev_rec = list_node(p_node); + p_dev_rec->ble.in_controller_list &= ~BTM_RESOLVING_LIST_BIT; + } + } +} + +/******************************************************************************* +** +** Function btm_ble_add_resolving_list_entry_complete +** +** Description This function is called when command complete for +** add resolving list entry +** +** Returns void +** +*******************************************************************************/ +void btm_ble_add_resolving_list_entry_complete(UINT8 *p, UINT16 evt_len) +{ + UINT8 status; + STREAM_TO_UINT8(status, p); + + BTM_TRACE_DEBUG("%s status = %d", __func__, status); + + BD_ADDR pseudo_bda; + if (!btm_ble_deq_resolving_pending(pseudo_bda)) { + BTM_TRACE_DEBUG("no pending resolving list operation"); + return; + } + + if (status == HCI_SUCCESS) { + /* privacy 1.2 command complete does not have these extra byte */ + if (evt_len > 2) { + /* VSC complete has one extra byte for op code, skip it here */ + p ++; + STREAM_TO_UINT8(btm_cb.ble_ctr_cb.resolving_list_avail_size, p); + } else { + btm_cb.ble_ctr_cb.resolving_list_avail_size --; + } + } else if (status == HCI_ERR_MEMORY_FULL) { /* BT_ERROR_CODE_MEMORY_CAPACITY_EXCEEDED */ + btm_cb.ble_ctr_cb.resolving_list_avail_size = 0; + BTM_TRACE_ERROR("%s Resolving list Full ", __func__); + } else { + BTM_TRACE_ERROR("%s Add resolving list error %d ", __func__, status); + } + +} + +/******************************************************************************* +** +** Function btm_ble_remove_resolving_list_entry_complete +** +** Description This function is called when command complete for +** remove resolving list entry +** +** Returns void +** +*******************************************************************************/ +void btm_ble_remove_resolving_list_entry_complete(UINT8 *p, UINT16 evt_len) +{ + BD_ADDR pseudo_bda; + UINT8 status; + + STREAM_TO_UINT8(status, p); + + BTM_TRACE_DEBUG("%s status = %d", __func__, status); + + if (!btm_ble_deq_resolving_pending(pseudo_bda)) { + BTM_TRACE_ERROR("%s no pending resolving list operation", __func__); + return; + } + + if (status == HCI_SUCCESS) { + /* proprietary: spec does not have these extra bytes */ + if (evt_len > 2) { + p ++; /* skip opcode */ + STREAM_TO_UINT8(btm_cb.ble_ctr_cb.resolving_list_avail_size, p); + } else { + btm_cb.ble_ctr_cb.resolving_list_avail_size++; + } + } else { + BTM_TRACE_ERROR("%s remove resolving list error 0x%x", __func__, status); + } +} + +/******************************************************************************* +** +** Function btm_ble_read_resolving_list_entry_complete +** +** Description This function is called when command complete for +** remove resolving list entry +** +** Returns void +** +*******************************************************************************/ +void btm_ble_read_resolving_list_entry_complete(UINT8 *p, UINT16 evt_len) +{ + UINT8 status, rra_type = BTM_BLE_ADDR_PSEUDO; + BD_ADDR rra, pseudo_bda; + + STREAM_TO_UINT8 (status, p); + + BTM_TRACE_DEBUG("%s status = %d", __func__, status); + + if (!btm_ble_deq_resolving_pending(pseudo_bda)) { + BTM_TRACE_ERROR("no pending resolving list operation"); + return; + } + + if (status == HCI_SUCCESS) { + /* proprietary spec has extra bytes */ + if (evt_len > 8) { + p += (2 + 16 + 1 + 6); /* skip subcode, index, IRK value, address type, identity addr type */ + STREAM_TO_BDADDR(rra, p); + + BTM_TRACE_ERROR("%s peer_addr: %02x:%02x:%02x:%02x:%02x:%02x", + __func__, rra[0], rra[1], rra[2], rra[3], rra[4], rra[5]); + } else { + STREAM_TO_BDADDR(rra, p); + } + btm_ble_refresh_peer_resolvable_private_addr(pseudo_bda, rra, rra_type); + } +} + +/******************************************************************************* +** +** Function btm_ble_set_addr_resolution_enable_complete +** +** Description This function is called when the command to set address +** resolution enable completes. +** +** Parameters p: Pointer to the command complete event data. +** evt_len: Length of the event data. +** +** Returns void +** +*******************************************************************************/ +void btm_ble_set_addr_resolution_enable_complete(UINT8 *p, UINT16 evt_len) +{ + UINT8 status; + + STREAM_TO_UINT8(status, p); + + BTM_TRACE_DEBUG("%s status = %d", __func__, status); + + tBTM_LE_RANDOM_CB *random_cb = &btm_cb.ble_ctr_cb.addr_mgnt_cb; + + if (!(random_cb && random_cb->set_local_privacy_cback)) { + return; + } + + if (status == HCI_SUCCESS) { + random_cb->set_local_privacy_cback(BTM_SUCCESS); + return; + } else if (status == HCI_ERR_COMMAND_DISALLOWED) { + BTM_TRACE_ERROR("a non-connected activity is ongoing, such as advertising and scanning"); + } else { + BTM_TRACE_ERROR("set local privacy failed"); + } + random_cb->set_local_privacy_cback(BTM_ILLEGAL_VALUE); +} + +/******************************************************************************* + VSC that implement controller based privacy +********************************************************************************/ +/******************************************************************************* +** +** Function btm_ble_resolving_list_vsc_op_cmpl +** +** Description IRK operation VSC complete handler +** +** Parameters +** +** Returns void +** +*******************************************************************************/ +void btm_ble_resolving_list_vsc_op_cmpl (tBTM_VSC_CMPL *p_params) +{ + UINT8 *p = p_params->p_param_buf, op_subcode; + UINT16 evt_len = p_params->param_len; + + op_subcode = *(p + 1); + + BTM_TRACE_DEBUG("%s op_subcode = %d", __func__, op_subcode); + + if (op_subcode == BTM_BLE_META_CLEAR_IRK_LIST) { + btm_ble_clear_resolving_list_complete(p, evt_len); + } else if (op_subcode == BTM_BLE_META_ADD_IRK_ENTRY) { + btm_ble_add_resolving_list_entry_complete(p, evt_len); + } else if (op_subcode == BTM_BLE_META_REMOVE_IRK_ENTRY) { + btm_ble_remove_resolving_list_entry_complete(p, evt_len); + } else if (op_subcode == BTM_BLE_META_READ_IRK_ENTRY) { + btm_ble_read_resolving_list_entry_complete(p, evt_len); + } else if (op_subcode == BTM_BLE_META_IRK_ENABLE) { + /* RPA offloading enable/disabled */ + } +} + +/******************************************************************************* +** +** Function btm_ble_remove_resolving_list_entry +** +** Description This function to remove an IRK entry from the list +** +** Parameters ble_addr_type: address type +** ble_addr: LE adddress +** +** Returns status +** +*******************************************************************************/ +tBTM_STATUS btm_ble_remove_resolving_list_entry(tBTM_SEC_DEV_REC *p_dev_rec) +{ + /* if controller does not support RPA offloading or privacy 1.2, skip */ + if (controller_get_interface()->get_ble_resolving_list_max_size() == 0) { + return BTM_WRONG_MODE; + } + + tBTM_STATUS st = BTM_NO_RESOURCES; + if (controller_get_interface()->supports_ble_privacy()) { + #if CONTROLLER_RPA_LIST_ENABLE + if (btsnd_hcic_ble_rm_device_resolving_list(p_dev_rec->ble.static_addr_type, + p_dev_rec->ble.static_addr)) { + st = BTM_CMD_STARTED; + } + #else + // do nothing + /* It will cause that scanner doesn't send scan request to advertiser + * which has sent IRK to us and we have stored the IRK in controller. + * It is a hardware limitation. The preliminary solution is not to + * send key to the controller, but to resolve the random address in host. */ + #endif + } else { + UINT8 param[20] = {0}; + UINT8 *p = param; + + UINT8_TO_STREAM(p, BTM_BLE_META_REMOVE_IRK_ENTRY); + UINT8_TO_STREAM(p, p_dev_rec->ble.static_addr_type); + BDADDR_TO_STREAM(p, p_dev_rec->ble.static_addr); + + st = BTM_VendorSpecificCommand(HCI_VENDOR_BLE_RPA_VSC, + BTM_BLE_META_REMOVE_IRK_LEN, + param, + btm_ble_resolving_list_vsc_op_cmpl); + } + + if (st == BTM_CMD_STARTED) { + btm_ble_enq_resolving_list_pending( p_dev_rec->bd_addr, BTM_BLE_META_REMOVE_IRK_ENTRY); + } + + return st; +} + +/******************************************************************************* +** +** Function btm_ble_clear_resolving_list +** +** Description This function clears the resolving list +** +** Parameters None. +** +** Returns status +** +*******************************************************************************/ +tBTM_STATUS btm_ble_clear_resolving_list(void) +{ + tBTM_STATUS st = BTM_NO_RESOURCES; + + if (controller_get_interface()->supports_ble_privacy()) { + if (btsnd_hcic_ble_clear_resolving_list()) { + st = BTM_SUCCESS; + } + } else { + UINT8 param[20] = {0}; + UINT8 *p = param; + + UINT8_TO_STREAM(p, BTM_BLE_META_CLEAR_IRK_LIST); + st = BTM_VendorSpecificCommand (HCI_VENDOR_BLE_RPA_VSC, + BTM_BLE_META_CLEAR_IRK_LEN, + param, + btm_ble_resolving_list_vsc_op_cmpl); + } + + return st; +} + +/******************************************************************************* +** +** Function btm_ble_read_resolving_list_entry +** +** Description This function read an IRK entry by index +** +** Parameters entry index. +** +** Returns status +** +*******************************************************************************/ +tBTM_STATUS btm_ble_read_resolving_list_entry(tBTM_SEC_DEV_REC *p_dev_rec) +{ + tBTM_STATUS st = BTM_NO_RESOURCES; + + if (!(p_dev_rec->ble.in_controller_list & BTM_RESOLVING_LIST_BIT)) { + return BTM_WRONG_MODE; + } + + if (controller_get_interface()->supports_ble_privacy()) { + if (btsnd_hcic_ble_read_resolvable_addr_peer(p_dev_rec->ble.static_addr_type, + p_dev_rec->ble.static_addr)) { + st = BTM_CMD_STARTED; + } + } else { + UINT8 param[20] = {0}; + UINT8 *p = param; + + UINT8_TO_STREAM(p, BTM_BLE_META_READ_IRK_ENTRY); + UINT8_TO_STREAM(p, p_dev_rec->ble.resolving_list_index); + + st = BTM_VendorSpecificCommand (HCI_VENDOR_BLE_RPA_VSC, + BTM_BLE_META_READ_IRK_LEN, + param, + btm_ble_resolving_list_vsc_op_cmpl); + } + + if (st == BTM_CMD_STARTED) { + btm_ble_enq_resolving_list_pending(p_dev_rec->bd_addr, + BTM_BLE_META_READ_IRK_ENTRY); + } + + return st; +} + + +/******************************************************************************* +** +** Function btm_ble_suspend_resolving_list_activity +** +** Description This function suspends all resolving list activity, including +** scan, initiating, and advertising, if resolving list is being +** enabled. +** +** Parameters +** +** Returns TRUE if suspended; FALSE otherwise +** +*******************************************************************************/ +BOOLEAN btm_ble_suspend_resolving_list_activity(void) +{ + tBTM_BLE_CB *p_ble_cb = &btm_cb.ble_ctr_cb; + + /* if resolving list is not enabled, do not need to terminate any activity */ + /* if asking for stop all activity */ + /* if already suspended */ + if (p_ble_cb->suspended_rl_state != BTM_BLE_RL_IDLE) { + return TRUE; + } + + /* direct connection active, wait until it completed */ + if (btm_ble_get_conn_st() == BLE_DIR_CONN) { + BTM_TRACE_ERROR("resolving list can not be edited, EnQ now"); + return FALSE; + } + + p_ble_cb->suspended_rl_state = BTM_BLE_RL_IDLE; + + if (p_ble_cb->inq_var.adv_mode == BTM_BLE_ADV_ENABLE) { + btm_ble_stop_adv(); + p_ble_cb->suspended_rl_state |= BTM_BLE_RL_ADV; + } + + if (BTM_BLE_IS_SCAN_ACTIVE(p_ble_cb->scan_activity)) { + btm_ble_stop_scan(); + p_ble_cb->suspended_rl_state |= BTM_BLE_RL_SCAN; + } + + if (btm_ble_suspend_bg_conn()) { + p_ble_cb->suspended_rl_state |= BTM_BLE_RL_INIT; + } + + return TRUE; +} + +/******************************************************************************* +** +** Function btm_ble_resume_resolving_list_activity +** +** Description This function resumes the resolving list activity, including +** scanning, initiating, and advertising, if any of these +** activities has been suspended earlier. +** +** Returns none +** +*******************************************************************************/ +void btm_ble_resume_resolving_list_activity(void) +{ + tBTM_BLE_CB *p_ble_cb = &btm_cb.ble_ctr_cb; + + if (p_ble_cb->suspended_rl_state & BTM_BLE_RL_ADV) { + btm_ble_start_adv(); + } + + if (p_ble_cb->suspended_rl_state & BTM_BLE_RL_SCAN) { + btm_ble_start_scan(); + } + + if (p_ble_cb->suspended_rl_state & BTM_BLE_RL_INIT) { + btm_ble_resume_bg_conn(); + } + + p_ble_cb->suspended_rl_state = BTM_BLE_RL_IDLE; +} + +/******************************************************************************* +** +** Function btm_ble_vendor_enable_irk_feature +** +** Description This function is called to enable or disable the RRA +** offloading feature. +** +** Parameters enable: enable or disable the RRA offloading feature +** +** Returns BTM_SUCCESS if successful +** +*******************************************************************************/ +tBTM_STATUS btm_ble_vendor_enable_irk_feature(BOOLEAN enable) +{ + UINT8 param[20], *p; + tBTM_STATUS st = BTM_MODE_UNSUPPORTED; + + p = param; + memset(param, 0, 20); + + /* select feature based on control block settings */ + UINT8_TO_STREAM(p, BTM_BLE_META_IRK_ENABLE); + UINT8_TO_STREAM(p, enable ? 0x01 : 0x00); + + st = BTM_VendorSpecificCommand (HCI_VENDOR_BLE_RPA_VSC, BTM_BLE_IRK_ENABLE_LEN, + param, btm_ble_resolving_list_vsc_op_cmpl); + + return st; +} + +/******************************************************************************* +** +** Function btm_ble_exe_disable_resolving_list +** +** Description execute resolving list disable +** +** Returns none +** +*******************************************************************************/ +BOOLEAN btm_ble_exe_disable_resolving_list(void) +{ + if (!btm_ble_suspend_resolving_list_activity()) { + return FALSE; + } + + if (!controller_get_interface()->supports_ble_privacy()) { + btm_ble_vendor_enable_irk_feature(FALSE); + } else { + //btsnd_hcic_ble_set_addr_resolution_enable(FALSE); + } + + return TRUE; +} + +/******************************************************************************* +** +** Function btm_ble_exe_enable_resolving_list +** +** Description enable LE resolve address list +** +** Returns none +** +*******************************************************************************/ +void btm_ble_exe_enable_resolving_list(void) +{ + if (!btm_ble_suspend_resolving_list_activity()) { + return; + } + + if (!controller_get_interface()->supports_ble_privacy()) { + btm_ble_vendor_enable_irk_feature(TRUE); + } else { + //btsnd_hcic_ble_set_addr_resolution_enable(TRUE); + } +} + +/******************************************************************************* +** +** Function btm_ble_disable_resolving_list +** +** Description Disable LE Address resolution +** +** Returns none +** +*******************************************************************************/ +BOOLEAN btm_ble_disable_resolving_list(UINT8 rl_mask, BOOLEAN to_resume ) +{ + UINT8 rl_state = btm_cb.ble_ctr_cb.rl_state; + + /* if controller does not support RPA offloading or privacy 1.2, skip */ + if (controller_get_interface()->get_ble_resolving_list_max_size() == 0) { + return FALSE; + } + + btm_cb.ble_ctr_cb.rl_state &= ~rl_mask; + + if (rl_state != BTM_BLE_RL_IDLE && btm_cb.ble_ctr_cb.rl_state == BTM_BLE_RL_IDLE) { + if (btm_ble_exe_disable_resolving_list()) { + if (to_resume) { + btm_ble_resume_resolving_list_activity(); + } + + return TRUE; + } else { + return FALSE; + } + } + + return TRUE; +} + +/******************************************************************************* +** +** Function btm_ble_resolving_list_load_dev +** +** Description This function add a device which is using RPA into white list +** +** Parameters pointer to device security record +** +** Returns TRUE if device added, otherwise falase. +** +*******************************************************************************/ +BOOLEAN btm_ble_resolving_list_load_dev(tBTM_SEC_DEV_REC *p_dev_rec) +{ + BOOLEAN rt = FALSE; +#if (SMP_INCLUDED == TRUE) + UINT8 rl_mask = btm_cb.ble_ctr_cb.rl_state; + + BTM_TRACE_DEBUG("%s btm_cb.ble_ctr_cb.privacy_mode = %d\n", __func__, + btm_cb.ble_ctr_cb.privacy_mode); + + /* if controller does not support RPA offloading or privacy 1.2, skip */ + if (controller_get_interface()->get_ble_resolving_list_max_size() == 0) { + return FALSE; + } + + BTM_TRACE_DEBUG("%s btm_cb.ble_ctr_cb.privacy_mode = %d\n", + __func__, btm_cb.ble_ctr_cb.privacy_mode); + + /* only add RPA enabled device into resolving list */ + if (p_dev_rec != NULL && /* RPA is being used and PID is known */ + (p_dev_rec->sec_flags & BTM_SEC_IN_USE) != 0 && + ((p_dev_rec->ble.key_type & BTM_LE_KEY_PID) != 0 || + (p_dev_rec->ble.key_type & BTM_LE_KEY_LID) != 0)) { + if (!(p_dev_rec->ble.in_controller_list & BTM_RESOLVING_LIST_BIT) && + btm_ble_brcm_find_resolving_pending_entry(p_dev_rec->bd_addr, + BTM_BLE_META_ADD_IRK_ENTRY) == FALSE) { + if (btm_cb.ble_ctr_cb.resolving_list_avail_size > 0) { + if (rl_mask) { + if (!btm_ble_disable_resolving_list (rl_mask, FALSE)) { + return FALSE; + } + } + + btm_ble_update_resolving_list(p_dev_rec->bd_addr, TRUE); + if (controller_get_interface()->supports_ble_privacy()) { + BD_ADDR dummy_bda = {0}; + if (memcmp(p_dev_rec->ble.static_addr, dummy_bda, BD_ADDR_LEN) == 0) { + memcpy(p_dev_rec->ble.static_addr, p_dev_rec->bd_addr, BD_ADDR_LEN); + p_dev_rec->ble.static_addr_type = p_dev_rec->ble.ble_addr_type; + } + +#if CONTROLLER_RPA_LIST_ENABLE + BTM_TRACE_DEBUG("%s:adding device to controller resolving list\n", __func__); + UINT8 *peer_irk = p_dev_rec->ble.keys.irk; + UINT8 *local_irk = btm_cb.devcb.id_keys.irk; + //use identical IRK for now + rt = btsnd_hcic_ble_add_device_resolving_list(p_dev_rec->ble.static_addr_type, + p_dev_rec->ble.static_addr, peer_irk, local_irk); +#else + // do nothing + /* It will cause that scanner doesn't send scan request to advertiser + * which has sent IRK to us and we have stored the IRK in controller. + * It is a hardware limitation. The preliminary solution is not to + * send key to the controller, but to resolve the random address in host. */ + +#endif + + } else { + UINT8 param[40] = {0}; + UINT8 *p = param; + + UINT8_TO_STREAM(p, BTM_BLE_META_ADD_IRK_ENTRY); + ARRAY_TO_STREAM(p, p_dev_rec->ble.keys.irk, BT_OCTET16_LEN); + UINT8_TO_STREAM(p, p_dev_rec->ble.static_addr_type); + BDADDR_TO_STREAM(p, p_dev_rec->ble.static_addr); + + if (BTM_VendorSpecificCommand (HCI_VENDOR_BLE_RPA_VSC, + BTM_BLE_META_ADD_IRK_LEN, + param, + btm_ble_resolving_list_vsc_op_cmpl) + == BTM_CMD_STARTED) { + rt = TRUE; + } + } + + if (rt) { + btm_ble_enq_resolving_list_pending(p_dev_rec->bd_addr, + BTM_BLE_META_ADD_IRK_ENTRY); + } + + /* if resolving list has been turned on, re-enable it */ + // if (rl_mask) { + // btm_ble_enable_resolving_list(rl_mask); + // } else { + // btm_ble_enable_resolving_list(BTM_BLE_RL_INIT); + // } + } else { + BTM_TRACE_WARNING("%s Resolving list full ", __func__); + } + } else { + BTM_TRACE_DEBUG("Device already in Resolving list\n"); + rt = TRUE; + } + } else { + BTM_TRACE_DEBUG("Device not a RPA enabled device\n"); + } +#endif ///SMP_INCLUDED == TRUE + return rt; +} + +/******************************************************************************* +** +** Function btm_ble_resolving_list_remove_dev +** +** Description This function removes the device from resolving list +** +** Parameters +** +** Returns status +** +*******************************************************************************/ +void btm_ble_resolving_list_remove_dev(tBTM_SEC_DEV_REC *p_dev_rec) +{ + UINT8 rl_mask = btm_cb.ble_ctr_cb.rl_state; + + BTM_TRACE_EVENT ("%s\n", __func__); + if (rl_mask) { + if (!btm_ble_disable_resolving_list (rl_mask, FALSE)) { + return; + } + } + + if ((p_dev_rec->ble.in_controller_list & BTM_RESOLVING_LIST_BIT) && + btm_ble_brcm_find_resolving_pending_entry(p_dev_rec->bd_addr, + BTM_BLE_META_REMOVE_IRK_ENTRY) == FALSE) { + btm_ble_update_resolving_list( p_dev_rec->bd_addr, FALSE); + btm_ble_remove_resolving_list_entry(p_dev_rec); + } else { + BTM_TRACE_DEBUG("Device not in resolving list\n"); + } + + /* if resolving list has been turned on, re-enable it */ + // if (rl_mask) { + // btm_ble_enable_resolving_list(rl_mask); + // } +} + +/******************************************************************************* +** +** Function btm_ble_enable_resolving_list +** +** Description enable LE resolve address list +** +** Returns none +** +*******************************************************************************/ +void btm_ble_enable_resolving_list(UINT8 rl_mask) +{ + UINT8 rl_state = btm_cb.ble_ctr_cb.rl_state; + + btm_cb.ble_ctr_cb.rl_state |= rl_mask; + if (rl_state == BTM_BLE_RL_IDLE && + btm_cb.ble_ctr_cb.rl_state != BTM_BLE_RL_IDLE && + controller_get_interface()->get_ble_resolving_list_max_size() != 0) { + btm_ble_exe_enable_resolving_list(); + btm_ble_resume_resolving_list_activity(); + } +} + +#if 0 //Unused +/******************************************************************************* +** +** Function btm_ble_resolving_list_empty +** +** Description check to see if resoving list is empty or not +** +** Returns TRUE: empty; FALSE non-empty +** +*******************************************************************************/ +BOOLEAN btm_ble_resolving_list_empty(void) +{ + return (controller_get_interface()->get_ble_resolving_list_max_size() == + btm_cb.ble_ctr_cb.resolving_list_avail_size); +} +#endif + +/******************************************************************************* +** +** Function btm_ble_enable_resolving_list_for_platform +** +** Description enable/disable resolving list feature depending on if any +** resolving list is empty and whitelist is involoved in the +** operation. +** +** Returns none +** +*******************************************************************************/ +void btm_ble_enable_resolving_list_for_platform (UINT8 rl_mask) +{ + /* if controller does not support, skip */ + if (controller_get_interface()->get_ble_resolving_list_max_size() == 0) { + return; + } + + if (btm_cb.ble_ctr_cb.wl_state == BTM_BLE_WL_IDLE) { + if (controller_get_interface()->get_ble_resolving_list_max_size() > + btm_cb.ble_ctr_cb.resolving_list_avail_size) { + btm_ble_enable_resolving_list(rl_mask); + } else { + btm_ble_disable_resolving_list(rl_mask, TRUE); + } + return; + } + + tBTM_SEC_DEV_REC *p_dev = NULL; + list_node_t *p_node = NULL; + for (p_node = list_begin(btm_cb.p_sec_dev_rec_list); p_node; p_node = list_next(p_node)) { + p_dev = list_node(p_node); + if ((p_dev->ble.in_controller_list & BTM_RESOLVING_LIST_BIT) && + (p_dev->ble.in_controller_list & BTM_WHITE_LIST_BIT)) { + btm_ble_enable_resolving_list(rl_mask); + return; + } + } + btm_ble_disable_resolving_list(rl_mask, TRUE); +} + +/******************************************************************************* +** +** Function btm_ble_resolving_list_init +** +** Description Initialize resolving list in host stack +** +** Parameters Max resolving list size +** +** Returns void +** +*******************************************************************************/ +void btm_ble_resolving_list_init(UINT8 max_irk_list_sz) +{ + tBTM_BLE_RESOLVE_Q *p_q = &btm_cb.ble_ctr_cb.resolving_list_pend_q; + UINT8 irk_mask_size = (max_irk_list_sz % 8) ? + (max_irk_list_sz / 8 + 1) : (max_irk_list_sz / 8); + + if (max_irk_list_sz > 0) { + p_q->resolve_q_random_pseudo = (BD_ADDR *)osi_malloc(sizeof(BD_ADDR) * max_irk_list_sz); + p_q->resolve_q_action = (UINT8 *)osi_malloc(max_irk_list_sz); + + /* RPA offloading feature */ + if (btm_cb.ble_ctr_cb.irk_list_mask == NULL) { + btm_cb.ble_ctr_cb.irk_list_mask = (UINT8 *)osi_malloc(irk_mask_size); + } + + BTM_TRACE_DEBUG ("%s max_irk_list_sz = %d", __func__, max_irk_list_sz); + } + + controller_get_interface()->set_ble_resolving_list_max_size(max_irk_list_sz); + btm_ble_clear_resolving_list(); + btm_cb.ble_ctr_cb.resolving_list_avail_size = max_irk_list_sz; +} + +/******************************************************************************* +** +** Function btm_ble_resolving_list_cleanup +** +** Description Cleanup resolving list dynamic memory +** +** Parameters +** +** Returns void +** +*******************************************************************************/ +void btm_ble_resolving_list_cleanup(void) +{ + tBTM_BLE_RESOLVE_Q *p_q = &btm_cb.ble_ctr_cb.resolving_list_pend_q; + + if (p_q->resolve_q_random_pseudo) { + osi_free(p_q->resolve_q_random_pseudo); + p_q->resolve_q_random_pseudo = NULL; + } + + if (p_q->resolve_q_action) { + osi_free(p_q->resolve_q_action); + p_q->resolve_q_action = NULL; + } + + controller_get_interface()->set_ble_resolving_list_max_size(0); + if (btm_cb.ble_ctr_cb.irk_list_mask) { + osi_free(btm_cb.ble_ctr_cb.irk_list_mask); + btm_cb.ble_ctr_cb.irk_list_mask = NULL; + } + +} + +void btm_ble_add_default_entry_to_resolving_list(void) +{ + /* + * Add local IRK entry with 00:00:00:00:00:00 address. This entry will + * be used to generate RPA for non-directed advertising if own_addr_type + * is set to rpa_pub since we use all-zero address as peer addres in + * such case. Peer IRK should be left all-zero since this is not for an + * actual peer. + */ + BD_ADDR peer_addr = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0}; + BT_OCTET16 peer_irk = {0x0}; + + btsnd_hcic_ble_add_device_resolving_list (BLE_ADDR_PUBLIC, peer_addr, peer_irk, btm_cb.devcb.id_keys.irk); +} +#endif diff --git a/lib/bt/host/bluedroid/stack/btm/btm_dev.c b/lib/bt/host/bluedroid/stack/btm/btm_dev.c new file mode 100644 index 00000000..f9e3ed2b --- /dev/null +++ b/lib/bt/host/bluedroid/stack/btm/btm_dev.c @@ -0,0 +1,741 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains functions for the Bluetooth Device Manager + * + ******************************************************************************/ + +#include <stdlib.h> +#include <string.h> +//#include <stdio.h> +#include <stddef.h> + +#include "stack/bt_types.h" +#include "device/controller.h" +#include "stack/hcimsgs.h" +#include "stack/btu.h" +#include "stack/btm_api.h" +#include "btm_int.h" +#include "stack/hcidefs.h" +#include "stack/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 features (up to 3 pages). 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, + UINT8 *features, UINT32 trusted_mask[], + LINK_KEY link_key, UINT8 key_type, tBTM_IO_CAP io_cap, + UINT8 pin_length, UINT8 sc_support) +{ +#if (SMP_INCLUDED == TRUE) + tBTM_SEC_DEV_REC *p_dev_rec; + int i, j; + BOOLEAN found = FALSE; + + BTM_TRACE_API("%s, link key type:%x\n", __FUNCTION__, key_type); + 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. */ + if (list_length(btm_cb.p_sec_dev_rec_list) < BTM_SEC_MAX_DEVICE_RECORDS) { + p_dev_rec = (tBTM_SEC_DEV_REC *)osi_malloc(sizeof(tBTM_SEC_DEV_REC)); + if(p_dev_rec) { + list_append(btm_cb.p_sec_dev_rec_list, p_dev_rec); + /* 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, BT_TRANSPORT_BR_EDR); + p_dev_rec->ble_hci_handle = BTM_GetHCIConnHandle (bd_addr, BT_TRANSPORT_LE); + p_dev_rec->enc_mode = BTM_ENC_MODE_UNKNOWN; + +#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 + } + } + + if (!p_dev_rec) { + return (FALSE); + } + } + p_dev_rec->bond_type = BOND_TYPE_UNKNOWN; /* Default value */ + p_dev_rec->timestamp = btm_cb.dev_rec_count++; + p_dev_rec->remote_secure_connection_previous_state = sc_support; + + 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, (char *)bd_name, BTM_MAX_REM_BD_NAME_LEN); + } + + p_dev_rec->num_read_pages = 0; + if (features) { + memcpy (p_dev_rec->features, features, sizeof (p_dev_rec->features)); + for (i = HCI_EXT_FEATURES_PAGE_MAX; i >= 0; i--) { + for (j = 0; j < HCI_FEATURE_BYTES_PER_PAGE; j++) { + if (p_dev_rec->features[i][j] != 0) { + found = TRUE; + break; + } + } + if (found) { + p_dev_rec->num_read_pages = i + 1; + break; + } + } + } else { + memset (p_dev_rec->features, 0, sizeof (p_dev_rec->features)); + } + + BTM_SEC_COPY_TRUSTED_DEVICE(trusted_mask, p_dev_rec->trusted_mask); + + if (link_key) { + BTM_TRACE_EVENT ("BTM_SecAddDevice() BDA: %02x:%02x:%02x:%02x:%02x:%02x\n", + 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; + p_dev_rec->pin_code_length = pin_length; + + if (pin_length >= 16 || + key_type == BTM_LKEY_TYPE_AUTH_COMB || + key_type == BTM_LKEY_TYPE_AUTH_COMB_P_256) { + // Set the fiag if the link key was made by using either a 16 digit + // pin or MITM. + p_dev_rec->sec_flags |= BTM_SEC_16_DIGIT_PIN_AUTHED; + } + } + +#if defined(BTIF_MIXED_MODE_INCLUDED) && (BTIF_MIXED_MODE_INCLUDED == TRUE) + if (key_type < BTM_MAX_PRE_SM4_LKEY_TYPE) { + p_dev_rec->sm4 = BTM_SM4_KNOWN; + } else { + p_dev_rec->sm4 = BTM_SM4_TRUE; + } +#endif + + p_dev_rec->rmt_io_caps = io_cap; + p_dev_rec->device_type |= BT_DEVICE_TYPE_BREDR; +#endif ///SMP_INCLUDED == TRUE + return (TRUE); +} + + +/******************************************************************************* +** +** Function BTM_SecDeleteDevice +** +** Description Free resources associated with the device. +** +** Parameters: bd_addr - BD address of the peer +** transport - BT_TRANSPORT_BR_EDR or BT_TRANSPORT_LE +** +** Returns TRUE if removed OK, FALSE if not found or ACL link is active +** +*******************************************************************************/ +BOOLEAN BTM_SecDeleteDevice (BD_ADDR bd_addr, tBT_TRANSPORT transport) +{ + + tBTM_SEC_DEV_REC *p_dev_rec; + + if (BTM_IsAclConnectionUp(bd_addr, transport)) { + BTM_TRACE_WARNING("%s FAILED: Cannot Delete when connection is active\n", __func__); + return FALSE; + } + if ((p_dev_rec = btm_find_dev(bd_addr)) != NULL) { + /* Tell controller to get rid of the link key, if it has one stored */ + BTM_DeleteStoredLinkKey (p_dev_rec->bd_addr, NULL); + + btm_sec_free_dev(p_dev_rec, transport); + } + + return TRUE; +} + +/******************************************************************************* +** +** Function BTM_SecClearSecurityFlags +** +** Description Reset the security flags (mark as not-paired) for a given +** remove device. +** +*******************************************************************************/ +extern void BTM_SecClearSecurityFlags (BD_ADDR bd_addr) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev(bd_addr); + if (p_dev_rec == NULL) { + return; + } + + p_dev_rec->sec_flags = 0; + p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; + p_dev_rec->sm4 = BTM_SM4_UNKNOWN; +} + + +/******************************************************************************* +** +** 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_find_sec_dev_in_list +** +** Description Look for the record in the device database for the record +** with specified address +** +** Returns Pointer to the record or NULL +** +*******************************************************************************/ +BOOLEAN btm_find_sec_dev_in_list (void *p_node_data, void *context) +{ + tBTM_SEC_DEV_REC *p_sec_dev = (tBTM_SEC_DEV_REC *)p_node_data; + BOOLEAN ret = TRUE; + BOOLEAN dev_free = !(p_sec_dev->sec_flags & BTM_SEC_IN_USE); + tSecDevContext *p_context = (tSecDevContext *)context; + + if (dev_free == p_context->free_check) { + switch (p_context->type) { + case SEC_DEV_BDA: + if (!memcmp(p_context->context.p_bd_addr, p_sec_dev->bd_addr, BD_ADDR_LEN)) { + ret = FALSE; + } + break; + case SEC_DEV_HDL: + if (p_context->context.handle == p_sec_dev->hci_handle +#if BLE_INCLUDED == TRUE + || (p_context->context.handle == p_sec_dev->ble_hci_handle) +#endif + ) { + ret = FALSE; + } + break; +#if BLE_PRIVACY_SPT == TRUE + case SEC_DEV_ID_ADDR: + if (!memcmp(p_context->context.p_bd_addr, p_sec_dev->ble.static_addr, BD_ADDR_LEN)) { + ret = FALSE; + } + break; +#endif //BLE_PRIVACY_SPT == TRUE + case SEC_DEV_BTDM_BDA: + if (!memcmp(p_context->context.p_bd_addr, p_sec_dev->bd_addr, BD_ADDR_LEN)) { + ret = FALSE; + } +#if BLE_INCLUDED == TRUE + // If a LE random address is looking for device record + if (!memcmp(p_sec_dev->ble.pseudo_addr, p_context->context.p_bd_addr, BD_ADDR_LEN)) { + ret = FALSE; + } + + if (btm_ble_addr_resolvable(p_context->context.p_bd_addr, p_sec_dev)) { + ret = FALSE; + } +#endif + break; + default: + break; + } + } + return ret; +} + +/******************************************************************************* +** +** Function btm_sec_alloc_dev +** +** Description Look for the record in the device database for the record +** with specified address +** +** 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_SEC_DEV_REC *p_dev_new_rec = NULL; + tBTM_SEC_DEV_REC *p_dev_old_rec = NULL; + tBTM_INQ_INFO *p_inq_info; + list_node_t *p_node = NULL; + BOOLEAN new_entry_found = FALSE; + BOOLEAN old_entry_found = FALSE; + BOOLEAN malloc_new_entry = FALSE; + BTM_TRACE_EVENT ("btm_sec_alloc_dev\n"); + for (p_node = list_begin(btm_cb.p_sec_dev_rec_list); p_node; p_node = list_next(p_node)) { + p_dev_old_rec = list_node(p_node); + /* look for old entry which match the bd_addr and the BTM_SEC_IN_USE is cleared */ + if (!(p_dev_old_rec->sec_flags & BTM_SEC_IN_USE) && + (!memcmp (p_dev_old_rec->bd_addr, bd_addr, BD_ADDR_LEN))) { + old_entry_found = TRUE; + BTM_TRACE_EVENT ("btm_sec_alloc_dev old device found\n"); + break; + } + } + for (p_node = list_begin(btm_cb.p_sec_dev_rec_list); p_node; p_node = list_next(p_node)) { + p_dev_new_rec = list_node(p_node); + /* find the first entry whose BTM_SEC_IN_USE is cleared */ + if (!(p_dev_new_rec->sec_flags & BTM_SEC_IN_USE)) { + new_entry_found = TRUE; + break; + } + } + if (!new_entry_found) { + /* We can not find new device. We need malloc a new one if p_sec_dev_rec_list is not full */ + if (list_length(btm_cb.p_sec_dev_rec_list) < BTM_SEC_MAX_DEVICE_RECORDS){ + p_dev_new_rec = (tBTM_SEC_DEV_REC *)osi_malloc(sizeof(tBTM_SEC_DEV_REC)); + if (p_dev_new_rec) { + new_entry_found = TRUE; + malloc_new_entry = TRUE; + } else { + return NULL; + } + } + } + if (!new_entry_found) { + p_dev_rec = btm_find_oldest_dev(); + } else { + /* if the old device entry not present go with new entry */ + if (old_entry_found) { + p_dev_rec = p_dev_old_rec; + if (malloc_new_entry) { + osi_free(p_dev_new_rec); + } + } else { + if (malloc_new_entry) { + list_append(btm_cb.p_sec_dev_rec_list, p_dev_new_rec); + } + p_dev_rec = p_dev_new_rec; + } + } + memset (p_dev_rec, 0, sizeof (tBTM_SEC_DEV_REC)); + + p_dev_rec->bond_type = BOND_TYPE_UNKNOWN; /* Default value */ + 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 + } else { +#if BLE_INCLUDED == TRUE + /* 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); + +#if BLE_INCLUDED == TRUE + p_dev_rec->ble_hci_handle = BTM_GetHCIConnHandle (bd_addr, BT_TRANSPORT_LE); +#endif + p_dev_rec->hci_handle = BTM_GetHCIConnHandle (bd_addr, BT_TRANSPORT_BR_EDR); + 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, tBT_TRANSPORT transport) +{ + if (transport == BT_TRANSPORT_BR_EDR) { + memset(p_dev_rec->link_key, 0, LINK_KEY_LEN); + p_dev_rec->sec_flags &= ~(BTM_SEC_AUTHORIZED | BTM_SEC_AUTHENTICATED + | BTM_SEC_ENCRYPTED | BTM_SEC_NAME_KNOWN + | BTM_SEC_LINK_KEY_KNOWN | BTM_SEC_LINK_KEY_AUTHED + | BTM_SEC_ROLE_SWITCHED | BTM_SEC_16_DIGIT_PIN_AUTHED); + } else if (transport == BT_TRANSPORT_LE) { + p_dev_rec->bond_type = BOND_TYPE_UNKNOWN; + p_dev_rec->sec_flags &= ~(BTM_SEC_LE_AUTHENTICATED | BTM_SEC_LE_ENCRYPTED + | BTM_SEC_LE_NAME_KNOWN | BTM_SEC_LE_LINK_KEY_KNOWN + | BTM_SEC_LE_LINK_KEY_AUTHED | BTM_SEC_ROLE_SWITCHED); +#if BLE_INCLUDED == TRUE + /* Clear out any saved BLE keys */ + btm_sec_clear_ble_keys (p_dev_rec); +#endif + } else { + p_dev_rec->bond_type = BOND_TYPE_UNKNOWN; + memset(p_dev_rec->link_key, 0, LINK_KEY_LEN); + 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 + } + /* No BLE keys and BT keys, clear the sec_flags */ + if(p_dev_rec->sec_flags == BTM_SEC_IN_USE) { + p_dev_rec->sec_flags = 0; + } + list_remove(btm_cb.p_sec_dev_rec_list, p_dev_rec); +} + +/******************************************************************************* +** +** 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 && controller_get_interface()->supports_master_slave_role_switch()) { + if (HCI_SWITCH_SUPPORTED(p_dev_rec->features[HCI_EXT_FEATURES_PAGE_0])) { + BTM_TRACE_DEBUG("btm_dev_support_switch return TRUE (feature found)\n"); + 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[HCI_EXT_FEATURES_PAGE_0][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_DEBUG("btm_dev_support_switch return TRUE (feature empty)\n"); + return (TRUE); + } + } + + BTM_TRACE_DEBUG("btm_dev_support_switch return FALSE\n"); + 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 = NULL; + list_node_t *p_node = NULL; + tSecDevContext context; + context.type = SEC_DEV_HDL; + context.context.handle = handle; + context.free_check = FALSE; + + p_node = list_foreach(btm_cb.p_sec_dev_rec_list, btm_find_sec_dev_in_list, &context); + if (p_node) { + p_dev_rec = list_node(p_node); + } + return (p_dev_rec); +} +/******************************************************************************* +** +** 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) +{ + if(bd_addr) { + list_node_t *p_node = NULL; + tSecDevContext context; + context.type = SEC_DEV_BTDM_BDA; + context.context.p_bd_addr = bd_addr; + context.free_check = FALSE; + p_node = list_foreach(btm_cb.p_sec_dev_rec_list, btm_find_sec_dev_in_list, &context); + if (p_node) { + return(list_node(p_node)); + } + } + return (NULL); +} +/******************************************************************************* +** +** Function btm_consolidate_dev +** +** Description combine security records if identified as same peer +** +** Returns none +** +*******************************************************************************/ +void btm_consolidate_dev(tBTM_SEC_DEV_REC *p_target_rec) +{ +#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE + tBTM_SEC_DEV_REC *p_dev_rec = NULL; + tBTM_SEC_DEV_REC temp_rec = *p_target_rec; + list_node_t *p_node = NULL; + BTM_TRACE_DEBUG("%s\n", __func__); + + for (p_node = list_begin(btm_cb.p_sec_dev_rec_list); p_node; p_node = list_next(p_node)) { + p_dev_rec = list_node(p_node); + if (p_target_rec != p_dev_rec && p_dev_rec->sec_flags & BTM_SEC_IN_USE) { + if (!memcmp (p_dev_rec->bd_addr, p_target_rec->bd_addr, BD_ADDR_LEN)) { + memcpy(p_target_rec, p_dev_rec, sizeof(tBTM_SEC_DEV_REC)); + p_target_rec->ble = temp_rec.ble; + p_target_rec->ble_hci_handle = temp_rec.ble_hci_handle; + p_target_rec->enc_key_size = temp_rec.enc_key_size; + p_target_rec->conn_params = temp_rec.conn_params; + p_target_rec->device_type |= temp_rec.device_type; + p_target_rec->sec_flags |= temp_rec.sec_flags; + + p_target_rec->new_encryption_key_is_p256 = temp_rec.new_encryption_key_is_p256; + p_target_rec->no_smp_on_br = temp_rec.no_smp_on_br; + p_target_rec->bond_type = temp_rec.bond_type; + /* Remove the unused device from the list */ + list_remove(btm_cb.p_sec_dev_rec_list, p_dev_rec); + break; + } + + /* an RPA device entry is a duplicate of the target record */ + if (btm_ble_addr_resolvable(p_dev_rec->bd_addr, p_target_rec)) { + if (memcmp(p_target_rec->ble.pseudo_addr, p_dev_rec->bd_addr, BD_ADDR_LEN) == 0) { + p_target_rec->ble.ble_addr_type = p_dev_rec->ble.ble_addr_type; + p_target_rec->device_type |= p_dev_rec->device_type; + /* Remove the unused device from the list */ + list_remove(btm_cb.p_sec_dev_rec_list, p_dev_rec); + } + break; + } + } + } +#endif +} + +/******************************************************************************* +** +** 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_EVENT ("btm_find_or_alloc_dev\n"); + 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 = NULL; + tBTM_SEC_DEV_REC *p_oldest = NULL; + list_node_t *p_node = NULL; + UINT32 ot = 0xFFFFFFFF; + + /* First look for the non-paired devices for the oldest entry */ + for (p_node = list_begin(btm_cb.p_sec_dev_rec_list); p_node; p_node = list_next(p_node)) { + p_dev_rec = list_node(p_node); + if (((p_dev_rec->sec_flags & BTM_SEC_IN_USE) == 0) + || ((p_dev_rec->sec_flags & (BTM_SEC_LINK_KEY_KNOWN | BTM_SEC_LE_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 */ + for (p_node = list_begin(btm_cb.p_sec_dev_rec_list); p_node; p_node = list_next(p_node)) { + 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); +} +/******************************************************************************* +** +** Function btm_get_bond_type_dev +** +** Description Get the bond type for a device in the device database +** with specified BD address +** +** Returns The device bond type if known, otherwise BOND_TYPE_UNKNOWN +** +*******************************************************************************/ +tBTM_BOND_TYPE btm_get_bond_type_dev(BD_ADDR bd_addr) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev(bd_addr); + + if (p_dev_rec == NULL) { + return BOND_TYPE_UNKNOWN; + } + + return p_dev_rec->bond_type; +} + +/******************************************************************************* +** +** Function btm_set_bond_type_dev +** +** Description Set the bond type for a device in the device database +** with specified BD address +** +** Returns TRUE on success, otherwise FALSE +** +*******************************************************************************/ +BOOLEAN btm_set_bond_type_dev(BD_ADDR bd_addr, tBTM_BOND_TYPE bond_type) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev(bd_addr); + + if (p_dev_rec == NULL) { + return FALSE; + } + + p_dev_rec->bond_type = bond_type; + return TRUE; +} + +/******************************************************************************* +** +** Function btm_sec_dev_init +** +** Description Create new linked list for dynamic allocation on sec_dev_rec +** +*******************************************************************************/ +void btm_sec_dev_init(void) +{ + btm_cb.p_sec_dev_rec_list = list_new(osi_free_func); +} + +/******************************************************************************* +** +** Function btm_sec_dev_free +** +** Description Delete sec_dev_rec list when btm_cb is being released +** +*******************************************************************************/ +void btm_sec_dev_free(void) +{ + list_free(btm_cb.p_sec_dev_rec_list); +} diff --git a/lib/bt/host/bluedroid/stack/btm/btm_devctl.c b/lib/bt/host/bluedroid/stack/btm/btm_devctl.c new file mode 100644 index 00000000..bffb5bbe --- /dev/null +++ b/lib/bt/host/bluedroid/stack/btm/btm_devctl.c @@ -0,0 +1,1294 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains functions that handle BTM interface functions for the + * Bluetooth device including Rest, HCI buffer size and others + * + ******************************************************************************/ + +#include <stdlib.h> +#include <string.h> +//#include <stdio.h> +#include <stddef.h> +#include "common/bt_trace.h" +#include "stack/bt_types.h" +//#include "bt_utils.h" +#include "btm_int.h" +#include "stack/btu.h" +#include "device/controller.h" +#include "hci/hci_layer.h" +#include "stack/hcimsgs.h" +#include "l2c_int.h" +//#include "btcore/include/module.h" +//#include "osi/include/osi/thread.h" + +#if BLE_INCLUDED == TRUE +#include "gatt_int.h" +#endif /* BLE_INCLUDED */ + +//extern thread_t *bt_workqueue_thread; + +/********************************************************************************/ +/* 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 */ + +/********************************************************************************/ +/* 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_decode_ext_features_page (UINT8 page_number, const BD_FEATURES p_features); + +/******************************************************************************* +** +** 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)); +#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.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; +} + + +/******************************************************************************* +** +** 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_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); + } + } +} + +static void reset_complete(void) +{ + const controller_t *controller = controller_get_interface(); + + /* Tell L2CAP that all connections are gone */ + l2cu_device_reset (); +#if (SMP_INCLUDED == TRUE) + /* Clear current security state */ + { + list_node_t *p_node = NULL; + for (p_node = list_begin(btm_cb.p_sec_dev_rec_list); p_node; p_node = list_next(p_node)) { + tBTM_SEC_DEV_REC *p_dev_rec = (tBTM_SEC_DEV_REC *) list_node(p_node); + p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; + } + } +#endif ///SMP_INCLUDED == TRUE + /* 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; + btm_cb.btm_inq_vars.page_timeout = HCI_DEFAULT_PAGE_TOUT; + +#if (BLE_INCLUDED == TRUE) + btm_cb.ble_ctr_cb.conn_state = BLE_CONN_IDLE; + btm_cb.ble_ctr_cb.bg_conn_type = BTM_BLE_CONN_NONE; + btm_cb.ble_ctr_cb.p_select_cback = NULL; + gatt_reset_bgdev_list(); + btm_ble_multi_adv_init(); +#endif + + btm_pm_reset(); + + l2c_link_processs_num_bufs(controller->get_acl_buffer_count_classic()); +#if BTM_SCO_HCI_INCLUDED == TRUE + btm_sco_process_num_bufs(controller->get_sco_buffer_count()); +#endif +#if (BLE_INCLUDED == TRUE) + +#if (defined BLE_PRIVACY_SPT && BLE_PRIVACY_SPT == TRUE) + /* Set up the BLE privacy settings */ + if (controller->supports_ble() && controller->supports_ble_privacy() && + controller->get_ble_resolving_list_max_size() > 0) { + btm_ble_resolving_list_init(controller->get_ble_resolving_list_max_size()); + /* set the default random private address timeout */ + btsnd_hcic_ble_set_rand_priv_addr_timeout(BTM_BLE_PRIVATE_ADDR_INT); + } +#endif + + if (controller->supports_ble()) { + btm_ble_white_list_init(controller->get_ble_white_list_size()); + l2c_link_processs_ble_num_bufs(controller->get_acl_buffer_count_ble()); + } +#endif +#if (SMP_INCLUDED == TRUE && CLASSIC_BT_INCLUDED == TRUE) + BTM_SetPinType (btm_cb.cfg.pin_type, btm_cb.cfg.pin_code, btm_cb.cfg.pin_code_len); +#endif ///SMP_INCLUDED == TRUE && CLASSIC_BT_INCLUDED == TRUE +#if (CLASSIC_BT_INCLUDED == TRUE) + BTM_WritePageTimeout(btm_cb.btm_inq_vars.page_timeout, NULL); +#endif ///CLASSIC_BT_INCLUDED == TRUE + for (int i = 0; i <= controller->get_last_features_classic_index(); i++) { + btm_decode_ext_features_page(i, controller->get_features_classic(i)->as_array); + } + + btm_report_device_status(BTM_DEV_STATUS_UP); +} + +// TODO(zachoverflow): remove this function +void BTM_DeviceReset (UNUSED_ATTR tBTM_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(); + + /* todo: review the below logic; start_up executes under another task context + * reset_complete runs in btu task */ + controller_get_interface()->start_up(); + reset_complete(); +} + +/******************************************************************************* +** +** 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 controller_get_interface()->get_is_ready(); +} + +/******************************************************************************* +** +** 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 == (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_decode_ext_features_page +** +** Description This function is decodes a features page. +** +** Returns void +** +*******************************************************************************/ +static void btm_decode_ext_features_page (UINT8 page_number, const BD_FEATURES p_features) +{ + BTM_TRACE_DEBUG ("btm_decode_ext_features_page page: %d", page_number); + switch (page_number) { + /* Extended (Legacy) Page 0 */ + case HCI_EXT_FEATURES_PAGE_0: + + /* 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_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_features)) { + btm_cb.btm_acl_pkt_types_supported |= (BTM_ACL_PKT_TYPES_MASK_DH5 + + BTM_ACL_PKT_TYPES_MASK_DM5); + } + + /* Add in EDR related ACL types */ + if (!HCI_EDR_ACL_2MPS_SUPPORTED(p_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_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_features) || + HCI_EDR_ACL_3MPS_SUPPORTED(p_features)) { + if (!HCI_3_SLOT_EDR_ACL_SUPPORTED(p_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_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_DEBUG("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_features)) { + btm_cb.btm_sco_pkt_types_supported = BTM_SCO_PKT_TYPES_MASK_HV1; + + if (HCI_HV2_PACKETS_SUPPORTED(p_features)) { + btm_cb.btm_sco_pkt_types_supported |= BTM_SCO_PKT_TYPES_MASK_HV2; + } + + if (HCI_HV3_PACKETS_SUPPORTED(p_features)) { + btm_cb.btm_sco_pkt_types_supported |= BTM_SCO_PKT_TYPES_MASK_HV3; + } + } + + if (HCI_ESCO_EV3_SUPPORTED(p_features)) { + btm_cb.btm_sco_pkt_types_supported |= BTM_SCO_PKT_TYPES_MASK_EV3; + } + + if (HCI_ESCO_EV4_SUPPORTED(p_features)) { + btm_cb.btm_sco_pkt_types_supported |= BTM_SCO_PKT_TYPES_MASK_EV4; + } + + if (HCI_ESCO_EV5_SUPPORTED(p_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_features)) { + if (!HCI_3_SLOT_EDR_ESCO_SUPPORTED(p_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_features)) { + if (!HCI_3_SLOT_EDR_ESCO_SUPPORTED(p_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_DEBUG("Local supported SCO packet types: 0x%04x", + btm_cb.btm_sco_pkt_types_supported); + + /* Create Default Policy Settings */ + if (HCI_SWITCH_SUPPORTED(p_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_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_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_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 (HCI_LMP_INQ_RSSI_SUPPORTED(p_features)) { + if (HCI_EXT_INQ_RSP_SUPPORTED(p_features)) { + BTM_SetInquiryMode (BTM_INQ_RESULT_EXTENDED); + } else { + BTM_SetInquiryMode (BTM_INQ_RESULT_WITH_RSSI); + } + } + +#if L2CAP_NON_FLUSHABLE_PB_INCLUDED == TRUE + if ( HCI_NON_FLUSHABLE_PB_SUPPORTED(p_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); + + break; + + /* Extended Page 1 */ + case HCI_EXT_FEATURES_PAGE_1: + /* Nothing to do for page 1 */ + break; + + /* Extended Page 2 */ + case HCI_EXT_FEATURES_PAGE_2: + /* Nothing to do for page 2 */ + break; + + default: + BTM_TRACE_ERROR("btm_decode_ext_features_page page=%d unknown", page_number); + break; + } +} + +/******************************************************************************* +** +** 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 (!p_name || !p_name[0] || (strlen ((char *)p_name) > BD_NAME_LEN)) { + return (BTM_ILLEGAL_VALUE); + } + + if (!controller_get_interface()->get_is_ready()) { + 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, 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 CLASSIC_BT_INCLUDED + if (btsnd_hcic_change_name(p)) { + return (BTM_CMD_STARTED); + } else +#endif + { + 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; + UNUSED(evt_len); + + btu_free_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_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 (!controller_get_interface()->get_is_ready()) { + 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 +** +*******************************************************************************/ +// TODO(zachoverflow): get rid of this function +UINT8 *BTM_ReadLocalFeatures (void) +{ + // Discarding const modifier for now, until this function dies + return (UINT8 *)controller_get_interface()->get_features_classic(0)->as_array; +} + +/******************************************************************************* +** +** 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) +{ + BT_HDR *p_buf; + + BTM_TRACE_EVENT ("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 = HCI_GET_CMD_BUF(param_len)) != 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); + } + +} + +#if (ESP_COEX_VSC_INCLUDED == TRUE) +tBTM_STATUS BTM_ConfigCoexStatus(tBTM_COEX_OPERATION op, tBTM_COEX_TYPE type, UINT8 status) +{ + UINT8 param[3]; + UINT8 *p = (UINT8 *)param; + + UINT8_TO_STREAM(p, type); + UINT8_TO_STREAM(p, op); + UINT8_TO_STREAM(p, status); + + return BTM_VendorSpecificCommand(HCI_VENDOR_COMMON_COEX_STATUS_CMD_OPCODE, 3, param, NULL); +} +#endif + +/******************************************************************************* +** +** 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) +{ +#if (BLE_INCLUDED == TRUE) + tBTM_BLE_CB *ble_cb = &btm_cb.ble_ctr_cb; + switch(opcode) { + case HCI_VENDOR_BLE_LONG_ADV_DATA: + BTM_TRACE_EVENT("Set long adv data complete\n"); + break; + case HCI_VENDOR_BLE_UPDATE_DUPLICATE_EXCEPTIONAL_LIST: { + uint8_t subcode, status; uint32_t length; + STREAM_TO_UINT8(status, p); + STREAM_TO_UINT8(subcode, p); + STREAM_TO_UINT32(length, p); + if(ble_cb && ble_cb->update_exceptional_list_cmp_cb) { + (*ble_cb->update_exceptional_list_cmp_cb)(status, subcode, length, p); + } + break; + } + case HCI_VENDOR_BLE_CLEAR_ADV: { + uint8_t status; + STREAM_TO_UINT8(status, p); + if (ble_cb && ble_cb->inq_var.p_clear_adv_cb) { + ble_cb->inq_var.p_clear_adv_cb(status); + } + break; + } + default: + break; + } + 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 */ + } +#endif +} + +/******************************************************************************* +** +** 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_EVENT("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_EVENT("BTM Register For VSEvents is successfully"); + } else { + /* No free entries available */ + BTM_TRACE_ERROR ("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; + +#if (CLASSIC_BT_INCLUDED == TRUE) + UINT8 sub_event; + UINT8 *p_evt = p; + + STREAM_TO_UINT8(sub_event, p_evt); + /* Check in subevent if authentication is through Legacy Authentication. */ + if (sub_event == ESP_VS_REM_LEGACY_AUTH_CMP) { + UINT16 hci_handle; + STREAM_TO_UINT16(hci_handle, p_evt); + btm_sec_handle_remote_legacy_auth_cmp(hci_handle); + } +#endif /// (CLASSIC_BT_INCLUDED == TRUE) + 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); + } + } + BTM_TRACE_DEBUG ("BTM Event: Vendor Specific event from controller"); +} + +#if (CLASSIC_BT_INCLUDED == TRUE) +/******************************************************************************* +** +** 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, tBTM_CMPL_CB *p_cb) +{ + BTM_TRACE_EVENT ("BTM: BTM_WritePageTimeout: Timeout: %d.", timeout); + + if (timeout >= HCI_MIN_PAGE_TOUT) { + btm_cb.btm_inq_vars.page_timeout = timeout; + } + btm_cb.devcb.p_page_to_set_cmpl_cb = p_cb; + + /* Send the HCI command */ + if (!btsnd_hcic_write_page_tout (timeout)) { + return (BTM_NO_RESOURCES); + } + + if (p_cb) { + btu_start_timer(&btm_cb.devcb.page_timeout_set_timer, BTU_TTYPE_BTM_SET_PAGE_TO, BTM_DEV_REPLY_TIMEOUT); + } + + return (BTM_CMD_STARTED); +} + +/******************************************************************************* +** +** Function btm_set_page_timeout_complete +** +** Description This function is called when setting page timeout complete. +** message is received from the HCI. +** +** Returns void +** +*******************************************************************************/ +void btm_set_page_timeout_complete (const UINT8 *p) +{ + tBTM_SET_PAGE_TIMEOUT_RESULTS results; + + tBTM_CMPL_CB *p_cb = btm_cb.devcb.p_page_to_set_cmpl_cb; + btm_cb.devcb.p_page_to_set_cmpl_cb = NULL; + btu_free_timer (&btm_cb.devcb.page_timeout_set_timer); + + /* If there is a callback address for setting page timeout, call it */ + if (p_cb) { + if (p) { + STREAM_TO_UINT8 (results.hci_status, p); + switch (results.hci_status) { + case HCI_SUCCESS: + results.status = BTM_SUCCESS; + break; + case HCI_ERR_UNSUPPORTED_VALUE: + case HCI_ERR_ILLEGAL_PARAMETER_FMT: + results.status = BTM_ILLEGAL_VALUE; + break; + default: + results.status = BTM_NO_RESOURCES; + break; + } + } else { + results.hci_status = HCI_ERR_HOST_TIMEOUT; + results.status = BTM_DEVICE_TIMEOUT; + } + (*p_cb)(&results); + } +} +#endif /// CLASSIC_BT_INCLUDED == TRUE + +/******************************************************************************* +** +** Function btm_page_to_setup_timeout +** +** Description This function processes a timeout. +** Currently, we just report an error log +** +** Returns void +** +*******************************************************************************/ +void btm_page_to_setup_timeout (void *p_tle) +{ + UNUSED(p_tle); + BTM_TRACE_DEBUG ("%s\n", __func__); + +#if (CLASSIC_BT_INCLUDED == TRUE) + btm_set_page_timeout_complete (NULL); +#endif /// CLASSIC_BT_INCLUDED == TRUE +} + +/******************************************************************************* +** +** Function BTM_ReadPageTimeout +** +** Description Send HCI Read Page Timeout. +** +** Returns +** BTM_SUCCESS Command sent. +** BTM_NO_RESOURCES If out of resources to send the command. +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_ReadPageTimeout(tBTM_CMPL_CB *p_cb) +{ + BTM_TRACE_EVENT ("BTM: BTM_ReadPageTimeout"); + + /* Get the page timeout */ + tBTM_GET_PAGE_TIMEOUT_RESULTS results; + + if (p_cb) { + results.hci_status = HCI_SUCCESS; + results.status = BTM_SUCCESS; + results.page_to = btm_cb.btm_inq_vars.page_timeout; + + (*p_cb)(&results); + return (BTM_SUCCESS); + } else { + return (BTM_NO_RESOURCES); + } +} + +/******************************************************************************* +** +** Function BTM_WriteVoiceSettings +** +** Description Send HCI Write Voice Settings command. +** See stack/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_EVENT ("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_EVENT ("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 */ + hci_layer_get_interface()->transmit_command( + hci_packet_factory_get_interface()->make_set_event_mask((const bt_event_mask_t *)("\x00\x00\x00\x00\x00\x00\x00\x00")), + NULL, + NULL, + NULL); + + /* Send the HCI command */ + if (btsnd_hcic_enable_test_mode ()) { + 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 = {0}; + 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_EVENT ("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_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_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); + } +} + +#if (CLASSIC_BT_INCLUDED == TRUE) +/******************************************************************************* +** +** Function BTM_SetAfhChannels +** +** Description This function is called to set AFH channels +** +** Returns status of the operation +** +*******************************************************************************/ +tBTM_STATUS BTM_SetAfhChannels (AFH_CHANNELS channels, tBTM_CMPL_CB *p_afh_channels_cmpl_cback) +{ + if (!controller_get_interface()->get_is_ready()) { + return (BTM_DEV_RESET); + } + + /* Check if set afh already in progress */ + if (btm_cb.devcb.p_afh_channels_cmpl_cb) { + return (BTM_NO_RESOURCES); + } + + /* Save callback */ + btm_cb.devcb.p_afh_channels_cmpl_cb = p_afh_channels_cmpl_cback; + + if (!btsnd_hcic_set_afh_channels (channels)) { + return (BTM_NO_RESOURCES); + } + + btu_start_timer (&btm_cb.devcb.afh_channels_timer, BTU_TTYPE_BTM_ACL, BTM_DEV_REPLY_TIMEOUT); + + return BTM_CMD_STARTED; +} + +/******************************************************************************* +** +** Function btm_set_afh_channels_complete +** +** Description This function is called when setting AFH channels complete. +** message is received from the HCI. +** +** Returns void +** +*******************************************************************************/ +void btm_set_afh_channels_complete (UINT8 *p) +{ + tBTM_CMPL_CB *p_cb = btm_cb.devcb.p_afh_channels_cmpl_cb; + tBTM_SET_AFH_CHANNELS_RESULTS results; + + btu_free_timer (&btm_cb.devcb.afh_channels_timer); + + /* If there is a callback address for setting AFH channels, call it */ + btm_cb.devcb.p_afh_channels_cmpl_cb = NULL; + + if (p_cb) { + STREAM_TO_UINT8 (results.hci_status, p); + + switch (results.hci_status){ + case HCI_SUCCESS: + results.status = BTM_SUCCESS; + break; + case HCI_ERR_UNSUPPORTED_VALUE: + case HCI_ERR_ILLEGAL_PARAMETER_FMT: + results.status = BTM_ILLEGAL_VALUE; + break; + default: + results.status = BTM_ERR_PROCESSING; + break; + } + (*p_cb)(&results); + } +} +#endif /// CLASSIC_BT_INCLUDED == TRUE + +#if (BLE_INCLUDED == TRUE) +/******************************************************************************* +** +** Function BTM_BleSetChannels +** +** Description This function is called to set BLE channels +** +** Returns status of the operation +** +*******************************************************************************/ +tBTM_STATUS BTM_BleSetChannels (BLE_CHANNELS channels, tBTM_CMPL_CB *p_ble_channels_cmpl_cback) +{ + if (!controller_get_interface()->get_is_ready()) { + return (BTM_DEV_RESET); + } + + /* Check if set afh already in progress */ + if (btm_cb.devcb.p_ble_channels_cmpl_cb) { + return (BTM_NO_RESOURCES); + } + + /* Save callback */ + btm_cb.devcb.p_ble_channels_cmpl_cb = p_ble_channels_cmpl_cback; + + if (!btsnd_hcic_ble_set_channels (channels)) { + return (BTM_NO_RESOURCES); + } + + btu_start_timer (&btm_cb.devcb.ble_channels_timer, BTU_TTYPE_BTM_ACL, BTM_DEV_REPLY_TIMEOUT); + + return BTM_CMD_STARTED; +} + +/******************************************************************************* +** +** Function btm_ble_set_channels_complete +** +** Description This function is called when setting AFH channels complete. +** message is received from the HCI. +** +** Returns void +** +*******************************************************************************/ +void btm_ble_set_channels_complete (UINT8 *p) +{ + tBTM_CMPL_CB *p_cb = btm_cb.devcb.p_ble_channels_cmpl_cb; + tBTM_BLE_SET_CHANNELS_RESULTS results; + + btu_free_timer (&btm_cb.devcb.ble_channels_timer); + + /* If there is a callback address for setting AFH channels, call it */ + btm_cb.devcb.p_ble_channels_cmpl_cb = NULL; + + if (p_cb) { + STREAM_TO_UINT8 (results.hci_status, p); + + switch (results.hci_status){ + case HCI_SUCCESS: + results.status = BTM_SUCCESS; + break; + case HCI_ERR_UNSUPPORTED_VALUE: + case HCI_ERR_ILLEGAL_PARAMETER_FMT: + results.status = BTM_ILLEGAL_VALUE; + break; + default: + results.status = BTM_ERR_PROCESSING; + break; + } + (*p_cb)(&results); + } +} +#endif /// BLE_INCLUDED == TRUE diff --git a/lib/bt/host/bluedroid/stack/btm/btm_inq.c b/lib/bt/host/bluedroid/stack/btm/btm_inq.c new file mode 100644 index 00000000..b8da2327 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/btm/btm_inq.c @@ -0,0 +1,2973 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2014 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains functions that handle 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. + * + ******************************************************************************/ + +#include <string.h> +#include <stdio.h> +#include <stddef.h> + +#include "osi/alarm.h" +#include "stack/bt_types.h" +#include "device/controller.h" +#include "stack/hcimsgs.h" +#include "stack/btu.h" +#include "stack/btm_api.h" +#include "btm_int.h" +#include "stack/hcidefs.h" +#if (defined(SDP_INCLUDED) && SDP_INCLUDED == TRUE) +#include "stack/sdpdefs.h" +#endif + +#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 (defined(SDP_INCLUDED) && SDP_INCLUDED == TRUE) +static 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 +static const UINT16 BTM_EIR_UUID_LKUP_TBL[BTM_EIR_MAX_SERVICES]; +#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_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); + +static UINT8 btm_convert_uuid_to_eir_service( UINT16 uuid16 ); +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 ); + +/******************************************************************************* +** +** 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_API ("BTM_SetDiscoverability\n"); + + /*** Check mode parameter ***/ + if (inq_mode > BTM_MAX_DISCOVERABLE) { + return (BTM_ILLEGAL_VALUE); + } + + /* Make sure the controller is active */ + if (!controller_get_interface()->get_is_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_API ("BTM_SetDiscoverability: mode %d [NonDisc-0, Lim-1, Gen-2], window 0x%04x, interval 0x%04x\n", + 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_API ("BTM_SetInquiryScanType\n"); + 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 (!controller_get_interface()->supports_interlaced_inquiry_scan()) { + 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_API ("BTM_SetPageScanType\n"); + 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 (!controller_get_interface()->supports_interlaced_inquiry_scan()) { + 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) +{ + const controller_t *controller = controller_get_interface(); + BTM_TRACE_API ("BTM_SetInquiryMode\n"); + if (mode == BTM_INQ_RESULT_STANDARD) { + /* mandatory mode */ + } else if (mode == BTM_INQ_RESULT_WITH_RSSI) { + if (!controller->supports_rssi_with_inquiry_results()) { + return (BTM_MODE_UNSUPPORTED); + } + } else if (mode == BTM_INQ_RESULT_EXTENDED) { + if (!controller->supports_extended_inquiry_response()) { + return (BTM_MODE_UNSUPPORTED); + } + } 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_API ("BTM_ReadDiscoverability\n"); + 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_API ("BTM_SetPeriodicInquiryMode: mode: %d, dur: %d, rsps: %d, flt: %d, min: %d, max: %d\n", + 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)*/ + /* BTM_PER_INQ_MAX_MAX_PERIOD set to 1's in all bits. Condition resulting in false always*/ + { + 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 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; + + } + + 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_API ("BTM_CancelPeriodicInquiry called\n"); + + /*** 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_API ("BTM_SetConnectability\n"); + + /*** Check mode parameter ***/ + if (page_mode != BTM_NON_CONNECTABLE && page_mode != BTM_CONNECTABLE) { + return (BTM_ILLEGAL_VALUE); + } + + /* Make sure the controller is active */ + if (!controller_get_interface()->get_is_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_API ("BTM_SetConnectability: mode %d [NonConn-0, Conn-1], window 0x%04x, interval 0x%04x\n", + 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_API ("BTM_ReadConnectability\n"); + 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_API ("BTM_IsInquiryActive\n"); + + 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; +#if (defined(BTA_HOST_INTERLEAVE_SEARCH) && BTA_HOST_INTERLEAVE_SEARCH == TRUE) + UINT8 active_mode = p_inq->inq_active; +#endif + BTM_TRACE_API ("BTM_CancelInquiry called\n"); + + /*** 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 & BTM_INQUIRY_ACTIVE_MASK) != 0 && + (!(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 (((p_inq->inqparms.mode & BTM_BR_INQUIRY_MASK) != 0) +#if (defined(BTA_HOST_INTERLEAVE_SEARCH) && BTA_HOST_INTERLEAVE_SEARCH == TRUE) + && (active_mode & BTM_BR_INQUIRY_MASK) +#endif + ) { + if (!btsnd_hcic_inq_cancel()) { + status = BTM_NO_RESOURCES; + } + } +#if BLE_INCLUDED == TRUE + if (((p_inq->inqparms.mode & BTM_BLE_INQUIRY_MASK) != 0) +#if (defined(BTA_HOST_INTERLEAVE_SEARCH) && BTA_HOST_INTERLEAVE_SEARCH == TRUE) + && (active_mode & BTM_BLE_INQ_ACTIVE_MASK) +#endif + ) { + btm_ble_stop_inquiry(); + } +#endif + } + + /* Do not send the BUSY_LEVEL event yet. Wait for the cancel_complete event + * and then send the BUSY_LEVEL event + * btm_acl_update_busy_level (BTM_BLI_INQ_DONE_EVT); + */ + + 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, BR/LE bit mask seperately +** 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 = BTM_CMD_STARTED; + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + + BTM_TRACE_API ("BTM_StartInquiry: mode: %d, dur: %d, rsps: %d, flt: %d\n", + 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) { +#if (defined BLE_INCLUDED && BLE_INCLUDED == TRUE) + /*check if LE observe is already running*/ + if (p_inq->scan_type == INQ_LE_OBSERVE && p_inq->p_inq_ble_results_cb != NULL) { + BTM_TRACE_API("BTM_StartInquiry: LE observe in progress"); + p_inq->scan_type = INQ_GENERAL; + p_inq->inq_active = BTM_INQUIRY_INACTIVE; + btm_cb.ble_ctr_cb.inq_var.scan_type = BTM_BLE_SCAN_MODE_NONE; + btsnd_hcic_ble_set_scan_enable (BTM_BLE_SCAN_DISABLE, BTM_BLE_DUPLICATE_ENABLE); + } else +#endif + { + return (BTM_BUSY); + BTM_TRACE_API("BTM_StartInquiry: return BUSY\n"); + } + } else { + p_inq->scan_type = INQ_GENERAL; + } + + /*** 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 +#if (BLE_INCLUDED == TRUE) + && (p_inqparms->mode & BTM_BLE_INQUIRY_MASK) != BTM_BLE_GENERAL_INQUIRY + && (p_inqparms->mode & BTM_BLE_INQUIRY_MASK) != BTM_BLE_LIMITED_INQUIRY +#endif + ) { + return (BTM_ILLEGAL_VALUE); + } + +#if (defined(BTA_HOST_INTERLEAVE_SEARCH) && BTA_HOST_INTERLEAVE_SEARCH == TRUE) + if (p_inq->next_state == BTM_FINISH) { + return BTM_ILLEGAL_VALUE; + } +#endif + + + /* Save the inquiry parameters to be used upon the completion of setting/clearing the inquiry filter */ + p_inq->inqparms = *p_inqparms; + + /* 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 = p_inqparms->mode; + + BTM_TRACE_DEBUG("BTM_StartInquiry: p_inq->inq_active = 0x%02x\n", p_inq->inq_active); + + /* interleave scan minimal conditions */ +#if (BLE_INCLUDED==TRUE && (defined(BTA_HOST_INTERLEAVE_SEARCH) && BTA_HOST_INTERLEAVE_SEARCH == TRUE)) + + /* check if both modes are present */ + if ((p_inqparms->mode & BTM_BLE_INQUIRY_MASK) && (p_inqparms->mode & BTM_BR_INQUIRY_MASK)) { + BTM_TRACE_API("BTM:Interleave Inquiry Mode Set\n"); + p_inqparms->duration = p_inqparms->intl_duration[p_inq->next_state]; + p_inq->inqparms.duration = p_inqparms->duration; + } else { + BTM_TRACE_API("BTM:Single Mode: No interleaving, Mode:0x%02x\n", p_inqparms->mode); + p_inq->next_state = BTM_NO_INTERLEAVING; + } +#endif + + + + /* start LE inquiry here if requested */ +#if BLE_INCLUDED == TRUE + if ((p_inqparms->mode & BTM_BLE_INQUIRY_MASK) +#if (defined(BTA_HOST_INTERLEAVE_SEARCH) && BTA_HOST_INTERLEAVE_SEARCH == TRUE) + && (p_inq->next_state == BTM_BLE_ONE || p_inq->next_state == BTM_BLE_TWO || + p_inq->next_state == BTM_NO_INTERLEAVING) +#endif + ) + + { +#if (defined(BTA_HOST_INTERLEAVE_SEARCH) && BTA_HOST_INTERLEAVE_SEARCH == TRUE) + p_inq->inq_active = (p_inqparms->mode & BTM_BLE_INQUIRY_MASK); + BTM_TRACE_API("BTM:Starting LE Scan with duration %d and activeMode:0x%02x\n", + p_inqparms->duration, (p_inqparms->mode & BTM_BLE_INQUIRY_MASK)); +#endif + if (!controller_get_interface()->supports_ble()) { + p_inq->inqparms.mode &= ~ BTM_BLE_INQUIRY_MASK; + status = BTM_ILLEGAL_VALUE; + } + /* BLE for now does not support filter condition for inquiry */ + else if ((status = btm_ble_start_inquiry((UINT8)(p_inqparms->mode & BTM_BLE_INQUIRY_MASK), + p_inqparms->duration)) != BTM_CMD_STARTED) { + BTM_TRACE_ERROR("Err Starting LE Inquiry.\n"); + p_inq->inqparms.mode &= ~ BTM_BLE_INQUIRY_MASK; + } +#if (!defined(BTA_HOST_INTERLEAVE_SEARCH) || BTA_HOST_INTERLEAVE_SEARCH == FALSE) + p_inqparms->mode &= ~BTM_BLE_INQUIRY_MASK; +#endif + +#if (defined(BTA_HOST_INTERLEAVE_SEARCH) && BTA_HOST_INTERLEAVE_SEARCH == TRUE) + if (p_inq->next_state == BTM_NO_INTERLEAVING) { + p_inq->next_state = BTM_FINISH; + } else { + BTM_TRACE_API("BTM:Interleaving: started LE scan, Advancing to next state: %d\n", + p_inq->next_state + 1); + p_inq->next_state += 1; + } + /* reset next_state if status <> BTM_Started */ + if (status != BTM_CMD_STARTED) { + p_inq->next_state = BTM_BR_ONE; + } + + /* if interleave scan..return here */ + return status; +#endif + + + BTM_TRACE_DEBUG("BTM_StartInquiry: mode = %02x\n", p_inqparms->mode); + } +#endif /* end of BLE_INCLUDED */ + + /* we're done with this routine if BR/EDR inquiry is not desired. */ + if ((p_inqparms->mode & BTM_BR_INQUIRY_MASK) == BTM_INQUIRY_NONE) { + return status; + } + + /* BR/EDR inquiry portion */ +#if (defined(BTA_HOST_INTERLEAVE_SEARCH) && BTA_HOST_INTERLEAVE_SEARCH == TRUE) + if ((p_inq->next_state == BTM_BR_ONE || p_inq->next_state == BTM_BR_TWO || + p_inq->next_state == BTM_NO_INTERLEAVING )) { + p_inq->inq_active = (p_inqparms->mode & BTM_BR_INQUIRY_MASK); +#endif + /* 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; + } + +#if (defined(BTA_HOST_INTERLEAVE_SEARCH) && BTA_HOST_INTERLEAVE_SEARCH == TRUE) + if (p_inq->next_state == BTM_NO_INTERLEAVING) { + p_inq->next_state = BTM_FINISH; + } else { + BTM_TRACE_API("BTM:Interleaving: Started BTM inq, Advancing to next state: %d\n", + p_inq->next_state + 1); + p_inq->next_state += 1; + } + } + if (status != BTM_CMD_STARTED) { + /* Some error beginning the scan process. + Reset the next_state parameter.. Do we need to reset the inq_active also? + */ + BTM_TRACE_API("BTM:Interleaving: Error in Starting inquiry, status: 0x%02x\n", status); + p_inq->next_state = BTM_BR_ONE; + } +#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 + , tBT_TRANSPORT transport) +{ + tBTM_INQ_INFO *p_cur = NULL; + tINQ_DB_ENT *p_i; + + BTM_TRACE_API ("BTM_ReadRemoteDeviceName: bd addr [%02x%02x%02x%02x%02x%02x]\n", + 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; + } + BTM_TRACE_API ("no device found in inquiry db\n"); + +#if (BLE_INCLUDED == TRUE) + if (transport == BT_TRANSPORT_LE) { + 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; + + BTM_TRACE_API ("BTM_CancelRemoteDeviceName()\n"); + + /* Make sure there is not already one in progress */ + if (p_inq->remname_active) { +#if BLE_INCLUDED == TRUE + if (BTM_UseLeLink(p_inq->remname_bda)) { + 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_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) +{ + BTM_TRACE_API ("BTM_InqDbRead: bd addr [%02x%02x%02x%02x%02x%02x]\n", + p_bda[0], p_bda[1], p_bda[2], p_bda[3], p_bda[4], p_bda[5]); + + tINQ_DB_ENT *p_ent = btm_inq_db_find(p_bda); + if (!p_ent) { + return NULL; + } + + return &p_ent->inq_info; +} + + +/******************************************************************************* +** +** 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_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); + } +} + +/********************************************************************************* +********************************************************************************** +** ** +** 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 + + btu_free_timer(&btm_cb.btm_inq_vars.rmt_name_timer_ent); + memset(&btm_cb.btm_inq_vars.rmt_name_timer_ent, 0, sizeof(TIMER_LIST_ENT)); + btu_free_timer(&btm_cb.btm_inq_vars.inq_timer_ent); + memset(&btm_cb.btm_inq_vars.inq_timer_ent, 0, sizeof(TIMER_LIST_ENT)); + + 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_DEBUG ("btm_inq_stop_on_ssp: no_inc_ssp=%d inq_active:0x%x state:%d inqfilt_active:%d\n", + 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_DEBUG ("btm_clr_inq_db: inq_active:0x%x state:%d\n", + 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_DEBUG == TRUE) + BTM_TRACE_DEBUG ("inq_active:0x%x state:%d\n", + 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) +{ + tBTM_INQUIRY_VAR_ST *p_inq = &btm_cb.btm_inq_vars; + + if (p_inq->p_bd_db) { + osi_free(p_inq->p_bd_db); + p_inq->p_bd_db = NULL; + } + p_inq->num_bd_entries = 0; + p_inq->max_bd_entries = 0; +} + +/******************************************************************************* +** +** 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) +{ + 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++; + } + + /* 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; + + 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. */ + + 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; + + 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_DEBUG ("btm_set_inq_event_filter: filter type %d [Clear-0, COD-1, BDADDR-2]\n", + filter_cond_type); + BTM_TRACE_DEBUG (" condition [%02x%02x%02x %02x%02x%02x]\n", + 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_DEBUG ("btm_event_filter_complete: inq_active:0x%x state:%d inqfilt_active:%d\n", + 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_WARNING ("BTM Warning: Set Event Filter Failed (HCI returned 0x%x)\n", 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_DEBUG ("btm_initiate_inquiry: inq_active:0x%x state:%d inqfilt_active:%d\n", + btm_cb.btm_inq_vars.inq_active, btm_cb.btm_inq_vars.state, btm_cb.btm_inq_vars.inqfilt_active); +#endif + btm_acl_update_busy_level (BTM_BLI_INQ_EVT); + + 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 { + btm_clr_inq_result_flt(); + + /* Allocate memory to hold bd_addrs responding */ + if ((p_inq->p_bd_db = (tINQ_BDADDR *)osi_calloc(BT_DEFAULT_BUFFER_SIZE)) != NULL) { + p_inq->max_bd_entries = (UINT16)(BT_DEFAULT_BUFFER_SIZE / sizeof(tINQ_BDADDR)); + /* BTM_TRACE_DEBUG("btm_initiate_inquiry: memory allocated for %d bdaddrs", + p_inq->max_bd_entries); */ + } + + if (!btsnd_hcic_inquiry(*lap, p_inqparms->duration, 0)) { + 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 = NULL; + 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; + UINT8 *p_eir_data = NULL; + +#if (BTM_INQ_DEBUG == TRUE) + BTM_TRACE_DEBUG ("btm_process_inq_results inq_active:0x%x state:%d inqfilt_active:%d inq_res_mode=%d\n", + btm_cb.btm_inq_vars.inq_active, btm_cb.btm_inq_vars.state, btm_cb.btm_inq_vars.inqfilt_active, inq_res_mode); +#endif + /* Only process the results if the BR inquiry is still active */ + if (!(p_inq->inq_active & BTM_BR_INQ_ACTIVE_MASK)) { + 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); + + /* 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_WARNING("INQ RES: Extra Response Received...ignoring\n"); + return; + } + + /* Check if this address has already been processed for this inquiry */ + if (btm_inq_find_bdaddr(bda)) { + BTM_TRACE_DEBUG("BDA seen before [%02x%02x %02x%02x %02x%02x]\n", + 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_DEBUG("update RSSI new:%d, old:%d\n", 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; + } + } + + /* 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 = osi_time_get_os_boottime_ms(); + + 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 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_DEBUG("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_inquiry(); + } +#endif + btm_acl_update_busy_level (BTM_BLI_INQ_DONE_EVT); + } + /* 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 ( 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; + } + + /* If a callback is registered, call it with the results */ + if (p_inq_results_cb) { + (p_inq_results_cb)((tBTM_INQ_RESULTS *) p_cur, p_eir_data); + } + } + } +} + +/******************************************************************************* +** +** 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 *)osi_malloc(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); + } + } + } + + osi_free(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 (defined(BTA_HOST_INTERLEAVE_SEARCH) && BTA_HOST_INTERLEAVE_SEARCH == TRUE) + /* inquiry inactive case happens when inquiry is cancelled. + Make mode 0 for no further inquiries from the current inquiry process + */ + if (status != HCI_SUCCESS || p_inq->next_state == BTM_FINISH || !p_inq->inq_active) { + /* re-initialize for next inquiry request */ + p_inq->next_state = BTM_BR_ONE; + /* make the mode 0 here */ + p_inq->inqparms.mode &= ~(p_inq->inqparms.mode); + + } +#endif + +#if (!defined(BTA_HOST_INTERLEAVE_SEARCH) || BTA_HOST_INTERLEAVE_SEARCH == FALSE) + p_inq->inqparms.mode &= ~(mode); +#endif + + if (p_inq->scan_type == INQ_LE_OBSERVE && !p_inq->inq_active) { + /*end of LE observe*/ + p_inq->p_inq_ble_results_cb = (tBTM_INQ_RESULTS_CB *) NULL; + p_inq->p_inq_ble_cmpl_cb = (tBTM_CMPL_CB *) NULL; + p_inq->scan_type = INQ_NONE; + } + + +#if (BTM_INQ_DEBUG == TRUE) + BTM_TRACE_DEBUG ("btm_process_inq_complete inq_active:0x%x state:%d inqfilt_active:%d\n", + btm_cb.btm_inq_vars.inq_active, btm_cb.btm_inq_vars.state, btm_cb.btm_inq_vars.inqfilt_active); +#endif + btm_acl_update_busy_level (BTM_BLI_INQ_DONE_EVT); + /* 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); + + /* 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) { +#if BLE_INCLUDED == TRUE + btm_clear_all_pending_le_entry(); +#endif + 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) && + controller_get_interface()->supports_rssi_with_inquiry_results()) { + 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_DEBUG ("BTM Inq Compl Callback: status 0x%02x, num results %d\n", + 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 (defined(BTA_HOST_INTERLEAVE_SEARCH) && BTA_HOST_INTERLEAVE_SEARCH == TRUE) + if (p_inq->inqparms.mode != 0 && !(p_inq->inq_active & BTM_PERIODIC_INQUIRY_ACTIVE)) { + /* make inquiry inactive for next iteration */ + p_inq->inq_active = BTM_INQUIRY_INACTIVE; + /* call the inquiry again */ + BTM_StartInquiry(&p_inq->inqparms, p_inq->p_inq_results_cb, p_inq->p_inq_cmpl_cb); + } +#endif + } + if (p_inq->inqparms.mode == 0 && p_inq->scan_type == INQ_GENERAL) { //this inquiry is complete + p_inq->scan_type = INQ_NONE; +#if (defined BLE_INCLUDED && BLE_INCLUDED == TRUE) + /* check if the LE observe is pending */ + if (p_inq->p_inq_ble_results_cb != NULL) { + BTM_TRACE_DEBUG("BTM Inq Compl: resuming a pending LE scan"); + BTM_BleObserve(1, 0, p_inq->p_inq_ble_results_cb, p_inq->p_inq_ble_cmpl_cb); + } +#endif + } +#if (BTM_INQ_DEBUG == TRUE) + BTM_TRACE_DEBUG ("inq_active:0x%x state:%d inqfilt_active:%d\n", + btm_cb.btm_inq_vars.inq_active, btm_cb.btm_inq_vars.state, btm_cb.btm_inq_vars.inqfilt_active); +#endif +} + +/******************************************************************************* +** +** Function btm_process_cancel_complete +** +** Description This function is called when inquiry cancel complete is received +** from the device.This function will also call the btm_process_inq_complete +** This function is needed to differentiate a cancel_cmpl_evt from the +** inq_cmpl_evt +** +** Returns void +** +*******************************************************************************/ +void btm_process_cancel_complete(UINT8 status, UINT8 mode) +{ + btm_acl_update_busy_level (BTM_BLI_INQ_CANCEL_EVT); + btm_process_inq_complete(status, mode); +} +/******************************************************************************* +** +** 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; + } + } + } 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; + 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 (bda != NULL) { + BTM_TRACE_EVENT("BDA %02x:%02x:%02x:%02x:%02x:%02x\n", bda[0], bda[1], + bda[2], bda[3], + bda[4], bda[5]); + } + + BTM_TRACE_EVENT("Inquire BDA %02x:%02x:%02x:%02x:%02x:%02x\n", 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 + if (BTM_UseLeLink(p_inq->remname_bda)) { + 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.remote_bd_name[rem_name.length] = 0; + rem_name.status = BTM_SUCCESS; + temp_evt_len = rem_name.length; + + while (temp_evt_len > 0) { + *p_n1++ = *bdn++; + temp_evt_len--; + } + rem_name.remote_bd_name[rem_name.length] = 0; + } + /* 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'; + } + memcpy(rem_name.bd_addr, p_inq->remname_bda, BD_ADDR_LEN); + /* 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); + } + } +} + +/******************************************************************************* +** +** 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_ERROR ("btm_inq_rmt_name_failed() remname_active=%d\n", 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); + } +#if (SMP_INCLUDED == TRUE) + btm_sec_rmt_name_request_complete (NULL, NULL, HCI_ERR_UNSPECIFIED); +#endif ///SMP_INCLUDED == TRUE +} +/******************************************************************************* +** +** 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_EVENT ("BTM INQ TX POWER Complete: tx_power %d, hci status 0x%02x\n", + 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 +** fec_required - FEC is required or not +** +** Returns BTM_SUCCESS - if successful +** BTM_MODE_UNSUPPORTED - if local device cannot support it +** +*******************************************************************************/ +tBTM_STATUS BTM_WriteEIR( BT_HDR *p_buff, BOOLEAN fec_required) +{ + if (controller_get_interface()->supports_extended_inquiry_response()) { + BTM_TRACE_API("Write Extended Inquiry Response to controller\n"); + btsnd_hcic_write_ext_inquiry_response (p_buff, fec_required); + osi_free(p_buff); + return BTM_SUCCESS; + } else { + osi_free(p_buff); + return BTM_MODE_UNSUPPORTED; + } +} + +/******************************************************************************* +** +** 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 ) +{ + UINT8 *p = p_eir; + UINT8 length; + UINT8 eir_type; + BTM_TRACE_API("BTM_CheckEirData type=0x%02X\n", 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; +} + +/******************************************************************************* +** +** 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 +** +*******************************************************************************/ +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; +} + +/******************************************************************************* +** +** 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 ) +{ + 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 ); + } +} + +/******************************************************************************* +** +** 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_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; + } +} + +/******************************************************************************* +** +** 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 ) +{ + 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 ); + } +} + + +/******************************************************************************* +** +** Function btm_compare_uuid +** +** Description Helper function for custom service managing routines. +** +** Parameters uuid1 - pointer to the first tBT_UUID struct +** uuid2 - pointer to the second tBT_UUID struct +** +** Returns true if UUID structs are identical +** +*******************************************************************************/ +static bool btm_compare_uuid(tBT_UUID *uuid1, tBT_UUID *uuid2) +{ + if (uuid1->len != uuid2->len) { + return FALSE; + } + + return (memcmp(&uuid1->uu, &uuid2->uu, uuid1->len) == 0); +} + +/******************************************************************************* +** +** Function btm_find_empty_custom_uuid_slot +** +** Description Helper function for custom service managing routines. +** +** Parameters custom_uuid - pointer to custom_uuid array in tBTA_DM_CB +** uuid - UUID struct +** +** Returns Slot number if there is empty slot, +** otherwise - BTA_EIR_SERVER_NUM_CUSTOM_UUID +** +*******************************************************************************/ +static UINT8 btm_find_empty_custom_uuid_slot(tBT_UUID *custom_uuid, tBT_UUID uuid) +{ + for (UINT8 xx = 0; xx < BTA_EIR_SERVER_NUM_CUSTOM_UUID; xx++) { + if (custom_uuid[xx].len == 0) { + return xx; + } + } + return BTA_EIR_SERVER_NUM_CUSTOM_UUID; +} + +/******************************************************************************* +** +** Function btm_find_match_custom_uuid_slot +** +** Description Helper function for custom service managing routines. +** +** Parameters custom_uuid - pointer to custom_uuid array in tBTA_DM_CB +** uuid - UUID struct +** +** Returns Slot number if given UUID is already in slots array, +** otherwise - BTA_EIR_SERVER_NUM_CUSTOM_UUID +** +*******************************************************************************/ +static UINT8 btm_find_match_custom_uuid_slot(tBT_UUID *custom_uuid, tBT_UUID uuid) +{ + for (UINT8 xx = 0; xx < BTA_EIR_SERVER_NUM_CUSTOM_UUID; xx++) { + if (btm_compare_uuid(&custom_uuid[xx], &uuid)) { + return xx; + } + } + return BTA_EIR_SERVER_NUM_CUSTOM_UUID; +} + +/******************************************************************************* +** +** Function BTM_HasCustomEirService +** +** Description This function is called to know if UUID is already in custom +** UUID list. +** +** Parameters custom_uuid - pointer to custom_uuid array in tBTA_DM_CB +** uuid - UUID struct +** +** Returns TRUE - if found +** FALSE - if not found +** +*******************************************************************************/ +BOOLEAN BTM_HasCustomEirService(tBT_UUID *custom_uuid, tBT_UUID uuid) +{ + UINT8 match_slot = btm_find_match_custom_uuid_slot(custom_uuid, uuid); + + if (match_slot == BTA_EIR_SERVER_NUM_CUSTOM_UUID) { + return FALSE; + } + return TRUE; +} + +/******************************************************************************* +** +** Function BTM_AddCustomEirService +** +** Description This function is called to add a custom UUID. +** +** Parameters custom_uuid - pointer to custom_uuid array in tBTA_DM_CB +** uuid - UUID struct +** +** Returns None +** +*******************************************************************************/ +void BTM_AddCustomEirService(tBT_UUID *custom_uuid, tBT_UUID uuid) +{ + UINT8 empty_slot = btm_find_empty_custom_uuid_slot(custom_uuid, uuid); + + if (empty_slot == BTA_EIR_SERVER_NUM_CUSTOM_UUID) { + BTM_TRACE_WARNING("No space to add UUID for EIR"); + } else { + memcpy(&(custom_uuid[empty_slot]), &(uuid), sizeof(tBT_UUID)); + BTM_TRACE_EVENT("UUID saved in %d slot", empty_slot); + } +} + +/******************************************************************************* +** +** Function BTM_RemoveCustomEirService +** +** 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 ) +{ + 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 ); + } +} + +/******************************************************************************* +** +** Function BTM_RemoveCustomEirService +** +** Description This function is called to remove a a custom UUID. +** +** Parameters custom_uuid - pointer to custom_uuid array in tBTA_DM_CB +** uuid - UUID struct +** +** Returns None +** +*******************************************************************************/ +void BTM_RemoveCustomEirService(tBT_UUID *custom_uuid, tBT_UUID uuid) +{ + UINT8 match_slot = btm_find_match_custom_uuid_slot(custom_uuid, uuid); + + if (match_slot == BTA_EIR_SERVER_NUM_CUSTOM_UUID) { + BTM_TRACE_WARNING("UUID is not found for EIR"); + return; + } else { + memset(&(custom_uuid[match_slot]), 0, sizeof(tBT_UUID)); + } +} + +/******************************************************************************* +** +** 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) +{ + 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; +} + +/******************************************************************************* +** +** 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) +{ + 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_WARNING("BTM_GetEirUuidList number of uuid in EIR = %d, size of uuid list = %d\n", + *p_num_uuid, max_num_uuid ); + *p_num_uuid = max_num_uuid; + } + + BTM_TRACE_DEBUG("BTM_GetEirUuidList type = %02X, number of uuid = %d\n", 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_DEBUG(" 0x%04X\n", *(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_DEBUG(" 0x%08X\n", *(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_DEBUG(" 0x%s\n", buff); + } + } + + return type; +} + + +/******************************************************************************* +** +** 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_WARNING("btm_convert_uuid_to_uuid16 invalid uuid size\n"); + 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_API("btm_set_eir_uuid eir_complete_list=0x%02X\n", 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 ); + } + } + } +} diff --git a/lib/bt/host/bluedroid/stack/btm/btm_main.c b/lib/bt/host/bluedroid/stack/btm/btm_main.c new file mode 100644 index 00000000..05ef1837 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/btm/btm_main.c @@ -0,0 +1,152 @@ +/****************************************************************************** + * + * Copyright (C) 2002-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains the definition of the btm control block when + * BTM_DYNAMIC_MEMORY is used. + * + ******************************************************************************/ + +#include "stack/bt_types.h" +#include "common/bt_target.h" +#include <string.h> +#include "btm_int.h" +#include "osi/allocator.h" + +/* Global BTM control block structure +*/ +#if BTM_DYNAMIC_MEMORY == FALSE +tBTM_CB btm_cb; +#else +tBTM_CB *btm_cb_ptr; +#endif + +#if (BLE_50_FEATURE_SUPPORT == TRUE) +extern void btm_ble_extendadvcb_init(void); +extern void btm_ble_advrecod_init(void); +#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) +{ +#if BTM_DYNAMIC_MEMORY + btm_cb_ptr = (tBTM_CB *)osi_malloc(sizeof(tBTM_CB)); +#endif /* #if BTM_DYNAMIC_MEMORY */ + /* All fields are cleared; nonzero fields are reinitialized in appropriate function */ + memset(&btm_cb, 0, sizeof(tBTM_CB)); + btm_cb.page_queue = fixed_queue_new(QUEUE_SIZE_MAX); + btm_cb.sec_pending_q = fixed_queue_new(QUEUE_SIZE_MAX); + +#if defined(BTM_INITIAL_TRACE_LEVEL) + btm_cb.trace_level = BTM_INITIAL_TRACE_LEVEL; +#else + btm_cb.trace_level = BT_TRACE_LEVEL_NONE; +#endif + /* Initialize BTM component structures */ + btm_inq_db_init(); /* Inquiry Database and Structures */ + btm_acl_init(); /* ACL Database and Structures */ +#if (SMP_INCLUDED == TRUE) + btm_sec_init(BTM_SEC_MODE_SP); /* Security Manager Database and Structures */ +#endif ///SMP_INCLUDED == TRUE +#if BTM_SCO_INCLUDED == TRUE + btm_sco_init(); /* SCO Database and Structures (If included) */ +#endif + + btm_dev_init(); /* Device Manager Structures & HCI_Reset */ +#if BLE_INCLUDED == TRUE + btm_ble_lock_init(); + btm_ble_sem_init(); +#endif + btm_sec_dev_init(); +#if (BLE_50_FEATURE_SUPPORT == TRUE) + btm_ble_extendadvcb_init(); + btm_ble_advrecod_init(); +#endif + +} + + +/******************************************************************************* +** +** Function btm_free +** +** Description This function is called at btu core free the fixed queue +** +** Returns void +** +*******************************************************************************/ +void btm_free(void) +{ + fixed_queue_free(btm_cb.page_queue, osi_free_func); + fixed_queue_free(btm_cb.sec_pending_q, osi_free_func); + btm_acl_free(); + btm_sec_dev_free(); +#if BTM_DYNAMIC_MEMORY + FREE_AND_RESET(btm_cb_ptr); +#endif +#if BLE_INCLUDED == TRUE + btm_ble_lock_free(); + btm_ble_sem_free(); +#endif +} + +uint8_t btm_acl_active_count(void) +{ + list_node_t *p_node = NULL; + tACL_CONN *p_acl_conn = NULL; + uint8_t count = 0; + + for (p_node = list_begin(btm_cb.p_acl_db_list); p_node; p_node = list_next(p_node)) { + p_acl_conn = list_node(p_node); + if (p_acl_conn && p_acl_conn->in_use) { + count++; + } + } + + return count; +} + +uint8_t btdm_sec_dev_active_count(void) +{ + tBTM_SEC_DEV_REC *p_dev_rec = NULL; + list_node_t *p_node = NULL; + uint8_t count = 0; + + /* First look for the non-paired devices for the oldest entry */ + for (p_node = list_begin(btm_cb.p_sec_dev_rec_list); p_node; p_node = list_next(p_node)) { + p_dev_rec = list_node(p_node); + if (p_dev_rec && (p_dev_rec->sec_flags & BTM_SEC_IN_USE)) { + count++; + } + } + + return count; +} diff --git a/lib/bt/host/bluedroid/stack/btm/btm_pm.c b/lib/bt/host/bluedroid/stack/btm/btm_pm.c new file mode 100644 index 00000000..4c52348e --- /dev/null +++ b/lib/bt/host/bluedroid/stack/btm/btm_pm.c @@ -0,0 +1,965 @@ +/****************************************************************************** + * + * Copyright (C) 2000-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/***************************************************************************** + * + * This file contains functions that 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. + * + *****************************************************************************/ + +//#define LOG_TAG "bt_btm_pm" + +#include <stdlib.h> +#include <string.h> +//#include <stdio.h> +#include <stddef.h> + +#include "stack/bt_types.h" +#include "stack/hcimsgs.h" +#include "stack/btu.h" +#include "stack/btm_api.h" +#include "btm_int.h" +#include "l2c_int.h" +#include "stack/hcidefs.h" +//#include "bt_utils.h" +//#include "osi/include/log.h" +#include "osi/allocator.h" +/*****************************************************************************/ +/* 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 tBTM_STATUS btm_pm_snd_md_req( UINT8 pm_id, UINT16 link_hdl, tBTM_PM_PWR_MD *p_mode ); +#if (!CONFIG_BT_STACK_NO_LOG) +static const char *mode_to_string(tBTM_PM_MODE mode); +#endif + +/* +#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 // BTM_PM_DEBUG + +/*****************************************************************************/ +/* 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; + tBTM_PM_MCB *p_cb = NULL; /* per ACL link */ + tBTM_PM_MODE mode; + int temp_pm_id; + tACL_CONN *p_acl_cb; + + if (pm_id >= BTM_MAX_PM_RECORDS) { + pm_id = BTM_PM_SET_ONLY_ID; + } + + if (p_mode == NULL) { + return BTM_ILLEGAL_VALUE; + } + + BTM_TRACE_API( "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; + + p_acl_cb = btm_bda_to_acl(remote_bda, BT_TRANSPORT_BR_EDR); + if (p_acl_cb == NULL){ + return BTM_UNKNOWN_ADDR; + } + + p_cb = p_acl_cb->p_pm_mode_db; + 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_DEBUG( "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_hdl != BTM_INVALID_HANDLE)) ) { +#if BTM_PM_DEBUG == TRUE + BTM_TRACE_DEBUG( "BTM_SetPowerMode: Saving cmd acl handle %d temp_pm_id %d", p_acl_cb->hci_handle, temp_pm_id); +#endif // BTM_PM_DEBUG + /* 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_DEBUG( "btm_pm state:0x%x, pm_pend_link_hdl: %d", p_cb->state, btm_cb.pm_pend_link_hdl); +#endif // BTM_PM_DEBUG + /* 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_hdl != BTM_INVALID_HANDLE) || + (p_cb->state & BTM_PM_STORED_MASK) ) { /* command pending */ + if (p_acl_cb->hci_handle != btm_cb.pm_pend_link_hdl) { + /* set the stored mask */ + p_cb->state |= BTM_PM_STORED_MASK; + BTM_TRACE_DEBUG( "btm_pm state stored:%d", p_acl_cb->hci_handle); + } + return BTM_CMD_STORED; + } + + + + return btm_pm_snd_md_req(pm_id, p_acl_cb->hci_handle, 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) +{ + tACL_CONN *p_acl_cb = btm_bda_to_acl(remote_bda, BT_TRANSPORT_BR_EDR); + if (!p_acl_cb) { + return (BTM_UNKNOWN_ADDR); + } + + *p_mode = p_acl_cb->p_pm_mode_db->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) + tBTM_PM_MCB *p_cb; + tACL_CONN *p_acl_cb = NULL; + + p_acl_cb = btm_bda_to_acl(remote_bda, BT_TRANSPORT_BR_EDR); + if (!p_acl_cb) { + return (BTM_UNKNOWN_ADDR); + } + p_cb = p_acl_cb->p_pm_mode_db; + + if (BTM_PM_STS_ACTIVE == p_cb->state || + BTM_PM_STS_SNIFF == p_cb->state) { + if (btsnd_hcic_sniff_sub_rate(p_acl_cb->hci_handle, max_lat, + min_rmt_to, min_loc_to)) { + return BTM_SUCCESS; + } else { + return BTM_NO_RESOURCES; + } + } + 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 // BTM_SSR_INCLUDED +} + +/******************************************************************************* +** +** 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; + } + + + /* 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 && btm_cb.pm_pend_link_hdl != BTM_INVALID_HANDLE) { + (*cb)((btm_handle_to_acl(btm_cb.pm_pend_link_hdl))->remote_addr, BTM_PM_STS_ERROR, BTM_DEV_RESET, 0); + } + + /* no command pending */ + btm_cb.pm_pend_link_hdl = BTM_INVALID_HANDLE; +} + +/******************************************************************************* +** +** 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 +** +*******************************************************************************/ +tBTM_PM_MCB *btm_pm_sm_alloc(void) +{ + tBTM_PM_MCB *p_db = (tBTM_PM_MCB *) osi_malloc(sizeof(tBTM_PM_MCB)); /* per ACL link */ + if (p_db) { + memset (p_db, 0, sizeof(tBTM_PM_MCB)); + p_db->state = BTM_PM_ST_ACTIVE; + if (list_length(btm_cb.p_pm_mode_db_list) >= MAX_L2CAP_LINKS) { + osi_free(p_db); + p_db = NULL; + } + if (!list_append(btm_cb.p_pm_mode_db_list, p_db)) { + osi_free(p_db); + p_db = NULL; + } + } + return p_db; +} +/******************************************************************************* +** +** 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 +** +*******************************************************************************/ + +/******************************************************************************* +** +** 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, UINT16 link_hdl, tBTM_PM_PWR_MD *p_mode) +{ + tBTM_PM_PWR_MD md_res; + tBTM_PM_MODE mode; + tACL_CONN *p_acl_cb = btm_handle_to_acl(link_hdl); + tBTM_PM_MCB *p_cb = p_acl_cb->p_pm_mode_db; + 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_DEBUG( "btm_pm_snd_md_req link_hdl:%d, mode: %d", + link_hdl, mode); +#endif // BTM_PM_DEBUG + + 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(link_hdl, p_cb->max_lat, + p_cb->min_rmt_to, p_cb->min_loc_to); + p_cb->max_lat = 0; + } +#endif // BTM_SSR_INCLUDED + /* Default is failure */ + btm_cb.pm_pend_link_hdl = BTM_INVALID_HANDLE; + + /* send the appropriate HCI command */ + btm_cb.pm_pend_id = pm_id; + +#if BTM_PM_DEBUG == TRUE + BTM_TRACE_DEBUG("btm_pm_snd_md_req state:0x%x, link_hdl: %d", p_cb->state, link_hdl); +#endif // BTM_PM_DEBUG + + BTM_TRACE_DEBUG("%s switching from %s to %s.", __func__, mode_to_string(p_cb->state), mode_to_string(md_res.mode)); + switch (md_res.mode) { + case BTM_PM_MD_ACTIVE: + switch (p_cb->state) { + case BTM_PM_MD_SNIFF: + if (btsnd_hcic_exit_sniff_mode(link_hdl)) { + btm_cb.pm_pend_link_hdl = link_hdl; + } + break; + case BTM_PM_MD_PARK: + if (btsnd_hcic_exit_park_mode(link_hdl)) { + btm_cb.pm_pend_link_hdl = link_hdl; + } + break; + default: + /* Failure btm_cb.pm_pend_link = MAX_L2CAP_LINKS */ + break; + } + break; + + case BTM_PM_MD_HOLD: + if (btsnd_hcic_hold_mode (link_hdl, + md_res.max, md_res.min)) { + btm_cb.pm_pend_link_hdl = link_hdl; + } + break; + + case BTM_PM_MD_SNIFF: + if (btsnd_hcic_sniff_mode (link_hdl, + md_res.max, md_res.min, md_res.attempt, + md_res.timeout)) { + btm_cb.pm_pend_link_hdl = link_hdl; + } + break; + + case BTM_PM_MD_PARK: + if (btsnd_hcic_park_mode (link_hdl, + md_res.max, md_res.min)) { + btm_cb.pm_pend_link_hdl = link_hdl; + } + break; + default: + /* Failure btm_cb.pm_pend_link = MAX_L2CAP_LINKS */ + break; + } + + if (btm_cb.pm_pend_link_hdl == BTM_INVALID_HANDLE) { + /* the command was not sent */ +#if BTM_PM_DEBUG == TRUE + BTM_TRACE_DEBUG( "pm_pend_link_hdl: %d", btm_cb.pm_pend_link_hdl); +#endif // BTM_PM_DEBUG + 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) +{ + tACL_CONN *p_acl_cb = NULL; + list_node_t *p_node = NULL; + for (p_node = list_begin(btm_cb.p_acl_db_list); p_node; p_node = list_next(p_node)) { + p_acl_cb = list_node(p_node); + if (p_acl_cb->p_pm_mode_db->state & BTM_PM_STORED_MASK) { + p_acl_cb->p_pm_mode_db->state &= ~BTM_PM_STORED_MASK; + BTM_TRACE_DEBUG( "btm_pm_check_stored :%d", p_acl_cb->hci_handle); + btm_pm_snd_md_req(BTM_PM_SET_ONLY_ID, p_acl_cb->hci_handle, 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; + tACL_CONN *p_acl_cb; + + if (btm_cb.pm_pend_link_hdl == BTM_INVALID_HANDLE) { + return; + } + + + p_acl_cb = btm_handle_to_acl(btm_cb.pm_pend_link_hdl); + if (p_acl_cb == NULL) { + return; + } + p_cb = p_acl_cb->p_pm_mode_db; + + if (status == HCI_SUCCESS) { + p_cb->state = BTM_PM_ST_PENDING; + pm_status = BTM_PM_STS_PENDING; +#if BTM_PM_DEBUG == TRUE + BTM_TRACE_DEBUG( "btm_pm_proc_cmd_status new state:0x%x", p_cb->state); +#endif // BTM_PM_DEBUG + } 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)(p_acl_cb->remote_addr, pm_status, 0, status); + } + + /* no pending cmd now */ +#if BTM_PM_DEBUG == TRUE + BTM_TRACE_DEBUG( "btm_pm_proc_cmd_status state:0x%x, pm_pend_link: %d(new: %d)", + p_cb->state, btm_cb.pm_pend_link_hdl, MAX_L2CAP_LINKS); +#endif // BTM_PM_DEBUG + btm_cb.pm_pend_link_hdl = BTM_INVALID_HANDLE; + + 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 yy; + tBTM_PM_STATE old_state; + tL2C_LCB *p_lcb; + + /* get the index to acl_db */ + p = btm_handle_to_acl(hci_handle); + if (!p) { + return; + } + + /* update control block */ + p_cb = p->p_pm_mode_db; + old_state = p_cb->state; + p_cb->state = mode; + p_cb->interval = interval; + + BTM_TRACE_DEBUG("%s switched from %s to %s.", __func__, mode_to_string(old_state), mode_to_string(p_cb->state)); + + if ((p_lcb = l2cu_find_lcb_by_bd_addr(p->remote_addr, BT_TRANSPORT_BR_EDR)) != NULL) { + if ((p_cb->state == BTM_PM_ST_ACTIVE) || (p_cb->state == BTM_PM_ST_SNIFF)) { + /* There might be any pending packets due to SNIFF or PENDING state */ + /* Trigger L2C to start transmission of the pending packets. */ + BTM_TRACE_DEBUG("btm mode change to active; check l2c_link for outgoing packets"); + l2c_link_check_send_pkts(p_lcb, NULL, NULL); + } + } + + /* 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_DEBUG( "btm_pm_proc_mode_change: Sending stored req:%d", xx); +#endif // BTM_PM_DEBUG + btm_pm_snd_md_req(BTM_PM_SET_ONLY_ID, hci_handle, NULL); + } else { + list_node_t *p_node = NULL; + + for (p_node =(list_begin(btm_cb.p_pm_mode_db_list)); p_node; p_node = (list_next(p_node))) { + p_cb = (tBTM_PM_MCB *)list_node(p_node); + if (p_cb->chg_ind == TRUE) { +#if BTM_PM_DEBUG == TRUE + BTM_TRACE_DEBUG( "btm_pm_proc_mode_change: Sending PM req :%d", zz); +#endif // BTM_PM_DEBUG + btm_pm_snd_md_req(BTM_PM_SET_ONLY_ID, hci_handle, 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(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_rx_lat; + int xx; + tBTM_PM_MCB *p_cb; + tACL_CONN *p_acl = NULL; + UINT16 use_ssr = TRUE; + UNUSED(evt_len); + + STREAM_TO_UINT8 (status, p); + + STREAM_TO_UINT16 (handle, p); + /* get the index to acl_db */ + + p += 2; + STREAM_TO_UINT16 (max_rx_lat, p); + p_acl = btm_handle_to_acl(handle); + if (!p_acl) { + return; + } + p_cb = p_acl->p_pm_mode_db; + if (p_cb->interval == max_rx_lat) { + /* using legacy sniff */ + use_ssr = FALSE; + } + + /* notify registered parties */ + for (xx = 0; xx < BTM_MAX_PM_RECORDS; xx++) { + if (btm_cb.pm_reg_db[xx].mask & BTM_PM_REG_NOTIF) { + if ( p_acl) { + (*btm_cb.pm_reg_db[xx].cback)( p_acl->remote_addr, BTM_PM_STS_SSR, use_ssr, status); + } + } + } +} +#endif // BTM_SSR_INCLUDED + +/******************************************************************************* +** +** Function btm_pm_device_in_active_or_sniff_mode +** +** Description This function is called to check if in active or sniff mode +** +** Returns TRUE, if in active or sniff mode +** +*******************************************************************************/ +BOOLEAN btm_pm_device_in_active_or_sniff_mode(void) +{ + /* The active state is the highest state-includes connected device and sniff mode*/ + + /* Covers active and sniff modes */ + if (BTM_GetNumAclLinks() > 0) { + BTM_TRACE_DEBUG("%s - ACL links: %d", __func__, BTM_GetNumAclLinks()); + return TRUE; + } + +#if ((defined BLE_INCLUDED) && (BLE_INCLUDED == TRUE)) + /* Check BLE states */ + if (btm_ble_get_conn_st() != BLE_CONN_IDLE) { + BTM_TRACE_DEBUG("%s - BLE state: %x", __func__, btm_ble_get_conn_st()); + return TRUE; + } +#endif + + return FALSE; +} + +/******************************************************************************* +** +** Function btm_pm_device_in_scan_state +** +** Description This function is called to check if in paging, inquiry or connecting mode +** +** Returns TRUE, if in paging, inquiry or connecting mode +** +*******************************************************************************/ +BOOLEAN btm_pm_device_in_scan_state(void) +{ + /* Scan state-paging, inquiry, and trying to connect */ + + /* Check for paging */ + if (btm_cb.is_paging || (!fixed_queue_is_empty(btm_cb.page_queue)) || + BTM_BL_PAGING_STARTED == btm_cb.busy_level) { + BTM_TRACE_DEBUG("btm_pm_device_in_scan_state- paging"); + return TRUE; + } + + /* Check for inquiry */ + if ((btm_cb.btm_inq_vars.inq_active & (BTM_BR_INQ_ACTIVE_MASK | BTM_BLE_INQ_ACTIVE_MASK)) != 0) { + BTM_TRACE_DEBUG("btm_pm_device_in_scan_state- Inq active"); + return TRUE; + } + + return FALSE; +} + +/******************************************************************************* +** +** Function BTM_PM_ReadControllerState +** +** Description This function is called to obtain the controller state +** +** Returns Controller State-BTM_CONTRL_ACTIVE, BTM_CONTRL_SCAN, and BTM_CONTRL_IDLE +** +*******************************************************************************/ +tBTM_CONTRL_STATE BTM_PM_ReadControllerState(void) +{ + if (TRUE == btm_pm_device_in_active_or_sniff_mode()) { + return BTM_CONTRL_ACTIVE; + } else if (TRUE == btm_pm_device_in_scan_state()) { + return BTM_CONTRL_SCAN; + } else { + return BTM_CONTRL_IDLE; + } +} + +#if (!CONFIG_BT_STACK_NO_LOG) +static const char *mode_to_string(tBTM_PM_MODE mode) +{ + switch (mode) { + case BTM_PM_MD_ACTIVE: return "ACTIVE"; + case BTM_PM_MD_SNIFF: return "SNIFF"; + case BTM_PM_MD_PARK: return "PARK"; + case BTM_PM_MD_HOLD: return "HOLD"; + default: return "UNKNOWN"; + } +} +#endif diff --git a/lib/bt/host/bluedroid/stack/btm/btm_sco.c b/lib/bt/host/bluedroid/stack/btm/btm_sco.c new file mode 100644 index 00000000..53a9768a --- /dev/null +++ b/lib/bt/host/bluedroid/stack/btm/btm_sco.c @@ -0,0 +1,1907 @@ +/****************************************************************************** + * + * Copyright (C) 2000-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains functions that handle SCO connections. This includes + * operations such as connect, disconnect, change supported packet types. + * + ******************************************************************************/ + +#include <string.h> +#include "stack/bt_types.h" +#include "common/bt_target.h" +#include "stack/bt_types.h" +#include "stack/hcimsgs.h" +#include "stack/btu.h" +#include "stack/btm_api.h" +#include "osi/allocator.h" +#include "btm_int.h" +#include "stack/hcidefs.h" +//#include "bt_utils.h" + +#if BTM_SCO_INCLUDED == TRUE + +/********************************************************************************/ +/* L O C A L D A T A D E F I N I T I O N S */ +/********************************************************************************/ + +#define SCO_ST_UNUSED 0 +#define SCO_ST_LISTENING 1 +#define SCO_ST_W4_CONN_RSP 2 +#define SCO_ST_CONNECTING 3 +#define SCO_ST_CONNECTED 4 +#define SCO_ST_DISCONNECTING 5 +#define SCO_ST_PEND_UNPARK 6 +#define SCO_ST_PEND_ROLECHANGE 7 + +/********************************************************************************/ +/* L O C A L F U N C T I O N P R O T O T Y P E S */ +/********************************************************************************/ + +static const tBTM_ESCO_PARAMS btm_esco_defaults = { + BTM_64KBITS_RATE, /* TX Bandwidth (64 kbits/sec) */ + BTM_64KBITS_RATE, /* RX Bandwidth (64 kbits/sec) */ + 0x000a, /* 10 ms (HS/HF can use EV3, 2-EV3, 3-EV3) */ + 0x0060, /* Inp Linear, Air CVSD, 2s Comp, 16bit */ + (BTM_SCO_PKT_TYPES_MASK_HV1 + /* Packet Types */ + BTM_SCO_PKT_TYPES_MASK_HV2 + + BTM_SCO_PKT_TYPES_MASK_HV3 + + BTM_SCO_PKT_TYPES_MASK_EV3 + + BTM_SCO_PKT_TYPES_MASK_EV4 + + BTM_SCO_PKT_TYPES_MASK_EV5), + BTM_ESCO_RETRANS_POWER /* Retransmission Effort (Power) */ +}; + +/******************************************************************************* +** +** Function btm_sco_flush_sco_data +** +** Description This function is called to flush the SCO data for this channel. +** +** Returns void +** +*******************************************************************************/ +void btm_sco_flush_sco_data(UINT16 sco_inx) +{ +#if BTM_SCO_HCI_INCLUDED == TRUE +#if (BTM_MAX_SCO_LINKS>0) + tSCO_CONN *p ; + BT_HDR *p_buf; + + if (sco_inx < BTM_MAX_SCO_LINKS) { + p = &btm_cb.sco_cb.sco_db[sco_inx]; + while ((p_buf = (BT_HDR *)fixed_queue_dequeue(p->xmit_data_q, 0)) != NULL) { + osi_free(p_buf); + } + } +#else + UNUSED(sco_inx); +#endif +#else + UNUSED(sco_inx); +#endif +} +/******************************************************************************* +** +** Function btm_sco_init +** +** Description This function is called at BTM startup to initialize +** +** Returns void +** +*******************************************************************************/ +void btm_sco_init (void) +{ +#if 0 /* cleared in btm_init; put back in if called from anywhere else! */ + memset (&btm_cb.sco_cb, 0, sizeof(tSCO_CB)); +#endif +#if (BTM_SCO_HCI_INCLUDED == TRUE) + for (int i = 0; i < BTM_MAX_SCO_LINKS; i++) { + btm_cb.sco_cb.sco_db[i].xmit_data_q = fixed_queue_new(QUEUE_SIZE_MAX); + } +#endif + /* Initialize nonzero defaults */ + btm_cb.sco_cb.sco_disc_reason = BTM_INVALID_SCO_DISC_REASON; + + btm_cb.sco_cb.def_esco_parms = 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_ERROR("Could not reject (e)SCO conn: No Buffer!!!"); + } + } else { + if (!btsnd_hcic_reject_esco_conn (bda, hci_status)) { + BTM_TRACE_ERROR("Could not reject (e)SCO conn: No Buffer!!!"); + } + } + } else { /* Connection is being accepted */ + p_sco->state = SCO_ST_CONNECTING; + 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; + temp_pkt_types |= BTM_SCO_EXCEPTION_PKTS_MASK; + } else { + /* OR in any exception packet types */ + 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_ERROR("Could not accept SCO conn: No Buffer!!!"); + } + } +#endif +} + + +#if BTM_SCO_HCI_INCLUDED == TRUE +void btm_sco_process_num_bufs (UINT16 num_lm_sco_bufs) +{ + BTM_TRACE_DEBUG("%s, %d", __FUNCTION__, num_lm_sco_bufs); + btm_cb.sco_cb.num_lm_sco_bufs = btm_cb.sco_cb.xmit_window_size = num_lm_sco_bufs; +} + +/******************************************************************************* +** +** Function BTM_ConfigScoPath +** +** Description This function enable/disable SCO over HCI and registers SCO +** data callback if SCO over HCI is enabled. +** +** Parameter path: SCO or HCI +** p_sco_data_cb: callback function or SCO data if path is set +** to transport. +** p_pcm_param: pointer to the PCM interface parameter. If a NULL +** pointer is used, PCM parameter maintained in +** the control block will be used; otherwise update +** control block value. +** err_data_rpt: Lisbon feature to enable the erronous data report +** or not. +** +** Returns BTM_SUCCESS if the successful. +** BTM_NO_RESOURCES: no rsource to start the command. +** BTM_ILLEGAL_VALUE: invalid callback function pointer. +** BTM_CMD_STARTED :Command sent. Waiting for command cmpl event. +** +** +*******************************************************************************/ +//extern +tBTM_STATUS BTM_ConfigScoPath (tBTM_SCO_ROUTE_TYPE path, + tBTM_SCO_DATA_CB *p_sco_data_cb, + tBTM_SCO_PCM_PARAM *p_pcm_param, + BOOLEAN err_data_rpt) +{ + UNUSED(err_data_rpt); + UNUSED(p_pcm_param); + btm_cb.sco_cb.sco_path = path; + if (path == BTM_SCO_ROUTE_PCM) { + return BTM_SUCCESS; + } else if (path == BTM_SCO_ROUTE_HCI) { + if (p_sco_data_cb) { + btm_cb.sco_cb.p_data_cb = p_sco_data_cb; + } + } + + return BTM_SUCCESS; +} + +static void hci_sco_data_to_lower(BT_HDR *p_buf) +{ + p_buf->event = BT_EVT_TO_LM_HCI_SCO; + if (p_buf->offset == 0) { + BTM_TRACE_ERROR("offset cannot be 0"); + osi_free(p_buf); + } + + bte_main_hci_send(p_buf, (UINT16)(BT_EVT_TO_LM_HCI_SCO | LOCAL_BLE_CONTROLLER_ID)); +} +/******************************************************************************* +** +** 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) +{ + tSCO_CB *p_cb = &btm_cb.sco_cb; + tSCO_CONN *p_ccb = &p_cb->sco_db[sco_inx]; + + /* If there is data to send, send it now */ + BT_HDR *p_buf; + while (p_cb->xmit_window_size != 0) + { + if ((p_buf = (BT_HDR *)fixed_queue_dequeue(p_ccb->xmit_data_q, 0)) == NULL) { + break; + } +#if BTM_SCO_HCI_DEBUG + BTM_TRACE_DEBUG("btm: [%d] buf in xmit_data_q", + fixed_queue_length(p_ccb->xmit_data_q) + 1); +#endif + /* Don't go negative */ + p_cb->xmit_window_size -= 1; + p_ccb->sent_not_acked += 1; + + // HCI_SCO_DATA_TO_LOWER(p_buf); + hci_sco_data_to_lower(p_buf); + } +} + +void btm_sco_process_num_completed_pkts (UINT8 *p) +{ + UINT8 num_handles, xx; + UINT16 handle; + UINT16 num_sent; + UINT16 sco_inx; + tSCO_CB *p_cb = &btm_cb.sco_cb; + tSCO_CONN * p_ccb; + STREAM_TO_UINT8 (num_handles, p); + for (xx = 0; xx < num_handles; xx++) { + STREAM_TO_UINT16 (handle, p); + STREAM_TO_UINT16 (num_sent, p); + if ((sco_inx = btm_find_scb_by_handle(handle)) == BTM_MAX_SCO_LINKS) { + continue; + } + BTM_TRACE_DEBUG("%s, %d, %u", __FUNCTION__, handle, p_cb->xmit_window_size); //debug + p_ccb = &p_cb->sco_db[sco_inx]; + p_ccb->sent_not_acked -= num_sent; + // don't go negative + if (p_ccb->sent_not_acked < 0) { + BTM_TRACE_WARNING("SCO: un-acked underf: %u", p_ccb->sent_not_acked); + p_ccb->sent_not_acked = 0; + } + p_cb->xmit_window_size += num_sent; + if (p_cb->xmit_window_size > p_cb->num_lm_sco_bufs) { + BTM_TRACE_WARNING("SCO xwind: %d, max %d", p_cb->xmit_window_size, p_cb->num_lm_sco_bufs); + p_cb->xmit_window_size = p_cb->num_lm_sco_bufs; + } + btm_sco_check_send_pkts (sco_inx); + } + + return; +} + +/******************************************************************************* +** +** Function btm_pkt_stat_nums_update +** +** Description Update the number of received SCO data packet status. +** +** Returns void +** +*******************************************************************************/ +static void btm_pkt_stat_nums_update(uint16_t sco_inx, uint8_t pkt_status) +{ + tSCO_CONN *p_ccb = &btm_cb.sco_cb.sco_db[sco_inx]; + p_ccb->pkt_stat_nums.rx_total++; + if (pkt_status == BTM_SCO_DATA_CORRECT) { + p_ccb->pkt_stat_nums.rx_correct++; + } else if (pkt_status == BTM_SCO_DATA_PAR_ERR) { + p_ccb->pkt_stat_nums.rx_err++; + } else if (pkt_status == BTM_SCO_DATA_NONE) { + p_ccb->pkt_stat_nums.rx_none++; + } else { + p_ccb->pkt_stat_nums.rx_lost++; + } +} + +/******************************************************************************* +** +** Function btm_pkt_stat_send_nums_update +** +** Description Update the number of send packet status. +** +** Returns void +** +*******************************************************************************/ +static void btm_pkt_stat_send_nums_update(uint16_t sco_inx, uint8_t pkt_status) +{ + tSCO_CONN *p_ccb = &btm_cb.sco_cb.sco_db[sco_inx]; + p_ccb->pkt_stat_nums.tx_total++; + if (pkt_status != BTM_SUCCESS && pkt_status != BTM_NO_RESOURCES && pkt_status != BTM_SCO_BAD_LENGTH) { + p_ccb->pkt_stat_nums.tx_discarded++; + } +} + +/******************************************************************************* +** +** Function btm_pkt_stat_nums_reset +** +** Description This function is called to reset the number of packet status struct +** +** Returns void +** +*******************************************************************************/ +static void btm_pkt_stat_nums_reset(uint16_t sco_inx) +{ + memset(&btm_cb.sco_cb.sco_db[sco_inx].pkt_stat_nums, 0, sizeof(tBTM_SCO_PKT_STAT_NUMS)); +} + +/******************************************************************************* +** +** Function BTM_PktStatNumsGet +** +** Description This function is called to get the number of packet status struct +** +** Returns void +** +*******************************************************************************/ +void BTM_PktStatNumsGet(uint16_t sync_conn_handle, tBTM_SCO_PKT_STAT_NUMS *p_pkt_nums) +{ + uint16_t sco_inx = btm_find_scb_by_handle(sync_conn_handle); + if (sco_inx < BTM_MAX_SCO_LINKS) { + memcpy(p_pkt_nums, &btm_cb.sco_cb.sco_db[sco_inx].pkt_stat_nums, sizeof(tBTM_SCO_PKT_STAT_NUMS)); + } else { + memset(p_pkt_nums, 0, sizeof(tBTM_SCO_PKT_STAT_NUMS)); + } +} + +#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); + UNUSED(pkt_size); + 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 */ + { + osi_free (p_msg); + } else { + btm_pkt_stat_nums_update(sco_inx, pkt_status); + (*btm_cb.sco_cb.p_data_cb)(sco_inx, p_msg, (tBTM_SCO_DATA_FLAG) pkt_status); + } + } else { /* no mapping handle SCO connection is active, free the buffer */ + osi_free (p_msg); + } + BTM_TRACE_DEBUG ("SCO: hdl %x, len %d, pkt_sz %d\n", handle, p_msg->len, pkt_size); +#else + osi_free(p_msg); +#endif +} + +/******************************************************************************* +** +** Function BTM_WriteScoData +** +** Description This function write SCO data to a specified instance. The data +** to be written p_buf needs to carry an offset of +** HCI_SCO_PREAMBLE_SIZE bytes, and the data length can not +** exceed BTM_SCO_DATA_SIZE_MAX bytes, whose default value is set +** to 60 and is configurable. Data longer than the maximum bytes +** will be truncated. +** +** Returns BTM_SUCCESS: data write is successful +** BTM_ILLEGAL_VALUE: SCO data contains illegal offset value. +** BTM_SCO_BAD_LENGTH: SCO data length exceeds the max SCO packet +** size. +** BTM_NO_RESOURCES: no resources. +** BTM_UNKNOWN_ADDR: unknown SCO connection handle, or SCO is not +** routed via HCI. +** BTM_ERR_PROCESSING: transmit queue overflow +** +** +*******************************************************************************/ +tBTM_STATUS BTM_WriteScoData (UINT16 sco_inx, BT_HDR *p_buf) +{ + APPL_TRACE_DEBUG("%s", __FUNCTION__); +#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_ERROR ("BTM SCO - cannot send buffer, offset: %d", p_buf->offset); + 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) { + BTM_TRACE_WARNING ("BTM SCO hdl %x, bad len %u", p_ccb->hci_handle, p_buf->len); + 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; + + if (fixed_queue_length(p_ccb->xmit_data_q) < BTM_SCO_XMIT_QUEUE_THRS) { + if (fixed_queue_length(p_ccb->xmit_data_q) >= BTM_SCO_XMIT_QUEUE_HIGH_WM) { + status = BTM_NO_RESOURCES; + } + fixed_queue_enqueue(p_ccb->xmit_data_q, p_buf, FIXED_QUEUE_MAX_TIMEOUT); + btm_sco_check_send_pkts (sco_inx); + } else { + BTM_TRACE_WARNING ("SCO xmit Q overflow, pkt dropped"); + status = BTM_ERR_PROCESSING; + } + } + } else { + BTM_TRACE_WARNING ("BTM_WriteScoData, invalid sco index: %d at state [%d]", + sco_inx, btm_cb.sco_cb.sco_db[sco_inx].state); + status = BTM_UNKNOWN_ADDR; + } + + if (status != BTM_SUCCESS && status!= BTM_NO_RESOURCES && status != BTM_SCO_BAD_LENGTH) { + BTM_TRACE_WARNING ("stat %d", status); + osi_free(p_buf); + } + btm_pkt_stat_send_nums_update(sco_inx, status); + return (status); + +#else + UNUSED(sco_inx); + UNUSED(p_buf); + return (BTM_NO_RESOURCES); +#endif +} + +#if (BTM_MAX_SCO_LINKS>0) +/******************************************************************************* +** +** Function btm_send_connect_request +** +** Description This function is called to respond to SCO connect indications +** +** Returns void +** +*******************************************************************************/ +static tBTM_STATUS btm_send_connect_request(UINT16 acl_handle, + tBTM_ESCO_PARAMS *p_setup) +{ + UINT16 temp_pkt_types; + 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 */ + 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 */ + p_acl = btm_handle_to_acl(acl_handle); + if (p_acl) { + if (!HCI_EDR_ESCO_2MPS_SUPPORTED(p_acl->peer_lmp_features[HCI_EXT_FEATURES_PAGE_0])) { + + BTM_TRACE_WARNING("BTM Remote does not support 2-EDR eSCO"); + temp_pkt_types |= (HCI_ESCO_PKT_TYPES_MASK_NO_2_EV3 | + HCI_ESCO_PKT_TYPES_MASK_NO_2_EV5); + } + if (!HCI_EDR_ESCO_3MPS_SUPPORTED(p_acl->peer_lmp_features[HCI_EXT_FEATURES_PAGE_0])) { + + BTM_TRACE_WARNING("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); + } + + /* Check to see if BR/EDR Secure Connections is being used + ** If so, we cannot use SCO-only packet types (HFP 1.7) + */ + if (BTM_BothEndsSupportSecureConnections(p_acl->remote_addr)) { + temp_pkt_types &= ~(BTM_SCO_PKT_TYPE_MASK); + BTM_TRACE_DEBUG("%s: SCO Conn: pkt_types after removing SCO (0x%04x)", __FUNCTION__, + temp_pkt_types); + + /* Return error if no packet types left */ + if (temp_pkt_types == 0) { + BTM_TRACE_ERROR("%s: SCO Conn (BR/EDR SC): No packet types available",__FUNCTION__); + return (BTM_WRONG_MODE); + } + } else { + BTM_TRACE_DEBUG("%s: SCO Conn(BR/EDR SC):local or peer does not support BR/EDR SC",__FUNCTION__); + } + } + + + BTM_TRACE_API("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_ERROR("btm_accept_sco_link: Invalid sco_inx(%d)", sco_inx); + return; + } + + /* Link role is ignored in for this message */ + p_sco = &btm_cb.sco_cb.sco_db[sco_inx]; + p_sco->p_conn_cb = p_conn_cb; + p_sco->p_disc_cb = p_disc_cb; + p_sco->esco.data.link_type = BTM_LINK_TYPE_ESCO; /* Accept with all supported types */ + + BTM_TRACE_DEBUG("TCS accept SCO: Packet Types 0x%04x", p_setup->packet_types); + + btm_esco_conn_rsp(sco_inx, HCI_SUCCESS, p_sco->esco.data.bd_addr, p_setup); +#else + btm_reject_sco_link(sco_inx); +#endif +} + +/******************************************************************************* +** +** Function btm_reject_sco_link +** +** Description This function is called to respond to SCO connect indications +** +** Returns void +** +*******************************************************************************/ +void btm_reject_sco_link( UINT16 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_SCO_WAKE_PARKED_LINK == TRUE) + tBTM_PM_MODE md; + tBTM_PM_PWR_MD pm; +#else // BTM_SCO_WAKE_PARKED_LINK + UINT8 mode; +#endif // BTM_SCO_WAKE_PARKED_LINK + + *p_sco_inx = BTM_INVALID_SCO_INDEX; + + /* If originating, ensure that there is an ACL connection to the BD Address */ + if (is_orig) { + if ((!remote_bda) || ((acl_handle = BTM_GetHCIConnHandle (remote_bda, BT_TRANSPORT_BR_EDR)) == 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_SCO_WAKE_PARKED_LINK == TRUE + if (BTM_ReadPowerMode(remote_bda, &md) == BTM_SUCCESS) { + if (md == BTM_PM_MD_PARK || md == BTM_PM_MD_SNIFF) { + memset( (void *)&pm, 0, sizeof(pm)); + pm.mode = BTM_PM_MD_ACTIVE; + BTM_SetPowerMode(BTM_PM_SET_ONLY_ID, remote_bda, &pm); + p->state = SCO_ST_PEND_UNPARK; + } + } +#else // BTM_SCO_WAKE_PARKED_LINK + if ( (BTM_ReadPowerMode(remote_bda, &mode) == BTM_SUCCESS) && (mode == BTM_PM_MD_PARK) ) { + return (BTM_WRONG_MODE); + } +#endif // BTM_SCO_WAKE_PARKED_LINK + } + 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 (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, BT_TRANSPORT_BR_EDR); + if (p_acl && p_acl->switch_role_state != BTM_ACL_SWKEY_STATE_IDLE) { + BTM_TRACE_API("Role Change is in progress for ACL handle 0x%04x", acl_handle); + p->state = SCO_ST_PEND_ROLECHANGE; + + } + } + } + + if ( p->state != SCO_ST_PEND_UNPARK && p->state != SCO_ST_PEND_ROLECHANGE ) { + if (is_orig) { + BTM_TRACE_API("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_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, BT_TRANSPORT_BR_EDR)) == hci_handle)) + + { + BTM_TRACE_API("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 // BTM_MAX_SCO_LINKS +} +#endif // BTM_SCO_WAKE_PARKED_LINK + +/******************************************************************************* +** +** Function btm_sco_chk_pend_rolechange +** +** Description This function is called by BTIF when there is a role change +** event to see if there are SCO commands waiting for the role change. +** +** Returns void +** +*******************************************************************************/ +void btm_sco_chk_pend_rolechange (UINT16 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, BT_TRANSPORT_BR_EDR)) == hci_handle)) + + { + BTM_TRACE_API("btm_sco_chk_pend_rolechange -> (e)SCO Link for ACL handle 0x%04x", acl_handle); + + if ((btm_send_connect_request(acl_handle, &p->esco.setup)) == BTM_CMD_STARTED) { + p->state = SCO_ST_CONNECTING; + } + } + } +#endif +} + +/******************************************************************************* +** +** Function btm_sco_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 + */ + int rem_bd_matches = p->rem_bd_known && + !memcmp (p->esco.data.bd_addr, bda, BD_ADDR_LEN); + if (((p->state == SCO_ST_CONNECTING) && rem_bd_matches) || + ((p->state == SCO_ST_LISTENING) && (rem_bd_matches || !p->rem_bd_known))) { + /* If this guy was a wildcard, he is not one any more */ + p->rem_bd_known = TRUE; + p->esco.data.link_type = link_type; + p->state = SCO_ST_W4_CONN_RSP; + 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_WARNING("btm_sco_conn_req: No one wants this SCO connection; rejecting it"); + btm_esco_conn_rsp(BTM_MAX_SCO_LINKS, HCI_ERR_HOST_REJECT_RESOURCES, bda, NULL); +} + +/******************************************************************************* +** +** Function btm_sco_connected +** +** Description This function is called by BTIF when an (e)SCO connection +** is connected. +** +** Returns void +** +*******************************************************************************/ +void btm_sco_connected (UINT8 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; + BTM_TRACE_API("%s, handle %x", __FUNCTION__, hci_handle); +#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_API("Role Change pending for HCI handle 0x%04x", hci_handle); + p->state = SCO_ST_PEND_ROLECHANGE; + } + /* avoid calling disconnect callback because of sco creation race */ + else if (hci_status != HCI_ERR_LMP_ERR_TRANS_COLLISION) { + p->state = SCO_ST_UNUSED; + (*p->p_disc_cb)(xx); + } + } else { + /* Notify the upper layer that incoming sco connection has failed. */ + if (p->state == SCO_ST_CONNECTING) { + p->state = SCO_ST_UNUSED; + (*p->p_disc_cb)(xx); + } else { + p->state = SCO_ST_LISTENING; + } + } + + return; + } + + if (p->state == SCO_ST_LISTENING) { + spt = TRUE; + } +#if BTM_SCO_HCI_INCLUDED == TRUE + p->sent_not_acked = 0; + btm_pkt_stat_nums_reset(xx); +#endif + 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; +#if BTM_SCO_HCI_INCLUDED == TRUE + btm_cb.sco_cb.xmit_window_size += p->sent_not_acked; + /* avoid overflow */ + if (btm_cb.sco_cb.xmit_window_size > btm_cb.sco_cb.num_lm_sco_bufs) { + btm_cb.sco_cb.xmit_window_size = btm_cb.sco_cb.num_lm_sco_bufs; + } + p->sent_not_acked = 0; +#endif + 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 */ + p_def->packet_types |= BTM_SCO_EXCEPTION_PKTS_MASK; + } + } + p_esco->desired_sco_mode = sco_mode; + BTM_TRACE_API("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_API("BTM_SetEScoMode -> mode SCO (eSCO not supported)"); + } + + BTM_TRACE_DEBUG(" txbw 0x%08x, rxbw 0x%08x, max_lat 0x%04x, voice 0x%04x, pkt 0x%04x, rtx effort 0x%02x", + p_def->tx_bw, p_def->rx_bw, p_def->max_latency, + p_def->voice_contfmt, p_def->packet_types, + p_def->retrans_effort); + + return (BTM_SUCCESS); +} + + + +/******************************************************************************* +** +** Function BTM_RegForEScoEvts +** +** Description This function registers a SCO event callback with the +** specified instance. It should be used to received +** connection indication events and change of link parameter +** events. +** +** Returns BTM_SUCCESS if the successful. +** BTM_ILLEGAL_VALUE if there is an illegal sco_inx +** BTM_MODE_UNSUPPORTED if controller version is not BT1.2 or +** later or does not support eSCO. +** +*******************************************************************************/ +tBTM_STATUS BTM_RegForEScoEvts (UINT16 sco_inx, tBTM_ESCO_CBACK *p_esco_cback) +{ +#if (BTM_MAX_SCO_LINKS>0) + if (!btm_cb.sco_cb.esco_supported) { + btm_cb.sco_cb.sco_db[sco_inx].esco.p_esco_cback = NULL; + return (BTM_MODE_UNSUPPORTED); + } + + if (sco_inx < BTM_MAX_SCO_LINKS && + btm_cb.sco_cb.sco_db[sco_inx].state != SCO_ST_UNUSED) { + btm_cb.sco_cb.sco_db[sco_inx].esco.p_esco_cback = p_esco_cback; + return (BTM_SUCCESS); + } + return (BTM_ILLEGAL_VALUE); +#else + return (BTM_MODE_UNSUPPORTED); +#endif +} + +/******************************************************************************* +** +** Function BTM_ReadEScoLinkParms +** +** Description This function returns the current eSCO link parameters for +** the specified handle. This can be called anytime a connection +** is active, but is typically called after receiving the SCO +** opened callback. +** +** Note: If called over a 1.1 controller, only the packet types +** field has meaning. +** +** Returns BTM_SUCCESS if returned data is valid connection. +** BTM_WRONG_MODE if no connection with a peer device or bad sco_inx. +** +*******************************************************************************/ +tBTM_STATUS BTM_ReadEScoLinkParms (UINT16 sco_inx, tBTM_ESCO_DATA *p_parms) +{ +#if (BTM_MAX_SCO_LINKS>0) + UINT8 index; + + BTM_TRACE_API("BTM_ReadEScoLinkParms -> sco_inx 0x%04x", sco_inx); + + if (sco_inx < BTM_MAX_SCO_LINKS && + btm_cb.sco_cb.sco_db[sco_inx].state >= SCO_ST_CONNECTED) { + *p_parms = btm_cb.sco_cb.sco_db[sco_inx].esco.data; + return (BTM_SUCCESS); + } + + if (sco_inx == BTM_FIRST_ACTIVE_SCO_INDEX) { + for (index = 0; index < BTM_MAX_SCO_LINKS; index++) { + if (btm_cb.sco_cb.sco_db[index].state >= SCO_ST_CONNECTED) { + BTM_TRACE_API("BTM_ReadEScoLinkParms the first active SCO index is %d", index); + *p_parms = btm_cb.sco_cb.sco_db[index].esco.data; + return (BTM_SUCCESS); + } + } + } + +#endif + + BTM_TRACE_API("BTM_ReadEScoLinkParms cannot find the SCO index!"); + memset(p_parms, 0, sizeof(tBTM_ESCO_DATA)); + return (BTM_WRONG_MODE); +} + +/******************************************************************************* +** +** Function BTM_ChangeEScoLinkParms +** +** Description This function requests renegotiation of the parameters on +** the current eSCO Link. If any of the changes are accepted +** by the controllers, the BTM_ESCO_CHG_EVT event is sent in +** the tBTM_ESCO_CBACK function with the current settings of +** the link. The callback is registered through the call to +** BTM_SetEScoMode. +** +** Note: If called over a SCO link (including 1.1 controller), +** a change packet type request is sent out instead. +** +** Returns BTM_CMD_STARTED if command is successfully initiated. +** BTM_NO_RESOURCES - not enough resources to initiate command. +** BTM_WRONG_MODE if no connection with a peer device or bad sco_inx. +** +*******************************************************************************/ +tBTM_STATUS BTM_ChangeEScoLinkParms (UINT16 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_API("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 */ + 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_API("BTM_ChangeEScoLinkParms -> eSCO Link for handle 0x%04x", p_sco->hci_handle); + BTM_TRACE_API(" 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_EVENT("btm_esco_proc_conn_chg -> handle 0x%04x, status 0x%02x", + handle, status); + + for (xx = 0; xx < BTM_MAX_SCO_LINKS; xx++, p++) { + if (p->state == SCO_ST_CONNECTED && handle == p->hci_handle) { + /* If upper layer wants notification */ + if (p->esco.p_esco_cback) { + 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/lib/bt/host/bluedroid/stack/btm/btm_sec.c b/lib/bt/host/bluedroid/stack/btm/btm_sec.c new file mode 100644 index 00000000..7d06a45e --- /dev/null +++ b/lib/bt/host/bluedroid/stack/btm/btm_sec.c @@ -0,0 +1,6382 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * This file contains functions for the Bluetooth Security Manager + * + ******************************************************************************/ + +//#define LOG_TAG "bt_btm_sec" + +#include <stdarg.h> +#include <string.h> + +#include "stack/bt_types.h" +#include "device/controller.h" +#include "stack/hcimsgs.h" +#include "stack/btu.h" +#include "btm_int.h" +#include "l2c_int.h" +#include "osi/fixed_queue.h" +#include "osi/alarm.h" +#include "stack/btm_ble_api.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 (5000) + +#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 * +*********************************************************************************/ +#if (SMP_INCLUDED == TRUE) +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); +#endif ///SMP_INCLUDED == TRUE +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) +static char *btm_pair_state_descr (tBTM_PAIRING_STATE state); +#endif +#if (SMP_INCLUDED == TRUE) +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); +#endif ///SMP_INCLUDED == TRUE +BOOLEAN btm_sec_are_all_trusted(UINT32 p_mask[]); +#if (SMP_INCLUDED == TRUE) +static tBTM_STATUS btm_sec_send_hci_disconnect (tBTM_SEC_DEV_REC *p_dev_rec, UINT8 reason, UINT16 conn_handle); +#endif ///SMP_INCLUDED == TRUE +UINT8 btm_sec_start_role_switch (tBTM_SEC_DEV_REC *p_dev_rec); +tBTM_SEC_DEV_REC *btm_sec_find_dev_by_sec_state (UINT8 state); + +static BOOLEAN btm_sec_set_security_level ( CONNECTION_TYPE conn_type, const char *p_name, UINT8 service_id, + UINT16 sec_level, UINT16 psm, UINT32 mx_proto_id, + UINT32 mx_chan_id); +#if (SMP_INCLUDED == TRUE) +static BOOLEAN btm_dev_authenticated(tBTM_SEC_DEV_REC *p_dev_rec); +static BOOLEAN btm_dev_encrypted(tBTM_SEC_DEV_REC *p_dev_rec); +static BOOLEAN btm_dev_authorized(tBTM_SEC_DEV_REC *p_dev_rec); +static BOOLEAN btm_serv_trusted(tBTM_SEC_DEV_REC *p_dev_rec, tBTM_SEC_SERV_REC *p_serv_rec); +static BOOLEAN btm_sec_is_serv_level0 (UINT16 psm); +static UINT16 btm_sec_set_serv_level4_flags (UINT16 cur_security, BOOLEAN is_originator); + +static BOOLEAN btm_sec_queue_encrypt_request (BD_ADDR bd_addr, tBT_TRANSPORT transport, + tBTM_SEC_CALLBACK *p_callback, void *p_ref_data); +static void btm_sec_check_pending_enc_req (tBTM_SEC_DEV_REC *p_dev_rec, tBT_TRANSPORT transport, + UINT8 encr_enable); +static BOOLEAN btm_sec_use_smp_br_chnl(tBTM_SEC_DEV_REC *p_dev_rec); +static BOOLEAN btm_sec_is_master(tBTM_SEC_DEV_REC *p_dev_rec); +#endif ///SMP_INCLUDED == TRUE + +/* 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_dev_authenticated +** +** Description check device is authenticated +** +** Returns BOOLEAN TRUE or FALSE +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +static BOOLEAN btm_dev_authenticated (tBTM_SEC_DEV_REC *p_dev_rec) +{ + if (p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED) { + return (TRUE); + } + return (FALSE); +} + +/******************************************************************************* +** +** Function btm_dev_encrypted +** +** Description check device is encrypted +** +** Returns BOOLEAN TRUE or FALSE +** +*******************************************************************************/ +static BOOLEAN btm_dev_encrypted (tBTM_SEC_DEV_REC *p_dev_rec) +{ + if (p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED) { + return (TRUE); + } + return (FALSE); +} + +/******************************************************************************* +** +** Function btm_dev_authorized +** +** Description check device is authorized +** +** Returns BOOLEAN TRUE or FALSE +** +*******************************************************************************/ +static BOOLEAN btm_dev_authorized (tBTM_SEC_DEV_REC *p_dev_rec) +{ + if (p_dev_rec->sec_flags & BTM_SEC_AUTHORIZED) { + return (TRUE); + } + return (FALSE); +} + +/******************************************************************************* +** +** Function btm_dev_16_digit_authenticated +** +** Description check device is authenticated by using 16 digit pin or MITM +** +** Returns BOOLEAN TRUE or FALSE +** +*******************************************************************************/ +static BOOLEAN btm_dev_16_digit_authenticated(tBTM_SEC_DEV_REC *p_dev_rec) +{ + // BTM_SEC_16_DIGIT_PIN_AUTHED is set if MITM or 16 digit pin is used + if (p_dev_rec->sec_flags & BTM_SEC_16_DIGIT_PIN_AUTHED) { + return (TRUE); + } + return (FALSE); +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function btm_serv_trusted +** +** Description check service is trusted +** +** Returns BOOLEAN TRUE or FALSE +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +static BOOLEAN btm_serv_trusted(tBTM_SEC_DEV_REC *p_dev_rec, tBTM_SEC_SERV_REC *p_serv_rec) +{ + if (BTM_SEC_IS_SERVICE_TRUSTED(p_dev_rec->trusted_mask, p_serv_rec->service_id)) { + return (TRUE); + } + return (FALSE); +} +#endif ///SMP_INCLUDED == TRUE +/******************************************************************************* +** +** 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 && SMP_INCLUDED == TRUE + BT_OCTET16 temp_value = {0}; +#endif + + BTM_TRACE_EVENT("%s application registered\n", __func__); + +#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE + BTM_TRACE_DEBUG("%s p_cb_info->p_le_callback == 0x%p\n", __func__, p_cb_info->p_le_callback); + if (p_cb_info->p_le_callback) { + BTM_TRACE_EVENT("%s SMP_Register( btm_proc_smp_cback )\n", __func__); + SMP_Register(btm_proc_smp_cback); + /* 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(); + } +#if (CONTROLLER_RPA_LIST_ENABLE == TRUE) + else { + btm_ble_add_default_entry_to_resolving_list(); + } +#endif + } else { + BTM_TRACE_WARNING("%s p_cb_info->p_le_callback == NULL\n", __func__); + } +#endif + + btm_cb.api = *p_cb_info; +#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE + BTM_TRACE_DEBUG("%s btm_cb.api.p_le_callback = 0x%p\n", __func__, btm_cb.api.p_le_callback); +#endif + BTM_TRACE_EVENT("%s application registered\n", __func__); + 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 +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) || (CLASSIC_BT_INCLUDED == TRUE) +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); +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function BTM_SecDeleteRmtNameNotifyCallback +** +** Description Any profile can deregister notification when a new Link Key +** is generated per connection. +** +** Returns TRUE if OK, else FALSE +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) || (CLASSIC_BT_INCLUDED == TRUE) +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); +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** 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 = (UINT8) p_dev_rec->sec_flags; + return (TRUE); + } + BTM_TRACE_ERROR ("BTM_GetSecurityFlags false"); + return (FALSE); +} + +/******************************************************************************* +** +** Function BTM_GetSecurityFlagsByTransport +** +** Description Get security flags for the device on a particular transport +** +** Returns BOOLEAN TRUE or FALSE is device found +** +*******************************************************************************/ +BOOLEAN BTM_GetSecurityFlagsByTransport (BD_ADDR bd_addr, UINT8 *p_sec_flags, + tBT_TRANSPORT transport) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + + if ((p_dev_rec = btm_find_dev (bd_addr)) != NULL) { + if (transport == BT_TRANSPORT_BR_EDR) { + *p_sec_flags = (UINT8) p_dev_rec->sec_flags; + } else { + *p_sec_flags = (UINT8) (p_dev_rec->sec_flags >> 8); + } + + return (TRUE); + } + BTM_TRACE_ERROR ("BTM_GetSecurityFlags false\n"); + return (FALSE); +} + +#if (CLASSIC_BT_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_API ("BTM_SetPinType: pin type %d [variable-0, fixed-1], code %s, length %d\n", + 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) + && controller_get_interface()->get_is_ready() ) { + 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); +} +#endif ///CLASSIC_BT_INCLUDED == TRUE + +/******************************************************************************* +** +** 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_API ("BTM_SetPairableMode() allow_pairing: %u connect_only_paired: %u\n", allow_pairing, connect_only_paired); + + btm_cb.pairing_disabled = !allow_pairing; + btm_cb.connect_only_paired = connect_only_paired; +} + +/******************************************************************************* +** +** Function BTM_SetSecureConnectionsOnly +** +** Description Enable or disable default treatment for Mode 4 Level 0 services +** +** Parameter secure_connections_only_mode - (TRUE or FALSE) whether or not the device +** TRUE means that the device should treat Mode 4 Level 0 services as +** services of other levels. (Secure_connections_only_mode) +** FALSE means that the device should provide default treatment for +** Mode 4 Level 0 services. +** +** Returns void +** +*******************************************************************************/ +void BTM_SetSecureConnectionsOnly (BOOLEAN secure_connections_only_mode) +{ + BTM_TRACE_API("%s: Mode : %u\n", __FUNCTION__, + secure_connections_only_mode); + + btm_cb.devcb.secure_connections_only = secure_connections_only_mode; + btm_cb.security_mode = BTM_SEC_MODE_SC; +} +#define BTM_NO_AVAIL_SEC_SERVICES ((UINT16) 0xffff) + +/******************************************************************************* +** +** 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, const 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, const char *p_name, UINT8 service_id, + UINT16 sec_level, UINT16 psm, UINT32 mx_proto_id, + UINT32 mx_chan_id) +{ +#if (SMP_INCLUDED == TRUE) + 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 + + BTM_TRACE_API("%s : sec: 0x%x\n", __func__, sec_level); + + /* 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_WARNING("BTM_SEC_REG: Out of Service Records (%d)\n", 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, p_name, BTM_SEC_SERVICE_NAME_LEN); + p_srec->orig_service_name[BTM_SEC_SERVICE_NAME_LEN] = '\0'; +#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 | BTM_SEC_IN_MIN_16_DIGIT_PIN ); + + if (btm_cb.security_mode == BTM_SEC_MODE_SP || + btm_cb.security_mode == BTM_SEC_MODE_SP_DEBUG || + btm_cb.security_mode == BTM_SEC_MODE_SC) { + 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, p_name, BTM_SEC_SERVICE_NAME_LEN); + p_srec->term_service_name[BTM_SEC_SERVICE_NAME_LEN] = '\0'; +#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 + | BTM_SEC_IN_MIN_16_DIGIT_PIN); + } 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 + | BTM_SEC_IN_MIN_16_DIGIT_PIN); + } + + /* 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 || + btm_cb.security_mode == BTM_SEC_MODE_SP_DEBUG || + btm_cb.security_mode == BTM_SEC_MODE_SC) { + 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_API("BTM_SEC_REG[%d]: id %d, conn_type 0x%x, psm 0x%04x, proto_id %d, chan_id %d\n", + index, service_id, conn_type, psm, mx_proto_id, mx_chan_id); + + BTM_TRACE_API(" : security_flags: 0x%04x, ucd_security_flags: 0x%04x\n", + p_srec->security_flags, p_srec->ucd_security_flags); + +#if BTM_SEC_SERVICE_NAME_LEN > 0 + BTM_TRACE_API(" : service name [%s] (up to %d chars saved)\n", + p_name, BTM_SEC_SERVICE_NAME_LEN); +#endif +#else + p_srec->security_flags |= (UINT16)(sec_level | BTM_SEC_IN_USE); + + BTM_TRACE_API("BTM_SEC_REG[%d]: id %d, is_orig %d, psm 0x%04x, proto_id %d, chan_id %d\n", + index, service_id, is_originator, psm, mx_proto_id, mx_chan_id); + +#if BTM_SEC_SERVICE_NAME_LEN > 0 + BTM_TRACE_API(" : sec: 0x%x, service name [%s] (up to %d chars saved)\n", + p_srec->security_flags, p_name, BTM_SEC_SERVICE_NAME_LEN); +#endif +#endif + + + return (record_allocated); +#else + return FALSE; +#endif ///SMP_INCLUDED == TRUE +} + +/******************************************************************************* +** +** 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. +** +*******************************************************************************/ +#if (SDP_INCLUDED == TRUE) +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_API("BTM_SEC_CLR[%d]: id %d\n", 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); +} +#endif ///SDP_INCLUDED == TRUE + +/******************************************************************************* +** +** 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. +** +*******************************************************************************/ +#if (SDP_INCLUDED== TRUE) +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_API("BTM_SEC_CLR[%d]: id %d\n", i, p_srec->service_id); + p_srec->security_flags = 0; + num_freed++; + } + } + BTM_TRACE_API("btm_sec_clr_service_by_psm psm:0x%x num_freed:%d\n", psm, num_freed); + + return (num_freed); +} +#endif ///SDP_INCLUDED== TRUE + +/******************************************************************************* +** +** Function btm_sec_clr_temp_auth_service +** +** Description Removes specified device record's temporary authorization +** flag from the security database. +** +** Parameters Device address to be cleared +** +** Returns void. +** +*******************************************************************************/ +void btm_sec_clr_temp_auth_service (BD_ADDR bda) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + + if ((p_dev_rec = btm_find_dev (bda)) == NULL) { + BTM_TRACE_WARNING ("btm_sec_clr_temp_auth_service() - no dev CB\n"); + return; + } + + /* Reset the temporary authorized flag so that next time (untrusted) service is accessed autorization will take place */ + if (p_dev_rec->last_author_service_id != BTM_SEC_NO_LAST_SERVICE_ID && p_dev_rec->p_cur_service) { + BTM_TRACE_DEBUG ("btm_sec_clr_auth_service_by_psm [clearing device: %02x:%02x:%02x:%02x:%02x:%02x]\n", + bda[0], bda[1], bda[2], bda[3], bda[4], bda[5]); + + p_dev_rec->last_author_service_id = BTM_SEC_NO_LAST_SERVICE_ID; + } +} + +/******************************************************************************* +** +** 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) +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +#if (CLASSIC_BT_INCLUDED == TRUE) +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; +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + BTM_TRACE_API ("BTM_PINCodeReply(): PairState: %s PairFlags: 0x%02x PinLen:%d Result:%d\n", + btm_pair_state_descr(btm_cb.pairing_state), btm_cb.pairing_flags, pin_len, res); +#endif ///BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE + /* If timeout already expired or has been canceled, ignore the reply */ + if (btm_cb.pairing_state != BTM_PAIR_STATE_WAIT_LOCAL_PIN) { + BTM_TRACE_WARNING ("BTM_PINCodeReply() - Wrong State: %d\n", btm_cb.pairing_state); + return; + } + + if (memcmp (bd_addr, btm_cb.pairing_bda, BD_ADDR_LEN) != 0) { + BTM_TRACE_ERROR ("BTM_PINCodeReply() - Wrong BD Addr\n"); + return; + } + + if ((p_dev_rec = btm_find_dev (bd_addr)) == NULL) { + BTM_TRACE_ERROR ("BTM_PINCodeReply() - no dev CB\n"); + return; + } + + if ( (pin_len > PIN_CODE_LEN) || (pin_len == 0) || (p_pin == NULL) ) { + res = BTM_ILLEGAL_VALUE; + } + + if (res != BTM_SUCCESS) { + /* if peer started dd OR we started dd and pre-fetch pin was not used send negative reply */ + if ((btm_cb.pairing_flags & BTM_PAIR_FLAGS_PEER_STARTED_DD) || + ((btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_STARTED_DD) && + (btm_cb.pairing_flags & BTM_PAIR_FLAGS_DISC_WHEN_DONE)) ) { + /* 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 (pin_len >= 16) { + p_dev_rec->sec_flags |= BTM_SEC_16_DIGIT_PIN_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; + p_dev_rec->pin_code_length = 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_WARNING ("BTM_PINCodeReply(): waiting HCI_Connection_Complete after rejected incoming connection\n"); + /* 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_WARNING ("BTM_PINCodeReply(): link is connecting so wait pin code request from peer\n"); + 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; + + 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_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_EVENT("BTM_PINCodeReply(): Saving pin_len: %d btm_cb.pin_code_len: %d\n", 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); +} +#endif ///CLASSIC_BT_INCLUDED == TRUE +#endif ///SMP_INCLUDED == TRUE + + +/******************************************************************************* +** +** Function btm_sec_bond_by_transport +** +** Description this is the bond function that will start either SSP or SMP. +** +** 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 +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +tBTM_STATUS btm_sec_bond_by_transport (BD_ADDR bd_addr, tBT_TRANSPORT transport, + UINT8 pin_len, UINT8 *p_pin, UINT32 trusted_mask[]) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + tBTM_STATUS status; +#if (!CONFIG_BT_STACK_NO_LOG) + UINT8 *p_features; +#endif + UINT8 ii; + tACL_CONN *p = btm_bda_to_acl(bd_addr, transport); + BTM_TRACE_API ("btm_sec_bond_by_transport BDA: %02x:%02x:%02x:%02x:%02x:%02x\n", + bd_addr[0], bd_addr[1], bd_addr[2], bd_addr[3], bd_addr[4], bd_addr[5]); + + BTM_TRACE_DEBUG("btm_sec_bond_by_transport: Transport used %d\n" , transport); + + + /* Other security process is in progress */ + if (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) { +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + BTM_TRACE_ERROR ("BTM_SecBond: already busy in state: %s\n", btm_pair_state_descr(btm_cb.pairing_state)); +#endif ///BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE + return (BTM_WRONG_MODE); + } + + if ((p_dev_rec = btm_find_or_alloc_dev (bd_addr)) == NULL) { + return (BTM_NO_RESOURCES); + } + + BTM_TRACE_DEBUG ("before update sec_flags=0x%x\n", p_dev_rec->sec_flags); + + /* Finished if connection is active and already paired */ + if ( ((p_dev_rec->hci_handle != BTM_SEC_INVALID_HANDLE) && transport == BT_TRANSPORT_BR_EDR + && (p_dev_rec->sec_flags & BTM_SEC_AUTHENTICATED)) +#if (BLE_INCLUDED == TRUE) + || ((p_dev_rec->ble_hci_handle != BTM_SEC_INVALID_HANDLE) && transport == BT_TRANSPORT_LE + && (p_dev_rec->sec_flags & BTM_SEC_LE_AUTHENTICATED)) +#endif + + ) { + BTM_TRACE_WARNING("BTM_SecBond -> Already Paired\n"); + return (BTM_SUCCESS); + } + + /* 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); + } + +#if (CLASSIC_BT_INCLUDED == TRUE) + /* 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; + p_dev_rec->pin_code_length = pin_len; + memcpy (btm_cb.pin_code, p_pin, PIN_CODE_LEN); + } +#endif ///CLASSIC_BT_INCLUDED == TRUE + + 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 BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE + if (transport == BT_TRANSPORT_LE) { + btm_ble_init_pseudo_addr (p_dev_rec, bd_addr); + p_dev_rec->sec_flags &= ~ BTM_SEC_LE_MASK; + + if (SMP_Pair(bd_addr) == SMP_STARTED) { + btm_cb.pairing_flags |= BTM_PAIR_FLAGS_LE_ACTIVE; + p_dev_rec->sec_state = BTM_SEC_STATE_AUTHENTICATING; + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_AUTH_COMPLETE); + return BTM_CMD_STARTED; + } + + btm_cb.pairing_flags = 0; + return (BTM_NO_RESOURCES); + } +#endif + + 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); + + + BTM_TRACE_DEBUG ("after update sec_flags=0x%x\n", p_dev_rec->sec_flags); + +#if (CLASSIC_BT_INCLUDED == TRUE) + if (!controller_get_interface()->supports_simple_pairing()) { + /* 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); + } + } +#endif ///CLASSIC_BT_INCLUDED == TRUE + + for (ii = 0; ii <= HCI_EXT_FEATURES_PAGE_MAX; ii++) { +#if (!CONFIG_BT_STACK_NO_LOG) + p_features = p_dev_rec->features[ii]; +#endif + BTM_TRACE_EVENT(" remote_features page[%1d] = %02x-%02x-%02x-%02x\n", + ii, p_features[0], p_features[1], p_features[2], p_features[3]); + BTM_TRACE_EVENT(" %02x-%02x-%02x-%02x\n", + p_features[4], p_features[5], p_features[6], p_features[7]); + } + + BTM_TRACE_EVENT ("BTM_SecBond: Remote sm4: 0x%x HCI Handle: 0x%04x\n", 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 && p->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_DEBUG ("sec mode: %d sm4:x%x\n", btm_cb.security_mode, p_dev_rec->sm4); + if (!controller_get_interface()->supports_simple_pairing() + || (p_dev_rec->sm4 == BTM_SM4_KNOWN)) { + if ( btm_sec_check_prefetch_pin (p_dev_rec) ) { + return (BTM_CMD_STARTED); + } + } + if ((btm_cb.security_mode == BTM_SEC_MODE_SP || + btm_cb.security_mode == BTM_SEC_MODE_SP_DEBUG || + btm_cb.security_mode == BTM_SEC_MODE_SC) && + 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, BT_TRANSPORT_BR_EDR); + } else { + /* We are accepting connection request from peer */ + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_PIN_REQ); + } +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + BTM_TRACE_DEBUG ("State:%s sm4: 0x%x sec_state:%d\n", + btm_pair_state_descr (btm_cb.pairing_state), p_dev_rec->sm4, p_dev_rec->sec_state); +#endif ///BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE + 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_SecBondByTransport +** +** 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 +** transport - doing SSP over BR/EDR or SMP over LE +** 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_SecBondByTransport (BD_ADDR bd_addr, tBT_TRANSPORT transport, + UINT8 pin_len, UINT8 *p_pin, UINT32 trusted_mask[]) +{ +#if (BLE_INCLUDED == TRUE) + tBT_DEVICE_TYPE dev_type; + tBLE_ADDR_TYPE addr_type = 0; + + BTM_ReadDevInfo(bd_addr, &dev_type, &addr_type); + /* LE device, do SMP pairing */ + if ((transport == BT_TRANSPORT_LE && (dev_type & BT_DEVICE_TYPE_BLE) == 0) || + (transport == BT_TRANSPORT_BR_EDR && (dev_type & BT_DEVICE_TYPE_BREDR) == 0)) { + return BTM_ILLEGAL_ACTION; + } +#endif ///BLE_INCLUDED == TRUE + + return btm_sec_bond_by_transport(bd_addr, transport, pin_len, p_pin, trusted_mask); +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** 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 +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +tBTM_STATUS BTM_SecBond (BD_ADDR bd_addr, UINT8 pin_len, UINT8 *p_pin, UINT32 trusted_mask[]) +{ + tBT_TRANSPORT transport = BT_TRANSPORT_BR_EDR; +#if (BLE_INCLUDED == TRUE) + if (BTM_UseLeLink(bd_addr)) { + transport = BT_TRANSPORT_LE; + } +#endif ///BLE_INCLUDED == TRUE + return btm_sec_bond_by_transport(bd_addr, transport, pin_len, p_pin, trusted_mask); +} +/******************************************************************************* +** +** Function BTM_SecBondCancel +** +** Description This function is called to cancel ongoing bonding process +** with peer device. +** +** Parameters: bd_addr - Address of the peer device +** transport - FALSE for BR/EDR link; TRUE for LE link +** +*******************************************************************************/ +tBTM_STATUS BTM_SecBondCancel (BD_ADDR bd_addr) +{ + tBTM_SEC_DEV_REC *p_dev_rec; +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + BTM_TRACE_API ("BTM_SecBondCancel() State: %s flags:0x%x\n", + btm_pair_state_descr (btm_cb.pairing_state), btm_cb.pairing_flags); +#endif ///BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE + 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 + if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_LE_ACTIVE) { + if (p_dev_rec->sec_state == BTM_SEC_STATE_AUTHENTICATING) { + BTM_TRACE_DEBUG ("Cancel LE pairing\n"); + if (SMP_PairCancel(bd_addr)) { + return BTM_CMD_STARTED; + } + } + return BTM_WRONG_MODE; + } + +#endif + BTM_TRACE_DEBUG ("hci_handle:0x%x sec_state:%d\n", 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) || + (p_dev_rec->sec_state == BTM_SEC_STATE_DISCONNECTING_BOTH)) { + 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, p_dev_rec->hci_handle); + } 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; + } + if (btm_cb.pairing_state == BTM_PAIR_STATE_GET_REM_NAME) { + BTM_CancelRemoteDeviceName(); + btm_cb.pairing_flags |= BTM_PAIR_FLAGS_WE_CANCEL_DD; + return BTM_CMD_STARTED; + } + return BTM_NOT_AUTHORIZED; + } + } + return BTM_WRONG_MODE; +} + + +/******************************************************************************* +** +** 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); +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function BTM_SecGetDeviceLinkKeyType +** +** Description This function is called to obtain link key type 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 +** +** Returns BTM_LKEY_TYPE_IGNORE if link key is unknown, link type +** otherwise. +** +*******************************************************************************/ +tBTM_LINK_KEY_TYPE BTM_SecGetDeviceLinkKeyType (BD_ADDR bd_addr) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bd_addr); + + if ((p_dev_rec != NULL) && (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN)) { + return p_dev_rec->link_key_type; + } + return BTM_LKEY_TYPE_IGNORE; +} + +/******************************************************************************* +** +** 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. +** transport - TRUE to encryption the link over LE transport +** or FALSE for BR/EDR transport +** +** 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. +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +tBTM_STATUS BTM_SetEncryption (BD_ADDR bd_addr, tBT_TRANSPORT transport, tBTM_SEC_CBACK *p_callback, + void *p_ref_data) +{ + tBTM_STATUS rc = 0; + + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bd_addr); + if (!p_dev_rec || + (transport == BT_TRANSPORT_BR_EDR && p_dev_rec->hci_handle == BTM_SEC_INVALID_HANDLE) +#if BLE_INCLUDED == TRUE + || (transport == BT_TRANSPORT_LE && p_dev_rec->ble_hci_handle == BTM_SEC_INVALID_HANDLE) +#endif + ) { + /* Connection should be up and runnning */ + BTM_TRACE_WARNING ("Security Manager: BTM_SetEncryption not connected\n"); + + if (p_callback) { + (*p_callback) (bd_addr, transport, p_ref_data, BTM_WRONG_MODE); + } + + return (BTM_WRONG_MODE); + } + + if ((transport == BT_TRANSPORT_BR_EDR && + (p_dev_rec->sec_flags & BTM_SEC_ENCRYPTED)) +#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE + || (transport == BT_TRANSPORT_LE && + (p_dev_rec->sec_flags & BTM_SEC_LE_ENCRYPTED)) +#endif + ) { + BTM_TRACE_EVENT ("Security Manager: BTM_SetEncryption already encrypted\n"); + + if (p_callback) { + (*p_callback) (bd_addr, transport, p_ref_data, BTM_SUCCESS); + } + + return (BTM_SUCCESS); + } + p_dev_rec->enc_init_by_we = TRUE; + /* enqueue security request if security is active */ + if (p_dev_rec->p_callback || (p_dev_rec->sec_state != BTM_SEC_STATE_IDLE)) { + BTM_TRACE_WARNING ("Security Manager: BTM_SetEncryption busy, enqueue request\n"); + + if (btm_sec_queue_encrypt_request(bd_addr, transport, p_callback, p_ref_data)) { + return BTM_CMD_STARTED; + } else { + if (p_callback) { + (*p_callback) (bd_addr, transport, p_ref_data, BTM_NO_RESOURCES); + } + return BTM_NO_RESOURCES; + } + } + + 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_API ("Security Manager: BTM_SetEncryption Handle:%d State:%d Flags:0x%x Required:0x%x\n", + 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 (transport == BT_TRANSPORT_LE) { + tACL_CONN *p = btm_bda_to_acl(bd_addr, transport); + if (p) { + rc = btm_ble_set_encryption(bd_addr, p_ref_data, p->link_role); + } else { + rc = BTM_WRONG_MODE; + BTM_TRACE_WARNING("%s: cannot call btm_ble_set_encryption, p is NULL\n", __FUNCTION__); + } + } else +#endif + { + rc = btm_sec_execute_procedure (p_dev_rec); + } + + if (rc != BTM_CMD_STARTED && rc != BTM_BUSY) { + if (p_callback) { + p_dev_rec->p_callback = NULL; + (*p_callback) (bd_addr, transport, 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, UINT16 conn_handle) +{ + UINT8 old_state = p_dev_rec->sec_state; + tBTM_STATUS status = BTM_CMD_STARTED; + + BTM_TRACE_EVENT ("btm_sec_send_hci_disconnect: handle:0x%x, reason=0x%x\n", + conn_handle, reason); + + /* send HCI_Disconnect on a transport only once */ + switch (old_state) { + case BTM_SEC_STATE_DISCONNECTING: + if (conn_handle == p_dev_rec->hci_handle) { + return status; + } + + p_dev_rec->sec_state = BTM_SEC_STATE_DISCONNECTING_BOTH; + break; + +#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE + case BTM_SEC_STATE_DISCONNECTING_BLE: + if (conn_handle == p_dev_rec->ble_hci_handle) { + return status; + } + + p_dev_rec->sec_state = BTM_SEC_STATE_DISCONNECTING_BOTH; + break; + + case BTM_SEC_STATE_DISCONNECTING_BOTH: + return status; +#endif + + default: + p_dev_rec->sec_state = (conn_handle == p_dev_rec->hci_handle) ? + BTM_SEC_STATE_DISCONNECTING : BTM_SEC_STATE_DISCONNECTING_BLE; + + break; + } + + /* If a role switch is in progress, delay the HCI Disconnect to avoid controller problem */ + if (p_dev_rec->rs_disc_pending == BTM_SEC_RS_PENDING && p_dev_rec->hci_handle == conn_handle) { + BTM_TRACE_DEBUG("RS in progress - Set DISC Pending flag in btm_sec_send_hci_disconnect to delay disconnect\n"); + p_dev_rec->rs_disc_pending = BTM_SEC_DISC_PENDING; + status = BTM_SUCCESS; + } + /* Tear down the HCI link */ + else if (!btsnd_hcic_disconnect (conn_handle, reason)) { + /* could not send disconnect. restore old state */ + p_dev_rec->sec_state = old_state; + status = BTM_NO_RESOURCES; + } + + return status; +} +#endif ///SMP_INCLUDED == TRUE +/******************************************************************************* +** +** 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 +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +void BTM_ConfirmReqReply(tBTM_STATUS res, BD_ADDR bd_addr) +{ + tBTM_SEC_DEV_REC *p_dev_rec; +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + BTM_TRACE_EVENT ("BTM_ConfirmReqReply() State: %s Res: %u", + btm_pair_state_descr(btm_cb.pairing_state), res); +#endif ///BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE + /* 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; + p_dev_rec->sec_flags |= BTM_SEC_16_DIGIT_PIN_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); + } +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** 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 (CLASSIC_BT_INCLUDED == TRUE) +void BTM_PasskeyReqReply(tBTM_STATUS res, BD_ADDR bd_addr, UINT32 passkey) +{ +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + BTM_TRACE_API ("BTM_PasskeyReqReply: State: %s res:%d\n", + btm_pair_state_descr(btm_cb.pairing_state), res); +#endif ///BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE + 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) ) { + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bd_addr); + if (p_dev_rec != 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, p_dev_rec->hci_handle); + } 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 /* (CLASSIC_BT_INCLUDED == TRUE) */ + +/******************************************************************************* +** +** 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 (CLASSIC_BT_INCLUDED == TRUE) +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 /* (CLASSIC_BT_INCLUDED == TRUE) */ + +#if BTM_OOB_INCLUDED == TRUE && SMP_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) +{ +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + BTM_TRACE_EVENT ("BTM_IoCapRsp: state: %s oob: %d io_cap: %d\n", + btm_pair_state_descr(btm_cb.pairing_state), oob, io_cap); +#endif ///BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE + 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) +{ +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + BTM_TRACE_EVENT ("%s() - State: %s res: %d\n", __func__, + btm_pair_state_descr(btm_cb.pairing_state), res); +#endif ///BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE + /* 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; +#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, controller_get_interface()->get_address()->address); + + len = BTM_OOB_MANDATORY_SIZE; + max_len -= len; + + /* now optional part */ + + /* add Hash C */ + UINT16 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 ///BTM_OOB_INCLUDED == TRUE && SMP_INCLUDED == TRUE + +#if (CLASSIC_BT_INCLUDED == TRUE) +/******************************************************************************* +** +** Function BTM_BothEndsSupportSecureConnections +** +** Description This function is called to check if both the local device and the peer device +** specified by bd_addr support BR/EDR Secure Connections. +** +** Parameters: bd_addr - address of the peer +** +** Returns TRUE if BR/EDR Secure Connections are supported by both local +** and the remote device. +** else FALSE. +** +*******************************************************************************/ +BOOLEAN BTM_BothEndsSupportSecureConnections(BD_ADDR bd_addr) +{ + return ((controller_get_interface()->supports_secure_connections()) && + (BTM_PeerSupportsSecureConnections(bd_addr))); +} + +/******************************************************************************* +** +** Function BTM_PeerSupportsSecureConnections +** +** Description This function is called to check if the peer supports +** BR/EDR Secure Connections. +** +** Parameters: bd_addr - address of the peer +** +** Returns TRUE if BR/EDR Secure Connections are supported by the peer, +** else FALSE. +** +*******************************************************************************/ +BOOLEAN BTM_PeerSupportsSecureConnections(BD_ADDR bd_addr) +{ + tBTM_SEC_DEV_REC *p_dev_rec; + + if ((p_dev_rec = btm_find_dev(bd_addr)) == NULL) { + BTM_TRACE_WARNING("%s: unknown BDA: %08x%04x\n", __FUNCTION__, + (bd_addr[0] << 24) + (bd_addr[1] << 16) + (bd_addr[2] << 8) + bd_addr[3], + (bd_addr[4] << 8) + bd_addr[5]); + return FALSE; + } + + return (p_dev_rec->remote_supports_secure_connections); +} + +/******************************************************************************* +** +** 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]; + + btm_cb.p_out_serv = p_serv_rec; + p_dev_rec = btm_find_dev (bd_addr); + + for (int 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_API("BTM_SetOutService p_out_serv id %d, psm 0x%04x, proto_id %d, chan_id %d\n", + 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; + } + } +} +#endif ///CLASSIC_BT_INCLUDED == TRUE + + +/************************************************************************ +** I N T E R N A L F U N C T I O N S +*************************************************************************/ +/******************************************************************************* +** +** Function btm_sec_is_upgrade_possible +** +** Description This function returns TRUE if the existing link key +** can be upgraded or if the link key does not exist. +** +** Returns BOOLEAN +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +static BOOLEAN btm_sec_is_upgrade_possible(tBTM_SEC_DEV_REC *p_dev_rec, BOOLEAN is_originator) +{ + UINT16 mtm_check = is_originator ? BTM_SEC_OUT_MITM : BTM_SEC_IN_MITM; + BOOLEAN is_possible = TRUE; + + if (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN) { + is_possible = FALSE; + if (p_dev_rec->p_cur_service) { + BTM_TRACE_DEBUG ("%s() id: %d, link_key_typet: %d, rmt_io_caps: %d, chk flags: 0x%x, flags: 0x%x\n", + __func__, 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); + } else { + BTM_TRACE_DEBUG ("%s() link_key_typet: %d, rmt_io_caps: %d, chk flags: 0x%x\n", + __func__, p_dev_rec->link_key_type, p_dev_rec->rmt_io_caps, mtm_check); + } + /* 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) || + (p_dev_rec->link_key_type == BTM_LKEY_TYPE_UNAUTH_COMB_P_256)) + /* 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 */ + { + /* 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 */ + is_possible = TRUE; + } + } + BTM_TRACE_DEBUG ("%s() is_possible: %d sec_flags: 0x%x\n", __func__, is_possible, p_dev_rec->sec_flags); + return is_possible; +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function btm_sec_check_upgrade +** +** Description This function is called to check if the existing link key +** needs to be upgraded. +** +** Returns void +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +static void btm_sec_check_upgrade(tBTM_SEC_DEV_REC *p_dev_rec, BOOLEAN is_originator) +{ + + BTM_TRACE_DEBUG ("%s()\n", __func__); + + /* Only check if link key already exists */ + if (!(p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN)) { + return; + } + + if (btm_sec_is_upgrade_possible (p_dev_rec, is_originator) == TRUE) { + BTM_TRACE_DEBUG ("need upgrade!! sec_flags:0x%x\n", 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 */ + tBTM_SP_UPGRADE evt_data; + 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_DEBUG ("evt_data.upgrade:0x%x\n", 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_DEBUG ("sec_flags:0x%x\n", p_dev_rec->sec_flags); + } + } +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** 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 +** +*******************************************************************************/ +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) +{ +#if (SMP_INCLUDED == TRUE) + 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; + BOOLEAN transport = FALSE; /* should check PSM range in LE connection oriented L2CAP connection */ + +#if (L2CAP_UCD_INCLUDED == TRUE) + if (conn_type & CONNECTION_TYPE_ORIG_MASK) { + is_originator = TRUE; + } else { + is_originator = FALSE; + } + + BTM_TRACE_DEBUG ("%s() conn_type: 0x%x, %p\n", __func__, conn_type, p_ref_data); +#else + is_originator = conn_type; + + BTM_TRACE_DEBUG ("%s() is_originator:%d, %p\n", __func__, 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_WARNING ("%s() PSM: %d no application registerd\n", __func__, psm); + (*p_callback) (bd_addr, transport, p_ref_data, BTM_MODE_UNSUPPORTED); + return (BTM_MODE_UNSUPPORTED); + } + + /* Services level0 by default have no security */ + if ((btm_sec_is_serv_level0(psm)) && (!btm_cb.devcb.secure_connections_only)) { + (*p_callback) (bd_addr, transport, p_ref_data, BTM_SUCCESS_NO_SECURITY); + return (BTM_SUCCESS); + } +#if (L2CAP_UCD_INCLUDED == TRUE) + if ( conn_type & CONNECTION_TYPE_CONNLESS_MASK ) { + if (btm_cb.security_mode == BTM_SEC_MODE_SC) { + security_required = btm_sec_set_serv_level4_flags (p_serv_rec->ucd_security_flags, + is_originator); + } else { + 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))) ) { + // Check for 16 digits (or MITM) + if (((security_required & BTM_SEC_IN_MIN_16_DIGIT_PIN) == 0) || + (((security_required & BTM_SEC_IN_MIN_16_DIGIT_PIN) == BTM_SEC_IN_MIN_16_DIGIT_PIN) && + btm_dev_16_digit_authenticated(p_dev_rec))) { + rc = BTM_SUCCESS; + } + } + } + + if ((rc == BTM_SUCCESS) && (security_required & BTM_SEC_MODE4_LEVEL4) && + (p_dev_rec->link_key_type != BTM_LKEY_TYPE_AUTH_COMB_P_256)) { + rc = BTM_CMD_STARTED; + } + + if (rc == BTM_SUCCESS) { + if (p_callback) { + (*p_callback) (bd_addr, transport, (void *)p_ref_data, BTM_SUCCESS); + } + + return (BTM_SUCCESS); + } + } else +#endif + { + if (btm_cb.security_mode == BTM_SEC_MODE_SC) { + security_required = btm_sec_set_serv_level4_flags (p_serv_rec->security_flags, + is_originator); + } else { + security_required = p_serv_rec->security_flags; + } + } + + BTM_TRACE_DEBUG("%s: security_required 0x%04x, is_originator 0x%02x, psm 0x%04x\n", + __FUNCTION__, security_required, is_originator, psm); + + if ((!is_originator) && (security_required & BTM_SEC_MODE4_LEVEL4)) { + BOOLEAN local_supports_sc = controller_get_interface()->supports_secure_connections(); + /* acceptor receives L2CAP Channel Connect Request for Secure Connections Only service */ + if (!(local_supports_sc) || !(p_dev_rec->remote_supports_secure_connections)) { + BTM_TRACE_DEBUG("%s: SC only service, local_support_for_sc %d\n" + "rmt_support_for_sc : %d -> fail pairing\n", __FUNCTION__, + local_supports_sc, + p_dev_rec->remote_supports_secure_connections); + if (p_callback) { + (*p_callback) (bd_addr, transport, (void *)p_ref_data, + BTM_MODE4_LEVEL4_NOT_SUPPORTED); + } + + return (BTM_MODE4_LEVEL4_NOT_SUPPORTED); + } + } + + /* 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) ) { +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + BTM_TRACE_EVENT ("%s() - busy - PSM:%d delayed state: %s mode:%d, sm4:0x%x\n", __func__, + psm, btm_pair_state_descr(btm_cb.pairing_state), btm_cb.security_mode, p_dev_rec->sm4); +#endif ///BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE + BTM_TRACE_EVENT ("security_flags:x%x, sec_flags:x%x\n", security_required, p_dev_rec->sec_flags); + rc = BTM_CMD_STARTED; + if ((btm_cb.security_mode == BTM_SEC_MODE_UNDEFINED || + btm_cb.security_mode == BTM_SEC_MODE_NONE || + btm_cb.security_mode == BTM_SEC_MODE_SERVICE || + btm_cb.security_mode == BTM_SEC_MODE_LINK) || + (BTM_SM4_KNOWN == p_dev_rec->sm4) || (BTM_SEC_IS_SM4(p_dev_rec->sm4) && + (btm_sec_is_upgrade_possible(p_dev_rec, is_originator) == FALSE))) { + /* legacy mode - local is legacy or local is lisbon/peer is legacy + * or SM4 with no possibility of link key upgrade */ + if (is_originator) { + if (((security_required & BTM_SEC_OUT_FLAGS) == 0) || + ((((security_required & BTM_SEC_OUT_FLAGS) == BTM_SEC_OUT_AUTHENTICATE) && btm_dev_authenticated(p_dev_rec))) || + ((((security_required & BTM_SEC_OUT_FLAGS) == (BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_ENCRYPT)) && btm_dev_encrypted(p_dev_rec))) || + ((((security_required & BTM_SEC_OUT_FLAGS) == BTM_SEC_OUT_FLAGS) && btm_dev_authorized(p_dev_rec) && btm_dev_encrypted(p_dev_rec))) ) { + rc = BTM_SUCCESS; + } + } else { + if (((security_required & BTM_SEC_IN_FLAGS) == 0) || + (((security_required & BTM_SEC_IN_FLAGS) == BTM_SEC_IN_AUTHENTICATE) && btm_dev_authenticated(p_dev_rec)) || + (((security_required & BTM_SEC_IN_FLAGS) == (BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_ENCRYPT)) && btm_dev_encrypted(p_dev_rec)) || + (((security_required & BTM_SEC_IN_FLAGS) == BTM_SEC_IN_AUTHORIZE) && (btm_dev_authorized(p_dev_rec) || btm_serv_trusted(p_dev_rec, p_serv_rec))) || + (((security_required & BTM_SEC_IN_FLAGS) == (BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_AUTHORIZE)) && ((btm_dev_authorized(p_dev_rec) || btm_serv_trusted(p_dev_rec, p_serv_rec)) && btm_dev_authenticated(p_dev_rec))) || + (((security_required & BTM_SEC_IN_FLAGS) == (BTM_SEC_IN_ENCRYPT | BTM_SEC_IN_AUTHORIZE)) && ((btm_dev_authorized(p_dev_rec) || btm_serv_trusted(p_dev_rec, p_serv_rec)) && btm_dev_encrypted(p_dev_rec))) || + (((security_required & BTM_SEC_IN_FLAGS) == BTM_SEC_IN_FLAGS) && btm_dev_encrypted(p_dev_rec) && (btm_dev_authorized(p_dev_rec) || btm_serv_trusted(p_dev_rec, p_serv_rec)))) { + // Check for 16 digits (or MITM) + if (((security_required & BTM_SEC_IN_MIN_16_DIGIT_PIN) == 0) || + (((security_required & BTM_SEC_IN_MIN_16_DIGIT_PIN) == BTM_SEC_IN_MIN_16_DIGIT_PIN) && btm_dev_16_digit_authenticated(p_dev_rec))) { + rc = BTM_SUCCESS; + } + } + } + + if ((rc == BTM_SUCCESS) && (security_required & BTM_SEC_MODE4_LEVEL4) && + (p_dev_rec->link_key_type != BTM_LKEY_TYPE_AUTH_COMB_P_256)) { + rc = BTM_CMD_STARTED; + } + + if (rc == BTM_SUCCESS) { + if (p_callback) { + (*p_callback) (bd_addr, transport, (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; + + /* Modify security_required in btm_sec_l2cap_access_req for Lisbon */ + if (btm_cb.security_mode == BTM_SEC_MODE_SP || + btm_cb.security_mode == BTM_SEC_MODE_SP_DEBUG || + btm_cb.security_mode == BTM_SEC_MODE_SC) { + if (BTM_SEC_IS_SM4(p_dev_rec->sm4)) { + if (is_originator) { + /* SM4 to SM4 -> always authenticate & encrypt */ + security_required |= (BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_ENCRYPT); + } else { /* acceptor */ + /* 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)) { + /* the remote features are not known yet */ + BTM_TRACE_ERROR("%s: (%s) remote features unknown!!sec_flags:0x%02x\n", __FUNCTION__, + (is_originator) ? "initiator" : "acceptor", p_dev_rec->sec_flags); + + p_dev_rec->sm4 |= BTM_SM4_REQ_PEND; + return (BTM_CMD_STARTED); + } + } + + BTM_TRACE_DEBUG ("%s() sm4:0x%x, sec_flags:0x%x, security_required:0x%x chk:%d\n", __func__, + 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_DEBUG ("no next_serv sm4:0x%x, chk:%d\n", p_dev_rec->sm4, chk_acp_auth_done); + if (!BTM_SEC_IS_SM4(p_dev_rec->sm4)) { + BTM_TRACE_EVENT ("Security Manager: l2cap_access_req PSM:%d postponed for multiplexer\n", 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, transport, 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_UNDEFINED || + btm_cb.security_mode == BTM_SEC_MODE_NONE || + btm_cb.security_mode == BTM_SEC_MODE_SERVICE || + btm_cb.security_mode == BTM_SEC_MODE_LINK) || + !BTM_SEC_IS_SM4(p_dev_rec->sm4)) && (psm >= 0x1001)) { + BTM_TRACE_EVENT ("dynamic PSM:0x%x in legacy mode - postponed for upper layer\n", 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, transport, p_ref_data, BTM_SUCCESS); + + return (BTM_SUCCESS); + } + + if (chk_acp_auth_done) { + BTM_TRACE_DEBUG ("(SM4 to SM4) btm_sec_l2cap_access_req rspd. authenticated: x%x, enc: x%x\n", + (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_API("%s peer should have initiated security process by now (SM4 to SM4)\n", __func__); + p_dev_rec->p_callback = p_callback; + p_dev_rec->sec_state = BTM_SEC_STATE_DELAY_FOR_ENC; + (*p_callback) (bd_addr, transport, p_ref_data, rc); + + return BTM_SUCCESS; + } + } + + p_dev_rec->p_callback = p_callback; + + if (p_dev_rec->last_author_service_id == BTM_SEC_NO_LAST_SERVICE_ID + || p_dev_rec->last_author_service_id != p_dev_rec->p_cur_service->service_id) { + /* 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 ((p_dev_rec->security_required & BTM_SEC_MODE4_LEVEL4) && + (p_dev_rec->link_key_type != BTM_LKEY_TYPE_AUTH_COMB_P_256)) { + /* BTM_LKEY_TYPE_AUTH_COMB_P_256 is the only acceptable key in this case */ + if ((p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN) != 0) { + p_dev_rec->sm4 |= BTM_SM4_UPGRADE; + } + p_dev_rec->sec_flags &= ~(BTM_SEC_LINK_KEY_KNOWN | BTM_SEC_LINK_KEY_AUTHED | + BTM_SEC_AUTHENTICATED); + BTM_TRACE_DEBUG ("%s: sec_flags:0x%x", __FUNCTION__, p_dev_rec->sec_flags); + } else { + /* If we already have a link key to the connected peer, is it secure enough? */ + btm_sec_check_upgrade(p_dev_rec, is_originator); + } + } + + BTM_TRACE_EVENT ("%s() PSM:%d Handle:%d State:%d Flags: 0x%x Required: 0x%x Service ID:%d\n", + __func__, 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, transport, p_dev_rec->p_ref_data, (UINT8)rc); + } + + return (rc); +#else + return BTM_MODE_UNSUPPORTED; +#endif ///SMP_INCLUDED == TRUE +} + +/******************************************************************************* +** +** 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) +{ +#if (SMP_INCLUDED == TRUE) + tBTM_SEC_DEV_REC *p_dev_rec; + tBTM_SEC_SERV_REC *p_serv_rec; + tBTM_STATUS rc; + UINT16 security_required; + BOOLEAN transport = FALSE;/* should check PSM range in LE connection oriented L2CAP connection */ + + BTM_TRACE_DEBUG ("%s() is_originator: %d\n", __func__, 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, transport, p_ref_data, BTM_MODE_UNSUPPORTED); + } + + BTM_TRACE_ERROR ("Security Manager: MX service not found PSM:%d Proto:%d SCN:%d\n", + psm, mx_proto_id, mx_chan_id); + return BTM_NO_RESOURCES; + } + + if ((btm_cb.security_mode == BTM_SEC_MODE_SC) && (!btm_sec_is_serv_level0(psm))) { + security_required = btm_sec_set_serv_level4_flags (p_serv_rec->security_flags, + is_originator); + } else { + security_required = p_serv_rec->security_flags; + } + + /* 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) ) { +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + BTM_TRACE_EVENT ("%s() service PSM:%d Proto:%d SCN:%d delayed state: %s\n", __func__, + psm, mx_proto_id, mx_chan_id, btm_pair_state_descr(btm_cb.pairing_state)); +#endif ///BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE + rc = BTM_CMD_STARTED; + + if ((btm_cb.security_mode == BTM_SEC_MODE_UNDEFINED || + btm_cb.security_mode == BTM_SEC_MODE_NONE || + btm_cb.security_mode == BTM_SEC_MODE_SERVICE || + btm_cb.security_mode == BTM_SEC_MODE_LINK) || + (BTM_SM4_KNOWN == p_dev_rec->sm4) || (BTM_SEC_IS_SM4(p_dev_rec->sm4) && + (btm_sec_is_upgrade_possible(p_dev_rec, is_originator) == FALSE))) { + /* legacy mode - local is legacy or local is lisbon/peer is legacy + * or SM4 with no possibility of link key upgrade */ + if (is_originator) { + if (((security_required & BTM_SEC_OUT_FLAGS) == 0) || + ((((security_required & BTM_SEC_OUT_FLAGS) == BTM_SEC_OUT_AUTHENTICATE) && btm_dev_authenticated(p_dev_rec))) || + ((((security_required & BTM_SEC_OUT_FLAGS) == (BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_ENCRYPT)) && btm_dev_encrypted(p_dev_rec))) + ) { + rc = BTM_SUCCESS; + } + } else { + if (((security_required & BTM_SEC_IN_FLAGS) == 0) || + ((((security_required & BTM_SEC_IN_FLAGS) == BTM_SEC_IN_AUTHENTICATE) && btm_dev_authenticated(p_dev_rec))) || + (((security_required & BTM_SEC_IN_FLAGS) == BTM_SEC_IN_AUTHORIZE) && (btm_dev_authorized(p_dev_rec) || btm_serv_trusted(p_dev_rec, p_serv_rec))) || + (((security_required & BTM_SEC_IN_FLAGS) == (BTM_SEC_IN_AUTHORIZE | BTM_SEC_IN_AUTHENTICATE)) && ((btm_dev_authorized(p_dev_rec) || btm_serv_trusted(p_dev_rec, p_serv_rec)) && btm_dev_authenticated(p_dev_rec))) || + (((security_required & BTM_SEC_IN_FLAGS) == (BTM_SEC_IN_AUTHORIZE | BTM_SEC_IN_ENCRYPT)) && ((btm_dev_authorized(p_dev_rec) || btm_serv_trusted(p_dev_rec, p_serv_rec)) && btm_dev_encrypted(p_dev_rec))) || + ((((security_required & BTM_SEC_IN_FLAGS) == (BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_ENCRYPT)) && btm_dev_encrypted(p_dev_rec))) + ) { + // Check for 16 digits (or MITM) + if (((security_required & BTM_SEC_IN_MIN_16_DIGIT_PIN) == 0) || + (((security_required & BTM_SEC_IN_MIN_16_DIGIT_PIN) == BTM_SEC_IN_MIN_16_DIGIT_PIN) && btm_dev_16_digit_authenticated(p_dev_rec))) { + rc = BTM_SUCCESS; + } + } + } + if ((rc == BTM_SUCCESS) && (security_required & BTM_SEC_MODE4_LEVEL4) && + (p_dev_rec->link_key_type != BTM_LKEY_TYPE_AUTH_COMB_P_256)) { + rc = BTM_CMD_STARTED; + } + } + + if (rc == BTM_SUCCESS) { + BTM_TRACE_EVENT("%s: allow to bypass, checking authorization\n", __FUNCTION__); + /* the security in BTM_SEC_IN_FLAGS is fullfilled so far, check the requirements in */ + /* btm_sec_execute_procedure */ + if ((is_originator && (p_serv_rec->security_flags & BTM_SEC_OUT_AUTHORIZE)) || + (!is_originator && (p_serv_rec->security_flags & BTM_SEC_IN_AUTHORIZE))) { + BTM_TRACE_EVENT("%s: still need authorization\n", __FUNCTION__); + rc = BTM_CMD_STARTED; + } + } + + /* Check whether there is a pending security procedure, if so we should always queue */ + /* the new security request */ + if (p_dev_rec->sec_state != BTM_SEC_STATE_IDLE) { + BTM_TRACE_EVENT("%s: There is a pending security procedure\n", __FUNCTION__); + rc = BTM_CMD_STARTED; + } + if (rc == BTM_CMD_STARTED) { + BTM_TRACE_EVENT("%s: call btm_sec_queue_mx_request\n", __FUNCTION__); + btm_sec_queue_mx_request (bd_addr, psm, is_originator, mx_proto_id, + mx_chan_id, p_callback, p_ref_data); + } else { /* rc == BTM_SUCCESS */ + /* access granted */ + if (p_callback) { + (*p_callback) (bd_addr, transport, p_ref_data, (UINT8)rc); + } + } +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + BTM_TRACE_EVENT("%s: return with rc = 0x%02x in delayed state %s\n", __FUNCTION__, rc, + btm_pair_state_descr(btm_cb.pairing_state)); +#endif ///BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE + return rc; + } + + if ((!is_originator) && ((security_required & BTM_SEC_MODE4_LEVEL4) || + (btm_cb.security_mode == BTM_SEC_MODE_SC))) { + BOOLEAN local_supports_sc = controller_get_interface()->supports_secure_connections(); + /* acceptor receives service connection establishment Request for */ + /* Secure Connections Only service */ + if (!(local_supports_sc) || !(p_dev_rec->remote_supports_secure_connections)) { + BTM_TRACE_DEBUG("%s: SC only service,local_support_for_sc %d,\n" + "remote_support_for_sc %d: fail pairing\n", __FUNCTION__, + local_supports_sc, p_dev_rec->remote_supports_secure_connections); + + if (p_callback) { + (*p_callback) (bd_addr, transport, (void *)p_ref_data, + BTM_MODE4_LEVEL4_NOT_SUPPORTED); + } + + return (BTM_MODE4_LEVEL4_NOT_SUPPORTED); + } + } + + p_dev_rec->p_cur_service = p_serv_rec; + p_dev_rec->security_required = security_required; + + if (btm_cb.security_mode == BTM_SEC_MODE_SP || + btm_cb.security_mode == BTM_SEC_MODE_SP_DEBUG || + btm_cb.security_mode == BTM_SEC_MODE_SC) { + if (BTM_SEC_IS_SM4(p_dev_rec->sm4)) { + if ((p_dev_rec->security_required & BTM_SEC_MODE4_LEVEL4) && + (p_dev_rec->link_key_type != BTM_LKEY_TYPE_AUTH_COMB_P_256)) { + /* BTM_LKEY_TYPE_AUTH_COMB_P_256 is the only acceptable key in this case */ + if ((p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN) != 0) { + p_dev_rec->sm4 |= BTM_SM4_UPGRADE; + } + + p_dev_rec->sec_flags &= ~(BTM_SEC_LINK_KEY_KNOWN | BTM_SEC_LINK_KEY_AUTHED | + BTM_SEC_AUTHENTICATED); + BTM_TRACE_DEBUG("%s: sec_flags:0x%x\n", __FUNCTION__, p_dev_rec->sec_flags); + } else { + /* 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_EVENT ("%s() proto_id:%d chan_id:%d State:%d Flags:0x%x Required:0x%x Service ID:%d\n", + __func__, 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, transport, p_ref_data, (UINT8)rc); + } + } + + return rc; +#else + return BTM_MODE_UNSUPPORTED; +#endif ///SMP_INCLUDED == TRUE +} + +/******************************************************************************* +** +** Function btm_sec_conn_req +** +** Description This function is when the peer device is requesting +** connection +** +** Returns void +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +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 (!controller_get_interface()->get_is_ready()) { + BTM_TRACE_ERROR ("Security Manager: connect request when device not ready\n"); + 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_ERROR ("Security Manager: connect request from non-paired device\n"); + 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_ERROR ("Security Manager: connect request from not paired device\n"); + btsnd_hcic_reject_conn (bda, HCI_ERR_HOST_REJECT_DEVICE); + return; + } + } +#endif + + 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_ERROR ("Security Manager: reject connect request from bonding device\n"); + + /* 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; + } + } +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function btm_sec_bond_cancel_complete +** +** Description This function is called to report bond cancel complete +** event. +** +** Returns void +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +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) || + (btm_cb.pairing_state == BTM_PAIR_STATE_GET_REM_NAME && + BTM_PAIR_FLAGS_WE_CANCEL_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); + } + } +} +#endif ///SMP_INCLUDED == TRUE + + +/******************************************************************************* +** +** 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_EVENT ("btm_create_conn_cancel_complete(): in State: %s status:%d\n", + // 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: +#if (SMP_INCLUDED == TRUE) + btm_sec_bond_cancel_complete(); +#endif ///SMP_INCLUDED == TRUE + 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 +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +void btm_sec_check_pending_reqs (void) +{ + tBTM_SEC_QUEUE_ENTRY *p_e; + fixed_queue_t *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; +#if (CLASSIC_BT_INCLUDED == TRUE) + l2cu_resubmit_pending_sec_req (NULL); +#endif ///SMP_INCLUDED == TRUE + } + + /* Now, re-submit anything in the mux queue */ + bq = btm_cb.sec_pending_q; + + btm_cb.sec_pending_q = fixed_queue_new(QUEUE_SIZE_MAX); + + + while ((p_e = (tBTM_SEC_QUEUE_ENTRY *)fixed_queue_dequeue(bq, 0)) != NULL) { + /* Check that the ACL is still up before starting security procedures */ + if (btm_bda_to_acl(p_e->bd_addr, p_e->transport) != NULL) { + if (p_e->psm != 0) { + BTM_TRACE_EVENT("%s PSM:0x%04x Is_Orig:%u mx_proto_id:%u mx_chan_id:%u\n", + __FUNCTION__, 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); + } else { + BTM_SetEncryption(p_e->bd_addr, p_e->transport, p_e->p_callback, + p_e->p_ref_data); + } + } + + osi_free (p_e); + } + fixed_queue_free(bq, NULL); + } +} +#endif ///SMP_INCLUDED == TRUE + + +/******************************************************************************* +** +** Function btm_sec_init +** +** Description This function is on the SEC startup +** +** Returns void +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +void btm_sec_init (UINT8 sec_mode) +{ + 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; +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function btm_sec_device_down +** +** Description This function should be called when device is disabled or +** turned off +** +** Returns void +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +void btm_sec_device_down (void) +{ +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + BTM_TRACE_EVENT ("%s() State: %s\n", __func__, btm_pair_state_descr(btm_cb.pairing_state)); +#endif ///BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); +} +#endif ///SMP_INCLUDED == TRUE + + +/******************************************************************************* +** +** Function btm_sec_dev_reset +** +** Description This function should be called after device reset +** +** Returns void +** +*******************************************************************************/ +void btm_sec_dev_reset (void) +{ + if (controller_get_interface()->supports_simple_pairing()) { + /* set the default IO capabilities */ + btm_cb.devcb.loc_io_caps = BTM_LOCAL_IO_CAPS; + /* add mx service to use no security */ + BTM_SetSecurityLevel(FALSE, "RFC_MUX\n", BTM_SEC_SERVICE_RFC_MUX, + BTM_SEC_NONE, BT_PSM_RFCOMM, BTM_SEC_PROTO_RFCOMM, 0); + } else { + btm_cb.security_mode = BTM_SEC_MODE_SERVICE; + } + + BTM_TRACE_DEBUG ("btm_sec_dev_reset sec mode: %d\n", 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 ((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 +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +static tBTM_STATUS btm_sec_dd_create_conn (tBTM_SEC_DEV_REC *p_dev_rec) +{ + tL2C_LCB *p_lcb = l2cu_find_lcb_by_bd_addr(p_dev_rec->bd_addr, BT_TRANSPORT_BR_EDR); + if (p_lcb && (p_lcb->link_state == LST_CONNECTED || p_lcb->link_state == LST_CONNECTING)) { + BTM_TRACE_WARNING("%s Connection already exists\n", __func__); + return BTM_CMD_STARTED; + } + + /* Make sure an L2cap link control block is available */ + if (!p_lcb && (p_lcb = l2cu_allocate_lcb (p_dev_rec->bd_addr, TRUE, BT_TRANSPORT_BR_EDR)) == NULL) { + BTM_TRACE_WARNING ("Security Manager: failed allocate LCB [%02x%02x%02x%02x%02x%02x]\n", + 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, BT_TRANSPORT_BR_EDR) == FALSE) { + BTM_TRACE_WARNING ("Security Manager: failed create [%02x%02x%02x%02x%02x%02x]\n", + 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); + } + + btm_acl_update_busy_level (BTM_BLI_PAGE_EVT); + + BTM_TRACE_DEBUG ("Security Manager: btm_sec_dd_create_conn [%02x%02x%02x%02x%02x%02x]\n", + 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); +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function btm_sec_rmt_name_request_complete +** +** Description This function is called when remote name was obtained from +** the peer device +** +** Returns void +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +void btm_sec_rmt_name_request_complete (UINT8 *p_bd_addr, UINT8 *p_bd_name, UINT8 status) +{ + tBTM_SEC_DEV_REC *p_dev_rec = NULL; + int i; + DEV_CLASS dev_class; + UINT8 old_sec_state; + UINT8 res; + + BTM_TRACE_EVENT ("btm_sec_rmt_name_request_complete\n"); + 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 { + list_node_t *p_node = NULL; + for (p_node = list_begin(btm_cb.p_sec_dev_rec_list); p_node; p_node = list_next(p_node)) { + p_dev_rec = list_node(p_node); + 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 (!p_bd_addr) { + p_dev_rec = NULL; + } + } + + + /* Commenting out trace due to obf/compilation problems. + */ +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + if (!p_bd_name) { + p_bd_name = (UINT8 *)""; + } + + if (p_dev_rec) { + BTM_TRACE_EVENT ("Security Manager: rmt_name_complete PairState: %s RemName: %s status: %d State:%d p_dev_rec: %p \n", + btm_pair_state_descr (btm_cb.pairing_state), p_bd_name, + status, p_dev_rec->sec_state, p_dev_rec); + } else { + BTM_TRACE_EVENT ("Security Manager: rmt_name_complete PairState: %s RemName: %s status: %d\n", + 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, (char *)p_bd_name, BTM_MAX_REM_BD_NAME_LEN); + p_dev_rec->sec_bd_name[BTM_MAX_REM_BD_NAME_LEN] = '\0'; + p_dev_rec->sec_flags |= BTM_SEC_NAME_KNOWN; + BTM_TRACE_EVENT ("setting BTM_SEC_NAME_KNOWN sec_flags:0x%x\n", 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] && p_bd_addr) { + (*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] && p_bd_addr) { + (*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_EVENT ("btm_sec_rmt_name_request_complete() delayed pin now being requested flags:0x%x, (p_pin_callback=%p)\n", 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_EVENT ("btm_sec_rmt_name_request_complete() calling pin_callback\n"); + 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, + (p_dev_rec->p_cur_service == NULL) ? FALSE + : (p_dev_rec->p_cur_service->security_flags & BTM_SEC_IN_MIN_16_DIGIT_PIN)); + } + + /* 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_EVENT ("btm_sec_rmt_name_request_complete() continue bonding sm4: 0x%04x, status:0x%x\n", p_dev_rec->sm4, status); + if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_WE_CANCEL_DD) { + btm_sec_bond_cancel_complete(); + return; + } + + if (status != HCI_SUCCESS) { + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + + 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); + 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_DEBUG ("btm_sec_rmt_name_request_complete IS_SM4_UNKNOWN Flags:0x%04x\n", + btm_cb.pairing_flags); + if ((btm_cb.pairing_flags & BTM_PAIR_FLAGS_REJECTED_CONNECT) == 0) { + p_dev_rec->sm4 |= BTM_SM4_KNOWN; + } + } + + BTM_TRACE_DEBUG("%s, SM4 Value: %x, Legacy:%d,IS SM4:%d, Unknown:%d\n", __FUNCTION__, + p_dev_rec->sm4, BTM_SEC_IS_SM4_LEGACY(p_dev_rec->sm4), + BTM_SEC_IS_SM4(p_dev_rec->sm4), BTM_SEC_IS_SM4_UNKNOWN(p_dev_rec->sm4)); + + /* 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_WARNING ("btm_sec_rmt_name_request_complete: waiting HCI_Connection_Complete after rejecting connection\n"); + } + /* 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_WARNING ("btm_sec_rmt_name_request_complete: failed to start connection\n"); + + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + + 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_ERR_MEMORY_FULL); + } + } + } + return; + } else { + BTM_TRACE_WARNING ("btm_sec_rmt_name_request_complete: wrong BDA, retry with pairing BDA\n"); + + BTM_ReadRemoteDeviceName (btm_cb.pairing_bda, NULL, BT_TRANSPORT_BR_EDR); + 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) { + res = (*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 (res == BTM_SEC_DEV_REC_REMOVED) { + p_dev_rec = NULL; + } + } + + } + } + + if(!p_dev_rec) { + return; + } + + /* 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_WARNING ("btm_sec_rmt_name_request_complete (none/ce)\n"); + 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, FALSE); + return; + } + + if (p_dev_rec->sm4 & BTM_SM4_REQ_PEND) { + BTM_TRACE_EVENT ("waiting for remote features!!\n"); + 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, FALSE); +} +#endif ///SMP_INCLUDED == TRUE + + +/******************************************************************************* +** +** 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_EVENT ("btm_sec_rmt_host_support_feat_evt sm4: 0x%x p[0]: 0x%x\n", 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, HCI_FEATURE_BYTES_PER_PAGE); + if (HCI_SSP_HOST_SUPPORTED(features)) { + p_dev_rec->sm4 = BTM_SM4_TRUE; + } + BTM_TRACE_EVENT ("btm_sec_rmt_host_support_feat_evt sm4: 0x%x features[0]: 0x%x\n", 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 +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +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; +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + BTM_TRACE_EVENT("%s: State: %s\n", __FUNCTION__, btm_pair_state_descr(btm_cb.pairing_state)); +#endif ///BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE + p_dev_rec = btm_find_or_alloc_dev (evt_data.bd_addr); + + BTM_TRACE_DEBUG("%s:Security mode: %d, Num Read Remote Feat pages: %d\n", __FUNCTION__, + btm_cb.security_mode, p_dev_rec->num_read_pages); + + if ((btm_cb.security_mode == BTM_SEC_MODE_SC) && (p_dev_rec->num_read_pages == 0)) { + BTM_TRACE_EVENT("%s: Device security mode is SC only.\n" + "To continue need to know remote features.\n", __FUNCTION__); + + p_dev_rec->remote_features_needed = TRUE; + return; + } + + p_dev_rec->sm4 |= BTM_SM4_TRUE; +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + BTM_TRACE_EVENT("%s: State: %s Flags: 0x%04x p_cur_service: %p\n", + __FUNCTION__, btm_pair_state_descr(btm_cb.pairing_state), + btm_cb.pairing_flags, p_dev_rec->p_cur_service); +#endif ///BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE + if (p_dev_rec->p_cur_service) { + BTM_TRACE_EVENT("%s: cur_service psm: 0x%04x, security_flags: 0x%04x\n", + __FUNCTION__, p_dev_rec->p_cur_service->psm, + p_dev_rec->p_cur_service->security_flags); + } + + switch (btm_cb.pairing_state) { + /* initiator connecting */ + case BTM_PAIR_STATE_IDLE: + //TODO: Handle Idle pairing state + //security_required = p_dev_rec->security_required; + break; + + /* received IO capability response already->acceptor */ + case BTM_PAIR_STATE_INCOMING_SSP: + is_orig = FALSE; + + if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_PEER_STARTED_DD) { + /* acceptor in dedicated bonding */ + evt_data.auth_req = BTM_DEFAULT_DD_AUTH_REQ; + } + break; + + /* initiator, at this point it is expected to be dedicated bonding + initiated by local device */ + case BTM_PAIR_STATE_WAIT_PIN_REQ: + if (!memcmp (evt_data.bd_addr, btm_cb.pairing_bda, BD_ADDR_LEN)) { + evt_data.auth_req = BTM_DEFAULT_DD_AUTH_REQ; + } else { + err_code = HCI_ERR_HOST_BUSY_PAIRING; + } + break; + + /* any other state is unexpected */ + default: + err_code = HCI_ERR_HOST_BUSY_PAIRING; + BTM_TRACE_ERROR("%s: Unexpected Pairing state received %d\n", __FUNCTION__, + btm_cb.pairing_state); + break; + } + + if (btm_cb.pairing_disabled) { + /* pairing is not allowed */ + BTM_TRACE_DEBUG("%s: Pairing is not allowed -> fail pairing.\n", __FUNCTION__); + err_code = HCI_ERR_PAIRING_NOT_ALLOWED; + } else if (btm_cb.security_mode == BTM_SEC_MODE_SC) { + BOOLEAN local_supports_sc = controller_get_interface()->supports_secure_connections(); + /* device in Secure Connections Only mode */ + if (!(local_supports_sc) || !(p_dev_rec->remote_supports_secure_connections)) { + BTM_TRACE_DEBUG("%s: SC only service, local_support_for_sc %d,\n" + " remote_support_for_sc 0x%02x -> fail pairing\n", __FUNCTION__, + local_supports_sc, p_dev_rec->remote_supports_secure_connections); + + 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)) { + if (btm_cb.security_mode == BTM_SEC_MODE_SC) { + /* SC only mode device requires MITM protection */ + evt_data.auth_req = BTM_AUTH_SP_YES; + } else { + 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 (btm_cb.security_mode == BTM_SEC_MODE_SC) { + /* At this moment we know that both sides are SC capable, device in */ + /* SC only mode requires MITM for any service so let's set MITM bit */ + evt_data.auth_req |= BTM_AUTH_YN_BIT; + BTM_TRACE_DEBUG("%s: for device in \"SC only\" mode set auth_req to 0x%02x\n", + __FUNCTION__, evt_data.auth_req); + } + + /* 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; +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + BTM_TRACE_EVENT("%s: State: %s IO_CAP:%d oob_data:%d auth_req:%d", + __FUNCTION__, btm_pair_state_descr(btm_cb.pairing_state), evt_data.io_cap, + evt_data.oob_data, evt_data.auth_req); +#endif ///BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE + 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 +** +*******************************************************************************/ +#if (CLASSIC_BT_INCLUDED == TRUE) +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); +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + BTM_TRACE_EVENT ("btm_proc_sp_req_evt() BDA: %08x%04x event: 0x%x, State: %s\n", + (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)); +#endif ///BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE + 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_STRCPY_S ((char *)evt_data.cfm_req.bd_name,(char *)p_dev_rec->sec_bd_name); + + 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_DEBUG ("btm_proc_sp_req_evt() just_works:%d, io loc:%d, rmt:%d, auth loc:%d, rmt:%d\n", + 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; + evt_data.cfm_req.loc_io_caps = btm_cb.devcb.loc_io_caps; + evt_data.cfm_req.rmt_io_caps = p_dev_rec->rmt_io_caps; + 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_DEBUG ("BTM_SP_KEY_NOTIF_EVT: passkey: %u\n", evt_data.key_notif.passkey); + + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_AUTH_COMPLETE); + break; + + case BTM_SP_KEY_REQ_EVT: + /* HCI_USER_PASSKEY_REQUEST_EVT */ + btm_sec_change_pairing_state (BTM_PAIR_STATE_KEY_ENTRY); + break; + } + + 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_DEBUG ("calling BTM_ConfirmReqReply with status: %d\n", status); + BTM_ConfirmReqReply (status, p_bda); + } else if (event == BTM_SP_KEY_REQ_EVT) { + BTM_PasskeyReqReply(status, p_bda, 0); + } + 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); + } + } else { + btsnd_hcic_user_passkey_neg_reply(p_bda); + } +} + +/******************************************************************************* +** +** 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_ERROR ("btm_simple_pair_complete() with unknown BDA: %08x%04x\n", + (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; + } +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + BTM_TRACE_EVENT ("btm_simple_pair_complete() Pair State: %s Status:%d sec_state: %u\n", + btm_pair_state_descr(btm_cb.pairing_state), status, p_dev_rec->sec_state); +#endif ///BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE + 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 */ + /* Avoid sending disconnect on HCI_ERR_PEER_USER */ + if ((status != HCI_ERR_PEER_USER) && (status != HCI_ERR_CONN_CAUSE_LOCAL_HOST)) { + btm_sec_send_hci_disconnect (p_dev_rec, HCI_ERR_AUTH_FAILURE, p_dev_rec->hci_handle); + } + } +} +#endif /* (CLASSIC_BT_INCLUDED == TRUE) */ +#endif ///SMP_INCLUDED == TRUE + + +#if BTM_OOB_INCLUDED == TRUE && SMP_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_EVENT ("btm_rem_oob_req() BDA: %02x:%02x:%02x:%02x:%02x:%02x\n", + 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, (char *)p_dev_rec->sec_bd_name, BTM_MAX_REM_BD_NAME_LEN); + evt_data.bd_name[BTM_MAX_REM_BD_NAME_LEN] = '\0'; + + 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_EVENT ("btm_read_local_oob_complete:%d\n", 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 +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +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 = osi_time_get_os_boottime_ms(); + } + + if ((osi_time_get_os_boottime_ms() - 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_DEBUG ("btm_sec_auth_collision: state %d (retrying in a moment...)\n", 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); + } + } +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function btm_sec_auth_complete +** +** Description This function is when authentication of the connection is +** completed by the LM +** +** Returns void +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +void btm_sec_auth_complete (UINT16 handle, UINT8 status) +{ + UINT8 res; + 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 && SMP_INCLUDED == TRUE) + if (p_dev_rec) { + BTM_TRACE_EVENT ("Security Manager: auth_complete PairState: %s handle:%u status:%d dev->sec_state: %u Bda:%08x, RName:%s\n", + 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_EVENT ("Security Manager: auth_complete PairState: %s handle:%u status:%d\n", + 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; + } + + if ( (btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) + && (memcmp (p_dev_rec->bd_addr, btm_cb.pairing_bda, BD_ADDR_LEN) == 0) ) { + 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 asleep. Let her try */ + if (btm_cb.api.p_auth_complete_callback) { + /* report the authentication status */ + if (old_state != BTM_PAIR_STATE_IDLE) { + res = (*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 (res == BTM_SEC_DEV_REC_REMOVED) { + p_dev_rec = NULL; + } + } + } + + if(!p_dev_rec) { + return; + } + + p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; + +#if (CLASSIC_BT_INCLUDED == TRUE) + btm_sec_update_legacy_auth_state(btm_bda_to_acl(p_dev_rec->bd_addr, BT_TRANSPORT_BR_EDR), BTM_ACL_LEGACY_AUTH_SELF); +#endif + /* 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) { + if (((status != HCI_ERR_PEER_USER) && (status != HCI_ERR_CONN_CAUSE_LOCAL_HOST))) { + btm_sec_send_hci_disconnect (p_dev_rec, HCI_ERR_PEER_USER, p_dev_rec->hci_handle); + } + } else { + BTM_TRACE_DEBUG ("TRYING TO DECIDE IF CAN USE SMP_BR_CHNL\n"); + if (p_dev_rec->new_encryption_key_is_p256 && (btm_sec_use_smp_br_chnl(p_dev_rec)) + /* no LE keys are available, do deriving */ + && (!(p_dev_rec->sec_flags & BTM_SEC_LE_LINK_KEY_KNOWN) || + /* or BR key is higher security than existing LE keys */ + (!(p_dev_rec->sec_flags & BTM_SEC_LE_LINK_KEY_AUTHED) && + (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_AUTHED)))) { + BTM_TRACE_DEBUG ("link encrypted afer dedic bonding can use SMP_BR_CHNL\n"); + + if (btm_sec_is_master(p_dev_rec)) { + // Encryption is required to start SM over BR/EDR + // indicate that this is encryption after authentication + BTM_SetEncryption(p_dev_rec->bd_addr, BT_TRANSPORT_BR_EDR, NULL, NULL); + } + } + 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_DEBUG ("Collision retry sm4:x%x sec_flags:0x%x\n", 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_DEBUG ("Retry for missing key sm4:x%x sec_flags:0x%x\n", 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, FALSE); + + if (btm_cb.pairing_flags & BTM_PAIR_FLAGS_DISC_WHEN_DONE) { + btm_sec_send_hci_disconnect (p_dev_rec, HCI_ERR_AUTH_FAILURE, p_dev_rec->hci_handle); + } + return; + } + + p_dev_rec->sec_flags |= BTM_SEC_AUTHENTICATED; + + if (p_dev_rec->pin_code_length >= 16 || + p_dev_rec->link_key_type == BTM_LKEY_TYPE_AUTH_COMB || + p_dev_rec->link_key_type == BTM_LKEY_TYPE_AUTH_COMB_P_256) { + // If we have MITM protection we have a higher level of security than + // provided by 16 digits PIN + p_dev_rec->sec_flags |= BTM_SEC_16_DIGIT_PIN_AUTHED; + } + + /* 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, FALSE); + } +} +#endif ///SMP_INCLUDED == TRUE + + +/******************************************************************************* +** +** Function btm_sec_encrypt_change +** +** Description This function is when encryption of the connection is +** completed by the LM +** +** Returns void +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +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); +#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE + tACL_CONN *p_acl = NULL; +#endif + BTM_TRACE_EVENT ("Security Manager: encrypt_change status:%d State:%d, encr_enable = %d\n", + status, (p_dev_rec) ? p_dev_rec->sec_state : 0, encr_enable); + BTM_TRACE_DEBUG ("before update p_dev_rec->sec_flags=0x%x\n", (p_dev_rec) ? p_dev_rec->sec_flags : 0 ); + + /* 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) { + if (p_dev_rec->hci_handle == handle) { + p_dev_rec->sec_flags |= (BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED); + if (p_dev_rec->pin_code_length >= 16 || + p_dev_rec->link_key_type == BTM_LKEY_TYPE_AUTH_COMB || + p_dev_rec->link_key_type == BTM_LKEY_TYPE_AUTH_COMB_P_256) { + p_dev_rec->sec_flags |= BTM_SEC_16_DIGIT_PIN_AUTHED; + } + if (p_dev_rec->enc_mode != encr_enable) { + p_dev_rec->enc_mode = encr_enable; + /* Report the encryption change state of BR/EDR to upper layer */ + if (btm_cb.api.p_enc_change_callback) { + (*btm_cb.api.p_enc_change_callback) (p_dev_rec->bd_addr, encr_enable); + } + } + } else { + p_dev_rec->sec_flags |= BTM_SEC_LE_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) { + if (p_dev_rec->hci_handle == handle) { + p_dev_rec->sec_flags &= ~BTM_SEC_ENCRYPTED; + if (p_dev_rec->enc_mode != encr_enable) { + p_dev_rec->enc_mode = encr_enable; + /* Report the encryption change state of BR/EDR to upper layer */ + if (btm_cb.api.p_enc_change_callback) { + (*btm_cb.api.p_enc_change_callback) (p_dev_rec->bd_addr, encr_enable); + } + } + } else { + p_dev_rec->sec_flags &= ~BTM_SEC_LE_ENCRYPTED; + } + } + + BTM_TRACE_DEBUG ("after update p_dev_rec->sec_flags=0x%x\n", p_dev_rec->sec_flags ); + +#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE + p_acl = btm_handle_to_acl(handle); + + if (p_acl != NULL) { + btm_sec_check_pending_enc_req(p_dev_rec, p_acl->transport, encr_enable); + } + + if (p_acl && p_acl->transport == BT_TRANSPORT_LE) { + if (status == HCI_ERR_KEY_MISSING || status == HCI_ERR_AUTH_FAILURE || + status == HCI_ERR_ENCRY_MODE_NOT_ACCEPTABLE) { + p_dev_rec->sec_flags &= ~ (BTM_SEC_LE_LINK_KEY_KNOWN); + p_dev_rec->ble.key_type = BTM_LE_KEY_NONE; + } + btm_ble_link_encrypted(p_dev_rec->ble.pseudo_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; + } + + BTM_TRACE_DEBUG ("in %s new_encr_key_256 is %d\n", + __func__, p_dev_rec->new_encryption_key_is_p256); + + if ((status == HCI_SUCCESS) && encr_enable && (p_dev_rec->hci_handle == handle)) { + if (p_dev_rec->new_encryption_key_is_p256) { + if (btm_sec_use_smp_br_chnl(p_dev_rec) && + btm_sec_is_master(p_dev_rec) && + /* if LE key is not known, do deriving */ + (!(p_dev_rec->sec_flags & BTM_SEC_LE_LINK_KEY_KNOWN) || + /* or BR key is higher security than existing LE keys */ + (!(p_dev_rec->sec_flags & BTM_SEC_LE_LINK_KEY_AUTHED) + && (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_AUTHED)))) { + /* BR/EDR is encrypted with LK that can be used to derive LE LTK */ + p_dev_rec->new_encryption_key_is_p256 = FALSE; + + if (p_dev_rec->no_smp_on_br) { + BTM_TRACE_DEBUG ("%s NO SM over BR/EDR\n", __func__); + } else { +#if (CLASSIC_BT_INCLUDED == TRUE) + BTM_TRACE_DEBUG ("%s start SM over BR/EDR\n", __func__); + SMP_BR_PairWith(p_dev_rec->bd_addr); +#endif ///CLASSIC_BT_INCLUDED == TRUE + } + } + } else { + // BR/EDR is successfully encrypted. Correct LK type if needed + // (BR/EDR LK derived from LE LTK was used for encryption) + if ((encr_enable == 1) && /* encryption is ON for SSP */ + /* LK type is for BR/EDR SC */ + (p_dev_rec->link_key_type == BTM_LKEY_TYPE_UNAUTH_COMB_P_256 || + p_dev_rec->link_key_type == BTM_LKEY_TYPE_AUTH_COMB_P_256)) { + if (p_dev_rec->link_key_type == BTM_LKEY_TYPE_UNAUTH_COMB_P_256) { + p_dev_rec->link_key_type = BTM_LKEY_TYPE_UNAUTH_COMB; + } else { /* BTM_LKEY_TYPE_AUTH_COMB_P_256 */ + p_dev_rec->link_key_type = BTM_LKEY_TYPE_AUTH_COMB; + } + + BTM_TRACE_DEBUG("updated link key type to %d\n", p_dev_rec->link_key_type); + btm_send_link_key_notif(p_dev_rec); + } + } + } +#else + btm_sec_check_pending_enc_req (p_dev_rec, BT_TRANSPORT_BR_EDR, encr_enable); +#endif /* BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE */ + + /* 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; +#if (CLASSIC_BT_INCLUDED == TRUE) + l2cu_resubmit_pending_sec_req (p_dev_rec->bd_addr); +#endif ///CLASSIC_BT_INCLUDED == TRUE + } + 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, FALSE); + 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, FALSE); + } +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** 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 +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +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; + UNUSED(p_tle); + + BTM_TRACE_EVENT ("btm_sec_connect_after_reject_timeout()\n"); + 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_WARNING ("Security Manager: btm_sec_connect_after_reject_timeout: failed to start connection\n"); + + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + + 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_ERR_MEMORY_FULL); + } + } +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function btm_sec_connected +** +** Description This function is when a connection to the peer device is +** establsihed +** +** Returns void +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +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; + UINT8 sec_dev_rec_status; + BOOLEAN is_pairing_device = FALSE; + tACL_CONN *p_acl_cb; + UINT8 bit_shift = 0; + + btm_acl_resubmit_page(); + + /* Commenting out trace due to obf/compilation problems. + */ +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + if (p_dev_rec) { + BTM_TRACE_EVENT ("Security Manager: btm_sec_connected in state: %s handle:%d status:%d enc_mode:%d bda:%x RName:%s\n", + 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_EVENT ("Security Manager: btm_sec_connected in state: %s handle:%d status:%d enc_mode:%d bda:%x \n", + 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 { + /* If the device matches with stored paring address + * reset the paring state to idle */ + if ((btm_cb.pairing_state != BTM_PAIR_STATE_IDLE) && + (memcmp (btm_cb.pairing_bda, bda, BD_ADDR_LEN) == 0)) { + btm_sec_change_pairing_state(BTM_PAIR_STATE_IDLE); + } + + /* can not find the device record and the status is error, + * just ignore it */ + return; + } + } else { /* Update the timestamp for this device */ + +#if BLE_INCLUDED == TRUE + bit_shift = (handle == p_dev_rec->ble_hci_handle) ? 8 : 0; +#endif + 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_WARNING ("Security Manager: btm_sec_connected: incoming connection failed without asking PIN\n"); + + 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, BT_TRANSPORT_BR_EDR); + } +#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 BLE_INCLUDED == TRUE + p_dev_rec->device_type |= BT_DEVICE_TYPE_BREDR; +#endif + +#if BTM_DISC_DURING_RS == TRUE + p_dev_rec->rs_disc_pending = BTM_SEC_RS_NOT_PENDING; /* reset flag */ +#endif + + p_dev_rec->rs_disc_pending = BTM_SEC_RS_NOT_PENDING; /* reset flag */ + + 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_WARNING ("Security Manager: btm_sec_connected: HCI_Conn_Comp Flags:0x%04x, sm4: 0x%x\n", + 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, BT_TRANSPORT_BR_EDR); + 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_WARNING ("Security Manager: btm_sec_connected: Wait for incoming connection\n"); + 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) << bit_shift); + BTM_TRACE_DEBUG ("security_required:%x \n", 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) { + sec_dev_rec_status = (*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 (sec_dev_rec_status == BTM_SEC_DEV_REC_REMOVED) { + p_dev_rec = NULL; + } + } + } + /* + Do not send authentication failure, if following conditions hold good + 1. BTM Sec Pairing state is idle + 2. Link key for the remote device is present. + 3. Remote is SSP capable. + */ + else if ((p_dev_rec->link_key_type <= BTM_LKEY_TYPE_REMOTE_UNIT) && + (((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_LE_LINK_KEY_KNOWN << bit_shift); + + +#ifdef BRCM_NOT_4_BTE + /* If we rejected pairing, pass this special result code */ + if (btm_cb.acl_disc_reason == HCI_ERR_HOST_REJECT_SECURITY) { + status = HCI_ERR_HOST_REJECT_SECURITY; + } +#endif + + /* We need to notify host that the key is not known any more */ + if (btm_cb.api.p_auth_complete_callback) { + sec_dev_rec_status = (*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 (sec_dev_rec_status == BTM_SEC_DEV_REC_REMOVED) { + p_dev_rec = NULL; + } + } + } + + 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, FALSE); + } else { + btm_sec_dev_rec_cback_event (p_dev_rec, BTM_ERR_PROCESSING, FALSE); + } + + 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) { + sec_dev_rec_status = (*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 (sec_dev_rec_status == BTM_SEC_DEV_REC_REMOVED) { + p_dev_rec = NULL; + } + } + + 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, BT_TRANSPORT_BR_EDR); + 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_acl_created (bda, p_dev_rec->dev_class, p_dev_rec->sec_bd_name, handle, HCI_ROLE_SLAVE, BT_TRANSPORT_BR_EDR); + + /* 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) << bit_shift); + + if (enc_mode != HCI_ENCRYPT_MODE_DISABLED) { + p_dev_rec->sec_flags |= ((BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED) << bit_shift); + } + + if (btm_cb.security_mode == BTM_SEC_MODE_LINK) { + p_dev_rec->sec_flags |= (BTM_SEC_AUTHENTICATED << bit_shift); + } + + if (p_dev_rec->pin_code_length >= 16 || + p_dev_rec->link_key_type == BTM_LKEY_TYPE_AUTH_COMB || + p_dev_rec->link_key_type == BTM_LKEY_TYPE_AUTH_COMB_P_256) { + p_dev_rec->sec_flags |= (BTM_SEC_16_DIGIT_PIN_AUTHED << bit_shift); + } + + 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_DEBUG ("is_originator:%d \n", 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, FALSE); + } + } + return; +} + +/******************************************************************************* +** +** 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, handle)); +} + +/******************************************************************************* +** +** 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; + tBTM_SEC_CALLBACK *p_callback = NULL; + tBT_TRANSPORT transport = BT_TRANSPORT_BR_EDR; + + /* If page was delayed for disc complete, can do it now */ + btm_cb.discing = FALSE; + +#if (CLASSIC_BT_INCLUDED == TRUE) + btm_acl_resubmit_page(); +#endif + + if (!p_dev_rec) { + return; + } + p_dev_rec->enc_init_by_we = FALSE; + transport = (handle == p_dev_rec->hci_handle) ? BT_TRANSPORT_BR_EDR : BT_TRANSPORT_LE; + + p_dev_rec->rs_disc_pending = BTM_SEC_RS_NOT_PENDING; /* reset flag */ + +#if BTM_DISC_DURING_RS == TRUE + p_dev_rec->rs_disc_pending = BTM_SEC_RS_NOT_PENDING; /* reset flag */ +#endif + + /* clear unused flags */ + p_dev_rec->sm4 &= BTM_SM4_TRUE; +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + uint8_t *bd_addr = (uint8_t *)p_dev_rec->bd_addr; + BTM_TRACE_EVENT("%s sec_req:x%x state:%s reason:%d bd_addr:%02x:%02x:%02x:%02x:%02x:%02x" + " remote_name:%s\n", __func__, p_dev_rec->security_required, btm_pair_state_descr(btm_cb.pairing_state), + reason, bd_addr[0], bd_addr[1], bd_addr[2], bd_addr[3], bd_addr[4], bd_addr[5], p_dev_rec->sec_bd_name); +#endif ///BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE + BTM_TRACE_EVENT("%s before update sec_flags=0x%x\n", __func__, p_dev_rec->sec_flags); + +#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE + btm_ble_update_mode_operation(HCI_ROLE_UNKNOWN, p_dev_rec->bd_addr, HCI_SUCCESS); + /* see sec_flags processing in btm_acl_removed */ + + if (transport == BT_TRANSPORT_LE) { + p_dev_rec->ble_hci_handle = BTM_SEC_INVALID_HANDLE; + p_dev_rec->sec_flags &= ~(BTM_SEC_LE_AUTHENTICATED | BTM_SEC_LE_ENCRYPTED); + p_dev_rec->enc_key_size = 0; + } else +#endif + { + p_dev_rec->hci_handle = BTM_SEC_INVALID_HANDLE; + p_dev_rec->sec_flags &= ~(BTM_SEC_AUTHORIZED | BTM_SEC_AUTHENTICATED | BTM_SEC_ENCRYPTED + | BTM_SEC_ROLE_SWITCHED | BTM_SEC_16_DIGIT_PIN_AUTHED); + } + +#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE + if (p_dev_rec->sec_state == BTM_SEC_STATE_DISCONNECTING_BOTH) { + p_dev_rec->sec_state = (transport == BT_TRANSPORT_LE) ? + BTM_SEC_STATE_DISCONNECTING : BTM_SEC_STATE_DISCONNECTING_BLE; + return; + } +#endif + p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; + p_dev_rec->security_required = BTM_SEC_NONE; + + p_callback = p_dev_rec->p_callback; + + /* if security is pending, send callback to clean up the security state */ + if (p_callback) { + p_dev_rec->p_callback = NULL; /* when the peer device time out the authentication before + we do, this call back must be reset here */ + (*p_callback) (p_dev_rec->bd_addr, transport, p_dev_rec->p_ref_data, BTM_ERR_PROCESSING); + } + + BTM_TRACE_EVENT("%s after update sec_flags=0x%x\n", __func__, 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, result); + } + } +} + +/******************************************************************************* +** +** 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; + BOOLEAN ltk_derived_lk = FALSE; + UINT8 res; + + BTM_TRACE_EVENT ("btm_sec_link_key_notification() BDA:%04x%08x, TYPE: %d\n", + (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 ((key_type >= BTM_LTK_DERIVED_LKEY_OFFSET + BTM_LKEY_TYPE_COMBINATION) && + (key_type <= BTM_LTK_DERIVED_LKEY_OFFSET + BTM_LKEY_TYPE_AUTH_COMB_P_256)) { + ltk_derived_lk = TRUE; + key_type -= BTM_LTK_DERIVED_LKEY_OFFSET; + } + /* If connection was made to do bonding restore link security if changed */ + btm_restore_mode(); + + /* Store the previous state of secure connection as current state. Since + * this is the first encounter with the remote device, whatever the remote + * device's SC state is, it cannot lower the SC level from this. */ + p_dev_rec->remote_secure_connection_previous_state = p_dev_rec->remote_supports_secure_connections; + if (p_dev_rec->remote_supports_secure_connections) { + BTM_TRACE_EVENT ("Remote device supports Secure Connection"); + } else { + BTM_TRACE_EVENT ("Remote device does not support Secure Connection"); + } + 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; + +#if (CLASSIC_BT_INCLUDED == TRUE) + btm_sec_update_legacy_auth_state(btm_bda_to_acl(p_dev_rec->bd_addr, BT_TRANSPORT_BR_EDR), BTM_ACL_LEGACY_AUTH_NONE); +#endif + /* + * Until this point in time, we do not know if MITM was enabled, hence we + * add the extended security flag here. + */ + if (p_dev_rec->pin_code_length >= 16 || + p_dev_rec->link_key_type == BTM_LKEY_TYPE_AUTH_COMB || + p_dev_rec->link_key_type == BTM_LKEY_TYPE_AUTH_COMB_P_256) { + p_dev_rec->sec_flags |= BTM_SEC_16_DIGIT_PIN_AUTHED; + } + +#if (BLE_INCLUDED == TRUE) + /* BR/EDR connection, update the encryption key size to be 16 as always */ + p_dev_rec->enc_key_size = 16; +#endif + 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); + } + } + + /* save LTK derived LK no matter what */ + if (ltk_derived_lk) { + if (btm_cb.api.p_link_key_callback) { + BTM_TRACE_DEBUG ("%s() Save LTK derived LK (key_type = %d)\n", + __FUNCTION__, p_dev_rec->link_key_type); + (*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, + p_dev_rec->remote_supports_secure_connections); + } + } else { + if ((p_dev_rec->link_key_type == BTM_LKEY_TYPE_UNAUTH_COMB_P_256) || + (p_dev_rec->link_key_type == BTM_LKEY_TYPE_AUTH_COMB_P_256)) { + p_dev_rec->new_encryption_key_is_p256 = TRUE; + BTM_TRACE_DEBUG ("%s set new_encr_key_256 to %d\n", + __func__, p_dev_rec->new_encryption_key_is_p256); + } + } + + /* 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)) + && !ltk_derived_lk) { + BTM_TRACE_EVENT ("btm_sec_link_key_notification() Delayed BDA: %08x%04x Type:%d\n", + (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_EVENT ("rmt_io_caps:%d, sec_flags:x%x, dev_class[1]:x%02x\n", 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) + /* for derived key, always send authentication callback for BR channel */ + || ltk_derived_lk) { + if (btm_cb.api.p_auth_complete_callback) { + res = (*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 (res == BTM_SEC_DEV_REC_REMOVED) { + p_dev_rec = NULL; + } + } + } + + if(!p_dev_rec) { + return; + } + + /* 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) { + if (ltk_derived_lk) { + BTM_TRACE_DEBUG ("btm_sec_link_key_notification() LTK derived LK is saved already" + " (key_type = %d)\n", p_dev_rec->link_key_type); + } else { + (*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, + p_dev_rec->remote_supports_secure_connections); + } + } + } +} + +/******************************************************************************* +** +** 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_EVENT ("btm_sec_link_key_request() BDA: %02x:%02x:%02x:%02x:%02x:%02x\n", + p_bda[0], p_bda[1], p_bda[2], p_bda[3], p_bda[4], p_bda[5]); + + if ( (btm_cb.pairing_state == BTM_PAIR_STATE_WAIT_PIN_REQ) && + (btm_cb.collision_start_time != 0) && + (memcmp (btm_cb.p_collided_dev_rec->bd_addr, p_bda, BD_ADDR_LEN) == 0) ) { + BTM_TRACE_EVENT ("btm_sec_link_key_request() rejecting link key req " + "State: %d START_TIMEOUT : %d\n", + btm_cb.pairing_state, btm_cb.collision_start_time); + btsnd_hcic_link_key_neg_reply (p_bda); + return; + } + 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); + + /* The link key is not in the database and it is not known to the manager */ + btsnd_hcic_link_key_neg_reply (p_bda); +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** 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 +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +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]; + UNUSED(p_tle); + + 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); +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + BTM_TRACE_EVENT ("btm_sec_pairing_timeout() State: %s Flags: %u\n", + btm_pair_state_descr(p_cb->pairing_state), p_cb->pairing_flags); +#endif ///BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE + 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 (CLASSIC_BT_INCLUDED == TRUE) + 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 /* (CLASSIC_BT_INCLUDED == TRUE) */ + +#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_ERROR ("btm_sec_pairing_timeout() BTM_PAIR_STATE_WAIT_DISCONNECT unknown BDA: %08x%04x\n", + (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, p_dev_rec->hci_handle); + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + break; + + case BTM_PAIR_STATE_WAIT_AUTH_COMPLETE: + case BTM_PAIR_STATE_GET_REM_NAME: + /* We need to notify the UI that timeout has happened while waiting for authentication*/ + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + 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; + + default: +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + BTM_TRACE_WARNING ("btm_sec_pairing_timeout() not processed state: %s\n", btm_pair_state_descr(btm_cb.pairing_state)); +#endif ///BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE + btm_sec_change_pairing_state (BTM_PAIR_STATE_IDLE); + break; + } +} + +#if (CLASSIC_BT_INCLUDED == TRUE) +/******************************************************************************* +** +** 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; + +#ifdef PORCHE_PAIRING_CONFLICT + UINT8 default_pin_code_len = 4; + PIN_CODE default_pin_code = {0x30, 0x30, 0x30, 0x30}; +#endif +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + BTM_TRACE_EVENT ("btm_sec_pin_code_request() State: %s, BDA:%04x%08x\n", + 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] ); +#endif ///BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE + 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) { +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + BTM_TRACE_WARNING ("btm_sec_pin_code_request() rejected - state: %s\n", + btm_pair_state_descr(btm_cb.pairing_state)); +#endif ///BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE +#ifdef PORCHE_PAIRING_CONFLICT + /* reply pin code again due to counter in_rand when local initiates pairing */ + BTM_TRACE_EVENT ("btm_sec_pin_code_request from remote dev. for local initiated pairing\n"); + if (! btm_cb.pin_code_len_saved) { + btm_sec_change_pairing_state (BTM_PAIR_STATE_WAIT_AUTH_COMPLETE); + btsnd_hcic_pin_code_req_reply (p_bda, default_pin_code_len, default_pin_code); + } 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_EVENT ("btm_sec_pin_code_request fixed pin replying\n"); + 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_EVENT ("btm_sec_pin_code_request bonding sending reply\n"); + 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_WARNING("btm_sec_pin_code_request(): Pairing disabled:%d; PIN callback:%p, Dev Rec:%p!\n", + 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); + + BTM_TRACE_EVENT ("btm_sec_pin_code_request going for callback\n"); + 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, + (p_dev_rec->p_cur_service == NULL) ? FALSE + : (p_dev_rec->p_cur_service->security_flags + & BTM_SEC_IN_MIN_16_DIGIT_PIN)); + } + } + return; +} +#endif ///CLASSIC_BT_INCLUDED == TRUE +#endif ///SMP_INCLUDED == TRUE + + +/******************************************************************************* +** +** 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 +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +static tBTM_STATUS btm_sec_execute_procedure (tBTM_SEC_DEV_REC *p_dev_rec) +{ + BTM_TRACE_EVENT ("btm_sec_execute_procedure: Required:0x%x Flags:0x%x State:%d\n", + 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_EVENT ("Security Manager: Start get name\n"); + if (!btm_sec_start_get_name (p_dev_rec)) { + return (BTM_NO_RESOURCES); + } + return (BTM_CMD_STARTED); + } + +#if (CLASSIC_BT_INCLUDED == TRUE) + tACL_CONN *p_acl_cb = btm_handle_to_acl(p_dev_rec->hci_handle); + /* + * To prevent a remote device from doing a Bluetooth Impersonation Attack, a suggested fix by SIG is: + * + * "Hosts performing legacy (non-mutual) authentication must ensure a remote device is authenticated + * prior to proceeding with encryption establishment, regardless of role." + * + * As an implementation, we enforce mutual authentication when devices use Legacy Authentication. + */ + if ((p_acl_cb != NULL) && (BTM_BothEndsSupportSecureConnections(p_acl_cb->remote_addr) == 0) && + ((p_acl_cb->legacy_auth_state & BTM_ACL_LEGACY_AUTH_SELF) == 0)) { + p_dev_rec->sec_flags &= ~BTM_SEC_AUTHENTICATED; + } +#endif + + /* 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->sec_flags & BTM_SEC_16_DIGIT_PIN_AUTHED) + && (!p_dev_rec->is_originator + && (p_dev_rec->security_required & BTM_SEC_IN_MIN_16_DIGIT_PIN)))) + && (p_dev_rec->hci_handle != BTM_SEC_INVALID_HANDLE)) { + /* + * We rely on BTM_SEC_16_DIGIT_PIN_AUTHED being set if MITM is in use, + * as 16 DIGIT is only needed if MITM is not used. Unfortunately, the + * BTM_SEC_AUTHENTICATED is used for both MITM and non-MITM + * authenticated connections, hence we cannot distinguish here. + */ + +#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_EVENT ("Security Manager: Start authentication\n"); + + /* + * If we do have a link-key, but we end up here because we need an + * upgrade, then clear the link-key known and authenticated flag before + * restarting authentication. + * WARNING: If the controller has link-key, it is optional and + * recommended for the controller to send a Link_Key_Request. + * In case we need an upgrade, the only alternative would be to delete + * the existing link-key. That could lead to very bad user experience + * or even IOP issues, if a reconnect causes a new connection that + * requires an upgrade. + */ + if ((p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN) + && (!(p_dev_rec->sec_flags & BTM_SEC_16_DIGIT_PIN_AUTHED) + && (!p_dev_rec->is_originator && (p_dev_rec->security_required + & BTM_SEC_IN_MIN_16_DIGIT_PIN)))) { + p_dev_rec->sec_flags &= ~(BTM_SEC_LINK_KEY_KNOWN | BTM_SEC_LINK_KEY_AUTHED + | BTM_SEC_AUTHENTICATED); + } + + 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_EVENT ("Security Manager: Start encryption\n"); + + if (!btm_sec_start_encryption (p_dev_rec)) { + return (BTM_NO_RESOURCES); + } + return (BTM_CMD_STARTED); + } + + if ((p_dev_rec->security_required & BTM_SEC_MODE4_LEVEL4) && + (p_dev_rec->link_key_type != BTM_LKEY_TYPE_AUTH_COMB_P_256)) { + BTM_TRACE_EVENT("%s: Security Manager: SC only service, but link key type is 0x%02x -" + "security failure\n", __FUNCTION__, p_dev_rec->link_key_type); + return (BTM_FAILED_ON_SECURITY); + } + + /* 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_EVENT ("service id:%d, is trusted:%d\n", + 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_EVENT ("Security Manager: Start authorization\n"); + 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_EVENT ("Security Manager: trusted:0x%04x%04x\n", p_dev_rec->trusted_mask[1], p_dev_rec->trusted_mask[0]); + BTM_TRACE_EVENT ("Security Manager: access granted\n"); + + 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; + } + + /* Send authorization request if not already sent during this service connection */ + if (p_dev_rec->last_author_service_id == BTM_SEC_NO_LAST_SERVICE_ID + || p_dev_rec->last_author_service_id != service_id) { + 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); + } + + else { /* Already authorized once for this L2CAP bringup */ + BTM_TRACE_DEBUG ("btm_sec_start_authorization: (Ignoring extra Authorization prompt for service %d)\n", service_id); + return (BTM_SUCCESS); + } + + if (result == BTM_SUCCESS) { + p_dev_rec->sec_flags |= BTM_SEC_AUTHORIZED; + + /* Save the currently authorized service in case we are asked again by another multiplexer layer */ + if (!p_dev_rec->is_originator) { + p_dev_rec->last_author_service_id = service_id; + } + + p_dev_rec->sec_state = BTM_SEC_STATE_IDLE; + } + return (result); + } + btm_sec_start_get_name (p_dev_rec); + return (BTM_CMD_STARTED); +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** 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[]) +{ + UINT32 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 +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +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_DEBUG ("%s()\n", __func__); + 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); +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** 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 +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +static void btm_sec_collision_timeout (TIMER_LIST_ENT *p_tle) +{ + UNUSED(p_tle); + + BTM_TRACE_EVENT ("%s()\n", __func__); + btm_cb.sec_collision_tle.param = 0; + + tBTM_STATUS 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, FALSE); + } +} + +/******************************************************************************* +** +** Function btm_send_link_key_notif +** +** 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, + p_dev_rec->remote_supports_secure_connections); + + } +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** 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 = btm_find_dev (bd_addr); + if (p_dev_rec != NULL) { + return (p_dev_rec->trusted_mask); + } + return NULL; +} + +/******************************************************************************* +** +** Function btm_restore_mode +** +** Description This function returns the security mode to previous setting +** if it was changed during bonding. +** +** +** Parameters: void +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +static void btm_restore_mode(void) +{ + if (btm_cb.security_mode_changed) { + btm_cb.security_mode_changed = FALSE; + BTM_TRACE_DEBUG("%s() Auth enable -> %d\n", __func__, (btm_cb.security_mode == BTM_SEC_MODE_LINK)); + btsnd_hcic_write_auth_enable ((UINT8)(btm_cb.security_mode == BTM_SEC_MODE_LINK)); + } + +#if (CLASSIC_BT_INCLUDED == TRUE) + if (btm_cb.pin_type_changed) { + btm_cb.pin_type_changed = FALSE; + btsnd_hcic_write_pin_type (btm_cb.cfg.pin_type); + } +#endif ///CLASSIC_BT_INCLUDED == TRUE +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** 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) +{ +#if (SMP_INCLUDED == TRUE) + tBTM_SEC_DEV_REC *p_dev_rec = NULL; + list_node_t *p_node = NULL; + for (p_node = list_begin(btm_cb.p_sec_dev_rec_list); p_node; p_node = list_next(p_node)) { + p_dev_rec = list_node(p_node); + if ((p_dev_rec->sec_flags & BTM_SEC_IN_USE) + && (p_dev_rec->sec_state == state)) { + return (p_dev_rec); + } + } +#endif ///SMP_INCLUDED == TRUE + return (NULL); +} +/******************************************************************************* +** +** Function btm_sec_change_pairing_state +** +** Description This function is called to change pairing state +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +static void btm_sec_change_pairing_state (tBTM_PAIRING_STATE new_state) +{ + tBTM_PAIRING_STATE old_state = btm_cb.pairing_state; +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE) + BTM_TRACE_EVENT ("%s() Old: %s\n", __func__, btm_pair_state_descr(btm_cb.pairing_state)); + BTM_TRACE_EVENT ("%s() New: %s pairing_flags:0x%x\n\n", __func__, + btm_pair_state_descr(new_state), btm_cb.pairing_flags); +#endif ///BT_USE_TRACES == TRUE && SMP_INCLUDED == TRUE + + 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; +#if (CLASSIC_BT_INCLUDED == TRUE) + btm_cb.pin_code_len = 0; +#endif ///CLASSIC_BT_INCLUDED == TRUE + + /* 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); + } +} +#endif ///SMP_INCLUDED == TRUE + + +/******************************************************************************* +** +** Function btm_pair_state_descr +** +** Description Return state description for tracing +** +*******************************************************************************/ +#if (BT_USE_TRACES == TRUE && SMP_INCLUDED == 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, "%d", 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, BOOLEAN is_le_transport) +{ + tBTM_SEC_CALLBACK *p_callback; + + if (p_dev_rec && p_dev_rec->p_callback) { + p_callback = p_dev_rec->p_callback; + p_dev_rec->p_callback = NULL; + +#if BLE_INCLUDED == TRUE + if (is_le_transport) { + (*p_callback) (p_dev_rec->ble.pseudo_addr, BT_TRANSPORT_LE, p_dev_rec->p_ref_data, res); + } else +#endif + { + (*p_callback) (p_dev_rec->bd_addr, BT_TRANSPORT_BR_EDR, p_dev_rec->p_ref_data, res); + } + } +#if (SMP_INCLUDED == TRUE) + btm_sec_check_pending_reqs(); +#endif ///SMP_INCLUDED == TRUE +} + +/******************************************************************************* +** +** Function btm_sec_queue_mx_request +** +** Description Return state description for tracing +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +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 = (tBTM_SEC_QUEUE_ENTRY *)osi_malloc (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; + p_e->transport = BT_TRANSPORT_BR_EDR; + + memcpy (p_e->bd_addr, bd_addr, BD_ADDR_LEN); + + BTM_TRACE_EVENT ("%s() PSM: 0x%04x Is_Orig: %u mx_proto_id: %u mx_chan_id: %u\n", + __func__, psm, is_orig, mx_proto_id, mx_chan_id); + + fixed_queue_enqueue(btm_cb.sec_pending_q, p_e, FIXED_QUEUE_MAX_TIMEOUT); + + return (TRUE); + } + + return (FALSE); +} +static BOOLEAN btm_sec_check_prefetch_pin (tBTM_SEC_DEV_REC *p_dev_rec) +{ + BOOLEAN rv = FALSE; +#if (CLASSIC_BT_INCLUDED == TRUE) + 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); + rv = TRUE; + + if ((major == BTM_COD_MAJOR_AUDIO) + && ((minor == BTM_COD_MINOR_CONFM_HANDSFREE) || (minor == BTM_COD_MINOR_CAR_AUDIO)) ) { + BTM_TRACE_EVENT ("%s() Skipping pre-fetch PIN for carkit COD Major: 0x%02x Minor: 0x%02x\n", + __func__, 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_DEBUG("%s() PIN code callback called\n", __func__); + if (btm_bda_to_acl(p_dev_rec->bd_addr, BT_TRANSPORT_BR_EDR) == 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, (p_dev_rec->p_cur_service == NULL) ? FALSE + : (p_dev_rec->p_cur_service->security_flags + & BTM_SEC_IN_MIN_16_DIGIT_PIN)); + } + } + + rv = TRUE; + } +#endif ///CLASSIC_BT_INCLUDED == TRUE +# + return rv; +} + +/******************************************************************************* +** +** Function btm_sec_auth_payload_tout +** +** Description Processes the HCI Autheniticated Payload Timeout Event +** indicating that a packet containing a valid MIC on the +** connection handle was not received within the programmed +** timeout value. (Spec Default is 30 secs, but can be +** changed via the BTM_SecSetAuthPayloadTimeout() function. +** +*******************************************************************************/ +void btm_sec_auth_payload_tout (UINT8 *p, UINT16 hci_evt_len) +{ + UINT16 handle; + + STREAM_TO_UINT16 (handle, p); + handle = HCID_GET_HANDLE (handle); + + /* Will be exposed to upper layers in the future if/when determined necessary */ + BTM_TRACE_ERROR ("%s on handle 0x%02x\n", __func__, handle); +} + +/******************************************************************************* +** +** Function btm_sec_queue_encrypt_request +** +** Description encqueue encryption request when device has active security +** process pending. +** +*******************************************************************************/ +static BOOLEAN btm_sec_queue_encrypt_request (BD_ADDR bd_addr, tBT_TRANSPORT transport, + tBTM_SEC_CALLBACK *p_callback, void *p_ref_data) +{ + tBTM_SEC_QUEUE_ENTRY *p_e; + p_e = (tBTM_SEC_QUEUE_ENTRY *)osi_malloc(sizeof(tBTM_SEC_QUEUE_ENTRY) + 1); + + if (p_e) { + p_e->psm = 0; /* if PSM 0, encryption request */ + p_e->p_callback = p_callback; + p_e->p_ref_data = (void *)(p_e + 1); + *(UINT8 *)p_e->p_ref_data = *(UINT8 *)(p_ref_data); + p_e->transport = transport; + memcpy(p_e->bd_addr, bd_addr, BD_ADDR_LEN); + fixed_queue_enqueue(btm_cb.sec_pending_q, p_e, FIXED_QUEUE_MAX_TIMEOUT); + return TRUE; + } + + return FALSE; +} + +/******************************************************************************* +** +** Function btm_sec_set_peer_sec_caps +** +** Description This function is called to set sm4 and rmt_sec_caps fields +** based on the available peer device features. +** +** Returns void +** +*******************************************************************************/ +void btm_sec_set_peer_sec_caps(tACL_CONN *p_acl_cb, tBTM_SEC_DEV_REC *p_dev_rec) +{ + BD_ADDR rem_bd_addr; + UINT8 *p_rem_bd_addr; + + if ((btm_cb.security_mode == BTM_SEC_MODE_SP || + btm_cb.security_mode == BTM_SEC_MODE_SP_DEBUG || + btm_cb.security_mode == BTM_SEC_MODE_SC) && + HCI_SSP_HOST_SUPPORTED(p_acl_cb->peer_lmp_features[HCI_EXT_FEATURES_PAGE_1])) { + p_dev_rec->sm4 = BTM_SM4_TRUE; + p_dev_rec->remote_supports_secure_connections = + (HCI_SC_HOST_SUPPORTED(p_acl_cb->peer_lmp_features[HCI_EXT_FEATURES_PAGE_1])); + } else { + p_dev_rec->sm4 = BTM_SM4_KNOWN; + p_dev_rec->remote_supports_secure_connections = FALSE; + } + + BTM_TRACE_API("%s: sm4: 0x%02x, rmt_support_for_secure_connections %d\n", __FUNCTION__, + p_dev_rec->sm4, p_dev_rec->remote_supports_secure_connections); + + /* Store previous state of remote device to check if peer device downgraded + * it's secure connection state. */ +#if (CLASSIC_BT_INCLUDED == TRUE) + if (p_dev_rec->remote_supports_secure_connections >= p_dev_rec->remote_secure_connection_previous_state) { + p_dev_rec->remote_secure_connection_previous_state = p_dev_rec->remote_supports_secure_connections; + } else { + BTM_TRACE_ERROR("Remote Device downgraded security from SC, deleting Link Key"); + + /* Mark in ACL packet that secure connection is downgraded. */ + p_acl_cb->sc_downgrade = 1; + p_dev_rec->remote_secure_connection_previous_state = 0; + + /* As peer device downgraded it's security, peer device is a suspicious + * device. Hence remove pairing information by removing link key + * information. */ + memset(p_dev_rec->link_key, 0, LINK_KEY_LEN); + p_dev_rec->sec_flags &= ~(BTM_SEC_AUTHORIZED | BTM_SEC_AUTHENTICATED + | BTM_SEC_ENCRYPTED | BTM_SEC_NAME_KNOWN + | BTM_SEC_LINK_KEY_KNOWN | BTM_SEC_LINK_KEY_AUTHED + | BTM_SEC_ROLE_SWITCHED | BTM_SEC_16_DIGIT_PIN_AUTHED); + return; + } +#endif + + if (p_dev_rec->remote_features_needed) { + BTM_TRACE_EVENT("%s: Now device in SC Only mode, waiting for peer remote features!\n", + __FUNCTION__); + p_rem_bd_addr = (UINT8 *) rem_bd_addr; + BDADDR_TO_STREAM(p_rem_bd_addr, p_dev_rec->bd_addr); + p_rem_bd_addr = (UINT8 *) rem_bd_addr; + btm_io_capabilities_req(p_rem_bd_addr); + p_dev_rec->remote_features_needed = FALSE; + } +} + +/******************************************************************************* +** +** Function btm_sec_is_serv_level0 +** +** Description This function is called to check if the service corresponding +** to PSM is security mode 4 level 0 service. +** +** Returns TRUE if the service is security mode 4 level 0 service +** +*******************************************************************************/ +static BOOLEAN btm_sec_is_serv_level0(UINT16 psm) +{ + if (psm == BT_PSM_SDP) { + BTM_TRACE_DEBUG("%s: PSM: 0x%04x -> mode 4 level 0 service\n", __FUNCTION__, psm); + return TRUE; + } + return FALSE; +} + +/******************************************************************************* +** +** Function btm_sec_check_pending_enc_req +** +** Description This function is called to send pending encryption callback if +** waiting +** +** Returns void +** +*******************************************************************************/ +static void btm_sec_check_pending_enc_req (tBTM_SEC_DEV_REC *p_dev_rec, tBT_TRANSPORT transport, + UINT8 encr_enable) +{ + if (fixed_queue_is_empty(btm_cb.sec_pending_q)) { + return; + } + + UINT8 res = encr_enable ? BTM_SUCCESS : BTM_ERR_PROCESSING; + list_t *list = fixed_queue_get_list(btm_cb.sec_pending_q); + for (const list_node_t *node = list_begin(list); node != list_end(list); ) { + tBTM_SEC_QUEUE_ENTRY *p_e = (tBTM_SEC_QUEUE_ENTRY *)list_node(node); + node = list_next(node); + + if (memcmp(p_e->bd_addr, p_dev_rec->bd_addr, BD_ADDR_LEN) == 0 && p_e->psm == 0 +#if BLE_INCLUDED == TRUE + && p_e->transport == transport +#endif + ) { +#if BLE_INCLUDED == TRUE + UINT8 sec_act = *(UINT8 *)(p_e->p_ref_data); +#endif + + if (encr_enable == 0 || transport == BT_TRANSPORT_BR_EDR +#if BLE_INCLUDED == TRUE + || (sec_act == BTM_BLE_SEC_ENCRYPT || sec_act == BTM_BLE_SEC_ENCRYPT_NO_MITM) + || (sec_act == BTM_BLE_SEC_ENCRYPT_MITM && p_dev_rec->sec_flags + & BTM_SEC_LE_AUTHENTICATED) +#endif + ) { + if (p_e->p_callback) { + (*p_e->p_callback) (p_dev_rec->bd_addr, transport, p_e->p_ref_data, res); + } + + fixed_queue_try_remove_from_queue(btm_cb.sec_pending_q, (void *)p_e); + } + } + } +} +#endif ///SMP_INCLUDED == TRUE + +/******************************************************************************* +** +** Function btm_sec_set_serv_level4_flags +** +** Description This function is called to set security mode 4 level 4 flags. +** +** Returns service security requirements updated to include secure +** connections only mode. +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +static UINT16 btm_sec_set_serv_level4_flags(UINT16 cur_security, BOOLEAN is_originator) +{ + UINT16 sec_level4_flags = is_originator ? BTM_SEC_OUT_LEVEL4_FLAGS : BTM_SEC_IN_LEVEL4_FLAGS; + + return cur_security | sec_level4_flags; +} +#endif ///SMP_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 +** +*******************************************************************************/ +#if (BLE_INCLUDED == TRUE) +void btm_sec_clear_ble_keys (tBTM_SEC_DEV_REC *p_dev_rec) +{ + + BTM_TRACE_DEBUG ("%s() Clearing BLE Keys\n", __func__); +#if (SMP_INCLUDED== TRUE) + p_dev_rec->ble.key_type = BTM_LE_KEY_NONE; + memset (&p_dev_rec->ble.keys, 0, sizeof(tBTM_SEC_BLE_KEYS)); + +#if (BLE_PRIVACY_SPT == TRUE) + btm_ble_resolving_list_remove_dev(p_dev_rec); +#endif +#endif +} +#endif ///BLE_INCLUDED == TRUE +/******************************************************************************* +** +** 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 (p_dev_rec && +#if (SMP_INCLUDED == TRUE && BLE_INCLUDED == TRUE) + ((p_dev_rec->ble.key_type && (p_dev_rec->sec_flags & BTM_SEC_LE_LINK_KEY_KNOWN)) || +#else + ( +#endif + (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN))) { + is_bonded = TRUE; + } + BTM_TRACE_DEBUG ("%s() is_bonded=%d\n", __func__, is_bonded); + return (is_bonded); +} + +/******************************************************************************* +** +** Function btm_sec_is_le_capable_dev +** +** Description Is the specified device is dual mode or LE only device +** +** Returns TRUE - dev is a dual mode +** +*******************************************************************************/ +BOOLEAN btm_sec_is_le_capable_dev (BD_ADDR bda) +{ + BOOLEAN le_capable = FALSE; + +#if (BLE_INCLUDED== TRUE) + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev (bda); + if (p_dev_rec && (p_dev_rec->device_type & BT_DEVICE_TYPE_BLE) == BT_DEVICE_TYPE_BLE) { + le_capable = TRUE; + } +#endif + return le_capable; +} + +/******************************************************************************* +** +** Function btm_sec_find_bonded_dev +** +** Description Find a bonded device starting from the specified index +** +** Returns TRUE - found a bonded device +** +*******************************************************************************/ +#if (BLE_INCLUDED == TRUE) +BOOLEAN btm_sec_find_bonded_dev (UINT8 start_idx, UINT16 *p_found_handle, tBTM_SEC_DEV_REC **p_rec) +{ + BOOLEAN found = FALSE; + +#if (SMP_INCLUDED== TRUE) + tBTM_SEC_DEV_REC *p_dev_rec; + list_node_t *p_node = NULL; + for (p_node = list_begin(btm_cb.p_sec_dev_rec_list); p_node; p_node = list_next(p_node)) { + p_dev_rec = list_node(p_node); + if (p_dev_rec->ble.key_type || (p_dev_rec->sec_flags & BTM_SEC_LINK_KEY_KNOWN)) { + *p_found_handle = p_dev_rec->hci_handle; + *p_rec = p_dev_rec; + break; + } + } + BTM_TRACE_DEBUG ("%s() found=%d\n", __func__, found); +#endif + return (found); +} +#endif ///BLE_INCLUDED == TRUE +/******************************************************************************* +** +** Function btm_sec_use_smp_br_chnl +** +** Description The function checks if SMP BR connection can be used with +** the peer. +** Is called when authentication for dedicated bonding is +** successfully completed. +** +** Returns TRUE - if SMP BR connection can be used (the link key is +** generated from P-256 and the peer supports Security +** Manager over BR). +** +*******************************************************************************/ +#if (SMP_INCLUDED == TRUE) +static BOOLEAN btm_sec_use_smp_br_chnl(tBTM_SEC_DEV_REC *p_dev_rec) +{ + UINT32 ext_feat; + UINT8 chnl_mask[L2CAP_FIXED_CHNL_ARRAY_SIZE]; + + BTM_TRACE_DEBUG ("%s() link_key_type = 0x%x\n", __func__, + p_dev_rec->link_key_type); + + if ((p_dev_rec->link_key_type != BTM_LKEY_TYPE_UNAUTH_COMB_P_256) && + (p_dev_rec->link_key_type != BTM_LKEY_TYPE_AUTH_COMB_P_256)) { + return FALSE; + } + + if (!L2CA_GetPeerFeatures (p_dev_rec->bd_addr, &ext_feat, chnl_mask)) { + return FALSE; + } + + if (!(chnl_mask[0] & L2CAP_FIXED_CHNL_SMP_BR_BIT)) { + return FALSE; + } + + return TRUE; +} + +/******************************************************************************* +** +** Function btm_sec_is_master +** +** Description The function checks if the device is BR/EDR master after +** pairing is completed. +** +** Returns TRUE - if the device is master. +** +*******************************************************************************/ +static BOOLEAN btm_sec_is_master(tBTM_SEC_DEV_REC *p_dev_rec) +{ + tACL_CONN *p = btm_bda_to_acl(p_dev_rec->bd_addr, BT_TRANSPORT_BR_EDR); + return (p && (p->link_role == BTM_ROLE_MASTER)); +} + +#if (CLASSIC_BT_INCLUDED == TRUE) +/******************************************************************************* +** +** Function btm_sec_legacy_authentication_mutual +** +** Description This function is called when legacy authentication is used +** and only remote device has completed the authentication +** +** Returns TRUE if aunthentication command sent successfully +** +*******************************************************************************/ +BOOLEAN btm_sec_legacy_authentication_mutual (tBTM_SEC_DEV_REC *p_dev_rec) +{ + return (btm_sec_start_authentication (p_dev_rec)); +} + +/******************************************************************************* +** +** Function btm_sec_update_legacy_auth_state +** +** Description This function updates the legacy authentication state +** +** Returns void +** +*******************************************************************************/ +void btm_sec_update_legacy_auth_state(tACL_CONN *p_acl_cb, UINT8 legacy_auth_state) +{ + if (p_acl_cb) { + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev_by_handle (p_acl_cb->hci_handle); + if (p_dev_rec) { + if ((BTM_BothEndsSupportSecureConnections(p_dev_rec->bd_addr) == 0) && + (legacy_auth_state != BTM_ACL_LEGACY_AUTH_NONE)) { + p_acl_cb->legacy_auth_state |= legacy_auth_state; + } else { + p_acl_cb->legacy_auth_state = BTM_ACL_LEGACY_AUTH_NONE; + } + } + } +} + +/******************************************************************************* +** +** Function btm_sec_handle_remote_legacy_auth_cmp +** +** Description This function updates the legacy authneticaiton state +** to indicate that remote device has completed the authentication +** +** Returns void +** +*******************************************************************************/ +void btm_sec_handle_remote_legacy_auth_cmp(UINT16 handle) +{ + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev_by_handle (handle); + tACL_CONN *p_acl_cb = btm_bda_to_acl(p_dev_rec->bd_addr, BT_TRANSPORT_BR_EDR); + btm_sec_update_legacy_auth_state(p_acl_cb, BTM_ACL_LEGACY_AUTH_REMOTE); +} +#endif /// (CLASSIC_BT_INCLUDED == TRUE) +#endif ///SMP_INCLUDED == TRUE + +/****************************************************************************** + ** + ** Function btm_sec_dev_authorization + ** + ** Description This function is used to authorize a specified device(BLE) + ** + ****************************************************************************** + */ +#if (BLE_INCLUDED == TRUE) +BOOLEAN btm_sec_dev_authorization(BD_ADDR bd_addr, BOOLEAN authorized) +{ +#if (SMP_INCLUDED == TRUE) + UINT8 sec_flag = 0; + tBTM_SEC_DEV_REC *p_dev_rec = btm_find_dev(bd_addr); + if (p_dev_rec) { + sec_flag = (UINT8)(p_dev_rec->sec_flags >> 8); + if (!(sec_flag & BTM_SEC_LINK_KEY_AUTHED)) { + BTM_TRACE_ERROR("Authorized should after successful Authentication(MITM protection)\n"); + return FALSE; + } + + if (authorized) { + p_dev_rec->sec_flags |= BTM_SEC_LE_AUTHORIZATION; + } else { + p_dev_rec->sec_flags &= ~(BTM_SEC_LE_AUTHORIZATION); + } + } else { + BTM_TRACE_ERROR("%s, can't find device\n", __func__); + return FALSE; + } + return TRUE; +#endif ///SMP_INCLUDED == TRUE + return FALSE; +} +#endif /// BLE_INCLUDE == TRUE diff --git a/lib/bt/host/bluedroid/stack/btm/include/btm_ble_int.h b/lib/bt/host/bluedroid/stack/btm/include/btm_ble_int.h new file mode 100644 index 00000000..1f55289c --- /dev/null +++ b/lib/bt/host/bluedroid/stack/btm/include/btm_ble_int.h @@ -0,0 +1,548 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains the main Bluetooth Manager (BTM) internal + * definitions. + * + ******************************************************************************/ + +#ifndef BTM_BLE_INT_H +#define BTM_BLE_INT_H + +#include "common/bt_target.h" +#include "osi/fixed_queue.h" +#include "osi/pkt_queue.h" +#include "osi/thread.h" +#include "stack/hcidefs.h" +#include "stack/btm_ble_api.h" +#include "btm_int.h" + +#if BLE_INCLUDED == TRUE && SMP_INCLUDED == TRUE +#include "stack/smp_api.h" +#endif + + +/* 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_DISABLE 0 +#define BTM_BLE_DUPLICATE_ENABLE 1 +#define BTM_BLE_DUPLICATE_MAX BTM_BLE_DUPLICATE_ENABLE + +#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 180 /* Tgap(lim_timeout) = 180s max */ +#define BTM_BLE_LOW_LATENCY_SCAN_INT 8000 /* Interval(scan_int) = 5s= 8000 * 0.625 ms */ +#define BTM_BLE_LOW_LATENCY_SCAN_WIN 8000 /* scan_window = 5s= 8000 * 0.625 ms */ + + +#define BTM_BLE_GAP_ADV_FAST_INT_1 48 /* TGAP(adv_fast_interval1) = 30(used) ~ 60 ms = 48 *0.625 */ +#define BTM_BLE_GAP_ADV_FAST_INT_2 160 /* TGAP(adv_fast_interval2) = 100(used) ~ 150 ms = 160 * 0.625 ms */ +#define BTM_BLE_GAP_ADV_SLOW_INT 2048 /* Tgap(adv_slow_interval) = 1.28 s= 512 * 0.625 ms */ +#define BTM_BLE_GAP_ADV_DIR_MAX_INT 800 /* Tgap(dir_conn_adv_int_max) = 500 ms = 800 * 0.625 ms */ +#define BTM_BLE_GAP_ADV_DIR_MIN_INT 400 /* Tgap(dir_conn_adv_int_min) = 250 ms = 400 * 0.625 ms */ + +#define BTM_BLE_GAP_FAST_ADV_TOUT 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; + +#define BLE_STATIC_PRIVATE_MSB_MASK 0x3f +#define BLE_NON_RESOLVE_ADDR_MSB 0x00 /* most significant bit, bit7, bit6 is 00 to be non-resolvable random */ +#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_NON_RESLVE_BDA(x) ((x[0] & BLE_RESOLVE_ADDR_MASK) == BLE_NON_RESOLVE_ADDR_MSB) +#define BTM_BLE_IS_RESOLVE_BDA(x) ((x[0] & BLE_RESOLVE_ADDR_MASK) == BLE_RESOLVE_ADDR_MSB) + +/* LE scan activity bit mask, continue with LE inquiry bits */ +#define BTM_LE_SELECT_CONN_ACTIVE 0x0040 /* selection connection is in progress */ +#define BTM_LE_OBSERVE_ACTIVE 0x0080 /* observe is in progress */ +#define BTM_LE_DISCOVER_ACTIVE 0x0100 /* scan is in progress */ + +/* BLE scan activity mask checking */ +#define BTM_BLE_IS_SCAN_ACTIVE(x) ((x) & BTM_BLE_SCAN_ACTIVE_MASK) +#define BTM_BLE_IS_INQ_ACTIVE(x) ((x) & BTM_BLE_INQUIRY_MASK) +#define BTM_BLE_IS_OBS_ACTIVE(x) ((x) & BTM_LE_OBSERVE_ACTIVE) +#define BTM_BLE_IS_DISCO_ACTIVE(x) ((x) & BTM_LE_DISCOVER_ACTIVE) +#define BTM_BLE_IS_SEL_CONN_ACTIVE(x) ((x) & BTM_LE_SELECT_CONN_ACTIVE) + +/* BLE ADDR type ID bit */ +#define BLE_ADDR_TYPE_ID_BIT 0x02 + +#define BTM_VSC_CHIP_CAPABILITY_L_VERSION 55 +#define BTM_VSC_CHIP_CAPABILITY_M_VERSION 95 + +typedef enum { + BTM_BLE_IDLE = 0, + BTM_BLE_SCANNING = 1, + BTM_BLE_ADVERTISING = 2, +}tBTM_BLE_GAP_STATE; + +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 +#if (BLE_50_FEATURE_SUPPORT == TRUE) +#define BTM_BLE_EXT_ADV_DATA_LEN_MAX 251 +#define BTM_BLE_PERIODIC_ADV_DATA_LEN_MAX 252 + +#define BTM_BLE_ADV_DATA_OP_INTERMEDIATE_FRAG 0 +#define BTM_BLE_ADV_DATA_OP_FIRST_FRAG 1 +#define BTM_BLE_ADV_DATA_OP_LAST_FRAG 2 +#define BTM_BLE_ADV_DATA_OP_COMPLETE 3 +#define BTM_BLE_ADV_DATA_OP_UNCHANGED_DATA 4 +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) + +#define BTM_BLE_ISVALID_PARAM(x, min, max) (((x) >= (min) && (x) <= (max)) || ((x) == BTM_BLE_CONN_PARAM_UNDEF)) + +typedef struct { + UINT16 discoverable_mode; + UINT16 connectable_mode; + BOOLEAN scan_params_set; + UINT32 scan_window; + UINT32 scan_interval; + UINT8 scan_type; /* current scan type: active or passive */ + UINT8 scan_duplicate_filter; /* duplicate filter enabled for scan */ + UINT16 adv_interval_min; + UINT16 adv_interval_max; + tBTM_BLE_AFP afp; /* advertising filter policy */ + tBTM_BLE_SFP sfp; /* scanning filter policy */ + tBTM_START_ADV_CMPL_CBACK *p_adv_cb; + tBTM_START_STOP_ADV_CMPL_CBACK *p_stop_adv_cb; + tBTM_CLEAR_ADV_CMPL_CBACK *p_clear_adv_cb; + tBLE_ADDR_TYPE adv_addr_type; + UINT8 evt_type; + UINT8 adv_mode; + tBLE_BD_ADDR direct_bda; + tBTM_BLE_EVT directed_conn; + BOOLEAN fast_adv_on; + TIMER_LIST_ENT fast_adv_timer; + + UINT8 adv_len; + UINT8 adv_data_cache[BTM_BLE_CACHE_ADV_DATA_MAX]; + BD_ADDR adv_addr; + /* inquiry BD addr database */ + UINT8 num_bd_entries; + UINT8 max_bd_entries; + tBTM_BLE_LOCAL_ADV_DATA adv_data; + tBTM_BLE_ADV_CHNL_MAP adv_chnl_map; + + TIMER_LIST_ENT inq_timer_ent; + BOOLEAN scan_rsp; + tBTM_BLE_GAP_STATE state; /* Current state that the adv or scan process is in */ + INT8 tx_power; +} tBTM_BLE_INQ_CB; + + +/* random address resolving complete callback */ +typedef void (tBTM_BLE_RESOLVE_CBACK) (void *match_rec, void *p); + +typedef void (tBTM_BLE_ADDR_CBACK) (BD_ADDR_PTR static_random, void *p); + +#define BTM_BLE_GAP_ADDR_BIT_RANDOM (1<<0) +#define BTM_BLE_GAP_ADDR_BIT_RESOLVABLE (1<<1) + +/* random address management control block */ +typedef struct { + tBLE_ADDR_TYPE own_addr_type; /* local device LE address type */ + UINT8 exist_addr_bit; + BD_ADDR static_rand_addr; + BD_ADDR resolvale_addr; + BD_ADDR private_addr; + BD_ADDR random_bda; + BOOLEAN busy; + tBTM_SEC_DEV_REC *p_dev_rec; + tBTM_BLE_RESOLVE_CBACK *p_resolve_cback; + tBTM_BLE_ADDR_CBACK *p_generate_cback; + void *p; + TIMER_LIST_ENT raddr_timer_ent; + tBTM_SET_LOCAL_PRIVACY_CBACK *set_local_privacy_cback; +} 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; + + +typedef struct { + BD_ADDR bd_addr; + UINT8 attr; + BOOLEAN is_connected; + BOOLEAN in_use; +} tBTM_LE_BG_CONN_DEV; + +/* white list using state as a bit mask */ +#define BTM_BLE_WL_IDLE 0 +#define BTM_BLE_WL_INIT 1 +#define BTM_BLE_WL_SCAN 2 +#define BTM_BLE_WL_ADV 4 +typedef UINT8 tBTM_BLE_WL_STATE; + +/* resolving list using state as a bit mask */ +#define BTM_BLE_RL_IDLE 0 +#define BTM_BLE_RL_INIT 1 +#define BTM_BLE_RL_SCAN 2 +#define BTM_BLE_RL_ADV 4 +typedef UINT8 tBTM_BLE_RL_STATE; + +/* BLE connection state */ +#define BLE_CONN_IDLE 0 +#define BLE_DIR_CONN 1 +#define BLE_BG_CONN 2 +#define BLE_CONN_CANCEL 3 +typedef UINT8 tBTM_BLE_CONN_ST; + +typedef struct { + void *p_param; +} tBTM_BLE_CONN_REQ; + +/* LE state request */ +#define BTM_BLE_STATE_INVALID 0 +#define BTM_BLE_STATE_CONN_ADV 1 +#define BTM_BLE_STATE_INIT 2 +#define BTM_BLE_STATE_MASTER 3 +#define BTM_BLE_STATE_SLAVE 4 +#define BTM_BLE_STATE_LO_DUTY_DIR_ADV 5 +#define BTM_BLE_STATE_HI_DUTY_DIR_ADV 6 +#define BTM_BLE_STATE_NON_CONN_ADV 7 +#define BTM_BLE_STATE_PASSIVE_SCAN 8 +#define BTM_BLE_STATE_ACTIVE_SCAN 9 +#define BTM_BLE_STATE_SCAN_ADV 10 +#define BTM_BLE_STATE_MAX 11 +typedef UINT8 tBTM_BLE_STATE; + +#define BTM_BLE_STATE_CONN_ADV_BIT 0x0001 +#define BTM_BLE_STATE_INIT_BIT 0x0002 +#define BTM_BLE_STATE_MASTER_BIT 0x0004 +#define BTM_BLE_STATE_SLAVE_BIT 0x0008 +#define BTM_BLE_STATE_LO_DUTY_DIR_ADV_BIT 0x0010 +#define BTM_BLE_STATE_HI_DUTY_DIR_ADV_BIT 0x0020 +#define BTM_BLE_STATE_NON_CONN_ADV_BIT 0x0040 +#define BTM_BLE_STATE_PASSIVE_SCAN_BIT 0x0080 +#define BTM_BLE_STATE_ACTIVE_SCAN_BIT 0x0100 +#define BTM_BLE_STATE_SCAN_ADV_BIT 0x0200 +typedef UINT16 tBTM_BLE_STATE_MASK; + +#define BTM_BLE_STATE_ALL_MASK 0x03ff +#define BTM_BLE_STATE_ALL_ADV_MASK (BTM_BLE_STATE_CONN_ADV_BIT|BTM_BLE_STATE_LO_DUTY_DIR_ADV_BIT|BTM_BLE_STATE_HI_DUTY_DIR_ADV_BIT|BTM_BLE_STATE_SCAN_ADV_BIT) +#define BTM_BLE_STATE_ALL_SCAN_MASK (BTM_BLE_STATE_PASSIVE_SCAN_BIT|BTM_BLE_STATE_ACTIVE_SCAN_BIT) +#define BTM_BLE_STATE_ALL_CONN_MASK (BTM_BLE_STATE_MASTER_BIT|BTM_BLE_STATE_SLAVE_BIT) + +#ifndef BTM_LE_RESOLVING_LIST_MAX +#define BTM_LE_RESOLVING_LIST_MAX 0x20 +#endif + +#define BTM_DUPLICATE_SCAN_EXCEPTIONAL_INFO_ADV_ADDR 0 +#define BTM_DUPLICATE_SCAN_EXCEPTIONAL_INFO_MESH_LINK_ID 1 +#define BTM_DUPLICATE_SCAN_EXCEPTIONAL_INFO_MESH_BEACON_TYPE 2 +#define BTM_DUPLICATE_SCAN_EXCEPTIONAL_INFO_MESH_PROV_SRV_ADV 3 +#define BTM_DUPLICATE_SCAN_EXCEPTIONAL_INFO_MESH_PROXY_SRV_ADV 4 + +typedef struct { + BD_ADDR *resolve_q_random_pseudo; + UINT8 *resolve_q_action; + UINT8 q_next; + UINT8 q_pending; +} tBTM_BLE_RESOLVE_Q; + +typedef struct { + BOOLEAN in_use; + BOOLEAN to_add; + BD_ADDR bd_addr; + tBLE_ADDR_TYPE addr_type; + UINT8 attr; +} tBTM_BLE_WL_OP; + +/* BLE privacy mode */ +#define BTM_PRIVACY_NONE 0 /* BLE no privacy */ +#define BTM_PRIVACY_1_1 1 /* BLE privacy 1.1, do not support privacy 1.0 */ +#define BTM_PRIVACY_1_2 2 /* BLE privacy 1.2 */ +#define BTM_PRIVACY_MIXED 3 /* BLE privacy mixed mode, broadcom propietary mode */ +typedef UINT8 tBTM_PRIVACY_MODE; + +/* data length change event callback */ +typedef void (tBTM_DATA_LENGTH_CHANGE_CBACK) (UINT16 max_tx_length, UINT16 max_rx_length); + +/* Define BLE Device Management control structure +*/ +typedef struct { + UINT16 scan_activity; /* LE scan activity mask */ + + /***************************************************** + ** BLE Inquiry + *****************************************************/ + tBTM_BLE_INQ_CB inq_var; + + /* observer callback and timer */ + tBTM_INQ_RESULTS_CB *p_obs_results_cb; + tBTM_CMPL_CB *p_obs_cmpl_cb; + tBTM_INQ_DIS_CB *p_obs_discard_cb; + TIMER_LIST_ENT obs_timer_ent; + + /* scan callback and timer */ + tBTM_INQ_RESULTS_CB *p_scan_results_cb; + tBTM_CMPL_CB *p_scan_cmpl_cb; + TIMER_LIST_ENT scan_timer_ent; + + struct pkt_queue *adv_rpt_queue; + struct osi_event *adv_rpt_ready; + + /* background connection procedure cb value */ + tBTM_BLE_CONN_TYPE bg_conn_type; + UINT32 scan_int; + UINT32 scan_win; + tBTM_BLE_SEL_CBACK *p_select_cback; + /* white list information */ + UINT8 white_list_avail_size; + tBTM_UPDATE_WHITELIST_CBACK *update_wl_cb; + tBTM_BLE_WL_STATE wl_state; + + fixed_queue_t *conn_pending_q; + tBTM_BLE_CONN_ST conn_state; + + /* random address management control block */ + tBTM_LE_RANDOM_CB addr_mgnt_cb; + + BOOLEAN enabled; + +#if BLE_PRIVACY_SPT == TRUE + BOOLEAN mixed_mode; /* privacy 1.2 mixed mode is on or not */ + tBTM_PRIVACY_MODE privacy_mode; /* privacy mode */ + UINT8 resolving_list_avail_size; /* resolving list available size */ + tBTM_BLE_RESOLVE_Q resolving_list_pend_q; /* Resolving list queue */ + tBTM_BLE_RL_STATE suspended_rl_state; /* Suspended resolving list state */ + UINT8 *irk_list_mask; /* IRK list availability mask, up to max entry bits */ + tBTM_BLE_RL_STATE rl_state; /* Resolving list state */ +#endif + + tBTM_BLE_WL_OP wl_op_q[BTM_BLE_MAX_BG_CONN_DEV_NUM]; + + /* current BLE link state */ + tBTM_BLE_STATE_MASK cur_states; /* bit mask of tBTM_BLE_STATE */ + UINT8 link_count[2]; /* total link count master and slave*/ + tBTM_UPDATE_DUPLICATE_EXCEPTIONAL_LIST_CMPL_CBACK *update_exceptional_list_cmp_cb; +} tBTM_BLE_CB; + +#ifdef __cplusplus +extern "C" { +#endif + +void btm_ble_timeout(TIMER_LIST_ENT *p_tle); +void btm_ble_process_adv_pkt (UINT8 *p); +void btm_ble_process_adv_discard_evt(UINT8 *p); +void btm_ble_process_direct_adv_pkt (UINT8 *p); +bool btm_ble_adv_pkt_ready(void); +bool btm_ble_adv_pkt_post(pkt_linked_item_t *pkt); +void btm_ble_proc_scan_rsp_rpt (UINT8 *p); +tBTM_STATUS btm_ble_read_remote_name(BD_ADDR remote_bda, tBTM_INQ_INFO *p_cur, tBTM_CMPL_CB *p_cb); +BOOLEAN btm_ble_cancel_remote_name(BD_ADDR remote_bda); + +tBTM_STATUS btm_ble_set_discoverability(UINT16 combined_mode); +tBTM_STATUS btm_ble_set_connectability(UINT16 combined_mode); +tBTM_STATUS btm_ble_start_inquiry (UINT8 mode, UINT8 duration); +void btm_ble_stop_scan(void); +void btm_clear_all_pending_le_entry(void); + +BOOLEAN btm_ble_send_extended_scan_params(UINT8 scan_type, UINT32 scan_int, UINT32 scan_win, UINT8 addr_type_own, UINT8 scan_filter_policy); +void btm_ble_stop_inquiry(void); +void btm_ble_init (void); +void btm_ble_free (void); +void btm_ble_connected (UINT8 *bda, UINT16 handle, UINT8 enc_mode, UINT8 role, tBLE_ADDR_TYPE addr_type, BOOLEAN addr_matched); +void btm_ble_read_remote_features_complete(UINT8 *p); +void btm_ble_write_adv_enable_complete(UINT8 *p); +void btm_ble_conn_complete(UINT8 *p, UINT16 evt_len, BOOLEAN enhanced); +void btm_read_ble_local_supported_states_complete(UINT8 *p, UINT16 evt_len); +tBTM_BLE_CONN_ST btm_ble_get_conn_st(void); +void btm_ble_set_conn_st(tBTM_BLE_CONN_ST new_st); +UINT8 *btm_ble_build_adv_data(tBTM_BLE_AD_MASK *p_data_mask, UINT8 **p_dst, tBTM_BLE_ADV_DATA *p_data); +tBTM_STATUS btm_ble_start_adv(void); +tBTM_STATUS btm_ble_stop_adv(void); +tBTM_STATUS btm_ble_start_scan(void); +void btm_ble_create_ll_conn_complete (UINT8 status); +void btm_ble_create_conn_cancel_complete (UINT8 *p); +tBTM_STATUS btm_ble_set_random_addr(BD_ADDR random_bda); + +/* LE security function from btm_sec.c */ +#if SMP_INCLUDED == TRUE +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); +void btm_ble_ltk_request_reply(BD_ADDR bda, BOOLEAN use_stk, BT_OCTET16 stk); +UINT8 btm_proc_smp_cback(tSMP_EVT event, BD_ADDR bd_addr, tSMP_EVT_DATA *p_data); +tBTM_STATUS btm_ble_set_encryption (BD_ADDR bd_addr, void *p_ref_data, UINT8 link_role); +void btm_ble_ltk_request(UINT16 handle, UINT8 rand[8], UINT16 ediv); +tBTM_STATUS btm_ble_start_encrypt(BD_ADDR bda, BOOLEAN use_stk, BT_OCTET16 stk); +void btm_ble_link_encrypted(BD_ADDR bd_addr, UINT8 encr_enable); +#endif + +/* LE device management functions */ +void btm_ble_reset_id( void ); + +/* security related functions */ +void btm_ble_increment_sign_ctr(BD_ADDR bd_addr, BOOLEAN is_local ); +BOOLEAN btm_get_local_div (BD_ADDR bd_addr, UINT16 *p_div); +BOOLEAN btm_ble_get_enc_key_type(BD_ADDR bd_addr, UINT8 *p_key_types); + +void btm_ble_test_command_complete(UINT8 *p); +void btm_ble_rand_enc_complete (UINT8 *p, UINT16 op_code, tBTM_RAND_ENC_CB *p_enc_cplt_cback); + +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); +void btm_ble_update_sec_key_size(BD_ADDR bd_addr, UINT8 enc_key_size); +UINT8 btm_ble_read_sec_key_size(BD_ADDR bd_addr); + +/* white list function */ +BOOLEAN btm_update_dev_to_white_list(BOOLEAN to_add, BD_ADDR bd_addr, tBLE_ADDR_TYPE addr_type, tBTM_UPDATE_WHITELIST_CBACK *update_wl_cb); +void btm_update_scanner_filter_policy(tBTM_BLE_SFP scan_policy); +void btm_update_adv_filter_policy(tBTM_BLE_AFP adv_policy); +void btm_ble_clear_white_list (tBTM_UPDATE_WHITELIST_CBACK *update_wl_cb); +void btm_read_white_list_size_complete(UINT8 *p, UINT16 evt_len); +void btm_ble_add_2_white_list_complete(UINT8 status); +void btm_ble_remove_from_white_list_complete(UINT8 *p, UINT16 evt_len); +void btm_ble_clear_white_list_complete(UINT8 *p, UINT16 evt_len); +void btm_ble_white_list_init(UINT8 white_list_size); + +/* background connection function */ +BOOLEAN btm_ble_suspend_bg_conn(void); +BOOLEAN btm_ble_resume_bg_conn(void); +void btm_ble_initiate_select_conn(BD_ADDR bda); +BOOLEAN btm_ble_start_auto_conn(BOOLEAN start); +BOOLEAN btm_ble_start_select_conn(BOOLEAN start, tBTM_BLE_SEL_CBACK *p_select_cback); +BOOLEAN btm_ble_renew_bg_conn_params(BOOLEAN add, BD_ADDR bd_addr); +void btm_write_dir_conn_wl(BD_ADDR target_addr); +BOOLEAN btm_ble_update_mode_operation(UINT8 link_role, BD_ADDR bda, UINT8 status); +BOOLEAN btm_execute_wl_dev_operation(void); +void btm_ble_update_link_topology_mask(UINT8 role, BOOLEAN increase); + +/* direct connection utility */ +BOOLEAN btm_send_pending_direct_conn(void); +void btm_ble_enqueue_direct_conn_req(void *p_param); + +/* BLE address management */ +void btm_gen_resolvable_private_addr (void *p_cmd_cplt_cback); +void btm_gen_non_resolvable_private_addr (tBTM_BLE_ADDR_CBACK *p_cback, void *p); +void btm_ble_resolve_random_addr(BD_ADDR random_bda, tBTM_BLE_RESOLVE_CBACK *p_cback, void *p); +void btm_gen_resolve_paddr_low(tBTM_RAND_ENC *p); + +/* privacy function */ +#if (defined BLE_PRIVACY_SPT && BLE_PRIVACY_SPT == TRUE) +/* BLE address mapping with CS feature */ +BOOLEAN btm_identity_addr_to_random_pseudo(BD_ADDR bd_addr, UINT8 *p_addr_type, BOOLEAN refresh); +BOOLEAN btm_random_pseudo_to_identity_addr(BD_ADDR random_pseudo, UINT8 *p_static_addr_type); +void btm_ble_refresh_peer_resolvable_private_addr(BD_ADDR pseudo_bda, BD_ADDR rra, UINT8 rra_type); +void btm_ble_refresh_local_resolvable_private_addr(BD_ADDR pseudo_addr, BD_ADDR local_rpa); +void btm_ble_read_resolving_list_entry_complete(UINT8 *p, UINT16 evt_len) ; +void btm_ble_set_addr_resolution_enable_complete(UINT8 *p, UINT16 evt_len) ; +void btm_ble_remove_resolving_list_entry_complete(UINT8 *p, UINT16 evt_len); +void btm_ble_add_resolving_list_entry_complete(UINT8 *p, UINT16 evt_len); +void btm_ble_clear_resolving_list_complete(UINT8 *p, UINT16 evt_len); +void btm_read_ble_resolving_list_size_complete (UINT8 *p, UINT16 evt_len); +void btm_ble_enable_resolving_list(UINT8); +BOOLEAN btm_ble_disable_resolving_list(UINT8 rl_mask, BOOLEAN to_resume); +void btm_ble_enable_resolving_list_for_platform (UINT8 rl_mask); +void btm_ble_resolving_list_init(UINT8 max_irk_list_sz); +void btm_ble_resolving_list_cleanup(void); +void btm_ble_add_default_entry_to_resolving_list(void); +#endif + +void btm_ble_multi_adv_configure_rpa (tBTM_BLE_MULTI_ADV_INST *p_inst); +void btm_ble_multi_adv_init(void); +void *btm_ble_multi_adv_get_ref(UINT8 inst_id); +void btm_ble_multi_adv_cleanup(void); +void btm_ble_multi_adv_reenable(UINT8 inst_id); +void btm_ble_multi_adv_enb_privacy(BOOLEAN enable); +char btm_ble_map_adv_tx_power(int tx_power_index); +void btm_ble_batchscan_init(void); +void btm_ble_batchscan_cleanup(void); +void btm_ble_adv_filter_init(void); +void btm_ble_adv_filter_cleanup(void); +BOOLEAN btm_ble_topology_check(tBTM_BLE_STATE_MASK request); +BOOLEAN btm_ble_clear_topology_mask(tBTM_BLE_STATE_MASK request_state); +BOOLEAN btm_ble_set_topology_mask(tBTM_BLE_STATE_MASK request_state); +tBTM_BLE_STATE_MASK btm_ble_get_topology_mask(void); + +#if BTM_BLE_CONFORMANCE_TESTING == TRUE +void btm_ble_set_no_disc_if_pair_fail (BOOLEAN disble_disc); +void btm_ble_set_test_mac_value (BOOLEAN enable, UINT8 *p_test_mac_val); +void btm_ble_set_test_local_sign_cntr_value(BOOLEAN enable, UINT32 test_local_sign_cntr); +void btm_set_random_address(BD_ADDR random_bda); +void btm_ble_set_keep_rfu_in_auth_req(BOOLEAN keep_rfu); +#endif + +BOOLEAN btm_get_current_conn_params(BD_ADDR bda, UINT16 *interval, UINT16 *latency, UINT16 *timeout); + +#if (BLE_50_FEATURE_SUPPORT == TRUE) +void btm_ble_update_phy_evt(tBTM_BLE_UPDATE_PHY *params); +void btm_ble_scan_timeout_evt(void); +void btm_ble_adv_set_terminated_evt(tBTM_BLE_ADV_TERMINAT *params); +void btm_ble_ext_adv_report_evt(tBTM_BLE_EXT_ADV_REPORT *params); +void btm_ble_scan_req_received_evt(tBTM_BLE_SCAN_REQ_RECEIVED *params); +void btm_ble_channel_select_algorithm_evt(tBTM_BLE_CHANNEL_SEL_ALG *params); +void btm_ble_periodic_adv_report_evt(tBTM_PERIOD_ADV_REPORT *params); +void btm_ble_periodic_adv_sync_lost_evt(tBTM_BLE_PERIOD_ADV_SYNC_LOST *params); +void btm_ble_periodic_adv_sync_establish_evt(tBTM_BLE_PERIOD_ADV_SYNC_ESTAB *params); +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) + +#if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) +void btm_ble_periodic_adv_sync_trans_recv_evt(tBTM_BLE_PERIOD_ADV_SYNC_TRANS_RECV *params); +#endif // #if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) + +/* +#ifdef __cplusplus +} +#endif +*/ +#endif diff --git a/lib/bt/host/bluedroid/stack/btm/include/btm_int.h b/lib/bt/host/bluedroid/stack/btm/include/btm_int.h new file mode 100644 index 00000000..c22423a1 --- /dev/null +++ b/lib/bt/host/bluedroid/stack/btm/include/btm_int.h @@ -0,0 +1,1258 @@ +/****************************************************************************** + * + * Copyright (C) 1999-2012 Broadcom Corporation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + ******************************************************************************/ + +/****************************************************************************** + * + * this file contains the main Bluetooth Manager (BTM) internal + * definitions. + * + ******************************************************************************/ +#ifndef BTM_INT_H +#define BTM_INT_H + +typedef struct tBTM_SEC_DEV_REC tBTM_SEC_DEV_REC; + +#include "common/bt_defs.h" +#include "common/bt_target.h" +#include "stack/hcidefs.h" + +#include "stack/rfcdefs.h" + +#include "stack/btm_api.h" +#include "osi/fixed_queue.h" + +#if (BLE_INCLUDED == TRUE) +#include "btm_ble_int.h" +#endif +#if (SMP_INCLUDED == TRUE) +#include "stack/smp_api.h" +#endif + +#define ESP_VS_REM_LEGACY_AUTH_CMP 0x03 + +#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, BT_TRANSPORT_BR_EDR) != 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)->peer_lmp_features[HCI_EXT_FEATURES_PAGE_0]) && \ + HCI_ATOMIC_ENCRYPT_SUPPORTED(controller_get_interface()->get_features_classic(0)->as_array)) \ + ? TRUE : FALSE) + +#define BTM_IS_BRCM_CONTROLLER() (controller_get_interface()->get_bt_version()->manufacturer == LMP_COMPID_BROADCOM) + +typedef struct t_acl_db_param{ +#define ACL_DB_HANDLE 0x00 +#define ACL_DB_BDA 0x01 + UINT8 type; + void *p_data1; + void *p_data2; +}tACL_DB_PARAM; + +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; + +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 the ACL Management control structure +*/ +typedef struct { +UINT16 hci_handle; +UINT16 pkt_types_mask; +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 peer_lmp_features[HCI_EXT_FEATURES_PAGE_MAX + 1]; /* Peer LMP Extended features mask table for the device */ +UINT8 num_read_pages; +UINT8 lmp_version; + +BOOLEAN in_use; +UINT8 link_role; +BOOLEAN link_up_issued; /* True if busy_level link up has been issued */ +BOOLEAN sc_downgrade; /* Store if security is downgraded or not. */ + +#define BTM_ACL_LEGACY_AUTH_NONE (0) +#define BTM_ACL_LEGACY_AUTH_SELF (1<<0) +#define BTM_ACL_LEGACY_AUTH_REMOTE (1<<1) +#define BTM_ACL_LEGACY_AUTH_MUTUAL (1<<2) +UINT8 legacy_auth_state; + +#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; + +#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 BLE_INCLUDED == TRUE +tBT_TRANSPORT transport; +BD_ADDR conn_addr; /* local device address used for this connection */ +UINT8 conn_addr_type; /* local device address type for this connection */ +BD_ADDR active_remote_addr; /* remote address used on this connection */ +UINT8 active_remote_addr_type; /* local device address type for this connection */ +BD_FEATURES peer_le_features; /* Peer LE Used features mask for the device */ +tBTM_SET_PKT_DATA_LENGTH_CBACK *p_set_pkt_data_cback; +tBTM_LE_SET_PKT_DATA_LENGTH_PARAMS data_length_params; +BOOLEAN data_len_updating; +// data len update cmd cache +BOOLEAN data_len_waiting; +tBTM_SET_PKT_DATA_LENGTH_CBACK *p_set_data_len_cback_waiting; +UINT16 tx_len_waiting; +#endif +tBTM_PM_MCB *p_pm_mode_db; /* Pointer to PM mode control block per ACL link */ + +} 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; + +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 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 */ + +TIMER_LIST_ENT tx_power_timer; +tBTM_CMPL_CB *p_tx_power_cmpl_cb;/* Callback function to be called */ + +#if CLASSIC_BT_INCLUDED == TRUE +TIMER_LIST_ENT afh_channels_timer; +tBTM_CMPL_CB *p_afh_channels_cmpl_cb; /* Callback function to be called When */ +/* set AFH channels is completed */ + +TIMER_LIST_ENT page_timeout_set_timer; +tBTM_CMPL_CB *p_page_to_set_cmpl_cb; /* Callback function to be called when */ +/* set page timeout is completed */ +TIMER_LIST_ENT set_acl_pkt_types_timer; +tBTM_CMPL_CB *p_set_acl_pkt_types_cmpl_cb; /* Callback function to be called when */ +/* set ACL packet types is completed */ +#endif + +DEV_CLASS dev_class; /* Local device class */ + +#if BLE_INCLUDED == TRUE + +TIMER_LIST_ENT ble_channels_timer; +tBTM_CMPL_CB *p_ble_channels_cmpl_cb; /* Callback function to be called When + ble set host channels is completed */ + +tBTM_CMPL_CB *p_le_test_cmd_cmpl_cb; /* Callback function to be called when + LE test mode command has been sent successfully */ + +BD_ADDR read_tx_pwr_addr; /* read TX power target address */ + +#define BTM_LE_SUPPORT_STATE_SIZE 8 +UINT8 le_supported_states[BTM_LE_SUPPORT_STATE_SIZE]; + +tBTM_BLE_LOCAL_ID_KEYS id_keys; /* local BLE ID keys */ +BT_OCTET16 ble_encryption_key_value; /* 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 */ + +tBTM_IO_CAP loc_io_caps; /* IO capability of the local device */ +tBTM_AUTH_REQ loc_auth_req; /* the auth_req flag */ +BOOLEAN secure_connections_only; /* Rejects service level 0 connections if */ +/* itself or peer device doesn't support */ +/* secure connections */ +} 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 + +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; + +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; + + +enum { +INQ_NONE, +INQ_LE_OBSERVE, +INQ_GENERAL +}; +typedef UINT8 tBTM_INQ_TYPE; + +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 */ + tBTM_INQ_TYPE scan_type; + UINT16 page_timeout; + + 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_inq_ble_cmpl_cb; /*completion callback exclusively for LE Observe*/ + tBTM_INQ_RESULTS_CB *p_inq_ble_results_cb;/*results callback exclusively for LE observe*/ + tBTM_CMPL_CB *p_inqfilter_cmpl_cb; /* Called (if not NULL) after inquiry filter completed */ + 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; + 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 */ + 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 */ +#if (defined(BTA_HOST_INTERLEAVE_SEARCH) && BTA_HOST_INTERLEAVE_SEARCH == TRUE) + btm_inq_state next_state; /*interleaving state to determine next mode to be inquired*/ +#endif +} 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; + +#define BTM_DATA_HANDLE_MASK 0x0FFF + +#define BTMD_GET_HANDLE(u16) (UINT16)((u16) & BTM_DATA_HANDLE_MASK) + +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 +#define BTM_SCO_XMIT_QUEUE_THRS 30 +#define BTM_SCO_XMIT_QUEUE_HIGH_WM 20 + fixed_queue_t *xmit_data_q; /* SCO data transmitting queue */ + INT16 sent_not_acked; + tBTM_SCO_PKT_STAT_NUMS pkt_stat_nums; +#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 */ + UINT16 num_lm_sco_bufs; +#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 +void btm_set_sco_ind_cback( tBTM_SCO_IND_CBACK *sco_ind_cb ); +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); +void btm_reject_sco_link(UINT16 sco_inx ); +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 +*/ +#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) + +#define BTM_SEC_OUT_LEVEL4_FLAGS (BTM_SEC_OUT_AUTHENTICATE | BTM_SEC_OUT_ENCRYPT | \ + BTM_SEC_OUT_MITM | BTM_SEC_MODE4_LEVEL4) + +#define BTM_SEC_IN_LEVEL4_FLAGS (BTM_SEC_IN_AUTHENTICATE | BTM_SEC_IN_ENCRYPT | \ + BTM_SEC_IN_MITM | BTM_SEC_MODE4_LEVEL4) + +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 pltk; /* peer long term key */ + BT_OCTET16 pcsrk; /* peer SRK peer device used to secured sign local data */ + + BT_OCTET16 lltk; /* local long term key */ + BT_OCTET16 lcsrk; /* local 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; + +typedef struct { + BD_ADDR pseudo_addr; /* LE pseudo address of the device if different from device address */ + tBLE_ADDR_TYPE ble_addr_type; /* LE device type: public or random address */ + tBLE_ADDR_TYPE static_addr_type; /* static address type */ + BD_ADDR static_addr; /* static address */ + +#define BTM_WHITE_LIST_BIT 0x01 +#define BTM_RESOLVING_LIST_BIT 0x02 + UINT8 in_controller_list; /* in controller resolving list or not */ + UINT8 resolving_list_index; +#if BLE_PRIVACY_SPT == TRUE + BD_ADDR cur_rand_addr; /* current random address */ + +#define BTM_BLE_ADDR_PSEUDO 0 /* address index device record */ +#define BTM_BLE_ADDR_RRA 1 /* cur_rand_addr */ +#define BTM_BLE_ADDR_STATIC 2 /* static_addr */ + UINT8 active_addr_type; +#endif + +#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 */ +#if (SMP_SLAVE_CON_PARAMS_UPD_ENABLE == TRUE) + bool skip_update_conn_param; /* skip update connection paraams or not*/ +#endif + UINT16 auth_mode; /* Authentication mode */ +#endif +#if (BLE_PRIVACY_SPT == TRUE && (!CONTROLLER_RPA_LIST_ENABLE)) + tBLE_ADDR_TYPE current_addr_type; /* current adv addr type*/ + BD_ADDR current_addr; /* current adv addr*/ + bool current_addr_valid; /* current addr info is valid or not*/ +#endif +} tBTM_SEC_BLE; + + +#endif /* BLE_INCLUDED */ + +/* Peering bond type */ +enum { + BOND_TYPE_UNKNOWN, + BOND_TYPE_PERSISTENT, + BOND_TYPE_TEMPORARY +}; +typedef UINT8 tBTM_BOND_TYPE; + +/* +** Define structure for Security Device Record. +** A record exists for each device authenticated with this device +*/ +struct tBTM_SEC_DEV_REC{ + 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 */ + UINT8 pin_code_length; /* Length of the pin_code used for paring */ + +#define BTM_SEC_AUTHORIZED BTM_SEC_FLAG_AUTHORIZED /* 0x01 */ +#define BTM_SEC_AUTHENTICATED BTM_SEC_FLAG_AUTHENTICATED /* 0x02 */ +#define BTM_SEC_ENCRYPTED BTM_SEC_FLAG_ENCRYPTED /* 0x04 */ +#define BTM_SEC_NAME_KNOWN 0x08 +#define BTM_SEC_LINK_KEY_KNOWN BTM_SEC_FLAG_LKEY_KNOWN /* 0x10 */ +#define BTM_SEC_LINK_KEY_AUTHED BTM_SEC_FLAG_LKEY_AUTHED /* 0x20 */ +#define BTM_SEC_ROLE_SWITCHED 0x40 +#define BTM_SEC_IN_USE 0x80 + /* LE link security flag */ +#define BTM_SEC_LE_AUTHORIZATION 0x0100 /* LE link is authorized */ +#define BTM_SEC_LE_AUTHENTICATED 0x0200 /* LE link is encrypted after pairing with MITM */ +#define BTM_SEC_LE_ENCRYPTED 0x0400 /* LE link is encrypted */ +#define BTM_SEC_LE_NAME_KNOWN 0x0800 /* not used */ +#define BTM_SEC_LE_LINK_KEY_KNOWN 0x1000 /* bonded with peer (peer LTK and/or SRK is saved) */ +#define BTM_SEC_LE_LINK_KEY_AUTHED 0x2000 /* pairing is done with MITM */ +#define BTM_SEC_16_DIGIT_PIN_AUTHED 0x4000 /* pairing is done with 16 digit pin */ + + UINT16 sec_flags; /* Current device security state */ + + tBTM_BD_NAME sec_bd_name; /* User friendly name of the device. (may be truncated to save space in dev_rec table) */ + BD_FEATURES features[HCI_EXT_FEATURES_PAGE_MAX + 1]; /* Features supported by the device */ + UINT8 num_read_pages; + +#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 /* disconnecting BR/EDR */ +#define BTM_SEC_STATE_DELAY_FOR_ENC 7 /* delay to check for encryption to work around */ + /* controller problems */ +#define BTM_SEC_STATE_DISCONNECTING_BLE 8 /* disconnecting BLE */ +#define BTM_SEC_STATE_DISCONNECTING_BOTH 9 /* disconnecting BR/EDR and BLE */ + + 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_MAX_PRE_SM4_LKEY_TYPE BTM_LKEY_TYPE_REMOTE_UNIT /* the link key type used by legacy pairing */ + +#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 */ + tBTM_AUTH_REQ rmt_auth_req; /* the auth_req flag as in the IO caps rsp evt */ + BOOLEAN remote_supports_secure_connections; + BOOLEAN remote_features_needed; /* set to true if the local device is in */ + /* "Secure Connections Only" mode and it receives */ + /* HCI_IO_CAPABILITY_REQUEST_EVT from the peer before */ + /* it knows peer's support for Secure Connections */ + BOOLEAN remote_secure_connection_previous_state; /* Stores if peer ever supported + secure connection. This will be helpful to know when peer device downgrades it's security. */ + + UINT16 ble_hci_handle; /* use in DUMO connection */ + +#define BTM_ENC_MODE_UNKNOWN 0xff + UINT8 enc_mode; /* encryption mode of current link */ + UINT8 enc_key_size; /* current link encryption key size */ + tBT_DEVICE_TYPE device_type; + BOOLEAN new_encryption_key_is_p256; /* Set to TRUE when the newly generated LK + ** is generated from P-256. + ** Link encrypted with such LK can be used + ** for SM over BR/EDR. + */ + BOOLEAN no_smp_on_br; /* if set to TRUE then SMP on BR/EDR doesn't */ + /* work, i.e. link keys crosspairing */ + /* SC BR/EDR->SC LE doesn't happen */ + tBTM_BOND_TYPE bond_type; /* peering bond type */ + +#if BLE_INCLUDED == TRUE + tBTM_SEC_BLE ble; + tBTM_LE_CONN_PRAMS conn_params; +#if (BLE_50_FEATURE_SUPPORT == TRUE) + tBTM_EXT_CONN_PARAMS ext_conn_params; +#endif // #if (BLE_50_FEATURE_SUPPORT == TRUE) +#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 -- +#define BTM_SEC_NO_LAST_SERVICE_ID 0 + UINT8 last_author_service_id; /* ID of last serviced authorized: Reset after each l2cap connection */ + BOOLEAN enc_init_by_we; +}; + +#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 BTM_SEC_LE_MASK (BTM_SEC_LE_AUTHENTICATED|BTM_SEC_LE_ENCRYPTED|BTM_SEC_LE_LINK_KEY_KNOWN|BTM_SEC_LE_LINK_KEY_AUTHED) + +/* +** 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; + +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; + +#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; + +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_CANCEL_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 complete */ + 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 /* Disconnect when done */ +#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 */ +#define BTM_PAIR_FLAGS_WE_CANCEL_DD 0x40 /* set this bit when cancelling a bonding procedure */ +#define BTM_PAIR_FLAGS_LE_ACTIVE 0x80 /* use this bit when SMP pairing is active */ + + +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; + tBT_TRANSPORT transport; +} 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 */ + +#define BTM_INVALID_HANDLE 0xFFFF + +typedef struct { + tBTM_CFG cfg; /* Device configuration */ + + /**************************************************** + ** ACL Management + ****************************************************/ + list_t *p_acl_db_list; +#if (CLASSIC_BT_INCLUDED == TRUE) + UINT8 btm_scn[BTM_MAX_SCN]; /* current SCNs: TRUE if SCN is in use */ +#endif ///CLASSIC_BT_INCLUDED == TRUE + UINT16 btm_def_link_policy; + UINT16 btm_def_link_super_tout; + + tBTM_ACL_LINK_STAT_CB *p_acl_link_stat_cb; /* Callback for when ACL link related events came */ + + tBTM_BL_EVENT_MASK bl_evt_mask; + tBTM_BL_CHANGE_CB *p_bl_changed_cb; /* Callback for when Busy Level changed */ + + /**************************************************** + ** Power Management + ****************************************************/ + list_t *p_pm_mode_db_list; + tBTM_PM_RCB pm_reg_db[BTM_MAX_PM_RECORDS + 1]; /* per application/module */ + UINT16 pm_pend_link_hdl; /* 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 */ + + /***************************************************** + ** 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; + tBTM_BLE_VSC_CB cmn_ble_vsc_cb; +#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; + + /***************************************************** + ** 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]; +#if (SMP_INCLUDED == TRUE) + tBTM_SEC_DEV_REC *p_collided_dev_rec; +#endif ///SMP_INCLUDED == TRUE + 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 sec_req_pending; /* TRUE if a request is pending */ +#if (CLASSIC_BT_INCLUDED == TRUE) + BOOLEAN pin_type_changed; /* pin type changed during bonding */ +#endif ///CLASSIC_BT_INCLUDED == TRUE +#if (SMP_INCLUDED == TRUE) +#if (CLASSIC_BT_INCLUDED == TRUE) +// 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 */ + UINT8 disc_reason; /* for legacy devices */ + UINT16 disc_handle; /* for legacy devices */ +#endif ///CLASSIC_BT_INCLUDED == TRUE + 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 */ + +#endif ///SMP_INCLUDED == TRUE +#if SMP_INCLUDED == TRUE || CLASSIC_BT_INCLUDED == TRUE + tBTM_SEC_SERV_REC sec_serv_rec[BTM_SEC_MAX_SERVICE_RECORDS]; +#endif // SMP_INCLUDED == TRUE || BT_CLASSIC_ENABLED == TRUE + list_t *p_sec_dev_rec_list; + tBTM_SEC_SERV_REC *p_out_serv; + tBTM_MKEY_CALLBACK *mkey_cback; + + BD_ADDR connecting_bda; + DEV_CLASS connecting_dc; + + UINT8 acl_disc_reason; + UINT8 trace_level; + 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 */ + fixed_queue_t *page_queue; + BOOLEAN paging; + BOOLEAN discing; + fixed_queue_t *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 +} tBTM_CB; + +typedef struct{ + //connection parameters update callback + tBTM_UPDATE_CONN_PARAM_CBACK *update_conn_param_cb; +}tBTM_CallbackFunc; + +extern tBTM_CallbackFunc conn_param_update_cb; +/* security action for L2CAP COC channels */ +#define BTM_SEC_OK 1 +#define BTM_SEC_ENCRYPT 2 /* encrypt the link with current key */ +#define BTM_SEC_ENCRYPT_NO_MITM 3 /* unauthenticated encryption or better */ +#define BTM_SEC_ENCRYPT_MITM 4 /* authenticated encryption */ +#define BTM_SEC_ENC_PENDING 5 /* wait for link encryption pending */ + +typedef UINT8 tBTM_SEC_ACTION; + +#ifdef __cplusplus +extern "C" +{ +#endif + +#if BTM_DYNAMIC_MEMORY == FALSE +extern tBTM_CB btm_cb; +#else +extern tBTM_CB *btm_cb_ptr; +#define btm_cb (*btm_cb_ptr) +#endif + +typedef struct tSecDevContext { +#define SEC_DEV_BTDM_BDA 0x01 +#define SEC_DEV_BDA 0x02 +#define SEC_DEV_HDL 0x03 +#define SEC_DEV_ID_ADDR 0x04 + UINT8 type; + BOOLEAN free_check; + union { + BD_ADDR_PTR p_bd_addr; + UINT16 handle; + }context; +}tSecDevContext; + +/* Internal functions provided by btm_main.c +******************************************** +*/ +void btm_init (void); +void btm_free (void); + +/* Internal functions provided by btm_inq.c +******************************************* +*/ +tBTM_STATUS btm_initiate_rem_name (BD_ADDR remote_bda, + tBTM_INQ_INFO *p_cur, + UINT8 origin, UINT32 timeout, + tBTM_CMPL_CB *p_cb); + +void btm_process_remote_name (BD_ADDR bda, BD_NAME name, UINT16 evt_len, + UINT8 hci_status); +void btm_inq_rmt_name_failed(void); + +/* Inquiry related functions */ +void btm_clr_inq_db (BD_ADDR p_bda); +void btm_inq_db_init (void); +void btm_process_inq_results (UINT8 *p, UINT8 inq_res_mode); +void btm_process_inq_complete (UINT8 status, UINT8 mode); +void btm_process_cancel_complete(UINT8 status, UINT8 mode); +void btm_event_filter_complete (UINT8 *p); +void btm_inq_stop_on_ssp(void); +void btm_inq_clear_ssp(void); +tINQ_DB_ENT *btm_inq_db_find (BD_ADDR p_bda); +BOOLEAN btm_inq_find_bdaddr (BD_ADDR p_bda); + +BOOLEAN btm_lookup_eir(BD_ADDR_PTR p_rem_addr); + +/* Internal functions provided by btm_acl.c +******************************************** +*/ +void btm_acl_free(void); +void btm_acl_init (void); +void btm_acl_created (BD_ADDR bda, DEV_CLASS dc, UINT8 bdn[BTM_MAX_REM_BD_NAME_LEN], + UINT16 hci_handle, UINT8 link_role, tBT_TRANSPORT transport); +void btm_acl_removed (BD_ADDR bda, tBT_TRANSPORT transport); +void btm_acl_device_down (void); +void btm_acl_update_busy_level (tBTM_BLI_EVENT event); +void btm_acl_link_stat_report(tBTM_ACL_LINK_STAT_EVENT_DATA *p_data); + +void btm_cont_rswitch (tACL_CONN *p, + tBTM_SEC_DEV_REC *p_dev_rec, + UINT8 hci_status); + +tACL_CONN *btm_handle_to_acl (UINT16 hci_handle); +void btm_read_link_policy_complete (UINT8 *p); +void btm_read_rssi_complete (UINT8 *p); +void btm_read_tx_power_complete (UINT8 *p, BOOLEAN is_ble); +void btm_acl_pkt_types_changed(UINT8 status, UINT16 handle, UINT16 pkt_types); +void btm_read_link_quality_complete (UINT8 *p); +tBTM_STATUS btm_set_packet_types (tACL_CONN *p, UINT16 pkt_types); +void btm_process_clk_off_comp_evt (UINT16 hci_handle, UINT16 clock_offset); +void btm_acl_role_changed (UINT8 hci_status, BD_ADDR bd_addr, UINT8 new_role); +void btm_acl_encrypt_change (UINT16 handle, UINT8 status, UINT8 encr_enable); +UINT16 btm_get_acl_disc_reason_code (void); +tBTM_STATUS btm_remove_acl (BD_ADDR bd_addr, tBT_TRANSPORT transport); +void btm_read_remote_features_complete (UINT8 *p); +void btm_read_remote_ext_features_complete (UINT8 *p); +void btm_read_remote_ext_features_failed (UINT8 status, UINT16 handle); +void btm_read_remote_version_complete (UINT8 *p); +void btm_establish_continue (tACL_CONN *p_acl_cb); + +// btla-specific ++ +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 */ +UINT16 btm_get_max_packet_size (BD_ADDR addr); +tACL_CONN *btm_bda_to_acl (BD_ADDR bda, tBT_TRANSPORT transport); +BOOLEAN btm_acl_notif_conn_collision (BD_ADDR bda); + +void btm_pm_reset(void); +tBTM_PM_MCB *btm_pm_sm_alloc(void); +void btm_pm_proc_cmd_status(UINT8 status); +void btm_pm_proc_mode_change (UINT8 hci_status, UINT16 hci_handle, UINT8 mode, + UINT16 interval); +void btm_pm_proc_ssr_evt (UINT8 *p, UINT16 evt_len); +#if BTM_SCO_INCLUDED == TRUE +void btm_sco_chk_pend_unpark (UINT8 hci_status, UINT16 hci_handle); +#if (BTM_SCO_HCI_INCLUDED == TRUE ) +void btm_sco_process_num_bufs (UINT16 num_lm_sco_bufs); +void btm_sco_process_num_completed_pkts (UINT8 *p); +#endif /* (BTM_SCO_HCI_INCLUDED == TRUE ) */ +#else +#define btm_sco_chk_pend_unpark(hci_status, hci_handle) +#endif /* BTM_SCO_INCLUDED */ +void btm_qos_setup_complete (UINT8 status, UINT16 handle, FLOW_SPEC *p_flow); +void btm_qos_setup_timeout (void *p_tle); + + +#if (BLE_50_FEATURE_SUPPORT == TRUE) +void btm_create_sync_callback(UINT8 status); +void btm_set_phy_callback(UINT8 status); +void btm_read_phy_callback(uint8_t hci_status, uint16_t conn_handle, uint8_t tx_phy, uint8_t rx_phy); +#endif +#if (BLE_FEAT_PERIODIC_ADV_SYNC_TRANSFER == TRUE) +void btm_ble_periodic_adv_sync_trans_complete(UINT16 op_code, UINT8 hci_status, UINT16 conn_handle); +#endif +/* Internal functions provided by btm_sco.c +******************************************** +*/ +void btm_sco_init (void); +void btm_sco_connected (UINT8 hci_status, BD_ADDR bda, UINT16 hci_handle, + tBTM_ESCO_DATA *p_esco_data); +void btm_esco_proc_conn_chg (UINT8 status, UINT16 handle, UINT8 tx_interval, + UINT8 retrans_window, UINT16 rx_pkt_len, + UINT16 tx_pkt_len); +void btm_sco_conn_req (BD_ADDR bda, DEV_CLASS dev_class, UINT8 link_type); +void btm_sco_removed (UINT16 hci_handle, UINT8 reason); +void btm_sco_acl_removed (BD_ADDR bda); +void btm_route_sco_data (BT_HDR *p_msg); +BOOLEAN btm_is_sco_active (UINT16 handle); +void btm_remove_sco_links (BD_ADDR bda); +BOOLEAN btm_is_sco_active_by_bdaddr (BD_ADDR remote_bda); + +tBTM_SCO_TYPE btm_read_def_esco_mode (tBTM_ESCO_PARAMS *p_parms); +UINT16 btm_find_scb_by_handle (UINT16 handle); +void btm_sco_flush_sco_data(UINT16 sco_inx); + +/* Internal functions provided by btm_devctl.c +********************************************** +*/ +void btm_dev_init (void); +void btm_dev_timeout (TIMER_LIST_ENT *p_tle); +void btm_read_local_name_complete (UINT8 *p, UINT16 evt_len); + +#if (BLE_INCLUDED == TRUE) +void btm_ble_add_2_white_list_complete(UINT8 status); +void btm_ble_remove_from_white_list_complete(UINT8 *p, UINT16 evt_len); +void btm_ble_clear_white_list_complete(UINT8 *p, UINT16 evt_len); +BOOLEAN btm_ble_addr_resolvable(BD_ADDR rpa, tBTM_SEC_DEV_REC *p_dev_rec); +tBTM_STATUS btm_ble_read_resolving_list_entry(tBTM_SEC_DEV_REC *p_dev_rec); +BOOLEAN btm_ble_resolving_list_load_dev(tBTM_SEC_DEV_REC *p_dev_rec); +void btm_ble_resolving_list_remove_dev(tBTM_SEC_DEV_REC *p_dev_rec); +#endif /* BLE_INCLUDED */ + +/* Vendor Specific Command complete evt handler */ +void btm_vsc_complete (UINT8 *p, UINT16 cc_opcode, UINT16 evt_len, + tBTM_CMPL_CB *p_vsc_cplt_cback); +void btm_inq_db_reset (void); +void btm_vendor_specific_evt (UINT8 *p, UINT8 evt_len); +void btm_delete_stored_link_key_complete (UINT8 *p); +void btm_report_device_status (tBTM_DEV_STATUS status); +void btm_set_afh_channels_complete (UINT8 *p); +void btm_ble_set_channels_complete (UINT8 *p); +void btm_set_page_timeout_complete (const UINT8 *p); +void btm_page_to_setup_timeout (void *p_tle); + +/* Internal functions provided by btm_dev.c +********************************************** +*/ +BOOLEAN btm_dev_support_switch (BD_ADDR bd_addr); + +tBTM_SEC_DEV_REC *btm_sec_alloc_dev (BD_ADDR bd_addr); +void btm_sec_free_dev (tBTM_SEC_DEV_REC *p_dev_rec, tBT_TRANSPORT transport); +tBTM_SEC_DEV_REC *btm_find_dev (BD_ADDR bd_addr); +tBTM_SEC_DEV_REC *btm_find_or_alloc_dev (BD_ADDR bd_addr); +tBTM_SEC_DEV_REC *btm_find_dev_by_handle (UINT16 handle); +tBTM_BOND_TYPE btm_get_bond_type_dev(BD_ADDR bd_addr); +BOOLEAN btm_set_bond_type_dev(BD_ADDR bd_addr, + tBTM_BOND_TYPE bond_type); +void btm_sec_dev_init(void); +void btm_sec_dev_free(void); + +/* Internal functions provided by btm_sec.c +********************************************** +*/ +BOOLEAN btm_dev_support_switch (BD_ADDR bd_addr); +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_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); +void btm_sec_conn_req (UINT8 *bda, UINT8 *dc); +void btm_create_conn_cancel_complete (UINT8 *p); +void btm_read_linq_tx_power_complete (UINT8 *p); + +void btm_sec_init (UINT8 sec_mode); +void btm_sec_dev_reset (void); +void btm_sec_abort_access_req (BD_ADDR bd_addr); +void btm_sec_auth_complete (UINT16 handle, UINT8 status); +void btm_sec_encrypt_change (UINT16 handle, UINT8 status, UINT8 encr_enable); +void btm_sec_connected (UINT8 *bda, UINT16 handle, UINT8 status, UINT8 enc_mode); +tBTM_STATUS btm_sec_disconnect (UINT16 handle, UINT8 reason); +void btm_sec_disconnected (UINT16 handle, UINT8 reason); +void btm_sec_rmt_name_request_complete (UINT8 *bd_addr, UINT8 *bd_name, UINT8 status); +void btm_sec_rmt_host_support_feat_evt (UINT8 *p); +void btm_io_capabilities_req (UINT8 *p); +void btm_io_capabilities_rsp (UINT8 *p); +#if (CLASSIC_BT_INCLUDED == TRUE) +void btm_proc_sp_req_evt (tBTM_SP_EVT event, UINT8 *p); +void btm_keypress_notif_evt (UINT8 *p); +void btm_simple_pair_complete (UINT8 *p); +#endif /* (CLASSIC_BT_INCLUDED == TRUE) */ +void btm_sec_link_key_notification (UINT8 *p_bda, UINT8 *p_link_key, UINT8 key_type); +void btm_sec_link_key_request (UINT8 *p_bda); +void btm_sec_pin_code_request (UINT8 *p_bda); +void btm_sec_update_clock_offset (UINT16 handle, UINT16 clock_offset); +void btm_sec_dev_rec_cback_event (tBTM_SEC_DEV_REC *p_dev_rec, UINT8 res, BOOLEAN is_le_transport); +void btm_sec_set_peer_sec_caps (tACL_CONN *p_acl_cb, tBTM_SEC_DEV_REC *p_dev_rec); + +#if BLE_INCLUDED == TRUE +void btm_sec_clear_ble_keys (tBTM_SEC_DEV_REC *p_dev_rec); +BOOLEAN btm_sec_find_bonded_dev (UINT8 start_idx, UINT16 *p_found_handle, tBTM_SEC_DEV_REC **p_rec); +BOOLEAN btm_sec_is_a_bonded_dev (BD_ADDR bda); +void btm_consolidate_dev(tBTM_SEC_DEV_REC *p_target_rec); +BOOLEAN btm_sec_is_le_capable_dev (BD_ADDR bda); +BOOLEAN btm_ble_init_pseudo_addr (tBTM_SEC_DEV_REC *p_dev_rec, BD_ADDR new_pseudo_addr); +extern BOOLEAN btm_ble_start_sec_check(BD_ADDR bd_addr, UINT16 psm, BOOLEAN is_originator, + tBTM_SEC_CALLBACK *p_callback, void *p_ref_data); +#endif /* BLE_INCLUDED */ + +extern tBTM_SEC_SERV_REC *btm_sec_find_first_serv (CONNECTION_TYPE conn_type, UINT16 psm); + +tINQ_DB_ENT *btm_inq_db_new (BD_ADDR p_bda); + +#if BTM_OOB_INCLUDED == TRUE +void btm_rem_oob_req (UINT8 *p); +void btm_read_local_oob_complete (UINT8 *p); +#else +#define btm_rem_oob_req(p) +#define btm_read_local_oob_complete(p) +#endif + +void btm_acl_resubmit_page (void); +void btm_acl_reset_paging (void); +void btm_acl_paging (BT_HDR *p, BD_ADDR dest); +UINT8 btm_sec_clr_service_by_psm (UINT16 psm); +void btm_sec_clr_temp_auth_service (BD_ADDR bda); + +void btm_ble_lock_init(void); + +void btm_ble_sem_init(void); + +void btm_ble_sem_free(void); + +void btm_ble_lock_free(void); + +void btm_sec_handle_remote_legacy_auth_cmp(UINT16 handle); +void btm_sec_update_legacy_auth_state(tACL_CONN *p_acl_cb, UINT8 legacy_auth_state); +BOOLEAN btm_sec_legacy_authentication_mutual (tBTM_SEC_DEV_REC *p_dev_rec); +BOOLEAN btm_find_sec_dev_in_list (void *p_node_data, void *context); + +BOOLEAN btm_sec_dev_authorization(BD_ADDR bd_addr, BOOLEAN authorized); + +/* +#ifdef __cplusplus +} +#endif +*/ + +#endif |
