test: add btusb ZLP quirk for issue 63

This commit is contained in:
Shen Mintao
2026-07-17 21:00:18 +08:00
parent 8006daf6c7
commit cbc12c7bdb
4 changed files with 333 additions and 0 deletions
@@ -0,0 +1,74 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Shen Mintao <cx330.shen@autocore.ai>
Date: Fri, 17 Jul 2026 21:00:00 +0800
Subject: [PATCH] Bluetooth: btusb: add bulk TX ZLP quirk for AIC 8800D80
The AIC 8800D80 controller can stop returning HCI Number Of Completed
Packets events during sustained AAC A2DP traffic when driven by the
generic btusb transport. Once the controller's ACL credits are
exhausted, the connection is terminated by the HCI ACL TX watchdog.
The vendor transport sets URB_ZERO_PACKET on every ACL bulk OUT URB.
Add an equivalent, device-specific quirk to btusb for the 368b:8d81
variant so that the standard kernel driver retains control of the
device.
This is intentionally limited to the USB ID from the reproducer while
the behaviour is validated on additional AIC variants.
Link: https://github.com/shenmintao/aic8800d80/issues/63
Signed-off-by: Shen Mintao <cx330.shen@autocore.ai>
---
drivers/bluetooth/btusb.c | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/drivers/bluetooth/btusb.c b/drivers/bluetooth/btusb.c
index 830fefb342c6..97118c095369 100644
--- a/drivers/bluetooth/btusb.c
+++ b/drivers/bluetooth/btusb.c
@@ -67,6 +67,7 @@ static bool reset = true;
#define BTUSB_INTEL_NO_WBS_SUPPORT BIT(26)
#define BTUSB_ACTIONS_SEMI BIT(27)
#define BTUSB_BARROT BIT(28)
+#define BTUSB_BULK_TX_ZLP BIT(29)
static const struct usb_device_id btusb_table[] = {
/* Generic Bluetooth USB device */
@@ -178,5 +179,9 @@ MODULE_DEVICE_TABLE(usb, btusb_table);
static const struct usb_device_id quirks_table[] = {
+ /* AIC Semiconductor AIC 8800D80 */
+ { USB_DEVICE(0x368b, 0x8d81),
+ .driver_info = BTUSB_BULK_TX_ZLP },
+
/* CSR BlueCore devices */
{ USB_DEVICE(0x0a12, 0x0001), .driver_info = BTUSB_CSR },
@@ -949,6 +954,7 @@ struct btusb_data {
unsigned long flags;
bool poll_sync;
+ bool bulk_tx_zlp;
int intr_interval;
struct work_struct work;
struct work_struct waker;
@@ -2138,6 +2144,9 @@ static struct urb *alloc_bulk_urb(struct hci_dev *hdev, struct sk_buff *skb)
usb_fill_bulk_urb(urb, data->udev, pipe,
skb->data, skb->len, btusb_tx_complete, skb);
+ if (data->bulk_tx_zlp)
+ urb->transfer_flags |= URB_ZERO_PACKET;
+
skb->dev = (void *)hdev;
return urb;
@@ -4100,6 +4109,7 @@ static int btusb_probe(struct usb_interface *intf,
data->udev = interface_to_usbdev(intf);
data->intf = intf;
+ data->bulk_tx_zlp = id->driver_info & BTUSB_BULK_TX_ZLP;
INIT_WORK(&data->work, btusb_work);
INIT_WORK(&data->waker, btusb_waker);
--
2.50.1
+61
View File
@@ -0,0 +1,61 @@
# Issue #63: system `btusb` ZLP test
This test keeps the normal `aic_load_fw + btusb` architecture. It does not
restore or install the vendor `aic_btusb` driver.
The patch adds one quirk to the standard Linux `btusb` source:
- match only AICSemi `368b:8d81`;
- set `URB_ZERO_PACKET` on Bluetooth ACL bulk OUT transfers;
- leave every other `btusb` device and code path unchanged.
The test installer downloads the standard `btusb` source matching the running
kernel's upstream version, applies the patch, and installs the resulting
`btusb.ko` through DKMS. The distribution module is not overwritten and can be
restored by removing the DKMS test package.
## Install the test module
On Fedora, make sure the test build dependencies are installed:
```bash
sudo dnf install dkms gcc make kernel-devel-$(uname -r) curl patch
```
Stop Bluetooth scanning and disconnect Bluetooth devices, then run:
```bash
cd tests/issue63-btusb-zlp
sudo ./btusb-zlp-test.sh install
./btusb-zlp-test.sh status
```
`modinfo -n btusb` should point to an `updates/dkms` path. Secure Boot systems
may require the DKMS module to be signed or enrolled before it can load.
Select AAC again and play audio for at least one hour:
```bash
bluetoothctl scan off
pactl set-card-profile bluez_card.28_6F_40_46_AB_B1 a2dp-sink-aac
```
Please capture `sudo btmon -w issue63-zlp.btsnoop` and relevant `dmesg` lines if
the connection still stalls.
## Restore the distribution module
```bash
sudo ./btusb-zlp-test.sh remove
./btusb-zlp-test.sh status
```
After removal, `modinfo -n btusb` should point back to the distribution's
`kernel/drivers/bluetooth/btusb.ko` path.
## Scope
This is an A/B diagnostic patch, not a production workaround. If AAC remains
stable, the small kernel patch can be submitted upstream. If it still fails,
the next isolated experiment should add an AIC-specific runtime-PM quirk rather
than replacing the standard Bluetooth driver.
+196
View File
@@ -0,0 +1,196 @@
#!/usr/bin/env bash
set -euo pipefail
PACKAGE_NAME="btusb-aic-zlp"
PACKAGE_VERSION="0.1"
KERNEL_RELEASE="${KERNEL_RELEASE:-$(uname -r)}"
UPSTREAM_VERSION="${UPSTREAM_VERSION:-$(printf '%s' "$KERNEL_RELEASE" | sed -nE 's/^([0-9]+\.[0-9]+\.[0-9]+).*/\1/p')}"
UPSTREAM_TAG="${UPSTREAM_TAG:-v${UPSTREAM_VERSION}}"
KERNEL_BUILD_DIR="/lib/modules/${KERNEL_RELEASE}/build"
SOURCE_DIR="/usr/src/${PACKAGE_NAME}-${PACKAGE_VERSION}"
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
PATCH_FILE="${SCRIPT_DIR}/0001-Bluetooth-btusb-add-AIC-bulk-TX-ZLP-quirk.patch"
BASE_URL="https://raw.githubusercontent.com/gregkh/linux/${UPSTREAM_TAG}/drivers/bluetooth"
log() {
printf '[btusb-zlp-test] %s\n' "$*"
}
die() {
printf '[btusb-zlp-test] ERROR: %s\n' "$*" >&2
exit 1
}
require_root() {
[ "$(id -u)" -eq 0 ] || die "run this command as root"
}
require_command() {
command -v "$1" >/dev/null 2>&1 || die "required command not found: $1"
}
stop_bluetooth() {
if command -v systemctl >/dev/null 2>&1; then
systemctl stop bluetooth.service 2>/dev/null || true
fi
}
start_bluetooth() {
if command -v systemctl >/dev/null 2>&1; then
systemctl start bluetooth.service 2>/dev/null || true
fi
}
rollback_test_install() {
log "rolling back to the distribution btusb module"
dkms remove -m "${PACKAGE_NAME}" -v "${PACKAGE_VERSION}" --all >/dev/null 2>&1 || true
rm -rf -- "${SOURCE_DIR}"
depmod -a "${KERNEL_RELEASE}" || true
modprobe btusb || true
start_bluetooth
}
activate_test_module() {
stop_bluetooth
if ! modprobe -r btusb; then
rollback_test_install
die "could not unload btusb; disconnect active Bluetooth devices and retry"
fi
if ! modprobe btusb; then
rollback_test_install
die "the patched btusb could not load; the distribution module was restored"
fi
start_bluetooth
}
write_build_files() {
cat > "${SOURCE_DIR}/Makefile" <<'EOF'
KVER ?= $(shell uname -r)
KDIR ?= /lib/modules/$(KVER)/build
obj-m += btusb.o
btusb-y := drivers/bluetooth/btusb.o
ccflags-y += -I$(src)/drivers/bluetooth
all:
$(MAKE) -C $(KDIR) M=$(CURDIR) modules
clean:
$(MAKE) -C $(KDIR) M=$(CURDIR) clean
EOF
cat > "${SOURCE_DIR}/dkms.conf" <<'EOF'
PACKAGE_NAME="btusb-aic-zlp"
PACKAGE_VERSION="0.1"
MAKE[0]="make KVER=${kernelver}"
CLEAN="make KVER=${kernelver} clean"
BUILT_MODULE_NAME[0]="btusb"
BUILT_MODULE_LOCATION[0]="."
DEST_MODULE_LOCATION[0]="/updates/dkms"
AUTOINSTALL="no"
EOF
}
download_sources() {
local file
install -d "${SOURCE_DIR}/drivers/bluetooth"
for file in btusb.c btintel.h btbcm.h btrtl.h btmtk.h; do
log "downloading ${file} from Linux ${UPSTREAM_TAG}"
curl --fail --location --silent --show-error \
"${BASE_URL}/${file}" \
--output "${SOURCE_DIR}/drivers/bluetooth/${file}"
done
}
install_test_module() {
require_root
require_command curl
require_command dkms
require_command make
require_command modprobe
require_command patch
[ -n "${UPSTREAM_VERSION}" ] || die "could not derive an upstream version from ${KERNEL_RELEASE}"
[ -r "${KERNEL_BUILD_DIR}/Makefile" ] || die "kernel build tree not found: ${KERNEL_BUILD_DIR}"
[ -r "${PATCH_FILE}" ] || die "patch file not found: ${PATCH_FILE}"
if command -v lsusb >/dev/null 2>&1 && ! lsusb -d 368b:8d81 >/dev/null 2>&1; then
die "the test patch is restricted to USB device 368b:8d81, which is not connected"
fi
if dkms status -m "${PACKAGE_NAME}" -v "${PACKAGE_VERSION}" 2>/dev/null | grep -q .; then
log "removing an earlier test build"
dkms remove -m "${PACKAGE_NAME}" -v "${PACKAGE_VERSION}" --all || true
fi
rm -rf -- "${SOURCE_DIR}"
download_sources
write_build_files
log "applying the AIC bulk TX ZLP quirk"
patch --directory "${SOURCE_DIR}" --strip 1 --input "${PATCH_FILE}"
log "building patched system btusb for ${KERNEL_RELEASE}"
dkms add -m "${PACKAGE_NAME}" -v "${PACKAGE_VERSION}"
dkms build -m "${PACKAGE_NAME}" -v "${PACKAGE_VERSION}" -k "${KERNEL_RELEASE}"
dkms install -m "${PACKAGE_NAME}" -v "${PACKAGE_VERSION}" \
-k "${KERNEL_RELEASE}" --force
depmod -a "${KERNEL_RELEASE}"
activate_test_module
log "loaded module: $(modinfo -n btusb)"
log "test AAC playback for at least one hour; use '$0 remove' to restore the distribution module"
}
remove_test_module() {
require_root
require_command dkms
require_command modprobe
stop_bluetooth
modprobe -r btusb || {
start_bluetooth
die "could not unload btusb; disconnect active Bluetooth devices and retry"
}
dkms remove -m "${PACKAGE_NAME}" -v "${PACKAGE_VERSION}" --all || true
rm -rf -- "${SOURCE_DIR}"
depmod -a "${KERNEL_RELEASE}"
modprobe btusb || {
start_bluetooth
die "the distribution btusb module could not be loaded"
}
start_bluetooth
log "restored module: $(modinfo -n btusb)"
}
show_status() {
printf 'kernel: %s\n' "${KERNEL_RELEASE}"
printf 'upstream tag: %s\n' "${UPSTREAM_TAG}"
printf 'btusb module: %s\n' "$(modinfo -n btusb 2>/dev/null || printf 'not found')"
printf 'dkms status: %s\n' "$(dkms status -m "${PACKAGE_NAME}" -v "${PACKAGE_VERSION}" 2>/dev/null || printf 'not installed')"
}
case "${1:-}" in
install)
install_test_module
;;
remove)
remove_test_module
;;
status)
show_status
;;
*)
printf 'Usage: %s {install|remove|status}\n' "$0" >&2
exit 2
;;
esac