| 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 |
| |