☰
iMX6ULL实战:rtl8723 WiFi驱动移植与联网调试全流程
2026/10/17 8:38:03 网站建设 项目流程

iMX6ULL终结者开发板跑起来不难,难的是让它用WiFi联网。rtl8723这个芯片,在Linux下从驱动到联网要经历一段不算短的路:确认芯片接口、准备驱动源码、裁剪内核、交叉编译、修改设备树、放置固件,最后还要用wpa_supplicant把网络拉起来。这篇文章就是这条路的完整记录,把所有会卡住你的地方摊开来说,适合正在调同款或类似板子的朋友作为参考。

说实话,这个实验真正繁琐的不是某一个命令,而是整个链条里的依赖关系。驱动文件、固件、内核配置、设备树描述,四样东西必须对得上,缺一个你都会看到"莫名其妙"的失败。下面按我实际推进的顺序逐步拆开,每个步骤都写清楚为什么这么做,以及失败时怎么定位。

1. 动手前先确认rtl8723的接口形态与驱动来源

1.1 rtl8723不是一款芯片,是一族芯片

拿到板子先别急着找驱动源码。rtl8723这个命名很容易让人以为是一个固定的版本,实际上它是一个产品家族,常见的就包括rtl8723au、rtl8723be、rtl8723bs、rtl8723ds。这些型号之间的接口类型差别很大:rtl8723au走USB,rtl8723be走PCIe,rtl8723bs和rtl8723ds走SDIO。驱动源码并不通用,USB版本的主控代码和SDIO版本的主控代码结构完全不同,选错驱动,后面怎么改设备树都救不回来。

怎么判断接口类型?看原理图最直接。WiFi模块到主控之间有SD_CLK、SD_CMD、SD_DATA0到SD_DATA3,那基本就是SDIO接口;看到USB_DP、USB_DM两根差分线,那才是USB接口。没有原理图的时候,从模块丝印也能猜个大概,rtl8723bs和rtl8723ds通常以SDIO方式放在板载模块上。

结合iMX6ULL这类嵌入式平台的实际板卡搭配,SDIO接口的rtl8723ds更常见,下面整个流程就按这个来展开。如果手头是rtl8723bs,思路一致,只是驱动源码和固件文件不同;如果换成了USB接口,那设备树部分大概率不用动,直接看USB host控制器配置就行,但内核无线选项和联网测试的步骤同样适用。

1.2 驱动来源怎么选:原厂SDK优先,主线内核兜底

确定接口后就要找驱动源码。这里我的习惯是优先找原厂SDK或者与当前内核版本匹配的移植包,而不是默认"主线内核自带驱动"。rtl8723bs在部分长期维护的内核树中确实有支持,但rtl8723ds的支持通常需要单独编译,主线内核对它的支持并不完整,单纯在内核menuconfig里勾选相关选项不一定能编出能用的模块。

从代码托管平台找移植包时,需要注意它对应哪一版内核。3.x内核和4.x/5.x内核对无线子系统API的差异很大,一份针对老内核的驱动直接拿到新内核上交叉编译,大概率会出现一堆编译错误,比如某个结构体字段不存在、某个函数签名变了。这类问题不是改两个宏就能解决的,最省时间的办法还是换到与原厂SDK配套的移植包。

另外有个容易忽略的点:某些驱动源码包会附带固件,但固件文件并不会在make时自动安装到开发板文件系统里。需要手动把它们复制到/lib/firmware下的指定路径,这一步放在后面单独讲。

1.3 驱动、固件、内核配置的"铁三角"关系

在进入具体编译之前,必须先把这三者的关系说透。WiFi芯片不是上电就能用的,它内部没有完整的ROM固件,每次上电都需要主控把固件文件加载进芯片内存。驱动模块负责告诉内核怎么跟芯片对话,固件负责让芯片内部跑起来,内核配置则提供驱动运行所要依赖的无线子系统接口。三者版本和路径必须互相匹配,缺任何一个环节,wlan0这个接口都不会出现。

我用一个生活化类比来说明:驱动模块像是给芯片写的"驱动程序说明书",固件像是芯片自己的"开机自检程序",而内核的无线子系统像是操作系统提供的"公共插座"。说明书写得再详细,也要插在正确型号的插座上才能通电;芯片内部的启动程序不对版,也一样开不了机。所以后面每一步排查都不是孤立的,要始终记得这三者互相约束。

2. 内核裁剪与驱动交叉编译:一次unknown symbol的完整排查

2.1 必开的内核配置项与为什么没有它一定失败

