summaryrefslogtreecommitdiff
path: root/net/bluetooth/mgmt.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/bluetooth/mgmt.c')
-rw-r--r--net/bluetooth/mgmt.c577
1 files changed, 563 insertions, 14 deletions
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index 9e8a3cccc6ca..5bbe71002fb9 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -36,9 +36,11 @@
#include "hci_request.h"
#include "smp.h"
#include "mgmt_util.h"
+#include "mgmt_config.h"
+#include "msft.h"
#define MGMT_VERSION 1
-#define MGMT_REVISION 17
+#define MGMT_REVISION 18
static const u16 mgmt_commands[] = {
MGMT_OP_READ_INDEX_LIST,
@@ -111,6 +113,15 @@ static const u16 mgmt_commands[] = {
MGMT_OP_READ_SECURITY_INFO,
MGMT_OP_READ_EXP_FEATURES_INFO,
MGMT_OP_SET_EXP_FEATURE,
+ MGMT_OP_READ_DEF_SYSTEM_CONFIG,
+ MGMT_OP_SET_DEF_SYSTEM_CONFIG,
+ MGMT_OP_READ_DEF_RUNTIME_CONFIG,
+ MGMT_OP_SET_DEF_RUNTIME_CONFIG,
+ MGMT_OP_GET_DEVICE_FLAGS,
+ MGMT_OP_SET_DEVICE_FLAGS,
+ MGMT_OP_READ_ADV_MONITOR_FEATURES,
+ MGMT_OP_ADD_ADV_PATTERNS_MONITOR,
+ MGMT_OP_REMOVE_ADV_MONITOR,
};
static const u16 mgmt_events[] = {
@@ -151,6 +162,7 @@ static const u16 mgmt_events[] = {
MGMT_EV_EXT_INFO_CHANGED,
MGMT_EV_PHY_CONFIGURATION_CHANGED,
MGMT_EV_EXP_FEATURE_CHANGED,
+ MGMT_EV_DEVICE_FLAGS_CHANGED,
};
static const u16 mgmt_untrusted_commands[] = {
@@ -162,6 +174,8 @@ static const u16 mgmt_untrusted_commands[] = {
MGMT_OP_READ_EXT_INFO,
MGMT_OP_READ_SECURITY_INFO,
MGMT_OP_READ_EXP_FEATURES_INFO,
+ MGMT_OP_READ_DEF_SYSTEM_CONFIG,
+ MGMT_OP_READ_DEF_RUNTIME_CONFIG,
};
static const u16 mgmt_untrusted_events[] = {
@@ -177,6 +191,8 @@ static const u16 mgmt_untrusted_events[] = {
MGMT_EV_EXT_INDEX_REMOVED,
MGMT_EV_EXT_INFO_CHANGED,
MGMT_EV_EXP_FEATURE_CHANGED,
+ MGMT_EV_ADV_MONITOR_ADDED,
+ MGMT_EV_ADV_MONITOR_REMOVED,
};
#define CACHE_TIMEOUT msecs_to_jiffies(2 * 1000)
@@ -779,10 +795,15 @@ static u32 get_supported_settings(struct hci_dev *hdev)
if (lmp_le_capable(hdev)) {
settings |= MGMT_SETTING_LE;
- settings |= MGMT_SETTING_ADVERTISING;
settings |= MGMT_SETTING_SECURE_CONN;
settings |= MGMT_SETTING_PRIVACY;
settings |= MGMT_SETTING_STATIC_ADDRESS;
+
+ /* When the experimental feature for LL Privacy support is
+ * enabled, then advertising is no longer supported.
+ */
+ if (!hci_dev_test_flag(hdev, HCI_ENABLE_LL_PRIVACY))
+ settings |= MGMT_SETTING_ADVERTISING;
}
if (test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks) ||
@@ -2915,7 +2936,7 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
if (cp->addr.type == BDADDR_BREDR) {
conn = hci_connect_acl(hdev, &cp->addr.bdaddr, sec_level,
- auth_type);
+ auth_type, CONN_REASON_PAIR_DEVICE);
} else {
u8 addr_type = le_addr_type(cp->addr.type);
struct hci_conn_params *p;
@@ -2934,9 +2955,9 @@ static int pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
if (p->auto_connect == HCI_AUTO_CONN_EXPLICIT)
p->auto_connect = HCI_AUTO_CONN_DISABLED;
- conn = hci_connect_le_scan(hdev, &cp->addr.bdaddr,
- addr_type, sec_level,
- HCI_LE_CONN_TIMEOUT);
+ conn = hci_connect_le_scan(hdev, &cp->addr.bdaddr, addr_type,
+ sec_level, HCI_LE_CONN_TIMEOUT,
+ CONN_REASON_PAIR_DEVICE);
}
if (IS_ERR(conn)) {
@@ -3037,6 +3058,20 @@ static int cancel_pair_device(struct sock *sk, struct hci_dev *hdev, void *data,
err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_CANCEL_PAIR_DEVICE, 0,
addr, sizeof(*addr));
+
+ /* Since user doesn't want to proceed with the connection, abort any
+ * ongoing pairing and then terminate the link if it was created
+ * because of the pair device action.
+ */
+ if (addr->type == BDADDR_BREDR)
+ hci_remove_link_key(hdev, &addr->bdaddr);
+ else
+ smp_cancel_and_remove_pairing(hdev, &addr->bdaddr,
+ le_addr_type(addr->type));
+
+ if (conn->conn_reason == CONN_REASON_PAIR_DEVICE)
+ hci_abort_conn(conn, HCI_ERROR_REMOTE_USER_TERM);
+
unlock:
hci_dev_unlock(hdev);
return err;
@@ -3723,12 +3758,25 @@ static const u8 debug_uuid[16] = {
};
#endif
+/* 671b10b5-42c0-4696-9227-eb28d1b049d6 */
+static const u8 simult_central_periph_uuid[16] = {
+ 0xd6, 0x49, 0xb0, 0xd1, 0x28, 0xeb, 0x27, 0x92,
+ 0x96, 0x46, 0xc0, 0x42, 0xb5, 0x10, 0x1b, 0x67,
+};
+
+/* 15c0a148-c273-11ea-b3de-0242ac130004 */
+static const u8 rpa_resolution_uuid[16] = {
+ 0x04, 0x00, 0x13, 0xac, 0x42, 0x02, 0xde, 0xb3,
+ 0xea, 0x11, 0x73, 0xc2, 0x48, 0xa1, 0xc0, 0x15,
+};
+
static int read_exp_features_info(struct sock *sk, struct hci_dev *hdev,
void *data, u16 data_len)
{
- char buf[42];
+ char buf[62]; /* Enough space for 3 features */
struct mgmt_rp_read_exp_features_info *rp = (void *)buf;
u16 idx = 0;
+ u32 flags;
bt_dev_dbg(hdev, "sock %p", sk);
@@ -3736,7 +3784,7 @@ static int read_exp_features_info(struct sock *sk, struct hci_dev *hdev,
#ifdef CONFIG_BT_FEATURE_DEBUG
if (!hdev) {
- u32 flags = bt_dbg_get() ? BIT(0) : 0;
+ flags = bt_dbg_get() ? BIT(0) : 0;
memcpy(rp->features[idx].uuid, debug_uuid, 16);
rp->features[idx].flags = cpu_to_le32(flags);
@@ -3744,6 +3792,31 @@ static int read_exp_features_info(struct sock *sk, struct hci_dev *hdev,
}
#endif
+ if (hdev) {
+ if (test_bit(HCI_QUIRK_VALID_LE_STATES, &hdev->quirks) &&
+ (hdev->le_states[4] & 0x08) && /* Central */
+ (hdev->le_states[4] & 0x40) && /* Peripheral */
+ (hdev->le_states[3] & 0x10)) /* Simultaneous */
+ flags = BIT(0);
+ else
+ flags = 0;
+
+ memcpy(rp->features[idx].uuid, simult_central_periph_uuid, 16);
+ rp->features[idx].flags = cpu_to_le32(flags);
+ idx++;
+ }
+
+ if (hdev && use_ll_privacy(hdev)) {
+ if (hci_dev_test_flag(hdev, HCI_ENABLE_LL_PRIVACY))
+ flags = BIT(0) | BIT(1);
+ else
+ flags = BIT(1);
+
+ memcpy(rp->features[idx].uuid, rpa_resolution_uuid, 16);
+ rp->features[idx].flags = cpu_to_le32(flags);
+ idx++;
+ }
+
rp->feature_count = cpu_to_le16(idx);
/* After reading the experimental features information, enable
@@ -3756,6 +3829,21 @@ static int read_exp_features_info(struct sock *sk, struct hci_dev *hdev,
0, rp, sizeof(*rp) + (20 * idx));
}
+static int exp_ll_privacy_feature_changed(bool enabled, struct hci_dev *hdev,
+ struct sock *skip)
+{
+ struct mgmt_ev_exp_feature_changed ev;
+
+ memset(&ev, 0, sizeof(ev));
+ memcpy(ev.uuid, rpa_resolution_uuid, 16);
+ ev.flags = cpu_to_le32((enabled ? BIT(0) : 0) | BIT(1));
+
+ return mgmt_limited_event(MGMT_EV_EXP_FEATURE_CHANGED, hdev,
+ &ev, sizeof(ev),
+ HCI_MGMT_EXP_FEATURE_EVENTS, skip);
+
+}
+
#ifdef CONFIG_BT_FEATURE_DEBUG
static int exp_debug_feature_changed(bool enabled, struct sock *skip)
{
@@ -3794,6 +3882,16 @@ static int set_exp_feature(struct sock *sk, struct hci_dev *hdev,
}
#endif
+ if (hdev && use_ll_privacy(hdev) && !hdev_is_powered(hdev)) {
+ bool changed = hci_dev_test_flag(hdev,
+ HCI_ENABLE_LL_PRIVACY);
+
+ hci_dev_clear_flag(hdev, HCI_ENABLE_LL_PRIVACY);
+
+ if (changed)
+ exp_ll_privacy_feature_changed(false, hdev, sk);
+ }
+
hci_sock_set_flag(sk, HCI_MGMT_EXP_FEATURE_EVENTS);
return mgmt_cmd_complete(sk, hdev ? hdev->id : MGMT_INDEX_NONE,
@@ -3844,11 +3942,401 @@ static int set_exp_feature(struct sock *sk, struct hci_dev *hdev,
}
#endif
+ if (!memcmp(cp->uuid, rpa_resolution_uuid, 16)) {
+ bool val, changed;
+ int err;
+ u32 flags;
+
+ /* Command requires to use the controller index */
+ if (!hdev)
+ return mgmt_cmd_status(sk, MGMT_INDEX_NONE,
+ MGMT_OP_SET_EXP_FEATURE,
+ MGMT_STATUS_INVALID_INDEX);
+
+ /* Changes can only be made when controller is powered down */
+ if (hdev_is_powered(hdev))
+ return mgmt_cmd_status(sk, hdev->id,
+ MGMT_OP_SET_EXP_FEATURE,
+ MGMT_STATUS_NOT_POWERED);
+
+ /* Parameters are limited to a single octet */
+ if (data_len != MGMT_SET_EXP_FEATURE_SIZE + 1)
+ return mgmt_cmd_status(sk, hdev->id,
+ MGMT_OP_SET_EXP_FEATURE,
+ MGMT_STATUS_INVALID_PARAMS);
+
+ /* Only boolean on/off is supported */
+ if (cp->param[0] != 0x00 && cp->param[0] != 0x01)
+ return mgmt_cmd_status(sk, hdev->id,
+ MGMT_OP_SET_EXP_FEATURE,
+ MGMT_STATUS_INVALID_PARAMS);
+
+ val = !!cp->param[0];
+
+ if (val) {
+ changed = !hci_dev_test_flag(hdev,
+ HCI_ENABLE_LL_PRIVACY);
+ hci_dev_set_flag(hdev, HCI_ENABLE_LL_PRIVACY);
+ hci_dev_clear_flag(hdev, HCI_ADVERTISING);
+
+ /* Enable LL privacy + supported settings changed */
+ flags = BIT(0) | BIT(1);
+ } else {
+ changed = hci_dev_test_flag(hdev,
+ HCI_ENABLE_LL_PRIVACY);
+ hci_dev_clear_flag(hdev, HCI_ENABLE_LL_PRIVACY);
+
+ /* Disable LL privacy + supported settings changed */
+ flags = BIT(1);
+ }
+
+ memcpy(rp.uuid, rpa_resolution_uuid, 16);
+ rp.flags = cpu_to_le32(flags);
+
+ hci_sock_set_flag(sk, HCI_MGMT_EXP_FEATURE_EVENTS);
+
+ err = mgmt_cmd_complete(sk, hdev->id,
+ MGMT_OP_SET_EXP_FEATURE, 0,
+ &rp, sizeof(rp));
+
+ if (changed)
+ exp_ll_privacy_feature_changed(val, hdev, sk);
+
+ return err;
+ }
+
return mgmt_cmd_status(sk, hdev ? hdev->id : MGMT_INDEX_NONE,
MGMT_OP_SET_EXP_FEATURE,
MGMT_STATUS_NOT_SUPPORTED);
}
+#define SUPPORTED_DEVICE_FLAGS() ((1U << HCI_CONN_FLAG_MAX) - 1)
+
+static int get_device_flags(struct sock *sk, struct hci_dev *hdev, void *data,
+ u16 data_len)
+{
+ struct mgmt_cp_get_device_flags *cp = data;
+ struct mgmt_rp_get_device_flags rp;
+ struct bdaddr_list_with_flags *br_params;
+ struct hci_conn_params *params;
+ u32 supported_flags = SUPPORTED_DEVICE_FLAGS();
+ u32 current_flags = 0;
+ u8 status = MGMT_STATUS_INVALID_PARAMS;
+
+ bt_dev_dbg(hdev, "Get device flags %pMR (type 0x%x)\n",
+ &cp->addr.bdaddr, cp->addr.type);
+
+ hci_dev_lock(hdev);
+
+ if (cp->addr.type == BDADDR_BREDR) {
+ br_params = hci_bdaddr_list_lookup_with_flags(&hdev->whitelist,
+ &cp->addr.bdaddr,
+ cp->addr.type);
+ if (!br_params)
+ goto done;
+
+ current_flags = br_params->current_flags;
+ } else {
+ params = hci_conn_params_lookup(hdev, &cp->addr.bdaddr,
+ le_addr_type(cp->addr.type));
+
+ if (!params)
+ goto done;
+
+ current_flags = params->current_flags;
+ }
+
+ bacpy(&rp.addr.bdaddr, &cp->addr.bdaddr);
+ rp.addr.type = cp->addr.type;
+ rp.supported_flags = cpu_to_le32(supported_flags);
+ rp.current_flags = cpu_to_le32(current_flags);
+
+ status = MGMT_STATUS_SUCCESS;
+
+done:
+ hci_dev_unlock(hdev);
+
+ return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_GET_DEVICE_FLAGS, status,
+ &rp, sizeof(rp));
+}
+
+static void device_flags_changed(struct sock *sk, struct hci_dev *hdev,
+ bdaddr_t *bdaddr, u8 bdaddr_type,
+ u32 supported_flags, u32 current_flags)
+{
+ struct mgmt_ev_device_flags_changed ev;
+
+ bacpy(&ev.addr.bdaddr, bdaddr);
+ ev.addr.type = bdaddr_type;
+ ev.supported_flags = cpu_to_le32(supported_flags);
+ ev.current_flags = cpu_to_le32(current_flags);
+
+ mgmt_event(MGMT_EV_DEVICE_FLAGS_CHANGED, hdev, &ev, sizeof(ev), sk);
+}
+
+static int set_device_flags(struct sock *sk, struct hci_dev *hdev, void *data,
+ u16 len)
+{
+ struct mgmt_cp_set_device_flags *cp = data;
+ struct bdaddr_list_with_flags *br_params;
+ struct hci_conn_params *params;
+ u8 status = MGMT_STATUS_INVALID_PARAMS;
+ u32 supported_flags = SUPPORTED_DEVICE_FLAGS();
+ u32 current_flags = __le32_to_cpu(cp->current_flags);
+
+ bt_dev_dbg(hdev, "Set device flags %pMR (type 0x%x) = 0x%x",
+ &cp->addr.bdaddr, cp->addr.type,
+ __le32_to_cpu(current_flags));
+
+ if ((supported_flags | current_flags) != supported_flags) {
+ bt_dev_warn(hdev, "Bad flag given (0x%x) vs supported (0x%0x)",
+ current_flags, supported_flags);
+ goto done;
+ }
+
+ hci_dev_lock(hdev);
+
+ if (cp->addr.type == BDADDR_BREDR) {
+ br_params = hci_bdaddr_list_lookup_with_flags(&hdev->whitelist,
+ &cp->addr.bdaddr,
+ cp->addr.type);
+
+ if (br_params) {
+ br_params->current_flags = current_flags;
+ status = MGMT_STATUS_SUCCESS;
+ } else {
+ bt_dev_warn(hdev, "No such BR/EDR device %pMR (0x%x)",
+ &cp->addr.bdaddr, cp->addr.type);
+ }
+ } else {
+ params = hci_conn_params_lookup(hdev, &cp->addr.bdaddr,
+ le_addr_type(cp->addr.type));
+ if (params) {
+ params->current_flags = current_flags;
+ status = MGMT_STATUS_SUCCESS;
+ } else {
+ bt_dev_warn(hdev, "No such LE device %pMR (0x%x)",
+ &cp->addr.bdaddr,
+ le_addr_type(cp->addr.type));
+ }
+ }
+
+done:
+ hci_dev_unlock(hdev);
+
+ if (status == MGMT_STATUS_SUCCESS)
+ device_flags_changed(sk, hdev, &cp->addr.bdaddr, cp->addr.type,
+ supported_flags, current_flags);
+
+ return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_SET_DEVICE_FLAGS, status,
+ &cp->addr, sizeof(cp->addr));
+}
+
+static void mgmt_adv_monitor_added(struct sock *sk, struct hci_dev *hdev,
+ u16 handle)
+{
+ struct mgmt_ev_adv_monitor_added ev;
+
+ ev.monitor_handle = cpu_to_le16(handle);
+
+ mgmt_event(MGMT_EV_ADV_MONITOR_ADDED, hdev, &ev, sizeof(ev), sk);
+}
+
+static void mgmt_adv_monitor_removed(struct sock *sk, struct hci_dev *hdev,
+ u16 handle)
+{
+ struct mgmt_ev_adv_monitor_added ev;
+
+ ev.monitor_handle = cpu_to_le16(handle);
+
+ mgmt_event(MGMT_EV_ADV_MONITOR_REMOVED, hdev, &ev, sizeof(ev), sk);
+}
+
+static int read_adv_mon_features(struct sock *sk, struct hci_dev *hdev,
+ void *data, u16 len)
+{
+ struct adv_monitor *monitor = NULL;
+ struct mgmt_rp_read_adv_monitor_features *rp = NULL;
+ int handle;
+ size_t rp_size = 0;
+ __u32 supported = 0;
+ __u16 num_handles = 0;
+ __u16 handles[HCI_MAX_ADV_MONITOR_NUM_HANDLES];
+
+ BT_DBG("request for %s", hdev->name);
+
+ hci_dev_lock(hdev);
+
+ if (msft_get_features(hdev) & MSFT_FEATURE_MASK_LE_ADV_MONITOR)
+ supported |= MGMT_ADV_MONITOR_FEATURE_MASK_OR_PATTERNS;
+
+ idr_for_each_entry(&hdev->adv_monitors_idr, monitor, handle) {
+ handles[num_handles++] = monitor->handle;
+ }
+
+ hci_dev_unlock(hdev);
+
+ rp_size = sizeof(*rp) + (num_handles * sizeof(u16));
+ rp = kmalloc(rp_size, GFP_KERNEL);
+ if (!rp)
+ return -ENOMEM;
+
+ /* Once controller-based monitoring is in place, the enabled_features
+ * should reflect the use.
+ */
+ rp->supported_features = cpu_to_le32(supported);
+ rp->enabled_features = 0;
+ rp->max_num_handles = cpu_to_le16(HCI_MAX_ADV_MONITOR_NUM_HANDLES);
+ rp->max_num_patterns = HCI_MAX_ADV_MONITOR_NUM_PATTERNS;
+ rp->num_handles = cpu_to_le16(num_handles);
+ if (num_handles)
+ memcpy(&rp->handles, &handles, (num_handles * sizeof(u16)));
+
+ return mgmt_cmd_complete(sk, hdev->id,
+ MGMT_OP_READ_ADV_MONITOR_FEATURES,
+ MGMT_STATUS_SUCCESS, rp, rp_size);
+}
+
+static int add_adv_patterns_monitor(struct sock *sk, struct hci_dev *hdev,
+ void *data, u16 len)
+{
+ struct mgmt_cp_add_adv_patterns_monitor *cp = data;
+ struct mgmt_rp_add_adv_patterns_monitor rp;
+ struct adv_monitor *m = NULL;
+ struct adv_pattern *p = NULL;
+ unsigned int mp_cnt = 0, prev_adv_monitors_cnt;
+ __u8 cp_ofst = 0, cp_len = 0;
+ int err, i;
+
+ BT_DBG("request for %s", hdev->name);
+
+ if (len <= sizeof(*cp) || cp->pattern_count == 0) {
+ err = mgmt_cmd_status(sk, hdev->id,
+ MGMT_OP_ADD_ADV_PATTERNS_MONITOR,
+ MGMT_STATUS_INVALID_PARAMS);
+ goto failed;
+ }
+
+ m = kmalloc(sizeof(*m), GFP_KERNEL);
+ if (!m) {
+ err = -ENOMEM;
+ goto failed;
+ }
+
+ INIT_LIST_HEAD(&m->patterns);
+ m->active = false;
+
+ for (i = 0; i < cp->pattern_count; i++) {
+ if (++mp_cnt > HCI_MAX_ADV_MONITOR_NUM_PATTERNS) {
+ err = mgmt_cmd_status(sk, hdev->id,
+ MGMT_OP_ADD_ADV_PATTERNS_MONITOR,
+ MGMT_STATUS_INVALID_PARAMS);
+ goto failed;
+ }
+
+ cp_ofst = cp->patterns[i].offset;
+ cp_len = cp->patterns[i].length;
+ if (cp_ofst >= HCI_MAX_AD_LENGTH ||
+ cp_len > HCI_MAX_AD_LENGTH ||
+ (cp_ofst + cp_len) > HCI_MAX_AD_LENGTH) {
+ err = mgmt_cmd_status(sk, hdev->id,
+ MGMT_OP_ADD_ADV_PATTERNS_MONITOR,
+ MGMT_STATUS_INVALID_PARAMS);
+ goto failed;
+ }
+
+ p = kmalloc(sizeof(*p), GFP_KERNEL);
+ if (!p) {
+ err = -ENOMEM;
+ goto failed;
+ }
+
+ p->ad_type = cp->patterns[i].ad_type;
+ p->offset = cp->patterns[i].offset;
+ p->length = cp->patterns[i].length;
+ memcpy(p->value, cp->patterns[i].value, p->length);
+
+ INIT_LIST_HEAD(&p->list);
+ list_add(&p->list, &m->patterns);
+ }
+
+ if (mp_cnt != cp->pattern_count) {
+ err = mgmt_cmd_status(sk, hdev->id,
+ MGMT_OP_ADD_ADV_PATTERNS_MONITOR,
+ MGMT_STATUS_INVALID_PARAMS);
+ goto failed;
+ }
+
+ hci_dev_lock(hdev);
+
+ prev_adv_monitors_cnt = hdev->adv_monitors_cnt;
+
+ err = hci_add_adv_monitor(hdev, m);
+ if (err) {
+ if (err == -ENOSPC) {
+ mgmt_cmd_status(sk, hdev->id,
+ MGMT_OP_ADD_ADV_PATTERNS_MONITOR,
+ MGMT_STATUS_NO_RESOURCES);
+ }
+ goto unlock;
+ }
+
+ if (hdev->adv_monitors_cnt > prev_adv_monitors_cnt)
+ mgmt_adv_monitor_added(sk, hdev, m->handle);
+
+ hci_dev_unlock(hdev);
+
+ rp.monitor_handle = cpu_to_le16(m->handle);
+
+ return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_ADD_ADV_PATTERNS_MONITOR,
+ MGMT_STATUS_SUCCESS, &rp, sizeof(rp));
+
+unlock:
+ hci_dev_unlock(hdev);
+
+failed:
+ hci_free_adv_monitor(m);
+ return err;
+}
+
+static int remove_adv_monitor(struct sock *sk, struct hci_dev *hdev,
+ void *data, u16 len)
+{
+ struct mgmt_cp_remove_adv_monitor *cp = data;
+ struct mgmt_rp_remove_adv_monitor rp;
+ unsigned int prev_adv_monitors_cnt;
+ u16 handle;
+ int err;
+
+ BT_DBG("request for %s", hdev->name);
+
+ hci_dev_lock(hdev);
+
+ handle = __le16_to_cpu(cp->monitor_handle);
+ prev_adv_monitors_cnt = hdev->adv_monitors_cnt;
+
+ err = hci_remove_adv_monitor(hdev, handle);
+ if (err == -ENOENT) {
+ err = mgmt_cmd_status(sk, hdev->id, MGMT_OP_REMOVE_ADV_MONITOR,
+ MGMT_STATUS_INVALID_INDEX);
+ goto unlock;
+ }
+
+ if (hdev->adv_monitors_cnt < prev_adv_monitors_cnt)
+ mgmt_adv_monitor_removed(sk, hdev, handle);
+
+ hci_dev_unlock(hdev);
+
+ rp.monitor_handle = cp->monitor_handle;
+
+ return mgmt_cmd_complete(sk, hdev->id, MGMT_OP_REMOVE_ADV_MONITOR,
+ MGMT_STATUS_SUCCESS, &rp, sizeof(rp));
+
+unlock:
+ hci_dev_unlock(hdev);
+ return err;
+}
+
static void read_local_oob_data_complete(struct hci_dev *hdev, u8 status,
u16 opcode, struct sk_buff *skb)
{
@@ -4147,7 +4635,7 @@ static bool discovery_type_is_valid(struct hci_dev *hdev, uint8_t type,
*mgmt_status = mgmt_le_support(hdev);
if (*mgmt_status)
return false;
- /* Intentional fall-through */
+ fallthrough;
case DISCOV_TYPE_BREDR:
*mgmt_status = mgmt_bredr_support(hdev);
if (*mgmt_status)
@@ -4662,6 +5150,13 @@ static int set_advertising(struct sock *sk, struct hci_dev *hdev, void *data,
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING,
status);
+ /* Enabling the experimental LL Privay support disables support for
+ * advertising.
+ */
+ if (hci_dev_test_flag(hdev, HCI_ENABLE_LL_PRIVACY))
+ return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING,
+ MGMT_STATUS_NOT_SUPPORTED);
+
if (cp->val != 0x00 && cp->val != 0x01 && cp->val != 0x02)
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING,
MGMT_STATUS_INVALID_PARAMS);
@@ -4848,7 +5343,7 @@ static int set_scan_params(struct sock *sk, struct hci_dev *hdev,
hci_req_init(&req, hdev);
- hci_req_add_le_scan_disable(&req);
+ hci_req_add_le_scan_disable(&req, false);
hci_req_add_le_passive_scan(&req);
hci_req_run(&req, NULL);
@@ -5523,7 +6018,7 @@ static int load_long_term_keys(struct sock *sk, struct hci_dev *hdev,
case MGMT_LTK_P256_DEBUG:
authenticated = 0x00;
type = SMP_LTK_P256_DEBUG;
- /* fall through */
+ fallthrough;
default:
continue;
}
@@ -5966,7 +6461,9 @@ static int add_device(struct sock *sk, struct hci_dev *hdev,
{
struct mgmt_cp_add_device *cp = data;
u8 auto_conn, addr_type;
+ struct hci_conn_params *params;
int err;
+ u32 current_flags = 0;
bt_dev_dbg(hdev, "sock %p", sk);
@@ -5993,8 +6490,9 @@ static int add_device(struct sock *sk, struct hci_dev *hdev,
goto unlock;
}
- err = hci_bdaddr_list_add(&hdev->whitelist, &cp->addr.bdaddr,
- cp->addr.type);
+ err = hci_bdaddr_list_add_with_flags(&hdev->whitelist,
+ &cp->addr.bdaddr,
+ cp->addr.type, 0);
if (err)
goto unlock;
@@ -6033,12 +6531,19 @@ static int add_device(struct sock *sk, struct hci_dev *hdev,
MGMT_STATUS_FAILED, &cp->addr,
sizeof(cp->addr));
goto unlock;
+ } else {
+ params = hci_conn_params_lookup(hdev, &cp->addr.bdaddr,
+ addr_type);
+ if (params)
+ current_flags = params->current_flags;
}
hci_update_background_scan(hdev);
added:
device_added(sk, hdev, &cp->addr.bdaddr, cp->addr.type, cp->action);
+ device_flags_changed(NULL, hdev, &cp->addr.bdaddr, cp->addr.type,
+ SUPPORTED_DEVICE_FLAGS(), current_flags);
err = mgmt_cmd_complete(sk, hdev->id, MGMT_OP_ADD_DEVICE,
MGMT_STATUS_SUCCESS, &cp->addr,
@@ -6724,6 +7229,13 @@ static int read_adv_features(struct sock *sk, struct hci_dev *hdev,
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_READ_ADV_FEATURES,
MGMT_STATUS_REJECTED);
+ /* Enabling the experimental LL Privay support disables support for
+ * advertising.
+ */
+ if (hci_dev_test_flag(hdev, HCI_ENABLE_LL_PRIVACY))
+ return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING,
+ MGMT_STATUS_NOT_SUPPORTED);
+
hci_dev_lock(hdev);
rp_len = sizeof(*rp) + hdev->adv_instance_cnt;
@@ -6927,6 +7439,13 @@ static int add_advertising(struct sock *sk, struct hci_dev *hdev,
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING,
status);
+ /* Enabling the experimental LL Privay support disables support for
+ * advertising.
+ */
+ if (hci_dev_test_flag(hdev, HCI_ENABLE_LL_PRIVACY))
+ return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING,
+ MGMT_STATUS_NOT_SUPPORTED);
+
if (cp->instance < 1 || cp->instance > HCI_MAX_ADV_INSTANCES)
return mgmt_cmd_status(sk, hdev->id, MGMT_OP_ADD_ADVERTISING,
MGMT_STATUS_INVALID_PARAMS);
@@ -7091,6 +7610,13 @@ static int remove_advertising(struct sock *sk, struct hci_dev *hdev,
bt_dev_dbg(hdev, "sock %p", sk);
+ /* Enabling the experimental LL Privay support disables support for
+ * advertising.
+ */
+ if (hci_dev_test_flag(hdev, HCI_ENABLE_LL_PRIVACY))
+ return mgmt_cmd_status(sk, hdev->id, MGMT_OP_SET_ADVERTISING,
+ MGMT_STATUS_NOT_SUPPORTED);
+
hci_dev_lock(hdev);
if (cp->instance && !hci_find_adv_instance(hdev, cp->instance)) {
@@ -7116,6 +7642,12 @@ static int remove_advertising(struct sock *sk, struct hci_dev *hdev,
hci_req_init(&req, hdev);
+ /* If we use extended advertising, instance is disabled and removed */
+ if (ext_adv_capable(hdev)) {
+ __hci_req_disable_ext_adv_instance(&req, cp->instance);
+ __hci_req_remove_ext_adv_instance(&req, cp->instance);
+ }
+
hci_req_clear_adv_instance(hdev, sk, &req, cp->instance, true);
if (list_empty(&hdev->adv_instances))
@@ -7297,6 +7829,20 @@ static const struct hci_mgmt_handler mgmt_handlers[] = {
{ set_exp_feature, MGMT_SET_EXP_FEATURE_SIZE,
HCI_MGMT_VAR_LEN |
HCI_MGMT_HDEV_OPTIONAL },
+ { read_def_system_config, MGMT_READ_DEF_SYSTEM_CONFIG_SIZE,
+ HCI_MGMT_UNTRUSTED },
+ { set_def_system_config, MGMT_SET_DEF_SYSTEM_CONFIG_SIZE,
+ HCI_MGMT_VAR_LEN },
+ { read_def_runtime_config, MGMT_READ_DEF_RUNTIME_CONFIG_SIZE,
+ HCI_MGMT_UNTRUSTED },
+ { set_def_runtime_config, MGMT_SET_DEF_RUNTIME_CONFIG_SIZE,
+ HCI_MGMT_VAR_LEN },
+ { get_device_flags, MGMT_GET_DEVICE_FLAGS_SIZE },
+ { set_device_flags, MGMT_SET_DEVICE_FLAGS_SIZE },
+ { read_adv_mon_features, MGMT_READ_ADV_MONITOR_FEATURES_SIZE },
+ { add_adv_patterns_monitor,MGMT_ADD_ADV_PATTERNS_MONITOR_SIZE,
+ HCI_MGMT_VAR_LEN },
+ { remove_adv_monitor, MGMT_REMOVE_ADV_MONITOR_SIZE },
};
void mgmt_index_added(struct hci_dev *hdev)
@@ -8216,8 +8762,11 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
if (!hci_discovery_active(hdev)) {
if (link_type == ACL_LINK)
return;
- if (link_type == LE_LINK && list_empty(&hdev->pend_le_reports))
+ if (link_type == LE_LINK &&
+ list_empty(&hdev->pend_le_reports) &&
+ !hci_is_adv_monitoring(hdev)) {
return;
+ }
}
if (hdev->discovery.result_filtering) {