说明

ROCK 4D 板载一块 AIC8800D80 无线网卡。在 Radxa 提供的 Armbian 中,这块网卡可以正常工作,但刷入 OpenWrt 官方固件后,系统里没有无线设备。本文记录从 USB 枚举、设备树供电到驱动和固件打包的完整处理过程。

最后确认这不是一个单独的“缺驱动”问题,而是两个条件同时没有满足:板载模组没有在启动早期保持上电,OpenWrt 中也没有 AIC8800 对应的厂商驱动和固件。两部分都补齐后,AIC8800D80 可以在冷启动时正常枚举并建立 2.4 GHz AP。

本文涉及的实现保存在我的 GitHub 仓库 ovo-ukiyo/openwrtrock-4d-aic8800-router-hat 分支中。驱动打包和设备树供电修改分别保留为独立 commit,便于复现和后续整理上游补丁。

初始现象

在 OpenWrt 中执行 lsusb 时,可以看到下面这个设备:

1
Bus 001 Device 002: ID 1a40:0101 Terminus Technology Inc. Hub

开发板没有外接 USB 设备,所以最初容易把它当作板载无线网卡。实际情况是,1a40:0101 只是 ROCK 4D 板上的内部 USB Hub。AIC8800D80 接在这个 Hub 后面,只有模组完成上电并进入工作状态后,它的 USB 功能才会继续枚举。

AIC8800D80 正常出现时的 USB ID 为:

1
a69c:8d81

原始 OpenWrt 启动后只能看到 Hub,看不到 a69c:8d81。这说明排查不能只停留在无线配置和内核模块,还要先确认模组供电和 USB 枚举。

对比设备树

ROCK 4D 的无线使能脚为 GPIO2_PD1。原始设备树把它描述成一个 rfkill-gpio 节点:

1
2
3
4
5
6
7
rfkill {
compatible = "rfkill-gpio";
pinctrl-names = "default";
pinctrl-0 = <&wifi_en_h>;
radio-type = "wlan";
shutdown-gpios = <&gpio2 RK_PD1 GPIO_ACTIVE_HIGH>;
};

这个描述没有让模组在 USB 主机枚举前稳定地保持使能。对于焊在板上的 USB 无线模组,比较直接的处理方式是将该引脚描述为常开的固定电源,使 GPIO 在启动早期拉高,并在系统运行期间保持高电平。

修改后的节点如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
wifi_chip_en: regulator-wifi-chip-en {
compatible = "regulator-fixed";
regulator-name = "wifi_chip_en";
regulator-min-microvolt = <3300000>;
regulator-max-microvolt = <3300000>;
regulator-boot-on;
regulator-always-on;
enable-active-high;
gpios = <&gpio2 RK_PD1 GPIO_ACTIVE_HIGH>;
startup-delay-us = <5000>;
pinctrl-names = "default";
pinctrl-0 = <&wifi_en_h>;
};

这里保留了原来的 wifi_en_h pinctrl 状态,并增加了 5 ms 的启动等待。regulator-boot-onregulator-always-on 的目的,是让板载模组在 USB 枚举之前就具备稳定的工作条件,而不是等到用户态加载无线配置后才处理开关。

对应的 OpenWrt 补丁放在:

1
target/linux/rockchip/patches-6.12/051-11-rock4d-aic8800-enable.patch

完整修改可以直接查看:

这个补丁没有修改 USB 控制器或无线驱动的探测逻辑,只是改变板载模组的供电描述。固定 regulator 在启动阶段由内核 regulator 框架接管,使能脚会比用户态无线服务更早拉高,因此 USB Host 第一次枚举时就能够看到模组。

将厂商驱动做成 OpenWrt 包

解决供电后还需要驱动。AIC8800D80 使用的不是内核主线中的常规 mac80211 驱动,因此不能只安装一个通用 kmod。本次使用 Radxa 维护的 radxa-pkg/aic8800 源码,并固定到一个确定的提交,避免以后上游仓库变化造成构建结果漂移:

1
2
PKG_SOURCE_URL:=https://github.com/radxa-pkg/aic8800.git
PKG_SOURCE_VERSION:=bd11969265809a0fc948f1107c8256bbb2c1aa60

OpenWrt 包路径为:

1
package/kernel/aic8800-usb/