编译rtl8723ds驱动之前,先把Linux内核的无线相关配置打开。如果开着无线子系统的选项去编译驱动,很可能出现"insmod报unknown symbol"的现象。所谓unknown symbol,通俗说就是驱动模块在运行时想调用某个系统函数,但当前内核里没有这个函数,于是内核拒绝加载。

以下是这个实验在内核侧必须确认的配置项:

配置项作用建议状态
CONFIG_WIRELESS无线子系统总开关编译进内核
CONFIG_CFG80211802.11配置协议层,rtl8723ds直接依赖编译进内核更省事
CONFIG_MAC80211802.11协议栈编译进内核更省事
CONFIG_RFKILL射频开关控制开启
CONFIG_MMCSDIO宿主控制器支持开启
CONFIG_FW_LOADER固件加载机制开启

CFG80211和MAC80211如果编译成模块,也不是不行,但modprobe时必须先加载它们,再加载rtl8723ds的模块。试过几次之后,我倾向于把无线子系统直接编进内核,减少模块依赖顺序带来的麻烦。嵌入式板子本身没有太多需要动态加载内核模块的场景,能编进去的都编进去,系统启动后wlan0直接可用。

内核Menuconfig里配置时不要凭记忆找路径,直接在配置界面按"/"键搜索关键字,比如输入CFG80211,它会列出所有相关选项所在的位置。改完配置后执行常规的内核编译。

2.2 交叉编译Makefile的三处关键修改

拿到rtl8723ds驱动源码包后,打开顶层Makefile会看到一大段平台判断逻辑,这是原厂驱动为了支持多种开发板适配留下的。编译前主要改三个地方:平台类型、交叉编译工具链路径、内核源码路径。

以常见的源码包为例:

PLATFORM = 6 ifeq ($(PLATFORM), 6) ARCH := arm CROSS_COMPILE := /opt/arm-toolchain/arm-linux-gnueabihf- KSRC := /home/user/linux-kernel-source endif

CROSS_COMPILE必须写到可执行文件前缀对应的完整路径。可以在终端先执行arm-linux-gnueabihf-gcc -v确认工具链已经加入了PATH,否则Makefile里写了工具链路径还是找不到编译器。

KSRC指向的内核源码目录,必须是你实际在用的那份内核,并且已经完成了ARCH=arm make xxx_defconfig和后续的配置。驱动编译过程中会读取内核源码里的头文件,如果内核源码本身没有配置过,编译时会报错说缺少生成的文件。

设置好这三处后执行:

make -j4

正常情况下会生成rtl8723ds.ko模块文件。有些源码包会生成多个.ko,不要慌,看make输出就知道该拷贝哪个。生成完毕后,把模块文件拷贝到开发板根文件系统的/lib/modules/对应内核版本/extra/目录里。

2.3 insmod报错后的定位思路

模块拷贝到开发板上,执行modprobe rtl8723ds,第一次就成功是运气好,失败才是常态。最常见的报错是:

rtl8723ds: Unknown symbol cfg80211_xxx (err -2)

注意看到这种日志时,第一反应不要觉得是模块文件损坏,而是去检查内核配置。err -2就是ENOENT,内核里找不到这个被引用的符号。进入内核源码目录,执行:

grep CONFIG_CFG80211 .config

如果显示未定义,说明当初编译内核时没有打开无线子系统,驱动模块自然找不到对应的接口。解决方法是回到menuconfig打开配置项,重新编译内核和内核模块,然后把新内核烧进板子。

另一种情况是内核配置开了,但被编成了模块,而驱动加载时cfg80211模块还没有被加载。先执行一条modprobe cfg80211看看是否能加载,再modprobe rtl8723ds。如果cfg80211被编进了内核,就不会有这个问题。所以我在做内核配置时优先选y,而不是m。

一个值得养成的习惯:每次改动内核配置后,把.config文件保存一份。这样当驱动报依赖问题时,能快速确认当时到底开了哪些选项,不用靠记忆回想。

3. 设备树里SDIO引脚与频率:probe失败的主要来源

3.1 分清楚iMX6ULL的USDHC与引脚mux

在iMX6ULL平台上,SDIO控制器叫USDHC,通常有两路可以复用,比如USDHC1和USDHC2。WiFi模块具体挂在哪一路,取决于板卡原理图设计,不能照抄其他板子。设备树里要同时配置两个层面:一是IOMUXC(引脚复用控制器)里的pinctrl节点,二是USDHC控制器节点。

引脚复用是最容易出错的地方。iMX6ULL的每个引脚都能复用成多种功能,比如某个引脚既能做GPIO,也能做USDHC的数据线。设备树的作用就是告诉内核:"请把这个引脚切成SDIO功能"。如果引脚复用配置错了或者遗漏了某个数据线,驱动在初始化SDIO总线时会卡在探测阶段,被反复assert/deassert。

