
前阵子接手一块新的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驱动适配的思路是通用的。对比项RTL8723DSRTL8723BSAP6212WiFi规格802.11 b/g/n 2.4G802.11 b/g/n 2.4G802.11 b/g/n 2.4G蓝牙规格BT 4.2BT 4.0BT 4.2WiFi接口SDIOSDIOSDIOBT接口UART/PCMUART/PCMUART/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驱动内核rtl8xxxuSTA模式支持完善基础可用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 ARCHarm export CROSS_COMPILEarm-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_CFG80211y CONFIG_RFKILLy CONFIG_RFKILL_GPIOy CONFIG_MMCy CONFIG_MMC_SDHCIy CONFIG_MMC_SDHCI_PLTFMy CONFIG_FIRMWARE_LOADERy CONFIG_WIRELESSy CONFIG_NETDEVICESyCONFIG_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; wifi1 { 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 ARCHarm CROSS_COMPILEarm-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 countryCN network{ ssidMyTestAP psktestpassword key_mgmtWPA-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配置示例interfacewlan0 drivernl80211 ssidMyAP_Test hw_modeg channel6 wpa2 wpa_passphrase12345678 wpa_key_mgmtWPA-PSK country_codeCN启动后hostapd -B /etc/hostapd/hostapd.conf dnsmasq --interfacewlan0 --dhcp-range192.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支持FTMFactory 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频段和蓝牙挤在一起。我这篇只讲WiFiBT共存对WiFi吞吐量的影响细节留在BT篇单独说但先给大家打个预防针如果测试时BT也在工作数据变差不一定是坏事可能只是共存算法在起作用。这次移植整体花了两天多前期主要耗在内核配置和设备树排查上一旦跑通流程后面的测试和调优反而顺理成章。我个人实际体会是RTL8723DS这类芯片的移植工作七分在准备阶段三分在编码阶段。把设备树、内核配置、固件部署这三件事一次性做对剩下就是常规的网络调试。如果中间任何一个环节吊链子排查时间往往比重新编译一次驱动还长。最后再分享一个小技巧调试WiFi驱动时建议在dmesg里加上dyndbg过滤比如modprobe 8723ds dyndbgp可以看到驱动内部的详细日志很多WiFi连不上的问题在驱动日志里会直接给出原因。这个开关比反复猜谜高效得多反正驱动代码分支多出问题别硬看代码打开日志让代码自己说话。BT部分的内容我下一篇再接着写。