blob: 27cd20ce0721f18c9fef10e6bfb76a7d2a0cef15 [file] [log] [blame]
From 5861d188e64096639ab86ef64447a612f0ad761d Mon Sep 17 00:00:00 2001
From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Date: Tue, 14 Jul 2020 16:12:00 -0700
Subject: [PATCH] BACKPORT: FROMLIST: Bluetooth: btusb: Add support for queuing
during polling interval
This makes btusb to queue ACL and events during a polling interval
by using of a delayed work, with the interval working as a time window
where frames received from different endpoints are considered to be
arrived at same time and then attempt to resolve potential conflics by
processing the events ahead of ACL packets.
It worth noting though that priorizing events over ACL data may result
in inverting the order compared to how they appeared over the air, for
instance there may be packets received before a disconnect event that
will be discarded and unencrypted packets received before encryption
change which would considered encrypted, because of these potential
changes on the order the support for queuing during the polling
interval is not enabled by default so platforms have the following
means to enable it:
At build-time:
CONFIG_BT_HCIBTUSB_INTERVAL=y
At runtime with use of module option:
enable_interval
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
(am from https://patchwork.kernel.org/patch/11663935/)
Changes from original commit:
fix some whitespace inconsistencies
Signed-off-by: Archie Pusaka <apusaka@chromium.org>
BUG=b:150892513
TEST=run quick sanity test
Change-Id: I732dfb436460cce99461c916168b52d61f1e4f6a
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/kernel/+/2312116
Reviewed-by: Alain Michaud <alainm@chromium.org>
Commit-Queue: Alain Michaud <alainm@chromium.org>
Tested-by: Alain Michaud <alainm@chromium.org>
(cherry picked from commit e245cc6a3be8cbacc5361ab16aafe043f0680a7b)
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/kernel/+/2315363
Tested-by: Archie Pusaka <apusaka@chromium.org>
---
drivers/bluetooth/Kconfig | 7 ++++
drivers/bluetooth/btusb.c | 84 +++++++++++++++++++++++++++++++++++----
2 files changed, 84 insertions(+), 7 deletions(-)
diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
--- a/drivers/bluetooth/Kconfig
+++ b/drivers/bluetooth/Kconfig
@@ -41,6 +41,13 @@ config BT_HCIBTUSB_AUTOSUSPEND
This can be overridden by passing btusb.enable_autosuspend=[y|n]
on the kernel commandline.
+config BT_HCIBTUSB_INTERVAL
+ bool "Enable notification of USB polling interval"
+ depends on BT_HCIBTUSB
+ help
+ Say Y here to enable notification of USB polling interval for
+ Bluetooth USB devices by default.
+
config BT_HCIBTUSB_BCM
bool "Broadcom protocol support"
depends on BT_HCIBTUSB
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -30,7 +30,7 @@
static bool disable_scofix;
static bool force_scofix;
static bool enable_autosuspend = IS_ENABLED(CONFIG_BT_HCIBTUSB_AUTOSUSPEND);
-
+static bool enable_interval = IS_ENABLED(CONFIG_BT_HCIBTUSB_INTERVAL);
static bool reset = true;
static struct usb_driver btusb_driver;
@@ -542,6 +542,10 @@ struct btusb_data {
struct work_struct work;
struct work_struct waker;
+ struct delayed_work rx_work;
+
+ struct sk_buff_head acl_q;
+ struct sk_buff_head evt_q;
struct usb_anchor deferred;
struct usb_anchor tx_anchor;
@@ -578,7 +582,7 @@ struct btusb_data {
int isoc_altsetting;
int suspend_count;
- int (*recv_event)(struct hci_dev *hdev, struct sk_buff *skb);
+ int (*recv_event)(struct btusb_data *data, struct sk_buff *skb);
int (*recv_acl)(struct hci_dev *hdev, struct sk_buff *skb);
int (*recv_bulk)(struct btusb_data *data, void *buffer, int count);
@@ -729,7 +733,7 @@ static int btusb_recv_intr(struct btusb_data *data, void *buffer, int count)
if (!hci_skb_expect(skb)) {
/* Complete frame */
- data->recv_event(data->hdev, skb);
+ data->recv_event(data, skb);
skb = NULL;
}
}
@@ -740,6 +744,25 @@ static int btusb_recv_intr(struct btusb_data *data, void *buffer, int count)
return err;
}
+static int btusb_rx_queue(struct btusb_data *data, struct sk_buff *skb,
+ struct sk_buff_head *queue, unsigned int interval)
+{
+ skb_queue_tail(queue, skb);
+
+ schedule_delayed_work(&data->rx_work, interval);
+
+ return 0;
+}
+
+static int btusb_recv_acl(struct btusb_data *data, struct sk_buff *skb)
+{
+ if (!enable_interval)
+ return hci_recv_frame(data->hdev, skb);
+
+ /* TODO: Calculate polling interval based on endpoint bInterval? */
+ return btusb_rx_queue(data, skb, &data->acl_q, msecs_to_jiffies(1));
+}
+
static int btusb_recv_bulk(struct btusb_data *data, void *buffer, int count)
{
struct sk_buff *skb;
@@ -787,7 +810,7 @@ static int btusb_recv_bulk(struct btusb_data *data, void *buffer, int count)
if (!hci_skb_expect(skb)) {
/* Complete frame */
- data->recv_acl(data->hdev, skb);
+ btusb_recv_acl(data, skb);
skb = NULL;
}
}
@@ -1398,9 +1421,13 @@ static int btusb_close(struct hci_dev *hdev)
BT_DBG("%s", hdev->name);
+ cancel_delayed_work(&data->rx_work);
cancel_work_sync(&data->work);
cancel_work_sync(&data->waker);
+ skb_queue_purge(&data->acl_q);
+ skb_queue_purge(&data->evt_q);
+
clear_bit(BTUSB_ISOC_RUNNING, &data->flags);
clear_bit(BTUSB_BULK_RUNNING, &data->flags);
clear_bit(BTUSB_INTR_RUNNING, &data->flags);
@@ -1432,6 +1459,11 @@ static int btusb_flush(struct hci_dev *hdev)
BT_DBG("%s", hdev->name);
+ cancel_delayed_work(&data->rx_work);
+
+ skb_queue_purge(&data->acl_q);
+ skb_queue_purge(&data->evt_q);
+
usb_kill_anchored_urbs(&data->tx_anchor);
btusb_free_frags(data);
@@ -1795,6 +1827,25 @@ static void btusb_waker(struct work_struct *work)
usb_autopm_put_interface(data->intf);
}
+static void btusb_rx_dequeue(struct btusb_data *data,
+ struct sk_buff_head *queue)
+{
+ struct sk_buff *skb;
+
+ while ((skb = skb_dequeue(queue)))
+ hci_recv_frame(data->hdev, skb);
+}
+
+static void btusb_rx_work(struct work_struct *work)
+{
+ struct btusb_data *data = container_of(work, struct btusb_data,
+ rx_work.work);
+
+ /* Process HCI event packets so states changes are synchronized first */
+ btusb_rx_dequeue(data, &data->evt_q);
+ btusb_rx_dequeue(data, &data->acl_q);
+}
+
static int btusb_setup_bcm92035(struct hci_dev *hdev)
{
struct sk_buff *skb;
@@ -1996,9 +2047,11 @@ static int btusb_recv_bulk_intel(struct btusb_data *data, void *buffer,
return btusb_recv_bulk(data, buffer, count);
}
-static int btusb_recv_event_intel(struct hci_dev *hdev, struct sk_buff *skb)
+static int btusb_recv_event_intel(struct btusb_data *data, struct sk_buff *skb)
{
- if (btintel_test_flag(hdev, INTEL_BOOTLOADER)) {
+ struct hci_dev *hdev = data->hdev;
+
+ if (btintel_test_flag(hdev, INTEL_BOOTLOADER)) {
struct hci_event_hdr *hdr = (void *)skb->data;
if (skb->len > HCI_EVENT_HDR_SIZE && hdr->evt == 0xff &&
@@ -3598,6 +3651,15 @@ static bool btusb_prevent_wake(struct hci_dev *hdev)
return !device_may_wakeup(&data->udev->dev);
}
+static int btusb_recv_evt(struct btusb_data *data, struct sk_buff *skb)
+{
+ if (!enable_interval)
+ return hci_recv_frame(data->hdev, skb);
+
+ /* Don't delay event processing */
+ return btusb_rx_queue(data, skb, &data->evt_q, 0);
+}
+
static int btusb_shutdown_qca(struct hci_dev *hdev)
{
struct sk_buff *skb;
@@ -3695,6 +3757,11 @@ static int btusb_probe(struct usb_interface *intf,
INIT_WORK(&data->work, btusb_work);
INIT_WORK(&data->waker, btusb_waker);
+ INIT_DELAYED_WORK(&data->rx_work, btusb_rx_work);
+
+ skb_queue_head_init(&data->acl_q);
+ skb_queue_head_init(&data->evt_q);
+
init_usb_anchor(&data->deferred);
init_usb_anchor(&data->tx_anchor);
spin_lock_init(&data->txlock);
@@ -3708,7 +3775,7 @@ static int btusb_probe(struct usb_interface *intf,
priv_size = 0;
- data->recv_event = hci_recv_frame;
+ data->recv_event = btusb_recv_evt;
data->recv_bulk = btusb_recv_bulk;
if (id->driver_info & BTUSB_INTEL_COMBINED) {
@@ -4181,6 +4248,9 @@ MODULE_PARM_DESC(force_scofix, "Force fixup of wrong SCO buffers size");
module_param(enable_autosuspend, bool, 0644);
MODULE_PARM_DESC(enable_autosuspend, "Enable USB autosuspend by default");
+module_param(enable_interval, bool, 0644);
+MODULE_PARM_DESC(enable_interval, "Enable USB polling interval by default");
+
module_param(reset, bool, 0644);
MODULE_PARM_DESC(reset, "Send HCI reset command on initialization");
--
2.33.0.464.g1972c5931b-goog