Re: [RFC PATCH] Bluetooth: btusb: use shared ACL buffers for TP-Link UB500
From: cochcoder
Date: Wed Oct 28 2026 - 10:02:00 EST
Just checking in on this patch. Is there anything else I can do to help it move forward?
On Monday, October 12th, 2026 at 11:28 PM, Kyle Pepper <cochcoder@xxxxxxxxx> wrote:
> The TP-Link UB500 (2357:0604) reports a dedicated LE ACL pool of 27
> bytes by eight packets and a BR/EDR ACL pool of 1021 bytes by six
> packets. Sustained large-SDU LE CoC traffic exhausts the small pool,
> although this controller accepts LE traffic through the larger ACL pool.
>
> Add an explicit HCI quirk and enable it only for the tested USB ID. When
> set, treat the LE buffer as shared so the existing HCI scheduler uses
> the ACL pool. Avoid inferring this from buffer sizes globally because
> other controllers may have legitimate separate pools.
>
> Assisted-by: OpenCode:gpt-6.1-sol
> Signed-off-by: Kyle Pepper <cochcoder@xxxxxxxxx>
> ---
> Submission review notes (not part of the commit message):
> October 12 N8/UB500 no-quirk trial opens CoC, completes START, DLE
> and 2M, but sends no audio frames and logs two watchdog stalls. The
> host advertises MPS 23 instead of the baseline 512; peer MPS remains
> 168. Firmware pool semantics and simultaneous BR/EDR/LE accounting are
> not established. Keep the quirk device-specific and this proposal RFC.
>
> drivers/bluetooth/btusb_main.c | 7 ++++++-
> include/net/bluetooth/hci.h | 7 +++++++
> net/bluetooth/hci_event.c | 22 ++++++++++++++++++++++
> 3 files changed, 35 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/bluetooth/btusb_main.c b/drivers/bluetooth/btusb_main.c
> index 71d8dccb2..5e1fb26ab 100644
> --- a/drivers/bluetooth/btusb_main.c
> +++ b/drivers/bluetooth/btusb_main.c
> @@ -70,6 +70,7 @@ static struct usb_driver btusb_driver;
> #define BTUSB_INTEL_NO_WBS_SUPPORT BIT(26)
> #define BTUSB_ACTIONS_SEMI BIT(27)
> #define BTUSB_BARROT BIT(28)
> +#define BTUSB_USE_BREDR_ACL_BUFFERS_FOR_LE BIT(29)
> #define BTUSB_QUALCOMM BIT(30)
>
> static const struct usb_device_id btusb_table[] = {
> @@ -872,7 +873,8 @@ static const struct usb_device_id quirks_table[] = {
> { USB_DEVICE(0x2c4e, 0x0115), .driver_info = BTUSB_REALTEK |
> BTUSB_WIDEBAND_SPEECH },
> { USB_DEVICE(0x2357, 0x0604), .driver_info = BTUSB_REALTEK |
> - BTUSB_WIDEBAND_SPEECH },
> + BTUSB_WIDEBAND_SPEECH |
> + BTUSB_USE_BREDR_ACL_BUFFERS_FOR_LE },
> { USB_DEVICE(0x2357, 0x0607), .driver_info = BTUSB_REALTEK |
> BTUSB_WIDEBAND_SPEECH },
> { USB_DEVICE(0x0b05, 0x190e), .driver_info = BTUSB_REALTEK |
> @@ -4748,6 +4750,9 @@ static int btusb_probe(struct usb_interface *intf,
> if (id->driver_info & BTUSB_WIDEBAND_SPEECH)
> hci_set_quirk(hdev, HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED);
>
> + if (id->driver_info & BTUSB_USE_BREDR_ACL_BUFFERS_FOR_LE)
> + hci_set_quirk(hdev, HCI_QUIRK_USE_BREDR_ACL_BUFFERS_FOR_LE);
> +
> if (id->driver_info & BTUSB_INVALID_LE_STATES)
> hci_set_quirk(hdev, HCI_QUIRK_BROKEN_LE_STATES);
>
> diff --git a/include/net/bluetooth/hci.h b/include/net/bluetooth/hci.h
> index 1641d879d..d39a64663 100644
> --- a/include/net/bluetooth/hci.h
> +++ b/include/net/bluetooth/hci.h
> @@ -375,6 +375,13 @@ enum {
> */
> HCI_QUIRK_BROKEN_READ_PAGE_SCAN_TYPE,
>
> + /* When this quirk is set, LE connections use the BR/EDR ACL buffer
> + * pool instead of the dedicated LE pool reported by the controller.
> + * This is required for controllers that expose an unusably small LE
> + * pool while accepting LE traffic through their shared ACL pool.
> + */
> + HCI_QUIRK_USE_BREDR_ACL_BUFFERS_FOR_LE,
> +
> __HCI_NUM_QUIRKS,
> };
>
> diff --git a/net/bluetooth/hci_event.c b/net/bluetooth/hci_event.c
> index c055d16cf..9936f6365 100644
> --- a/net/bluetooth/hci_event.c
> +++ b/net/bluetooth/hci_event.c
> @@ -1227,6 +1227,24 @@ static u8 hci_cc_pin_code_neg_reply(struct hci_dev *hdev, void *data,
> return rp->status;
> }
>
> +/* Use the shared ACL pool for controllers whose dedicated LE pool is known to
> + * be unusable for sustained large-SDU traffic.
> + */
> +static void hci_le_use_shared_acl_buffer(struct hci_dev *hdev)
> +{
> + if (hci_test_quirk(hdev, HCI_QUIRK_USE_BREDR_ACL_BUFFERS_FOR_LE) &&
> + hdev->le_mtu == HCI_MIN_LE_MTU &&
> + hdev->acl_mtu > hdev->le_mtu && hdev->acl_pkts) {
> + bt_dev_dbg(hdev,
> + "LE buffer at minimum (%u:%u), sharing ACL buffer %u:%u",
> + hdev->le_mtu, hdev->le_pkts,
> + hdev->acl_mtu, hdev->acl_pkts);
> + hdev->le_mtu = 0;
> + hdev->le_pkts = 0;
> + hdev->le_cnt = 0;
> + }
> +}
> +
> static u8 hci_cc_le_read_buffer_size(struct hci_dev *hdev, void *data,
> struct sk_buff *skb)
> {
> @@ -1247,6 +1265,8 @@ static u8 hci_cc_le_read_buffer_size(struct hci_dev *hdev, void *data,
> if (hdev->le_mtu && hdev->le_mtu < HCI_MIN_LE_MTU)
> return HCI_ERROR_INVALID_PARAMETERS;
>
> + hci_le_use_shared_acl_buffer(hdev);
> +
> return rp->status;
> }
>
> @@ -3939,6 +3959,8 @@ static u8 hci_cc_le_read_buffer_size_v2(struct hci_dev *hdev, void *data,
> if (hdev->le_mtu && hdev->le_mtu < HCI_MIN_LE_MTU)
> return HCI_ERROR_INVALID_PARAMETERS;
>
> + hci_le_use_shared_acl_buffer(hdev);
> +
> return rp->status;
> }
>
>
> base-commit: 964430c038967fd06643e866f68115664e1a1bd1
>