记录从开箱到开箱即用的完整折腾路径,包括内核兼容性修复和开机自动配置。
目录
背景
硬件:UGREEN AX900 USB WiFi 6 网卡
芯片:AICSemi AIC8800DC
系统:Ubuntu 24.04 LTS
内核:6.17.0-20-generic
刚装完系统,插入网卡,没有任何反应。
$ lsusb | grep -i aic
# 空,什么都没有
$ ip link
# 只有 lo 和有线网卡,没有无线网卡AIC8800 官方驱动只支持到内核 3.10+,在 Linux 6.x 上编译直接报错。于是开始了折腾之路。
问题一:驱动编译失败
现象
从绿联官网或 AICSemi 下载的驱动源码,直接 make 报错:
$ make
...
error: implicit declaration of function 'del_timer'
error: too few arguments to function 'cfg80211_rx_spurious_frame'
error: initialization of 'int (*)(struct wiphy *, int, u32)' from incompatible pointer type
...原因分析
Linux 6.x 内核有几处 API 变更:
| API 变更 | 旧版本 | Linux 6.x |
|---|---|---|
| 定时器 | del_timer() | timer_delete() |
| cfg80211 | set_wiphy_params(wiphy, changed) | 增加 radio_idx 参数 |
| 唤醒源 | wakeup_source_create() + wakeup_source_add() | wakeup_source_register() |
修复方案
1. 修复 del_timer API
在 rwnx_main.c、rwnx_rx.c、aicwf_tcp_ack.c 中添加:
#include <linux/version.h>
#include <linux/timer.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)
#define del_timer(timer) timer_delete(timer)
#define del_timer_sync(timer) timer_delete_sync(timer)
#endif2. 修复 cfg80211 API
在 rwnx_main.c 中修改两个函数签名:
// set_wiphy_params 增加 radio_idx 参数
static int rwnx_cfg80211_set_wiphy_params(struct wiphy *wiphy,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)
int radio_idx,
#endif
u32 changed)
// set_tx_power 增加 radio_idx 参数
static int rwnx_cfg80211_set_tx_power(struct wiphy *wiphy,
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 8, 0)
struct wireless_dev *wdev,
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)
int radio_idx,
#endif
enum nl80211_tx_power_setting type, int mbm)3. 修复 wakeup_source API
在 rwnx_wakelock.c 中:
struct wakeup_source *rwnx_wakeup_init(const char *name)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0)
return wakeup_source_register(NULL, name);
#else
struct wakeup_source *ws;
ws = wakeup_source_create(name);
wakeup_source_add(ws);
return ws;
#endif
}4. 修复 sprintf 源目标重叠
在 aicwf_compat_8800d80.c 等文件中:
// 错误的写法(GCC 会警告)
sprintf(aic_fw_path, "%s/%s", aic_fw_path, "aic8800DC");
// 正确的写法
{
char tmp_path[200];
strncpy(tmp_path, aic_fw_path, sizeof(tmp_path) - 1);
tmp_path[sizeof(tmp_path) - 1] = '\0';
snprintf(aic_fw_path, sizeof(aic_fw_path), "%s/%s", tmp_path, "aic8800DC");
}5. 修复 from_timer 宏
在需要使用的文件中添加:
#ifndef from_timer
#define from_timer(var, callback_timer, timer_fieldname) \
container_of(callback_timer, typeof(*var), timer_fieldname)
#endif编译安装
cd aic8800_linux_driver/drivers/aic8800
make clean
make
sudo make install成功后会生成两个 .ko 文件:
aic_load_fw/aic_load_fw.ko- 固件加载器aic8800_fdrv/aic8800_fdrv.ko- WiFi 驱动
问题二:每次开机都要手动加载驱动
现象
每次重启后:
$ lsmod | grep aic
# 空的,驱动没加载
$ ip link
# 没有无线网卡必须手动执行:
sudo modprobe cfg80211
sudo modprobe aic_load_fw
sudo modprobe aic8800_fdrv原因
驱动没有配置为开机自动加载。
解决方案
方案 1:配置 modules-load.d(推荐)
echo -e "cfg80211\naic_load_fw\naic8800_fdrv" | sudo tee /etc/modules-load.d/aic8800.conf方案 2:使用 systemd 服务
创建 /etc/systemd/system/aic8800-autoload.service:
[Unit]
Description=AIC8800 USB WiFi Driver
After=systemd-udev-settle.service
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/sbin/modprobe aic8800_fdrv
[Install]
WantedBy=multi-user.target启用服务:
sudo systemctl daemon-reload
sudo systemctl enable aic8800-autoload.service问题三:为什么要"拔掉再插上"
现象
按照网上教程,加载驱动后要:
lsmod | grep aic确认驱动加载成功- 拔掉网卡
- 重新插入网卡
- 网卡才能工作
根本原因:USB 网卡的双模式设计
AIC8800 芯片的 USB WiFi 网卡采用双模式设计:
| 模式 | VID:PID | 说明 |
|---|---|---|
| 存储模式 | a69c:5721 | 插入时默认模式,系统识别为 U 盘(用于 Windows 自动安装驱动) |
| WiFi 模式 | a69c:8800 | 实际工作的无线网卡模式 |
工作流程
插入网卡
↓
系统识别为 USB 存储设备 (a69c:5721)
↓
执行 eject /dev/aicudisk(弹出存储设备)
↓
网卡自动重新枚举为 WiFi 设备 (a69c:8800)
↓
驱动识别并初始化设备
↓
网卡正常工作为什么必须先加载驱动再插网卡?
驱动加载顺序问题:
如果先插网卡,后加载驱动:
- 网卡处于存储模式 (
a69c:5721) - 驱动加载时寻找 WiFi 设备 (
a69c:8800),找不到 - 驱动初始化失败或无法绑定设备
- 网卡处于存储模式 (
正确的顺序:
- 先加载驱动(注册 USB 设备监听)
- 插入网卡 → 触发 udev 规则 → 自动执行
eject切换模式 - 网卡切换到 WiFi 模式 → 驱动识别并绑定
手动操作流程
# 1. 加载驱动(等待设备)
sudo modprobe cfg80211
sudo modprobe aic_load_fw
sudo modprobe aic8800_fdrv
# 2. 确认驱动已加载
lsmod | grep aic
# aic8800_fdrv 696320 0
# aic_load_fw 94208 1 aic8800_fdrv
# 3. 插入网卡(udev 自动处理模式切换)
# 或者手动切换:sudo eject /dev/aicudisk
# 4. 查看网卡
ip link
# wlan0: <BROADCAST,MULTICAST> mtu 1500 qdisc noop state DOWN mode DEFAULT group default qlen 1000完整解决方案
一键配置开机自动加载
我已经创建了完整的自动配置脚本:
cd aic8800_linux_driver/tools/autostart
sudo ./setup-autostart.sh这个脚本会自动完成:
- 安装固件到
/lib/firmware/aic8800D80/ 配置 udev 规则:
- 检测到存储模式 (
a69c:5721) → 自动eject切换模式 - 检测到 WiFi 模式 (
a69c:8800) → 自动加载驱动
- 检测到存储模式 (
- 配置模块开机加载:
cfg80211、aic_load_fw、aic8800_fdrv - 创建 systemd 服务:处理开机时网卡已插入的情况
然后重启电脑,网卡就能自动识别了。
卸载自动配置
如果需要恢复手动操作:
sudo ./remove-autostart.sh验证
检查网卡是否识别
$ lsusb | grep a69c
Bus 001 Device 007: ID a69c:8800 AICSemi AIC8800DC
# 注意 VID:PID 是 8800(WiFi 模式),不是 5721(存储模式)检查驱动是否加载
$ lsmod | grep aic
aic8800_fdrv 696320 0
aic_load_fw 94208 1 aic8800_fdrv
cfg80211 1462272 3 b43,mac80211,aic8800_fdrv检查网络接口
$ ip link
3: wlan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DORMANT group default qlen 1000
link/ether xx:xx:xx:xx:xx:xx brd ff:ff:ff:ff:ff:ff连接 WiFi
nmcli device wifi list
nmcli device wifi connect "SSID" password "password"总结
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 编译失败 | Linux 6.x API 变更 | 修改 9 个文件,添加兼容性宏 |
| 开机不自动加载 | 未配置模块自动加载 | 配置 modules-load.d 和 systemd 服务 |
| 需要拔插 | USB 双模式设计,需要切换模式 | udev 规则自动处理模式切换 |
最终效果:
- 开机自动识别网卡,无需手动操作
- 热插拔支持,随时插拔都能自动识别
- 适配 Linux 6.x 内核
参考
- 修复后的驱动源码:ugreen-ax900-6.x-kernel-fix-ver
- AICSemi 官方驱动(旧版)
- Linux 内核 API 变更文档
Ubuntu 24.04 + 内核 6.17.0