Skip to content
Permalink
Browse files
bluetooth: Add support to handle MSFT Monitor Device event
MSFT Monitor Device event indicates that the controller has either
started or stopped monitoring a Bluetooth device. This event is used by
the bluetoothd to notify clients with DeviceFound/DeviceLost.

Whenever the controller starts monitoring a device, 'Device Tracked'
flag in the 'Device Found' event is set and 'Monitor_Handle' indicates
the matched monitor id. When the controller stops monitoring the device,
the 'Device Lost' event is sent to the bluetoothd.

Test performed:
- verified by logs that Monitor Device is received from the controller
  and sent to the bluetoothd when the controller starts/stops monitoring
  a bluetooth device.

Signed-off-by: Manish Mandlik <mmandlik@google.com>
  • Loading branch information
liveusr authored and intel-lab-lkp committed Oct 13, 2021
1 parent dc1650f commit 8c24f97d82e241c5605046401a106ace240d1a5d
Show file tree
Hide file tree
Showing 5 changed files with 151 additions and 22 deletions.
@@ -256,6 +256,14 @@ struct adv_info {

#define HCI_ADV_TX_POWER_NO_PREFERENCE 0x7F

struct monitored_device {
struct list_head list;

bdaddr_t bdaddr;
__u8 addr_type;
__u16 handle;
};

struct adv_pattern {
struct list_head list;
__u8 ad_type;
@@ -548,6 +556,7 @@ struct hci_dev {
struct list_head pend_le_reports;
struct list_head blocked_keys;
struct list_head local_codecs;
struct list_head monitored_devices;

struct hci_dev_stats stat;

@@ -1842,6 +1851,8 @@ void mgmt_adv_monitor_removed(struct hci_dev *hdev, u16 handle);
int mgmt_phy_configuration_changed(struct hci_dev *hdev, struct sock *skip);
int mgmt_add_adv_patterns_monitor_complete(struct hci_dev *hdev, u8 status);
int mgmt_remove_adv_monitor_complete(struct hci_dev *hdev, u8 status);
void mgmt_adv_monitor_device_lost(struct hci_dev *hdev, u16 handle,
bdaddr_t *addr, u8 addr_type);

u8 hci_le_conn_update(struct hci_conn *conn, u16 min, u16 max, u16 latency,
u16 to_multiplier);
@@ -940,12 +940,14 @@ struct mgmt_ev_auth_failed {
#define MGMT_DEV_FOUND_LEGACY_PAIRING 0x02
#define MGMT_DEV_FOUND_NOT_CONNECTABLE 0x04
#define MGMT_DEV_FOUND_INITIATED_CONN 0x08
#define MGMT_DEV_FOUND_MONITORING 0x10

#define MGMT_EV_DEVICE_FOUND 0x0012
struct mgmt_ev_device_found {
struct mgmt_addr_info addr;
__s8 rssi;
__le32 flags;
__le16 monitor_handle;
__le16 eir_len;
__u8 eir[];
} __packed;
@@ -1103,3 +1105,9 @@ struct mgmt_ev_controller_resume {
#define MGMT_WAKE_REASON_NON_BT_WAKE 0x0
#define MGMT_WAKE_REASON_UNEXPECTED 0x1
#define MGMT_WAKE_REASON_REMOTE_WAKE 0x2

#define MGMT_EV_ADV_MONITOR_DEVICE_LOST 0x002f
struct mgmt_ev_adv_monitor_device_lost {
__le16 monitor_handle;
struct mgmt_addr_info addr;
} __packed;
@@ -1600,6 +1600,16 @@ static void hci_pend_le_actions_clear(struct hci_dev *hdev)
BT_DBG("All LE pending actions cleared");
}

static void hci_monitored_devices_clear(struct hci_dev *hdev)
{
struct monitored_device *dev, *tmp;

list_for_each_entry_safe(dev, tmp, &hdev->monitored_devices, list) {
list_del(&dev->list);
kfree(dev);
}
}

int hci_dev_do_close(struct hci_dev *hdev)
{
bool auto_off;
@@ -1670,6 +1680,7 @@ int hci_dev_do_close(struct hci_dev *hdev)
hci_inquiry_cache_flush(hdev);
hci_pend_le_actions_clear(hdev);
hci_conn_hash_flush(hdev);
hci_monitored_devices_clear(hdev);
hci_dev_unlock(hdev);

smp_unregister(hdev);
@@ -3738,6 +3749,7 @@ struct hci_dev *hci_alloc_dev_priv(int sizeof_priv)
INIT_LIST_HEAD(&hdev->conn_hash.list);
INIT_LIST_HEAD(&hdev->adv_instances);
INIT_LIST_HEAD(&hdev->blocked_keys);
INIT_LIST_HEAD(&hdev->monitored_devices);

INIT_LIST_HEAD(&hdev->local_codecs);
INIT_WORK(&hdev->rx_work, hci_rx_work);
@@ -173,6 +173,7 @@ static const u16 mgmt_events[] = {
MGMT_EV_ADV_MONITOR_REMOVED,
MGMT_EV_CONTROLLER_SUSPEND,
MGMT_EV_CONTROLLER_RESUME,
MGMT_EV_ADV_MONITOR_DEVICE_LOST,
};

static const u16 mgmt_untrusted_commands[] = {
@@ -4396,6 +4397,19 @@ static int set_device_flags(struct sock *sk, struct hci_dev *hdev, void *data,
&cp->addr, sizeof(cp->addr));
}

void mgmt_adv_monitor_device_lost(struct hci_dev *hdev, u16 handle,
bdaddr_t *addr, u8 addr_type)
{
struct mgmt_ev_adv_monitor_device_lost ev;

ev.monitor_handle = cpu_to_le16(handle);
bacpy(&ev.addr.bdaddr, addr);
ev.addr.type = addr_type;

mgmt_event(MGMT_EV_ADV_MONITOR_DEVICE_LOST, hdev, &ev, sizeof(ev),
NULL);
}

static void mgmt_adv_monitor_added(struct sock *sk, struct hci_dev *hdev,
u16 handle)
{
@@ -9609,8 +9623,10 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
u8 *eir, u16 eir_len, u8 *scan_rsp, u8 scan_rsp_len)
{
char buf[512];
struct monitored_device *dev, *tmp_dev;
struct mgmt_ev_device_found *ev = (void *)buf;
size_t ev_size;
bool monitored = false;

/* Don't send events for a non-kernel initiated discovery. With
* LE one exception is if we have pend_le_reports > 0 in which
@@ -9686,7 +9702,28 @@ void mgmt_device_found(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
ev->eir_len = cpu_to_le16(eir_len + scan_rsp_len);
ev_size = sizeof(*ev) + eir_len + scan_rsp_len;

mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev, ev_size, NULL);
if (!list_empty(&hdev->monitored_devices)) {
/* An advertisement could match multiple advertisement monitors.
* Send the Device Found event once for all matched monitors.
*/
list_for_each_entry_safe(dev, tmp_dev, &hdev->monitored_devices,
list) {
if (!bacmp(&dev->bdaddr, &ev->addr.bdaddr)) {
ev->flags |= MGMT_DEV_FOUND_MONITORING;
ev->monitor_handle = cpu_to_le16(dev->handle);

list_del(&dev->list);
kfree(dev);

mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev,
ev_size, NULL);
monitored = true;
}
}
}

if (!monitored)
mgmt_event(MGMT_EV_DEVICE_FOUND, hdev, ev, ev_size, NULL);
}

void mgmt_remote_name(struct hci_dev *hdev, bdaddr_t *bdaddr, u8 link_type,
@@ -80,6 +80,14 @@ struct msft_rp_le_set_advertisement_filter_enable {
__u8 sub_opcode;
} __packed;

#define MSFT_EV_LE_MONITOR_DEVICE 0x02
struct msft_ev_le_monitor_device {
__u8 addr_type;
bdaddr_t bdaddr;
__u8 monitor_handle;
__u8 monitor_state;
} __packed;

struct msft_monitor_advertisement_handle_data {
__u8 msft_handle;
__u16 mgmt_handle;
@@ -103,6 +111,26 @@ static int __msft_add_monitor_pattern(struct hci_dev *hdev,
static int __msft_remove_monitor(struct hci_dev *hdev,
struct adv_monitor *monitor, u16 handle);

/* is_mgmt = true matches the handle exposed to userspace via mgmt.
* is_mgmt = false matches the handle used by the msft controller.
* This function requires the caller holds hdev->lock
*/
static struct msft_monitor_advertisement_handle_data *msft_find_handle_data
(struct hci_dev *hdev, u16 handle, bool is_mgmt)
{
struct msft_monitor_advertisement_handle_data *entry;
struct msft_data *msft = hdev->msft_data;

list_for_each_entry(entry, &msft->handle_map, list) {
if (is_mgmt && entry->mgmt_handle == handle)
return entry;
if (!is_mgmt && entry->msft_handle == handle)
return entry;
}

return NULL;
}

bool msft_monitor_supported(struct hci_dev *hdev)
{
return !!(msft_get_features(hdev) & MSFT_FEATURE_MASK_LE_ADV_MONITOR);
@@ -341,6 +369,47 @@ void msft_unregister(struct hci_dev *hdev)
kfree(msft);
}

/* This function requires the caller holds hdev->lock */
static void msft_monitor_device_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
struct msft_ev_le_monitor_device *ev = (void *)skb->data;
struct msft_monitor_advertisement_handle_data *handle_data;
struct monitored_device *dev;

if (skb->len < sizeof(*ev)) {
bt_dev_err(hdev,
"MSFT vendor event %u: insufficient data (len: %u)",
MSFT_EV_LE_MONITOR_DEVICE, skb->len);
return;
}
skb_pull(skb, sizeof(*ev));

bt_dev_dbg(hdev,
"MSFT vendor event %u: handle 0x%04x state %d addr %pMR",
MSFT_EV_LE_MONITOR_DEVICE, ev->monitor_handle,
ev->monitor_state, &ev->bdaddr);

handle_data = msft_find_handle_data(hdev, ev->monitor_handle, false);

if (ev->monitor_state) {
dev = kmalloc(sizeof(*dev), GFP_KERNEL);
if (!dev) {
bt_dev_err(hdev, "MSFT vendor event %u: no memory");
return;
}

bacpy(&dev->bdaddr, &ev->bdaddr);
dev->addr_type = ev->addr_type;
dev->handle = handle_data->mgmt_handle;

INIT_LIST_HEAD(&dev->list);
list_add(&dev->list, &hdev->monitored_devices);
} else {
mgmt_adv_monitor_device_lost(hdev, handle_data->mgmt_handle,
&ev->bdaddr, ev->addr_type);
}
}

void msft_vendor_evt(struct hci_dev *hdev, struct sk_buff *skb)
{
struct msft_data *msft = hdev->msft_data;
@@ -368,37 +437,29 @@ void msft_vendor_evt(struct hci_dev *hdev, struct sk_buff *skb)
if (skb->len < 1)
return;

hci_dev_lock(hdev);

event = *skb->data;
skb_pull(skb, 1);

bt_dev_dbg(hdev, "MSFT vendor event %u", event);
}
switch (event) {
case MSFT_EV_LE_MONITOR_DEVICE:
msft_monitor_device_evt(hdev, skb);
break;

__u64 msft_get_features(struct hci_dev *hdev)
{
struct msft_data *msft = hdev->msft_data;
default:
bt_dev_dbg(hdev, "MSFT vendor event %u", event);
break;
}

return msft ? msft->features : 0;
hci_dev_unlock(hdev);
}

/* is_mgmt = true matches the handle exposed to userspace via mgmt.
* is_mgmt = false matches the handle used by the msft controller.
* This function requires the caller holds hdev->lock
*/
static struct msft_monitor_advertisement_handle_data *msft_find_handle_data
(struct hci_dev *hdev, u16 handle, bool is_mgmt)
__u64 msft_get_features(struct hci_dev *hdev)
{
struct msft_monitor_advertisement_handle_data *entry;
struct msft_data *msft = hdev->msft_data;

list_for_each_entry(entry, &msft->handle_map, list) {
if (is_mgmt && entry->mgmt_handle == handle)
return entry;
if (!is_mgmt && entry->msft_handle == handle)
return entry;
}

return NULL;
return msft ? msft->features : 0;
}

static void msft_le_monitor_advertisement_cb(struct hci_dev *hdev,

0 comments on commit 8c24f97

Please sign in to comment.