MastaG 2c4ab4b421 fix: support Linux 7.3 cfg80211 API
7.3 lands the last part of a two-commit series that converts several
cfg80211_ops cookie parameters from driver-invented output values to
values cfg80211 pre-assigns and passes in as input, and renames
probe_client to probe_peer. Building against 7.3 fails with 5 errors:

  - cfg80211_probe_status() gained an MLO link_id parameter between cookie
    and acked (kernel commit 010e955c203e). This driver has no MLO
    support; -1 is the documented value for non-MLO.
  - cfg80211_ops::probe_client was renamed to probe_peer (4ab9b637b94a).
  - cfg80211_ops::mgmt_tx, ::remain_on_channel and ::probe_peer all
    changed their cookie parameter from u64 * to u64 (914781c72813):
    cfg80211 now pre-assigns the cookie before calling the driver instead
    of the driver inventing one and returning it through the pointer.

The cookie change is more than a type fix. This driver invents its
mgmt_tx/remain_on_channel/probe_peer cookies from a skb pointer, an
internal roc_cookie_cnt counter, and an internal probe_id counter
respectively, and separately re-derives "the same" value later when
reporting completion. Once cfg80211 assigns the cookie itself, the
completion report must echo back the value cfg80211 gave the driver.

Two of the three ops are thin wrappers around a lower-level helper that
is also called from a purely-internal path with no cfg80211-assigned
cookie to honor: rwnx_start_mgmt_xmit() is reused by TDLS, and
rwnx_cfg80211_remain_on_channel_() starts the internal RoC from inside
mgmt_tx. Each of those helpers gets a bool (use_given_cookie / the
existing mgmt_roc_flag) so the internal callers keep inventing a cookie
exactly as before on every kernel, while the cfg80211 op wrappers honor
the pre-assigned value on 7.3+ and thread it through to the completion
report. probe_peer has no helper and no internal caller; the op stores
the cookie itself.

  - rwnx_start_mgmt_xmit() gains use_given_cookie; the resulting cookie is
    stored in a new rwnx_sw_txhdr::cookie and echoed back via
    cfg80211_mgmt_tx_status(). Because sw_txhdr comes from a kmem_cache
    and is never zeroed, every one of the five allocation sites
    initialises the field -- to the skb pointer, which is exactly what the
    completion path reported before the field existed -- so the
    TXU_CNTRL_MGMT filter on the reporting side is not what keeps it safe.
  - rwnx_cfg80211_remain_on_channel_()'s mgmt_roc_flag also selects the
    cookie source, stored in a new rwnx_roc_elem::cookie that the
    rwnx_msg_rx.c completion handlers read.
  - rwnx_cfg80211_probe_client() stores the given cookie in a new
    apm_probe_sta::cookie field for the async probe-status work.

Every gate this adds uses AICWF_CFG80211_VERSION_CODE, so an OpenWrt
backports build selects these paths from the wireless stack's version
rather than the kernel's, matching the existing 7.2 gates.

On kernels before 7.3 the compiled behaviour is identical to main with
two exceptions, both requested in review and both changing only the
order of a store relative to a queue/send: *cookie for probe_client is
taken from probe_id before queue_work() rather than after it, and the
RoC cookie is read from roc_cookie_cnt once, before rwnx_send_roc(),
rather than after it returns. In each case the value differs from
main's only if the worker/RX path has already completed and bumped the
counter in between -- a window in which main returns N+1 while having
already reported N to cfg80211. The reported and returned values now
agree by construction.

v2, addressing review:
  - RoC: the cookie is decided and stored in roc_elem before the element
    is published and rwnx_send_roc() is called, so an early channel-switch
    indication cannot report an uninitialised cookie.
  - probe_peer: the cookie is stored before queue_work(), so a worker
    running immediately on another CPU reports the right one.
  - Monitor injection: intended to initialise sw_txhdr->cookie in
    rwnx_start_monitor_if_xmit(), but the store landed in
    rwnx_start_xmit() instead, where it is never read. Fixed in v5.
  - Pre-3.14: use_given_cookie is part of that signature too and both
    old-signature callers pass false.
  - Rebased onto the OpenWrt backport-version change.

v3:
  - rwnx_main.h gated the probe_client prototype on LINUX_VERSION_CODE
    while rwnx_main.c gated the definition on AICWF_CFG80211_VERSION_CODE.
    Identical on a native build, but a backports build with the two
    decoupled would see a u64 * prototype against a u64 definition.
  - rwnx_cfg80211_remain_on_channel() no longer duplicates the whole call
    to the shared helper just to change one argument; only the cookie
    pointer is selected by version.

v4:
  - rwnx_cfg80211_remain_on_channel_() no longer writes *cookie before
    rwnx_send_roc(). The cookie is snapshotted into a local, roc_elem
    keeps its early assignment, and *cookie is written where it was
    before this patch: inside the error == 0 branch.

