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(&params->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(&params->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(&params->action, &hdev->pend_le_conns);
64608c2ecf20Sopenharmony_ci		else
64618c2ecf20Sopenharmony_ci			list_add(&params->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(&params->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(&params->action);
66768c2ecf20Sopenharmony_ci		list_del(&params->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)", &param->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, &param->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