很多板子会有一个共用的pinctrl_hog节点,负责把一些不影响控制器功能的引脚(比如WiFi的使能GPIO)提前配置好。这个节点也很重要,后面单独说。

3.2 设备树示例与关键属性逐条解读

下面给出一个典型的SDIO WiFi设备树片段,以WiFi挂在USDHC1为例。实际使用时宏名和控制器编号请严格按照自己板子的原理图修改:

&iomuxc { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_hog_1>; pinctrl_usdhc1: usdhc1grp { fsl,pins = < MX6UL_PAD_SD1_CLK__USDHC1_CLK 0x17059 MX6UL_PAD_SD1_CMD__USDHC1_CMD 0x17059 MX6UL_PAD_SD1_DATA0__USDHC1_DATA0 0x17059 MX6UL_PAD_SD1_DATA1__USDHC1_DATA1 0x17059 MX6UL_PAD_SD1_DATA2__USDHC1_DATA2 0x17059 MX6UL_PAD_SD1_DATA3__USDHC1_DATA3 0x17059 >; }; }; &usdhc1 { pinctrl-names = "default"; pinctrl-0 = <&pinctrl_usdhc1>; bus-width = <4>; max-frequency = <20000000>; non-removable; status = "okay"; };

逐条解读一下关键属性:

bus-width = <4>表示SDIO使用4位数据线。rtl8723ds支持4位模式,四根数据线必须全部配置正确。如果只配置了两根,驱动初始化到一半就会异常。对于板载WiFi,通常不会用到8位模式。

max-frequency = <20000000>将SDIO时钟限制在20MHz。很多参考手册会给你一个更高的频率,但在实际测试中,把频率限制在20MHz以内能明显降低初始化失败概率。这不是性能妥协,WiFi吞吐量的瓶颈通常不在SDIO时钟上,而在射频链路和AP协商速率上。

non-removable表示这是一个板载设备,不是可插拔的SD卡。没有这个属性时,内核会按可插拔设备处理,加载初始化过程中可能会因为"卡检测"逻辑出错,导致设备没有被正常激活。

0x17059这个数值是引脚配置选项,不同板子可能不同。建议先使用原厂BSP里针对该引脚的配置值,不要随意调整,否则会导致信号质量不稳,出现随机性初始化失败。

3.3 上电使能、时钟频率这两个隐蔽因素

设备树里还有一类很容易被忽略的节点:WiFi模块的电源和复位GPIO。很多板载模块需要一个GPIO拉高来解除复位,另一个GPIO控制供电使能。如果这些引脚没有正确配置,驱动可能在probe阶段莫名其妙失败,或者出现"时好时坏"的诡异现象。

排查时可以用GPIO操作验证:如果原理图标注了某引脚是WIFI_REG_ON这类使能信号,手动将其拉高,再反复执行modprobe -r rtl8723ds和modprobe rtl8723ds,观察芯片是否稳定识别。如果拉高后问题消失,说明设备树里使能引脚的初始化有问题。

时钟频率也一样。某些板子在默认最高频率下能偶发成功,重启后又失败,没有规律可循。把max-frequency改小到20MHz甚至15MHz后再试,很多"玄学"问题就消失了。修改dtb后重新烧写,启动后建议用cat /sys/kernel/debug/pinctrl/引脚控制器/pinmux-pins检查引脚是否真的切到了USDHC功能。这一步能帮你把引脚配置问题从驱动问题里快速分离出来。

4. 固件文件命名与加载:wlan0出现的最后一道关卡

4.1 固件请求路径与常见报错

驱动模块加载成功、probe成功,但ifconfig -a还是看不到wlan0的话,下一个怀疑对象就是固件。rtl8723ds模块在初始化时会调用request_firmware来加载固件文件。如果文件不存在,dmesg里会看到类似这样的输出:

rtl8723ds: Direct firmware load for rtlwifi/rtl8723d_fw.bin failed with error -2

错误码-2对应ENOENT,也就是文件不存在。内核默认会在/lib/firmware目录下查找固件,所以上面请求的rtlwifi/rtl8723d_fw.bin实际对应开发板文件系统的/lib/firmware/rtlwifi/rtl8723d_fw.bin。

我见过不少人在这个环节卡住,明明固件文件就在某个目录下,但驱动一直报加载失败。原因多半是位置放错。注意驱动请求路径里的那层rtlwifi/子目录,必须一起创建,不能把固件文件平铺在/lib/firmware根目录下。

4.2 匹配版本:不要随便拿一个固件进来

固件文件要放在正确位置,这还不够,版本也必须匹配。驱动源码包通常自带一个firmware子目录,里面放着配套固件。优先使用源码包里的文件,不要图方便从网上随便下载一个看起来名字相同的固件文件。

我曾经在一次测试里换了一个较新的驱动源码包,固件也跟着换了一份,结果加载时日志里出现了固件CRC校验相关的错误。后来换回与驱动版本完全配套的固件文件,问题立刻消失。固件和驱动之间通常有版本协议关联,跨版本组合不一定报错,常见的是芯片初始化到一半就卡住,wlan0迟迟不出现。

拷贝固件到开发板时还要检查权限。有些嵌入式文件系统挂载选项带了noexec或只读限制,如果rootfs是只读的,固件写不进去也会出问题。正确做法是先把固件打包进镜像,或者挂载读写后修改,改完再恢复只读。

4.3 用dmesg判断驱动是否完整注册wlan0

固件加载成功后,驱动会继续执行后续初始化。此时用dmesg | grep rtl观察日志,正常流程中应该能看到芯片识别信息、固件下载完成信息,以及wlan0接口注册信息。

不同驱动版本的日志格式略有差异,但大致会有以下片段:

rtl8723ds: 8723ds RFE[0] TCM[0] rtl8723ds: Fw download finish! rtl8723ds: wlan0 ...

如果只看到芯片识别信息,但迟迟没有看到wlan0,说明驱动可能卡在SDIO通信或者固件校验阶段。此时回到第3章检查设备树,或者把SDIO频率进一步降低,再重新加载模块。

还有一类情况:wlan0确实注册了,但ifconfig -a看不到。有些系统里wlan0在驱动加载后处于down状态,需要先执行ifconfig wlan0 up,然后iw dev才能看到无线设备。如果这一步缺失,后面扫描、连接都会失败。

5. wpa_supplicant联网实测:扫描、关联、DHCP与通断验证

5.1 先用手动扫描确认射频和驱动

当wlan0真正出现后,先别急着进入联网配置。第一步是确认WiFi模块的射频部分能正常工作,最简单的方式是手动扫描周围AP:

ifconfig wlan0 up iw dev wlan0 scan | grep SSID

如果iw命令不可用,说明内核的nl80211接口没有正确编译,或者busybox里没有集成iw工具。这时可以改用wpa_cli来做扫描:

wpa_supplicant -B -i wlan0 -c /etc/wpa_supplicant.conf wpa_cli -i wlan0 scan wpa_cli -i wlan0 scan_results

扫描结果里如果能看到一堆周围的SSID,说明从驱动到射频链路是通的,问题只可能出在连接配置上。如果扫描结果为空,但驱动日志没有报错,优先怀疑模块天线没接好,或者模块本身没有供电到位,而不是急着改配置文件。

5.2 wpa_supplicant配置文件与关联

扫描没问题后,创建wpa_supplicant配置文件,并填入目标路由器的SSID与密码:

ctrl_interface=/var/run/wpa_supplicant ap_scan=1 network={ ssid="MyAP" psk="MyPassword" key_mgmt=WPA-PSK pairwise=CCMP TKIP group=CCMP TKIP }

ap_scan=1表示让wpa_supplicant自己处理AP扫描和关联流程,绝大多数家用路由器场景下这个值是安全的。有些驱动里ap_scan=2更适合在AP模式下使用,这里联网场景不需要改动。

启动wpa_supplicant:

wpa_supplicant -B -i wlan0 -c /etc/wpa_supplicant.conf

等待几秒后,用wpa_cli -i wlan0 status查看关联状态。重点看wpa_state字段。正常情况下会经历SCANNING、ASSOCIATING、COMPLETED三个阶段。如果一直停在ASSOCIATING,最常见的原因是路由器开了WPA3或者混合加密模式,导致旧版本wpa_supplicant不兼容。此时可以把路由器临时改成WPA2-PSK,或者编译新版本wpa_supplicant放进去。

看到wpa_state=COMPLETED后,四步握手基本已经成功了。但关联成功不代表能上网,下一步要配置IP地址。

5.3 三层验证法:地址、网关、公网

我从实际测试中总结了一个三层验证法,每层验证都对应不同的问题定位。第一层看本机有没有拿到IP地址:

ifconfig wlan0

正常情况下inet地址应该出现在输出里。如果wlan0没有地址,就需要走DHCP流程。嵌入式环境常用udhcpc:

udhcpc -i wlan0

第二层验证是和网关的连通性。先拿到网关地址(通常是路由器的LAN IP),执行:

ping 192.168.1.1

