ROCK 4D 在 OpenWrt 下驱动板载 AIC8800D80
说明
ROCK 4D 板载一块 AIC8800D80 无线网卡。在 Radxa 提供的 Armbian 中,这块网卡可以正常工作,但刷入 OpenWrt 官方固件后,系统里没有无线设备。本文记录从 USB 枚举、设备树供电到驱动和固件打包的完整处理过程。
最后确认这不是一个单独的“缺驱动”问题,而是两个条件同时没有满足:板载模组没有在启动早期保持上电,OpenWrt 中也没有 AIC8800 对应的厂商驱动和固件。两部分都补齐后,AIC8800D80 可以在冷启动时正常枚举并建立 2.4 GHz AP。
本文涉及的实现保存在我的 GitHub 仓库 ovo-ukiyo/openwrt 的 rock-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 | rfkill { |
这个描述没有让模组在 USB 主机枚举前稳定地保持使能。对于焊在板上的 USB 无线模组,比较直接的处理方式是将该引脚描述为常开的固定电源,使 GPIO 在启动早期拉高,并在系统运行期间保持高电平。
修改后的节点如下:
1 | wifi_chip_en: regulator-wifi-chip-en { |
这里保留了原来的 wifi_en_h pinctrl 状态,并增加了 5 ms 的启动等待。regulator-boot-on 和 regulator-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 | PKG_SOURCE_URL:=https://github.com/radxa-pkg/aic8800.git |
OpenWrt 包路径为:
1 | package/kernel/aic8800-usb/ |
仓库中的完整打包实现如下:
这里把内核模块和固件拆成 kmod-aic8800-usb 与 aic8800-usb-firmware 两部分。驱动包负责生成和自动加载 .ko,固件包负责把二进制文件放到驱动实际查找的位置。这样既符合 OpenWrt 的包结构,也能避免把固件文件直接散落在 target 目录中。
最终安装两个内核模块:
1 | aic_load_fw.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 | 100-linux-6.12-unused-putbss-label.patch |
三份兼容补丁可以在上述 驱动打包 commit 中查看完整 diff。它们只负责让厂商源码通过当前 OpenWrt 内核与工具链构建,没有改变射频参数、国家码或发射功率配置。
当前打包还通过替换 LINUX_VERSION_CODE 来适配厂商源码内部的大量版本判断。这可以用于验证和本地固件,但不是理想的长期方案。后续若提交到上游,需要把真正涉及的 API 差异逐项整理,去掉这种全局版本伪装。
编译与验证
选择驱动和固件包后,先下载所有源码,再进行正式构建:
1 | make defconfig |
刷入固件后要做一次彻底断电再上电。只执行软件重启不能完整验证模组的上电时序。启动后依次检查:
1 | lsusb |
本次验证结果为:
- 冷启动后出现
a69c:8d81; aic_load_fw和aic8800_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 的维护方式,以及各项内核版本兼容修改。





