/****************************************************************************** * * 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 #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); if (btm_cb.devcb.p_add_dev_to_resolving_list_cmpl_cb) { tBTM_ADD_DEV_TO_RESOLVING_LIST_CMPL_CBACK *p_cb = btm_cb.devcb.p_add_dev_to_resolving_list_cmpl_cb; if (p_cb) { (*p_cb)(status); } } else { BTM_TRACE_DEBUG("no resolving list callback"); } 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); } /******************************************************************************* ** ** Function btm_ble_set_rpa_timeout_complete ** ** Description This function is called when the LE Set Resolvable Private ** Address Timeout command completes. ** ** Parameters p: Pointer to the command complete event data. ** evt_len: Length of the event data. ** ** Returns void ** *******************************************************************************/ void btm_ble_set_rpa_timeout_complete(UINT8 *p, UINT16 evt_len) { UINT8 status; // Extract the status of the command completion from the event data STREAM_TO_UINT8(status, p); BTM_TRACE_DEBUG("%s status = %d", __func__, status); tBTM_SET_RPA_TIMEOUT_CMPL_CBACK *p_cb = btm_cb.devcb.p_ble_set_rpa_timeout_cmpl_cb; if (p_cb) { (*p_cb)(status); } } /******************************************************************************* 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 address ** ** 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 resolving 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 address 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