Replace every em dash with real punctuation (rewrites, not hyphen swaps) in docs, code comments, scripts, configs, and the port records; convert en dashes, curly quotes, ellipsis glyphs, arrows, and section signs to ASCII; drop machine-writing tell phrases from living docs. ADR titles now use a colon. The M2 bring-up DTS model string carried an em dash into the patch series and its record echoes; fixed at both, and the full series re-verified to apply cleanly onto pristine 6.18.46. One comment in freshness.h deliberately names the em dash glyph the UI renders as the unknown mark; that is data, kept as prose naming it. Verified: cargo tests (sim, config-lint, rs485-bridge), shellcheck, both driver MC/DC harnesses, patches-apply.
36 KiB
AIC8800 WiFi+BT SDIO driver: 5.10 -> 6.18 port plan
Target: the M5 milestone of ../PORT-STATUS.md / ../../docs/bringup.md, port
the AIC8800 SDIO WiFi+BT driver onto the self-built Linux 6.18.46 kernel that
already boots WardenOS on warden-c8a3 (M0-M3 done; M4 display in progress). No
plan44 code (per the 2026-08-23 decision in bringup.md): this is a direct
forward-port of our own vendor source, same method already proven for
clk/pinctrl/mach/mmc.
0. Source recommendation
Port from our own vendor SDK source, not from any external fork:
flare-edge/sdk/sysdrv/drv_ko/wifi/aic8800dc/ (section 1 below is the full map). This
is not a default-to-what-we-have choice; it's the correct one on the evidence:
- Nothing more upstream exists. AICSemi has no public upstream driver repo
with a stable, discoverable URL, and there is no aic8800 code in Linux
stagingor any LKML patch series (mainline/staging status: absent, confirmed by a dedicated web-research pass; no counter-evidence found). There is nothing more "canonical" to port from than the vendor source we already have. - Our tree already carries two hardware-verified fixes newer forks don't
have: the
queue_sz-zero console-flood clamp (flare-edge/*/sdk-patches/wifi/patches/0001-aic8800dc-no-zero-timeout-spin-and-ratelimit.patch) and the SDIO-wakeup busy-spin->sleep Tier-1 hardening (live in the vendor tree today perluckfox-pico-86-panel/wifi-bluetooth-aic8800.md, tracked asfuture-features-2/sdk-patches/wifi/patches/0002-aic8800dc-sdio-wakeup-sleep-not-spin.patch, not yet copied into this branch'ssdk-patches/; carry both forward into the ported tree, and file the housekeeping gap separately). Starting from a third-party fork would mean re-discovering and re-fixing both bugs on hardware. - The vendor source already anticipates part of the delta.
rwnx_compat.hhas#if LINUX_VERSION_CODEshims up toKERNEL_VERSION(5, 15, 60), e.g.rwnx_main.c'snet_device_opsalready switches between.ndo_do_ioctland.ndo_siocdevprivateat the 5.15 boundary (rwnx_main.c:1457-1474). That removes one whole item from the delta checklist below at zero cost. - A cleaner community fork exists but is not itself a base: it's a reference.
radxa-pkg/aic8800(github.com/radxa-pkg/aic8800) is an actively maintained, GPL-3.0, DKMS-packaged build of the same AICSemi driver family, explicitly patched for kernel 6.12/6.13 on Rockchip SDIO boards (Rock 3C/5C), and is the only community effort found that has already absorbed the post-5.15 cfg80211/netdev/timer churn this port needs. Use it the same way the pinctrl port used the upstream RV1106 patch series, as a correctness oracle to diff against, not as code we vendor: it targets AIC8800D80 SDIO/USB/PCIe variants generically, not our AIC8800DC + our two local hardening patches, and an Armbian forum thread reports open regressions on it at the 6.12+ boundary, so treat its fixes as a second opinion on the wiphy-lock and timer-rename hunks, verify each independently. - No mainline/staging path exists to lean on instead. Confirmed by the
research pass: no aic8800 in
drivers/net/wireless/,staging/, or any LKML series. This matches the driver-parity table's row (AIC8800 wifi (bsp/fdrv) | out-of-tree) and the bring-up doc's framing: "AIC8800 wifi (plan44 has none; ours)."
1. SDK source map
Vendor tree root: flare-edge/sdk/sysdrv/drv_ko/wifi/aic8800dc/ (an out-of-tree
external-module build against KDIR := ../../../source/kernel, i.e. the vendor
5.10.160 tree at flare-edge/sdk/sysdrv/source/kernel/). Selected by
RK_ENABLE_WIFI_CHIP=AIC8800DC in
flare-edge/sdk/project/cfg/BoardConfig_IPC/BoardConfig-EMMC-Buildroot-RV1106_Luckfox_Pico_86Panel-IPC.mk:138,
dispatched by sdk/sysdrv/drv_ko/wifi/Makefile's build-sdio target
(ifneq ($(findstring $(RK_ENABLE_WIFI_CHIP),"AIC8800DC"),) @make -C aic8800dc/).
88,601 total lines across the three sub-drivers.
aic8800dc/
Kconfig # AIC_WLAN_SUPPORT, AIC_FW_PATH (default "/oem/usr/ko/aic8800dc_fw")
# sources drivers/net/wireless/aic8800/{aic8800_fdrv,aic8800_btlpm}/Kconfig
# -- note this path is ALREADY the mainline drivers/net/wireless/
# convention; the vendor Kconfig assumes it, easing the in-tree move.
Makefile # obj-$(CONFIG_AIC8800_BTLPM_SUPPORT) += aic8800_btlpm/
# obj-$(CONFIG_AIC8800_WLAN_SUPPORT) += aic8800_fdrv/
# obj-$(CONFIG_AIC_WLAN_SUPPORT) += aic8800_bsp/
# (link order = bsp, fdrv, btlpm, matches the insmod order below)
aic8800_bsp/ # SDIO bus glue + firmware bootstrap ("bsp" = board support package)
aic_bsp_main.c # module_init/exit; per-chip firmware filename tables (fw_u02,
# fw_8800dc_u01, fw_8800dc_u02, ...); aicbsp_probe_semaphore
aicsdio.c # struct sdio_driver aicbsp_sdio_driver; SDIO_DEVICE_CLASS(WLAN)
# wildcard match + internal vendor/device ID probe:
# SDIO_VENDOR_ID_AIC8800DC=0xc8a1, SDIO_DEVICE_ID_AIC8800DC=0xc08d
# (aicsdio.c:75,80); calls rockchip_wifi_power()/
# rockchip_wifi_set_carddetect() (aicsdio.c:515-580), see section 3.7
aic8800dc_compat.c/.h, aic8800d80_compat.c/.h # per-chip-variant glue
aicwf_txq_prealloc.c, md5.c, aic_bsp_driver.c/.h, aicwf_firmware_array.c/.h
aic8800_fdrv/ # the actual cfg80211 full-MAC driver ("fdrv" = fullmac driver)
rwnx_main.c # struct cfg80211_ops rwnx_cfg80211_ops (line 5365); wiphy_new()
# (5732), wiphy_register() (6035); struct net_device_ops
# rwnx_netdev_ops (1457) / rwnx_netdev_monitor_ops (1477);
# 3 rtnl_lock()/rtnl_unlock() pairs (5429,6063,6097)
rwnx_msg_rx.c # firmware->driver event handling incl. cfg80211_connect_result()
# (line 958) and cfg80211_roamed() (1006,1010)
rwnx_cfgfile.c, rwnx_tx.c, rwnx_rx.c, rwnx_txq.c # datapath
aicwf_sdio.c # the actual SDIO transport (`aicwf_sdio_driver`, line 1216),
# THIS is the file the task's "aicwf_sdio" driver name refers to;
# it lives inside aic8800_fdrv/, not a separate directory. Also
# calls rockchip_wifi_power()/set_carddetect() (1260-1331) and
# contains the Tier-1 wakeup-sleep hardening (section 0) around the
# `aicwf_sdio_wakeup()` retry loop (~line 1363 in the older
# line numbering cited by the wiki; grep for `usleep_range`).
aicwf_tcp_ack.c, aicwf_rx_prealloc.c, aic_priv_cmd.c, aic_vendor.c
rwnx_compat.h # vendor compat shim layer, #if LINUX_VERSION_CODE guards up to
# 5.15.60 ONLY: everything past that (timer renames, wiphy
# locking, netif_rx_ni removal) is new territory, not covered.
usb_host.c/.h, rwnx_pci.*, rwnx_mesh.* # dead code on this board (no USB/PCI variant used)
aic8800_btlpm/ # Bluetooth low-power-mode / HCI wake companion module
aic8800_btlpm.c, aic_bluetooth_main.c, lpm.c, rfkill.c # standard rfkill_register(), no
# Rockchip-specific RFKILL_RK dependency (checked, none found)
aic8800dc_fw/ # firmware blobs, see section 5
Modules actually insmoded on the running 5.10 panel (from
sdk/sysdrv/drv_ko/wifi/insmod_wifi.sh:109-124, the #aic8800 stanza gated on
/proc/device-tree/model containing "W" or an SDIO uevent match):
cfg80211.ko -> libarc4.ko -> ctr.ko -> ccm.ko -> libaes.ko -> aes_generic.ko
-> aic8800_bsp.ko (sleep 0.2s) -> aic8800_fdrv.ko (sleep 2s) -> aic8800_btlpm.ko (sleep 0.1s)
rkwifi_server is deliberately never started (WardenOS owns wlan0 directly,
see insmod_wifi.sh:126-137 and wifi-bluetooth-aic8800.md); nothing to
replicate there. The crypto modules (arc4/ctr/ccm/aes) are dependencies of the
driver's internal key-handling, not aic8800-specific, confirm they're already
=y/reachable in the 6.18 config (crypto is currently listed as "[ ] batch2" in
../DRIVER-PARITY.md; flip alongside this work).
DT node: correction to the task's framing. The task description assumed
sdio: mmc@ff9a0000. That is wrong for this board. The AIC8800 is wired to
sdmmc: mmc@ffaa0000 (mmc1), not sdio: mmc@ff9a0000 (mmc2), confirmed
directly in rv1106g-luckfox-pico-86panel.dts:83-97 (comment literally reads
/**********SDIO-WIFI**********/ over the &sdmmc node) and independently in
luckfox-pico-86-panel/wifi-bluetooth-aic8800.md:11/hardware-86-panel.md:69.
&sdio (mmc@ff9a0000) stays status = "disabled" and is unused on this board.
There is no separate DT child node for the aic8800 chip itself: it's
discovered purely by SDIO bus-scan + vendor/device ID match inside the driver
(aicsdio.c, no of_match_table anywhere in the tree, checked, none found);
the only DT surface is the MMC controller node plus the power-sequencing node:
sdio_pwrseq: sdio-pwrseq { /* rv1106g-luckfox-pico-86panel.dts:20-23 */
compatible = "mmc-pwrseq-simple";
reset-gpios = <&gpio1 RK_PA2 GPIO_ACTIVE_LOW>;
};
&sdmmc { /* rv1106g-luckfox-pico-86panel.dts:83-97 */
max-frequency = <50000000>;
bus-width = <4>;
cap-sd-highspeed;
cap-sdio-irq;
keep-power-in-suspend;
non-removable;
rockchip,default-sample-phase = <90>;
supports-sdio;
mmc-pwrseq = <&sdio_pwrseq>;
pinctrl-names = "default";
pinctrl-0 = <&sdmmc0_clk &sdmmc0_cmd &sdmmc0_bus4 &sdmmc0_det>;
status = "okay";
};
The sdmmc0_{clk,cmd,bus4,det} pin groups are defined in
sdk/sysdrv/source/kernel/arch/arm/boot/dts/rv1106-pinctrl.dtsi:700-731 (a
separate include from rv1106.dtsi): port that block, it isn't in the DT
files the M1-M3 work already ported.
2. Open-source landscape (web research)
- AICSemi upstream: no discoverable, stable public GitHub org/repo carrying aic8800_bsp/fdrv/btlpm as canonical upstream, vendor releases exist only as SoC-vendor SDK drops (Rockchip's, in our case). Nothing more upstream to point at than what we have.
- Mainline/staging: absent. No aic8800 anywhere in
drivers/net/wireless/,drivers/staging/, or any LKML/patchwork series as of this research pass. - Cleanest newer-kernel fork found:
radxa-pkg/aic8800(github.com/radxa-pkg/aic8800), actively maintained, GPL-3.0, DKMS-packaged, patched for kernel 6.12/6.13 on Rockchip SDIO boards (Radxa Rock 3C/5C), covering SDIO/USB/PCIe AIC8800D80 variants. Firmware ships as a companionaic8800-firmwarepackage into/lib/firmware/aic8800_fw/. Treat as a reference/oracle for the API-delta hunks (section 3), not a vendoring source, see section 0 for why. An Armbian forum thread ("AIC8800 wifi sdio module not working with kernel 6.12+") reports it has its own open regressions at that boundary, so cross-check rather than trust each hunk blindly. - Other community efforts (Armbian, LuckFox's own OpenWrt branch, generic
BananaPi/OpenWrt feeds): no aic8800 kmod bundled by default even where the DT
wiring exists,
luckfox-pico-86-panel/alternative-bsps.md:20documents this exact gap for LuckFox's own OpenWrt target (cortexa7.mkshipskmod-rknpu-rockchiponly, nokmod-aic8800). Confirms there is no ready-made newer-kernel package to pull instead of porting. - plan44's OpenWrt RV1106 fork (
flare-edge/research/plan44-openwrt/, Linux 6.6, 152 RV1106 patches): checked directly, contains no aic8800 code at all (find ... -ipath '*aic8800*'empty). Confirmsbringup.md's "AIC8800 wifi (plan44 has none; ours)" and the "no plan44 code" decision doesn't cost us anything here: there's nothing to take.
3. 5.10 -> 6.18 API-delta checklist
Grounded in two passes: (a) direct kernel.org/bootlin/LWN research on 5.10->6.18
API history, (b) grep evidence from the actual vendor source (file:line cited
below) so this is a checklist against our code, not a generic survey.
3.1 Timers: confirmed hard breaks, exact call sites found
del_timer_sync/del_timer -> timer_delete_sync/timer_delete: renamed
in 9b13df3fb64e (landed v6.2-rc1) as a compat-wrapped rename; the
compat wrapper was removed in v6.15-rc1, so by 6.18 the old names do not
exist. from_timer() -> timer_container_of(): a real mainline rename
(~6.14-6.15 treewide timer-API cleanup; exact tag unconfirmed, verify against
the actual 6.18.46 headers already unpacked at
flare-edge/research/linux-6.18.46/include/linux/timer.h). Every call site in
the driver, found by direct grep (not estimated):
| API | File:line(s) |
|---|---|
from_timer() |
aic8800_bsp/aicsdio.c:1442; aic8800_fdrv/aicwf_sdio.c:279,317,2933; aic8800_fdrv/rwnx_main.c:1724; aic8800_fdrv/rwnx_rx.c:1751,2027 |
del_timer_sync() |
aic8800_bsp/aicsdio.c:1677; aic8800_fdrv/aicwf_sdio.c:1048,1054,1297,1303,3152; aic8800_fdrv/rwnx_main.c:2181; aic8800_fdrv/rwnx_rx.c:1501,1526 |
del_timer() (non-sync) |
aic8800_fdrv/aicwf_tcp_ack.c:108,367,401,462; aic8800_fdrv/rwnx_rx.c:1921,2607; aic8800_btlpm/aic8800_btlpm.c:580,603,951; aic8800_btlpm/lpm.c:558,581,935 |
setup_timer() (pre-4.14 dead branch) |
aic8800_fdrv/aicwf_tcp_ack.c:84: already #if LINUX_VERSION_CODE < KERNEL_VERSION(4,14,0) guarded against a live timer_setup() branch; delete the dead #if branch, don't port it |
timer_setup() calls (unaffected, just listed for completeness) |
aicsdio.c:2059; aicwf_sdio.c:3564,3589,3590; rwnx_main.c:1841,6118; rwnx_rx.c:1433,2510; aicwf_tcp_ack.c:87; both btlpm files:1104/1054 |
Mechanical fix: sed-rename from_timer->timer_container_of, del_timer_sync->
timer_delete_sync, del_timer->timer_delete across the ~20 call sites above.
timer_setup() itself is unchanged.
3.2 netdev
ndo_do_ioctlremoval (~6.7-era, split intondo_eth_ioctl/ndo_siocdevprivatearound 5.14/5.15): already handled by the vendor.rwnx_main.c:1457-1474already has#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 0)/#else .ndo_siocdevprivate = rwnx_do_ioctl,for bothrwnx_netdev_opsandrwnx_netdev_monitor_ops. Zero work needed: the guard picks the 6.18-correct member automatically. Verify only thatrwnx_do_ioctl's body (line 1368) still compiles against thendo_siocdevprivatesignature (int (*)(struct net_device *, struct ifreq *, void __user *, int)vs the old ioctl signature), check on the actual 6.18.46 headers.netif_rx_ni()removed (5.18, merged into plainnetif_rx(), safe from any context): three call sites, not version-guarded:rwnx_rx.c:404,598,1651. Mechanical fix:netif_rx_ni(rx_skb)->netif_rx(rx_skb).netif_napi_add()weight arg dropped (~6.1): not used anywhere in this driver (checked, nonetif_napi_add/NAPI in the tree, the driver does its own kthread-based RX processing, not NAPI polling). No action.
3.3 cfg80211: the hard, non-mechanical part
Verified directly against flare-edge/research/linux-6.18.46/include/net/cfg80211.h
(the actual target tree, not a guess from release notes):
cfg80211_connect_result(): confirmed unchanged and safe. In 6.18.46 it's astatic inlinethat just forwards tocfg80211_connect_bss()(cfg80211.h:8654-8661), with the exact same 8-argument signature the driver already calls atrwnx_msg_rx.c:958.cfg80211_roamed(), also confirmed unchanged: 6.18.46 signature iscfg80211_roamed(struct net_device *dev, struct cfg80211_roam_info *info, gfp_t gfp)(cfg80211.h:8748), matching the driver's call atrwnx_msg_rx.c:1006,1010(cfg80211_roamed(dev, &info, GFP_ATOMIC)against a localstruct cfg80211_roam_info info). No code change required here; migrating tocfg80211_connect_bss()directly is optional cleanup, not a requirement.wiphy_new()/wiphy_new_nm(): confirmed unchanged.wiphy_new(const struct cfg80211_ops *ops, int sizeof_priv)(cfg80211.h:6250-6253, inline wrapper overwiphy_new_nm(ops, sizeof_priv, NULL)): matchesrwnx_main.c:5732'swiphy_new(&rwnx_cfg80211_ops, sizeof(struct rwnx_hw))exactly. No signature-driven work needed.- Wiphy locking overhaul, real, confirmed present, and now precisely scoped
(not a guess).
cfg80211.h(~6266, 6325-6362) confirmswiphy_lock()/wiphy_unlock()/lockdep_assert_wiphy()and astruct wiphy_workdeferred-work mechanism all exist in 6.18.46, and (the decisive sentence, directly from thewiphy_lock()doc comment) "When cfg80211 ops are called, the wiphy is already locked." That means:- The driver's
cfg80211_opscallbacks themselves (rwnx_cfg80211_scan,_connect,_disconnect,_add_key,_mgmt_tx, etc.,rwnx_main.c:5365-5382) need no new locking: cfg80211 core now takes the wiphy mutex before calling into any of them, where 5.10-era cfg80211 relied on the caller holding RTNL instead. - The real risk is the other direction: this driver calls
cfg80211_scan_done()/cfg80211_connect_result()/cfg80211_roamed()from firmware-event handling, not from inside an ops callback,rwnx_msg_rx.c(async, driven by SDIO RX) andrwnx_main.c:1212,2082,2168(also async paths, not the ops entry points). Multiplecfg80211.hdoc comments for adjacent notification APIs state "the caller must hold ... wiphy mutex" (e.g.cfg80211.h:9428: "Caller must hold wiphy mutex, therefore must only be called from sleepable context"), the pattern that replaced the old "hold RTNL" requirement. Concretely: everycfg80211_*notification call reached fromrwnx_msg_rx.c/ the async paths inrwnx_main.cneedswiphy_lock(wiphy)/wiphy_unlock(wiphy)wrapped around it that wasn't there before (5.10 only neededrtnl_lock(), which this driver's 3 existingrtnl_lock()/rtnl_unlock()sites atrwnx_main.c:5429/5433, 6063/6075, 6097/6102already show it knows how to take defensively: the fix is adding the wiphy-mutex equivalent at the async notification sites, not at those 3 sites, which are netdev-registration paths and likely stay RTNL-only). - This is still the single highest-effort item in the port, not because the
contract is unknown (it's now confirmed above), but because applying it
correctly means auditing every async->cfg80211 call site in
rwnx_msg_rx.cand the async branches ofrwnx_main.c(1212, 2082, 2168, 2445-2645) one by one, and because a wrong lock order (wiphy mutex vs. RTNL vs. this driver's own internal locks/semaphores) produces lockdep splats or deadlocks that only show up under real traffic, not at compile time. Cross- check each hunk against howradxa-pkg/aic8800(section 0) handled the same transition on its 6.12/6.13 port: it hit this exact wall first.
- The driver's
3.4 SDIO/MMC
No breaking changes found in sdio_driver, sdio_claim_host/release_host,
sdio_readb/writesb, sdio_set_block_size between 5.10 and 6.18 (low
research depth on this axis, treat as low-risk, smoke-test rather than
line-audit). The mainline dw_mmc/dw_mmc-rockchip host driver is already
proven on 6.18 for eMMC (../DRIVER-PARITY.md: dw_mmc (eMMC) | mainline | [x] M3)
and CONFIG_MMC_DW_ROCKCHIP=y is already in the live .config, the SDIO
controller side of this port is de-risked; only the AIC8800 card driver
above the sdmmc bus is new work.
3.5 proc_ops
file_operations->proc_ops for procfs landed in 5.6: already true at the
5.10 baseline this driver was written against, and no further proc_ops changes
were found 5.10->6.18. The three files using proc_ops/proc_create
(aicwf_sdio.c, aic8800_btlpm.c, lpm.c) should need no change here.
3.6 DMA
No breaking coherent/streaming DMA API changes found 5.10->6.18 relevant to this
driver; SDIO drivers ride MMC-core DMA rather than calling
dma_alloc_coherent/dma_map_single directly for the card-side data path.
Low risk, not independently line-audited: flag if the build surfaces anything.
3.7 Vendor/Rockchip-only helpers: real mainline gap, not a version delta
rockchip_wifi_power() / rockchip_wifi_set_carddetect() (declared in
include/linux/rfkill-wlan.h, implemented in net/rfkill/rfkill-wlan.c in the
vendor 5.10 tree only: this is a Rockchip-BSP-vendor subsystem, not
mainline Linux, and does not exist in flare-edge/research/linux-6.18.46/).
Call sites: aic8800_bsp/aicsdio.c:515,517,556,580 and
aic8800_fdrv/aicwf_sdio.c:1260,1262,1329,1331 (all under
#ifdef CONFIG_PLATFORM_ROCKCHIP, which is true for this board). This is not
a rename: there is nothing to rename to. Recommended fix: stub these
calls out entirely rather than port rfkill-wlan.c. The DT already declares
a standard mainline mmc-pwrseq-simple (sdio_pwrseq, section 1) bound via
mmc-pwrseq = <&sdio_pwrseq>, which the mainline MMC core already drives at
bus-scan/power-up time through drivers/mmc/core/pwrseq_simple.c, a real
mainline mechanism doing the same job (GPIO power/reset sequencing) these
vendor calls were a pre-DT-pwrseq-era stand-in for. rockchip_wifi_set_carddetect()
is likewise redundant for a non-removable MMC device, which mainline already
auto-rescans. Treat every call site as #if 0/deleted, not ported.
get_cpu_version/rockchip_soc_id-style helpers: not called anywhere in
this driver (checked, no hits), the task's assumption that this driver calls
such a helper does not hold; no action needed. module_param, kthread_run/
kthread_should_stop: stable, unaffected, used extensively (RX/TX kthreads in
aicwf_sdio.c), no changes needed.
3.8 Firmware loading
request_firmware/request_firmware_nowait/release_firmware: stable
5.10->6.18, no signature changes found. Note this driver's actual firmware load
path is AIC_FW_PATH-relative direct file read via its own loader
(aic_load_fw/CONFIG_USE_FW_REQUEST is ?= n in the Makefile, the vendor
driver does not use the standard request_firmware() API by default, it
reads firmware files from a configurable path itself). Confirm this stays true
after the port; it's a deliberate vendor choice, not a version-driven gap.
4. File / config / DT port plan
4.1 Files to bring into the 6.18 tree
Copy aic8800dc/{aic8800_bsp,aic8800_fdrv,aic8800_btlpm}/*.{c,h} (source only:
exclude every generated .o/.ko/.mod.*/.cmd/Module.symvers/
modules.order artifact already sitting in the vendor tree from prior
out-of-tree builds) into the 6.18 tree at:
flare-edge/research/linux-6.18.46/drivers/net/wireless/aic8800/
Kconfig # new: top-level "source" wrapper, see 4.2
Makefile # obj-y for the 3 subdirs, preserving bsp->fdrv->btlpm link order
aic8800_bsp/ (from aic8800dc/aic8800_bsp/)
aic8800_fdrv/ (from aic8800dc/aic8800_fdrv/, minus usb_host.c/rwnx_pci.*/rwnx_mesh.*: dead
code on this board's SDIO-only, non-mesh config; keep out unless a build
error proves them referenced elsewhere)
aic8800_btlpm/ (from aic8800dc/aic8800_btlpm/)
This is exactly the path the vendor's own Kconfig already assumes
(source "drivers/net/wireless/aic8800/aic8800_fdrv/Kconfig", section 1), no path
rewriting needed inside the sub-Kconfigs. Firmware blobs (section 5) go to
aic8800dc_fw/ under the rootfs firmware path, not into the kernel tree.
Apply, in this order, on top of the copied source: the 0001 queue_sz clamp
patch, and the Tier-1 SDIO-wakeup-sleep patch (currently only tracked under
flare-edge/future-features-2/sdk-patches/wifi/patches/0002-aic8800dc-sdio-wakeup-sleep-not-spin.patch:
pull it from there; it is not in this branch's own sdk-patches/wifi/, a
separate housekeeping gap worth closing regardless of this port). Then apply
the API-delta fixes from section 3.
4.2 Kconfig wiring
Add one line to flare-edge/research/linux-6.18.46/drivers/net/wireless/Kconfig
(alongside the existing source "drivers/net/wireless/broadcom/Kconfig" etc.
list, Kconfig:22-38):
source "drivers/net/wireless/aic8800/Kconfig"
New drivers/net/wireless/aic8800/Kconfig, carry the vendor's aic8800dc/Kconfig
content forward nearly verbatim (it already has the right shape: an
AIC_WLAN_SUPPORT bool gate, an AIC_FW_PATH string default, and source
lines for the fdrv/btlpm sub-Kconfigs) but change the two tristate symbols in
aic8800_fdrv/Kconfig (AIC8800_WLAN_SUPPORT) and aic8800_btlpm/Kconfig
(AIC8800_BTLPM_SUPPORT) to default y, and set AIC_WLAN_SUPPORT
default y, per the task's requirement, everything built in, not modular,
since the 6.18 rootfs has no working module-loading pipeline yet
(../PORT-STATUS.md M3 note: the old 5.10 .kos already fail on 6.18 from
vermagic mismatch, and a rebuilt 6.18 module tree for Buildroot doesn't exist
yet either).
4.3 Config symbols (=y, built-in)
CONFIG_WIRELESS=y # already =y in the live 6.18 .config
CONFIG_CFG80211=y # already =y, flipped in "batch2" per ../DRIVER-PARITY.md
CONFIG_WLAN=y # already =y
CONFIG_MMC=y # already =y
CONFIG_MMC_DW_ROCKCHIP=y # already =y
CONFIG_RFKILL=y # already =y
CONFIG_AIC_WLAN_SUPPORT=y # new
CONFIG_AIC8800_WLAN_SUPPORT=y # new
CONFIG_AIC8800_BTLPM_SUPPORT=y # new
CONFIG_BT=y # currently =m in the live .config, flip to =y for the same
# no-working-module-pipeline reason as the aic8800 pieces
CONFIG_BT_HCIUART=y # currently =m, flip alongside CONFIG_BT
CONFIG_BT_HCIUART_H4=y # already =y (H4 is the transport this board's BT actually uses,
# per hardware-86-panel.md: UART1/ttyS1, hciattach -s 1500000
# ... any 1500000 flow nosleep)
CONFIG_CRYPTO_ARC4=y CONFIG_CRYPTO_CTR=y CONFIG_CRYPTO_CCM=y CONFIG_CRYPTO_AES=y # driver's
# internal key-handling deps, currently "[ ] batch2" in
# ../DRIVER-PARITY.md, confirm =y, not =m, alongside this work
Do not enable CONFIG_MAC80211 for this driver: confirmed by source
inspection (section 3.3 note, no ieee80211_hw/mac80211 symbol usage anywhere in
aic8800_fdrv/) that this is a pure cfg80211_ops full-MAC driver; mac80211
was only ever needed for the other vendor WiFi chips in the shared
sdk/sysdrv/drv_ko/wifi/ tree (RTL8189FS etc.), not this one. Leaving it out
avoids pulling in a subsystem this port doesn't need. Do not carry
CONFIG_RFKILL_RK: confirmed unused (aic8800_btlpm/rfkill.c calls the
standard mainline rfkill_register(), no Rockchip-specific rfkill hook).
4.4 DT changes
On the board DT that M4/M5 work extends
(warden-sdk/kernel/rv1106-enablement/dts/rv1106-warden.dts or its successor,
follow the pattern of the M3 &emmc addition in rv1106-warden-m2.dts:126-142):
- Port the
sdmmc0_{clk,cmd,bus4,det}pinctrl group fromsdk/sysdrv/source/kernel/arch/arm/boot/dts/rv1106-pinctrl.dtsi:700-731(not yet in the ported tree: M1-M3 only needed the eMMC/uart2/eth pin groups). - Add the
sdmmc: mmc@ffaa0000node (clocksHCLK_SDMMC/CCLK_SRC_SDMMCoff&cru,SCLK_SDMMC_DRV/SCLK_SDMMC_SAMPLEoff&grf_cru, the samegrf_crudependency the M2 eMMC fix already established, so no new prerequisite) and thesdio_pwrseqnode, both transplanted verbatim from section 1's block (interruptGIC_SPI 52, perrv1106.dtsi:1403-1412in the vendor tree). status = "okay"on&sdmmc, matching the vendor board DT exactly (section 1).- Do not touch
&sdio(mmc@ff9a0000): leavedisabled, it's genuinely unused hardware on this board (section 1 correction).
4.5 Driver init-order note
The 5.10 module load order (insmod_wifi.sh, section 1) is
aic8800_bsp -> (200ms) -> aic8800_fdrv -> (2s!) -> aic8800_btlpm, with real sleep
delays between each. Built-in (=y), there is no equivalent explicit delay:
initcall order is controlled by link order (the vendor Makefile's obj-y list
is already aic8800_btlpm/ aic8800_fdrv/ aic8800_bsp/ in Makefile-declaration
order but Kbuild links obj-y in Makefile order regardless of which appears
first in the ifeq block, preserve aic8800_bsp before aic8800_fdrv
before aic8800_btlpm in the new drivers/net/wireless/aic8800/Makefile's
obj-y list, mirroring the working insmod order) and by each subsystem's
declared module_init()/initcall level (all three currently use plain
module_init(), which becomes device_initcall level when built-in, same
level for all three, so link order is what decides relative sequencing among
them). The observed 2-second gap between aic8800_bsp and aic8800_fdrv on
the running 5.10 system is suspicious enough (firmware download + chip
bring-up time) that if the built-in probe races ahead of firmware readiness,
watch for it specifically during bring-up (section 6): this is a real risk the
static-link change introduces that modular loading didn't have, not just a
formality.
5. Firmware notes
Firmware is not open source (binary blobs, as expected for WiFi/BT RF/PHY
patches) but is freely redistributable: no export-control/NDA marking found on
the files themselves or in the vendor tree's licensing. 21 files,
sdk/sysdrv/drv_ko/wifi/aic8800dc/aic8800dc_fw/ (484 KB total):
aic_userconfig_8800dc.txt, aic_userconfig_8800dw.txt # text config, not firmware
fmacfw_calib_8800dc_{u02,h_u02,hbt_u02}.bin # full-MAC calibration firmware
fmacfw_patch_8800dc_{u02,h_u02,hbt_u02,ipc_u02}.bin # full-MAC patch firmware
fmacfw_patch_tbl_8800dc_{u02,h_u02,hbt_u02,ipc_u02}.bin # patch tables
fw_adid_8800dc_{u02,u02h}.bin # analog/RF ADID cal data
fw_patch_{8800dc_u02,8800dc_u02_ext0,8800dc_u02h}.bin # BT patch firmware
fw_patch_table_8800dc_{u02,u02h}.bin # BT patch tables
lmacfw_rf_8800dc.bin # RF test-mode firmware
Per aic_bsp_main.c's fw_8800dc_u02[] table (section 1), the exact subset loaded at
runtime depends on AICBSP_CPMODE_WORK vs _TEST and on CONFIG_SDIO_BT
(=n in the vendor Makefile: WiFi and BT firmware are loaded/attached
separately, not as one combo blob, despite the combo chip). Firmware load
path: AIC_FW_PATH Kconfig default is /oem/usr/ko/aic8800dc_fw, this is a
5.10-era Buildroot-rootfs-layout artifact (the /oem partition), not a kernel
concept; on the 6.18 rootfs, point this at wherever WardenOS's 6.18 Buildroot
userspace places firmware (likely /lib/firmware/aic8800dc/ if following
standard mainline convention, or keep /oem/usr/ko/aic8800dc_fw if the 6.18
rootfs partition layout is unchanged from 5.10, confirm against whatever the
M4/M5 rootfs build actually produces, this is a rootfs-layout decision, not a
kernel one). Not found in linux-firmware.git (the mainline firmware
project): AIC8800 firmware has not been upstreamed there; continue shipping
it the way the vendor tree already does (bundled alongside the driver, loaded
by direct file read per section 3.8, not request_firmware()).
Chip variant: AIC8800DC, confirmed authoritative by the board config
(RK_ENABLE_WIFI_CHIP=AIC8800DC) and the SDIO device ID match in source
(aicsdio.c:75,80: vendor 0xc8a1, device 0xc08d), not the dual-band
AIC8800D80 seen on other LuckFox boards. 2.4 GHz only in practice (confirmed
on hardware per wifi-bluetooth-aic8800.md:10: iw phy shows 1 band, 0
5 GHz channels, despite AIC8800DC being marketed dual-band-capable elsewhere).
"AIC8800DW" appears loosely in some sources for the same part; the board .mk
is authoritative.
6. Verify steps
Follow the existing A/B _b-slot hardware-verification loop
(warden-sdk/kernel/docs/m2-boot-on-c8a3.md), same method M1-M3 already used:
- Build: driver compiles clean into the 6.18.46 tree (
make ... modulesis not the target, it's=y, so this is justmake zImage/whatever M2'sbuild-m2.shwraps, succeeding with the newdrivers/net/wireless/aic8800/objects linked intovmlinux/the zImage). - Probe: boot on
warden-c8a3, confirm indmesg: thesdmmcMMC host binds (dwmmc_rockchip ffaa0000.mmc: ..., same pattern M3 already proved forffa90000.mmc/eMMC), then an SDIO card enumerates on it, thenaic8800_bsp's probe fires (vendor/device ID match, firmware file opens succeed, watch specifically forAIC_FW_PATHresolution failures, section 5), thenaic8800_fdrvattaches andwlan0appears inip link. - cfg80211 sanity:
iw physhows the expected single 2.4 GHz band/14 channels (matching the known-good 5.10 baseline inwifi-bluetooth-aic8800.md:10, a mismatch here is a signal something in the cfg80211/wiphy port is wrong, not a chip regression). - Scan:
iw dev wlan0 scanreturns nearby APs, exercisesrwnx_cfg80211_scan/cfg80211_scan_done()and, indirectly, whether the wiphy-locking port (section 3.3) is functioning rather than deadlocking. - Connect: associate to a real AP (
wpa_supplicant/wpa_cli, matching WardenOS's ownwlan0ownership model, do not startrkwifi_server, section 1) and confirmCOMPLETEDstate plus a DHCP lease, exercisesrwnx_cfg80211_connect/cfg80211_connect_result(). - Known-regression checks (carry forward, don't re-discover): confirm the
two hardening patches (section 0/section 4.1) are actually effective, no console-flood
"cmd timed-out" spam under load, and no CPU-pinning busy-spin if the SDIO
link is stressed (
aicwf_bustx_thrshould sleep, not spin, on a wakeup failure). Confirmiw dev wlan0 set power_save offstill behaves as expected (a wall-powered panel, no reason to want power-save:wifi-bluetooth-aic8800.md:41). - BT (secondary to WiFi but same milestone): confirm
aic8800_btlpmattaches andhciattach -s 1500000 /dev/ttyS1 any 1500000 flow nosleep(the known-good invocation) brings up an HCI device. - Update on landing: mark the
AIC8800 wifi (bsp/fdrv)andAIC8800 BT (btlpm)rows in../DRIVER-PARITY.md[x], with the same "hardware-verified, not just compiled" bar every other row uses.
Sources
- Local (SDK/repo evidence, cited inline above by path:line):
flare-edge/sdk/sysdrv/drv_ko/wifi/aic8800dc/**,flare-edge/sdk/sysdrv/drv_ko/wifi/Makefile,flare-edge/sdk/sysdrv/source/kernel/arch/arm/boot/dts/{rv1106.dtsi,rv1106-pinctrl.dtsi,rv1106g-luckfox-pico-86panel.dts},flare-edge/sdk/sysdrv/source/kernel/arch/arm/configs/{rv1106-sdiowifi.config,luckfox_rv1106_linux_defconfig},flare-edge/*/sdk-patches/wifi/patches/*.patch,flare-edge/research/linux-6.18.46/.config(live, post-M3, batch2 CFG80211=y),flare-edge/research/linux-6.18.46/include/net/cfg80211.h(directly read to verifywiphy_new,cfg80211_connect_result,wiphy_lock/lockdep_assert_wiphyagainst the actual target tree, not release notes, section 3.3),flare-edge/research/plan44-openwrt/(checked, no aic8800),warden-sdk/kernel/rv1106-enablement/{PORT-STATUS.md,DRIVER-PARITY.md,OVERNIGHT-PLAN.md},warden-sdk/kernel/docs/{bringup.md,m2-boot-on-c8a3.md},luckfox-pico-86-panel/{wifi-bluetooth-aic8800.md,hardware-86-panel.md,alternative-bsps.md,sdk-patches.md,boot-chain.md}. - Web (via research pass, see section 0/section 2): github.com/radxa-pkg/aic8800 +
deepwiki.com/radxa-pkg/aic8800; forum.armbian.com topic 50332 ("AIC8800 wifi
sdio module not working with kernel 6.12+"); kernel.org/patchwork commits
9b13df3fb64e(timer_delete rename),2655926aea9b(netif_rx_ni removal),a05829a7222e(wiphy-lock RTNL migration start); kernelnewbies.org/Linux_6.12; LKML/lkml.iu.edu mirrors for the timer-rename and wiphy-guard series. Patchwork/LWN direct fetches were partially blocked by anti-bot walls during research, so the exact commit/version the wiphy-lock migration landed in is not pinned precisely, but its presence and current contract in the actual 6.18.46 tree we're porting to is directly confirmed (previous paragraph), which is the fact that actually matters for this port.