18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci BlueZ - Bluetooth protocol stack for Linux 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci Copyright (C) 2010 Nokia Corporation 58c2ecf20Sopenharmony_ci Copyright (C) 2011-2012 Intel Corporation 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci This program is free software; you can redistribute it and/or modify 88c2ecf20Sopenharmony_ci it under the terms of the GNU General Public License version 2 as 98c2ecf20Sopenharmony_ci published by the Free Software Foundation; 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 128c2ecf20Sopenharmony_ci OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 138c2ecf20Sopenharmony_ci FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. 148c2ecf20Sopenharmony_ci IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) AND AUTHOR(S) BE LIABLE FOR ANY 158c2ecf20Sopenharmony_ci CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES 168c2ecf20Sopenharmony_ci WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 178c2ecf20Sopenharmony_ci ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 188c2ecf20Sopenharmony_ci OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci ALL LIABILITY, INCLUDING LIABILITY FOR INFRINGEMENT OF ANY PATENTS, 218c2ecf20Sopenharmony_ci COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS, RELATING TO USE OF THIS 228c2ecf20Sopenharmony_ci SOFTWARE IS DISCLAIMED. 238c2ecf20Sopenharmony_ci*/ 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci/* Bluetooth HCI Management interface */ 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#include <linux/module.h> 288c2ecf20Sopenharmony_ci#include <asm/unaligned.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#include <net/bluetooth/bluetooth.h> 318c2ecf20Sopenharmony_ci#include <net/bluetooth/hci_core.h> 328c2ecf20Sopenharmony_ci#include <net/bluetooth/hci_sock.h> 338c2ecf20Sopenharmony_ci#include <net/bluetooth/l2cap.h> 348c2ecf20Sopenharmony_ci#include <net/bluetooth/mgmt.h> 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_ci#include "hci_request.h" 378c2ecf20Sopenharmony_ci#include "smp.h" 388c2ecf20Sopenharmony_ci#include "mgmt_util.h" 398c2ecf20Sopenharmony_ci#include "mgmt_config.h" 408c2ecf20Sopenharmony_ci#include "msft.h" 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define MGMT_VERSION 1 438c2ecf20Sopenharmony_ci#define MGMT_REVISION 18 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic const u16 mgmt_commands[] = { 468c2ecf20Sopenharmony_ci MGMT_OP_READ_INDEX_LIST, 478c2ecf20Sopenharmony_ci MGMT_OP_READ_INFO, 488c2ecf20Sopenharmony_ci MGMT_OP_SET_POWERED, 498c2ecf20Sopenharmony_ci MGMT_OP_SET_DISCOVERABLE, 508c2ecf20Sopenharmony_ci MGMT_OP_SET_CONNECTABLE, 518c2ecf20Sopenharmony_ci MGMT_OP_SET_FAST_CONNECTABLE, 528c2ecf20Sopenharmony_ci MGMT_OP_SET_BONDABLE, 538c2ecf20Sopenharmony_ci MGMT_OP_SET_LINK_SECURITY, 548c2ecf20Sopenharmony_ci MGMT_OP_SET_SSP, 558c2ecf20Sopenharmony_ci MGMT_OP_SET_HS, 568c2ecf20Sopenharmony_ci MGMT_OP_SET_LE, 578c2ecf20Sopenharmony_ci MGMT_OP_SET_DEV_CLASS, 588c2ecf20Sopenharmony_ci MGMT_OP_SET_LOCAL_NAME, 598c2ecf20Sopenharmony_ci MGMT_OP_ADD_UUID, 608c2ecf20Sopenharmony_ci MGMT_OP_REMOVE_UUID, 618c2ecf20Sopenharmony_ci MGMT_OP_LOAD_LINK_KEYS, 628c2ecf20Sopenharmony_ci MGMT_OP_LOAD_LONG_TERM_KEYS, 638c2ecf20Sopenharmony_ci MGMT_OP_DISCONNECT, 648c2ecf20Sopenharmony_ci MGMT_OP_GET_CONNECTIONS, 658c2ecf20Sopenharmony_ci MGMT_OP_PIN_CODE_REPLY, 668c2ecf20Sopenharmony_ci MGMT_OP_PIN_CODE_NEG_REPLY, 678c2ecf20Sopenharmony_ci MGMT_OP_SET_IO_CAPABILITY, 688c2ecf20Sopenharmony_ci MGMT_OP_PAIR_DEVICE, 698c2ecf20Sopenharmony_ci MGMT_OP_CANCEL_PAIR_DEVICE, 708c2ecf20Sopenharmony_ci MGMT_OP_UNPAIR_DEVICE, 718c2ecf20Sopenharmony_ci MGMT_OP_USER_CONFIRM_REPLY, 728c2ecf20Sopenharmony_ci MGMT_OP_USER_CONFIRM_NEG_REPLY, 738c2ecf20Sopenharmony_ci MGMT_OP_USER_PASSKEY_REPLY, 748c2ecf20Sopenharmony_ci MGMT_OP_USER_PASSKEY_NEG_REPLY, 758c2ecf20Sopenharmony_ci MGMT_OP_READ_LOCAL_OOB_DATA, 768c2ecf20Sopenharmony_ci MGMT_OP_ADD_REMOTE_OOB_DATA, 778c2ecf20Sopenharmony_ci MGMT_OP_REMOVE_REMOTE_OOB_DATA, 788c2ecf20Sopenharmony_ci MGMT_OP_START_DISCOVERY, 798c2ecf20Sopenharmony_ci MGMT_OP_STOP_DISCOVERY, 808c2ecf20Sopenharmony_ci MGMT_OP_CONFIRM_NAME, 818c2ecf20Sopenharmony_ci MGMT_OP_BLOCK_DEVICE, 828c2ecf20Sopenharmony_ci MGMT_OP_UNBLOCK_DEVICE, 838c2ecf20Sopenharmony_ci MGMT_OP_SET_DEVICE_ID, 848c2ecf20Sopenharmony_ci MGMT_OP_SET_ADVERTISING, 858c2ecf20Sopenharmony_ci MGMT_OP_SET_BREDR, 868c2ecf20Sopenharmony_ci MGMT_OP_SET_STATIC_ADDRESS, 878c2ecf20Sopenharmony_ci MGMT_OP_SET_SCAN_PARAMS, 888c2ecf20Sopenharmony_ci MGMT_OP_SET_SECURE_CONN, 898c2ecf20Sopenharmony_ci MGMT_OP_SET_DEBUG_KEYS, 908c2ecf20Sopenharmony_ci MGMT_OP_SET_PRIVACY, 918c2ecf20Sopenharmony_ci MGMT_OP_LOAD_IRKS, 928c2ecf20Sopenharmony_ci MGMT_OP_GET_CONN_INFO, 938c2ecf20Sopenharmony_ci MGMT_OP_GET_CLOCK_INFO, 948c2ecf20Sopenharmony_ci MGMT_OP_ADD_DEVICE, 958c2ecf20Sopenharmony_ci MGMT_OP_REMOVE_DEVICE, 968c2ecf20Sopenharmony_ci MGMT_OP_LOAD_CONN_PARAM, 978c2ecf20Sopenharmony_ci MGMT_OP_READ_UNCONF_INDEX_LIST, 988c2ecf20Sopenharmony_ci MGMT_OP_READ_CONFIG_INFO, 998c2ecf20Sopenharmony_ci MGMT_OP_SET_EXTERNAL_CONFIG, 1008c2ecf20Sopenharmony_ci MGMT_OP_SET_PUBLIC_ADDRESS, 1018c2ecf20Sopenharmony_ci MGMT_OP_START_SERVICE_DISCOVERY, 1028c2ecf20Sopenharmony_ci MGMT_OP_READ_LOCAL_OOB_EXT_DATA, 1038c2ecf20Sopenharmony_ci MGMT_OP_READ_EXT_INDEX_LIST, 1048c2ecf20Sopenharmony_ci MGMT_OP_READ_ADV_FEATURES, 1058c2ecf20Sopenharmony_ci MGMT_OP_ADD_ADVERTISING, 1068c2ecf20Sopenharmony_ci MGMT_OP_REMOVE_ADVERTISING, 1078c2ecf20Sopenharmony_ci MGMT_OP_GET_ADV_SIZE_INFO, 1088c2ecf20Sopenharmony_ci MGMT_OP_START_LIMITED_DISCOVERY, 1098c2ecf20Sopenharmony_ci MGMT_OP_READ_EXT_INFO, 1108c2ecf20Sopenharmony_ci MGMT_OP_SET_APPEARANCE, 1118c2ecf20Sopenharmony_ci MGMT_OP_SET_BLOCKED_KEYS, 1128c2ecf20Sopenharmony_ci MGMT_OP_SET_WIDEBAND_SPEECH, 1138c2ecf20Sopenharmony_ci MGMT_OP_READ_SECURITY_INFO, 1148c2ecf20Sopenharmony_ci MGMT_OP_READ_EXP_FEATURES_INFO, 1158c2ecf20Sopenharmony_ci MGMT_OP_SET_EXP_FEATURE, 1168c2ecf20Sopenharmony_ci MGMT_OP_READ_DEF_SYSTEM_CONFIG, 1178c2ecf20Sopenharmony_ci MGMT_OP_SET_DEF_SYSTEM_CONFIG, 1188c2ecf20Sopenharmony_ci MGMT_OP_READ_DEF_RUNTIME_CONFIG, 1198c2ecf20Sopenharmony_ci MGMT_OP_SET_DEF_RUNTIME_CONFIG, 1208c2ecf20Sopenharmony_ci MGMT_OP_GET_DEVICE_FLAGS, 1218c2ecf20Sopenharmony_ci MGMT_OP_SET_DEVICE_FLAGS, 1228c2ecf20Sopenharmony_ci MGMT_OP_READ_ADV_MONITOR_FEATURES, 1238c2ecf20Sopenharmony_ci MGMT_OP_ADD_ADV_PATTERNS_MONITOR, 1248c2ecf20Sopenharmony_ci MGMT_OP_REMOVE_ADV_MONITOR, 1258c2ecf20Sopenharmony_ci}; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic const u16 mgmt_events[] = { 1288c2ecf20Sopenharmony_ci MGMT_EV_CONTROLLER_ERROR, 1298c2ecf20Sopenharmony_ci MGMT_EV_INDEX_ADDED, 1308c2ecf20Sopenharmony_ci MGMT_EV_INDEX_REMOVED, 1318c2ecf20Sopenharmony_ci MGMT_EV_NEW_SETTINGS, 1328c2ecf20Sopenharmony_ci MGMT_EV_CLASS_OF_DEV_CHANGED, 1338c2ecf20Sopenharmony_ci MGMT_EV_LOCAL_NAME_CHANGED, 1348c2ecf20Sopenharmony_ci MGMT_EV_NEW_LINK_KEY, 1358c2ecf20Sopenharmony_ci MGMT_EV_NEW_LONG_TERM_KEY, 1368c2ecf20Sopenharmony_ci MGMT_EV_DEVICE_CONNECTED, 1378c2ecf20Sopenharmony_ci MGMT_EV_DEVICE_DISCONNECTED, 1388c2ecf20Sopenharmony_ci MGMT_EV_CONNECT_FAILED, 1398c2ecf20Sopenharmony_ci MGMT_EV_PIN_CODE_REQUEST, 1408c2ecf20Sopenharmony_ci MGMT_EV_USER_CONFIRM_REQUEST, 1418c2ecf20Sopenharmony_ci MGMT_EV_USER_PASSKEY_REQUEST, 1428c2ecf20Sopenharmony_ci MGMT_EV_AUTH_FAILED, 1438c2ecf20Sopenharmony_ci MGMT_EV_DEVICE_FOUND, 1448c2ecf20Sopenharmony_ci MGMT_EV_DISCOVERING, 1458c2ecf20Sopenharmony_ci MGMT_EV_DEVICE_BLOCKED, 1468c2ecf20Sopenharmony_ci MGMT_EV_DEVICE_UNBLOCKED, 1478c2ecf20Sopenharmony_ci MGMT_EV_DEVICE_UNPAIRED, 1488c2ecf20Sopenharmony_ci MGMT_EV_PASSKEY_NOTIFY, 1498c2ecf20Sopenharmony_ci MGMT_EV_NEW_IRK, 1508c2ecf20Sopenharmony_ci MGMT_EV_NEW_CSRK, 1518c2ecf20Sopenharmony_ci MGMT_EV_DEVICE_ADDED, 1528c2ecf20Sopenharmony_ci MGMT_EV_DEVICE_REMOVED, 1538c2ecf20Sopenharmony_ci MGMT_EV_NEW_CONN_PARAM, 1548c2ecf20Sopenharmony_ci MGMT_EV_UNCONF_INDEX_ADDED, 1558c2ecf20Sopenharmony_ci MGMT_EV_UNCONF_INDEX_REMOVED, 1568c2ecf20Sopenharmony_ci MGMT_EV_NEW_CONFIG_OPTIONS, 1578c2ecf20Sopenharmony_ci MGMT_EV_EXT_INDEX_ADDED, 1588c2ecf20Sopenharmony_ci MGMT_EV_EXT_INDEX_REMOVED, 1598c2ecf20Sopenharmony_ci MGMT_EV_LOCAL_OOB_DATA_UPDATED, 1608c2ecf20Sopenharmony_ci MGMT_EV_ADVERTISING_ADDED, 1618c2ecf20Sopenharmony_ci MGMT_EV_ADVERTISING_REMOVED, 1628c2ecf20Sopenharmony_ci MGMT_EV_EXT_INFO_CHANGED, 1638c2ecf20Sopenharmony_ci MGMT_EV_PHY_CONFIGURATION_CHANGED, 1648c2ecf20Sopenharmony_ci MGMT_EV_EXP_FEATURE_CHANGED, 1658c2ecf20Sopenharmony_ci MGMT_EV_DEVICE_FLAGS_CHANGED, 1668c2ecf20Sopenharmony_ci MGMT_EV_CONTROLLER_SUSPEND, 1678c2ecf20Sopenharmony_ci MGMT_EV_CONTROLLER_RESUME, 1688c2ecf20Sopenharmony_ci}; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_cistatic const u16 mgmt_untrusted_commands[] = { 1718c2ecf20Sopenharmony_ci MGMT_OP_READ_INDEX_LIST, 1728c2ecf20Sopenharmony_ci MGMT_OP_READ_INFO, 1738c2ecf20Sopenharmony_ci MGMT_OP_READ_UNCONF_INDEX_LIST, 1748c2ecf20Sopenharmony_ci MGMT_OP_READ_CONFIG_INFO, 1758c2ecf20Sopenharmony_ci MGMT_OP_READ_EXT_INDEX_LIST, 1768c2ecf20Sopenharmony_ci MGMT_OP_READ_EXT_INFO, 1778c2ecf20Sopenharmony_ci MGMT_OP_READ_SECURITY_INFO, 1788c2ecf20Sopenharmony_ci MGMT_OP_READ_EXP_FEATURES_INFO, 1798c2ecf20Sopenharmony_ci MGMT_OP_READ_DEF_SYSTEM_CONFIG, 1808c2ecf20Sopenharmony_ci MGMT_OP_READ_DEF_RUNTIME_CONFIG, 1818c2ecf20Sopenharmony_ci}; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_cistatic const u16 mgmt_untrusted_events[] = { 1848c2ecf20Sopenharmony_ci MGMT_EV_INDEX_ADDED, 1858c2ecf20Sopenharmony_ci MGMT_EV_INDEX_REMOVED, 1868c2ecf20Sopenharmony_ci MGMT_EV_NEW_SETTINGS, 1878c2ecf20Sopenharmony_ci MGMT_EV_CLASS_OF_DEV_CHANGED, 1888c2ecf20Sopenharmony_ci MGMT_EV_LOCAL_NAME_CHANGED, 1898c2ecf20Sopenharmony_ci MGMT_EV_UNCONF_INDEX_ADDED, 1908c2ecf20Sopenharmony_ci MGMT_EV_UNCONF_INDEX_REMOVED, 1918c2ecf20Sopenharmony_ci MGMT_EV_NEW_CONFIG_OPTIONS, 1928c2ecf20Sopenharmony_ci MGMT_EV_EXT_INDEX_ADDED, 1938c2ecf20Sopenharmony_ci MGMT_EV_EXT_INDEX_REMOVED, 1948c2ecf20Sopenharmony_ci MGMT_EV_EXT_INFO_CHANGED, 1958c2ecf20Sopenharmony_ci MGMT_EV_EXP_FEATURE_CHANGED, 1968c2ecf20Sopenharmony_ci MGMT_EV_ADV_MONITOR_ADDED, 1978c2ecf20Sopenharmony_ci MGMT_EV_ADV_MONITOR_REMOVED, 1988c2ecf20Sopenharmony_ci}; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci#define CACHE_TIMEOUT msecs_to_jiffies(2 * 1000) 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci#define ZERO_KEY "\x00\x00\x00\x00\x00\x00\x00\x00" \ 2038c2ecf20Sopenharmony_ci "\x00\x00\x00\x00\x00\x00\x00\x00" 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci/* HCI to MGMT error code conversion table */ 2068c2ecf20Sopenharmony_cistatic const u8 mgmt_status_table[] = { 2078c2ecf20Sopenharmony_ci MGMT_STATUS_SUCCESS, 2088c2ecf20Sopenharmony_ci MGMT_STATUS_UNKNOWN_COMMAND, /* Unknown Command */ 2098c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_CONNECTED, /* No Connection */ 2108c2ecf20Sopenharmony_ci MGMT_STATUS_FAILED, /* Hardware Failure */ 2118c2ecf20Sopenharmony_ci MGMT_STATUS_CONNECT_FAILED, /* Page Timeout */ 2128c2ecf20Sopenharmony_ci MGMT_STATUS_AUTH_FAILED, /* Authentication Failed */ 2138c2ecf20Sopenharmony_ci MGMT_STATUS_AUTH_FAILED, /* PIN or Key Missing */ 2148c2ecf20Sopenharmony_ci MGMT_STATUS_NO_RESOURCES, /* Memory Full */ 2158c2ecf20Sopenharmony_ci MGMT_STATUS_TIMEOUT, /* Connection Timeout */ 2168c2ecf20Sopenharmony_ci MGMT_STATUS_NO_RESOURCES, /* Max Number of Connections */ 2178c2ecf20Sopenharmony_ci MGMT_STATUS_NO_RESOURCES, /* Max Number of SCO Connections */ 2188c2ecf20Sopenharmony_ci MGMT_STATUS_ALREADY_CONNECTED, /* ACL Connection Exists */ 2198c2ecf20Sopenharmony_ci MGMT_STATUS_BUSY, /* Command Disallowed */ 2208c2ecf20Sopenharmony_ci MGMT_STATUS_NO_RESOURCES, /* Rejected Limited Resources */ 2218c2ecf20Sopenharmony_ci MGMT_STATUS_REJECTED, /* Rejected Security */ 2228c2ecf20Sopenharmony_ci MGMT_STATUS_REJECTED, /* Rejected Personal */ 2238c2ecf20Sopenharmony_ci MGMT_STATUS_TIMEOUT, /* Host Timeout */ 2248c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_SUPPORTED, /* Unsupported Feature */ 2258c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS, /* Invalid Parameters */ 2268c2ecf20Sopenharmony_ci MGMT_STATUS_DISCONNECTED, /* OE User Ended Connection */ 2278c2ecf20Sopenharmony_ci MGMT_STATUS_NO_RESOURCES, /* OE Low Resources */ 2288c2ecf20Sopenharmony_ci MGMT_STATUS_DISCONNECTED, /* OE Power Off */ 2298c2ecf20Sopenharmony_ci MGMT_STATUS_DISCONNECTED, /* Connection Terminated */ 2308c2ecf20Sopenharmony_ci MGMT_STATUS_BUSY, /* Repeated Attempts */ 2318c2ecf20Sopenharmony_ci MGMT_STATUS_REJECTED, /* Pairing Not Allowed */ 2328c2ecf20Sopenharmony_ci MGMT_STATUS_FAILED, /* Unknown LMP PDU */ 2338c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_SUPPORTED, /* Unsupported Remote Feature */ 2348c2ecf20Sopenharmony_ci MGMT_STATUS_REJECTED, /* SCO Offset Rejected */ 2358c2ecf20Sopenharmony_ci MGMT_STATUS_REJECTED, /* SCO Interval Rejected */ 2368c2ecf20Sopenharmony_ci MGMT_STATUS_REJECTED, /* Air Mode Rejected */ 2378c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS, /* Invalid LMP Parameters */ 2388c2ecf20Sopenharmony_ci MGMT_STATUS_FAILED, /* Unspecified Error */ 2398c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_SUPPORTED, /* Unsupported LMP Parameter Value */ 2408c2ecf20Sopenharmony_ci MGMT_STATUS_FAILED, /* Role Change Not Allowed */ 2418c2ecf20Sopenharmony_ci MGMT_STATUS_TIMEOUT, /* LMP Response Timeout */ 2428c2ecf20Sopenharmony_ci MGMT_STATUS_FAILED, /* LMP Error Transaction Collision */ 2438c2ecf20Sopenharmony_ci MGMT_STATUS_FAILED, /* LMP PDU Not Allowed */ 2448c2ecf20Sopenharmony_ci MGMT_STATUS_REJECTED, /* Encryption Mode Not Accepted */ 2458c2ecf20Sopenharmony_ci MGMT_STATUS_FAILED, /* Unit Link Key Used */ 2468c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_SUPPORTED, /* QoS Not Supported */ 2478c2ecf20Sopenharmony_ci MGMT_STATUS_TIMEOUT, /* Instant Passed */ 2488c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_SUPPORTED, /* Pairing Not Supported */ 2498c2ecf20Sopenharmony_ci MGMT_STATUS_FAILED, /* Transaction Collision */ 2508c2ecf20Sopenharmony_ci MGMT_STATUS_FAILED, /* Reserved for future use */ 2518c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS, /* Unacceptable Parameter */ 2528c2ecf20Sopenharmony_ci MGMT_STATUS_REJECTED, /* QoS Rejected */ 2538c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_SUPPORTED, /* Classification Not Supported */ 2548c2ecf20Sopenharmony_ci MGMT_STATUS_REJECTED, /* Insufficient Security */ 2558c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS, /* Parameter Out Of Range */ 2568c2ecf20Sopenharmony_ci MGMT_STATUS_FAILED, /* Reserved for future use */ 2578c2ecf20Sopenharmony_ci MGMT_STATUS_BUSY, /* Role Switch Pending */ 2588c2ecf20Sopenharmony_ci MGMT_STATUS_FAILED, /* Reserved for future use */ 2598c2ecf20Sopenharmony_ci MGMT_STATUS_FAILED, /* Slot Violation */ 2608c2ecf20Sopenharmony_ci MGMT_STATUS_FAILED, /* Role Switch Failed */ 2618c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS, /* EIR Too Large */ 2628c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_SUPPORTED, /* Simple Pairing Not Supported */ 2638c2ecf20Sopenharmony_ci MGMT_STATUS_BUSY, /* Host Busy Pairing */ 2648c2ecf20Sopenharmony_ci MGMT_STATUS_REJECTED, /* Rejected, No Suitable Channel */ 2658c2ecf20Sopenharmony_ci MGMT_STATUS_BUSY, /* Controller Busy */ 2668c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS, /* Unsuitable Connection Interval */ 2678c2ecf20Sopenharmony_ci MGMT_STATUS_TIMEOUT, /* Directed Advertising Timeout */ 2688c2ecf20Sopenharmony_ci MGMT_STATUS_AUTH_FAILED, /* Terminated Due to MIC Failure */ 2698c2ecf20Sopenharmony_ci MGMT_STATUS_CONNECT_FAILED, /* Connection Establishment Failed */ 2708c2ecf20Sopenharmony_ci MGMT_STATUS_CONNECT_FAILED, /* MAC Connection Failed */ 2718c2ecf20Sopenharmony_ci}; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_cistatic u8 mgmt_status(u8 hci_status) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci if (hci_status < ARRAY_SIZE(mgmt_status_table)) 2768c2ecf20Sopenharmony_ci return mgmt_status_table[hci_status]; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci return MGMT_STATUS_FAILED; 2798c2ecf20Sopenharmony_ci} 2808c2ecf20Sopenharmony_ci 2818c2ecf20Sopenharmony_cistatic int mgmt_index_event(u16 event, struct hci_dev *hdev, void *data, 2828c2ecf20Sopenharmony_ci u16 len, int flag) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci return mgmt_send_event(event, hdev, HCI_CHANNEL_CONTROL, data, len, 2858c2ecf20Sopenharmony_ci flag, NULL); 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_cistatic int mgmt_limited_event(u16 event, struct hci_dev *hdev, void *data, 2898c2ecf20Sopenharmony_ci u16 len, int flag, struct sock *skip_sk) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci return mgmt_send_event(event, hdev, HCI_CHANNEL_CONTROL, data, len, 2928c2ecf20Sopenharmony_ci flag, skip_sk); 2938c2ecf20Sopenharmony_ci} 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_cistatic int mgmt_event(u16 event, struct hci_dev *hdev, void *data, u16 len, 2968c2ecf20Sopenharmony_ci struct sock *skip_sk) 2978c2ecf20Sopenharmony_ci{ 2988c2ecf20Sopenharmony_ci return mgmt_send_event(event, hdev, HCI_CHANNEL_CONTROL, data, len, 2998c2ecf20Sopenharmony_ci HCI_SOCK_TRUSTED, skip_sk); 3008c2ecf20Sopenharmony_ci} 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_cistatic u8 le_addr_type(u8 mgmt_addr_type) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci if (mgmt_addr_type == BDADDR_LE_PUBLIC) 3058c2ecf20Sopenharmony_ci return ADDR_LE_DEV_PUBLIC; 3068c2ecf20Sopenharmony_ci else 3078c2ecf20Sopenharmony_ci return ADDR_LE_DEV_RANDOM; 3088c2ecf20Sopenharmony_ci} 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_civoid mgmt_fill_version_info(void *ver) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci struct mgmt_rp_read_version *rp = ver; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci rp->version = MGMT_VERSION; 3158c2ecf20Sopenharmony_ci rp->revision = cpu_to_le16(MGMT_REVISION); 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic int read_version(struct sock *sk, struct hci_dev *hdev, void *data, 3198c2ecf20Sopenharmony_ci u16 data_len) 3208c2ecf20Sopenharmony_ci{ 3218c2ecf20Sopenharmony_ci struct mgmt_rp_read_version rp; 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 3248c2ecf20Sopenharmony_ci 3258c2ecf20Sopenharmony_ci mgmt_fill_version_info(&rp); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci return mgmt_cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_VERSION, 0, 3288c2ecf20Sopenharmony_ci &rp, sizeof(rp)); 3298c2ecf20Sopenharmony_ci} 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_cistatic int read_commands(struct sock *sk, struct hci_dev *hdev, void *data, 3328c2ecf20Sopenharmony_ci u16 data_len) 3338c2ecf20Sopenharmony_ci{ 3348c2ecf20Sopenharmony_ci struct mgmt_rp_read_commands *rp; 3358c2ecf20Sopenharmony_ci u16 num_commands, num_events; 3368c2ecf20Sopenharmony_ci size_t rp_size; 3378c2ecf20Sopenharmony_ci int i, err; 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci if (hci_sock_test_flag(sk, HCI_SOCK_TRUSTED)) { 3428c2ecf20Sopenharmony_ci num_commands = ARRAY_SIZE(mgmt_commands); 3438c2ecf20Sopenharmony_ci num_events = ARRAY_SIZE(mgmt_events); 3448c2ecf20Sopenharmony_ci } else { 3458c2ecf20Sopenharmony_ci num_commands = ARRAY_SIZE(mgmt_untrusted_commands); 3468c2ecf20Sopenharmony_ci num_events = ARRAY_SIZE(mgmt_untrusted_events); 3478c2ecf20Sopenharmony_ci } 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci rp_size = sizeof(*rp) + ((num_commands + num_events) * sizeof(u16)); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci rp = kmalloc(rp_size, GFP_KERNEL); 3528c2ecf20Sopenharmony_ci if (!rp) 3538c2ecf20Sopenharmony_ci return -ENOMEM; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci rp->num_commands = cpu_to_le16(num_commands); 3568c2ecf20Sopenharmony_ci rp->num_events = cpu_to_le16(num_events); 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci if (hci_sock_test_flag(sk, HCI_SOCK_TRUSTED)) { 3598c2ecf20Sopenharmony_ci __le16 *opcode = rp->opcodes; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci for (i = 0; i < num_commands; i++, opcode++) 3628c2ecf20Sopenharmony_ci put_unaligned_le16(mgmt_commands[i], opcode); 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci for (i = 0; i < num_events; i++, opcode++) 3658c2ecf20Sopenharmony_ci put_unaligned_le16(mgmt_events[i], opcode); 3668c2ecf20Sopenharmony_ci } else { 3678c2ecf20Sopenharmony_ci __le16 *opcode = rp->opcodes; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci for (i = 0; i < num_commands; i++, opcode++) 3708c2ecf20Sopenharmony_ci put_unaligned_le16(mgmt_untrusted_commands[i], opcode); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci for (i = 0; i < num_events; i++, opcode++) 3738c2ecf20Sopenharmony_ci put_unaligned_le16(mgmt_untrusted_events[i], opcode); 3748c2ecf20Sopenharmony_ci } 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_COMMANDS, 0, 3778c2ecf20Sopenharmony_ci rp, rp_size); 3788c2ecf20Sopenharmony_ci kfree(rp); 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci return err; 3818c2ecf20Sopenharmony_ci} 3828c2ecf20Sopenharmony_ci 3838c2ecf20Sopenharmony_cistatic int read_index_list(struct sock *sk, struct hci_dev *hdev, void *data, 3848c2ecf20Sopenharmony_ci u16 data_len) 3858c2ecf20Sopenharmony_ci{ 3868c2ecf20Sopenharmony_ci struct mgmt_rp_read_index_list *rp; 3878c2ecf20Sopenharmony_ci struct hci_dev *d; 3888c2ecf20Sopenharmony_ci size_t rp_len; 3898c2ecf20Sopenharmony_ci u16 count; 3908c2ecf20Sopenharmony_ci int err; 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci read_lock(&hci_dev_list_lock); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci count = 0; 3978c2ecf20Sopenharmony_ci list_for_each_entry(d, &hci_dev_list, list) { 3988c2ecf20Sopenharmony_ci if (d->dev_type == HCI_PRIMARY && 3998c2ecf20Sopenharmony_ci !hci_dev_test_flag(d, HCI_UNCONFIGURED)) 4008c2ecf20Sopenharmony_ci count++; 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci rp_len = sizeof(*rp) + (2 * count); 4048c2ecf20Sopenharmony_ci rp = kmalloc(rp_len, GFP_ATOMIC); 4058c2ecf20Sopenharmony_ci if (!rp) { 4068c2ecf20Sopenharmony_ci read_unlock(&hci_dev_list_lock); 4078c2ecf20Sopenharmony_ci return -ENOMEM; 4088c2ecf20Sopenharmony_ci } 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci count = 0; 4118c2ecf20Sopenharmony_ci list_for_each_entry(d, &hci_dev_list, list) { 4128c2ecf20Sopenharmony_ci if (hci_dev_test_flag(d, HCI_SETUP) || 4138c2ecf20Sopenharmony_ci hci_dev_test_flag(d, HCI_CONFIG) || 4148c2ecf20Sopenharmony_ci hci_dev_test_flag(d, HCI_USER_CHANNEL)) 4158c2ecf20Sopenharmony_ci continue; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci /* Devices marked as raw-only are neither configured 4188c2ecf20Sopenharmony_ci * nor unconfigured controllers. 4198c2ecf20Sopenharmony_ci */ 4208c2ecf20Sopenharmony_ci if (test_bit(HCI_QUIRK_RAW_DEVICE, &d->quirks)) 4218c2ecf20Sopenharmony_ci continue; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci if (d->dev_type == HCI_PRIMARY && 4248c2ecf20Sopenharmony_ci !hci_dev_test_flag(d, HCI_UNCONFIGURED)) { 4258c2ecf20Sopenharmony_ci rp->index[count++] = cpu_to_le16(d->id); 4268c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "Added hci%u", d->id); 4278c2ecf20Sopenharmony_ci } 4288c2ecf20Sopenharmony_ci } 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci rp->num_controllers = cpu_to_le16(count); 4318c2ecf20Sopenharmony_ci rp_len = sizeof(*rp) + (2 * count); 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci read_unlock(&hci_dev_list_lock); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, MGMT_INDEX_NONE, MGMT_OP_READ_INDEX_LIST, 4368c2ecf20Sopenharmony_ci 0, rp, rp_len); 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci kfree(rp); 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_ci return err; 4418c2ecf20Sopenharmony_ci} 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_cistatic int read_unconf_index_list(struct sock *sk, struct hci_dev *hdev, 4448c2ecf20Sopenharmony_ci void *data, u16 data_len) 4458c2ecf20Sopenharmony_ci{ 4468c2ecf20Sopenharmony_ci struct mgmt_rp_read_unconf_index_list *rp; 4478c2ecf20Sopenharmony_ci struct hci_dev *d; 4488c2ecf20Sopenharmony_ci size_t rp_len; 4498c2ecf20Sopenharmony_ci u16 count; 4508c2ecf20Sopenharmony_ci int err; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci read_lock(&hci_dev_list_lock); 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci count = 0; 4578c2ecf20Sopenharmony_ci list_for_each_entry(d, &hci_dev_list, list) { 4588c2ecf20Sopenharmony_ci if (d->dev_type == HCI_PRIMARY && 4598c2ecf20Sopenharmony_ci hci_dev_test_flag(d, HCI_UNCONFIGURED)) 4608c2ecf20Sopenharmony_ci count++; 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci rp_len = sizeof(*rp) + (2 * count); 4648c2ecf20Sopenharmony_ci rp = kmalloc(rp_len, GFP_ATOMIC); 4658c2ecf20Sopenharmony_ci if (!rp) { 4668c2ecf20Sopenharmony_ci read_unlock(&hci_dev_list_lock); 4678c2ecf20Sopenharmony_ci return -ENOMEM; 4688c2ecf20Sopenharmony_ci } 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci count = 0; 4718c2ecf20Sopenharmony_ci list_for_each_entry(d, &hci_dev_list, list) { 4728c2ecf20Sopenharmony_ci if (hci_dev_test_flag(d, HCI_SETUP) || 4738c2ecf20Sopenharmony_ci hci_dev_test_flag(d, HCI_CONFIG) || 4748c2ecf20Sopenharmony_ci hci_dev_test_flag(d, HCI_USER_CHANNEL)) 4758c2ecf20Sopenharmony_ci continue; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci /* Devices marked as raw-only are neither configured 4788c2ecf20Sopenharmony_ci * nor unconfigured controllers. 4798c2ecf20Sopenharmony_ci */ 4808c2ecf20Sopenharmony_ci if (test_bit(HCI_QUIRK_RAW_DEVICE, &d->quirks)) 4818c2ecf20Sopenharmony_ci continue; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci if (d->dev_type == HCI_PRIMARY && 4848c2ecf20Sopenharmony_ci hci_dev_test_flag(d, HCI_UNCONFIGURED)) { 4858c2ecf20Sopenharmony_ci rp->index[count++] = cpu_to_le16(d->id); 4868c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "Added hci%u", d->id); 4878c2ecf20Sopenharmony_ci } 4888c2ecf20Sopenharmony_ci } 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci rp->num_controllers = cpu_to_le16(count); 4918c2ecf20Sopenharmony_ci rp_len = sizeof(*rp) + (2 * count); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci read_unlock(&hci_dev_list_lock); 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, MGMT_INDEX_NONE, 4968c2ecf20Sopenharmony_ci MGMT_OP_READ_UNCONF_INDEX_LIST, 0, rp, rp_len); 4978c2ecf20Sopenharmony_ci 4988c2ecf20Sopenharmony_ci kfree(rp); 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_ci return err; 5018c2ecf20Sopenharmony_ci} 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_cistatic int read_ext_index_list(struct sock *sk, struct hci_dev *hdev, 5048c2ecf20Sopenharmony_ci void *data, u16 data_len) 5058c2ecf20Sopenharmony_ci{ 5068c2ecf20Sopenharmony_ci struct mgmt_rp_read_ext_index_list *rp; 5078c2ecf20Sopenharmony_ci struct hci_dev *d; 5088c2ecf20Sopenharmony_ci u16 count; 5098c2ecf20Sopenharmony_ci int err; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci read_lock(&hci_dev_list_lock); 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci count = 0; 5168c2ecf20Sopenharmony_ci list_for_each_entry(d, &hci_dev_list, list) { 5178c2ecf20Sopenharmony_ci if (d->dev_type == HCI_PRIMARY || d->dev_type == HCI_AMP) 5188c2ecf20Sopenharmony_ci count++; 5198c2ecf20Sopenharmony_ci } 5208c2ecf20Sopenharmony_ci 5218c2ecf20Sopenharmony_ci rp = kmalloc(struct_size(rp, entry, count), GFP_ATOMIC); 5228c2ecf20Sopenharmony_ci if (!rp) { 5238c2ecf20Sopenharmony_ci read_unlock(&hci_dev_list_lock); 5248c2ecf20Sopenharmony_ci return -ENOMEM; 5258c2ecf20Sopenharmony_ci } 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci count = 0; 5288c2ecf20Sopenharmony_ci list_for_each_entry(d, &hci_dev_list, list) { 5298c2ecf20Sopenharmony_ci if (hci_dev_test_flag(d, HCI_SETUP) || 5308c2ecf20Sopenharmony_ci hci_dev_test_flag(d, HCI_CONFIG) || 5318c2ecf20Sopenharmony_ci hci_dev_test_flag(d, HCI_USER_CHANNEL)) 5328c2ecf20Sopenharmony_ci continue; 5338c2ecf20Sopenharmony_ci 5348c2ecf20Sopenharmony_ci /* Devices marked as raw-only are neither configured 5358c2ecf20Sopenharmony_ci * nor unconfigured controllers. 5368c2ecf20Sopenharmony_ci */ 5378c2ecf20Sopenharmony_ci if (test_bit(HCI_QUIRK_RAW_DEVICE, &d->quirks)) 5388c2ecf20Sopenharmony_ci continue; 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci if (d->dev_type == HCI_PRIMARY) { 5418c2ecf20Sopenharmony_ci if (hci_dev_test_flag(d, HCI_UNCONFIGURED)) 5428c2ecf20Sopenharmony_ci rp->entry[count].type = 0x01; 5438c2ecf20Sopenharmony_ci else 5448c2ecf20Sopenharmony_ci rp->entry[count].type = 0x00; 5458c2ecf20Sopenharmony_ci } else if (d->dev_type == HCI_AMP) { 5468c2ecf20Sopenharmony_ci rp->entry[count].type = 0x02; 5478c2ecf20Sopenharmony_ci } else { 5488c2ecf20Sopenharmony_ci continue; 5498c2ecf20Sopenharmony_ci } 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci rp->entry[count].bus = d->bus; 5528c2ecf20Sopenharmony_ci rp->entry[count++].index = cpu_to_le16(d->id); 5538c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "Added hci%u", d->id); 5548c2ecf20Sopenharmony_ci } 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci rp->num_controllers = cpu_to_le16(count); 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci read_unlock(&hci_dev_list_lock); 5598c2ecf20Sopenharmony_ci 5608c2ecf20Sopenharmony_ci /* If this command is called at least once, then all the 5618c2ecf20Sopenharmony_ci * default index and unconfigured index events are disabled 5628c2ecf20Sopenharmony_ci * and from now on only extended index events are used. 5638c2ecf20Sopenharmony_ci */ 5648c2ecf20Sopenharmony_ci hci_sock_set_flag(sk, HCI_MGMT_EXT_INDEX_EVENTS); 5658c2ecf20Sopenharmony_ci hci_sock_clear_flag(sk, HCI_MGMT_INDEX_EVENTS); 5668c2ecf20Sopenharmony_ci hci_sock_clear_flag(sk, HCI_MGMT_UNCONF_INDEX_EVENTS); 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, MGMT_INDEX_NONE, 5698c2ecf20Sopenharmony_ci MGMT_OP_READ_EXT_INDEX_LIST, 0, rp, 5708c2ecf20Sopenharmony_ci struct_size(rp, entry, count)); 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci kfree(rp); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci return err; 5758c2ecf20Sopenharmony_ci} 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_cistatic bool is_configured(struct hci_dev *hdev) 5788c2ecf20Sopenharmony_ci{ 5798c2ecf20Sopenharmony_ci if (test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks) && 5808c2ecf20Sopenharmony_ci !hci_dev_test_flag(hdev, HCI_EXT_CONFIGURED)) 5818c2ecf20Sopenharmony_ci return false; 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci if ((test_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks) || 5848c2ecf20Sopenharmony_ci test_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &hdev->quirks)) && 5858c2ecf20Sopenharmony_ci !bacmp(&hdev->public_addr, BDADDR_ANY)) 5868c2ecf20Sopenharmony_ci return false; 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_ci return true; 5898c2ecf20Sopenharmony_ci} 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_cistatic __le32 get_missing_options(struct hci_dev *hdev) 5928c2ecf20Sopenharmony_ci{ 5938c2ecf20Sopenharmony_ci u32 options = 0; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci if (test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks) && 5968c2ecf20Sopenharmony_ci !hci_dev_test_flag(hdev, HCI_EXT_CONFIGURED)) 5978c2ecf20Sopenharmony_ci options |= MGMT_OPTION_EXTERNAL_CONFIG; 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci if ((test_bit(HCI_QUIRK_INVALID_BDADDR, &hdev->quirks) || 6008c2ecf20Sopenharmony_ci test_bit(HCI_QUIRK_USE_BDADDR_PROPERTY, &hdev->quirks)) && 6018c2ecf20Sopenharmony_ci !bacmp(&hdev->public_addr, BDADDR_ANY)) 6028c2ecf20Sopenharmony_ci options |= MGMT_OPTION_PUBLIC_ADDRESS; 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci return cpu_to_le32(options); 6058c2ecf20Sopenharmony_ci} 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_cistatic int new_options(struct hci_dev *hdev, struct sock *skip) 6088c2ecf20Sopenharmony_ci{ 6098c2ecf20Sopenharmony_ci __le32 options = get_missing_options(hdev); 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci return mgmt_limited_event(MGMT_EV_NEW_CONFIG_OPTIONS, hdev, &options, 6128c2ecf20Sopenharmony_ci sizeof(options), HCI_MGMT_OPTION_EVENTS, skip); 6138c2ecf20Sopenharmony_ci} 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_cistatic int send_options_rsp(struct sock *sk, u16 opcode, struct hci_dev *hdev) 6168c2ecf20Sopenharmony_ci{ 6178c2ecf20Sopenharmony_ci __le32 options = get_missing_options(hdev); 6188c2ecf20Sopenharmony_ci 6198c2ecf20Sopenharmony_ci return mgmt_cmd_complete(sk, hdev->id, opcode, 0, &options, 6208c2ecf20Sopenharmony_ci sizeof(options)); 6218c2ecf20Sopenharmony_ci} 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_cistatic int read_config_info(struct sock *sk, struct hci_dev *hdev, 6248c2ecf20Sopenharmony_ci void *data, u16 data_len) 6258c2ecf20Sopenharmony_ci{ 6268c2ecf20Sopenharmony_ci struct mgmt_rp_read_config_info rp; 6278c2ecf20Sopenharmony_ci u32 options = 0; 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 6308c2ecf20Sopenharmony_ci 6318c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci memset(&rp, 0, sizeof(rp)); 6348c2ecf20Sopenharmony_ci rp.manufacturer = cpu_to_le16(hdev->manufacturer); 6358c2ecf20Sopenharmony_ci 6368c2ecf20Sopenharmony_ci if (test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks)) 6378c2ecf20Sopenharmony_ci options |= MGMT_OPTION_EXTERNAL_CONFIG; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci if (hdev->set_bdaddr) 6408c2ecf20Sopenharmony_ci options |= MGMT_OPTION_PUBLIC_ADDRESS; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci rp.supported_options = cpu_to_le32(options); 6438c2ecf20Sopenharmony_ci rp.missing_options = get_missing_options(hdev); 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 6468c2ecf20Sopenharmony_ci 6478c2ecf20Sopenharmony_ci return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_READ_CONFIG_INFO, 0, 6488c2ecf20Sopenharmony_ci &rp, sizeof(rp)); 6498c2ecf20Sopenharmony_ci} 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_cistatic u32 get_supported_phys(struct hci_dev *hdev) 6528c2ecf20Sopenharmony_ci{ 6538c2ecf20Sopenharmony_ci u32 supported_phys = 0; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci if (lmp_bredr_capable(hdev)) { 6568c2ecf20Sopenharmony_ci supported_phys |= MGMT_PHY_BR_1M_1SLOT; 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_ci if (hdev->features[0][0] & LMP_3SLOT) 6598c2ecf20Sopenharmony_ci supported_phys |= MGMT_PHY_BR_1M_3SLOT; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci if (hdev->features[0][0] & LMP_5SLOT) 6628c2ecf20Sopenharmony_ci supported_phys |= MGMT_PHY_BR_1M_5SLOT; 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci if (lmp_edr_2m_capable(hdev)) { 6658c2ecf20Sopenharmony_ci supported_phys |= MGMT_PHY_EDR_2M_1SLOT; 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci if (lmp_edr_3slot_capable(hdev)) 6688c2ecf20Sopenharmony_ci supported_phys |= MGMT_PHY_EDR_2M_3SLOT; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci if (lmp_edr_5slot_capable(hdev)) 6718c2ecf20Sopenharmony_ci supported_phys |= MGMT_PHY_EDR_2M_5SLOT; 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci if (lmp_edr_3m_capable(hdev)) { 6748c2ecf20Sopenharmony_ci supported_phys |= MGMT_PHY_EDR_3M_1SLOT; 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci if (lmp_edr_3slot_capable(hdev)) 6778c2ecf20Sopenharmony_ci supported_phys |= MGMT_PHY_EDR_3M_3SLOT; 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci if (lmp_edr_5slot_capable(hdev)) 6808c2ecf20Sopenharmony_ci supported_phys |= MGMT_PHY_EDR_3M_5SLOT; 6818c2ecf20Sopenharmony_ci } 6828c2ecf20Sopenharmony_ci } 6838c2ecf20Sopenharmony_ci } 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci if (lmp_le_capable(hdev)) { 6868c2ecf20Sopenharmony_ci supported_phys |= MGMT_PHY_LE_1M_TX; 6878c2ecf20Sopenharmony_ci supported_phys |= MGMT_PHY_LE_1M_RX; 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci if (hdev->le_features[1] & HCI_LE_PHY_2M) { 6908c2ecf20Sopenharmony_ci supported_phys |= MGMT_PHY_LE_2M_TX; 6918c2ecf20Sopenharmony_ci supported_phys |= MGMT_PHY_LE_2M_RX; 6928c2ecf20Sopenharmony_ci } 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_ci if (hdev->le_features[1] & HCI_LE_PHY_CODED) { 6958c2ecf20Sopenharmony_ci supported_phys |= MGMT_PHY_LE_CODED_TX; 6968c2ecf20Sopenharmony_ci supported_phys |= MGMT_PHY_LE_CODED_RX; 6978c2ecf20Sopenharmony_ci } 6988c2ecf20Sopenharmony_ci } 6998c2ecf20Sopenharmony_ci 7008c2ecf20Sopenharmony_ci return supported_phys; 7018c2ecf20Sopenharmony_ci} 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_cistatic u32 get_selected_phys(struct hci_dev *hdev) 7048c2ecf20Sopenharmony_ci{ 7058c2ecf20Sopenharmony_ci u32 selected_phys = 0; 7068c2ecf20Sopenharmony_ci 7078c2ecf20Sopenharmony_ci if (lmp_bredr_capable(hdev)) { 7088c2ecf20Sopenharmony_ci selected_phys |= MGMT_PHY_BR_1M_1SLOT; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci if (hdev->pkt_type & (HCI_DM3 | HCI_DH3)) 7118c2ecf20Sopenharmony_ci selected_phys |= MGMT_PHY_BR_1M_3SLOT; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci if (hdev->pkt_type & (HCI_DM5 | HCI_DH5)) 7148c2ecf20Sopenharmony_ci selected_phys |= MGMT_PHY_BR_1M_5SLOT; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci if (lmp_edr_2m_capable(hdev)) { 7178c2ecf20Sopenharmony_ci if (!(hdev->pkt_type & HCI_2DH1)) 7188c2ecf20Sopenharmony_ci selected_phys |= MGMT_PHY_EDR_2M_1SLOT; 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci if (lmp_edr_3slot_capable(hdev) && 7218c2ecf20Sopenharmony_ci !(hdev->pkt_type & HCI_2DH3)) 7228c2ecf20Sopenharmony_ci selected_phys |= MGMT_PHY_EDR_2M_3SLOT; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci if (lmp_edr_5slot_capable(hdev) && 7258c2ecf20Sopenharmony_ci !(hdev->pkt_type & HCI_2DH5)) 7268c2ecf20Sopenharmony_ci selected_phys |= MGMT_PHY_EDR_2M_5SLOT; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci if (lmp_edr_3m_capable(hdev)) { 7298c2ecf20Sopenharmony_ci if (!(hdev->pkt_type & HCI_3DH1)) 7308c2ecf20Sopenharmony_ci selected_phys |= MGMT_PHY_EDR_3M_1SLOT; 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci if (lmp_edr_3slot_capable(hdev) && 7338c2ecf20Sopenharmony_ci !(hdev->pkt_type & HCI_3DH3)) 7348c2ecf20Sopenharmony_ci selected_phys |= MGMT_PHY_EDR_3M_3SLOT; 7358c2ecf20Sopenharmony_ci 7368c2ecf20Sopenharmony_ci if (lmp_edr_5slot_capable(hdev) && 7378c2ecf20Sopenharmony_ci !(hdev->pkt_type & HCI_3DH5)) 7388c2ecf20Sopenharmony_ci selected_phys |= MGMT_PHY_EDR_3M_5SLOT; 7398c2ecf20Sopenharmony_ci } 7408c2ecf20Sopenharmony_ci } 7418c2ecf20Sopenharmony_ci } 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci if (lmp_le_capable(hdev)) { 7448c2ecf20Sopenharmony_ci if (hdev->le_tx_def_phys & HCI_LE_SET_PHY_1M) 7458c2ecf20Sopenharmony_ci selected_phys |= MGMT_PHY_LE_1M_TX; 7468c2ecf20Sopenharmony_ci 7478c2ecf20Sopenharmony_ci if (hdev->le_rx_def_phys & HCI_LE_SET_PHY_1M) 7488c2ecf20Sopenharmony_ci selected_phys |= MGMT_PHY_LE_1M_RX; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci if (hdev->le_tx_def_phys & HCI_LE_SET_PHY_2M) 7518c2ecf20Sopenharmony_ci selected_phys |= MGMT_PHY_LE_2M_TX; 7528c2ecf20Sopenharmony_ci 7538c2ecf20Sopenharmony_ci if (hdev->le_rx_def_phys & HCI_LE_SET_PHY_2M) 7548c2ecf20Sopenharmony_ci selected_phys |= MGMT_PHY_LE_2M_RX; 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_ci if (hdev->le_tx_def_phys & HCI_LE_SET_PHY_CODED) 7578c2ecf20Sopenharmony_ci selected_phys |= MGMT_PHY_LE_CODED_TX; 7588c2ecf20Sopenharmony_ci 7598c2ecf20Sopenharmony_ci if (hdev->le_rx_def_phys & HCI_LE_SET_PHY_CODED) 7608c2ecf20Sopenharmony_ci selected_phys |= MGMT_PHY_LE_CODED_RX; 7618c2ecf20Sopenharmony_ci } 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci return selected_phys; 7648c2ecf20Sopenharmony_ci} 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_cistatic u32 get_configurable_phys(struct hci_dev *hdev) 7678c2ecf20Sopenharmony_ci{ 7688c2ecf20Sopenharmony_ci return (get_supported_phys(hdev) & ~MGMT_PHY_BR_1M_1SLOT & 7698c2ecf20Sopenharmony_ci ~MGMT_PHY_LE_1M_TX & ~MGMT_PHY_LE_1M_RX); 7708c2ecf20Sopenharmony_ci} 7718c2ecf20Sopenharmony_ci 7728c2ecf20Sopenharmony_cistatic u32 get_supported_settings(struct hci_dev *hdev) 7738c2ecf20Sopenharmony_ci{ 7748c2ecf20Sopenharmony_ci u32 settings = 0; 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci settings |= MGMT_SETTING_POWERED; 7778c2ecf20Sopenharmony_ci settings |= MGMT_SETTING_BONDABLE; 7788c2ecf20Sopenharmony_ci settings |= MGMT_SETTING_DEBUG_KEYS; 7798c2ecf20Sopenharmony_ci settings |= MGMT_SETTING_CONNECTABLE; 7808c2ecf20Sopenharmony_ci settings |= MGMT_SETTING_DISCOVERABLE; 7818c2ecf20Sopenharmony_ci 7828c2ecf20Sopenharmony_ci if (lmp_bredr_capable(hdev)) { 7838c2ecf20Sopenharmony_ci if (hdev->hci_ver >= BLUETOOTH_VER_1_2) 7848c2ecf20Sopenharmony_ci settings |= MGMT_SETTING_FAST_CONNECTABLE; 7858c2ecf20Sopenharmony_ci settings |= MGMT_SETTING_BREDR; 7868c2ecf20Sopenharmony_ci settings |= MGMT_SETTING_LINK_SECURITY; 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci if (lmp_ssp_capable(hdev)) { 7898c2ecf20Sopenharmony_ci settings |= MGMT_SETTING_SSP; 7908c2ecf20Sopenharmony_ci if (IS_ENABLED(CONFIG_BT_HS)) 7918c2ecf20Sopenharmony_ci settings |= MGMT_SETTING_HS; 7928c2ecf20Sopenharmony_ci } 7938c2ecf20Sopenharmony_ci 7948c2ecf20Sopenharmony_ci if (lmp_sc_capable(hdev)) 7958c2ecf20Sopenharmony_ci settings |= MGMT_SETTING_SECURE_CONN; 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci if (test_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, 7988c2ecf20Sopenharmony_ci &hdev->quirks)) 7998c2ecf20Sopenharmony_ci settings |= MGMT_SETTING_WIDEBAND_SPEECH; 8008c2ecf20Sopenharmony_ci } 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci if (lmp_le_capable(hdev)) { 8038c2ecf20Sopenharmony_ci settings |= MGMT_SETTING_LE; 8048c2ecf20Sopenharmony_ci settings |= MGMT_SETTING_SECURE_CONN; 8058c2ecf20Sopenharmony_ci settings |= MGMT_SETTING_PRIVACY; 8068c2ecf20Sopenharmony_ci settings |= MGMT_SETTING_STATIC_ADDRESS; 8078c2ecf20Sopenharmony_ci 8088c2ecf20Sopenharmony_ci /* When the experimental feature for LL Privacy support is 8098c2ecf20Sopenharmony_ci * enabled, then advertising is no longer supported. 8108c2ecf20Sopenharmony_ci */ 8118c2ecf20Sopenharmony_ci if (!hci_dev_test_flag(hdev, HCI_ENABLE_LL_PRIVACY)) 8128c2ecf20Sopenharmony_ci settings |= MGMT_SETTING_ADVERTISING; 8138c2ecf20Sopenharmony_ci } 8148c2ecf20Sopenharmony_ci 8158c2ecf20Sopenharmony_ci if (test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks) || 8168c2ecf20Sopenharmony_ci hdev->set_bdaddr) 8178c2ecf20Sopenharmony_ci settings |= MGMT_SETTING_CONFIGURATION; 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci settings |= MGMT_SETTING_PHY_CONFIGURATION; 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci return settings; 8228c2ecf20Sopenharmony_ci} 8238c2ecf20Sopenharmony_ci 8248c2ecf20Sopenharmony_cistatic u32 get_current_settings(struct hci_dev *hdev) 8258c2ecf20Sopenharmony_ci{ 8268c2ecf20Sopenharmony_ci u32 settings = 0; 8278c2ecf20Sopenharmony_ci 8288c2ecf20Sopenharmony_ci if (hdev_is_powered(hdev)) 8298c2ecf20Sopenharmony_ci settings |= MGMT_SETTING_POWERED; 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci if (hci_dev_test_flag(hdev, HCI_CONNECTABLE)) 8328c2ecf20Sopenharmony_ci settings |= MGMT_SETTING_CONNECTABLE; 8338c2ecf20Sopenharmony_ci 8348c2ecf20Sopenharmony_ci if (hci_dev_test_flag(hdev, HCI_FAST_CONNECTABLE)) 8358c2ecf20Sopenharmony_ci settings |= MGMT_SETTING_FAST_CONNECTABLE; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci if (hci_dev_test_flag(hdev, HCI_DISCOVERABLE)) 8388c2ecf20Sopenharmony_ci settings |= MGMT_SETTING_DISCOVERABLE; 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ci if (hci_dev_test_flag(hdev, HCI_BONDABLE)) 8418c2ecf20Sopenharmony_ci settings |= MGMT_SETTING_BONDABLE; 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci if (hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) 8448c2ecf20Sopenharmony_ci settings |= MGMT_SETTING_BREDR; 8458c2ecf20Sopenharmony_ci 8468c2ecf20Sopenharmony_ci if (hci_dev_test_flag(hdev, HCI_LE_ENABLED)) 8478c2ecf20Sopenharmony_ci settings |= MGMT_SETTING_LE; 8488c2ecf20Sopenharmony_ci 8498c2ecf20Sopenharmony_ci if (hci_dev_test_flag(hdev, HCI_LINK_SECURITY)) 8508c2ecf20Sopenharmony_ci settings |= MGMT_SETTING_LINK_SECURITY; 8518c2ecf20Sopenharmony_ci 8528c2ecf20Sopenharmony_ci if (hci_dev_test_flag(hdev, HCI_SSP_ENABLED)) 8538c2ecf20Sopenharmony_ci settings |= MGMT_SETTING_SSP; 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci if (hci_dev_test_flag(hdev, HCI_HS_ENABLED)) 8568c2ecf20Sopenharmony_ci settings |= MGMT_SETTING_HS; 8578c2ecf20Sopenharmony_ci 8588c2ecf20Sopenharmony_ci if (hci_dev_test_flag(hdev, HCI_ADVERTISING)) 8598c2ecf20Sopenharmony_ci settings |= MGMT_SETTING_ADVERTISING; 8608c2ecf20Sopenharmony_ci 8618c2ecf20Sopenharmony_ci if (hci_dev_test_flag(hdev, HCI_SC_ENABLED)) 8628c2ecf20Sopenharmony_ci settings |= MGMT_SETTING_SECURE_CONN; 8638c2ecf20Sopenharmony_ci 8648c2ecf20Sopenharmony_ci if (hci_dev_test_flag(hdev, HCI_KEEP_DEBUG_KEYS)) 8658c2ecf20Sopenharmony_ci settings |= MGMT_SETTING_DEBUG_KEYS; 8668c2ecf20Sopenharmony_ci 8678c2ecf20Sopenharmony_ci if (hci_dev_test_flag(hdev, HCI_PRIVACY)) 8688c2ecf20Sopenharmony_ci settings |= MGMT_SETTING_PRIVACY; 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci /* The current setting for static address has two purposes. The 8718c2ecf20Sopenharmony_ci * first is to indicate if the static address will be used and 8728c2ecf20Sopenharmony_ci * the second is to indicate if it is actually set. 8738c2ecf20Sopenharmony_ci * 8748c2ecf20Sopenharmony_ci * This means if the static address is not configured, this flag 8758c2ecf20Sopenharmony_ci * will never be set. If the address is configured, then if the 8768c2ecf20Sopenharmony_ci * address is actually used decides if the flag is set or not. 8778c2ecf20Sopenharmony_ci * 8788c2ecf20Sopenharmony_ci * For single mode LE only controllers and dual-mode controllers 8798c2ecf20Sopenharmony_ci * with BR/EDR disabled, the existence of the static address will 8808c2ecf20Sopenharmony_ci * be evaluated. 8818c2ecf20Sopenharmony_ci */ 8828c2ecf20Sopenharmony_ci if (hci_dev_test_flag(hdev, HCI_FORCE_STATIC_ADDR) || 8838c2ecf20Sopenharmony_ci !hci_dev_test_flag(hdev, HCI_BREDR_ENABLED) || 8848c2ecf20Sopenharmony_ci !bacmp(&hdev->bdaddr, BDADDR_ANY)) { 8858c2ecf20Sopenharmony_ci if (bacmp(&hdev->static_addr, BDADDR_ANY)) 8868c2ecf20Sopenharmony_ci settings |= MGMT_SETTING_STATIC_ADDRESS; 8878c2ecf20Sopenharmony_ci } 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci if (hci_dev_test_flag(hdev, HCI_WIDEBAND_SPEECH_ENABLED)) 8908c2ecf20Sopenharmony_ci settings |= MGMT_SETTING_WIDEBAND_SPEECH; 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci return settings; 8938c2ecf20Sopenharmony_ci} 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_cistatic struct mgmt_pending_cmd *pending_find(u16 opcode, struct hci_dev *hdev) 8968c2ecf20Sopenharmony_ci{ 8978c2ecf20Sopenharmony_ci return mgmt_pending_find(HCI_CHANNEL_CONTROL, opcode, hdev); 8988c2ecf20Sopenharmony_ci} 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_cistatic struct mgmt_pending_cmd *pending_find_data(u16 opcode, 9018c2ecf20Sopenharmony_ci struct hci_dev *hdev, 9028c2ecf20Sopenharmony_ci const void *data) 9038c2ecf20Sopenharmony_ci{ 9048c2ecf20Sopenharmony_ci return mgmt_pending_find_data(HCI_CHANNEL_CONTROL, opcode, hdev, data); 9058c2ecf20Sopenharmony_ci} 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ciu8 mgmt_get_adv_discov_flags(struct hci_dev *hdev) 9088c2ecf20Sopenharmony_ci{ 9098c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci /* If there's a pending mgmt command the flags will not yet have 9128c2ecf20Sopenharmony_ci * their final values, so check for this first. 9138c2ecf20Sopenharmony_ci */ 9148c2ecf20Sopenharmony_ci cmd = pending_find(MGMT_OP_SET_DISCOVERABLE, hdev); 9158c2ecf20Sopenharmony_ci if (cmd) { 9168c2ecf20Sopenharmony_ci struct mgmt_mode *cp = cmd->param; 9178c2ecf20Sopenharmony_ci if (cp->val == 0x01) 9188c2ecf20Sopenharmony_ci return LE_AD_GENERAL; 9198c2ecf20Sopenharmony_ci else if (cp->val == 0x02) 9208c2ecf20Sopenharmony_ci return LE_AD_LIMITED; 9218c2ecf20Sopenharmony_ci } else { 9228c2ecf20Sopenharmony_ci if (hci_dev_test_flag(hdev, HCI_LIMITED_DISCOVERABLE)) 9238c2ecf20Sopenharmony_ci return LE_AD_LIMITED; 9248c2ecf20Sopenharmony_ci else if (hci_dev_test_flag(hdev, HCI_DISCOVERABLE)) 9258c2ecf20Sopenharmony_ci return LE_AD_GENERAL; 9268c2ecf20Sopenharmony_ci } 9278c2ecf20Sopenharmony_ci 9288c2ecf20Sopenharmony_ci return 0; 9298c2ecf20Sopenharmony_ci} 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_cibool mgmt_get_connectable(struct hci_dev *hdev) 9328c2ecf20Sopenharmony_ci{ 9338c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci /* If there's a pending mgmt command the flag will not yet have 9368c2ecf20Sopenharmony_ci * it's final value, so check for this first. 9378c2ecf20Sopenharmony_ci */ 9388c2ecf20Sopenharmony_ci cmd = pending_find(MGMT_OP_SET_CONNECTABLE, hdev); 9398c2ecf20Sopenharmony_ci if (cmd) { 9408c2ecf20Sopenharmony_ci struct mgmt_mode *cp = cmd->param; 9418c2ecf20Sopenharmony_ci 9428c2ecf20Sopenharmony_ci return cp->val; 9438c2ecf20Sopenharmony_ci } 9448c2ecf20Sopenharmony_ci 9458c2ecf20Sopenharmony_ci return hci_dev_test_flag(hdev, HCI_CONNECTABLE); 9468c2ecf20Sopenharmony_ci} 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_cistatic void service_cache_off(struct work_struct *work) 9498c2ecf20Sopenharmony_ci{ 9508c2ecf20Sopenharmony_ci struct hci_dev *hdev = container_of(work, struct hci_dev, 9518c2ecf20Sopenharmony_ci service_cache.work); 9528c2ecf20Sopenharmony_ci struct hci_request req; 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci if (!hci_dev_test_and_clear_flag(hdev, HCI_SERVICE_CACHE)) 9558c2ecf20Sopenharmony_ci return; 9568c2ecf20Sopenharmony_ci 9578c2ecf20Sopenharmony_ci hci_req_init(&req, hdev); 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ci __hci_req_update_eir(&req); 9628c2ecf20Sopenharmony_ci __hci_req_update_class(&req); 9638c2ecf20Sopenharmony_ci 9648c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 9658c2ecf20Sopenharmony_ci 9668c2ecf20Sopenharmony_ci hci_req_run(&req, NULL); 9678c2ecf20Sopenharmony_ci} 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_cistatic void rpa_expired(struct work_struct *work) 9708c2ecf20Sopenharmony_ci{ 9718c2ecf20Sopenharmony_ci struct hci_dev *hdev = container_of(work, struct hci_dev, 9728c2ecf20Sopenharmony_ci rpa_expired.work); 9738c2ecf20Sopenharmony_ci struct hci_request req; 9748c2ecf20Sopenharmony_ci 9758c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, ""); 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci hci_dev_set_flag(hdev, HCI_RPA_EXPIRED); 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci if (!hci_dev_test_flag(hdev, HCI_ADVERTISING)) 9808c2ecf20Sopenharmony_ci return; 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_ci /* The generation of a new RPA and programming it into the 9838c2ecf20Sopenharmony_ci * controller happens in the hci_req_enable_advertising() 9848c2ecf20Sopenharmony_ci * function. 9858c2ecf20Sopenharmony_ci */ 9868c2ecf20Sopenharmony_ci hci_req_init(&req, hdev); 9878c2ecf20Sopenharmony_ci if (ext_adv_capable(hdev)) 9888c2ecf20Sopenharmony_ci __hci_req_start_ext_adv(&req, hdev->cur_adv_instance); 9898c2ecf20Sopenharmony_ci else 9908c2ecf20Sopenharmony_ci __hci_req_enable_advertising(&req); 9918c2ecf20Sopenharmony_ci hci_req_run(&req, NULL); 9928c2ecf20Sopenharmony_ci} 9938c2ecf20Sopenharmony_ci 9948c2ecf20Sopenharmony_cistatic void mgmt_init_hdev(struct sock *sk, struct hci_dev *hdev) 9958c2ecf20Sopenharmony_ci{ 9968c2ecf20Sopenharmony_ci if (hci_dev_test_and_set_flag(hdev, HCI_MGMT)) 9978c2ecf20Sopenharmony_ci return; 9988c2ecf20Sopenharmony_ci 9998c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&hdev->service_cache, service_cache_off); 10008c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&hdev->rpa_expired, rpa_expired); 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci /* Non-mgmt controlled devices get this bit set 10038c2ecf20Sopenharmony_ci * implicitly so that pairing works for them, however 10048c2ecf20Sopenharmony_ci * for mgmt we require user-space to explicitly enable 10058c2ecf20Sopenharmony_ci * it 10068c2ecf20Sopenharmony_ci */ 10078c2ecf20Sopenharmony_ci hci_dev_clear_flag(hdev, HCI_BONDABLE); 10088c2ecf20Sopenharmony_ci} 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_cistatic int read_controller_info(struct sock *sk, struct hci_dev *hdev, 10118c2ecf20Sopenharmony_ci void *data, u16 data_len) 10128c2ecf20Sopenharmony_ci{ 10138c2ecf20Sopenharmony_ci struct mgmt_rp_read_info rp; 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 10168c2ecf20Sopenharmony_ci 10178c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci memset(&rp, 0, sizeof(rp)); 10208c2ecf20Sopenharmony_ci 10218c2ecf20Sopenharmony_ci bacpy(&rp.bdaddr, &hdev->bdaddr); 10228c2ecf20Sopenharmony_ci 10238c2ecf20Sopenharmony_ci rp.version = hdev->hci_ver; 10248c2ecf20Sopenharmony_ci rp.manufacturer = cpu_to_le16(hdev->manufacturer); 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci rp.supported_settings = cpu_to_le32(get_supported_settings(hdev)); 10278c2ecf20Sopenharmony_ci rp.current_settings = cpu_to_le32(get_current_settings(hdev)); 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci memcpy(rp.dev_class, hdev->dev_class, 3); 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_ci memcpy(rp.name, hdev->dev_name, sizeof(hdev->dev_name)); 10328c2ecf20Sopenharmony_ci memcpy(rp.short_name, hdev->short_name, sizeof(hdev->short_name)); 10338c2ecf20Sopenharmony_ci 10348c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_READ_INFO, 0, &rp, 10378c2ecf20Sopenharmony_ci sizeof(rp)); 10388c2ecf20Sopenharmony_ci} 10398c2ecf20Sopenharmony_ci 10408c2ecf20Sopenharmony_cistatic u16 append_eir_data_to_buf(struct hci_dev *hdev, u8 *eir) 10418c2ecf20Sopenharmony_ci{ 10428c2ecf20Sopenharmony_ci u16 eir_len = 0; 10438c2ecf20Sopenharmony_ci size_t name_len; 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci if (hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) 10468c2ecf20Sopenharmony_ci eir_len = eir_append_data(eir, eir_len, EIR_CLASS_OF_DEV, 10478c2ecf20Sopenharmony_ci hdev->dev_class, 3); 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci if (hci_dev_test_flag(hdev, HCI_LE_ENABLED)) 10508c2ecf20Sopenharmony_ci eir_len = eir_append_le16(eir, eir_len, EIR_APPEARANCE, 10518c2ecf20Sopenharmony_ci hdev->appearance); 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci name_len = strlen(hdev->dev_name); 10548c2ecf20Sopenharmony_ci eir_len = eir_append_data(eir, eir_len, EIR_NAME_COMPLETE, 10558c2ecf20Sopenharmony_ci hdev->dev_name, name_len); 10568c2ecf20Sopenharmony_ci 10578c2ecf20Sopenharmony_ci name_len = strlen(hdev->short_name); 10588c2ecf20Sopenharmony_ci eir_len = eir_append_data(eir, eir_len, EIR_NAME_SHORT, 10598c2ecf20Sopenharmony_ci hdev->short_name, name_len); 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci return eir_len; 10628c2ecf20Sopenharmony_ci} 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_cistatic int read_ext_controller_info(struct sock *sk, struct hci_dev *hdev, 10658c2ecf20Sopenharmony_ci void *data, u16 data_len) 10668c2ecf20Sopenharmony_ci{ 10678c2ecf20Sopenharmony_ci char buf[512]; 10688c2ecf20Sopenharmony_ci struct mgmt_rp_read_ext_info *rp = (void *)buf; 10698c2ecf20Sopenharmony_ci u16 eir_len; 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci memset(&buf, 0, sizeof(buf)); 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 10768c2ecf20Sopenharmony_ci 10778c2ecf20Sopenharmony_ci bacpy(&rp->bdaddr, &hdev->bdaddr); 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci rp->version = hdev->hci_ver; 10808c2ecf20Sopenharmony_ci rp->manufacturer = cpu_to_le16(hdev->manufacturer); 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci rp->supported_settings = cpu_to_le32(get_supported_settings(hdev)); 10838c2ecf20Sopenharmony_ci rp->current_settings = cpu_to_le32(get_current_settings(hdev)); 10848c2ecf20Sopenharmony_ci 10858c2ecf20Sopenharmony_ci 10868c2ecf20Sopenharmony_ci eir_len = append_eir_data_to_buf(hdev, rp->eir); 10878c2ecf20Sopenharmony_ci rp->eir_len = cpu_to_le16(eir_len); 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci /* If this command is called at least once, then the events 10928c2ecf20Sopenharmony_ci * for class of device and local name changes are disabled 10938c2ecf20Sopenharmony_ci * and only the new extended controller information event 10948c2ecf20Sopenharmony_ci * is used. 10958c2ecf20Sopenharmony_ci */ 10968c2ecf20Sopenharmony_ci hci_sock_set_flag(sk, HCI_MGMT_EXT_INFO_EVENTS); 10978c2ecf20Sopenharmony_ci hci_sock_clear_flag(sk, HCI_MGMT_DEV_CLASS_EVENTS); 10988c2ecf20Sopenharmony_ci hci_sock_clear_flag(sk, HCI_MGMT_LOCAL_NAME_EVENTS); 10998c2ecf20Sopenharmony_ci 11008c2ecf20Sopenharmony_ci return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_READ_EXT_INFO, 0, rp, 11018c2ecf20Sopenharmony_ci sizeof(*rp) + eir_len); 11028c2ecf20Sopenharmony_ci} 11038c2ecf20Sopenharmony_ci 11048c2ecf20Sopenharmony_cistatic int ext_info_changed(struct hci_dev *hdev, struct sock *skip) 11058c2ecf20Sopenharmony_ci{ 11068c2ecf20Sopenharmony_ci char buf[512]; 11078c2ecf20Sopenharmony_ci struct mgmt_ev_ext_info_changed *ev = (void *)buf; 11088c2ecf20Sopenharmony_ci u16 eir_len; 11098c2ecf20Sopenharmony_ci 11108c2ecf20Sopenharmony_ci memset(buf, 0, sizeof(buf)); 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci eir_len = append_eir_data_to_buf(hdev, ev->eir); 11138c2ecf20Sopenharmony_ci ev->eir_len = cpu_to_le16(eir_len); 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci return mgmt_limited_event(MGMT_EV_EXT_INFO_CHANGED, hdev, ev, 11168c2ecf20Sopenharmony_ci sizeof(*ev) + eir_len, 11178c2ecf20Sopenharmony_ci HCI_MGMT_EXT_INFO_EVENTS, skip); 11188c2ecf20Sopenharmony_ci} 11198c2ecf20Sopenharmony_ci 11208c2ecf20Sopenharmony_cistatic int send_settings_rsp(struct sock *sk, u16 opcode, struct hci_dev *hdev) 11218c2ecf20Sopenharmony_ci{ 11228c2ecf20Sopenharmony_ci __le32 settings = cpu_to_le32(get_current_settings(hdev)); 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci return mgmt_cmd_complete(sk, hdev->id, opcode, 0, &settings, 11258c2ecf20Sopenharmony_ci sizeof(settings)); 11268c2ecf20Sopenharmony_ci} 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_cistatic void clean_up_hci_complete(struct hci_dev *hdev, u8 status, u16 opcode) 11298c2ecf20Sopenharmony_ci{ 11308c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "status 0x%02x", status); 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci if (hci_conn_count(hdev) == 0) { 11338c2ecf20Sopenharmony_ci cancel_delayed_work(&hdev->power_off); 11348c2ecf20Sopenharmony_ci queue_work(hdev->req_workqueue, &hdev->power_off.work); 11358c2ecf20Sopenharmony_ci } 11368c2ecf20Sopenharmony_ci} 11378c2ecf20Sopenharmony_ci 11388c2ecf20Sopenharmony_civoid mgmt_advertising_added(struct sock *sk, struct hci_dev *hdev, u8 instance) 11398c2ecf20Sopenharmony_ci{ 11408c2ecf20Sopenharmony_ci struct mgmt_ev_advertising_added ev; 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci ev.instance = instance; 11438c2ecf20Sopenharmony_ci 11448c2ecf20Sopenharmony_ci mgmt_event(MGMT_EV_ADVERTISING_ADDED, hdev, &ev, sizeof(ev), sk); 11458c2ecf20Sopenharmony_ci} 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_civoid mgmt_advertising_removed(struct sock *sk, struct hci_dev *hdev, 11488c2ecf20Sopenharmony_ci u8 instance) 11498c2ecf20Sopenharmony_ci{ 11508c2ecf20Sopenharmony_ci struct mgmt_ev_advertising_removed ev; 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci ev.instance = instance; 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ci mgmt_event(MGMT_EV_ADVERTISING_REMOVED, hdev, &ev, sizeof(ev), sk); 11558c2ecf20Sopenharmony_ci} 11568c2ecf20Sopenharmony_ci 11578c2ecf20Sopenharmony_cistatic void cancel_adv_timeout(struct hci_dev *hdev) 11588c2ecf20Sopenharmony_ci{ 11598c2ecf20Sopenharmony_ci if (hdev->adv_instance_timeout) { 11608c2ecf20Sopenharmony_ci hdev->adv_instance_timeout = 0; 11618c2ecf20Sopenharmony_ci cancel_delayed_work(&hdev->adv_instance_expire); 11628c2ecf20Sopenharmony_ci } 11638c2ecf20Sopenharmony_ci} 11648c2ecf20Sopenharmony_ci 11658c2ecf20Sopenharmony_cistatic int clean_up_hci_state(struct hci_dev *hdev) 11668c2ecf20Sopenharmony_ci{ 11678c2ecf20Sopenharmony_ci struct hci_request req; 11688c2ecf20Sopenharmony_ci struct hci_conn *conn; 11698c2ecf20Sopenharmony_ci bool discov_stopped; 11708c2ecf20Sopenharmony_ci int err; 11718c2ecf20Sopenharmony_ci 11728c2ecf20Sopenharmony_ci hci_req_init(&req, hdev); 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci if (test_bit(HCI_ISCAN, &hdev->flags) || 11758c2ecf20Sopenharmony_ci test_bit(HCI_PSCAN, &hdev->flags)) { 11768c2ecf20Sopenharmony_ci u8 scan = 0x00; 11778c2ecf20Sopenharmony_ci hci_req_add(&req, HCI_OP_WRITE_SCAN_ENABLE, 1, &scan); 11788c2ecf20Sopenharmony_ci } 11798c2ecf20Sopenharmony_ci 11808c2ecf20Sopenharmony_ci hci_req_clear_adv_instance(hdev, NULL, NULL, 0x00, false); 11818c2ecf20Sopenharmony_ci 11828c2ecf20Sopenharmony_ci if (hci_dev_test_flag(hdev, HCI_LE_ADV)) 11838c2ecf20Sopenharmony_ci __hci_req_disable_advertising(&req); 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci discov_stopped = hci_req_stop_discovery(&req); 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci list_for_each_entry(conn, &hdev->conn_hash.list, list) { 11888c2ecf20Sopenharmony_ci /* 0x15 == Terminated due to Power Off */ 11898c2ecf20Sopenharmony_ci __hci_abort_conn(&req, conn, 0x15); 11908c2ecf20Sopenharmony_ci } 11918c2ecf20Sopenharmony_ci 11928c2ecf20Sopenharmony_ci err = hci_req_run(&req, clean_up_hci_complete); 11938c2ecf20Sopenharmony_ci if (!err && discov_stopped) 11948c2ecf20Sopenharmony_ci hci_discovery_set_state(hdev, DISCOVERY_STOPPING); 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci return err; 11978c2ecf20Sopenharmony_ci} 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_cistatic int set_powered(struct sock *sk, struct hci_dev *hdev, void *data, 12008c2ecf20Sopenharmony_ci u16 len) 12018c2ecf20Sopenharmony_ci{ 12028c2ecf20Sopenharmony_ci struct mgmt_mode *cp = data; 12038c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 12048c2ecf20Sopenharmony_ci int err; 12058c2ecf20Sopenharmony_ci 12068c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci if (cp->val != 0x00 && cp->val != 0x01) 12098c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_POWERED, 12108c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_ci if (pending_find(MGMT_OP_SET_POWERED, hdev)) { 12158c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_POWERED, 12168c2ecf20Sopenharmony_ci MGMT_STATUS_BUSY); 12178c2ecf20Sopenharmony_ci goto failed; 12188c2ecf20Sopenharmony_ci } 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci if (!!cp->val == hdev_is_powered(hdev)) { 12218c2ecf20Sopenharmony_ci err = send_settings_rsp(sk, MGMT_OP_SET_POWERED, hdev); 12228c2ecf20Sopenharmony_ci goto failed; 12238c2ecf20Sopenharmony_ci } 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci cmd = mgmt_pending_add(sk, MGMT_OP_SET_POWERED, hdev, data, len); 12268c2ecf20Sopenharmony_ci if (!cmd) { 12278c2ecf20Sopenharmony_ci err = -ENOMEM; 12288c2ecf20Sopenharmony_ci goto failed; 12298c2ecf20Sopenharmony_ci } 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci if (cp->val) { 12328c2ecf20Sopenharmony_ci queue_work(hdev->req_workqueue, &hdev->power_on); 12338c2ecf20Sopenharmony_ci err = 0; 12348c2ecf20Sopenharmony_ci } else { 12358c2ecf20Sopenharmony_ci /* Disconnect connections, stop scans, etc */ 12368c2ecf20Sopenharmony_ci err = clean_up_hci_state(hdev); 12378c2ecf20Sopenharmony_ci if (!err) 12388c2ecf20Sopenharmony_ci queue_delayed_work(hdev->req_workqueue, &hdev->power_off, 12398c2ecf20Sopenharmony_ci HCI_POWER_OFF_TIMEOUT); 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci /* ENODATA means there were no HCI commands queued */ 12428c2ecf20Sopenharmony_ci if (err == -ENODATA) { 12438c2ecf20Sopenharmony_ci cancel_delayed_work(&hdev->power_off); 12448c2ecf20Sopenharmony_ci queue_work(hdev->req_workqueue, &hdev->power_off.work); 12458c2ecf20Sopenharmony_ci err = 0; 12468c2ecf20Sopenharmony_ci } 12478c2ecf20Sopenharmony_ci } 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_cifailed: 12508c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 12518c2ecf20Sopenharmony_ci return err; 12528c2ecf20Sopenharmony_ci} 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_cistatic int new_settings(struct hci_dev *hdev, struct sock *skip) 12558c2ecf20Sopenharmony_ci{ 12568c2ecf20Sopenharmony_ci __le32 ev = cpu_to_le32(get_current_settings(hdev)); 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ci return mgmt_limited_event(MGMT_EV_NEW_SETTINGS, hdev, &ev, 12598c2ecf20Sopenharmony_ci sizeof(ev), HCI_MGMT_SETTING_EVENTS, skip); 12608c2ecf20Sopenharmony_ci} 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ciint mgmt_new_settings(struct hci_dev *hdev) 12638c2ecf20Sopenharmony_ci{ 12648c2ecf20Sopenharmony_ci return new_settings(hdev, NULL); 12658c2ecf20Sopenharmony_ci} 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_cistruct cmd_lookup { 12688c2ecf20Sopenharmony_ci struct sock *sk; 12698c2ecf20Sopenharmony_ci struct hci_dev *hdev; 12708c2ecf20Sopenharmony_ci u8 mgmt_status; 12718c2ecf20Sopenharmony_ci}; 12728c2ecf20Sopenharmony_ci 12738c2ecf20Sopenharmony_cistatic void settings_rsp(struct mgmt_pending_cmd *cmd, void *data) 12748c2ecf20Sopenharmony_ci{ 12758c2ecf20Sopenharmony_ci struct cmd_lookup *match = data; 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci send_settings_rsp(cmd->sk, cmd->opcode, match->hdev); 12788c2ecf20Sopenharmony_ci 12798c2ecf20Sopenharmony_ci list_del(&cmd->list); 12808c2ecf20Sopenharmony_ci 12818c2ecf20Sopenharmony_ci if (match->sk == NULL) { 12828c2ecf20Sopenharmony_ci match->sk = cmd->sk; 12838c2ecf20Sopenharmony_ci sock_hold(match->sk); 12848c2ecf20Sopenharmony_ci } 12858c2ecf20Sopenharmony_ci 12868c2ecf20Sopenharmony_ci mgmt_pending_free(cmd); 12878c2ecf20Sopenharmony_ci} 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_cistatic void cmd_status_rsp(struct mgmt_pending_cmd *cmd, void *data) 12908c2ecf20Sopenharmony_ci{ 12918c2ecf20Sopenharmony_ci u8 *status = data; 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci mgmt_cmd_status(cmd->sk, cmd->index, cmd->opcode, *status); 12948c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 12958c2ecf20Sopenharmony_ci} 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_cistatic void cmd_complete_rsp(struct mgmt_pending_cmd *cmd, void *data) 12988c2ecf20Sopenharmony_ci{ 12998c2ecf20Sopenharmony_ci if (cmd->cmd_complete) { 13008c2ecf20Sopenharmony_ci u8 *status = data; 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci cmd->cmd_complete(cmd, *status); 13038c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci return; 13068c2ecf20Sopenharmony_ci } 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci cmd_status_rsp(cmd, data); 13098c2ecf20Sopenharmony_ci} 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_cistatic int generic_cmd_complete(struct mgmt_pending_cmd *cmd, u8 status) 13128c2ecf20Sopenharmony_ci{ 13138c2ecf20Sopenharmony_ci return mgmt_cmd_complete(cmd->sk, cmd->index, cmd->opcode, status, 13148c2ecf20Sopenharmony_ci cmd->param, cmd->param_len); 13158c2ecf20Sopenharmony_ci} 13168c2ecf20Sopenharmony_ci 13178c2ecf20Sopenharmony_cistatic int addr_cmd_complete(struct mgmt_pending_cmd *cmd, u8 status) 13188c2ecf20Sopenharmony_ci{ 13198c2ecf20Sopenharmony_ci return mgmt_cmd_complete(cmd->sk, cmd->index, cmd->opcode, status, 13208c2ecf20Sopenharmony_ci cmd->param, sizeof(struct mgmt_addr_info)); 13218c2ecf20Sopenharmony_ci} 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_cistatic u8 mgmt_bredr_support(struct hci_dev *hdev) 13248c2ecf20Sopenharmony_ci{ 13258c2ecf20Sopenharmony_ci if (!lmp_bredr_capable(hdev)) 13268c2ecf20Sopenharmony_ci return MGMT_STATUS_NOT_SUPPORTED; 13278c2ecf20Sopenharmony_ci else if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) 13288c2ecf20Sopenharmony_ci return MGMT_STATUS_REJECTED; 13298c2ecf20Sopenharmony_ci else 13308c2ecf20Sopenharmony_ci return MGMT_STATUS_SUCCESS; 13318c2ecf20Sopenharmony_ci} 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_cistatic u8 mgmt_le_support(struct hci_dev *hdev) 13348c2ecf20Sopenharmony_ci{ 13358c2ecf20Sopenharmony_ci if (!lmp_le_capable(hdev)) 13368c2ecf20Sopenharmony_ci return MGMT_STATUS_NOT_SUPPORTED; 13378c2ecf20Sopenharmony_ci else if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED)) 13388c2ecf20Sopenharmony_ci return MGMT_STATUS_REJECTED; 13398c2ecf20Sopenharmony_ci else 13408c2ecf20Sopenharmony_ci return MGMT_STATUS_SUCCESS; 13418c2ecf20Sopenharmony_ci} 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_civoid mgmt_set_discoverable_complete(struct hci_dev *hdev, u8 status) 13448c2ecf20Sopenharmony_ci{ 13458c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "status 0x%02x", status); 13488c2ecf20Sopenharmony_ci 13498c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_ci cmd = pending_find(MGMT_OP_SET_DISCOVERABLE, hdev); 13528c2ecf20Sopenharmony_ci if (!cmd) 13538c2ecf20Sopenharmony_ci goto unlock; 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci if (status) { 13568c2ecf20Sopenharmony_ci u8 mgmt_err = mgmt_status(status); 13578c2ecf20Sopenharmony_ci mgmt_cmd_status(cmd->sk, cmd->index, cmd->opcode, mgmt_err); 13588c2ecf20Sopenharmony_ci hci_dev_clear_flag(hdev, HCI_LIMITED_DISCOVERABLE); 13598c2ecf20Sopenharmony_ci goto remove_cmd; 13608c2ecf20Sopenharmony_ci } 13618c2ecf20Sopenharmony_ci 13628c2ecf20Sopenharmony_ci if (hci_dev_test_flag(hdev, HCI_DISCOVERABLE) && 13638c2ecf20Sopenharmony_ci hdev->discov_timeout > 0) { 13648c2ecf20Sopenharmony_ci int to = msecs_to_jiffies(hdev->discov_timeout * 1000); 13658c2ecf20Sopenharmony_ci queue_delayed_work(hdev->req_workqueue, &hdev->discov_off, to); 13668c2ecf20Sopenharmony_ci } 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ci send_settings_rsp(cmd->sk, MGMT_OP_SET_DISCOVERABLE, hdev); 13698c2ecf20Sopenharmony_ci new_settings(hdev, cmd->sk); 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ciremove_cmd: 13728c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ciunlock: 13758c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 13768c2ecf20Sopenharmony_ci} 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_cistatic int set_discoverable(struct sock *sk, struct hci_dev *hdev, void *data, 13798c2ecf20Sopenharmony_ci u16 len) 13808c2ecf20Sopenharmony_ci{ 13818c2ecf20Sopenharmony_ci struct mgmt_cp_set_discoverable *cp = data; 13828c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 13838c2ecf20Sopenharmony_ci u16 timeout; 13848c2ecf20Sopenharmony_ci int err; 13858c2ecf20Sopenharmony_ci 13868c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 13878c2ecf20Sopenharmony_ci 13888c2ecf20Sopenharmony_ci if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED) && 13898c2ecf20Sopenharmony_ci !hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) 13908c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, 13918c2ecf20Sopenharmony_ci MGMT_STATUS_REJECTED); 13928c2ecf20Sopenharmony_ci 13938c2ecf20Sopenharmony_ci if (cp->val != 0x00 && cp->val != 0x01 && cp->val != 0x02) 13948c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, 13958c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 13968c2ecf20Sopenharmony_ci 13978c2ecf20Sopenharmony_ci timeout = __le16_to_cpu(cp->timeout); 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci /* Disabling discoverable requires that no timeout is set, 14008c2ecf20Sopenharmony_ci * and enabling limited discoverable requires a timeout. 14018c2ecf20Sopenharmony_ci */ 14028c2ecf20Sopenharmony_ci if ((cp->val == 0x00 && timeout > 0) || 14038c2ecf20Sopenharmony_ci (cp->val == 0x02 && timeout == 0)) 14048c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, 14058c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 14088c2ecf20Sopenharmony_ci 14098c2ecf20Sopenharmony_ci if (!hdev_is_powered(hdev) && timeout > 0) { 14108c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, 14118c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_POWERED); 14128c2ecf20Sopenharmony_ci goto failed; 14138c2ecf20Sopenharmony_ci } 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ci if (pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) || 14168c2ecf20Sopenharmony_ci pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) { 14178c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, 14188c2ecf20Sopenharmony_ci MGMT_STATUS_BUSY); 14198c2ecf20Sopenharmony_ci goto failed; 14208c2ecf20Sopenharmony_ci } 14218c2ecf20Sopenharmony_ci 14228c2ecf20Sopenharmony_ci if (!hci_dev_test_flag(hdev, HCI_CONNECTABLE)) { 14238c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, 14248c2ecf20Sopenharmony_ci MGMT_STATUS_REJECTED); 14258c2ecf20Sopenharmony_ci goto failed; 14268c2ecf20Sopenharmony_ci } 14278c2ecf20Sopenharmony_ci 14288c2ecf20Sopenharmony_ci if (hdev->advertising_paused) { 14298c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_DISCOVERABLE, 14308c2ecf20Sopenharmony_ci MGMT_STATUS_BUSY); 14318c2ecf20Sopenharmony_ci goto failed; 14328c2ecf20Sopenharmony_ci } 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci if (!hdev_is_powered(hdev)) { 14358c2ecf20Sopenharmony_ci bool changed = false; 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci /* Setting limited discoverable when powered off is 14388c2ecf20Sopenharmony_ci * not a valid operation since it requires a timeout 14398c2ecf20Sopenharmony_ci * and so no need to check HCI_LIMITED_DISCOVERABLE. 14408c2ecf20Sopenharmony_ci */ 14418c2ecf20Sopenharmony_ci if (!!cp->val != hci_dev_test_flag(hdev, HCI_DISCOVERABLE)) { 14428c2ecf20Sopenharmony_ci hci_dev_change_flag(hdev, HCI_DISCOVERABLE); 14438c2ecf20Sopenharmony_ci changed = true; 14448c2ecf20Sopenharmony_ci } 14458c2ecf20Sopenharmony_ci 14468c2ecf20Sopenharmony_ci err = send_settings_rsp(sk, MGMT_OP_SET_DISCOVERABLE, hdev); 14478c2ecf20Sopenharmony_ci if (err < 0) 14488c2ecf20Sopenharmony_ci goto failed; 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_ci if (changed) 14518c2ecf20Sopenharmony_ci err = new_settings(hdev, sk); 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_ci goto failed; 14548c2ecf20Sopenharmony_ci } 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_ci /* If the current mode is the same, then just update the timeout 14578c2ecf20Sopenharmony_ci * value with the new value. And if only the timeout gets updated, 14588c2ecf20Sopenharmony_ci * then no need for any HCI transactions. 14598c2ecf20Sopenharmony_ci */ 14608c2ecf20Sopenharmony_ci if (!!cp->val == hci_dev_test_flag(hdev, HCI_DISCOVERABLE) && 14618c2ecf20Sopenharmony_ci (cp->val == 0x02) == hci_dev_test_flag(hdev, 14628c2ecf20Sopenharmony_ci HCI_LIMITED_DISCOVERABLE)) { 14638c2ecf20Sopenharmony_ci cancel_delayed_work(&hdev->discov_off); 14648c2ecf20Sopenharmony_ci hdev->discov_timeout = timeout; 14658c2ecf20Sopenharmony_ci 14668c2ecf20Sopenharmony_ci if (cp->val && hdev->discov_timeout > 0) { 14678c2ecf20Sopenharmony_ci int to = msecs_to_jiffies(hdev->discov_timeout * 1000); 14688c2ecf20Sopenharmony_ci queue_delayed_work(hdev->req_workqueue, 14698c2ecf20Sopenharmony_ci &hdev->discov_off, to); 14708c2ecf20Sopenharmony_ci } 14718c2ecf20Sopenharmony_ci 14728c2ecf20Sopenharmony_ci err = send_settings_rsp(sk, MGMT_OP_SET_DISCOVERABLE, hdev); 14738c2ecf20Sopenharmony_ci goto failed; 14748c2ecf20Sopenharmony_ci } 14758c2ecf20Sopenharmony_ci 14768c2ecf20Sopenharmony_ci cmd = mgmt_pending_add(sk, MGMT_OP_SET_DISCOVERABLE, hdev, data, len); 14778c2ecf20Sopenharmony_ci if (!cmd) { 14788c2ecf20Sopenharmony_ci err = -ENOMEM; 14798c2ecf20Sopenharmony_ci goto failed; 14808c2ecf20Sopenharmony_ci } 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_ci /* Cancel any potential discoverable timeout that might be 14838c2ecf20Sopenharmony_ci * still active and store new timeout value. The arming of 14848c2ecf20Sopenharmony_ci * the timeout happens in the complete handler. 14858c2ecf20Sopenharmony_ci */ 14868c2ecf20Sopenharmony_ci cancel_delayed_work(&hdev->discov_off); 14878c2ecf20Sopenharmony_ci hdev->discov_timeout = timeout; 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ci if (cp->val) 14908c2ecf20Sopenharmony_ci hci_dev_set_flag(hdev, HCI_DISCOVERABLE); 14918c2ecf20Sopenharmony_ci else 14928c2ecf20Sopenharmony_ci hci_dev_clear_flag(hdev, HCI_DISCOVERABLE); 14938c2ecf20Sopenharmony_ci 14948c2ecf20Sopenharmony_ci /* Limited discoverable mode */ 14958c2ecf20Sopenharmony_ci if (cp->val == 0x02) 14968c2ecf20Sopenharmony_ci hci_dev_set_flag(hdev, HCI_LIMITED_DISCOVERABLE); 14978c2ecf20Sopenharmony_ci else 14988c2ecf20Sopenharmony_ci hci_dev_clear_flag(hdev, HCI_LIMITED_DISCOVERABLE); 14998c2ecf20Sopenharmony_ci 15008c2ecf20Sopenharmony_ci queue_work(hdev->req_workqueue, &hdev->discoverable_update); 15018c2ecf20Sopenharmony_ci err = 0; 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_cifailed: 15048c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 15058c2ecf20Sopenharmony_ci return err; 15068c2ecf20Sopenharmony_ci} 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_civoid mgmt_set_connectable_complete(struct hci_dev *hdev, u8 status) 15098c2ecf20Sopenharmony_ci{ 15108c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 15118c2ecf20Sopenharmony_ci 15128c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "status 0x%02x", status); 15138c2ecf20Sopenharmony_ci 15148c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_ci cmd = pending_find(MGMT_OP_SET_CONNECTABLE, hdev); 15178c2ecf20Sopenharmony_ci if (!cmd) 15188c2ecf20Sopenharmony_ci goto unlock; 15198c2ecf20Sopenharmony_ci 15208c2ecf20Sopenharmony_ci if (status) { 15218c2ecf20Sopenharmony_ci u8 mgmt_err = mgmt_status(status); 15228c2ecf20Sopenharmony_ci mgmt_cmd_status(cmd->sk, cmd->index, cmd->opcode, mgmt_err); 15238c2ecf20Sopenharmony_ci goto remove_cmd; 15248c2ecf20Sopenharmony_ci } 15258c2ecf20Sopenharmony_ci 15268c2ecf20Sopenharmony_ci send_settings_rsp(cmd->sk, MGMT_OP_SET_CONNECTABLE, hdev); 15278c2ecf20Sopenharmony_ci new_settings(hdev, cmd->sk); 15288c2ecf20Sopenharmony_ci 15298c2ecf20Sopenharmony_ciremove_cmd: 15308c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_ciunlock: 15338c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 15348c2ecf20Sopenharmony_ci} 15358c2ecf20Sopenharmony_ci 15368c2ecf20Sopenharmony_cistatic int set_connectable_update_settings(struct hci_dev *hdev, 15378c2ecf20Sopenharmony_ci struct sock *sk, u8 val) 15388c2ecf20Sopenharmony_ci{ 15398c2ecf20Sopenharmony_ci bool changed = false; 15408c2ecf20Sopenharmony_ci int err; 15418c2ecf20Sopenharmony_ci 15428c2ecf20Sopenharmony_ci if (!!val != hci_dev_test_flag(hdev, HCI_CONNECTABLE)) 15438c2ecf20Sopenharmony_ci changed = true; 15448c2ecf20Sopenharmony_ci 15458c2ecf20Sopenharmony_ci if (val) { 15468c2ecf20Sopenharmony_ci hci_dev_set_flag(hdev, HCI_CONNECTABLE); 15478c2ecf20Sopenharmony_ci } else { 15488c2ecf20Sopenharmony_ci hci_dev_clear_flag(hdev, HCI_CONNECTABLE); 15498c2ecf20Sopenharmony_ci hci_dev_clear_flag(hdev, HCI_DISCOVERABLE); 15508c2ecf20Sopenharmony_ci } 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_ci err = send_settings_rsp(sk, MGMT_OP_SET_CONNECTABLE, hdev); 15538c2ecf20Sopenharmony_ci if (err < 0) 15548c2ecf20Sopenharmony_ci return err; 15558c2ecf20Sopenharmony_ci 15568c2ecf20Sopenharmony_ci if (changed) { 15578c2ecf20Sopenharmony_ci hci_req_update_scan(hdev); 15588c2ecf20Sopenharmony_ci hci_update_background_scan(hdev); 15598c2ecf20Sopenharmony_ci return new_settings(hdev, sk); 15608c2ecf20Sopenharmony_ci } 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_ci return 0; 15638c2ecf20Sopenharmony_ci} 15648c2ecf20Sopenharmony_ci 15658c2ecf20Sopenharmony_cistatic int set_connectable(struct sock *sk, struct hci_dev *hdev, void *data, 15668c2ecf20Sopenharmony_ci u16 len) 15678c2ecf20Sopenharmony_ci{ 15688c2ecf20Sopenharmony_ci struct mgmt_mode *cp = data; 15698c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 15708c2ecf20Sopenharmony_ci int err; 15718c2ecf20Sopenharmony_ci 15728c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 15738c2ecf20Sopenharmony_ci 15748c2ecf20Sopenharmony_ci if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED) && 15758c2ecf20Sopenharmony_ci !hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) 15768c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE, 15778c2ecf20Sopenharmony_ci MGMT_STATUS_REJECTED); 15788c2ecf20Sopenharmony_ci 15798c2ecf20Sopenharmony_ci if (cp->val != 0x00 && cp->val != 0x01) 15808c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE, 15818c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 15828c2ecf20Sopenharmony_ci 15838c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 15848c2ecf20Sopenharmony_ci 15858c2ecf20Sopenharmony_ci if (!hdev_is_powered(hdev)) { 15868c2ecf20Sopenharmony_ci err = set_connectable_update_settings(hdev, sk, cp->val); 15878c2ecf20Sopenharmony_ci goto failed; 15888c2ecf20Sopenharmony_ci } 15898c2ecf20Sopenharmony_ci 15908c2ecf20Sopenharmony_ci if (pending_find(MGMT_OP_SET_DISCOVERABLE, hdev) || 15918c2ecf20Sopenharmony_ci pending_find(MGMT_OP_SET_CONNECTABLE, hdev)) { 15928c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_CONNECTABLE, 15938c2ecf20Sopenharmony_ci MGMT_STATUS_BUSY); 15948c2ecf20Sopenharmony_ci goto failed; 15958c2ecf20Sopenharmony_ci } 15968c2ecf20Sopenharmony_ci 15978c2ecf20Sopenharmony_ci cmd = mgmt_pending_add(sk, MGMT_OP_SET_CONNECTABLE, hdev, data, len); 15988c2ecf20Sopenharmony_ci if (!cmd) { 15998c2ecf20Sopenharmony_ci err = -ENOMEM; 16008c2ecf20Sopenharmony_ci goto failed; 16018c2ecf20Sopenharmony_ci } 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_ci if (cp->val) { 16048c2ecf20Sopenharmony_ci hci_dev_set_flag(hdev, HCI_CONNECTABLE); 16058c2ecf20Sopenharmony_ci } else { 16068c2ecf20Sopenharmony_ci if (hdev->discov_timeout > 0) 16078c2ecf20Sopenharmony_ci cancel_delayed_work(&hdev->discov_off); 16088c2ecf20Sopenharmony_ci 16098c2ecf20Sopenharmony_ci hci_dev_clear_flag(hdev, HCI_LIMITED_DISCOVERABLE); 16108c2ecf20Sopenharmony_ci hci_dev_clear_flag(hdev, HCI_DISCOVERABLE); 16118c2ecf20Sopenharmony_ci hci_dev_clear_flag(hdev, HCI_CONNECTABLE); 16128c2ecf20Sopenharmony_ci } 16138c2ecf20Sopenharmony_ci 16148c2ecf20Sopenharmony_ci queue_work(hdev->req_workqueue, &hdev->connectable_update); 16158c2ecf20Sopenharmony_ci err = 0; 16168c2ecf20Sopenharmony_ci 16178c2ecf20Sopenharmony_cifailed: 16188c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 16198c2ecf20Sopenharmony_ci return err; 16208c2ecf20Sopenharmony_ci} 16218c2ecf20Sopenharmony_ci 16228c2ecf20Sopenharmony_cistatic int set_bondable(struct sock *sk, struct hci_dev *hdev, void *data, 16238c2ecf20Sopenharmony_ci u16 len) 16248c2ecf20Sopenharmony_ci{ 16258c2ecf20Sopenharmony_ci struct mgmt_mode *cp = data; 16268c2ecf20Sopenharmony_ci bool changed; 16278c2ecf20Sopenharmony_ci int err; 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 16308c2ecf20Sopenharmony_ci 16318c2ecf20Sopenharmony_ci if (cp->val != 0x00 && cp->val != 0x01) 16328c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_BONDABLE, 16338c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 16348c2ecf20Sopenharmony_ci 16358c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_ci if (cp->val) 16388c2ecf20Sopenharmony_ci changed = !hci_dev_test_and_set_flag(hdev, HCI_BONDABLE); 16398c2ecf20Sopenharmony_ci else 16408c2ecf20Sopenharmony_ci changed = hci_dev_test_and_clear_flag(hdev, HCI_BONDABLE); 16418c2ecf20Sopenharmony_ci 16428c2ecf20Sopenharmony_ci err = send_settings_rsp(sk, MGMT_OP_SET_BONDABLE, hdev); 16438c2ecf20Sopenharmony_ci if (err < 0) 16448c2ecf20Sopenharmony_ci goto unlock; 16458c2ecf20Sopenharmony_ci 16468c2ecf20Sopenharmony_ci if (changed) { 16478c2ecf20Sopenharmony_ci /* In limited privacy mode the change of bondable mode 16488c2ecf20Sopenharmony_ci * may affect the local advertising address. 16498c2ecf20Sopenharmony_ci */ 16508c2ecf20Sopenharmony_ci if (hdev_is_powered(hdev) && 16518c2ecf20Sopenharmony_ci hci_dev_test_flag(hdev, HCI_ADVERTISING) && 16528c2ecf20Sopenharmony_ci hci_dev_test_flag(hdev, HCI_DISCOVERABLE) && 16538c2ecf20Sopenharmony_ci hci_dev_test_flag(hdev, HCI_LIMITED_PRIVACY)) 16548c2ecf20Sopenharmony_ci queue_work(hdev->req_workqueue, 16558c2ecf20Sopenharmony_ci &hdev->discoverable_update); 16568c2ecf20Sopenharmony_ci 16578c2ecf20Sopenharmony_ci err = new_settings(hdev, sk); 16588c2ecf20Sopenharmony_ci } 16598c2ecf20Sopenharmony_ci 16608c2ecf20Sopenharmony_ciunlock: 16618c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 16628c2ecf20Sopenharmony_ci return err; 16638c2ecf20Sopenharmony_ci} 16648c2ecf20Sopenharmony_ci 16658c2ecf20Sopenharmony_cistatic int set_link_security(struct sock *sk, struct hci_dev *hdev, void *data, 16668c2ecf20Sopenharmony_ci u16 len) 16678c2ecf20Sopenharmony_ci{ 16688c2ecf20Sopenharmony_ci struct mgmt_mode *cp = data; 16698c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 16708c2ecf20Sopenharmony_ci u8 val, status; 16718c2ecf20Sopenharmony_ci int err; 16728c2ecf20Sopenharmony_ci 16738c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 16748c2ecf20Sopenharmony_ci 16758c2ecf20Sopenharmony_ci status = mgmt_bredr_support(hdev); 16768c2ecf20Sopenharmony_ci if (status) 16778c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_LINK_SECURITY, 16788c2ecf20Sopenharmony_ci status); 16798c2ecf20Sopenharmony_ci 16808c2ecf20Sopenharmony_ci if (cp->val != 0x00 && cp->val != 0x01) 16818c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_LINK_SECURITY, 16828c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 16838c2ecf20Sopenharmony_ci 16848c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 16858c2ecf20Sopenharmony_ci 16868c2ecf20Sopenharmony_ci if (!hdev_is_powered(hdev)) { 16878c2ecf20Sopenharmony_ci bool changed = false; 16888c2ecf20Sopenharmony_ci 16898c2ecf20Sopenharmony_ci if (!!cp->val != hci_dev_test_flag(hdev, HCI_LINK_SECURITY)) { 16908c2ecf20Sopenharmony_ci hci_dev_change_flag(hdev, HCI_LINK_SECURITY); 16918c2ecf20Sopenharmony_ci changed = true; 16928c2ecf20Sopenharmony_ci } 16938c2ecf20Sopenharmony_ci 16948c2ecf20Sopenharmony_ci err = send_settings_rsp(sk, MGMT_OP_SET_LINK_SECURITY, hdev); 16958c2ecf20Sopenharmony_ci if (err < 0) 16968c2ecf20Sopenharmony_ci goto failed; 16978c2ecf20Sopenharmony_ci 16988c2ecf20Sopenharmony_ci if (changed) 16998c2ecf20Sopenharmony_ci err = new_settings(hdev, sk); 17008c2ecf20Sopenharmony_ci 17018c2ecf20Sopenharmony_ci goto failed; 17028c2ecf20Sopenharmony_ci } 17038c2ecf20Sopenharmony_ci 17048c2ecf20Sopenharmony_ci if (pending_find(MGMT_OP_SET_LINK_SECURITY, hdev)) { 17058c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_LINK_SECURITY, 17068c2ecf20Sopenharmony_ci MGMT_STATUS_BUSY); 17078c2ecf20Sopenharmony_ci goto failed; 17088c2ecf20Sopenharmony_ci } 17098c2ecf20Sopenharmony_ci 17108c2ecf20Sopenharmony_ci val = !!cp->val; 17118c2ecf20Sopenharmony_ci 17128c2ecf20Sopenharmony_ci if (test_bit(HCI_AUTH, &hdev->flags) == val) { 17138c2ecf20Sopenharmony_ci err = send_settings_rsp(sk, MGMT_OP_SET_LINK_SECURITY, hdev); 17148c2ecf20Sopenharmony_ci goto failed; 17158c2ecf20Sopenharmony_ci } 17168c2ecf20Sopenharmony_ci 17178c2ecf20Sopenharmony_ci cmd = mgmt_pending_add(sk, MGMT_OP_SET_LINK_SECURITY, hdev, data, len); 17188c2ecf20Sopenharmony_ci if (!cmd) { 17198c2ecf20Sopenharmony_ci err = -ENOMEM; 17208c2ecf20Sopenharmony_ci goto failed; 17218c2ecf20Sopenharmony_ci } 17228c2ecf20Sopenharmony_ci 17238c2ecf20Sopenharmony_ci err = hci_send_cmd(hdev, HCI_OP_WRITE_AUTH_ENABLE, sizeof(val), &val); 17248c2ecf20Sopenharmony_ci if (err < 0) { 17258c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 17268c2ecf20Sopenharmony_ci goto failed; 17278c2ecf20Sopenharmony_ci } 17288c2ecf20Sopenharmony_ci 17298c2ecf20Sopenharmony_cifailed: 17308c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 17318c2ecf20Sopenharmony_ci return err; 17328c2ecf20Sopenharmony_ci} 17338c2ecf20Sopenharmony_ci 17348c2ecf20Sopenharmony_cistatic int set_ssp(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) 17358c2ecf20Sopenharmony_ci{ 17368c2ecf20Sopenharmony_ci struct mgmt_mode *cp = data; 17378c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 17388c2ecf20Sopenharmony_ci u8 status; 17398c2ecf20Sopenharmony_ci int err; 17408c2ecf20Sopenharmony_ci 17418c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_ci status = mgmt_bredr_support(hdev); 17448c2ecf20Sopenharmony_ci if (status) 17458c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_SSP, status); 17468c2ecf20Sopenharmony_ci 17478c2ecf20Sopenharmony_ci if (!lmp_ssp_capable(hdev)) 17488c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_SSP, 17498c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_SUPPORTED); 17508c2ecf20Sopenharmony_ci 17518c2ecf20Sopenharmony_ci if (cp->val != 0x00 && cp->val != 0x01) 17528c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_SSP, 17538c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 17548c2ecf20Sopenharmony_ci 17558c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 17568c2ecf20Sopenharmony_ci 17578c2ecf20Sopenharmony_ci if (!hdev_is_powered(hdev)) { 17588c2ecf20Sopenharmony_ci bool changed; 17598c2ecf20Sopenharmony_ci 17608c2ecf20Sopenharmony_ci if (cp->val) { 17618c2ecf20Sopenharmony_ci changed = !hci_dev_test_and_set_flag(hdev, 17628c2ecf20Sopenharmony_ci HCI_SSP_ENABLED); 17638c2ecf20Sopenharmony_ci } else { 17648c2ecf20Sopenharmony_ci changed = hci_dev_test_and_clear_flag(hdev, 17658c2ecf20Sopenharmony_ci HCI_SSP_ENABLED); 17668c2ecf20Sopenharmony_ci if (!changed) 17678c2ecf20Sopenharmony_ci changed = hci_dev_test_and_clear_flag(hdev, 17688c2ecf20Sopenharmony_ci HCI_HS_ENABLED); 17698c2ecf20Sopenharmony_ci else 17708c2ecf20Sopenharmony_ci hci_dev_clear_flag(hdev, HCI_HS_ENABLED); 17718c2ecf20Sopenharmony_ci } 17728c2ecf20Sopenharmony_ci 17738c2ecf20Sopenharmony_ci err = send_settings_rsp(sk, MGMT_OP_SET_SSP, hdev); 17748c2ecf20Sopenharmony_ci if (err < 0) 17758c2ecf20Sopenharmony_ci goto failed; 17768c2ecf20Sopenharmony_ci 17778c2ecf20Sopenharmony_ci if (changed) 17788c2ecf20Sopenharmony_ci err = new_settings(hdev, sk); 17798c2ecf20Sopenharmony_ci 17808c2ecf20Sopenharmony_ci goto failed; 17818c2ecf20Sopenharmony_ci } 17828c2ecf20Sopenharmony_ci 17838c2ecf20Sopenharmony_ci if (pending_find(MGMT_OP_SET_SSP, hdev)) { 17848c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_SSP, 17858c2ecf20Sopenharmony_ci MGMT_STATUS_BUSY); 17868c2ecf20Sopenharmony_ci goto failed; 17878c2ecf20Sopenharmony_ci } 17888c2ecf20Sopenharmony_ci 17898c2ecf20Sopenharmony_ci if (!!cp->val == hci_dev_test_flag(hdev, HCI_SSP_ENABLED)) { 17908c2ecf20Sopenharmony_ci err = send_settings_rsp(sk, MGMT_OP_SET_SSP, hdev); 17918c2ecf20Sopenharmony_ci goto failed; 17928c2ecf20Sopenharmony_ci } 17938c2ecf20Sopenharmony_ci 17948c2ecf20Sopenharmony_ci cmd = mgmt_pending_add(sk, MGMT_OP_SET_SSP, hdev, data, len); 17958c2ecf20Sopenharmony_ci if (!cmd) { 17968c2ecf20Sopenharmony_ci err = -ENOMEM; 17978c2ecf20Sopenharmony_ci goto failed; 17988c2ecf20Sopenharmony_ci } 17998c2ecf20Sopenharmony_ci 18008c2ecf20Sopenharmony_ci if (!cp->val && hci_dev_test_flag(hdev, HCI_USE_DEBUG_KEYS)) 18018c2ecf20Sopenharmony_ci hci_send_cmd(hdev, HCI_OP_WRITE_SSP_DEBUG_MODE, 18028c2ecf20Sopenharmony_ci sizeof(cp->val), &cp->val); 18038c2ecf20Sopenharmony_ci 18048c2ecf20Sopenharmony_ci err = hci_send_cmd(hdev, HCI_OP_WRITE_SSP_MODE, 1, &cp->val); 18058c2ecf20Sopenharmony_ci if (err < 0) { 18068c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 18078c2ecf20Sopenharmony_ci goto failed; 18088c2ecf20Sopenharmony_ci } 18098c2ecf20Sopenharmony_ci 18108c2ecf20Sopenharmony_cifailed: 18118c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 18128c2ecf20Sopenharmony_ci return err; 18138c2ecf20Sopenharmony_ci} 18148c2ecf20Sopenharmony_ci 18158c2ecf20Sopenharmony_cistatic int set_hs(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) 18168c2ecf20Sopenharmony_ci{ 18178c2ecf20Sopenharmony_ci struct mgmt_mode *cp = data; 18188c2ecf20Sopenharmony_ci bool changed; 18198c2ecf20Sopenharmony_ci u8 status; 18208c2ecf20Sopenharmony_ci int err; 18218c2ecf20Sopenharmony_ci 18228c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 18238c2ecf20Sopenharmony_ci 18248c2ecf20Sopenharmony_ci if (!IS_ENABLED(CONFIG_BT_HS)) 18258c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_HS, 18268c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_SUPPORTED); 18278c2ecf20Sopenharmony_ci 18288c2ecf20Sopenharmony_ci status = mgmt_bredr_support(hdev); 18298c2ecf20Sopenharmony_ci if (status) 18308c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_HS, status); 18318c2ecf20Sopenharmony_ci 18328c2ecf20Sopenharmony_ci if (!lmp_ssp_capable(hdev)) 18338c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_HS, 18348c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_SUPPORTED); 18358c2ecf20Sopenharmony_ci 18368c2ecf20Sopenharmony_ci if (!hci_dev_test_flag(hdev, HCI_SSP_ENABLED)) 18378c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_HS, 18388c2ecf20Sopenharmony_ci MGMT_STATUS_REJECTED); 18398c2ecf20Sopenharmony_ci 18408c2ecf20Sopenharmony_ci if (cp->val != 0x00 && cp->val != 0x01) 18418c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_HS, 18428c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 18438c2ecf20Sopenharmony_ci 18448c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 18458c2ecf20Sopenharmony_ci 18468c2ecf20Sopenharmony_ci if (pending_find(MGMT_OP_SET_SSP, hdev)) { 18478c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_HS, 18488c2ecf20Sopenharmony_ci MGMT_STATUS_BUSY); 18498c2ecf20Sopenharmony_ci goto unlock; 18508c2ecf20Sopenharmony_ci } 18518c2ecf20Sopenharmony_ci 18528c2ecf20Sopenharmony_ci if (cp->val) { 18538c2ecf20Sopenharmony_ci changed = !hci_dev_test_and_set_flag(hdev, HCI_HS_ENABLED); 18548c2ecf20Sopenharmony_ci } else { 18558c2ecf20Sopenharmony_ci if (hdev_is_powered(hdev)) { 18568c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_HS, 18578c2ecf20Sopenharmony_ci MGMT_STATUS_REJECTED); 18588c2ecf20Sopenharmony_ci goto unlock; 18598c2ecf20Sopenharmony_ci } 18608c2ecf20Sopenharmony_ci 18618c2ecf20Sopenharmony_ci changed = hci_dev_test_and_clear_flag(hdev, HCI_HS_ENABLED); 18628c2ecf20Sopenharmony_ci } 18638c2ecf20Sopenharmony_ci 18648c2ecf20Sopenharmony_ci err = send_settings_rsp(sk, MGMT_OP_SET_HS, hdev); 18658c2ecf20Sopenharmony_ci if (err < 0) 18668c2ecf20Sopenharmony_ci goto unlock; 18678c2ecf20Sopenharmony_ci 18688c2ecf20Sopenharmony_ci if (changed) 18698c2ecf20Sopenharmony_ci err = new_settings(hdev, sk); 18708c2ecf20Sopenharmony_ci 18718c2ecf20Sopenharmony_ciunlock: 18728c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 18738c2ecf20Sopenharmony_ci return err; 18748c2ecf20Sopenharmony_ci} 18758c2ecf20Sopenharmony_ci 18768c2ecf20Sopenharmony_cistatic void le_enable_complete(struct hci_dev *hdev, u8 status, u16 opcode) 18778c2ecf20Sopenharmony_ci{ 18788c2ecf20Sopenharmony_ci struct cmd_lookup match = { NULL, hdev }; 18798c2ecf20Sopenharmony_ci 18808c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 18818c2ecf20Sopenharmony_ci 18828c2ecf20Sopenharmony_ci if (status) { 18838c2ecf20Sopenharmony_ci u8 mgmt_err = mgmt_status(status); 18848c2ecf20Sopenharmony_ci 18858c2ecf20Sopenharmony_ci mgmt_pending_foreach(MGMT_OP_SET_LE, hdev, cmd_status_rsp, 18868c2ecf20Sopenharmony_ci &mgmt_err); 18878c2ecf20Sopenharmony_ci goto unlock; 18888c2ecf20Sopenharmony_ci } 18898c2ecf20Sopenharmony_ci 18908c2ecf20Sopenharmony_ci mgmt_pending_foreach(MGMT_OP_SET_LE, hdev, settings_rsp, &match); 18918c2ecf20Sopenharmony_ci 18928c2ecf20Sopenharmony_ci new_settings(hdev, match.sk); 18938c2ecf20Sopenharmony_ci 18948c2ecf20Sopenharmony_ci if (match.sk) 18958c2ecf20Sopenharmony_ci sock_put(match.sk); 18968c2ecf20Sopenharmony_ci 18978c2ecf20Sopenharmony_ci /* Make sure the controller has a good default for 18988c2ecf20Sopenharmony_ci * advertising data. Restrict the update to when LE 18998c2ecf20Sopenharmony_ci * has actually been enabled. During power on, the 19008c2ecf20Sopenharmony_ci * update in powered_update_hci will take care of it. 19018c2ecf20Sopenharmony_ci */ 19028c2ecf20Sopenharmony_ci if (hci_dev_test_flag(hdev, HCI_LE_ENABLED)) { 19038c2ecf20Sopenharmony_ci struct hci_request req; 19048c2ecf20Sopenharmony_ci hci_req_init(&req, hdev); 19058c2ecf20Sopenharmony_ci if (ext_adv_capable(hdev)) { 19068c2ecf20Sopenharmony_ci int err; 19078c2ecf20Sopenharmony_ci 19088c2ecf20Sopenharmony_ci err = __hci_req_setup_ext_adv_instance(&req, 0x00); 19098c2ecf20Sopenharmony_ci if (!err) 19108c2ecf20Sopenharmony_ci __hci_req_update_scan_rsp_data(&req, 0x00); 19118c2ecf20Sopenharmony_ci } else { 19128c2ecf20Sopenharmony_ci __hci_req_update_adv_data(&req, 0x00); 19138c2ecf20Sopenharmony_ci __hci_req_update_scan_rsp_data(&req, 0x00); 19148c2ecf20Sopenharmony_ci } 19158c2ecf20Sopenharmony_ci hci_req_run(&req, NULL); 19168c2ecf20Sopenharmony_ci hci_update_background_scan(hdev); 19178c2ecf20Sopenharmony_ci } 19188c2ecf20Sopenharmony_ci 19198c2ecf20Sopenharmony_ciunlock: 19208c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 19218c2ecf20Sopenharmony_ci} 19228c2ecf20Sopenharmony_ci 19238c2ecf20Sopenharmony_cistatic int set_le(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) 19248c2ecf20Sopenharmony_ci{ 19258c2ecf20Sopenharmony_ci struct mgmt_mode *cp = data; 19268c2ecf20Sopenharmony_ci struct hci_cp_write_le_host_supported hci_cp; 19278c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 19288c2ecf20Sopenharmony_ci struct hci_request req; 19298c2ecf20Sopenharmony_ci int err; 19308c2ecf20Sopenharmony_ci u8 val, enabled; 19318c2ecf20Sopenharmony_ci 19328c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 19338c2ecf20Sopenharmony_ci 19348c2ecf20Sopenharmony_ci if (!lmp_le_capable(hdev)) 19358c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_LE, 19368c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_SUPPORTED); 19378c2ecf20Sopenharmony_ci 19388c2ecf20Sopenharmony_ci if (cp->val != 0x00 && cp->val != 0x01) 19398c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_LE, 19408c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 19418c2ecf20Sopenharmony_ci 19428c2ecf20Sopenharmony_ci /* Bluetooth single mode LE only controllers or dual-mode 19438c2ecf20Sopenharmony_ci * controllers configured as LE only devices, do not allow 19448c2ecf20Sopenharmony_ci * switching LE off. These have either LE enabled explicitly 19458c2ecf20Sopenharmony_ci * or BR/EDR has been previously switched off. 19468c2ecf20Sopenharmony_ci * 19478c2ecf20Sopenharmony_ci * When trying to enable an already enabled LE, then gracefully 19488c2ecf20Sopenharmony_ci * send a positive response. Trying to disable it however will 19498c2ecf20Sopenharmony_ci * result into rejection. 19508c2ecf20Sopenharmony_ci */ 19518c2ecf20Sopenharmony_ci if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) { 19528c2ecf20Sopenharmony_ci if (cp->val == 0x01) 19538c2ecf20Sopenharmony_ci return send_settings_rsp(sk, MGMT_OP_SET_LE, hdev); 19548c2ecf20Sopenharmony_ci 19558c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_LE, 19568c2ecf20Sopenharmony_ci MGMT_STATUS_REJECTED); 19578c2ecf20Sopenharmony_ci } 19588c2ecf20Sopenharmony_ci 19598c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 19608c2ecf20Sopenharmony_ci 19618c2ecf20Sopenharmony_ci val = !!cp->val; 19628c2ecf20Sopenharmony_ci enabled = lmp_host_le_capable(hdev); 19638c2ecf20Sopenharmony_ci 19648c2ecf20Sopenharmony_ci if (!val) 19658c2ecf20Sopenharmony_ci hci_req_clear_adv_instance(hdev, NULL, NULL, 0x00, true); 19668c2ecf20Sopenharmony_ci 19678c2ecf20Sopenharmony_ci if (!hdev_is_powered(hdev) || val == enabled) { 19688c2ecf20Sopenharmony_ci bool changed = false; 19698c2ecf20Sopenharmony_ci 19708c2ecf20Sopenharmony_ci if (val != hci_dev_test_flag(hdev, HCI_LE_ENABLED)) { 19718c2ecf20Sopenharmony_ci hci_dev_change_flag(hdev, HCI_LE_ENABLED); 19728c2ecf20Sopenharmony_ci changed = true; 19738c2ecf20Sopenharmony_ci } 19748c2ecf20Sopenharmony_ci 19758c2ecf20Sopenharmony_ci if (!val && hci_dev_test_flag(hdev, HCI_ADVERTISING)) { 19768c2ecf20Sopenharmony_ci hci_dev_clear_flag(hdev, HCI_ADVERTISING); 19778c2ecf20Sopenharmony_ci changed = true; 19788c2ecf20Sopenharmony_ci } 19798c2ecf20Sopenharmony_ci 19808c2ecf20Sopenharmony_ci err = send_settings_rsp(sk, MGMT_OP_SET_LE, hdev); 19818c2ecf20Sopenharmony_ci if (err < 0) 19828c2ecf20Sopenharmony_ci goto unlock; 19838c2ecf20Sopenharmony_ci 19848c2ecf20Sopenharmony_ci if (changed) 19858c2ecf20Sopenharmony_ci err = new_settings(hdev, sk); 19868c2ecf20Sopenharmony_ci 19878c2ecf20Sopenharmony_ci goto unlock; 19888c2ecf20Sopenharmony_ci } 19898c2ecf20Sopenharmony_ci 19908c2ecf20Sopenharmony_ci if (pending_find(MGMT_OP_SET_LE, hdev) || 19918c2ecf20Sopenharmony_ci pending_find(MGMT_OP_SET_ADVERTISING, hdev)) { 19928c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_LE, 19938c2ecf20Sopenharmony_ci MGMT_STATUS_BUSY); 19948c2ecf20Sopenharmony_ci goto unlock; 19958c2ecf20Sopenharmony_ci } 19968c2ecf20Sopenharmony_ci 19978c2ecf20Sopenharmony_ci cmd = mgmt_pending_add(sk, MGMT_OP_SET_LE, hdev, data, len); 19988c2ecf20Sopenharmony_ci if (!cmd) { 19998c2ecf20Sopenharmony_ci err = -ENOMEM; 20008c2ecf20Sopenharmony_ci goto unlock; 20018c2ecf20Sopenharmony_ci } 20028c2ecf20Sopenharmony_ci 20038c2ecf20Sopenharmony_ci hci_req_init(&req, hdev); 20048c2ecf20Sopenharmony_ci 20058c2ecf20Sopenharmony_ci memset(&hci_cp, 0, sizeof(hci_cp)); 20068c2ecf20Sopenharmony_ci 20078c2ecf20Sopenharmony_ci if (val) { 20088c2ecf20Sopenharmony_ci hci_cp.le = val; 20098c2ecf20Sopenharmony_ci hci_cp.simul = 0x00; 20108c2ecf20Sopenharmony_ci } else { 20118c2ecf20Sopenharmony_ci if (hci_dev_test_flag(hdev, HCI_LE_ADV)) 20128c2ecf20Sopenharmony_ci __hci_req_disable_advertising(&req); 20138c2ecf20Sopenharmony_ci 20148c2ecf20Sopenharmony_ci if (ext_adv_capable(hdev)) 20158c2ecf20Sopenharmony_ci __hci_req_clear_ext_adv_sets(&req); 20168c2ecf20Sopenharmony_ci } 20178c2ecf20Sopenharmony_ci 20188c2ecf20Sopenharmony_ci hci_req_add(&req, HCI_OP_WRITE_LE_HOST_SUPPORTED, sizeof(hci_cp), 20198c2ecf20Sopenharmony_ci &hci_cp); 20208c2ecf20Sopenharmony_ci 20218c2ecf20Sopenharmony_ci err = hci_req_run(&req, le_enable_complete); 20228c2ecf20Sopenharmony_ci if (err < 0) 20238c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 20248c2ecf20Sopenharmony_ci 20258c2ecf20Sopenharmony_ciunlock: 20268c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 20278c2ecf20Sopenharmony_ci return err; 20288c2ecf20Sopenharmony_ci} 20298c2ecf20Sopenharmony_ci 20308c2ecf20Sopenharmony_ci/* This is a helper function to test for pending mgmt commands that can 20318c2ecf20Sopenharmony_ci * cause CoD or EIR HCI commands. We can only allow one such pending 20328c2ecf20Sopenharmony_ci * mgmt command at a time since otherwise we cannot easily track what 20338c2ecf20Sopenharmony_ci * the current values are, will be, and based on that calculate if a new 20348c2ecf20Sopenharmony_ci * HCI command needs to be sent and if yes with what value. 20358c2ecf20Sopenharmony_ci */ 20368c2ecf20Sopenharmony_cistatic bool pending_eir_or_class(struct hci_dev *hdev) 20378c2ecf20Sopenharmony_ci{ 20388c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 20398c2ecf20Sopenharmony_ci 20408c2ecf20Sopenharmony_ci list_for_each_entry(cmd, &hdev->mgmt_pending, list) { 20418c2ecf20Sopenharmony_ci switch (cmd->opcode) { 20428c2ecf20Sopenharmony_ci case MGMT_OP_ADD_UUID: 20438c2ecf20Sopenharmony_ci case MGMT_OP_REMOVE_UUID: 20448c2ecf20Sopenharmony_ci case MGMT_OP_SET_DEV_CLASS: 20458c2ecf20Sopenharmony_ci case MGMT_OP_SET_POWERED: 20468c2ecf20Sopenharmony_ci return true; 20478c2ecf20Sopenharmony_ci } 20488c2ecf20Sopenharmony_ci } 20498c2ecf20Sopenharmony_ci 20508c2ecf20Sopenharmony_ci return false; 20518c2ecf20Sopenharmony_ci} 20528c2ecf20Sopenharmony_ci 20538c2ecf20Sopenharmony_cistatic const u8 bluetooth_base_uuid[] = { 20548c2ecf20Sopenharmony_ci 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 20558c2ecf20Sopenharmony_ci 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 20568c2ecf20Sopenharmony_ci}; 20578c2ecf20Sopenharmony_ci 20588c2ecf20Sopenharmony_cistatic u8 get_uuid_size(const u8 *uuid) 20598c2ecf20Sopenharmony_ci{ 20608c2ecf20Sopenharmony_ci u32 val; 20618c2ecf20Sopenharmony_ci 20628c2ecf20Sopenharmony_ci if (memcmp(uuid, bluetooth_base_uuid, 12)) 20638c2ecf20Sopenharmony_ci return 128; 20648c2ecf20Sopenharmony_ci 20658c2ecf20Sopenharmony_ci val = get_unaligned_le32(&uuid[12]); 20668c2ecf20Sopenharmony_ci if (val > 0xffff) 20678c2ecf20Sopenharmony_ci return 32; 20688c2ecf20Sopenharmony_ci 20698c2ecf20Sopenharmony_ci return 16; 20708c2ecf20Sopenharmony_ci} 20718c2ecf20Sopenharmony_ci 20728c2ecf20Sopenharmony_cistatic void mgmt_class_complete(struct hci_dev *hdev, u16 mgmt_op, u8 status) 20738c2ecf20Sopenharmony_ci{ 20748c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 20758c2ecf20Sopenharmony_ci 20768c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 20778c2ecf20Sopenharmony_ci 20788c2ecf20Sopenharmony_ci cmd = pending_find(mgmt_op, hdev); 20798c2ecf20Sopenharmony_ci if (!cmd) 20808c2ecf20Sopenharmony_ci goto unlock; 20818c2ecf20Sopenharmony_ci 20828c2ecf20Sopenharmony_ci mgmt_cmd_complete(cmd->sk, cmd->index, cmd->opcode, 20838c2ecf20Sopenharmony_ci mgmt_status(status), hdev->dev_class, 3); 20848c2ecf20Sopenharmony_ci 20858c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 20868c2ecf20Sopenharmony_ci 20878c2ecf20Sopenharmony_ciunlock: 20888c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 20898c2ecf20Sopenharmony_ci} 20908c2ecf20Sopenharmony_ci 20918c2ecf20Sopenharmony_cistatic void add_uuid_complete(struct hci_dev *hdev, u8 status, u16 opcode) 20928c2ecf20Sopenharmony_ci{ 20938c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "status 0x%02x", status); 20948c2ecf20Sopenharmony_ci 20958c2ecf20Sopenharmony_ci mgmt_class_complete(hdev, MGMT_OP_ADD_UUID, status); 20968c2ecf20Sopenharmony_ci} 20978c2ecf20Sopenharmony_ci 20988c2ecf20Sopenharmony_cistatic int add_uuid(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) 20998c2ecf20Sopenharmony_ci{ 21008c2ecf20Sopenharmony_ci struct mgmt_cp_add_uuid *cp = data; 21018c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 21028c2ecf20Sopenharmony_ci struct hci_request req; 21038c2ecf20Sopenharmony_ci struct bt_uuid *uuid; 21048c2ecf20Sopenharmony_ci int err; 21058c2ecf20Sopenharmony_ci 21068c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 21078c2ecf20Sopenharmony_ci 21088c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 21098c2ecf20Sopenharmony_ci 21108c2ecf20Sopenharmony_ci if (pending_eir_or_class(hdev)) { 21118c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_UUID, 21128c2ecf20Sopenharmony_ci MGMT_STATUS_BUSY); 21138c2ecf20Sopenharmony_ci goto failed; 21148c2ecf20Sopenharmony_ci } 21158c2ecf20Sopenharmony_ci 21168c2ecf20Sopenharmony_ci uuid = kmalloc(sizeof(*uuid), GFP_KERNEL); 21178c2ecf20Sopenharmony_ci if (!uuid) { 21188c2ecf20Sopenharmony_ci err = -ENOMEM; 21198c2ecf20Sopenharmony_ci goto failed; 21208c2ecf20Sopenharmony_ci } 21218c2ecf20Sopenharmony_ci 21228c2ecf20Sopenharmony_ci memcpy(uuid->uuid, cp->uuid, 16); 21238c2ecf20Sopenharmony_ci uuid->svc_hint = cp->svc_hint; 21248c2ecf20Sopenharmony_ci uuid->size = get_uuid_size(cp->uuid); 21258c2ecf20Sopenharmony_ci 21268c2ecf20Sopenharmony_ci list_add_tail(&uuid->list, &hdev->uuids); 21278c2ecf20Sopenharmony_ci 21288c2ecf20Sopenharmony_ci hci_req_init(&req, hdev); 21298c2ecf20Sopenharmony_ci 21308c2ecf20Sopenharmony_ci __hci_req_update_class(&req); 21318c2ecf20Sopenharmony_ci __hci_req_update_eir(&req); 21328c2ecf20Sopenharmony_ci 21338c2ecf20Sopenharmony_ci err = hci_req_run(&req, add_uuid_complete); 21348c2ecf20Sopenharmony_ci if (err < 0) { 21358c2ecf20Sopenharmony_ci if (err != -ENODATA) 21368c2ecf20Sopenharmony_ci goto failed; 21378c2ecf20Sopenharmony_ci 21388c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_ADD_UUID, 0, 21398c2ecf20Sopenharmony_ci hdev->dev_class, 3); 21408c2ecf20Sopenharmony_ci goto failed; 21418c2ecf20Sopenharmony_ci } 21428c2ecf20Sopenharmony_ci 21438c2ecf20Sopenharmony_ci cmd = mgmt_pending_add(sk, MGMT_OP_ADD_UUID, hdev, data, len); 21448c2ecf20Sopenharmony_ci if (!cmd) { 21458c2ecf20Sopenharmony_ci err = -ENOMEM; 21468c2ecf20Sopenharmony_ci goto failed; 21478c2ecf20Sopenharmony_ci } 21488c2ecf20Sopenharmony_ci 21498c2ecf20Sopenharmony_ci err = 0; 21508c2ecf20Sopenharmony_ci 21518c2ecf20Sopenharmony_cifailed: 21528c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 21538c2ecf20Sopenharmony_ci return err; 21548c2ecf20Sopenharmony_ci} 21558c2ecf20Sopenharmony_ci 21568c2ecf20Sopenharmony_cistatic bool enable_service_cache(struct hci_dev *hdev) 21578c2ecf20Sopenharmony_ci{ 21588c2ecf20Sopenharmony_ci if (!hdev_is_powered(hdev)) 21598c2ecf20Sopenharmony_ci return false; 21608c2ecf20Sopenharmony_ci 21618c2ecf20Sopenharmony_ci if (!hci_dev_test_and_set_flag(hdev, HCI_SERVICE_CACHE)) { 21628c2ecf20Sopenharmony_ci queue_delayed_work(hdev->workqueue, &hdev->service_cache, 21638c2ecf20Sopenharmony_ci CACHE_TIMEOUT); 21648c2ecf20Sopenharmony_ci return true; 21658c2ecf20Sopenharmony_ci } 21668c2ecf20Sopenharmony_ci 21678c2ecf20Sopenharmony_ci return false; 21688c2ecf20Sopenharmony_ci} 21698c2ecf20Sopenharmony_ci 21708c2ecf20Sopenharmony_cistatic void remove_uuid_complete(struct hci_dev *hdev, u8 status, u16 opcode) 21718c2ecf20Sopenharmony_ci{ 21728c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "status 0x%02x", status); 21738c2ecf20Sopenharmony_ci 21748c2ecf20Sopenharmony_ci mgmt_class_complete(hdev, MGMT_OP_REMOVE_UUID, status); 21758c2ecf20Sopenharmony_ci} 21768c2ecf20Sopenharmony_ci 21778c2ecf20Sopenharmony_cistatic int remove_uuid(struct sock *sk, struct hci_dev *hdev, void *data, 21788c2ecf20Sopenharmony_ci u16 len) 21798c2ecf20Sopenharmony_ci{ 21808c2ecf20Sopenharmony_ci struct mgmt_cp_remove_uuid *cp = data; 21818c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 21828c2ecf20Sopenharmony_ci struct bt_uuid *match, *tmp; 21838c2ecf20Sopenharmony_ci u8 bt_uuid_any[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; 21848c2ecf20Sopenharmony_ci struct hci_request req; 21858c2ecf20Sopenharmony_ci int err, found; 21868c2ecf20Sopenharmony_ci 21878c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 21888c2ecf20Sopenharmony_ci 21898c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 21908c2ecf20Sopenharmony_ci 21918c2ecf20Sopenharmony_ci if (pending_eir_or_class(hdev)) { 21928c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_REMOVE_UUID, 21938c2ecf20Sopenharmony_ci MGMT_STATUS_BUSY); 21948c2ecf20Sopenharmony_ci goto unlock; 21958c2ecf20Sopenharmony_ci } 21968c2ecf20Sopenharmony_ci 21978c2ecf20Sopenharmony_ci if (memcmp(cp->uuid, bt_uuid_any, 16) == 0) { 21988c2ecf20Sopenharmony_ci hci_uuids_clear(hdev); 21998c2ecf20Sopenharmony_ci 22008c2ecf20Sopenharmony_ci if (enable_service_cache(hdev)) { 22018c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, 22028c2ecf20Sopenharmony_ci MGMT_OP_REMOVE_UUID, 22038c2ecf20Sopenharmony_ci 0, hdev->dev_class, 3); 22048c2ecf20Sopenharmony_ci goto unlock; 22058c2ecf20Sopenharmony_ci } 22068c2ecf20Sopenharmony_ci 22078c2ecf20Sopenharmony_ci goto update_class; 22088c2ecf20Sopenharmony_ci } 22098c2ecf20Sopenharmony_ci 22108c2ecf20Sopenharmony_ci found = 0; 22118c2ecf20Sopenharmony_ci 22128c2ecf20Sopenharmony_ci list_for_each_entry_safe(match, tmp, &hdev->uuids, list) { 22138c2ecf20Sopenharmony_ci if (memcmp(match->uuid, cp->uuid, 16) != 0) 22148c2ecf20Sopenharmony_ci continue; 22158c2ecf20Sopenharmony_ci 22168c2ecf20Sopenharmony_ci list_del(&match->list); 22178c2ecf20Sopenharmony_ci kfree(match); 22188c2ecf20Sopenharmony_ci found++; 22198c2ecf20Sopenharmony_ci } 22208c2ecf20Sopenharmony_ci 22218c2ecf20Sopenharmony_ci if (found == 0) { 22228c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_REMOVE_UUID, 22238c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 22248c2ecf20Sopenharmony_ci goto unlock; 22258c2ecf20Sopenharmony_ci } 22268c2ecf20Sopenharmony_ci 22278c2ecf20Sopenharmony_ciupdate_class: 22288c2ecf20Sopenharmony_ci hci_req_init(&req, hdev); 22298c2ecf20Sopenharmony_ci 22308c2ecf20Sopenharmony_ci __hci_req_update_class(&req); 22318c2ecf20Sopenharmony_ci __hci_req_update_eir(&req); 22328c2ecf20Sopenharmony_ci 22338c2ecf20Sopenharmony_ci err = hci_req_run(&req, remove_uuid_complete); 22348c2ecf20Sopenharmony_ci if (err < 0) { 22358c2ecf20Sopenharmony_ci if (err != -ENODATA) 22368c2ecf20Sopenharmony_ci goto unlock; 22378c2ecf20Sopenharmony_ci 22388c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_UUID, 0, 22398c2ecf20Sopenharmony_ci hdev->dev_class, 3); 22408c2ecf20Sopenharmony_ci goto unlock; 22418c2ecf20Sopenharmony_ci } 22428c2ecf20Sopenharmony_ci 22438c2ecf20Sopenharmony_ci cmd = mgmt_pending_add(sk, MGMT_OP_REMOVE_UUID, hdev, data, len); 22448c2ecf20Sopenharmony_ci if (!cmd) { 22458c2ecf20Sopenharmony_ci err = -ENOMEM; 22468c2ecf20Sopenharmony_ci goto unlock; 22478c2ecf20Sopenharmony_ci } 22488c2ecf20Sopenharmony_ci 22498c2ecf20Sopenharmony_ci err = 0; 22508c2ecf20Sopenharmony_ci 22518c2ecf20Sopenharmony_ciunlock: 22528c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 22538c2ecf20Sopenharmony_ci return err; 22548c2ecf20Sopenharmony_ci} 22558c2ecf20Sopenharmony_ci 22568c2ecf20Sopenharmony_cistatic void set_class_complete(struct hci_dev *hdev, u8 status, u16 opcode) 22578c2ecf20Sopenharmony_ci{ 22588c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "status 0x%02x", status); 22598c2ecf20Sopenharmony_ci 22608c2ecf20Sopenharmony_ci mgmt_class_complete(hdev, MGMT_OP_SET_DEV_CLASS, status); 22618c2ecf20Sopenharmony_ci} 22628c2ecf20Sopenharmony_ci 22638c2ecf20Sopenharmony_cistatic int set_dev_class(struct sock *sk, struct hci_dev *hdev, void *data, 22648c2ecf20Sopenharmony_ci u16 len) 22658c2ecf20Sopenharmony_ci{ 22668c2ecf20Sopenharmony_ci struct mgmt_cp_set_dev_class *cp = data; 22678c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 22688c2ecf20Sopenharmony_ci struct hci_request req; 22698c2ecf20Sopenharmony_ci int err; 22708c2ecf20Sopenharmony_ci 22718c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 22728c2ecf20Sopenharmony_ci 22738c2ecf20Sopenharmony_ci if (!lmp_bredr_capable(hdev)) 22748c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, 22758c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_SUPPORTED); 22768c2ecf20Sopenharmony_ci 22778c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 22788c2ecf20Sopenharmony_ci 22798c2ecf20Sopenharmony_ci if (pending_eir_or_class(hdev)) { 22808c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, 22818c2ecf20Sopenharmony_ci MGMT_STATUS_BUSY); 22828c2ecf20Sopenharmony_ci goto unlock; 22838c2ecf20Sopenharmony_ci } 22848c2ecf20Sopenharmony_ci 22858c2ecf20Sopenharmony_ci if ((cp->minor & 0x03) != 0 || (cp->major & 0xe0) != 0) { 22868c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, 22878c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 22888c2ecf20Sopenharmony_ci goto unlock; 22898c2ecf20Sopenharmony_ci } 22908c2ecf20Sopenharmony_ci 22918c2ecf20Sopenharmony_ci hdev->major_class = cp->major; 22928c2ecf20Sopenharmony_ci hdev->minor_class = cp->minor; 22938c2ecf20Sopenharmony_ci 22948c2ecf20Sopenharmony_ci if (!hdev_is_powered(hdev)) { 22958c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, 0, 22968c2ecf20Sopenharmony_ci hdev->dev_class, 3); 22978c2ecf20Sopenharmony_ci goto unlock; 22988c2ecf20Sopenharmony_ci } 22998c2ecf20Sopenharmony_ci 23008c2ecf20Sopenharmony_ci hci_req_init(&req, hdev); 23018c2ecf20Sopenharmony_ci 23028c2ecf20Sopenharmony_ci if (hci_dev_test_and_clear_flag(hdev, HCI_SERVICE_CACHE)) { 23038c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 23048c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&hdev->service_cache); 23058c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 23068c2ecf20Sopenharmony_ci __hci_req_update_eir(&req); 23078c2ecf20Sopenharmony_ci } 23088c2ecf20Sopenharmony_ci 23098c2ecf20Sopenharmony_ci __hci_req_update_class(&req); 23108c2ecf20Sopenharmony_ci 23118c2ecf20Sopenharmony_ci err = hci_req_run(&req, set_class_complete); 23128c2ecf20Sopenharmony_ci if (err < 0) { 23138c2ecf20Sopenharmony_ci if (err != -ENODATA) 23148c2ecf20Sopenharmony_ci goto unlock; 23158c2ecf20Sopenharmony_ci 23168c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_DEV_CLASS, 0, 23178c2ecf20Sopenharmony_ci hdev->dev_class, 3); 23188c2ecf20Sopenharmony_ci goto unlock; 23198c2ecf20Sopenharmony_ci } 23208c2ecf20Sopenharmony_ci 23218c2ecf20Sopenharmony_ci cmd = mgmt_pending_add(sk, MGMT_OP_SET_DEV_CLASS, hdev, data, len); 23228c2ecf20Sopenharmony_ci if (!cmd) { 23238c2ecf20Sopenharmony_ci err = -ENOMEM; 23248c2ecf20Sopenharmony_ci goto unlock; 23258c2ecf20Sopenharmony_ci } 23268c2ecf20Sopenharmony_ci 23278c2ecf20Sopenharmony_ci err = 0; 23288c2ecf20Sopenharmony_ci 23298c2ecf20Sopenharmony_ciunlock: 23308c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 23318c2ecf20Sopenharmony_ci return err; 23328c2ecf20Sopenharmony_ci} 23338c2ecf20Sopenharmony_ci 23348c2ecf20Sopenharmony_cistatic int load_link_keys(struct sock *sk, struct hci_dev *hdev, void *data, 23358c2ecf20Sopenharmony_ci u16 len) 23368c2ecf20Sopenharmony_ci{ 23378c2ecf20Sopenharmony_ci struct mgmt_cp_load_link_keys *cp = data; 23388c2ecf20Sopenharmony_ci const u16 max_key_count = ((U16_MAX - sizeof(*cp)) / 23398c2ecf20Sopenharmony_ci sizeof(struct mgmt_link_key_info)); 23408c2ecf20Sopenharmony_ci u16 key_count, expected_len; 23418c2ecf20Sopenharmony_ci bool changed; 23428c2ecf20Sopenharmony_ci int i; 23438c2ecf20Sopenharmony_ci 23448c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 23458c2ecf20Sopenharmony_ci 23468c2ecf20Sopenharmony_ci if (!lmp_bredr_capable(hdev)) 23478c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS, 23488c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_SUPPORTED); 23498c2ecf20Sopenharmony_ci 23508c2ecf20Sopenharmony_ci key_count = __le16_to_cpu(cp->key_count); 23518c2ecf20Sopenharmony_ci if (key_count > max_key_count) { 23528c2ecf20Sopenharmony_ci bt_dev_err(hdev, "load_link_keys: too big key_count value %u", 23538c2ecf20Sopenharmony_ci key_count); 23548c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS, 23558c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 23568c2ecf20Sopenharmony_ci } 23578c2ecf20Sopenharmony_ci 23588c2ecf20Sopenharmony_ci expected_len = struct_size(cp, keys, key_count); 23598c2ecf20Sopenharmony_ci if (expected_len != len) { 23608c2ecf20Sopenharmony_ci bt_dev_err(hdev, "load_link_keys: expected %u bytes, got %u bytes", 23618c2ecf20Sopenharmony_ci expected_len, len); 23628c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS, 23638c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 23648c2ecf20Sopenharmony_ci } 23658c2ecf20Sopenharmony_ci 23668c2ecf20Sopenharmony_ci if (cp->debug_keys != 0x00 && cp->debug_keys != 0x01) 23678c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS, 23688c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 23698c2ecf20Sopenharmony_ci 23708c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "debug_keys %u key_count %u", cp->debug_keys, 23718c2ecf20Sopenharmony_ci key_count); 23728c2ecf20Sopenharmony_ci 23738c2ecf20Sopenharmony_ci for (i = 0; i < key_count; i++) { 23748c2ecf20Sopenharmony_ci struct mgmt_link_key_info *key = &cp->keys[i]; 23758c2ecf20Sopenharmony_ci 23768c2ecf20Sopenharmony_ci /* Considering SMP over BREDR/LE, there is no need to check addr_type */ 23778c2ecf20Sopenharmony_ci if (key->type > 0x08) 23788c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, 23798c2ecf20Sopenharmony_ci MGMT_OP_LOAD_LINK_KEYS, 23808c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 23818c2ecf20Sopenharmony_ci } 23828c2ecf20Sopenharmony_ci 23838c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 23848c2ecf20Sopenharmony_ci 23858c2ecf20Sopenharmony_ci hci_link_keys_clear(hdev); 23868c2ecf20Sopenharmony_ci 23878c2ecf20Sopenharmony_ci if (cp->debug_keys) 23888c2ecf20Sopenharmony_ci changed = !hci_dev_test_and_set_flag(hdev, HCI_KEEP_DEBUG_KEYS); 23898c2ecf20Sopenharmony_ci else 23908c2ecf20Sopenharmony_ci changed = hci_dev_test_and_clear_flag(hdev, 23918c2ecf20Sopenharmony_ci HCI_KEEP_DEBUG_KEYS); 23928c2ecf20Sopenharmony_ci 23938c2ecf20Sopenharmony_ci if (changed) 23948c2ecf20Sopenharmony_ci new_settings(hdev, NULL); 23958c2ecf20Sopenharmony_ci 23968c2ecf20Sopenharmony_ci for (i = 0; i < key_count; i++) { 23978c2ecf20Sopenharmony_ci struct mgmt_link_key_info *key = &cp->keys[i]; 23988c2ecf20Sopenharmony_ci 23998c2ecf20Sopenharmony_ci if (hci_is_blocked_key(hdev, 24008c2ecf20Sopenharmony_ci HCI_BLOCKED_KEY_TYPE_LINKKEY, 24018c2ecf20Sopenharmony_ci key->val)) { 24028c2ecf20Sopenharmony_ci bt_dev_warn(hdev, "Skipping blocked link key for %pMR", 24038c2ecf20Sopenharmony_ci &key->addr.bdaddr); 24048c2ecf20Sopenharmony_ci continue; 24058c2ecf20Sopenharmony_ci } 24068c2ecf20Sopenharmony_ci 24078c2ecf20Sopenharmony_ci /* Always ignore debug keys and require a new pairing if 24088c2ecf20Sopenharmony_ci * the user wants to use them. 24098c2ecf20Sopenharmony_ci */ 24108c2ecf20Sopenharmony_ci if (key->type == HCI_LK_DEBUG_COMBINATION) 24118c2ecf20Sopenharmony_ci continue; 24128c2ecf20Sopenharmony_ci 24138c2ecf20Sopenharmony_ci hci_add_link_key(hdev, NULL, &key->addr.bdaddr, key->val, 24148c2ecf20Sopenharmony_ci key->type, key->pin_len, NULL); 24158c2ecf20Sopenharmony_ci } 24168c2ecf20Sopenharmony_ci 24178c2ecf20Sopenharmony_ci mgmt_cmd_complete(sk, hdev->id, MGMT_OP_LOAD_LINK_KEYS, 0, NULL, 0); 24188c2ecf20Sopenharmony_ci 24198c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 24208c2ecf20Sopenharmony_ci 24218c2ecf20Sopenharmony_ci return 0; 24228c2ecf20Sopenharmony_ci} 24238c2ecf20Sopenharmony_ci 24248c2ecf20Sopenharmony_cistatic int device_unpaired(struct hci_dev *hdev, bdaddr_t *bdaddr, 24258c2ecf20Sopenharmony_ci u8 addr_type, struct sock *skip_sk) 24268c2ecf20Sopenharmony_ci{ 24278c2ecf20Sopenharmony_ci struct mgmt_ev_device_unpaired ev; 24288c2ecf20Sopenharmony_ci 24298c2ecf20Sopenharmony_ci bacpy(&ev.addr.bdaddr, bdaddr); 24308c2ecf20Sopenharmony_ci ev.addr.type = addr_type; 24318c2ecf20Sopenharmony_ci 24328c2ecf20Sopenharmony_ci return mgmt_event(MGMT_EV_DEVICE_UNPAIRED, hdev, &ev, sizeof(ev), 24338c2ecf20Sopenharmony_ci skip_sk); 24348c2ecf20Sopenharmony_ci} 24358c2ecf20Sopenharmony_ci 24368c2ecf20Sopenharmony_cistatic int unpair_device(struct sock *sk, struct hci_dev *hdev, void *data, 24378c2ecf20Sopenharmony_ci u16 len) 24388c2ecf20Sopenharmony_ci{ 24398c2ecf20Sopenharmony_ci struct mgmt_cp_unpair_device *cp = data; 24408c2ecf20Sopenharmony_ci struct mgmt_rp_unpair_device rp; 24418c2ecf20Sopenharmony_ci struct hci_conn_params *params; 24428c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 24438c2ecf20Sopenharmony_ci struct hci_conn *conn; 24448c2ecf20Sopenharmony_ci u8 addr_type; 24458c2ecf20Sopenharmony_ci int err; 24468c2ecf20Sopenharmony_ci 24478c2ecf20Sopenharmony_ci memset(&rp, 0, sizeof(rp)); 24488c2ecf20Sopenharmony_ci bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); 24498c2ecf20Sopenharmony_ci rp.addr.type = cp->addr.type; 24508c2ecf20Sopenharmony_ci 24518c2ecf20Sopenharmony_ci if (!bdaddr_type_is_valid(cp->addr.type)) 24528c2ecf20Sopenharmony_ci return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE, 24538c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS, 24548c2ecf20Sopenharmony_ci &rp, sizeof(rp)); 24558c2ecf20Sopenharmony_ci 24568c2ecf20Sopenharmony_ci if (cp->disconnect != 0x00 && cp->disconnect != 0x01) 24578c2ecf20Sopenharmony_ci return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE, 24588c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS, 24598c2ecf20Sopenharmony_ci &rp, sizeof(rp)); 24608c2ecf20Sopenharmony_ci 24618c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 24628c2ecf20Sopenharmony_ci 24638c2ecf20Sopenharmony_ci if (!hdev_is_powered(hdev)) { 24648c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE, 24658c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_POWERED, &rp, 24668c2ecf20Sopenharmony_ci sizeof(rp)); 24678c2ecf20Sopenharmony_ci goto unlock; 24688c2ecf20Sopenharmony_ci } 24698c2ecf20Sopenharmony_ci 24708c2ecf20Sopenharmony_ci if (cp->addr.type == BDADDR_BREDR) { 24718c2ecf20Sopenharmony_ci /* If disconnection is requested, then look up the 24728c2ecf20Sopenharmony_ci * connection. If the remote device is connected, it 24738c2ecf20Sopenharmony_ci * will be later used to terminate the link. 24748c2ecf20Sopenharmony_ci * 24758c2ecf20Sopenharmony_ci * Setting it to NULL explicitly will cause no 24768c2ecf20Sopenharmony_ci * termination of the link. 24778c2ecf20Sopenharmony_ci */ 24788c2ecf20Sopenharmony_ci if (cp->disconnect) 24798c2ecf20Sopenharmony_ci conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, 24808c2ecf20Sopenharmony_ci &cp->addr.bdaddr); 24818c2ecf20Sopenharmony_ci else 24828c2ecf20Sopenharmony_ci conn = NULL; 24838c2ecf20Sopenharmony_ci 24848c2ecf20Sopenharmony_ci err = hci_remove_link_key(hdev, &cp->addr.bdaddr); 24858c2ecf20Sopenharmony_ci if (err < 0) { 24868c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, 24878c2ecf20Sopenharmony_ci MGMT_OP_UNPAIR_DEVICE, 24888c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_PAIRED, &rp, 24898c2ecf20Sopenharmony_ci sizeof(rp)); 24908c2ecf20Sopenharmony_ci goto unlock; 24918c2ecf20Sopenharmony_ci } 24928c2ecf20Sopenharmony_ci 24938c2ecf20Sopenharmony_ci goto done; 24948c2ecf20Sopenharmony_ci } 24958c2ecf20Sopenharmony_ci 24968c2ecf20Sopenharmony_ci /* LE address type */ 24978c2ecf20Sopenharmony_ci addr_type = le_addr_type(cp->addr.type); 24988c2ecf20Sopenharmony_ci 24998c2ecf20Sopenharmony_ci /* Abort any ongoing SMP pairing. Removes ltk and irk if they exist. */ 25008c2ecf20Sopenharmony_ci err = smp_cancel_and_remove_pairing(hdev, &cp->addr.bdaddr, addr_type); 25018c2ecf20Sopenharmony_ci if (err < 0) { 25028c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE, 25038c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_PAIRED, &rp, 25048c2ecf20Sopenharmony_ci sizeof(rp)); 25058c2ecf20Sopenharmony_ci goto unlock; 25068c2ecf20Sopenharmony_ci } 25078c2ecf20Sopenharmony_ci 25088c2ecf20Sopenharmony_ci conn = hci_conn_hash_lookup_le(hdev, &cp->addr.bdaddr, addr_type); 25098c2ecf20Sopenharmony_ci if (!conn) { 25108c2ecf20Sopenharmony_ci hci_conn_params_del(hdev, &cp->addr.bdaddr, addr_type); 25118c2ecf20Sopenharmony_ci goto done; 25128c2ecf20Sopenharmony_ci } 25138c2ecf20Sopenharmony_ci 25148c2ecf20Sopenharmony_ci 25158c2ecf20Sopenharmony_ci /* Defer clearing up the connection parameters until closing to 25168c2ecf20Sopenharmony_ci * give a chance of keeping them if a repairing happens. 25178c2ecf20Sopenharmony_ci */ 25188c2ecf20Sopenharmony_ci set_bit(HCI_CONN_PARAM_REMOVAL_PEND, &conn->flags); 25198c2ecf20Sopenharmony_ci 25208c2ecf20Sopenharmony_ci /* Disable auto-connection parameters if present */ 25218c2ecf20Sopenharmony_ci params = hci_conn_params_lookup(hdev, &cp->addr.bdaddr, addr_type); 25228c2ecf20Sopenharmony_ci if (params) { 25238c2ecf20Sopenharmony_ci if (params->explicit_connect) 25248c2ecf20Sopenharmony_ci params->auto_connect = HCI_AUTO_CONN_EXPLICIT; 25258c2ecf20Sopenharmony_ci else 25268c2ecf20Sopenharmony_ci params->auto_connect = HCI_AUTO_CONN_DISABLED; 25278c2ecf20Sopenharmony_ci } 25288c2ecf20Sopenharmony_ci 25298c2ecf20Sopenharmony_ci /* If disconnection is not requested, then clear the connection 25308c2ecf20Sopenharmony_ci * variable so that the link is not terminated. 25318c2ecf20Sopenharmony_ci */ 25328c2ecf20Sopenharmony_ci if (!cp->disconnect) 25338c2ecf20Sopenharmony_ci conn = NULL; 25348c2ecf20Sopenharmony_ci 25358c2ecf20Sopenharmony_cidone: 25368c2ecf20Sopenharmony_ci /* If the connection variable is set, then termination of the 25378c2ecf20Sopenharmony_ci * link is requested. 25388c2ecf20Sopenharmony_ci */ 25398c2ecf20Sopenharmony_ci if (!conn) { 25408c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_UNPAIR_DEVICE, 0, 25418c2ecf20Sopenharmony_ci &rp, sizeof(rp)); 25428c2ecf20Sopenharmony_ci device_unpaired(hdev, &cp->addr.bdaddr, cp->addr.type, sk); 25438c2ecf20Sopenharmony_ci goto unlock; 25448c2ecf20Sopenharmony_ci } 25458c2ecf20Sopenharmony_ci 25468c2ecf20Sopenharmony_ci cmd = mgmt_pending_add(sk, MGMT_OP_UNPAIR_DEVICE, hdev, cp, 25478c2ecf20Sopenharmony_ci sizeof(*cp)); 25488c2ecf20Sopenharmony_ci if (!cmd) { 25498c2ecf20Sopenharmony_ci err = -ENOMEM; 25508c2ecf20Sopenharmony_ci goto unlock; 25518c2ecf20Sopenharmony_ci } 25528c2ecf20Sopenharmony_ci 25538c2ecf20Sopenharmony_ci cmd->cmd_complete = addr_cmd_complete; 25548c2ecf20Sopenharmony_ci 25558c2ecf20Sopenharmony_ci err = hci_abort_conn(conn, HCI_ERROR_REMOTE_USER_TERM); 25568c2ecf20Sopenharmony_ci if (err < 0) 25578c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 25588c2ecf20Sopenharmony_ci 25598c2ecf20Sopenharmony_ciunlock: 25608c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 25618c2ecf20Sopenharmony_ci return err; 25628c2ecf20Sopenharmony_ci} 25638c2ecf20Sopenharmony_ci 25648c2ecf20Sopenharmony_cistatic int disconnect(struct sock *sk, struct hci_dev *hdev, void *data, 25658c2ecf20Sopenharmony_ci u16 len) 25668c2ecf20Sopenharmony_ci{ 25678c2ecf20Sopenharmony_ci struct mgmt_cp_disconnect *cp = data; 25688c2ecf20Sopenharmony_ci struct mgmt_rp_disconnect rp; 25698c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 25708c2ecf20Sopenharmony_ci struct hci_conn *conn; 25718c2ecf20Sopenharmony_ci int err; 25728c2ecf20Sopenharmony_ci 25738c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 25748c2ecf20Sopenharmony_ci 25758c2ecf20Sopenharmony_ci memset(&rp, 0, sizeof(rp)); 25768c2ecf20Sopenharmony_ci bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); 25778c2ecf20Sopenharmony_ci rp.addr.type = cp->addr.type; 25788c2ecf20Sopenharmony_ci 25798c2ecf20Sopenharmony_ci if (!bdaddr_type_is_valid(cp->addr.type)) 25808c2ecf20Sopenharmony_ci return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_DISCONNECT, 25818c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS, 25828c2ecf20Sopenharmony_ci &rp, sizeof(rp)); 25838c2ecf20Sopenharmony_ci 25848c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 25858c2ecf20Sopenharmony_ci 25868c2ecf20Sopenharmony_ci if (!test_bit(HCI_UP, &hdev->flags)) { 25878c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_DISCONNECT, 25888c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_POWERED, &rp, 25898c2ecf20Sopenharmony_ci sizeof(rp)); 25908c2ecf20Sopenharmony_ci goto failed; 25918c2ecf20Sopenharmony_ci } 25928c2ecf20Sopenharmony_ci 25938c2ecf20Sopenharmony_ci if (pending_find(MGMT_OP_DISCONNECT, hdev)) { 25948c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_DISCONNECT, 25958c2ecf20Sopenharmony_ci MGMT_STATUS_BUSY, &rp, sizeof(rp)); 25968c2ecf20Sopenharmony_ci goto failed; 25978c2ecf20Sopenharmony_ci } 25988c2ecf20Sopenharmony_ci 25998c2ecf20Sopenharmony_ci if (cp->addr.type == BDADDR_BREDR) 26008c2ecf20Sopenharmony_ci conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, 26018c2ecf20Sopenharmony_ci &cp->addr.bdaddr); 26028c2ecf20Sopenharmony_ci else 26038c2ecf20Sopenharmony_ci conn = hci_conn_hash_lookup_le(hdev, &cp->addr.bdaddr, 26048c2ecf20Sopenharmony_ci le_addr_type(cp->addr.type)); 26058c2ecf20Sopenharmony_ci 26068c2ecf20Sopenharmony_ci if (!conn || conn->state == BT_OPEN || conn->state == BT_CLOSED) { 26078c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_DISCONNECT, 26088c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_CONNECTED, &rp, 26098c2ecf20Sopenharmony_ci sizeof(rp)); 26108c2ecf20Sopenharmony_ci goto failed; 26118c2ecf20Sopenharmony_ci } 26128c2ecf20Sopenharmony_ci 26138c2ecf20Sopenharmony_ci cmd = mgmt_pending_add(sk, MGMT_OP_DISCONNECT, hdev, data, len); 26148c2ecf20Sopenharmony_ci if (!cmd) { 26158c2ecf20Sopenharmony_ci err = -ENOMEM; 26168c2ecf20Sopenharmony_ci goto failed; 26178c2ecf20Sopenharmony_ci } 26188c2ecf20Sopenharmony_ci 26198c2ecf20Sopenharmony_ci cmd->cmd_complete = generic_cmd_complete; 26208c2ecf20Sopenharmony_ci 26218c2ecf20Sopenharmony_ci err = hci_disconnect(conn, HCI_ERROR_REMOTE_USER_TERM); 26228c2ecf20Sopenharmony_ci if (err < 0) 26238c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 26248c2ecf20Sopenharmony_ci 26258c2ecf20Sopenharmony_cifailed: 26268c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 26278c2ecf20Sopenharmony_ci return err; 26288c2ecf20Sopenharmony_ci} 26298c2ecf20Sopenharmony_ci 26308c2ecf20Sopenharmony_cistatic u8 link_to_bdaddr(u8 link_type, u8 addr_type) 26318c2ecf20Sopenharmony_ci{ 26328c2ecf20Sopenharmony_ci switch (link_type) { 26338c2ecf20Sopenharmony_ci case LE_LINK: 26348c2ecf20Sopenharmony_ci switch (addr_type) { 26358c2ecf20Sopenharmony_ci case ADDR_LE_DEV_PUBLIC: 26368c2ecf20Sopenharmony_ci return BDADDR_LE_PUBLIC; 26378c2ecf20Sopenharmony_ci 26388c2ecf20Sopenharmony_ci default: 26398c2ecf20Sopenharmony_ci /* Fallback to LE Random address type */ 26408c2ecf20Sopenharmony_ci return BDADDR_LE_RANDOM; 26418c2ecf20Sopenharmony_ci } 26428c2ecf20Sopenharmony_ci 26438c2ecf20Sopenharmony_ci default: 26448c2ecf20Sopenharmony_ci /* Fallback to BR/EDR type */ 26458c2ecf20Sopenharmony_ci return BDADDR_BREDR; 26468c2ecf20Sopenharmony_ci } 26478c2ecf20Sopenharmony_ci} 26488c2ecf20Sopenharmony_ci 26498c2ecf20Sopenharmony_cistatic int get_connections(struct sock *sk, struct hci_dev *hdev, void *data, 26508c2ecf20Sopenharmony_ci u16 data_len) 26518c2ecf20Sopenharmony_ci{ 26528c2ecf20Sopenharmony_ci struct mgmt_rp_get_connections *rp; 26538c2ecf20Sopenharmony_ci struct hci_conn *c; 26548c2ecf20Sopenharmony_ci int err; 26558c2ecf20Sopenharmony_ci u16 i; 26568c2ecf20Sopenharmony_ci 26578c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 26588c2ecf20Sopenharmony_ci 26598c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 26608c2ecf20Sopenharmony_ci 26618c2ecf20Sopenharmony_ci if (!hdev_is_powered(hdev)) { 26628c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_GET_CONNECTIONS, 26638c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_POWERED); 26648c2ecf20Sopenharmony_ci goto unlock; 26658c2ecf20Sopenharmony_ci } 26668c2ecf20Sopenharmony_ci 26678c2ecf20Sopenharmony_ci i = 0; 26688c2ecf20Sopenharmony_ci list_for_each_entry(c, &hdev->conn_hash.list, list) { 26698c2ecf20Sopenharmony_ci if (test_bit(HCI_CONN_MGMT_CONNECTED, &c->flags)) 26708c2ecf20Sopenharmony_ci i++; 26718c2ecf20Sopenharmony_ci } 26728c2ecf20Sopenharmony_ci 26738c2ecf20Sopenharmony_ci rp = kmalloc(struct_size(rp, addr, i), GFP_KERNEL); 26748c2ecf20Sopenharmony_ci if (!rp) { 26758c2ecf20Sopenharmony_ci err = -ENOMEM; 26768c2ecf20Sopenharmony_ci goto unlock; 26778c2ecf20Sopenharmony_ci } 26788c2ecf20Sopenharmony_ci 26798c2ecf20Sopenharmony_ci i = 0; 26808c2ecf20Sopenharmony_ci list_for_each_entry(c, &hdev->conn_hash.list, list) { 26818c2ecf20Sopenharmony_ci if (!test_bit(HCI_CONN_MGMT_CONNECTED, &c->flags)) 26828c2ecf20Sopenharmony_ci continue; 26838c2ecf20Sopenharmony_ci bacpy(&rp->addr[i].bdaddr, &c->dst); 26848c2ecf20Sopenharmony_ci rp->addr[i].type = link_to_bdaddr(c->type, c->dst_type); 26858c2ecf20Sopenharmony_ci if (c->type == SCO_LINK || c->type == ESCO_LINK) 26868c2ecf20Sopenharmony_ci continue; 26878c2ecf20Sopenharmony_ci i++; 26888c2ecf20Sopenharmony_ci } 26898c2ecf20Sopenharmony_ci 26908c2ecf20Sopenharmony_ci rp->conn_count = cpu_to_le16(i); 26918c2ecf20Sopenharmony_ci 26928c2ecf20Sopenharmony_ci /* Recalculate length in case of filtered SCO connections, etc */ 26938c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_GET_CONNECTIONS, 0, rp, 26948c2ecf20Sopenharmony_ci struct_size(rp, addr, i)); 26958c2ecf20Sopenharmony_ci 26968c2ecf20Sopenharmony_ci kfree(rp); 26978c2ecf20Sopenharmony_ci 26988c2ecf20Sopenharmony_ciunlock: 26998c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 27008c2ecf20Sopenharmony_ci return err; 27018c2ecf20Sopenharmony_ci} 27028c2ecf20Sopenharmony_ci 27038c2ecf20Sopenharmony_cistatic int send_pin_code_neg_reply(struct sock *sk, struct hci_dev *hdev, 27048c2ecf20Sopenharmony_ci struct mgmt_cp_pin_code_neg_reply *cp) 27058c2ecf20Sopenharmony_ci{ 27068c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 27078c2ecf20Sopenharmony_ci int err; 27088c2ecf20Sopenharmony_ci 27098c2ecf20Sopenharmony_ci cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_NEG_REPLY, hdev, cp, 27108c2ecf20Sopenharmony_ci sizeof(*cp)); 27118c2ecf20Sopenharmony_ci if (!cmd) 27128c2ecf20Sopenharmony_ci return -ENOMEM; 27138c2ecf20Sopenharmony_ci 27148c2ecf20Sopenharmony_ci cmd->cmd_complete = addr_cmd_complete; 27158c2ecf20Sopenharmony_ci 27168c2ecf20Sopenharmony_ci err = hci_send_cmd(hdev, HCI_OP_PIN_CODE_NEG_REPLY, 27178c2ecf20Sopenharmony_ci sizeof(cp->addr.bdaddr), &cp->addr.bdaddr); 27188c2ecf20Sopenharmony_ci if (err < 0) 27198c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 27208c2ecf20Sopenharmony_ci 27218c2ecf20Sopenharmony_ci return err; 27228c2ecf20Sopenharmony_ci} 27238c2ecf20Sopenharmony_ci 27248c2ecf20Sopenharmony_cistatic int pin_code_reply(struct sock *sk, struct hci_dev *hdev, void *data, 27258c2ecf20Sopenharmony_ci u16 len) 27268c2ecf20Sopenharmony_ci{ 27278c2ecf20Sopenharmony_ci struct hci_conn *conn; 27288c2ecf20Sopenharmony_ci struct mgmt_cp_pin_code_reply *cp = data; 27298c2ecf20Sopenharmony_ci struct hci_cp_pin_code_reply reply; 27308c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 27318c2ecf20Sopenharmony_ci int err; 27328c2ecf20Sopenharmony_ci 27338c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 27348c2ecf20Sopenharmony_ci 27358c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 27368c2ecf20Sopenharmony_ci 27378c2ecf20Sopenharmony_ci if (!hdev_is_powered(hdev)) { 27388c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_PIN_CODE_REPLY, 27398c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_POWERED); 27408c2ecf20Sopenharmony_ci goto failed; 27418c2ecf20Sopenharmony_ci } 27428c2ecf20Sopenharmony_ci 27438c2ecf20Sopenharmony_ci conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &cp->addr.bdaddr); 27448c2ecf20Sopenharmony_ci if (!conn) { 27458c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_PIN_CODE_REPLY, 27468c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_CONNECTED); 27478c2ecf20Sopenharmony_ci goto failed; 27488c2ecf20Sopenharmony_ci } 27498c2ecf20Sopenharmony_ci 27508c2ecf20Sopenharmony_ci if (conn->pending_sec_level == BT_SECURITY_HIGH && cp->pin_len != 16) { 27518c2ecf20Sopenharmony_ci struct mgmt_cp_pin_code_neg_reply ncp; 27528c2ecf20Sopenharmony_ci 27538c2ecf20Sopenharmony_ci memcpy(&ncp.addr, &cp->addr, sizeof(ncp.addr)); 27548c2ecf20Sopenharmony_ci 27558c2ecf20Sopenharmony_ci bt_dev_err(hdev, "PIN code is not 16 bytes long"); 27568c2ecf20Sopenharmony_ci 27578c2ecf20Sopenharmony_ci err = send_pin_code_neg_reply(sk, hdev, &ncp); 27588c2ecf20Sopenharmony_ci if (err >= 0) 27598c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_PIN_CODE_REPLY, 27608c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 27618c2ecf20Sopenharmony_ci 27628c2ecf20Sopenharmony_ci goto failed; 27638c2ecf20Sopenharmony_ci } 27648c2ecf20Sopenharmony_ci 27658c2ecf20Sopenharmony_ci cmd = mgmt_pending_add(sk, MGMT_OP_PIN_CODE_REPLY, hdev, data, len); 27668c2ecf20Sopenharmony_ci if (!cmd) { 27678c2ecf20Sopenharmony_ci err = -ENOMEM; 27688c2ecf20Sopenharmony_ci goto failed; 27698c2ecf20Sopenharmony_ci } 27708c2ecf20Sopenharmony_ci 27718c2ecf20Sopenharmony_ci cmd->cmd_complete = addr_cmd_complete; 27728c2ecf20Sopenharmony_ci 27738c2ecf20Sopenharmony_ci bacpy(&reply.bdaddr, &cp->addr.bdaddr); 27748c2ecf20Sopenharmony_ci reply.pin_len = cp->pin_len; 27758c2ecf20Sopenharmony_ci memcpy(reply.pin_code, cp->pin_code, sizeof(reply.pin_code)); 27768c2ecf20Sopenharmony_ci 27778c2ecf20Sopenharmony_ci err = hci_send_cmd(hdev, HCI_OP_PIN_CODE_REPLY, sizeof(reply), &reply); 27788c2ecf20Sopenharmony_ci if (err < 0) 27798c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 27808c2ecf20Sopenharmony_ci 27818c2ecf20Sopenharmony_cifailed: 27828c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 27838c2ecf20Sopenharmony_ci return err; 27848c2ecf20Sopenharmony_ci} 27858c2ecf20Sopenharmony_ci 27868c2ecf20Sopenharmony_cistatic int set_io_capability(struct sock *sk, struct hci_dev *hdev, void *data, 27878c2ecf20Sopenharmony_ci u16 len) 27888c2ecf20Sopenharmony_ci{ 27898c2ecf20Sopenharmony_ci struct mgmt_cp_set_io_capability *cp = data; 27908c2ecf20Sopenharmony_ci 27918c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 27928c2ecf20Sopenharmony_ci 27938c2ecf20Sopenharmony_ci if (cp->io_capability > SMP_IO_KEYBOARD_DISPLAY) 27948c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_IO_CAPABILITY, 27958c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 27968c2ecf20Sopenharmony_ci 27978c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 27988c2ecf20Sopenharmony_ci 27998c2ecf20Sopenharmony_ci hdev->io_capability = cp->io_capability; 28008c2ecf20Sopenharmony_ci 28018c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "IO capability set to 0x%02x", hdev->io_capability); 28028c2ecf20Sopenharmony_ci 28038c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 28048c2ecf20Sopenharmony_ci 28058c2ecf20Sopenharmony_ci return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_IO_CAPABILITY, 0, 28068c2ecf20Sopenharmony_ci NULL, 0); 28078c2ecf20Sopenharmony_ci} 28088c2ecf20Sopenharmony_ci 28098c2ecf20Sopenharmony_cistatic struct mgmt_pending_cmd *find_pairing(struct hci_conn *conn) 28108c2ecf20Sopenharmony_ci{ 28118c2ecf20Sopenharmony_ci struct hci_dev *hdev = conn->hdev; 28128c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 28138c2ecf20Sopenharmony_ci 28148c2ecf20Sopenharmony_ci list_for_each_entry(cmd, &hdev->mgmt_pending, list) { 28158c2ecf20Sopenharmony_ci if (cmd->opcode != MGMT_OP_PAIR_DEVICE) 28168c2ecf20Sopenharmony_ci continue; 28178c2ecf20Sopenharmony_ci 28188c2ecf20Sopenharmony_ci if (cmd->user_data != conn) 28198c2ecf20Sopenharmony_ci continue; 28208c2ecf20Sopenharmony_ci 28218c2ecf20Sopenharmony_ci return cmd; 28228c2ecf20Sopenharmony_ci } 28238c2ecf20Sopenharmony_ci 28248c2ecf20Sopenharmony_ci return NULL; 28258c2ecf20Sopenharmony_ci} 28268c2ecf20Sopenharmony_ci 28278c2ecf20Sopenharmony_cistatic int pairing_complete(struct mgmt_pending_cmd *cmd, u8 status) 28288c2ecf20Sopenharmony_ci{ 28298c2ecf20Sopenharmony_ci struct mgmt_rp_pair_device rp; 28308c2ecf20Sopenharmony_ci struct hci_conn *conn = cmd->user_data; 28318c2ecf20Sopenharmony_ci int err; 28328c2ecf20Sopenharmony_ci 28338c2ecf20Sopenharmony_ci bacpy(&rp.addr.bdaddr, &conn->dst); 28348c2ecf20Sopenharmony_ci rp.addr.type = link_to_bdaddr(conn->type, conn->dst_type); 28358c2ecf20Sopenharmony_ci 28368c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(cmd->sk, cmd->index, MGMT_OP_PAIR_DEVICE, 28378c2ecf20Sopenharmony_ci status, &rp, sizeof(rp)); 28388c2ecf20Sopenharmony_ci 28398c2ecf20Sopenharmony_ci /* So we don't get further callbacks for this connection */ 28408c2ecf20Sopenharmony_ci conn->connect_cfm_cb = NULL; 28418c2ecf20Sopenharmony_ci conn->security_cfm_cb = NULL; 28428c2ecf20Sopenharmony_ci conn->disconn_cfm_cb = NULL; 28438c2ecf20Sopenharmony_ci 28448c2ecf20Sopenharmony_ci hci_conn_drop(conn); 28458c2ecf20Sopenharmony_ci 28468c2ecf20Sopenharmony_ci /* The device is paired so there is no need to remove 28478c2ecf20Sopenharmony_ci * its connection parameters anymore. 28488c2ecf20Sopenharmony_ci */ 28498c2ecf20Sopenharmony_ci clear_bit(HCI_CONN_PARAM_REMOVAL_PEND, &conn->flags); 28508c2ecf20Sopenharmony_ci 28518c2ecf20Sopenharmony_ci hci_conn_put(conn); 28528c2ecf20Sopenharmony_ci 28538c2ecf20Sopenharmony_ci return err; 28548c2ecf20Sopenharmony_ci} 28558c2ecf20Sopenharmony_ci 28568c2ecf20Sopenharmony_civoid mgmt_smp_complete(struct hci_conn *conn, bool complete) 28578c2ecf20Sopenharmony_ci{ 28588c2ecf20Sopenharmony_ci u8 status = complete ? MGMT_STATUS_SUCCESS : MGMT_STATUS_FAILED; 28598c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 28608c2ecf20Sopenharmony_ci 28618c2ecf20Sopenharmony_ci cmd = find_pairing(conn); 28628c2ecf20Sopenharmony_ci if (cmd) { 28638c2ecf20Sopenharmony_ci cmd->cmd_complete(cmd, status); 28648c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 28658c2ecf20Sopenharmony_ci } 28668c2ecf20Sopenharmony_ci} 28678c2ecf20Sopenharmony_ci 28688c2ecf20Sopenharmony_cistatic void pairing_complete_cb(struct hci_conn *conn, u8 status) 28698c2ecf20Sopenharmony_ci{ 28708c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 28718c2ecf20Sopenharmony_ci 28728c2ecf20Sopenharmony_ci BT_DBG("status %u", status); 28738c2ecf20Sopenharmony_ci 28748c2ecf20Sopenharmony_ci cmd = find_pairing(conn); 28758c2ecf20Sopenharmony_ci if (!cmd) { 28768c2ecf20Sopenharmony_ci BT_DBG("Unable to find a pending command"); 28778c2ecf20Sopenharmony_ci return; 28788c2ecf20Sopenharmony_ci } 28798c2ecf20Sopenharmony_ci 28808c2ecf20Sopenharmony_ci cmd->cmd_complete(cmd, mgmt_status(status)); 28818c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 28828c2ecf20Sopenharmony_ci} 28838c2ecf20Sopenharmony_ci 28848c2ecf20Sopenharmony_cistatic void le_pairing_complete_cb(struct hci_conn *conn, u8 status) 28858c2ecf20Sopenharmony_ci{ 28868c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 28878c2ecf20Sopenharmony_ci 28888c2ecf20Sopenharmony_ci BT_DBG("status %u", status); 28898c2ecf20Sopenharmony_ci 28908c2ecf20Sopenharmony_ci if (!status) 28918c2ecf20Sopenharmony_ci return; 28928c2ecf20Sopenharmony_ci 28938c2ecf20Sopenharmony_ci cmd = find_pairing(conn); 28948c2ecf20Sopenharmony_ci if (!cmd) { 28958c2ecf20Sopenharmony_ci BT_DBG("Unable to find a pending command"); 28968c2ecf20Sopenharmony_ci return; 28978c2ecf20Sopenharmony_ci } 28988c2ecf20Sopenharmony_ci 28998c2ecf20Sopenharmony_ci cmd->cmd_complete(cmd, mgmt_status(status)); 29008c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 29018c2ecf20Sopenharmony_ci} 29028c2ecf20Sopenharmony_ci 29038c2ecf20Sopenharmony_cistatic int pair_device(struct sock *sk, struct hci_dev *hdev, void *data, 29048c2ecf20Sopenharmony_ci u16 len) 29058c2ecf20Sopenharmony_ci{ 29068c2ecf20Sopenharmony_ci struct mgmt_cp_pair_device *cp = data; 29078c2ecf20Sopenharmony_ci struct mgmt_rp_pair_device rp; 29088c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 29098c2ecf20Sopenharmony_ci u8 sec_level, auth_type; 29108c2ecf20Sopenharmony_ci struct hci_conn *conn; 29118c2ecf20Sopenharmony_ci int err; 29128c2ecf20Sopenharmony_ci 29138c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 29148c2ecf20Sopenharmony_ci 29158c2ecf20Sopenharmony_ci memset(&rp, 0, sizeof(rp)); 29168c2ecf20Sopenharmony_ci bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); 29178c2ecf20Sopenharmony_ci rp.addr.type = cp->addr.type; 29188c2ecf20Sopenharmony_ci 29198c2ecf20Sopenharmony_ci if (!bdaddr_type_is_valid(cp->addr.type)) 29208c2ecf20Sopenharmony_ci return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_PAIR_DEVICE, 29218c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS, 29228c2ecf20Sopenharmony_ci &rp, sizeof(rp)); 29238c2ecf20Sopenharmony_ci 29248c2ecf20Sopenharmony_ci if (cp->io_cap > SMP_IO_KEYBOARD_DISPLAY) 29258c2ecf20Sopenharmony_ci return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_PAIR_DEVICE, 29268c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS, 29278c2ecf20Sopenharmony_ci &rp, sizeof(rp)); 29288c2ecf20Sopenharmony_ci 29298c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 29308c2ecf20Sopenharmony_ci 29318c2ecf20Sopenharmony_ci if (!hdev_is_powered(hdev)) { 29328c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_PAIR_DEVICE, 29338c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_POWERED, &rp, 29348c2ecf20Sopenharmony_ci sizeof(rp)); 29358c2ecf20Sopenharmony_ci goto unlock; 29368c2ecf20Sopenharmony_ci } 29378c2ecf20Sopenharmony_ci 29388c2ecf20Sopenharmony_ci if (hci_bdaddr_is_paired(hdev, &cp->addr.bdaddr, cp->addr.type)) { 29398c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_PAIR_DEVICE, 29408c2ecf20Sopenharmony_ci MGMT_STATUS_ALREADY_PAIRED, &rp, 29418c2ecf20Sopenharmony_ci sizeof(rp)); 29428c2ecf20Sopenharmony_ci goto unlock; 29438c2ecf20Sopenharmony_ci } 29448c2ecf20Sopenharmony_ci 29458c2ecf20Sopenharmony_ci sec_level = BT_SECURITY_MEDIUM; 29468c2ecf20Sopenharmony_ci auth_type = HCI_AT_DEDICATED_BONDING; 29478c2ecf20Sopenharmony_ci 29488c2ecf20Sopenharmony_ci if (cp->addr.type == BDADDR_BREDR) { 29498c2ecf20Sopenharmony_ci conn = hci_connect_acl(hdev, &cp->addr.bdaddr, sec_level, 29508c2ecf20Sopenharmony_ci auth_type, CONN_REASON_PAIR_DEVICE); 29518c2ecf20Sopenharmony_ci } else { 29528c2ecf20Sopenharmony_ci u8 addr_type = le_addr_type(cp->addr.type); 29538c2ecf20Sopenharmony_ci struct hci_conn_params *p; 29548c2ecf20Sopenharmony_ci 29558c2ecf20Sopenharmony_ci /* When pairing a new device, it is expected to remember 29568c2ecf20Sopenharmony_ci * this device for future connections. Adding the connection 29578c2ecf20Sopenharmony_ci * parameter information ahead of time allows tracking 29588c2ecf20Sopenharmony_ci * of the slave preferred values and will speed up any 29598c2ecf20Sopenharmony_ci * further connection establishment. 29608c2ecf20Sopenharmony_ci * 29618c2ecf20Sopenharmony_ci * If connection parameters already exist, then they 29628c2ecf20Sopenharmony_ci * will be kept and this function does nothing. 29638c2ecf20Sopenharmony_ci */ 29648c2ecf20Sopenharmony_ci p = hci_conn_params_add(hdev, &cp->addr.bdaddr, addr_type); 29658c2ecf20Sopenharmony_ci if (!p) { 29668c2ecf20Sopenharmony_ci err = -EIO; 29678c2ecf20Sopenharmony_ci goto unlock; 29688c2ecf20Sopenharmony_ci } 29698c2ecf20Sopenharmony_ci 29708c2ecf20Sopenharmony_ci if (p->auto_connect == HCI_AUTO_CONN_EXPLICIT) 29718c2ecf20Sopenharmony_ci p->auto_connect = HCI_AUTO_CONN_DISABLED; 29728c2ecf20Sopenharmony_ci 29738c2ecf20Sopenharmony_ci conn = hci_connect_le_scan(hdev, &cp->addr.bdaddr, addr_type, 29748c2ecf20Sopenharmony_ci sec_level, HCI_LE_CONN_TIMEOUT, 29758c2ecf20Sopenharmony_ci CONN_REASON_PAIR_DEVICE); 29768c2ecf20Sopenharmony_ci } 29778c2ecf20Sopenharmony_ci 29788c2ecf20Sopenharmony_ci if (IS_ERR(conn)) { 29798c2ecf20Sopenharmony_ci int status; 29808c2ecf20Sopenharmony_ci 29818c2ecf20Sopenharmony_ci if (PTR_ERR(conn) == -EBUSY) 29828c2ecf20Sopenharmony_ci status = MGMT_STATUS_BUSY; 29838c2ecf20Sopenharmony_ci else if (PTR_ERR(conn) == -EOPNOTSUPP) 29848c2ecf20Sopenharmony_ci status = MGMT_STATUS_NOT_SUPPORTED; 29858c2ecf20Sopenharmony_ci else if (PTR_ERR(conn) == -ECONNREFUSED) 29868c2ecf20Sopenharmony_ci status = MGMT_STATUS_REJECTED; 29878c2ecf20Sopenharmony_ci else 29888c2ecf20Sopenharmony_ci status = MGMT_STATUS_CONNECT_FAILED; 29898c2ecf20Sopenharmony_ci 29908c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_PAIR_DEVICE, 29918c2ecf20Sopenharmony_ci status, &rp, sizeof(rp)); 29928c2ecf20Sopenharmony_ci goto unlock; 29938c2ecf20Sopenharmony_ci } 29948c2ecf20Sopenharmony_ci 29958c2ecf20Sopenharmony_ci if (conn->connect_cfm_cb) { 29968c2ecf20Sopenharmony_ci hci_conn_drop(conn); 29978c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_PAIR_DEVICE, 29988c2ecf20Sopenharmony_ci MGMT_STATUS_BUSY, &rp, sizeof(rp)); 29998c2ecf20Sopenharmony_ci goto unlock; 30008c2ecf20Sopenharmony_ci } 30018c2ecf20Sopenharmony_ci 30028c2ecf20Sopenharmony_ci cmd = mgmt_pending_add(sk, MGMT_OP_PAIR_DEVICE, hdev, data, len); 30038c2ecf20Sopenharmony_ci if (!cmd) { 30048c2ecf20Sopenharmony_ci err = -ENOMEM; 30058c2ecf20Sopenharmony_ci hci_conn_drop(conn); 30068c2ecf20Sopenharmony_ci goto unlock; 30078c2ecf20Sopenharmony_ci } 30088c2ecf20Sopenharmony_ci 30098c2ecf20Sopenharmony_ci cmd->cmd_complete = pairing_complete; 30108c2ecf20Sopenharmony_ci 30118c2ecf20Sopenharmony_ci /* For LE, just connecting isn't a proof that the pairing finished */ 30128c2ecf20Sopenharmony_ci if (cp->addr.type == BDADDR_BREDR) { 30138c2ecf20Sopenharmony_ci conn->connect_cfm_cb = pairing_complete_cb; 30148c2ecf20Sopenharmony_ci conn->security_cfm_cb = pairing_complete_cb; 30158c2ecf20Sopenharmony_ci conn->disconn_cfm_cb = pairing_complete_cb; 30168c2ecf20Sopenharmony_ci } else { 30178c2ecf20Sopenharmony_ci conn->connect_cfm_cb = le_pairing_complete_cb; 30188c2ecf20Sopenharmony_ci conn->security_cfm_cb = le_pairing_complete_cb; 30198c2ecf20Sopenharmony_ci conn->disconn_cfm_cb = le_pairing_complete_cb; 30208c2ecf20Sopenharmony_ci } 30218c2ecf20Sopenharmony_ci 30228c2ecf20Sopenharmony_ci conn->io_capability = cp->io_cap; 30238c2ecf20Sopenharmony_ci cmd->user_data = hci_conn_get(conn); 30248c2ecf20Sopenharmony_ci 30258c2ecf20Sopenharmony_ci if ((conn->state == BT_CONNECTED || conn->state == BT_CONFIG) && 30268c2ecf20Sopenharmony_ci hci_conn_security(conn, sec_level, auth_type, true)) { 30278c2ecf20Sopenharmony_ci cmd->cmd_complete(cmd, 0); 30288c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 30298c2ecf20Sopenharmony_ci } 30308c2ecf20Sopenharmony_ci 30318c2ecf20Sopenharmony_ci err = 0; 30328c2ecf20Sopenharmony_ci 30338c2ecf20Sopenharmony_ciunlock: 30348c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 30358c2ecf20Sopenharmony_ci return err; 30368c2ecf20Sopenharmony_ci} 30378c2ecf20Sopenharmony_ci 30388c2ecf20Sopenharmony_cistatic int cancel_pair_device(struct sock *sk, struct hci_dev *hdev, void *data, 30398c2ecf20Sopenharmony_ci u16 len) 30408c2ecf20Sopenharmony_ci{ 30418c2ecf20Sopenharmony_ci struct mgmt_addr_info *addr = data; 30428c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 30438c2ecf20Sopenharmony_ci struct hci_conn *conn; 30448c2ecf20Sopenharmony_ci int err; 30458c2ecf20Sopenharmony_ci 30468c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 30478c2ecf20Sopenharmony_ci 30488c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 30498c2ecf20Sopenharmony_ci 30508c2ecf20Sopenharmony_ci if (!hdev_is_powered(hdev)) { 30518c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE, 30528c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_POWERED); 30538c2ecf20Sopenharmony_ci goto unlock; 30548c2ecf20Sopenharmony_ci } 30558c2ecf20Sopenharmony_ci 30568c2ecf20Sopenharmony_ci cmd = pending_find(MGMT_OP_PAIR_DEVICE, hdev); 30578c2ecf20Sopenharmony_ci if (!cmd) { 30588c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE, 30598c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 30608c2ecf20Sopenharmony_ci goto unlock; 30618c2ecf20Sopenharmony_ci } 30628c2ecf20Sopenharmony_ci 30638c2ecf20Sopenharmony_ci conn = cmd->user_data; 30648c2ecf20Sopenharmony_ci 30658c2ecf20Sopenharmony_ci if (bacmp(&addr->bdaddr, &conn->dst) != 0) { 30668c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE, 30678c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 30688c2ecf20Sopenharmony_ci goto unlock; 30698c2ecf20Sopenharmony_ci } 30708c2ecf20Sopenharmony_ci 30718c2ecf20Sopenharmony_ci cmd->cmd_complete(cmd, MGMT_STATUS_CANCELLED); 30728c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 30738c2ecf20Sopenharmony_ci 30748c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE, 0, 30758c2ecf20Sopenharmony_ci addr, sizeof(*addr)); 30768c2ecf20Sopenharmony_ci 30778c2ecf20Sopenharmony_ci /* Since user doesn't want to proceed with the connection, abort any 30788c2ecf20Sopenharmony_ci * ongoing pairing and then terminate the link if it was created 30798c2ecf20Sopenharmony_ci * because of the pair device action. 30808c2ecf20Sopenharmony_ci */ 30818c2ecf20Sopenharmony_ci if (addr->type == BDADDR_BREDR) 30828c2ecf20Sopenharmony_ci hci_remove_link_key(hdev, &addr->bdaddr); 30838c2ecf20Sopenharmony_ci else 30848c2ecf20Sopenharmony_ci smp_cancel_and_remove_pairing(hdev, &addr->bdaddr, 30858c2ecf20Sopenharmony_ci le_addr_type(addr->type)); 30868c2ecf20Sopenharmony_ci 30878c2ecf20Sopenharmony_ci if (conn->conn_reason == CONN_REASON_PAIR_DEVICE) 30888c2ecf20Sopenharmony_ci hci_abort_conn(conn, HCI_ERROR_REMOTE_USER_TERM); 30898c2ecf20Sopenharmony_ci 30908c2ecf20Sopenharmony_ciunlock: 30918c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 30928c2ecf20Sopenharmony_ci return err; 30938c2ecf20Sopenharmony_ci} 30948c2ecf20Sopenharmony_ci 30958c2ecf20Sopenharmony_cistatic int user_pairing_resp(struct sock *sk, struct hci_dev *hdev, 30968c2ecf20Sopenharmony_ci struct mgmt_addr_info *addr, u16 mgmt_op, 30978c2ecf20Sopenharmony_ci u16 hci_op, __le32 passkey) 30988c2ecf20Sopenharmony_ci{ 30998c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 31008c2ecf20Sopenharmony_ci struct hci_conn *conn; 31018c2ecf20Sopenharmony_ci int err; 31028c2ecf20Sopenharmony_ci 31038c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 31048c2ecf20Sopenharmony_ci 31058c2ecf20Sopenharmony_ci if (!hdev_is_powered(hdev)) { 31068c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, mgmt_op, 31078c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_POWERED, addr, 31088c2ecf20Sopenharmony_ci sizeof(*addr)); 31098c2ecf20Sopenharmony_ci goto done; 31108c2ecf20Sopenharmony_ci } 31118c2ecf20Sopenharmony_ci 31128c2ecf20Sopenharmony_ci if (addr->type == BDADDR_BREDR) 31138c2ecf20Sopenharmony_ci conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, &addr->bdaddr); 31148c2ecf20Sopenharmony_ci else 31158c2ecf20Sopenharmony_ci conn = hci_conn_hash_lookup_le(hdev, &addr->bdaddr, 31168c2ecf20Sopenharmony_ci le_addr_type(addr->type)); 31178c2ecf20Sopenharmony_ci 31188c2ecf20Sopenharmony_ci if (!conn) { 31198c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, mgmt_op, 31208c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_CONNECTED, addr, 31218c2ecf20Sopenharmony_ci sizeof(*addr)); 31228c2ecf20Sopenharmony_ci goto done; 31238c2ecf20Sopenharmony_ci } 31248c2ecf20Sopenharmony_ci 31258c2ecf20Sopenharmony_ci if (addr->type == BDADDR_LE_PUBLIC || addr->type == BDADDR_LE_RANDOM) { 31268c2ecf20Sopenharmony_ci err = smp_user_confirm_reply(conn, mgmt_op, passkey); 31278c2ecf20Sopenharmony_ci if (!err) 31288c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, mgmt_op, 31298c2ecf20Sopenharmony_ci MGMT_STATUS_SUCCESS, addr, 31308c2ecf20Sopenharmony_ci sizeof(*addr)); 31318c2ecf20Sopenharmony_ci else 31328c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, mgmt_op, 31338c2ecf20Sopenharmony_ci MGMT_STATUS_FAILED, addr, 31348c2ecf20Sopenharmony_ci sizeof(*addr)); 31358c2ecf20Sopenharmony_ci 31368c2ecf20Sopenharmony_ci goto done; 31378c2ecf20Sopenharmony_ci } 31388c2ecf20Sopenharmony_ci 31398c2ecf20Sopenharmony_ci cmd = mgmt_pending_add(sk, mgmt_op, hdev, addr, sizeof(*addr)); 31408c2ecf20Sopenharmony_ci if (!cmd) { 31418c2ecf20Sopenharmony_ci err = -ENOMEM; 31428c2ecf20Sopenharmony_ci goto done; 31438c2ecf20Sopenharmony_ci } 31448c2ecf20Sopenharmony_ci 31458c2ecf20Sopenharmony_ci cmd->cmd_complete = addr_cmd_complete; 31468c2ecf20Sopenharmony_ci 31478c2ecf20Sopenharmony_ci /* Continue with pairing via HCI */ 31488c2ecf20Sopenharmony_ci if (hci_op == HCI_OP_USER_PASSKEY_REPLY) { 31498c2ecf20Sopenharmony_ci struct hci_cp_user_passkey_reply cp; 31508c2ecf20Sopenharmony_ci 31518c2ecf20Sopenharmony_ci bacpy(&cp.bdaddr, &addr->bdaddr); 31528c2ecf20Sopenharmony_ci cp.passkey = passkey; 31538c2ecf20Sopenharmony_ci err = hci_send_cmd(hdev, hci_op, sizeof(cp), &cp); 31548c2ecf20Sopenharmony_ci } else 31558c2ecf20Sopenharmony_ci err = hci_send_cmd(hdev, hci_op, sizeof(addr->bdaddr), 31568c2ecf20Sopenharmony_ci &addr->bdaddr); 31578c2ecf20Sopenharmony_ci 31588c2ecf20Sopenharmony_ci if (err < 0) 31598c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 31608c2ecf20Sopenharmony_ci 31618c2ecf20Sopenharmony_cidone: 31628c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 31638c2ecf20Sopenharmony_ci return err; 31648c2ecf20Sopenharmony_ci} 31658c2ecf20Sopenharmony_ci 31668c2ecf20Sopenharmony_cistatic int pin_code_neg_reply(struct sock *sk, struct hci_dev *hdev, 31678c2ecf20Sopenharmony_ci void *data, u16 len) 31688c2ecf20Sopenharmony_ci{ 31698c2ecf20Sopenharmony_ci struct mgmt_cp_pin_code_neg_reply *cp = data; 31708c2ecf20Sopenharmony_ci 31718c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 31728c2ecf20Sopenharmony_ci 31738c2ecf20Sopenharmony_ci return user_pairing_resp(sk, hdev, &cp->addr, 31748c2ecf20Sopenharmony_ci MGMT_OP_PIN_CODE_NEG_REPLY, 31758c2ecf20Sopenharmony_ci HCI_OP_PIN_CODE_NEG_REPLY, 0); 31768c2ecf20Sopenharmony_ci} 31778c2ecf20Sopenharmony_ci 31788c2ecf20Sopenharmony_cistatic int user_confirm_reply(struct sock *sk, struct hci_dev *hdev, void *data, 31798c2ecf20Sopenharmony_ci u16 len) 31808c2ecf20Sopenharmony_ci{ 31818c2ecf20Sopenharmony_ci struct mgmt_cp_user_confirm_reply *cp = data; 31828c2ecf20Sopenharmony_ci 31838c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 31848c2ecf20Sopenharmony_ci 31858c2ecf20Sopenharmony_ci if (len != sizeof(*cp)) 31868c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_USER_CONFIRM_REPLY, 31878c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 31888c2ecf20Sopenharmony_ci 31898c2ecf20Sopenharmony_ci return user_pairing_resp(sk, hdev, &cp->addr, 31908c2ecf20Sopenharmony_ci MGMT_OP_USER_CONFIRM_REPLY, 31918c2ecf20Sopenharmony_ci HCI_OP_USER_CONFIRM_REPLY, 0); 31928c2ecf20Sopenharmony_ci} 31938c2ecf20Sopenharmony_ci 31948c2ecf20Sopenharmony_cistatic int user_confirm_neg_reply(struct sock *sk, struct hci_dev *hdev, 31958c2ecf20Sopenharmony_ci void *data, u16 len) 31968c2ecf20Sopenharmony_ci{ 31978c2ecf20Sopenharmony_ci struct mgmt_cp_user_confirm_neg_reply *cp = data; 31988c2ecf20Sopenharmony_ci 31998c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 32008c2ecf20Sopenharmony_ci 32018c2ecf20Sopenharmony_ci return user_pairing_resp(sk, hdev, &cp->addr, 32028c2ecf20Sopenharmony_ci MGMT_OP_USER_CONFIRM_NEG_REPLY, 32038c2ecf20Sopenharmony_ci HCI_OP_USER_CONFIRM_NEG_REPLY, 0); 32048c2ecf20Sopenharmony_ci} 32058c2ecf20Sopenharmony_ci 32068c2ecf20Sopenharmony_cistatic int user_passkey_reply(struct sock *sk, struct hci_dev *hdev, void *data, 32078c2ecf20Sopenharmony_ci u16 len) 32088c2ecf20Sopenharmony_ci{ 32098c2ecf20Sopenharmony_ci struct mgmt_cp_user_passkey_reply *cp = data; 32108c2ecf20Sopenharmony_ci 32118c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 32128c2ecf20Sopenharmony_ci 32138c2ecf20Sopenharmony_ci return user_pairing_resp(sk, hdev, &cp->addr, 32148c2ecf20Sopenharmony_ci MGMT_OP_USER_PASSKEY_REPLY, 32158c2ecf20Sopenharmony_ci HCI_OP_USER_PASSKEY_REPLY, cp->passkey); 32168c2ecf20Sopenharmony_ci} 32178c2ecf20Sopenharmony_ci 32188c2ecf20Sopenharmony_cistatic int user_passkey_neg_reply(struct sock *sk, struct hci_dev *hdev, 32198c2ecf20Sopenharmony_ci void *data, u16 len) 32208c2ecf20Sopenharmony_ci{ 32218c2ecf20Sopenharmony_ci struct mgmt_cp_user_passkey_neg_reply *cp = data; 32228c2ecf20Sopenharmony_ci 32238c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 32248c2ecf20Sopenharmony_ci 32258c2ecf20Sopenharmony_ci return user_pairing_resp(sk, hdev, &cp->addr, 32268c2ecf20Sopenharmony_ci MGMT_OP_USER_PASSKEY_NEG_REPLY, 32278c2ecf20Sopenharmony_ci HCI_OP_USER_PASSKEY_NEG_REPLY, 0); 32288c2ecf20Sopenharmony_ci} 32298c2ecf20Sopenharmony_ci 32308c2ecf20Sopenharmony_cistatic void adv_expire(struct hci_dev *hdev, u32 flags) 32318c2ecf20Sopenharmony_ci{ 32328c2ecf20Sopenharmony_ci struct adv_info *adv_instance; 32338c2ecf20Sopenharmony_ci struct hci_request req; 32348c2ecf20Sopenharmony_ci int err; 32358c2ecf20Sopenharmony_ci 32368c2ecf20Sopenharmony_ci adv_instance = hci_find_adv_instance(hdev, hdev->cur_adv_instance); 32378c2ecf20Sopenharmony_ci if (!adv_instance) 32388c2ecf20Sopenharmony_ci return; 32398c2ecf20Sopenharmony_ci 32408c2ecf20Sopenharmony_ci /* stop if current instance doesn't need to be changed */ 32418c2ecf20Sopenharmony_ci if (!(adv_instance->flags & flags)) 32428c2ecf20Sopenharmony_ci return; 32438c2ecf20Sopenharmony_ci 32448c2ecf20Sopenharmony_ci cancel_adv_timeout(hdev); 32458c2ecf20Sopenharmony_ci 32468c2ecf20Sopenharmony_ci adv_instance = hci_get_next_instance(hdev, adv_instance->instance); 32478c2ecf20Sopenharmony_ci if (!adv_instance) 32488c2ecf20Sopenharmony_ci return; 32498c2ecf20Sopenharmony_ci 32508c2ecf20Sopenharmony_ci hci_req_init(&req, hdev); 32518c2ecf20Sopenharmony_ci err = __hci_req_schedule_adv_instance(&req, adv_instance->instance, 32528c2ecf20Sopenharmony_ci true); 32538c2ecf20Sopenharmony_ci if (err) 32548c2ecf20Sopenharmony_ci return; 32558c2ecf20Sopenharmony_ci 32568c2ecf20Sopenharmony_ci hci_req_run(&req, NULL); 32578c2ecf20Sopenharmony_ci} 32588c2ecf20Sopenharmony_ci 32598c2ecf20Sopenharmony_cistatic void set_name_complete(struct hci_dev *hdev, u8 status, u16 opcode) 32608c2ecf20Sopenharmony_ci{ 32618c2ecf20Sopenharmony_ci struct mgmt_cp_set_local_name *cp; 32628c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 32638c2ecf20Sopenharmony_ci 32648c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "status 0x%02x", status); 32658c2ecf20Sopenharmony_ci 32668c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 32678c2ecf20Sopenharmony_ci 32688c2ecf20Sopenharmony_ci cmd = pending_find(MGMT_OP_SET_LOCAL_NAME, hdev); 32698c2ecf20Sopenharmony_ci if (!cmd) 32708c2ecf20Sopenharmony_ci goto unlock; 32718c2ecf20Sopenharmony_ci 32728c2ecf20Sopenharmony_ci cp = cmd->param; 32738c2ecf20Sopenharmony_ci 32748c2ecf20Sopenharmony_ci if (status) { 32758c2ecf20Sopenharmony_ci mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, 32768c2ecf20Sopenharmony_ci mgmt_status(status)); 32778c2ecf20Sopenharmony_ci } else { 32788c2ecf20Sopenharmony_ci mgmt_cmd_complete(cmd->sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, 0, 32798c2ecf20Sopenharmony_ci cp, sizeof(*cp)); 32808c2ecf20Sopenharmony_ci 32818c2ecf20Sopenharmony_ci if (hci_dev_test_flag(hdev, HCI_LE_ADV)) 32828c2ecf20Sopenharmony_ci adv_expire(hdev, MGMT_ADV_FLAG_LOCAL_NAME); 32838c2ecf20Sopenharmony_ci } 32848c2ecf20Sopenharmony_ci 32858c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 32868c2ecf20Sopenharmony_ci 32878c2ecf20Sopenharmony_ciunlock: 32888c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 32898c2ecf20Sopenharmony_ci} 32908c2ecf20Sopenharmony_ci 32918c2ecf20Sopenharmony_cistatic int set_local_name(struct sock *sk, struct hci_dev *hdev, void *data, 32928c2ecf20Sopenharmony_ci u16 len) 32938c2ecf20Sopenharmony_ci{ 32948c2ecf20Sopenharmony_ci struct mgmt_cp_set_local_name *cp = data; 32958c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 32968c2ecf20Sopenharmony_ci struct hci_request req; 32978c2ecf20Sopenharmony_ci int err; 32988c2ecf20Sopenharmony_ci 32998c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 33008c2ecf20Sopenharmony_ci 33018c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 33028c2ecf20Sopenharmony_ci 33038c2ecf20Sopenharmony_ci /* If the old values are the same as the new ones just return a 33048c2ecf20Sopenharmony_ci * direct command complete event. 33058c2ecf20Sopenharmony_ci */ 33068c2ecf20Sopenharmony_ci if (!memcmp(hdev->dev_name, cp->name, sizeof(hdev->dev_name)) && 33078c2ecf20Sopenharmony_ci !memcmp(hdev->short_name, cp->short_name, 33088c2ecf20Sopenharmony_ci sizeof(hdev->short_name))) { 33098c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, 0, 33108c2ecf20Sopenharmony_ci data, len); 33118c2ecf20Sopenharmony_ci goto failed; 33128c2ecf20Sopenharmony_ci } 33138c2ecf20Sopenharmony_ci 33148c2ecf20Sopenharmony_ci memcpy(hdev->short_name, cp->short_name, sizeof(hdev->short_name)); 33158c2ecf20Sopenharmony_ci 33168c2ecf20Sopenharmony_ci if (!hdev_is_powered(hdev)) { 33178c2ecf20Sopenharmony_ci memcpy(hdev->dev_name, cp->name, sizeof(hdev->dev_name)); 33188c2ecf20Sopenharmony_ci 33198c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_LOCAL_NAME, 0, 33208c2ecf20Sopenharmony_ci data, len); 33218c2ecf20Sopenharmony_ci if (err < 0) 33228c2ecf20Sopenharmony_ci goto failed; 33238c2ecf20Sopenharmony_ci 33248c2ecf20Sopenharmony_ci err = mgmt_limited_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, data, 33258c2ecf20Sopenharmony_ci len, HCI_MGMT_LOCAL_NAME_EVENTS, sk); 33268c2ecf20Sopenharmony_ci ext_info_changed(hdev, sk); 33278c2ecf20Sopenharmony_ci 33288c2ecf20Sopenharmony_ci goto failed; 33298c2ecf20Sopenharmony_ci } 33308c2ecf20Sopenharmony_ci 33318c2ecf20Sopenharmony_ci cmd = mgmt_pending_add(sk, MGMT_OP_SET_LOCAL_NAME, hdev, data, len); 33328c2ecf20Sopenharmony_ci if (!cmd) { 33338c2ecf20Sopenharmony_ci err = -ENOMEM; 33348c2ecf20Sopenharmony_ci goto failed; 33358c2ecf20Sopenharmony_ci } 33368c2ecf20Sopenharmony_ci 33378c2ecf20Sopenharmony_ci memcpy(hdev->dev_name, cp->name, sizeof(hdev->dev_name)); 33388c2ecf20Sopenharmony_ci 33398c2ecf20Sopenharmony_ci hci_req_init(&req, hdev); 33408c2ecf20Sopenharmony_ci 33418c2ecf20Sopenharmony_ci if (lmp_bredr_capable(hdev)) { 33428c2ecf20Sopenharmony_ci __hci_req_update_name(&req); 33438c2ecf20Sopenharmony_ci __hci_req_update_eir(&req); 33448c2ecf20Sopenharmony_ci } 33458c2ecf20Sopenharmony_ci 33468c2ecf20Sopenharmony_ci /* The name is stored in the scan response data and so 33478c2ecf20Sopenharmony_ci * no need to udpate the advertising data here. 33488c2ecf20Sopenharmony_ci */ 33498c2ecf20Sopenharmony_ci if (lmp_le_capable(hdev) && hci_dev_test_flag(hdev, HCI_ADVERTISING)) 33508c2ecf20Sopenharmony_ci __hci_req_update_scan_rsp_data(&req, hdev->cur_adv_instance); 33518c2ecf20Sopenharmony_ci 33528c2ecf20Sopenharmony_ci err = hci_req_run(&req, set_name_complete); 33538c2ecf20Sopenharmony_ci if (err < 0) 33548c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 33558c2ecf20Sopenharmony_ci 33568c2ecf20Sopenharmony_cifailed: 33578c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 33588c2ecf20Sopenharmony_ci return err; 33598c2ecf20Sopenharmony_ci} 33608c2ecf20Sopenharmony_ci 33618c2ecf20Sopenharmony_cistatic int set_appearance(struct sock *sk, struct hci_dev *hdev, void *data, 33628c2ecf20Sopenharmony_ci u16 len) 33638c2ecf20Sopenharmony_ci{ 33648c2ecf20Sopenharmony_ci struct mgmt_cp_set_appearance *cp = data; 33658c2ecf20Sopenharmony_ci u16 appearance; 33668c2ecf20Sopenharmony_ci int err; 33678c2ecf20Sopenharmony_ci 33688c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 33698c2ecf20Sopenharmony_ci 33708c2ecf20Sopenharmony_ci if (!lmp_le_capable(hdev)) 33718c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_APPEARANCE, 33728c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_SUPPORTED); 33738c2ecf20Sopenharmony_ci 33748c2ecf20Sopenharmony_ci appearance = le16_to_cpu(cp->appearance); 33758c2ecf20Sopenharmony_ci 33768c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 33778c2ecf20Sopenharmony_ci 33788c2ecf20Sopenharmony_ci if (hdev->appearance != appearance) { 33798c2ecf20Sopenharmony_ci hdev->appearance = appearance; 33808c2ecf20Sopenharmony_ci 33818c2ecf20Sopenharmony_ci if (hci_dev_test_flag(hdev, HCI_LE_ADV)) 33828c2ecf20Sopenharmony_ci adv_expire(hdev, MGMT_ADV_FLAG_APPEARANCE); 33838c2ecf20Sopenharmony_ci 33848c2ecf20Sopenharmony_ci ext_info_changed(hdev, sk); 33858c2ecf20Sopenharmony_ci } 33868c2ecf20Sopenharmony_ci 33878c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_APPEARANCE, 0, NULL, 33888c2ecf20Sopenharmony_ci 0); 33898c2ecf20Sopenharmony_ci 33908c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 33918c2ecf20Sopenharmony_ci 33928c2ecf20Sopenharmony_ci return err; 33938c2ecf20Sopenharmony_ci} 33948c2ecf20Sopenharmony_ci 33958c2ecf20Sopenharmony_cistatic int get_phy_configuration(struct sock *sk, struct hci_dev *hdev, 33968c2ecf20Sopenharmony_ci void *data, u16 len) 33978c2ecf20Sopenharmony_ci{ 33988c2ecf20Sopenharmony_ci struct mgmt_rp_get_phy_confguration rp; 33998c2ecf20Sopenharmony_ci 34008c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 34018c2ecf20Sopenharmony_ci 34028c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 34038c2ecf20Sopenharmony_ci 34048c2ecf20Sopenharmony_ci memset(&rp, 0, sizeof(rp)); 34058c2ecf20Sopenharmony_ci 34068c2ecf20Sopenharmony_ci rp.supported_phys = cpu_to_le32(get_supported_phys(hdev)); 34078c2ecf20Sopenharmony_ci rp.selected_phys = cpu_to_le32(get_selected_phys(hdev)); 34088c2ecf20Sopenharmony_ci rp.configurable_phys = cpu_to_le32(get_configurable_phys(hdev)); 34098c2ecf20Sopenharmony_ci 34108c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 34118c2ecf20Sopenharmony_ci 34128c2ecf20Sopenharmony_ci return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_GET_PHY_CONFIGURATION, 0, 34138c2ecf20Sopenharmony_ci &rp, sizeof(rp)); 34148c2ecf20Sopenharmony_ci} 34158c2ecf20Sopenharmony_ci 34168c2ecf20Sopenharmony_ciint mgmt_phy_configuration_changed(struct hci_dev *hdev, struct sock *skip) 34178c2ecf20Sopenharmony_ci{ 34188c2ecf20Sopenharmony_ci struct mgmt_ev_phy_configuration_changed ev; 34198c2ecf20Sopenharmony_ci 34208c2ecf20Sopenharmony_ci memset(&ev, 0, sizeof(ev)); 34218c2ecf20Sopenharmony_ci 34228c2ecf20Sopenharmony_ci ev.selected_phys = cpu_to_le32(get_selected_phys(hdev)); 34238c2ecf20Sopenharmony_ci 34248c2ecf20Sopenharmony_ci return mgmt_event(MGMT_EV_PHY_CONFIGURATION_CHANGED, hdev, &ev, 34258c2ecf20Sopenharmony_ci sizeof(ev), skip); 34268c2ecf20Sopenharmony_ci} 34278c2ecf20Sopenharmony_ci 34288c2ecf20Sopenharmony_cistatic void set_default_phy_complete(struct hci_dev *hdev, u8 status, 34298c2ecf20Sopenharmony_ci u16 opcode, struct sk_buff *skb) 34308c2ecf20Sopenharmony_ci{ 34318c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 34328c2ecf20Sopenharmony_ci 34338c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "status 0x%02x", status); 34348c2ecf20Sopenharmony_ci 34358c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 34368c2ecf20Sopenharmony_ci 34378c2ecf20Sopenharmony_ci cmd = pending_find(MGMT_OP_SET_PHY_CONFIGURATION, hdev); 34388c2ecf20Sopenharmony_ci if (!cmd) 34398c2ecf20Sopenharmony_ci goto unlock; 34408c2ecf20Sopenharmony_ci 34418c2ecf20Sopenharmony_ci if (status) { 34428c2ecf20Sopenharmony_ci mgmt_cmd_status(cmd->sk, hdev->id, 34438c2ecf20Sopenharmony_ci MGMT_OP_SET_PHY_CONFIGURATION, 34448c2ecf20Sopenharmony_ci mgmt_status(status)); 34458c2ecf20Sopenharmony_ci } else { 34468c2ecf20Sopenharmony_ci mgmt_cmd_complete(cmd->sk, hdev->id, 34478c2ecf20Sopenharmony_ci MGMT_OP_SET_PHY_CONFIGURATION, 0, 34488c2ecf20Sopenharmony_ci NULL, 0); 34498c2ecf20Sopenharmony_ci 34508c2ecf20Sopenharmony_ci mgmt_phy_configuration_changed(hdev, cmd->sk); 34518c2ecf20Sopenharmony_ci } 34528c2ecf20Sopenharmony_ci 34538c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 34548c2ecf20Sopenharmony_ci 34558c2ecf20Sopenharmony_ciunlock: 34568c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 34578c2ecf20Sopenharmony_ci} 34588c2ecf20Sopenharmony_ci 34598c2ecf20Sopenharmony_cistatic int set_phy_configuration(struct sock *sk, struct hci_dev *hdev, 34608c2ecf20Sopenharmony_ci void *data, u16 len) 34618c2ecf20Sopenharmony_ci{ 34628c2ecf20Sopenharmony_ci struct mgmt_cp_set_phy_confguration *cp = data; 34638c2ecf20Sopenharmony_ci struct hci_cp_le_set_default_phy cp_phy; 34648c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 34658c2ecf20Sopenharmony_ci struct hci_request req; 34668c2ecf20Sopenharmony_ci u32 selected_phys, configurable_phys, supported_phys, unconfigure_phys; 34678c2ecf20Sopenharmony_ci u16 pkt_type = (HCI_DH1 | HCI_DM1); 34688c2ecf20Sopenharmony_ci bool changed = false; 34698c2ecf20Sopenharmony_ci int err; 34708c2ecf20Sopenharmony_ci 34718c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 34728c2ecf20Sopenharmony_ci 34738c2ecf20Sopenharmony_ci configurable_phys = get_configurable_phys(hdev); 34748c2ecf20Sopenharmony_ci supported_phys = get_supported_phys(hdev); 34758c2ecf20Sopenharmony_ci selected_phys = __le32_to_cpu(cp->selected_phys); 34768c2ecf20Sopenharmony_ci 34778c2ecf20Sopenharmony_ci if (selected_phys & ~supported_phys) 34788c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, 34798c2ecf20Sopenharmony_ci MGMT_OP_SET_PHY_CONFIGURATION, 34808c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 34818c2ecf20Sopenharmony_ci 34828c2ecf20Sopenharmony_ci unconfigure_phys = supported_phys & ~configurable_phys; 34838c2ecf20Sopenharmony_ci 34848c2ecf20Sopenharmony_ci if ((selected_phys & unconfigure_phys) != unconfigure_phys) 34858c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, 34868c2ecf20Sopenharmony_ci MGMT_OP_SET_PHY_CONFIGURATION, 34878c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 34888c2ecf20Sopenharmony_ci 34898c2ecf20Sopenharmony_ci if (selected_phys == get_selected_phys(hdev)) 34908c2ecf20Sopenharmony_ci return mgmt_cmd_complete(sk, hdev->id, 34918c2ecf20Sopenharmony_ci MGMT_OP_SET_PHY_CONFIGURATION, 34928c2ecf20Sopenharmony_ci 0, NULL, 0); 34938c2ecf20Sopenharmony_ci 34948c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 34958c2ecf20Sopenharmony_ci 34968c2ecf20Sopenharmony_ci if (!hdev_is_powered(hdev)) { 34978c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, 34988c2ecf20Sopenharmony_ci MGMT_OP_SET_PHY_CONFIGURATION, 34998c2ecf20Sopenharmony_ci MGMT_STATUS_REJECTED); 35008c2ecf20Sopenharmony_ci goto unlock; 35018c2ecf20Sopenharmony_ci } 35028c2ecf20Sopenharmony_ci 35038c2ecf20Sopenharmony_ci if (pending_find(MGMT_OP_SET_PHY_CONFIGURATION, hdev)) { 35048c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, 35058c2ecf20Sopenharmony_ci MGMT_OP_SET_PHY_CONFIGURATION, 35068c2ecf20Sopenharmony_ci MGMT_STATUS_BUSY); 35078c2ecf20Sopenharmony_ci goto unlock; 35088c2ecf20Sopenharmony_ci } 35098c2ecf20Sopenharmony_ci 35108c2ecf20Sopenharmony_ci if (selected_phys & MGMT_PHY_BR_1M_3SLOT) 35118c2ecf20Sopenharmony_ci pkt_type |= (HCI_DH3 | HCI_DM3); 35128c2ecf20Sopenharmony_ci else 35138c2ecf20Sopenharmony_ci pkt_type &= ~(HCI_DH3 | HCI_DM3); 35148c2ecf20Sopenharmony_ci 35158c2ecf20Sopenharmony_ci if (selected_phys & MGMT_PHY_BR_1M_5SLOT) 35168c2ecf20Sopenharmony_ci pkt_type |= (HCI_DH5 | HCI_DM5); 35178c2ecf20Sopenharmony_ci else 35188c2ecf20Sopenharmony_ci pkt_type &= ~(HCI_DH5 | HCI_DM5); 35198c2ecf20Sopenharmony_ci 35208c2ecf20Sopenharmony_ci if (selected_phys & MGMT_PHY_EDR_2M_1SLOT) 35218c2ecf20Sopenharmony_ci pkt_type &= ~HCI_2DH1; 35228c2ecf20Sopenharmony_ci else 35238c2ecf20Sopenharmony_ci pkt_type |= HCI_2DH1; 35248c2ecf20Sopenharmony_ci 35258c2ecf20Sopenharmony_ci if (selected_phys & MGMT_PHY_EDR_2M_3SLOT) 35268c2ecf20Sopenharmony_ci pkt_type &= ~HCI_2DH3; 35278c2ecf20Sopenharmony_ci else 35288c2ecf20Sopenharmony_ci pkt_type |= HCI_2DH3; 35298c2ecf20Sopenharmony_ci 35308c2ecf20Sopenharmony_ci if (selected_phys & MGMT_PHY_EDR_2M_5SLOT) 35318c2ecf20Sopenharmony_ci pkt_type &= ~HCI_2DH5; 35328c2ecf20Sopenharmony_ci else 35338c2ecf20Sopenharmony_ci pkt_type |= HCI_2DH5; 35348c2ecf20Sopenharmony_ci 35358c2ecf20Sopenharmony_ci if (selected_phys & MGMT_PHY_EDR_3M_1SLOT) 35368c2ecf20Sopenharmony_ci pkt_type &= ~HCI_3DH1; 35378c2ecf20Sopenharmony_ci else 35388c2ecf20Sopenharmony_ci pkt_type |= HCI_3DH1; 35398c2ecf20Sopenharmony_ci 35408c2ecf20Sopenharmony_ci if (selected_phys & MGMT_PHY_EDR_3M_3SLOT) 35418c2ecf20Sopenharmony_ci pkt_type &= ~HCI_3DH3; 35428c2ecf20Sopenharmony_ci else 35438c2ecf20Sopenharmony_ci pkt_type |= HCI_3DH3; 35448c2ecf20Sopenharmony_ci 35458c2ecf20Sopenharmony_ci if (selected_phys & MGMT_PHY_EDR_3M_5SLOT) 35468c2ecf20Sopenharmony_ci pkt_type &= ~HCI_3DH5; 35478c2ecf20Sopenharmony_ci else 35488c2ecf20Sopenharmony_ci pkt_type |= HCI_3DH5; 35498c2ecf20Sopenharmony_ci 35508c2ecf20Sopenharmony_ci if (pkt_type != hdev->pkt_type) { 35518c2ecf20Sopenharmony_ci hdev->pkt_type = pkt_type; 35528c2ecf20Sopenharmony_ci changed = true; 35538c2ecf20Sopenharmony_ci } 35548c2ecf20Sopenharmony_ci 35558c2ecf20Sopenharmony_ci if ((selected_phys & MGMT_PHY_LE_MASK) == 35568c2ecf20Sopenharmony_ci (get_selected_phys(hdev) & MGMT_PHY_LE_MASK)) { 35578c2ecf20Sopenharmony_ci if (changed) 35588c2ecf20Sopenharmony_ci mgmt_phy_configuration_changed(hdev, sk); 35598c2ecf20Sopenharmony_ci 35608c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, 35618c2ecf20Sopenharmony_ci MGMT_OP_SET_PHY_CONFIGURATION, 35628c2ecf20Sopenharmony_ci 0, NULL, 0); 35638c2ecf20Sopenharmony_ci 35648c2ecf20Sopenharmony_ci goto unlock; 35658c2ecf20Sopenharmony_ci } 35668c2ecf20Sopenharmony_ci 35678c2ecf20Sopenharmony_ci cmd = mgmt_pending_add(sk, MGMT_OP_SET_PHY_CONFIGURATION, hdev, data, 35688c2ecf20Sopenharmony_ci len); 35698c2ecf20Sopenharmony_ci if (!cmd) { 35708c2ecf20Sopenharmony_ci err = -ENOMEM; 35718c2ecf20Sopenharmony_ci goto unlock; 35728c2ecf20Sopenharmony_ci } 35738c2ecf20Sopenharmony_ci 35748c2ecf20Sopenharmony_ci hci_req_init(&req, hdev); 35758c2ecf20Sopenharmony_ci 35768c2ecf20Sopenharmony_ci memset(&cp_phy, 0, sizeof(cp_phy)); 35778c2ecf20Sopenharmony_ci 35788c2ecf20Sopenharmony_ci if (!(selected_phys & MGMT_PHY_LE_TX_MASK)) 35798c2ecf20Sopenharmony_ci cp_phy.all_phys |= 0x01; 35808c2ecf20Sopenharmony_ci 35818c2ecf20Sopenharmony_ci if (!(selected_phys & MGMT_PHY_LE_RX_MASK)) 35828c2ecf20Sopenharmony_ci cp_phy.all_phys |= 0x02; 35838c2ecf20Sopenharmony_ci 35848c2ecf20Sopenharmony_ci if (selected_phys & MGMT_PHY_LE_1M_TX) 35858c2ecf20Sopenharmony_ci cp_phy.tx_phys |= HCI_LE_SET_PHY_1M; 35868c2ecf20Sopenharmony_ci 35878c2ecf20Sopenharmony_ci if (selected_phys & MGMT_PHY_LE_2M_TX) 35888c2ecf20Sopenharmony_ci cp_phy.tx_phys |= HCI_LE_SET_PHY_2M; 35898c2ecf20Sopenharmony_ci 35908c2ecf20Sopenharmony_ci if (selected_phys & MGMT_PHY_LE_CODED_TX) 35918c2ecf20Sopenharmony_ci cp_phy.tx_phys |= HCI_LE_SET_PHY_CODED; 35928c2ecf20Sopenharmony_ci 35938c2ecf20Sopenharmony_ci if (selected_phys & MGMT_PHY_LE_1M_RX) 35948c2ecf20Sopenharmony_ci cp_phy.rx_phys |= HCI_LE_SET_PHY_1M; 35958c2ecf20Sopenharmony_ci 35968c2ecf20Sopenharmony_ci if (selected_phys & MGMT_PHY_LE_2M_RX) 35978c2ecf20Sopenharmony_ci cp_phy.rx_phys |= HCI_LE_SET_PHY_2M; 35988c2ecf20Sopenharmony_ci 35998c2ecf20Sopenharmony_ci if (selected_phys & MGMT_PHY_LE_CODED_RX) 36008c2ecf20Sopenharmony_ci cp_phy.rx_phys |= HCI_LE_SET_PHY_CODED; 36018c2ecf20Sopenharmony_ci 36028c2ecf20Sopenharmony_ci hci_req_add(&req, HCI_OP_LE_SET_DEFAULT_PHY, sizeof(cp_phy), &cp_phy); 36038c2ecf20Sopenharmony_ci 36048c2ecf20Sopenharmony_ci err = hci_req_run_skb(&req, set_default_phy_complete); 36058c2ecf20Sopenharmony_ci if (err < 0) 36068c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 36078c2ecf20Sopenharmony_ci 36088c2ecf20Sopenharmony_ciunlock: 36098c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 36108c2ecf20Sopenharmony_ci 36118c2ecf20Sopenharmony_ci return err; 36128c2ecf20Sopenharmony_ci} 36138c2ecf20Sopenharmony_ci 36148c2ecf20Sopenharmony_cistatic int set_blocked_keys(struct sock *sk, struct hci_dev *hdev, void *data, 36158c2ecf20Sopenharmony_ci u16 len) 36168c2ecf20Sopenharmony_ci{ 36178c2ecf20Sopenharmony_ci int err = MGMT_STATUS_SUCCESS; 36188c2ecf20Sopenharmony_ci struct mgmt_cp_set_blocked_keys *keys = data; 36198c2ecf20Sopenharmony_ci const u16 max_key_count = ((U16_MAX - sizeof(*keys)) / 36208c2ecf20Sopenharmony_ci sizeof(struct mgmt_blocked_key_info)); 36218c2ecf20Sopenharmony_ci u16 key_count, expected_len; 36228c2ecf20Sopenharmony_ci int i; 36238c2ecf20Sopenharmony_ci 36248c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 36258c2ecf20Sopenharmony_ci 36268c2ecf20Sopenharmony_ci key_count = __le16_to_cpu(keys->key_count); 36278c2ecf20Sopenharmony_ci if (key_count > max_key_count) { 36288c2ecf20Sopenharmony_ci bt_dev_err(hdev, "too big key_count value %u", key_count); 36298c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_BLOCKED_KEYS, 36308c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 36318c2ecf20Sopenharmony_ci } 36328c2ecf20Sopenharmony_ci 36338c2ecf20Sopenharmony_ci expected_len = struct_size(keys, keys, key_count); 36348c2ecf20Sopenharmony_ci if (expected_len != len) { 36358c2ecf20Sopenharmony_ci bt_dev_err(hdev, "expected %u bytes, got %u bytes", 36368c2ecf20Sopenharmony_ci expected_len, len); 36378c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_BLOCKED_KEYS, 36388c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 36398c2ecf20Sopenharmony_ci } 36408c2ecf20Sopenharmony_ci 36418c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 36428c2ecf20Sopenharmony_ci 36438c2ecf20Sopenharmony_ci hci_blocked_keys_clear(hdev); 36448c2ecf20Sopenharmony_ci 36458c2ecf20Sopenharmony_ci for (i = 0; i < keys->key_count; ++i) { 36468c2ecf20Sopenharmony_ci struct blocked_key *b = kzalloc(sizeof(*b), GFP_KERNEL); 36478c2ecf20Sopenharmony_ci 36488c2ecf20Sopenharmony_ci if (!b) { 36498c2ecf20Sopenharmony_ci err = MGMT_STATUS_NO_RESOURCES; 36508c2ecf20Sopenharmony_ci break; 36518c2ecf20Sopenharmony_ci } 36528c2ecf20Sopenharmony_ci 36538c2ecf20Sopenharmony_ci b->type = keys->keys[i].type; 36548c2ecf20Sopenharmony_ci memcpy(b->val, keys->keys[i].val, sizeof(b->val)); 36558c2ecf20Sopenharmony_ci list_add_rcu(&b->list, &hdev->blocked_keys); 36568c2ecf20Sopenharmony_ci } 36578c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 36588c2ecf20Sopenharmony_ci 36598c2ecf20Sopenharmony_ci return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_BLOCKED_KEYS, 36608c2ecf20Sopenharmony_ci err, NULL, 0); 36618c2ecf20Sopenharmony_ci} 36628c2ecf20Sopenharmony_ci 36638c2ecf20Sopenharmony_cistatic int set_wideband_speech(struct sock *sk, struct hci_dev *hdev, 36648c2ecf20Sopenharmony_ci void *data, u16 len) 36658c2ecf20Sopenharmony_ci{ 36668c2ecf20Sopenharmony_ci struct mgmt_mode *cp = data; 36678c2ecf20Sopenharmony_ci int err; 36688c2ecf20Sopenharmony_ci bool changed = false; 36698c2ecf20Sopenharmony_ci 36708c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 36718c2ecf20Sopenharmony_ci 36728c2ecf20Sopenharmony_ci if (!test_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks)) 36738c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, 36748c2ecf20Sopenharmony_ci MGMT_OP_SET_WIDEBAND_SPEECH, 36758c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_SUPPORTED); 36768c2ecf20Sopenharmony_ci 36778c2ecf20Sopenharmony_ci if (cp->val != 0x00 && cp->val != 0x01) 36788c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, 36798c2ecf20Sopenharmony_ci MGMT_OP_SET_WIDEBAND_SPEECH, 36808c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 36818c2ecf20Sopenharmony_ci 36828c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 36838c2ecf20Sopenharmony_ci 36848c2ecf20Sopenharmony_ci if (pending_find(MGMT_OP_SET_WIDEBAND_SPEECH, hdev)) { 36858c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, 36868c2ecf20Sopenharmony_ci MGMT_OP_SET_WIDEBAND_SPEECH, 36878c2ecf20Sopenharmony_ci MGMT_STATUS_BUSY); 36888c2ecf20Sopenharmony_ci goto unlock; 36898c2ecf20Sopenharmony_ci } 36908c2ecf20Sopenharmony_ci 36918c2ecf20Sopenharmony_ci if (hdev_is_powered(hdev) && 36928c2ecf20Sopenharmony_ci !!cp->val != hci_dev_test_flag(hdev, 36938c2ecf20Sopenharmony_ci HCI_WIDEBAND_SPEECH_ENABLED)) { 36948c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, 36958c2ecf20Sopenharmony_ci MGMT_OP_SET_WIDEBAND_SPEECH, 36968c2ecf20Sopenharmony_ci MGMT_STATUS_REJECTED); 36978c2ecf20Sopenharmony_ci goto unlock; 36988c2ecf20Sopenharmony_ci } 36998c2ecf20Sopenharmony_ci 37008c2ecf20Sopenharmony_ci if (cp->val) 37018c2ecf20Sopenharmony_ci changed = !hci_dev_test_and_set_flag(hdev, 37028c2ecf20Sopenharmony_ci HCI_WIDEBAND_SPEECH_ENABLED); 37038c2ecf20Sopenharmony_ci else 37048c2ecf20Sopenharmony_ci changed = hci_dev_test_and_clear_flag(hdev, 37058c2ecf20Sopenharmony_ci HCI_WIDEBAND_SPEECH_ENABLED); 37068c2ecf20Sopenharmony_ci 37078c2ecf20Sopenharmony_ci err = send_settings_rsp(sk, MGMT_OP_SET_WIDEBAND_SPEECH, hdev); 37088c2ecf20Sopenharmony_ci if (err < 0) 37098c2ecf20Sopenharmony_ci goto unlock; 37108c2ecf20Sopenharmony_ci 37118c2ecf20Sopenharmony_ci if (changed) 37128c2ecf20Sopenharmony_ci err = new_settings(hdev, sk); 37138c2ecf20Sopenharmony_ci 37148c2ecf20Sopenharmony_ciunlock: 37158c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 37168c2ecf20Sopenharmony_ci return err; 37178c2ecf20Sopenharmony_ci} 37188c2ecf20Sopenharmony_ci 37198c2ecf20Sopenharmony_cistatic int read_security_info(struct sock *sk, struct hci_dev *hdev, 37208c2ecf20Sopenharmony_ci void *data, u16 data_len) 37218c2ecf20Sopenharmony_ci{ 37228c2ecf20Sopenharmony_ci char buf[16]; 37238c2ecf20Sopenharmony_ci struct mgmt_rp_read_security_info *rp = (void *)buf; 37248c2ecf20Sopenharmony_ci u16 sec_len = 0; 37258c2ecf20Sopenharmony_ci u8 flags = 0; 37268c2ecf20Sopenharmony_ci 37278c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 37288c2ecf20Sopenharmony_ci 37298c2ecf20Sopenharmony_ci memset(&buf, 0, sizeof(buf)); 37308c2ecf20Sopenharmony_ci 37318c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 37328c2ecf20Sopenharmony_ci 37338c2ecf20Sopenharmony_ci /* When the Read Simple Pairing Options command is supported, then 37348c2ecf20Sopenharmony_ci * the remote public key validation is supported. 37358c2ecf20Sopenharmony_ci */ 37368c2ecf20Sopenharmony_ci if (hdev->commands[41] & 0x08) 37378c2ecf20Sopenharmony_ci flags |= 0x01; /* Remote public key validation (BR/EDR) */ 37388c2ecf20Sopenharmony_ci 37398c2ecf20Sopenharmony_ci flags |= 0x02; /* Remote public key validation (LE) */ 37408c2ecf20Sopenharmony_ci 37418c2ecf20Sopenharmony_ci /* When the Read Encryption Key Size command is supported, then the 37428c2ecf20Sopenharmony_ci * encryption key size is enforced. 37438c2ecf20Sopenharmony_ci */ 37448c2ecf20Sopenharmony_ci if (hdev->commands[20] & 0x10) 37458c2ecf20Sopenharmony_ci flags |= 0x04; /* Encryption key size enforcement (BR/EDR) */ 37468c2ecf20Sopenharmony_ci 37478c2ecf20Sopenharmony_ci flags |= 0x08; /* Encryption key size enforcement (LE) */ 37488c2ecf20Sopenharmony_ci 37498c2ecf20Sopenharmony_ci sec_len = eir_append_data(rp->sec, sec_len, 0x01, &flags, 1); 37508c2ecf20Sopenharmony_ci 37518c2ecf20Sopenharmony_ci /* When the Read Simple Pairing Options command is supported, then 37528c2ecf20Sopenharmony_ci * also max encryption key size information is provided. 37538c2ecf20Sopenharmony_ci */ 37548c2ecf20Sopenharmony_ci if (hdev->commands[41] & 0x08) 37558c2ecf20Sopenharmony_ci sec_len = eir_append_le16(rp->sec, sec_len, 0x02, 37568c2ecf20Sopenharmony_ci hdev->max_enc_key_size); 37578c2ecf20Sopenharmony_ci 37588c2ecf20Sopenharmony_ci sec_len = eir_append_le16(rp->sec, sec_len, 0x03, SMP_MAX_ENC_KEY_SIZE); 37598c2ecf20Sopenharmony_ci 37608c2ecf20Sopenharmony_ci rp->sec_len = cpu_to_le16(sec_len); 37618c2ecf20Sopenharmony_ci 37628c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 37638c2ecf20Sopenharmony_ci 37648c2ecf20Sopenharmony_ci return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_READ_SECURITY_INFO, 0, 37658c2ecf20Sopenharmony_ci rp, sizeof(*rp) + sec_len); 37668c2ecf20Sopenharmony_ci} 37678c2ecf20Sopenharmony_ci 37688c2ecf20Sopenharmony_ci#ifdef CONFIG_BT_FEATURE_DEBUG 37698c2ecf20Sopenharmony_ci/* d4992530-b9ec-469f-ab01-6c481c47da1c */ 37708c2ecf20Sopenharmony_cistatic const u8 debug_uuid[16] = { 37718c2ecf20Sopenharmony_ci 0x1c, 0xda, 0x47, 0x1c, 0x48, 0x6c, 0x01, 0xab, 37728c2ecf20Sopenharmony_ci 0x9f, 0x46, 0xec, 0xb9, 0x30, 0x25, 0x99, 0xd4, 37738c2ecf20Sopenharmony_ci}; 37748c2ecf20Sopenharmony_ci#endif 37758c2ecf20Sopenharmony_ci 37768c2ecf20Sopenharmony_ci/* 671b10b5-42c0-4696-9227-eb28d1b049d6 */ 37778c2ecf20Sopenharmony_cistatic const u8 simult_central_periph_uuid[16] = { 37788c2ecf20Sopenharmony_ci 0xd6, 0x49, 0xb0, 0xd1, 0x28, 0xeb, 0x27, 0x92, 37798c2ecf20Sopenharmony_ci 0x96, 0x46, 0xc0, 0x42, 0xb5, 0x10, 0x1b, 0x67, 37808c2ecf20Sopenharmony_ci}; 37818c2ecf20Sopenharmony_ci 37828c2ecf20Sopenharmony_ci/* 15c0a148-c273-11ea-b3de-0242ac130004 */ 37838c2ecf20Sopenharmony_cistatic const u8 rpa_resolution_uuid[16] = { 37848c2ecf20Sopenharmony_ci 0x04, 0x00, 0x13, 0xac, 0x42, 0x02, 0xde, 0xb3, 37858c2ecf20Sopenharmony_ci 0xea, 0x11, 0x73, 0xc2, 0x48, 0xa1, 0xc0, 0x15, 37868c2ecf20Sopenharmony_ci}; 37878c2ecf20Sopenharmony_ci 37888c2ecf20Sopenharmony_cistatic int read_exp_features_info(struct sock *sk, struct hci_dev *hdev, 37898c2ecf20Sopenharmony_ci void *data, u16 data_len) 37908c2ecf20Sopenharmony_ci{ 37918c2ecf20Sopenharmony_ci char buf[62]; /* Enough space for 3 features */ 37928c2ecf20Sopenharmony_ci struct mgmt_rp_read_exp_features_info *rp = (void *)buf; 37938c2ecf20Sopenharmony_ci u16 idx = 0; 37948c2ecf20Sopenharmony_ci u32 flags; 37958c2ecf20Sopenharmony_ci 37968c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 37978c2ecf20Sopenharmony_ci 37988c2ecf20Sopenharmony_ci memset(&buf, 0, sizeof(buf)); 37998c2ecf20Sopenharmony_ci 38008c2ecf20Sopenharmony_ci#ifdef CONFIG_BT_FEATURE_DEBUG 38018c2ecf20Sopenharmony_ci if (!hdev) { 38028c2ecf20Sopenharmony_ci flags = bt_dbg_get() ? BIT(0) : 0; 38038c2ecf20Sopenharmony_ci 38048c2ecf20Sopenharmony_ci memcpy(rp->features[idx].uuid, debug_uuid, 16); 38058c2ecf20Sopenharmony_ci rp->features[idx].flags = cpu_to_le32(flags); 38068c2ecf20Sopenharmony_ci idx++; 38078c2ecf20Sopenharmony_ci } 38088c2ecf20Sopenharmony_ci#endif 38098c2ecf20Sopenharmony_ci 38108c2ecf20Sopenharmony_ci if (hdev) { 38118c2ecf20Sopenharmony_ci if (test_bit(HCI_QUIRK_VALID_LE_STATES, &hdev->quirks) && 38128c2ecf20Sopenharmony_ci (hdev->le_states[4] & 0x08) && /* Central */ 38138c2ecf20Sopenharmony_ci (hdev->le_states[4] & 0x40) && /* Peripheral */ 38148c2ecf20Sopenharmony_ci (hdev->le_states[3] & 0x10)) /* Simultaneous */ 38158c2ecf20Sopenharmony_ci flags = BIT(0); 38168c2ecf20Sopenharmony_ci else 38178c2ecf20Sopenharmony_ci flags = 0; 38188c2ecf20Sopenharmony_ci 38198c2ecf20Sopenharmony_ci memcpy(rp->features[idx].uuid, simult_central_periph_uuid, 16); 38208c2ecf20Sopenharmony_ci rp->features[idx].flags = cpu_to_le32(flags); 38218c2ecf20Sopenharmony_ci idx++; 38228c2ecf20Sopenharmony_ci } 38238c2ecf20Sopenharmony_ci 38248c2ecf20Sopenharmony_ci if (hdev && use_ll_privacy(hdev)) { 38258c2ecf20Sopenharmony_ci if (hci_dev_test_flag(hdev, HCI_ENABLE_LL_PRIVACY)) 38268c2ecf20Sopenharmony_ci flags = BIT(0) | BIT(1); 38278c2ecf20Sopenharmony_ci else 38288c2ecf20Sopenharmony_ci flags = BIT(1); 38298c2ecf20Sopenharmony_ci 38308c2ecf20Sopenharmony_ci memcpy(rp->features[idx].uuid, rpa_resolution_uuid, 16); 38318c2ecf20Sopenharmony_ci rp->features[idx].flags = cpu_to_le32(flags); 38328c2ecf20Sopenharmony_ci idx++; 38338c2ecf20Sopenharmony_ci } 38348c2ecf20Sopenharmony_ci 38358c2ecf20Sopenharmony_ci rp->feature_count = cpu_to_le16(idx); 38368c2ecf20Sopenharmony_ci 38378c2ecf20Sopenharmony_ci /* After reading the experimental features information, enable 38388c2ecf20Sopenharmony_ci * the events to update client on any future change. 38398c2ecf20Sopenharmony_ci */ 38408c2ecf20Sopenharmony_ci hci_sock_set_flag(sk, HCI_MGMT_EXP_FEATURE_EVENTS); 38418c2ecf20Sopenharmony_ci 38428c2ecf20Sopenharmony_ci return mgmt_cmd_complete(sk, hdev ? hdev->id : MGMT_INDEX_NONE, 38438c2ecf20Sopenharmony_ci MGMT_OP_READ_EXP_FEATURES_INFO, 38448c2ecf20Sopenharmony_ci 0, rp, sizeof(*rp) + (20 * idx)); 38458c2ecf20Sopenharmony_ci} 38468c2ecf20Sopenharmony_ci 38478c2ecf20Sopenharmony_cistatic int exp_ll_privacy_feature_changed(bool enabled, struct hci_dev *hdev, 38488c2ecf20Sopenharmony_ci struct sock *skip) 38498c2ecf20Sopenharmony_ci{ 38508c2ecf20Sopenharmony_ci struct mgmt_ev_exp_feature_changed ev; 38518c2ecf20Sopenharmony_ci 38528c2ecf20Sopenharmony_ci memset(&ev, 0, sizeof(ev)); 38538c2ecf20Sopenharmony_ci memcpy(ev.uuid, rpa_resolution_uuid, 16); 38548c2ecf20Sopenharmony_ci ev.flags = cpu_to_le32((enabled ? BIT(0) : 0) | BIT(1)); 38558c2ecf20Sopenharmony_ci 38568c2ecf20Sopenharmony_ci return mgmt_limited_event(MGMT_EV_EXP_FEATURE_CHANGED, hdev, 38578c2ecf20Sopenharmony_ci &ev, sizeof(ev), 38588c2ecf20Sopenharmony_ci HCI_MGMT_EXP_FEATURE_EVENTS, skip); 38598c2ecf20Sopenharmony_ci 38608c2ecf20Sopenharmony_ci} 38618c2ecf20Sopenharmony_ci 38628c2ecf20Sopenharmony_ci#ifdef CONFIG_BT_FEATURE_DEBUG 38638c2ecf20Sopenharmony_cistatic int exp_debug_feature_changed(bool enabled, struct sock *skip) 38648c2ecf20Sopenharmony_ci{ 38658c2ecf20Sopenharmony_ci struct mgmt_ev_exp_feature_changed ev; 38668c2ecf20Sopenharmony_ci 38678c2ecf20Sopenharmony_ci memset(&ev, 0, sizeof(ev)); 38688c2ecf20Sopenharmony_ci memcpy(ev.uuid, debug_uuid, 16); 38698c2ecf20Sopenharmony_ci ev.flags = cpu_to_le32(enabled ? BIT(0) : 0); 38708c2ecf20Sopenharmony_ci 38718c2ecf20Sopenharmony_ci return mgmt_limited_event(MGMT_EV_EXP_FEATURE_CHANGED, NULL, 38728c2ecf20Sopenharmony_ci &ev, sizeof(ev), 38738c2ecf20Sopenharmony_ci HCI_MGMT_EXP_FEATURE_EVENTS, skip); 38748c2ecf20Sopenharmony_ci} 38758c2ecf20Sopenharmony_ci#endif 38768c2ecf20Sopenharmony_ci 38778c2ecf20Sopenharmony_cistatic int set_exp_feature(struct sock *sk, struct hci_dev *hdev, 38788c2ecf20Sopenharmony_ci void *data, u16 data_len) 38798c2ecf20Sopenharmony_ci{ 38808c2ecf20Sopenharmony_ci struct mgmt_cp_set_exp_feature *cp = data; 38818c2ecf20Sopenharmony_ci struct mgmt_rp_set_exp_feature rp; 38828c2ecf20Sopenharmony_ci 38838c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 38848c2ecf20Sopenharmony_ci 38858c2ecf20Sopenharmony_ci if (!memcmp(cp->uuid, ZERO_KEY, 16)) { 38868c2ecf20Sopenharmony_ci memset(rp.uuid, 0, 16); 38878c2ecf20Sopenharmony_ci rp.flags = cpu_to_le32(0); 38888c2ecf20Sopenharmony_ci 38898c2ecf20Sopenharmony_ci#ifdef CONFIG_BT_FEATURE_DEBUG 38908c2ecf20Sopenharmony_ci if (!hdev) { 38918c2ecf20Sopenharmony_ci bool changed = bt_dbg_get(); 38928c2ecf20Sopenharmony_ci 38938c2ecf20Sopenharmony_ci bt_dbg_set(false); 38948c2ecf20Sopenharmony_ci 38958c2ecf20Sopenharmony_ci if (changed) 38968c2ecf20Sopenharmony_ci exp_debug_feature_changed(false, sk); 38978c2ecf20Sopenharmony_ci } 38988c2ecf20Sopenharmony_ci#endif 38998c2ecf20Sopenharmony_ci 39008c2ecf20Sopenharmony_ci if (hdev && use_ll_privacy(hdev) && !hdev_is_powered(hdev)) { 39018c2ecf20Sopenharmony_ci bool changed = hci_dev_test_flag(hdev, 39028c2ecf20Sopenharmony_ci HCI_ENABLE_LL_PRIVACY); 39038c2ecf20Sopenharmony_ci 39048c2ecf20Sopenharmony_ci hci_dev_clear_flag(hdev, HCI_ENABLE_LL_PRIVACY); 39058c2ecf20Sopenharmony_ci 39068c2ecf20Sopenharmony_ci if (changed) 39078c2ecf20Sopenharmony_ci exp_ll_privacy_feature_changed(false, hdev, sk); 39088c2ecf20Sopenharmony_ci } 39098c2ecf20Sopenharmony_ci 39108c2ecf20Sopenharmony_ci hci_sock_set_flag(sk, HCI_MGMT_EXP_FEATURE_EVENTS); 39118c2ecf20Sopenharmony_ci 39128c2ecf20Sopenharmony_ci return mgmt_cmd_complete(sk, hdev ? hdev->id : MGMT_INDEX_NONE, 39138c2ecf20Sopenharmony_ci MGMT_OP_SET_EXP_FEATURE, 0, 39148c2ecf20Sopenharmony_ci &rp, sizeof(rp)); 39158c2ecf20Sopenharmony_ci } 39168c2ecf20Sopenharmony_ci 39178c2ecf20Sopenharmony_ci#ifdef CONFIG_BT_FEATURE_DEBUG 39188c2ecf20Sopenharmony_ci if (!memcmp(cp->uuid, debug_uuid, 16)) { 39198c2ecf20Sopenharmony_ci bool val, changed; 39208c2ecf20Sopenharmony_ci int err; 39218c2ecf20Sopenharmony_ci 39228c2ecf20Sopenharmony_ci /* Command requires to use the non-controller index */ 39238c2ecf20Sopenharmony_ci if (hdev) 39248c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, 39258c2ecf20Sopenharmony_ci MGMT_OP_SET_EXP_FEATURE, 39268c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_INDEX); 39278c2ecf20Sopenharmony_ci 39288c2ecf20Sopenharmony_ci /* Parameters are limited to a single octet */ 39298c2ecf20Sopenharmony_ci if (data_len != MGMT_SET_EXP_FEATURE_SIZE + 1) 39308c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, MGMT_INDEX_NONE, 39318c2ecf20Sopenharmony_ci MGMT_OP_SET_EXP_FEATURE, 39328c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 39338c2ecf20Sopenharmony_ci 39348c2ecf20Sopenharmony_ci /* Only boolean on/off is supported */ 39358c2ecf20Sopenharmony_ci if (cp->param[0] != 0x00 && cp->param[0] != 0x01) 39368c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, MGMT_INDEX_NONE, 39378c2ecf20Sopenharmony_ci MGMT_OP_SET_EXP_FEATURE, 39388c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 39398c2ecf20Sopenharmony_ci 39408c2ecf20Sopenharmony_ci val = !!cp->param[0]; 39418c2ecf20Sopenharmony_ci changed = val ? !bt_dbg_get() : bt_dbg_get(); 39428c2ecf20Sopenharmony_ci bt_dbg_set(val); 39438c2ecf20Sopenharmony_ci 39448c2ecf20Sopenharmony_ci memcpy(rp.uuid, debug_uuid, 16); 39458c2ecf20Sopenharmony_ci rp.flags = cpu_to_le32(val ? BIT(0) : 0); 39468c2ecf20Sopenharmony_ci 39478c2ecf20Sopenharmony_ci hci_sock_set_flag(sk, HCI_MGMT_EXP_FEATURE_EVENTS); 39488c2ecf20Sopenharmony_ci 39498c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, MGMT_INDEX_NONE, 39508c2ecf20Sopenharmony_ci MGMT_OP_SET_EXP_FEATURE, 0, 39518c2ecf20Sopenharmony_ci &rp, sizeof(rp)); 39528c2ecf20Sopenharmony_ci 39538c2ecf20Sopenharmony_ci if (changed) 39548c2ecf20Sopenharmony_ci exp_debug_feature_changed(val, sk); 39558c2ecf20Sopenharmony_ci 39568c2ecf20Sopenharmony_ci return err; 39578c2ecf20Sopenharmony_ci } 39588c2ecf20Sopenharmony_ci#endif 39598c2ecf20Sopenharmony_ci 39608c2ecf20Sopenharmony_ci if (!memcmp(cp->uuid, rpa_resolution_uuid, 16)) { 39618c2ecf20Sopenharmony_ci bool val, changed; 39628c2ecf20Sopenharmony_ci int err; 39638c2ecf20Sopenharmony_ci u32 flags; 39648c2ecf20Sopenharmony_ci 39658c2ecf20Sopenharmony_ci /* Command requires to use the controller index */ 39668c2ecf20Sopenharmony_ci if (!hdev) 39678c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, MGMT_INDEX_NONE, 39688c2ecf20Sopenharmony_ci MGMT_OP_SET_EXP_FEATURE, 39698c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_INDEX); 39708c2ecf20Sopenharmony_ci 39718c2ecf20Sopenharmony_ci /* Changes can only be made when controller is powered down */ 39728c2ecf20Sopenharmony_ci if (hdev_is_powered(hdev)) 39738c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, 39748c2ecf20Sopenharmony_ci MGMT_OP_SET_EXP_FEATURE, 39758c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_POWERED); 39768c2ecf20Sopenharmony_ci 39778c2ecf20Sopenharmony_ci /* Parameters are limited to a single octet */ 39788c2ecf20Sopenharmony_ci if (data_len != MGMT_SET_EXP_FEATURE_SIZE + 1) 39798c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, 39808c2ecf20Sopenharmony_ci MGMT_OP_SET_EXP_FEATURE, 39818c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 39828c2ecf20Sopenharmony_ci 39838c2ecf20Sopenharmony_ci /* Only boolean on/off is supported */ 39848c2ecf20Sopenharmony_ci if (cp->param[0] != 0x00 && cp->param[0] != 0x01) 39858c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, 39868c2ecf20Sopenharmony_ci MGMT_OP_SET_EXP_FEATURE, 39878c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 39888c2ecf20Sopenharmony_ci 39898c2ecf20Sopenharmony_ci val = !!cp->param[0]; 39908c2ecf20Sopenharmony_ci 39918c2ecf20Sopenharmony_ci if (val) { 39928c2ecf20Sopenharmony_ci changed = !hci_dev_test_flag(hdev, 39938c2ecf20Sopenharmony_ci HCI_ENABLE_LL_PRIVACY); 39948c2ecf20Sopenharmony_ci hci_dev_set_flag(hdev, HCI_ENABLE_LL_PRIVACY); 39958c2ecf20Sopenharmony_ci hci_dev_clear_flag(hdev, HCI_ADVERTISING); 39968c2ecf20Sopenharmony_ci 39978c2ecf20Sopenharmony_ci /* Enable LL privacy + supported settings changed */ 39988c2ecf20Sopenharmony_ci flags = BIT(0) | BIT(1); 39998c2ecf20Sopenharmony_ci } else { 40008c2ecf20Sopenharmony_ci changed = hci_dev_test_flag(hdev, 40018c2ecf20Sopenharmony_ci HCI_ENABLE_LL_PRIVACY); 40028c2ecf20Sopenharmony_ci hci_dev_clear_flag(hdev, HCI_ENABLE_LL_PRIVACY); 40038c2ecf20Sopenharmony_ci 40048c2ecf20Sopenharmony_ci /* Disable LL privacy + supported settings changed */ 40058c2ecf20Sopenharmony_ci flags = BIT(1); 40068c2ecf20Sopenharmony_ci } 40078c2ecf20Sopenharmony_ci 40088c2ecf20Sopenharmony_ci memcpy(rp.uuid, rpa_resolution_uuid, 16); 40098c2ecf20Sopenharmony_ci rp.flags = cpu_to_le32(flags); 40108c2ecf20Sopenharmony_ci 40118c2ecf20Sopenharmony_ci hci_sock_set_flag(sk, HCI_MGMT_EXP_FEATURE_EVENTS); 40128c2ecf20Sopenharmony_ci 40138c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, 40148c2ecf20Sopenharmony_ci MGMT_OP_SET_EXP_FEATURE, 0, 40158c2ecf20Sopenharmony_ci &rp, sizeof(rp)); 40168c2ecf20Sopenharmony_ci 40178c2ecf20Sopenharmony_ci if (changed) 40188c2ecf20Sopenharmony_ci exp_ll_privacy_feature_changed(val, hdev, sk); 40198c2ecf20Sopenharmony_ci 40208c2ecf20Sopenharmony_ci return err; 40218c2ecf20Sopenharmony_ci } 40228c2ecf20Sopenharmony_ci 40238c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev ? hdev->id : MGMT_INDEX_NONE, 40248c2ecf20Sopenharmony_ci MGMT_OP_SET_EXP_FEATURE, 40258c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_SUPPORTED); 40268c2ecf20Sopenharmony_ci} 40278c2ecf20Sopenharmony_ci 40288c2ecf20Sopenharmony_ci#define SUPPORTED_DEVICE_FLAGS() ((1U << HCI_CONN_FLAG_MAX) - 1) 40298c2ecf20Sopenharmony_ci 40308c2ecf20Sopenharmony_cistatic int get_device_flags(struct sock *sk, struct hci_dev *hdev, void *data, 40318c2ecf20Sopenharmony_ci u16 data_len) 40328c2ecf20Sopenharmony_ci{ 40338c2ecf20Sopenharmony_ci struct mgmt_cp_get_device_flags *cp = data; 40348c2ecf20Sopenharmony_ci struct mgmt_rp_get_device_flags rp; 40358c2ecf20Sopenharmony_ci struct bdaddr_list_with_flags *br_params; 40368c2ecf20Sopenharmony_ci struct hci_conn_params *params; 40378c2ecf20Sopenharmony_ci u32 supported_flags = SUPPORTED_DEVICE_FLAGS(); 40388c2ecf20Sopenharmony_ci u32 current_flags = 0; 40398c2ecf20Sopenharmony_ci u8 status = MGMT_STATUS_INVALID_PARAMS; 40408c2ecf20Sopenharmony_ci 40418c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "Get device flags %pMR (type 0x%x)\n", 40428c2ecf20Sopenharmony_ci &cp->addr.bdaddr, cp->addr.type); 40438c2ecf20Sopenharmony_ci 40448c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 40458c2ecf20Sopenharmony_ci 40468c2ecf20Sopenharmony_ci memset(&rp, 0, sizeof(rp)); 40478c2ecf20Sopenharmony_ci 40488c2ecf20Sopenharmony_ci if (cp->addr.type == BDADDR_BREDR) { 40498c2ecf20Sopenharmony_ci br_params = hci_bdaddr_list_lookup_with_flags(&hdev->accept_list, 40508c2ecf20Sopenharmony_ci &cp->addr.bdaddr, 40518c2ecf20Sopenharmony_ci cp->addr.type); 40528c2ecf20Sopenharmony_ci if (!br_params) 40538c2ecf20Sopenharmony_ci goto done; 40548c2ecf20Sopenharmony_ci 40558c2ecf20Sopenharmony_ci current_flags = br_params->current_flags; 40568c2ecf20Sopenharmony_ci } else { 40578c2ecf20Sopenharmony_ci params = hci_conn_params_lookup(hdev, &cp->addr.bdaddr, 40588c2ecf20Sopenharmony_ci le_addr_type(cp->addr.type)); 40598c2ecf20Sopenharmony_ci 40608c2ecf20Sopenharmony_ci if (!params) 40618c2ecf20Sopenharmony_ci goto done; 40628c2ecf20Sopenharmony_ci 40638c2ecf20Sopenharmony_ci current_flags = params->current_flags; 40648c2ecf20Sopenharmony_ci } 40658c2ecf20Sopenharmony_ci 40668c2ecf20Sopenharmony_ci bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); 40678c2ecf20Sopenharmony_ci rp.addr.type = cp->addr.type; 40688c2ecf20Sopenharmony_ci rp.supported_flags = cpu_to_le32(supported_flags); 40698c2ecf20Sopenharmony_ci rp.current_flags = cpu_to_le32(current_flags); 40708c2ecf20Sopenharmony_ci 40718c2ecf20Sopenharmony_ci status = MGMT_STATUS_SUCCESS; 40728c2ecf20Sopenharmony_ci 40738c2ecf20Sopenharmony_cidone: 40748c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 40758c2ecf20Sopenharmony_ci 40768c2ecf20Sopenharmony_ci return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_GET_DEVICE_FLAGS, status, 40778c2ecf20Sopenharmony_ci &rp, sizeof(rp)); 40788c2ecf20Sopenharmony_ci} 40798c2ecf20Sopenharmony_ci 40808c2ecf20Sopenharmony_cistatic void device_flags_changed(struct sock *sk, struct hci_dev *hdev, 40818c2ecf20Sopenharmony_ci bdaddr_t *bdaddr, u8 bdaddr_type, 40828c2ecf20Sopenharmony_ci u32 supported_flags, u32 current_flags) 40838c2ecf20Sopenharmony_ci{ 40848c2ecf20Sopenharmony_ci struct mgmt_ev_device_flags_changed ev; 40858c2ecf20Sopenharmony_ci 40868c2ecf20Sopenharmony_ci bacpy(&ev.addr.bdaddr, bdaddr); 40878c2ecf20Sopenharmony_ci ev.addr.type = bdaddr_type; 40888c2ecf20Sopenharmony_ci ev.supported_flags = cpu_to_le32(supported_flags); 40898c2ecf20Sopenharmony_ci ev.current_flags = cpu_to_le32(current_flags); 40908c2ecf20Sopenharmony_ci 40918c2ecf20Sopenharmony_ci mgmt_event(MGMT_EV_DEVICE_FLAGS_CHANGED, hdev, &ev, sizeof(ev), sk); 40928c2ecf20Sopenharmony_ci} 40938c2ecf20Sopenharmony_ci 40948c2ecf20Sopenharmony_cistatic int set_device_flags(struct sock *sk, struct hci_dev *hdev, void *data, 40958c2ecf20Sopenharmony_ci u16 len) 40968c2ecf20Sopenharmony_ci{ 40978c2ecf20Sopenharmony_ci struct mgmt_cp_set_device_flags *cp = data; 40988c2ecf20Sopenharmony_ci struct bdaddr_list_with_flags *br_params; 40998c2ecf20Sopenharmony_ci struct hci_conn_params *params; 41008c2ecf20Sopenharmony_ci u8 status = MGMT_STATUS_INVALID_PARAMS; 41018c2ecf20Sopenharmony_ci u32 supported_flags = SUPPORTED_DEVICE_FLAGS(); 41028c2ecf20Sopenharmony_ci u32 current_flags = __le32_to_cpu(cp->current_flags); 41038c2ecf20Sopenharmony_ci 41048c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "Set device flags %pMR (type 0x%x) = 0x%x", 41058c2ecf20Sopenharmony_ci &cp->addr.bdaddr, cp->addr.type, 41068c2ecf20Sopenharmony_ci __le32_to_cpu(current_flags)); 41078c2ecf20Sopenharmony_ci 41088c2ecf20Sopenharmony_ci if ((supported_flags | current_flags) != supported_flags) { 41098c2ecf20Sopenharmony_ci bt_dev_warn(hdev, "Bad flag given (0x%x) vs supported (0x%0x)", 41108c2ecf20Sopenharmony_ci current_flags, supported_flags); 41118c2ecf20Sopenharmony_ci goto done; 41128c2ecf20Sopenharmony_ci } 41138c2ecf20Sopenharmony_ci 41148c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 41158c2ecf20Sopenharmony_ci 41168c2ecf20Sopenharmony_ci if (cp->addr.type == BDADDR_BREDR) { 41178c2ecf20Sopenharmony_ci br_params = hci_bdaddr_list_lookup_with_flags(&hdev->accept_list, 41188c2ecf20Sopenharmony_ci &cp->addr.bdaddr, 41198c2ecf20Sopenharmony_ci cp->addr.type); 41208c2ecf20Sopenharmony_ci 41218c2ecf20Sopenharmony_ci if (br_params) { 41228c2ecf20Sopenharmony_ci br_params->current_flags = current_flags; 41238c2ecf20Sopenharmony_ci status = MGMT_STATUS_SUCCESS; 41248c2ecf20Sopenharmony_ci } else { 41258c2ecf20Sopenharmony_ci bt_dev_warn(hdev, "No such BR/EDR device %pMR (0x%x)", 41268c2ecf20Sopenharmony_ci &cp->addr.bdaddr, cp->addr.type); 41278c2ecf20Sopenharmony_ci } 41288c2ecf20Sopenharmony_ci } else { 41298c2ecf20Sopenharmony_ci params = hci_conn_params_lookup(hdev, &cp->addr.bdaddr, 41308c2ecf20Sopenharmony_ci le_addr_type(cp->addr.type)); 41318c2ecf20Sopenharmony_ci if (params) { 41328c2ecf20Sopenharmony_ci params->current_flags = current_flags; 41338c2ecf20Sopenharmony_ci status = MGMT_STATUS_SUCCESS; 41348c2ecf20Sopenharmony_ci } else { 41358c2ecf20Sopenharmony_ci bt_dev_warn(hdev, "No such LE device %pMR (0x%x)", 41368c2ecf20Sopenharmony_ci &cp->addr.bdaddr, 41378c2ecf20Sopenharmony_ci le_addr_type(cp->addr.type)); 41388c2ecf20Sopenharmony_ci } 41398c2ecf20Sopenharmony_ci } 41408c2ecf20Sopenharmony_ci 41418c2ecf20Sopenharmony_cidone: 41428c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 41438c2ecf20Sopenharmony_ci 41448c2ecf20Sopenharmony_ci if (status == MGMT_STATUS_SUCCESS) 41458c2ecf20Sopenharmony_ci device_flags_changed(sk, hdev, &cp->addr.bdaddr, cp->addr.type, 41468c2ecf20Sopenharmony_ci supported_flags, current_flags); 41478c2ecf20Sopenharmony_ci 41488c2ecf20Sopenharmony_ci return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_DEVICE_FLAGS, status, 41498c2ecf20Sopenharmony_ci &cp->addr, sizeof(cp->addr)); 41508c2ecf20Sopenharmony_ci} 41518c2ecf20Sopenharmony_ci 41528c2ecf20Sopenharmony_cistatic void mgmt_adv_monitor_added(struct sock *sk, struct hci_dev *hdev, 41538c2ecf20Sopenharmony_ci u16 handle) 41548c2ecf20Sopenharmony_ci{ 41558c2ecf20Sopenharmony_ci struct mgmt_ev_adv_monitor_added ev; 41568c2ecf20Sopenharmony_ci 41578c2ecf20Sopenharmony_ci ev.monitor_handle = cpu_to_le16(handle); 41588c2ecf20Sopenharmony_ci 41598c2ecf20Sopenharmony_ci mgmt_event(MGMT_EV_ADV_MONITOR_ADDED, hdev, &ev, sizeof(ev), sk); 41608c2ecf20Sopenharmony_ci} 41618c2ecf20Sopenharmony_ci 41628c2ecf20Sopenharmony_cistatic void mgmt_adv_monitor_removed(struct sock *sk, struct hci_dev *hdev, 41638c2ecf20Sopenharmony_ci u16 handle) 41648c2ecf20Sopenharmony_ci{ 41658c2ecf20Sopenharmony_ci struct mgmt_ev_adv_monitor_added ev; 41668c2ecf20Sopenharmony_ci 41678c2ecf20Sopenharmony_ci ev.monitor_handle = cpu_to_le16(handle); 41688c2ecf20Sopenharmony_ci 41698c2ecf20Sopenharmony_ci mgmt_event(MGMT_EV_ADV_MONITOR_REMOVED, hdev, &ev, sizeof(ev), sk); 41708c2ecf20Sopenharmony_ci} 41718c2ecf20Sopenharmony_ci 41728c2ecf20Sopenharmony_cistatic int read_adv_mon_features(struct sock *sk, struct hci_dev *hdev, 41738c2ecf20Sopenharmony_ci void *data, u16 len) 41748c2ecf20Sopenharmony_ci{ 41758c2ecf20Sopenharmony_ci struct adv_monitor *monitor = NULL; 41768c2ecf20Sopenharmony_ci struct mgmt_rp_read_adv_monitor_features *rp = NULL; 41778c2ecf20Sopenharmony_ci int handle, err; 41788c2ecf20Sopenharmony_ci size_t rp_size = 0; 41798c2ecf20Sopenharmony_ci __u32 supported = 0; 41808c2ecf20Sopenharmony_ci __u16 num_handles = 0; 41818c2ecf20Sopenharmony_ci __u16 handles[HCI_MAX_ADV_MONITOR_NUM_HANDLES]; 41828c2ecf20Sopenharmony_ci 41838c2ecf20Sopenharmony_ci BT_DBG("request for %s", hdev->name); 41848c2ecf20Sopenharmony_ci 41858c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 41868c2ecf20Sopenharmony_ci 41878c2ecf20Sopenharmony_ci if (msft_get_features(hdev) & MSFT_FEATURE_MASK_LE_ADV_MONITOR) 41888c2ecf20Sopenharmony_ci supported |= MGMT_ADV_MONITOR_FEATURE_MASK_OR_PATTERNS; 41898c2ecf20Sopenharmony_ci 41908c2ecf20Sopenharmony_ci idr_for_each_entry(&hdev->adv_monitors_idr, monitor, handle) { 41918c2ecf20Sopenharmony_ci handles[num_handles++] = monitor->handle; 41928c2ecf20Sopenharmony_ci } 41938c2ecf20Sopenharmony_ci 41948c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 41958c2ecf20Sopenharmony_ci 41968c2ecf20Sopenharmony_ci rp_size = sizeof(*rp) + (num_handles * sizeof(u16)); 41978c2ecf20Sopenharmony_ci rp = kmalloc(rp_size, GFP_KERNEL); 41988c2ecf20Sopenharmony_ci if (!rp) 41998c2ecf20Sopenharmony_ci return -ENOMEM; 42008c2ecf20Sopenharmony_ci 42018c2ecf20Sopenharmony_ci /* Once controller-based monitoring is in place, the enabled_features 42028c2ecf20Sopenharmony_ci * should reflect the use. 42038c2ecf20Sopenharmony_ci */ 42048c2ecf20Sopenharmony_ci rp->supported_features = cpu_to_le32(supported); 42058c2ecf20Sopenharmony_ci rp->enabled_features = 0; 42068c2ecf20Sopenharmony_ci rp->max_num_handles = cpu_to_le16(HCI_MAX_ADV_MONITOR_NUM_HANDLES); 42078c2ecf20Sopenharmony_ci rp->max_num_patterns = HCI_MAX_ADV_MONITOR_NUM_PATTERNS; 42088c2ecf20Sopenharmony_ci rp->num_handles = cpu_to_le16(num_handles); 42098c2ecf20Sopenharmony_ci if (num_handles) 42108c2ecf20Sopenharmony_ci memcpy(&rp->handles, &handles, (num_handles * sizeof(u16))); 42118c2ecf20Sopenharmony_ci 42128c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, 42138c2ecf20Sopenharmony_ci MGMT_OP_READ_ADV_MONITOR_FEATURES, 42148c2ecf20Sopenharmony_ci MGMT_STATUS_SUCCESS, rp, rp_size); 42158c2ecf20Sopenharmony_ci 42168c2ecf20Sopenharmony_ci kfree(rp); 42178c2ecf20Sopenharmony_ci 42188c2ecf20Sopenharmony_ci return err; 42198c2ecf20Sopenharmony_ci} 42208c2ecf20Sopenharmony_ci 42218c2ecf20Sopenharmony_cistatic int add_adv_patterns_monitor(struct sock *sk, struct hci_dev *hdev, 42228c2ecf20Sopenharmony_ci void *data, u16 len) 42238c2ecf20Sopenharmony_ci{ 42248c2ecf20Sopenharmony_ci struct mgmt_cp_add_adv_patterns_monitor *cp = data; 42258c2ecf20Sopenharmony_ci struct mgmt_rp_add_adv_patterns_monitor rp; 42268c2ecf20Sopenharmony_ci struct adv_monitor *m = NULL; 42278c2ecf20Sopenharmony_ci struct adv_pattern *p = NULL; 42288c2ecf20Sopenharmony_ci unsigned int mp_cnt = 0, prev_adv_monitors_cnt; 42298c2ecf20Sopenharmony_ci __u8 cp_ofst = 0, cp_len = 0; 42308c2ecf20Sopenharmony_ci int err, i; 42318c2ecf20Sopenharmony_ci 42328c2ecf20Sopenharmony_ci BT_DBG("request for %s", hdev->name); 42338c2ecf20Sopenharmony_ci 42348c2ecf20Sopenharmony_ci if (len <= sizeof(*cp) || cp->pattern_count == 0) { 42358c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, 42368c2ecf20Sopenharmony_ci MGMT_OP_ADD_ADV_PATTERNS_MONITOR, 42378c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 42388c2ecf20Sopenharmony_ci goto failed; 42398c2ecf20Sopenharmony_ci } 42408c2ecf20Sopenharmony_ci 42418c2ecf20Sopenharmony_ci m = kmalloc(sizeof(*m), GFP_KERNEL); 42428c2ecf20Sopenharmony_ci if (!m) { 42438c2ecf20Sopenharmony_ci err = -ENOMEM; 42448c2ecf20Sopenharmony_ci goto failed; 42458c2ecf20Sopenharmony_ci } 42468c2ecf20Sopenharmony_ci 42478c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&m->patterns); 42488c2ecf20Sopenharmony_ci m->active = false; 42498c2ecf20Sopenharmony_ci 42508c2ecf20Sopenharmony_ci for (i = 0; i < cp->pattern_count; i++) { 42518c2ecf20Sopenharmony_ci if (++mp_cnt > HCI_MAX_ADV_MONITOR_NUM_PATTERNS) { 42528c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, 42538c2ecf20Sopenharmony_ci MGMT_OP_ADD_ADV_PATTERNS_MONITOR, 42548c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 42558c2ecf20Sopenharmony_ci goto failed; 42568c2ecf20Sopenharmony_ci } 42578c2ecf20Sopenharmony_ci 42588c2ecf20Sopenharmony_ci cp_ofst = cp->patterns[i].offset; 42598c2ecf20Sopenharmony_ci cp_len = cp->patterns[i].length; 42608c2ecf20Sopenharmony_ci if (cp_ofst >= HCI_MAX_AD_LENGTH || 42618c2ecf20Sopenharmony_ci cp_len > HCI_MAX_AD_LENGTH || 42628c2ecf20Sopenharmony_ci (cp_ofst + cp_len) > HCI_MAX_AD_LENGTH) { 42638c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, 42648c2ecf20Sopenharmony_ci MGMT_OP_ADD_ADV_PATTERNS_MONITOR, 42658c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 42668c2ecf20Sopenharmony_ci goto failed; 42678c2ecf20Sopenharmony_ci } 42688c2ecf20Sopenharmony_ci 42698c2ecf20Sopenharmony_ci p = kmalloc(sizeof(*p), GFP_KERNEL); 42708c2ecf20Sopenharmony_ci if (!p) { 42718c2ecf20Sopenharmony_ci err = -ENOMEM; 42728c2ecf20Sopenharmony_ci goto failed; 42738c2ecf20Sopenharmony_ci } 42748c2ecf20Sopenharmony_ci 42758c2ecf20Sopenharmony_ci p->ad_type = cp->patterns[i].ad_type; 42768c2ecf20Sopenharmony_ci p->offset = cp->patterns[i].offset; 42778c2ecf20Sopenharmony_ci p->length = cp->patterns[i].length; 42788c2ecf20Sopenharmony_ci memcpy(p->value, cp->patterns[i].value, p->length); 42798c2ecf20Sopenharmony_ci 42808c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&p->list); 42818c2ecf20Sopenharmony_ci list_add(&p->list, &m->patterns); 42828c2ecf20Sopenharmony_ci } 42838c2ecf20Sopenharmony_ci 42848c2ecf20Sopenharmony_ci if (mp_cnt != cp->pattern_count) { 42858c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, 42868c2ecf20Sopenharmony_ci MGMT_OP_ADD_ADV_PATTERNS_MONITOR, 42878c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 42888c2ecf20Sopenharmony_ci goto failed; 42898c2ecf20Sopenharmony_ci } 42908c2ecf20Sopenharmony_ci 42918c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 42928c2ecf20Sopenharmony_ci 42938c2ecf20Sopenharmony_ci prev_adv_monitors_cnt = hdev->adv_monitors_cnt; 42948c2ecf20Sopenharmony_ci 42958c2ecf20Sopenharmony_ci err = hci_add_adv_monitor(hdev, m); 42968c2ecf20Sopenharmony_ci if (err) { 42978c2ecf20Sopenharmony_ci if (err == -ENOSPC) { 42988c2ecf20Sopenharmony_ci mgmt_cmd_status(sk, hdev->id, 42998c2ecf20Sopenharmony_ci MGMT_OP_ADD_ADV_PATTERNS_MONITOR, 43008c2ecf20Sopenharmony_ci MGMT_STATUS_NO_RESOURCES); 43018c2ecf20Sopenharmony_ci } 43028c2ecf20Sopenharmony_ci goto unlock; 43038c2ecf20Sopenharmony_ci } 43048c2ecf20Sopenharmony_ci 43058c2ecf20Sopenharmony_ci if (hdev->adv_monitors_cnt > prev_adv_monitors_cnt) 43068c2ecf20Sopenharmony_ci mgmt_adv_monitor_added(sk, hdev, m->handle); 43078c2ecf20Sopenharmony_ci 43088c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 43098c2ecf20Sopenharmony_ci 43108c2ecf20Sopenharmony_ci rp.monitor_handle = cpu_to_le16(m->handle); 43118c2ecf20Sopenharmony_ci 43128c2ecf20Sopenharmony_ci return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_ADD_ADV_PATTERNS_MONITOR, 43138c2ecf20Sopenharmony_ci MGMT_STATUS_SUCCESS, &rp, sizeof(rp)); 43148c2ecf20Sopenharmony_ci 43158c2ecf20Sopenharmony_ciunlock: 43168c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 43178c2ecf20Sopenharmony_ci 43188c2ecf20Sopenharmony_cifailed: 43198c2ecf20Sopenharmony_ci hci_free_adv_monitor(m); 43208c2ecf20Sopenharmony_ci return err; 43218c2ecf20Sopenharmony_ci} 43228c2ecf20Sopenharmony_ci 43238c2ecf20Sopenharmony_cistatic int remove_adv_monitor(struct sock *sk, struct hci_dev *hdev, 43248c2ecf20Sopenharmony_ci void *data, u16 len) 43258c2ecf20Sopenharmony_ci{ 43268c2ecf20Sopenharmony_ci struct mgmt_cp_remove_adv_monitor *cp = data; 43278c2ecf20Sopenharmony_ci struct mgmt_rp_remove_adv_monitor rp; 43288c2ecf20Sopenharmony_ci unsigned int prev_adv_monitors_cnt; 43298c2ecf20Sopenharmony_ci u16 handle; 43308c2ecf20Sopenharmony_ci int err; 43318c2ecf20Sopenharmony_ci 43328c2ecf20Sopenharmony_ci BT_DBG("request for %s", hdev->name); 43338c2ecf20Sopenharmony_ci 43348c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 43358c2ecf20Sopenharmony_ci 43368c2ecf20Sopenharmony_ci handle = __le16_to_cpu(cp->monitor_handle); 43378c2ecf20Sopenharmony_ci prev_adv_monitors_cnt = hdev->adv_monitors_cnt; 43388c2ecf20Sopenharmony_ci 43398c2ecf20Sopenharmony_ci err = hci_remove_adv_monitor(hdev, handle); 43408c2ecf20Sopenharmony_ci if (err == -ENOENT) { 43418c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_REMOVE_ADV_MONITOR, 43428c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_INDEX); 43438c2ecf20Sopenharmony_ci goto unlock; 43448c2ecf20Sopenharmony_ci } 43458c2ecf20Sopenharmony_ci 43468c2ecf20Sopenharmony_ci if (hdev->adv_monitors_cnt < prev_adv_monitors_cnt) 43478c2ecf20Sopenharmony_ci mgmt_adv_monitor_removed(sk, hdev, handle); 43488c2ecf20Sopenharmony_ci 43498c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 43508c2ecf20Sopenharmony_ci 43518c2ecf20Sopenharmony_ci rp.monitor_handle = cp->monitor_handle; 43528c2ecf20Sopenharmony_ci 43538c2ecf20Sopenharmony_ci return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_ADV_MONITOR, 43548c2ecf20Sopenharmony_ci MGMT_STATUS_SUCCESS, &rp, sizeof(rp)); 43558c2ecf20Sopenharmony_ci 43568c2ecf20Sopenharmony_ciunlock: 43578c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 43588c2ecf20Sopenharmony_ci return err; 43598c2ecf20Sopenharmony_ci} 43608c2ecf20Sopenharmony_ci 43618c2ecf20Sopenharmony_cistatic void read_local_oob_data_complete(struct hci_dev *hdev, u8 status, 43628c2ecf20Sopenharmony_ci u16 opcode, struct sk_buff *skb) 43638c2ecf20Sopenharmony_ci{ 43648c2ecf20Sopenharmony_ci struct mgmt_rp_read_local_oob_data mgmt_rp; 43658c2ecf20Sopenharmony_ci size_t rp_size = sizeof(mgmt_rp); 43668c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 43678c2ecf20Sopenharmony_ci 43688c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "status %u", status); 43698c2ecf20Sopenharmony_ci 43708c2ecf20Sopenharmony_ci cmd = pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev); 43718c2ecf20Sopenharmony_ci if (!cmd) 43728c2ecf20Sopenharmony_ci return; 43738c2ecf20Sopenharmony_ci 43748c2ecf20Sopenharmony_ci if (status || !skb) { 43758c2ecf20Sopenharmony_ci mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA, 43768c2ecf20Sopenharmony_ci status ? mgmt_status(status) : MGMT_STATUS_FAILED); 43778c2ecf20Sopenharmony_ci goto remove; 43788c2ecf20Sopenharmony_ci } 43798c2ecf20Sopenharmony_ci 43808c2ecf20Sopenharmony_ci memset(&mgmt_rp, 0, sizeof(mgmt_rp)); 43818c2ecf20Sopenharmony_ci 43828c2ecf20Sopenharmony_ci if (opcode == HCI_OP_READ_LOCAL_OOB_DATA) { 43838c2ecf20Sopenharmony_ci struct hci_rp_read_local_oob_data *rp = (void *) skb->data; 43848c2ecf20Sopenharmony_ci 43858c2ecf20Sopenharmony_ci if (skb->len < sizeof(*rp)) { 43868c2ecf20Sopenharmony_ci mgmt_cmd_status(cmd->sk, hdev->id, 43878c2ecf20Sopenharmony_ci MGMT_OP_READ_LOCAL_OOB_DATA, 43888c2ecf20Sopenharmony_ci MGMT_STATUS_FAILED); 43898c2ecf20Sopenharmony_ci goto remove; 43908c2ecf20Sopenharmony_ci } 43918c2ecf20Sopenharmony_ci 43928c2ecf20Sopenharmony_ci memcpy(mgmt_rp.hash192, rp->hash, sizeof(rp->hash)); 43938c2ecf20Sopenharmony_ci memcpy(mgmt_rp.rand192, rp->rand, sizeof(rp->rand)); 43948c2ecf20Sopenharmony_ci 43958c2ecf20Sopenharmony_ci rp_size -= sizeof(mgmt_rp.hash256) + sizeof(mgmt_rp.rand256); 43968c2ecf20Sopenharmony_ci } else { 43978c2ecf20Sopenharmony_ci struct hci_rp_read_local_oob_ext_data *rp = (void *) skb->data; 43988c2ecf20Sopenharmony_ci 43998c2ecf20Sopenharmony_ci if (skb->len < sizeof(*rp)) { 44008c2ecf20Sopenharmony_ci mgmt_cmd_status(cmd->sk, hdev->id, 44018c2ecf20Sopenharmony_ci MGMT_OP_READ_LOCAL_OOB_DATA, 44028c2ecf20Sopenharmony_ci MGMT_STATUS_FAILED); 44038c2ecf20Sopenharmony_ci goto remove; 44048c2ecf20Sopenharmony_ci } 44058c2ecf20Sopenharmony_ci 44068c2ecf20Sopenharmony_ci memcpy(mgmt_rp.hash192, rp->hash192, sizeof(rp->hash192)); 44078c2ecf20Sopenharmony_ci memcpy(mgmt_rp.rand192, rp->rand192, sizeof(rp->rand192)); 44088c2ecf20Sopenharmony_ci 44098c2ecf20Sopenharmony_ci memcpy(mgmt_rp.hash256, rp->hash256, sizeof(rp->hash256)); 44108c2ecf20Sopenharmony_ci memcpy(mgmt_rp.rand256, rp->rand256, sizeof(rp->rand256)); 44118c2ecf20Sopenharmony_ci } 44128c2ecf20Sopenharmony_ci 44138c2ecf20Sopenharmony_ci mgmt_cmd_complete(cmd->sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA, 44148c2ecf20Sopenharmony_ci MGMT_STATUS_SUCCESS, &mgmt_rp, rp_size); 44158c2ecf20Sopenharmony_ci 44168c2ecf20Sopenharmony_ciremove: 44178c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 44188c2ecf20Sopenharmony_ci} 44198c2ecf20Sopenharmony_ci 44208c2ecf20Sopenharmony_cistatic int read_local_oob_data(struct sock *sk, struct hci_dev *hdev, 44218c2ecf20Sopenharmony_ci void *data, u16 data_len) 44228c2ecf20Sopenharmony_ci{ 44238c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 44248c2ecf20Sopenharmony_ci struct hci_request req; 44258c2ecf20Sopenharmony_ci int err; 44268c2ecf20Sopenharmony_ci 44278c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 44288c2ecf20Sopenharmony_ci 44298c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 44308c2ecf20Sopenharmony_ci 44318c2ecf20Sopenharmony_ci if (!hdev_is_powered(hdev)) { 44328c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA, 44338c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_POWERED); 44348c2ecf20Sopenharmony_ci goto unlock; 44358c2ecf20Sopenharmony_ci } 44368c2ecf20Sopenharmony_ci 44378c2ecf20Sopenharmony_ci if (!lmp_ssp_capable(hdev)) { 44388c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA, 44398c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_SUPPORTED); 44408c2ecf20Sopenharmony_ci goto unlock; 44418c2ecf20Sopenharmony_ci } 44428c2ecf20Sopenharmony_ci 44438c2ecf20Sopenharmony_ci if (pending_find(MGMT_OP_READ_LOCAL_OOB_DATA, hdev)) { 44448c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_DATA, 44458c2ecf20Sopenharmony_ci MGMT_STATUS_BUSY); 44468c2ecf20Sopenharmony_ci goto unlock; 44478c2ecf20Sopenharmony_ci } 44488c2ecf20Sopenharmony_ci 44498c2ecf20Sopenharmony_ci cmd = mgmt_pending_add(sk, MGMT_OP_READ_LOCAL_OOB_DATA, hdev, NULL, 0); 44508c2ecf20Sopenharmony_ci if (!cmd) { 44518c2ecf20Sopenharmony_ci err = -ENOMEM; 44528c2ecf20Sopenharmony_ci goto unlock; 44538c2ecf20Sopenharmony_ci } 44548c2ecf20Sopenharmony_ci 44558c2ecf20Sopenharmony_ci hci_req_init(&req, hdev); 44568c2ecf20Sopenharmony_ci 44578c2ecf20Sopenharmony_ci if (bredr_sc_enabled(hdev)) 44588c2ecf20Sopenharmony_ci hci_req_add(&req, HCI_OP_READ_LOCAL_OOB_EXT_DATA, 0, NULL); 44598c2ecf20Sopenharmony_ci else 44608c2ecf20Sopenharmony_ci hci_req_add(&req, HCI_OP_READ_LOCAL_OOB_DATA, 0, NULL); 44618c2ecf20Sopenharmony_ci 44628c2ecf20Sopenharmony_ci err = hci_req_run_skb(&req, read_local_oob_data_complete); 44638c2ecf20Sopenharmony_ci if (err < 0) 44648c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 44658c2ecf20Sopenharmony_ci 44668c2ecf20Sopenharmony_ciunlock: 44678c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 44688c2ecf20Sopenharmony_ci return err; 44698c2ecf20Sopenharmony_ci} 44708c2ecf20Sopenharmony_ci 44718c2ecf20Sopenharmony_cistatic int add_remote_oob_data(struct sock *sk, struct hci_dev *hdev, 44728c2ecf20Sopenharmony_ci void *data, u16 len) 44738c2ecf20Sopenharmony_ci{ 44748c2ecf20Sopenharmony_ci struct mgmt_addr_info *addr = data; 44758c2ecf20Sopenharmony_ci int err; 44768c2ecf20Sopenharmony_ci 44778c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 44788c2ecf20Sopenharmony_ci 44798c2ecf20Sopenharmony_ci if (!bdaddr_type_is_valid(addr->type)) 44808c2ecf20Sopenharmony_ci return mgmt_cmd_complete(sk, hdev->id, 44818c2ecf20Sopenharmony_ci MGMT_OP_ADD_REMOTE_OOB_DATA, 44828c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS, 44838c2ecf20Sopenharmony_ci addr, sizeof(*addr)); 44848c2ecf20Sopenharmony_ci 44858c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 44868c2ecf20Sopenharmony_ci 44878c2ecf20Sopenharmony_ci if (len == MGMT_ADD_REMOTE_OOB_DATA_SIZE) { 44888c2ecf20Sopenharmony_ci struct mgmt_cp_add_remote_oob_data *cp = data; 44898c2ecf20Sopenharmony_ci u8 status; 44908c2ecf20Sopenharmony_ci 44918c2ecf20Sopenharmony_ci if (cp->addr.type != BDADDR_BREDR) { 44928c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, 44938c2ecf20Sopenharmony_ci MGMT_OP_ADD_REMOTE_OOB_DATA, 44948c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS, 44958c2ecf20Sopenharmony_ci &cp->addr, sizeof(cp->addr)); 44968c2ecf20Sopenharmony_ci goto unlock; 44978c2ecf20Sopenharmony_ci } 44988c2ecf20Sopenharmony_ci 44998c2ecf20Sopenharmony_ci err = hci_add_remote_oob_data(hdev, &cp->addr.bdaddr, 45008c2ecf20Sopenharmony_ci cp->addr.type, cp->hash, 45018c2ecf20Sopenharmony_ci cp->rand, NULL, NULL); 45028c2ecf20Sopenharmony_ci if (err < 0) 45038c2ecf20Sopenharmony_ci status = MGMT_STATUS_FAILED; 45048c2ecf20Sopenharmony_ci else 45058c2ecf20Sopenharmony_ci status = MGMT_STATUS_SUCCESS; 45068c2ecf20Sopenharmony_ci 45078c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, 45088c2ecf20Sopenharmony_ci MGMT_OP_ADD_REMOTE_OOB_DATA, status, 45098c2ecf20Sopenharmony_ci &cp->addr, sizeof(cp->addr)); 45108c2ecf20Sopenharmony_ci } else if (len == MGMT_ADD_REMOTE_OOB_EXT_DATA_SIZE) { 45118c2ecf20Sopenharmony_ci struct mgmt_cp_add_remote_oob_ext_data *cp = data; 45128c2ecf20Sopenharmony_ci u8 *rand192, *hash192, *rand256, *hash256; 45138c2ecf20Sopenharmony_ci u8 status; 45148c2ecf20Sopenharmony_ci 45158c2ecf20Sopenharmony_ci if (bdaddr_type_is_le(cp->addr.type)) { 45168c2ecf20Sopenharmony_ci /* Enforce zero-valued 192-bit parameters as 45178c2ecf20Sopenharmony_ci * long as legacy SMP OOB isn't implemented. 45188c2ecf20Sopenharmony_ci */ 45198c2ecf20Sopenharmony_ci if (memcmp(cp->rand192, ZERO_KEY, 16) || 45208c2ecf20Sopenharmony_ci memcmp(cp->hash192, ZERO_KEY, 16)) { 45218c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, 45228c2ecf20Sopenharmony_ci MGMT_OP_ADD_REMOTE_OOB_DATA, 45238c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS, 45248c2ecf20Sopenharmony_ci addr, sizeof(*addr)); 45258c2ecf20Sopenharmony_ci goto unlock; 45268c2ecf20Sopenharmony_ci } 45278c2ecf20Sopenharmony_ci 45288c2ecf20Sopenharmony_ci rand192 = NULL; 45298c2ecf20Sopenharmony_ci hash192 = NULL; 45308c2ecf20Sopenharmony_ci } else { 45318c2ecf20Sopenharmony_ci /* In case one of the P-192 values is set to zero, 45328c2ecf20Sopenharmony_ci * then just disable OOB data for P-192. 45338c2ecf20Sopenharmony_ci */ 45348c2ecf20Sopenharmony_ci if (!memcmp(cp->rand192, ZERO_KEY, 16) || 45358c2ecf20Sopenharmony_ci !memcmp(cp->hash192, ZERO_KEY, 16)) { 45368c2ecf20Sopenharmony_ci rand192 = NULL; 45378c2ecf20Sopenharmony_ci hash192 = NULL; 45388c2ecf20Sopenharmony_ci } else { 45398c2ecf20Sopenharmony_ci rand192 = cp->rand192; 45408c2ecf20Sopenharmony_ci hash192 = cp->hash192; 45418c2ecf20Sopenharmony_ci } 45428c2ecf20Sopenharmony_ci } 45438c2ecf20Sopenharmony_ci 45448c2ecf20Sopenharmony_ci /* In case one of the P-256 values is set to zero, then just 45458c2ecf20Sopenharmony_ci * disable OOB data for P-256. 45468c2ecf20Sopenharmony_ci */ 45478c2ecf20Sopenharmony_ci if (!memcmp(cp->rand256, ZERO_KEY, 16) || 45488c2ecf20Sopenharmony_ci !memcmp(cp->hash256, ZERO_KEY, 16)) { 45498c2ecf20Sopenharmony_ci rand256 = NULL; 45508c2ecf20Sopenharmony_ci hash256 = NULL; 45518c2ecf20Sopenharmony_ci } else { 45528c2ecf20Sopenharmony_ci rand256 = cp->rand256; 45538c2ecf20Sopenharmony_ci hash256 = cp->hash256; 45548c2ecf20Sopenharmony_ci } 45558c2ecf20Sopenharmony_ci 45568c2ecf20Sopenharmony_ci err = hci_add_remote_oob_data(hdev, &cp->addr.bdaddr, 45578c2ecf20Sopenharmony_ci cp->addr.type, hash192, rand192, 45588c2ecf20Sopenharmony_ci hash256, rand256); 45598c2ecf20Sopenharmony_ci if (err < 0) 45608c2ecf20Sopenharmony_ci status = MGMT_STATUS_FAILED; 45618c2ecf20Sopenharmony_ci else 45628c2ecf20Sopenharmony_ci status = MGMT_STATUS_SUCCESS; 45638c2ecf20Sopenharmony_ci 45648c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, 45658c2ecf20Sopenharmony_ci MGMT_OP_ADD_REMOTE_OOB_DATA, 45668c2ecf20Sopenharmony_ci status, &cp->addr, sizeof(cp->addr)); 45678c2ecf20Sopenharmony_ci } else { 45688c2ecf20Sopenharmony_ci bt_dev_err(hdev, "add_remote_oob_data: invalid len of %u bytes", 45698c2ecf20Sopenharmony_ci len); 45708c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_REMOTE_OOB_DATA, 45718c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 45728c2ecf20Sopenharmony_ci } 45738c2ecf20Sopenharmony_ci 45748c2ecf20Sopenharmony_ciunlock: 45758c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 45768c2ecf20Sopenharmony_ci return err; 45778c2ecf20Sopenharmony_ci} 45788c2ecf20Sopenharmony_ci 45798c2ecf20Sopenharmony_cistatic int remove_remote_oob_data(struct sock *sk, struct hci_dev *hdev, 45808c2ecf20Sopenharmony_ci void *data, u16 len) 45818c2ecf20Sopenharmony_ci{ 45828c2ecf20Sopenharmony_ci struct mgmt_cp_remove_remote_oob_data *cp = data; 45838c2ecf20Sopenharmony_ci u8 status; 45848c2ecf20Sopenharmony_ci int err; 45858c2ecf20Sopenharmony_ci 45868c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 45878c2ecf20Sopenharmony_ci 45888c2ecf20Sopenharmony_ci if (cp->addr.type != BDADDR_BREDR) 45898c2ecf20Sopenharmony_ci return mgmt_cmd_complete(sk, hdev->id, 45908c2ecf20Sopenharmony_ci MGMT_OP_REMOVE_REMOTE_OOB_DATA, 45918c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS, 45928c2ecf20Sopenharmony_ci &cp->addr, sizeof(cp->addr)); 45938c2ecf20Sopenharmony_ci 45948c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 45958c2ecf20Sopenharmony_ci 45968c2ecf20Sopenharmony_ci if (!bacmp(&cp->addr.bdaddr, BDADDR_ANY)) { 45978c2ecf20Sopenharmony_ci hci_remote_oob_data_clear(hdev); 45988c2ecf20Sopenharmony_ci status = MGMT_STATUS_SUCCESS; 45998c2ecf20Sopenharmony_ci goto done; 46008c2ecf20Sopenharmony_ci } 46018c2ecf20Sopenharmony_ci 46028c2ecf20Sopenharmony_ci err = hci_remove_remote_oob_data(hdev, &cp->addr.bdaddr, cp->addr.type); 46038c2ecf20Sopenharmony_ci if (err < 0) 46048c2ecf20Sopenharmony_ci status = MGMT_STATUS_INVALID_PARAMS; 46058c2ecf20Sopenharmony_ci else 46068c2ecf20Sopenharmony_ci status = MGMT_STATUS_SUCCESS; 46078c2ecf20Sopenharmony_ci 46088c2ecf20Sopenharmony_cidone: 46098c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_REMOTE_OOB_DATA, 46108c2ecf20Sopenharmony_ci status, &cp->addr, sizeof(cp->addr)); 46118c2ecf20Sopenharmony_ci 46128c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 46138c2ecf20Sopenharmony_ci return err; 46148c2ecf20Sopenharmony_ci} 46158c2ecf20Sopenharmony_ci 46168c2ecf20Sopenharmony_civoid mgmt_start_discovery_complete(struct hci_dev *hdev, u8 status) 46178c2ecf20Sopenharmony_ci{ 46188c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 46198c2ecf20Sopenharmony_ci 46208c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "status %d", status); 46218c2ecf20Sopenharmony_ci 46228c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 46238c2ecf20Sopenharmony_ci 46248c2ecf20Sopenharmony_ci cmd = pending_find(MGMT_OP_START_DISCOVERY, hdev); 46258c2ecf20Sopenharmony_ci if (!cmd) 46268c2ecf20Sopenharmony_ci cmd = pending_find(MGMT_OP_START_SERVICE_DISCOVERY, hdev); 46278c2ecf20Sopenharmony_ci 46288c2ecf20Sopenharmony_ci if (!cmd) 46298c2ecf20Sopenharmony_ci cmd = pending_find(MGMT_OP_START_LIMITED_DISCOVERY, hdev); 46308c2ecf20Sopenharmony_ci 46318c2ecf20Sopenharmony_ci if (cmd) { 46328c2ecf20Sopenharmony_ci cmd->cmd_complete(cmd, mgmt_status(status)); 46338c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 46348c2ecf20Sopenharmony_ci } 46358c2ecf20Sopenharmony_ci 46368c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 46378c2ecf20Sopenharmony_ci 46388c2ecf20Sopenharmony_ci /* Handle suspend notifier */ 46398c2ecf20Sopenharmony_ci if (test_and_clear_bit(SUSPEND_UNPAUSE_DISCOVERY, 46408c2ecf20Sopenharmony_ci hdev->suspend_tasks)) { 46418c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "Unpaused discovery"); 46428c2ecf20Sopenharmony_ci wake_up(&hdev->suspend_wait_q); 46438c2ecf20Sopenharmony_ci } 46448c2ecf20Sopenharmony_ci} 46458c2ecf20Sopenharmony_ci 46468c2ecf20Sopenharmony_cistatic bool discovery_type_is_valid(struct hci_dev *hdev, uint8_t type, 46478c2ecf20Sopenharmony_ci uint8_t *mgmt_status) 46488c2ecf20Sopenharmony_ci{ 46498c2ecf20Sopenharmony_ci switch (type) { 46508c2ecf20Sopenharmony_ci case DISCOV_TYPE_LE: 46518c2ecf20Sopenharmony_ci *mgmt_status = mgmt_le_support(hdev); 46528c2ecf20Sopenharmony_ci if (*mgmt_status) 46538c2ecf20Sopenharmony_ci return false; 46548c2ecf20Sopenharmony_ci break; 46558c2ecf20Sopenharmony_ci case DISCOV_TYPE_INTERLEAVED: 46568c2ecf20Sopenharmony_ci *mgmt_status = mgmt_le_support(hdev); 46578c2ecf20Sopenharmony_ci if (*mgmt_status) 46588c2ecf20Sopenharmony_ci return false; 46598c2ecf20Sopenharmony_ci fallthrough; 46608c2ecf20Sopenharmony_ci case DISCOV_TYPE_BREDR: 46618c2ecf20Sopenharmony_ci *mgmt_status = mgmt_bredr_support(hdev); 46628c2ecf20Sopenharmony_ci if (*mgmt_status) 46638c2ecf20Sopenharmony_ci return false; 46648c2ecf20Sopenharmony_ci break; 46658c2ecf20Sopenharmony_ci default: 46668c2ecf20Sopenharmony_ci *mgmt_status = MGMT_STATUS_INVALID_PARAMS; 46678c2ecf20Sopenharmony_ci return false; 46688c2ecf20Sopenharmony_ci } 46698c2ecf20Sopenharmony_ci 46708c2ecf20Sopenharmony_ci return true; 46718c2ecf20Sopenharmony_ci} 46728c2ecf20Sopenharmony_ci 46738c2ecf20Sopenharmony_cistatic int start_discovery_internal(struct sock *sk, struct hci_dev *hdev, 46748c2ecf20Sopenharmony_ci u16 op, void *data, u16 len) 46758c2ecf20Sopenharmony_ci{ 46768c2ecf20Sopenharmony_ci struct mgmt_cp_start_discovery *cp = data; 46778c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 46788c2ecf20Sopenharmony_ci u8 status; 46798c2ecf20Sopenharmony_ci int err; 46808c2ecf20Sopenharmony_ci 46818c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 46828c2ecf20Sopenharmony_ci 46838c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 46848c2ecf20Sopenharmony_ci 46858c2ecf20Sopenharmony_ci if (!hdev_is_powered(hdev)) { 46868c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, op, 46878c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_POWERED, 46888c2ecf20Sopenharmony_ci &cp->type, sizeof(cp->type)); 46898c2ecf20Sopenharmony_ci goto failed; 46908c2ecf20Sopenharmony_ci } 46918c2ecf20Sopenharmony_ci 46928c2ecf20Sopenharmony_ci if (hdev->discovery.state != DISCOVERY_STOPPED || 46938c2ecf20Sopenharmony_ci hci_dev_test_flag(hdev, HCI_PERIODIC_INQ)) { 46948c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, op, MGMT_STATUS_BUSY, 46958c2ecf20Sopenharmony_ci &cp->type, sizeof(cp->type)); 46968c2ecf20Sopenharmony_ci goto failed; 46978c2ecf20Sopenharmony_ci } 46988c2ecf20Sopenharmony_ci 46998c2ecf20Sopenharmony_ci if (!discovery_type_is_valid(hdev, cp->type, &status)) { 47008c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, op, status, 47018c2ecf20Sopenharmony_ci &cp->type, sizeof(cp->type)); 47028c2ecf20Sopenharmony_ci goto failed; 47038c2ecf20Sopenharmony_ci } 47048c2ecf20Sopenharmony_ci 47058c2ecf20Sopenharmony_ci /* Can't start discovery when it is paused */ 47068c2ecf20Sopenharmony_ci if (hdev->discovery_paused) { 47078c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, op, MGMT_STATUS_BUSY, 47088c2ecf20Sopenharmony_ci &cp->type, sizeof(cp->type)); 47098c2ecf20Sopenharmony_ci goto failed; 47108c2ecf20Sopenharmony_ci } 47118c2ecf20Sopenharmony_ci 47128c2ecf20Sopenharmony_ci /* Clear the discovery filter first to free any previously 47138c2ecf20Sopenharmony_ci * allocated memory for the UUID list. 47148c2ecf20Sopenharmony_ci */ 47158c2ecf20Sopenharmony_ci hci_discovery_filter_clear(hdev); 47168c2ecf20Sopenharmony_ci 47178c2ecf20Sopenharmony_ci hdev->discovery.type = cp->type; 47188c2ecf20Sopenharmony_ci hdev->discovery.report_invalid_rssi = false; 47198c2ecf20Sopenharmony_ci if (op == MGMT_OP_START_LIMITED_DISCOVERY) 47208c2ecf20Sopenharmony_ci hdev->discovery.limited = true; 47218c2ecf20Sopenharmony_ci else 47228c2ecf20Sopenharmony_ci hdev->discovery.limited = false; 47238c2ecf20Sopenharmony_ci 47248c2ecf20Sopenharmony_ci cmd = mgmt_pending_add(sk, op, hdev, data, len); 47258c2ecf20Sopenharmony_ci if (!cmd) { 47268c2ecf20Sopenharmony_ci err = -ENOMEM; 47278c2ecf20Sopenharmony_ci goto failed; 47288c2ecf20Sopenharmony_ci } 47298c2ecf20Sopenharmony_ci 47308c2ecf20Sopenharmony_ci cmd->cmd_complete = generic_cmd_complete; 47318c2ecf20Sopenharmony_ci 47328c2ecf20Sopenharmony_ci hci_discovery_set_state(hdev, DISCOVERY_STARTING); 47338c2ecf20Sopenharmony_ci queue_work(hdev->req_workqueue, &hdev->discov_update); 47348c2ecf20Sopenharmony_ci err = 0; 47358c2ecf20Sopenharmony_ci 47368c2ecf20Sopenharmony_cifailed: 47378c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 47388c2ecf20Sopenharmony_ci return err; 47398c2ecf20Sopenharmony_ci} 47408c2ecf20Sopenharmony_ci 47418c2ecf20Sopenharmony_cistatic int start_discovery(struct sock *sk, struct hci_dev *hdev, 47428c2ecf20Sopenharmony_ci void *data, u16 len) 47438c2ecf20Sopenharmony_ci{ 47448c2ecf20Sopenharmony_ci return start_discovery_internal(sk, hdev, MGMT_OP_START_DISCOVERY, 47458c2ecf20Sopenharmony_ci data, len); 47468c2ecf20Sopenharmony_ci} 47478c2ecf20Sopenharmony_ci 47488c2ecf20Sopenharmony_cistatic int start_limited_discovery(struct sock *sk, struct hci_dev *hdev, 47498c2ecf20Sopenharmony_ci void *data, u16 len) 47508c2ecf20Sopenharmony_ci{ 47518c2ecf20Sopenharmony_ci return start_discovery_internal(sk, hdev, 47528c2ecf20Sopenharmony_ci MGMT_OP_START_LIMITED_DISCOVERY, 47538c2ecf20Sopenharmony_ci data, len); 47548c2ecf20Sopenharmony_ci} 47558c2ecf20Sopenharmony_ci 47568c2ecf20Sopenharmony_cistatic int service_discovery_cmd_complete(struct mgmt_pending_cmd *cmd, 47578c2ecf20Sopenharmony_ci u8 status) 47588c2ecf20Sopenharmony_ci{ 47598c2ecf20Sopenharmony_ci return mgmt_cmd_complete(cmd->sk, cmd->index, cmd->opcode, status, 47608c2ecf20Sopenharmony_ci cmd->param, 1); 47618c2ecf20Sopenharmony_ci} 47628c2ecf20Sopenharmony_ci 47638c2ecf20Sopenharmony_cistatic int start_service_discovery(struct sock *sk, struct hci_dev *hdev, 47648c2ecf20Sopenharmony_ci void *data, u16 len) 47658c2ecf20Sopenharmony_ci{ 47668c2ecf20Sopenharmony_ci struct mgmt_cp_start_service_discovery *cp = data; 47678c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 47688c2ecf20Sopenharmony_ci const u16 max_uuid_count = ((U16_MAX - sizeof(*cp)) / 16); 47698c2ecf20Sopenharmony_ci u16 uuid_count, expected_len; 47708c2ecf20Sopenharmony_ci u8 status; 47718c2ecf20Sopenharmony_ci int err; 47728c2ecf20Sopenharmony_ci 47738c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 47748c2ecf20Sopenharmony_ci 47758c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 47768c2ecf20Sopenharmony_ci 47778c2ecf20Sopenharmony_ci if (!hdev_is_powered(hdev)) { 47788c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, 47798c2ecf20Sopenharmony_ci MGMT_OP_START_SERVICE_DISCOVERY, 47808c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_POWERED, 47818c2ecf20Sopenharmony_ci &cp->type, sizeof(cp->type)); 47828c2ecf20Sopenharmony_ci goto failed; 47838c2ecf20Sopenharmony_ci } 47848c2ecf20Sopenharmony_ci 47858c2ecf20Sopenharmony_ci if (hdev->discovery.state != DISCOVERY_STOPPED || 47868c2ecf20Sopenharmony_ci hci_dev_test_flag(hdev, HCI_PERIODIC_INQ)) { 47878c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, 47888c2ecf20Sopenharmony_ci MGMT_OP_START_SERVICE_DISCOVERY, 47898c2ecf20Sopenharmony_ci MGMT_STATUS_BUSY, &cp->type, 47908c2ecf20Sopenharmony_ci sizeof(cp->type)); 47918c2ecf20Sopenharmony_ci goto failed; 47928c2ecf20Sopenharmony_ci } 47938c2ecf20Sopenharmony_ci 47948c2ecf20Sopenharmony_ci uuid_count = __le16_to_cpu(cp->uuid_count); 47958c2ecf20Sopenharmony_ci if (uuid_count > max_uuid_count) { 47968c2ecf20Sopenharmony_ci bt_dev_err(hdev, "service_discovery: too big uuid_count value %u", 47978c2ecf20Sopenharmony_ci uuid_count); 47988c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, 47998c2ecf20Sopenharmony_ci MGMT_OP_START_SERVICE_DISCOVERY, 48008c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS, &cp->type, 48018c2ecf20Sopenharmony_ci sizeof(cp->type)); 48028c2ecf20Sopenharmony_ci goto failed; 48038c2ecf20Sopenharmony_ci } 48048c2ecf20Sopenharmony_ci 48058c2ecf20Sopenharmony_ci expected_len = sizeof(*cp) + uuid_count * 16; 48068c2ecf20Sopenharmony_ci if (expected_len != len) { 48078c2ecf20Sopenharmony_ci bt_dev_err(hdev, "service_discovery: expected %u bytes, got %u bytes", 48088c2ecf20Sopenharmony_ci expected_len, len); 48098c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, 48108c2ecf20Sopenharmony_ci MGMT_OP_START_SERVICE_DISCOVERY, 48118c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS, &cp->type, 48128c2ecf20Sopenharmony_ci sizeof(cp->type)); 48138c2ecf20Sopenharmony_ci goto failed; 48148c2ecf20Sopenharmony_ci } 48158c2ecf20Sopenharmony_ci 48168c2ecf20Sopenharmony_ci if (!discovery_type_is_valid(hdev, cp->type, &status)) { 48178c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, 48188c2ecf20Sopenharmony_ci MGMT_OP_START_SERVICE_DISCOVERY, 48198c2ecf20Sopenharmony_ci status, &cp->type, sizeof(cp->type)); 48208c2ecf20Sopenharmony_ci goto failed; 48218c2ecf20Sopenharmony_ci } 48228c2ecf20Sopenharmony_ci 48238c2ecf20Sopenharmony_ci cmd = mgmt_pending_add(sk, MGMT_OP_START_SERVICE_DISCOVERY, 48248c2ecf20Sopenharmony_ci hdev, data, len); 48258c2ecf20Sopenharmony_ci if (!cmd) { 48268c2ecf20Sopenharmony_ci err = -ENOMEM; 48278c2ecf20Sopenharmony_ci goto failed; 48288c2ecf20Sopenharmony_ci } 48298c2ecf20Sopenharmony_ci 48308c2ecf20Sopenharmony_ci cmd->cmd_complete = service_discovery_cmd_complete; 48318c2ecf20Sopenharmony_ci 48328c2ecf20Sopenharmony_ci /* Clear the discovery filter first to free any previously 48338c2ecf20Sopenharmony_ci * allocated memory for the UUID list. 48348c2ecf20Sopenharmony_ci */ 48358c2ecf20Sopenharmony_ci hci_discovery_filter_clear(hdev); 48368c2ecf20Sopenharmony_ci 48378c2ecf20Sopenharmony_ci hdev->discovery.result_filtering = true; 48388c2ecf20Sopenharmony_ci hdev->discovery.type = cp->type; 48398c2ecf20Sopenharmony_ci hdev->discovery.rssi = cp->rssi; 48408c2ecf20Sopenharmony_ci hdev->discovery.uuid_count = uuid_count; 48418c2ecf20Sopenharmony_ci 48428c2ecf20Sopenharmony_ci if (uuid_count > 0) { 48438c2ecf20Sopenharmony_ci hdev->discovery.uuids = kmemdup(cp->uuids, uuid_count * 16, 48448c2ecf20Sopenharmony_ci GFP_KERNEL); 48458c2ecf20Sopenharmony_ci if (!hdev->discovery.uuids) { 48468c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, 48478c2ecf20Sopenharmony_ci MGMT_OP_START_SERVICE_DISCOVERY, 48488c2ecf20Sopenharmony_ci MGMT_STATUS_FAILED, 48498c2ecf20Sopenharmony_ci &cp->type, sizeof(cp->type)); 48508c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 48518c2ecf20Sopenharmony_ci goto failed; 48528c2ecf20Sopenharmony_ci } 48538c2ecf20Sopenharmony_ci } 48548c2ecf20Sopenharmony_ci 48558c2ecf20Sopenharmony_ci hci_discovery_set_state(hdev, DISCOVERY_STARTING); 48568c2ecf20Sopenharmony_ci queue_work(hdev->req_workqueue, &hdev->discov_update); 48578c2ecf20Sopenharmony_ci err = 0; 48588c2ecf20Sopenharmony_ci 48598c2ecf20Sopenharmony_cifailed: 48608c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 48618c2ecf20Sopenharmony_ci return err; 48628c2ecf20Sopenharmony_ci} 48638c2ecf20Sopenharmony_ci 48648c2ecf20Sopenharmony_civoid mgmt_stop_discovery_complete(struct hci_dev *hdev, u8 status) 48658c2ecf20Sopenharmony_ci{ 48668c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 48678c2ecf20Sopenharmony_ci 48688c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "status %d", status); 48698c2ecf20Sopenharmony_ci 48708c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 48718c2ecf20Sopenharmony_ci 48728c2ecf20Sopenharmony_ci cmd = pending_find(MGMT_OP_STOP_DISCOVERY, hdev); 48738c2ecf20Sopenharmony_ci if (cmd) { 48748c2ecf20Sopenharmony_ci cmd->cmd_complete(cmd, mgmt_status(status)); 48758c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 48768c2ecf20Sopenharmony_ci } 48778c2ecf20Sopenharmony_ci 48788c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 48798c2ecf20Sopenharmony_ci 48808c2ecf20Sopenharmony_ci /* Handle suspend notifier */ 48818c2ecf20Sopenharmony_ci if (test_and_clear_bit(SUSPEND_PAUSE_DISCOVERY, hdev->suspend_tasks)) { 48828c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "Paused discovery"); 48838c2ecf20Sopenharmony_ci wake_up(&hdev->suspend_wait_q); 48848c2ecf20Sopenharmony_ci } 48858c2ecf20Sopenharmony_ci} 48868c2ecf20Sopenharmony_ci 48878c2ecf20Sopenharmony_cistatic int stop_discovery(struct sock *sk, struct hci_dev *hdev, void *data, 48888c2ecf20Sopenharmony_ci u16 len) 48898c2ecf20Sopenharmony_ci{ 48908c2ecf20Sopenharmony_ci struct mgmt_cp_stop_discovery *mgmt_cp = data; 48918c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 48928c2ecf20Sopenharmony_ci int err; 48938c2ecf20Sopenharmony_ci 48948c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 48958c2ecf20Sopenharmony_ci 48968c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 48978c2ecf20Sopenharmony_ci 48988c2ecf20Sopenharmony_ci if (!hci_discovery_active(hdev)) { 48998c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_STOP_DISCOVERY, 49008c2ecf20Sopenharmony_ci MGMT_STATUS_REJECTED, &mgmt_cp->type, 49018c2ecf20Sopenharmony_ci sizeof(mgmt_cp->type)); 49028c2ecf20Sopenharmony_ci goto unlock; 49038c2ecf20Sopenharmony_ci } 49048c2ecf20Sopenharmony_ci 49058c2ecf20Sopenharmony_ci if (hdev->discovery.type != mgmt_cp->type) { 49068c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_STOP_DISCOVERY, 49078c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS, 49088c2ecf20Sopenharmony_ci &mgmt_cp->type, sizeof(mgmt_cp->type)); 49098c2ecf20Sopenharmony_ci goto unlock; 49108c2ecf20Sopenharmony_ci } 49118c2ecf20Sopenharmony_ci 49128c2ecf20Sopenharmony_ci cmd = mgmt_pending_add(sk, MGMT_OP_STOP_DISCOVERY, hdev, data, len); 49138c2ecf20Sopenharmony_ci if (!cmd) { 49148c2ecf20Sopenharmony_ci err = -ENOMEM; 49158c2ecf20Sopenharmony_ci goto unlock; 49168c2ecf20Sopenharmony_ci } 49178c2ecf20Sopenharmony_ci 49188c2ecf20Sopenharmony_ci cmd->cmd_complete = generic_cmd_complete; 49198c2ecf20Sopenharmony_ci 49208c2ecf20Sopenharmony_ci hci_discovery_set_state(hdev, DISCOVERY_STOPPING); 49218c2ecf20Sopenharmony_ci queue_work(hdev->req_workqueue, &hdev->discov_update); 49228c2ecf20Sopenharmony_ci err = 0; 49238c2ecf20Sopenharmony_ci 49248c2ecf20Sopenharmony_ciunlock: 49258c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 49268c2ecf20Sopenharmony_ci return err; 49278c2ecf20Sopenharmony_ci} 49288c2ecf20Sopenharmony_ci 49298c2ecf20Sopenharmony_cistatic int confirm_name(struct sock *sk, struct hci_dev *hdev, void *data, 49308c2ecf20Sopenharmony_ci u16 len) 49318c2ecf20Sopenharmony_ci{ 49328c2ecf20Sopenharmony_ci struct mgmt_cp_confirm_name *cp = data; 49338c2ecf20Sopenharmony_ci struct inquiry_entry *e; 49348c2ecf20Sopenharmony_ci int err; 49358c2ecf20Sopenharmony_ci 49368c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 49378c2ecf20Sopenharmony_ci 49388c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 49398c2ecf20Sopenharmony_ci 49408c2ecf20Sopenharmony_ci if (!hci_discovery_active(hdev)) { 49418c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_CONFIRM_NAME, 49428c2ecf20Sopenharmony_ci MGMT_STATUS_FAILED, &cp->addr, 49438c2ecf20Sopenharmony_ci sizeof(cp->addr)); 49448c2ecf20Sopenharmony_ci goto failed; 49458c2ecf20Sopenharmony_ci } 49468c2ecf20Sopenharmony_ci 49478c2ecf20Sopenharmony_ci e = hci_inquiry_cache_lookup_unknown(hdev, &cp->addr.bdaddr); 49488c2ecf20Sopenharmony_ci if (!e) { 49498c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_CONFIRM_NAME, 49508c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS, &cp->addr, 49518c2ecf20Sopenharmony_ci sizeof(cp->addr)); 49528c2ecf20Sopenharmony_ci goto failed; 49538c2ecf20Sopenharmony_ci } 49548c2ecf20Sopenharmony_ci 49558c2ecf20Sopenharmony_ci if (cp->name_known) { 49568c2ecf20Sopenharmony_ci e->name_state = NAME_KNOWN; 49578c2ecf20Sopenharmony_ci list_del(&e->list); 49588c2ecf20Sopenharmony_ci } else { 49598c2ecf20Sopenharmony_ci e->name_state = NAME_NEEDED; 49608c2ecf20Sopenharmony_ci hci_inquiry_cache_update_resolve(hdev, e); 49618c2ecf20Sopenharmony_ci } 49628c2ecf20Sopenharmony_ci 49638c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_CONFIRM_NAME, 0, 49648c2ecf20Sopenharmony_ci &cp->addr, sizeof(cp->addr)); 49658c2ecf20Sopenharmony_ci 49668c2ecf20Sopenharmony_cifailed: 49678c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 49688c2ecf20Sopenharmony_ci return err; 49698c2ecf20Sopenharmony_ci} 49708c2ecf20Sopenharmony_ci 49718c2ecf20Sopenharmony_cistatic int block_device(struct sock *sk, struct hci_dev *hdev, void *data, 49728c2ecf20Sopenharmony_ci u16 len) 49738c2ecf20Sopenharmony_ci{ 49748c2ecf20Sopenharmony_ci struct mgmt_cp_block_device *cp = data; 49758c2ecf20Sopenharmony_ci u8 status; 49768c2ecf20Sopenharmony_ci int err; 49778c2ecf20Sopenharmony_ci 49788c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 49798c2ecf20Sopenharmony_ci 49808c2ecf20Sopenharmony_ci if (!bdaddr_type_is_valid(cp->addr.type)) 49818c2ecf20Sopenharmony_ci return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_BLOCK_DEVICE, 49828c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS, 49838c2ecf20Sopenharmony_ci &cp->addr, sizeof(cp->addr)); 49848c2ecf20Sopenharmony_ci 49858c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 49868c2ecf20Sopenharmony_ci 49878c2ecf20Sopenharmony_ci err = hci_bdaddr_list_add(&hdev->reject_list, &cp->addr.bdaddr, 49888c2ecf20Sopenharmony_ci cp->addr.type); 49898c2ecf20Sopenharmony_ci if (err < 0) { 49908c2ecf20Sopenharmony_ci status = MGMT_STATUS_FAILED; 49918c2ecf20Sopenharmony_ci goto done; 49928c2ecf20Sopenharmony_ci } 49938c2ecf20Sopenharmony_ci 49948c2ecf20Sopenharmony_ci mgmt_event(MGMT_EV_DEVICE_BLOCKED, hdev, &cp->addr, sizeof(cp->addr), 49958c2ecf20Sopenharmony_ci sk); 49968c2ecf20Sopenharmony_ci status = MGMT_STATUS_SUCCESS; 49978c2ecf20Sopenharmony_ci 49988c2ecf20Sopenharmony_cidone: 49998c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_BLOCK_DEVICE, status, 50008c2ecf20Sopenharmony_ci &cp->addr, sizeof(cp->addr)); 50018c2ecf20Sopenharmony_ci 50028c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 50038c2ecf20Sopenharmony_ci 50048c2ecf20Sopenharmony_ci return err; 50058c2ecf20Sopenharmony_ci} 50068c2ecf20Sopenharmony_ci 50078c2ecf20Sopenharmony_cistatic int unblock_device(struct sock *sk, struct hci_dev *hdev, void *data, 50088c2ecf20Sopenharmony_ci u16 len) 50098c2ecf20Sopenharmony_ci{ 50108c2ecf20Sopenharmony_ci struct mgmt_cp_unblock_device *cp = data; 50118c2ecf20Sopenharmony_ci u8 status; 50128c2ecf20Sopenharmony_ci int err; 50138c2ecf20Sopenharmony_ci 50148c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 50158c2ecf20Sopenharmony_ci 50168c2ecf20Sopenharmony_ci if (!bdaddr_type_is_valid(cp->addr.type)) 50178c2ecf20Sopenharmony_ci return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_UNBLOCK_DEVICE, 50188c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS, 50198c2ecf20Sopenharmony_ci &cp->addr, sizeof(cp->addr)); 50208c2ecf20Sopenharmony_ci 50218c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 50228c2ecf20Sopenharmony_ci 50238c2ecf20Sopenharmony_ci err = hci_bdaddr_list_del(&hdev->reject_list, &cp->addr.bdaddr, 50248c2ecf20Sopenharmony_ci cp->addr.type); 50258c2ecf20Sopenharmony_ci if (err < 0) { 50268c2ecf20Sopenharmony_ci status = MGMT_STATUS_INVALID_PARAMS; 50278c2ecf20Sopenharmony_ci goto done; 50288c2ecf20Sopenharmony_ci } 50298c2ecf20Sopenharmony_ci 50308c2ecf20Sopenharmony_ci mgmt_event(MGMT_EV_DEVICE_UNBLOCKED, hdev, &cp->addr, sizeof(cp->addr), 50318c2ecf20Sopenharmony_ci sk); 50328c2ecf20Sopenharmony_ci status = MGMT_STATUS_SUCCESS; 50338c2ecf20Sopenharmony_ci 50348c2ecf20Sopenharmony_cidone: 50358c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_UNBLOCK_DEVICE, status, 50368c2ecf20Sopenharmony_ci &cp->addr, sizeof(cp->addr)); 50378c2ecf20Sopenharmony_ci 50388c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 50398c2ecf20Sopenharmony_ci 50408c2ecf20Sopenharmony_ci return err; 50418c2ecf20Sopenharmony_ci} 50428c2ecf20Sopenharmony_ci 50438c2ecf20Sopenharmony_cistatic int set_device_id(struct sock *sk, struct hci_dev *hdev, void *data, 50448c2ecf20Sopenharmony_ci u16 len) 50458c2ecf20Sopenharmony_ci{ 50468c2ecf20Sopenharmony_ci struct mgmt_cp_set_device_id *cp = data; 50478c2ecf20Sopenharmony_ci struct hci_request req; 50488c2ecf20Sopenharmony_ci int err; 50498c2ecf20Sopenharmony_ci __u16 source; 50508c2ecf20Sopenharmony_ci 50518c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 50528c2ecf20Sopenharmony_ci 50538c2ecf20Sopenharmony_ci source = __le16_to_cpu(cp->source); 50548c2ecf20Sopenharmony_ci 50558c2ecf20Sopenharmony_ci if (source > 0x0002) 50568c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_DEVICE_ID, 50578c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 50588c2ecf20Sopenharmony_ci 50598c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 50608c2ecf20Sopenharmony_ci 50618c2ecf20Sopenharmony_ci hdev->devid_source = source; 50628c2ecf20Sopenharmony_ci hdev->devid_vendor = __le16_to_cpu(cp->vendor); 50638c2ecf20Sopenharmony_ci hdev->devid_product = __le16_to_cpu(cp->product); 50648c2ecf20Sopenharmony_ci hdev->devid_version = __le16_to_cpu(cp->version); 50658c2ecf20Sopenharmony_ci 50668c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_DEVICE_ID, 0, 50678c2ecf20Sopenharmony_ci NULL, 0); 50688c2ecf20Sopenharmony_ci 50698c2ecf20Sopenharmony_ci hci_req_init(&req, hdev); 50708c2ecf20Sopenharmony_ci __hci_req_update_eir(&req); 50718c2ecf20Sopenharmony_ci hci_req_run(&req, NULL); 50728c2ecf20Sopenharmony_ci 50738c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 50748c2ecf20Sopenharmony_ci 50758c2ecf20Sopenharmony_ci return err; 50768c2ecf20Sopenharmony_ci} 50778c2ecf20Sopenharmony_ci 50788c2ecf20Sopenharmony_cistatic void enable_advertising_instance(struct hci_dev *hdev, u8 status, 50798c2ecf20Sopenharmony_ci u16 opcode) 50808c2ecf20Sopenharmony_ci{ 50818c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "status %d", status); 50828c2ecf20Sopenharmony_ci} 50838c2ecf20Sopenharmony_ci 50848c2ecf20Sopenharmony_cistatic void set_advertising_complete(struct hci_dev *hdev, u8 status, 50858c2ecf20Sopenharmony_ci u16 opcode) 50868c2ecf20Sopenharmony_ci{ 50878c2ecf20Sopenharmony_ci struct cmd_lookup match = { NULL, hdev }; 50888c2ecf20Sopenharmony_ci struct hci_request req; 50898c2ecf20Sopenharmony_ci u8 instance; 50908c2ecf20Sopenharmony_ci struct adv_info *adv_instance; 50918c2ecf20Sopenharmony_ci int err; 50928c2ecf20Sopenharmony_ci 50938c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 50948c2ecf20Sopenharmony_ci 50958c2ecf20Sopenharmony_ci if (status) { 50968c2ecf20Sopenharmony_ci u8 mgmt_err = mgmt_status(status); 50978c2ecf20Sopenharmony_ci 50988c2ecf20Sopenharmony_ci mgmt_pending_foreach(MGMT_OP_SET_ADVERTISING, hdev, 50998c2ecf20Sopenharmony_ci cmd_status_rsp, &mgmt_err); 51008c2ecf20Sopenharmony_ci goto unlock; 51018c2ecf20Sopenharmony_ci } 51028c2ecf20Sopenharmony_ci 51038c2ecf20Sopenharmony_ci if (hci_dev_test_flag(hdev, HCI_LE_ADV)) 51048c2ecf20Sopenharmony_ci hci_dev_set_flag(hdev, HCI_ADVERTISING); 51058c2ecf20Sopenharmony_ci else 51068c2ecf20Sopenharmony_ci hci_dev_clear_flag(hdev, HCI_ADVERTISING); 51078c2ecf20Sopenharmony_ci 51088c2ecf20Sopenharmony_ci mgmt_pending_foreach(MGMT_OP_SET_ADVERTISING, hdev, settings_rsp, 51098c2ecf20Sopenharmony_ci &match); 51108c2ecf20Sopenharmony_ci 51118c2ecf20Sopenharmony_ci new_settings(hdev, match.sk); 51128c2ecf20Sopenharmony_ci 51138c2ecf20Sopenharmony_ci if (match.sk) 51148c2ecf20Sopenharmony_ci sock_put(match.sk); 51158c2ecf20Sopenharmony_ci 51168c2ecf20Sopenharmony_ci /* Handle suspend notifier */ 51178c2ecf20Sopenharmony_ci if (test_and_clear_bit(SUSPEND_PAUSE_ADVERTISING, 51188c2ecf20Sopenharmony_ci hdev->suspend_tasks)) { 51198c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "Paused advertising"); 51208c2ecf20Sopenharmony_ci wake_up(&hdev->suspend_wait_q); 51218c2ecf20Sopenharmony_ci } else if (test_and_clear_bit(SUSPEND_UNPAUSE_ADVERTISING, 51228c2ecf20Sopenharmony_ci hdev->suspend_tasks)) { 51238c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "Unpaused advertising"); 51248c2ecf20Sopenharmony_ci wake_up(&hdev->suspend_wait_q); 51258c2ecf20Sopenharmony_ci } 51268c2ecf20Sopenharmony_ci 51278c2ecf20Sopenharmony_ci /* If "Set Advertising" was just disabled and instance advertising was 51288c2ecf20Sopenharmony_ci * set up earlier, then re-enable multi-instance advertising. 51298c2ecf20Sopenharmony_ci */ 51308c2ecf20Sopenharmony_ci if (hci_dev_test_flag(hdev, HCI_ADVERTISING) || 51318c2ecf20Sopenharmony_ci list_empty(&hdev->adv_instances)) 51328c2ecf20Sopenharmony_ci goto unlock; 51338c2ecf20Sopenharmony_ci 51348c2ecf20Sopenharmony_ci instance = hdev->cur_adv_instance; 51358c2ecf20Sopenharmony_ci if (!instance) { 51368c2ecf20Sopenharmony_ci adv_instance = list_first_entry_or_null(&hdev->adv_instances, 51378c2ecf20Sopenharmony_ci struct adv_info, list); 51388c2ecf20Sopenharmony_ci if (!adv_instance) 51398c2ecf20Sopenharmony_ci goto unlock; 51408c2ecf20Sopenharmony_ci 51418c2ecf20Sopenharmony_ci instance = adv_instance->instance; 51428c2ecf20Sopenharmony_ci } 51438c2ecf20Sopenharmony_ci 51448c2ecf20Sopenharmony_ci hci_req_init(&req, hdev); 51458c2ecf20Sopenharmony_ci 51468c2ecf20Sopenharmony_ci err = __hci_req_schedule_adv_instance(&req, instance, true); 51478c2ecf20Sopenharmony_ci 51488c2ecf20Sopenharmony_ci if (!err) 51498c2ecf20Sopenharmony_ci err = hci_req_run(&req, enable_advertising_instance); 51508c2ecf20Sopenharmony_ci 51518c2ecf20Sopenharmony_ci if (err) 51528c2ecf20Sopenharmony_ci bt_dev_err(hdev, "failed to re-configure advertising"); 51538c2ecf20Sopenharmony_ci 51548c2ecf20Sopenharmony_ciunlock: 51558c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 51568c2ecf20Sopenharmony_ci} 51578c2ecf20Sopenharmony_ci 51588c2ecf20Sopenharmony_cistatic int set_advertising(struct sock *sk, struct hci_dev *hdev, void *data, 51598c2ecf20Sopenharmony_ci u16 len) 51608c2ecf20Sopenharmony_ci{ 51618c2ecf20Sopenharmony_ci struct mgmt_mode *cp = data; 51628c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 51638c2ecf20Sopenharmony_ci struct hci_request req; 51648c2ecf20Sopenharmony_ci u8 val, status; 51658c2ecf20Sopenharmony_ci int err; 51668c2ecf20Sopenharmony_ci 51678c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 51688c2ecf20Sopenharmony_ci 51698c2ecf20Sopenharmony_ci status = mgmt_le_support(hdev); 51708c2ecf20Sopenharmony_ci if (status) 51718c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING, 51728c2ecf20Sopenharmony_ci status); 51738c2ecf20Sopenharmony_ci 51748c2ecf20Sopenharmony_ci /* Enabling the experimental LL Privay support disables support for 51758c2ecf20Sopenharmony_ci * advertising. 51768c2ecf20Sopenharmony_ci */ 51778c2ecf20Sopenharmony_ci if (hci_dev_test_flag(hdev, HCI_ENABLE_LL_PRIVACY)) 51788c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING, 51798c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_SUPPORTED); 51808c2ecf20Sopenharmony_ci 51818c2ecf20Sopenharmony_ci if (cp->val != 0x00 && cp->val != 0x01 && cp->val != 0x02) 51828c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING, 51838c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 51848c2ecf20Sopenharmony_ci 51858c2ecf20Sopenharmony_ci if (hdev->advertising_paused) 51868c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING, 51878c2ecf20Sopenharmony_ci MGMT_STATUS_BUSY); 51888c2ecf20Sopenharmony_ci 51898c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 51908c2ecf20Sopenharmony_ci 51918c2ecf20Sopenharmony_ci val = !!cp->val; 51928c2ecf20Sopenharmony_ci 51938c2ecf20Sopenharmony_ci /* The following conditions are ones which mean that we should 51948c2ecf20Sopenharmony_ci * not do any HCI communication but directly send a mgmt 51958c2ecf20Sopenharmony_ci * response to user space (after toggling the flag if 51968c2ecf20Sopenharmony_ci * necessary). 51978c2ecf20Sopenharmony_ci */ 51988c2ecf20Sopenharmony_ci if (!hdev_is_powered(hdev) || 51998c2ecf20Sopenharmony_ci (val == hci_dev_test_flag(hdev, HCI_ADVERTISING) && 52008c2ecf20Sopenharmony_ci (cp->val == 0x02) == hci_dev_test_flag(hdev, HCI_ADVERTISING_CONNECTABLE)) || 52018c2ecf20Sopenharmony_ci hci_conn_num(hdev, LE_LINK) > 0 || 52028c2ecf20Sopenharmony_ci (hci_dev_test_flag(hdev, HCI_LE_SCAN) && 52038c2ecf20Sopenharmony_ci hdev->le_scan_type == LE_SCAN_ACTIVE)) { 52048c2ecf20Sopenharmony_ci bool changed; 52058c2ecf20Sopenharmony_ci 52068c2ecf20Sopenharmony_ci if (cp->val) { 52078c2ecf20Sopenharmony_ci hdev->cur_adv_instance = 0x00; 52088c2ecf20Sopenharmony_ci changed = !hci_dev_test_and_set_flag(hdev, HCI_ADVERTISING); 52098c2ecf20Sopenharmony_ci if (cp->val == 0x02) 52108c2ecf20Sopenharmony_ci hci_dev_set_flag(hdev, HCI_ADVERTISING_CONNECTABLE); 52118c2ecf20Sopenharmony_ci else 52128c2ecf20Sopenharmony_ci hci_dev_clear_flag(hdev, HCI_ADVERTISING_CONNECTABLE); 52138c2ecf20Sopenharmony_ci } else { 52148c2ecf20Sopenharmony_ci changed = hci_dev_test_and_clear_flag(hdev, HCI_ADVERTISING); 52158c2ecf20Sopenharmony_ci hci_dev_clear_flag(hdev, HCI_ADVERTISING_CONNECTABLE); 52168c2ecf20Sopenharmony_ci } 52178c2ecf20Sopenharmony_ci 52188c2ecf20Sopenharmony_ci err = send_settings_rsp(sk, MGMT_OP_SET_ADVERTISING, hdev); 52198c2ecf20Sopenharmony_ci if (err < 0) 52208c2ecf20Sopenharmony_ci goto unlock; 52218c2ecf20Sopenharmony_ci 52228c2ecf20Sopenharmony_ci if (changed) 52238c2ecf20Sopenharmony_ci err = new_settings(hdev, sk); 52248c2ecf20Sopenharmony_ci 52258c2ecf20Sopenharmony_ci goto unlock; 52268c2ecf20Sopenharmony_ci } 52278c2ecf20Sopenharmony_ci 52288c2ecf20Sopenharmony_ci if (pending_find(MGMT_OP_SET_ADVERTISING, hdev) || 52298c2ecf20Sopenharmony_ci pending_find(MGMT_OP_SET_LE, hdev)) { 52308c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING, 52318c2ecf20Sopenharmony_ci MGMT_STATUS_BUSY); 52328c2ecf20Sopenharmony_ci goto unlock; 52338c2ecf20Sopenharmony_ci } 52348c2ecf20Sopenharmony_ci 52358c2ecf20Sopenharmony_ci cmd = mgmt_pending_add(sk, MGMT_OP_SET_ADVERTISING, hdev, data, len); 52368c2ecf20Sopenharmony_ci if (!cmd) { 52378c2ecf20Sopenharmony_ci err = -ENOMEM; 52388c2ecf20Sopenharmony_ci goto unlock; 52398c2ecf20Sopenharmony_ci } 52408c2ecf20Sopenharmony_ci 52418c2ecf20Sopenharmony_ci hci_req_init(&req, hdev); 52428c2ecf20Sopenharmony_ci 52438c2ecf20Sopenharmony_ci if (cp->val == 0x02) 52448c2ecf20Sopenharmony_ci hci_dev_set_flag(hdev, HCI_ADVERTISING_CONNECTABLE); 52458c2ecf20Sopenharmony_ci else 52468c2ecf20Sopenharmony_ci hci_dev_clear_flag(hdev, HCI_ADVERTISING_CONNECTABLE); 52478c2ecf20Sopenharmony_ci 52488c2ecf20Sopenharmony_ci cancel_adv_timeout(hdev); 52498c2ecf20Sopenharmony_ci 52508c2ecf20Sopenharmony_ci if (val) { 52518c2ecf20Sopenharmony_ci /* Switch to instance "0" for the Set Advertising setting. 52528c2ecf20Sopenharmony_ci * We cannot use update_[adv|scan_rsp]_data() here as the 52538c2ecf20Sopenharmony_ci * HCI_ADVERTISING flag is not yet set. 52548c2ecf20Sopenharmony_ci */ 52558c2ecf20Sopenharmony_ci hdev->cur_adv_instance = 0x00; 52568c2ecf20Sopenharmony_ci 52578c2ecf20Sopenharmony_ci if (ext_adv_capable(hdev)) { 52588c2ecf20Sopenharmony_ci __hci_req_start_ext_adv(&req, 0x00); 52598c2ecf20Sopenharmony_ci } else { 52608c2ecf20Sopenharmony_ci __hci_req_update_adv_data(&req, 0x00); 52618c2ecf20Sopenharmony_ci __hci_req_update_scan_rsp_data(&req, 0x00); 52628c2ecf20Sopenharmony_ci __hci_req_enable_advertising(&req); 52638c2ecf20Sopenharmony_ci } 52648c2ecf20Sopenharmony_ci } else { 52658c2ecf20Sopenharmony_ci __hci_req_disable_advertising(&req); 52668c2ecf20Sopenharmony_ci } 52678c2ecf20Sopenharmony_ci 52688c2ecf20Sopenharmony_ci err = hci_req_run(&req, set_advertising_complete); 52698c2ecf20Sopenharmony_ci if (err < 0) 52708c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 52718c2ecf20Sopenharmony_ci 52728c2ecf20Sopenharmony_ciunlock: 52738c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 52748c2ecf20Sopenharmony_ci return err; 52758c2ecf20Sopenharmony_ci} 52768c2ecf20Sopenharmony_ci 52778c2ecf20Sopenharmony_cistatic int set_static_address(struct sock *sk, struct hci_dev *hdev, 52788c2ecf20Sopenharmony_ci void *data, u16 len) 52798c2ecf20Sopenharmony_ci{ 52808c2ecf20Sopenharmony_ci struct mgmt_cp_set_static_address *cp = data; 52818c2ecf20Sopenharmony_ci int err; 52828c2ecf20Sopenharmony_ci 52838c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 52848c2ecf20Sopenharmony_ci 52858c2ecf20Sopenharmony_ci if (!lmp_le_capable(hdev)) 52868c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_STATIC_ADDRESS, 52878c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_SUPPORTED); 52888c2ecf20Sopenharmony_ci 52898c2ecf20Sopenharmony_ci if (hdev_is_powered(hdev)) 52908c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_STATIC_ADDRESS, 52918c2ecf20Sopenharmony_ci MGMT_STATUS_REJECTED); 52928c2ecf20Sopenharmony_ci 52938c2ecf20Sopenharmony_ci if (bacmp(&cp->bdaddr, BDADDR_ANY)) { 52948c2ecf20Sopenharmony_ci if (!bacmp(&cp->bdaddr, BDADDR_NONE)) 52958c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, 52968c2ecf20Sopenharmony_ci MGMT_OP_SET_STATIC_ADDRESS, 52978c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 52988c2ecf20Sopenharmony_ci 52998c2ecf20Sopenharmony_ci /* Two most significant bits shall be set */ 53008c2ecf20Sopenharmony_ci if ((cp->bdaddr.b[5] & 0xc0) != 0xc0) 53018c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, 53028c2ecf20Sopenharmony_ci MGMT_OP_SET_STATIC_ADDRESS, 53038c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 53048c2ecf20Sopenharmony_ci } 53058c2ecf20Sopenharmony_ci 53068c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 53078c2ecf20Sopenharmony_ci 53088c2ecf20Sopenharmony_ci bacpy(&hdev->static_addr, &cp->bdaddr); 53098c2ecf20Sopenharmony_ci 53108c2ecf20Sopenharmony_ci err = send_settings_rsp(sk, MGMT_OP_SET_STATIC_ADDRESS, hdev); 53118c2ecf20Sopenharmony_ci if (err < 0) 53128c2ecf20Sopenharmony_ci goto unlock; 53138c2ecf20Sopenharmony_ci 53148c2ecf20Sopenharmony_ci err = new_settings(hdev, sk); 53158c2ecf20Sopenharmony_ci 53168c2ecf20Sopenharmony_ciunlock: 53178c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 53188c2ecf20Sopenharmony_ci return err; 53198c2ecf20Sopenharmony_ci} 53208c2ecf20Sopenharmony_ci 53218c2ecf20Sopenharmony_cistatic int set_scan_params(struct sock *sk, struct hci_dev *hdev, 53228c2ecf20Sopenharmony_ci void *data, u16 len) 53238c2ecf20Sopenharmony_ci{ 53248c2ecf20Sopenharmony_ci struct mgmt_cp_set_scan_params *cp = data; 53258c2ecf20Sopenharmony_ci __u16 interval, window; 53268c2ecf20Sopenharmony_ci int err; 53278c2ecf20Sopenharmony_ci 53288c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 53298c2ecf20Sopenharmony_ci 53308c2ecf20Sopenharmony_ci if (!lmp_le_capable(hdev)) 53318c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_SCAN_PARAMS, 53328c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_SUPPORTED); 53338c2ecf20Sopenharmony_ci 53348c2ecf20Sopenharmony_ci interval = __le16_to_cpu(cp->interval); 53358c2ecf20Sopenharmony_ci 53368c2ecf20Sopenharmony_ci if (interval < 0x0004 || interval > 0x4000) 53378c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_SCAN_PARAMS, 53388c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 53398c2ecf20Sopenharmony_ci 53408c2ecf20Sopenharmony_ci window = __le16_to_cpu(cp->window); 53418c2ecf20Sopenharmony_ci 53428c2ecf20Sopenharmony_ci if (window < 0x0004 || window > 0x4000) 53438c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_SCAN_PARAMS, 53448c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 53458c2ecf20Sopenharmony_ci 53468c2ecf20Sopenharmony_ci if (window > interval) 53478c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_SCAN_PARAMS, 53488c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 53498c2ecf20Sopenharmony_ci 53508c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 53518c2ecf20Sopenharmony_ci 53528c2ecf20Sopenharmony_ci hdev->le_scan_interval = interval; 53538c2ecf20Sopenharmony_ci hdev->le_scan_window = window; 53548c2ecf20Sopenharmony_ci 53558c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_SCAN_PARAMS, 0, 53568c2ecf20Sopenharmony_ci NULL, 0); 53578c2ecf20Sopenharmony_ci 53588c2ecf20Sopenharmony_ci /* If background scan is running, restart it so new parameters are 53598c2ecf20Sopenharmony_ci * loaded. 53608c2ecf20Sopenharmony_ci */ 53618c2ecf20Sopenharmony_ci if (hci_dev_test_flag(hdev, HCI_LE_SCAN) && 53628c2ecf20Sopenharmony_ci hdev->discovery.state == DISCOVERY_STOPPED) { 53638c2ecf20Sopenharmony_ci struct hci_request req; 53648c2ecf20Sopenharmony_ci 53658c2ecf20Sopenharmony_ci hci_req_init(&req, hdev); 53668c2ecf20Sopenharmony_ci 53678c2ecf20Sopenharmony_ci hci_req_add_le_scan_disable(&req, false); 53688c2ecf20Sopenharmony_ci hci_req_add_le_passive_scan(&req); 53698c2ecf20Sopenharmony_ci 53708c2ecf20Sopenharmony_ci hci_req_run(&req, NULL); 53718c2ecf20Sopenharmony_ci } 53728c2ecf20Sopenharmony_ci 53738c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 53748c2ecf20Sopenharmony_ci 53758c2ecf20Sopenharmony_ci return err; 53768c2ecf20Sopenharmony_ci} 53778c2ecf20Sopenharmony_ci 53788c2ecf20Sopenharmony_cistatic void fast_connectable_complete(struct hci_dev *hdev, u8 status, 53798c2ecf20Sopenharmony_ci u16 opcode) 53808c2ecf20Sopenharmony_ci{ 53818c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 53828c2ecf20Sopenharmony_ci 53838c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "status 0x%02x", status); 53848c2ecf20Sopenharmony_ci 53858c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 53868c2ecf20Sopenharmony_ci 53878c2ecf20Sopenharmony_ci cmd = pending_find(MGMT_OP_SET_FAST_CONNECTABLE, hdev); 53888c2ecf20Sopenharmony_ci if (!cmd) 53898c2ecf20Sopenharmony_ci goto unlock; 53908c2ecf20Sopenharmony_ci 53918c2ecf20Sopenharmony_ci if (status) { 53928c2ecf20Sopenharmony_ci mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, 53938c2ecf20Sopenharmony_ci mgmt_status(status)); 53948c2ecf20Sopenharmony_ci } else { 53958c2ecf20Sopenharmony_ci struct mgmt_mode *cp = cmd->param; 53968c2ecf20Sopenharmony_ci 53978c2ecf20Sopenharmony_ci if (cp->val) 53988c2ecf20Sopenharmony_ci hci_dev_set_flag(hdev, HCI_FAST_CONNECTABLE); 53998c2ecf20Sopenharmony_ci else 54008c2ecf20Sopenharmony_ci hci_dev_clear_flag(hdev, HCI_FAST_CONNECTABLE); 54018c2ecf20Sopenharmony_ci 54028c2ecf20Sopenharmony_ci send_settings_rsp(cmd->sk, MGMT_OP_SET_FAST_CONNECTABLE, hdev); 54038c2ecf20Sopenharmony_ci new_settings(hdev, cmd->sk); 54048c2ecf20Sopenharmony_ci } 54058c2ecf20Sopenharmony_ci 54068c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 54078c2ecf20Sopenharmony_ci 54088c2ecf20Sopenharmony_ciunlock: 54098c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 54108c2ecf20Sopenharmony_ci} 54118c2ecf20Sopenharmony_ci 54128c2ecf20Sopenharmony_cistatic int set_fast_connectable(struct sock *sk, struct hci_dev *hdev, 54138c2ecf20Sopenharmony_ci void *data, u16 len) 54148c2ecf20Sopenharmony_ci{ 54158c2ecf20Sopenharmony_ci struct mgmt_mode *cp = data; 54168c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 54178c2ecf20Sopenharmony_ci struct hci_request req; 54188c2ecf20Sopenharmony_ci int err; 54198c2ecf20Sopenharmony_ci 54208c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 54218c2ecf20Sopenharmony_ci 54228c2ecf20Sopenharmony_ci if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED) || 54238c2ecf20Sopenharmony_ci hdev->hci_ver < BLUETOOTH_VER_1_2) 54248c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, 54258c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_SUPPORTED); 54268c2ecf20Sopenharmony_ci 54278c2ecf20Sopenharmony_ci if (cp->val != 0x00 && cp->val != 0x01) 54288c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, 54298c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 54308c2ecf20Sopenharmony_ci 54318c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 54328c2ecf20Sopenharmony_ci 54338c2ecf20Sopenharmony_ci if (pending_find(MGMT_OP_SET_FAST_CONNECTABLE, hdev)) { 54348c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, 54358c2ecf20Sopenharmony_ci MGMT_STATUS_BUSY); 54368c2ecf20Sopenharmony_ci goto unlock; 54378c2ecf20Sopenharmony_ci } 54388c2ecf20Sopenharmony_ci 54398c2ecf20Sopenharmony_ci if (!!cp->val == hci_dev_test_flag(hdev, HCI_FAST_CONNECTABLE)) { 54408c2ecf20Sopenharmony_ci err = send_settings_rsp(sk, MGMT_OP_SET_FAST_CONNECTABLE, 54418c2ecf20Sopenharmony_ci hdev); 54428c2ecf20Sopenharmony_ci goto unlock; 54438c2ecf20Sopenharmony_ci } 54448c2ecf20Sopenharmony_ci 54458c2ecf20Sopenharmony_ci if (!hdev_is_powered(hdev)) { 54468c2ecf20Sopenharmony_ci hci_dev_change_flag(hdev, HCI_FAST_CONNECTABLE); 54478c2ecf20Sopenharmony_ci err = send_settings_rsp(sk, MGMT_OP_SET_FAST_CONNECTABLE, 54488c2ecf20Sopenharmony_ci hdev); 54498c2ecf20Sopenharmony_ci new_settings(hdev, sk); 54508c2ecf20Sopenharmony_ci goto unlock; 54518c2ecf20Sopenharmony_ci } 54528c2ecf20Sopenharmony_ci 54538c2ecf20Sopenharmony_ci cmd = mgmt_pending_add(sk, MGMT_OP_SET_FAST_CONNECTABLE, hdev, 54548c2ecf20Sopenharmony_ci data, len); 54558c2ecf20Sopenharmony_ci if (!cmd) { 54568c2ecf20Sopenharmony_ci err = -ENOMEM; 54578c2ecf20Sopenharmony_ci goto unlock; 54588c2ecf20Sopenharmony_ci } 54598c2ecf20Sopenharmony_ci 54608c2ecf20Sopenharmony_ci hci_req_init(&req, hdev); 54618c2ecf20Sopenharmony_ci 54628c2ecf20Sopenharmony_ci __hci_req_write_fast_connectable(&req, cp->val); 54638c2ecf20Sopenharmony_ci 54648c2ecf20Sopenharmony_ci err = hci_req_run(&req, fast_connectable_complete); 54658c2ecf20Sopenharmony_ci if (err < 0) { 54668c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_FAST_CONNECTABLE, 54678c2ecf20Sopenharmony_ci MGMT_STATUS_FAILED); 54688c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 54698c2ecf20Sopenharmony_ci } 54708c2ecf20Sopenharmony_ci 54718c2ecf20Sopenharmony_ciunlock: 54728c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 54738c2ecf20Sopenharmony_ci 54748c2ecf20Sopenharmony_ci return err; 54758c2ecf20Sopenharmony_ci} 54768c2ecf20Sopenharmony_ci 54778c2ecf20Sopenharmony_cistatic void set_bredr_complete(struct hci_dev *hdev, u8 status, u16 opcode) 54788c2ecf20Sopenharmony_ci{ 54798c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 54808c2ecf20Sopenharmony_ci 54818c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "status 0x%02x", status); 54828c2ecf20Sopenharmony_ci 54838c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 54848c2ecf20Sopenharmony_ci 54858c2ecf20Sopenharmony_ci cmd = pending_find(MGMT_OP_SET_BREDR, hdev); 54868c2ecf20Sopenharmony_ci if (!cmd) 54878c2ecf20Sopenharmony_ci goto unlock; 54888c2ecf20Sopenharmony_ci 54898c2ecf20Sopenharmony_ci if (status) { 54908c2ecf20Sopenharmony_ci u8 mgmt_err = mgmt_status(status); 54918c2ecf20Sopenharmony_ci 54928c2ecf20Sopenharmony_ci /* We need to restore the flag if related HCI commands 54938c2ecf20Sopenharmony_ci * failed. 54948c2ecf20Sopenharmony_ci */ 54958c2ecf20Sopenharmony_ci hci_dev_clear_flag(hdev, HCI_BREDR_ENABLED); 54968c2ecf20Sopenharmony_ci 54978c2ecf20Sopenharmony_ci mgmt_cmd_status(cmd->sk, cmd->index, cmd->opcode, mgmt_err); 54988c2ecf20Sopenharmony_ci } else { 54998c2ecf20Sopenharmony_ci send_settings_rsp(cmd->sk, MGMT_OP_SET_BREDR, hdev); 55008c2ecf20Sopenharmony_ci new_settings(hdev, cmd->sk); 55018c2ecf20Sopenharmony_ci } 55028c2ecf20Sopenharmony_ci 55038c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 55048c2ecf20Sopenharmony_ci 55058c2ecf20Sopenharmony_ciunlock: 55068c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 55078c2ecf20Sopenharmony_ci} 55088c2ecf20Sopenharmony_ci 55098c2ecf20Sopenharmony_cistatic int set_bredr(struct sock *sk, struct hci_dev *hdev, void *data, u16 len) 55108c2ecf20Sopenharmony_ci{ 55118c2ecf20Sopenharmony_ci struct mgmt_mode *cp = data; 55128c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 55138c2ecf20Sopenharmony_ci struct hci_request req; 55148c2ecf20Sopenharmony_ci int err; 55158c2ecf20Sopenharmony_ci 55168c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 55178c2ecf20Sopenharmony_ci 55188c2ecf20Sopenharmony_ci if (!lmp_bredr_capable(hdev) || !lmp_le_capable(hdev)) 55198c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_BREDR, 55208c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_SUPPORTED); 55218c2ecf20Sopenharmony_ci 55228c2ecf20Sopenharmony_ci if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED)) 55238c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_BREDR, 55248c2ecf20Sopenharmony_ci MGMT_STATUS_REJECTED); 55258c2ecf20Sopenharmony_ci 55268c2ecf20Sopenharmony_ci if (cp->val != 0x00 && cp->val != 0x01) 55278c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_BREDR, 55288c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 55298c2ecf20Sopenharmony_ci 55308c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 55318c2ecf20Sopenharmony_ci 55328c2ecf20Sopenharmony_ci if (cp->val == hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) { 55338c2ecf20Sopenharmony_ci err = send_settings_rsp(sk, MGMT_OP_SET_BREDR, hdev); 55348c2ecf20Sopenharmony_ci goto unlock; 55358c2ecf20Sopenharmony_ci } 55368c2ecf20Sopenharmony_ci 55378c2ecf20Sopenharmony_ci if (!hdev_is_powered(hdev)) { 55388c2ecf20Sopenharmony_ci if (!cp->val) { 55398c2ecf20Sopenharmony_ci hci_dev_clear_flag(hdev, HCI_DISCOVERABLE); 55408c2ecf20Sopenharmony_ci hci_dev_clear_flag(hdev, HCI_SSP_ENABLED); 55418c2ecf20Sopenharmony_ci hci_dev_clear_flag(hdev, HCI_LINK_SECURITY); 55428c2ecf20Sopenharmony_ci hci_dev_clear_flag(hdev, HCI_FAST_CONNECTABLE); 55438c2ecf20Sopenharmony_ci hci_dev_clear_flag(hdev, HCI_HS_ENABLED); 55448c2ecf20Sopenharmony_ci } 55458c2ecf20Sopenharmony_ci 55468c2ecf20Sopenharmony_ci hci_dev_change_flag(hdev, HCI_BREDR_ENABLED); 55478c2ecf20Sopenharmony_ci 55488c2ecf20Sopenharmony_ci err = send_settings_rsp(sk, MGMT_OP_SET_BREDR, hdev); 55498c2ecf20Sopenharmony_ci if (err < 0) 55508c2ecf20Sopenharmony_ci goto unlock; 55518c2ecf20Sopenharmony_ci 55528c2ecf20Sopenharmony_ci err = new_settings(hdev, sk); 55538c2ecf20Sopenharmony_ci goto unlock; 55548c2ecf20Sopenharmony_ci } 55558c2ecf20Sopenharmony_ci 55568c2ecf20Sopenharmony_ci /* Reject disabling when powered on */ 55578c2ecf20Sopenharmony_ci if (!cp->val) { 55588c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_BREDR, 55598c2ecf20Sopenharmony_ci MGMT_STATUS_REJECTED); 55608c2ecf20Sopenharmony_ci goto unlock; 55618c2ecf20Sopenharmony_ci } else { 55628c2ecf20Sopenharmony_ci /* When configuring a dual-mode controller to operate 55638c2ecf20Sopenharmony_ci * with LE only and using a static address, then switching 55648c2ecf20Sopenharmony_ci * BR/EDR back on is not allowed. 55658c2ecf20Sopenharmony_ci * 55668c2ecf20Sopenharmony_ci * Dual-mode controllers shall operate with the public 55678c2ecf20Sopenharmony_ci * address as its identity address for BR/EDR and LE. So 55688c2ecf20Sopenharmony_ci * reject the attempt to create an invalid configuration. 55698c2ecf20Sopenharmony_ci * 55708c2ecf20Sopenharmony_ci * The same restrictions applies when secure connections 55718c2ecf20Sopenharmony_ci * has been enabled. For BR/EDR this is a controller feature 55728c2ecf20Sopenharmony_ci * while for LE it is a host stack feature. This means that 55738c2ecf20Sopenharmony_ci * switching BR/EDR back on when secure connections has been 55748c2ecf20Sopenharmony_ci * enabled is not a supported transaction. 55758c2ecf20Sopenharmony_ci */ 55768c2ecf20Sopenharmony_ci if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED) && 55778c2ecf20Sopenharmony_ci (bacmp(&hdev->static_addr, BDADDR_ANY) || 55788c2ecf20Sopenharmony_ci hci_dev_test_flag(hdev, HCI_SC_ENABLED))) { 55798c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_BREDR, 55808c2ecf20Sopenharmony_ci MGMT_STATUS_REJECTED); 55818c2ecf20Sopenharmony_ci goto unlock; 55828c2ecf20Sopenharmony_ci } 55838c2ecf20Sopenharmony_ci } 55848c2ecf20Sopenharmony_ci 55858c2ecf20Sopenharmony_ci if (pending_find(MGMT_OP_SET_BREDR, hdev)) { 55868c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_BREDR, 55878c2ecf20Sopenharmony_ci MGMT_STATUS_BUSY); 55888c2ecf20Sopenharmony_ci goto unlock; 55898c2ecf20Sopenharmony_ci } 55908c2ecf20Sopenharmony_ci 55918c2ecf20Sopenharmony_ci cmd = mgmt_pending_add(sk, MGMT_OP_SET_BREDR, hdev, data, len); 55928c2ecf20Sopenharmony_ci if (!cmd) { 55938c2ecf20Sopenharmony_ci err = -ENOMEM; 55948c2ecf20Sopenharmony_ci goto unlock; 55958c2ecf20Sopenharmony_ci } 55968c2ecf20Sopenharmony_ci 55978c2ecf20Sopenharmony_ci /* We need to flip the bit already here so that 55988c2ecf20Sopenharmony_ci * hci_req_update_adv_data generates the correct flags. 55998c2ecf20Sopenharmony_ci */ 56008c2ecf20Sopenharmony_ci hci_dev_set_flag(hdev, HCI_BREDR_ENABLED); 56018c2ecf20Sopenharmony_ci 56028c2ecf20Sopenharmony_ci hci_req_init(&req, hdev); 56038c2ecf20Sopenharmony_ci 56048c2ecf20Sopenharmony_ci __hci_req_write_fast_connectable(&req, false); 56058c2ecf20Sopenharmony_ci __hci_req_update_scan(&req); 56068c2ecf20Sopenharmony_ci 56078c2ecf20Sopenharmony_ci /* Since only the advertising data flags will change, there 56088c2ecf20Sopenharmony_ci * is no need to update the scan response data. 56098c2ecf20Sopenharmony_ci */ 56108c2ecf20Sopenharmony_ci __hci_req_update_adv_data(&req, hdev->cur_adv_instance); 56118c2ecf20Sopenharmony_ci 56128c2ecf20Sopenharmony_ci err = hci_req_run(&req, set_bredr_complete); 56138c2ecf20Sopenharmony_ci if (err < 0) 56148c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 56158c2ecf20Sopenharmony_ci 56168c2ecf20Sopenharmony_ciunlock: 56178c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 56188c2ecf20Sopenharmony_ci return err; 56198c2ecf20Sopenharmony_ci} 56208c2ecf20Sopenharmony_ci 56218c2ecf20Sopenharmony_cistatic void sc_enable_complete(struct hci_dev *hdev, u8 status, u16 opcode) 56228c2ecf20Sopenharmony_ci{ 56238c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 56248c2ecf20Sopenharmony_ci struct mgmt_mode *cp; 56258c2ecf20Sopenharmony_ci 56268c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "status %u", status); 56278c2ecf20Sopenharmony_ci 56288c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 56298c2ecf20Sopenharmony_ci 56308c2ecf20Sopenharmony_ci cmd = pending_find(MGMT_OP_SET_SECURE_CONN, hdev); 56318c2ecf20Sopenharmony_ci if (!cmd) 56328c2ecf20Sopenharmony_ci goto unlock; 56338c2ecf20Sopenharmony_ci 56348c2ecf20Sopenharmony_ci if (status) { 56358c2ecf20Sopenharmony_ci mgmt_cmd_status(cmd->sk, cmd->index, cmd->opcode, 56368c2ecf20Sopenharmony_ci mgmt_status(status)); 56378c2ecf20Sopenharmony_ci goto remove; 56388c2ecf20Sopenharmony_ci } 56398c2ecf20Sopenharmony_ci 56408c2ecf20Sopenharmony_ci cp = cmd->param; 56418c2ecf20Sopenharmony_ci 56428c2ecf20Sopenharmony_ci switch (cp->val) { 56438c2ecf20Sopenharmony_ci case 0x00: 56448c2ecf20Sopenharmony_ci hci_dev_clear_flag(hdev, HCI_SC_ENABLED); 56458c2ecf20Sopenharmony_ci hci_dev_clear_flag(hdev, HCI_SC_ONLY); 56468c2ecf20Sopenharmony_ci break; 56478c2ecf20Sopenharmony_ci case 0x01: 56488c2ecf20Sopenharmony_ci hci_dev_set_flag(hdev, HCI_SC_ENABLED); 56498c2ecf20Sopenharmony_ci hci_dev_clear_flag(hdev, HCI_SC_ONLY); 56508c2ecf20Sopenharmony_ci break; 56518c2ecf20Sopenharmony_ci case 0x02: 56528c2ecf20Sopenharmony_ci hci_dev_set_flag(hdev, HCI_SC_ENABLED); 56538c2ecf20Sopenharmony_ci hci_dev_set_flag(hdev, HCI_SC_ONLY); 56548c2ecf20Sopenharmony_ci break; 56558c2ecf20Sopenharmony_ci } 56568c2ecf20Sopenharmony_ci 56578c2ecf20Sopenharmony_ci send_settings_rsp(cmd->sk, MGMT_OP_SET_SECURE_CONN, hdev); 56588c2ecf20Sopenharmony_ci new_settings(hdev, cmd->sk); 56598c2ecf20Sopenharmony_ci 56608c2ecf20Sopenharmony_ciremove: 56618c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 56628c2ecf20Sopenharmony_ciunlock: 56638c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 56648c2ecf20Sopenharmony_ci} 56658c2ecf20Sopenharmony_ci 56668c2ecf20Sopenharmony_cistatic int set_secure_conn(struct sock *sk, struct hci_dev *hdev, 56678c2ecf20Sopenharmony_ci void *data, u16 len) 56688c2ecf20Sopenharmony_ci{ 56698c2ecf20Sopenharmony_ci struct mgmt_mode *cp = data; 56708c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 56718c2ecf20Sopenharmony_ci struct hci_request req; 56728c2ecf20Sopenharmony_ci u8 val; 56738c2ecf20Sopenharmony_ci int err; 56748c2ecf20Sopenharmony_ci 56758c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 56768c2ecf20Sopenharmony_ci 56778c2ecf20Sopenharmony_ci if (!lmp_sc_capable(hdev) && 56788c2ecf20Sopenharmony_ci !hci_dev_test_flag(hdev, HCI_LE_ENABLED)) 56798c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_SECURE_CONN, 56808c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_SUPPORTED); 56818c2ecf20Sopenharmony_ci 56828c2ecf20Sopenharmony_ci if (hci_dev_test_flag(hdev, HCI_BREDR_ENABLED) && 56838c2ecf20Sopenharmony_ci lmp_sc_capable(hdev) && 56848c2ecf20Sopenharmony_ci !hci_dev_test_flag(hdev, HCI_SSP_ENABLED)) 56858c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_SECURE_CONN, 56868c2ecf20Sopenharmony_ci MGMT_STATUS_REJECTED); 56878c2ecf20Sopenharmony_ci 56888c2ecf20Sopenharmony_ci if (cp->val != 0x00 && cp->val != 0x01 && cp->val != 0x02) 56898c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_SECURE_CONN, 56908c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 56918c2ecf20Sopenharmony_ci 56928c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 56938c2ecf20Sopenharmony_ci 56948c2ecf20Sopenharmony_ci if (!hdev_is_powered(hdev) || !lmp_sc_capable(hdev) || 56958c2ecf20Sopenharmony_ci !hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) { 56968c2ecf20Sopenharmony_ci bool changed; 56978c2ecf20Sopenharmony_ci 56988c2ecf20Sopenharmony_ci if (cp->val) { 56998c2ecf20Sopenharmony_ci changed = !hci_dev_test_and_set_flag(hdev, 57008c2ecf20Sopenharmony_ci HCI_SC_ENABLED); 57018c2ecf20Sopenharmony_ci if (cp->val == 0x02) 57028c2ecf20Sopenharmony_ci hci_dev_set_flag(hdev, HCI_SC_ONLY); 57038c2ecf20Sopenharmony_ci else 57048c2ecf20Sopenharmony_ci hci_dev_clear_flag(hdev, HCI_SC_ONLY); 57058c2ecf20Sopenharmony_ci } else { 57068c2ecf20Sopenharmony_ci changed = hci_dev_test_and_clear_flag(hdev, 57078c2ecf20Sopenharmony_ci HCI_SC_ENABLED); 57088c2ecf20Sopenharmony_ci hci_dev_clear_flag(hdev, HCI_SC_ONLY); 57098c2ecf20Sopenharmony_ci } 57108c2ecf20Sopenharmony_ci 57118c2ecf20Sopenharmony_ci err = send_settings_rsp(sk, MGMT_OP_SET_SECURE_CONN, hdev); 57128c2ecf20Sopenharmony_ci if (err < 0) 57138c2ecf20Sopenharmony_ci goto failed; 57148c2ecf20Sopenharmony_ci 57158c2ecf20Sopenharmony_ci if (changed) 57168c2ecf20Sopenharmony_ci err = new_settings(hdev, sk); 57178c2ecf20Sopenharmony_ci 57188c2ecf20Sopenharmony_ci goto failed; 57198c2ecf20Sopenharmony_ci } 57208c2ecf20Sopenharmony_ci 57218c2ecf20Sopenharmony_ci if (pending_find(MGMT_OP_SET_SECURE_CONN, hdev)) { 57228c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_SECURE_CONN, 57238c2ecf20Sopenharmony_ci MGMT_STATUS_BUSY); 57248c2ecf20Sopenharmony_ci goto failed; 57258c2ecf20Sopenharmony_ci } 57268c2ecf20Sopenharmony_ci 57278c2ecf20Sopenharmony_ci val = !!cp->val; 57288c2ecf20Sopenharmony_ci 57298c2ecf20Sopenharmony_ci if (val == hci_dev_test_flag(hdev, HCI_SC_ENABLED) && 57308c2ecf20Sopenharmony_ci (cp->val == 0x02) == hci_dev_test_flag(hdev, HCI_SC_ONLY)) { 57318c2ecf20Sopenharmony_ci err = send_settings_rsp(sk, MGMT_OP_SET_SECURE_CONN, hdev); 57328c2ecf20Sopenharmony_ci goto failed; 57338c2ecf20Sopenharmony_ci } 57348c2ecf20Sopenharmony_ci 57358c2ecf20Sopenharmony_ci cmd = mgmt_pending_add(sk, MGMT_OP_SET_SECURE_CONN, hdev, data, len); 57368c2ecf20Sopenharmony_ci if (!cmd) { 57378c2ecf20Sopenharmony_ci err = -ENOMEM; 57388c2ecf20Sopenharmony_ci goto failed; 57398c2ecf20Sopenharmony_ci } 57408c2ecf20Sopenharmony_ci 57418c2ecf20Sopenharmony_ci hci_req_init(&req, hdev); 57428c2ecf20Sopenharmony_ci hci_req_add(&req, HCI_OP_WRITE_SC_SUPPORT, 1, &val); 57438c2ecf20Sopenharmony_ci err = hci_req_run(&req, sc_enable_complete); 57448c2ecf20Sopenharmony_ci if (err < 0) { 57458c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 57468c2ecf20Sopenharmony_ci goto failed; 57478c2ecf20Sopenharmony_ci } 57488c2ecf20Sopenharmony_ci 57498c2ecf20Sopenharmony_cifailed: 57508c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 57518c2ecf20Sopenharmony_ci return err; 57528c2ecf20Sopenharmony_ci} 57538c2ecf20Sopenharmony_ci 57548c2ecf20Sopenharmony_cistatic int set_debug_keys(struct sock *sk, struct hci_dev *hdev, 57558c2ecf20Sopenharmony_ci void *data, u16 len) 57568c2ecf20Sopenharmony_ci{ 57578c2ecf20Sopenharmony_ci struct mgmt_mode *cp = data; 57588c2ecf20Sopenharmony_ci bool changed, use_changed; 57598c2ecf20Sopenharmony_ci int err; 57608c2ecf20Sopenharmony_ci 57618c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 57628c2ecf20Sopenharmony_ci 57638c2ecf20Sopenharmony_ci if (cp->val != 0x00 && cp->val != 0x01 && cp->val != 0x02) 57648c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_DEBUG_KEYS, 57658c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 57668c2ecf20Sopenharmony_ci 57678c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 57688c2ecf20Sopenharmony_ci 57698c2ecf20Sopenharmony_ci if (cp->val) 57708c2ecf20Sopenharmony_ci changed = !hci_dev_test_and_set_flag(hdev, HCI_KEEP_DEBUG_KEYS); 57718c2ecf20Sopenharmony_ci else 57728c2ecf20Sopenharmony_ci changed = hci_dev_test_and_clear_flag(hdev, 57738c2ecf20Sopenharmony_ci HCI_KEEP_DEBUG_KEYS); 57748c2ecf20Sopenharmony_ci 57758c2ecf20Sopenharmony_ci if (cp->val == 0x02) 57768c2ecf20Sopenharmony_ci use_changed = !hci_dev_test_and_set_flag(hdev, 57778c2ecf20Sopenharmony_ci HCI_USE_DEBUG_KEYS); 57788c2ecf20Sopenharmony_ci else 57798c2ecf20Sopenharmony_ci use_changed = hci_dev_test_and_clear_flag(hdev, 57808c2ecf20Sopenharmony_ci HCI_USE_DEBUG_KEYS); 57818c2ecf20Sopenharmony_ci 57828c2ecf20Sopenharmony_ci if (hdev_is_powered(hdev) && use_changed && 57838c2ecf20Sopenharmony_ci hci_dev_test_flag(hdev, HCI_SSP_ENABLED)) { 57848c2ecf20Sopenharmony_ci u8 mode = (cp->val == 0x02) ? 0x01 : 0x00; 57858c2ecf20Sopenharmony_ci hci_send_cmd(hdev, HCI_OP_WRITE_SSP_DEBUG_MODE, 57868c2ecf20Sopenharmony_ci sizeof(mode), &mode); 57878c2ecf20Sopenharmony_ci } 57888c2ecf20Sopenharmony_ci 57898c2ecf20Sopenharmony_ci err = send_settings_rsp(sk, MGMT_OP_SET_DEBUG_KEYS, hdev); 57908c2ecf20Sopenharmony_ci if (err < 0) 57918c2ecf20Sopenharmony_ci goto unlock; 57928c2ecf20Sopenharmony_ci 57938c2ecf20Sopenharmony_ci if (changed) 57948c2ecf20Sopenharmony_ci err = new_settings(hdev, sk); 57958c2ecf20Sopenharmony_ci 57968c2ecf20Sopenharmony_ciunlock: 57978c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 57988c2ecf20Sopenharmony_ci return err; 57998c2ecf20Sopenharmony_ci} 58008c2ecf20Sopenharmony_ci 58018c2ecf20Sopenharmony_cistatic int set_privacy(struct sock *sk, struct hci_dev *hdev, void *cp_data, 58028c2ecf20Sopenharmony_ci u16 len) 58038c2ecf20Sopenharmony_ci{ 58048c2ecf20Sopenharmony_ci struct mgmt_cp_set_privacy *cp = cp_data; 58058c2ecf20Sopenharmony_ci bool changed; 58068c2ecf20Sopenharmony_ci int err; 58078c2ecf20Sopenharmony_ci 58088c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 58098c2ecf20Sopenharmony_ci 58108c2ecf20Sopenharmony_ci if (!lmp_le_capable(hdev)) 58118c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_PRIVACY, 58128c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_SUPPORTED); 58138c2ecf20Sopenharmony_ci 58148c2ecf20Sopenharmony_ci if (cp->privacy != 0x00 && cp->privacy != 0x01 && cp->privacy != 0x02) 58158c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_PRIVACY, 58168c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 58178c2ecf20Sopenharmony_ci 58188c2ecf20Sopenharmony_ci if (hdev_is_powered(hdev)) 58198c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_PRIVACY, 58208c2ecf20Sopenharmony_ci MGMT_STATUS_REJECTED); 58218c2ecf20Sopenharmony_ci 58228c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 58238c2ecf20Sopenharmony_ci 58248c2ecf20Sopenharmony_ci /* If user space supports this command it is also expected to 58258c2ecf20Sopenharmony_ci * handle IRKs. Therefore, set the HCI_RPA_RESOLVING flag. 58268c2ecf20Sopenharmony_ci */ 58278c2ecf20Sopenharmony_ci hci_dev_set_flag(hdev, HCI_RPA_RESOLVING); 58288c2ecf20Sopenharmony_ci 58298c2ecf20Sopenharmony_ci if (cp->privacy) { 58308c2ecf20Sopenharmony_ci changed = !hci_dev_test_and_set_flag(hdev, HCI_PRIVACY); 58318c2ecf20Sopenharmony_ci memcpy(hdev->irk, cp->irk, sizeof(hdev->irk)); 58328c2ecf20Sopenharmony_ci hci_dev_set_flag(hdev, HCI_RPA_EXPIRED); 58338c2ecf20Sopenharmony_ci hci_adv_instances_set_rpa_expired(hdev, true); 58348c2ecf20Sopenharmony_ci if (cp->privacy == 0x02) 58358c2ecf20Sopenharmony_ci hci_dev_set_flag(hdev, HCI_LIMITED_PRIVACY); 58368c2ecf20Sopenharmony_ci else 58378c2ecf20Sopenharmony_ci hci_dev_clear_flag(hdev, HCI_LIMITED_PRIVACY); 58388c2ecf20Sopenharmony_ci } else { 58398c2ecf20Sopenharmony_ci changed = hci_dev_test_and_clear_flag(hdev, HCI_PRIVACY); 58408c2ecf20Sopenharmony_ci memset(hdev->irk, 0, sizeof(hdev->irk)); 58418c2ecf20Sopenharmony_ci hci_dev_clear_flag(hdev, HCI_RPA_EXPIRED); 58428c2ecf20Sopenharmony_ci hci_adv_instances_set_rpa_expired(hdev, false); 58438c2ecf20Sopenharmony_ci hci_dev_clear_flag(hdev, HCI_LIMITED_PRIVACY); 58448c2ecf20Sopenharmony_ci } 58458c2ecf20Sopenharmony_ci 58468c2ecf20Sopenharmony_ci err = send_settings_rsp(sk, MGMT_OP_SET_PRIVACY, hdev); 58478c2ecf20Sopenharmony_ci if (err < 0) 58488c2ecf20Sopenharmony_ci goto unlock; 58498c2ecf20Sopenharmony_ci 58508c2ecf20Sopenharmony_ci if (changed) 58518c2ecf20Sopenharmony_ci err = new_settings(hdev, sk); 58528c2ecf20Sopenharmony_ci 58538c2ecf20Sopenharmony_ciunlock: 58548c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 58558c2ecf20Sopenharmony_ci return err; 58568c2ecf20Sopenharmony_ci} 58578c2ecf20Sopenharmony_ci 58588c2ecf20Sopenharmony_cistatic bool irk_is_valid(struct mgmt_irk_info *irk) 58598c2ecf20Sopenharmony_ci{ 58608c2ecf20Sopenharmony_ci switch (irk->addr.type) { 58618c2ecf20Sopenharmony_ci case BDADDR_LE_PUBLIC: 58628c2ecf20Sopenharmony_ci return true; 58638c2ecf20Sopenharmony_ci 58648c2ecf20Sopenharmony_ci case BDADDR_LE_RANDOM: 58658c2ecf20Sopenharmony_ci /* Two most significant bits shall be set */ 58668c2ecf20Sopenharmony_ci if ((irk->addr.bdaddr.b[5] & 0xc0) != 0xc0) 58678c2ecf20Sopenharmony_ci return false; 58688c2ecf20Sopenharmony_ci return true; 58698c2ecf20Sopenharmony_ci } 58708c2ecf20Sopenharmony_ci 58718c2ecf20Sopenharmony_ci return false; 58728c2ecf20Sopenharmony_ci} 58738c2ecf20Sopenharmony_ci 58748c2ecf20Sopenharmony_cistatic int load_irks(struct sock *sk, struct hci_dev *hdev, void *cp_data, 58758c2ecf20Sopenharmony_ci u16 len) 58768c2ecf20Sopenharmony_ci{ 58778c2ecf20Sopenharmony_ci struct mgmt_cp_load_irks *cp = cp_data; 58788c2ecf20Sopenharmony_ci const u16 max_irk_count = ((U16_MAX - sizeof(*cp)) / 58798c2ecf20Sopenharmony_ci sizeof(struct mgmt_irk_info)); 58808c2ecf20Sopenharmony_ci u16 irk_count, expected_len; 58818c2ecf20Sopenharmony_ci int i, err; 58828c2ecf20Sopenharmony_ci 58838c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 58848c2ecf20Sopenharmony_ci 58858c2ecf20Sopenharmony_ci if (!lmp_le_capable(hdev)) 58868c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LOAD_IRKS, 58878c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_SUPPORTED); 58888c2ecf20Sopenharmony_ci 58898c2ecf20Sopenharmony_ci irk_count = __le16_to_cpu(cp->irk_count); 58908c2ecf20Sopenharmony_ci if (irk_count > max_irk_count) { 58918c2ecf20Sopenharmony_ci bt_dev_err(hdev, "load_irks: too big irk_count value %u", 58928c2ecf20Sopenharmony_ci irk_count); 58938c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LOAD_IRKS, 58948c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 58958c2ecf20Sopenharmony_ci } 58968c2ecf20Sopenharmony_ci 58978c2ecf20Sopenharmony_ci expected_len = struct_size(cp, irks, irk_count); 58988c2ecf20Sopenharmony_ci if (expected_len != len) { 58998c2ecf20Sopenharmony_ci bt_dev_err(hdev, "load_irks: expected %u bytes, got %u bytes", 59008c2ecf20Sopenharmony_ci expected_len, len); 59018c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LOAD_IRKS, 59028c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 59038c2ecf20Sopenharmony_ci } 59048c2ecf20Sopenharmony_ci 59058c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "irk_count %u", irk_count); 59068c2ecf20Sopenharmony_ci 59078c2ecf20Sopenharmony_ci for (i = 0; i < irk_count; i++) { 59088c2ecf20Sopenharmony_ci struct mgmt_irk_info *key = &cp->irks[i]; 59098c2ecf20Sopenharmony_ci 59108c2ecf20Sopenharmony_ci if (!irk_is_valid(key)) 59118c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, 59128c2ecf20Sopenharmony_ci MGMT_OP_LOAD_IRKS, 59138c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 59148c2ecf20Sopenharmony_ci } 59158c2ecf20Sopenharmony_ci 59168c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 59178c2ecf20Sopenharmony_ci 59188c2ecf20Sopenharmony_ci hci_smp_irks_clear(hdev); 59198c2ecf20Sopenharmony_ci 59208c2ecf20Sopenharmony_ci for (i = 0; i < irk_count; i++) { 59218c2ecf20Sopenharmony_ci struct mgmt_irk_info *irk = &cp->irks[i]; 59228c2ecf20Sopenharmony_ci u8 addr_type = le_addr_type(irk->addr.type); 59238c2ecf20Sopenharmony_ci 59248c2ecf20Sopenharmony_ci if (hci_is_blocked_key(hdev, 59258c2ecf20Sopenharmony_ci HCI_BLOCKED_KEY_TYPE_IRK, 59268c2ecf20Sopenharmony_ci irk->val)) { 59278c2ecf20Sopenharmony_ci bt_dev_warn(hdev, "Skipping blocked IRK for %pMR", 59288c2ecf20Sopenharmony_ci &irk->addr.bdaddr); 59298c2ecf20Sopenharmony_ci continue; 59308c2ecf20Sopenharmony_ci } 59318c2ecf20Sopenharmony_ci 59328c2ecf20Sopenharmony_ci /* When using SMP over BR/EDR, the addr type should be set to BREDR */ 59338c2ecf20Sopenharmony_ci if (irk->addr.type == BDADDR_BREDR) 59348c2ecf20Sopenharmony_ci addr_type = BDADDR_BREDR; 59358c2ecf20Sopenharmony_ci 59368c2ecf20Sopenharmony_ci hci_add_irk(hdev, &irk->addr.bdaddr, 59378c2ecf20Sopenharmony_ci addr_type, irk->val, 59388c2ecf20Sopenharmony_ci BDADDR_ANY); 59398c2ecf20Sopenharmony_ci } 59408c2ecf20Sopenharmony_ci 59418c2ecf20Sopenharmony_ci hci_dev_set_flag(hdev, HCI_RPA_RESOLVING); 59428c2ecf20Sopenharmony_ci 59438c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_LOAD_IRKS, 0, NULL, 0); 59448c2ecf20Sopenharmony_ci 59458c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 59468c2ecf20Sopenharmony_ci 59478c2ecf20Sopenharmony_ci return err; 59488c2ecf20Sopenharmony_ci} 59498c2ecf20Sopenharmony_ci 59508c2ecf20Sopenharmony_cistatic bool ltk_is_valid(struct mgmt_ltk_info *key) 59518c2ecf20Sopenharmony_ci{ 59528c2ecf20Sopenharmony_ci if (key->initiator != 0x00 && key->initiator != 0x01) 59538c2ecf20Sopenharmony_ci return false; 59548c2ecf20Sopenharmony_ci 59558c2ecf20Sopenharmony_ci switch (key->addr.type) { 59568c2ecf20Sopenharmony_ci case BDADDR_LE_PUBLIC: 59578c2ecf20Sopenharmony_ci return true; 59588c2ecf20Sopenharmony_ci 59598c2ecf20Sopenharmony_ci case BDADDR_LE_RANDOM: 59608c2ecf20Sopenharmony_ci /* Two most significant bits shall be set */ 59618c2ecf20Sopenharmony_ci if ((key->addr.bdaddr.b[5] & 0xc0) != 0xc0) 59628c2ecf20Sopenharmony_ci return false; 59638c2ecf20Sopenharmony_ci return true; 59648c2ecf20Sopenharmony_ci } 59658c2ecf20Sopenharmony_ci 59668c2ecf20Sopenharmony_ci return false; 59678c2ecf20Sopenharmony_ci} 59688c2ecf20Sopenharmony_ci 59698c2ecf20Sopenharmony_cistatic int load_long_term_keys(struct sock *sk, struct hci_dev *hdev, 59708c2ecf20Sopenharmony_ci void *cp_data, u16 len) 59718c2ecf20Sopenharmony_ci{ 59728c2ecf20Sopenharmony_ci struct mgmt_cp_load_long_term_keys *cp = cp_data; 59738c2ecf20Sopenharmony_ci const u16 max_key_count = ((U16_MAX - sizeof(*cp)) / 59748c2ecf20Sopenharmony_ci sizeof(struct mgmt_ltk_info)); 59758c2ecf20Sopenharmony_ci u16 key_count, expected_len; 59768c2ecf20Sopenharmony_ci int i, err; 59778c2ecf20Sopenharmony_ci 59788c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 59798c2ecf20Sopenharmony_ci 59808c2ecf20Sopenharmony_ci if (!lmp_le_capable(hdev)) 59818c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS, 59828c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_SUPPORTED); 59838c2ecf20Sopenharmony_ci 59848c2ecf20Sopenharmony_ci key_count = __le16_to_cpu(cp->key_count); 59858c2ecf20Sopenharmony_ci if (key_count > max_key_count) { 59868c2ecf20Sopenharmony_ci bt_dev_err(hdev, "load_ltks: too big key_count value %u", 59878c2ecf20Sopenharmony_ci key_count); 59888c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS, 59898c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 59908c2ecf20Sopenharmony_ci } 59918c2ecf20Sopenharmony_ci 59928c2ecf20Sopenharmony_ci expected_len = struct_size(cp, keys, key_count); 59938c2ecf20Sopenharmony_ci if (expected_len != len) { 59948c2ecf20Sopenharmony_ci bt_dev_err(hdev, "load_keys: expected %u bytes, got %u bytes", 59958c2ecf20Sopenharmony_ci expected_len, len); 59968c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS, 59978c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 59988c2ecf20Sopenharmony_ci } 59998c2ecf20Sopenharmony_ci 60008c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "key_count %u", key_count); 60018c2ecf20Sopenharmony_ci 60028c2ecf20Sopenharmony_ci for (i = 0; i < key_count; i++) { 60038c2ecf20Sopenharmony_ci struct mgmt_ltk_info *key = &cp->keys[i]; 60048c2ecf20Sopenharmony_ci 60058c2ecf20Sopenharmony_ci if (!ltk_is_valid(key)) 60068c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, 60078c2ecf20Sopenharmony_ci MGMT_OP_LOAD_LONG_TERM_KEYS, 60088c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 60098c2ecf20Sopenharmony_ci } 60108c2ecf20Sopenharmony_ci 60118c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 60128c2ecf20Sopenharmony_ci 60138c2ecf20Sopenharmony_ci hci_smp_ltks_clear(hdev); 60148c2ecf20Sopenharmony_ci 60158c2ecf20Sopenharmony_ci for (i = 0; i < key_count; i++) { 60168c2ecf20Sopenharmony_ci struct mgmt_ltk_info *key = &cp->keys[i]; 60178c2ecf20Sopenharmony_ci u8 type, authenticated; 60188c2ecf20Sopenharmony_ci u8 addr_type = le_addr_type(key->addr.type); 60198c2ecf20Sopenharmony_ci 60208c2ecf20Sopenharmony_ci if (hci_is_blocked_key(hdev, 60218c2ecf20Sopenharmony_ci HCI_BLOCKED_KEY_TYPE_LTK, 60228c2ecf20Sopenharmony_ci key->val)) { 60238c2ecf20Sopenharmony_ci bt_dev_warn(hdev, "Skipping blocked LTK for %pMR", 60248c2ecf20Sopenharmony_ci &key->addr.bdaddr); 60258c2ecf20Sopenharmony_ci continue; 60268c2ecf20Sopenharmony_ci } 60278c2ecf20Sopenharmony_ci 60288c2ecf20Sopenharmony_ci switch (key->type) { 60298c2ecf20Sopenharmony_ci case MGMT_LTK_UNAUTHENTICATED: 60308c2ecf20Sopenharmony_ci authenticated = 0x00; 60318c2ecf20Sopenharmony_ci type = key->initiator ? SMP_LTK : SMP_LTK_RESPONDER; 60328c2ecf20Sopenharmony_ci break; 60338c2ecf20Sopenharmony_ci case MGMT_LTK_AUTHENTICATED: 60348c2ecf20Sopenharmony_ci authenticated = 0x01; 60358c2ecf20Sopenharmony_ci type = key->initiator ? SMP_LTK : SMP_LTK_RESPONDER; 60368c2ecf20Sopenharmony_ci break; 60378c2ecf20Sopenharmony_ci case MGMT_LTK_P256_UNAUTH: 60388c2ecf20Sopenharmony_ci authenticated = 0x00; 60398c2ecf20Sopenharmony_ci type = SMP_LTK_P256; 60408c2ecf20Sopenharmony_ci break; 60418c2ecf20Sopenharmony_ci case MGMT_LTK_P256_AUTH: 60428c2ecf20Sopenharmony_ci authenticated = 0x01; 60438c2ecf20Sopenharmony_ci type = SMP_LTK_P256; 60448c2ecf20Sopenharmony_ci break; 60458c2ecf20Sopenharmony_ci case MGMT_LTK_P256_DEBUG: 60468c2ecf20Sopenharmony_ci authenticated = 0x00; 60478c2ecf20Sopenharmony_ci type = SMP_LTK_P256_DEBUG; 60488c2ecf20Sopenharmony_ci fallthrough; 60498c2ecf20Sopenharmony_ci default: 60508c2ecf20Sopenharmony_ci continue; 60518c2ecf20Sopenharmony_ci } 60528c2ecf20Sopenharmony_ci 60538c2ecf20Sopenharmony_ci /* When using SMP over BR/EDR, the addr type should be set to BREDR */ 60548c2ecf20Sopenharmony_ci if (key->addr.type == BDADDR_BREDR) 60558c2ecf20Sopenharmony_ci addr_type = BDADDR_BREDR; 60568c2ecf20Sopenharmony_ci 60578c2ecf20Sopenharmony_ci hci_add_ltk(hdev, &key->addr.bdaddr, 60588c2ecf20Sopenharmony_ci addr_type, type, authenticated, 60598c2ecf20Sopenharmony_ci key->val, key->enc_size, key->ediv, key->rand); 60608c2ecf20Sopenharmony_ci } 60618c2ecf20Sopenharmony_ci 60628c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_LOAD_LONG_TERM_KEYS, 0, 60638c2ecf20Sopenharmony_ci NULL, 0); 60648c2ecf20Sopenharmony_ci 60658c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 60668c2ecf20Sopenharmony_ci 60678c2ecf20Sopenharmony_ci return err; 60688c2ecf20Sopenharmony_ci} 60698c2ecf20Sopenharmony_ci 60708c2ecf20Sopenharmony_cistatic int conn_info_cmd_complete(struct mgmt_pending_cmd *cmd, u8 status) 60718c2ecf20Sopenharmony_ci{ 60728c2ecf20Sopenharmony_ci struct hci_conn *conn = cmd->user_data; 60738c2ecf20Sopenharmony_ci struct mgmt_rp_get_conn_info rp; 60748c2ecf20Sopenharmony_ci int err; 60758c2ecf20Sopenharmony_ci 60768c2ecf20Sopenharmony_ci memcpy(&rp.addr, cmd->param, sizeof(rp.addr)); 60778c2ecf20Sopenharmony_ci 60788c2ecf20Sopenharmony_ci if (status == MGMT_STATUS_SUCCESS) { 60798c2ecf20Sopenharmony_ci rp.rssi = conn->rssi; 60808c2ecf20Sopenharmony_ci rp.tx_power = conn->tx_power; 60818c2ecf20Sopenharmony_ci rp.max_tx_power = conn->max_tx_power; 60828c2ecf20Sopenharmony_ci } else { 60838c2ecf20Sopenharmony_ci rp.rssi = HCI_RSSI_INVALID; 60848c2ecf20Sopenharmony_ci rp.tx_power = HCI_TX_POWER_INVALID; 60858c2ecf20Sopenharmony_ci rp.max_tx_power = HCI_TX_POWER_INVALID; 60868c2ecf20Sopenharmony_ci } 60878c2ecf20Sopenharmony_ci 60888c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(cmd->sk, cmd->index, MGMT_OP_GET_CONN_INFO, 60898c2ecf20Sopenharmony_ci status, &rp, sizeof(rp)); 60908c2ecf20Sopenharmony_ci 60918c2ecf20Sopenharmony_ci hci_conn_drop(conn); 60928c2ecf20Sopenharmony_ci hci_conn_put(conn); 60938c2ecf20Sopenharmony_ci 60948c2ecf20Sopenharmony_ci return err; 60958c2ecf20Sopenharmony_ci} 60968c2ecf20Sopenharmony_ci 60978c2ecf20Sopenharmony_cistatic void conn_info_refresh_complete(struct hci_dev *hdev, u8 hci_status, 60988c2ecf20Sopenharmony_ci u16 opcode) 60998c2ecf20Sopenharmony_ci{ 61008c2ecf20Sopenharmony_ci struct hci_cp_read_rssi *cp; 61018c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 61028c2ecf20Sopenharmony_ci struct hci_conn *conn; 61038c2ecf20Sopenharmony_ci u16 handle; 61048c2ecf20Sopenharmony_ci u8 status; 61058c2ecf20Sopenharmony_ci 61068c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "status 0x%02x", hci_status); 61078c2ecf20Sopenharmony_ci 61088c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 61098c2ecf20Sopenharmony_ci 61108c2ecf20Sopenharmony_ci /* Commands sent in request are either Read RSSI or Read Transmit Power 61118c2ecf20Sopenharmony_ci * Level so we check which one was last sent to retrieve connection 61128c2ecf20Sopenharmony_ci * handle. Both commands have handle as first parameter so it's safe to 61138c2ecf20Sopenharmony_ci * cast data on the same command struct. 61148c2ecf20Sopenharmony_ci * 61158c2ecf20Sopenharmony_ci * First command sent is always Read RSSI and we fail only if it fails. 61168c2ecf20Sopenharmony_ci * In other case we simply override error to indicate success as we 61178c2ecf20Sopenharmony_ci * already remembered if TX power value is actually valid. 61188c2ecf20Sopenharmony_ci */ 61198c2ecf20Sopenharmony_ci cp = hci_sent_cmd_data(hdev, HCI_OP_READ_RSSI); 61208c2ecf20Sopenharmony_ci if (!cp) { 61218c2ecf20Sopenharmony_ci cp = hci_sent_cmd_data(hdev, HCI_OP_READ_TX_POWER); 61228c2ecf20Sopenharmony_ci status = MGMT_STATUS_SUCCESS; 61238c2ecf20Sopenharmony_ci } else { 61248c2ecf20Sopenharmony_ci status = mgmt_status(hci_status); 61258c2ecf20Sopenharmony_ci } 61268c2ecf20Sopenharmony_ci 61278c2ecf20Sopenharmony_ci if (!cp) { 61288c2ecf20Sopenharmony_ci bt_dev_err(hdev, "invalid sent_cmd in conn_info response"); 61298c2ecf20Sopenharmony_ci goto unlock; 61308c2ecf20Sopenharmony_ci } 61318c2ecf20Sopenharmony_ci 61328c2ecf20Sopenharmony_ci handle = __le16_to_cpu(cp->handle); 61338c2ecf20Sopenharmony_ci conn = hci_conn_hash_lookup_handle(hdev, handle); 61348c2ecf20Sopenharmony_ci if (!conn) { 61358c2ecf20Sopenharmony_ci bt_dev_err(hdev, "unknown handle (%d) in conn_info response", 61368c2ecf20Sopenharmony_ci handle); 61378c2ecf20Sopenharmony_ci goto unlock; 61388c2ecf20Sopenharmony_ci } 61398c2ecf20Sopenharmony_ci 61408c2ecf20Sopenharmony_ci cmd = pending_find_data(MGMT_OP_GET_CONN_INFO, hdev, conn); 61418c2ecf20Sopenharmony_ci if (!cmd) 61428c2ecf20Sopenharmony_ci goto unlock; 61438c2ecf20Sopenharmony_ci 61448c2ecf20Sopenharmony_ci cmd->cmd_complete(cmd, status); 61458c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 61468c2ecf20Sopenharmony_ci 61478c2ecf20Sopenharmony_ciunlock: 61488c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 61498c2ecf20Sopenharmony_ci} 61508c2ecf20Sopenharmony_ci 61518c2ecf20Sopenharmony_cistatic int get_conn_info(struct sock *sk, struct hci_dev *hdev, void *data, 61528c2ecf20Sopenharmony_ci u16 len) 61538c2ecf20Sopenharmony_ci{ 61548c2ecf20Sopenharmony_ci struct mgmt_cp_get_conn_info *cp = data; 61558c2ecf20Sopenharmony_ci struct mgmt_rp_get_conn_info rp; 61568c2ecf20Sopenharmony_ci struct hci_conn *conn; 61578c2ecf20Sopenharmony_ci unsigned long conn_info_age; 61588c2ecf20Sopenharmony_ci int err = 0; 61598c2ecf20Sopenharmony_ci 61608c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 61618c2ecf20Sopenharmony_ci 61628c2ecf20Sopenharmony_ci memset(&rp, 0, sizeof(rp)); 61638c2ecf20Sopenharmony_ci bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); 61648c2ecf20Sopenharmony_ci rp.addr.type = cp->addr.type; 61658c2ecf20Sopenharmony_ci 61668c2ecf20Sopenharmony_ci if (!bdaddr_type_is_valid(cp->addr.type)) 61678c2ecf20Sopenharmony_ci return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO, 61688c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS, 61698c2ecf20Sopenharmony_ci &rp, sizeof(rp)); 61708c2ecf20Sopenharmony_ci 61718c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 61728c2ecf20Sopenharmony_ci 61738c2ecf20Sopenharmony_ci if (!hdev_is_powered(hdev)) { 61748c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO, 61758c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_POWERED, &rp, 61768c2ecf20Sopenharmony_ci sizeof(rp)); 61778c2ecf20Sopenharmony_ci goto unlock; 61788c2ecf20Sopenharmony_ci } 61798c2ecf20Sopenharmony_ci 61808c2ecf20Sopenharmony_ci if (cp->addr.type == BDADDR_BREDR) 61818c2ecf20Sopenharmony_ci conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, 61828c2ecf20Sopenharmony_ci &cp->addr.bdaddr); 61838c2ecf20Sopenharmony_ci else 61848c2ecf20Sopenharmony_ci conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, &cp->addr.bdaddr); 61858c2ecf20Sopenharmony_ci 61868c2ecf20Sopenharmony_ci if (!conn || conn->state != BT_CONNECTED) { 61878c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO, 61888c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_CONNECTED, &rp, 61898c2ecf20Sopenharmony_ci sizeof(rp)); 61908c2ecf20Sopenharmony_ci goto unlock; 61918c2ecf20Sopenharmony_ci } 61928c2ecf20Sopenharmony_ci 61938c2ecf20Sopenharmony_ci if (pending_find_data(MGMT_OP_GET_CONN_INFO, hdev, conn)) { 61948c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO, 61958c2ecf20Sopenharmony_ci MGMT_STATUS_BUSY, &rp, sizeof(rp)); 61968c2ecf20Sopenharmony_ci goto unlock; 61978c2ecf20Sopenharmony_ci } 61988c2ecf20Sopenharmony_ci 61998c2ecf20Sopenharmony_ci /* To avoid client trying to guess when to poll again for information we 62008c2ecf20Sopenharmony_ci * calculate conn info age as random value between min/max set in hdev. 62018c2ecf20Sopenharmony_ci */ 62028c2ecf20Sopenharmony_ci conn_info_age = hdev->conn_info_min_age + 62038c2ecf20Sopenharmony_ci prandom_u32_max(hdev->conn_info_max_age - 62048c2ecf20Sopenharmony_ci hdev->conn_info_min_age); 62058c2ecf20Sopenharmony_ci 62068c2ecf20Sopenharmony_ci /* Query controller to refresh cached values if they are too old or were 62078c2ecf20Sopenharmony_ci * never read. 62088c2ecf20Sopenharmony_ci */ 62098c2ecf20Sopenharmony_ci if (time_after(jiffies, conn->conn_info_timestamp + 62108c2ecf20Sopenharmony_ci msecs_to_jiffies(conn_info_age)) || 62118c2ecf20Sopenharmony_ci !conn->conn_info_timestamp) { 62128c2ecf20Sopenharmony_ci struct hci_request req; 62138c2ecf20Sopenharmony_ci struct hci_cp_read_tx_power req_txp_cp; 62148c2ecf20Sopenharmony_ci struct hci_cp_read_rssi req_rssi_cp; 62158c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 62168c2ecf20Sopenharmony_ci 62178c2ecf20Sopenharmony_ci hci_req_init(&req, hdev); 62188c2ecf20Sopenharmony_ci req_rssi_cp.handle = cpu_to_le16(conn->handle); 62198c2ecf20Sopenharmony_ci hci_req_add(&req, HCI_OP_READ_RSSI, sizeof(req_rssi_cp), 62208c2ecf20Sopenharmony_ci &req_rssi_cp); 62218c2ecf20Sopenharmony_ci 62228c2ecf20Sopenharmony_ci /* For LE links TX power does not change thus we don't need to 62238c2ecf20Sopenharmony_ci * query for it once value is known. 62248c2ecf20Sopenharmony_ci */ 62258c2ecf20Sopenharmony_ci if (!bdaddr_type_is_le(cp->addr.type) || 62268c2ecf20Sopenharmony_ci conn->tx_power == HCI_TX_POWER_INVALID) { 62278c2ecf20Sopenharmony_ci req_txp_cp.handle = cpu_to_le16(conn->handle); 62288c2ecf20Sopenharmony_ci req_txp_cp.type = 0x00; 62298c2ecf20Sopenharmony_ci hci_req_add(&req, HCI_OP_READ_TX_POWER, 62308c2ecf20Sopenharmony_ci sizeof(req_txp_cp), &req_txp_cp); 62318c2ecf20Sopenharmony_ci } 62328c2ecf20Sopenharmony_ci 62338c2ecf20Sopenharmony_ci /* Max TX power needs to be read only once per connection */ 62348c2ecf20Sopenharmony_ci if (conn->max_tx_power == HCI_TX_POWER_INVALID) { 62358c2ecf20Sopenharmony_ci req_txp_cp.handle = cpu_to_le16(conn->handle); 62368c2ecf20Sopenharmony_ci req_txp_cp.type = 0x01; 62378c2ecf20Sopenharmony_ci hci_req_add(&req, HCI_OP_READ_TX_POWER, 62388c2ecf20Sopenharmony_ci sizeof(req_txp_cp), &req_txp_cp); 62398c2ecf20Sopenharmony_ci } 62408c2ecf20Sopenharmony_ci 62418c2ecf20Sopenharmony_ci err = hci_req_run(&req, conn_info_refresh_complete); 62428c2ecf20Sopenharmony_ci if (err < 0) 62438c2ecf20Sopenharmony_ci goto unlock; 62448c2ecf20Sopenharmony_ci 62458c2ecf20Sopenharmony_ci cmd = mgmt_pending_add(sk, MGMT_OP_GET_CONN_INFO, hdev, 62468c2ecf20Sopenharmony_ci data, len); 62478c2ecf20Sopenharmony_ci if (!cmd) { 62488c2ecf20Sopenharmony_ci err = -ENOMEM; 62498c2ecf20Sopenharmony_ci goto unlock; 62508c2ecf20Sopenharmony_ci } 62518c2ecf20Sopenharmony_ci 62528c2ecf20Sopenharmony_ci hci_conn_hold(conn); 62538c2ecf20Sopenharmony_ci cmd->user_data = hci_conn_get(conn); 62548c2ecf20Sopenharmony_ci cmd->cmd_complete = conn_info_cmd_complete; 62558c2ecf20Sopenharmony_ci 62568c2ecf20Sopenharmony_ci conn->conn_info_timestamp = jiffies; 62578c2ecf20Sopenharmony_ci } else { 62588c2ecf20Sopenharmony_ci /* Cache is valid, just reply with values cached in hci_conn */ 62598c2ecf20Sopenharmony_ci rp.rssi = conn->rssi; 62608c2ecf20Sopenharmony_ci rp.tx_power = conn->tx_power; 62618c2ecf20Sopenharmony_ci rp.max_tx_power = conn->max_tx_power; 62628c2ecf20Sopenharmony_ci 62638c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_GET_CONN_INFO, 62648c2ecf20Sopenharmony_ci MGMT_STATUS_SUCCESS, &rp, sizeof(rp)); 62658c2ecf20Sopenharmony_ci } 62668c2ecf20Sopenharmony_ci 62678c2ecf20Sopenharmony_ciunlock: 62688c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 62698c2ecf20Sopenharmony_ci return err; 62708c2ecf20Sopenharmony_ci} 62718c2ecf20Sopenharmony_ci 62728c2ecf20Sopenharmony_cistatic int clock_info_cmd_complete(struct mgmt_pending_cmd *cmd, u8 status) 62738c2ecf20Sopenharmony_ci{ 62748c2ecf20Sopenharmony_ci struct hci_conn *conn = cmd->user_data; 62758c2ecf20Sopenharmony_ci struct mgmt_rp_get_clock_info rp; 62768c2ecf20Sopenharmony_ci struct hci_dev *hdev; 62778c2ecf20Sopenharmony_ci int err; 62788c2ecf20Sopenharmony_ci 62798c2ecf20Sopenharmony_ci memset(&rp, 0, sizeof(rp)); 62808c2ecf20Sopenharmony_ci memcpy(&rp.addr, cmd->param, sizeof(rp.addr)); 62818c2ecf20Sopenharmony_ci 62828c2ecf20Sopenharmony_ci if (status) 62838c2ecf20Sopenharmony_ci goto complete; 62848c2ecf20Sopenharmony_ci 62858c2ecf20Sopenharmony_ci hdev = hci_dev_get(cmd->index); 62868c2ecf20Sopenharmony_ci if (hdev) { 62878c2ecf20Sopenharmony_ci rp.local_clock = cpu_to_le32(hdev->clock); 62888c2ecf20Sopenharmony_ci hci_dev_put(hdev); 62898c2ecf20Sopenharmony_ci } 62908c2ecf20Sopenharmony_ci 62918c2ecf20Sopenharmony_ci if (conn) { 62928c2ecf20Sopenharmony_ci rp.piconet_clock = cpu_to_le32(conn->clock); 62938c2ecf20Sopenharmony_ci rp.accuracy = cpu_to_le16(conn->clock_accuracy); 62948c2ecf20Sopenharmony_ci } 62958c2ecf20Sopenharmony_ci 62968c2ecf20Sopenharmony_cicomplete: 62978c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(cmd->sk, cmd->index, cmd->opcode, status, &rp, 62988c2ecf20Sopenharmony_ci sizeof(rp)); 62998c2ecf20Sopenharmony_ci 63008c2ecf20Sopenharmony_ci if (conn) { 63018c2ecf20Sopenharmony_ci hci_conn_drop(conn); 63028c2ecf20Sopenharmony_ci hci_conn_put(conn); 63038c2ecf20Sopenharmony_ci } 63048c2ecf20Sopenharmony_ci 63058c2ecf20Sopenharmony_ci return err; 63068c2ecf20Sopenharmony_ci} 63078c2ecf20Sopenharmony_ci 63088c2ecf20Sopenharmony_cistatic void get_clock_info_complete(struct hci_dev *hdev, u8 status, u16 opcode) 63098c2ecf20Sopenharmony_ci{ 63108c2ecf20Sopenharmony_ci struct hci_cp_read_clock *hci_cp; 63118c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 63128c2ecf20Sopenharmony_ci struct hci_conn *conn; 63138c2ecf20Sopenharmony_ci 63148c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "status %u", status); 63158c2ecf20Sopenharmony_ci 63168c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 63178c2ecf20Sopenharmony_ci 63188c2ecf20Sopenharmony_ci hci_cp = hci_sent_cmd_data(hdev, HCI_OP_READ_CLOCK); 63198c2ecf20Sopenharmony_ci if (!hci_cp) 63208c2ecf20Sopenharmony_ci goto unlock; 63218c2ecf20Sopenharmony_ci 63228c2ecf20Sopenharmony_ci if (hci_cp->which) { 63238c2ecf20Sopenharmony_ci u16 handle = __le16_to_cpu(hci_cp->handle); 63248c2ecf20Sopenharmony_ci conn = hci_conn_hash_lookup_handle(hdev, handle); 63258c2ecf20Sopenharmony_ci } else { 63268c2ecf20Sopenharmony_ci conn = NULL; 63278c2ecf20Sopenharmony_ci } 63288c2ecf20Sopenharmony_ci 63298c2ecf20Sopenharmony_ci cmd = pending_find_data(MGMT_OP_GET_CLOCK_INFO, hdev, conn); 63308c2ecf20Sopenharmony_ci if (!cmd) 63318c2ecf20Sopenharmony_ci goto unlock; 63328c2ecf20Sopenharmony_ci 63338c2ecf20Sopenharmony_ci cmd->cmd_complete(cmd, mgmt_status(status)); 63348c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 63358c2ecf20Sopenharmony_ci 63368c2ecf20Sopenharmony_ciunlock: 63378c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 63388c2ecf20Sopenharmony_ci} 63398c2ecf20Sopenharmony_ci 63408c2ecf20Sopenharmony_cistatic int get_clock_info(struct sock *sk, struct hci_dev *hdev, void *data, 63418c2ecf20Sopenharmony_ci u16 len) 63428c2ecf20Sopenharmony_ci{ 63438c2ecf20Sopenharmony_ci struct mgmt_cp_get_clock_info *cp = data; 63448c2ecf20Sopenharmony_ci struct mgmt_rp_get_clock_info rp; 63458c2ecf20Sopenharmony_ci struct hci_cp_read_clock hci_cp; 63468c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 63478c2ecf20Sopenharmony_ci struct hci_request req; 63488c2ecf20Sopenharmony_ci struct hci_conn *conn; 63498c2ecf20Sopenharmony_ci int err; 63508c2ecf20Sopenharmony_ci 63518c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 63528c2ecf20Sopenharmony_ci 63538c2ecf20Sopenharmony_ci memset(&rp, 0, sizeof(rp)); 63548c2ecf20Sopenharmony_ci bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr); 63558c2ecf20Sopenharmony_ci rp.addr.type = cp->addr.type; 63568c2ecf20Sopenharmony_ci 63578c2ecf20Sopenharmony_ci if (cp->addr.type != BDADDR_BREDR) 63588c2ecf20Sopenharmony_ci return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_GET_CLOCK_INFO, 63598c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS, 63608c2ecf20Sopenharmony_ci &rp, sizeof(rp)); 63618c2ecf20Sopenharmony_ci 63628c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 63638c2ecf20Sopenharmony_ci 63648c2ecf20Sopenharmony_ci if (!hdev_is_powered(hdev)) { 63658c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_GET_CLOCK_INFO, 63668c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_POWERED, &rp, 63678c2ecf20Sopenharmony_ci sizeof(rp)); 63688c2ecf20Sopenharmony_ci goto unlock; 63698c2ecf20Sopenharmony_ci } 63708c2ecf20Sopenharmony_ci 63718c2ecf20Sopenharmony_ci if (bacmp(&cp->addr.bdaddr, BDADDR_ANY)) { 63728c2ecf20Sopenharmony_ci conn = hci_conn_hash_lookup_ba(hdev, ACL_LINK, 63738c2ecf20Sopenharmony_ci &cp->addr.bdaddr); 63748c2ecf20Sopenharmony_ci if (!conn || conn->state != BT_CONNECTED) { 63758c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, 63768c2ecf20Sopenharmony_ci MGMT_OP_GET_CLOCK_INFO, 63778c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_CONNECTED, 63788c2ecf20Sopenharmony_ci &rp, sizeof(rp)); 63798c2ecf20Sopenharmony_ci goto unlock; 63808c2ecf20Sopenharmony_ci } 63818c2ecf20Sopenharmony_ci } else { 63828c2ecf20Sopenharmony_ci conn = NULL; 63838c2ecf20Sopenharmony_ci } 63848c2ecf20Sopenharmony_ci 63858c2ecf20Sopenharmony_ci cmd = mgmt_pending_add(sk, MGMT_OP_GET_CLOCK_INFO, hdev, data, len); 63868c2ecf20Sopenharmony_ci if (!cmd) { 63878c2ecf20Sopenharmony_ci err = -ENOMEM; 63888c2ecf20Sopenharmony_ci goto unlock; 63898c2ecf20Sopenharmony_ci } 63908c2ecf20Sopenharmony_ci 63918c2ecf20Sopenharmony_ci cmd->cmd_complete = clock_info_cmd_complete; 63928c2ecf20Sopenharmony_ci 63938c2ecf20Sopenharmony_ci hci_req_init(&req, hdev); 63948c2ecf20Sopenharmony_ci 63958c2ecf20Sopenharmony_ci memset(&hci_cp, 0, sizeof(hci_cp)); 63968c2ecf20Sopenharmony_ci hci_req_add(&req, HCI_OP_READ_CLOCK, sizeof(hci_cp), &hci_cp); 63978c2ecf20Sopenharmony_ci 63988c2ecf20Sopenharmony_ci if (conn) { 63998c2ecf20Sopenharmony_ci hci_conn_hold(conn); 64008c2ecf20Sopenharmony_ci cmd->user_data = hci_conn_get(conn); 64018c2ecf20Sopenharmony_ci 64028c2ecf20Sopenharmony_ci hci_cp.handle = cpu_to_le16(conn->handle); 64038c2ecf20Sopenharmony_ci hci_cp.which = 0x01; /* Piconet clock */ 64048c2ecf20Sopenharmony_ci hci_req_add(&req, HCI_OP_READ_CLOCK, sizeof(hci_cp), &hci_cp); 64058c2ecf20Sopenharmony_ci } 64068c2ecf20Sopenharmony_ci 64078c2ecf20Sopenharmony_ci err = hci_req_run(&req, get_clock_info_complete); 64088c2ecf20Sopenharmony_ci if (err < 0) 64098c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 64108c2ecf20Sopenharmony_ci 64118c2ecf20Sopenharmony_ciunlock: 64128c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 64138c2ecf20Sopenharmony_ci return err; 64148c2ecf20Sopenharmony_ci} 64158c2ecf20Sopenharmony_ci 64168c2ecf20Sopenharmony_cistatic bool is_connected(struct hci_dev *hdev, bdaddr_t *addr, u8 type) 64178c2ecf20Sopenharmony_ci{ 64188c2ecf20Sopenharmony_ci struct hci_conn *conn; 64198c2ecf20Sopenharmony_ci 64208c2ecf20Sopenharmony_ci conn = hci_conn_hash_lookup_ba(hdev, LE_LINK, addr); 64218c2ecf20Sopenharmony_ci if (!conn) 64228c2ecf20Sopenharmony_ci return false; 64238c2ecf20Sopenharmony_ci 64248c2ecf20Sopenharmony_ci if (conn->dst_type != type) 64258c2ecf20Sopenharmony_ci return false; 64268c2ecf20Sopenharmony_ci 64278c2ecf20Sopenharmony_ci if (conn->state != BT_CONNECTED) 64288c2ecf20Sopenharmony_ci return false; 64298c2ecf20Sopenharmony_ci 64308c2ecf20Sopenharmony_ci return true; 64318c2ecf20Sopenharmony_ci} 64328c2ecf20Sopenharmony_ci 64338c2ecf20Sopenharmony_ci/* This function requires the caller holds hdev->lock */ 64348c2ecf20Sopenharmony_cistatic int hci_conn_params_set(struct hci_dev *hdev, bdaddr_t *addr, 64358c2ecf20Sopenharmony_ci u8 addr_type, u8 auto_connect) 64368c2ecf20Sopenharmony_ci{ 64378c2ecf20Sopenharmony_ci struct hci_conn_params *params; 64388c2ecf20Sopenharmony_ci 64398c2ecf20Sopenharmony_ci params = hci_conn_params_add(hdev, addr, addr_type); 64408c2ecf20Sopenharmony_ci if (!params) 64418c2ecf20Sopenharmony_ci return -EIO; 64428c2ecf20Sopenharmony_ci 64438c2ecf20Sopenharmony_ci if (params->auto_connect == auto_connect) 64448c2ecf20Sopenharmony_ci return 0; 64458c2ecf20Sopenharmony_ci 64468c2ecf20Sopenharmony_ci list_del_init(¶ms->action); 64478c2ecf20Sopenharmony_ci 64488c2ecf20Sopenharmony_ci switch (auto_connect) { 64498c2ecf20Sopenharmony_ci case HCI_AUTO_CONN_DISABLED: 64508c2ecf20Sopenharmony_ci case HCI_AUTO_CONN_LINK_LOSS: 64518c2ecf20Sopenharmony_ci /* If auto connect is being disabled when we're trying to 64528c2ecf20Sopenharmony_ci * connect to device, keep connecting. 64538c2ecf20Sopenharmony_ci */ 64548c2ecf20Sopenharmony_ci if (params->explicit_connect) 64558c2ecf20Sopenharmony_ci list_add(¶ms->action, &hdev->pend_le_conns); 64568c2ecf20Sopenharmony_ci break; 64578c2ecf20Sopenharmony_ci case HCI_AUTO_CONN_REPORT: 64588c2ecf20Sopenharmony_ci if (params->explicit_connect) 64598c2ecf20Sopenharmony_ci list_add(¶ms->action, &hdev->pend_le_conns); 64608c2ecf20Sopenharmony_ci else 64618c2ecf20Sopenharmony_ci list_add(¶ms->action, &hdev->pend_le_reports); 64628c2ecf20Sopenharmony_ci break; 64638c2ecf20Sopenharmony_ci case HCI_AUTO_CONN_DIRECT: 64648c2ecf20Sopenharmony_ci case HCI_AUTO_CONN_ALWAYS: 64658c2ecf20Sopenharmony_ci if (!is_connected(hdev, addr, addr_type)) 64668c2ecf20Sopenharmony_ci list_add(¶ms->action, &hdev->pend_le_conns); 64678c2ecf20Sopenharmony_ci break; 64688c2ecf20Sopenharmony_ci } 64698c2ecf20Sopenharmony_ci 64708c2ecf20Sopenharmony_ci params->auto_connect = auto_connect; 64718c2ecf20Sopenharmony_ci 64728c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "addr %pMR (type %u) auto_connect %u", 64738c2ecf20Sopenharmony_ci addr, addr_type, auto_connect); 64748c2ecf20Sopenharmony_ci 64758c2ecf20Sopenharmony_ci return 0; 64768c2ecf20Sopenharmony_ci} 64778c2ecf20Sopenharmony_ci 64788c2ecf20Sopenharmony_cistatic void device_added(struct sock *sk, struct hci_dev *hdev, 64798c2ecf20Sopenharmony_ci bdaddr_t *bdaddr, u8 type, u8 action) 64808c2ecf20Sopenharmony_ci{ 64818c2ecf20Sopenharmony_ci struct mgmt_ev_device_added ev; 64828c2ecf20Sopenharmony_ci 64838c2ecf20Sopenharmony_ci bacpy(&ev.addr.bdaddr, bdaddr); 64848c2ecf20Sopenharmony_ci ev.addr.type = type; 64858c2ecf20Sopenharmony_ci ev.action = action; 64868c2ecf20Sopenharmony_ci 64878c2ecf20Sopenharmony_ci mgmt_event(MGMT_EV_DEVICE_ADDED, hdev, &ev, sizeof(ev), sk); 64888c2ecf20Sopenharmony_ci} 64898c2ecf20Sopenharmony_ci 64908c2ecf20Sopenharmony_cistatic int add_device(struct sock *sk, struct hci_dev *hdev, 64918c2ecf20Sopenharmony_ci void *data, u16 len) 64928c2ecf20Sopenharmony_ci{ 64938c2ecf20Sopenharmony_ci struct mgmt_cp_add_device *cp = data; 64948c2ecf20Sopenharmony_ci u8 auto_conn, addr_type; 64958c2ecf20Sopenharmony_ci struct hci_conn_params *params; 64968c2ecf20Sopenharmony_ci int err; 64978c2ecf20Sopenharmony_ci u32 current_flags = 0; 64988c2ecf20Sopenharmony_ci 64998c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 65008c2ecf20Sopenharmony_ci 65018c2ecf20Sopenharmony_ci if (!bdaddr_type_is_valid(cp->addr.type) || 65028c2ecf20Sopenharmony_ci !bacmp(&cp->addr.bdaddr, BDADDR_ANY)) 65038c2ecf20Sopenharmony_ci return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE, 65048c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS, 65058c2ecf20Sopenharmony_ci &cp->addr, sizeof(cp->addr)); 65068c2ecf20Sopenharmony_ci 65078c2ecf20Sopenharmony_ci if (cp->action != 0x00 && cp->action != 0x01 && cp->action != 0x02) 65088c2ecf20Sopenharmony_ci return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE, 65098c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS, 65108c2ecf20Sopenharmony_ci &cp->addr, sizeof(cp->addr)); 65118c2ecf20Sopenharmony_ci 65128c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 65138c2ecf20Sopenharmony_ci 65148c2ecf20Sopenharmony_ci if (cp->addr.type == BDADDR_BREDR) { 65158c2ecf20Sopenharmony_ci /* Only incoming connections action is supported for now */ 65168c2ecf20Sopenharmony_ci if (cp->action != 0x01) { 65178c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, 65188c2ecf20Sopenharmony_ci MGMT_OP_ADD_DEVICE, 65198c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS, 65208c2ecf20Sopenharmony_ci &cp->addr, sizeof(cp->addr)); 65218c2ecf20Sopenharmony_ci goto unlock; 65228c2ecf20Sopenharmony_ci } 65238c2ecf20Sopenharmony_ci 65248c2ecf20Sopenharmony_ci err = hci_bdaddr_list_add_with_flags(&hdev->accept_list, 65258c2ecf20Sopenharmony_ci &cp->addr.bdaddr, 65268c2ecf20Sopenharmony_ci cp->addr.type, 0); 65278c2ecf20Sopenharmony_ci if (err) 65288c2ecf20Sopenharmony_ci goto unlock; 65298c2ecf20Sopenharmony_ci 65308c2ecf20Sopenharmony_ci hci_req_update_scan(hdev); 65318c2ecf20Sopenharmony_ci 65328c2ecf20Sopenharmony_ci goto added; 65338c2ecf20Sopenharmony_ci } 65348c2ecf20Sopenharmony_ci 65358c2ecf20Sopenharmony_ci addr_type = le_addr_type(cp->addr.type); 65368c2ecf20Sopenharmony_ci 65378c2ecf20Sopenharmony_ci if (cp->action == 0x02) 65388c2ecf20Sopenharmony_ci auto_conn = HCI_AUTO_CONN_ALWAYS; 65398c2ecf20Sopenharmony_ci else if (cp->action == 0x01) 65408c2ecf20Sopenharmony_ci auto_conn = HCI_AUTO_CONN_DIRECT; 65418c2ecf20Sopenharmony_ci else 65428c2ecf20Sopenharmony_ci auto_conn = HCI_AUTO_CONN_REPORT; 65438c2ecf20Sopenharmony_ci 65448c2ecf20Sopenharmony_ci /* Kernel internally uses conn_params with resolvable private 65458c2ecf20Sopenharmony_ci * address, but Add Device allows only identity addresses. 65468c2ecf20Sopenharmony_ci * Make sure it is enforced before calling 65478c2ecf20Sopenharmony_ci * hci_conn_params_lookup. 65488c2ecf20Sopenharmony_ci */ 65498c2ecf20Sopenharmony_ci if (!hci_is_identity_address(&cp->addr.bdaddr, addr_type)) { 65508c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE, 65518c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS, 65528c2ecf20Sopenharmony_ci &cp->addr, sizeof(cp->addr)); 65538c2ecf20Sopenharmony_ci goto unlock; 65548c2ecf20Sopenharmony_ci } 65558c2ecf20Sopenharmony_ci 65568c2ecf20Sopenharmony_ci /* If the connection parameters don't exist for this device, 65578c2ecf20Sopenharmony_ci * they will be created and configured with defaults. 65588c2ecf20Sopenharmony_ci */ 65598c2ecf20Sopenharmony_ci if (hci_conn_params_set(hdev, &cp->addr.bdaddr, addr_type, 65608c2ecf20Sopenharmony_ci auto_conn) < 0) { 65618c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE, 65628c2ecf20Sopenharmony_ci MGMT_STATUS_FAILED, &cp->addr, 65638c2ecf20Sopenharmony_ci sizeof(cp->addr)); 65648c2ecf20Sopenharmony_ci goto unlock; 65658c2ecf20Sopenharmony_ci } else { 65668c2ecf20Sopenharmony_ci params = hci_conn_params_lookup(hdev, &cp->addr.bdaddr, 65678c2ecf20Sopenharmony_ci addr_type); 65688c2ecf20Sopenharmony_ci if (params) 65698c2ecf20Sopenharmony_ci current_flags = params->current_flags; 65708c2ecf20Sopenharmony_ci } 65718c2ecf20Sopenharmony_ci 65728c2ecf20Sopenharmony_ci hci_update_background_scan(hdev); 65738c2ecf20Sopenharmony_ci 65748c2ecf20Sopenharmony_ciadded: 65758c2ecf20Sopenharmony_ci device_added(sk, hdev, &cp->addr.bdaddr, cp->addr.type, cp->action); 65768c2ecf20Sopenharmony_ci device_flags_changed(NULL, hdev, &cp->addr.bdaddr, cp->addr.type, 65778c2ecf20Sopenharmony_ci SUPPORTED_DEVICE_FLAGS(), current_flags); 65788c2ecf20Sopenharmony_ci 65798c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE, 65808c2ecf20Sopenharmony_ci MGMT_STATUS_SUCCESS, &cp->addr, 65818c2ecf20Sopenharmony_ci sizeof(cp->addr)); 65828c2ecf20Sopenharmony_ci 65838c2ecf20Sopenharmony_ciunlock: 65848c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 65858c2ecf20Sopenharmony_ci return err; 65868c2ecf20Sopenharmony_ci} 65878c2ecf20Sopenharmony_ci 65888c2ecf20Sopenharmony_cistatic void device_removed(struct sock *sk, struct hci_dev *hdev, 65898c2ecf20Sopenharmony_ci bdaddr_t *bdaddr, u8 type) 65908c2ecf20Sopenharmony_ci{ 65918c2ecf20Sopenharmony_ci struct mgmt_ev_device_removed ev; 65928c2ecf20Sopenharmony_ci 65938c2ecf20Sopenharmony_ci bacpy(&ev.addr.bdaddr, bdaddr); 65948c2ecf20Sopenharmony_ci ev.addr.type = type; 65958c2ecf20Sopenharmony_ci 65968c2ecf20Sopenharmony_ci mgmt_event(MGMT_EV_DEVICE_REMOVED, hdev, &ev, sizeof(ev), sk); 65978c2ecf20Sopenharmony_ci} 65988c2ecf20Sopenharmony_ci 65998c2ecf20Sopenharmony_cistatic int remove_device(struct sock *sk, struct hci_dev *hdev, 66008c2ecf20Sopenharmony_ci void *data, u16 len) 66018c2ecf20Sopenharmony_ci{ 66028c2ecf20Sopenharmony_ci struct mgmt_cp_remove_device *cp = data; 66038c2ecf20Sopenharmony_ci int err; 66048c2ecf20Sopenharmony_ci 66058c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 66068c2ecf20Sopenharmony_ci 66078c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 66088c2ecf20Sopenharmony_ci 66098c2ecf20Sopenharmony_ci if (bacmp(&cp->addr.bdaddr, BDADDR_ANY)) { 66108c2ecf20Sopenharmony_ci struct hci_conn_params *params; 66118c2ecf20Sopenharmony_ci u8 addr_type; 66128c2ecf20Sopenharmony_ci 66138c2ecf20Sopenharmony_ci if (!bdaddr_type_is_valid(cp->addr.type)) { 66148c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, 66158c2ecf20Sopenharmony_ci MGMT_OP_REMOVE_DEVICE, 66168c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS, 66178c2ecf20Sopenharmony_ci &cp->addr, sizeof(cp->addr)); 66188c2ecf20Sopenharmony_ci goto unlock; 66198c2ecf20Sopenharmony_ci } 66208c2ecf20Sopenharmony_ci 66218c2ecf20Sopenharmony_ci if (cp->addr.type == BDADDR_BREDR) { 66228c2ecf20Sopenharmony_ci err = hci_bdaddr_list_del(&hdev->accept_list, 66238c2ecf20Sopenharmony_ci &cp->addr.bdaddr, 66248c2ecf20Sopenharmony_ci cp->addr.type); 66258c2ecf20Sopenharmony_ci if (err) { 66268c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, 66278c2ecf20Sopenharmony_ci MGMT_OP_REMOVE_DEVICE, 66288c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS, 66298c2ecf20Sopenharmony_ci &cp->addr, 66308c2ecf20Sopenharmony_ci sizeof(cp->addr)); 66318c2ecf20Sopenharmony_ci goto unlock; 66328c2ecf20Sopenharmony_ci } 66338c2ecf20Sopenharmony_ci 66348c2ecf20Sopenharmony_ci hci_req_update_scan(hdev); 66358c2ecf20Sopenharmony_ci 66368c2ecf20Sopenharmony_ci device_removed(sk, hdev, &cp->addr.bdaddr, 66378c2ecf20Sopenharmony_ci cp->addr.type); 66388c2ecf20Sopenharmony_ci goto complete; 66398c2ecf20Sopenharmony_ci } 66408c2ecf20Sopenharmony_ci 66418c2ecf20Sopenharmony_ci addr_type = le_addr_type(cp->addr.type); 66428c2ecf20Sopenharmony_ci 66438c2ecf20Sopenharmony_ci /* Kernel internally uses conn_params with resolvable private 66448c2ecf20Sopenharmony_ci * address, but Remove Device allows only identity addresses. 66458c2ecf20Sopenharmony_ci * Make sure it is enforced before calling 66468c2ecf20Sopenharmony_ci * hci_conn_params_lookup. 66478c2ecf20Sopenharmony_ci */ 66488c2ecf20Sopenharmony_ci if (!hci_is_identity_address(&cp->addr.bdaddr, addr_type)) { 66498c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, 66508c2ecf20Sopenharmony_ci MGMT_OP_REMOVE_DEVICE, 66518c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS, 66528c2ecf20Sopenharmony_ci &cp->addr, sizeof(cp->addr)); 66538c2ecf20Sopenharmony_ci goto unlock; 66548c2ecf20Sopenharmony_ci } 66558c2ecf20Sopenharmony_ci 66568c2ecf20Sopenharmony_ci params = hci_conn_params_lookup(hdev, &cp->addr.bdaddr, 66578c2ecf20Sopenharmony_ci addr_type); 66588c2ecf20Sopenharmony_ci if (!params) { 66598c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, 66608c2ecf20Sopenharmony_ci MGMT_OP_REMOVE_DEVICE, 66618c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS, 66628c2ecf20Sopenharmony_ci &cp->addr, sizeof(cp->addr)); 66638c2ecf20Sopenharmony_ci goto unlock; 66648c2ecf20Sopenharmony_ci } 66658c2ecf20Sopenharmony_ci 66668c2ecf20Sopenharmony_ci if (params->auto_connect == HCI_AUTO_CONN_DISABLED || 66678c2ecf20Sopenharmony_ci params->auto_connect == HCI_AUTO_CONN_EXPLICIT) { 66688c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, 66698c2ecf20Sopenharmony_ci MGMT_OP_REMOVE_DEVICE, 66708c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS, 66718c2ecf20Sopenharmony_ci &cp->addr, sizeof(cp->addr)); 66728c2ecf20Sopenharmony_ci goto unlock; 66738c2ecf20Sopenharmony_ci } 66748c2ecf20Sopenharmony_ci 66758c2ecf20Sopenharmony_ci list_del(¶ms->action); 66768c2ecf20Sopenharmony_ci list_del(¶ms->list); 66778c2ecf20Sopenharmony_ci kfree(params); 66788c2ecf20Sopenharmony_ci hci_update_background_scan(hdev); 66798c2ecf20Sopenharmony_ci 66808c2ecf20Sopenharmony_ci device_removed(sk, hdev, &cp->addr.bdaddr, cp->addr.type); 66818c2ecf20Sopenharmony_ci } else { 66828c2ecf20Sopenharmony_ci struct hci_conn_params *p, *tmp; 66838c2ecf20Sopenharmony_ci struct bdaddr_list *b, *btmp; 66848c2ecf20Sopenharmony_ci 66858c2ecf20Sopenharmony_ci if (cp->addr.type) { 66868c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, 66878c2ecf20Sopenharmony_ci MGMT_OP_REMOVE_DEVICE, 66888c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS, 66898c2ecf20Sopenharmony_ci &cp->addr, sizeof(cp->addr)); 66908c2ecf20Sopenharmony_ci goto unlock; 66918c2ecf20Sopenharmony_ci } 66928c2ecf20Sopenharmony_ci 66938c2ecf20Sopenharmony_ci list_for_each_entry_safe(b, btmp, &hdev->accept_list, list) { 66948c2ecf20Sopenharmony_ci device_removed(sk, hdev, &b->bdaddr, b->bdaddr_type); 66958c2ecf20Sopenharmony_ci list_del(&b->list); 66968c2ecf20Sopenharmony_ci kfree(b); 66978c2ecf20Sopenharmony_ci } 66988c2ecf20Sopenharmony_ci 66998c2ecf20Sopenharmony_ci hci_req_update_scan(hdev); 67008c2ecf20Sopenharmony_ci 67018c2ecf20Sopenharmony_ci list_for_each_entry_safe(p, tmp, &hdev->le_conn_params, list) { 67028c2ecf20Sopenharmony_ci if (p->auto_connect == HCI_AUTO_CONN_DISABLED) 67038c2ecf20Sopenharmony_ci continue; 67048c2ecf20Sopenharmony_ci device_removed(sk, hdev, &p->addr, p->addr_type); 67058c2ecf20Sopenharmony_ci if (p->explicit_connect) { 67068c2ecf20Sopenharmony_ci p->auto_connect = HCI_AUTO_CONN_EXPLICIT; 67078c2ecf20Sopenharmony_ci continue; 67088c2ecf20Sopenharmony_ci } 67098c2ecf20Sopenharmony_ci list_del(&p->action); 67108c2ecf20Sopenharmony_ci list_del(&p->list); 67118c2ecf20Sopenharmony_ci kfree(p); 67128c2ecf20Sopenharmony_ci } 67138c2ecf20Sopenharmony_ci 67148c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "All LE connection parameters were removed"); 67158c2ecf20Sopenharmony_ci 67168c2ecf20Sopenharmony_ci hci_update_background_scan(hdev); 67178c2ecf20Sopenharmony_ci } 67188c2ecf20Sopenharmony_ci 67198c2ecf20Sopenharmony_cicomplete: 67208c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_DEVICE, 67218c2ecf20Sopenharmony_ci MGMT_STATUS_SUCCESS, &cp->addr, 67228c2ecf20Sopenharmony_ci sizeof(cp->addr)); 67238c2ecf20Sopenharmony_ciunlock: 67248c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 67258c2ecf20Sopenharmony_ci return err; 67268c2ecf20Sopenharmony_ci} 67278c2ecf20Sopenharmony_ci 67288c2ecf20Sopenharmony_cistatic int load_conn_param(struct sock *sk, struct hci_dev *hdev, void *data, 67298c2ecf20Sopenharmony_ci u16 len) 67308c2ecf20Sopenharmony_ci{ 67318c2ecf20Sopenharmony_ci struct mgmt_cp_load_conn_param *cp = data; 67328c2ecf20Sopenharmony_ci const u16 max_param_count = ((U16_MAX - sizeof(*cp)) / 67338c2ecf20Sopenharmony_ci sizeof(struct mgmt_conn_param)); 67348c2ecf20Sopenharmony_ci u16 param_count, expected_len; 67358c2ecf20Sopenharmony_ci int i; 67368c2ecf20Sopenharmony_ci 67378c2ecf20Sopenharmony_ci if (!lmp_le_capable(hdev)) 67388c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LOAD_CONN_PARAM, 67398c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_SUPPORTED); 67408c2ecf20Sopenharmony_ci 67418c2ecf20Sopenharmony_ci param_count = __le16_to_cpu(cp->param_count); 67428c2ecf20Sopenharmony_ci if (param_count > max_param_count) { 67438c2ecf20Sopenharmony_ci bt_dev_err(hdev, "load_conn_param: too big param_count value %u", 67448c2ecf20Sopenharmony_ci param_count); 67458c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LOAD_CONN_PARAM, 67468c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 67478c2ecf20Sopenharmony_ci } 67488c2ecf20Sopenharmony_ci 67498c2ecf20Sopenharmony_ci expected_len = struct_size(cp, params, param_count); 67508c2ecf20Sopenharmony_ci if (expected_len != len) { 67518c2ecf20Sopenharmony_ci bt_dev_err(hdev, "load_conn_param: expected %u bytes, got %u bytes", 67528c2ecf20Sopenharmony_ci expected_len, len); 67538c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_LOAD_CONN_PARAM, 67548c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 67558c2ecf20Sopenharmony_ci } 67568c2ecf20Sopenharmony_ci 67578c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "param_count %u", param_count); 67588c2ecf20Sopenharmony_ci 67598c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 67608c2ecf20Sopenharmony_ci 67618c2ecf20Sopenharmony_ci hci_conn_params_clear_disabled(hdev); 67628c2ecf20Sopenharmony_ci 67638c2ecf20Sopenharmony_ci for (i = 0; i < param_count; i++) { 67648c2ecf20Sopenharmony_ci struct mgmt_conn_param *param = &cp->params[i]; 67658c2ecf20Sopenharmony_ci struct hci_conn_params *hci_param; 67668c2ecf20Sopenharmony_ci u16 min, max, latency, timeout; 67678c2ecf20Sopenharmony_ci u8 addr_type; 67688c2ecf20Sopenharmony_ci 67698c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "Adding %pMR (type %u)", ¶m->addr.bdaddr, 67708c2ecf20Sopenharmony_ci param->addr.type); 67718c2ecf20Sopenharmony_ci 67728c2ecf20Sopenharmony_ci if (param->addr.type == BDADDR_LE_PUBLIC) { 67738c2ecf20Sopenharmony_ci addr_type = ADDR_LE_DEV_PUBLIC; 67748c2ecf20Sopenharmony_ci } else if (param->addr.type == BDADDR_LE_RANDOM) { 67758c2ecf20Sopenharmony_ci addr_type = ADDR_LE_DEV_RANDOM; 67768c2ecf20Sopenharmony_ci } else { 67778c2ecf20Sopenharmony_ci bt_dev_err(hdev, "ignoring invalid connection parameters"); 67788c2ecf20Sopenharmony_ci continue; 67798c2ecf20Sopenharmony_ci } 67808c2ecf20Sopenharmony_ci 67818c2ecf20Sopenharmony_ci min = le16_to_cpu(param->min_interval); 67828c2ecf20Sopenharmony_ci max = le16_to_cpu(param->max_interval); 67838c2ecf20Sopenharmony_ci latency = le16_to_cpu(param->latency); 67848c2ecf20Sopenharmony_ci timeout = le16_to_cpu(param->timeout); 67858c2ecf20Sopenharmony_ci 67868c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "min 0x%04x max 0x%04x latency 0x%04x timeout 0x%04x", 67878c2ecf20Sopenharmony_ci min, max, latency, timeout); 67888c2ecf20Sopenharmony_ci 67898c2ecf20Sopenharmony_ci if (hci_check_conn_params(min, max, latency, timeout) < 0) { 67908c2ecf20Sopenharmony_ci bt_dev_err(hdev, "ignoring invalid connection parameters"); 67918c2ecf20Sopenharmony_ci continue; 67928c2ecf20Sopenharmony_ci } 67938c2ecf20Sopenharmony_ci 67948c2ecf20Sopenharmony_ci hci_param = hci_conn_params_add(hdev, ¶m->addr.bdaddr, 67958c2ecf20Sopenharmony_ci addr_type); 67968c2ecf20Sopenharmony_ci if (!hci_param) { 67978c2ecf20Sopenharmony_ci bt_dev_err(hdev, "failed to add connection parameters"); 67988c2ecf20Sopenharmony_ci continue; 67998c2ecf20Sopenharmony_ci } 68008c2ecf20Sopenharmony_ci 68018c2ecf20Sopenharmony_ci hci_param->conn_min_interval = min; 68028c2ecf20Sopenharmony_ci hci_param->conn_max_interval = max; 68038c2ecf20Sopenharmony_ci hci_param->conn_latency = latency; 68048c2ecf20Sopenharmony_ci hci_param->supervision_timeout = timeout; 68058c2ecf20Sopenharmony_ci } 68068c2ecf20Sopenharmony_ci 68078c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 68088c2ecf20Sopenharmony_ci 68098c2ecf20Sopenharmony_ci return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_LOAD_CONN_PARAM, 0, 68108c2ecf20Sopenharmony_ci NULL, 0); 68118c2ecf20Sopenharmony_ci} 68128c2ecf20Sopenharmony_ci 68138c2ecf20Sopenharmony_cistatic int set_external_config(struct sock *sk, struct hci_dev *hdev, 68148c2ecf20Sopenharmony_ci void *data, u16 len) 68158c2ecf20Sopenharmony_ci{ 68168c2ecf20Sopenharmony_ci struct mgmt_cp_set_external_config *cp = data; 68178c2ecf20Sopenharmony_ci bool changed; 68188c2ecf20Sopenharmony_ci int err; 68198c2ecf20Sopenharmony_ci 68208c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 68218c2ecf20Sopenharmony_ci 68228c2ecf20Sopenharmony_ci if (hdev_is_powered(hdev)) 68238c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_EXTERNAL_CONFIG, 68248c2ecf20Sopenharmony_ci MGMT_STATUS_REJECTED); 68258c2ecf20Sopenharmony_ci 68268c2ecf20Sopenharmony_ci if (cp->config != 0x00 && cp->config != 0x01) 68278c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_EXTERNAL_CONFIG, 68288c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 68298c2ecf20Sopenharmony_ci 68308c2ecf20Sopenharmony_ci if (!test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks)) 68318c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_EXTERNAL_CONFIG, 68328c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_SUPPORTED); 68338c2ecf20Sopenharmony_ci 68348c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 68358c2ecf20Sopenharmony_ci 68368c2ecf20Sopenharmony_ci if (cp->config) 68378c2ecf20Sopenharmony_ci changed = !hci_dev_test_and_set_flag(hdev, HCI_EXT_CONFIGURED); 68388c2ecf20Sopenharmony_ci else 68398c2ecf20Sopenharmony_ci changed = hci_dev_test_and_clear_flag(hdev, HCI_EXT_CONFIGURED); 68408c2ecf20Sopenharmony_ci 68418c2ecf20Sopenharmony_ci err = send_options_rsp(sk, MGMT_OP_SET_EXTERNAL_CONFIG, hdev); 68428c2ecf20Sopenharmony_ci if (err < 0) 68438c2ecf20Sopenharmony_ci goto unlock; 68448c2ecf20Sopenharmony_ci 68458c2ecf20Sopenharmony_ci if (!changed) 68468c2ecf20Sopenharmony_ci goto unlock; 68478c2ecf20Sopenharmony_ci 68488c2ecf20Sopenharmony_ci err = new_options(hdev, sk); 68498c2ecf20Sopenharmony_ci 68508c2ecf20Sopenharmony_ci if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED) == is_configured(hdev)) { 68518c2ecf20Sopenharmony_ci mgmt_index_removed(hdev); 68528c2ecf20Sopenharmony_ci 68538c2ecf20Sopenharmony_ci if (hci_dev_test_and_change_flag(hdev, HCI_UNCONFIGURED)) { 68548c2ecf20Sopenharmony_ci hci_dev_set_flag(hdev, HCI_CONFIG); 68558c2ecf20Sopenharmony_ci hci_dev_set_flag(hdev, HCI_AUTO_OFF); 68568c2ecf20Sopenharmony_ci 68578c2ecf20Sopenharmony_ci queue_work(hdev->req_workqueue, &hdev->power_on); 68588c2ecf20Sopenharmony_ci } else { 68598c2ecf20Sopenharmony_ci set_bit(HCI_RAW, &hdev->flags); 68608c2ecf20Sopenharmony_ci mgmt_index_added(hdev); 68618c2ecf20Sopenharmony_ci } 68628c2ecf20Sopenharmony_ci } 68638c2ecf20Sopenharmony_ci 68648c2ecf20Sopenharmony_ciunlock: 68658c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 68668c2ecf20Sopenharmony_ci return err; 68678c2ecf20Sopenharmony_ci} 68688c2ecf20Sopenharmony_ci 68698c2ecf20Sopenharmony_cistatic int set_public_address(struct sock *sk, struct hci_dev *hdev, 68708c2ecf20Sopenharmony_ci void *data, u16 len) 68718c2ecf20Sopenharmony_ci{ 68728c2ecf20Sopenharmony_ci struct mgmt_cp_set_public_address *cp = data; 68738c2ecf20Sopenharmony_ci bool changed; 68748c2ecf20Sopenharmony_ci int err; 68758c2ecf20Sopenharmony_ci 68768c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 68778c2ecf20Sopenharmony_ci 68788c2ecf20Sopenharmony_ci if (hdev_is_powered(hdev)) 68798c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_PUBLIC_ADDRESS, 68808c2ecf20Sopenharmony_ci MGMT_STATUS_REJECTED); 68818c2ecf20Sopenharmony_ci 68828c2ecf20Sopenharmony_ci if (!bacmp(&cp->bdaddr, BDADDR_ANY)) 68838c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_PUBLIC_ADDRESS, 68848c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 68858c2ecf20Sopenharmony_ci 68868c2ecf20Sopenharmony_ci if (!hdev->set_bdaddr) 68878c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_PUBLIC_ADDRESS, 68888c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_SUPPORTED); 68898c2ecf20Sopenharmony_ci 68908c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 68918c2ecf20Sopenharmony_ci 68928c2ecf20Sopenharmony_ci changed = !!bacmp(&hdev->public_addr, &cp->bdaddr); 68938c2ecf20Sopenharmony_ci bacpy(&hdev->public_addr, &cp->bdaddr); 68948c2ecf20Sopenharmony_ci 68958c2ecf20Sopenharmony_ci err = send_options_rsp(sk, MGMT_OP_SET_PUBLIC_ADDRESS, hdev); 68968c2ecf20Sopenharmony_ci if (err < 0) 68978c2ecf20Sopenharmony_ci goto unlock; 68988c2ecf20Sopenharmony_ci 68998c2ecf20Sopenharmony_ci if (!changed) 69008c2ecf20Sopenharmony_ci goto unlock; 69018c2ecf20Sopenharmony_ci 69028c2ecf20Sopenharmony_ci if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) 69038c2ecf20Sopenharmony_ci err = new_options(hdev, sk); 69048c2ecf20Sopenharmony_ci 69058c2ecf20Sopenharmony_ci if (is_configured(hdev)) { 69068c2ecf20Sopenharmony_ci mgmt_index_removed(hdev); 69078c2ecf20Sopenharmony_ci 69088c2ecf20Sopenharmony_ci hci_dev_clear_flag(hdev, HCI_UNCONFIGURED); 69098c2ecf20Sopenharmony_ci 69108c2ecf20Sopenharmony_ci hci_dev_set_flag(hdev, HCI_CONFIG); 69118c2ecf20Sopenharmony_ci hci_dev_set_flag(hdev, HCI_AUTO_OFF); 69128c2ecf20Sopenharmony_ci 69138c2ecf20Sopenharmony_ci queue_work(hdev->req_workqueue, &hdev->power_on); 69148c2ecf20Sopenharmony_ci } 69158c2ecf20Sopenharmony_ci 69168c2ecf20Sopenharmony_ciunlock: 69178c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 69188c2ecf20Sopenharmony_ci return err; 69198c2ecf20Sopenharmony_ci} 69208c2ecf20Sopenharmony_ci 69218c2ecf20Sopenharmony_cistatic void read_local_oob_ext_data_complete(struct hci_dev *hdev, u8 status, 69228c2ecf20Sopenharmony_ci u16 opcode, struct sk_buff *skb) 69238c2ecf20Sopenharmony_ci{ 69248c2ecf20Sopenharmony_ci const struct mgmt_cp_read_local_oob_ext_data *mgmt_cp; 69258c2ecf20Sopenharmony_ci struct mgmt_rp_read_local_oob_ext_data *mgmt_rp; 69268c2ecf20Sopenharmony_ci u8 *h192, *r192, *h256, *r256; 69278c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 69288c2ecf20Sopenharmony_ci u16 eir_len; 69298c2ecf20Sopenharmony_ci int err; 69308c2ecf20Sopenharmony_ci 69318c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "status %u", status); 69328c2ecf20Sopenharmony_ci 69338c2ecf20Sopenharmony_ci cmd = pending_find(MGMT_OP_READ_LOCAL_OOB_EXT_DATA, hdev); 69348c2ecf20Sopenharmony_ci if (!cmd) 69358c2ecf20Sopenharmony_ci return; 69368c2ecf20Sopenharmony_ci 69378c2ecf20Sopenharmony_ci mgmt_cp = cmd->param; 69388c2ecf20Sopenharmony_ci 69398c2ecf20Sopenharmony_ci if (status) { 69408c2ecf20Sopenharmony_ci status = mgmt_status(status); 69418c2ecf20Sopenharmony_ci eir_len = 0; 69428c2ecf20Sopenharmony_ci 69438c2ecf20Sopenharmony_ci h192 = NULL; 69448c2ecf20Sopenharmony_ci r192 = NULL; 69458c2ecf20Sopenharmony_ci h256 = NULL; 69468c2ecf20Sopenharmony_ci r256 = NULL; 69478c2ecf20Sopenharmony_ci } else if (opcode == HCI_OP_READ_LOCAL_OOB_DATA) { 69488c2ecf20Sopenharmony_ci struct hci_rp_read_local_oob_data *rp; 69498c2ecf20Sopenharmony_ci 69508c2ecf20Sopenharmony_ci if (skb->len != sizeof(*rp)) { 69518c2ecf20Sopenharmony_ci status = MGMT_STATUS_FAILED; 69528c2ecf20Sopenharmony_ci eir_len = 0; 69538c2ecf20Sopenharmony_ci } else { 69548c2ecf20Sopenharmony_ci status = MGMT_STATUS_SUCCESS; 69558c2ecf20Sopenharmony_ci rp = (void *)skb->data; 69568c2ecf20Sopenharmony_ci 69578c2ecf20Sopenharmony_ci eir_len = 5 + 18 + 18; 69588c2ecf20Sopenharmony_ci h192 = rp->hash; 69598c2ecf20Sopenharmony_ci r192 = rp->rand; 69608c2ecf20Sopenharmony_ci h256 = NULL; 69618c2ecf20Sopenharmony_ci r256 = NULL; 69628c2ecf20Sopenharmony_ci } 69638c2ecf20Sopenharmony_ci } else { 69648c2ecf20Sopenharmony_ci struct hci_rp_read_local_oob_ext_data *rp; 69658c2ecf20Sopenharmony_ci 69668c2ecf20Sopenharmony_ci if (skb->len != sizeof(*rp)) { 69678c2ecf20Sopenharmony_ci status = MGMT_STATUS_FAILED; 69688c2ecf20Sopenharmony_ci eir_len = 0; 69698c2ecf20Sopenharmony_ci } else { 69708c2ecf20Sopenharmony_ci status = MGMT_STATUS_SUCCESS; 69718c2ecf20Sopenharmony_ci rp = (void *)skb->data; 69728c2ecf20Sopenharmony_ci 69738c2ecf20Sopenharmony_ci if (hci_dev_test_flag(hdev, HCI_SC_ONLY)) { 69748c2ecf20Sopenharmony_ci eir_len = 5 + 18 + 18; 69758c2ecf20Sopenharmony_ci h192 = NULL; 69768c2ecf20Sopenharmony_ci r192 = NULL; 69778c2ecf20Sopenharmony_ci } else { 69788c2ecf20Sopenharmony_ci eir_len = 5 + 18 + 18 + 18 + 18; 69798c2ecf20Sopenharmony_ci h192 = rp->hash192; 69808c2ecf20Sopenharmony_ci r192 = rp->rand192; 69818c2ecf20Sopenharmony_ci } 69828c2ecf20Sopenharmony_ci 69838c2ecf20Sopenharmony_ci h256 = rp->hash256; 69848c2ecf20Sopenharmony_ci r256 = rp->rand256; 69858c2ecf20Sopenharmony_ci } 69868c2ecf20Sopenharmony_ci } 69878c2ecf20Sopenharmony_ci 69888c2ecf20Sopenharmony_ci mgmt_rp = kmalloc(sizeof(*mgmt_rp) + eir_len, GFP_KERNEL); 69898c2ecf20Sopenharmony_ci if (!mgmt_rp) 69908c2ecf20Sopenharmony_ci goto done; 69918c2ecf20Sopenharmony_ci 69928c2ecf20Sopenharmony_ci if (status) 69938c2ecf20Sopenharmony_ci goto send_rsp; 69948c2ecf20Sopenharmony_ci 69958c2ecf20Sopenharmony_ci eir_len = eir_append_data(mgmt_rp->eir, 0, EIR_CLASS_OF_DEV, 69968c2ecf20Sopenharmony_ci hdev->dev_class, 3); 69978c2ecf20Sopenharmony_ci 69988c2ecf20Sopenharmony_ci if (h192 && r192) { 69998c2ecf20Sopenharmony_ci eir_len = eir_append_data(mgmt_rp->eir, eir_len, 70008c2ecf20Sopenharmony_ci EIR_SSP_HASH_C192, h192, 16); 70018c2ecf20Sopenharmony_ci eir_len = eir_append_data(mgmt_rp->eir, eir_len, 70028c2ecf20Sopenharmony_ci EIR_SSP_RAND_R192, r192, 16); 70038c2ecf20Sopenharmony_ci } 70048c2ecf20Sopenharmony_ci 70058c2ecf20Sopenharmony_ci if (h256 && r256) { 70068c2ecf20Sopenharmony_ci eir_len = eir_append_data(mgmt_rp->eir, eir_len, 70078c2ecf20Sopenharmony_ci EIR_SSP_HASH_C256, h256, 16); 70088c2ecf20Sopenharmony_ci eir_len = eir_append_data(mgmt_rp->eir, eir_len, 70098c2ecf20Sopenharmony_ci EIR_SSP_RAND_R256, r256, 16); 70108c2ecf20Sopenharmony_ci } 70118c2ecf20Sopenharmony_ci 70128c2ecf20Sopenharmony_cisend_rsp: 70138c2ecf20Sopenharmony_ci mgmt_rp->type = mgmt_cp->type; 70148c2ecf20Sopenharmony_ci mgmt_rp->eir_len = cpu_to_le16(eir_len); 70158c2ecf20Sopenharmony_ci 70168c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(cmd->sk, hdev->id, 70178c2ecf20Sopenharmony_ci MGMT_OP_READ_LOCAL_OOB_EXT_DATA, status, 70188c2ecf20Sopenharmony_ci mgmt_rp, sizeof(*mgmt_rp) + eir_len); 70198c2ecf20Sopenharmony_ci if (err < 0 || status) 70208c2ecf20Sopenharmony_ci goto done; 70218c2ecf20Sopenharmony_ci 70228c2ecf20Sopenharmony_ci hci_sock_set_flag(cmd->sk, HCI_MGMT_OOB_DATA_EVENTS); 70238c2ecf20Sopenharmony_ci 70248c2ecf20Sopenharmony_ci err = mgmt_limited_event(MGMT_EV_LOCAL_OOB_DATA_UPDATED, hdev, 70258c2ecf20Sopenharmony_ci mgmt_rp, sizeof(*mgmt_rp) + eir_len, 70268c2ecf20Sopenharmony_ci HCI_MGMT_OOB_DATA_EVENTS, cmd->sk); 70278c2ecf20Sopenharmony_cidone: 70288c2ecf20Sopenharmony_ci kfree(mgmt_rp); 70298c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 70308c2ecf20Sopenharmony_ci} 70318c2ecf20Sopenharmony_ci 70328c2ecf20Sopenharmony_cistatic int read_local_ssp_oob_req(struct hci_dev *hdev, struct sock *sk, 70338c2ecf20Sopenharmony_ci struct mgmt_cp_read_local_oob_ext_data *cp) 70348c2ecf20Sopenharmony_ci{ 70358c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 70368c2ecf20Sopenharmony_ci struct hci_request req; 70378c2ecf20Sopenharmony_ci int err; 70388c2ecf20Sopenharmony_ci 70398c2ecf20Sopenharmony_ci cmd = mgmt_pending_add(sk, MGMT_OP_READ_LOCAL_OOB_EXT_DATA, hdev, 70408c2ecf20Sopenharmony_ci cp, sizeof(*cp)); 70418c2ecf20Sopenharmony_ci if (!cmd) 70428c2ecf20Sopenharmony_ci return -ENOMEM; 70438c2ecf20Sopenharmony_ci 70448c2ecf20Sopenharmony_ci hci_req_init(&req, hdev); 70458c2ecf20Sopenharmony_ci 70468c2ecf20Sopenharmony_ci if (bredr_sc_enabled(hdev)) 70478c2ecf20Sopenharmony_ci hci_req_add(&req, HCI_OP_READ_LOCAL_OOB_EXT_DATA, 0, NULL); 70488c2ecf20Sopenharmony_ci else 70498c2ecf20Sopenharmony_ci hci_req_add(&req, HCI_OP_READ_LOCAL_OOB_DATA, 0, NULL); 70508c2ecf20Sopenharmony_ci 70518c2ecf20Sopenharmony_ci err = hci_req_run_skb(&req, read_local_oob_ext_data_complete); 70528c2ecf20Sopenharmony_ci if (err < 0) { 70538c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 70548c2ecf20Sopenharmony_ci return err; 70558c2ecf20Sopenharmony_ci } 70568c2ecf20Sopenharmony_ci 70578c2ecf20Sopenharmony_ci return 0; 70588c2ecf20Sopenharmony_ci} 70598c2ecf20Sopenharmony_ci 70608c2ecf20Sopenharmony_cistatic int read_local_oob_ext_data(struct sock *sk, struct hci_dev *hdev, 70618c2ecf20Sopenharmony_ci void *data, u16 data_len) 70628c2ecf20Sopenharmony_ci{ 70638c2ecf20Sopenharmony_ci struct mgmt_cp_read_local_oob_ext_data *cp = data; 70648c2ecf20Sopenharmony_ci struct mgmt_rp_read_local_oob_ext_data *rp; 70658c2ecf20Sopenharmony_ci size_t rp_len; 70668c2ecf20Sopenharmony_ci u16 eir_len; 70678c2ecf20Sopenharmony_ci u8 status, flags, role, addr[7], hash[16], rand[16]; 70688c2ecf20Sopenharmony_ci int err; 70698c2ecf20Sopenharmony_ci 70708c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 70718c2ecf20Sopenharmony_ci 70728c2ecf20Sopenharmony_ci if (hdev_is_powered(hdev)) { 70738c2ecf20Sopenharmony_ci switch (cp->type) { 70748c2ecf20Sopenharmony_ci case BIT(BDADDR_BREDR): 70758c2ecf20Sopenharmony_ci status = mgmt_bredr_support(hdev); 70768c2ecf20Sopenharmony_ci if (status) 70778c2ecf20Sopenharmony_ci eir_len = 0; 70788c2ecf20Sopenharmony_ci else 70798c2ecf20Sopenharmony_ci eir_len = 5; 70808c2ecf20Sopenharmony_ci break; 70818c2ecf20Sopenharmony_ci case (BIT(BDADDR_LE_PUBLIC) | BIT(BDADDR_LE_RANDOM)): 70828c2ecf20Sopenharmony_ci status = mgmt_le_support(hdev); 70838c2ecf20Sopenharmony_ci if (status) 70848c2ecf20Sopenharmony_ci eir_len = 0; 70858c2ecf20Sopenharmony_ci else 70868c2ecf20Sopenharmony_ci eir_len = 9 + 3 + 18 + 18 + 3; 70878c2ecf20Sopenharmony_ci break; 70888c2ecf20Sopenharmony_ci default: 70898c2ecf20Sopenharmony_ci status = MGMT_STATUS_INVALID_PARAMS; 70908c2ecf20Sopenharmony_ci eir_len = 0; 70918c2ecf20Sopenharmony_ci break; 70928c2ecf20Sopenharmony_ci } 70938c2ecf20Sopenharmony_ci } else { 70948c2ecf20Sopenharmony_ci status = MGMT_STATUS_NOT_POWERED; 70958c2ecf20Sopenharmony_ci eir_len = 0; 70968c2ecf20Sopenharmony_ci } 70978c2ecf20Sopenharmony_ci 70988c2ecf20Sopenharmony_ci rp_len = sizeof(*rp) + eir_len; 70998c2ecf20Sopenharmony_ci rp = kmalloc(rp_len, GFP_ATOMIC); 71008c2ecf20Sopenharmony_ci if (!rp) 71018c2ecf20Sopenharmony_ci return -ENOMEM; 71028c2ecf20Sopenharmony_ci 71038c2ecf20Sopenharmony_ci if (status) 71048c2ecf20Sopenharmony_ci goto complete; 71058c2ecf20Sopenharmony_ci 71068c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 71078c2ecf20Sopenharmony_ci 71088c2ecf20Sopenharmony_ci eir_len = 0; 71098c2ecf20Sopenharmony_ci switch (cp->type) { 71108c2ecf20Sopenharmony_ci case BIT(BDADDR_BREDR): 71118c2ecf20Sopenharmony_ci if (hci_dev_test_flag(hdev, HCI_SSP_ENABLED)) { 71128c2ecf20Sopenharmony_ci err = read_local_ssp_oob_req(hdev, sk, cp); 71138c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 71148c2ecf20Sopenharmony_ci if (!err) 71158c2ecf20Sopenharmony_ci goto done; 71168c2ecf20Sopenharmony_ci 71178c2ecf20Sopenharmony_ci status = MGMT_STATUS_FAILED; 71188c2ecf20Sopenharmony_ci goto complete; 71198c2ecf20Sopenharmony_ci } else { 71208c2ecf20Sopenharmony_ci eir_len = eir_append_data(rp->eir, eir_len, 71218c2ecf20Sopenharmony_ci EIR_CLASS_OF_DEV, 71228c2ecf20Sopenharmony_ci hdev->dev_class, 3); 71238c2ecf20Sopenharmony_ci } 71248c2ecf20Sopenharmony_ci break; 71258c2ecf20Sopenharmony_ci case (BIT(BDADDR_LE_PUBLIC) | BIT(BDADDR_LE_RANDOM)): 71268c2ecf20Sopenharmony_ci if (hci_dev_test_flag(hdev, HCI_SC_ENABLED) && 71278c2ecf20Sopenharmony_ci smp_generate_oob(hdev, hash, rand) < 0) { 71288c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 71298c2ecf20Sopenharmony_ci status = MGMT_STATUS_FAILED; 71308c2ecf20Sopenharmony_ci goto complete; 71318c2ecf20Sopenharmony_ci } 71328c2ecf20Sopenharmony_ci 71338c2ecf20Sopenharmony_ci /* This should return the active RPA, but since the RPA 71348c2ecf20Sopenharmony_ci * is only programmed on demand, it is really hard to fill 71358c2ecf20Sopenharmony_ci * this in at the moment. For now disallow retrieving 71368c2ecf20Sopenharmony_ci * local out-of-band data when privacy is in use. 71378c2ecf20Sopenharmony_ci * 71388c2ecf20Sopenharmony_ci * Returning the identity address will not help here since 71398c2ecf20Sopenharmony_ci * pairing happens before the identity resolving key is 71408c2ecf20Sopenharmony_ci * known and thus the connection establishment happens 71418c2ecf20Sopenharmony_ci * based on the RPA and not the identity address. 71428c2ecf20Sopenharmony_ci */ 71438c2ecf20Sopenharmony_ci if (hci_dev_test_flag(hdev, HCI_PRIVACY)) { 71448c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 71458c2ecf20Sopenharmony_ci status = MGMT_STATUS_REJECTED; 71468c2ecf20Sopenharmony_ci goto complete; 71478c2ecf20Sopenharmony_ci } 71488c2ecf20Sopenharmony_ci 71498c2ecf20Sopenharmony_ci if (hci_dev_test_flag(hdev, HCI_FORCE_STATIC_ADDR) || 71508c2ecf20Sopenharmony_ci !bacmp(&hdev->bdaddr, BDADDR_ANY) || 71518c2ecf20Sopenharmony_ci (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED) && 71528c2ecf20Sopenharmony_ci bacmp(&hdev->static_addr, BDADDR_ANY))) { 71538c2ecf20Sopenharmony_ci memcpy(addr, &hdev->static_addr, 6); 71548c2ecf20Sopenharmony_ci addr[6] = 0x01; 71558c2ecf20Sopenharmony_ci } else { 71568c2ecf20Sopenharmony_ci memcpy(addr, &hdev->bdaddr, 6); 71578c2ecf20Sopenharmony_ci addr[6] = 0x00; 71588c2ecf20Sopenharmony_ci } 71598c2ecf20Sopenharmony_ci 71608c2ecf20Sopenharmony_ci eir_len = eir_append_data(rp->eir, eir_len, EIR_LE_BDADDR, 71618c2ecf20Sopenharmony_ci addr, sizeof(addr)); 71628c2ecf20Sopenharmony_ci 71638c2ecf20Sopenharmony_ci if (hci_dev_test_flag(hdev, HCI_ADVERTISING)) 71648c2ecf20Sopenharmony_ci role = 0x02; 71658c2ecf20Sopenharmony_ci else 71668c2ecf20Sopenharmony_ci role = 0x01; 71678c2ecf20Sopenharmony_ci 71688c2ecf20Sopenharmony_ci eir_len = eir_append_data(rp->eir, eir_len, EIR_LE_ROLE, 71698c2ecf20Sopenharmony_ci &role, sizeof(role)); 71708c2ecf20Sopenharmony_ci 71718c2ecf20Sopenharmony_ci if (hci_dev_test_flag(hdev, HCI_SC_ENABLED)) { 71728c2ecf20Sopenharmony_ci eir_len = eir_append_data(rp->eir, eir_len, 71738c2ecf20Sopenharmony_ci EIR_LE_SC_CONFIRM, 71748c2ecf20Sopenharmony_ci hash, sizeof(hash)); 71758c2ecf20Sopenharmony_ci 71768c2ecf20Sopenharmony_ci eir_len = eir_append_data(rp->eir, eir_len, 71778c2ecf20Sopenharmony_ci EIR_LE_SC_RANDOM, 71788c2ecf20Sopenharmony_ci rand, sizeof(rand)); 71798c2ecf20Sopenharmony_ci } 71808c2ecf20Sopenharmony_ci 71818c2ecf20Sopenharmony_ci flags = mgmt_get_adv_discov_flags(hdev); 71828c2ecf20Sopenharmony_ci 71838c2ecf20Sopenharmony_ci if (!hci_dev_test_flag(hdev, HCI_BREDR_ENABLED)) 71848c2ecf20Sopenharmony_ci flags |= LE_AD_NO_BREDR; 71858c2ecf20Sopenharmony_ci 71868c2ecf20Sopenharmony_ci eir_len = eir_append_data(rp->eir, eir_len, EIR_FLAGS, 71878c2ecf20Sopenharmony_ci &flags, sizeof(flags)); 71888c2ecf20Sopenharmony_ci break; 71898c2ecf20Sopenharmony_ci } 71908c2ecf20Sopenharmony_ci 71918c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 71928c2ecf20Sopenharmony_ci 71938c2ecf20Sopenharmony_ci hci_sock_set_flag(sk, HCI_MGMT_OOB_DATA_EVENTS); 71948c2ecf20Sopenharmony_ci 71958c2ecf20Sopenharmony_ci status = MGMT_STATUS_SUCCESS; 71968c2ecf20Sopenharmony_ci 71978c2ecf20Sopenharmony_cicomplete: 71988c2ecf20Sopenharmony_ci rp->type = cp->type; 71998c2ecf20Sopenharmony_ci rp->eir_len = cpu_to_le16(eir_len); 72008c2ecf20Sopenharmony_ci 72018c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_READ_LOCAL_OOB_EXT_DATA, 72028c2ecf20Sopenharmony_ci status, rp, sizeof(*rp) + eir_len); 72038c2ecf20Sopenharmony_ci if (err < 0 || status) 72048c2ecf20Sopenharmony_ci goto done; 72058c2ecf20Sopenharmony_ci 72068c2ecf20Sopenharmony_ci err = mgmt_limited_event(MGMT_EV_LOCAL_OOB_DATA_UPDATED, hdev, 72078c2ecf20Sopenharmony_ci rp, sizeof(*rp) + eir_len, 72088c2ecf20Sopenharmony_ci HCI_MGMT_OOB_DATA_EVENTS, sk); 72098c2ecf20Sopenharmony_ci 72108c2ecf20Sopenharmony_cidone: 72118c2ecf20Sopenharmony_ci kfree(rp); 72128c2ecf20Sopenharmony_ci 72138c2ecf20Sopenharmony_ci return err; 72148c2ecf20Sopenharmony_ci} 72158c2ecf20Sopenharmony_ci 72168c2ecf20Sopenharmony_cistatic u32 get_supported_adv_flags(struct hci_dev *hdev) 72178c2ecf20Sopenharmony_ci{ 72188c2ecf20Sopenharmony_ci u32 flags = 0; 72198c2ecf20Sopenharmony_ci 72208c2ecf20Sopenharmony_ci flags |= MGMT_ADV_FLAG_CONNECTABLE; 72218c2ecf20Sopenharmony_ci flags |= MGMT_ADV_FLAG_DISCOV; 72228c2ecf20Sopenharmony_ci flags |= MGMT_ADV_FLAG_LIMITED_DISCOV; 72238c2ecf20Sopenharmony_ci flags |= MGMT_ADV_FLAG_MANAGED_FLAGS; 72248c2ecf20Sopenharmony_ci flags |= MGMT_ADV_FLAG_APPEARANCE; 72258c2ecf20Sopenharmony_ci flags |= MGMT_ADV_FLAG_LOCAL_NAME; 72268c2ecf20Sopenharmony_ci 72278c2ecf20Sopenharmony_ci /* In extended adv TX_POWER returned from Set Adv Param 72288c2ecf20Sopenharmony_ci * will be always valid. 72298c2ecf20Sopenharmony_ci */ 72308c2ecf20Sopenharmony_ci if ((hdev->adv_tx_power != HCI_TX_POWER_INVALID) || 72318c2ecf20Sopenharmony_ci ext_adv_capable(hdev)) 72328c2ecf20Sopenharmony_ci flags |= MGMT_ADV_FLAG_TX_POWER; 72338c2ecf20Sopenharmony_ci 72348c2ecf20Sopenharmony_ci if (ext_adv_capable(hdev)) { 72358c2ecf20Sopenharmony_ci flags |= MGMT_ADV_FLAG_SEC_1M; 72368c2ecf20Sopenharmony_ci flags |= MGMT_ADV_FLAG_HW_OFFLOAD; 72378c2ecf20Sopenharmony_ci flags |= MGMT_ADV_FLAG_CAN_SET_TX_POWER; 72388c2ecf20Sopenharmony_ci 72398c2ecf20Sopenharmony_ci if (hdev->le_features[1] & HCI_LE_PHY_2M) 72408c2ecf20Sopenharmony_ci flags |= MGMT_ADV_FLAG_SEC_2M; 72418c2ecf20Sopenharmony_ci 72428c2ecf20Sopenharmony_ci if (hdev->le_features[1] & HCI_LE_PHY_CODED) 72438c2ecf20Sopenharmony_ci flags |= MGMT_ADV_FLAG_SEC_CODED; 72448c2ecf20Sopenharmony_ci } 72458c2ecf20Sopenharmony_ci 72468c2ecf20Sopenharmony_ci return flags; 72478c2ecf20Sopenharmony_ci} 72488c2ecf20Sopenharmony_ci 72498c2ecf20Sopenharmony_cistatic int read_adv_features(struct sock *sk, struct hci_dev *hdev, 72508c2ecf20Sopenharmony_ci void *data, u16 data_len) 72518c2ecf20Sopenharmony_ci{ 72528c2ecf20Sopenharmony_ci struct mgmt_rp_read_adv_features *rp; 72538c2ecf20Sopenharmony_ci size_t rp_len; 72548c2ecf20Sopenharmony_ci int err; 72558c2ecf20Sopenharmony_ci struct adv_info *adv_instance; 72568c2ecf20Sopenharmony_ci u32 supported_flags; 72578c2ecf20Sopenharmony_ci u8 *instance; 72588c2ecf20Sopenharmony_ci 72598c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 72608c2ecf20Sopenharmony_ci 72618c2ecf20Sopenharmony_ci if (!lmp_le_capable(hdev)) 72628c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_READ_ADV_FEATURES, 72638c2ecf20Sopenharmony_ci MGMT_STATUS_REJECTED); 72648c2ecf20Sopenharmony_ci 72658c2ecf20Sopenharmony_ci /* Enabling the experimental LL Privay support disables support for 72668c2ecf20Sopenharmony_ci * advertising. 72678c2ecf20Sopenharmony_ci */ 72688c2ecf20Sopenharmony_ci if (hci_dev_test_flag(hdev, HCI_ENABLE_LL_PRIVACY)) 72698c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING, 72708c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_SUPPORTED); 72718c2ecf20Sopenharmony_ci 72728c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 72738c2ecf20Sopenharmony_ci 72748c2ecf20Sopenharmony_ci rp_len = sizeof(*rp) + hdev->adv_instance_cnt; 72758c2ecf20Sopenharmony_ci rp = kmalloc(rp_len, GFP_ATOMIC); 72768c2ecf20Sopenharmony_ci if (!rp) { 72778c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 72788c2ecf20Sopenharmony_ci return -ENOMEM; 72798c2ecf20Sopenharmony_ci } 72808c2ecf20Sopenharmony_ci 72818c2ecf20Sopenharmony_ci supported_flags = get_supported_adv_flags(hdev); 72828c2ecf20Sopenharmony_ci 72838c2ecf20Sopenharmony_ci rp->supported_flags = cpu_to_le32(supported_flags); 72848c2ecf20Sopenharmony_ci rp->max_adv_data_len = HCI_MAX_AD_LENGTH; 72858c2ecf20Sopenharmony_ci rp->max_scan_rsp_len = HCI_MAX_AD_LENGTH; 72868c2ecf20Sopenharmony_ci rp->max_instances = hdev->le_num_of_adv_sets; 72878c2ecf20Sopenharmony_ci rp->num_instances = hdev->adv_instance_cnt; 72888c2ecf20Sopenharmony_ci 72898c2ecf20Sopenharmony_ci instance = rp->instance; 72908c2ecf20Sopenharmony_ci list_for_each_entry(adv_instance, &hdev->adv_instances, list) { 72918c2ecf20Sopenharmony_ci *instance = adv_instance->instance; 72928c2ecf20Sopenharmony_ci instance++; 72938c2ecf20Sopenharmony_ci } 72948c2ecf20Sopenharmony_ci 72958c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 72968c2ecf20Sopenharmony_ci 72978c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_READ_ADV_FEATURES, 72988c2ecf20Sopenharmony_ci MGMT_STATUS_SUCCESS, rp, rp_len); 72998c2ecf20Sopenharmony_ci 73008c2ecf20Sopenharmony_ci kfree(rp); 73018c2ecf20Sopenharmony_ci 73028c2ecf20Sopenharmony_ci return err; 73038c2ecf20Sopenharmony_ci} 73048c2ecf20Sopenharmony_ci 73058c2ecf20Sopenharmony_cistatic u8 calculate_name_len(struct hci_dev *hdev) 73068c2ecf20Sopenharmony_ci{ 73078c2ecf20Sopenharmony_ci u8 buf[HCI_MAX_SHORT_NAME_LENGTH + 3]; 73088c2ecf20Sopenharmony_ci 73098c2ecf20Sopenharmony_ci return append_local_name(hdev, buf, 0); 73108c2ecf20Sopenharmony_ci} 73118c2ecf20Sopenharmony_ci 73128c2ecf20Sopenharmony_cistatic u8 tlv_data_max_len(struct hci_dev *hdev, u32 adv_flags, 73138c2ecf20Sopenharmony_ci bool is_adv_data) 73148c2ecf20Sopenharmony_ci{ 73158c2ecf20Sopenharmony_ci u8 max_len = HCI_MAX_AD_LENGTH; 73168c2ecf20Sopenharmony_ci 73178c2ecf20Sopenharmony_ci if (is_adv_data) { 73188c2ecf20Sopenharmony_ci if (adv_flags & (MGMT_ADV_FLAG_DISCOV | 73198c2ecf20Sopenharmony_ci MGMT_ADV_FLAG_LIMITED_DISCOV | 73208c2ecf20Sopenharmony_ci MGMT_ADV_FLAG_MANAGED_FLAGS)) 73218c2ecf20Sopenharmony_ci max_len -= 3; 73228c2ecf20Sopenharmony_ci 73238c2ecf20Sopenharmony_ci if (adv_flags & MGMT_ADV_FLAG_TX_POWER) 73248c2ecf20Sopenharmony_ci max_len -= 3; 73258c2ecf20Sopenharmony_ci } else { 73268c2ecf20Sopenharmony_ci if (adv_flags & MGMT_ADV_FLAG_LOCAL_NAME) 73278c2ecf20Sopenharmony_ci max_len -= calculate_name_len(hdev); 73288c2ecf20Sopenharmony_ci 73298c2ecf20Sopenharmony_ci if (adv_flags & (MGMT_ADV_FLAG_APPEARANCE)) 73308c2ecf20Sopenharmony_ci max_len -= 4; 73318c2ecf20Sopenharmony_ci } 73328c2ecf20Sopenharmony_ci 73338c2ecf20Sopenharmony_ci return max_len; 73348c2ecf20Sopenharmony_ci} 73358c2ecf20Sopenharmony_ci 73368c2ecf20Sopenharmony_cistatic bool flags_managed(u32 adv_flags) 73378c2ecf20Sopenharmony_ci{ 73388c2ecf20Sopenharmony_ci return adv_flags & (MGMT_ADV_FLAG_DISCOV | 73398c2ecf20Sopenharmony_ci MGMT_ADV_FLAG_LIMITED_DISCOV | 73408c2ecf20Sopenharmony_ci MGMT_ADV_FLAG_MANAGED_FLAGS); 73418c2ecf20Sopenharmony_ci} 73428c2ecf20Sopenharmony_ci 73438c2ecf20Sopenharmony_cistatic bool tx_power_managed(u32 adv_flags) 73448c2ecf20Sopenharmony_ci{ 73458c2ecf20Sopenharmony_ci return adv_flags & MGMT_ADV_FLAG_TX_POWER; 73468c2ecf20Sopenharmony_ci} 73478c2ecf20Sopenharmony_ci 73488c2ecf20Sopenharmony_cistatic bool name_managed(u32 adv_flags) 73498c2ecf20Sopenharmony_ci{ 73508c2ecf20Sopenharmony_ci return adv_flags & MGMT_ADV_FLAG_LOCAL_NAME; 73518c2ecf20Sopenharmony_ci} 73528c2ecf20Sopenharmony_ci 73538c2ecf20Sopenharmony_cistatic bool appearance_managed(u32 adv_flags) 73548c2ecf20Sopenharmony_ci{ 73558c2ecf20Sopenharmony_ci return adv_flags & MGMT_ADV_FLAG_APPEARANCE; 73568c2ecf20Sopenharmony_ci} 73578c2ecf20Sopenharmony_ci 73588c2ecf20Sopenharmony_cistatic bool tlv_data_is_valid(struct hci_dev *hdev, u32 adv_flags, u8 *data, 73598c2ecf20Sopenharmony_ci u8 len, bool is_adv_data) 73608c2ecf20Sopenharmony_ci{ 73618c2ecf20Sopenharmony_ci int i, cur_len; 73628c2ecf20Sopenharmony_ci u8 max_len; 73638c2ecf20Sopenharmony_ci 73648c2ecf20Sopenharmony_ci max_len = tlv_data_max_len(hdev, adv_flags, is_adv_data); 73658c2ecf20Sopenharmony_ci 73668c2ecf20Sopenharmony_ci if (len > max_len) 73678c2ecf20Sopenharmony_ci return false; 73688c2ecf20Sopenharmony_ci 73698c2ecf20Sopenharmony_ci /* Make sure that the data is correctly formatted. */ 73708c2ecf20Sopenharmony_ci for (i = 0, cur_len = 0; i < len; i += (cur_len + 1)) { 73718c2ecf20Sopenharmony_ci cur_len = data[i]; 73728c2ecf20Sopenharmony_ci 73738c2ecf20Sopenharmony_ci if (!cur_len) 73748c2ecf20Sopenharmony_ci continue; 73758c2ecf20Sopenharmony_ci 73768c2ecf20Sopenharmony_ci if (data[i + 1] == EIR_FLAGS && 73778c2ecf20Sopenharmony_ci (!is_adv_data || flags_managed(adv_flags))) 73788c2ecf20Sopenharmony_ci return false; 73798c2ecf20Sopenharmony_ci 73808c2ecf20Sopenharmony_ci if (data[i + 1] == EIR_TX_POWER && tx_power_managed(adv_flags)) 73818c2ecf20Sopenharmony_ci return false; 73828c2ecf20Sopenharmony_ci 73838c2ecf20Sopenharmony_ci if (data[i + 1] == EIR_NAME_COMPLETE && name_managed(adv_flags)) 73848c2ecf20Sopenharmony_ci return false; 73858c2ecf20Sopenharmony_ci 73868c2ecf20Sopenharmony_ci if (data[i + 1] == EIR_NAME_SHORT && name_managed(adv_flags)) 73878c2ecf20Sopenharmony_ci return false; 73888c2ecf20Sopenharmony_ci 73898c2ecf20Sopenharmony_ci if (data[i + 1] == EIR_APPEARANCE && 73908c2ecf20Sopenharmony_ci appearance_managed(adv_flags)) 73918c2ecf20Sopenharmony_ci return false; 73928c2ecf20Sopenharmony_ci 73938c2ecf20Sopenharmony_ci /* If the current field length would exceed the total data 73948c2ecf20Sopenharmony_ci * length, then it's invalid. 73958c2ecf20Sopenharmony_ci */ 73968c2ecf20Sopenharmony_ci if (i + cur_len >= len) 73978c2ecf20Sopenharmony_ci return false; 73988c2ecf20Sopenharmony_ci } 73998c2ecf20Sopenharmony_ci 74008c2ecf20Sopenharmony_ci return true; 74018c2ecf20Sopenharmony_ci} 74028c2ecf20Sopenharmony_ci 74038c2ecf20Sopenharmony_cistatic void add_advertising_complete(struct hci_dev *hdev, u8 status, 74048c2ecf20Sopenharmony_ci u16 opcode) 74058c2ecf20Sopenharmony_ci{ 74068c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 74078c2ecf20Sopenharmony_ci struct mgmt_cp_add_advertising *cp; 74088c2ecf20Sopenharmony_ci struct mgmt_rp_add_advertising rp; 74098c2ecf20Sopenharmony_ci struct adv_info *adv_instance, *n; 74108c2ecf20Sopenharmony_ci u8 instance; 74118c2ecf20Sopenharmony_ci 74128c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "status %d", status); 74138c2ecf20Sopenharmony_ci 74148c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 74158c2ecf20Sopenharmony_ci 74168c2ecf20Sopenharmony_ci cmd = pending_find(MGMT_OP_ADD_ADVERTISING, hdev); 74178c2ecf20Sopenharmony_ci 74188c2ecf20Sopenharmony_ci list_for_each_entry_safe(adv_instance, n, &hdev->adv_instances, list) { 74198c2ecf20Sopenharmony_ci if (!adv_instance->pending) 74208c2ecf20Sopenharmony_ci continue; 74218c2ecf20Sopenharmony_ci 74228c2ecf20Sopenharmony_ci if (!status) { 74238c2ecf20Sopenharmony_ci adv_instance->pending = false; 74248c2ecf20Sopenharmony_ci continue; 74258c2ecf20Sopenharmony_ci } 74268c2ecf20Sopenharmony_ci 74278c2ecf20Sopenharmony_ci instance = adv_instance->instance; 74288c2ecf20Sopenharmony_ci 74298c2ecf20Sopenharmony_ci if (hdev->cur_adv_instance == instance) 74308c2ecf20Sopenharmony_ci cancel_adv_timeout(hdev); 74318c2ecf20Sopenharmony_ci 74328c2ecf20Sopenharmony_ci hci_remove_adv_instance(hdev, instance); 74338c2ecf20Sopenharmony_ci mgmt_advertising_removed(cmd ? cmd->sk : NULL, hdev, instance); 74348c2ecf20Sopenharmony_ci } 74358c2ecf20Sopenharmony_ci 74368c2ecf20Sopenharmony_ci if (!cmd) 74378c2ecf20Sopenharmony_ci goto unlock; 74388c2ecf20Sopenharmony_ci 74398c2ecf20Sopenharmony_ci cp = cmd->param; 74408c2ecf20Sopenharmony_ci rp.instance = cp->instance; 74418c2ecf20Sopenharmony_ci 74428c2ecf20Sopenharmony_ci if (status) 74438c2ecf20Sopenharmony_ci mgmt_cmd_status(cmd->sk, cmd->index, cmd->opcode, 74448c2ecf20Sopenharmony_ci mgmt_status(status)); 74458c2ecf20Sopenharmony_ci else 74468c2ecf20Sopenharmony_ci mgmt_cmd_complete(cmd->sk, cmd->index, cmd->opcode, 74478c2ecf20Sopenharmony_ci mgmt_status(status), &rp, sizeof(rp)); 74488c2ecf20Sopenharmony_ci 74498c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 74508c2ecf20Sopenharmony_ci 74518c2ecf20Sopenharmony_ciunlock: 74528c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 74538c2ecf20Sopenharmony_ci} 74548c2ecf20Sopenharmony_ci 74558c2ecf20Sopenharmony_cistatic int add_advertising(struct sock *sk, struct hci_dev *hdev, 74568c2ecf20Sopenharmony_ci void *data, u16 data_len) 74578c2ecf20Sopenharmony_ci{ 74588c2ecf20Sopenharmony_ci struct mgmt_cp_add_advertising *cp = data; 74598c2ecf20Sopenharmony_ci struct mgmt_rp_add_advertising rp; 74608c2ecf20Sopenharmony_ci u32 flags; 74618c2ecf20Sopenharmony_ci u32 supported_flags, phy_flags; 74628c2ecf20Sopenharmony_ci u8 status; 74638c2ecf20Sopenharmony_ci u16 timeout, duration; 74648c2ecf20Sopenharmony_ci unsigned int prev_instance_cnt = hdev->adv_instance_cnt; 74658c2ecf20Sopenharmony_ci u8 schedule_instance = 0; 74668c2ecf20Sopenharmony_ci struct adv_info *next_instance; 74678c2ecf20Sopenharmony_ci int err; 74688c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 74698c2ecf20Sopenharmony_ci struct hci_request req; 74708c2ecf20Sopenharmony_ci 74718c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 74728c2ecf20Sopenharmony_ci 74738c2ecf20Sopenharmony_ci status = mgmt_le_support(hdev); 74748c2ecf20Sopenharmony_ci if (status) 74758c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING, 74768c2ecf20Sopenharmony_ci status); 74778c2ecf20Sopenharmony_ci 74788c2ecf20Sopenharmony_ci /* Enabling the experimental LL Privay support disables support for 74798c2ecf20Sopenharmony_ci * advertising. 74808c2ecf20Sopenharmony_ci */ 74818c2ecf20Sopenharmony_ci if (hci_dev_test_flag(hdev, HCI_ENABLE_LL_PRIVACY)) 74828c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING, 74838c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_SUPPORTED); 74848c2ecf20Sopenharmony_ci 74858c2ecf20Sopenharmony_ci if (cp->instance < 1 || cp->instance > hdev->le_num_of_adv_sets) 74868c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING, 74878c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 74888c2ecf20Sopenharmony_ci 74898c2ecf20Sopenharmony_ci if (data_len != sizeof(*cp) + cp->adv_data_len + cp->scan_rsp_len) 74908c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING, 74918c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 74928c2ecf20Sopenharmony_ci 74938c2ecf20Sopenharmony_ci flags = __le32_to_cpu(cp->flags); 74948c2ecf20Sopenharmony_ci timeout = __le16_to_cpu(cp->timeout); 74958c2ecf20Sopenharmony_ci duration = __le16_to_cpu(cp->duration); 74968c2ecf20Sopenharmony_ci 74978c2ecf20Sopenharmony_ci /* The current implementation only supports a subset of the specified 74988c2ecf20Sopenharmony_ci * flags. Also need to check mutual exclusiveness of sec flags. 74998c2ecf20Sopenharmony_ci */ 75008c2ecf20Sopenharmony_ci supported_flags = get_supported_adv_flags(hdev); 75018c2ecf20Sopenharmony_ci phy_flags = flags & MGMT_ADV_FLAG_SEC_MASK; 75028c2ecf20Sopenharmony_ci if (flags & ~supported_flags || 75038c2ecf20Sopenharmony_ci ((phy_flags && (phy_flags ^ (phy_flags & -phy_flags))))) 75048c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING, 75058c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 75068c2ecf20Sopenharmony_ci 75078c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 75088c2ecf20Sopenharmony_ci 75098c2ecf20Sopenharmony_ci if (timeout && !hdev_is_powered(hdev)) { 75108c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING, 75118c2ecf20Sopenharmony_ci MGMT_STATUS_REJECTED); 75128c2ecf20Sopenharmony_ci goto unlock; 75138c2ecf20Sopenharmony_ci } 75148c2ecf20Sopenharmony_ci 75158c2ecf20Sopenharmony_ci if (pending_find(MGMT_OP_ADD_ADVERTISING, hdev) || 75168c2ecf20Sopenharmony_ci pending_find(MGMT_OP_REMOVE_ADVERTISING, hdev) || 75178c2ecf20Sopenharmony_ci pending_find(MGMT_OP_SET_LE, hdev)) { 75188c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING, 75198c2ecf20Sopenharmony_ci MGMT_STATUS_BUSY); 75208c2ecf20Sopenharmony_ci goto unlock; 75218c2ecf20Sopenharmony_ci } 75228c2ecf20Sopenharmony_ci 75238c2ecf20Sopenharmony_ci if (!tlv_data_is_valid(hdev, flags, cp->data, cp->adv_data_len, true) || 75248c2ecf20Sopenharmony_ci !tlv_data_is_valid(hdev, flags, cp->data + cp->adv_data_len, 75258c2ecf20Sopenharmony_ci cp->scan_rsp_len, false)) { 75268c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING, 75278c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 75288c2ecf20Sopenharmony_ci goto unlock; 75298c2ecf20Sopenharmony_ci } 75308c2ecf20Sopenharmony_ci 75318c2ecf20Sopenharmony_ci err = hci_add_adv_instance(hdev, cp->instance, flags, 75328c2ecf20Sopenharmony_ci cp->adv_data_len, cp->data, 75338c2ecf20Sopenharmony_ci cp->scan_rsp_len, 75348c2ecf20Sopenharmony_ci cp->data + cp->adv_data_len, 75358c2ecf20Sopenharmony_ci timeout, duration); 75368c2ecf20Sopenharmony_ci if (err < 0) { 75378c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING, 75388c2ecf20Sopenharmony_ci MGMT_STATUS_FAILED); 75398c2ecf20Sopenharmony_ci goto unlock; 75408c2ecf20Sopenharmony_ci } 75418c2ecf20Sopenharmony_ci 75428c2ecf20Sopenharmony_ci /* Only trigger an advertising added event if a new instance was 75438c2ecf20Sopenharmony_ci * actually added. 75448c2ecf20Sopenharmony_ci */ 75458c2ecf20Sopenharmony_ci if (hdev->adv_instance_cnt > prev_instance_cnt) 75468c2ecf20Sopenharmony_ci mgmt_advertising_added(sk, hdev, cp->instance); 75478c2ecf20Sopenharmony_ci 75488c2ecf20Sopenharmony_ci if (hdev->cur_adv_instance == cp->instance) { 75498c2ecf20Sopenharmony_ci /* If the currently advertised instance is being changed then 75508c2ecf20Sopenharmony_ci * cancel the current advertising and schedule the next 75518c2ecf20Sopenharmony_ci * instance. If there is only one instance then the overridden 75528c2ecf20Sopenharmony_ci * advertising data will be visible right away. 75538c2ecf20Sopenharmony_ci */ 75548c2ecf20Sopenharmony_ci cancel_adv_timeout(hdev); 75558c2ecf20Sopenharmony_ci 75568c2ecf20Sopenharmony_ci next_instance = hci_get_next_instance(hdev, cp->instance); 75578c2ecf20Sopenharmony_ci if (next_instance) 75588c2ecf20Sopenharmony_ci schedule_instance = next_instance->instance; 75598c2ecf20Sopenharmony_ci } else if (!hdev->adv_instance_timeout) { 75608c2ecf20Sopenharmony_ci /* Immediately advertise the new instance if no other 75618c2ecf20Sopenharmony_ci * instance is currently being advertised. 75628c2ecf20Sopenharmony_ci */ 75638c2ecf20Sopenharmony_ci schedule_instance = cp->instance; 75648c2ecf20Sopenharmony_ci } 75658c2ecf20Sopenharmony_ci 75668c2ecf20Sopenharmony_ci /* If the HCI_ADVERTISING flag is set or the device isn't powered or 75678c2ecf20Sopenharmony_ci * there is no instance to be advertised then we have no HCI 75688c2ecf20Sopenharmony_ci * communication to make. Simply return. 75698c2ecf20Sopenharmony_ci */ 75708c2ecf20Sopenharmony_ci if (!hdev_is_powered(hdev) || 75718c2ecf20Sopenharmony_ci hci_dev_test_flag(hdev, HCI_ADVERTISING) || 75728c2ecf20Sopenharmony_ci !schedule_instance) { 75738c2ecf20Sopenharmony_ci rp.instance = cp->instance; 75748c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_ADD_ADVERTISING, 75758c2ecf20Sopenharmony_ci MGMT_STATUS_SUCCESS, &rp, sizeof(rp)); 75768c2ecf20Sopenharmony_ci goto unlock; 75778c2ecf20Sopenharmony_ci } 75788c2ecf20Sopenharmony_ci 75798c2ecf20Sopenharmony_ci /* We're good to go, update advertising data, parameters, and start 75808c2ecf20Sopenharmony_ci * advertising. 75818c2ecf20Sopenharmony_ci */ 75828c2ecf20Sopenharmony_ci cmd = mgmt_pending_add(sk, MGMT_OP_ADD_ADVERTISING, hdev, data, 75838c2ecf20Sopenharmony_ci data_len); 75848c2ecf20Sopenharmony_ci if (!cmd) { 75858c2ecf20Sopenharmony_ci err = -ENOMEM; 75868c2ecf20Sopenharmony_ci goto unlock; 75878c2ecf20Sopenharmony_ci } 75888c2ecf20Sopenharmony_ci 75898c2ecf20Sopenharmony_ci hci_req_init(&req, hdev); 75908c2ecf20Sopenharmony_ci 75918c2ecf20Sopenharmony_ci err = __hci_req_schedule_adv_instance(&req, schedule_instance, true); 75928c2ecf20Sopenharmony_ci 75938c2ecf20Sopenharmony_ci if (!err) 75948c2ecf20Sopenharmony_ci err = hci_req_run(&req, add_advertising_complete); 75958c2ecf20Sopenharmony_ci 75968c2ecf20Sopenharmony_ci if (err < 0) { 75978c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING, 75988c2ecf20Sopenharmony_ci MGMT_STATUS_FAILED); 75998c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 76008c2ecf20Sopenharmony_ci } 76018c2ecf20Sopenharmony_ci 76028c2ecf20Sopenharmony_ciunlock: 76038c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 76048c2ecf20Sopenharmony_ci 76058c2ecf20Sopenharmony_ci return err; 76068c2ecf20Sopenharmony_ci} 76078c2ecf20Sopenharmony_ci 76088c2ecf20Sopenharmony_cistatic void remove_advertising_complete(struct hci_dev *hdev, u8 status, 76098c2ecf20Sopenharmony_ci u16 opcode) 76108c2ecf20Sopenharmony_ci{ 76118c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 76128c2ecf20Sopenharmony_ci struct mgmt_cp_remove_advertising *cp; 76138c2ecf20Sopenharmony_ci struct mgmt_rp_remove_advertising rp; 76148c2ecf20Sopenharmony_ci 76158c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "status %d", status); 76168c2ecf20Sopenharmony_ci 76178c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 76188c2ecf20Sopenharmony_ci 76198c2ecf20Sopenharmony_ci /* A failure status here only means that we failed to disable 76208c2ecf20Sopenharmony_ci * advertising. Otherwise, the advertising instance has been removed, 76218c2ecf20Sopenharmony_ci * so report success. 76228c2ecf20Sopenharmony_ci */ 76238c2ecf20Sopenharmony_ci cmd = pending_find(MGMT_OP_REMOVE_ADVERTISING, hdev); 76248c2ecf20Sopenharmony_ci if (!cmd) 76258c2ecf20Sopenharmony_ci goto unlock; 76268c2ecf20Sopenharmony_ci 76278c2ecf20Sopenharmony_ci cp = cmd->param; 76288c2ecf20Sopenharmony_ci rp.instance = cp->instance; 76298c2ecf20Sopenharmony_ci 76308c2ecf20Sopenharmony_ci mgmt_cmd_complete(cmd->sk, cmd->index, cmd->opcode, MGMT_STATUS_SUCCESS, 76318c2ecf20Sopenharmony_ci &rp, sizeof(rp)); 76328c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 76338c2ecf20Sopenharmony_ci 76348c2ecf20Sopenharmony_ciunlock: 76358c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 76368c2ecf20Sopenharmony_ci} 76378c2ecf20Sopenharmony_ci 76388c2ecf20Sopenharmony_cistatic int remove_advertising(struct sock *sk, struct hci_dev *hdev, 76398c2ecf20Sopenharmony_ci void *data, u16 data_len) 76408c2ecf20Sopenharmony_ci{ 76418c2ecf20Sopenharmony_ci struct mgmt_cp_remove_advertising *cp = data; 76428c2ecf20Sopenharmony_ci struct mgmt_rp_remove_advertising rp; 76438c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 76448c2ecf20Sopenharmony_ci struct hci_request req; 76458c2ecf20Sopenharmony_ci int err; 76468c2ecf20Sopenharmony_ci 76478c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 76488c2ecf20Sopenharmony_ci 76498c2ecf20Sopenharmony_ci /* Enabling the experimental LL Privay support disables support for 76508c2ecf20Sopenharmony_ci * advertising. 76518c2ecf20Sopenharmony_ci */ 76528c2ecf20Sopenharmony_ci if (hci_dev_test_flag(hdev, HCI_ENABLE_LL_PRIVACY)) 76538c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING, 76548c2ecf20Sopenharmony_ci MGMT_STATUS_NOT_SUPPORTED); 76558c2ecf20Sopenharmony_ci 76568c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 76578c2ecf20Sopenharmony_ci 76588c2ecf20Sopenharmony_ci if (cp->instance && !hci_find_adv_instance(hdev, cp->instance)) { 76598c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, 76608c2ecf20Sopenharmony_ci MGMT_OP_REMOVE_ADVERTISING, 76618c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 76628c2ecf20Sopenharmony_ci goto unlock; 76638c2ecf20Sopenharmony_ci } 76648c2ecf20Sopenharmony_ci 76658c2ecf20Sopenharmony_ci if (pending_find(MGMT_OP_ADD_ADVERTISING, hdev) || 76668c2ecf20Sopenharmony_ci pending_find(MGMT_OP_REMOVE_ADVERTISING, hdev) || 76678c2ecf20Sopenharmony_ci pending_find(MGMT_OP_SET_LE, hdev)) { 76688c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_REMOVE_ADVERTISING, 76698c2ecf20Sopenharmony_ci MGMT_STATUS_BUSY); 76708c2ecf20Sopenharmony_ci goto unlock; 76718c2ecf20Sopenharmony_ci } 76728c2ecf20Sopenharmony_ci 76738c2ecf20Sopenharmony_ci if (list_empty(&hdev->adv_instances)) { 76748c2ecf20Sopenharmony_ci err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_REMOVE_ADVERTISING, 76758c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 76768c2ecf20Sopenharmony_ci goto unlock; 76778c2ecf20Sopenharmony_ci } 76788c2ecf20Sopenharmony_ci 76798c2ecf20Sopenharmony_ci hci_req_init(&req, hdev); 76808c2ecf20Sopenharmony_ci 76818c2ecf20Sopenharmony_ci /* If we use extended advertising, instance is disabled and removed */ 76828c2ecf20Sopenharmony_ci if (ext_adv_capable(hdev)) { 76838c2ecf20Sopenharmony_ci __hci_req_disable_ext_adv_instance(&req, cp->instance); 76848c2ecf20Sopenharmony_ci __hci_req_remove_ext_adv_instance(&req, cp->instance); 76858c2ecf20Sopenharmony_ci } 76868c2ecf20Sopenharmony_ci 76878c2ecf20Sopenharmony_ci hci_req_clear_adv_instance(hdev, sk, &req, cp->instance, true); 76888c2ecf20Sopenharmony_ci 76898c2ecf20Sopenharmony_ci if (list_empty(&hdev->adv_instances)) 76908c2ecf20Sopenharmony_ci __hci_req_disable_advertising(&req); 76918c2ecf20Sopenharmony_ci 76928c2ecf20Sopenharmony_ci /* If no HCI commands have been collected so far or the HCI_ADVERTISING 76938c2ecf20Sopenharmony_ci * flag is set or the device isn't powered then we have no HCI 76948c2ecf20Sopenharmony_ci * communication to make. Simply return. 76958c2ecf20Sopenharmony_ci */ 76968c2ecf20Sopenharmony_ci if (skb_queue_empty(&req.cmd_q) || 76978c2ecf20Sopenharmony_ci !hdev_is_powered(hdev) || 76988c2ecf20Sopenharmony_ci hci_dev_test_flag(hdev, HCI_ADVERTISING)) { 76998c2ecf20Sopenharmony_ci hci_req_purge(&req); 77008c2ecf20Sopenharmony_ci rp.instance = cp->instance; 77018c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, 77028c2ecf20Sopenharmony_ci MGMT_OP_REMOVE_ADVERTISING, 77038c2ecf20Sopenharmony_ci MGMT_STATUS_SUCCESS, &rp, sizeof(rp)); 77048c2ecf20Sopenharmony_ci goto unlock; 77058c2ecf20Sopenharmony_ci } 77068c2ecf20Sopenharmony_ci 77078c2ecf20Sopenharmony_ci cmd = mgmt_pending_add(sk, MGMT_OP_REMOVE_ADVERTISING, hdev, data, 77088c2ecf20Sopenharmony_ci data_len); 77098c2ecf20Sopenharmony_ci if (!cmd) { 77108c2ecf20Sopenharmony_ci err = -ENOMEM; 77118c2ecf20Sopenharmony_ci goto unlock; 77128c2ecf20Sopenharmony_ci } 77138c2ecf20Sopenharmony_ci 77148c2ecf20Sopenharmony_ci err = hci_req_run(&req, remove_advertising_complete); 77158c2ecf20Sopenharmony_ci if (err < 0) 77168c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 77178c2ecf20Sopenharmony_ci 77188c2ecf20Sopenharmony_ciunlock: 77198c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 77208c2ecf20Sopenharmony_ci 77218c2ecf20Sopenharmony_ci return err; 77228c2ecf20Sopenharmony_ci} 77238c2ecf20Sopenharmony_ci 77248c2ecf20Sopenharmony_cistatic int get_adv_size_info(struct sock *sk, struct hci_dev *hdev, 77258c2ecf20Sopenharmony_ci void *data, u16 data_len) 77268c2ecf20Sopenharmony_ci{ 77278c2ecf20Sopenharmony_ci struct mgmt_cp_get_adv_size_info *cp = data; 77288c2ecf20Sopenharmony_ci struct mgmt_rp_get_adv_size_info rp; 77298c2ecf20Sopenharmony_ci u32 flags, supported_flags; 77308c2ecf20Sopenharmony_ci int err; 77318c2ecf20Sopenharmony_ci 77328c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "sock %p", sk); 77338c2ecf20Sopenharmony_ci 77348c2ecf20Sopenharmony_ci if (!lmp_le_capable(hdev)) 77358c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_GET_ADV_SIZE_INFO, 77368c2ecf20Sopenharmony_ci MGMT_STATUS_REJECTED); 77378c2ecf20Sopenharmony_ci 77388c2ecf20Sopenharmony_ci if (cp->instance < 1 || cp->instance > hdev->le_num_of_adv_sets) 77398c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_GET_ADV_SIZE_INFO, 77408c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 77418c2ecf20Sopenharmony_ci 77428c2ecf20Sopenharmony_ci flags = __le32_to_cpu(cp->flags); 77438c2ecf20Sopenharmony_ci 77448c2ecf20Sopenharmony_ci /* The current implementation only supports a subset of the specified 77458c2ecf20Sopenharmony_ci * flags. 77468c2ecf20Sopenharmony_ci */ 77478c2ecf20Sopenharmony_ci supported_flags = get_supported_adv_flags(hdev); 77488c2ecf20Sopenharmony_ci if (flags & ~supported_flags) 77498c2ecf20Sopenharmony_ci return mgmt_cmd_status(sk, hdev->id, MGMT_OP_GET_ADV_SIZE_INFO, 77508c2ecf20Sopenharmony_ci MGMT_STATUS_INVALID_PARAMS); 77518c2ecf20Sopenharmony_ci 77528c2ecf20Sopenharmony_ci rp.instance = cp->instance; 77538c2ecf20Sopenharmony_ci rp.flags = cp->flags; 77548c2ecf20Sopenharmony_ci rp.max_adv_data_len = tlv_data_max_len(hdev, flags, true); 77558c2ecf20Sopenharmony_ci rp.max_scan_rsp_len = tlv_data_max_len(hdev, flags, false); 77568c2ecf20Sopenharmony_ci 77578c2ecf20Sopenharmony_ci err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_GET_ADV_SIZE_INFO, 77588c2ecf20Sopenharmony_ci MGMT_STATUS_SUCCESS, &rp, sizeof(rp)); 77598c2ecf20Sopenharmony_ci 77608c2ecf20Sopenharmony_ci return err; 77618c2ecf20Sopenharmony_ci} 77628c2ecf20Sopenharmony_ci 77638c2ecf20Sopenharmony_cistatic const struct hci_mgmt_handler mgmt_handlers[] = { 77648c2ecf20Sopenharmony_ci { NULL }, /* 0x0000 (no command) */ 77658c2ecf20Sopenharmony_ci { read_version, MGMT_READ_VERSION_SIZE, 77668c2ecf20Sopenharmony_ci HCI_MGMT_NO_HDEV | 77678c2ecf20Sopenharmony_ci HCI_MGMT_UNTRUSTED }, 77688c2ecf20Sopenharmony_ci { read_commands, MGMT_READ_COMMANDS_SIZE, 77698c2ecf20Sopenharmony_ci HCI_MGMT_NO_HDEV | 77708c2ecf20Sopenharmony_ci HCI_MGMT_UNTRUSTED }, 77718c2ecf20Sopenharmony_ci { read_index_list, MGMT_READ_INDEX_LIST_SIZE, 77728c2ecf20Sopenharmony_ci HCI_MGMT_NO_HDEV | 77738c2ecf20Sopenharmony_ci HCI_MGMT_UNTRUSTED }, 77748c2ecf20Sopenharmony_ci { read_controller_info, MGMT_READ_INFO_SIZE, 77758c2ecf20Sopenharmony_ci HCI_MGMT_UNTRUSTED }, 77768c2ecf20Sopenharmony_ci { set_powered, MGMT_SETTING_SIZE }, 77778c2ecf20Sopenharmony_ci { set_discoverable, MGMT_SET_DISCOVERABLE_SIZE }, 77788c2ecf20Sopenharmony_ci { set_connectable, MGMT_SETTING_SIZE }, 77798c2ecf20Sopenharmony_ci { set_fast_connectable, MGMT_SETTING_SIZE }, 77808c2ecf20Sopenharmony_ci { set_bondable, MGMT_SETTING_SIZE }, 77818c2ecf20Sopenharmony_ci { set_link_security, MGMT_SETTING_SIZE }, 77828c2ecf20Sopenharmony_ci { set_ssp, MGMT_SETTING_SIZE }, 77838c2ecf20Sopenharmony_ci { set_hs, MGMT_SETTING_SIZE }, 77848c2ecf20Sopenharmony_ci { set_le, MGMT_SETTING_SIZE }, 77858c2ecf20Sopenharmony_ci { set_dev_class, MGMT_SET_DEV_CLASS_SIZE }, 77868c2ecf20Sopenharmony_ci { set_local_name, MGMT_SET_LOCAL_NAME_SIZE }, 77878c2ecf20Sopenharmony_ci { add_uuid, MGMT_ADD_UUID_SIZE }, 77888c2ecf20Sopenharmony_ci { remove_uuid, MGMT_REMOVE_UUID_SIZE }, 77898c2ecf20Sopenharmony_ci { load_link_keys, MGMT_LOAD_LINK_KEYS_SIZE, 77908c2ecf20Sopenharmony_ci HCI_MGMT_VAR_LEN }, 77918c2ecf20Sopenharmony_ci { load_long_term_keys, MGMT_LOAD_LONG_TERM_KEYS_SIZE, 77928c2ecf20Sopenharmony_ci HCI_MGMT_VAR_LEN }, 77938c2ecf20Sopenharmony_ci { disconnect, MGMT_DISCONNECT_SIZE }, 77948c2ecf20Sopenharmony_ci { get_connections, MGMT_GET_CONNECTIONS_SIZE }, 77958c2ecf20Sopenharmony_ci { pin_code_reply, MGMT_PIN_CODE_REPLY_SIZE }, 77968c2ecf20Sopenharmony_ci { pin_code_neg_reply, MGMT_PIN_CODE_NEG_REPLY_SIZE }, 77978c2ecf20Sopenharmony_ci { set_io_capability, MGMT_SET_IO_CAPABILITY_SIZE }, 77988c2ecf20Sopenharmony_ci { pair_device, MGMT_PAIR_DEVICE_SIZE }, 77998c2ecf20Sopenharmony_ci { cancel_pair_device, MGMT_CANCEL_PAIR_DEVICE_SIZE }, 78008c2ecf20Sopenharmony_ci { unpair_device, MGMT_UNPAIR_DEVICE_SIZE }, 78018c2ecf20Sopenharmony_ci { user_confirm_reply, MGMT_USER_CONFIRM_REPLY_SIZE }, 78028c2ecf20Sopenharmony_ci { user_confirm_neg_reply, MGMT_USER_CONFIRM_NEG_REPLY_SIZE }, 78038c2ecf20Sopenharmony_ci { user_passkey_reply, MGMT_USER_PASSKEY_REPLY_SIZE }, 78048c2ecf20Sopenharmony_ci { user_passkey_neg_reply, MGMT_USER_PASSKEY_NEG_REPLY_SIZE }, 78058c2ecf20Sopenharmony_ci { read_local_oob_data, MGMT_READ_LOCAL_OOB_DATA_SIZE }, 78068c2ecf20Sopenharmony_ci { add_remote_oob_data, MGMT_ADD_REMOTE_OOB_DATA_SIZE, 78078c2ecf20Sopenharmony_ci HCI_MGMT_VAR_LEN }, 78088c2ecf20Sopenharmony_ci { remove_remote_oob_data, MGMT_REMOVE_REMOTE_OOB_DATA_SIZE }, 78098c2ecf20Sopenharmony_ci { start_discovery, MGMT_START_DISCOVERY_SIZE }, 78108c2ecf20Sopenharmony_ci { stop_discovery, MGMT_STOP_DISCOVERY_SIZE }, 78118c2ecf20Sopenharmony_ci { confirm_name, MGMT_CONFIRM_NAME_SIZE }, 78128c2ecf20Sopenharmony_ci { block_device, MGMT_BLOCK_DEVICE_SIZE }, 78138c2ecf20Sopenharmony_ci { unblock_device, MGMT_UNBLOCK_DEVICE_SIZE }, 78148c2ecf20Sopenharmony_ci { set_device_id, MGMT_SET_DEVICE_ID_SIZE }, 78158c2ecf20Sopenharmony_ci { set_advertising, MGMT_SETTING_SIZE }, 78168c2ecf20Sopenharmony_ci { set_bredr, MGMT_SETTING_SIZE }, 78178c2ecf20Sopenharmony_ci { set_static_address, MGMT_SET_STATIC_ADDRESS_SIZE }, 78188c2ecf20Sopenharmony_ci { set_scan_params, MGMT_SET_SCAN_PARAMS_SIZE }, 78198c2ecf20Sopenharmony_ci { set_secure_conn, MGMT_SETTING_SIZE }, 78208c2ecf20Sopenharmony_ci { set_debug_keys, MGMT_SETTING_SIZE }, 78218c2ecf20Sopenharmony_ci { set_privacy, MGMT_SET_PRIVACY_SIZE }, 78228c2ecf20Sopenharmony_ci { load_irks, MGMT_LOAD_IRKS_SIZE, 78238c2ecf20Sopenharmony_ci HCI_MGMT_VAR_LEN }, 78248c2ecf20Sopenharmony_ci { get_conn_info, MGMT_GET_CONN_INFO_SIZE }, 78258c2ecf20Sopenharmony_ci { get_clock_info, MGMT_GET_CLOCK_INFO_SIZE }, 78268c2ecf20Sopenharmony_ci { add_device, MGMT_ADD_DEVICE_SIZE }, 78278c2ecf20Sopenharmony_ci { remove_device, MGMT_REMOVE_DEVICE_SIZE }, 78288c2ecf20Sopenharmony_ci { load_conn_param, MGMT_LOAD_CONN_PARAM_SIZE, 78298c2ecf20Sopenharmony_ci HCI_MGMT_VAR_LEN }, 78308c2ecf20Sopenharmony_ci { read_unconf_index_list, MGMT_READ_UNCONF_INDEX_LIST_SIZE, 78318c2ecf20Sopenharmony_ci HCI_MGMT_NO_HDEV | 78328c2ecf20Sopenharmony_ci HCI_MGMT_UNTRUSTED }, 78338c2ecf20Sopenharmony_ci { read_config_info, MGMT_READ_CONFIG_INFO_SIZE, 78348c2ecf20Sopenharmony_ci HCI_MGMT_UNCONFIGURED | 78358c2ecf20Sopenharmony_ci HCI_MGMT_UNTRUSTED }, 78368c2ecf20Sopenharmony_ci { set_external_config, MGMT_SET_EXTERNAL_CONFIG_SIZE, 78378c2ecf20Sopenharmony_ci HCI_MGMT_UNCONFIGURED }, 78388c2ecf20Sopenharmony_ci { set_public_address, MGMT_SET_PUBLIC_ADDRESS_SIZE, 78398c2ecf20Sopenharmony_ci HCI_MGMT_UNCONFIGURED }, 78408c2ecf20Sopenharmony_ci { start_service_discovery, MGMT_START_SERVICE_DISCOVERY_SIZE, 78418c2ecf20Sopenharmony_ci HCI_MGMT_VAR_LEN }, 78428c2ecf20Sopenharmony_ci { read_local_oob_ext_data, MGMT_READ_LOCAL_OOB_EXT_DATA_SIZE }, 78438c2ecf20Sopenharmony_ci { read_ext_index_list, MGMT_READ_EXT_INDEX_LIST_SIZE, 78448c2ecf20Sopenharmony_ci HCI_MGMT_NO_HDEV | 78458c2ecf20Sopenharmony_ci HCI_MGMT_UNTRUSTED }, 78468c2ecf20Sopenharmony_ci { read_adv_features, MGMT_READ_ADV_FEATURES_SIZE }, 78478c2ecf20Sopenharmony_ci { add_advertising, MGMT_ADD_ADVERTISING_SIZE, 78488c2ecf20Sopenharmony_ci HCI_MGMT_VAR_LEN }, 78498c2ecf20Sopenharmony_ci { remove_advertising, MGMT_REMOVE_ADVERTISING_SIZE }, 78508c2ecf20Sopenharmony_ci { get_adv_size_info, MGMT_GET_ADV_SIZE_INFO_SIZE }, 78518c2ecf20Sopenharmony_ci { start_limited_discovery, MGMT_START_DISCOVERY_SIZE }, 78528c2ecf20Sopenharmony_ci { read_ext_controller_info,MGMT_READ_EXT_INFO_SIZE, 78538c2ecf20Sopenharmony_ci HCI_MGMT_UNTRUSTED }, 78548c2ecf20Sopenharmony_ci { set_appearance, MGMT_SET_APPEARANCE_SIZE }, 78558c2ecf20Sopenharmony_ci { get_phy_configuration, MGMT_GET_PHY_CONFIGURATION_SIZE }, 78568c2ecf20Sopenharmony_ci { set_phy_configuration, MGMT_SET_PHY_CONFIGURATION_SIZE }, 78578c2ecf20Sopenharmony_ci { set_blocked_keys, MGMT_OP_SET_BLOCKED_KEYS_SIZE, 78588c2ecf20Sopenharmony_ci HCI_MGMT_VAR_LEN }, 78598c2ecf20Sopenharmony_ci { set_wideband_speech, MGMT_SETTING_SIZE }, 78608c2ecf20Sopenharmony_ci { read_security_info, MGMT_READ_SECURITY_INFO_SIZE, 78618c2ecf20Sopenharmony_ci HCI_MGMT_UNTRUSTED }, 78628c2ecf20Sopenharmony_ci { read_exp_features_info, MGMT_READ_EXP_FEATURES_INFO_SIZE, 78638c2ecf20Sopenharmony_ci HCI_MGMT_UNTRUSTED | 78648c2ecf20Sopenharmony_ci HCI_MGMT_HDEV_OPTIONAL }, 78658c2ecf20Sopenharmony_ci { set_exp_feature, MGMT_SET_EXP_FEATURE_SIZE, 78668c2ecf20Sopenharmony_ci HCI_MGMT_VAR_LEN | 78678c2ecf20Sopenharmony_ci HCI_MGMT_HDEV_OPTIONAL }, 78688c2ecf20Sopenharmony_ci { read_def_system_config, MGMT_READ_DEF_SYSTEM_CONFIG_SIZE, 78698c2ecf20Sopenharmony_ci HCI_MGMT_UNTRUSTED }, 78708c2ecf20Sopenharmony_ci { set_def_system_config, MGMT_SET_DEF_SYSTEM_CONFIG_SIZE, 78718c2ecf20Sopenharmony_ci HCI_MGMT_VAR_LEN }, 78728c2ecf20Sopenharmony_ci { read_def_runtime_config, MGMT_READ_DEF_RUNTIME_CONFIG_SIZE, 78738c2ecf20Sopenharmony_ci HCI_MGMT_UNTRUSTED }, 78748c2ecf20Sopenharmony_ci { set_def_runtime_config, MGMT_SET_DEF_RUNTIME_CONFIG_SIZE, 78758c2ecf20Sopenharmony_ci HCI_MGMT_VAR_LEN }, 78768c2ecf20Sopenharmony_ci { get_device_flags, MGMT_GET_DEVICE_FLAGS_SIZE }, 78778c2ecf20Sopenharmony_ci { set_device_flags, MGMT_SET_DEVICE_FLAGS_SIZE }, 78788c2ecf20Sopenharmony_ci { read_adv_mon_features, MGMT_READ_ADV_MONITOR_FEATURES_SIZE }, 78798c2ecf20Sopenharmony_ci { add_adv_patterns_monitor,MGMT_ADD_ADV_PATTERNS_MONITOR_SIZE, 78808c2ecf20Sopenharmony_ci HCI_MGMT_VAR_LEN }, 78818c2ecf20Sopenharmony_ci { remove_adv_monitor, MGMT_REMOVE_ADV_MONITOR_SIZE }, 78828c2ecf20Sopenharmony_ci}; 78838c2ecf20Sopenharmony_ci 78848c2ecf20Sopenharmony_civoid mgmt_index_added(struct hci_dev *hdev) 78858c2ecf20Sopenharmony_ci{ 78868c2ecf20Sopenharmony_ci struct mgmt_ev_ext_index ev; 78878c2ecf20Sopenharmony_ci 78888c2ecf20Sopenharmony_ci if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) 78898c2ecf20Sopenharmony_ci return; 78908c2ecf20Sopenharmony_ci 78918c2ecf20Sopenharmony_ci switch (hdev->dev_type) { 78928c2ecf20Sopenharmony_ci case HCI_PRIMARY: 78938c2ecf20Sopenharmony_ci if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) { 78948c2ecf20Sopenharmony_ci mgmt_index_event(MGMT_EV_UNCONF_INDEX_ADDED, hdev, 78958c2ecf20Sopenharmony_ci NULL, 0, HCI_MGMT_UNCONF_INDEX_EVENTS); 78968c2ecf20Sopenharmony_ci ev.type = 0x01; 78978c2ecf20Sopenharmony_ci } else { 78988c2ecf20Sopenharmony_ci mgmt_index_event(MGMT_EV_INDEX_ADDED, hdev, NULL, 0, 78998c2ecf20Sopenharmony_ci HCI_MGMT_INDEX_EVENTS); 79008c2ecf20Sopenharmony_ci ev.type = 0x00; 79018c2ecf20Sopenharmony_ci } 79028c2ecf20Sopenharmony_ci break; 79038c2ecf20Sopenharmony_ci case HCI_AMP: 79048c2ecf20Sopenharmony_ci ev.type = 0x02; 79058c2ecf20Sopenharmony_ci break; 79068c2ecf20Sopenharmony_ci default: 79078c2ecf20Sopenharmony_ci return; 79088c2ecf20Sopenharmony_ci } 79098c2ecf20Sopenharmony_ci 79108c2ecf20Sopenharmony_ci ev.bus = hdev->bus; 79118c2ecf20Sopenharmony_ci 79128c2ecf20Sopenharmony_ci mgmt_index_event(MGMT_EV_EXT_INDEX_ADDED, hdev, &ev, sizeof(ev), 79138c2ecf20Sopenharmony_ci HCI_MGMT_EXT_INDEX_EVENTS); 79148c2ecf20Sopenharmony_ci} 79158c2ecf20Sopenharmony_ci 79168c2ecf20Sopenharmony_civoid mgmt_index_removed(struct hci_dev *hdev) 79178c2ecf20Sopenharmony_ci{ 79188c2ecf20Sopenharmony_ci struct mgmt_ev_ext_index ev; 79198c2ecf20Sopenharmony_ci u8 status = MGMT_STATUS_INVALID_INDEX; 79208c2ecf20Sopenharmony_ci 79218c2ecf20Sopenharmony_ci if (test_bit(HCI_QUIRK_RAW_DEVICE, &hdev->quirks)) 79228c2ecf20Sopenharmony_ci return; 79238c2ecf20Sopenharmony_ci 79248c2ecf20Sopenharmony_ci switch (hdev->dev_type) { 79258c2ecf20Sopenharmony_ci case HCI_PRIMARY: 79268c2ecf20Sopenharmony_ci mgmt_pending_foreach(0, hdev, cmd_complete_rsp, &status); 79278c2ecf20Sopenharmony_ci 79288c2ecf20Sopenharmony_ci if (hci_dev_test_flag(hdev, HCI_UNCONFIGURED)) { 79298c2ecf20Sopenharmony_ci mgmt_index_event(MGMT_EV_UNCONF_INDEX_REMOVED, hdev, 79308c2ecf20Sopenharmony_ci NULL, 0, HCI_MGMT_UNCONF_INDEX_EVENTS); 79318c2ecf20Sopenharmony_ci ev.type = 0x01; 79328c2ecf20Sopenharmony_ci } else { 79338c2ecf20Sopenharmony_ci mgmt_index_event(MGMT_EV_INDEX_REMOVED, hdev, NULL, 0, 79348c2ecf20Sopenharmony_ci HCI_MGMT_INDEX_EVENTS); 79358c2ecf20Sopenharmony_ci ev.type = 0x00; 79368c2ecf20Sopenharmony_ci } 79378c2ecf20Sopenharmony_ci break; 79388c2ecf20Sopenharmony_ci case HCI_AMP: 79398c2ecf20Sopenharmony_ci ev.type = 0x02; 79408c2ecf20Sopenharmony_ci break; 79418c2ecf20Sopenharmony_ci default: 79428c2ecf20Sopenharmony_ci return; 79438c2ecf20Sopenharmony_ci } 79448c2ecf20Sopenharmony_ci 79458c2ecf20Sopenharmony_ci ev.bus = hdev->bus; 79468c2ecf20Sopenharmony_ci 79478c2ecf20Sopenharmony_ci mgmt_index_event(MGMT_EV_EXT_INDEX_REMOVED, hdev, &ev, sizeof(ev), 79488c2ecf20Sopenharmony_ci HCI_MGMT_EXT_INDEX_EVENTS); 79498c2ecf20Sopenharmony_ci} 79508c2ecf20Sopenharmony_ci 79518c2ecf20Sopenharmony_ci/* This function requires the caller holds hdev->lock */ 79528c2ecf20Sopenharmony_cistatic void restart_le_actions(struct hci_dev *hdev) 79538c2ecf20Sopenharmony_ci{ 79548c2ecf20Sopenharmony_ci struct hci_conn_params *p; 79558c2ecf20Sopenharmony_ci 79568c2ecf20Sopenharmony_ci list_for_each_entry(p, &hdev->le_conn_params, list) { 79578c2ecf20Sopenharmony_ci /* Needed for AUTO_OFF case where might not "really" 79588c2ecf20Sopenharmony_ci * have been powered off. 79598c2ecf20Sopenharmony_ci */ 79608c2ecf20Sopenharmony_ci list_del_init(&p->action); 79618c2ecf20Sopenharmony_ci 79628c2ecf20Sopenharmony_ci switch (p->auto_connect) { 79638c2ecf20Sopenharmony_ci case HCI_AUTO_CONN_DIRECT: 79648c2ecf20Sopenharmony_ci case HCI_AUTO_CONN_ALWAYS: 79658c2ecf20Sopenharmony_ci list_add(&p->action, &hdev->pend_le_conns); 79668c2ecf20Sopenharmony_ci break; 79678c2ecf20Sopenharmony_ci case HCI_AUTO_CONN_REPORT: 79688c2ecf20Sopenharmony_ci list_add(&p->action, &hdev->pend_le_reports); 79698c2ecf20Sopenharmony_ci break; 79708c2ecf20Sopenharmony_ci default: 79718c2ecf20Sopenharmony_ci break; 79728c2ecf20Sopenharmony_ci } 79738c2ecf20Sopenharmony_ci } 79748c2ecf20Sopenharmony_ci} 79758c2ecf20Sopenharmony_ci 79768c2ecf20Sopenharmony_civoid mgmt_power_on(struct hci_dev *hdev, int err) 79778c2ecf20Sopenharmony_ci{ 79788c2ecf20Sopenharmony_ci struct cmd_lookup match = { NULL, hdev }; 79798c2ecf20Sopenharmony_ci 79808c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "err %d", err); 79818c2ecf20Sopenharmony_ci 79828c2ecf20Sopenharmony_ci hci_dev_lock(hdev); 79838c2ecf20Sopenharmony_ci 79848c2ecf20Sopenharmony_ci if (!err) { 79858c2ecf20Sopenharmony_ci restart_le_actions(hdev); 79868c2ecf20Sopenharmony_ci hci_update_background_scan(hdev); 79878c2ecf20Sopenharmony_ci } 79888c2ecf20Sopenharmony_ci 79898c2ecf20Sopenharmony_ci mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match); 79908c2ecf20Sopenharmony_ci 79918c2ecf20Sopenharmony_ci new_settings(hdev, match.sk); 79928c2ecf20Sopenharmony_ci 79938c2ecf20Sopenharmony_ci if (match.sk) 79948c2ecf20Sopenharmony_ci sock_put(match.sk); 79958c2ecf20Sopenharmony_ci 79968c2ecf20Sopenharmony_ci hci_dev_unlock(hdev); 79978c2ecf20Sopenharmony_ci} 79988c2ecf20Sopenharmony_ci 79998c2ecf20Sopenharmony_civoid __mgmt_power_off(struct hci_dev *hdev) 80008c2ecf20Sopenharmony_ci{ 80018c2ecf20Sopenharmony_ci struct cmd_lookup match = { NULL, hdev }; 80028c2ecf20Sopenharmony_ci u8 status, zero_cod[] = { 0, 0, 0 }; 80038c2ecf20Sopenharmony_ci 80048c2ecf20Sopenharmony_ci mgmt_pending_foreach(MGMT_OP_SET_POWERED, hdev, settings_rsp, &match); 80058c2ecf20Sopenharmony_ci 80068c2ecf20Sopenharmony_ci /* If the power off is because of hdev unregistration let 80078c2ecf20Sopenharmony_ci * use the appropriate INVALID_INDEX status. Otherwise use 80088c2ecf20Sopenharmony_ci * NOT_POWERED. We cover both scenarios here since later in 80098c2ecf20Sopenharmony_ci * mgmt_index_removed() any hci_conn callbacks will have already 80108c2ecf20Sopenharmony_ci * been triggered, potentially causing misleading DISCONNECTED 80118c2ecf20Sopenharmony_ci * status responses. 80128c2ecf20Sopenharmony_ci */ 80138c2ecf20Sopenharmony_ci if (hci_dev_test_flag(hdev, HCI_UNREGISTER)) 80148c2ecf20Sopenharmony_ci status = MGMT_STATUS_INVALID_INDEX; 80158c2ecf20Sopenharmony_ci else 80168c2ecf20Sopenharmony_ci status = MGMT_STATUS_NOT_POWERED; 80178c2ecf20Sopenharmony_ci 80188c2ecf20Sopenharmony_ci mgmt_pending_foreach(0, hdev, cmd_complete_rsp, &status); 80198c2ecf20Sopenharmony_ci 80208c2ecf20Sopenharmony_ci if (memcmp(hdev->dev_class, zero_cod, sizeof(zero_cod)) != 0) { 80218c2ecf20Sopenharmony_ci mgmt_limited_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev, 80228c2ecf20Sopenharmony_ci zero_cod, sizeof(zero_cod), 80238c2ecf20Sopenharmony_ci HCI_MGMT_DEV_CLASS_EVENTS, NULL); 80248c2ecf20Sopenharmony_ci ext_info_changed(hdev, NULL); 80258c2ecf20Sopenharmony_ci } 80268c2ecf20Sopenharmony_ci 80278c2ecf20Sopenharmony_ci new_settings(hdev, match.sk); 80288c2ecf20Sopenharmony_ci 80298c2ecf20Sopenharmony_ci if (match.sk) 80308c2ecf20Sopenharmony_ci sock_put(match.sk); 80318c2ecf20Sopenharmony_ci} 80328c2ecf20Sopenharmony_ci 80338c2ecf20Sopenharmony_civoid mgmt_set_powered_failed(struct hci_dev *hdev, int err) 80348c2ecf20Sopenharmony_ci{ 80358c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 80368c2ecf20Sopenharmony_ci u8 status; 80378c2ecf20Sopenharmony_ci 80388c2ecf20Sopenharmony_ci cmd = pending_find(MGMT_OP_SET_POWERED, hdev); 80398c2ecf20Sopenharmony_ci if (!cmd) 80408c2ecf20Sopenharmony_ci return; 80418c2ecf20Sopenharmony_ci 80428c2ecf20Sopenharmony_ci if (err == -ERFKILL) 80438c2ecf20Sopenharmony_ci status = MGMT_STATUS_RFKILLED; 80448c2ecf20Sopenharmony_ci else 80458c2ecf20Sopenharmony_ci status = MGMT_STATUS_FAILED; 80468c2ecf20Sopenharmony_ci 80478c2ecf20Sopenharmony_ci mgmt_cmd_status(cmd->sk, hdev->id, MGMT_OP_SET_POWERED, status); 80488c2ecf20Sopenharmony_ci 80498c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 80508c2ecf20Sopenharmony_ci} 80518c2ecf20Sopenharmony_ci 80528c2ecf20Sopenharmony_civoid mgmt_new_link_key(struct hci_dev *hdev, struct link_key *key, 80538c2ecf20Sopenharmony_ci bool persistent) 80548c2ecf20Sopenharmony_ci{ 80558c2ecf20Sopenharmony_ci struct mgmt_ev_new_link_key ev; 80568c2ecf20Sopenharmony_ci 80578c2ecf20Sopenharmony_ci memset(&ev, 0, sizeof(ev)); 80588c2ecf20Sopenharmony_ci 80598c2ecf20Sopenharmony_ci ev.store_hint = persistent; 80608c2ecf20Sopenharmony_ci bacpy(&ev.key.addr.bdaddr, &key->bdaddr); 80618c2ecf20Sopenharmony_ci ev.key.addr.type = link_to_bdaddr(key->link_type, key->bdaddr_type); 80628c2ecf20Sopenharmony_ci ev.key.type = key->type; 80638c2ecf20Sopenharmony_ci memcpy(ev.key.val, key->val, HCI_LINK_KEY_SIZE); 80648c2ecf20Sopenharmony_ci ev.key.pin_len = key->pin_len; 80658c2ecf20Sopenharmony_ci 80668c2ecf20Sopenharmony_ci mgmt_event(MGMT_EV_NEW_LINK_KEY, hdev, &ev, sizeof(ev), NULL); 80678c2ecf20Sopenharmony_ci} 80688c2ecf20Sopenharmony_ci 80698c2ecf20Sopenharmony_cistatic u8 mgmt_ltk_type(struct smp_ltk *ltk) 80708c2ecf20Sopenharmony_ci{ 80718c2ecf20Sopenharmony_ci switch (ltk->type) { 80728c2ecf20Sopenharmony_ci case SMP_LTK: 80738c2ecf20Sopenharmony_ci case SMP_LTK_RESPONDER: 80748c2ecf20Sopenharmony_ci if (ltk->authenticated) 80758c2ecf20Sopenharmony_ci return MGMT_LTK_AUTHENTICATED; 80768c2ecf20Sopenharmony_ci return MGMT_LTK_UNAUTHENTICATED; 80778c2ecf20Sopenharmony_ci case SMP_LTK_P256: 80788c2ecf20Sopenharmony_ci if (ltk->authenticated) 80798c2ecf20Sopenharmony_ci return MGMT_LTK_P256_AUTH; 80808c2ecf20Sopenharmony_ci return MGMT_LTK_P256_UNAUTH; 80818c2ecf20Sopenharmony_ci case SMP_LTK_P256_DEBUG: 80828c2ecf20Sopenharmony_ci return MGMT_LTK_P256_DEBUG; 80838c2ecf20Sopenharmony_ci } 80848c2ecf20Sopenharmony_ci 80858c2ecf20Sopenharmony_ci return MGMT_LTK_UNAUTHENTICATED; 80868c2ecf20Sopenharmony_ci} 80878c2ecf20Sopenharmony_ci 80888c2ecf20Sopenharmony_civoid mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, bool persistent) 80898c2ecf20Sopenharmony_ci{ 80908c2ecf20Sopenharmony_ci struct mgmt_ev_new_long_term_key ev; 80918c2ecf20Sopenharmony_ci 80928c2ecf20Sopenharmony_ci memset(&ev, 0, sizeof(ev)); 80938c2ecf20Sopenharmony_ci 80948c2ecf20Sopenharmony_ci /* Devices using resolvable or non-resolvable random addresses 80958c2ecf20Sopenharmony_ci * without providing an identity resolving key don't require 80968c2ecf20Sopenharmony_ci * to store long term keys. Their addresses will change the 80978c2ecf20Sopenharmony_ci * next time around. 80988c2ecf20Sopenharmony_ci * 80998c2ecf20Sopenharmony_ci * Only when a remote device provides an identity address 81008c2ecf20Sopenharmony_ci * make sure the long term key is stored. If the remote 81018c2ecf20Sopenharmony_ci * identity is known, the long term keys are internally 81028c2ecf20Sopenharmony_ci * mapped to the identity address. So allow static random 81038c2ecf20Sopenharmony_ci * and public addresses here. 81048c2ecf20Sopenharmony_ci */ 81058c2ecf20Sopenharmony_ci if (key->bdaddr_type == ADDR_LE_DEV_RANDOM && 81068c2ecf20Sopenharmony_ci (key->bdaddr.b[5] & 0xc0) != 0xc0) 81078c2ecf20Sopenharmony_ci ev.store_hint = 0x00; 81088c2ecf20Sopenharmony_ci else 81098c2ecf20Sopenharmony_ci ev.store_hint = persistent; 81108c2ecf20Sopenharmony_ci 81118c2ecf20Sopenharmony_ci bacpy(&ev.key.addr.bdaddr, &key->bdaddr); 81128c2ecf20Sopenharmony_ci ev.key.addr.type = link_to_bdaddr(key->link_type, key->bdaddr_type); 81138c2ecf20Sopenharmony_ci ev.key.type = mgmt_ltk_type(key); 81148c2ecf20Sopenharmony_ci ev.key.enc_size = key->enc_size; 81158c2ecf20Sopenharmony_ci ev.key.ediv = key->ediv; 81168c2ecf20Sopenharmony_ci ev.key.rand = key->rand; 81178c2ecf20Sopenharmony_ci 81188c2ecf20Sopenharmony_ci if (key->type == SMP_LTK) 81198c2ecf20Sopenharmony_ci ev.key.initiator = 1; 81208c2ecf20Sopenharmony_ci 81218c2ecf20Sopenharmony_ci /* Make sure we copy only the significant bytes based on the 81228c2ecf20Sopenharmony_ci * encryption key size, and set the rest of the value to zeroes. 81238c2ecf20Sopenharmony_ci */ 81248c2ecf20Sopenharmony_ci memcpy(ev.key.val, key->val, key->enc_size); 81258c2ecf20Sopenharmony_ci memset(ev.key.val + key->enc_size, 0, 81268c2ecf20Sopenharmony_ci sizeof(ev.key.val) - key->enc_size); 81278c2ecf20Sopenharmony_ci 81288c2ecf20Sopenharmony_ci mgmt_event(MGMT_EV_NEW_LONG_TERM_KEY, hdev, &ev, sizeof(ev), NULL); 81298c2ecf20Sopenharmony_ci} 81308c2ecf20Sopenharmony_ci 81318c2ecf20Sopenharmony_civoid mgmt_new_irk(struct hci_dev *hdev, struct smp_irk *irk, bool persistent) 81328c2ecf20Sopenharmony_ci{ 81338c2ecf20Sopenharmony_ci struct mgmt_ev_new_irk ev; 81348c2ecf20Sopenharmony_ci 81358c2ecf20Sopenharmony_ci memset(&ev, 0, sizeof(ev)); 81368c2ecf20Sopenharmony_ci 81378c2ecf20Sopenharmony_ci ev.store_hint = persistent; 81388c2ecf20Sopenharmony_ci 81398c2ecf20Sopenharmony_ci bacpy(&ev.rpa, &irk->rpa); 81408c2ecf20Sopenharmony_ci bacpy(&ev.irk.addr.bdaddr, &irk->bdaddr); 81418c2ecf20Sopenharmony_ci ev.irk.addr.type = link_to_bdaddr(irk->link_type, irk->addr_type); 81428c2ecf20Sopenharmony_ci memcpy(ev.irk.val, irk->val, sizeof(irk->val)); 81438c2ecf20Sopenharmony_ci 81448c2ecf20Sopenharmony_ci mgmt_event(MGMT_EV_NEW_IRK, hdev, &ev, sizeof(ev), NULL); 81458c2ecf20Sopenharmony_ci} 81468c2ecf20Sopenharmony_ci 81478c2ecf20Sopenharmony_civoid mgmt_new_csrk(struct hci_dev *hdev, struct smp_csrk *csrk, 81488c2ecf20Sopenharmony_ci bool persistent) 81498c2ecf20Sopenharmony_ci{ 81508c2ecf20Sopenharmony_ci struct mgmt_ev_new_csrk ev; 81518c2ecf20Sopenharmony_ci 81528c2ecf20Sopenharmony_ci memset(&ev, 0, sizeof(ev)); 81538c2ecf20Sopenharmony_ci 81548c2ecf20Sopenharmony_ci /* Devices using resolvable or non-resolvable random addresses 81558c2ecf20Sopenharmony_ci * without providing an identity resolving key don't require 81568c2ecf20Sopenharmony_ci * to store signature resolving keys. Their addresses will change 81578c2ecf20Sopenharmony_ci * the next time around. 81588c2ecf20Sopenharmony_ci * 81598c2ecf20Sopenharmony_ci * Only when a remote device provides an identity address 81608c2ecf20Sopenharmony_ci * make sure the signature resolving key is stored. So allow 81618c2ecf20Sopenharmony_ci * static random and public addresses here. 81628c2ecf20Sopenharmony_ci */ 81638c2ecf20Sopenharmony_ci if (csrk->bdaddr_type == ADDR_LE_DEV_RANDOM && 81648c2ecf20Sopenharmony_ci (csrk->bdaddr.b[5] & 0xc0) != 0xc0) 81658c2ecf20Sopenharmony_ci ev.store_hint = 0x00; 81668c2ecf20Sopenharmony_ci else 81678c2ecf20Sopenharmony_ci ev.store_hint = persistent; 81688c2ecf20Sopenharmony_ci 81698c2ecf20Sopenharmony_ci bacpy(&ev.key.addr.bdaddr, &csrk->bdaddr); 81708c2ecf20Sopenharmony_ci ev.key.addr.type = link_to_bdaddr(csrk->link_type, csrk->bdaddr_type); 81718c2ecf20Sopenharmony_ci ev.key.type = csrk->type; 81728c2ecf20Sopenharmony_ci memcpy(ev.key.val, csrk->val, sizeof(csrk->val)); 81738c2ecf20Sopenharmony_ci 81748c2ecf20Sopenharmony_ci mgmt_event(MGMT_EV_NEW_CSRK, hdev, &ev, sizeof(ev), NULL); 81758c2ecf20Sopenharmony_ci} 81768c2ecf20Sopenharmony_ci 81778c2ecf20Sopenharmony_civoid mgmt_new_conn_param(struct hci_dev *hdev, bdaddr_t *bdaddr, 81788c2ecf20Sopenharmony_ci u8 bdaddr_type, u8 store_hint, u16 min_interval, 81798c2ecf20Sopenharmony_ci u16 max_interval, u16 latency, u16 timeout) 81808c2ecf20Sopenharmony_ci{ 81818c2ecf20Sopenharmony_ci struct mgmt_ev_new_conn_param ev; 81828c2ecf20Sopenharmony_ci 81838c2ecf20Sopenharmony_ci if (!hci_is_identity_address(bdaddr, bdaddr_type)) 81848c2ecf20Sopenharmony_ci return; 81858c2ecf20Sopenharmony_ci 81868c2ecf20Sopenharmony_ci memset(&ev, 0, sizeof(ev)); 81878c2ecf20Sopenharmony_ci bacpy(&ev.addr.bdaddr, bdaddr); 81888c2ecf20Sopenharmony_ci ev.addr.type = link_to_bdaddr(LE_LINK, bdaddr_type); 81898c2ecf20Sopenharmony_ci ev.store_hint = store_hint; 81908c2ecf20Sopenharmony_ci ev.min_interval = cpu_to_le16(min_interval); 81918c2ecf20Sopenharmony_ci ev.max_interval = cpu_to_le16(max_interval); 81928c2ecf20Sopenharmony_ci ev.latency = cpu_to_le16(latency); 81938c2ecf20Sopenharmony_ci ev.timeout = cpu_to_le16(timeout); 81948c2ecf20Sopenharmony_ci 81958c2ecf20Sopenharmony_ci mgmt_event(MGMT_EV_NEW_CONN_PARAM, hdev, &ev, sizeof(ev), NULL); 81968c2ecf20Sopenharmony_ci} 81978c2ecf20Sopenharmony_ci 81988c2ecf20Sopenharmony_civoid mgmt_device_connected(struct hci_dev *hdev, struct hci_conn *conn, 81998c2ecf20Sopenharmony_ci u32 flags, u8 *name, u8 name_len) 82008c2ecf20Sopenharmony_ci{ 82018c2ecf20Sopenharmony_ci char buf[512]; 82028c2ecf20Sopenharmony_ci struct mgmt_ev_device_connected *ev = (void *) buf; 82038c2ecf20Sopenharmony_ci u16 eir_len = 0; 82048c2ecf20Sopenharmony_ci 82058c2ecf20Sopenharmony_ci bacpy(&ev->addr.bdaddr, &conn->dst); 82068c2ecf20Sopenharmony_ci ev->addr.type = link_to_bdaddr(conn->type, conn->dst_type); 82078c2ecf20Sopenharmony_ci 82088c2ecf20Sopenharmony_ci ev->flags = __cpu_to_le32(flags); 82098c2ecf20Sopenharmony_ci 82108c2ecf20Sopenharmony_ci /* We must ensure that the EIR Data fields are ordered and 82118c2ecf20Sopenharmony_ci * unique. Keep it simple for now and avoid the problem by not 82128c2ecf20Sopenharmony_ci * adding any BR/EDR data to the LE adv. 82138c2ecf20Sopenharmony_ci */ 82148c2ecf20Sopenharmony_ci if (conn->le_adv_data_len > 0) { 82158c2ecf20Sopenharmony_ci memcpy(&ev->eir[eir_len], 82168c2ecf20Sopenharmony_ci conn->le_adv_data, conn->le_adv_data_len); 82178c2ecf20Sopenharmony_ci eir_len = conn->le_adv_data_len; 82188c2ecf20Sopenharmony_ci } else { 82198c2ecf20Sopenharmony_ci if (name_len > 0) 82208c2ecf20Sopenharmony_ci eir_len = eir_append_data(ev->eir, 0, EIR_NAME_COMPLETE, 82218c2ecf20Sopenharmony_ci name, name_len); 82228c2ecf20Sopenharmony_ci 82238c2ecf20Sopenharmony_ci if (memcmp(conn->dev_class, "\0\0\0", 3) != 0) 82248c2ecf20Sopenharmony_ci eir_len = eir_append_data(ev->eir, eir_len, 82258c2ecf20Sopenharmony_ci EIR_CLASS_OF_DEV, 82268c2ecf20Sopenharmony_ci conn->dev_class, 3); 82278c2ecf20Sopenharmony_ci } 82288c2ecf20Sopenharmony_ci 82298c2ecf20Sopenharmony_ci ev->eir_len = cpu_to_le16(eir_len); 82308c2ecf20Sopenharmony_ci 82318c2ecf20Sopenharmony_ci mgmt_event(MGMT_EV_DEVICE_CONNECTED, hdev, buf, 82328c2ecf20Sopenharmony_ci sizeof(*ev) + eir_len, NULL); 82338c2ecf20Sopenharmony_ci} 82348c2ecf20Sopenharmony_ci 82358c2ecf20Sopenharmony_cistatic void disconnect_rsp(struct mgmt_pending_cmd *cmd, void *data) 82368c2ecf20Sopenharmony_ci{ 82378c2ecf20Sopenharmony_ci struct sock **sk = data; 82388c2ecf20Sopenharmony_ci 82398c2ecf20Sopenharmony_ci cmd->cmd_complete(cmd, 0); 82408c2ecf20Sopenharmony_ci 82418c2ecf20Sopenharmony_ci *sk = cmd->sk; 82428c2ecf20Sopenharmony_ci sock_hold(*sk); 82438c2ecf20Sopenharmony_ci 82448c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 82458c2ecf20Sopenharmony_ci} 82468c2ecf20Sopenharmony_ci 82478c2ecf20Sopenharmony_cistatic void unpair_device_rsp(struct mgmt_pending_cmd *cmd, void *data) 82488c2ecf20Sopenharmony_ci{ 82498c2ecf20Sopenharmony_ci struct hci_dev *hdev = data; 82508c2ecf20Sopenharmony_ci struct mgmt_cp_unpair_device *cp = cmd->param; 82518c2ecf20Sopenharmony_ci 82528c2ecf20Sopenharmony_ci device_unpaired(hdev, &cp->addr.bdaddr, cp->addr.type, cmd->sk); 82538c2ecf20Sopenharmony_ci 82548c2ecf20Sopenharmony_ci cmd->cmd_complete(cmd, 0); 82558c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 82568c2ecf20Sopenharmony_ci} 82578c2ecf20Sopenharmony_ci 82588c2ecf20Sopenharmony_cibool mgmt_powering_down(struct hci_dev *hdev) 82598c2ecf20Sopenharmony_ci{ 82608c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 82618c2ecf20Sopenharmony_ci struct mgmt_mode *cp; 82628c2ecf20Sopenharmony_ci 82638c2ecf20Sopenharmony_ci cmd = pending_find(MGMT_OP_SET_POWERED, hdev); 82648c2ecf20Sopenharmony_ci if (!cmd) 82658c2ecf20Sopenharmony_ci return false; 82668c2ecf20Sopenharmony_ci 82678c2ecf20Sopenharmony_ci cp = cmd->param; 82688c2ecf20Sopenharmony_ci if (!cp->val) 82698c2ecf20Sopenharmony_ci return true; 82708c2ecf20Sopenharmony_ci 82718c2ecf20Sopenharmony_ci return false; 82728c2ecf20Sopenharmony_ci} 82738c2ecf20Sopenharmony_ci 82748c2ecf20Sopenharmony_civoid mgmt_device_disconnected(struct hci_dev *hdev, bdaddr_t *bdaddr, 82758c2ecf20Sopenharmony_ci u8 link_type, u8 addr_type, u8 reason, 82768c2ecf20Sopenharmony_ci bool mgmt_connected) 82778c2ecf20Sopenharmony_ci{ 82788c2ecf20Sopenharmony_ci struct mgmt_ev_device_disconnected ev; 82798c2ecf20Sopenharmony_ci struct sock *sk = NULL; 82808c2ecf20Sopenharmony_ci 82818c2ecf20Sopenharmony_ci /* The connection is still in hci_conn_hash so test for 1 82828c2ecf20Sopenharmony_ci * instead of 0 to know if this is the last one. 82838c2ecf20Sopenharmony_ci */ 82848c2ecf20Sopenharmony_ci if (mgmt_powering_down(hdev) && hci_conn_count(hdev) == 1) { 82858c2ecf20Sopenharmony_ci cancel_delayed_work(&hdev->power_off); 82868c2ecf20Sopenharmony_ci queue_work(hdev->req_workqueue, &hdev->power_off.work); 82878c2ecf20Sopenharmony_ci } 82888c2ecf20Sopenharmony_ci 82898c2ecf20Sopenharmony_ci if (!mgmt_connected) 82908c2ecf20Sopenharmony_ci return; 82918c2ecf20Sopenharmony_ci 82928c2ecf20Sopenharmony_ci if (link_type != ACL_LINK && link_type != LE_LINK) 82938c2ecf20Sopenharmony_ci return; 82948c2ecf20Sopenharmony_ci 82958c2ecf20Sopenharmony_ci mgmt_pending_foreach(MGMT_OP_DISCONNECT, hdev, disconnect_rsp, &sk); 82968c2ecf20Sopenharmony_ci 82978c2ecf20Sopenharmony_ci bacpy(&ev.addr.bdaddr, bdaddr); 82988c2ecf20Sopenharmony_ci ev.addr.type = link_to_bdaddr(link_type, addr_type); 82998c2ecf20Sopenharmony_ci ev.reason = reason; 83008c2ecf20Sopenharmony_ci 83018c2ecf20Sopenharmony_ci /* Report disconnects due to suspend */ 83028c2ecf20Sopenharmony_ci if (hdev->suspended) 83038c2ecf20Sopenharmony_ci ev.reason = MGMT_DEV_DISCONN_LOCAL_HOST_SUSPEND; 83048c2ecf20Sopenharmony_ci 83058c2ecf20Sopenharmony_ci mgmt_event(MGMT_EV_DEVICE_DISCONNECTED, hdev, &ev, sizeof(ev), sk); 83068c2ecf20Sopenharmony_ci 83078c2ecf20Sopenharmony_ci if (sk) 83088c2ecf20Sopenharmony_ci sock_put(sk); 83098c2ecf20Sopenharmony_ci 83108c2ecf20Sopenharmony_ci mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp, 83118c2ecf20Sopenharmony_ci hdev); 83128c2ecf20Sopenharmony_ci} 83138c2ecf20Sopenharmony_ci 83148c2ecf20Sopenharmony_civoid mgmt_disconnect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, 83158c2ecf20Sopenharmony_ci u8 link_type, u8 addr_type, u8 status) 83168c2ecf20Sopenharmony_ci{ 83178c2ecf20Sopenharmony_ci u8 bdaddr_type = link_to_bdaddr(link_type, addr_type); 83188c2ecf20Sopenharmony_ci struct mgmt_cp_disconnect *cp; 83198c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 83208c2ecf20Sopenharmony_ci 83218c2ecf20Sopenharmony_ci mgmt_pending_foreach(MGMT_OP_UNPAIR_DEVICE, hdev, unpair_device_rsp, 83228c2ecf20Sopenharmony_ci hdev); 83238c2ecf20Sopenharmony_ci 83248c2ecf20Sopenharmony_ci cmd = pending_find(MGMT_OP_DISCONNECT, hdev); 83258c2ecf20Sopenharmony_ci if (!cmd) 83268c2ecf20Sopenharmony_ci return; 83278c2ecf20Sopenharmony_ci 83288c2ecf20Sopenharmony_ci cp = cmd->param; 83298c2ecf20Sopenharmony_ci 83308c2ecf20Sopenharmony_ci if (bacmp(bdaddr, &cp->addr.bdaddr)) 83318c2ecf20Sopenharmony_ci return; 83328c2ecf20Sopenharmony_ci 83338c2ecf20Sopenharmony_ci if (cp->addr.type != bdaddr_type) 83348c2ecf20Sopenharmony_ci return; 83358c2ecf20Sopenharmony_ci 83368c2ecf20Sopenharmony_ci cmd->cmd_complete(cmd, mgmt_status(status)); 83378c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 83388c2ecf20Sopenharmony_ci} 83398c2ecf20Sopenharmony_ci 83408c2ecf20Sopenharmony_civoid mgmt_connect_failed(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, 83418c2ecf20Sopenharmony_ci u8 addr_type, u8 status) 83428c2ecf20Sopenharmony_ci{ 83438c2ecf20Sopenharmony_ci struct mgmt_ev_connect_failed ev; 83448c2ecf20Sopenharmony_ci 83458c2ecf20Sopenharmony_ci /* The connection is still in hci_conn_hash so test for 1 83468c2ecf20Sopenharmony_ci * instead of 0 to know if this is the last one. 83478c2ecf20Sopenharmony_ci */ 83488c2ecf20Sopenharmony_ci if (mgmt_powering_down(hdev) && hci_conn_count(hdev) == 1) { 83498c2ecf20Sopenharmony_ci cancel_delayed_work(&hdev->power_off); 83508c2ecf20Sopenharmony_ci queue_work(hdev->req_workqueue, &hdev->power_off.work); 83518c2ecf20Sopenharmony_ci } 83528c2ecf20Sopenharmony_ci 83538c2ecf20Sopenharmony_ci bacpy(&ev.addr.bdaddr, bdaddr); 83548c2ecf20Sopenharmony_ci ev.addr.type = link_to_bdaddr(link_type, addr_type); 83558c2ecf20Sopenharmony_ci ev.status = mgmt_status(status); 83568c2ecf20Sopenharmony_ci 83578c2ecf20Sopenharmony_ci mgmt_event(MGMT_EV_CONNECT_FAILED, hdev, &ev, sizeof(ev), NULL); 83588c2ecf20Sopenharmony_ci} 83598c2ecf20Sopenharmony_ci 83608c2ecf20Sopenharmony_civoid mgmt_pin_code_request(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 secure) 83618c2ecf20Sopenharmony_ci{ 83628c2ecf20Sopenharmony_ci struct mgmt_ev_pin_code_request ev; 83638c2ecf20Sopenharmony_ci 83648c2ecf20Sopenharmony_ci bacpy(&ev.addr.bdaddr, bdaddr); 83658c2ecf20Sopenharmony_ci ev.addr.type = BDADDR_BREDR; 83668c2ecf20Sopenharmony_ci ev.secure = secure; 83678c2ecf20Sopenharmony_ci 83688c2ecf20Sopenharmony_ci mgmt_event(MGMT_EV_PIN_CODE_REQUEST, hdev, &ev, sizeof(ev), NULL); 83698c2ecf20Sopenharmony_ci} 83708c2ecf20Sopenharmony_ci 83718c2ecf20Sopenharmony_civoid mgmt_pin_code_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, 83728c2ecf20Sopenharmony_ci u8 status) 83738c2ecf20Sopenharmony_ci{ 83748c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 83758c2ecf20Sopenharmony_ci 83768c2ecf20Sopenharmony_ci cmd = pending_find(MGMT_OP_PIN_CODE_REPLY, hdev); 83778c2ecf20Sopenharmony_ci if (!cmd) 83788c2ecf20Sopenharmony_ci return; 83798c2ecf20Sopenharmony_ci 83808c2ecf20Sopenharmony_ci cmd->cmd_complete(cmd, mgmt_status(status)); 83818c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 83828c2ecf20Sopenharmony_ci} 83838c2ecf20Sopenharmony_ci 83848c2ecf20Sopenharmony_civoid mgmt_pin_code_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, 83858c2ecf20Sopenharmony_ci u8 status) 83868c2ecf20Sopenharmony_ci{ 83878c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 83888c2ecf20Sopenharmony_ci 83898c2ecf20Sopenharmony_ci cmd = pending_find(MGMT_OP_PIN_CODE_NEG_REPLY, hdev); 83908c2ecf20Sopenharmony_ci if (!cmd) 83918c2ecf20Sopenharmony_ci return; 83928c2ecf20Sopenharmony_ci 83938c2ecf20Sopenharmony_ci cmd->cmd_complete(cmd, mgmt_status(status)); 83948c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 83958c2ecf20Sopenharmony_ci} 83968c2ecf20Sopenharmony_ci 83978c2ecf20Sopenharmony_ciint mgmt_user_confirm_request(struct hci_dev *hdev, bdaddr_t *bdaddr, 83988c2ecf20Sopenharmony_ci u8 link_type, u8 addr_type, u32 value, 83998c2ecf20Sopenharmony_ci u8 confirm_hint) 84008c2ecf20Sopenharmony_ci{ 84018c2ecf20Sopenharmony_ci struct mgmt_ev_user_confirm_request ev; 84028c2ecf20Sopenharmony_ci 84038c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "bdaddr %pMR", bdaddr); 84048c2ecf20Sopenharmony_ci 84058c2ecf20Sopenharmony_ci bacpy(&ev.addr.bdaddr, bdaddr); 84068c2ecf20Sopenharmony_ci ev.addr.type = link_to_bdaddr(link_type, addr_type); 84078c2ecf20Sopenharmony_ci ev.confirm_hint = confirm_hint; 84088c2ecf20Sopenharmony_ci ev.value = cpu_to_le32(value); 84098c2ecf20Sopenharmony_ci 84108c2ecf20Sopenharmony_ci return mgmt_event(MGMT_EV_USER_CONFIRM_REQUEST, hdev, &ev, sizeof(ev), 84118c2ecf20Sopenharmony_ci NULL); 84128c2ecf20Sopenharmony_ci} 84138c2ecf20Sopenharmony_ci 84148c2ecf20Sopenharmony_ciint mgmt_user_passkey_request(struct hci_dev *hdev, bdaddr_t *bdaddr, 84158c2ecf20Sopenharmony_ci u8 link_type, u8 addr_type) 84168c2ecf20Sopenharmony_ci{ 84178c2ecf20Sopenharmony_ci struct mgmt_ev_user_passkey_request ev; 84188c2ecf20Sopenharmony_ci 84198c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "bdaddr %pMR", bdaddr); 84208c2ecf20Sopenharmony_ci 84218c2ecf20Sopenharmony_ci bacpy(&ev.addr.bdaddr, bdaddr); 84228c2ecf20Sopenharmony_ci ev.addr.type = link_to_bdaddr(link_type, addr_type); 84238c2ecf20Sopenharmony_ci 84248c2ecf20Sopenharmony_ci return mgmt_event(MGMT_EV_USER_PASSKEY_REQUEST, hdev, &ev, sizeof(ev), 84258c2ecf20Sopenharmony_ci NULL); 84268c2ecf20Sopenharmony_ci} 84278c2ecf20Sopenharmony_ci 84288c2ecf20Sopenharmony_cistatic int user_pairing_resp_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, 84298c2ecf20Sopenharmony_ci u8 link_type, u8 addr_type, u8 status, 84308c2ecf20Sopenharmony_ci u8 opcode) 84318c2ecf20Sopenharmony_ci{ 84328c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 84338c2ecf20Sopenharmony_ci 84348c2ecf20Sopenharmony_ci cmd = pending_find(opcode, hdev); 84358c2ecf20Sopenharmony_ci if (!cmd) 84368c2ecf20Sopenharmony_ci return -ENOENT; 84378c2ecf20Sopenharmony_ci 84388c2ecf20Sopenharmony_ci cmd->cmd_complete(cmd, mgmt_status(status)); 84398c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 84408c2ecf20Sopenharmony_ci 84418c2ecf20Sopenharmony_ci return 0; 84428c2ecf20Sopenharmony_ci} 84438c2ecf20Sopenharmony_ci 84448c2ecf20Sopenharmony_ciint mgmt_user_confirm_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, 84458c2ecf20Sopenharmony_ci u8 link_type, u8 addr_type, u8 status) 84468c2ecf20Sopenharmony_ci{ 84478c2ecf20Sopenharmony_ci return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type, 84488c2ecf20Sopenharmony_ci status, MGMT_OP_USER_CONFIRM_REPLY); 84498c2ecf20Sopenharmony_ci} 84508c2ecf20Sopenharmony_ci 84518c2ecf20Sopenharmony_ciint mgmt_user_confirm_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, 84528c2ecf20Sopenharmony_ci u8 link_type, u8 addr_type, u8 status) 84538c2ecf20Sopenharmony_ci{ 84548c2ecf20Sopenharmony_ci return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type, 84558c2ecf20Sopenharmony_ci status, 84568c2ecf20Sopenharmony_ci MGMT_OP_USER_CONFIRM_NEG_REPLY); 84578c2ecf20Sopenharmony_ci} 84588c2ecf20Sopenharmony_ci 84598c2ecf20Sopenharmony_ciint mgmt_user_passkey_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, 84608c2ecf20Sopenharmony_ci u8 link_type, u8 addr_type, u8 status) 84618c2ecf20Sopenharmony_ci{ 84628c2ecf20Sopenharmony_ci return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type, 84638c2ecf20Sopenharmony_ci status, MGMT_OP_USER_PASSKEY_REPLY); 84648c2ecf20Sopenharmony_ci} 84658c2ecf20Sopenharmony_ci 84668c2ecf20Sopenharmony_ciint mgmt_user_passkey_neg_reply_complete(struct hci_dev *hdev, bdaddr_t *bdaddr, 84678c2ecf20Sopenharmony_ci u8 link_type, u8 addr_type, u8 status) 84688c2ecf20Sopenharmony_ci{ 84698c2ecf20Sopenharmony_ci return user_pairing_resp_complete(hdev, bdaddr, link_type, addr_type, 84708c2ecf20Sopenharmony_ci status, 84718c2ecf20Sopenharmony_ci MGMT_OP_USER_PASSKEY_NEG_REPLY); 84728c2ecf20Sopenharmony_ci} 84738c2ecf20Sopenharmony_ci 84748c2ecf20Sopenharmony_ciint mgmt_user_passkey_notify(struct hci_dev *hdev, bdaddr_t *bdaddr, 84758c2ecf20Sopenharmony_ci u8 link_type, u8 addr_type, u32 passkey, 84768c2ecf20Sopenharmony_ci u8 entered) 84778c2ecf20Sopenharmony_ci{ 84788c2ecf20Sopenharmony_ci struct mgmt_ev_passkey_notify ev; 84798c2ecf20Sopenharmony_ci 84808c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "bdaddr %pMR", bdaddr); 84818c2ecf20Sopenharmony_ci 84828c2ecf20Sopenharmony_ci bacpy(&ev.addr.bdaddr, bdaddr); 84838c2ecf20Sopenharmony_ci ev.addr.type = link_to_bdaddr(link_type, addr_type); 84848c2ecf20Sopenharmony_ci ev.passkey = __cpu_to_le32(passkey); 84858c2ecf20Sopenharmony_ci ev.entered = entered; 84868c2ecf20Sopenharmony_ci 84878c2ecf20Sopenharmony_ci return mgmt_event(MGMT_EV_PASSKEY_NOTIFY, hdev, &ev, sizeof(ev), NULL); 84888c2ecf20Sopenharmony_ci} 84898c2ecf20Sopenharmony_ci 84908c2ecf20Sopenharmony_civoid mgmt_auth_failed(struct hci_conn *conn, u8 hci_status) 84918c2ecf20Sopenharmony_ci{ 84928c2ecf20Sopenharmony_ci struct mgmt_ev_auth_failed ev; 84938c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 84948c2ecf20Sopenharmony_ci u8 status = mgmt_status(hci_status); 84958c2ecf20Sopenharmony_ci 84968c2ecf20Sopenharmony_ci bacpy(&ev.addr.bdaddr, &conn->dst); 84978c2ecf20Sopenharmony_ci ev.addr.type = link_to_bdaddr(conn->type, conn->dst_type); 84988c2ecf20Sopenharmony_ci ev.status = status; 84998c2ecf20Sopenharmony_ci 85008c2ecf20Sopenharmony_ci cmd = find_pairing(conn); 85018c2ecf20Sopenharmony_ci 85028c2ecf20Sopenharmony_ci mgmt_event(MGMT_EV_AUTH_FAILED, conn->hdev, &ev, sizeof(ev), 85038c2ecf20Sopenharmony_ci cmd ? cmd->sk : NULL); 85048c2ecf20Sopenharmony_ci 85058c2ecf20Sopenharmony_ci if (cmd) { 85068c2ecf20Sopenharmony_ci cmd->cmd_complete(cmd, status); 85078c2ecf20Sopenharmony_ci mgmt_pending_remove(cmd); 85088c2ecf20Sopenharmony_ci } 85098c2ecf20Sopenharmony_ci} 85108c2ecf20Sopenharmony_ci 85118c2ecf20Sopenharmony_civoid mgmt_auth_enable_complete(struct hci_dev *hdev, u8 status) 85128c2ecf20Sopenharmony_ci{ 85138c2ecf20Sopenharmony_ci struct cmd_lookup match = { NULL, hdev }; 85148c2ecf20Sopenharmony_ci bool changed; 85158c2ecf20Sopenharmony_ci 85168c2ecf20Sopenharmony_ci if (status) { 85178c2ecf20Sopenharmony_ci u8 mgmt_err = mgmt_status(status); 85188c2ecf20Sopenharmony_ci mgmt_pending_foreach(MGMT_OP_SET_LINK_SECURITY, hdev, 85198c2ecf20Sopenharmony_ci cmd_status_rsp, &mgmt_err); 85208c2ecf20Sopenharmony_ci return; 85218c2ecf20Sopenharmony_ci } 85228c2ecf20Sopenharmony_ci 85238c2ecf20Sopenharmony_ci if (test_bit(HCI_AUTH, &hdev->flags)) 85248c2ecf20Sopenharmony_ci changed = !hci_dev_test_and_set_flag(hdev, HCI_LINK_SECURITY); 85258c2ecf20Sopenharmony_ci else 85268c2ecf20Sopenharmony_ci changed = hci_dev_test_and_clear_flag(hdev, HCI_LINK_SECURITY); 85278c2ecf20Sopenharmony_ci 85288c2ecf20Sopenharmony_ci mgmt_pending_foreach(MGMT_OP_SET_LINK_SECURITY, hdev, settings_rsp, 85298c2ecf20Sopenharmony_ci &match); 85308c2ecf20Sopenharmony_ci 85318c2ecf20Sopenharmony_ci if (changed) 85328c2ecf20Sopenharmony_ci new_settings(hdev, match.sk); 85338c2ecf20Sopenharmony_ci 85348c2ecf20Sopenharmony_ci if (match.sk) 85358c2ecf20Sopenharmony_ci sock_put(match.sk); 85368c2ecf20Sopenharmony_ci} 85378c2ecf20Sopenharmony_ci 85388c2ecf20Sopenharmony_cistatic void clear_eir(struct hci_request *req) 85398c2ecf20Sopenharmony_ci{ 85408c2ecf20Sopenharmony_ci struct hci_dev *hdev = req->hdev; 85418c2ecf20Sopenharmony_ci struct hci_cp_write_eir cp; 85428c2ecf20Sopenharmony_ci 85438c2ecf20Sopenharmony_ci if (!lmp_ext_inq_capable(hdev)) 85448c2ecf20Sopenharmony_ci return; 85458c2ecf20Sopenharmony_ci 85468c2ecf20Sopenharmony_ci memset(hdev->eir, 0, sizeof(hdev->eir)); 85478c2ecf20Sopenharmony_ci 85488c2ecf20Sopenharmony_ci memset(&cp, 0, sizeof(cp)); 85498c2ecf20Sopenharmony_ci 85508c2ecf20Sopenharmony_ci hci_req_add(req, HCI_OP_WRITE_EIR, sizeof(cp), &cp); 85518c2ecf20Sopenharmony_ci} 85528c2ecf20Sopenharmony_ci 85538c2ecf20Sopenharmony_civoid mgmt_ssp_enable_complete(struct hci_dev *hdev, u8 enable, u8 status) 85548c2ecf20Sopenharmony_ci{ 85558c2ecf20Sopenharmony_ci struct cmd_lookup match = { NULL, hdev }; 85568c2ecf20Sopenharmony_ci struct hci_request req; 85578c2ecf20Sopenharmony_ci bool changed = false; 85588c2ecf20Sopenharmony_ci 85598c2ecf20Sopenharmony_ci if (status) { 85608c2ecf20Sopenharmony_ci u8 mgmt_err = mgmt_status(status); 85618c2ecf20Sopenharmony_ci 85628c2ecf20Sopenharmony_ci if (enable && hci_dev_test_and_clear_flag(hdev, 85638c2ecf20Sopenharmony_ci HCI_SSP_ENABLED)) { 85648c2ecf20Sopenharmony_ci hci_dev_clear_flag(hdev, HCI_HS_ENABLED); 85658c2ecf20Sopenharmony_ci new_settings(hdev, NULL); 85668c2ecf20Sopenharmony_ci } 85678c2ecf20Sopenharmony_ci 85688c2ecf20Sopenharmony_ci mgmt_pending_foreach(MGMT_OP_SET_SSP, hdev, cmd_status_rsp, 85698c2ecf20Sopenharmony_ci &mgmt_err); 85708c2ecf20Sopenharmony_ci return; 85718c2ecf20Sopenharmony_ci } 85728c2ecf20Sopenharmony_ci 85738c2ecf20Sopenharmony_ci if (enable) { 85748c2ecf20Sopenharmony_ci changed = !hci_dev_test_and_set_flag(hdev, HCI_SSP_ENABLED); 85758c2ecf20Sopenharmony_ci } else { 85768c2ecf20Sopenharmony_ci changed = hci_dev_test_and_clear_flag(hdev, HCI_SSP_ENABLED); 85778c2ecf20Sopenharmony_ci if (!changed) 85788c2ecf20Sopenharmony_ci changed = hci_dev_test_and_clear_flag(hdev, 85798c2ecf20Sopenharmony_ci HCI_HS_ENABLED); 85808c2ecf20Sopenharmony_ci else 85818c2ecf20Sopenharmony_ci hci_dev_clear_flag(hdev, HCI_HS_ENABLED); 85828c2ecf20Sopenharmony_ci } 85838c2ecf20Sopenharmony_ci 85848c2ecf20Sopenharmony_ci mgmt_pending_foreach(MGMT_OP_SET_SSP, hdev, settings_rsp, &match); 85858c2ecf20Sopenharmony_ci 85868c2ecf20Sopenharmony_ci if (changed) 85878c2ecf20Sopenharmony_ci new_settings(hdev, match.sk); 85888c2ecf20Sopenharmony_ci 85898c2ecf20Sopenharmony_ci if (match.sk) 85908c2ecf20Sopenharmony_ci sock_put(match.sk); 85918c2ecf20Sopenharmony_ci 85928c2ecf20Sopenharmony_ci hci_req_init(&req, hdev); 85938c2ecf20Sopenharmony_ci 85948c2ecf20Sopenharmony_ci if (hci_dev_test_flag(hdev, HCI_SSP_ENABLED)) { 85958c2ecf20Sopenharmony_ci if (hci_dev_test_flag(hdev, HCI_USE_DEBUG_KEYS)) 85968c2ecf20Sopenharmony_ci hci_req_add(&req, HCI_OP_WRITE_SSP_DEBUG_MODE, 85978c2ecf20Sopenharmony_ci sizeof(enable), &enable); 85988c2ecf20Sopenharmony_ci __hci_req_update_eir(&req); 85998c2ecf20Sopenharmony_ci } else { 86008c2ecf20Sopenharmony_ci clear_eir(&req); 86018c2ecf20Sopenharmony_ci } 86028c2ecf20Sopenharmony_ci 86038c2ecf20Sopenharmony_ci hci_req_run(&req, NULL); 86048c2ecf20Sopenharmony_ci} 86058c2ecf20Sopenharmony_ci 86068c2ecf20Sopenharmony_cistatic void sk_lookup(struct mgmt_pending_cmd *cmd, void *data) 86078c2ecf20Sopenharmony_ci{ 86088c2ecf20Sopenharmony_ci struct cmd_lookup *match = data; 86098c2ecf20Sopenharmony_ci 86108c2ecf20Sopenharmony_ci if (match->sk == NULL) { 86118c2ecf20Sopenharmony_ci match->sk = cmd->sk; 86128c2ecf20Sopenharmony_ci sock_hold(match->sk); 86138c2ecf20Sopenharmony_ci } 86148c2ecf20Sopenharmony_ci} 86158c2ecf20Sopenharmony_ci 86168c2ecf20Sopenharmony_civoid mgmt_set_class_of_dev_complete(struct hci_dev *hdev, u8 *dev_class, 86178c2ecf20Sopenharmony_ci u8 status) 86188c2ecf20Sopenharmony_ci{ 86198c2ecf20Sopenharmony_ci struct cmd_lookup match = { NULL, hdev, mgmt_status(status) }; 86208c2ecf20Sopenharmony_ci 86218c2ecf20Sopenharmony_ci mgmt_pending_foreach(MGMT_OP_SET_DEV_CLASS, hdev, sk_lookup, &match); 86228c2ecf20Sopenharmony_ci mgmt_pending_foreach(MGMT_OP_ADD_UUID, hdev, sk_lookup, &match); 86238c2ecf20Sopenharmony_ci mgmt_pending_foreach(MGMT_OP_REMOVE_UUID, hdev, sk_lookup, &match); 86248c2ecf20Sopenharmony_ci 86258c2ecf20Sopenharmony_ci if (!status) { 86268c2ecf20Sopenharmony_ci mgmt_limited_event(MGMT_EV_CLASS_OF_DEV_CHANGED, hdev, dev_class, 86278c2ecf20Sopenharmony_ci 3, HCI_MGMT_DEV_CLASS_EVENTS, NULL); 86288c2ecf20Sopenharmony_ci ext_info_changed(hdev, NULL); 86298c2ecf20Sopenharmony_ci } 86308c2ecf20Sopenharmony_ci 86318c2ecf20Sopenharmony_ci if (match.sk) 86328c2ecf20Sopenharmony_ci sock_put(match.sk); 86338c2ecf20Sopenharmony_ci} 86348c2ecf20Sopenharmony_ci 86358c2ecf20Sopenharmony_civoid mgmt_set_local_name_complete(struct hci_dev *hdev, u8 *name, u8 status) 86368c2ecf20Sopenharmony_ci{ 86378c2ecf20Sopenharmony_ci struct mgmt_cp_set_local_name ev; 86388c2ecf20Sopenharmony_ci struct mgmt_pending_cmd *cmd; 86398c2ecf20Sopenharmony_ci 86408c2ecf20Sopenharmony_ci if (status) 86418c2ecf20Sopenharmony_ci return; 86428c2ecf20Sopenharmony_ci 86438c2ecf20Sopenharmony_ci memset(&ev, 0, sizeof(ev)); 86448c2ecf20Sopenharmony_ci memcpy(ev.name, name, HCI_MAX_NAME_LENGTH); 86458c2ecf20Sopenharmony_ci memcpy(ev.short_name, hdev->short_name, HCI_MAX_SHORT_NAME_LENGTH); 86468c2ecf20Sopenharmony_ci 86478c2ecf20Sopenharmony_ci cmd = pending_find(MGMT_OP_SET_LOCAL_NAME, hdev); 86488c2ecf20Sopenharmony_ci if (!cmd) { 86498c2ecf20Sopenharmony_ci memcpy(hdev->dev_name, name, sizeof(hdev->dev_name)); 86508c2ecf20Sopenharmony_ci 86518c2ecf20Sopenharmony_ci /* If this is a HCI command related to powering on the 86528c2ecf20Sopenharmony_ci * HCI dev don't send any mgmt signals. 86538c2ecf20Sopenharmony_ci */ 86548c2ecf20Sopenharmony_ci if (pending_find(MGMT_OP_SET_POWERED, hdev)) 86558c2ecf20Sopenharmony_ci return; 86568c2ecf20Sopenharmony_ci } 86578c2ecf20Sopenharmony_ci 86588c2ecf20Sopenharmony_ci mgmt_limited_event(MGMT_EV_LOCAL_NAME_CHANGED, hdev, &ev, sizeof(ev), 86598c2ecf20Sopenharmony_ci HCI_MGMT_LOCAL_NAME_EVENTS, cmd ? cmd->sk : NULL); 86608c2ecf20Sopenharmony_ci ext_info_changed(hdev, cmd ? cmd->sk : NULL); 86618c2ecf20Sopenharmony_ci} 86628c2ecf20Sopenharmony_ci 86638c2ecf20Sopenharmony_cistatic inline bool has_uuid(u8 *uuid, u16 uuid_count, u8 (*uuids)[16]) 86648c2ecf20Sopenharmony_ci{ 86658c2ecf20Sopenharmony_ci int i; 86668c2ecf20Sopenharmony_ci 86678c2ecf20Sopenharmony_ci for (i = 0; i < uuid_count; i++) { 86688c2ecf20Sopenharmony_ci if (!memcmp(uuid, uuids[i], 16)) 86698c2ecf20Sopenharmony_ci return true; 86708c2ecf20Sopenharmony_ci } 86718c2ecf20Sopenharmony_ci 86728c2ecf20Sopenharmony_ci return false; 86738c2ecf20Sopenharmony_ci} 86748c2ecf20Sopenharmony_ci 86758c2ecf20Sopenharmony_cistatic bool eir_has_uuids(u8 *eir, u16 eir_len, u16 uuid_count, u8 (*uuids)[16]) 86768c2ecf20Sopenharmony_ci{ 86778c2ecf20Sopenharmony_ci u16 parsed = 0; 86788c2ecf20Sopenharmony_ci 86798c2ecf20Sopenharmony_ci while (parsed < eir_len) { 86808c2ecf20Sopenharmony_ci u8 field_len = eir[0]; 86818c2ecf20Sopenharmony_ci u8 uuid[16]; 86828c2ecf20Sopenharmony_ci int i; 86838c2ecf20Sopenharmony_ci 86848c2ecf20Sopenharmony_ci if (field_len == 0) 86858c2ecf20Sopenharmony_ci break; 86868c2ecf20Sopenharmony_ci 86878c2ecf20Sopenharmony_ci if (eir_len - parsed < field_len + 1) 86888c2ecf20Sopenharmony_ci break; 86898c2ecf20Sopenharmony_ci 86908c2ecf20Sopenharmony_ci switch (eir[1]) { 86918c2ecf20Sopenharmony_ci case EIR_UUID16_ALL: 86928c2ecf20Sopenharmony_ci case EIR_UUID16_SOME: 86938c2ecf20Sopenharmony_ci for (i = 0; i + 3 <= field_len; i += 2) { 86948c2ecf20Sopenharmony_ci memcpy(uuid, bluetooth_base_uuid, 16); 86958c2ecf20Sopenharmony_ci uuid[13] = eir[i + 3]; 86968c2ecf20Sopenharmony_ci uuid[12] = eir[i + 2]; 86978c2ecf20Sopenharmony_ci if (has_uuid(uuid, uuid_count, uuids)) 86988c2ecf20Sopenharmony_ci return true; 86998c2ecf20Sopenharmony_ci } 87008c2ecf20Sopenharmony_ci break; 87018c2ecf20Sopenharmony_ci case EIR_UUID32_ALL: 87028c2ecf20Sopenharmony_ci case EIR_UUID32_SOME: 87038c2ecf20Sopenharmony_ci for (i = 0; i + 5 <= field_len; i += 4) { 87048c2ecf20Sopenharmony_ci memcpy(uuid, bluetooth_base_uuid, 16); 87058c2ecf20Sopenharmony_ci uuid[15] = eir[i + 5]; 87068c2ecf20Sopenharmony_ci uuid[14] = eir[i + 4]; 87078c2ecf20Sopenharmony_ci uuid[13] = eir[i + 3]; 87088c2ecf20Sopenharmony_ci uuid[12] = eir[i + 2]; 87098c2ecf20Sopenharmony_ci if (has_uuid(uuid, uuid_count, uuids)) 87108c2ecf20Sopenharmony_ci return true; 87118c2ecf20Sopenharmony_ci } 87128c2ecf20Sopenharmony_ci break; 87138c2ecf20Sopenharmony_ci case EIR_UUID128_ALL: 87148c2ecf20Sopenharmony_ci case EIR_UUID128_SOME: 87158c2ecf20Sopenharmony_ci for (i = 0; i + 17 <= field_len; i += 16) { 87168c2ecf20Sopenharmony_ci memcpy(uuid, eir + i + 2, 16); 87178c2ecf20Sopenharmony_ci if (has_uuid(uuid, uuid_count, uuids)) 87188c2ecf20Sopenharmony_ci return true; 87198c2ecf20Sopenharmony_ci } 87208c2ecf20Sopenharmony_ci break; 87218c2ecf20Sopenharmony_ci } 87228c2ecf20Sopenharmony_ci 87238c2ecf20Sopenharmony_ci parsed += field_len + 1; 87248c2ecf20Sopenharmony_ci eir += field_len + 1; 87258c2ecf20Sopenharmony_ci } 87268c2ecf20Sopenharmony_ci 87278c2ecf20Sopenharmony_ci return false; 87288c2ecf20Sopenharmony_ci} 87298c2ecf20Sopenharmony_ci 87308c2ecf20Sopenharmony_cistatic void restart_le_scan(struct hci_dev *hdev) 87318c2ecf20Sopenharmony_ci{ 87328c2ecf20Sopenharmony_ci /* If controller is not scanning we are done. */ 87338c2ecf20Sopenharmony_ci if (!hci_dev_test_flag(hdev, HCI_LE_SCAN)) 87348c2ecf20Sopenharmony_ci return; 87358c2ecf20Sopenharmony_ci 87368c2ecf20Sopenharmony_ci if (time_after(jiffies + DISCOV_LE_RESTART_DELAY, 87378c2ecf20Sopenharmony_ci hdev->discovery.scan_start + 87388c2ecf20Sopenharmony_ci hdev->discovery.scan_duration)) 87398c2ecf20Sopenharmony_ci return; 87408c2ecf20Sopenharmony_ci 87418c2ecf20Sopenharmony_ci queue_delayed_work(hdev->req_workqueue, &hdev->le_scan_restart, 87428c2ecf20Sopenharmony_ci DISCOV_LE_RESTART_DELAY); 87438c2ecf20Sopenharmony_ci} 87448c2ecf20Sopenharmony_ci 87458c2ecf20Sopenharmony_cistatic bool is_filter_match(struct hci_dev *hdev, s8 rssi, u8 *eir, 87468c2ecf20Sopenharmony_ci u16 eir_len, u8 *scan_rsp, u8 scan_rsp_len) 87478c2ecf20Sopenharmony_ci{ 87488c2ecf20Sopenharmony_ci /* If a RSSI threshold has been specified, and 87498c2ecf20Sopenharmony_ci * HCI_QUIRK_STRICT_DUPLICATE_FILTER is not set, then all results with 87508c2ecf20Sopenharmony_ci * a RSSI smaller than the RSSI threshold will be dropped. If the quirk 87518c2ecf20Sopenharmony_ci * is set, let it through for further processing, as we might need to 87528c2ecf20Sopenharmony_ci * restart the scan. 87538c2ecf20Sopenharmony_ci * 87548c2ecf20Sopenharmony_ci * For BR/EDR devices (pre 1.2) providing no RSSI during inquiry, 87558c2ecf20Sopenharmony_ci * the results are also dropped. 87568c2ecf20Sopenharmony_ci */ 87578c2ecf20Sopenharmony_ci if (hdev->discovery.rssi != HCI_RSSI_INVALID && 87588c2ecf20Sopenharmony_ci (rssi == HCI_RSSI_INVALID || 87598c2ecf20Sopenharmony_ci (rssi < hdev->discovery.rssi && 87608c2ecf20Sopenharmony_ci !test_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks)))) 87618c2ecf20Sopenharmony_ci return false; 87628c2ecf20Sopenharmony_ci 87638c2ecf20Sopenharmony_ci if (hdev->discovery.uuid_count != 0) { 87648c2ecf20Sopenharmony_ci /* If a list of UUIDs is provided in filter, results with no 87658c2ecf20Sopenharmony_ci * matching UUID should be dropped. 87668c2ecf20Sopenharmony_ci */ 87678c2ecf20Sopenharmony_ci if (!eir_has_uuids(eir, eir_len, hdev->discovery.uuid_count, 87688c2ecf20Sopenharmony_ci hdev->discovery.uuids) && 87698c2ecf20Sopenharmony_ci !eir_has_uuids(scan_rsp, scan_rsp_len, 87708c2ecf20Sopenharmony_ci hdev->discovery.uuid_count, 87718c2ecf20Sopenharmony_ci hdev->discovery.uuids)) 87728c2ecf20Sopenharmony_ci return false; 87738c2ecf20Sopenharmony_ci } 87748c2ecf20Sopenharmony_ci 87758c2ecf20Sopenharmony_ci /* If duplicate filtering does not report RSSI changes, then restart 87768c2ecf20Sopenharmony_ci * scanning to ensure updated result with updated RSSI values. 87778c2ecf20Sopenharmony_ci */ 87788c2ecf20Sopenharmony_ci if (test_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks)) { 87798c2ecf20Sopenharmony_ci restart_le_scan(hdev); 87808c2ecf20Sopenharmony_ci 87818c2ecf20Sopenharmony_ci /* Validate RSSI value against the RSSI threshold once more. */ 87828c2ecf20Sopenharmony_ci if (hdev->discovery.rssi != HCI_RSSI_INVALID && 87838c2ecf20Sopenharmony_ci rssi < hdev->discovery.rssi) 87848c2ecf20Sopenharmony_ci return false; 87858c2ecf20Sopenharmony_ci } 87868c2ecf20Sopenharmony_ci 87878c2ecf20Sopenharmony_ci return true; 87888c2ecf20Sopenharmony_ci} 87898c2ecf20Sopenharmony_ci 87908c2ecf20Sopenharmony_civoid mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, 87918c2ecf20Sopenharmony_ci u8 addr_type, u8 *dev_class, s8 rssi, u32 flags, 87928c2ecf20Sopenharmony_ci u8 *eir, u16 eir_len, u8 *scan_rsp, u8 scan_rsp_len) 87938c2ecf20Sopenharmony_ci{ 87948c2ecf20Sopenharmony_ci char buf[512]; 87958c2ecf20Sopenharmony_ci struct mgmt_ev_device_found *ev = (void *)buf; 87968c2ecf20Sopenharmony_ci size_t ev_size; 87978c2ecf20Sopenharmony_ci 87988c2ecf20Sopenharmony_ci /* Don't send events for a non-kernel initiated discovery. With 87998c2ecf20Sopenharmony_ci * LE one exception is if we have pend_le_reports > 0 in which 88008c2ecf20Sopenharmony_ci * case we're doing passive scanning and want these events. 88018c2ecf20Sopenharmony_ci */ 88028c2ecf20Sopenharmony_ci if (!hci_discovery_active(hdev)) { 88038c2ecf20Sopenharmony_ci if (link_type == ACL_LINK) 88048c2ecf20Sopenharmony_ci return; 88058c2ecf20Sopenharmony_ci if (link_type == LE_LINK && 88068c2ecf20Sopenharmony_ci list_empty(&hdev->pend_le_reports) && 88078c2ecf20Sopenharmony_ci !hci_is_adv_monitoring(hdev)) { 88088c2ecf20Sopenharmony_ci return; 88098c2ecf20Sopenharmony_ci } 88108c2ecf20Sopenharmony_ci } 88118c2ecf20Sopenharmony_ci 88128c2ecf20Sopenharmony_ci if (hdev->discovery.result_filtering) { 88138c2ecf20Sopenharmony_ci /* We are using service discovery */ 88148c2ecf20Sopenharmony_ci if (!is_filter_match(hdev, rssi, eir, eir_len, scan_rsp, 88158c2ecf20Sopenharmony_ci scan_rsp_len)) 88168c2ecf20Sopenharmony_ci return; 88178c2ecf20Sopenharmony_ci } 88188c2ecf20Sopenharmony_ci 88198c2ecf20Sopenharmony_ci if (hdev->discovery.limited) { 88208c2ecf20Sopenharmony_ci /* Check for limited discoverable bit */ 88218c2ecf20Sopenharmony_ci if (dev_class) { 88228c2ecf20Sopenharmony_ci if (!(dev_class[1] & 0x20)) 88238c2ecf20Sopenharmony_ci return; 88248c2ecf20Sopenharmony_ci } else { 88258c2ecf20Sopenharmony_ci u8 *flags = eir_get_data(eir, eir_len, EIR_FLAGS, NULL); 88268c2ecf20Sopenharmony_ci if (!flags || !(flags[0] & LE_AD_LIMITED)) 88278c2ecf20Sopenharmony_ci return; 88288c2ecf20Sopenharmony_ci } 88298c2ecf20Sopenharmony_ci } 88308c2ecf20Sopenharmony_ci 88318c2ecf20Sopenharmony_ci /* Make sure that the buffer is big enough. The 5 extra bytes 88328c2ecf20Sopenharmony_ci * are for the potential CoD field. 88338c2ecf20Sopenharmony_ci */ 88348c2ecf20Sopenharmony_ci if (sizeof(*ev) + eir_len + scan_rsp_len + 5 > sizeof(buf)) 88358c2ecf20Sopenharmony_ci return; 88368c2ecf20Sopenharmony_ci 88378c2ecf20Sopenharmony_ci memset(buf, 0, sizeof(buf)); 88388c2ecf20Sopenharmony_ci 88398c2ecf20Sopenharmony_ci /* In case of device discovery with BR/EDR devices (pre 1.2), the 88408c2ecf20Sopenharmony_ci * RSSI value was reported as 0 when not available. This behavior 88418c2ecf20Sopenharmony_ci * is kept when using device discovery. This is required for full 88428c2ecf20Sopenharmony_ci * backwards compatibility with the API. 88438c2ecf20Sopenharmony_ci * 88448c2ecf20Sopenharmony_ci * However when using service discovery, the value 127 will be 88458c2ecf20Sopenharmony_ci * returned when the RSSI is not available. 88468c2ecf20Sopenharmony_ci */ 88478c2ecf20Sopenharmony_ci if (rssi == HCI_RSSI_INVALID && !hdev->discovery.report_invalid_rssi && 88488c2ecf20Sopenharmony_ci link_type == ACL_LINK) 88498c2ecf20Sopenharmony_ci rssi = 0; 88508c2ecf20Sopenharmony_ci 88518c2ecf20Sopenharmony_ci bacpy(&ev->addr.bdaddr, bdaddr); 88528c2ecf20Sopenharmony_ci ev->addr.type = link_to_bdaddr(link_type, addr_type); 88538c2ecf20Sopenharmony_ci ev->rssi = rssi; 88548c2ecf20Sopenharmony_ci ev->flags = cpu_to_le32(flags); 88558c2ecf20Sopenharmony_ci 88568c2ecf20Sopenharmony_ci if (eir_len > 0) 88578c2ecf20Sopenharmony_ci /* Copy EIR or advertising data into event */ 88588c2ecf20Sopenharmony_ci memcpy(ev->eir, eir, eir_len); 88598c2ecf20Sopenharmony_ci 88608c2ecf20Sopenharmony_ci if (dev_class && !eir_get_data(ev->eir, eir_len, EIR_CLASS_OF_DEV, 88618c2ecf20Sopenharmony_ci NULL)) 88628c2ecf20Sopenharmony_ci eir_len = eir_append_data(ev->eir, eir_len, EIR_CLASS_OF_DEV, 88638c2ecf20Sopenharmony_ci dev_class, 3); 88648c2ecf20Sopenharmony_ci 88658c2ecf20Sopenharmony_ci if (scan_rsp_len > 0) 88668c2ecf20Sopenharmony_ci /* Append scan response data to event */ 88678c2ecf20Sopenharmony_ci memcpy(ev->eir + eir_len, scan_rsp, scan_rsp_len); 88688c2ecf20Sopenharmony_ci 88698c2ecf20Sopenharmony_ci ev->eir_len = cpu_to_le16(eir_len + scan_rsp_len); 88708c2ecf20Sopenharmony_ci ev_size = sizeof(*ev) + eir_len + scan_rsp_len; 88718c2ecf20Sopenharmony_ci 88728c2ecf20Sopenharmony_ci mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev, ev_size, NULL); 88738c2ecf20Sopenharmony_ci} 88748c2ecf20Sopenharmony_ci 88758c2ecf20Sopenharmony_civoid mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type, 88768c2ecf20Sopenharmony_ci u8 addr_type, s8 rssi, u8 *name, u8 name_len) 88778c2ecf20Sopenharmony_ci{ 88788c2ecf20Sopenharmony_ci struct mgmt_ev_device_found *ev; 88798c2ecf20Sopenharmony_ci char buf[sizeof(*ev) + HCI_MAX_NAME_LENGTH + 2]; 88808c2ecf20Sopenharmony_ci u16 eir_len; 88818c2ecf20Sopenharmony_ci 88828c2ecf20Sopenharmony_ci ev = (struct mgmt_ev_device_found *) buf; 88838c2ecf20Sopenharmony_ci 88848c2ecf20Sopenharmony_ci memset(buf, 0, sizeof(buf)); 88858c2ecf20Sopenharmony_ci 88868c2ecf20Sopenharmony_ci bacpy(&ev->addr.bdaddr, bdaddr); 88878c2ecf20Sopenharmony_ci ev->addr.type = link_to_bdaddr(link_type, addr_type); 88888c2ecf20Sopenharmony_ci ev->rssi = rssi; 88898c2ecf20Sopenharmony_ci 88908c2ecf20Sopenharmony_ci eir_len = eir_append_data(ev->eir, 0, EIR_NAME_COMPLETE, name, 88918c2ecf20Sopenharmony_ci name_len); 88928c2ecf20Sopenharmony_ci 88938c2ecf20Sopenharmony_ci ev->eir_len = cpu_to_le16(eir_len); 88948c2ecf20Sopenharmony_ci 88958c2ecf20Sopenharmony_ci mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev, sizeof(*ev) + eir_len, NULL); 88968c2ecf20Sopenharmony_ci} 88978c2ecf20Sopenharmony_ci 88988c2ecf20Sopenharmony_civoid mgmt_discovering(struct hci_dev *hdev, u8 discovering) 88998c2ecf20Sopenharmony_ci{ 89008c2ecf20Sopenharmony_ci struct mgmt_ev_discovering ev; 89018c2ecf20Sopenharmony_ci 89028c2ecf20Sopenharmony_ci bt_dev_dbg(hdev, "discovering %u", discovering); 89038c2ecf20Sopenharmony_ci 89048c2ecf20Sopenharmony_ci memset(&ev, 0, sizeof(ev)); 89058c2ecf20Sopenharmony_ci ev.type = hdev->discovery.type; 89068c2ecf20Sopenharmony_ci ev.discovering = discovering; 89078c2ecf20Sopenharmony_ci 89088c2ecf20Sopenharmony_ci mgmt_event(MGMT_EV_DISCOVERING, hdev, &ev, sizeof(ev), NULL); 89098c2ecf20Sopenharmony_ci} 89108c2ecf20Sopenharmony_ci 89118c2ecf20Sopenharmony_civoid mgmt_suspending(struct hci_dev *hdev, u8 state) 89128c2ecf20Sopenharmony_ci{ 89138c2ecf20Sopenharmony_ci struct mgmt_ev_controller_suspend ev; 89148c2ecf20Sopenharmony_ci 89158c2ecf20Sopenharmony_ci ev.suspend_state = state; 89168c2ecf20Sopenharmony_ci mgmt_event(MGMT_EV_CONTROLLER_SUSPEND, hdev, &ev, sizeof(ev), NULL); 89178c2ecf20Sopenharmony_ci} 89188c2ecf20Sopenharmony_ci 89198c2ecf20Sopenharmony_civoid mgmt_resuming(struct hci_dev *hdev, u8 reason, bdaddr_t *bdaddr, 89208c2ecf20Sopenharmony_ci u8 addr_type) 89218c2ecf20Sopenharmony_ci{ 89228c2ecf20Sopenharmony_ci struct mgmt_ev_controller_resume ev; 89238c2ecf20Sopenharmony_ci 89248c2ecf20Sopenharmony_ci ev.wake_reason = reason; 89258c2ecf20Sopenharmony_ci if (bdaddr) { 89268c2ecf20Sopenharmony_ci bacpy(&ev.addr.bdaddr, bdaddr); 89278c2ecf20Sopenharmony_ci ev.addr.type = addr_type; 89288c2ecf20Sopenharmony_ci } else { 89298c2ecf20Sopenharmony_ci memset(&ev.addr, 0, sizeof(ev.addr)); 89308c2ecf20Sopenharmony_ci } 89318c2ecf20Sopenharmony_ci 89328c2ecf20Sopenharmony_ci mgmt_event(MGMT_EV_CONTROLLER_RESUME, hdev, &ev, sizeof(ev), NULL); 89338c2ecf20Sopenharmony_ci} 89348c2ecf20Sopenharmony_ci 89358c2ecf20Sopenharmony_cistatic struct hci_mgmt_chan chan = { 89368c2ecf20Sopenharmony_ci .channel = HCI_CHANNEL_CONTROL, 89378c2ecf20Sopenharmony_ci .handler_count = ARRAY_SIZE(mgmt_handlers), 89388c2ecf20Sopenharmony_ci .handlers = mgmt_handlers, 89398c2ecf20Sopenharmony_ci .hdev_init = mgmt_init_hdev, 89408c2ecf20Sopenharmony_ci}; 89418c2ecf20Sopenharmony_ci 89428c2ecf20Sopenharmony_ciint mgmt_init(void) 89438c2ecf20Sopenharmony_ci{ 89448c2ecf20Sopenharmony_ci return hci_mgmt_chan_register(&chan); 89458c2ecf20Sopenharmony_ci} 89468c2ecf20Sopenharmony_ci 89478c2ecf20Sopenharmony_civoid mgmt_exit(void) 89488c2ecf20Sopenharmony_ci{ 89498c2ecf20Sopenharmony_ci hci_mgmt_chan_unregister(&chan); 89508c2ecf20Sopenharmony_ci} 8951