@@ -91,17 +91,49 @@ struct msft_ev_le_monitor_device {
struct msft_monitor_advertisement_handle_data {
__u8 msft_handle;
__u16 mgmt_handle;
+ __s8 rssi_high;
+ __s8 rssi_low;
+ __u8 rssi_low_interval;
+ __u8 rssi_sampling_period;
+ __u8 cond_type;
struct list_head list;
};
+#define MSFT_MONITOR_ADVERTISEMENT_TYPE_ADDR 0x04
+struct msft_monitor_addr_filter_data {
+ __u8 msft_handle;
+ __u8 pattern_handle; /* address filters pertain to */
+ __u16 mgmt_handle;
+ bool active;
+ __s8 rssi_high;
+ __s8 rssi_low;
+ __u8 rssi_low_interval;
+ __u8 rssi_sampling_period;
+ __u8 addr_type;
+ bdaddr_t bdaddr;
+ struct list_head list;
+};
+
+struct addr_filter_skb_cb {
+ u8 pattern_handle;
+ u8 addr_type;
+ bdaddr_t bdaddr;
+};
+
+#define addr_filter_cb(skb) ((struct addr_filter_skb_cb *)((skb)->cb))
+
struct msft_data {
__u64 features;
__u8 evt_prefix_len;
__u8 *evt_prefix;
struct list_head handle_map;
+ struct list_head address_filters;
__u8 resuming;
__u8 suspending;
__u8 filter_enabled;
+ bool addr_monitor_assist;
+ /* To synchronize add/remove address filter and monitor device event.*/
+ struct mutex filter_lock;
};
bool msft_monitor_supported(struct hci_dev *hdev)
@@ -180,6 +212,24 @@ static struct msft_monitor_advertisement_handle_data *msft_find_handle_data
return NULL;
}
+/* This function requires the caller holds msft->filter_lock */
+static struct msft_monitor_addr_filter_data *msft_find_address_data
+ (struct hci_dev *hdev, u8 addr_type, bdaddr_t *addr,
+ u8 pattern_handle)
+{
+ struct msft_monitor_addr_filter_data *entry;
+ struct msft_data *msft = hdev->msft_data;
+
+ list_for_each_entry(entry, &msft->address_filters, list) {
+ if (entry->pattern_handle == pattern_handle &&
+ addr_type == entry->addr_type &&
+ !bacmp(addr, &entry->bdaddr))
+ return entry;
+ }
+
+ return NULL;
+}
+
/* This function requires the caller holds hdev->lock */
static int msft_monitor_device_del(struct hci_dev *hdev, __u16 mgmt_handle,
bdaddr_t *bdaddr, __u8 addr_type,
@@ -240,6 +290,7 @@ static int msft_le_monitor_advertisement_cb(struct hci_dev *hdev, u16 opcode,
handle_data->mgmt_handle = monitor->handle;
handle_data->msft_handle = rp->handle;
+ handle_data->cond_type = MSFT_MONITOR_ADVERTISEMENT_TYPE_PATTERN;
INIT_LIST_HEAD(&handle_data->list);
list_add(&handle_data->list, &msft->handle_map);
@@ -254,6 +305,64 @@ static int msft_le_monitor_advertisement_cb(struct hci_dev *hdev, u16 opcode,
return status;
}
+/* This function requires the caller holds hci_req_sync_lock */
+static int msft_remove_addr_filters_sync(struct hci_dev *hdev, u8 handle)
+{
+ struct msft_monitor_addr_filter_data *address_filter, *n;
+ struct msft_data *msft = hdev->msft_data;
+ struct msft_cp_le_cancel_monitor_advertisement cp;
+ struct sk_buff *skb;
+ struct list_head head;
+
+ INIT_LIST_HEAD(&head);
+
+ /* Cancel all corresponding address monitors */
+ mutex_lock(&msft->filter_lock);
+
+ list_for_each_entry_safe(address_filter, n, &msft->address_filters,
+ list) {
+ if (address_filter->pattern_handle != handle)
+ continue;
+
+ list_del(&address_filter->list);
+
+ /* If the address_filter was added but haven't been enabled,
+ * just free it.
+ */
+ if (!address_filter->active) {
+ kfree(address_filter);
+ continue;
+ }
+
+ list_add_tail(&address_filter->list, &head);
+ }
+
+ mutex_unlock(&msft->filter_lock);
+
+ list_for_each_entry_safe(address_filter, n, &head, list) {
+ list_del(&address_filter->list);
+
+ cp.sub_opcode = MSFT_OP_LE_CANCEL_MONITOR_ADVERTISEMENT;
+ cp.handle = address_filter->msft_handle;
+
+ skb = __hci_cmd_sync(hdev, hdev->msft_opcode, sizeof(cp), &cp,
+ HCI_CMD_TIMEOUT);
+ if (IS_ERR_OR_NULL(skb)) {
+ kfree(address_filter);
+ continue;
+ }
+
+ kfree_skb(skb);
+
+ bt_dev_info(hdev, "MSFT: Canceled device %pMR address filter",
+ &address_filter->bdaddr);
+
+ kfree(address_filter);
+ }
+
+ return 0;
+}
+
static int msft_le_cancel_monitor_advertisement_cb(struct hci_dev *hdev,
u16 opcode,
struct adv_monitor *monitor,
@@ -263,6 +372,7 @@ static int msft_le_cancel_monitor_advertisement_cb(struct hci_dev *hdev,
struct msft_monitor_advertisement_handle_data *handle_data;
struct msft_data *msft = hdev->msft_data;
int status = 0;
+ u8 msft_handle;
rp = (struct msft_rp_le_cancel_monitor_advertisement *)skb->data;
if (skb->len < sizeof(*rp)) {
@@ -293,11 +403,17 @@ static int msft_le_cancel_monitor_advertisement_cb(struct hci_dev *hdev,
NULL, 0, false);
}
+ msft_handle = handle_data->msft_handle;
+
list_del(&handle_data->list);
kfree(handle_data);
- }
- hci_dev_unlock(hdev);
+ hci_dev_unlock(hdev);
+
+ msft_remove_addr_filters_sync(hdev, msft_handle);
+ } else {
+ hci_dev_unlock(hdev);
+ }
done:
return status;
@@ -400,6 +516,9 @@ static int msft_add_monitor_sync(struct hci_dev *hdev,
ptrdiff_t offset = 0;
u8 pattern_count = 0;
struct sk_buff *skb;
+ int err;
+ struct msft_monitor_advertisement_handle_data *handle_data;
+ struct msft_rp_le_monitor_advertisement *rp;
if (!msft_monitor_pattern_valid(monitor))
return -EINVAL;
@@ -436,16 +555,30 @@ static int msft_add_monitor_sync(struct hci_dev *hdev,
skb = __hci_cmd_sync(hdev, hdev->msft_opcode, total_size, cp,
HCI_CMD_TIMEOUT);
- kfree(cp);
if (IS_ERR_OR_NULL(skb)) {
- if (!skb)
- return -EIO;
+ kfree(cp);
return PTR_ERR(skb);
}
- return msft_le_monitor_advertisement_cb(hdev, hdev->msft_opcode,
- monitor, skb);
+ err = msft_le_monitor_advertisement_cb(hdev, hdev->msft_opcode,
+ monitor, skb);
+ if (!err) {
+ rp = (struct msft_rp_le_monitor_advertisement *)skb->data;
+ handle_data = msft_find_handle_data(hdev, monitor->handle,
+ true);
+ if (handle_data) {
+ handle_data->rssi_high = cp->rssi_high;
+ handle_data->rssi_low = cp->rssi_low;
+ handle_data->rssi_low_interval =
+ cp->rssi_low_interval;
+ handle_data->rssi_sampling_period =
+ cp->rssi_sampling_period;
+ }
+ }
+ kfree(cp);
+
+ return err;
}
/* This function requires the caller holds hci_req_sync_lock */
@@ -497,6 +630,41 @@ int msft_resume_sync(struct hci_dev *hdev)
return 0;
}
+/* This function requires the caller holds hci_req_sync_lock */
+static bool msft_address_monitor_assist_realtek(struct hci_dev *hdev)
+{
+ struct sk_buff *skb;
+ struct {
+ __u8 status;
+ __u8 chip_id;
+ } *rp;
+
+ skb = __hci_cmd_sync(hdev, 0xfc6f, 0, NULL, HCI_CMD_TIMEOUT);
+ if (IS_ERR_OR_NULL(skb)) {
+ bt_dev_err(hdev, "MSFT: Failed to send the cmd 0xfc6f");
+ return false;
+ }
+
+ rp = (void *)skb->data;
+ if (skb->len < sizeof(*rp) || rp->status) {
+ kfree_skb(skb);
+ return false;
+ }
+
+ /* RTL8822C chip id: 13
+ * RTL8852A chip id: 18
+ * RTL8852C chip id: 25
+ */
+ if (rp->chip_id == 13 || rp->chip_id == 18 || rp->chip_id == 25) {
+ kfree_skb(skb);
+ return true;
+ }
+
+ kfree_skb(skb);
+
+ return false;
+}
+
/* This function requires the caller holds hci_req_sync_lock */
void msft_do_open(struct hci_dev *hdev)
{
@@ -518,6 +686,10 @@ void msft_do_open(struct hci_dev *hdev)
msft->evt_prefix_len = 0;
msft->features = 0;
+ if (hdev->manufacturer == 0x005d)
+ msft->addr_monitor_assist =
+ msft_address_monitor_assist_realtek(hdev);
+
if (!read_supported_features(hdev, msft)) {
hdev->msft_data = NULL;
kfree(msft);
@@ -538,6 +710,7 @@ void msft_do_close(struct hci_dev *hdev)
{
struct msft_data *msft = hdev->msft_data;
struct msft_monitor_advertisement_handle_data *handle_data, *tmp;
+ struct msft_monitor_addr_filter_data *address_filter, *n;
struct adv_monitor *monitor;
if (!msft)
@@ -559,6 +732,14 @@ void msft_do_close(struct hci_dev *hdev)
kfree(handle_data);
}
+ mutex_lock(&msft->filter_lock);
+ list_for_each_entry_safe(address_filter, n, &msft->address_filters,
+ list) {
+ list_del(&address_filter->list);
+ kfree(address_filter);
+ }
+ mutex_unlock(&msft->filter_lock);
+
hci_dev_lock(hdev);
/* Clear any devices that are being monitored and notify device lost */
@@ -568,6 +749,58 @@ void msft_do_close(struct hci_dev *hdev)
hci_dev_unlock(hdev);
}
+static int msft_cancel_address_filter_sync(struct hci_dev *hdev, void *data)
+{
+ struct msft_monitor_addr_filter_data *address_filter = NULL;
+ struct msft_cp_le_cancel_monitor_advertisement cp;
+ struct msft_data *msft = hdev->msft_data;
+ struct sk_buff *nskb;
+ u8 handle = PTR_ERR(data);
+
+ if (!msft) {
+ bt_dev_err(hdev, "MSFT: msft data is freed");
+ return -EINVAL;
+ }
+
+ mutex_lock(&msft->filter_lock);
+
+ list_for_each_entry(address_filter, &msft->address_filters, list) {
+ if (address_filter->active &&
+ handle == address_filter->msft_handle) {
+ break;
+ }
+ }
+ if (!address_filter) {
+ bt_dev_warn(hdev, "MSFT: No active addr filter (%u) to cancel",
+ handle);
+ mutex_unlock(&msft->filter_lock);
+ return -ENODEV;
+ }
+ list_del(&address_filter->list);
+
+ mutex_unlock(&msft->filter_lock);
+
+ cp.sub_opcode = MSFT_OP_LE_CANCEL_MONITOR_ADVERTISEMENT;
+ cp.handle = address_filter->msft_handle;
+
+ nskb = __hci_cmd_sync(hdev, hdev->msft_opcode, sizeof(cp), &cp,
+ HCI_CMD_TIMEOUT);
+ if (IS_ERR_OR_NULL(nskb)) {
+ bt_dev_err(hdev, "MSFT: Failed to cancel address (%pMR) filter",
+ &address_filter->bdaddr);
+ kfree(address_filter);
+ return -EIO;
+ }
+ kfree_skb(nskb);
+
+ bt_dev_info(hdev, "MSFT: Canceled device %pMR address filter",
+ &address_filter->bdaddr);
+
+ kfree(address_filter);
+
+ return 0;
+}
+
void msft_register(struct hci_dev *hdev)
{
struct msft_data *msft = NULL;
@@ -581,7 +814,9 @@ void msft_register(struct hci_dev *hdev)
}
INIT_LIST_HEAD(&msft->handle_map);
+ INIT_LIST_HEAD(&msft->address_filters);
hdev->msft_data = msft;
+ mutex_init(&msft->filter_lock);
}
void msft_unregister(struct hci_dev *hdev)
@@ -596,6 +831,7 @@ void msft_unregister(struct hci_dev *hdev)
hdev->msft_data = NULL;
kfree(msft->evt_prefix);
+ mutex_destroy(&msft->filter_lock);
kfree(msft);
}
@@ -645,12 +881,237 @@ static void *msft_skb_pull(struct hci_dev *hdev, struct sk_buff *skb,
return data;
}
+static int msft_add_address_filter_sync(struct hci_dev *hdev, void *data)
+{
+ struct sk_buff *skb = data;
+ struct msft_monitor_addr_filter_data *address_filter = NULL;
+ struct sk_buff *nskb;
+ struct msft_rp_le_monitor_advertisement *rp;
+ bool remove = false;
+ struct msft_data *msft = hdev->msft_data;
+ int err;
+
+ if (!msft) {
+ bt_dev_err(hdev, "MSFT: msft data is freed");
+ err = -EINVAL;
+ goto error;
+ }
+
+ mutex_lock(&msft->filter_lock);
+
+ address_filter = msft_find_address_data(hdev,
+ addr_filter_cb(skb)->addr_type,
+ &addr_filter_cb(skb)->bdaddr,
+ addr_filter_cb(skb)->pattern_handle);
+ if (!address_filter) {
+ bt_dev_warn(hdev, "MSFT: No address (%pMR) filter to enable",
+ &addr_filter_cb(skb)->bdaddr);
+ mutex_unlock(&msft->filter_lock);
+ err = -ENODEV;
+ goto error;
+ }
+
+ mutex_unlock(&msft->filter_lock);
+
+send_cmd:
+ nskb = __hci_cmd_sync(hdev, hdev->msft_opcode, skb->len, skb->data,
+ HCI_CMD_TIMEOUT);
+ if (IS_ERR_OR_NULL(nskb)) {
+ bt_dev_err(hdev, "Failed to enable address %pMR filter",
+ &address_filter->bdaddr);
+ nskb = NULL;
+ remove = true;
+ goto done;
+ }
+
+ rp = (struct msft_rp_le_monitor_advertisement *)nskb->data;
+ if (nskb->len < sizeof(*rp) ||
+ rp->sub_opcode != MSFT_OP_LE_MONITOR_ADVERTISEMENT) {
+ remove = true;
+ goto done;
+ }
+
+ /* If Controller's memory capacity exceeded, cancel the first address
+ * filter in the msft->address_filters, then try to add the new address
+ * filter.
+ */
+ if (rp->status == HCI_ERROR_MEMORY_EXCEEDED) {
+ struct msft_cp_le_cancel_monitor_advertisement cp;
+ struct msft_monitor_addr_filter_data *n;
+ u8 addr_type = 0xff;
+
+ mutex_lock(&msft->filter_lock);
+
+ /* If the current address filter is the first one in
+ * msft->address_filters, it means no active address filter in
+ * Controller.
+ */
+ if (list_is_first(&address_filter->list,
+ &msft->address_filters)) {
+ mutex_unlock(&msft->filter_lock);
+ bt_dev_err(hdev, "Memory capacity exceeded");
+ remove = true;
+ goto done;
+ }
+
+ n = list_first_entry(&msft->address_filters,
+ struct msft_monitor_addr_filter_data,
+ list);
+ list_del(&n->list);
+
+ mutex_unlock(&msft->filter_lock);
+
+ cp.sub_opcode = MSFT_OP_LE_CANCEL_MONITOR_ADVERTISEMENT;
+ cp.handle = n->msft_handle;
+
+ nskb = __hci_cmd_sync(hdev, hdev->msft_opcode, sizeof(cp), &cp,
+ HCI_CMD_TIMEOUT);
+ if (IS_ERR_OR_NULL(nskb)) {
+ bt_dev_err(hdev, "MSFT: Failed to cancel filter (%pMR)",
+ &n->bdaddr);
+ kfree(n);
+ remove = true;
+ goto done;
+ }
+
+ /* Fake a device lost event after canceling the corresponding
+ * address filter.
+ */
+ hci_dev_lock(hdev);
+
+ switch (n->addr_type) {
+ case ADDR_LE_DEV_PUBLIC:
+ addr_type = BDADDR_LE_PUBLIC;
+ break;
+
+ case ADDR_LE_DEV_RANDOM:
+ addr_type = BDADDR_LE_RANDOM;
+ break;
+
+ default:
+ bt_dev_err(hdev, "MSFT unknown addr type 0x%02x",
+ n->addr_type);
+ break;
+ }
+
+ msft_device_lost(hdev, &n->bdaddr, addr_type,
+ n->mgmt_handle);
+ hci_dev_unlock(hdev);
+
+ kfree(n);
+ kfree_skb(nskb);
+ goto send_cmd;
+ } else if (rp->status) {
+ bt_dev_err(hdev, "Enable address filter err (status 0x%02x)",
+ rp->status);
+ remove = true;
+ }
+
+done:
+ kfree_skb(skb);
+
+ mutex_lock(&msft->filter_lock);
+
+ /* Be careful about address_filter that is not protected by the
+ * filter_lock while the above __hci_cmd_sync() is running.
+ */
+ if (remove) {
+ bt_dev_warn(hdev, "MSFT: Remove address (%pMR) filter",
+ &address_filter->bdaddr);
+ list_del(&address_filter->list);
+ kfree(address_filter);
+ } else {
+ address_filter->active = true;
+ address_filter->msft_handle = rp->handle;
+ bt_dev_info(hdev, "MSFT: Address %pMR filter enabled",
+ &address_filter->bdaddr);
+ }
+
+ mutex_unlock(&msft->filter_lock);
+
+ kfree_skb(nskb);
+
+ return 0;
+error:
+ kfree_skb(skb);
+ return err;
+}
+
+/* This function requires the caller holds msft->filter_lock */
+static struct msft_monitor_addr_filter_data *msft_add_address_filter
+ (struct hci_dev *hdev, u8 addr_type, bdaddr_t *bdaddr,
+ struct msft_monitor_advertisement_handle_data *handle_data)
+{
+ struct sk_buff *skb;
+ struct msft_cp_le_monitor_advertisement *cp;
+ struct msft_monitor_addr_filter_data *address_filter = NULL;
+ size_t size;
+ struct msft_data *msft = hdev->msft_data;
+ int err;
+
+ size = sizeof(*cp) + sizeof(addr_type) + sizeof(*bdaddr);
+ skb = alloc_skb(size, GFP_KERNEL);
+ if (!skb) {
+ bt_dev_err(hdev, "MSFT: alloc skb err in device evt");
+ return NULL;
+ }
+
+ cp = skb_put(skb, sizeof(*cp));
+ cp->sub_opcode = MSFT_OP_LE_MONITOR_ADVERTISEMENT;
+ cp->rssi_high = handle_data->rssi_high;
+ cp->rssi_low = handle_data->rssi_low;
+ cp->rssi_low_interval = handle_data->rssi_low_interval;
+ cp->rssi_sampling_period = handle_data->rssi_sampling_period;
+ cp->cond_type = MSFT_MONITOR_ADVERTISEMENT_TYPE_ADDR;
+ skb_put_u8(skb, addr_type);
+ skb_put_data(skb, bdaddr, sizeof(*bdaddr));
+
+ address_filter = kzalloc(sizeof(*address_filter), GFP_KERNEL);
+ if (!address_filter) {
+ kfree_skb(skb);
+ return NULL;
+ }
+
+ address_filter->active = false;
+ address_filter->msft_handle = 0xff;
+ address_filter->pattern_handle = handle_data->msft_handle;
+ address_filter->mgmt_handle = handle_data->mgmt_handle;
+ address_filter->rssi_high = cp->rssi_high;
+ address_filter->rssi_low = cp->rssi_low;
+ address_filter->rssi_low_interval = cp->rssi_low_interval;
+ address_filter->rssi_sampling_period = cp->rssi_sampling_period;
+ address_filter->addr_type = addr_type;
+ bacpy(&address_filter->bdaddr, bdaddr);
+ list_add_tail(&address_filter->list, &msft->address_filters);
+
+ addr_filter_cb(skb)->pattern_handle = address_filter->pattern_handle;
+ addr_filter_cb(skb)->addr_type = addr_type;
+ bacpy(&addr_filter_cb(skb)->bdaddr, bdaddr);
+
+ err = hci_cmd_sync_queue(hdev, msft_add_address_filter_sync, skb, NULL);
+ if (err < 0) {
+ bt_dev_err(hdev, "MSFT: Add address %pMR filter err", bdaddr);
+ list_del(&address_filter->list);
+ kfree(address_filter);
+ kfree_skb(skb);
+ return NULL;
+ }
+
+ bt_dev_info(hdev, "MSFT: Add device %pMR address filter",
+ &address_filter->bdaddr);
+
+ return address_filter;
+}
+
/* 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;
struct msft_monitor_advertisement_handle_data *handle_data;
+ struct msft_monitor_addr_filter_data *n, *address_filter = NULL;
u8 addr_type;
+ u16 mgmt_handle = 0xffff;
+ struct msft_data *msft = hdev->msft_data;
ev = msft_skb_pull(hdev, skb, MSFT_EV_LE_MONITOR_DEVICE, sizeof(*ev));
if (!ev)
@@ -662,9 +1123,52 @@ static void msft_monitor_device_evt(struct hci_dev *hdev, struct sk_buff *skb)
ev->monitor_state, &ev->bdaddr);
handle_data = msft_find_handle_data(hdev, ev->monitor_handle, false);
- if (!handle_data)
+
+ if (!msft->addr_monitor_assist) {
+ if (!handle_data)
+ return;
+ mgmt_handle = handle_data->mgmt_handle;
+ goto report_state;
+ }
+
+ if (handle_data) {
+ /* Don't report any device found/lost event from pattern
+ * monitors. Pattern monitor always has its address filters for
+ * tracking devices.
+ */
+
+ address_filter = msft_find_address_data(hdev, ev->addr_type,
+ &ev->bdaddr,
+ handle_data->msft_handle);
+ if (address_filter)
+ return;
+
+ if (ev->monitor_state && handle_data->cond_type ==
+ MSFT_MONITOR_ADVERTISEMENT_TYPE_PATTERN)
+ msft_add_address_filter(hdev, ev->addr_type,
+ &ev->bdaddr, handle_data);
+
+ return;
+ }
+
+ /* This device event is not from pattern monitor.
+ * Report it if there is a corresponding address_filter for it.
+ */
+ list_for_each_entry(n, &msft->address_filters, list) {
+ if (n->active && n->msft_handle == ev->monitor_handle) {
+ mgmt_handle = n->mgmt_handle;
+ address_filter = n;
+ break;
+ }
+ }
+
+ if (!address_filter) {
+ bt_dev_warn(hdev, "MSFT: Unexpected device event %pMR, %u, %u",
+ &ev->bdaddr, ev->monitor_handle, ev->monitor_state);
return;
+ }
+report_state:
switch (ev->addr_type) {
case ADDR_LE_DEV_PUBLIC:
addr_type = BDADDR_LE_PUBLIC;
@@ -681,12 +1185,16 @@ static void msft_monitor_device_evt(struct hci_dev *hdev, struct sk_buff *skb)
return;
}
- if (ev->monitor_state)
- msft_device_found(hdev, &ev->bdaddr, addr_type,
- handle_data->mgmt_handle);
- else
- msft_device_lost(hdev, &ev->bdaddr, addr_type,
- handle_data->mgmt_handle);
+ if (ev->monitor_state) {
+ msft_device_found(hdev, &ev->bdaddr, addr_type, mgmt_handle);
+ } else {
+ if (address_filter && address_filter->active)
+ hci_cmd_sync_queue(hdev,
+ msft_cancel_address_filter_sync,
+ ERR_PTR(address_filter->msft_handle),
+ NULL);
+ msft_device_lost(hdev, &ev->bdaddr, addr_type, mgmt_handle);
+ }
}
void msft_vendor_evt(struct hci_dev *hdev, void *data, struct sk_buff *skb)
@@ -724,7 +1232,9 @@ void msft_vendor_evt(struct hci_dev *hdev, void *data, struct sk_buff *skb)
switch (*evt) {
case MSFT_EV_LE_MONITOR_DEVICE:
+ mutex_lock(&msft->filter_lock);
msft_monitor_device_evt(hdev, skb);
+ mutex_unlock(&msft->filter_lock);
break;
default: