1. 为什么我要从Linux 5.10升级到6.1大家好我是老张一个在嵌入式圈子里摸爬滚打了十来年的老码农。最近我手头有个项目用的是瑞芯微的RK3568平台要驱动一块高通的QCN9074 WiFi 6E模块。这块模块性能很强PCIe 3.0接口支持三频本来是块好料。但一开始我就踩了个大坑官方SDK默认的Linux内核版本是5.10里面的ath11k驱动版本太老根本认不出这块QCN9074。相信很多用RK3568的朋友都遇到过类似问题。官方BSP为了稳定内核版本往往比较保守。当你需要用到一些新硬件比如最新的WiFi模块、5G模组时老内核的驱动支持就成了拦路虎。我当时面临两个选择要么把Linux 6.1里新的ath11k驱动代码一点点抠出来移植回5.10内核里要么干脆把整个BSP的内核升级到6.1。我几乎没怎么犹豫就选了后者——直接升级内核。为什么因为移植驱动听起来简单实则是个“缝合怪”的活儿。驱动代码不是孤立的它依赖内核里其他子系统比如PCIe、网络栈、电源管理的特定API和数据结构。高版本驱动用了新内核才有的函数你硬搬到老内核里就得连带着改一堆依赖牵一发而动全身。我试过改到后面头都大了还引入了各种稳定性隐患。而升级内核虽然前期工作量看起来大但一旦打通整个系统的基础就更扎实后续维护、利用新内核的特性比如更好的调度器、文件系统性能也更容易。所以这篇文章就是记录我从Linux 5.10迁移到6.1最终让RK3568成功点亮QCN9074的全过程。这不是一个简单的“改个设备树”就能搞定的事它涉及到内核配置的差异、驱动编译选项的变化、固件加载机制的调整甚至编译脚本的修改。我会把每一步的操作、遇到的报错和解决思路都掰开揉碎了讲目标是让你看完就能自己动手复现避开我踩过的那些坑。2. 迁移前的准备理清差异与搭建环境决定升级内核后不能头脑一热就直接开干。首先得搞清楚从5.10到6.1RK3568的BSP有哪些关键变化我们又要准备哪些东西。2.1 内核版本差异带来的核心挑战Linux内核每个大版本更新都会引入大量改动。对于RK3568这个具体平台从5.10到6.1我总结下来主要面临这几个挑战设备树DTS的兼容性这是最直观的。内核版本升级往往伴随着设备树绑定Device Tree Bindings的更新。有些节点的属性名可能变了有些节点的必需属性增加了。直接用老版本的DTS文件编译可能通过但内核启动时解析会出错导致设备无法初始化。驱动配置菜单Kconfig的变迁驱动在内核配置菜单里的位置和依赖关系可能变了。在5.10里ath11k可能藏在某个子菜单下需要先开启好几层依赖而在6.1里可能因为驱动成熟了被提到了更显眼的位置或者依赖项简化了。这直接影响到我们make menuconfig时的操作。内核API与驱动接口的变化这是最隐蔽也最头疼的。内核内部函数原型变了数据结构成员增减了这些都会导致直接编译老驱动模块时出现一堆编译错误。好在我们是使用新内核自带的驱动这个问题由内核开发者解决了但如果我们自己写了内核模块就需要适配。构建系统与编译脚本的调整官方SDK的编译脚本比如build.sh、mkimage.sh通常是针对特定内核版本定制的。升级内核后这些脚本里关于内核版本号、压缩方式、设备树编译命令的参数可能需要相应调整。2.2 获取必要的源代码与工具工欲善其事必先利其器。我们需要准备两套代码新版内核源码我们需要找到针对RK3568适配较好的Linux 6.1内核源码。最理想的来源是芯片原厂或核心板供应商提供的更新版SDK。如果他们没有提供也可以从Linux内核官方主线kernel.org获取然后自己移植RK3568的芯片支持代码这工作量巨大不推荐。我这次比较幸运从合作伙伴那里拿到了一个基于Linux 6.1.118的RK3568 BSP包。旧版SDK的参考保留原有的Linux 5.10 SDK。它不是用来编译的而是作为“参考书”。当我们在新内核里配置设备树、查找驱动选项时可以对照老SDK里的设置理解原厂的默认配置意图避免配置错误。交叉编译工具链确认新BSP包配套的交叉编译工具链比如aarch64-linux-gnu-。通常新SDK会自带版本可能比老的要新。确保你的Ubuntu编译主机上已经安装好并且PATH环境变量设置正确。我的工作目录结构大概是这样的~/rk3568_project/ ├── sdk_linux_5.10/ # 老版本SDK仅作参考 ├── sdk_linux_6.1.118/ # 新版本SDK主要工作区 │ ├── kernel/ # Linux 6.1.118 内核源码 │ ├── u-boot/ │ ├── buildroot/ # 根文件系统构建 │ └── build.sh # 编译脚本 └── firmware/ # 存放QCN9074的固件文件准备好这些我们就可以开始动手修改了。记住先备份再操作这是血泪教训。3. 第一步让内核认识硬件——设备树DTS的适配内核启动后首先要能识别出硬件。对于PCIe设备这分两步一是PCIe控制器本身要工作二是挂在PCIe总线上的设备我们的QCN9074要被枚举到。设备树DTS负责描述第一步。3.1 PCIe控制器的供电与复位配置在RK3568上QCN9074通常连接在PCIe 3.0 x1或x2的控制器上。查看原理图找到模块的供电引脚可能是3.3V和复位引脚PERST#。这些需要通过GPIO来控制。在老版本5.10的DTS里配置可能长这样pcie3x2 { reset-gpios gpio2 RK_PD6 GPIO_ACTIVE_HIGH; vpcie3v3-supply vcc3v3_pcie; status okay; };这里有个关键点reset-gpios的属性是GPIO_ACTIVE_HIGH意味着复位引脚是高电平有效。但很多硬件设计包括我手头的模块是低电平复位即PERST#引脚拉低复位拉高正常工作。如果不匹配模块就一直处于复位状态自然无法被识别。所以我的修改如下/* 首先定义一个GPIO控制的供电稳压器节点 */ vcc_wifi_power: vcc-wifi-power { compatible regulator-fixed; regulator-name vcc_wifi_power; regulator-min-microvolt 3300000; regulator-max-microvolt 3300000; enable-active-high; /* 我的板子是GPIO高电平使能供电 */ gpio gpio0 RK_PC1 GPIO_ACTIVE_HIGH; startup-delay-us 50000; /* 上电后延时50ms让电源稳定 */ regulator-always-on; /* 保持常开避免休眠时掉电 */ pinctrl-names default; pinctrl-0 wifi_power_en; status okay; }; /* 然后修改PCIe控制器节点 */ pcie3x2 { /* 重点修正根据实际硬件改为低电平有效复位 */ reset-gpios gpio2 RK_PD6 GPIO_ACTIVE_LOW; /* 关联上面定义的供电 */ vpcie3v3-supply vcc_wifi_power; /* 确保PHY物理层引用正确 */ phys pcie30phy; phy-names pcie-phy; status okay; };踩坑记录我第一次只改了复位引脚没仔细看供电。结果内核启动时PCIe PHY初始化失败dmesg里刷出一堆phy init failed和lock failed的错误。排查了半天才发现是供电使能GPIO的极性配置反了我以为是高有效实际是低有效导致模块根本没上电。所以供电和复位这两个GPIO的极性active-high/active-low一定要对着原理图和模块手册反复确认。3.2 验证PCIe枚举成功设备树修改并编译进内核后烧录启动。第一个验证点就是PCIe总线是否枚举到了设备。 在开发板的串口终端里执行lspci如果一切正常你应该能看到类似下面的输出0002:20:00.0 PCI bridge: Fuzhou Rockchip Electronics Co., Ltd Device 3566 (rev 01) 0002:21:00.0 Network controller: Qualcomm Device 1104 (rev 01)这里0002:21:00.0就是我们的QCN9074 WiFi模块设备ID是1104。看到这个说明硬件连接、供电、复位、PCIe控制器配置都基本正确内核已经识别到了这个PCIe设备。如果lspci看不到任何PCIe设备或者没有这个Network controller那就得回头检查设备树、硬件焊接和电源了。4. 第二步让内核驱动硬件——WiFi驱动ath11k的配置与编译硬件识别了接下来就需要驱动来“驾驭”它。QCN9074使用高通的ath11k驱动。不同内核版本这个驱动的配置和集成方式差别很大。4.1 内核配置菜单menuconfig的寻找与开启进入新内核的源码目录开始配置cd ~/rk3568_project/sdk_linux_6.1.118/kernel make ARCHarm64 CROSS_COMPILEaarch64-linux-gnu- menuconfig在Linux 6.1.118里ath11k驱动的配置路径相对清晰。你可以直接按/键搜索“ath11k”它会告诉你在Device Drivers --- Network device support --- Wireless LAN --- Qualcomm devices --- * Qualcomm Atheros 802.11ax wireless cards support (ath11k)进入这个选项后里面还有子选项比如支持PCIe总线、支持调试等一般全选上就行。这里有个和5.10的巨大利好区别在Linux 5.10里ath11k驱动可能还处于STAGING试验阶段目录下或者依赖一堆诸如CFG80211、MAC80211的特定配置需要你像玩解谜游戏一样层层开启依赖才能让这个选项出现。而在6.1里它已经是一个成熟的一级驱动选项了配置起来省心很多。这本身就是升级内核的一大收益。4.2 编译驱动进内核镜像配置好后保存退出。我选择将ath11k驱动直接编译进内核*而不是编译成模块M。为什么这涉及到下一个大坑——固件加载。如果编译成模块那么ath11k.ko文件是在根文件系统挂载之后由用户手动insmod加载的。但驱动加载时会立刻去查找固件文件。如果固件放在根文件系统里而根文件系统还没挂载就会找不到固件导致驱动初始化失败。所以我的做法是将ath11k驱动编译进内核*。将WiFi固件也编译进内核镜像后面会讲。 这样在内核启动的早期阶段驱动和固件就都准备好了。执行编译make ARCHarm64 CROSS_COMPILEaarch64-linux-gnu- -j$(nproc)编译成功后你会得到arch/arm64/boot/Image内核镜像文件以及一系列设备树二进制文件.dtb。5. 第三步解决“最后一公里”问题——WiFi固件的加载这是整个适配过程中最折磨人的一环。驱动加载了但如果没有正确的固件Firmware硬件就像没有灵魂的躯壳无法工作。5.1 固件是什么从哪里来WiFi芯片如QCN9074内部有一个小处理器通常称为固件处理器或微码引擎它需要运行一段专门的二进制代码才能处理复杂的无线信号。这段代码就是固件。ath11k驱动只是一个“指挥官”它告诉固件该做什么具体脏活累活是固件干的。固件文件通常由芯片厂商高通提供但不会直接放在开源驱动里。你需要联系模块供应商比如我用的WLE3000HX模块的卖家获取或者从一些开源固件仓库如linux-firmware项目中寻找对应型号的版本。对于QCN9074通常需要三个文件amss.bin主固件最重要。m3.bin协处理器固件。board-2.bin包含设备校准数据和板级特定参数。5.2 固件加载失败的坑与解决方案把这三个.bin文件拷贝到开发板根文件系统的/lib/firmware/ath11k/QCN9074/hw1.0/目录下是最直观的做法。但重启后查看dmesg你大概率会看到这样的错误[ 5.423325] mhi mhi0: Direct firmware load for ath11k/QCN9074/hw1.0/amss.bin failed with error -2 [ 5.423459] ath11k_pci 0002:21:00.0: failed to start mhi: -110错误码-2是-ENOENT意思是“文件不存在”。内核说找不到固件可你明明放进去了啊问题根源内核启动流程中驱动加载ath11k_pci发生在根文件系统挂载之前。当驱动去/lib/firmware路径下找固件时这个目录所在的根文件系统分区还没被挂载自然就找不到了。解决方案有两种我选择了第二种将驱动编译为模块ko并延迟加载先让内核启动挂载根文件系统再把固件文件放到/lib/firmware最后用modprobe或insmod手动加载ath11k驱动。这种方法比较灵活但启动流程复杂不适合产品化。将固件直接编译进内核镜像这是最彻底的一劳永逸的方法。内核在编译时就把固件二进制数据打包进自己的镜像里启动时从内存中直接加载完全绕开了文件系统。5.3 如何将固件编译进内核具体操作如下放置固件文件将amss.bin、m3.bin、board-2.bin这三个文件放在编译主机的一个固定路径下例如/home/zhang/firmware/。修改内核顶层.config文件在内核源码目录下编辑.config文件在文件末尾添加或修改这两行CONFIG_EXTRA_FIRMWAREath11k/QCN9074/hw1.0/amss.bin ath11k/QCN9074/hw1.0/m3.bin ath11k/QCN9074/hw1.0/board-2.bin CONFIG_EXTRA_FIRMWARE_DIR/home/zhang/firmware注意CONFIG_EXTRA_FIRMWARE里的路径是固件在内核中的虚拟路径必须和驱动代码里查找的路径ath11k/QCN9074/hw1.0/完全一致。CONFIG_EXTRA_FIRMWARE_DIR是编译主机上固件文件的实际存放目录。重新编译内核执行make命令重新编译。编译过程中你会看到输出信息里包含了处理这些固件文件的步骤例如FIRMWARE ath11k/QCN9074/hw1.0/amss.bin FIRMWARE ath11k/QCN9074/hw1.0/m3.bin FIRMWARE ath11k/QCN9074/hw1.0/board-2.bin这表示固件已经被编译进内核了。将新的内核镜像烧录到板子上启动再次查看dmesg那些烦人的“firmware load failed”错误应该消失了取而代之的是驱动成功初始化的信息可能还会看到扫描到的无线网络。这时候用ifconfig -a命令你应该能看到一个新的网络接口通常是wlan0或wlx开头的名字。6. 效果验证与性能测试走到这一步理论上QCN9074已经驱动起来了。但我们需要用实际测试来验证它的工作是否正常、性能是否达标。6.1 基础功能测试首先使用ip或ifconfig命令查看接口状态ip link show wlan0应该显示state UP或state DORMANT。然后尝试启动接口并扫描周围的WiFi网络ip link set wlan0 up iw dev wlan0 scan | grep SSID如果能看到一堆WiFi名称SSID恭喜你模块的接收功能基本正常。接下来配置连接。这里以连接一个开放网络为例实际请使用wpa_supplicant连接加密网络# 假设你的路由器SSID是 MyWiFi没有密码 iw dev wlan0 connect MyWiFi # 然后通过DHCP获取IP地址 udhcpc -i wlan0获取到IP后可以ping一下网关或者外网测试连通性。6.2 性能与稳定性压力测试基础连通性没问题后还需要进行一些压力测试确保在复杂环境下稳定。iperf3吞吐量测试这是必测项。在开发板客户端和同一局域网内的一台高性能电脑服务器上分别运行iperf3。在电脑上启动服务器iperf3 -s在开发板上作为客户端测试上行速度iperf3 -c 电脑IP -t 60 -i 10测试下行速度iperf3 -c 电脑IP -t 60 -i 10 -R观察带宽是否能达到PCIe 3.0 x1的理论上限约800Mbps并注意测试过程中是否有吞吐量剧烈波动或断开连接的情况。QCN9074支持160MHz频宽在5GHz频段下跑满千兆网口是没问题的。长时间ping测试连续ping网关几个小时观察丢包率和延迟是否稳定。ping 网关IP -c 1000或者用mtr工具进行更详细的路径跟踪。热插拔与休眠唤醒测试如果你的应用场景涉及这些需要测试在系统休眠suspend后唤醒WiFi是否能自动重连或者模拟PCIe链路断开再恢复这比较危险慎做。6.3 可能遇到的残留问题及排查即使驱动起来了也可能遇到一些小问题接口命名随机每次启动WiFi接口可能从wlan0变成wlx001122334455。这是systemd或udev的规则导致的。如果想固定为wlan0可以创建udev规则。传输速率不稳定检查路由器是否开启了WiFi 6802.11ax模式并尝试固定信道和频宽如5GHz信道36频宽80MHz避免自动选择带来的波动。偶发性断开连接查看内核日志dmesg看是否有ath11k相关的错误或警告信息。可能是电源管理PM过于激进可以尝试在驱动配置中关闭省电模式但会增加功耗。整个从Linux 5.10到6.1的迁移以及QCN9074的适配花了我差不多一周的时间大部分都耗在理解内核配置差异和解决固件加载这个“时序问题”上。回过头看升级内核这个决定是对的。新内核的驱动更完善社区支持更好而且为后续使用其他新特性打下了基础。这个过程虽然繁琐但每一步都有迹可循核心就是理解硬件、内核驱动、固件三者之间的启动时序和依赖关系。希望我的这些实战记录能帮你少走些弯路。