前阵子接手一块新的Linux开发板,板载无线模块是RTL8723DS。这颗芯片在国产平板、机顶盒、智能语音设备里出现频率极高,属于WiFi加蓝牙二合一的低成本方案,WiFi走SDIO接口,蓝牙走UART接口,是非常典型的组合。移植过程中踩了不少坑,从内核配置、设备树到驱动编译、固件部署,再到最后的WiFi吞吐量测试,整个过程都值得整理一下,给后面做同款驱动的朋友省点时间。
这篇先写WiFi部分,蓝牙那部分打算单独成篇。文章不泛泛讲原理,全部按我实际操作的顺序来,关键配置和报错处理都会交代清楚。做嵌入式Linux驱动遇到RTL8723DS的,或者手头有板子正准备接WiFi功能的,可以参考这套流程直接落到代码上。
1. 方案选型:芯片背景与驱动来源
1.1 RTL8723DS这颗芯片到底什么来头
RTL8723DS是瑞昱(Realtek)的一颗WiFi/BT Combo芯片,WiFi部分支持802.11 b/g/n,工作在2.4GHz单频段,蓝牙部分支持Bluetooth 4.2。论性能它不算激进,但胜在成本低、方案成熟、驱动代码到处都能找到参考,所以很多国产方案平台都会把它作为默认无线配置。
和它同系列的还有RTL8723BS,两者的区别主要在于蓝牙版本和部分电源管理策略,引脚和软件框架高度相似。实际项目里如果你看到板子上丝印是RTL8723DS,基本可以确定WiFi走SDIO、蓝牙走UART或者PCM,驱动适配的思路是通用的。
| 对比项 | RTL8723DS | RTL8723BS | AP6212 |
|---|---|---|---|
| WiFi规格 | 802.11 b/g/n 2.4G | 802.11 b/g/n 2.4G | 802.11 b/g/n 2.4G |
| 蓝牙规格 | BT 4.2 | BT 4.0 | BT 4.2 |
| WiFi接口 | SDIO | SDIO | SDIO |
| BT接口 | UART/PCM | UART/PCM | UART/PCM |
| 典型应用 | 平板、智能语音、机顶盒 | 平板、IoT | 平板、音箱 |
我这次是在一块基于ARM Cortex-A7的Linux开发板上做的移植,内核版本用的5.10,工具链就是平台自带的交叉编译器。这种组合在项目里很常见,基本可以代表RTL8723DS主流的使用场景。
1.2 驱动来源:官方SDK还是内核自带
拿到芯片第一件事,确定驱动从哪里来。RTL8723DS的驱动来源主要分两条线。
一条是Realtek官方发布的SDK驱动,源码包一般叫rtl8723DS_WiFi_linux_v5.x.x_xxxxx,版本号比较长。这个驱动功能完整,支持STA、AP、P2P,而且对WiFi和蓝牙共存做了很多针对性的处理,编译出来是一个独立的88x2ds.ko或者8723ds.ko模块,需要通过内核的nl80211/cfg80211接口工作。
另一条是内核自带的rtl8xxxu驱动,它同时支持包括RTL8723DS在内的一批瑞昱芯片。内核主线驱动的好处是随内核维护、接口干净,但功能覆盖有限——实测下来对AP模式支持不完整,蓝牙共存相关的处理也比较弱,如果产品只需要简单的STA连接,可以用它;要做完整功能,还是得回到官方SDK。
| 评估维度 | 官方SDK驱动 | 内核rtl8xxxu |
|---|---|---|
| STA模式 | 支持完善 | 基础可用 |
| AP模式 | 支持,需配套hostapd | 支持不完整 |
| P2P | 支持 | 基本不可用 |
| WiFi/BT共存 | 有专门共存机制 | 较弱 |
| 编译接入 | 独立模块,需手动适配 | 内核Kconfig/menuconfig配置 |
| 维护状态 | 版本固定 | 跟随内核演进 |
我的选择很直接,官方SDK驱动。原因很简单,产品要同时用WiFi和蓝牙,共存问题绕不过去。官方SDK虽然代码风格Old School,里面宏定义多到吓人,但至少在共存场景是经过验证的。
1.3 移植前先搞清楚驱动、固件、内核三者的分工
在动手之前,必须把整个软件栈协同关系理清楚,否则后面遇到问题会无从下手。RTL8723DS正常工作依赖三层:内核的无线上层框架、驱动本身、芯片固件。
内核侧提供的是cfg80211和nl80211接口,用户空间的wpa_supplicant、hostapd通过netlink与内核通信,再传递到驱动层。rfkill是另一个关键子系统,用来控制WiFi和蓝牙的软开关状态,驱动没有它也能编译,但会出现设备无法使能的现象。
驱动本身负责SDIO总线上的读写、固件下载、中断处理,以及802.11协议的大部分处理。RTL8723DS这种芯片的协议栈实现相当一部分在驱动代码里,所以驱动包才那么大,编出来一个ko文件好几MB。
固件则是跑在芯片内部的程序,负责底层射频控制和MAC层辅助处理,通常以rtl8723ds_xxx.bin的形式存放在文件系统里。驱动加载时通过request_firmware机制把它读出来,再下载到芯片里。这三层只要有一层没打通,结果就是wlan0不出现、扫描不到AP、连上就掉线之类的奇怪问题。
2. 移植前的准备工作
2.1 先确认硬件连接,别在软件里瞎调
很多朋友拿到板子就开搞软件,结果折腾半天发现是硬件连得不对,这是最浪费时间的。RTL8723DS的WiFi部分是SDIO接口,至少要保证这四组信号正确连到主控:SDIO_CLK时钟、SDIO_CMD命令线、SDIO_D0方向数据线(实际会用到D0-D3四根),以及芯片的复位/使能脚。
SDIO_CLK的时钟频率不用一开始就给到最高。RTL8723DS支持的最大SDIO时钟可以到50MHz甚至更高,但调试阶段建议先从低速开始,比如把SDIO控制器配成25MHz,确认设备枚举正常后再逐步提频。高频下不稳定往往是走线、上拉电阻或者电平匹配的问题。
使能脚尤其重要。RTL8723DS的WiFi模块通常由一个WIFI_EN或者WL_REG_ON引脚控制开关,这个引脚必须由主控GPIO拉高,芯片才会退出复位状态,SDIO枚举才能成功。我在一块板子上遇到过设备没有被枚举到,查了一圈发现是这个GPIO配置成了输入模式,芯片一直处于复位状态。
2.2 编译环境与内核版本匹配
驱动是内核模块,必须和当前内核匹配编译。第一步确认目标板内核源码路径和版本号,进入内核目录执行make kernelversion,然后保证交叉编译工具链与内核编译用的工具链一致,这个真的是老生常谈,但我见过太多因为工具链不一致导致模块加载报invalid module format的例子。
编译命令一般长这样:
export ARCH=arm export CROSS_COMPILE=arm-linux-gnueabihf- export KSRC=/path/to/your/kernel官方驱动SDK通常用顶层Makefile控制编译参数,但默认配置往往只适配瑞昱自己的测试平台,拿到手需要重新指定CONFIG_PLATFORM_IOT_PC或者其他平台宏。这里建议直接看Makefile顶部的注释说明,每个版本稍微有点区别。
内核版本方面,官方SDK给出的支持范围往往落后于最新内核。我的经验是内核源码树尽量选LTS版本,比如5.4、5.10、6.1这种,遇到编译报错概率低一点。如果你非要在这个驱动上强行适配最新内核,等待你的可能就是一堆结构体字段变化带来的编译错误。
2.3 内核配置项逐一确认
RTL8723DS驱动依赖内核的几个子系统,对应的Kconfig配置必须打开,否则驱动编译时头文件都对不上,或者模块加载后没有可用的无线接口。下面这组配置是基础,直接在menuconfig里搜对应的选项打开。
CONFIG_CFG80211=y CONFIG_RFKILL=y CONFIG_RFKILL_GPIO=y CONFIG_MMC=y CONFIG_MMC_SDHCI=y CONFIG_MMC_SDHCI_PLTFM=y CONFIG_FIRMWARE_LOADER=y CONFIG_WIRELESS=y CONFIG_NETDEVICES=yCONFIG_CFG80211是所有现代无线驱动的地基,wpa_supplicant和hostapd都依赖它。CONFIG_RFKILL和CONFIG_RFKILL_GPIO用于管理射频开关,打开后驱动才能控制WIFI_EN这类GPIO。CONFIG_MMC系列是因为SDIO本身是MMC子系统的扩展,块设备用不到但WiFi要用。CONFIG_FIRMWARE_LOADER用于加载固件文件,一般内核默认就是y,但有些裁剪严重的内核可能把这个去掉,固件加载就会失败。
还有一点容易被忽略:如果你用的内核是64位ARM,工具链也是64位,那么模块编译后必须和内核的CONFIG_ARM64保持一致。RTL8723DS本身不挑架构,但驱动代码里有些地方会依赖架构相关的类型定义,编译前确认内核配置和目标架构一致即可。
3. 驱动移植完整实操
3.1 设备树修改是第一步,也最容易出错
设备树描述的是板级硬件信息,Linux内核在启动过程中通过它来知道SDIO控制器接了什么设备。RTL8723DS在设备树里体现为SDIO控制器下的一个SDIO function设备,但更常见的做法是使用mmc节点下的sdio_pwrseq机制来控制WiFi的电源复位时序。
我这份设备树的核心部分如下,注意WiFi使能引脚和复位引脚的时间顺序,这个是由pwrseq驱动的:
/ { sdio_pwrseq: sdio-pwrseq { compatible = "mmc-pwrseq-simple"; reset-gpios = <&gpio4 3 GPIO_ACTIVE_LOW>; post-power-on-delay-ms = <200>; }; }; &mmc1 { status = "okay"; bus-width = <4>; non-removable; cap-power-off-card; mmc-pwrseq = <&sdio_pwrseq>; vmmc-supply = <&vcc3v3_wifi>; vqmmc-supply = <&vcc_sdio>; #address-cells = <1>; #size-cells = <0>; wifi@1 { compatible = "realtek,rtl8723ds"; reg = <1>; interrupts = <27 IRQ_TYPE_EDGE_RISING>; interrupt-parent = <&gpio4>; }; };几个关键点展开说一下。bus-width = <4>是因为RTL8723DS用四线SDIO,如果配成1bit也能工作,但吞吐量会很难看。我这里实测1bit模式iperf3只有四线模式的四分之一左右。non-removable告诉内核这个设备不是可插拔的SD卡,避免拔插检测逻辑干扰。mmc-pwrseq指向上面定义的电源复位序列,这是很多人会漏掉的部分。GPIO号不是随便写的,要根据SoC的GPIO bank基址和引脚号换算,比如<&gpio4 3>表示GPIO4组的第3个引脚,具体数值查平台手册。
RTL8723DS也可以不用pwrseq,而是由驱动内部的GPIO_WL_REG_ON宏来直接控制WiFi使能。但这样需要驱动代码配合,而且电源时序控制不够精确。从稳定性角度我更推荐在设备树层把电源和复位搞定,让驱动专注业务部分。
3.2 官方驱动源码组织与Makefile调整
把官方SDK解压之后,你会看到一堆目录,核心的包括core/(协议核心)、hal/(硬件抽象层)、os_dep/(操作系统相关),以及platform/(平台适配)。这套代码经过多年迭代,庞杂得很,但通常不需要全部读懂,重点是让编译通过并跑起来。
编译前打开顶层Makefile,有一段平台配置区域,类似:
CONFIG_PLATFORM_IOT_PC = y CONFIG_PLATFORM_ARM_RPI = n CONFIG_PLATFORM_ROCKCHIP = n这里要小心,如果已经有你的平台宏就直接打开,没有的话选一个结构类似的ARM平台宏。不同平台宏主要影响platform_ops里的操作函数,比如SDIO读写接口、GPIO控制方式。我这次没有现成平台宏,直接借用了CONFIG_PLATFORM_IOT_PC,再把ARCH等参数在命令行指定,也能正常工作。
编译命令最终是:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KSRC=/path/to/kernel modules如果编译过程报一些结构体字段找不到的错误,重点看是不是内核版本太新导致struct cfg80211_ops之类的接口变了。此时要么换内核版本,要么给驱动代码打补丁。正常来讲5.10这种版本编译还是比较顺利的。
3.3 编译产物与固件部署
编好的模块在sdio/目录下,名字一般是8723ds.ko。拷贝到板子的文件系统里,建议放到/lib/modules/$(uname -r)/extra/下,后面用depmod统一管理。
固件文件是另一个容易被忽略的坑。旧版官方驱动有些把固件数组直接编码在源码里,编进驱动了;但新版驱动大多改成从文件系统加载,驱动初始化时会查找rtl8723ds_ant0.bin或者类似的固件文件名。把固件放到/lib/firmware/rtlwifi/rtl8723ds/目录下,文件名和驱动代码里request_firmware()的参数完全一致,一个字符都不能差,大小写也敏感。
加载模块用modprobe或者insmod都可以。modprobe的好处是自动解决依赖,前提是depmod生成过模块依赖信息。加载之后立刻看dmesg输出,关键日志类似下面这样:
usbcore: registered new interface driver rtl8723ds mmc1: new high speed SDIO card at address 0001 rtl8723ds: chip type detected as 8723DS rtl8723ds: Firmware Version = 24.11.0 rtl8723ds: wlan0 ready如果能出现wlan0 ready,说明SDIO枚举、固件下载、寄存器初始化一条龙已经通了,接下来进入功能测试阶段。
3.4 开机自动加载模块
开发调试阶段手动insmod没问题,但产品化一定要配置开机自动加载。最简单的办法是在/etc/modules里加一行8723ds,然后做一次depmod -a。如果你的系统是systemd管理,也可以丢一个rtl8723ds.service进去。
这里有个顺序问题。WiFi驱动模块必须在wpa_supplicant启动之前加载完,否则后面起来的进程发现没有无线接口,会直接退出或者报错。我一般会在init脚本里显式先modprobe 8723ds再启动网络服务。另外,如果你把RFKILL配置在设备树里,内核的rfkill核心会在驱动加载时自动注册对应设备,不需要额外处理。
开机自启之后,最好做一次冷启动验证,而不是热重启验证。因为热重启时某些GPIO状态可能残留,掩盖掉真正的电源时序问题。冷启动能过,才说明设计是可靠的。
4. WiFi功能测试与调优
4.1 基础识别测试:确认wlan0已经出现
驱动加载成功后,先用基本命令确认设备可见:
ifconfig -a ls /sys/class/net/ iw dev正常情况会出现wlan0。如果ifconfig -a能看到wlan0但没有IP地址,这个状态是正常的,尚未连接AP自然没有IP。接下来用iw检查无线能力:
iw phy phy0 info这条命令会列出驱动支持的频段、带宽、加密方式等。RTL8723DS是2.4G单频芯片,输出里只会看到2.4GHz的channel列表,这符合预期。此时还可以顺手扫一下周边信号:
iw dev wlan0 scan | head -50能扫描出周围的AP,说明射频链路和驱动主处理路径都没问题。如果扫描结果为空或者卡住不动,先别急着怀疑驱动,回顾一下天线是否接好,RF调试里天线接触不良导致扫描不到信号的情况非常多。
4.2 Station模式连接测试:wpa_supplicant配置
WiFi最基础的场景是作为Station连接路由器。RTL8723DS官方SDK不自带wpa_supplicant,直接用系统里的即可。写一个最简单的配置文件:
ctrl_interface=/var/run/wpa_supplicant country=CN network={ ssid="MyTestAP" psk="testpassword" key_mgmt=WPA-PSK }后台启动:
wpa_supplicant -B -i wlan0 -c /etc/wpa_supplicant.conf udhcpc -i wlan0连接成功的标志是wpa_supplicant日志出现4-way handshake completed,此时udhcpc可以拿到IP地址。这里有一个比较隐蔽的问题:RTL8723DS驱动的电源管理默认可能会开power save模式,表现为连接后偶尔ping不通网关,过一会儿又恢复。遇到这种情况,先临时关掉电源管理确认:
iw dev wlan0 set power_save off如果确认关掉后就稳定了,说明是省电策略和驱动间的兼容问题。建议在正式产品里按实际业务场景评估是否需要保持省电,毕竟关掉PS模式功耗会有明显上升。
4.3 AP模式测试:hostapd与DHCP服务
RTL8723DS做AP模式是这颗芯片很常见的使用场景,比如便携路由、WiFi音箱热点。AP模式下需要两个用户态工具配合,hostapd负责802.11协议接入,dnsmasq负责分配IP地址。
测AP模式前有一个大坑必须提醒:Realtek官方驱动对hostapd版本非常敏感。我实测过,较新版本的hostapd(比如v2.10)和RTL8723DS的驱动对接容易出现AP startup failed或者客户端连接后反复断开的怪问题,换回v2.4/v2.6则一切正常。如果你的驱动包源码里带了一份现成hostapd,直接用那份最省事;如果是系统自带的hostapd,出现异常先怀疑版本。
hostapd配置示例:
interface=wlan0 driver=nl80211 ssid=MyAP_Test hw_mode=g channel=6 wpa=2 wpa_passphrase=12345678 wpa_key_mgmt=WPA-PSK country_code=CN启动后:
hostapd -B /etc/hostapd/hostapd.conf dnsmasq --interface=wlan0 --dhcp-range=192.168.4.50,192.168.4.150,255.255.255.0用手机连接这个热点,能正常获取到192.168.4.x的IP,并且可以ping通板卡,AP模式就算活了。如果手机连上热点但获取不到IP,先看dnsmasq日志,八成是dnsmasq服务没绑定到wlan0,或者防火墙把DHCP请求拦了。
4.4 吞吐量测试与产测模式备注
连通性只是第一步,产品能不能用还得看吞吐量。我惯用的工具是iperf3,板子当服务端,PC当客户端。注意板子端和PC端都要装iperf3,版本最好一致,否则会有协议兼容问题。
板子端:
iperf3 -sPC端:
iperf3 -c 192.168.1.100 -t 60 -i 2左右两个方向(下载/上传)都要测。RTL8723DS实际吞吐量和环境关系很大,2.4G频段干扰多,能跑满40~50Mbps就算是好成绩了,理论值54Mbps只是参考。如果差距太大,检查AP的频道带宽设置,2.4G建议固定用HT20或HT40,不要用自动,自动模式选信道会很慢。
测试RF性能还有一个方向是工厂产测模式。RTL8723DS支持FTM(Factory Test Mode),可以通过专用命令让芯片进入continuous TX/RX模式,配合仪器测量射频指标。这个场景和普通驱动调试不太一样,一般需要产测工具配合,属于量产环节,这里不展开,但调试阶段知道有这个模式存在也有帮助。
5. 常见问题与排查实录
5.1 SDIO枚举不到设备,dmesg刷错误
最典型的报错是在dmesg里看到:
mmc1: error -110 whilst initialising SDIO card或者干脆什么日志都没有,/sys/bus/sdio/devices下面空空的。-110是ETIMEDOUT,意思是主控发了CMD5、CMD52都得不到芯片响应。排查顺序非常固定:先查WIFI_EN/复位引脚是不是拉高了,用万用表量一下电平;再查SDIO总线的供电电压是否稳定;最后检查SDIO走线有没有接反或者被其他外设占用。
我还遇到过一个比较隐蔽的问题:SDIO控制器在设备树里设了broken-cd(卡检测脚失效),但这颗芯片接的是非热插拔型SDIO,如果没有加non-removable,内核会周期性做卡检测,导致初始化失败。这类问题看dmesg能看到反复的card detect相关日志,加入non-removable后解决。
5.2 固件加载失败
dmesg出现类似:
rtl8723ds: Firmware request failed Direct firmware load for rtl8723ds_ant0.bin failed with error -2基本就是固件文件问题。第一步检查文件是否存在、路径对不对。第二步检查文件名是否和驱动代码完全一致,包括大小写。第三步确认内核CONFIG_FIRMWARE_LOADER打开,而且/sys/class/firmware/目录存在。
还有一个比较冷门但真实存在的坑:固件文件放在FAT32分区时,文件名可能因为8.3短名规则被截断。如果文件系统是VFAT,又开了短名兼容,rtl8723ds_ant0.bin可能被识别成RTL872~1.BIN之类的名字,导致加载失败。把固件放在ext4的/lib/firmware下最省心。
5.3 设备被RFKILL禁用
模块加载正常,wlan0也出现了,但执行ip link set wlan0 up时提示Operation not possible due to RF-kill。这是最折磨人的问题之一。先用rfkill list查看当前状态:
0: phy0: Wireless LAN Soft blocked: yes Hard blocked: no如果Soft blocked: yes,执行rfkill unblock wifi即可临时解锁。如果每次开机都这样,说明内核或系统服务里有一个默认的RFKILL策略。有些系统会默认把无线软开关设为blocked状态,需要在启动脚本里加一条rfkill unblock all。
还有个容易忽略的点:RTL8723DS的WiFi和蓝牙共用一个射频开关。如果你的系统里蓝牙驱动先加载并且把射频切到了蓝牙状态,WiFi可能出现RFKILL被置位的情况。这时候不能只查WiFi,要看整个射频管理的状态。
5.4 能扫描到AP但连接不上
扫描没问题,选好AP输密码,但wpa_supplicant一直刷Authentication timed out或者4-way handshake failed。这类问题分两种情况。一是加密方式不匹配,RTL8723DS对WPA3的支持要看驱动版本,老驱动只处理WPA/WPA2,路由器强制WPA3时会反复握手失败,把路由器改成WPA2-PSK再测。二是信道规划问题,2.4G频段如果周围信道拥堵太严重,握手包丢失率很高,把AP固定到1、6、11中干扰较小的信道再试。
还有一个驱动层面的因素:有些版本的官方SDK对IEEE80211W(管理帧保护)配置有BUG,如果AP开启了PMF,连接就失败。测试时先关掉PMF,能连上后再评估是否需要更新驱动。
5.5 吞吐量不到理论值的一半
这个现象分几种原因,逐个排查:先确认iw dev wlan0 link看到的速率是多少,如果速率很低,问题在链路层,可能AP信号弱、距离远或者天线匹配差。如果协商速率正常,比如显示72Mbps但iperf3只有5Mbps,重点检查是不是SDIO总线时钟太低或者1bit模式。四线SDIO和1bit模式在主机侧是能直接看出来的,读设备树配置即可。
然后检查驱动里的省电策略。RTL8723DS在遇到弱信号时会自动降低发射功率和接收灵敏度,这在低功耗场景是优点,但测试吞吐量时会误导人。我建议测试时固定AP位置,板子隔AP三米内,并且关闭power save,再优化天线匹配和SDIO时钟,这样测出来的数据才反应真实能力。
还有一点,WiFi和蓝牙同时工作时吞吐量会受影响,这是RTL8723DS这类Combo芯片的通病,因为2.4G频段和蓝牙挤在一起。我这篇只讲WiFi,BT共存对WiFi吞吐量的影响细节留在BT篇单独说,但先给大家打个预防针:如果测试时BT也在工作,数据变差不一定是坏事,可能只是共存算法在起作用。
这次移植整体花了两天多,前期主要耗在内核配置和设备树排查上,一旦跑通流程,后面的测试和调优反而顺理成章。我个人实际体会是,RTL8723DS这类芯片的移植工作,七分在准备阶段,三分在编码阶段。把设备树、内核配置、固件部署这三件事一次性做对,剩下就是常规的网络调试。如果中间任何一个环节吊链子,排查时间往往比重新编译一次驱动还长。
最后再分享一个小技巧:调试WiFi驱动时,建议在dmesg里加上dyndbg过滤,比如modprobe 8723ds dyndbg=+p,可以看到驱动内部的详细日志,很多WiFi连不上的问题在驱动日志里会直接给出原因。这个开关比反复猜谜高效得多,反正驱动代码分支多,出问题别硬看代码,打开日志让代码自己说话。BT部分的内容,我下一篇再接着写。