test: unify legacy Wi-Fi Bluetooth and ZLP support

# Conflicts:
#	.gitattributes
#	README.md
This commit is contained in:
Shen Mintao
2026-07-25 15:54:23 +08:00
17 changed files with 1135 additions and 35 deletions
+7
View File
@@ -2,3 +2,10 @@ fw/aic8800D80/*.txt text eol=lf
fw/aic8800D80/*.bin binary
fw/aic8800DC/*.txt text eol=lf
fw/aic8800DC/*.bin binary
/.gitattributes text eol=lf
drivers/aic8800/aic_zlp_quirk/*.c text eol=lf
drivers/aic8800/aic_zlp_quirk/Makefile text eol=lf
tests/issue63-zlp-quirk/*.conf text eol=lf
tests/issue63-zlp-quirk/*.md text eol=lf
tests/issue63-zlp-quirk/*.sh text eol=lf
tests/issue63-zlp-quirk/Makefile text eol=lf
+8 -1
View File
@@ -2,12 +2,19 @@
## Overview
This automated installation script (`install.sh`) simplifies the process of installing the AIC8800D80 WiFi driver on Linux systems.
This automated installation script (`install.sh`) installs the AIC8800 Wi-Fi
driver and firmware loader on Linux systems. Wi-Fi/Bluetooth combo adapters use
the standard kernel `btusb` driver after firmware initialization. For USB
device `368b:8d81`, it also installs a device-scoped ACL bulk TX ZLP companion
module without replacing the distribution's `btusb.ko`.
- Automatic Secure Boot detection
- Automatic dependency installation
- Automatic driver compilation and installation
- Automatic module loading on boot
- Automatic cleanup of obsolete `aic_btusb` configuration
- Automatic `aic_zlp_quirk` handling for the validated `368b:8d81` device
- Wi-Fi-only and Wi-Fi/Bluetooth combo adapter support from the same branch
- Comprehensive error handling
- Colored output and logging
- Compatible with Ubuntu, Debian, Fedora, and derivatives (DKMS supported `dkms.conf`)
+69 -8
View File
@@ -1,4 +1,4 @@
# AIC8800 Linux Driver
# AIC8800 Linux Wi-Fi and Bluetooth Driver
This driver supports AIC8800-family chipsets used by devices such as the Tenda U11, AX913B, and TP-Link Archer TX1U Nano.
> **Legacy MCU revision 1 branch:** You are viewing `legacy-mcu1`. This branch
@@ -12,14 +12,26 @@ This driver supports AIC8800-family chipsets used by devices such as the Tenda U
> `chip_mcu_id=0` hardware or when the MCU revision is unknown. See the
> [D80](tests/issue58-mcu1-legacy-fw/README.md) and
> [DC/DW](tests/issue71-mcu1-v3-profile/README.md) support notes before installing.
>
> After switching branches, rerun `sudo ./install.sh` and reboot; switching the
> Git branch alone does not replace firmware already installed under
> `/lib/firmware`.
Added support for devices with Vendor ID 368B (tested).
Tested on Linux kernel 6.16 with Ubuntu 25.04 and 6.1.0.27 with Debian 12.
> **Bluetooth support:** This branch initializes the integrated Bluetooth
> controller for the kernel's standard `btusb` driver. It does not contain or
> install `aic_btusb`.
The same driver supports Wi-Fi-only adapters and Wi-Fi/Bluetooth combo
adapters. On combo devices, `aic_load_fw` uploads the AIC firmware and the
standard Linux `btusb` driver handles the Bluetooth HCI interface. The obsolete
custom `aic_btusb` module is not used.
USB device `368b:8d81` also uses the bundled `aic_zlp_quirk` companion module.
It adds the Bluetooth ACL bulk TX zero-length-packet behavior validated in
[issue #63](https://github.com/shenmintao/aic8800d80/issues/63), while leaving
the distribution's original `btusb.ko` installed and bound to the device. The
quirk is filtered to that VID:PID and fails closed when the required kernel
probe support is unavailable.
### Disclaimer
I did not develop this software, The code is sourced from the Tenda U11 driver. I only made some modifications to the code to adapt it to newer kernel versions. Apart from compilation issues, I am unable to address other problems.
@@ -34,18 +46,18 @@ Before installing the driver, delete all aic8800-related folders under /lib/firm
#### Method 2: Manual Installation
#### Copy udev rules:
Copy the aic.rules file to /lib/udev/rules.d/:
Copy the aic.rules file to /usr/lib/udev/rules.d/:
```bash
sudo cp aic.rules /lib/udev/rules.d/
sudo cp aic.rules /usr/lib/udev/rules.d/
```
#### Copy firmware:
Copy the aic8800D80 folder from ./fw to /lib/firmware/:
Copy the firmware directories from `./fw` to `/lib/firmware/`:
```bash
sudo cp -r ./fw/aic8800D80 /lib/firmware/
sudo cp -r ./fw/aic8800* /lib/firmware/
```
#### Navigate to the driver directory:
@@ -112,3 +124,52 @@ If the device is still not active, check the kernel logs for any errors related
sudo dmesg
```
### Bluetooth on Combo Adapters
Bluetooth support does not require a separate AIC transport module. After
`aic_load_fw` initializes a combo adapter, the kernel automatically binds its
Bluetooth interface to `btusb`. A Wi-Fi-only adapter does not expose that
interface, so the Bluetooth path remains inactive.
Verify the expected modules and controller with:
```bash
lsmod | grep -E 'aic_load_fw|aic8800_fdrv|aic_zlp_quirk|btusb'
lsusb -t
bluetoothctl list
```
To scan after a controller appears:
```bash
bluetoothctl
power on
scan on
```
If Bluetooth is missing or reports HCI timeouts, run the read-only diagnostic
script and attach its output together with the current boot log:
```bash
chmod +x diagnose_bt.sh
sudo ./diagnose_bt.sh
sudo journalctl -k -b --no-pager
```
The installer removes active references to the retired `aic_btusb` integration.
It does not force-load `btusb` or globally change the Bluetooth rfkill state;
normal kernel device matching and the user's system policy remain in control.
For `368b:8d81`, verify the ZLP hook and its injection counter while Bluetooth
traffic is active:
```bash
cat /sys/module/aic_zlp_quirk/parameters/hook
cat /sys/module/aic_zlp_quirk/parameters/injections
```
The Wi-Fi-reset recovery behavior tracked in
[issue #53](https://github.com/shenmintao/aic8800d80/issues/53) remains a known
limitation: after an airplane-mode or hotspot reset, Bluetooth may require a
physical unplug/replug of the adapter.
+4
View File
@@ -1,3 +1,4 @@
# AIC8800 USB mass-storage mode switch rules
KERNEL=="sd*", ATTRS{idVendor}=="a69c", ATTRS{idProduct}=="5721", SYMLINK+="aicudisk", RUN+="/usr/bin/eject /dev/%k"
KERNEL=="sd*", ATTRS{idVendor}=="a69c", ATTRS{idProduct}=="5723", SYMLINK+="tendaudisk", RUN+="/usr/bin/eject /dev/%k"
KERNEL=="sd*", ATTRS{idVendor}=="a69c", ATTRS{idProduct}=="5724", SYMLINK+="ugreenax900", RUN+="/usr/bin/eject /dev/%k"
@@ -7,3 +8,6 @@ KERNEL=="sd*", ATTRS{idVendor}=="a69c", ATTRS{idProduct}=="5727", SYMLINK+="ten
KERNEL=="sd*", ATTRS{idVendor}=="a69c", ATTRS{idProduct}=="572a", SYMLINK+="tendaudiskv5", RUN+="/usr/bin/eject /dev/%k"
KERNEL=="sd*", ATTRS{idVendor}=="a69c", ATTRS{idProduct}=="572c", SYMLINK+="cudydiskv2", RUN+="/usr/bin/eject /dev/%k"
KERNEL=="sd*", ATTRS{idVendor}=="a69c", ATTRS{idProduct}=="572f", SYMLINK+="aicudisk572f", RUN+="/usr/bin/eject /dev/%k"
# AIC8800D80 "Pandora" clone: switch 1111:1111 to its operational USB ID.
ACTION=="add", SUBSYSTEM=="usb", ATTR{idVendor}=="1111", ATTR{idProduct}=="1111", RUN+="/bin/sh -c 'if [ -x /usr/sbin/usb_modeswitch ]; then exec /usr/sbin/usb_modeswitch -c /etc/usb_modeswitch.d/1111:1111; elif [ -x /usr/bin/usb_modeswitch ]; then exec /usr/bin/usb_modeswitch -c /etc/usb_modeswitch.d/1111:1111; fi'"
+5 -1
View File
@@ -56,7 +56,11 @@ sudo rpm-ostree install "$rpm_path"
sudo systemctl reboot
~~~
After reboot, the Wi-Fi module and firmware loader will be available.
After reboot, the Wi-Fi module and firmware loader will be available. On combo
adapters, Bluetooth is handled by the standard `btusb` kernel module after
firmware initialization. USB device `368b:8d81` also autoloads the packaged
`aic_zlp_quirk` companion module for its required Bluetooth ACL bulk TX zero
packet behavior; the distribution's `btusb` module is not replaced.
This RPM is built for the kernel reported by `uname -r`. Rebuild and reinstall
it after a Bazzite kernel upgrade.
+21 -5
View File
@@ -1,11 +1,11 @@
%global commit d10bc52903f30a45e49777fd8254765403c9eb64
%global commit 13baa9b0b1692f94233bcf6e752060b408062026
%global shortcommit %(echo %{commit} | cut -c1-7)
%{!?kver:%global kver %(uname -r)}
Name: aic8800d80
Version: %{shortcommit}
Release: 1%{?dist}
Summary: AIC8800D80 USB Wi-Fi driver
Summary: AIC8800 USB Wi-Fi, Bluetooth firmware, and ZLP quirk driver
License: GPL-2.0-only
URL: https://github.com/shenmintao/aic8800d80
@@ -22,9 +22,11 @@ Requires(post): /usr/sbin/depmod
Requires(postun): /usr/sbin/depmod
%description
Out-of-tree AIC8800D80 USB Wi-Fi driver with udev mode-switch rules and all
firmware variants shipped by the upstream repository. This main-branch package
does not install a custom Bluetooth transport driver.
Out-of-tree AIC8800 USB driver with Wi-Fi support, Bluetooth firmware loading,
udev mode-switch rules, and all firmware variants shipped by the upstream
repository. Combo adapters use the standard Linux btusb transport driver. A
device-scoped companion module supplies the required ACL bulk TX ZLP behavior
for USB device 368b:8d81 without replacing the distribution btusb module.
%prep
%autosetup -n %{name}-%{commit}
@@ -44,6 +46,10 @@ install -Dpm0644 \
drivers/aic8800/aic_load_fw/aic_load_fw.ko \
%{buildroot}/usr/lib/modules/%{kver}/kernel/drivers/net/wireless/aic8800/aic_load_fw.ko
install -Dpm0644 \
drivers/aic8800/aic_zlp_quirk/aic_zlp_quirk.ko \
%{buildroot}/usr/lib/modules/%{kver}/kernel/drivers/bluetooth/aic8800/aic_zlp_quirk.ko
install -Dpm0644 \
aic.rules \
%{buildroot}/usr/lib/udev/rules.d/90-aic8800-mode-switch.rules
@@ -65,11 +71,21 @@ cp -a fw/aic8800* %{buildroot}/usr/lib/firmware/
%dir /usr/lib/modules/%{kver}/kernel/drivers/net/wireless/aic8800
/usr/lib/modules/%{kver}/kernel/drivers/net/wireless/aic8800/aic8800_fdrv.ko
/usr/lib/modules/%{kver}/kernel/drivers/net/wireless/aic8800/aic_load_fw.ko
/usr/lib/modules/%{kver}/kernel/drivers/bluetooth/aic8800/aic_zlp_quirk.ko
/usr/lib/udev/rules.d/90-aic8800-mode-switch.rules
%config(noreplace) /etc/usb_modeswitch.d/1111:1111
/usr/lib/firmware/aic8800*
%changelog
* Sat Jul 25 2026 Shen Mintao <shenmintao@gmail.com> - 13baa9b-1
- Unify Wi-Fi and standard-btusb Bluetooth support in one package.
- Add the device-scoped 368b:8d81 ACL bulk TX ZLP companion module.
- Clean up obsolete aic_btusb and issue #63 diagnostic installations.
* Fri Jul 24 2026 Shen Mintao <shenmintao@gmail.com> - 88dbc0a-1
- Use the unified Wi-Fi and Bluetooth installer/package description.
- Keep Bluetooth transport on the standard Linux btusb driver.
* Tue Jul 14 2026 Shen Mintao <shenmintao@gmail.com> - d10bc52-1
- Build the current main branch without the legacy custom Bluetooth module.
- Package all firmware variants and current mode-switch rules.
+158
View File
@@ -0,0 +1,158 @@
#!/bin/bash
# Diagnose Bluetooth on AIC8800 Wi-Fi/Bluetooth combo adapters.
#
# aic_load_fw initializes the device and uploads the AIC firmware. The
# standard in-kernel btusb driver must then bind to the Bluetooth HCI USB
# interface. The removed aic_btusb module must not be installed or loaded.
echo "=== AIC8800 Bluetooth diagnostics ==="
echo ""
echo "1. AIC USB devices:"
if command -v lsusb >/dev/null 2>&1; then
lsusb | grep -iE 'a69c|368b|3625' || echo " No known AIC USB ID found"
else
echo " lsusb is not installed"
fi
echo ""
echo "2. USB interfaces and bound drivers:"
bt_interface_found=false
btusb_bound=false
zlp_target_found=false
for dev in /sys/bus/usb/devices/*; do
[ -f "$dev/idVendor" ] || continue
[ -f "$dev/idProduct" ] || continue
vid=$(cat "$dev/idVendor" 2>/dev/null)
pid=$(cat "$dev/idProduct" 2>/dev/null)
case "$vid" in
a69c|368b|3625) ;;
*) continue ;;
esac
echo " Device: $vid:$pid"
if [ "$vid" = "368b" ] && [ "$pid" = "8d81" ]; then
zlp_target_found=true
fi
for intf in "$dev"/*:*; do
[ -d "$intf" ] || continue
class=$(cat "$intf/bInterfaceClass" 2>/dev/null)
subclass=$(cat "$intf/bInterfaceSubClass" 2>/dev/null)
protocol=$(cat "$intf/bInterfaceProtocol" 2>/dev/null)
driver=$(basename "$(readlink "$intf/driver" 2>/dev/null)" 2>/dev/null)
echo " $(basename "$intf"): ${class:-??}/${subclass:-??}/${protocol:-??} -> ${driver:-unbound}"
if [ "$driver" = "btusb" ]; then
btusb_bound=true
bt_interface_found=true
elif [ "$class" = "e0" ] && [ "$subclass" = "01" ] && [ "$protocol" = "01" ]; then
bt_interface_found=true
fi
done
done
echo ""
echo "3. Relevant kernel modules (combo expectation: aic_load_fw + btusb):"
lsmod | grep -E '^(aic_load_fw|aic8800_fdrv|aic_zlp_quirk|aic_btusb|btusb|bluetooth)[[:space:]]' || \
echo " No related module is currently loaded"
echo ""
echo "4. Bluetooth controllers:"
if command -v bluetoothctl >/dev/null 2>&1; then
bluetoothctl list 2>/dev/null || echo " No controller reported by bluetoothctl"
elif command -v hciconfig >/dev/null 2>&1; then
hciconfig -a 2>/dev/null || echo " No controller reported by hciconfig"
else
echo " BlueZ command-line tools are not installed"
fi
echo ""
echo "5. Firmware, btusb, ZLP quirk, and HCI log messages:"
dmesg 2>/dev/null | grep -iE 'fw_patch|fw_adid|aicbt|aic_zlp_quirk|bluetooth|btusb|hci' | tail -80 || true
echo ""
echo "6. Bluetooth rfkill state:"
if command -v rfkill >/dev/null 2>&1; then
rfkill list bluetooth 2>/dev/null || echo " No Bluetooth rfkill entry"
else
echo " rfkill is not installed"
fi
echo ""
echo "7. Obsolete aic_btusb installation/configuration:"
legacy_refs_found=false
mapfile -t legacy_modprobe_files < <(
grep -RIlE '^[[:space:]]*(softdep|alias)[^#]*aic_btusb([[:space:]]|$)' \
/etc/modprobe.d /run/modprobe.d /usr/local/lib/modprobe.d \
/usr/lib/modprobe.d /lib/modprobe.d 2>/dev/null || true
)
mapfile -t legacy_udev_files < <(
grep -RIl 'aic_btusb/new_id' \
/etc/udev/rules.d /run/udev/rules.d \
/usr/lib/udev/rules.d /lib/udev/rules.d 2>/dev/null || true
)
for legacy_file in "${legacy_modprobe_files[@]}"; do
echo " Obsolete modprobe directive: $legacy_file"
legacy_refs_found=true
done
for legacy_file in "${legacy_udev_files[@]}"; do
echo " Obsolete udev binding rule: $legacy_file"
legacy_refs_found=true
done
if lsmod | awk '{print $1}' | grep -qx 'aic_btusb'; then
echo " Obsolete aic_btusb module is loaded"
legacy_refs_found=true
fi
if modinfo -n aic_btusb >/dev/null 2>&1; then
echo " Obsolete module file: $(modinfo -n aic_btusb 2>/dev/null)"
legacy_refs_found=true
fi
if [ "$legacy_refs_found" = false ]; then
echo " No active aic_btusb module, directive, or binding rule found (expected)"
fi
echo ""
echo "8. Device-scoped Bluetooth ACL ZLP quirk:"
if [ "$zlp_target_found" = true ]; then
if [ -d /sys/module/aic_zlp_quirk ]; then
zlp_hook="unknown"
zlp_injections="unavailable"
[ -r /sys/module/aic_zlp_quirk/parameters/hook ] && \
zlp_hook=$(cat /sys/module/aic_zlp_quirk/parameters/hook)
[ -r /sys/module/aic_zlp_quirk/parameters/injections ] && \
zlp_injections=$(cat /sys/module/aic_zlp_quirk/parameters/injections)
echo " Target 368b:8d81 present"
echo " aic_zlp_quirk loaded: yes"
echo " active hook: $zlp_hook"
echo " ZLP injections: $zlp_injections"
else
echo " Target 368b:8d81 present, but aic_zlp_quirk is not loaded"
fi
else
echo " Target 368b:8d81 not present; quirk is not required"
fi
echo ""
echo "=== Assessment ==="
if ! lsmod | awk '{print $1}' | grep -qx 'aic_load_fw'; then
echo "- aic_load_fw is not loaded; run: sudo modprobe aic8800_fdrv"
fi
if [ "$bt_interface_found" = true ] && [ "$btusb_bound" = false ]; then
echo "- A Bluetooth USB interface exists but is not bound to btusb."
echo " Check for a btusb blacklist, then try: sudo modprobe btusb"
fi
if dmesg 2>/dev/null | grep -iE 'hci[0-9]+:.*(command|opcode|tx).*timed out|hci[0-9]+: link tx timeout' | tail -1 | grep -q .; then
echo "- HCI timeout detected. Confirm that aic_load_fw uploaded the firmware before btusb bound."
echo " Also check for obsolete aic_btusb configuration above."
fi
if [ "$bt_interface_found" = false ]; then
echo "- No Bluetooth HCI USB interface was found. This is normal for a Wi-Fi-only adapter."
fi
if [ "$zlp_target_found" = true ] && [ ! -d /sys/module/aic_zlp_quirk ]; then
echo "- Device 368b:8d81 requires the ACL ZLP quirk, but the module is not loaded."
echo " Try: sudo modprobe aic_zlp_quirk"
fi
echo "=== Diagnostics complete ==="
+5
View File
@@ -13,4 +13,9 @@ BUILT_MODULE_NAME[1]="aic_load_fw"
BUILT_MODULE_LOCATION[1]="drivers/aic8800/aic_load_fw"
DEST_MODULE_LOCATION[1]="/updates/dkms"
# Device-scoped standard-btusb ACL bulk TX ZLP quirk (368b:8d81 only)
BUILT_MODULE_NAME[2]="aic_zlp_quirk"
BUILT_MODULE_LOCATION[2]="drivers/aic8800/aic_zlp_quirk"
DEST_MODULE_LOCATION[2]="/updates/dkms"
AUTOINSTALL="yes"
+7
View File
@@ -1,8 +1,10 @@
CONFIG_AIC_LOADFW_SUPPORT := m
CONFIG_AIC8800_WLAN_SUPPORT := m
CONFIG_AIC_ZLP_QUIRK := m
obj-$(CONFIG_AIC_LOADFW_SUPPORT) += aic_load_fw/
obj-$(CONFIG_AIC8800_WLAN_SUPPORT) += aic8800_fdrv/
obj-$(CONFIG_AIC_ZLP_QUIRK) += aic_zlp_quirk/
########## config option ##########
export CONFIG_USE_FW_REQUEST = n
@@ -51,6 +53,7 @@ KVER ?= $(shell uname -r)
KDIR ?= /lib/modules/$(KVER)/build
PWD = $(shell pwd)
MODDESTDIR = /lib/modules/$(KVER)/kernel/drivers/net/wireless/aic8800
BTMODDESTDIR = /lib/modules/$(KVER)/kernel/drivers/bluetooth/aic8800
SUBARCH = $(shell uname -m | sed -e s/i.86/i386/ -e s/armv.l/arm/ -e s/aarch64/arm64/ -e s/loongarch64/loongarch/ -e s/loong64/loongarch/ -e s/riscv64/riscv/)
ARCH ?= $(SUBARCH)
CROSS_COMPILE ?=
@@ -78,16 +81,20 @@ modules:
install:
mkdir -p $(MODDESTDIR)
mkdir -p $(BTMODDESTDIR)
install -p -m 644 aic_load_fw/aic_load_fw.ko $(MODDESTDIR)/
install -p -m 644 aic8800_fdrv/aic8800_fdrv.ko $(MODDESTDIR)/
install -p -m 644 aic_zlp_quirk/aic_zlp_quirk.ko $(BTMODDESTDIR)/
/sbin/depmod -a ${KVER}
uninstall:
rm -rfv $(MODDESTDIR)/aic_load_fw.ko
rm -rfv $(MODDESTDIR)/aic8800_fdrv.ko
rm -rfv $(BTMODDESTDIR)/aic_zlp_quirk.ko
/sbin/depmod -a ${KVER}
clean:
cd aic_load_fw/;make clean;cd ..
cd aic8800_fdrv/;make clean;cd ..
cd aic_zlp_quirk/;make clean;cd ..
rm -rf modules.order Module.symvers .modules.order.cmd .Module.symvers.cmd .tmp_versions/
+18
View File
@@ -0,0 +1,18 @@
ifneq ($(KERNELRELEASE),)
obj-m += aic_zlp_quirk.o
else
KVER ?= $(shell uname -r)
KDIR ?= /lib/modules/$(KVER)/build
.PHONY: all clean
all:
$(MAKE) -C $(KDIR) M=$(CURDIR) modules
clean:
$(MAKE) -C $(KDIR) M=$(CURDIR) clean
endif
@@ -0,0 +1,229 @@
// SPDX-License-Identifier: GPL-2.0
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/atomic.h>
#include <linux/kernel.h>
#include <linux/kprobes.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/ptrace.h>
#include <linux/usb.h>
#define AIC_USB_VENDOR_ID 0x368b
#define AIC_USB_PRODUCT_ID 0x8d81
#define USB_BT_SUBCLASS 0x01
#define USB_BT_PROTOCOL 0x01
static atomic64_t injection_count = ATOMIC64_INIT(0);
static const char *hook_name = "none";
static int injections_get(char *buffer, const struct kernel_param *kp)
{
(void)kp;
return scnprintf(buffer, PAGE_SIZE, "%lld\n",
(long long)atomic64_read(&injection_count));
}
static const struct kernel_param_ops injections_ops = {
.get = injections_get,
};
module_param_cb(injections, &injections_ops, NULL, 0444);
MODULE_PARM_DESC(injections, "Number of AIC ACL bulk OUT URBs modified");
static int hook_get(char *buffer, const struct kernel_param *kp)
{
(void)kp;
return scnprintf(buffer, PAGE_SIZE, "%s\n", hook_name);
}
static const struct kernel_param_ops hook_ops = {
.get = hook_get,
};
module_param_cb(hook, &hook_ops, NULL, 0444);
MODULE_PARM_DESC(hook, "Active injection hook");
#if IS_ENABLED(CONFIG_KPROBES)
enum aic_zlp_hook {
AIC_ZLP_HOOK_NONE,
AIC_ZLP_HOOK_BTUSB_RETURN,
AIC_ZLP_HOOK_USB_SUBMIT,
};
static enum aic_zlp_hook active_hook;
static bool is_aic_bulk_out(const struct urb *urb)
{
const struct usb_device *udev;
if (!urb)
return false;
udev = urb->dev;
if (!udev)
return false;
if (le16_to_cpu(udev->descriptor.idVendor) != AIC_USB_VENDOR_ID ||
le16_to_cpu(udev->descriptor.idProduct) != AIC_USB_PRODUCT_ID)
return false;
return usb_pipetype(urb->pipe) == PIPE_BULK &&
usb_pipeout(urb->pipe);
}
static bool is_bluetooth_acl_endpoint(const struct urb *urb)
{
const struct usb_endpoint_descriptor *ep;
struct usb_host_interface *alt;
struct usb_interface *intf;
unsigned int endpoint;
int i;
if (!is_aic_bulk_out(urb))
return false;
intf = usb_ifnum_to_if(urb->dev, 0);
if (!intf)
return false;
alt = READ_ONCE(intf->cur_altsetting);
if (!alt ||
alt->desc.bInterfaceClass != USB_CLASS_WIRELESS_CONTROLLER ||
alt->desc.bInterfaceSubClass != USB_BT_SUBCLASS ||
alt->desc.bInterfaceProtocol != USB_BT_PROTOCOL)
return false;
endpoint = usb_pipeendpoint(urb->pipe);
for (i = 0; i < alt->desc.bNumEndpoints; i++) {
ep = &alt->endpoint[i].desc;
if (usb_endpoint_is_bulk_out(ep) &&
usb_endpoint_num(ep) == endpoint)
return true;
}
return false;
}
static void enable_zlp(struct urb *urb)
{
long long count;
if (urb->transfer_flags & URB_ZERO_PACKET)
return;
urb->transfer_flags |= URB_ZERO_PACKET;
count = atomic64_inc_return(&injection_count);
if (count == 1)
pr_info("enabled ZLP on the first 368b:8d81 ACL bulk OUT URB\n");
}
static int alloc_bulk_urb_ret_handler(struct kretprobe_instance *ri,
struct pt_regs *regs)
{
struct urb *urb;
(void)ri;
urb = (struct urb *)regs_return_value(regs);
if (!is_aic_bulk_out(urb))
return 0;
enable_zlp(urb);
return 0;
}
static struct kretprobe alloc_bulk_urb_probe = {
.kp.symbol_name = "btusb:alloc_bulk_urb",
.handler = alloc_bulk_urb_ret_handler,
};
static int usb_submit_urb_pre_handler(struct kprobe *p, struct pt_regs *regs)
{
struct urb *urb;
(void)p;
urb = (struct urb *)regs_get_kernel_argument(regs, 0);
if (is_bluetooth_acl_endpoint(urb))
enable_zlp(urb);
return 0;
}
static struct kprobe usb_submit_urb_probe = {
.symbol_name = "usb_submit_urb",
.pre_handler = usb_submit_urb_pre_handler,
};
static int __init aic_zlp_quirk_init(void)
{
int ret;
ret = register_kretprobe(&alloc_bulk_urb_probe);
if (!ret) {
active_hook = AIC_ZLP_HOOK_BTUSB_RETURN;
hook_name = "btusb:alloc_bulk_urb";
pr_info("attached to system btusb for USB device 368b:8d81\n");
return 0;
}
pr_warn("btusb return hook unavailable (%d), trying USB submit fallback\n",
ret);
ret = register_kprobe(&usb_submit_urb_probe);
if (ret) {
pr_err("cannot attach to usb_submit_urb: %d\n", ret);
return ret;
}
active_hook = AIC_ZLP_HOOK_USB_SUBMIT;
hook_name = "usb_submit_urb";
pr_info("attached USB submit fallback for device 368b:8d81 interface 0\n");
return 0;
}
static void __exit aic_zlp_quirk_exit(void)
{
unsigned long missed = 0;
if (active_hook == AIC_ZLP_HOOK_BTUSB_RETURN) {
unregister_kretprobe(&alloc_bulk_urb_probe);
missed = alloc_bulk_urb_probe.nmissed;
} else if (active_hook == AIC_ZLP_HOOK_USB_SUBMIT) {
unregister_kprobe(&usb_submit_urb_probe);
missed = usb_submit_urb_probe.nmissed;
}
pr_info("detached %s after %lld injections (%lu missed hits)\n",
hook_name, (long long)atomic64_read(&injection_count), missed);
}
#else
static int __init aic_zlp_quirk_init(void)
{
pr_err("CONFIG_KPROBES is disabled in this kernel\n");
return -EOPNOTSUPP;
}
static void __exit aic_zlp_quirk_exit(void)
{
}
#endif
module_init(aic_zlp_quirk_init);
module_exit(aic_zlp_quirk_exit);
MODULE_AUTHOR("Shen Mintao <cx330.shen@autocore.ai>");
MODULE_DESCRIPTION("AIC 8800D80 standard btusb ACL bulk TX ZLP quirk");
MODULE_LICENSE("GPL");
MODULE_VERSION("1.0");
MODULE_SOFTDEP("pre: btusb");
MODULE_ALIAS("usb:v368Bp8D81d*dc*dsc*dp*ic*isc*ip*in*");
+152 -19
View File
@@ -323,6 +323,62 @@ check_kernel_build_tree() {
# Firmware Installation
#############################################################################
cleanup_legacy_bluetooth_config() {
local legacy_conf="/etc/modprobe.d/aic8800-bt.conf"
local legacy_udev="/etc/udev/rules.d/90-aic8800-mode-switch.rules"
local legacy_package
local changed=false
if [ -f "$legacy_conf" ] && grep -Eq '^[[:space:]]*(softdep|alias)[^#]*aic_btusb([[:space:]]|$)' "$legacy_conf"; then
print_info "Removing obsolete aic_btusb directives from $legacy_conf..."
if [ ! -f "${legacy_conf}.aic8800-backup" ]; then
cp -a "$legacy_conf" "${legacy_conf}.aic8800-backup" >> "$LOG_FILE" 2>&1
fi
sed -i -E '/^[[:space:]]*(softdep|alias)[^#]*aic_btusb([[:space:]]|$)/d' "$legacy_conf"
if ! grep -Eq '^[[:space:]]*[^#[:space:]]' "$legacy_conf"; then
rm -f "$legacy_conf"
fi
changed=true
fi
if [ -f "$legacy_udev" ] && grep -q 'aic_btusb/new_id' "$legacy_udev"; then
print_info "Removing obsolete aic_btusb binding rules from $legacy_udev..."
if [ ! -f "${legacy_udev}.aic8800-backup" ]; then
cp -a "$legacy_udev" "${legacy_udev}.aic8800-backup" >> "$LOG_FILE" 2>&1
fi
sed -i '/aic_btusb\/new_id/d' "$legacy_udev"
changed=true
fi
if lsmod | awk '{print $1}' | grep -qx 'aic_btusb'; then
print_info "Unloading obsolete aic_btusb module..."
if modprobe -r aic_btusb >> "$LOG_FILE" 2>&1; then
changed=true
else
print_warning "aic_btusb is still in use; reboot or replug the adapter after installation."
fi
fi
# Remove the two issue #63 diagnostic DKMS packages before the production
# quirk is installed as part of aic8800/1.0.0. A patched btusb may remain
# active in memory until reboot, but DKMS restores the distribution module
# on disk when its test package is removed.
for legacy_package in "btusb-aic-zlp/0.1" "aic-zlp-quirk/0.1"; do
if dkms status 2>/dev/null | grep -q "^${legacy_package},"; then
print_info "Removing superseded test package ${legacy_package}..."
if dkms remove "$legacy_package" --all >> "$LOG_FILE" 2>&1; then
changed=true
else
print_warning "Could not remove ${legacy_package}; remove it manually before reboot."
fi
fi
done
if [ "$changed" = true ]; then
print_success "Legacy Bluetooth configuration cleaned up."
fi
}
install_firmware() {
print_step "Installing firmware..."
@@ -334,6 +390,8 @@ install_firmware() {
exit 1
fi
cleanup_legacy_bluetooth_config
# Remove old firmware versions
if [ -d "/lib/firmware" ] && [ -n "$(find /lib/firmware -maxdepth 1 -name 'aic8800*' -type d 2>/dev/null)" ]; then
print_info "Removing existing firmware..."
@@ -351,6 +409,8 @@ install_firmware() {
fi
done
print_success "Firmware installed for all supported chip variants."
# Install udev rules
local rules_source="${SCRIPT_DIR}/aic.rules"
local rules_dest="/usr/lib/udev/rules.d/aic.rules"
@@ -416,6 +476,11 @@ BUILT_MODULE_NAME[1]="aic_load_fw"
BUILT_MODULE_LOCATION[1]="drivers/aic8800/aic_load_fw"
DEST_MODULE_LOCATION[1]="/updates/dkms"
# Quirk ZLP para Bluetooth ACL bulk TX (somente 368b:8d81)
BUILT_MODULE_NAME[2]="aic_zlp_quirk"
BUILT_MODULE_LOCATION[2]="drivers/aic8800/aic_zlp_quirk"
DEST_MODULE_LOCATION[2]="/updates/dkms"
AUTOINSTALL="yes"
EOF
@@ -525,23 +590,38 @@ refresh_initramfs() {
# Module Loading
#############################################################################
aic_zlp_target_present() {
local device
for device in /sys/bus/usb/devices/*; do
[ -r "$device/idVendor" ] || continue
[ -r "$device/idProduct" ] || continue
[ "$(cat "$device/idVendor")" = "368b" ] || continue
[ "$(cat "$device/idProduct")" = "8d81" ] || continue
return 0
done
return 1
}
load_module() {
print_step "Loading kernel module..."
print_step "Loading kernel modules..."
# Update module dependencies
print_info "Updating module dependencies..."
depmod -a >> "$LOG_FILE" 2>&1
# Unload module if already loaded
if lsmod | grep -q "$MODULE_NAME"; then
print_info "Module already loaded. Reloading..."
print_info "Wi-Fi module already loaded. Reloading..."
modprobe -r "$MODULE_NAME" >> "$LOG_FILE" 2>&1 || true
fi
# Load the module
# aic8800_fdrv depends on aic_load_fw, which initializes both Wi-Fi-only
# adapters and the Bluetooth firmware on combo adapters. If a standard HCI
# USB interface appears afterwards, the kernel USB modalias loads btusb.
print_info "Loading $MODULE_NAME..."
if modprobe "$MODULE_NAME" >> "$LOG_FILE" 2>&1; then
print_success "Module loaded successfully."
print_success "Wi-Fi module loaded successfully."
# Verify module is loaded
print_info "Waiting for module to initialize..."
@@ -555,15 +635,30 @@ load_module() {
done
if [ "$module_loaded" = true ]; then
print_success "Module is active in kernel."
print_success "Wi-Fi module is active in kernel."
else
print_warning "Module may not be fully initialized yet."
print_warning "Wi-Fi module may not be fully initialized yet."
fi
else
print_warning "Module installed but could not be loaded immediately."
print_warning "Wi-Fi module installed but could not be loaded immediately."
print_info "This may be due to Secure Boot or missing hardware."
print_info "Try rebooting or check: sudo dmesg | grep aic8800"
fi
print_info "Combo adapters use the standard btusb driver when their Bluetooth interface appears."
# The module has a USB alias and normally autoloads when 368b:8d81 appears.
# Explicitly load it here as well when that device was already present before
# DKMS installation and therefore did not generate a new modalias event.
if aic_zlp_target_present; then
print_info "Loading the 368b:8d81 Bluetooth ACL ZLP quirk..."
if modprobe aic_zlp_quirk >> "$LOG_FILE" 2>&1; then
print_success "Device-scoped Bluetooth ZLP quirk is active."
else
print_warning "The ZLP quirk could not attach; system btusb remains unchanged."
print_info "Check CONFIG_KPROBES, Secure Boot, and: sudo dmesg | grep aic_zlp_quirk"
fi
fi
}
#############################################################################
@@ -583,15 +678,37 @@ verify_installation() {
print_warning "DKMS status unclear: $dkms_status"
fi
# Check if module is loaded
# Check if the Wi-Fi module is loaded
if lsmod | grep -q "$MODULE_NAME"; then
print_success "Kernel module is loaded."
echo ""
lsmod | grep aic
print_success "Wi-Fi kernel module is loaded."
else
print_info "Module not currently loaded (this is OK if no hardware is connected)."
print_info "Wi-Fi module not currently loaded (this is OK if no hardware is connected)."
fi
if lsmod | awk '{print $1}' | grep -qx 'btusb' || compgen -G '/sys/class/bluetooth/hci*' > /dev/null; then
print_success "A Bluetooth controller or the standard btusb module is present."
else
print_info "No Bluetooth controller is present (normal for Wi-Fi-only adapters or when no combo adapter is connected)."
fi
if aic_zlp_target_present; then
if lsmod | awk '{print $1}' | grep -qx 'aic_zlp_quirk'; then
local zlp_hook="unknown"
if [ -r /sys/module/aic_zlp_quirk/parameters/hook ]; then
zlp_hook=$(cat /sys/module/aic_zlp_quirk/parameters/hook)
fi
print_success "Bluetooth ACL ZLP quirk is loaded (hook: $zlp_hook)."
else
print_warning "USB device 368b:8d81 is present but aic_zlp_quirk is not loaded."
fi
else
print_info "Bluetooth ZLP quirk is installed but inactive (no 368b:8d81 device present)."
fi
echo ""
print_info "Loaded AIC modules:"
lsmod | grep aic || echo " (none)"
# Check firmware
local fw_count=0
for fw_dir in /lib/firmware/aic8800*; do
@@ -610,6 +727,15 @@ verify_installation() {
if command -v iwconfig &> /dev/null; then
iwconfig 2>/dev/null | grep -E "wlan|IEEE" || echo "No wireless interfaces detected (hardware may not be connected)"
fi
print_info "Checking for Bluetooth interfaces on combo adapters..."
if command -v bluetoothctl &> /dev/null; then
bluetoothctl list 2>/dev/null || echo "No Bluetooth interfaces detected (normal for Wi-Fi-only adapters)"
elif command -v hciconfig &> /dev/null; then
hciconfig 2>/dev/null || echo "No Bluetooth interfaces detected (normal for Wi-Fi-only adapters)"
else
echo "Bluetooth tools not installed (install BlueZ to manage a combo adapter)"
fi
}
#############################################################################
@@ -624,13 +750,15 @@ show_final_instructions() {
echo ""
echo -e "${CYAN}Important Information:${NC}"
echo ""
echo "✓ Driver installed via DKMS"
echo "✓ Wi-Fi driver and firmware loader installed via DKMS"
echo "✓ Combo adapters use the standard Linux btusb driver"
echo "✓ Device-scoped Bluetooth ACL ZLP quirk installed for 368b:8d81"
echo "✓ Automatic rebuild enabled for kernel updates"
echo "✓ Firmware installed in /lib/firmware/"
echo ""
echo -e "${CYAN}Next Steps:${NC}"
echo ""
echo "1. Connect your AIC8800D80 USB WiFi adapter"
echo "1. Connect your AIC8800 USB Wi-Fi or Wi-Fi/Bluetooth adapter"
echo ""
echo "2. Check if the adapter is detected:"
echo " ${BLUE}lsusb | grep -i aic${NC}"
@@ -639,32 +767,37 @@ show_final_instructions() {
echo ""
echo "3. View kernel messages about the driver:"
echo " ${BLUE}sudo dmesg | grep aic8800${NC}"
echo " ${BLUE}sudo dmesg | grep -iE 'bluetooth|btusb|hci'${NC}"
echo ""
echo "4. Connect to a WiFi network:"
echo " ${BLUE}nmcli device wifi list${NC}"
echo " ${BLUE}nmcli device wifi connect \"SSID\" password \"PASSWORD\"${NC}"
echo ""
echo "5. For a combo adapter, check Bluetooth:"
echo " ${BLUE}bluetoothctl list${NC}"
echo " ${BLUE}bluetoothctl scan on${NC}"
echo ""
echo -e "${CYAN}Troubleshooting:${NC}"
echo ""
echo "• Check DKMS status:"
echo " ${BLUE}dkms status${NC}"
echo ""
echo "• Check loaded modules:"
echo " ${BLUE}lsmod | grep aic8800${NC}"
echo " ${BLUE}lsmod | grep -E 'aic|btusb'${NC}"
echo " ${BLUE}cat /sys/module/aic_zlp_quirk/parameters/{hook,injections}${NC}"
echo ""
echo "• Manually load the module:"
echo "• Manually load the Wi-Fi module and firmware loader:"
echo " ${BLUE}sudo modprobe aic8800_fdrv${NC}"
echo ""
echo "• View detailed logs:"
echo " ${BLUE}cat $LOG_FILE${NC}"
echo ""
echo -e "${YELLOW}Known Limitations:${NC}"
echo "• Bluetooth functionality is not supported"
echo "• Secure Boot may prevent module loading (disable in BIOS if needed)"
echo ""
echo -e "${CYAN}Uninstallation:${NC}"
echo " ${BLUE}sudo dkms remove ${DRV_NAME}/${DRV_VERSION} --all${NC}"
echo " ${BLUE}sudo rm -rf /lib/firmware/aic8800D80${NC}"
echo " ${BLUE}sudo rm -rf /lib/firmware/aic8800*${NC}"
echo ""
}
+12
View File
@@ -0,0 +1,12 @@
KVER ?= $(shell uname -r)
KDIR ?= /lib/modules/$(KVER)/build
obj-m += aic_zlp_quirk.o
.PHONY: all clean
all:
$(MAKE) -C $(KDIR) M=$(CURDIR) modules
clean:
$(MAKE) -C $(KDIR) M=$(CURDIR) clean
+109
View File
@@ -0,0 +1,109 @@
# Issue #63: standard `btusb` ACL ZLP support
The `aic_zlp_quirk` companion module supplies the Bluetooth ACL bulk TX
zero-length-packet behavior confirmed in
[issue #63](https://github.com/shenmintao/aic8800d80/issues/63). It is now part
of the normal `aic8800` DKMS build rather than a replacement for the
distribution's Bluetooth driver.
The validated failure was specific and reproducible: AAC playback through USB
device `368b:8d81` stalled after about one minute, while SBC remained stable.
Adding `URB_ZERO_PACKET` to the ACL bulk OUT URBs kept AAC connected for more
than one hour. The standalone module was then verified with the system
`btusb`, no loaded `aic_btusb`, and 2,113 observed ZLP injections.
## Architecture and scope
The module first tries to attach a kretprobe to the standard `btusb` function
that allocates ACL bulk OUT URBs. The target is module-qualified as
`btusb:alloc_bulk_urb`, avoiding similarly named symbols in unrelated USB
drivers.
Some distribution builds inline that private function. When the preferred hook
is unavailable, the module falls back to the stable `usb_submit_urb` entry
point. The fallback changes an URB only when all of these checks pass:
- USB device is exactly `368b:8d81`;
- the transfer is bulk OUT;
- the endpoint belongs to Bluetooth interface 0 (`e0/01/01`).
Both paths add only `URB_ZERO_PACKET`. Wi-Fi interfaces and unrelated USB
devices are unchanged. If neither probe can be installed or kprobes are
disabled, the module refuses to load and system `btusb` remains unchanged.
The module carries a USB modalias for `368b:8d81`, so it is inactive on other
hardware and normally autoloads only when the validated device appears.
## Install from the unified branch
```bash
git fetch origin
git switch test/unified-wifi-bt-zlp
git pull --ff-only
sudo ./install.sh
sudo reboot
```
The installer removes the earlier `btusb-aic-zlp/0.1` and
`aic-zlp-quirk/0.1` diagnostic DKMS packages when present. To remove them
manually before installation, use:
```bash
sudo dkms remove btusb-aic-zlp/0.1 --all
sudo dkms remove aic-zlp-quirk/0.1 --all
sudo depmod -a
```
The unified installer also removes active configuration left by the obsolete
custom `aic_btusb` transport.
## Verify
Confirm that system `btusb` owns Bluetooth interfaces 0/1 and that the quirk is
loaded:
```bash
lsusb -t
lsmod | grep -E '^(btusb|aic_zlp_quirk|aic_btusb)\b'
modinfo -n btusb
modinfo -n aic_zlp_quirk
cat /sys/module/aic_zlp_quirk/parameters/hook
cat /sys/module/aic_zlp_quirk/parameters/injections
sudo ./diagnose_bt.sh
```
Expected results include:
```text
Bluetooth interfaces 0/1: btusb
aic_btusb: not loaded
aic_zlp_quirk: loaded
active hook: btusb:alloc_bulk_urb
```
`active hook: usb_submit_urb` is also valid when the compiler inlined the
preferred function. During Bluetooth traffic, the `injections` value must
increase.
For the promotion test, select AAC and play audio for at least one hour while
also confirming that Wi-Fi scan, association, DHCP, and real traffic remain
working.
## Isolated retest helper
`aic-zlp-quirk-test.sh` remains available only for isolated issue #63 retests.
It builds the same canonical source from
`drivers/aic8800/aic_zlp_quirk/aic_zlp_quirk.c` as a separate DKMS package. A
normal installation should use the repository-level `install.sh` instead.
## Kernel requirements
- matching kernel headers;
- `CONFIG_KPROBES=y` and kretprobe support;
- standard `btusb` plus either its `alloc_bulk_urb` symbol or the USB core
`usb_submit_urb` symbol;
- a valid module signature when Secure Boot policy requires one.
The preferred function has the same signature in Linux 5.15, 6.1, 6.6, 6.12,
6.18, and 7.1.3, although compilers may inline it. DKMS builds one module for
each installed kernel from the single canonical source file.
+233
View File
@@ -0,0 +1,233 @@
#!/usr/bin/env bash
set -euo pipefail
PACKAGE_NAME="aic-zlp-quirk"
PACKAGE_VERSION="0.1"
MODULE_NAME="aic_zlp_quirk"
KERNEL_RELEASE="${KERNEL_RELEASE:-$(uname -r)}"
KERNEL_BUILD_DIR="/lib/modules/${KERNEL_RELEASE}/build"
SOURCE_DIR="/usr/src/${PACKAGE_NAME}-${PACKAGE_VERSION}"
MODULES_LOAD_FILE="/etc/modules-load.d/${PACKAGE_NAME}.conf"
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd -- "${SCRIPT_DIR}/../.." && pwd)"
QUIRK_SOURCE="${REPO_ROOT}/drivers/aic8800/aic_zlp_quirk/aic_zlp_quirk.c"
log() {
printf '[aic-zlp-quirk-test] %s\n' "$*"
}
die() {
printf '[aic-zlp-quirk-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
}
module_loaded() {
[ -d "/sys/module/$1" ]
}
legacy_btusb_loaded() {
module_loaded aic_btusb
}
patched_btusb_installed() {
dkms status -m btusb-aic-zlp -v 0.1 2>/dev/null | grep -q .
}
copy_sources() {
install -d "${SOURCE_DIR}"
install -m 0644 "${QUIRK_SOURCE}" "${SOURCE_DIR}/"
install -m 0644 "${SCRIPT_DIR}/Makefile" "${SOURCE_DIR}/"
install -m 0644 "${SCRIPT_DIR}/dkms.conf" "${SOURCE_DIR}/"
}
rollback_install() {
log "rolling back the ZLP companion module"
rm -f -- "${MODULES_LOAD_FILE}"
modprobe -r "${MODULE_NAME}" >/dev/null 2>&1 || true
dkms remove -m "${PACKAGE_NAME}" -v "${PACKAGE_VERSION}" --all >/dev/null 2>&1 || true
rm -rf -- "${SOURCE_DIR}"
depmod -a "${KERNEL_RELEASE}" >/dev/null 2>&1 || true
start_bluetooth
}
verify_standard_btusb() {
if legacy_btusb_loaded; then
die "obsolete aic_btusb is loaded; run 'sudo modprobe -r aic_btusb' and remove its old autoload configuration first"
fi
if patched_btusb_installed; then
die "the patched btusb test package is still installed; remove it before testing the companion module"
fi
modprobe btusb || die "could not load the system btusb module"
}
install_module() {
require_root
require_command dkms
require_command make
require_command modprobe
require_command depmod
[ -r "${KERNEL_BUILD_DIR}/Makefile" ] || \
die "kernel build tree not found: ${KERNEL_BUILD_DIR}"
if command -v lsusb >/dev/null 2>&1 && ! lsusb -d 368b:8d81 >/dev/null 2>&1; then
die "this test is restricted to USB device 368b:8d81, which is not connected"
fi
verify_standard_btusb
if module_loaded "${MODULE_NAME}"; then
log "unloading the earlier companion module"
if ! modprobe -r "${MODULE_NAME}"; then
die "could not unload the earlier ${MODULE_NAME} module"
fi
fi
trap rollback_install ERR
if dkms status -m "${PACKAGE_NAME}" -v "${PACKAGE_VERSION}" 2>/dev/null | grep -q .; then
log "removing an earlier companion-module build"
dkms remove -m "${PACKAGE_NAME}" -v "${PACKAGE_VERSION}" --all
fi
rm -rf -- "${SOURCE_DIR}"
copy_sources
log "building the ZLP companion module 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}"
printf '%s\n' "${MODULE_NAME}" > "${MODULES_LOAD_FILE}"
stop_bluetooth
if ! modprobe "${MODULE_NAME}"; then
trap - ERR
rollback_install
die "the companion module could not attach; the system btusb was left unchanged"
fi
start_bluetooth
trap - ERR
log "loaded module: $(modinfo -n "${MODULE_NAME}")"
log "system btusb remains active; test AAC playback for at least one hour"
}
remove_module() {
require_root
require_command dkms
require_command modprobe
require_command depmod
stop_bluetooth
rm -f -- "${MODULES_LOAD_FILE}"
if module_loaded "${MODULE_NAME}" && ! modprobe -r "${MODULE_NAME}"; then
start_bluetooth
die "could not unload ${MODULE_NAME}; no files were removed"
fi
if dkms status -m "${PACKAGE_NAME}" -v "${PACKAGE_VERSION}" 2>/dev/null | grep -q .; then
if ! dkms remove -m "${PACKAGE_NAME}" -v "${PACKAGE_VERSION}" --all; then
start_bluetooth
die "DKMS removal failed; source files were left in place"
fi
fi
rm -rf -- "${SOURCE_DIR}"
depmod -a "${KERNEL_RELEASE}"
start_bluetooth
log "removed the companion module; the distribution btusb was never replaced"
}
show_aic_binding() {
local device
local driver
for device in /sys/bus/usb/devices/*; do
[ -r "${device}/idVendor" ] || continue
[ -r "${device}/idProduct" ] || continue
[ "$(cat "${device}/idVendor")" = "368b" ] || continue
[ "$(cat "${device}/idProduct")" = "8d81" ] || continue
driver="not bound"
if [ -L "${device}:1.0/driver" ]; then
driver="$(basename "$(readlink -f "${device}:1.0/driver")")"
fi
printf 'AIC BT driver: %s\n' "${driver}"
return
done
printf 'AIC BT driver: device not found\n'
}
show_status() {
local loaded="no"
local injections="unavailable"
local hook="unavailable"
local dkms_status
if module_loaded "${MODULE_NAME}"; then
loaded="yes"
if [ -r "/sys/module/${MODULE_NAME}/parameters/injections" ]; then
injections="$(cat "/sys/module/${MODULE_NAME}/parameters/injections")"
fi
if [ -r "/sys/module/${MODULE_NAME}/parameters/hook" ]; then
hook="$(cat "/sys/module/${MODULE_NAME}/parameters/hook")"
fi
fi
dkms_status="$(dkms status -m "${PACKAGE_NAME}" -v "${PACKAGE_VERSION}" 2>/dev/null || true)"
if [ -z "${dkms_status}" ]; then
dkms_status="not installed"
fi
printf 'kernel: %s\n' "${KERNEL_RELEASE}"
printf 'btusb module: %s\n' "$(modinfo -n btusb 2>/dev/null || printf 'not found')"
printf 'quirk module: %s\n' "$(modinfo -n "${MODULE_NAME}" 2>/dev/null || printf 'not found')"
printf 'quirk loaded: %s\n' "${loaded}"
printf 'active hook: %s\n' "${hook}"
printf 'ZLP injections:%s\n' " ${injections}"
printf 'legacy aic_btusb: %s\n' "$(legacy_btusb_loaded && printf 'loaded' || printf 'not loaded')"
printf 'dkms status: %s\n' "${dkms_status}"
show_aic_binding
}
case "${1:-}" in
install)
install_module
;;
remove)
remove_module
;;
status)
show_status
;;
*)
printf 'Usage: %s {install|remove|status}\n' "$0" >&2
exit 2
;;
esac
+10
View File
@@ -0,0 +1,10 @@
PACKAGE_NAME="aic-zlp-quirk"
PACKAGE_VERSION="0.1"
MAKE[0]="make KVER=${kernelver}"
CLEAN="make KVER=${kernelver} clean"
BUILT_MODULE_NAME[0]="aic_zlp_quirk"
DEST_MODULE_LOCATION[0]="/updates/dkms"
AUTOINSTALL="yes"
+87
View File
@@ -0,0 +1,87 @@
# Unified Wi-Fi, Bluetooth, and ZLP branch test
This branch tests replacing the separate `main` and `bluetooth` branches with
one driver and installer. The Wi-Fi driver and firmware were already identical
on both branches. This test unifies their installer, migration, diagnostics,
packaging, and documentation, and adds the device-scoped ZLP fix from issue
#63 as a third DKMS module.
The expected architecture is:
- `aic_load_fw` initializes every supported adapter and uploads firmware.
- `aic8800_fdrv` handles Wi-Fi.
- A combo adapter exposes a Bluetooth HCI USB interface after initialization;
the standard Linux `btusb` driver binds through normal USB device matching.
- A Wi-Fi-only adapter exposes no HCI interface and does not need `btusb`.
- The retired custom `aic_btusb` module is never installed or used.
- USB device `368b:8d81` autoloads `aic_zlp_quirk`, which adds
`URB_ZERO_PACKET` only to its Bluetooth ACL bulk OUT transfers.
- The distribution's original `btusb.ko` is never replaced.
The installer deliberately does not force-load `btusb` and does not globally
unblock Bluetooth through rfkill.
## Install
```bash
git fetch origin
git switch test/unified-wifi-bt-zlp
git pull --ff-only
sudo ./install.sh
sudo reboot
```
## Test a Wi-Fi-only adapter
Confirm that:
1. The Wi-Fi interface is created.
2. Scanning, association, DHCP, and real traffic work.
3. No `aic_btusb` module is loaded.
4. The absence of `btusb` or an HCI controller is treated as normal.
## Test a Wi-Fi/Bluetooth combo adapter
Confirm that:
1. Wi-Fi scanning, association, DHCP, and real traffic work.
2. `aic_load_fw` uploads the firmware before the HCI interface is used.
3. The Bluetooth USB interface is bound to the standard `btusb` driver.
4. `bluetoothctl` can power on, scan, pair, and exchange real traffic.
5. No `aic_btusb` module, alias, soft dependency, or udev binding rule remains.
## Test the `368b:8d81` ZLP path
In addition to the combo-adapter checks, confirm that:
1. `aic_zlp_quirk` loads automatically and system `btusb` owns interfaces 0/1.
2. `hook` reports `btusb:alloc_bulk_urb` or `usb_submit_urb`.
3. The `injections` counter increases during Bluetooth audio traffic.
4. AAC playback remains connected for at least one hour.
5. Wi-Fi scan, association, and traffic remain unaffected during the test.
## Collect results
```bash
git rev-parse --short HEAD
lsusb
lsusb -t
lsmod | grep -E 'aic|btusb|bluetooth'
iw dev
bluetoothctl list
cat /sys/module/aic_zlp_quirk/parameters/{hook,injections} 2>/dev/null || true
sudo ./diagnose_bt.sh | tee unified-wifi-bt-diagnostic.log
sudo journalctl -k -b --no-pager | tee unified-wifi-bt-kernel.log
```
Please attach both logs and identify whether the tested adapter is Wi-Fi-only
or a combo device.
## Roll back during testing
```bash
git switch main
git pull --ff-only
sudo ./install.sh
sudo reboot
```