v5:
  - The monitor-injection initialisation is now where it was always
    meant to be: rwnx_start_monitor_if_xmit(), between the skb
    assignment and raw_frame = 1, ungated, since that path runs on every
    kernel with CONFIG_RWNX_MON_XMIT. The value is (unsigned long)skb_mgmt,
    which is what rwnx_txdatacfm() reported for these frames before.
  - The remaining allocation sites that did not set the field --
    intf_tx() and rwnx_probersp_work() -- now do, so all five do.
  - The internal RoC's block-local cookie in rwnx_cfg80211_mgmt_tx() is
    renamed roc_cookie: on 7.3 it had come to shadow a same-typed
    parameter with a different meaning.
  - Comment fixes: the rwnx_start_mgmt_xmit() kerneldoc documents
    use_given_cookie; the probe_peer comment states the 7.3 contract; the
    two #endif trailers that close chains now opening on the 7.3 gate say
    so; the unused rx_addr is explained on the 7.3 arm as well.
  - Rebased onto main (set_monitor_channel on 6.12 stable).

Verified against real linux-cachyos-bc250 (7.2.6) and linux-cachyos-rc-bc250
(7.3-rc4) headers with LLVM=1, the way DKMS builds them, asserting on exit
code and module count:

  upstream main @ 7.2.6   rc=0, 3 modules, 13 warnings
  upstream main @ 7.3-rc4 rc=2 - the errors this patch fixes
  this branch   @ 7.2.6   rc=0, 3 modules, 13 warnings
  this branch   @ 7.3-rc4 rc=0, 3 modules, 13 warnings
  warning sets on 7.2.6: identical, and none names a function this
  patch touches.

The backports configuration is not something I can build here, so the
header/definition agreement in v3 rests on both sites using the same
macro rather than on a test.
2026-09-24 12:34:45 +02:00

AIC8800 Linux Wi-Fi and Bluetooth Driver

This driver is for the AIC8800D80 chipset, supported by devices such as the Tenda U11 and AX913B.

Important

Choose the branch for your hardware revision before installing. If the driver log reports chip_id=7, chip_mcu_id=1 on an AIC8800D80 or AIC8800DC/DW device, use the legacy-mcu1 branch. It contains the matched legacy firmware and loader profile required to avoid the deterministic firmware upload timeout at 0x170400. Use main when chip_mcu_id=0 or when the MCU revision is unknown.

Hardware revision determines the firmware branch. MCU1 devices should use legacy-mcu1, which also initializes Bluetooth for the kernel's standard btusb driver. After switching branches, rerun sudo ./install.sh and reboot; switching the Git branch alone does not replace the 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.

Note

Maintained branches: This repository now maintains two hardware branches: main for chip_mcu_id=0 or an unknown MCU revision, and legacy-mcu1 for chip_mcu_id=1. Wi-Fi, the kernel's standard btusb Bluetooth path, and the device-scoped ZLP support are integrated into both branches. The former separate bluetooth branch is retired and should no longer be used.

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

Attention

Before installing the driver, delete all aic8800-related folders under /lib/firmware. Using an incorrect firmware version may cause the system to freeze.

Pandora 88M80 mode switching

Pandora 88M80 adapters that initially appear as USB device 1111:1111 are automatically switched to a69c:8d80 by sending the required F3 then F2 commands. Existing users must rerun sudo ./install.sh to replace the installed usb_modeswitch configuration, then unplug the adapter completely and plug it back in.

If automatic switching does not occur, try the same sequence manually:

sudo usb_modeswitch -v 1111 -p 1111 \
  -M "555342438765432100000000000010fd0000000000000000000000000000f3" \
  -2 "555342438765432100000000000010fd0000000000000000000000000000f2"

This only addresses switching from 1111:1111 to a69c:8d80. If the device has reached a69c:8d80 but firmware startup still fails, see issue #79.

Installation Steps

Method 2: Manual Installation

Copy udev rules:

Copy the aic.rules file to /usr/lib/udev/rules.d/:

sudo cp aic.rules /usr/lib/udev/rules.d/

Copy firmware:

Copy the firmware directories from ./fw to /lib/firmware/:

sudo cp -r ./fw/aic8800* /lib/firmware/

Navigate to the driver directory:

Change to the drivers/aic8800 directory:

cd ./drivers/aic8800

Compile and Install the Driver:

First, compile the driver:

make

Then, install the driver:

sudo make install

For any kernel updates, you'll need to reinstall the driver:

make clean
make
sudo make install

Load the Driver

After installation, load the driver with the following command:

sudo modprobe aic8800_fdrv

Verify the Module is Active

Check if the module is loaded correctly:

lsmod | grep aic

You should see output similar to:

aic8800_fdrv    536576  0
cfg80211        1146880 1   aic8800_fdrv
aic_load_fw     69632   1   aic8800_fdrv
usbcore         348160  10  xhci_hcd,ehci_pci,usbhid,usb_storage,ehci_hcd,xhci_pci,uas,aic_load_fw,uhci_hcd,aic8800_fdrv

After that, plug in your USB wireless network card.

Verify Wi-Fi Device is Active

To check if the Wi-Fi interface is recognized, run:

iwconfig

If the device is still not active, check the kernel logs for any errors related to the driver:

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:

lsmod | grep -E 'aic_load_fw|aic8800_fdrv|aic_zlp_quirk|btusb'
lsusb -t
bluetoothctl list

To scan after a controller appears:

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:

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:

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 remains a known limitation: after an airplane-mode or hotspot reset, Bluetooth may require a physical unplug/replug of the adapter.

S
Description
No description provided
Readme
7.9 MiB
Languages
C 97.2%
Shell 1.9%
Makefile 0.9%