ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Ubuntu 20.04离线安装Realtek b852无线网卡驱动全攻略

Ubuntu 20.04离线安装Realtek b852无线网卡驱动全攻略 1. 一块网卡引发的折腾为什么离线装驱动比想象中麻烦Realtek b852 这块无线网卡最近两年在不少轻薄本和迷你主机上出现得挺频繁。它本身是 RTL8852BE 系列的衍生型号支持 Wi-Fi 6 和蓝牙 5.2纸面参数不差。但问题在于Ubuntu 20.04 自带的 5.4 内核里rtw89 驱动还处于非常早期的阶段压根认不出这块卡。你装完系统打开网络设置会发现Wi-Fi那一栏直接是灰的连未发现适配器都懒得提示你。更麻烦的是很多人的使用场景是离线环境。比如公司内网的开发机、实验室里不接外网的工控主机、或者你手头只有一台能上网的机器但目标机器在另一个房间。这时候你没法直接apt install也没法git clone所有依赖都得靠 U 盘或者内网共享一点点搬过去。而 Linux 驱动编译这件事最恶心的恰恰就是依赖链——你缺一个libelf-dev编译就报错你补上libelf-dev它又告诉你pkg-config版本不对。一环扣一环像拆炸弹。这篇内容就是把我自己在 Ubuntu 20.04 上离线搞定 b852 驱动的完整过程拆开讲。包括怎么在联网机器上把依赖包一次性下全、怎么判断内核头文件版本是否匹配、编译报错时怎么顺着日志找到真正缺的东西、以及装完之后怎么验证驱动真的在工作而不是看起来在工作。适合两类人看一类是手里有 b852 网卡但被离线环境卡住的运维或开发另一类是想搞明白 Linux 驱动离线安装这套方法论的人——因为换成 aic8800 或者其他网卡思路是一样的。先说一个反直觉的结论离线装驱动最难的不是编译本身而是你不知道自己缺什么。联网环境下apt会自动帮你算依赖离线环境下这个算的过程得你自己来。所以整篇的核心逻辑就是先在联网机器上模拟出完整的依赖集合再把这个集合搬到离线机器上复现。2. 动手之前先把账算清楚b852 的驱动来源与内核匹配逻辑2.1 为什么不能用 Ubuntu 自带的驱动Ubuntu 20.04 默认内核是 5.4.0-xx-generic。这个内核里的rtw89模块只覆盖了 RTL8852AE 的早期版本对 b852 这种后续修订版支持极差。你就算手动modprobe rtw89pci大概率也是加载失败或者加载了但扫不到任何 AP。Realtek 官方在 GitHub 上维护了一个rtw89的独立仓库里面的代码比内核主线新不少b852 的支持就是在这个仓库里补进去的。所以路线很明确放弃内核自带模块用 Realtek 官方仓库的源码自己编译。这就引出了第一个关键判断——你的内核版本和驱动源码的兼容性。2.2 内核头文件版本必须和运行内核严格一致编译内核模块/lib/modules/$(uname -r)/build这个路径必须指向当前运行内核对应的头文件。很多人离线装的时候从别的机器拷了一堆linux-headers-5.4.0-150过来结果自己机器跑的是5.4.0-135编译直接报找不到 build 目录或者更隐蔽的结构体成员偏移不匹配。查当前内核版本uname -r假设输出是5.4.0-135-generic那你需要的头文件包就是linux-headers-5.4.0-135-generic和linux-headers-5.4.0-135后者是通用部分。这两个包在联网机器上用apt download拿下来注意不要用apt install因为 install 会顺带装一堆你不需要的东西而且离线机器上也没法 install。提示如果你不确定离线机器当前跑的是哪个内核可以在它上面执行uname -r记下来再去联网机器下载对应版本。千万别想当然地认为都是 20.04 所以内核一样20.04 的小版本更新会升级内核135 和 150 之间头文件是不通用的。2.3 驱动源码的获取与版本选择Realtek 的 rtw89 仓库地址是https://github.com/lwfinger/rtw89这是社区维护的镜像更新比较及时。下载的时候建议直接下 zip 包而不是git clone因为离线机器上没有 git你 clone 下来还得把.git目录删掉反而麻烦。选分支的时候注意仓库的main分支对应较新内核如果你坚持用 5.4 内核可能需要往回找 commit。我实测下来2023 年中的某个 commit 对 5.4 兼容性最好。具体做法是在联网机器上git log看一下提交记录找那种 commit message 里提到support kernel 5.4或者时间点在 2023 年 6 月前后的。把源码 zip 和头文件 deb 包一起放进 U 盘离线机器的准备工作就算完成了一半。3. 联网机器上的依赖打包把 apt 的活手动干一遍3.1 用 apt-rdepends 把依赖树挖到底离线装驱动最怕的就是装一个缺一个。解决办法是在联网机器上先把依赖树完整展开。装个apt-rdependssudo apt install apt-rdepends然后对编译驱动需要的基础包做递归依赖查询apt-rdepends build-essential libelf-dev dwarves flex bison | grep -v ^ | sort -u这条命令会列出所有直接和间接依赖的包名。注意grep -v ^ 是过滤掉缩进的依赖关系行只保留包名。输出会有几十个别嫌多全下下来。3.2 批量下载 deb 包的实操脚本手动一个个apt download太蠢了写个循环mkdir -p ~/offline-driver/pkgs cd ~/offline-driver/pkgs for pkg in $(apt-rdepends build-essential libelf-dev dwarves flex bison | grep -v ^ | sort -u); do apt download $pkg 2/dev/null done这里2/dev/null是为了忽略那些虚拟包比如libc-dev这种由其他包提供的的报错。下完之后ls看一下应该有一两百个 deb 文件。但这里有个坑apt download只下当前架构的包。如果你的联网机器是 x86_64离线机器也是 x86_64那没问题。如果架构不同比如联网机器是 ARM 的树莓派得加--arch参数指定。3.3 别忘了 linux-headers 和驱动源码除了编译工具链头文件包单独下apt download linux-headers-$(uname -r) linux-headers-$(uname -r | sed s/-generic//)注意第二条命令是下通用头文件包比如linux-headers-5.4.0-135。这两个包名字很像但内容不同都要下。驱动源码从 GitHub 下载 zipwget https://github.com/lwfinger/rtw89/archive/refs/heads/main.zip -O rtw89-main.zip如果你确定了要用某个特定 commit就在浏览器里找到那个 commit 页面点Download ZIP。3.4 打包与校验把所有东西塞进一个目录打个 tar 包cd ~/offline-driver tar czvf driver-offline.tar.gz pkgs/ rtw89-main.zip打包完算个 md5拷到 U 盘之后再算一次确保传输过程没出错md5sum driver-offline.tar.gz这个习惯看着多余但我确实遇到过 U 盘静默损坏导致 deb 包解压失败的情况查了半天才发现是文件本身坏了。4. 离线机器上的安装顺序错了就前功尽弃4.1 先装依赖再解压源码最后编译到了离线机器上把 tar 包解开tar xzvf driver-offline.tar.gz cd offline-driver装 deb 包的时候不要用dpkg -i *.deb一把梭。因为 deb 之间有依赖顺序dpkg 不解决依赖顺序错了就报错。正确做法是sudo dpkg -i pkgs/*.deb如果报依赖错误再执行sudo dpkg -i pkgs/*.deb对就是再执行一遍。dpkg 第一次装的时候有些包因为依赖没满足会配置失败但包本身已经解压了。第二次执行时之前解压的包就能满足依赖配置就能成功。这个跑两遍的技巧在离线装 deb 时非常实用。如果两遍之后还有报错用sudo apt install -f --no-download--no-download是关键它让 apt 只用本地已有的 deb 包来修复依赖不会尝试联网。4.2 验证头文件路径是否正确装完头文件后检查这个路径ls -l /lib/modules/$(uname -r)/build应该是一个指向/usr/src/linux-headers-$(uname -r)的软链接。如果这个链接不存在或者指向错误手动建一个sudo ln -s /usr/src/linux-headers-$(uname -r) /lib/modules/$(uname -r)/build这一步不做后面make会直接报找不到内核源码目录。4.3 编译 rtw89 驱动解压驱动源码unzip rtw89-main.zip cd rtw89-main先看一眼 Makefile 里的CONFIG_PLATFORM相关配置确认没有硬编码的内核路径。然后make -j$(nproc)-j$(nproc)是用满所有 CPU 核心并行编译能快不少。编译过程大概一两分钟如果报错往下看第 5 章。编译成功后sudo make install这个命令会把编译好的.ko文件拷到/lib/modules/$(uname -r)/kernel/drivers/net/wireless/下面并执行depmod。4.4 加载模块并验证sudo modprobe rtw89pci然后检查lsmod | grep rtw89应该能看到rtw89pci、rtw89_core、rtw89_8852be这几个模块。如果只有rtw89pci没有rtw89_8852be说明固件没加载成功检查/lib/firmware/rtw89/目录下有没有rtw8852b_fw.bin这个文件。驱动源码的firmware/目录里应该带了手动拷过去sudo cp firmware/rtw8852b_fw.bin /lib/firmware/rtw89/最后看网络接口ip link show应该多出一个wlan0或者wlp2s0之类的接口。用nmcli device wifi list扫一下 AP能扫到就说明驱动真的在工作了。5. 编译报错排查顺着日志找到真正缺的东西5.1 No such file or directory: linux/module.h这个报错 90% 是头文件路径不对。先确认/lib/modules/$(uname -r)/build存在且指向正确。如果路径没问题那就是linux-headers包没装全回去检查linux-headers-$(uname -r)和linux-headers-$(uname -r | sed s/-generic//)是不是都装了。5.2 ERROR: modpost: Module.symvers is missing这个报错说明内核头文件包里缺少Module.symvers文件。Ubuntu 的linux-headers包有时候确实不带这个文件需要额外装linux-modules-$(uname -r)或者从/boot目录下找。如果实在找不到可以在 Makefile 里加一行忽略符号版本检查KBUILD_MODPOST_WARN 1但这只是绕过不是解决。更好的办法是从联网机器上把/usr/src/linux-headers-$(uname -r)/Module.symvers拷过来。5.3 unknown type name bool 之类的语法错误这种报错通常是因为编译器版本和内核源码不匹配。Ubuntu 20.04 默认 gcc 是 9.x如果驱动源码里用了 gcc 10 的语法特性就会报这种错。解决办法是装gcc-9和g-9然后在 Makefile 里指定CC gcc-9或者编译时传参make CCgcc-9 -j$(nproc)5.4 编译通过但 modprobe 报 Invalid argument这个最隐蔽。编译没问题模块也能insmod但modprobe就是报错。原因通常是模块签名问题。Ubuntu 20.04 默认开启了 Secure Boot 的话未签名的内核模块会被拒绝加载。检查mokutil --sb-state如果输出SecureBoot enabled你有两个选择进 BIOS 关掉 Secure Boot或者给模块签名。离线环境下签名很麻烦需要生成密钥、导入 MOK所以关 Secure Boot 是最省事的。5.5 驱动加载了但扫不到 AP先看dmesg | grep rtw89的输出。如果看到firmware failed to load就是固件文件没放对位置。如果看到rfkill相关字样检查rfkill list有时候无线被软阻塞了sudo rfkill unblock wifi解一下就行。6. 装完之后别急着走稳定性验证与长期维护6.1 跑个持续 ping 看会不会掉线驱动刚装上能连不代表稳定。我习惯跑一个长 ping 测试ping -i 0.2 -c 3000 192.168.1.1 | tail -5-i 0.2是 0.2 秒发一个包3000 个包大概跑 10 分钟。看最后统计的丢包率如果超过 1%说明驱动或者固件有问题。b852 在早期驱动版本上确实有断流问题换更新的 commit 能缓解。6.2 内核升级后驱动会失效这是离线装驱动最大的长期痛点。Ubuntu 20.04 的安全更新会升级内核比如从 5.4.0-135 升到 5.4.0-150。升级后重启新内核没有对应的头文件也没有编译好的 rtw89 模块Wi-Fi 又没了。应对办法有两个一是锁定内核版本禁止自动升级sudo apt-mark hold linux-image-$(uname -r) linux-headers-$(uname -r)二是每次内核升级后重新走一遍离线编译流程。前者省事但牺牲安全更新后者麻烦但更稳妥。我个人的选择是锁定内核因为这台机器在内网安全更新的优先级没那么高。6.3 把驱动加入 initramfs 避免开机加载失败有时候模块编译好了但开机时加载顺序不对导致 Wi-Fi 起不来。把模块写进/etc/modulesecho rtw89pci | sudo tee -a /etc/modules然后更新 initramfssudo update-initramfs -u这样开机时会尽早加载驱动减少开机后要等半分钟 Wi-Fi 才出来的情况。6.4 备份编译好的模块编译一次不容易把.ko文件备份出来mkdir -p ~/rtw89-backup cp /lib/modules/$(uname -r)/kernel/drivers/net/wireless/rtw89/*.ko ~/rtw89-backup/下次如果只是模块被误删直接拷回去depmod -a就行不用重新编译。7. 几个容易忽略的细节和我的实际体会第一个细节是固件文件的权限。/lib/firmware/rtw89/下的.bin文件权限应该是 644如果从 U 盘拷过来变成 600驱动加载时会报权限错误。sudo chmod 644 /lib/firmware/rtw89/*.bin一下就好。第二个细节是蓝牙。b852 是 Wi-Fi 蓝牙二合一Wi-Fi 驱动装好了蓝牙可能还是不能用。蓝牙走的是 USB 接口需要btusb模块和对应的固件。检查dmesg | grep -i blue如果看到firmware not found需要把rtl8852bu_fw.bin和rtl8852bu_config.bin放到/lib/firmware/rtl_bt/下面。这两个文件在驱动源码的firmware/目录里也有。第三个细节是不要同时装多个版本的 rtw89。如果你之前尝试过从别的来源装驱动先把旧的卸干净sudo modprobe -r rtw89pci rtw89_core rtw89_8852be sudo rm -rf /lib/modules/$(uname -r)/kernel/drivers/net/wireless/rtw89 sudo depmod -a然后再装新的否则模块版本冲突会导致各种诡异问题。我自己在这块网卡上前前后后折腾了大概三个晚上。最开始想走捷径直接从另一台同型号机器的/lib/modules目录把整个 wireless 文件夹拷过来结果因为内核小版本差了两个数模块加载直接 kernel panic。后来老老实实按联网机下依赖、离线机编译的流程走一次就过了。所以我的建议是别偷懒依赖该下全就下全头文件版本该对齐就对齐。离线环境没有 apt 帮你兜底每一步都得自己确认。另外如果你手头有多个离线机器要装可以把编译好的.ko文件和固件一起打包只要目标机器内核版本完全一致直接拷过去depmod -a就能用不用每台都编译。这个办法在批量部署的时候能省不少时间。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进