仓库中的完整打包实现如下:

这里把内核模块和固件拆成 kmod-aic8800-usbaic8800-usb-firmware 两部分。驱动包负责生成和自动加载 .ko,固件包负责把二进制文件放到驱动实际查找的位置。这样既符合 OpenWrt 的包结构,也能避免把固件文件直接散落在 target 目录中。

最终安装两个内核模块:

1
2
aic_load_fw.ko
aic8800_fdrv.ko

加载顺序为先加载固件辅助模块,再加载无线功能驱动:

1
AUTOLOAD:=$(call AutoLoad,70,aic_load_fw aic8800_fdrv)

固件安装到:

1
/lib/firmware/aic8800_fw/USB/aic8800D80/

同时建立兼容厂商驱动默认查找路径的软链接:

1
/lib/firmware/aic8800D80 -> aic8800_fw/USB/aic8800D80

驱动需要使用 OpenWrt 的 mac80211 backports 头文件,并引用 mac80211.symvers 中的符号版本。否则即使厂商源码可以单独编译,也不能保证生成的模块能与当前 OpenWrt 内核正常链接和加载。

处理内核兼容问题

测试使用的是 OpenWrt 25.12 开发版本和 Linux 6.12。厂商驱动不是为这套构建环境直接准备的,编译时处理了三类问题。

第一处是 CONFIG_USE_WIRELESS_EXT 关闭后留下的未使用标签。OpenWrt 将部分警告作为错误处理,因此需要保留对公共清理标签的语法引用。

第二处是较新 cfg80211 接口的参数变化。cfg80211_ops.get_tx_power 在新接口中增加了 radio 和 link 参数,驱动回调需要按对应内核版本补齐签名。

第三处是厂商源码使用同一个缓冲区作为 sprintf 的输入和输出,拼接固件路径时会触发重叠访问问题。补丁改用有边界的字符串追加方式:

1
strlcat(aic_fw_path, "/aic8800D80", sizeof(aic_fw_path));

对应补丁为:

1
2
3
100-linux-6.12-unused-putbss-label.patch
110-linux-6.17-get-tx-power-signature.patch
120-fix-overlapping-firmware-path-format.patch

三份兼容补丁可以在上述 驱动打包 commit 中查看完整 diff。它们只负责让厂商源码通过当前 OpenWrt 内核与工具链构建,没有改变射频参数、国家码或发射功率配置。

当前打包还通过替换 LINUX_VERSION_CODE 来适配厂商源码内部的大量版本判断。这可以用于验证和本地固件,但不是理想的长期方案。后续若提交到上游,需要把真正涉及的 API 差异逐项整理,去掉这种全局版本伪装。

编译与验证

选择驱动和固件包后,先下载所有源码,再进行正式构建:

1
2
3
make defconfig
make download -j8
make -j"$(nproc)"

刷入固件后要做一次彻底断电再上电。只执行软件重启不能完整验证模组的上电时序。启动后依次检查:

1
2
3
4
5
lsusb
lsmod | grep -E 'aic_load_fw|aic8800_fdrv'
find /lib/firmware/aic8800_fw/USB -maxdepth 2 -type f
dmesg | grep -Ei 'aic|8d81|firmware|usb'
iw dev

本次验证结果为:

  • 冷启动后出现 a69c:8d81
  • aic_load_fwaic8800_fdrv 均成功加载;
  • 驱动能够从 /lib/firmware/aic8800_fw/USB 找到固件;
  • 系统生成无线接口,2.4 GHz AP 可以正常工作。

小结

ROCK 4D 板载 AIC8800D80 在 OpenWrt 中不可用由两部分共同造成。设备树中的 rfkill-gpio 没有为 USB 模组提供合适的早期常开使能,OpenWrt 本身也缺少对应的厂商驱动和固件。

GPIO2_PD1 改为启动即开启的固定 3.3 V regulator,解决了模组在 USB 枚举阶段没有准备好的问题;加入 AIC8800 USB 驱动、固件以及内核兼容补丁后,才完成了无线功能支持。

目前这套实现适合保存本地可复现构建。由于驱动仍是 out-of-tree 厂商代码,正式提交 OpenWrt 前还需要继续确认源码与固件许可、mac80211 backports 的维护方式,以及各项内核版本兼容修改。