diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..c96967b --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +tests/issue63-btusb-zlp/*.patch -whitespace +tests/issue63-btusb-zlp/*.sh text eol=lf diff --git a/tests/issue63-btusb-zlp/0001-Bluetooth-btusb-add-AIC-bulk-TX-ZLP-quirk.patch b/tests/issue63-btusb-zlp/0001-Bluetooth-btusb-add-AIC-bulk-TX-ZLP-quirk.patch new file mode 100644 index 0000000..01e5e30 --- /dev/null +++ b/tests/issue63-btusb-zlp/0001-Bluetooth-btusb-add-AIC-bulk-TX-ZLP-quirk.patch @@ -0,0 +1,74 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Shen Mintao +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 +--- + 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 diff --git a/tests/issue63-btusb-zlp/README.md b/tests/issue63-btusb-zlp/README.md new file mode 100644 index 0000000..c257882 --- /dev/null +++ b/tests/issue63-btusb-zlp/README.md @@ -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. diff --git a/tests/issue63-btusb-zlp/btusb-zlp-test.sh b/tests/issue63-btusb-zlp/btusb-zlp-test.sh new file mode 100755 index 0000000..99da1b2 --- /dev/null +++ b/tests/issue63-btusb-zlp/btusb-zlp-test.sh @@ -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