这个步骤能通,说明二层关联、三层寻址、AP端的转发都正常。如果ping网关不通,多半是AP开启了客户端隔离,或者MAC地址过滤,需要到路由器后台检查。

第三层验证才是真正测试WiFi能否访问外部网络。用路由器下发的DNS服务器地址或者公网稳定测试地址,执行:

ping 8.8.8.8

如果这一步能通,说明默认路由、NAT等都没有问题。如果ping公网IP通但ping域名不通,那是DNS解析问题,检查/etc/resolv.conf里的DNS配置。

这里必须强调验证默认路由。开发板常常同时插着网线和WiFi,eth0的默认路由会优先于wlan0。执行ip route,确认默认路由确实指向wlan0的网关,否则你ping外网时流量可能仍然走的是有线网口,WiFi链路并没有真正承担数据传输。

5.4 静态IP和DHCP哪个更适合第一次测试

第一次调试WiFi网络时,我反而建议使用静态IP,而不是一上来就用DHCP。原因很简单:DHCP失败和WiFi关联成功是两个不同层面的问题,如果混在一起排查,会很麻烦。先用静态IP把网络链路确认通,再换成DHCP验证自动获取流程。

静态IP配置示例:

ifconfig wlan0 192.168.1.100 netmask 255.255.255.0 ip route add default via 192.168.1.1

这里注意IP不能和路由器DHCP地址池冲突,否则可能造成局域网内IP冲突。建议临时用地址池范围外的IP,比如路由器地址池是100到200,那静态IP就用192.168.1.50。测试完毕后再切换回DHCP。

切换DHCP时先清理旧配置:

killall wpa_supplicant ifconfig wlan0 0.0.0.0 udhcpc -i wlan0

有人会在配置静态IP之后没有清除旧地址就直接启动udhcpc,出现一直获取失败的情况,其实是旧IP还在占用接口。

6. 几个反复出现的暗坑与最小化排查顺序

6.1 排查顺序表:按系统启动的时间线走

把前面讨论过的问题汇总一下,按系统启动的时间线整理成一张排查顺序表。这张表是我后来再做同类板卡时的标准流程。

现象最可能原因优先检查
ifconfig -a无wlan0驱动模块没加载,或加载失败dmesg看模块加载日志
insmod提示unknown symbol内核无线配置缺失检查CONFIG_CFG80211
probe失败设备树引脚或使能GPIO异常检查pinctrl和GPIO状态
固件加载失败固件路径错误或权限不对检查/lib/firmware/rtlwifi/
扫描不到AP天线、电源、射频异常手动扫描观察结果
一直ASSOCIATING密码或加密方式不匹配改路由器为WPA2-PSK
关联成功但拿不到IPDHCP服务或AP隔离换静态IP判断

这张表的逻辑很清晰:先确认内核配置,再确认硬件描述,再确认固件,最后才是用户态的wpa_supplicant。我见过太多人一开始就在wpa_supplicant配置上反复折腾,最后发现模块根本没加载成功,白白浪费大半天。

6.2 让WiFi功能开机自启的落地做法

实验做完后,如果要把WiFi功能固化,不要只在当前终端里敲命令。iMX6ULL上很多系统是busybox环境,没有systemd,最稳妥的做法是在/etc/init.d里写一个启动脚本,按固定顺序执行依赖加载和网络连接。

脚本的核心逻辑可以简化为:先执行depmod -a重建模块依赖,确保modprobe能解析;再依次加载cfg80211、rtl8723ds;等wlan0出现后,启动wpa_supplicant,最后执行udhcpc。把依赖顺序写进脚本,能有效避免断电重启后wlan0消失的问题。

在实际操作中发现,脚本里启动wpa_supplicant和dhclient之间最好加一个短暂延时,比如sleep 2。因为wpa_supplicant刚启动时还在扫描,关联状态还没准备好,DHCP客户端太早请求会拿不到地址。这个延时虽然糙,但在busybox环境下非常实用。

6.3 一点个人体会

整个实验走完之后,我最大的体会是:WIFI驱动实验真正考验人的不是某个单独环节,而是你能不能理解"驱动、固件、内核配置、设备树"这四个东西之间的互相依赖。单独看每一项都不难,难的是它们叠加在一起时的排查路径。每次改动只动一个变量,然后重新验证,这是效率最高的调试方式。

另外一个心得是,测试阶段不妨故意制造一次"失败",比如故意去掉固件文件、故意改错一个引脚,然后观察dmesg日志的变化。通过对比正常与异常的日志差异,你会对这个驱动的启动流程理解得更深。以后在正式项目中遇到类似问题,光凭日志就能快速锁定方向,不用再靠猜。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询